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. 21
      src/wrf/__init__.py
  4. 55
      src/wrf/api.py
  5. 139
      src/wrf/cache.py
  6. 2349
      src/wrf/computation.py
  7. 104
      src/wrf/config.py
  8. 43
      src/wrf/constants.py
  9. 243
      src/wrf/coordpair.py
  10. 456
      src/wrf/decorators.py
  11. 56
      src/wrf/destag.py
  12. 1263
      src/wrf/extension.py
  13. 809
      src/wrf/g_cape.py
  14. 688
      src/wrf/g_cloudfrac.py
  15. 149
      src/wrf/g_ctt.py
  16. 228
      src/wrf/g_dbz.py
  17. 191
      src/wrf/g_dewpoint.py
  18. 465
      src/wrf/g_geoht.py
  19. 233
      src/wrf/g_helicity.py
  20. 816
      src/wrf/g_latlon.py
  21. 87
      src/wrf/g_omega.py
  22. 12
      src/wrf/g_precip.py
  23. 183
      src/wrf/g_pressure.py
  24. 95
      src/wrf/g_pw.py
  25. 176
      src/wrf/g_rh.py
  26. 106
      src/wrf/g_slp.py
  27. 623
      src/wrf/g_temp.py
  28. 97
      src/wrf/g_terrain.py
  29. 161
      src/wrf/g_times.py
  30. 1007
      src/wrf/g_uvmet.py
  31. 179
      src/wrf/g_vorticity.py
  32. 989
      src/wrf/g_wind.py
  33. 71
      src/wrf/geobnds.py
  34. 945
      src/wrf/interp.py
  35. 454
      src/wrf/interputils.py
  36. 547
      src/wrf/latlonutils.py
  37. 1841
      src/wrf/metadecorators.py
  38. 988
      src/wrf/projection.py
  39. 13
      src/wrf/projutils.py
  40. 118
      src/wrf/py3compat.py
  41. 489
      src/wrf/routines.py
  42. 463
      src/wrf/specialdec.py
  43. 440
      src/wrf/units.py
  44. 3505
      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.*

21
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__)

55
src/wrf/api.py

@ -3,24 +3,24 @@ from .config import (xarray_enabled, disable_xarray, enable_xarray,
basemap_enabled, disable_basemap, enable_basemap, basemap_enabled, disable_basemap, enable_basemap,
pyngl_enabled, enable_pyngl, disable_pyngl, pyngl_enabled, enable_pyngl, disable_pyngl,
set_cache_size, get_cache_size, omp_enabled) set_cache_size, get_cache_size, omp_enabled)
from .constants import (ALL_TIMES, Constants, ConversionFactors, from .constants import (ALL_TIMES, Constants, ConversionFactors,
ProjectionTypes, default_fill, ProjectionTypes, default_fill,
OMP_SCHED_STATIC, OMP_SCHED_DYNAMIC, OMP_SCHED_STATIC, OMP_SCHED_DYNAMIC,
OMP_SCHED_GUIDED, OMP_SCHED_AUTO) OMP_SCHED_GUIDED, OMP_SCHED_AUTO)
from .destag import destagger from .destag import destagger
from .routines import getvar from .routines import getvar
from .computation import (xy, interp1d, interp2dxy, interpz3d, slp, tk, td, rh, from .computation import (xy, interp1d, interp2dxy, interpz3d, slp, tk, td, rh,
uvmet, smooth2d, cape_2d, cape_3d, cloudfrac, ctt, uvmet, smooth2d, cape_2d, cape_3d, cloudfrac, ctt,
dbz, srhel, udhel, avo, pvo, eth, wetbulb, tvirtual, dbz, srhel, udhel, avo, pvo, eth, wetbulb, tvirtual,
omega, pw) omega, pw)
from .extension import (DiagnosticError, omp_set_num_threads, from .extension import (DiagnosticError, omp_set_num_threads,
omp_get_num_threads, omp_get_num_threads,
omp_get_max_threads, omp_get_thread_num, omp_get_max_threads, omp_get_thread_num,
omp_get_num_procs, omp_in_parallel, omp_get_num_procs, omp_in_parallel,
omp_set_dynamic, omp_get_dynamic, omp_set_nested, omp_set_dynamic, omp_get_dynamic, omp_set_nested,
omp_get_nested, omp_set_schedule, omp_get_nested, omp_set_schedule,
omp_get_schedule, omp_get_thread_limit, omp_get_schedule, omp_get_thread_limit,
omp_set_max_active_levels, omp_set_max_active_levels,
omp_get_max_active_levels, omp_get_level, omp_get_max_active_levels, omp_get_level,
omp_get_ancestor_thread_num, omp_get_team_size, omp_get_ancestor_thread_num, omp_get_team_size,
omp_get_active_level, omp_in_final, omp_get_active_level, omp_in_final,
@ -32,23 +32,23 @@ from .extension import (DiagnosticError, omp_set_num_threads,
omp_get_wtime, omp_get_wtick) omp_get_wtime, omp_get_wtick)
from .interp import (interplevel, vertcross, interpline, vinterp) from .interp import (interplevel, vertcross, interpline, vinterp)
from .g_latlon import (xy_to_ll, ll_to_xy, xy_to_ll_proj, ll_to_xy_proj) from .g_latlon import (xy_to_ll, ll_to_xy, xy_to_ll_proj, ll_to_xy_proj)
from .py3compat import (viewitems, viewkeys, viewvalues, isstr, py2round, from .py3compat import (viewitems, viewkeys, viewvalues, isstr, py2round,
py3range, ucode) py3range, ucode)
from .util import (to_np, extract_global_attrs, is_standard_wrf_var, from .util import (to_np, extract_global_attrs, is_standard_wrf_var,
extract_dim, extract_vars, extract_times, combine_files, extract_dim, extract_vars, extract_times, combine_files,
extract_times, npbytes_to_str, is_moving_domain, extract_times, npbytes_to_str, is_moving_domain,
is_staggered, get_left_indexes, iter_left_indexes, is_staggered, get_left_indexes, iter_left_indexes,
get_right_slices, get_proj_params, from_args, get_right_slices, get_proj_params, from_args,
args_to_list, arg_location, psafilepath, get_id, args_to_list, arg_location, psafilepath, get_id,
from_var, combine_dims, either, get_iterable, from_var, combine_dims, either, get_iterable,
IterWrapper, is_coordvar, latlon_coordvars, is_mapping, IterWrapper, is_coordvar, latlon_coordvars, is_mapping,
has_time_coord, is_multi_file, is_multi_time_req, has_time_coord, is_multi_file, is_multi_time_req,
get_coord_pairs, is_time_coord_var, geo_bounds, get_coord_pairs, is_time_coord_var, geo_bounds,
get_cartopy, get_basemap, get_pyngl, cartopy_xlim, get_cartopy, get_basemap, get_pyngl, cartopy_xlim,
cartopy_ylim, latlon_coords, ll_points, pairs_to_latlon) cartopy_ylim, latlon_coords, ll_points, pairs_to_latlon)
from .geobnds import GeoBounds, NullGeoBounds from .geobnds import GeoBounds, NullGeoBounds
from .projection import (WrfProj, NullProjection, LambertConformal, Mercator, from .projection import (WrfProj, NullProjection, LambertConformal, Mercator,
PolarStereographic, LatLon, RotatedLatLon, PolarStereographic, LatLon, RotatedLatLon,
getproj) getproj)
from .coordpair import CoordPair from .coordpair import CoordPair
from .interputils import to_xy_coords from .interputils import to_xy_coords
@ -62,22 +62,22 @@ __all__ += ["xarray_enabled", "disable_xarray", "enable_xarray",
"pyngl_enabled", "enable_pyngl", "disable_pyngl", "pyngl_enabled", "enable_pyngl", "disable_pyngl",
"set_cache_size", "get_cache_size", "omp_enabled"] "set_cache_size", "get_cache_size", "omp_enabled"]
__all__ += ["ALL_TIMES", "Constants", "ConversionFactors", "ProjectionTypes", __all__ += ["ALL_TIMES", "Constants", "ConversionFactors", "ProjectionTypes",
"default_fill", "OMP_SCHED_STATIC", "OMP_SCHED_DYNAMIC", "default_fill", "OMP_SCHED_STATIC", "OMP_SCHED_DYNAMIC",
"OMP_SCHED_GUIDED", "OMP_SCHED_AUTO"] "OMP_SCHED_GUIDED", "OMP_SCHED_AUTO"]
__all__ += ["destagger"] __all__ += ["destagger"]
__all__ += ["getvar"] __all__ += ["getvar"]
__all__ += ["xy", "interp1d", "interp2dxy", "interpz3d", "slp", "tk", "td", __all__ += ["xy", "interp1d", "interp2dxy", "interpz3d", "slp", "tk", "td",
"rh", "uvmet", "smooth2d", "cape_2d", "cape_3d", "cloudfrac", "rh", "uvmet", "smooth2d", "cape_2d", "cape_3d", "cloudfrac",
"ctt", "dbz", "srhel", "udhel", "avo", "pvo", "eth", "wetbulb", "ctt", "dbz", "srhel", "udhel", "avo", "pvo", "eth", "wetbulb",
"tvirtual", "omega", "pw"] "tvirtual", "omega", "pw"]
__all__ += ["DiagnosticError", "omp_set_num_threads", __all__ += ["DiagnosticError", "omp_set_num_threads",
"omp_get_num_threads", "omp_get_num_threads",
"omp_get_max_threads", "omp_get_thread_num", "omp_get_max_threads", "omp_get_thread_num",
"omp_get_num_procs", "omp_in_parallel", "omp_get_num_procs", "omp_in_parallel",
"omp_set_dynamic", "omp_get_dynamic", "omp_set_nested", "omp_set_dynamic", "omp_get_dynamic", "omp_set_nested",
"omp_get_nested", "omp_set_schedule", "omp_get_nested", "omp_set_schedule",
"omp_get_schedule", "omp_get_thread_limit", "omp_get_schedule", "omp_get_thread_limit",
"omp_set_max_active_levels", "omp_set_max_active_levels",
"omp_get_max_active_levels", "omp_get_level", "omp_get_max_active_levels", "omp_get_level",
"omp_get_ancestor_thread_num", "omp_get_team_size", "omp_get_ancestor_thread_num", "omp_get_team_size",
"omp_get_active_level", "omp_in_final", "omp_get_active_level", "omp_in_final",
@ -89,18 +89,18 @@ __all__ += ["DiagnosticError", "omp_set_num_threads",
"omp_get_wtime", "omp_get_wtick"] "omp_get_wtime", "omp_get_wtick"]
__all__ += ["interplevel", "vertcross", "interpline", "vinterp"] __all__ += ["interplevel", "vertcross", "interpline", "vinterp"]
__all__ += ["xy_to_ll", "ll_to_xy", "xy_to_ll_proj", "ll_to_xy_proj"] __all__ += ["xy_to_ll", "ll_to_xy", "xy_to_ll_proj", "ll_to_xy_proj"]
__all__ += ["viewitems", "viewkeys", "viewvalues", "isstr", "py2round", __all__ += ["viewitems", "viewkeys", "viewvalues", "isstr", "py2round",
"py3range", "ucode"] "py3range", "ucode"]
__all__ += ["to_np", "extract_global_attrs", "is_standard_wrf_var", __all__ += ["to_np", "extract_global_attrs", "is_standard_wrf_var",
"extract_dim", "extract_vars", "extract_times", "combine_files", "extract_dim", "extract_vars", "extract_times", "combine_files",
"extract_times", "npbytes_to_str", "is_moving_domain", "extract_times", "npbytes_to_str", "is_moving_domain",
"is_staggered", "get_left_indexes", "iter_left_indexes", "is_staggered", "get_left_indexes", "iter_left_indexes",
"get_right_slices", "get_proj_params", "from_args", "get_right_slices", "get_proj_params", "from_args",
"args_to_list", "arg_location", "psafilepath", "get_id", "args_to_list", "arg_location", "psafilepath", "get_id",
"from_var", "combine_dims", "either", "get_iterable", "from_var", "combine_dims", "either", "get_iterable",
"IterWrapper", "is_coordvar", "latlon_coordvars", "is_mapping", "IterWrapper", "is_coordvar", "latlon_coordvars", "is_mapping",
"has_time_coord", "is_multi_file", "is_multi_time_req", "has_time_coord", "is_multi_file", "is_multi_time_req",
"get_coord_pairs", "is_time_coord_var", "geo_bounds", "get_coord_pairs", "is_time_coord_var", "geo_bounds",
"get_cartopy", "get_basemap", "get_pyngl", "cartopy_xlim", "get_cartopy", "get_basemap", "get_pyngl", "cartopy_xlim",
"cartopy_ylim", "latlon_coords", "ll_points", "pairs_to_latlon"] "cartopy_ylim", "latlon_coords", "ll_points", "pairs_to_latlon"]
__all__ += ["GeoBounds", "NullGeoBounds"] __all__ += ["GeoBounds", "NullGeoBounds"]
@ -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__"]

139
src/wrf/cache.py

@ -11,158 +11,153 @@ _local_storage = local()
def _shrink_cache(): def _shrink_cache():
"""Shrink the cache if applicable. """Shrink the cache if applicable.
This only applies if a user has modified the cache size, otherwise it This only applies if a user has modified the cache size, otherwise it
just returns. just returns.
Returns: Returns:
None None
""" """
global _local_storage global _local_storage
try: try:
cache = _local_storage.cache cache = _local_storage.cache
except AttributeError: except AttributeError:
return return
diff = len(cache) - get_cache_size() diff = len(cache) - get_cache_size()
if diff > 0: if diff > 0:
for _ in py3range(diff): for _ in py3range(diff):
cache.popitem(last=False) cache.popitem(last=False)
def cache_item(key, product, value): def cache_item(key, product, value):
"""Store an item in the threadlocal cache. """Store an item in the threadlocal cache.
The cache should be viewed as two nested dictionaries. The outer key is The cache should be viewed as two nested dictionaries. The outer key is
usually the id for the sequence where the cached item was generated. The usually the id for the sequence where the cached item was generated. The
inner key is the product type. inner key is the product type.
Storing a cached item behaves like: Storing a cached item behaves like:
cache[key][product] = value cache[key][product] = value
The cache is thread local, so stored items are only available in The cache is thread local, so stored items are only available in
the thread that cached them. the thread that cached them.
Args: Args:
key (:obj:`int`): The outer dictionary cache key, which is typically key (:obj:`int`): The outer dictionary cache key, which is typically
the id of the sequence where the cached item was generated. the id of the sequence where the cached item was generated.
product (:obj:`str`): The inner dictionary cache key, which is a product (:obj:`str`): The inner dictionary cache key, which is a
string for the product type. string for the product type.
value (:obj:`object`): The object to store in the cache. value (:obj:`object`): The object to store in the cache.
Returns: Returns:
None. None.
See Also: See Also:
:meth:`get_cached_item` :meth:`get_cached_item`
""" """
global _local_storage global _local_storage
_shrink_cache() _shrink_cache()
if key is None or get_cache_size() == 0: if key is None or get_cache_size() == 0:
return return
try: try:
cache = _local_storage.cache cache = _local_storage.cache
except AttributeError: except AttributeError:
_local_storage.cache = OrderedDict() _local_storage.cache = OrderedDict()
cache = _local_storage.cache cache = _local_storage.cache
try: try:
_ = cache[key] _ = cache[key]
except KeyError: except KeyError:
if len(cache) >= get_cache_size(): if len(cache) >= get_cache_size():
cache.popitem(last=False) # Remove the oldest dataset cache.popitem(last=False) # Remove the oldest dataset
cache[key] = OrderedDict() cache[key] = OrderedDict()
cache[key][product] = value cache[key][product] = value
def get_cached_item(key, product): def get_cached_item(key, product):
"""Return an item from the threadlocal cache. """Return an item from the threadlocal cache.
The cache should be viewed as two nested dictionaries. The outer key is The cache should be viewed as two nested dictionaries. The outer key is
usually the id for the sequence where the cached item was generated. The usually the id for the sequence where the cached item was generated. The
inner key is the product type. inner key is the product type.
Retrieving a cached item behaves like: Retrieving a cached item behaves like:
value = cache[key][product] value = cache[key][product]
The cache is thread local, so stored items are only available in The cache is thread local, so stored items are only available in
the thread that cached them. the thread that cached them.
Args: Args:
key (:obj:`int`): The outer dictionary cache key, which is typically key (:obj:`int`): The outer dictionary cache key, which is typically
the id of the sequence where the cached item was generated. the id of the sequence where the cached item was generated.
product (:obj:`str`): The inner dictionary cache key, which is a product (:obj:`str`): The inner dictionary cache key, which is a
string for the product type. string for the product type.
Returns: Returns:
:obj:`object`: The cached object. :obj:`object`: The cached object.
See Also: See Also:
:meth:`cache_item` :meth:`cache_item`
""" """
global _local_storage global _local_storage
_shrink_cache() _shrink_cache()
if key is None or get_cache_size == 0: if key is None or get_cache_size == 0:
return None return None
cache = getattr(_local_storage, "cache", None) cache = getattr(_local_storage, "cache", None)
if cache is None: if cache is None:
return None return None
if len(cache) == 0: if len(cache) == 0:
return None return None
prod_dict = cache.get(key, None) prod_dict = cache.get(key, None)
if prod_dict is None: if prod_dict is None:
return None return None
result = prod_dict.get(product, None) result = prod_dict.get(product, None)
return result return result
def _get_cache(): def _get_cache():
"""Return the threadlocal cache. """Return the threadlocal cache.
This is primarily used for testing. This is primarily used for testing.
Returns: Returns:
:class:`threading.local` :class:`threading.local`
""" """
global _local_storage global _local_storage
_shrink_cache() _shrink_cache()
return getattr(_local_storage, "cache", None) return getattr(_local_storage, "cache", None)

2349
src/wrf/computation.py

File diff suppressed because it is too large Load Diff

104
src/wrf/config.py

@ -3,42 +3,43 @@ from __future__ import (absolute_import, division, print_function)
from threading import local from threading import local
import wrapt import wrapt
from ._wrffortran import (fomp_enabled, fomp_set_num_threads, from ._wrffortran import (fomp_enabled, fomp_set_num_threads,
fomp_set_schedule, fomp_set_dynamic, fomp_set_schedule, fomp_set_dynamic,
omp_constants) omp_constants)
_local_config = local() _local_config = local()
def _init_local(): def _init_local():
global _local_config global _local_config
_local_config.xarray_enabled = True _local_config.xarray_enabled = True
_local_config.cartopy_enabled = True _local_config.cartopy_enabled = True
_local_config.basemap_enabled = True _local_config.basemap_enabled = True
_local_config.pyngl_enabled = True _local_config.pyngl_enabled = True
_local_config.cache_size = 20 _local_config.cache_size = 20
_local_config.initialized = True _local_config.initialized = True
try: try:
from xarray import DataArray from xarray import DataArray
except ImportError: except ImportError:
_local_config.xarray_enabled = False _local_config.xarray_enabled = False
try: try:
from cartopy import crs from cartopy import crs
except ImportError: except ImportError:
_local_config.cartopy_enabled = False _local_config.cartopy_enabled = False
try: try:
from mpl_toolkits.basemap import Basemap from mpl_toolkits.basemap import Basemap
except ImportError: except ImportError:
_local_config.basemap_enabled = False _local_config.basemap_enabled = False
try: try:
from Ngl import Resources from Ngl import Resources
except ImportError: except ImportError:
_local_config.pyngl_enabled = False _local_config.pyngl_enabled = False
# Initialize the main thread's configuration # Initialize the main thread's configuration
_init_local() _init_local()
@ -55,21 +56,21 @@ def init_local():
_init_local() _init_local()
else: else:
if not init: if not init:
_init_local() _init_local()
return wrapped(*args, **kwargs) return wrapped(*args, **kwargs)
return func_wrapper return func_wrapper
@init_local() @init_local()
def xarray_enabled(): def xarray_enabled():
"""Return True if xarray is installed and enabled. """Return True if xarray is installed and enabled.
Returns: Returns:
:obj:`bool`: True if xarray is installed and enabled. :obj:`bool`: True if xarray is installed and enabled.
""" """
global _local_config global _local_config
return _local_config.xarray_enabled return _local_config.xarray_enabled
@ -80,23 +81,23 @@ def disable_xarray():
"""Disable xarray.""" """Disable xarray."""
global _local_config global _local_config
_local_config.xarray_enabled = False _local_config.xarray_enabled = False
@init_local() @init_local()
def enable_xarray(): def enable_xarray():
"""Enable xarray.""" """Enable xarray."""
global _local_config global _local_config
_local_config.xarray_enabled = True _local_config.xarray_enabled = True
@init_local() @init_local()
def cartopy_enabled(): def cartopy_enabled():
"""Return True if cartopy is installed and enabled. """Return True if cartopy is installed and enabled.
Returns: Returns:
:obj:`bool`: True if cartopy is installed and enabled. :obj:`bool`: True if cartopy is installed and enabled.
""" """
global _local_config global _local_config
return _local_config.cartopy_enabled return _local_config.cartopy_enabled
@ -107,23 +108,23 @@ def enable_cartopy():
"""Enable cartopy.""" """Enable cartopy."""
global _local_config global _local_config
_local_config.cartopy_enabled = True _local_config.cartopy_enabled = True
@init_local() @init_local()
def disable_cartopy(): def disable_cartopy():
"""Disable cartopy.""" """Disable cartopy."""
global _local_config global _local_config
_local_config.cartopy_enabled = True _local_config.cartopy_enabled = True
@init_local() @init_local()
def basemap_enabled(): def basemap_enabled():
"""Return True if basemap is installed and enabled. """Return True if basemap is installed and enabled.
Returns: Returns:
:obj:`bool`: True if basemap is installed and enabled. :obj:`bool`: True if basemap is installed and enabled.
""" """
global _local_config global _local_config
return _local_config.basemap_enabled return _local_config.basemap_enabled
@ -134,7 +135,7 @@ def enable_basemap():
"""Enable basemap.""" """Enable basemap."""
global _local_config global _local_config
_local_config.basemap_enabled = True _local_config.basemap_enabled = True
@init_local() @init_local()
def disable_basemap(): def disable_basemap():
@ -146,11 +147,11 @@ def disable_basemap():
@init_local() @init_local()
def pyngl_enabled(): def pyngl_enabled():
"""Return True if pyngl is installed and enabled. """Return True if pyngl is installed and enabled.
Returns: Returns:
:obj:`bool`: True if pyngl is installed and enabled. :obj:`bool`: True if pyngl is installed and enabled.
""" """
global _local_config global _local_config
return _local_config.pyngl_enabled return _local_config.pyngl_enabled
@ -161,63 +162,64 @@ def enable_pyngl():
"""Enable pyngl.""" """Enable pyngl."""
global _local_config global _local_config
_local_config.pyngl_enabled = True _local_config.pyngl_enabled = True
@init_local()
@init_local()
def disable_pyngl(): def disable_pyngl():
"""Disable pyngl.""" """Disable pyngl."""
global _local_config global _local_config
_local_config.pyngl_enabled = True _local_config.pyngl_enabled = True
@init_local()
@init_local()
def set_cache_size(size): def set_cache_size(size):
"""Set the maximum number of items that the threadlocal cache can retain. """Set the maximum number of items that the threadlocal cache can retain.
This cache is primarily used for coordinate variables. This cache is primarily used for coordinate variables.
Args: Args:
size (:obj:`int`): The number of items to retain in the cache. size (:obj:`int`): The number of items to retain in the cache.
Returns: Returns:
None None
""" """
global _local_config global _local_config
_local_config.cache_size = size _local_config.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:
:obj:`int`: The maximum number of items the cache can retain. :obj:`int`: The maximum number of items the cache can retain.
""" """
global _local_config global _local_config
return int(_local_config.cache_size) return int(_local_config.cache_size)
def omp_enabled(): def omp_enabled():
"""Return True if OpenMP is enabled. """Return True if OpenMP is enabled.
OpenMP is only enabled if compiled with OpenMP features. OpenMP is only enabled if compiled with OpenMP features.
Returns: Returns:
:obj:`bool`: True if OpenMP is enabled, otherwise False. :obj:`bool`: True if OpenMP is enabled, otherwise False.
""" """
return True if fomp_enabled() else False return True if fomp_enabled() else False
# Set OpenMP to use 1 thread, static scheduler, and no dynamic # Set OpenMP to use 1 thread, static scheduler, and no dynamic
# Note: Using the raw extension functions here to prevent possible # Note: Using the raw extension functions here to prevent possible
# circular import problems in the future. # circular import problems in the future.
fomp_set_num_threads(1) fomp_set_num_threads(1)
fomp_set_schedule(omp_constants.fomp_sched_static, 0) fomp_set_schedule(omp_constants.fomp_sched_static, 0)

43
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
@ -35,7 +37,7 @@ class ConversionFactors(object):
M_TO_DM = 1.0/10.0 M_TO_DM = 1.0/10.0
M_TO_FT = 3.28084 M_TO_FT = 3.28084
M_TO_MILES = .000621371 M_TO_MILES = .000621371
class ProjectionTypes(object): class ProjectionTypes(object):
ZERO = 0 ZERO = 0
@ -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)

243
src/wrf/coordpair.py

@ -1,265 +1,254 @@
from __future__ import (absolute_import, division, print_function) from __future__ import (absolute_import, division, print_function)
from .py3compat import py2round from .py3compat import py2round
def _binary_operator(operator): def _binary_operator(operator):
"""Function wrapper for binary operators. """Function wrapper for binary operators.
Args: Args:
operator (method): The operator to wrap. operator (method): The operator to wrap.
Returns: Returns:
method: An implementation for the *operator* type. method: An implementation for the *operator* type.
""" """
def func(self, other): def func(self, other):
"""Operator implementation. """Operator implementation.
Operator action is performed across the same class attributes when Operator action is performed across the same class attributes when
the *other* object is a :class:`CoordPair`. If the *other* object is the *other* object is a :class:`CoordPair`. If the *other* object is
a scalar value, the operator action is performed across all a scalar value, the operator action is performed across all
attributes with the scalar value. attributes with the scalar value.
Args: Args:
other (:class:`CoordPair` or scalar): A separate :class:`CoordPair` other (:class:`CoordPair` or scalar): A separate :class:`CoordPair`
object or scalar value. object or scalar value.
Returns: Returns:
:class:`CoordPair`: A new :class:`CoordPair` object that is the :class:`CoordPair`: A new :class:`CoordPair` object that is the
result of the operator action. result of the operator action.
""" """
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,
for attr in ("x", "y", "lat", "lon")] attr))
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")]
return CoordPair(*args) return CoordPair(*args)
return func return func
def _unary_operator(operator): def _unary_operator(operator):
"""Function wrapper for unary operators. """Function wrapper for unary operators.
Args: Args:
operator (method): The operator to wrap. operator (method): The operator to wrap.
Returns: Returns:
method: An implementation for the *operator* type. method: An implementation for the *operator* type.
""" """
def func(self): def func(self):
"""Operator implementation. """Operator implementation.
Operator action is performed across all class attributes. Operator action is performed across all class attributes.
Returns: Returns:
:class:`CoordPair`: A new :class:`CoordPair` object that is the :class:`CoordPair`: A new :class:`CoordPair` object that is the
result of the operator action. result of the operator action.
""" """
args = [None if getattr(self, attr) is None args = [None if getattr(self, attr) is None
else getattr(getattr(self, attr), operator)() else getattr(getattr(self, attr), operator)()
for attr in ("x", "y", "lat", "lon")] for attr in ("x", "y", "lat", "lon")]
return CoordPair(*args) return CoordPair(*args)
return func return func
def _cmp_operator(operator): def _cmp_operator(operator):
"""Function wrapper for comparison operators. """Function wrapper for comparison operators.
Args: Args:
operator (method): The operator to wrap. operator (method): The operator to wrap.
Returns: Returns:
method: An implementation for the *operator* type. method: An implementation for the *operator* type.
""" """
def func(self, other): def func(self, other):
"""Operator implementation. """Operator implementation.
Performs a comparison operation across all of the same class Performs a comparison operation across all of the same class
attributes, and returns True if all these operations are True. attributes, and returns True if all these operations are True.
Returns: Returns:
:obj:`boot`: Returns True if all comparisons across class :obj:`boot`: Returns True if all comparisons across class
attributes returns True, otherwise False. attributes returns True, otherwise False.
""" """
vals = [getattr(getattr(self, attr), operator)(getattr(other, attr)) vals = [getattr(getattr(self, attr), operator)(getattr(other, attr))
for attr in ("x", "y", "lat", "lon") for attr in ("x", "y", "lat", "lon")
if getattr(self, attr) is not None] if getattr(self, attr) is not None]
return all(vals) return all(vals)
return func return func
class CoordPair(object): class CoordPair(object):
"""A class that stores (x, y) and/or (latitude, longitude) """A class that stores (x, y) and/or (latitude, longitude)
coordinate pairs. coordinate pairs.
Most math operators are supplied. When the other operand is a Most math operators are supplied. When the other operand is a
:class:`CoordPair`, the operation is performed with the same attribute. :class:`CoordPair`, the operation is performed with the same attribute.
When a math operation uses a scalar as the other operand, the When a math operation uses a scalar as the other operand, the
operation is applied across all attributes. operation is applied across all attributes.
Attributes: Attributes:
x (:obj:`float`): The x-coordinate. x (:obj:`float`): The x-coordinate.
y (:obj:`float`): The y-coordinate. y (:obj:`float`): The y-coordinate.
lat (:obj:`float`): The latitude coordinate. lat (:obj:`float`): The latitude coordinate.
lon (:obj:`float`): The longitude coordinate. lon (:obj:`float`): The longitude coordinate.
""" """
def __init__(self, x=None, y=None, lat=None, lon=None): def __init__(self, x=None, y=None, lat=None, lon=None):
"""Initialize a :class:`CoordPair` object. """Initialize a :class:`CoordPair` object.
Args: Args:
x (:obj:`float`, optional): The x-coordinate. x (:obj:`float`, optional): The x-coordinate.
y (:obj:`float`, optional): The y-coordinate. y (:obj:`float`, optional): The y-coordinate.
lat (:obj:`float`, optional): The latitude coordinate. lat (:obj:`float`, optional): The latitude coordinate.
lon (:obj:`float`, optional): The longitude coordinate. lon (:obj:`float`, optional): The longitude coordinate.
""" """
self.x = x self.x = x
self.y = y self.y = y
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:
args.append("x={}".format(self.x)) args.append("x={}".format(self.x))
args.append("y={}".format(self.y)) args.append("y={}".format(self.y))
if self.lat is not None: if self.lat is not None:
args.append("lat={}".format(self.lat)) args.append("lat={}".format(self.lat))
args.append("lon={}".format(self.lon)) args.append("lon={}".format(self.lon))
argstr = ", ".join(args) argstr = ", ".join(args)
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.
Args: Args:
fmt (:obj:`str`): The format string. Default is '{:.4f}, {:.4f}' fmt (:obj:`str`): The format string. Default is '{:.4f}, {:.4f}'
Returns: Returns:
:obj:`str`: A string for the (x,y) coordinate pair :obj:`str`: A string for the (x,y) coordinate pair
""" """
if self.x is None or self.y is None: if self.x is None or self.y is None:
return None return None
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.
Args: Args:
fmt (:obj:`str`): The format string. Default is '{:.4f}, {:.4f}' fmt (:obj:`str`): The format string. Default is '{:.4f}, {:.4f}'
Returns: Returns:
:obj:`str`: A string for the (latitude, longitude) coordinate pair :obj:`str`: A string for the (latitude, longitude) coordinate pair
""" """
if self.lat is None or self.lon is None: if self.lat is None or self.lon is None:
return None return None
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.
Args: Args:
ndigits (:obj:`int`): The number of digits. ndigits (:obj:`int`): The number of digits.
Returns: Returns:
:class:`CoordPair`: A CoordPair object. :class:`CoordPair`: A CoordPair object.
""" """
args = [None if getattr(self, attr) is None args = [None if getattr(self, attr) is None
else py2round(getattr(self, attr), ndigits) else py2round(getattr(self, attr), ndigits)
for attr in ("x", "y", "lat", "lon")] for attr in ("x", "y", "lat", "lon")]
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)
for operator in ("__add__", "__divmod__", "__floordiv__", "__mod__", for operator in ("__add__", "__divmod__", "__floordiv__", "__mod__",
"__mul__", "__sub__", "__truediv__", "__radd__", "__mul__", "__sub__", "__truediv__", "__radd__",
"__rdivmod__", "__rsub__", "__rmul__", "__rtruediv__", "__rdivmod__", "__rsub__", "__rmul__", "__rtruediv__",
"__rfloordiv__", "__rmod__"): "__rfloordiv__", "__rmod__"):
setattr(CoordPair, operator, _binary_operator(operator)) setattr(CoordPair, operator, _binary_operator(operator))
for operator in ("__neg__", "__pos__", "__abs__", "__invert__"): for operator in ("__neg__", "__pos__", "__abs__", "__invert__"):
setattr(CoordPair, operator, _unary_operator(operator)) setattr(CoordPair, operator, _unary_operator(operator))
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))

456
src/wrf/decorators.py

@ -2,7 +2,7 @@ from __future__ import (absolute_import, division, print_function)
from collections import Iterable, OrderedDict from collections import Iterable, OrderedDict
import wrapt import wrapt
import numpy as np import numpy as np
import numpy.ma as ma import numpy.ma as ma
@ -14,188 +14,177 @@ 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.
The desired units are determined from the wrapped function's arguments. The desired units are determined from the wrapped function's arguments.
Args: Args:
unit_type (:obj:`str`): The unit type. Choices are: 'wind', unit_type (:obj:`str`): The unit type. Choices are: 'wind',
'pressure', 'temp', or 'height'. 'pressure', 'temp', or 'height'.
alg_unit (:obj:`str`): The units returned by the wrapped function, alg_unit (:obj:`str`): The units returned by the wrapped function,
which is usually the units returned by the Fortran routine. which is usually the units returned by the Fortran routine.
Returns: Returns:
:class:`numpy.ndarray`: The wrapped function's output in the desired :class:`numpy.ndarray`: The wrapped function's output in the desired
units. units.
""" """
@wrapt.decorator @wrapt.decorator
def func_wrapper(wrapped, instance, args, kwargs): def func_wrapper(wrapped, instance, args, kwargs):
desired_units = from_args(wrapped, "units", *args, **kwargs)["units"] desired_units = from_args(wrapped, "units", *args, **kwargs)["units"]
u_cleaned = dealias_and_clean_unit(desired_units) u_cleaned = dealias_and_clean_unit(desired_units)
check_units(u_cleaned, unit_type) check_units(u_cleaned, unit_type)
# Unit conversion done here # Unit conversion done here
return do_conversion(wrapped(*args, **kwargs), unit_type, return do_conversion(wrapped(*args, **kwargs), unit_type,
alg_unit, desired_units) alg_unit, desired_units)
return func_wrapper
return 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,
ref_var_idx=None, ref_var_idx=None,
ref_var_name=None, ref_var_name=None,
ignore_args=None, ignore_args=None,
ignore_kargs=None, ignore_kargs=None,
outviews="outview", outviews="outview",
alg_dtype=np.float64, alg_dtype=np.float64,
cast_output=True): cast_output=True):
"""A decorator to handle iterating over the leftmost dimensions. """A decorator to handle iterating over the leftmost dimensions.
For example, if a wrapped function works with three-dimensional arrays, but For example, if a wrapped function works with three-dimensional arrays, but
the variables include a 4th leftmost dimension for 'Time', this decorator the variables include a 4th leftmost dimension for 'Time', this decorator
will iterate over all times, call the 3D Fortran routine, and aggregate the will iterate over all times, call the 3D Fortran routine, and aggregate the
results in to a 4D output array. results in to a 4D output array.
It is also important to note that the final output array is allocated It is also important to note that the final output array is allocated
first, and then views are passed to the wrapped function so that values first, and then views are passed to the wrapped function so that values
do not need to get copied in to the final output array. do not need to get copied in to the final output array.
Args: Args:
ref_var_expected_dims (:obj:`int`): The number of dimensions that the ref_var_expected_dims (:obj:`int`): The number of dimensions that the
Fortran routine is expecting for the reference variable. Fortran routine is expecting for the reference variable.
ref_var_right_ndims (:obj:`int`): The number of dimensions from the ref_var_right_ndims (:obj:`int`): The number of dimensions from the
right to keep for the reference variable when making the output. right to keep for the reference variable when making the output.
Can also be a :class:`combine_dims` object if the sizes are Can also be a :class:`combine_dims` object if the sizes are
determined from multiple variables. determined from multiple variables.
insert_dims (sequence of :obj:`int`, optional): A sequence of insert_dims (sequence of :obj:`int`, optional): A sequence of
dimensions to insert between the left dimensions (e.g. time) and dimensions to insert between the left dimensions (e.g. time) and
the kept right dimensions. Default is None. the kept right dimensions. Default is None.
ref_var_idx (:obj:`int`, optional): The index in the wrapped function's ref_var_idx (:obj:`int`, optional): The index in the wrapped function's
positional arguments to be used as the reference variable for positional arguments to be used as the reference variable for
determining the leftmost dimensions. Must be specified if determining the leftmost dimensions. Must be specified if
*ref_var_name* is None. Default is None. *ref_var_name* is None. Default is None.
ref_var_name (:obj:`str`, optional): The keyword argument name for the ref_var_name (:obj:`str`, optional): The keyword argument name for the
wrapped function's keyword arguments to be used as the reference wrapped function's keyword arguments to be used as the reference
variable for calculating the leftmost dimensions. Must be variable for calculating the leftmost dimensions. Must be
specified if *ref_var_idx* is None. Default is None. specified if *ref_var_idx* is None. Default is None.
ignore_args (sequence of :obj:`int`): Indexes of any arguments that ignore_args (sequence of :obj:`int`): Indexes of any arguments that
should be ignored when creating the sliced views that are should be ignored when creating the sliced views that are
passed to the Fortran routine. passed to the Fortran routine.
ignore_kargs (sequence of :obj:`str`): Keys of any keyword arguments ignore_kargs (sequence of :obj:`str`): Keys of any keyword arguments
that should be ignored when creating the sliced views that are that should be ignored when creating the sliced views that are
passed to the Fortran routine. passed to the Fortran routine.
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
as the output variable(s) in the wrapped function. as the output variable(s) in the wrapped function.
alg_dtype (:class:`numpy.dtype` or :obj:`str`): The numpy data type alg_dtype (:class:`numpy.dtype` or :obj:`str`): The numpy data type
used in the wrapped function. used in the wrapped function.
cast_output (:obj:`bool`): Set to True to cast the wrapped function's cast_output (:obj:`bool`): Set to True to cast the wrapped function's
output to the same type as the reference variable. output to the same type as the reference variable.
Returns: Returns:
:class:`numpy.ndarray`: The aggregated output array that includes :class:`numpy.ndarray`: The aggregated output array that includes
all extra leftmost dimensions found in the reference variable. all extra leftmost dimensions found in the reference variable.
""" """
@wrapt.decorator @wrapt.decorator
def func_wrapper(wrapped, instance, args, kwargs): def func_wrapper(wrapped, instance, args, kwargs):
_ignore_args = ignore_args if ignore_args is not None else () _ignore_args = ignore_args if ignore_args is not None else ()
_ignore_kargs = ignore_kargs if ignore_kargs is not None else () _ignore_kargs = ignore_kargs if ignore_kargs is not None else ()
_outkeys = [outviews] if isstr(outviews) else outviews _outkeys = [outviews] if isstr(outviews) else outviews
if ref_var_idx is not None: if ref_var_idx is not None:
ref_var = args[ref_var_idx] ref_var = args[ref_var_idx]
else: else:
ref_var = kwargs[ref_var_name] ref_var = kwargs[ref_var_name]
ref_var_dtype = ref_var.dtype ref_var_dtype = ref_var.dtype
ref_var_shape = ref_var.shape ref_var_shape = ref_var.shape
extra_dim_num = ref_var.ndim - ref_var_expected_dims extra_dim_num = ref_var.ndim - ref_var_expected_dims
# No special left side iteration, return the function result # No special left side iteration, return the function result
if (extra_dim_num == 0): if (extra_dim_num == 0):
return wrapped(*args, **kwargs) return wrapped(*args, **kwargs)
# Start by getting the left-most 'extra' dims # Start by getting the left-most 'extra' dims
extra_dims = ref_var_shape[0:extra_dim_num] extra_dims = ref_var_shape[0:extra_dim_num]
mid_dims = () if insert_dims is None else tuple(insert_dims) mid_dims = () if insert_dims is None else tuple(insert_dims)
if not isinstance(ref_var_right_ndims, combine_dims): if not isinstance(ref_var_right_ndims, combine_dims):
right_dims = ref_var_shape[-ref_var_right_ndims:] right_dims = ref_var_shape[-ref_var_right_ndims:]
else: else:
right_dims = ref_var_right_ndims(*args) right_dims = ref_var_right_ndims(*args)
left_dims = extra_dims left_dims = extra_dims
outdims = left_dims + mid_dims + right_dims outdims = left_dims + mid_dims + right_dims
if "outview" not in kwargs: if "outview" not in kwargs:
outd = OrderedDict((outkey, np.empty(outdims, alg_dtype)) outd = OrderedDict((outkey, np.empty(outdims, alg_dtype))
for outkey in _outkeys) for outkey in _outkeys)
mask_output = False mask_output = False
for left_idxs in iter_left_indexes(extra_dims): for left_idxs in iter_left_indexes(extra_dims):
# Make the left indexes plus a single slice object # Make the left indexes plus a single slice object
# The single slice will handle all the dimensions to # The single slice will handle all the dimensions to
# the right (e.g. [1,1,:]) # the right (e.g. [1,1,:])
left_and_slice_idxs = 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
for arg in new_args: for arg in new_args:
try: try:
_ = arg.ndim _ = arg.ndim
except AttributeError: except AttributeError:
continue # Not an array object continue # Not an array object
else: else:
arr = to_np(arg) arr = to_np(arg)
try: try:
all_masked = arr.mask.all() all_masked = arr.mask.all()
except AttributeError: except AttributeError:
pass # Not a masked array pass # Not a masked array
else: else:
if all_masked: if all_masked:
for output in viewvalues(outd): for output in viewvalues(outd):
@ -204,261 +193,257 @@ def left_iteration(ref_var_expected_dims,
skip_missing = True skip_missing = True
mask_output = True mask_output = True
break break
if skip_missing: if skip_missing:
continue continue
# 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
result = wrapped(*new_args, **new_kargs) result = wrapped(*new_args, **new_kargs)
# Make sure the result is the same data as what got passed in # Make sure the result is the same data as what got passed in
# Can delete this once everything works # Can delete this once everything works
if (result.__array_interface__["data"][0] != if (result.__array_interface__["data"][0] !=
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)
else: else:
output = tuple(arr.astype(ref_var_dtype) for arr in output) output = tuple(arr.astype(ref_var_dtype) for arr in output)
# Mostly when used with join # Mostly when used with join
if mask_output: if mask_output:
if isinstance(output, np.ndarray): if isinstance(output, np.ndarray):
output = ma.masked_values(output, default_fill(np.float64)) output = ma.masked_values(output, default_fill(np.float64))
else: else:
output = tuple(ma.masked_values(arr, default_fill(np.float64)) output = tuple(ma.masked_values(arr, default_fill(np.float64))
for arr in output) for arr in output)
return output return output
return func_wrapper return func_wrapper
def cast_type(ref_idx=0, arg_idxs=None, karg_names=None, def cast_type(ref_idx=0, arg_idxs=None, karg_names=None,
alg_dtype=np.float64, outviews="outview"): alg_dtype=np.float64, outviews="outview"):
"""A decorator to handle type casting. """A decorator to handle type casting.
This decorator is used to cast variables to and from the required This decorator is used to cast variables to and from the required
:class:`numpy.dtype` used in the wrapped function. :class:`numpy.dtype` used in the wrapped function.
Args: Args:
ref_idx (:obj:`int`, optional): The index in the wrapped function's ref_idx (:obj:`int`, optional): The index in the wrapped function's
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.
karg_names (sequence of :obj:`str`): A sequence of keyword arguments karg_names (sequence of :obj:`str`): A sequence of keyword arguments
in the wrapped function's keyword arguments that indicate the in the wrapped function's keyword arguments that indicate the
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
as the output variable(s) in the wrapped function. as the output variable(s) in the wrapped function.
Returns: Returns:
:class:`numpy.ndarray`: The wrapped function's output cast to the :class:`numpy.ndarray`: The wrapped function's output cast to the
same :class:`numpy.dtype` as the reference variable. same :class:`numpy.dtype` as the reference variable.
""" """
@wrapt.decorator @wrapt.decorator
def func_wrapper(wrapped, instance, args, kwargs): def func_wrapper(wrapped, instance, args, kwargs):
_arg_idxs = arg_idxs if arg_idxs is not None else () _arg_idxs = arg_idxs if arg_idxs is not None else ()
_karg_names = karg_names if karg_names is not None else () _karg_names = karg_names if karg_names is not None else ()
# Handle output views if applicable # Handle output views if applicable
_outkeys = [outviews] if isstr(outviews) else outviews _outkeys = [outviews] if isstr(outviews) else outviews
_outviews = from_args(wrapped, _outkeys, *args, **kwargs) _outviews = from_args(wrapped, _outkeys, *args, **kwargs)
has_outview = False has_outview = False
for outkey in _outkeys: for outkey in _outkeys:
_outview = _outviews[outkey] _outview = _outviews[outkey]
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)
# Do nothing for supplied output views # Do nothing for supplied output views
if not has_outview: if not has_outview:
if isinstance(result, np.ndarray): if isinstance(result, np.ndarray):
if result.dtype == orig_type: if result.dtype == orig_type:
return result return result
return result.astype(orig_type) return result.astype(orig_type)
elif isinstance(result, Iterable): # got back a sequence of arrays elif isinstance(result, Iterable): # got back a sequence of arrays
return tuple(arr.astype(orig_type) return tuple(arr.astype(orig_type)
if arr.dtype != orig_type else arr if arr.dtype != orig_type else arr
for arr in result) for arr in result)
return result return result
return func_wrapper return func_wrapper
def _extract_and_transpose(arg, do_transpose): def _extract_and_transpose(arg, do_transpose):
"""Return a transposed view of the :class:`numpy.ndarray` inside of a """Return a transposed view of the :class:`numpy.ndarray` inside of a
:class:`xarray.DataArray` object. :class:`xarray.DataArray` object.
If the *arg* parameter is not a :class:`xarray.DataArray` object, then If the *arg* parameter is not a :class:`xarray.DataArray` object, then
*arg* is returned. *arg* is returned.
Args: Args:
arg (:class:`xarray.DataArray` or :obj:`object`): Can be any object arg (:class:`xarray.DataArray` or :obj:`object`): Can be any object
type. type.
do_transpose: Set to False to only extract the variable. When True, do_transpose: Set to False to only extract the variable. When True,
the extracted array will also be transposed to a Fortran view if the extracted array will also be transposed to a Fortran view if
it is not already Fortran contiguous. it is not already Fortran contiguous.
Returns: Returns:
:class:`numpy.ndarray`: A numpy array. If *do_transpose* is True, :class:`numpy.ndarray`: A numpy array. If *do_transpose* is True,
the numpy array will also be a Fortran contiguous view. the numpy array will also be a Fortran contiguous view.
""" """
if xarray_enabled(): if xarray_enabled():
if isinstance(arg, DataArray): if isinstance(arg, DataArray):
arg = to_np(arg) arg = to_np(arg)
if do_transpose: if do_transpose:
if isinstance(arg, np.ndarray): if isinstance(arg, np.ndarray):
if not arg.flags.f_contiguous and arg.ndim > 1: if not arg.flags.f_contiguous and arg.ndim > 1:
return arg.T return arg.T
return arg return arg
def extract_and_transpose(do_transpose=True, outviews="outview"): def extract_and_transpose(do_transpose=True, outviews="outview"):
"""A decorator to extract the data array from a :class:`xarray.DataArray` """A decorator to extract the data array from a :class:`xarray.DataArray`
This decorator also transposes the view of the data to Fortran This decorator also transposes the view of the data to Fortran
contiguous if *do_transpose* is True. contiguous if *do_transpose* is True.
Args: Args:
do_transpose: Set to False to only extract the variable. When True, do_transpose: Set to False to only extract the variable. When True,
the extracted array will also be transposed to a Fortran view if the extracted array will also be transposed to a Fortran view if
it is not already Fortran contiguous. it is not already Fortran contiguous.
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
as the output variable(s) in the wrapped function. as the output variable(s) in the wrapped function.
Returns: Returns:
:class:`numpy.ndarray`: A numpy array. If *do_transpose* is True, :class:`numpy.ndarray`: A numpy array. If *do_transpose* is True,
the numpy array will also be a Fortran contiguous view. the numpy array will also be a Fortran contiguous view.
""" """
@wrapt.decorator @wrapt.decorator
def func_wrapper(wrapped, instance, args, kwargs): def func_wrapper(wrapped, instance, args, kwargs):
# Handle output views if applicable # Handle output views if applicable
_outkeys = [outviews] if isstr(outviews) else outviews _outkeys = [outviews] if isstr(outviews) else outviews
_outviews = from_args(wrapped, _outkeys, *args, **kwargs) _outviews = from_args(wrapped, _outkeys, *args, **kwargs)
has_outview = False has_outview = False
for outkey in _outkeys: for outkey in _outkeys:
_outview = _outviews[outkey] _outview = _outviews[outkey]
if _outview is not None: if _outview is not None:
has_outview = True has_outview = True
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)
# Do nothing for supplied output views # Do nothing for supplied output views
if has_outview: if has_outview:
return result return result
if isinstance(result, np.ndarray): if isinstance(result, np.ndarray):
if result.flags.f_contiguous and result.ndim > 1: if result.flags.f_contiguous and result.ndim > 1:
return result.T return result.T
elif isinstance(result, Iterable): elif isinstance(result, Iterable):
return tuple(x.T if x.flags.f_contiguous and x.ndim > 1 else x return tuple(x.T if x.flags.f_contiguous and x.ndim > 1 else x
for x in result) for x in result)
return result return result
return func_wrapper return func_wrapper
def check_args(refvaridx, refvarndim, rightdims, stagger=None, def check_args(refvaridx, refvarndim, rightdims, stagger=None,
refstagdim=None): refstagdim=None):
"""A decorator to check that the wrapped function's arguments are valid. """A decorator to check that the wrapped function's arguments are valid.
An exception is raised when an invalid argument is found. An exception is raised when an invalid argument is found.
Args: Args:
refvaridx (:obj:`int`): The wrapped function's positional argument refvaridx (:obj:`int`): The wrapped function's positional argument
index to use as the reference variable. index to use as the reference variable.
refvarndim (:obj:`int`): The number of dimensions for the reference refvarndim (:obj:`int`): The number of dimensions for the reference
variable that is expected by the wrapped function. variable that is expected by the wrapped function.
rightdims (sequence of :obj:`int`): The expected number of right rightdims (sequence of :obj:`int`): The expected number of right
dimensions for each argument. dimensions for each argument.
stagger (sequence of :obj:`int` or :obj:`None`, optional): The stagger (sequence of :obj:`int` or :obj:`None`, optional): The
dimension that is staggered for each argument in the wrapped dimension that is staggered for each argument in the wrapped
function. Use :obj:`None` in the sequence to indicate no function. Use :obj:`None` in the sequence to indicate no
staggering for that argument. Default is None. staggering for that argument. Default is None.
refstagdim (:obj:`int`, optional): The staggered dimension for the refstagdim (:obj:`int`, optional): The staggered dimension for the
reference variable, if applicable. Default is None. reference variable, if applicable. Default is None.
Returns: Returns:
None None
Raises: Raises:
:class:`ValueError`: Raised when an invalid argument is detected. :class:`ValueError`: Raised when an invalid argument is detected.
""" """
@wrapt.decorator @wrapt.decorator
def func_wrapper(wrapped, instance, args, kwargs): def func_wrapper(wrapped, instance, args, kwargs):
refvar = args[refvaridx] refvar = args[refvaridx]
try: try:
_ndim = refvar.ndim _ndim = refvar.ndim
@ -467,7 +452,7 @@ def check_args(refvaridx, refvarndim, rightdims, stagger=None,
"object".format(refvaridx)) "object".format(refvaridx))
else: else:
extra_dims = refvar.ndim - refvarndim extra_dims = refvar.ndim - refvarndim
# Always use unstaggered as the basis of comparison # Always use unstaggered as the basis of comparison
if refstagdim is not None: if refstagdim is not None:
_refshape = list(refvar.shape) _refshape = list(refvar.shape)
@ -475,33 +460,34 @@ def check_args(refvaridx, refvarndim, rightdims, stagger=None,
_refshape = tuple(_refshape) _refshape = tuple(_refshape)
else: else:
_refshape = refvar.shape _refshape = refvar.shape
if stagger is None: if stagger is None:
_stagger = [None]*len(rightdims) _stagger = [None]*len(rightdims)
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
var = args[i] var = args[i]
try: try:
_ = var.ndim _ = var.ndim
except AttributeError: except AttributeError:
raise ValueError("argument {} is not an arraylike " raise ValueError("argument {} is not an arraylike "
"object".format(i)) "object".format(i))
right_var_ndims = rightdims[i] right_var_ndims = rightdims[i]
# 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(
var.ndim, i,
right_var_ndims + extra_dims)) var.ndim,
right_var_ndims + extra_dims))
# Add 1 to the reference staggered dim index before doing the check # Add 1 to the reference staggered dim index before doing the check
if _stagger[i] is not None: if _stagger[i] is not None:
ref_shape = list(_refshape) ref_shape = list(_refshape)
@ -509,27 +495,19 @@ def check_args(refvaridx, refvarndim, rightdims, stagger=None,
ref_shape = tuple(ref_shape) ref_shape = tuple(ref_shape)
else: else:
ref_shape = _refshape ref_shape = _refshape
ref_right_sizes = ref_shape[extra_dims:]
# Check that right dimensions are lined up
if (var.shape[-right_var_ndims:] !=
ref_right_sizes[-right_var_ndims:]):
raise ValueError("invalid shape for argument "
"{} (got {}, expected {})".format(i,
var.shape[-right_var_ndims:],
ref_right_sizes[-right_var_ndims:]))
return wrapped(*args, **kwargs)
return func_wrapper
ref_right_sizes = ref_shape[extra_dims:]
# Check that right dimensions are lined up
if (var.shape[-right_var_ndims:] !=
ref_right_sizes[-right_var_ndims:]):
raise ValueError("invalid shape for argument "
"{} (got {}, expected {})".format(
i,
var.shape[-right_var_ndims:],
ref_right_sizes[-right_var_ndims:]))
return wrapped(*args, **kwargs)
return func_wrapper

56
src/wrf/destag.py

@ -8,57 +8,55 @@ from .metadecorators import set_destag_metadata
@extract_and_transpose(do_transpose=False) @extract_and_transpose(do_transpose=False)
def destagger(var, stagger_dim, meta=False): def destagger(var, stagger_dim, meta=False):
"""Return the variable on the unstaggered grid. """Return the variable on the unstaggered grid.
This function destaggers the variable by taking the average of the This function destaggers the variable by taking the average of the
values located on either side of the grid box. values located on either side of the grid box.
Args: Args:
var (:class:`xarray.DataArray` or :class:`numpy.ndarray`): A variable var (:class:`xarray.DataArray` or :class:`numpy.ndarray`): A variable
on a staggered grid. on a staggered grid.
stagger_dim (:obj:`int`): The dimension index to destagger. stagger_dim (:obj:`int`): The dimension index to destagger.
Negative values can be used to choose dimensions referenced Negative values can be used to choose dimensions referenced
from the right hand side (-1 is the rightmost dimension). from the right hand side (-1 is the rightmost dimension).
meta (:obj:`bool`, optional): Set to False to disable metadata and meta (:obj:`bool`, optional): Set to False to disable metadata and
return :class:`numpy.ndarray` instead of return :class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is False. :class:`xarray.DataArray`. Default is False.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: :class:`xarray.DataArray` or :class:`numpy.ndarray`:
The destaggered variable. If xarray is enabled and The destaggered variable. If xarray is enabled and
the *meta* parameter is True, then the result will be a the *meta* parameter is True, then the result will be a
:class:`xarray.DataArray` object. Otherwise, the result will be a :class:`xarray.DataArray` object. Otherwise, the result will be a
:class:`numpy.ndarray` object with no metadata. :class:`numpy.ndarray` object with no metadata.
""" """
var_shape = var.shape var_shape = var.shape
num_dims = var.ndim num_dims = var.ndim
stagger_dim_size = var_shape[stagger_dim] stagger_dim_size = var_shape[stagger_dim]
# Dynamically building the range slices to create the appropriate # Dynamically building the range slices to create the appropriate
# number of ':'s in the array accessor lists. # number of ':'s in the array accessor lists.
# For example, for a 3D array, the calculation would be # For example, for a 3D array, the calculation would be
# result = .5 * (var[:,:,0:stagger_dim_size-2] # result = .5 * (var[:,:,0:stagger_dim_size-2]
# + var[:,:,1:stagger_dim_size-1]) # + var[:,:,1:stagger_dim_size-1])
# for stagger_dim=2. So, full slices would be used for dims 0 and 1, but # for stagger_dim=2. So, full slices would be used for dims 0 and 1, but
# dim 2 needs the special slice. # dim 2 needs the special slice.
full_slice = slice(None) full_slice = slice(None)
slice1 = slice(0, stagger_dim_size - 1, 1) slice1 = slice(0, stagger_dim_size - 1, 1)
slice2 = slice(1, stagger_dim_size, 1) slice2 = slice(1, stagger_dim_size, 1)
# default to full slices # default to full slices
dim_ranges_1 = [full_slice] * num_dims dim_ranges_1 = [full_slice] * num_dims
dim_ranges_2 = [full_slice] * num_dims dim_ranges_2 = [full_slice] * num_dims
# for the stagger dim, insert the appropriate slice range # for the stagger dim, insert the appropriate slice range
dim_ranges_1[stagger_dim] = slice1 dim_ranges_1[stagger_dim] = slice1
dim_ranges_2[stagger_dim] = slice2 dim_ranges_2[stagger_dim] = slice2
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

1263
src/wrf/extension.py

File diff suppressed because it is too large Load Diff

809
src/wrf/g_cape.py

File diff suppressed because it is too large Load Diff

688
src/wrf/g_cloudfrac.py

@ -11,142 +11,142 @@ from .g_geoht import _get_geoht
@set_cloudfrac_metadata() @set_cloudfrac_metadata()
def get_cloudfrac(wrfin, timeidx=0, method="cat", squeeze=True, def get_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, mid_thresh=None,
high_thresh=None, missing=default_fill(np.float64)): high_thresh=None, missing=default_fill(np.float64)):
"""Return the cloud fraction for low, mid, and high level clouds. """Return the cloud fraction for low, mid, and high level clouds.
The leftmost dimension of the returned array represents three different The leftmost dimension of the returned array represents three different
quantities: quantities:
- return_val[0,...] will contain LOW level cloud fraction - return_val[0,...] will contain LOW level cloud fraction
- return_val[1,...] will contain MID level cloud fraction - return_val[1,...] will contain MID level cloud fraction
- return_val[2,...] will contain HIGH level cloud fraction - return_val[2,...] will contain HIGH level cloud fraction
If the vertical coordinate type is 'height_agl' or 'height_msl', the If the vertical coordinate type is 'height_agl' or 'height_msl', the
default cloud levels are defined as: default cloud levels are defined as:
300 m <= low_cloud < 2000 m 300 m <= low_cloud < 2000 m
2000 m <= mid_cloud < 6000 m 2000 m <= mid_cloud < 6000 m
6000 m <= high_cloud 6000 m <= high_cloud
For 'pressure', the default cloud levels are defined as: For 'pressure', the default cloud levels are defined as:
97000 Pa <= low_cloud < 80000 Pa 97000 Pa <= low_cloud < 80000 Pa
80000 Pa <= mid_cloud < 45000 Pa 80000 Pa <= mid_cloud < 45000 Pa
45000 Pa <= high_cloud 45000 Pa <= high_cloud
Note that the default low cloud levels are chosen to Note that the default low cloud levels are chosen to
exclude clouds near the surface (fog). If you want fog included, set exclude clouds near the surface (fog). If you want fog included, set
*low_thresh* to ~99500 Pa if *vert_type* is set to 'pressure', or 15 m if *low_thresh* to ~99500 Pa if *vert_type* is set to 'pressure', or 15 m if
using 'height_msl' or 'height_agl'. Keep in mind that the lowest mass grid using 'height_msl' or 'height_agl'. Keep in mind that the lowest mass grid
points are slightly above the ground, and in order to find clouds, the points are slightly above the ground, and in order to find clouds, the
*low_thresh* needs to be set to values that are slightly greater than *low_thresh* needs to be set to values that are slightly greater than
(less than) the lowest height (pressure) values. (less than) the lowest height (pressure) values.
When using 'pressure' or 'height_agl' for *vert_type*, there is a When using 'pressure' or 'height_agl' for *vert_type*, there is a
possibility that the lowest WRF level will be higher than the low_cloud or possibility that the lowest WRF level will be higher than the low_cloud or
mid_cloud threshold, particularly for mountainous regions. When this mid_cloud threshold, particularly for mountainous regions. When this
happens, a fill value will be used in the output. happens, a fill value will be used in the output.
This functions extracts the necessary variables from the NetCDF file This functions extracts the necessary variables from the NetCDF file
object in order to perform the calculation. object in order to perform the calculation.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. The default is 0. all times in the file or sequence. The default is 0.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata and meta (:obj:`bool`, optional): Set to False to disable metadata and
return :class:`numpy.ndarray` instead of return :class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is True. :class:`xarray.DataArray`. Default is True.
_key (:obj:`int`, optional): A caching key. This is used for internal _key (:obj:`int`, optional): A caching key. This is used for internal
purposes only. Default is None. purposes only. Default is None.
vert_type (:obj:`str`, optional): The type of vertical coordinate used vert_type (:obj:`str`, optional): The type of vertical coordinate used
to determine cloud type thresholds. Must be 'height_agl', to determine cloud type thresholds. Must be 'height_agl',
'height_msl', or 'pres'. The default is 'height_agl'. 'height_msl', or 'pres'. The default is 'height_agl'.
low_thresh (:obj:`float`, optional): The lower bound for what is low_thresh (:obj:`float`, optional): The lower bound for what is
considered a low cloud. If *vert_type* is 'pres', the default is considered a low cloud. If *vert_type* is 'pres', the default is
97000 Pa. If *vert_type* is 'height_agl' or 'height_msl', then the 97000 Pa. If *vert_type* is 'height_agl' or 'height_msl', then the
default is 300 m. default is 300 m.
mid_thresh (:obj:`float`, optional): The lower bound for what is mid_thresh (:obj:`float`, optional): The lower bound for what is
considered a mid level cloud. If *vert_type* is 'pres', the considered a mid level cloud. If *vert_type* is 'pres', the
default is 80000 Pa. If *vert_type* is 'height_agl' or default is 80000 Pa. If *vert_type* is 'height_agl' or
'height_msl', then the default is 2000 m. 'height_msl', then the default is 2000 m.
high_thresh (:obj:`float`, optional): The lower bound for what is high_thresh (:obj:`float`, optional): The lower bound for what is
considered a high level cloud. If *vert_type* is 'pres', the considered a high level cloud. If *vert_type* is 'pres', the
default is 45000 Pa. If *vert_type* is 'height_agl' or default is 45000 Pa. If *vert_type* is 'height_agl' or
'height_msl', then the default is 6000 m. 'height_msl', then the default is 6000 m.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The :class:`xarray.DataArray` or :class:`numpy.ndarray`: The
cloud fraction array whose leftmost dimension is 3 (LOW=0, MID=1, cloud fraction array whose leftmost dimension is 3 (LOW=0, MID=1,
HIGH=2). HIGH=2).
If xarray is enabled and the *meta* parameter is True, then the result If xarray is enabled and the *meta* parameter is True, then the result
will be a :class:`xarray.DataArray` object. Otherwise, the result will will be a :class:`xarray.DataArray` object. Otherwise, the result will
be a :class:`numpy.ndarray` object with no metadata. be a :class:`numpy.ndarray` object with no metadata.
""" """
ncvars = extract_vars(wrfin, timeidx, ("P", "PB", "QVAPOR", "T"), ncvars = extract_vars(wrfin, timeidx, ("P", "PB", "QVAPOR", "T"),
method, squeeze, cache, meta=False, method, squeeze, cache, meta=False,
_key=_key) _key=_key)
p = ncvars["P"] p = ncvars["P"]
pb = ncvars["PB"] pb = ncvars["PB"]
qv = ncvars["QVAPOR"] qv = ncvars["QVAPOR"]
t = ncvars["T"] t = ncvars["T"]
full_p = p + pb full_p = p + pb
full_t = t + Constants.T_BASE full_t = t + Constants.T_BASE
tk = _tk(full_p, full_t) tk = _tk(full_p, full_t)
rh = _rh(qv, full_p, tk) rh = _rh(qv, full_p, tk)
if vert_type.lower() == "pres" or vert_type.lower() == "pressure": if vert_type.lower() == "pres" or vert_type.lower() == "pressure":
v_coord = full_p v_coord = full_p
_low_thresh = 97000. if low_thresh is None else low_thresh _low_thresh = 97000. if low_thresh is None else low_thresh
_mid_thresh = 80000. if mid_thresh is None else mid_thresh _mid_thresh = 80000. if mid_thresh is None else mid_thresh
_high_thresh = 45000. if high_thresh is None else high_thresh _high_thresh = 45000. if high_thresh is None else high_thresh
vert_inc_w_height = 0 vert_inc_w_height = 0
elif (vert_type.lower() == "height_msl" elif (vert_type.lower() == "height_msl"
or vert_type.lower() == "height_agl"): or vert_type.lower() == "height_agl"):
is_msl = vert_type.lower() == "height_msl" is_msl = vert_type.lower() == "height_msl"
v_coord = _get_geoht(wrfin, timeidx, method, squeeze, v_coord = _get_geoht(wrfin, timeidx, method, squeeze,
cache, meta=False, _key=_key, height=True, cache, meta=False, _key=_key, height=True,
msl=is_msl) msl=is_msl)
_low_thresh = 300. if low_thresh is None else low_thresh _low_thresh = 300. if low_thresh is None else low_thresh
_mid_thresh = 2000. if mid_thresh is None else mid_thresh _mid_thresh = 2000. if mid_thresh is None else mid_thresh
@ -155,344 +155,346 @@ def get_cloudfrac(wrfin, timeidx=0, method="cat", squeeze=True,
else: else:
raise ValueError("'vert_type' must be 'pres', 'height_msl', " raise ValueError("'vert_type' must be 'pres', 'height_msl', "
"or 'height_agl'") "or 'height_agl'")
cfrac = _cloudfrac(v_coord, rh, vert_inc_w_height, cfrac = _cloudfrac(v_coord, rh, vert_inc_w_height,
_low_thresh, _mid_thresh, _high_thresh, missing) _low_thresh, _mid_thresh, _high_thresh, missing)
return ma.masked_values(cfrac, missing) return ma.masked_values(cfrac, missing)
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
default cloud levels are defined as: default cloud levels are defined as:
300 m <= low_cloud < 2000 m 300 m <= low_cloud < 2000 m
2000 m <= mid_cloud < 6000 m 2000 m <= mid_cloud < 6000 m
6000 m <= high_cloud 6000 m <= high_cloud
For 'pressure', the default cloud levels are defined as: For 'pressure', the default cloud levels are defined as:
97000 Pa <= low_cloud < 80000 Pa 97000 Pa <= low_cloud < 80000 Pa
80000 Pa <= mid_cloud < 45000 Pa 80000 Pa <= mid_cloud < 45000 Pa
45000 Pa <= high_cloud 45000 Pa <= high_cloud
Note that the default low cloud levels are chosen to Note that the default low cloud levels are chosen to
exclude clouds near the surface (fog). If you want fog included, set exclude clouds near the surface (fog). If you want fog included, set
*low_thresh* to ~99500 Pa if *vert_type* is set to 'pressure', or 15 m if *low_thresh* to ~99500 Pa if *vert_type* is set to 'pressure', or 15 m if
using 'height_msl' or 'height_agl'. Keep in mind that the lowest mass grid using 'height_msl' or 'height_agl'. Keep in mind that the lowest mass grid
points are slightly above the ground, and in order to find clouds, the points are slightly above the ground, and in order to find clouds, the
*low_thresh* needs to be set to values that are slightly greater than *low_thresh* needs to be set to values that are slightly greater than
(less than) the lowest height (pressure) values. (less than) the lowest height (pressure) values.
When using 'pressure' or 'height_agl' for *vert_type*, there is a When using 'pressure' or 'height_agl' for *vert_type*, there is a
possibility that the lowest WRF level will be higher than the low_cloud or possibility that the lowest WRF level will be higher than the low_cloud or
mid_cloud threshold, particularly for mountainous regions. When this mid_cloud threshold, particularly for mountainous regions. When this
happens, a fill value will be used in the output. happens, a fill value will be used in the output.
This functions extracts the necessary variables from the NetCDF file This functions extracts the necessary variables from the NetCDF file
object in order to perform the calculation. object in order to perform the calculation.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. The default is 0. all times in the file or sequence. The default is 0.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata and meta (:obj:`bool`, optional): Set to False to disable metadata and
return :class:`numpy.ndarray` instead of return :class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is True. :class:`xarray.DataArray`. Default is True.
_key (:obj:`int`, optional): A caching key. This is used for internal _key (:obj:`int`, optional): A caching key. This is used for internal
purposes only. Default is None. purposes only. Default is None.
vert_type (:obj:`str`, optional): The type of vertical coordinate used vert_type (:obj:`str`, optional): The type of vertical coordinate used
to determine cloud type thresholds. Must be 'height_agl', to determine cloud type thresholds. Must be 'height_agl',
'height_msl', or 'pres'. The default is 'height_agl'. 'height_msl', or 'pres'. The default is 'height_agl'.
low_thresh (:obj:`float`, optional): The lower bound for what is low_thresh (:obj:`float`, optional): The lower bound for what is
considered a low cloud. If *vert_type* is 'pres', the default is considered a low cloud. If *vert_type* is 'pres', the default is
97000 Pa. If *vert_type* is 'height_agl' or 'height_msl', then the 97000 Pa. If *vert_type* is 'height_agl' or 'height_msl', then the
default is 300 m. default is 300 m.
mid_thresh (:obj:`float`, optional): The lower bound for what is mid_thresh (:obj:`float`, optional): The lower bound for what is
considered a mid level cloud. If *vert_type* is 'pres', the considered a mid level cloud. If *vert_type* is 'pres', the
default is 80000 Pa. If *vert_type* is 'height_agl' or default is 80000 Pa. If *vert_type* is 'height_agl' or
'height_msl', then the default is 2000 m. 'height_msl', then the default is 2000 m.
high_thresh (:obj:`float`, optional): The lower bound for what is high_thresh (:obj:`float`, optional): The lower bound for what is
considered a high level cloud. If *vert_type* is 'pres', the considered a high level cloud. If *vert_type* is 'pres', the
default is 45000 Pa. If *vert_type* is 'height_agl' or default is 45000 Pa. If *vert_type* is 'height_agl' or
'height_msl', then the default is 6000 m. 'height_msl', then the default is 6000 m.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The :class:`xarray.DataArray` or :class:`numpy.ndarray`: The
cloud fraction array for low level clouds. cloud fraction array for low level clouds.
If xarray is enabled and the *meta* parameter is True, then the result If xarray is enabled and the *meta* parameter is True, then the result
will be a :class:`xarray.DataArray` object. Otherwise, the result will will be a :class:`xarray.DataArray` object. Otherwise, the result will
be a :class:`numpy.ndarray` object with no metadata. be a :class:`numpy.ndarray` object with no metadata.
""" """
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"
return result return result
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
default cloud levels are defined as: default cloud levels are defined as:
300 m <= low_cloud < 2000 m 300 m <= low_cloud < 2000 m
2000 m <= mid_cloud < 6000 m 2000 m <= mid_cloud < 6000 m
6000 m <= high_cloud 6000 m <= high_cloud
For 'pressure', the default cloud levels are defined as: For 'pressure', the default cloud levels are defined as:
97000 Pa <= low_cloud < 80000 Pa 97000 Pa <= low_cloud < 80000 Pa
80000 Pa <= mid_cloud < 45000 Pa 80000 Pa <= mid_cloud < 45000 Pa
45000 Pa <= high_cloud 45000 Pa <= high_cloud
Note that the default low cloud levels are chosen to Note that the default low cloud levels are chosen to
exclude clouds near the surface (fog). If you want fog included, set exclude clouds near the surface (fog). If you want fog included, set
*low_thresh* to ~99500 Pa if *vert_type* is set to 'pressure', or 15 m if *low_thresh* to ~99500 Pa if *vert_type* is set to 'pressure', or 15 m if
using 'height_msl' or 'height_agl'. Keep in mind that the lowest mass grid using 'height_msl' or 'height_agl'. Keep in mind that the lowest mass grid
points are slightly above the ground, and in order to find clouds, the points are slightly above the ground, and in order to find clouds, the
*low_thresh* needs to be set to values that are slightly greater than *low_thresh* needs to be set to values that are slightly greater than
(less than) the lowest height (pressure) values. (less than) the lowest height (pressure) values.
When using 'pressure' or 'height_agl' for *vert_type*, there is a When using 'pressure' or 'height_agl' for *vert_type*, there is a
possibility that the lowest WRF level will be higher than the low_cloud or possibility that the lowest WRF level will be higher than the low_cloud or
mid_cloud threshold, particularly for mountainous regions. When this mid_cloud threshold, particularly for mountainous regions. When this
happens, a fill value will be used in the output. happens, a fill value will be used in the output.
This functions extracts the necessary variables from the NetCDF file This functions extracts the necessary variables from the NetCDF file
object in order to perform the calculation. object in order to perform the calculation.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. The default is 0. all times in the file or sequence. The default is 0.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata and meta (:obj:`bool`, optional): Set to False to disable metadata and
return :class:`numpy.ndarray` instead of return :class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is True. :class:`xarray.DataArray`. Default is True.
_key (:obj:`int`, optional): A caching key. This is used for internal _key (:obj:`int`, optional): A caching key. This is used for internal
purposes only. Default is None. purposes only. Default is None.
vert_type (:obj:`str`, optional): The type of vertical coordinate used vert_type (:obj:`str`, optional): The type of vertical coordinate used
to determine cloud type thresholds. Must be 'height_agl', to determine cloud type thresholds. Must be 'height_agl',
'height_msl', or 'pres'. The default is 'height_agl'. 'height_msl', or 'pres'. The default is 'height_agl'.
low_thresh (:obj:`float`, optional): The lower bound for what is low_thresh (:obj:`float`, optional): The lower bound for what is
considered a low cloud. If *vert_type* is 'pres', the default is considered a low cloud. If *vert_type* is 'pres', the default is
97000 Pa. If *vert_type* is 'height_agl' or 'height_msl', then the 97000 Pa. If *vert_type* is 'height_agl' or 'height_msl', then the
default is 300 m. default is 300 m.
mid_thresh (:obj:`float`, optional): The lower bound for what is mid_thresh (:obj:`float`, optional): The lower bound for what is
considered a mid level cloud. If *vert_type* is 'pres', the considered a mid level cloud. If *vert_type* is 'pres', the
default is 80000 Pa. If *vert_type* is 'height_agl' or default is 80000 Pa. If *vert_type* is 'height_agl' or
'height_msl', then the default is 2000 m. 'height_msl', then the default is 2000 m.
high_thresh (:obj:`float`, optional): The lower bound for what is high_thresh (:obj:`float`, optional): The lower bound for what is
considered a high level cloud. If *vert_type* is 'pres', the considered a high level cloud. If *vert_type* is 'pres', the
default is 45000 Pa. If *vert_type* is 'height_agl' or default is 45000 Pa. If *vert_type* is 'height_agl' or
'height_msl', then the default is 6000 m. 'height_msl', then the default is 6000 m.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The :class:`xarray.DataArray` or :class:`numpy.ndarray`: The
cloud fraction array for mid level clouds. cloud fraction array for mid level clouds.
If xarray is enabled and the *meta* parameter is True, then the result If xarray is enabled and the *meta* parameter is True, then the result
will be a :class:`xarray.DataArray` object. Otherwise, the result will will be a :class:`xarray.DataArray` object. Otherwise, the result will
be a :class:`numpy.ndarray` object with no metadata. be a :class:`numpy.ndarray` object with no metadata.
""" """
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"
return result return result
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
default cloud levels are defined as: default cloud levels are defined as:
300 m <= low_cloud < 2000 m 300 m <= low_cloud < 2000 m
2000 m <= mid_cloud < 6000 m 2000 m <= mid_cloud < 6000 m
6000 m <= high_cloud 6000 m <= high_cloud
For 'pressure', the default cloud levels are defined as: For 'pressure', the default cloud levels are defined as:
97000 Pa <= low_cloud < 80000 Pa 97000 Pa <= low_cloud < 80000 Pa
80000 Pa <= mid_cloud < 45000 Pa 80000 Pa <= mid_cloud < 45000 Pa
45000 Pa <= high_cloud 45000 Pa <= high_cloud
Note that the default low cloud levels are chosen to Note that the default low cloud levels are chosen to
exclude clouds near the surface (fog). If you want fog included, set exclude clouds near the surface (fog). If you want fog included, set
*low_thresh* to ~99500 Pa if *vert_type* is set to 'pressure', or 15 m if *low_thresh* to ~99500 Pa if *vert_type* is set to 'pressure', or 15 m if
using 'height_msl' or 'height_agl'. Keep in mind that the lowest mass grid using 'height_msl' or 'height_agl'. Keep in mind that the lowest mass grid
points are slightly above the ground, and in order to find clouds, the points are slightly above the ground, and in order to find clouds, the
*low_thresh* needs to be set to values that are slightly greater than *low_thresh* needs to be set to values that are slightly greater than
(less than) the lowest height (pressure) values. (less than) the lowest height (pressure) values.
When using 'pressure' or 'height_agl' for *vert_type*, there is a When using 'pressure' or 'height_agl' for *vert_type*, there is a
possibility that the lowest WRF level will be higher than the low_cloud or possibility that the lowest WRF level will be higher than the low_cloud or
mid_cloud threshold, particularly for mountainous regions. When this mid_cloud threshold, particularly for mountainous regions. When this
happens, a fill value will be used in the output. happens, a fill value will be used in the output.
This functions extracts the necessary variables from the NetCDF file This functions extracts the necessary variables from the NetCDF file
object in order to perform the calculation. object in order to perform the calculation.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. The default is 0. all times in the file or sequence. The default is 0.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata and meta (:obj:`bool`, optional): Set to False to disable metadata and
return :class:`numpy.ndarray` instead of return :class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is True. :class:`xarray.DataArray`. Default is True.
_key (:obj:`int`, optional): A caching key. This is used for internal _key (:obj:`int`, optional): A caching key. This is used for internal
purposes only. Default is None. purposes only. Default is None.
vert_type (:obj:`str`, optional): The type of vertical coordinate used vert_type (:obj:`str`, optional): The type of vertical coordinate used
to determine cloud type thresholds. Must be 'height_agl', to determine cloud type thresholds. Must be 'height_agl',
'height_msl', or 'pres'. The default is 'height_agl'. 'height_msl', or 'pres'. The default is 'height_agl'.
low_thresh (:obj:`float`, optional): The lower bound for what is low_thresh (:obj:`float`, optional): The lower bound for what is
considered a low cloud. If *vert_type* is 'pres', the default is considered a low cloud. If *vert_type* is 'pres', the default is
97000 Pa. If *vert_type* is 'height_agl' or 'height_msl', then the 97000 Pa. If *vert_type* is 'height_agl' or 'height_msl', then the
default is 300 m. default is 300 m.
mid_thresh (:obj:`float`, optional): The lower bound for what is mid_thresh (:obj:`float`, optional): The lower bound for what is
considered a mid level cloud. If *vert_type* is 'pres', the considered a mid level cloud. If *vert_type* is 'pres', the
default is 80000 Pa. If *vert_type* is 'height_agl' or default is 80000 Pa. If *vert_type* is 'height_agl' or
'height_msl', then the default is 2000 m. 'height_msl', then the default is 2000 m.
high_thresh (:obj:`float`, optional): The lower bound for what is high_thresh (:obj:`float`, optional): The lower bound for what is
considered a high level cloud. If *vert_type* is 'pres', the considered a high level cloud. If *vert_type* is 'pres', the
default is 45000 Pa. If *vert_type* is 'height_agl' or default is 45000 Pa. If *vert_type* is 'height_agl' or
'height_msl', then the default is 6000 m. 'height_msl', then the default is 6000 m.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The :class:`xarray.DataArray` or :class:`numpy.ndarray`: The
cloud fraction array for high level clouds. cloud fraction array for high level clouds.
If xarray is enabled and the *meta* parameter is True, then the result If xarray is enabled and the *meta* parameter is True, then the result
will be a :class:`xarray.DataArray` object. Otherwise, the result will will be a :class:`xarray.DataArray` object. Otherwise, the result will
be a :class:`numpy.ndarray` object with no metadata. be a :class:`numpy.ndarray` object with no metadata.
""" """
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"
return result return result

149
src/wrf/g_ctt.py

@ -3,11 +3,10 @@ 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
from .decorators import convert_units from .decorators import convert_units
from .metadecorators import copy_and_set_metadata from .metadecorators import copy_and_set_metadata
from .util import extract_vars from .util import extract_vars
@ -17,86 +16,86 @@ from .util import extract_vars
description="cloud top temperature", description="cloud top temperature",
MemoryOrder="XY") MemoryOrder="XY")
@convert_units("temp", "c") @convert_units("temp", "c")
def get_ctt(wrfin, timeidx=0, method="cat", def get_ctt(wrfin, timeidx=0, method="cat",
squeeze=True, cache=None, meta=True, _key=None, squeeze=True, cache=None, meta=True, _key=None,
fill_nocloud=False, missing=default_fill(np.float64), fill_nocloud=False, missing=default_fill(np.float64),
opt_thresh=1.0, units="degC"): opt_thresh=1.0, units="degC"):
"""Return the cloud top temperature. """Return the cloud top temperature.
This functions extracts the necessary variables from the NetCDF file This functions extracts the necessary variables from the NetCDF file
object in order to perform the calculation. object in order to perform the calculation.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. The default is 0. all times in the file or sequence. The default is 0.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata and meta (:obj:`bool`, optional): Set to False to disable metadata and
return :class:`numpy.ndarray` instead of return :class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is True. :class:`xarray.DataArray`. Default is True.
_key (:obj:`int`, optional): A caching key. This is used for internal _key (:obj:`int`, optional): A caching key. This is used for internal
purposes only. Default is None. purposes only. Default is None.
fill_nocloud (:obj:`bool`, optional): Set to True to use fill values in fill_nocloud (:obj:`bool`, optional): Set to True to use fill values in
regions where clouds are not detected (optical depth less than 1). regions where clouds are not detected (optical depth less than 1).
Otherwise, the output will contain the surface temperature for Otherwise, the output will contain the surface temperature for
areas without clouds. Default is False. areas without clouds. Default is False.
missing (:obj:`float`, optional): The fill value to use for areas missing (:obj:`float`, optional): The fill value to use for areas
where no clouds are detected. Only used if *fill_nocloud* is where no clouds are detected. Only used if *fill_nocloud* is
True. Default is True. Default is
:data:`wrf.default_fill(numpy.float64)`. :data:`wrf.default_fill(numpy.float64)`.
opt_thresh (:obj:`float`, optional): The amount of optical opt_thresh (:obj:`float`, optional): The amount of optical
depth (integrated from top down) required to trigger a cloud top depth (integrated from top down) required to trigger a cloud top
temperature calculation. The cloud top temperature is calculated at temperature calculation. The cloud top temperature is calculated at
the vertical level where this threshold is met. Vertical columns the vertical level where this threshold is met. Vertical columns
with less than this threshold will be treated as cloud free areas. with less than this threshold will be treated as cloud free areas.
In general, the larger the value is for this In general, the larger the value is for this
threshold, the lower the altitude will be for the cloud top threshold, the lower the altitude will be for the cloud top
temperature calculation, and therefore higher cloud top temperature calculation, and therefore higher cloud top
temperature values. Default is 1.0, which should be sufficient for temperature values. Default is 1.0, which should be sufficient for
most users. most users.
units (:obj:`str`): The desired units. Refer to the :meth:`getvar` units (:obj:`str`): The desired units. Refer to the :meth:`getvar`
product table for a list of available units for 'ctt'. Default product table for a list of available units for 'ctt'. Default
is 'degC'. is 'degC'.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The :class:`xarray.DataArray` or :class:`numpy.ndarray`: The
cloud top temperature. cloud top temperature.
If xarray is enabled and the *meta* parameter is True, then the result If xarray is enabled and the *meta* parameter is True, then the result
will be a :class:`xarray.DataArray` object. Otherwise, the result will will be a :class:`xarray.DataArray` object. Otherwise, the result will
be a :class:`numpy.ndarray` object with no metadata. be a :class:`numpy.ndarray` object with no metadata.
""" """
varnames = ("T", "P", "PB", "PH", "PHB", "HGT", "QVAPOR") varnames = ("T", "P", "PB", "PH", "PHB", "HGT", "QVAPOR")
ncvars = extract_vars(wrfin, timeidx, varnames, method, squeeze, cache, ncvars = extract_vars(wrfin, timeidx, varnames, method, squeeze, cache,
@ -107,40 +106,40 @@ def get_ctt(wrfin, timeidx=0, method="cat",
ph = ncvars["PH"] ph = ncvars["PH"]
phb = ncvars["PHB"] phb = ncvars["PHB"]
ter = ncvars["HGT"] ter = ncvars["HGT"]
qv = ncvars["QVAPOR"] * 1000.0 # g/kg qv = ncvars["QVAPOR"] * 1000.0 # g/kg
haveqci = 1 haveqci = 1
try: try:
icevars = extract_vars(wrfin, timeidx, "QICE", icevars = extract_vars(wrfin, timeidx, "QICE",
method, squeeze, cache, meta=False, method, squeeze, cache, meta=False,
_key=_key) _key=_key)
except KeyError: except KeyError:
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",
method, squeeze, cache, meta=False, method, squeeze, cache, meta=False,
_key=_key) _key=_key)
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
full_t = t + Constants.T_BASE full_t = t + Constants.T_BASE
tk = _tk(full_p, full_t) tk = _tk(full_p, full_t)
geopt = ph + phb geopt = ph + phb
geopt_unstag = destagger(geopt, -3) geopt_unstag = destagger(geopt, -3)
ght = geopt_unstag / Constants.G ght = geopt_unstag / Constants.G
_fill_nocloud = 1 if fill_nocloud else 0 _fill_nocloud = 1 if fill_nocloud else 0
ctt = _ctt(p_hpa, tk, qice, qcld, qv, ght, ter, haveqci, _fill_nocloud, ctt = _ctt(p_hpa, tk, qice, qcld, qv, ght, ter, haveqci, _fill_nocloud,
missing, opt_thresh) missing, opt_thresh)
return ma.masked_values(ctt, missing) return ma.masked_values(ctt, missing)

228
src/wrf/g_dbz.py

@ -2,82 +2,81 @@ 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
from .metadecorators import copy_and_set_metadata from .metadecorators import copy_and_set_metadata
@copy_and_set_metadata(copy_varname="T", name="dbz", @copy_and_set_metadata(copy_varname="T", name="dbz",
description="radar reflectivity", description="radar reflectivity",
units="dBZ") units="dBZ")
def get_dbz(wrfin, timeidx=0, method="cat", def get_dbz(wrfin, timeidx=0, method="cat",
squeeze=True, cache=None, meta=True, _key=None, squeeze=True, cache=None, meta=True, _key=None,
use_varint=False, use_liqskin=False): use_varint=False, use_liqskin=False):
"""Return the simulated radar reflectivity. """Return the simulated radar reflectivity.
This functions extracts the necessary variables from the NetCDF file This functions extracts the necessary variables from the NetCDF file
object in order to perform the calculation. object in order to perform the calculation.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. The default is 0. all times in the file or sequence. The default is 0.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata and meta (:obj:`bool`, optional): Set to False to disable metadata and
return :class:`numpy.ndarray` instead of return :class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is True. :class:`xarray.DataArray`. Default is True.
_key (:obj:`int`, optional): A caching key. This is used for internal _key (:obj:`int`, optional): A caching key. This is used for internal
purposes only. Default is None. purposes only. Default is None.
use_varint (:obj:`bool`, optional): When set to False, use_varint (:obj:`bool`, optional): When set to False,
the intercept parameters are assumed constant the intercept parameters are assumed constant
(as in MM5's Reisner-2 bulk microphysical scheme). (as in MM5's Reisner-2 bulk microphysical scheme).
When set to True, the variable intercept When set to True, the variable intercept
parameters are used as in the more recent version of Reisner-2 parameters are used as in the more recent version of Reisner-2
(based on Thompson, Rasmussen, and Manning, 2004, Monthly weather (based on Thompson, Rasmussen, and Manning, 2004, Monthly weather
Review, Vol. 132, No. 2, pp. 519-542.). Review, Vol. 132, No. 2, pp. 519-542.).
use_liqskin (:obj:`bool`, optional): When set to True, frozen particles use_liqskin (:obj:`bool`, optional): When set to True, frozen particles
that are at a temperature above freezing are assumed to scatter that are at a temperature above freezing are assumed to scatter
as a liquid particle. Set to False to disable. as a liquid particle. Set to False to disable.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The simulated :class:`xarray.DataArray` or :class:`numpy.ndarray`: The simulated
radar reflectivity. radar reflectivity.
If xarray is enabled and the *meta* parameter is True, then the result If xarray is enabled and the *meta* parameter is True, then the result
will be a :class:`xarray.DataArray` object. Otherwise, the result will will be a :class:`xarray.DataArray` object. Otherwise, the result will
be a :class:`numpy.ndarray` object with no metadata. be a :class:`numpy.ndarray` object with no metadata.
""" """
varnames = ("T", "P", "PB", "QVAPOR", "QRAIN") varnames = ("T", "P", "PB", "QVAPOR", "QRAIN")
ncvars = extract_vars(wrfin, timeidx, varnames, method, squeeze, cache, ncvars = extract_vars(wrfin, timeidx, varnames, method, squeeze, cache,
@ -87,113 +86,112 @@ def get_dbz(wrfin, timeidx=0, method="cat",
pb = ncvars["PB"] pb = ncvars["PB"]
qv = ncvars["QVAPOR"] qv = ncvars["QVAPOR"]
qr = ncvars["QRAIN"] qr = ncvars["QRAIN"]
try: try:
snowvars = extract_vars(wrfin, timeidx, "QSNOW", snowvars = extract_vars(wrfin, timeidx, "QSNOW",
method, squeeze, cache, meta=False, method, squeeze, cache, meta=False,
_key=_key) _key=_key)
except KeyError: except KeyError:
qs = np.zeros(qv.shape, qv.dtype) qs = np.zeros(qv.shape, qv.dtype)
else: else:
qs = snowvars["QSNOW"] qs = snowvars["QSNOW"]
try: try:
graupvars = extract_vars(wrfin, timeidx, "QGRAUP", graupvars = extract_vars(wrfin, timeidx, "QGRAUP",
method, squeeze, cache, meta=False, method, squeeze, cache, meta=False,
_key=_key) _key=_key)
except KeyError: except KeyError:
qg = np.zeros(qv.shape, qv.dtype) qg = np.zeros(qv.shape, qv.dtype)
else: else:
qg = graupvars["QGRAUP"] qg = graupvars["QGRAUP"]
full_t = t + Constants.T_BASE full_t = t + Constants.T_BASE
full_p = p + pb full_p = p + pb
tk = _tk(full_p, full_t) tk = _tk(full_p, full_t)
# If qsnow is not all 0, set sn0 to 1 # If qsnow is not all 0, set sn0 to 1
sn0 = 1 if qs.any() else 0 sn0 = 1 if qs.any() else 0
ivarint = 1 if use_varint else 0 ivarint = 1 if use_varint else 0
iliqskin = 1 if use_liqskin else 0 iliqskin = 1 if use_liqskin else 0
return _dbz(full_p, tk, qv, qr, qs, qg, sn0, ivarint, iliqskin) return _dbz(full_p, tk, qv, qr, qs, qg, sn0, ivarint, iliqskin)
@copy_and_set_metadata(copy_varname="T", name="max_dbz", @copy_and_set_metadata(copy_varname="T", name="max_dbz",
remove_dims=("bottom_top",), remove_dims=("bottom_top",),
description="maximum radar reflectivity", description="maximum radar reflectivity",
units="dBZ", units="dBZ",
MemoryOrder="XY") MemoryOrder="XY")
def get_max_dbz(wrfin, timeidx=0, method="cat", def get_max_dbz(wrfin, timeidx=0, method="cat",
squeeze=True, cache=None, meta=True, _key=None, squeeze=True, cache=None, meta=True, _key=None,
use_varint=False, use_liqskin=False): use_varint=False, use_liqskin=False):
"""Return the maximum simulated radar reflectivity. """Return the maximum simulated radar reflectivity.
This function returns the maximum reflectivity found in the column for This function returns the maximum reflectivity found in the column for
each grid point. each grid point.
This functions extracts the necessary variables from the NetCDF file This functions extracts the necessary variables from the NetCDF file
object in order to perform the calculation. object in order to perform the calculation.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. The default is 0. all times in the file or sequence. The default is 0.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata and meta (:obj:`bool`, optional): Set to False to disable metadata and
return :class:`numpy.ndarray` instead of return :class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is True. :class:`xarray.DataArray`. Default is True.
_key (:obj:`int`, optional): A caching key. This is used for internal _key (:obj:`int`, optional): A caching key. This is used for internal
purposes only. Default is None. purposes only. Default is None.
use_varint (:obj:`bool`, optional): When set to False, use_varint (:obj:`bool`, optional): When set to False,
the intercept parameters are assumed constant the intercept parameters are assumed constant
(as in MM5's Reisner-2 bulk microphysical scheme). (as in MM5's Reisner-2 bulk microphysical scheme).
When set to True, the variable intercept When set to True, the variable intercept
parameters are used as in the more recent version of Reisner-2 parameters are used as in the more recent version of Reisner-2
(based on Thompson, Rasmussen, and Manning, 2004, Monthly weather (based on Thompson, Rasmussen, and Manning, 2004, Monthly weather
Review, Vol. 132, No. 2, pp. 519-542.). Review, Vol. 132, No. 2, pp. 519-542.).
use_liqskin (:obj:`bool`, optional): When set to True, frozen particles use_liqskin (:obj:`bool`, optional): When set to True, frozen particles
that are at a temperature above freezing are assumed to scatter that are at a temperature above freezing are assumed to scatter
as a liquid particle. Set to False to disable. as a liquid particle. Set to False to disable.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The maximum :class:`xarray.DataArray` or :class:`numpy.ndarray`: The maximum
simulated radar reflectivity. simulated radar reflectivity.
If xarray is enabled and the *meta* parameter is True, then the result If xarray is enabled and the *meta* parameter is True, then the result
will be a :class:`xarray.DataArray` object. Otherwise, the result will will be a :class:`xarray.DataArray` object. Otherwise, the result will
be a :class:`numpy.ndarray` object with no metadata. be a :class:`numpy.ndarray` object with no metadata.
""" """
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)

191
src/wrf/g_dewpoint.py

@ -1,164 +1,163 @@
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
from .util import extract_vars from .util import extract_vars
@copy_and_set_metadata(copy_varname="QVAPOR", name="td", @copy_and_set_metadata(copy_varname="QVAPOR", name="td",
description="dew point temperature") description="dew point temperature")
@convert_units("temp", "c") @convert_units("temp", "c")
def get_dp(wrfin, timeidx=0, method="cat", squeeze=True, def get_dp(wrfin, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, _key=None, units="degC"): cache=None, meta=True, _key=None, units="degC"):
"""Return the dewpoint temperature. """Return the dewpoint temperature.
This functions extracts the necessary variables from the NetCDF file This functions extracts the necessary variables from the NetCDF file
object in order to perform the calculation. object in order to perform the calculation.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. The default is 0. all times in the file or sequence. The default is 0.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata and meta (:obj:`bool`, optional): Set to False to disable metadata and
return :class:`numpy.ndarray` instead of return :class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is True. :class:`xarray.DataArray`. Default is True.
_key (:obj:`int`, optional): A caching key. This is used for internal _key (:obj:`int`, optional): A caching key. This is used for internal
purposes only. Default is None. purposes only. Default is None.
units (:obj:`str`): The desired units. Refer to the :meth:`getvar` units (:obj:`str`): The desired units. Refer to the :meth:`getvar`
product table for a list of available units for 'td'. Default product table for a list of available units for 'td'. Default
is 'degC'. is 'degC'.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The :class:`xarray.DataArray` or :class:`numpy.ndarray`: The
dewpoint temperature. dewpoint temperature.
If xarray is enabled and the *meta* parameter is True, then the result If xarray is enabled and the *meta* parameter is True, then the result
will be a :class:`xarray.DataArray` object. Otherwise, the result will will be a :class:`xarray.DataArray` object. Otherwise, the result will
be a :class:`numpy.ndarray` object with no metadata. be a :class:`numpy.ndarray` object with no metadata.
""" """
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)
p = ncvars["P"] p = ncvars["P"]
pb = ncvars["PB"] pb = ncvars["PB"]
# Copy needed for the mmap nonsense of scipy.io.netcdf, which seems to # Copy needed for the mmap nonsense of scipy.io.netcdf, which seems to
# break with every release # break with every release
qvapor = ncvars["QVAPOR"].copy() qvapor = ncvars["QVAPOR"].copy()
# Algorithm requires hPa # Algorithm requires hPa
full_p = .01*(p + pb) full_p = .01*(p + pb)
qvapor[qvapor < 0] = 0 qvapor[qvapor < 0] = 0
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")
def get_dp_2m(wrfin, timeidx=0, method="cat", squeeze=True, def get_dp_2m(wrfin, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, _key=None, units="degC"): cache=None, meta=True, _key=None, units="degC"):
"""Return the 2m dewpoint temperature. """Return the 2m dewpoint temperature.
This functions extracts the necessary variables from the NetCDF file This functions extracts the necessary variables from the NetCDF file
object in order to perform the calculation. object in order to perform the calculation.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. The default is 0. all times in the file or sequence. The default is 0.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata and meta (:obj:`bool`, optional): Set to False to disable metadata and
return :class:`numpy.ndarray` instead of return :class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is True. :class:`xarray.DataArray`. Default is True.
_key (:obj:`int`, optional): A caching key. This is used for internal _key (:obj:`int`, optional): A caching key. This is used for internal
purposes only. Default is None. purposes only. Default is None.
units (:obj:`str`): The desired units. Refer to the :meth:`getvar` units (:obj:`str`): The desired units. Refer to the :meth:`getvar`
product table for a list of available units for 'td2'. Default product table for a list of available units for 'td2'. Default
is 'degC'. is 'degC'.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The :class:`xarray.DataArray` or :class:`numpy.ndarray`: The
2m dewpoint temperature. 2m dewpoint temperature.
If xarray is enabled and the *meta* parameter is True, then the result If xarray is enabled and the *meta* parameter is True, then the result
will be a :class:`xarray.DataArray` object. Otherwise, the result will will be a :class:`xarray.DataArray` object. Otherwise, the result will
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)
# Algorithm requires hPa # Algorithm requires hPa
psfc = .01*(ncvars["PSFC"]) psfc = .01*(ncvars["PSFC"])
# Copy needed for the mmap nonsense of scipy.io.netcdf, which seems to # Copy needed for the mmap nonsense of scipy.io.netcdf, which seems to
# break with every release # break with every release
q2 = ncvars["Q2"].copy() q2 = ncvars["Q2"].copy()
q2[q2 < 0] = 0 q2[q2 < 0] = 0
td = _td(psfc, q2) td = _td(psfc, q2)
return td
return td

465
src/wrf/g_geoht.py

@ -8,79 +8,80 @@ 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):
"""Return the geopotential or geopotential height. """Return the geopotential or geopotential height.
If *height* is False, then geopotential is returned in units of If *height* is False, then geopotential is returned in units of
[m2 s-2]. If *height* is True, then geopotential height is [m2 s-2]. If *height* is True, then geopotential height is
returned in units of [m]. If *msl* is True, then geopotential height returned in units of [m]. If *msl* is True, then geopotential height
is return as Mean Sea Level (MSL). If *msl* is False, then geopotential is return as Mean Sea Level (MSL). If *msl* is False, then geopotential
height is returned as Above Ground Level (AGL). height is returned as Above Ground Level (AGL).
This functions extracts the necessary variables from the NetCDF file This functions extracts the necessary variables from the NetCDF file
object in order to perform the calculation. object in order to perform the calculation.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. The default is 0. all times in the file or sequence. The default is 0.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata and meta (:obj:`bool`, optional): Set to False to disable metadata and
return :class:`numpy.ndarray` instead of return :class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is True. :class:`xarray.DataArray`. Default is True.
_key (:obj:`int`, optional): A caching key. This is used for internal _key (:obj:`int`, optional): A caching key. This is used for internal
purposes only. Default is None. purposes only. Default is None.
height (:obj:`bool`, optional): Set to True to return geopotential height (:obj:`bool`, optional): Set to True to return geopotential
height instead of geopotential. Default is True. height instead of geopotential. Default is True.
msl (:obj:`bool`, optional): Set to True to return geopotential height msl (:obj:`bool`, optional): Set to True to return geopotential height
as Mean Sea Level (MSL). Set to False to return the as Mean Sea Level (MSL). Set to False to return the
geopotential height as Above Ground Level (AGL) by subtracting geopotential height as Above Ground Level (AGL) by subtracting
the terrain height. Default is True. the terrain height. Default is True.
stag (:obj:`bool`, optional): Set to True to use the vertical stag (:obj:`bool`, optional): Set to True to use the vertical
staggered grid, rather than the mass grid. Default is False. staggered grid, rather than the mass grid. Default is False.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The :class:`xarray.DataArray` or :class:`numpy.ndarray`: The
geopotential or geopotential height. geopotential or geopotential height.
If xarray is enabled and the *meta* parameter is True, then the result If xarray is enabled and the *meta* parameter is True, then the result
will be a :class:`xarray.DataArray` object. Otherwise, the result will will be a :class:`xarray.DataArray` object. Otherwise, the result will
be a :class:`numpy.ndarray` object with no metadata. be a :class:`numpy.ndarray` object with no metadata.
""" """
varname = either("PH", "GHT")(wrfin) varname = either("PH", "GHT")(wrfin)
if varname == "PH": if varname == "PH":
ph_vars = extract_vars(wrfin, timeidx, ("PH", "PHB", "HGT"), ph_vars = extract_vars(wrfin, timeidx, ("PH", "PHB", "HGT"),
@ -100,11 +101,11 @@ def _get_geoht(wrfin, timeidx, method="cat", squeeze=True,
_key=_key) _key=_key)
geopt_unstag = ght_vars["GHT"] * Constants.G geopt_unstag = ght_vars["GHT"] * Constants.G
hgt = ght_vars["HGT_M"] hgt = ght_vars["HGT_M"]
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,70 +114,70 @@ 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
else: else:
return geopt_unstag return geopt_unstag
@set_height_metadata(geopt=True, stag=False) @set_height_metadata(geopt=True, stag=False)
def get_geopt(wrfin, timeidx=0, method="cat", squeeze=True, cache=None, def get_geopt(wrfin, timeidx=0, method="cat", squeeze=True, cache=None,
meta=True, _key=None): meta=True, _key=None):
"""Return the geopotential. """Return the geopotential.
The geopotential is returned in units of [m2 s-2]. The geopotential is returned in units of [m2 s-2].
This functions extracts the necessary variables from the NetCDF file This functions extracts the necessary variables from the NetCDF file
object in order to perform the calculation. object in order to perform the calculation.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. The default is 0. all times in the file or sequence. The default is 0.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata and meta (:obj:`bool`, optional): Set to False to disable metadata and
return :class:`numpy.ndarray` instead of return :class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is True. :class:`xarray.DataArray`. Default is True.
_key (:obj:`int`, optional): A caching key. This is used for internal _key (:obj:`int`, optional): A caching key. This is used for internal
purposes only. Default is None. purposes only. Default is None.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The :class:`xarray.DataArray` or :class:`numpy.ndarray`: The
geopotential. geopotential.
If xarray is enabled and the *meta* parameter is True, then the result If xarray is enabled and the *meta* parameter is True, then the result
will be a :class:`xarray.DataArray` object. Otherwise, the result will will be a :class:`xarray.DataArray` object. Otherwise, the result will
be a :class:`numpy.ndarray` object with no metadata. be a :class:`numpy.ndarray` object with no metadata.
""" """
return _get_geoht(wrfin, timeidx, method, squeeze, cache, meta, _key, return _get_geoht(wrfin, timeidx, method, squeeze, cache, meta, _key,
False, True) False, True)
@ -184,135 +185,135 @@ def get_geopt(wrfin, timeidx=0, method="cat", squeeze=True, cache=None,
@set_height_metadata(geopt=False, stag=False) @set_height_metadata(geopt=False, stag=False)
@convert_units("height", "m") @convert_units("height", "m")
def get_height(wrfin, timeidx=0, method="cat", squeeze=True, def get_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. """Return the geopotential height.
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
(MSL). If *msl* is False, then geopotential height is returned as (MSL). If *msl* is False, then geopotential height is returned as
Above Ground Level (AGL) by subtracting the terrain height. Above Ground Level (AGL) by subtracting the terrain height.
This functions extracts the necessary variables from the NetCDF file This functions extracts the necessary variables from the NetCDF file
object in order to perform the calculation. object in order to perform the calculation.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. The default is 0. all times in the file or sequence. The default is 0.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata and meta (:obj:`bool`, optional): Set to False to disable metadata and
return :class:`numpy.ndarray` instead of return :class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is True. :class:`xarray.DataArray`. Default is True.
_key (:obj:`int`, optional): A caching key. This is used for internal _key (:obj:`int`, optional): A caching key. This is used for internal
purposes only. Default is None. purposes only. Default is None.
msl (:obj:`bool`, optional): Set to True to return geopotential height msl (:obj:`bool`, optional): Set to True to return geopotential height
as Mean Sea Level (MSL). Set to False to return the as Mean Sea Level (MSL). Set to False to return the
geopotential height as Above Ground Level (AGL) by subtracting geopotential height as Above Ground Level (AGL) by subtracting
the terrain height. Default is True. the terrain height. Default is True.
units (:obj:`str`): The desired units. Refer to the :meth:`getvar` units (:obj:`str`): The desired units. Refer to the :meth:`getvar`
product table for a list of available units for 'z'. Default product table for a list of available units for 'z'. Default
is 'm'. is 'm'.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The :class:`xarray.DataArray` or :class:`numpy.ndarray`: The
geopotential height. geopotential height.
If xarray is enabled and the *meta* parameter is True, then the result If xarray is enabled and the *meta* parameter is True, then the result
will be a :class:`xarray.DataArray` object. Otherwise, the result will will be a :class:`xarray.DataArray` object. Otherwise, the result will
be a :class:`numpy.ndarray` object with no metadata. be a :class:`numpy.ndarray` object with no metadata.
""" """
return _get_geoht(wrfin, timeidx, method, squeeze, cache, meta, _key, return _get_geoht(wrfin, timeidx, method, squeeze, cache, meta, _key,
True, msl) True, msl)
@set_height_metadata(geopt=True, stag=True) @set_height_metadata(geopt=True, stag=True)
def get_stag_geopt(wrfin, timeidx=0, method="cat", squeeze=True, cache=None, def get_stag_geopt(wrfin, timeidx=0, method="cat", squeeze=True, cache=None,
meta=True, _key=None): meta=True, _key=None):
"""Return the geopotential for the vertically staggered grid. """Return the geopotential for the vertically staggered grid.
The geopotential is returned in units of [m2 s-2]. The geopotential is returned in units of [m2 s-2].
This functions extracts the necessary variables from the NetCDF file This functions extracts the necessary variables from the NetCDF file
object in order to perform the calculation. object in order to perform the calculation.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. The default is 0. all times in the file or sequence. The default is 0.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata and meta (:obj:`bool`, optional): Set to False to disable metadata and
return :class:`numpy.ndarray` instead of return :class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is True. :class:`xarray.DataArray`. Default is True.
_key (:obj:`int`, optional): A caching key. This is used for internal _key (:obj:`int`, optional): A caching key. This is used for internal
purposes only. Default is None. purposes only. Default is None.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The :class:`xarray.DataArray` or :class:`numpy.ndarray`: The
geopotential. geopotential.
If xarray is enabled and the *meta* parameter is True, then the result If xarray is enabled and the *meta* parameter is True, then the result
will be a :class:`xarray.DataArray` object. Otherwise, the result will will be a :class:`xarray.DataArray` object. Otherwise, the result will
be a :class:`numpy.ndarray` object with no metadata. be a :class:`numpy.ndarray` object with no metadata.
""" """
return _get_geoht(wrfin, timeidx, method, squeeze, cache, meta, _key, return _get_geoht(wrfin, timeidx, method, squeeze, cache, meta, _key,
False, True, stag=True) False, True, stag=True)
@ -320,75 +321,73 @@ 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
(MSL). If *msl* is False, then geopotential height is returned as (MSL). If *msl* is False, then geopotential height is returned as
Above Ground Level (AGL) by subtracting the terrain height. Above Ground Level (AGL) by subtracting the terrain height.
This functions extracts the necessary variables from the NetCDF file This functions extracts the necessary variables from the NetCDF file
object in order to perform the calculation. object in order to perform the calculation.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. The default is 0. all times in the file or sequence. The default is 0.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata and meta (:obj:`bool`, optional): Set to False to disable metadata and
return :class:`numpy.ndarray` instead of return :class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is True. :class:`xarray.DataArray`. Default is True.
_key (:obj:`int`, optional): A caching key. This is used for internal _key (:obj:`int`, optional): A caching key. This is used for internal
purposes only. Default is None. purposes only. Default is None.
msl (:obj:`bool`, optional): Set to True to return geopotential height msl (:obj:`bool`, optional): Set to True to return geopotential height
as Mean Sea Level (MSL). Set to False to return the as Mean Sea Level (MSL). Set to False to return the
geopotential height as Above Ground Level (AGL) by subtracting geopotential height as Above Ground Level (AGL) by subtracting
the terrain height. Default is True. the terrain height. Default is True.
units (:obj:`str`): The desired units. Refer to the :meth:`getvar` units (:obj:`str`): The desired units. Refer to the :meth:`getvar`
product table for a list of available units for 'z'. Default product table for a list of available units for 'z'. Default
is 'm'. is 'm'.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The :class:`xarray.DataArray` or :class:`numpy.ndarray`: The
geopotential height. geopotential height.
If xarray is enabled and the *meta* parameter is True, then the result If xarray is enabled and the *meta* parameter is True, then the result
will be a :class:`xarray.DataArray` object. Otherwise, the result will will be a :class:`xarray.DataArray` object. Otherwise, the result will
be a :class:`numpy.ndarray` object with no metadata. be a :class:`numpy.ndarray` object with no metadata.
""" """
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)

233
src/wrf/g_helicity.py

@ -9,87 +9,88 @@ 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")
def get_srh(wrfin, timeidx=0, method="cat", squeeze=True, def get_srh(wrfin, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, _key=None, top=3000.0): cache=None, meta=True, _key=None, top=3000.0):
"""Return the storm relative helicity. """Return the storm relative helicity.
The *top* argument specifies the top of the integration in [m]. The *top* argument specifies the top of the integration in [m].
This functions extracts the necessary variables from the NetCDF file This functions extracts the necessary variables from the NetCDF file
object in order to perform the calculation. object in order to perform the calculation.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. The default is 0. all times in the file or sequence. The default is 0.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata and meta (:obj:`bool`, optional): Set to False to disable metadata and
return :class:`numpy.ndarray` instead of return :class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is True. :class:`xarray.DataArray`. Default is True.
_key (:obj:`int`, optional): A caching key. This is used for internal _key (:obj:`int`, optional): A caching key. This is used for internal
purposes only. Default is None. purposes only. Default is None.
top (:obj:`float`, optional): The top of the integration in [m]. top (:obj:`float`, optional): The top of the integration in [m].
Default is 3000.0. Default is 3000.0.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The :class:`xarray.DataArray` or :class:`numpy.ndarray`: The
storm relative helicity. storm relative helicity.
If xarray is enabled and the *meta* parameter is True, then the result If xarray is enabled and the *meta* parameter is True, then the result
will be a :class:`xarray.DataArray` object. Otherwise, the result will will be a :class:`xarray.DataArray` object. Otherwise, the result will
be a :class:`numpy.ndarray` object with no metadata. be a :class:`numpy.ndarray` object with no metadata.
""" """
# Top can either be 3000 or 1000 (for 0-1 srh or 0-3 srh) # Top can either be 3000 or 1000 (for 0-1 srh or 0-3 srh)
lats = get_lat(wrfin, timeidx, method, squeeze, lats = get_lat(wrfin, timeidx, method, squeeze,
cache, meta=False, _key=_key, stagger=None) cache, meta=False, _key=_key, stagger=None)
ncvars = extract_vars(wrfin, timeidx, ("HGT", "PH", "PHB"), ncvars = extract_vars(wrfin, timeidx, ("HGT", "PH", "PHB"),
method, squeeze, cache, meta=False, method, squeeze, cache, meta=False,
_key=_key) _key=_key)
ter = ncvars["HGT"] ter = ncvars["HGT"]
ph = ncvars["PH"] ph = ncvars["PH"]
phb = ncvars["PHB"] phb = ncvars["PHB"]
# As coded in NCL, but not sure this is possible # As coded in NCL, but not sure this is possible
varname = either("U", "UU")(wrfin) varname = either("U", "UU")(wrfin)
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 = destagger(u_vars[varname], -1) u = destagger(u_vars[varname], -1)
varname = either("V", "VV")(wrfin) varname = either("V", "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)
@ -97,117 +98,113 @@ def get_srh(wrfin, timeidx=0, method="cat", squeeze=True,
geopt = ph + phb geopt = ph + phb
geopt_unstag = destagger(geopt, -3) geopt_unstag = destagger(geopt, -3)
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")
def get_uh(wrfin, timeidx=0, method="cat", squeeze=True, def get_uh(wrfin, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, _key=None, cache=None, meta=True, _key=None,
bottom=2000.0, top=5000.0): bottom=2000.0, top=5000.0):
"""Return the updraft helicity. """Return the updraft helicity.
The *bottom* and *top* arguments specify the bottom and top limits The *bottom* and *top* arguments specify the bottom and top limits
for the integration in [m]. for the integration in [m].
This functions extracts the necessary variables from the NetCDF file This functions extracts the necessary variables from the NetCDF file
object in order to perform the calculation. object in order to perform the calculation.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. The default is 0. all times in the file or sequence. The default is 0.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata and meta (:obj:`bool`, optional): Set to False to disable metadata and
return :class:`numpy.ndarray` instead of return :class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is True. :class:`xarray.DataArray`. Default is True.
_key (:obj:`int`, optional): A caching key. This is used for internal _key (:obj:`int`, optional): A caching key. This is used for internal
purposes only. Default is None. purposes only. Default is None.
bottom (:obj:`float`, optional): The bottom limit for the integration bottom (:obj:`float`, optional): The bottom limit for the integration
in [m]. Default is 2000.0. in [m]. Default is 2000.0.
top (:obj:`float`, optional): The top limit for the integration in [m]. top (:obj:`float`, optional): The top limit for the integration in [m].
Default is 5000.0. Default is 5000.0.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The :class:`xarray.DataArray` or :class:`numpy.ndarray`: The
updraft helicity. updraft helicity.
If xarray is enabled and the *meta* parameter is True, then the result If xarray is enabled and the *meta* parameter is True, then the result
will be a :class:`xarray.DataArray` object. Otherwise, the result will will be a :class:`xarray.DataArray` object. Otherwise, the result will
be a :class:`numpy.ndarray` object with no metadata. be a :class:`numpy.ndarray` object with no metadata.
""" """
ncvars = extract_vars(wrfin, timeidx, ("W", "PH", "PHB", "MAPFAC_M"), ncvars = extract_vars(wrfin, timeidx, ("W", "PH", "PHB", "MAPFAC_M"),
method, squeeze, cache, meta=False, _key=_key) method, squeeze, cache, meta=False, _key=_key)
wstag = ncvars["W"] wstag = ncvars["W"]
ph = ncvars["PH"] ph = ncvars["PH"]
phb = ncvars["PHB"] phb = ncvars["PHB"]
mapfct = ncvars["MAPFAC_M"] mapfct = ncvars["MAPFAC_M"]
attrs = extract_global_attrs(wrfin, attrs=("DX", "DY")) attrs = extract_global_attrs(wrfin, attrs=("DX", "DY"))
dx = attrs["DX"] dx = attrs["DX"]
dy = attrs["DY"] dy = attrs["DY"]
# As coded in NCL, but not sure this is possible # As coded in NCL, but not sure this is possible
varname = either("U", "UU")(wrfin) varname = either("U", "UU")(wrfin)
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 = destagger(u_vars[varname], -1) u = destagger(u_vars[varname], -1)
varname = either("V", "VV")(wrfin) varname = either("V", "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 = destagger(v_vars[varname], -2) v = destagger(v_vars[varname], -2)
zp = (ph + phb) / Constants.G zp = (ph + phb) / Constants.G
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

816
src/wrf/g_latlon.py

File diff suppressed because it is too large Load Diff

87
src/wrf/g_omega.py

@ -7,64 +7,64 @@ from .util import extract_vars
from .metadecorators import copy_and_set_metadata from .metadecorators import copy_and_set_metadata
@copy_and_set_metadata(copy_varname="T", name="omega", @copy_and_set_metadata(copy_varname="T", name="omega",
description="omega", description="omega",
units="Pa s-1") units="Pa s-1")
def get_omega(wrfin, timeidx=0, method="cat", squeeze=True, cache=None, def get_omega(wrfin, timeidx=0, method="cat", squeeze=True, cache=None,
meta=True, _key=None): meta=True, _key=None):
"""Return Omega. """Return Omega.
This functions extracts the necessary variables from the NetCDF file This functions extracts the necessary variables from the NetCDF file
object in order to perform the calculation. object in order to perform the calculation.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. The default is 0. all times in the file or sequence. The default is 0.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata and meta (:obj:`bool`, optional): Set to False to disable metadata and
return :class:`numpy.ndarray` instead of return :class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is True. :class:`xarray.DataArray`. Default is True.
_key (:obj:`int`, optional): A caching key. This is used for internal _key (:obj:`int`, optional): A caching key. This is used for internal
purposes only. Default is None. purposes only. Default is None.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: Omega. :class:`xarray.DataArray` or :class:`numpy.ndarray`: Omega.
If xarray is If xarray is
enabled and the *meta* parameter is True, then the result will be a enabled and the *meta* parameter is True, then the result will be a
:class:`xarray.DataArray` object. Otherwise, the result will be a :class:`xarray.DataArray` object. Otherwise, the result will be a
: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"]
@ -72,13 +72,12 @@ def get_omega(wrfin, timeidx=0, method="cat", squeeze=True, cache=None,
w = ncvars["W"] w = ncvars["W"]
pb = ncvars["PB"] pb = ncvars["PB"]
qv = ncvars["QVAPOR"] qv = ncvars["QVAPOR"]
wa = destagger(w, -3) wa = destagger(w, -3)
full_t = t + Constants.T_BASE full_t = t + Constants.T_BASE
full_p = p + pb full_p = p + pb
tk = _tk(full_p, full_t) tk = _tk(full_p, full_t)
omega = _omega(qv, tk, wa, full_p) omega = _omega(qv, tk, wa, full_p)
return omega return omega

12
src/wrf/g_precip.py

@ -2,29 +2,29 @@ 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"))
rainc = ncvars["RAINC"] rainc = ncvars["RAINC"]
rainnc = ncvars["RAINNC"] rainnc = ncvars["RAINNC"]
rainsum = rainc + rainnc rainsum = rainc + rainnc
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"))
rainc1 = vars1["RAINC"] rainc1 = vars1["RAINC"]
rainnc1 = vars1["RAINNC"] rainnc1 = vars1["RAINNC"]
rainc2 = vars2["RAINC"] rainc2 = vars2["RAINC"]
rainnc2 = vars2["RAINNC"] rainnc2 = vars2["RAINNC"]
rainsum1 = rainc1 + rainnc1 rainsum1 = rainc1 + rainnc1
rainsum2 = rainc2 + rainnc2 rainsum2 = rainc2 + rainnc2
return (rainsum1 - rainsum2) return (rainsum1 - rainsum2)
# TODO: Handle bucket flipping # TODO: Handle bucket flipping

183
src/wrf/g_pressure.py

@ -5,144 +5,141 @@ from .metadecorators import copy_and_set_metadata
from .util import extract_vars, either from .util import extract_vars, either
@copy_and_set_metadata(copy_varname=either("P", "PRES"), name="pressure", @copy_and_set_metadata(copy_varname=either("P", "PRES"), name="pressure",
description="pressure") description="pressure")
@convert_units("pressure", "pa") @convert_units("pressure", "pa")
def get_pressure(wrfin, timeidx=0, method="cat", squeeze=True, def get_pressure(wrfin, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, _key=None, cache=None, meta=True, _key=None,
units="Pa"): units="Pa"):
"""Return the pressure in the specified units. """Return the pressure in the specified units.
This functions extracts the necessary variables from the NetCDF file This functions extracts the necessary variables from the NetCDF file
object in order to perform the calculation. object in order to perform the calculation.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. The default is 0. all times in the file or sequence. The default is 0.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata and meta (:obj:`bool`, optional): Set to False to disable metadata and
return :class:`numpy.ndarray` instead of return :class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is True. :class:`xarray.DataArray`. Default is True.
_key (:obj:`int`, optional): A caching key. This is used for internal _key (:obj:`int`, optional): A caching key. This is used for internal
purposes only. Default is None. purposes only. Default is None.
units (:obj:`str`): The desired units. Refer to the :meth:`getvar` units (:obj:`str`): The desired units. Refer to the :meth:`getvar`
product table for a list of available units for 'pres'. Default product table for a list of available units for 'pres'. Default
is 'Pa'. is 'Pa'.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The pressure in :class:`xarray.DataArray` or :class:`numpy.ndarray`: The pressure in
the specified units. the specified units.
If xarray is If xarray is
enabled and the *meta* parameter is True, then the result will be a enabled and the *meta* parameter is True, then the result will be a
:class:`xarray.DataArray` object. Otherwise, the result will be a :class:`xarray.DataArray` object. Otherwise, the result will be a
:class:`numpy.ndarray` object with no metadata. :class:`numpy.ndarray` object with no metadata.
""" """
varname = either("P", "PRES")(wrfin) varname = either("P", "PRES")(wrfin)
if varname == "P": if varname == "P":
p_vars = extract_vars(wrfin, timeidx, ("P", "PB"), p_vars = extract_vars(wrfin, timeidx, ("P", "PB"),
method, squeeze, cache, meta=False, method, squeeze, cache, meta=False,
_key=_key) _key=_key)
p = p_vars["P"] p = p_vars["P"]
pb = p_vars["PB"] pb = p_vars["PB"]
pres = p + pb pres = p + pb
else: else:
pres = extract_vars(wrfin, timeidx, "PRES", pres = extract_vars(wrfin, timeidx, "PRES",
method, squeeze, cache, meta=False, method, squeeze, cache, meta=False,
_key=_key)["PRES"] _key=_key)["PRES"]
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].
This functions extracts the necessary variables from the NetCDF file This functions extracts the necessary variables from the NetCDF file
object in order to perform the calculation. object in order to perform the calculation.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. The default is 0. all times in the file or sequence. The default is 0.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata and meta (:obj:`bool`, optional): Set to False to disable metadata and
return :class:`numpy.ndarray` instead of return :class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is True. :class:`xarray.DataArray`. Default is True.
_key (:obj:`int`, optional): A caching key. This is used for internal _key (:obj:`int`, optional): A caching key. This is used for internal
purposes only. Default is None. purposes only. Default is None.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The pressure in :class:`xarray.DataArray` or :class:`numpy.ndarray`: The pressure in
[hPa]. [hPa].
If xarray is If xarray is
enabled and the *meta* parameter is True, then the result will be a enabled and the *meta* parameter is True, then the result will be a
:class:`xarray.DataArray` object. Otherwise, the result will be a :class:`xarray.DataArray` object. Otherwise, the result will be a
:class:`numpy.ndarray` object with no metadata. :class:`numpy.ndarray` object with no metadata.
""" """
return get_pressure(wrfin, timeidx, method, squeeze, cache, meta, _key, return get_pressure(wrfin, timeidx, method, squeeze, cache, meta, _key,
units="hPa") units="hPa")

95
src/wrf/g_pw.py

@ -1,91 +1,86 @@
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
from .metadecorators import copy_and_set_metadata from .metadecorators import copy_and_set_metadata
@copy_and_set_metadata(copy_varname="T", name="pw", @copy_and_set_metadata(copy_varname="T", name="pw",
remove_dims=("bottom_top",), remove_dims=("bottom_top",),
description="precipitable water", description="precipitable water",
MemoryOrder="XY", MemoryOrder="XY",
units="kg m-2") units="kg m-2")
def get_pw(wrfin, timeidx=0, method="cat", squeeze=True, cache=None, def get_pw(wrfin, timeidx=0, method="cat", squeeze=True, cache=None,
meta=True, _key=None): meta=True, _key=None):
"""Return the preciptiable water. """Return the preciptiable water.
This functions extracts the necessary variables from the NetCDF file This functions extracts the necessary variables from the NetCDF file
object in order to perform the calculation. object in order to perform the calculation.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. The default is 0. all times in the file or sequence. The default is 0.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata and meta (:obj:`bool`, optional): Set to False to disable metadata and
return :class:`numpy.ndarray` instead of return :class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is True. :class:`xarray.DataArray`. Default is True.
_key (:obj:`int`, optional): A caching key. This is used for internal _key (:obj:`int`, optional): A caching key. This is used for internal
purposes only. Default is None. purposes only. Default is None.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The preciptable :class:`xarray.DataArray` or :class:`numpy.ndarray`: The preciptable
water. If xarray is water. If xarray is
enabled and the *meta* parameter is True, then the result will be a enabled and the *meta* parameter is True, then the result will be a
:class:`xarray.DataArray` object. Otherwise, the result will be a :class:`xarray.DataArray` object. Otherwise, the result will be a
: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)
t = ncvars["T"] t = ncvars["T"]
p = ncvars["P"] p = ncvars["P"]
pb = ncvars["PB"] pb = ncvars["PB"]
ph = ncvars["PH"] ph = ncvars["PH"]
phb = ncvars["PHB"] phb = ncvars["PHB"]
qv = ncvars["QVAPOR"] qv = ncvars["QVAPOR"]
full_p = p + pb full_p = p + pb
ht = (ph + phb)/Constants.G ht = (ph + phb)/Constants.G
full_t = t + Constants.T_BASE full_t = t + Constants.T_BASE
tk = _tk(full_p, full_t) tk = _tk(full_p, full_t)
tv = _tv(tk, qv) tv = _tv(tk, qv)
return _pw(full_p, tv, qv, ht) return _pw(full_p, tv, qv, ht)

176
src/wrf/g_rh.py

@ -1,156 +1,154 @@
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
@copy_and_set_metadata(copy_varname="T", name="rh", @copy_and_set_metadata(copy_varname="T", name="rh",
description="relative humidity", description="relative humidity",
units="%") units="%")
def get_rh(wrfin, timeidx=0, method="cat", squeeze=True, cache=None, def get_rh(wrfin, timeidx=0, method="cat", squeeze=True, cache=None,
meta=True, _key=None): meta=True, _key=None):
"""Return the relative humidity. """Return the relative humidity.
This functions extracts the necessary variables from the NetCDF file This functions extracts the necessary variables from the NetCDF file
object in order to perform the calculation. object in order to perform the calculation.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. The default is 0. all times in the file or sequence. The default is 0.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata and meta (:obj:`bool`, optional): Set to False to disable metadata and
return :class:`numpy.ndarray` instead of return :class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is True. :class:`xarray.DataArray`. Default is True.
_key (:obj:`int`, optional): A caching key. This is used for internal _key (:obj:`int`, optional): A caching key. This is used for internal
purposes only. Default is None. purposes only. Default is None.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The relative :class:`xarray.DataArray` or :class:`numpy.ndarray`: The relative
humidity. If xarray is humidity. If xarray is
enabled and the *meta* parameter is True, then the result will be a enabled and the *meta* parameter is True, then the result will be a
:class:`xarray.DataArray` object. Otherwise, the result will be a :class:`xarray.DataArray` object. Otherwise, the result will be a
: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"]
p = ncvars["P"] p = ncvars["P"]
pb = ncvars["PB"] pb = ncvars["PB"]
# Copy needed for the mmap nonsense of scipy.io.netcdf, which seems to # Copy needed for the mmap nonsense of scipy.io.netcdf, which seems to
# break with every release # break with every release
qvapor = ncvars["QVAPOR"].copy() qvapor = ncvars["QVAPOR"].copy()
full_t = t + Constants.T_BASE full_t = t + Constants.T_BASE
full_p = p + pb full_p = p + pb
qvapor[qvapor < 0] = 0 qvapor[qvapor < 0] = 0
tk = _tk(full_p, full_t) tk = _tk(full_p, full_t)
rh = _rh(qvapor, full_p, tk) rh = _rh(qvapor, full_p, tk)
return rh return rh
@copy_and_set_metadata(copy_varname="T2", name="rh2", @copy_and_set_metadata(copy_varname="T2", name="rh2",
description="2m relative humidity", description="2m relative humidity",
units="%") units="%")
def get_rh_2m(wrfin, timeidx=0, method="cat", squeeze=True, cache=None, def get_rh_2m(wrfin, timeidx=0, method="cat", squeeze=True, cache=None,
meta=True, _key=None): meta=True, _key=None):
"""Return the 2m relative humidity. """Return the 2m relative humidity.
This functions extracts the necessary variables from the NetCDF file This functions extracts the necessary variables from the NetCDF file
object in order to perform the calculation. object in order to perform the calculation.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. The default is 0. all times in the file or sequence. The default is 0.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata and meta (:obj:`bool`, optional): Set to False to disable metadata and
return :class:`numpy.ndarray` instead of return :class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is True. :class:`xarray.DataArray`. Default is True.
_key (:obj:`int`, optional): A caching key. This is used for internal _key (:obj:`int`, optional): A caching key. This is used for internal
purposes only. Default is None. purposes only. Default is None.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The 2m relative :class:`xarray.DataArray` or :class:`numpy.ndarray`: The 2m relative
humidity. If xarray is humidity. If xarray is
enabled and the *meta* parameter is True, then the result will be a enabled and the *meta* parameter is True, then the result will be a
:class:`xarray.DataArray` object. Otherwise, the result will be a :class:`xarray.DataArray` object. Otherwise, the result will be a
: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"]
psfc = ncvars["PSFC"] psfc = ncvars["PSFC"]
# Copy needed for the mmap nonsense of scipy.io.netcdf, which seems to # Copy needed for the mmap nonsense of scipy.io.netcdf, which seems to
# break with every release # break with every release
q2 = ncvars["Q2"].copy() q2 = ncvars["Q2"].copy()
q2[q2 < 0] = 0 q2[q2 < 0] = 0
rh = _rh(q2, psfc, t2) rh = _rh(q2, psfc, t2)
return rh
return rh

106
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
@ -10,94 +9,93 @@ from .util import extract_vars
@copy_and_set_metadata(copy_varname="T", name="slp", @copy_and_set_metadata(copy_varname="T", name="slp",
remove_dims=("bottom_top",), remove_dims=("bottom_top",),
description="sea level pressure", description="sea level pressure",
MemoryOrder="XY") MemoryOrder="XY")
@convert_units("pressure", "hpa") @convert_units("pressure", "hpa")
def get_slp(wrfin, timeidx=0, method="cat", squeeze=True, def get_slp(wrfin, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, _key=None, cache=None, meta=True, _key=None,
units="hPa"): units="hPa"):
"""Return the sea level pressure in the specified units. """Return the sea level pressure in the specified units.
This function extracts the necessary variables from the NetCDF file This function extracts the necessary variables from the NetCDF file
object in order to perform the calculation. object in order to perform the calculation.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. The default is 0. all times in the file or sequence. The default is 0.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata and meta (:obj:`bool`, optional): Set to False to disable metadata and
return :class:`numpy.ndarray` instead of return :class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is True. :class:`xarray.DataArray`. Default is True.
_key (:obj:`int`, optional): A caching key. This is used for internal _key (:obj:`int`, optional): A caching key. This is used for internal
purposes only. Default is None. purposes only. Default is None.
units (:obj:`str`): The desired units. Refer to the :meth:`getvar` units (:obj:`str`): The desired units. Refer to the :meth:`getvar`
product table for a list of available units for 'slp'. Default product table for a list of available units for 'slp'. Default
is 'Pa'. is 'Pa'.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The sea level :class:`xarray.DataArray` or :class:`numpy.ndarray`: The sea level
pressure in the specified units. If xarray is pressure in the specified units. If xarray is
enabled and the *meta* parameter is True, then the result will be a enabled and the *meta* parameter is True, then the result will be a
:class:`xarray.DataArray` object. Otherwise, the result will be a :class:`xarray.DataArray` object. Otherwise, the result will be a
: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)
t = ncvars["T"] t = ncvars["T"]
p = ncvars["P"] p = ncvars["P"]
pb = ncvars["PB"] pb = ncvars["PB"]
# Copy needed for the mmap nonsense of scipy.io.netcdf, which seems to # Copy needed for the mmap nonsense of scipy.io.netcdf, which seems to
# break with every release # break with every release
qvapor = ncvars["QVAPOR"].copy() qvapor = ncvars["QVAPOR"].copy()
ph = ncvars["PH"] ph = ncvars["PH"]
phb = ncvars["PHB"] phb = ncvars["PHB"]
full_t = t + Constants.T_BASE full_t = t + Constants.T_BASE
full_p = p + pb full_p = p + pb
qvapor[qvapor < 0] = 0. qvapor[qvapor < 0] = 0.
full_ph = (ph + phb) / Constants.G full_ph = (ph + phb) / Constants.G
destag_ph = destagger(full_ph, -3) destag_ph = destagger(full_ph, -3)
tk = _tk(full_p, full_t) tk = _tk(full_p, full_t)
slp = _slp(destag_ph, tk, full_p, qvapor) slp = _slp(destag_ph, tk, full_p, qvapor)
return slp
return slp

623
src/wrf/g_temp.py

@ -7,70 +7,70 @@ from .metadecorators import copy_and_set_metadata
from .util import extract_vars from .util import extract_vars
@copy_and_set_metadata(copy_varname="T", name="theta", @copy_and_set_metadata(copy_varname="T", name="theta",
description="potential temperature") description="potential temperature")
@convert_units("temp", "k") @convert_units("temp", "k")
def get_theta(wrfin, timeidx=0, method="cat", squeeze=True, def get_theta(wrfin, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, _key=None, cache=None, meta=True, _key=None,
units="K"): units="K"):
"""Return the potential temperature. """Return the potential temperature.
This functions extracts the necessary variables from the NetCDF file This functions extracts the necessary variables from the NetCDF file
object in order to perform the calculation. object in order to perform the calculation.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. The default is 0. all times in the file or sequence. The default is 0.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata and meta (:obj:`bool`, optional): Set to False to disable metadata and
return :class:`numpy.ndarray` instead of return :class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is True. :class:`xarray.DataArray`. Default is True.
_key (:obj:`int`, optional): A caching key. This is used for internal _key (:obj:`int`, optional): A caching key. This is used for internal
purposes only. Default is None. purposes only. Default is None.
units (:obj:`str`): The desired units. Refer to the :meth:`getvar` units (:obj:`str`): The desired units. Refer to the :meth:`getvar`
product table for a list of available units for 'theta'. Default product table for a list of available units for 'theta'. Default
is 'K'. is 'K'.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The :class:`xarray.DataArray` or :class:`numpy.ndarray`: The
potential temperature. potential temperature.
If xarray is enabled and the *meta* parameter is True, then the result If xarray is enabled and the *meta* parameter is True, then the result
will be a :class:`xarray.DataArray` object. Otherwise, the result will will be a :class:`xarray.DataArray` object. Otherwise, the result will
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)
t = ncvars["T"] t = ncvars["T"]
@ -79,438 +79,435 @@ def get_theta(wrfin, timeidx=0, method="cat", squeeze=True,
return full_t return full_t
@copy_and_set_metadata(copy_varname="T", name="temp", @copy_and_set_metadata(copy_varname="T", name="temp",
description="temperature") description="temperature")
@convert_units("temp", "k") @convert_units("temp", "k")
def get_temp(wrfin, timeidx=0, method="cat", squeeze=True, def get_temp(wrfin, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, _key=None, cache=None, meta=True, _key=None,
units="K"): units="K"):
"""Return the temperature in the specified units. """Return the temperature in the specified units.
This functions extracts the necessary variables from the NetCDF file This functions extracts the necessary variables from the NetCDF file
object in order to perform the calculation. object in order to perform the calculation.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. The default is 0. all times in the file or sequence. The default is 0.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata and meta (:obj:`bool`, optional): Set to False to disable metadata and
return :class:`numpy.ndarray` instead of return :class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is True. :class:`xarray.DataArray`. Default is True.
_key (:obj:`int`, optional): A caching key. This is used for internal _key (:obj:`int`, optional): A caching key. This is used for internal
purposes only. Default is None. purposes only. Default is None.
units (:obj:`str`): The desired units. Refer to the :meth:`getvar` units (:obj:`str`): The desired units. Refer to the :meth:`getvar`
product table for a list of available units for 'temp'. Default product table for a list of available units for 'temp'. Default
is 'K'. is 'K'.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The :class:`xarray.DataArray` or :class:`numpy.ndarray`: The
temperature in the specified units. temperature in the specified units.
If xarray is enabled and the *meta* parameter is True, then the result If xarray is enabled and the *meta* parameter is True, then the result
will be a :class:`xarray.DataArray` object. Otherwise, the result will will be a :class:`xarray.DataArray` object. Otherwise, the result will
be a :class:`numpy.ndarray` object with no metadata. be a :class:`numpy.ndarray` object with no metadata.
""" """
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"]
p = ncvars["P"] p = ncvars["P"]
pb = ncvars["PB"] pb = ncvars["PB"]
full_t = t + Constants.T_BASE full_t = t + Constants.T_BASE
full_p = p + pb full_p = p + pb
tk = _tk(full_p, full_t) tk = _tk(full_p, full_t)
return tk return tk
@copy_and_set_metadata(copy_varname="T", name="theta_e", @copy_and_set_metadata(copy_varname="T", name="theta_e",
description="equivalent potential temperature") description="equivalent potential temperature")
@convert_units("temp", "K") @convert_units("temp", "K")
def get_eth(wrfin, timeidx=0, method="cat", squeeze=True, def get_eth(wrfin, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, _key=None, cache=None, meta=True, _key=None,
units="K"): units="K"):
"""Return the equivalent potential temperature. """Return the equivalent potential temperature.
This functions extracts the necessary variables from the NetCDF file This functions extracts the necessary variables from the NetCDF file
object in order to perform the calculation. object in order to perform the calculation.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. The default is 0. all times in the file or sequence. The default is 0.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata and meta (:obj:`bool`, optional): Set to False to disable metadata and
return :class:`numpy.ndarray` instead of return :class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is True. :class:`xarray.DataArray`. Default is True.
_key (:obj:`int`, optional): A caching key. This is used for internal _key (:obj:`int`, optional): A caching key. This is used for internal
purposes only. Default is None. purposes only. Default is None.
units (:obj:`str`): The desired units. Refer to the :meth:`getvar` units (:obj:`str`): The desired units. Refer to the :meth:`getvar`
product table for a list of available units for 'eth'. Default product table for a list of available units for 'eth'. Default
is 'K'. is 'K'.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The :class:`xarray.DataArray` or :class:`numpy.ndarray`: The
equivalent potential temperature. equivalent potential temperature.
If xarray is enabled and the *meta* parameter is True, then the result If xarray is enabled and the *meta* parameter is True, then the result
will be a :class:`xarray.DataArray` object. Otherwise, the result will will be a :class:`xarray.DataArray` object. Otherwise, the result will
be a :class:`numpy.ndarray` object with no metadata. be a :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"]
p = ncvars["P"] p = ncvars["P"]
pb = ncvars["PB"] pb = ncvars["PB"]
qv = ncvars["QVAPOR"] qv = ncvars["QVAPOR"]
full_t = t + Constants.T_BASE full_t = t + Constants.T_BASE
full_p = p + pb full_p = p + pb
tk = _tk(full_p, full_t) tk = _tk(full_p, full_t)
eth = _eth(qv, tk, full_p) eth = _eth(qv, tk, full_p)
return eth return eth
@copy_and_set_metadata(copy_varname="T", name="tv", @copy_and_set_metadata(copy_varname="T", name="tv",
description="virtual temperature") description="virtual temperature")
@convert_units("temp", "k") @convert_units("temp", "k")
def get_tv(wrfin, timeidx=0, method="cat", squeeze=True, def get_tv(wrfin, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, _key=None, cache=None, meta=True, _key=None,
units="K"): units="K"):
"""Return the virtual temperature. """Return the virtual temperature.
This functions extracts the necessary variables from the NetCDF file This functions extracts the necessary variables from the NetCDF file
object in order to perform the calculation. object in order to perform the calculation.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. The default is 0. all times in the file or sequence. The default is 0.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata and meta (:obj:`bool`, optional): Set to False to disable metadata and
return :class:`numpy.ndarray` instead of return :class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is True. :class:`xarray.DataArray`. Default is True.
_key (:obj:`int`, optional): A caching key. This is used for internal _key (:obj:`int`, optional): A caching key. This is used for internal
purposes only. Default is None. purposes only. Default is None.
units (:obj:`str`): The desired units. Refer to the :meth:`getvar` units (:obj:`str`): The desired units. Refer to the :meth:`getvar`
product table for a list of available units for 'tv'. Default product table for a list of available units for 'tv'. Default
is 'K'. is 'K'.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The :class:`xarray.DataArray` or :class:`numpy.ndarray`: The
virtual temperature. virtual temperature.
If xarray is enabled and the *meta* parameter is True, then the result If xarray is enabled and the *meta* parameter is True, then the result
will be a :class:`xarray.DataArray` object. Otherwise, the result will will be a :class:`xarray.DataArray` object. Otherwise, the result will
be a :class:`numpy.ndarray` object with no metadata. be a :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"]
p = ncvars["P"] p = ncvars["P"]
pb = ncvars["PB"] pb = ncvars["PB"]
qv = ncvars["QVAPOR"] qv = ncvars["QVAPOR"]
full_t = t + Constants.T_BASE full_t = t + Constants.T_BASE
full_p = p + pb full_p = p + pb
tk = _tk(full_p, full_t) tk = _tk(full_p, full_t)
tv = _tv(tk, qv) tv = _tv(tk, qv)
return tv return tv
@copy_and_set_metadata(copy_varname="T", name="twb", @copy_and_set_metadata(copy_varname="T", name="twb",
description="wetbulb temperature") description="wetbulb temperature")
@convert_units("temp", "k") @convert_units("temp", "k")
def get_tw(wrfin, timeidx=0, method="cat", squeeze=True, def get_tw(wrfin, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, _key=None, cache=None, meta=True, _key=None,
units="K"): units="K"):
"""Return the wetbulb temperature. """Return the wetbulb temperature.
This functions extracts the necessary variables from the NetCDF file This functions extracts the necessary variables from the NetCDF file
object in order to perform the calculation. object in order to perform the calculation.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. The default is 0. all times in the file or sequence. The default is 0.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata and meta (:obj:`bool`, optional): Set to False to disable metadata and
return :class:`numpy.ndarray` instead of return :class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is True. :class:`xarray.DataArray`. Default is True.
_key (:obj:`int`, optional): A caching key. This is used for internal _key (:obj:`int`, optional): A caching key. This is used for internal
purposes only. Default is None. purposes only. Default is None.
units (:obj:`str`): The desired units. Refer to the :meth:`getvar` units (:obj:`str`): The desired units. Refer to the :meth:`getvar`
product table for a list of available units for 'twb'. Default product table for a list of available units for 'twb'. Default
is 'K'. is 'K'.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The :class:`xarray.DataArray` or :class:`numpy.ndarray`: The
wetbulb temperature. wetbulb temperature.
If xarray is enabled and the *meta* parameter is True, then the result If xarray is enabled and the *meta* parameter is True, then the result
will be a :class:`xarray.DataArray` object. Otherwise, the result will will be a :class:`xarray.DataArray` object. Otherwise, the result will
be a :class:`numpy.ndarray` object with no metadata. be a :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"]
p = ncvars["P"] p = ncvars["P"]
pb = ncvars["PB"] pb = ncvars["PB"]
qv = ncvars["QVAPOR"] qv = ncvars["QVAPOR"]
full_t = t + Constants.T_BASE full_t = t + Constants.T_BASE
full_p = p + pb full_p = p + pb
tk = _tk(full_p, full_t) tk = _tk(full_p, full_t)
tw = _wetbulb(full_p, tk, qv) tw = _wetbulb(full_p, tk, qv)
return tw return tw
def get_tk(wrfin, timeidx=0, method="cat", squeeze=True, cache=None, def get_tk(wrfin, timeidx=0, method="cat", squeeze=True, cache=None,
meta=True, _key=None): meta=True, _key=None):
"""Return the temperature in [K]. """Return the temperature in [K].
This functions extracts the necessary variables from the NetCDF file This functions extracts the necessary variables from the NetCDF file
object in order to perform the calculation. object in order to perform the calculation.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. The default is 0. all times in the file or sequence. The default is 0.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata and meta (:obj:`bool`, optional): Set to False to disable metadata and
return :class:`numpy.ndarray` instead of return :class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is True. :class:`xarray.DataArray`. Default is True.
_key (:obj:`int`, optional): A caching key. This is used for internal _key (:obj:`int`, optional): A caching key. This is used for internal
purposes only. Default is None. purposes only. Default is None.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The :class:`xarray.DataArray` or :class:`numpy.ndarray`: The
temperature in [K]. temperature in [K].
If xarray is enabled and the *meta* parameter is True, then the result If xarray is enabled and the *meta* parameter is True, then the result
will be a :class:`xarray.DataArray` object. Otherwise, the result will will be a :class:`xarray.DataArray` object. Otherwise, the result will
be a :class:`numpy.ndarray` object with no metadata. be a :class:`numpy.ndarray` object with no metadata.
""" """
return get_temp(wrfin, timeidx, method, squeeze, cache, meta, _key, return get_temp(wrfin, timeidx, method, squeeze, cache, meta, _key,
units="K") units="K")
def get_tc(wrfin, timeidx=0, method="cat", squeeze=True, cache=None, def get_tc(wrfin, timeidx=0, method="cat", squeeze=True, cache=None,
meta=True, _key=None): meta=True, _key=None):
"""Return the temperature in [degC]. """Return the temperature in [degC].
This functions extracts the necessary variables from the NetCDF file This functions extracts the necessary variables from the NetCDF file
object in order to perform the calculation. object in order to perform the calculation.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. The default is 0. all times in the file or sequence. The default is 0.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata and meta (:obj:`bool`, optional): Set to False to disable metadata and
return :class:`numpy.ndarray` instead of return :class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is True. :class:`xarray.DataArray`. Default is True.
_key (:obj:`int`, optional): A caching key. This is used for internal _key (:obj:`int`, optional): A caching key. This is used for internal
purposes only. Default is None. purposes only. Default is None.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The :class:`xarray.DataArray` or :class:`numpy.ndarray`: The
temperature in [degC]. temperature in [degC].
If xarray is enabled and the *meta* parameter is True, then the result If xarray is enabled and the *meta* parameter is True, then the result
will be a :class:`xarray.DataArray` object. Otherwise, the result will will be a :class:`xarray.DataArray` object. Otherwise, the result will
be a :class:`numpy.ndarray` object with no metadata. be a :class:`numpy.ndarray` object with no metadata.
""" """
return get_temp(wrfin, timeidx, method, squeeze, cache, meta, _key, return get_temp(wrfin, timeidx, method, squeeze, cache, meta, _key,
units="degC") units="degC")

97
src/wrf/g_terrain.py

@ -6,73 +6,70 @@ from .util import extract_vars, either
# Need to handle either # Need to handle either
@copy_and_set_metadata(copy_varname=either("HGT", "HGT_M"), name="terrain", @copy_and_set_metadata(copy_varname=either("HGT", "HGT_M"), name="terrain",
description="terrain height") description="terrain height")
@convert_units("height", "m") @convert_units("height", "m")
def get_terrain(wrfin, timeidx=0, method="cat", squeeze=True, def get_terrain(wrfin, timeidx=0, method="cat", squeeze=True,
cache=None, meta=False, _key=None, units="m"): cache=None, meta=False, _key=None, units="m"):
"""Return the terrain height in the specified units. """Return the terrain height in the specified units.
This functions extracts the necessary variables from the NetCDF file This functions extracts the necessary variables from the NetCDF file
object in order to perform the calculation. object in order to perform the calculation.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. The default is 0. all times in the file or sequence. The default is 0.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata and meta (:obj:`bool`, optional): Set to False to disable metadata and
return :class:`numpy.ndarray` instead of return :class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is True. :class:`xarray.DataArray`. Default is True.
_key (:obj:`int`, optional): A caching key. This is used for internal _key (:obj:`int`, optional): A caching key. This is used for internal
purposes only. Default is None. purposes only. Default is None.
units (:obj:`str`): The desired units. Refer to the :meth:`getvar` units (:obj:`str`): The desired units. Refer to the :meth:`getvar`
product table for a list of available units for 'ter'. Default product table for a list of available units for 'ter'. Default
is 'm'. is 'm'.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The terrain :class:`xarray.DataArray` or :class:`numpy.ndarray`: The terrain
height in the specified units. If xarray is height in the specified units. If xarray is
enabled and the *meta* parameter is True, then the result will be a enabled and the *meta* parameter is True, then the result will be a
:class:`xarray.DataArray` object. Otherwise, the result will be a :class:`xarray.DataArray` object. Otherwise, the result will be a
:class:`numpy.ndarray` object with no metadata. :class:`numpy.ndarray` object with no metadata.
""" """
varname = either("HGT", "HGT_M")(wrfin) varname = either("HGT", "HGT_M")(wrfin)
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]

161
src/wrf/g_times.py

@ -3,119 +3,118 @@ from __future__ import (absolute_import, division, print_function)
from .util import extract_times from .util import extract_times
def get_times(wrfin, timeidx=0, method="cat", squeeze=True, cache=None, def get_times(wrfin, timeidx=0, method="cat", squeeze=True, cache=None,
meta=True, _key=None): meta=True, _key=None):
"""Return a sequence of time objects. """Return a sequence of time objects.
This function reads the 'Times' variable and creates a sequence of This function reads the 'Times' variable and creates a sequence of
:class:`datetime.datetime` objects. :class:`datetime.datetime` objects.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. all times in the file or sequence.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata. meta (:obj:`bool`, optional): Set to False to disable metadata.
_key (:obj:`int`, optional): A caching key. This is used for internal _key (:obj:`int`, optional): A caching key. This is used for internal
purposes only. Default is None. purposes only. Default is None.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: A sequence of :class:`xarray.DataArray` or :class:`numpy.ndarray`: A sequence of
:class:`datetime.datetime` objects. If *meta* is True, the sequence :class:`datetime.datetime` objects. If *meta* is True, the sequence
will be of type :class:`xarray.DataArray`, otherwise the sequence is will be of type :class:`xarray.DataArray`, otherwise the sequence is
:class:`numpy.ndarray`. :class:`numpy.ndarray`.
""" """
return extract_times(wrfin, timeidx, method, squeeze, cache, return extract_times(wrfin, timeidx, method, squeeze, cache,
meta=meta, do_xtime=False) meta=meta, do_xtime=False)
def get_xtimes(wrfin, timeidx=0, method="cat", squeeze=True, cache=None, def get_xtimes(wrfin, timeidx=0, method="cat", squeeze=True, cache=None,
meta=True, _key=None): meta=True, _key=None):
"""Return a sequence of time objects. """Return a sequence of time objects.
This function reads the 'XTIME' variable and creates a sequence of This function reads the 'XTIME' variable and creates a sequence of
:obj:`float` objects. :obj:`float` objects.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. all times in the file or sequence.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata. meta (:obj:`bool`, optional): Set to False to disable metadata.
_key (:obj:`int`, optional): A caching key. This is used for internal _key (:obj:`int`, optional): A caching key. This is used for internal
purposes only. Default is None. purposes only. Default is None.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: A sequence of :class:`xarray.DataArray` or :class:`numpy.ndarray`: A sequence of
:obj:`float` objects. If *meta* is True, the sequence will be of type :obj:`float` objects. If *meta* is True, the sequence will be of type
:class:`xarray.DataArray`, otherwise the sequence is :class:`xarray.DataArray`, otherwise the sequence is
:class:`numpy.ndarray`. :class:`numpy.ndarray`.
Raises: Raises:
:class:`KeyError`: Raised if the 'XTIME' variable is not present :class:`KeyError`: Raised if the 'XTIME' variable is not present
in the NetCDF file. in the NetCDF file.
""" """
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)

1007
src/wrf/g_uvmet.py

File diff suppressed because it is too large Load Diff

179
src/wrf/g_vorticity.py

@ -5,69 +5,69 @@ from .util import extract_vars, extract_global_attrs
from .metadecorators import copy_and_set_metadata from .metadecorators import copy_and_set_metadata
@copy_and_set_metadata(copy_varname="T", name="avo", @copy_and_set_metadata(copy_varname="T", name="avo",
description="absolute vorticity", description="absolute vorticity",
units="10-5 s-1") units="10-5 s-1")
def get_avo(wrfin, timeidx=0, method="cat", squeeze=True, cache=None, def get_avo(wrfin, timeidx=0, method="cat", squeeze=True, cache=None,
meta=True, _key=None): meta=True, _key=None):
"""Return the absolute vorticity. """Return the absolute vorticity.
This function extracts the necessary variables from the NetCDF file This function extracts the necessary variables from the NetCDF file
object in order to perform the calculation. object in order to perform the calculation.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. The default is 0. all times in the file or sequence. The default is 0.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata and meta (:obj:`bool`, optional): Set to False to disable metadata and
return :class:`numpy.ndarray` instead of return :class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is True. :class:`xarray.DataArray`. Default is True.
_key (:obj:`int`, optional): A caching key. This is used for internal _key (:obj:`int`, optional): A caching key. This is used for internal
purposes only. Default is None. purposes only. Default is None.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The absolute :class:`xarray.DataArray` or :class:`numpy.ndarray`: The absolute
vorticity. If xarray is vorticity. If xarray is
enabled and the *meta* parameter is True, then the result will be a enabled and the *meta* parameter is True, then the result will be a
:class:`xarray.DataArray` object. Otherwise, the result will be a :class:`xarray.DataArray` object. Otherwise, the result will be a
:class:`numpy.ndarray` object with no metadata. :class:`numpy.ndarray` object with no metadata.
""" """
ncvars = extract_vars(wrfin, timeidx, ("U", "V", "MAPFAC_U", ncvars = extract_vars(wrfin, timeidx, ("U", "V", "MAPFAC_U",
"MAPFAC_V", "MAPFAC_M", "MAPFAC_V", "MAPFAC_M",
"F"), "F"),
method, squeeze, cache, meta=False, _key=_key) method, squeeze, cache, meta=False, _key=_key)
attrs = extract_global_attrs(wrfin, attrs=("DX", "DY")) attrs = extract_global_attrs(wrfin, attrs=("DX", "DY"))
u = ncvars["U"] u = ncvars["U"]
v = ncvars["V"] v = ncvars["V"]
@ -75,70 +75,70 @@ def get_avo(wrfin, timeidx=0, method="cat", squeeze=True, cache=None,
msfv = ncvars["MAPFAC_V"] msfv = ncvars["MAPFAC_V"]
msfm = ncvars["MAPFAC_M"] msfm = ncvars["MAPFAC_M"]
cor = ncvars["F"] cor = ncvars["F"]
dx = attrs["DX"] dx = attrs["DX"]
dy = attrs["DY"] dy = attrs["DY"]
return _avo(u, v, msfu, msfv, msfm, cor, dx, dy) return _avo(u, v, msfu, msfv, msfm, cor, dx, dy)
@copy_and_set_metadata(copy_varname="T", name="pvo", @copy_and_set_metadata(copy_varname="T", name="pvo",
description="potential vorticity", description="potential vorticity",
units="PVU") units="PVU")
def get_pvo(wrfin, timeidx=0, method="cat", squeeze=True, cache=None, def get_pvo(wrfin, timeidx=0, method="cat", squeeze=True, cache=None,
meta=True, _key=None): meta=True, _key=None):
"""Return the potential vorticity. """Return the potential vorticity.
This function extracts the necessary variables from the NetCDF file This function extracts the necessary variables from the NetCDF file
object in order to perform the calculation. object in order to perform the calculation.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. The default is 0. all times in the file or sequence. The default is 0.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata and meta (:obj:`bool`, optional): Set to False to disable metadata and
return :class:`numpy.ndarray` instead of return :class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is True. :class:`xarray.DataArray`. Default is True.
_key (:obj:`int`, optional): A caching key. This is used for internal _key (:obj:`int`, optional): A caching key. This is used for internal
purposes only. Default is None. purposes only. Default is None.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The potential :class:`xarray.DataArray` or :class:`numpy.ndarray`: The potential
vorticity. If xarray is vorticity. If xarray is
enabled and the *meta* parameter is True, then the result will be a enabled and the *meta* parameter is True, then the result will be a
:class:`xarray.DataArray` object. Otherwise, the result will be a :class:`xarray.DataArray` object. Otherwise, the result will be a
:class:`numpy.ndarray` object with no metadata. :class:`numpy.ndarray` object with no metadata.
""" """
ncvars = extract_vars(wrfin, timeidx, ("U", "V", "T", "P", ncvars = extract_vars(wrfin, timeidx, ("U", "V", "T", "P",
"PB", "MAPFAC_U", "PB", "MAPFAC_U",
@ -146,7 +146,7 @@ def get_pvo(wrfin, timeidx=0, method="cat", squeeze=True, cache=None,
"F"), "F"),
method, squeeze, cache, meta=False, _key=_key) method, squeeze, cache, meta=False, _key=_key)
attrs = extract_global_attrs(wrfin, attrs=("DX", "DY")) attrs = extract_global_attrs(wrfin, attrs=("DX", "DY"))
u = ncvars["U"] u = ncvars["U"]
v = ncvars["V"] v = ncvars["V"]
t = ncvars["T"] t = ncvars["T"]
@ -156,12 +156,11 @@ def get_pvo(wrfin, timeidx=0, method="cat", squeeze=True, cache=None,
msfv = ncvars["MAPFAC_V"] msfv = ncvars["MAPFAC_V"]
msfm = ncvars["MAPFAC_M"] msfm = ncvars["MAPFAC_M"]
cor = ncvars["F"] cor = ncvars["F"]
dx = attrs["DX"] dx = attrs["DX"]
dy = attrs["DY"] dy = attrs["DY"]
full_t = t + 300 full_t = t + 300
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)

989
src/wrf/g_wind.py

File diff suppressed because it is too large Load Diff

71
src/wrf/geobnds.py

@ -2,46 +2,47 @@ 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.
Currently, only corner points are used, specified as the bottom left and Currently, only corner points are used, specified as the bottom left and
top right corners. Users can specify the corner points directly, or top right corners. Users can specify the corner points directly, or
specify two-dimensional latitude and longitude arrays and the corner points specify two-dimensional latitude and longitude arrays and the corner points
will be extracted from them. will be extracted from them.
Attributes: Attributes:
bottom_left (:class:`wrf.CoordPair`): The bottom left coordinate. bottom_left (:class:`wrf.CoordPair`): The bottom left coordinate.
top_right (:class:`wrf.CoordPair`): The top right coordinate. top_right (:class:`wrf.CoordPair`): The top right coordinate.
""" """
def __init__(self, bottom_left=None, top_right=None, lats=None, lons=None): def __init__(self, bottom_left=None, top_right=None, lats=None, lons=None):
""" Initialize a :class:`wrf.GeoBounds` object. """ Initialize a :class:`wrf.GeoBounds` object.
Args: Args:
bottom_left (:class:`wrf.CoordPair`, optional): The lower left bottom_left (:class:`wrf.CoordPair`, optional): The lower left
corner. Must also specify *top_right* if used. corner. Must also specify *top_right* if used.
Default is None. Default is None.
top_right (:class:`wrf.CoordPair`, optional): The upper right top_right (:class:`wrf.CoordPair`, optional): The upper right
corner. Must also specify *bottom_left* if used. corner. Must also specify *bottom_left* if used.
Default is None. Default is None.
lats (:class:`numpy.ndarray`, optional): An array of at least lats (:class:`numpy.ndarray`, optional): An array of at least
two dimensions containing all of the latitude values. Must two dimensions containing all of the latitude values. Must
also specify *lons* if used. Default is None. also specify *lons* if used. Default is None.
lons (:class:`numpy.ndarray`, optional): An array of at least lons (:class:`numpy.ndarray`, optional): An array of at least
two dimensions containing all of the longitude values. Must two dimensions containing all of the longitude values. Must
also specify *lats* if used. Default is None. also specify *lats* if used. Default is None.
""" """
if bottom_left is not None and top_right is not None: if bottom_left is not None and top_right is not None:
self.bottom_left = bottom_left self.bottom_left = bottom_left
self.top_right = top_right self.top_right = top_right
# Make sure the users set lat/lon coordinates # Make sure the users set lat/lon coordinates
if self.bottom_left.lat is None: if self.bottom_left.lat is None:
raise ValueError("'bottom_left' parameter does not contain a " raise ValueError("'bottom_left' parameter does not contain a "
@ -56,35 +57,31 @@ 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 "
"or 'lats' and 'lons' parameters") "or 'lats' and 'lons' parameters")
def __repr__(self): def __repr__(self):
argstr = "{}, {}".format(repr(self.bottom_left), argstr = "{}, {}".format(repr(self.bottom_left),
repr(self.top_right)) repr(self.top_right))
return "{}({})".format(self.__class__.__name__, argstr) return "{}({})".format(self.__class__.__name__, argstr)
class NullGeoBounds(GeoBounds): class NullGeoBounds(GeoBounds):
"""An emtpy :class:`wrf.GeoBounds` subclass. """An emtpy :class:`wrf.GeoBounds` subclass.
This is used for initializing arrays of :class:`wrf.GeoBounds`, in This is used for initializing arrays of :class:`wrf.GeoBounds`, in
particular when working with moving domains and variables combined with the particular when working with moving domains and variables combined with the
'join' method. 'join' method.
""" """
def __init__(self): def __init__(self):
""" Initialize a :class:`wrf.NullGeoBounds` object.""" """ Initialize a :class:`wrf.NullGeoBounds` object."""
pass pass
def __repr__(self): def __repr__(self):
return "{}()".format(self.__class__.__name__) return "{}()".format(self.__class__.__name__)

945
src/wrf/interp.py

File diff suppressed because it is too large Load Diff

454
src/wrf/interputils.py

@ -14,165 +14,164 @@ from .util import pairs_to_latlon
def to_positive_idxs(shape, coord): def to_positive_idxs(shape, coord):
"""Return the positive index values. """Return the positive index values.
This function converts negative index values to positive index values. This function converts negative index values to positive index values.
Args: Args:
shape (indexable sequence): The array shape. shape (indexable sequence): The array shape.
coord (indexable sequence): The coordinate pair for x and y. coord (indexable sequence): The coordinate pair for x and y.
Returns: Returns:
:obj:`list`: The coordinate values with all positive indexes. :obj:`list`: The coordinate values with all positive indexes.
""" """
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,
start_point=None, end_point=None): def _calc_xy(xdim, ydim, pivot_point=None, angle=None,
start_point=None, end_point=None):
"""Return the x,y points for the horizontal cross section line. """Return the x,y points for the horizontal cross section line.
Args: Args:
xdim (:obj:`int`): The x-dimension size. xdim (:obj:`int`): The x-dimension size.
ydim (:obj:`int`): The y-dimension size. ydim (:obj:`int`): The y-dimension size.
pivot_point (:obj:`tuple` or :obj:`list`, optional): A pivot_point (:obj:`tuple` or :obj:`list`, optional): A
:obj:`tuple` or :obj:`list` with two entries, :obj:`tuple` or :obj:`list` with two entries,
in the form of [x, y] (or [west_east, south_north]), which in the form of [x, y] (or [west_east, south_north]), which
indicates the x,y location through which the plane will pass. indicates the x,y location through which the plane will pass.
Must also specify `angle`. Must also specify `angle`.
angle (:obj:`float`, optional): Only valid for cross sections where angle (:obj:`float`, optional): Only valid for cross sections where
a plane will be plotted through a plane will be plotted through
a given point on the model domain. 0.0 represents a S-N cross a given point on the model domain. 0.0 represents a S-N cross
section. 90.0 is a W-E cross section. section. 90.0 is a W-E cross section.
start_point (:obj:`tuple` or :obj:`list`, optional): A start_point (:obj:`tuple` or :obj:`list`, optional): A
:obj:`tuple` or :obj:`list` with two entries, in the form of :obj:`tuple` or :obj:`list` with two entries, in the form of
[x, y] (or [west_east, south_north]), which indicates the start [x, y] (or [west_east, south_north]), which indicates the start
x,y location through which the plane will pass. x,y location through which the plane will pass.
end_point (:obj:`tuple` or :obj:`list`, optional): A end_point (:obj:`tuple` or :obj:`list`, optional): A
:obj:`tuple` or :obj:`list` with two entries, in the form of :obj:`tuple` or :obj:`list` with two entries, in the form of
[x, y] (or [west_east, south_north]), which indicates the end x,y [x, y] (or [west_east, south_north]), which indicates the end x,y
location through which the plane will pass. location through which the plane will pass.
Returns: Returns:
:class:`np.ndarray`: A two-dimensional array with the left index :class:`np.ndarray`: A two-dimensional array with the left index
representing each point along the line, and the rightmost dimension representing each point along the line, and the rightmost dimension
having two values for the x and y coordinates [0=X, 1=Y]. having two values for the x and y coordinates [0=X, 1=Y].
""" """
# Have a pivot point with an angle to find cross section # Have a pivot point with an angle to find cross section
if pivot_point is not None and angle is not None: if pivot_point is not None and angle is not None:
xp = pivot_point[-2] xp = pivot_point[-2]
yp = pivot_point[-1] yp = pivot_point[-1]
if xp >= xdim or yp >= ydim: if xp >= xdim or yp >= ydim:
raise ValueError("pivot point {} is outside of domain " raise ValueError("pivot point {} is outside of domain "
"with shape {}".format(pivot_point, "with shape {}".format(pivot_point,
(xdim, ydim))) (xdim, ydim)))
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
# find intersections with domain boundaries # find intersections with domain boundaries
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:
x0 = start_point[-2] x0 = start_point[-2]
y0 = start_point[-1] y0 = start_point[-1]
x1 = end_point[-2] x1 = end_point[-2]
y1 = end_point[-1] y1 = end_point[-1]
if x0 >= xdim or y0 >= ydim: if x0 >= xdim or y0 >= ydim:
raise ValueError("start_point {} is outside of domain " raise ValueError("start_point {} is outside of domain "
"with shape {}".format(start_point, (xdim, ydim))) "with shape {}".format(start_point, (xdim, ydim)))
if x1 >= xdim or y1 >= ydim: if x1 >= xdim or y1 >= ydim:
raise ValueError("end_point {} is outside of domain " raise ValueError("end_point {} is outside of domain "
"with shape {}".format(end_point, (xdim, ydim))) "with shape {}".format(end_point, (xdim, ydim)))
else: else:
raise ValueError("invalid start/end or pivot/angle arguments") raise ValueError("invalid start/end or pivot/angle arguments")
dx = x1 - x0 dx = x1 - x0
dy = y1 - y0 dy = y1 - y0
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
@ -180,66 +179,66 @@ def get_xy_z_params(z, pivot_point=None, angle=None,
start_point=None, end_point=None, start_point=None, end_point=None,
levels=None, autolevels=100): levels=None, autolevels=100):
"""Return the cross section parameters. """Return the cross section parameters.
This function returns the xy horizontal cross section line coordinates, This function returns the xy horizontal cross section line coordinates,
the xy x z vertical values interpolated along the xy cross section the xy x z vertical values interpolated along the xy cross section
line, and the fixed vertical levels to be used by the cross section line, and the fixed vertical levels to be used by the cross section
algorithm (at ~1% increments for the minimum to maximum vertical algorithm (at ~1% increments for the minimum to maximum vertical
span). span).
Args: Args:
z (:class:`numpy.ndarray`): The vertical coordinate, whose rightmost z (:class:`numpy.ndarray`): The vertical coordinate, whose rightmost
dimensions are bottom_top x south_north x west_east. dimensions are bottom_top x south_north x west_east.
pivot_point (:obj:`tuple` or :obj:`list`, optional): A pivot_point (:obj:`tuple` or :obj:`list`, optional): A
:obj:`tuple` or :obj:`list` with two entries, :obj:`tuple` or :obj:`list` with two entries,
in the form of [x, y] (or [west_east, south_north]), which in the form of [x, y] (or [west_east, south_north]), which
indicates the x,y location through which the plane will pass. indicates the x,y location through which the plane will pass.
Must also specify `angle`. Must also specify `angle`.
angle (:obj:`float`, optional): Only valid for cross sections where angle (:obj:`float`, optional): Only valid for cross sections where
a plane will be plotted through a plane will be plotted through
a given point on the model domain. 0.0 represents a S-N cross a given point on the model domain. 0.0 represents a S-N cross
section. 90.0 is a W-E cross section. section. 90.0 is a W-E cross section.
start_point (:obj:`tuple` or :obj:`list`, optional): A start_point (:obj:`tuple` or :obj:`list`, optional): A
:obj:`tuple` or :obj:`list` with two entries, in the form of :obj:`tuple` or :obj:`list` with two entries, in the form of
[x, y] (or [west_east, south_north]), which indicates the start [x, y] (or [west_east, south_north]), which indicates the start
x,y location through which the plane will pass. x,y location through which the plane will pass.
end_point (:obj:`tuple` or :obj:`list`, optional): A end_point (:obj:`tuple` or :obj:`list`, optional): A
:obj:`tuple` or :obj:`list` with two entries, in the form of :obj:`tuple` or :obj:`list` with two entries, in the form of
[x, y] (or [west_east, south_north]), which indicates the end x,y [x, y] (or [west_east, south_north]), which indicates the end x,y
location through which the plane will pass. location through which the plane will pass.
levels (sequence): A sequence of :obj:`float` for the desired levels (sequence): A sequence of :obj:`float` for the desired
vertical levels in the output array. If None, a fixed set of vertical levels in the output array. If None, a fixed set of
vertical levels is provided. Default is None. vertical levels is provided. Default is None.
autolevels(:obj:`int`, optional): The number of evenly spaced autolevels(:obj:`int`, optional): The number of evenly spaced
automatically chosen vertical levels to use when *levels* automatically chosen vertical levels to use when *levels*
is None. Default is 100. is None. Default is 100.
Returns: Returns:
:obj:`tuple`: A tuple containing the xy horizontal cross section :obj:`tuple`: A tuple containing the xy horizontal cross section
coordinates, the vertical values interpolated along the xy cross coordinates, the vertical values interpolated along the xy cross
section line, and the fixed vertical levels used by the section line, and the fixed vertical levels used by the
cross section algorithm at ~1% increments of minimum to maximum cross section algorithm at ~1% increments of minimum to maximum
vertical span. vertical span.
""" """
xy = get_xy(z, pivot_point, angle, start_point, end_point) xy = get_xy(z, pivot_point, angle, start_point, end_point)
# Interp z # Interp z
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
if(var2dz[idx1] > var2dz[idx2]): # monotonically decreasing coordinate if(var2dz[idx1] > var2dz[idx2]): # monotonically decreasing coordinate
@ -255,181 +254,182 @@ def get_xy_z_params(z, pivot_point=None, angle=None,
dz = (1.0/autolevels)*z_max dz = (1.0/autolevels)*z_max
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)
return xy, var2dz, z_var2d return xy, var2dz, z_var2d
def get_xy(var, pivot_point=None, angle=None, def get_xy(var, pivot_point=None, angle=None,
start_point=None, end_point=None): start_point=None, end_point=None):
"""Return the x,y points for the horizontal cross section line. """Return the x,y points for the horizontal cross section line.
Args: Args:
var (:class:`xarray.DataArray` or :class:`numpy.ndarray`): A variable var (:class:`xarray.DataArray` or :class:`numpy.ndarray`): A variable
that contains a :attr:`shape` attribute. that contains a :attr:`shape` attribute.
pivot_point (:obj:`tuple` or :obj:`list`, optional): A pivot_point (:obj:`tuple` or :obj:`list`, optional): A
:obj:`tuple` or :obj:`list` with two entries, :obj:`tuple` or :obj:`list` with two entries,
in the form of [x, y] (or [west_east, south_north]), which in the form of [x, y] (or [west_east, south_north]), which
indicates the x,y location through which the plane will pass. indicates the x,y location through which the plane will pass.
Must also specify `angle`. Must also specify `angle`.
angle (:obj:`float`, optional): Only valid for cross sections where angle (:obj:`float`, optional): Only valid for cross sections where
a plane will be plotted through a plane will be plotted through
a given point on the model domain. 0.0 represents a S-N cross a given point on the model domain. 0.0 represents a S-N cross
section. 90.0 is a W-E cross section. section. 90.0 is a W-E cross section.
start_point (:obj:`tuple` or :obj:`list`, optional): A start_point (:obj:`tuple` or :obj:`list`, optional): A
:obj:`tuple` or :obj:`list` with two entries, in the form of :obj:`tuple` or :obj:`list` with two entries, in the form of
[x, y] (or [west_east, south_north]), which indicates the start [x, y] (or [west_east, south_north]), which indicates the start
x,y location through which the plane will pass. x,y location through which the plane will pass.
end_point (:obj:`tuple` or :obj:`list`, optional): A end_point (:obj:`tuple` or :obj:`list`, optional): A
:obj:`tuple` or :obj:`list` with two entries, in the form of :obj:`tuple` or :obj:`list` with two entries, in the form of
[x, y] (or [west_east, south_north]), which indicates the end x,y [x, y] (or [west_east, south_north]), which indicates the end x,y
location through which the plane will pass. location through which the plane will pass.
Returns: Returns:
:class:`np.ndarray`: A two-dimensional array with the left index :class:`np.ndarray`: A two-dimensional array with the left index
representing each point along the line, and the rightmost dimension representing each point along the line, and the rightmost dimension
having two values for the x and y coordinates [0=X, 1=Y]. having two values for the x and y coordinates [0=X, 1=Y].
""" """
if pivot_point is not None: if pivot_point is not None:
pos_pivot = to_positive_idxs(var.shape[-2:], pivot_point) pos_pivot = to_positive_idxs(var.shape[-2:], pivot_point)
else: else:
pos_pivot = pivot_point pos_pivot = pivot_point
if start_point is not None: if start_point is not None:
pos_start = to_positive_idxs(var.shape[-2:], start_point) pos_start = to_positive_idxs(var.shape[-2:], start_point)
else: else:
pos_start = start_point pos_start = start_point
if end_point is not None: if end_point is not None:
pos_end = to_positive_idxs(var.shape[-2:], end_point) pos_end = to_positive_idxs(var.shape[-2:], end_point)
else: else:
pos_end = start_point pos_end = start_point
xdim = var.shape[-1] xdim = var.shape[-1]
ydim = var.shape[-2] ydim = var.shape[-2]
xy = _calc_xy(xdim, ydim, pos_pivot, angle, pos_start, pos_end) xy = _calc_xy(xdim, ydim, pos_pivot, angle, pos_start, pos_end)
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.
This function converts latitude,longitude coordinate pairs to This function converts latitude,longitude coordinate pairs to
x,y coordinate pairs. x,y coordinate pairs.
Args: Args:
pairs (:class:`CoordPair` or sequence): A single coordinate pair or pairs (:class:`CoordPair` or sequence): A single coordinate pair or
a sequence of coordinate pairs to be converted. a sequence of coordinate pairs to be converted.
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable, optional): WRF-ARW NetCDF iterable, optional): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. This is used or an iterable sequence of the aforementioned types. This is used
to obtain the map projection when using latitude,longitude to obtain the map projection when using latitude,longitude
coordinates. Should not be used when working with x,y coordinates. Should not be used when working with x,y
coordinates. Default is None. coordinates. Default is None.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, 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 integer, from moving nests. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. Only required when all times in the file or sequence. Only required when
*wrfin* is specified and the nest is moving. Default is 0. *wrfin* is specified and the nest is moving. Default is 0.
stagger (:obj:`str`): If using latitude, longitude coordinate pairs stagger (:obj:`str`): If using latitude, longitude coordinate pairs
for *start_point*, *end_point*, or *pivot_point*, for *start_point*, *end_point*, or *pivot_point*,
set the appropriate grid staggering type for *field2d*. By default, set the appropriate grid staggering type for *field2d*. By default,
the mass grid is used. The options are: the mass grid is used. The options are:
- 'm': Use the mass grid (default). - 'm': Use the mass grid (default).
- 'u': Use the same staggered grid as the u wind component, - 'u': Use the same staggered grid as the u wind component,
which has a staggered west_east (x) dimension. which has a staggered west_east (x) dimension.
- 'v': Use the same staggered grid as the v wind component, - 'v': Use the same staggered grid as the v wind component,
which has a staggered south_north (y) dimension. which has a staggered south_north (y) dimension.
projection (:class:`wrf.WrfProj`, optional): The map projection (:class:`wrf.WrfProj`, optional): The map
projection object to use when working with latitude, longitude projection object to use when working with latitude, longitude
coordinates, and must be specified if *wrfin* is None. Default coordinates, and must be specified if *wrfin* is None. Default
is None. is None.
ll_point (:class:`wrf.CoordPair`, sequence of :class:`wrf.CoordPair`, \ ll_point (:class:`wrf.CoordPair`, sequence of :class:`wrf.CoordPair`, \
optional): The lower left latitude, longitude point for your domain, optional): The lower left latitude, longitude point for your domain,
and must be specified and must be specified
if *wrfin* is None. If the domain is a moving nest, this should be if *wrfin* is None. If the domain is a moving nest, this should be
a sequence of :class:`wrf.CoordPair`. Default is None. a sequence of :class:`wrf.CoordPair`. Default is None.
Returns: Returns:
:class:`wrf.CoordPair` or sequence: The coordinate pair(s) in :class:`wrf.CoordPair` or sequence: The coordinate pair(s) in
x,y grid coordinates. x,y grid coordinates.
""" """
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")
lat, lon = pairs_to_latlon(pairs) lat, lon = pairs_to_latlon(pairs)
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
if map_proj == ProjectionTypes.LAT_LON: if map_proj == ProjectionTypes.LAT_LON:
pole_lat = projection.pole_lat pole_lat = projection.pole_lat
pole_lon = projection.pole_lon pole_lon = projection.pole_lon
latinc = ((projection.dy*360.0)/2.0 / latinc = ((projection.dy*360.0)/2.0 /
Constants.PI/Constants.WRF_EARTH_RADIUS) Constants.PI/Constants.WRF_EARTH_RADIUS)
loninc = ((projection.dx*360.0)/2.0 / loninc = ((projection.dx*360.0)/2.0 /
Constants.PI/Constants.WRF_EARTH_RADIUS) Constants.PI/Constants.WRF_EARTH_RADIUS)
else: else:
pole_lat = 90.0 pole_lat = 90.0
pole_lon = 0.0 pole_lon = 0.0
latinc = 0.0 latinc = 0.0
loninc = 0.0 loninc = 0.0
ll_lat, ll_lon = pairs_to_latlon(ll_point) ll_lat, ll_lon = pairs_to_latlon(ll_point)
xy_vals = _ll_to_xy(lat, lon, meta=False, squeeze=True, xy_vals = _ll_to_xy(lat, lon, meta=False, squeeze=True,
as_int=True, as_int=True,
map_proj=projection.map_proj, map_proj=projection.map_proj,
truelat1=projection.truelat1, truelat1=projection.truelat1,
truelat2=projection.truelat2, truelat2=projection.truelat2,
stand_lon=projection.stand_lon, stand_lon=projection.stand_lon,
ref_lat=ll_lat, ref_lat=ll_lat,
ref_lon=ll_lon, ref_lon=ll_lon,
pole_lat=pole_lat, pole_lat=pole_lat,
pole_lon=pole_lon, pole_lon=pole_lon,
known_x=0, known_x=0,
known_y=0, known_y=0,
dx=projection.dx, dx=projection.dx,
dy=projection.dy, dy=projection.dy,
latinc=latinc, latinc=latinc,
loninc=loninc) loninc=loninc)
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])]

547
src/wrf/latlonutils.py

@ -6,36 +6,36 @@ 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
def _lat_varname(wrfin, stagger): def _lat_varname(wrfin, stagger):
"""Return the latitude variable name for the specified stagger type. """Return the latitude variable name for the specified stagger type.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
stagger (:obj:`str`): The staggered grid type which is one of the stagger (:obj:`str`): The staggered grid type which is one of the
following: following:
- 'm': Use the mass grid (default). - 'm': Use the mass grid (default).
- 'u': Use the same staggered grid as the u wind component, - 'u': Use the same staggered grid as the u wind component,
which has a staggered west_east (x) dimension. which has a staggered west_east (x) dimension.
- 'v': Use the same staggered grid as the v wind component, - 'v': Use the same staggered grid as the v wind component,
which has a staggered south_north (y) dimension. which has a staggered south_north (y) dimension.
Returns: Returns:
:obj:`str`: The latitude variable name. :obj:`str`: The latitude variable name.
""" """
if stagger is None or stagger.lower() == "m": if stagger is None or stagger.lower() == "m":
varname = either("XLAT", "XLAT_M")(wrfin) varname = either("XLAT", "XLAT_M")(wrfin)
@ -43,32 +43,33 @@ def _lat_varname(wrfin, stagger):
varname = "XLAT_{}".format(stagger.upper()) varname = "XLAT_{}".format(stagger.upper())
else: else:
raise ValueError("invalid 'stagger' value") raise ValueError("invalid 'stagger' value")
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.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
stagger (:obj:`str`): The staggered grid type, which is one of the stagger (:obj:`str`): The staggered grid type, which is one of the
following: following:
- 'm': Use the mass grid (default). - 'm': Use the mass grid (default).
- 'u': Use the same staggered grid as the u wind component, - 'u': Use the same staggered grid as the u wind component,
which has a staggered west_east (x) dimension. which has a staggered west_east (x) dimension.
- 'v': Use the same staggered grid as the v wind component, - 'v': Use the same staggered grid as the v wind component,
which has a staggered south_north (y) dimension. which has a staggered south_north (y) dimension.
Returns: Returns:
:obj:`str`: The latitude variable name. :obj:`str`: The latitude variable name.
""" """
if stagger is None or stagger.lower() == "m": if stagger is None or stagger.lower() == "m":
varname = either("XLONG", "XLONG_M")(wrfin) varname = either("XLONG", "XLONG_M")(wrfin)
@ -76,64 +77,65 @@ def _lon_varname(wrfin, stagger):
varname = "XLONG_{}".format(stagger.upper()) varname = "XLONG_{}".format(stagger.upper())
else: else:
raise ValueError("invalid 'stagger' value") raise ValueError("invalid 'stagger' value")
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.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. The default is 0. all times in the file or sequence. The default is 0.
stagger (:obj:`str`): The staggered grid type, which is one of the stagger (:obj:`str`): The staggered grid type, which is one of the
following: following:
- 'm': Use the mass grid (default). - 'm': Use the mass grid (default).
- 'u': Use the same staggered grid as the u wind component, - 'u': Use the same staggered grid as the u wind component,
which has a staggered west_east (x) dimension. which has a staggered west_east (x) dimension.
- 'v': Use the same staggered grid as the v wind component, - 'v': Use the same staggered grid as the v wind component,
which has a staggered south_north (y) dimension. which has a staggered south_north (y) dimension.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
_key (:obj:`int`, optional): A caching key. This is used for internal _key (:obj:`int`, optional): A caching key. This is used for internal
purposes only. Default is None. purposes only. Default is None.
Returns: Returns:
""" """
if timeidx is not None: if timeidx is not None:
if timeidx < 0: if timeidx < 0:
raise ValueError("'timeidx' must be greater than 0") raise ValueError("'timeidx' must be greater than 0")
attrs = extract_global_attrs(wrfin, attrs=("MAP_PROJ", "TRUELAT1", attrs = extract_global_attrs(wrfin, attrs=("MAP_PROJ", "TRUELAT1",
"TRUELAT2", "STAND_LON", "TRUELAT2", "STAND_LON",
"DX", "DY")) "DX", "DY"))
@ -143,9 +145,9 @@ def _get_proj_params(wrfin, timeidx, stagger, method, squeeze, cache, _key):
stdlon = attrs["STAND_LON"] stdlon = attrs["STAND_LON"]
dx = attrs["DX"] dx = attrs["DX"]
dy = attrs["DY"] dy = attrs["DY"]
if map_proj == ProjectionTypes.LAT_LON: if map_proj == ProjectionTypes.LAT_LON:
pole_attrs = extract_global_attrs(wrfin, attrs=("POLE_LAT", pole_attrs = extract_global_attrs(wrfin, attrs=("POLE_LAT",
"POLE_LON")) "POLE_LON"))
pole_lat = pole_attrs["POLE_LAT"] pole_lat = pole_attrs["POLE_LAT"]
pole_lon = pole_attrs["POLE_LON"] pole_lon = pole_attrs["POLE_LON"]
@ -156,20 +158,20 @@ def _get_proj_params(wrfin, timeidx, stagger, method, squeeze, cache, _key):
pole_lon = 0.0 pole_lon = 0.0
latinc = 0.0 latinc = 0.0
loninc = 0.0 loninc = 0.0
latvar = _lat_varname(wrfin, stagger) latvar = _lat_varname(wrfin, stagger)
lonvar = _lon_varname(wrfin, stagger) lonvar = _lon_varname(wrfin, stagger)
lat_timeidx = timeidx lat_timeidx = timeidx
is_moving = is_moving_domain(wrfin, latvar=latvar, lonvar=lonvar, is_moving = is_moving_domain(wrfin, latvar=latvar, lonvar=lonvar,
_key=_key) _key=_key)
# Only need one file and one time if the domain is not moving # Only need one file and one time if the domain is not moving
if not is_moving: if not is_moving:
# Always use the 0th time for non-moving domains to avoid problems # Always use the 0th time for non-moving domains to avoid problems
lat_timeidx = 0 lat_timeidx = 0
if is_multi_file(wrfin): if is_multi_file(wrfin):
if not is_mapping(wrfin): if not is_mapping(wrfin):
wrfin = next(iter(wrfin)) # only need one file wrfin = next(iter(wrfin)) # only need one file
@ -177,43 +179,43 @@ def _get_proj_params(wrfin, timeidx, stagger, method, squeeze, cache, _key):
first_entry = next(iter(viewkeys(wrfin))) first_entry = next(iter(viewkeys(wrfin)))
wrfin = wrfin[first_entry] wrfin = wrfin[first_entry]
key = _key[first_entry] key = _key[first_entry]
return _get_proj_params(wrfin, timeidx, stagger, return _get_proj_params(wrfin, timeidx, stagger,
method, squeeze, cache, key) method, squeeze, cache, key)
xlat = extract_vars(wrfin, lat_timeidx, (latvar,), method, squeeze, cache, xlat = extract_vars(wrfin, lat_timeidx, (latvar,), method, squeeze, cache,
meta=False, _key=_key)[latvar] meta=False, _key=_key)[latvar]
xlon = extract_vars(wrfin, lat_timeidx, (lonvar,), method, squeeze, cache, xlon = extract_vars(wrfin, lat_timeidx, (lonvar,), method, squeeze, cache,
meta=False, _key=_key)[lonvar] meta=False, _key=_key)[lonvar]
ref_lat = np.ravel(xlat[..., 0, 0]) ref_lat = np.ravel(xlat[..., 0, 0])
ref_lon = np.ravel(xlon[..., 0, 0]) ref_lon = np.ravel(xlon[..., 0, 0])
# Note: fortran index # Note: fortran index
known_x = 1.0 known_x = 1.0
known_y = 1.0 known_y = 1.0
return (map_proj, truelat1, truelat2, stdlon, ref_lat, ref_lon, return (map_proj, truelat1, truelat2, stdlon, ref_lat, ref_lon,
pole_lat, pole_lon, known_x, known_y, dx, dy, latinc, loninc) pole_lat, pole_lon, known_x, known_y, dx, dy, latinc, loninc)
# known_x and known_y are 0-based # known_x and known_y are 0-based
def _kwarg_proj_params(**projparams): def _kwarg_proj_params(**projparams):
"""Return the map projection parameters. """Return the map projection parameters.
This function aggregates the projection parameter keyword This function aggregates the projection parameter keyword
arguments and also performs sanity checking on them. arguments and also performs sanity checking on them.
Args: Args:
**projparams: Projection parameter keyword arguments. **projparams: Projection parameter keyword arguments.
Returns: Returns:
:obj:`tuple`: The map projection parameters. :obj:`tuple`: The map projection parameters.
""" """
projparams = dict_keys_to_upper(projparams) projparams = dict_keys_to_upper(projparams)
map_proj = projparams.get("MAP_PROJ") map_proj = projparams.get("MAP_PROJ")
truelat1 = projparams.get("TRUELAT1") truelat1 = projparams.get("TRUELAT1")
truelat2 = projparams.get("TRUELAT2") truelat2 = projparams.get("TRUELAT2")
@ -222,164 +224,164 @@ def _kwarg_proj_params(**projparams):
ref_lon = projparams.get("REF_LON") ref_lon = projparams.get("REF_LON")
pole_lat = projparams.get("POLE_LAT", 90.0) pole_lat = projparams.get("POLE_LAT", 90.0)
pole_lon = projparams.get("POLE_LON", 0.0) pole_lon = projparams.get("POLE_LON", 0.0)
known_x = projparams.get("KNOWN_X") # Use 0-based known_x = projparams.get("KNOWN_X") # Use 0-based
known_y = projparams.get("KNOWN_Y") # Use 0-based known_y = projparams.get("KNOWN_Y") # Use 0-based
dx = projparams.get("DX") dx = projparams.get("DX")
dy = projparams.get("DY") dy = projparams.get("DY")
latinc = projparams.get("LATINC") latinc = projparams.get("LATINC")
loninc = projparams.get("LONINC") loninc = projparams.get("LONINC")
# 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))
# ref_lat and ref_lon are expected to be lists # ref_lat and ref_lon are expected to be lists
ref_lat = np.ravel(np.asarray([ref_lat])) ref_lat = np.ravel(np.asarray([ref_lat]))
ref_lon = np.ravel(np.asarray([ref_lon])) ref_lon = np.ravel(np.asarray([ref_lon]))
# Fortran wants 1-based indexing # Fortran wants 1-based indexing
known_x = known_x + 1 known_x = known_x + 1
known_y = known_y + 1 known_y = known_y + 1
if map_proj in (ProjectionTypes.LAMBERT_CONFORMAL, if map_proj in (ProjectionTypes.LAMBERT_CONFORMAL,
ProjectionTypes.POLAR_STEREOGRAPHIC, ProjectionTypes.POLAR_STEREOGRAPHIC,
ProjectionTypes.MERCATOR): ProjectionTypes.MERCATOR):
if truelat1 is None: if truelat1 is None:
raise ValueError("'TRUELAT1' argument required") raise ValueError("'TRUELAT1' argument required")
else: else:
if truelat1 is None: if truelat1 is None:
truelat1 = 0.0 truelat1 = 0.0
# Map projection 6 (lat lon) required latinc, loninc, and dy # Map projection 6 (lat lon) required latinc, loninc, and dy
if map_proj == ProjectionTypes.LAT_LON: if map_proj == ProjectionTypes.LAT_LON:
if latinc is None: if latinc is None:
raise ValueError("'LATINC' argument required") raise ValueError("'LATINC' argument required")
if loninc is None: if loninc is None:
raise ValueError("'LONINC' argument required") raise ValueError("'LONINC' argument required")
if dy is None: if dy is None:
raise ValueError("'DY' argument required") raise ValueError("'DY' argument required")
else: else:
latinc = 0.0 latinc = 0.0
loninc = 0.0 loninc = 0.0
dy = 0.0 dy = 0.0
return (map_proj, truelat1, truelat2, stdlon, ref_lat, ref_lon, pole_lat, return (map_proj, truelat1, truelat2, stdlon, ref_lat, ref_lon, pole_lat,
pole_lon, known_x, known_y, dx, dy, latinc, loninc) pole_lon, known_x, known_y, dx, dy, latinc, loninc)
# Will return 0-based indexes # Will return 0-based indexes
def _ll_to_xy(latitude, longitude, wrfin=None, timeidx=0, def _ll_to_xy(latitude, longitude, wrfin=None, timeidx=0,
stagger=None, method="cat", squeeze=True, cache=None, stagger=None, method="cat", squeeze=True, cache=None,
_key=None, as_int=True, **projparams): _key=None, as_int=True, **projparams):
"""Return the x,y coordinates for a specified latitude and longitude. """Return the x,y coordinates for a specified latitude and longitude.
The *latitude* and *longitude* arguments can be a single value or a The *latitude* and *longitude* arguments can be a single value or a
sequence of values. sequence of values.
The leftmost dimension of the returned array represents two different The leftmost dimension of the returned array represents two different
quantities: quantities:
- return_val[0,...] will contain the X (west_east) values. - return_val[0,...] will contain the X (west_east) values.
- return_val[1,...] will contain the Y (south_north) values. - return_val[1,...] will contain the Y (south_north) values.
Args: Args:
latitude (:obj:`float` or sequence): A single latitude or a sequence latitude (:obj:`float` or sequence): A single latitude or a sequence
of latitude values to be converted. of latitude values to be converted.
longitude (:obj:`float` or sequence): A single longitude or a sequence longitude (:obj:`float` or sequence): A single longitude or a sequence
of latitude values to be converted. of latitude values to be converted.
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. The default is 0. all times in the file or sequence. The default is 0.
stagger (:obj:`str`): By default, the latitude and longitude are stagger (:obj:`str`): By default, the latitude and longitude are
returned on the mass grid, but a staggered grid can be chosen returned on the mass grid, but a staggered grid can be chosen
with the following options: with the following options:
- 'm': Use the mass grid (default). - 'm': Use the mass grid (default).
- 'u': Use the same staggered grid as the u wind component, - 'u': Use the same staggered grid as the u wind component,
which has a staggered west_east (x) dimension. which has a staggered west_east (x) dimension.
- 'v': Use the same staggered grid as the v wind component, - 'v': Use the same staggered grid as the v wind component,
which has a staggered south_north (y) dimension. which has a staggered south_north (y) dimension.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
_key (:obj:`int`, optional): A caching key. This is used for internal _key (:obj:`int`, optional): A caching key. This is used for internal
purposes only. Default is None. purposes only. Default is None.
as_int (:obj:`bool`): Set to True to return the x,y values as as_int (:obj:`bool`): Set to True to return the x,y values as
:obj:`int`, otherwise they will be returned as :obj:`float`. :obj:`int`, otherwise they will be returned as :obj:`float`.
**projparams: Map projection keyword arguments to set manually. **projparams: Map projection keyword arguments to set manually.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The :class:`xarray.DataArray` or :class:`numpy.ndarray`: The
x,y coordinate value(s) whose leftmost dimension is 2 (0=X, 1=Y). x,y coordinate value(s) whose leftmost dimension is 2 (0=X, 1=Y).
If xarray is enabled and the *meta* parameter is True, then the result If xarray is enabled and the *meta* parameter is True, then the result
will be a :class:`xarray.DataArray` object. Otherwise, the result will will be a :class:`xarray.DataArray` object. Otherwise, the result will
be a :class:`numpy.ndarray` object with no metadata. be a :class:`numpy.ndarray` object with no metadata.
""" """
if wrfin is not None: if wrfin is not None:
(map_proj, truelat1, truelat2, stdlon, ref_lat, ref_lon, (map_proj, truelat1, truelat2, stdlon, ref_lat, ref_lon,
pole_lat, pole_lon, known_x, known_y, dx, dy, latinc, pole_lat, pole_lon, known_x, known_y, dx, dy, latinc,
loninc) = _get_proj_params(wrfin, timeidx, stagger, method, squeeze, loninc) = _get_proj_params(wrfin, timeidx, stagger, method, squeeze,
cache, _key) cache, _key)
else: else:
(map_proj, truelat1, truelat2, stdlon, ref_lat, ref_lon, (map_proj, truelat1, truelat2, stdlon, ref_lat, ref_lon,
pole_lat, pole_lon, known_x, known_y, dx, dy, latinc, pole_lat, pole_lon, known_x, known_y, dx, dy, latinc,
loninc) = _kwarg_proj_params(**projparams) loninc) = _kwarg_proj_params(**projparams)
if isinstance(latitude, Iterable): if isinstance(latitude, Iterable):
lats = np.asarray(latitude) lats = np.asarray(latitude)
lons = np.asarray(longitude) lons = np.asarray(longitude)
# Note: For scalars, this will make a single element array # Note: For scalars, this will make a single element array
lats = lats.ravel() lats = lats.ravel()
lons = lons.ravel() lons = lons.ravel()
if (lats.size != lons.size): if (lats.size != lons.size):
raise ValueError("'latitude' and 'longitude' " raise ValueError("'latitude' and 'longitude' "
"must be the same length") "must be the same length")
if ref_lat.size == 1: if ref_lat.size == 1:
outdim = [2, lats.size] outdim = [2, lats.size]
extra_dims = [outdim[1]] extra_dims = [outdim[1]]
@ -387,11 +389,10 @@ def _ll_to_xy(latitude, longitude, wrfin=None, timeidx=0,
# Moving domain will have moving ref_lats/ref_lons # Moving domain will have moving ref_lats/ref_lons
outdim = [2, ref_lat.size, lats.size] outdim = [2, ref_lat.size, lats.size]
extra_dims = outdim[1:] extra_dims = outdim[1:]
result = np.empty(outdim, np.float64) result = np.empty(outdim, np.float64)
for left_idxs in iter_left_indexes(extra_dims): for left_idxs in iter_left_indexes(extra_dims):
#left_and_slice_idxs = left_idxs + (slice(None), )
# Left 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
@ -401,149 +402,148 @@ def _ll_to_xy(latitude, longitude, wrfin=None, timeidx=0,
else: else:
ref_lat_val = ref_lat[left_idxs[-2]] ref_lat_val = ref_lat[left_idxs[-2]]
ref_lon_val = ref_lon[left_idxs[-2]] ref_lon_val = ref_lon[left_idxs[-2]]
lat = lats[left_idxs[-1]] lat = lats[left_idxs[-1]]
lon = lons[left_idxs[-1]] lon = lons[left_idxs[-1]]
xy = _lltoxy(map_proj, truelat1, truelat2, stdlon, xy = _lltoxy(map_proj, truelat1, truelat2, stdlon,
ref_lat_val, ref_lon_val, pole_lat, pole_lon, ref_lat_val, ref_lon_val, pole_lat, pole_lon,
known_x, known_y, dx, dy, latinc, loninc, known_x, known_y, dx, dy, latinc, loninc,
lat, lon) lat, lon)
# Note: comes back from fortran as y,x # Note: comes back from fortran as y,x
result[x_idxs] = xy[1] result[x_idxs] = xy[1]
result[y_idxs] = xy[0] result[y_idxs] = xy[0]
else: else:
result = np.empty((2,), np.float64) result = np.empty((2,), np.float64)
fort_out = _lltoxy(map_proj, truelat1, truelat2, stdlon, fort_out = _lltoxy(map_proj, truelat1, truelat2, stdlon,
ref_lat, ref_lon, pole_lat, pole_lon, ref_lat, ref_lon, pole_lat, pole_lon,
known_x, known_y, dx, dy, latinc, loninc, known_x, known_y, dx, dy, latinc, loninc,
latitude, longitude) latitude, longitude)
# Note, comes back from fortran as y,x. So, need to swap them. # Note, comes back from fortran as y,x. So, need to swap them.
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
if as_int: if as_int:
result = np.rint(result).astype(int) result = np.rint(result).astype(int)
return result return result
# X and Y should be 0-based # X and Y should be 0-based
def _xy_to_ll(x, y, 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,
**projparams): **projparams):
"""Return the latitude and longitude for specified x,y coordinates. """Return the latitude and longitude for specified x,y coordinates.
The *x* and *y* arguments can be a single value or a sequence of values. The *x* and *y* arguments can be a single value or a sequence of values.
The leftmost dimension of the returned array represents two different The leftmost dimension of the returned array represents two different
quantities: quantities:
- return_val[0,...] will contain the latitude values. - return_val[0,...] will contain the latitude values.
- return_val[1,...] will contain the longitude values. - return_val[1,...] will contain the longitude values.
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
x (:obj:`float` or sequence): A single x-coordinate or a sequence x (:obj:`float` or sequence): A single x-coordinate or a sequence
of x-coordinate values to be converted. of x-coordinate values to be converted.
y (:obj:`float` or sequence): A single y-coordinate or a sequence y (:obj:`float` or sequence): A single y-coordinate or a sequence
of y-coordinate values to be converted. of y-coordinate values to be converted.
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. The default is 0. all times in the file or sequence. The default is 0.
stagger (:obj:`str`): By default, the latitude and longitude are stagger (:obj:`str`): By default, the latitude and longitude are
returned on the mass grid, but a staggered grid can be chosen returned on the mass grid, but a staggered grid can be chosen
with the following options: with the following options:
- 'm': Use the mass grid (default). - 'm': Use the mass grid (default).
- 'u': Use the same staggered grid as the u wind component, - 'u': Use the same staggered grid as the u wind component,
which has a staggered west_east (x) dimension. which has a staggered west_east (x) dimension.
- 'v': Use the same staggered grid as the v wind component, - 'v': Use the same staggered grid as the v wind component,
which has a staggered south_north (y) dimension. which has a staggered south_north (y) dimension.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
_key (:obj:`int`, optional): A caching key. This is used for internal _key (:obj:`int`, optional): A caching key. This is used for internal
purposes only. Default is None. purposes only. Default is None.
**projparams: Map projection keyword arguments to set manually. **projparams: Map projection keyword arguments to set manually.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The :class:`xarray.DataArray` or :class:`numpy.ndarray`: The
latitude and longitude values whose leftmost dimension is 2 latitude and longitude values whose leftmost dimension is 2
(0=latitude, 1=longitude). (0=latitude, 1=longitude).
If xarray is enabled and the *meta* parameter is True, then the result If xarray is enabled and the *meta* parameter is True, then the result
will be a :class:`xarray.DataArray` object. Otherwise, the result will will be a :class:`xarray.DataArray` object. Otherwise, the result will
be a :class:`numpy.ndarray` object with no metadata. be a :class:`numpy.ndarray` object with no metadata.
""" """
if wrfin is not None: if wrfin is not None:
(map_proj, truelat1, truelat2, stdlon, ref_lat, ref_lon, (map_proj, truelat1, truelat2, stdlon, ref_lat, ref_lon,
pole_lat, pole_lon, known_x, known_y, dx, dy, latinc, pole_lat, pole_lon, known_x, known_y, dx, dy, latinc,
loninc) = _get_proj_params(wrfin, timeidx, stagger, method, squeeze, loninc) = _get_proj_params(wrfin, timeidx, stagger, method, squeeze,
cache, _key) cache, _key)
else: else:
(map_proj, truelat1, truelat2, stdlon, ref_lat, ref_lon, (map_proj, truelat1, truelat2, stdlon, ref_lat, ref_lon,
pole_lat, pole_lon, known_x, known_y, dx, dy, latinc, pole_lat, pole_lon, known_x, known_y, dx, dy, latinc,
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)
# Convert 0-based x, y to 1-based # Convert 0-based x, y to 1-based
x_arr = x_arr + 1 x_arr = x_arr + 1
y_arr = y_arr + 1 y_arr = y_arr + 1
x_arr = x_arr.ravel() x_arr = x_arr.ravel()
y_arr = y_arr.ravel() y_arr = y_arr.ravel()
if (x_arr.size != y_arr.size): if (x_arr.size != y_arr.size):
raise ValueError("'x' and 'y' must be the same length") raise ValueError("'x' and 'y' must be the same length")
if ref_lat.size == 1: if ref_lat.size == 1:
outdim = [2, x_arr.size] outdim = [2, x_arr.size]
extra_dims = [outdim[1]] extra_dims = [outdim[1]]
@ -551,44 +551,37 @@ def _xy_to_ll(x, y, wrfin=None, timeidx=0, stagger=None,
# Moving domain will have moving ref_lats/ref_lons # Moving domain will have moving ref_lats/ref_lons
outdim = [2, ref_lat.size, x_arr.size] outdim = [2, ref_lat.size, x_arr.size]
extra_dims = outdim[1:] extra_dims = outdim[1:]
result = np.empty(outdim, np.float64) result = np.empty(outdim, np.float64)
for left_idxs in iter_left_indexes(extra_dims): for left_idxs in iter_left_indexes(extra_dims):
#left_and_slice_idxs = left_idxs + (slice(None), )
lat_idxs = (0,) + left_idxs lat_idxs = (0,) + left_idxs
lon_idxs = (1,) + left_idxs lon_idxs = (1,) + left_idxs
if ref_lat.size == 1: if ref_lat.size == 1:
ref_lat_val = ref_lat[0] ref_lat_val = ref_lat[0]
ref_lon_val = ref_lon[0] ref_lon_val = ref_lon[0]
else: else:
ref_lat_val = ref_lat[left_idxs[-2]] ref_lat_val = ref_lat[left_idxs[-2]]
ref_lon_val = ref_lon[left_idxs[-2]] ref_lon_val = ref_lon[left_idxs[-2]]
x_val = x_arr[left_idxs[-1]] x_val = x_arr[left_idxs[-1]]
y_val = y_arr[left_idxs[-1]] y_val = y_arr[left_idxs[-1]]
ll = _xytoll(map_proj, truelat1, truelat2, stdlon, ref_lat_val, ll = _xytoll(map_proj, truelat1, truelat2, stdlon, ref_lat_val,
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]
else: else:
# Convert 0-based to 1-based for Fortran # Convert 0-based to 1-based for Fortran
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,
pole_lat, pole_lon, known_x, known_y, dx, dy, latinc,
loninc, x_val, y_val)
return result
result = _xytoll(map_proj, truelat1, truelat2, stdlon, ref_lat,
ref_lon, pole_lat, pole_lon, known_x, known_y,
dx, dy, latinc, loninc, x_val, y_val)
return result

1841
src/wrf/metadecorators.py

File diff suppressed because it is too large Load Diff

988
src/wrf/projection.py

File diff suppressed because it is too large Load Diff

13
src/wrf/projutils.py

@ -2,16 +2,17 @@ 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.
Args: Args:
d (:obj:`dict`): A dictionary. d (:obj:`dict`): A dictionary.
Returns: Returns:
: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)}

118
src/wrf/py3compat.py

@ -3,18 +3,19 @@ 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.
Args: Args:
d (:obj:`dict`): A dictionary. d (:obj:`dict`): A dictionary.
Returns: Returns:
view method: Either the items or viewitems method. view method: Either the items or viewitems method.
""" """
func = getattr(d, "viewitems", None) func = getattr(d, "viewitems", None)
if func is None: if func is None:
@ -24,15 +25,15 @@ def viewitems(d):
def viewkeys(d): def viewkeys(d):
"""Return either the keys or viewkeys method for a dictionary. """Return either the keys or viewkeys method for a dictionary.
Args: Args:
d (:obj:`dict`): A dictionary. d (:obj:`dict`): A dictionary.
Returns: Returns:
view method: Either the keys or viewkeys method. view method: Either the keys or viewkeys method.
""" """
func = getattr(d, "viewkeys", None) func = getattr(d, "viewkeys", None)
if func is None: if func is None:
@ -42,32 +43,33 @@ def viewkeys(d):
def viewvalues(d): def viewvalues(d):
"""Return either the values or viewvalues method for a dictionary. """Return either the values or viewvalues method for a dictionary.
Args: Args:
d (:obj:`dict`): A dictionary. d (:obj:`dict`): A dictionary.
Returns: Returns:
view method: Either the values or viewvalues method. view method: Either the values or viewvalues method.
""" """
func = getattr(d, "viewvalues", None) func = getattr(d, "viewvalues", None)
if func is None: if func is None:
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.
Args: Args:
s (string): A string (str, unicode, bytes). s (string): A string (str, unicode, bytes).
Returns: Returns:
:obj:`bool`: True if the object is a type of string. Otherwise, False. :obj:`bool`: True if the object is a type of string. Otherwise, False.
""" """
try: try:
return isinstance(s, basestring) return isinstance(s, basestring)
@ -75,25 +77,25 @@ def isstr(s):
return isinstance(s, str) return isinstance(s, str)
# Python 2 rounding behavior # Python 2 rounding behavior
def _round2(x, d=None): def _round2(x, d=None):
"""Return the result of Python 2.x rounding, which is to round the number """Return the result of Python 2.x rounding, which is to round the number
to the nearest integer. to the nearest integer.
Python 3.x uses banker's rounding, which is not applicable for nearest Python 3.x uses banker's rounding, which is not applicable for nearest
neighbor approaches with grid boxes. neighbor approaches with grid boxes.
Args: Args:
x (:obj:`float`): A number, usually a float. x (:obj:`float`): A number, usually a float.
d (:obj:`int`, optional): The number of digits. Default is None, d (:obj:`int`, optional): The number of digits. Default is None,
which indicates the nearest integer. which indicates the nearest integer.
Returns: Returns:
:obj:`float`: The rounded number. :obj:`float`: The rounded number.
""" """
d = 0 if d is None else d d = 0 if d is None else d
p = 10 ** d p = 10 ** d
@ -101,69 +103,67 @@ def _round2(x, d=None):
def py2round(x, d=None): def py2round(x, d=None):
"""Return the result of Python 2.x rounding, which is to round the number """Return the result of Python 2.x rounding, which is to round the number
to the nearest integer. to the nearest integer.
Python 3.x uses banker's rounding, which is not applicable for nearest Python 3.x uses banker's rounding, which is not applicable for nearest
neighbor approaches with grid boxes. neighbor approaches with grid boxes.
Args: Args:
x (:obj:`float`): A number, usually a float. x (:obj:`float`): A number, usually a float.
d (:obj:`int`, optional): The number of digits. Default is None, d (:obj:`int`, optional): The number of digits. Default is None,
which indicates the nearest integer. which indicates the nearest integer.
Returns: Returns:
:obj:`float`: The rounded number. :obj:`float`: The rounded number.
""" """
if version_info >= (3,): if version_info >= (3,):
return _round2(x, d) return _round2(x, d)
return round(x, d) return round(x, d)
def py3range(*args): def py3range(*args):
"""Return the equivalent of the range function in Python 3.x. """Return the equivalent of the range function in Python 3.x.
For Python 2.x, this is the same as the xrange function. For Python 2.x, this is the same as the xrange function.
Args: Args:
*args: The function arguments for range or xrange. *args: The function arguments for range or xrange.
Returns: Returns:
iterable: An iterable sequence. iterable: An iterable sequence.
""" """
if version_info >= (3,): if version_info >= (3,):
return range(*args) return range(*args)
return xrange(*args) return xrange(*args)
def ucode(*args, **kwargs): def ucode(*args, **kwargs):
"""Return a Python 3.x unicode string. """Return a Python 3.x unicode string.
For Python 2.x, this is accomplished by using the unicode function. For Python 2.x, this is accomplished by using the unicode function.
Args: Args:
*args: The function positional arguments for str or unicode. *args: The function positional arguments for str or unicode.
**kwargs: The function keyword arguments for str or unicode. **kwargs: The function keyword arguments for str or unicode.
Returns: Returns:
string: A unicode string. string: A unicode string.
""" """
if version_info >= (3, ): if version_info >= (3, ):
return str(*args, **kwargs) return str(*args, **kwargs)
return unicode(*args, **kwargs)
return unicode(*args, **kwargs)

489
src/wrf/routines.py

@ -2,8 +2,8 @@ from __future__ import (absolute_import, division, print_function)
from .util import (get_iterable, is_standard_wrf_var, extract_vars, viewkeys, from .util import (get_iterable, is_standard_wrf_var, extract_vars, viewkeys,
get_id) get_id)
from .g_cape import (get_2dcape, get_3dcape, get_cape2d_only, from .g_cape import (get_2dcape, get_3dcape, get_cape2d_only,
get_cin2d_only, get_lcl, get_lfc, get_3dcape_only, get_cin2d_only, get_lcl, get_lfc, get_3dcape_only,
get_3dcin_only) get_3dcin_only)
from .g_ctt import get_ctt from .g_ctt import get_ctt
from .g_dbz import get_dbz, get_max_dbz from .g_dbz import get_dbz, get_max_dbz
@ -16,334 +16,337 @@ 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,
get_uvmet10_wspd, get_uvmet10_wdir) get_uvmet10_wspd, get_uvmet10_wdir)
from .g_vorticity import get_avo, get_pvo from .g_vorticity import get_avo, get_pvo
from .g_wind import (get_destag_wspd_wdir, get_destag_wspd_wdir10, from .g_wind import (get_destag_wspd_wdir, get_destag_wspd_wdir10,
get_u_destag, get_v_destag, get_w_destag, get_u_destag, get_v_destag, get_w_destag,
get_destag_wspd, get_destag_wdir, get_destag_wspd10, get_destag_wspd, get_destag_wdir, get_destag_wspd10,
get_destag_wdir10) get_destag_wdir10)
from .g_times import get_times, get_xtimes from .g_times import get_times, get_xtimes
from .g_cloudfrac import (get_cloudfrac, get_low_cloudfrac, get_mid_cloudfrac, from .g_cloudfrac import (get_cloudfrac, get_low_cloudfrac, get_mid_cloudfrac,
get_high_cloudfrac) get_high_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
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:
return alias return 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]:
if var != "default": if var != "default":
raise ValueError("'{}' is an invalid keyword " raise ValueError("'{}' is an invalid keyword "
"argument for '{}'".format(arg, var)) "argument for '{}'".format(arg, var))
else: else:
raise ValueError("'{}' is an invalid keyword " raise ValueError("'{}' is an invalid keyword "
"argument".format(arg)) "argument".format(arg))
def getvar(wrfin, varname, timeidx=0, def getvar(wrfin, varname, timeidx=0,
method="cat", squeeze=True, cache=None, meta=True, method="cat", squeeze=True, cache=None, meta=True,
**kwargs): **kwargs):
"""Returns basic diagnostics from the WRF ARW model output. """Returns basic diagnostics from the WRF ARW model output.
A table of all available diagnostics is below. A table of all available diagnostics is below.
.. include:: ../../_templates/product_table.txt .. include:: ../../_templates/product_table.txt
Args: Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable): WRF-ARW NetCDF iterable): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. or an iterable sequence of the aforementioned types.
varname (:obj:`str`) : The variable name. varname (:obj:`str`) : The variable name.
timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The
desired time index. This value can be a positive integer, desired time index. This value can be a positive integer,
negative integer, or negative integer, or
:data:`wrf.ALL_TIMES` (an alias for None) to return :data:`wrf.ALL_TIMES` (an alias for None) to return
all times in the file or sequence. The default is 0. all times in the file or sequence. The default is 0.
method (:obj:`str`, optional): The aggregation method to use for method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'. sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension. 'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index. 'join' creates a new dimension for the file index.
The default is 'cat'. The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape with a size of 1 from being automatically removed from the shape
of the output. Default is True. of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using used in multiple diagnostics calculations, particularly when using
large sequences of files. large sequences of files.
Default is None. Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata and meta (:obj:`bool`, optional): Set to False to disable metadata and
return :class:`numpy.ndarray` instead of return :class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is True. :class:`xarray.DataArray`. Default is True.
**kwargs: Optional keyword arguments for certain diagnostics. **kwargs: Optional keyword arguments for certain diagnostics.
See table above. See table above.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: If xarray is :class:`xarray.DataArray` or :class:`numpy.ndarray`: If xarray is
enabled and the *meta* parameter is True, then the result will be a enabled and the *meta* parameter is True, then the result will be a
:class:`xarray.DataArray` object. Otherwise, the result will be a :class:`xarray.DataArray` object. Otherwise, the result will be a
:class:`numpy.ndarray` object with no metadata. :class:`numpy.ndarray` object with no metadata.
Raises: Raises:
:class:`ValueError`: Raised when an invalid diagnostic type or :class:`ValueError`: Raised when an invalid diagnostic type or
keyword argument is passed to the routine. keyword argument is passed to the routine.
:class:`FortranError`: Raised when a problem occurs during a Fortran :class:`FortranError`: Raised when a problem occurs during a Fortran
calculation. calculation.
See Also: See Also:
:class:`numpy.ndarray`, :class:`xarray.DataArray` :class:`numpy.ndarray`, :class:`xarray.DataArray`
Examples: Examples:
Using netCDF4 Using netCDF4
.. code-block:: python .. code-block:: python
from netCDF4 import Dataset from netCDF4 import Dataset
from wrf import getvar from wrf import getvar
wrfnc = Dataset("wrfout_d02_2010-06-13_21:00:00") wrfnc = Dataset("wrfout_d02_2010-06-13_21:00:00")
slp = getvar(wrfnc, "slp") slp = getvar(wrfnc, "slp")
Using PyNIO Using PyNIO
.. code-block:: python .. code-block:: python
from Nio import open_file from Nio import open_file
from wrf import getvar from wrf import getvar
wrfnc = open_file("wrfout_d02_2010-06-13_21:00:00"+".nc", "r") wrfnc = open_file("wrfout_d02_2010-06-13_21:00:00"+".nc", "r")
slp = getvar(wrfnc, "slp") slp = getvar(wrfnc, "slp")
Using Iterables: Using Iterables:
.. code-block:: python .. code-block:: python
import os import os
from netCDF4 import Dataset from netCDF4 import Dataset
from wrf import getvar from wrf import getvar
filedir = "/path/to/wrf/files" filedir = "/path/to/wrf/files"
wrfin = [Dataset(f) for f in os.listdir(filedir) wrfin = [Dataset(f) for f in os.listdir(filedir)
if f.startswith("wrfout_d02_")] if f.startswith("wrfout_d02_")]
uvmet = getvar(wrfin, "uvmet", timeidx=3, units="kt") uvmet = getvar(wrfin, "uvmet", timeidx=3, units="kt")
""" """
_key = get_id(wrfin) _key = get_id(wrfin)
wrfin = get_iterable(wrfin) wrfin = get_iterable(wrfin)
if is_standard_wrf_var(wrfin, varname) and varname != "Times": if is_standard_wrf_var(wrfin, varname) and varname != "Times":
_check_kargs("default", kwargs) _check_kargs("default", kwargs)
return extract_vars(wrfin, timeidx, varname, return extract_vars(wrfin, timeidx, varname,
method, squeeze, cache, meta, _key)[varname] method, squeeze, cache, meta, _key)[varname]
elif varname == "Times": elif varname == "Times":
varname = "times" # Diverting to the get_times routine varname = "times" # Diverting to the get_times routine
actual_var = _undo_alias(varname) actual_var = _undo_alias(varname)
if actual_var not in _VALID_KARGS: if actual_var not in _VALID_KARGS:
raise 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)

463
src/wrf/specialdec.py

@ -2,7 +2,7 @@ from __future__ import (absolute_import, division, print_function)
import numpy as np import numpy as np
import wrapt import wrapt
from .util import iter_left_indexes, to_np from .util import iter_left_indexes, to_np
from .py3compat import py3range from .py3compat import py3range
@ -14,28 +14,28 @@ if xarray_enabled():
def uvmet_left_iter(alg_dtype=np.float64): def uvmet_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
uvmet diagnostic. uvmet diagnostic.
For example, if a wrapped function works with three-dimensional arrays, but For example, if a wrapped function works with three-dimensional arrays, but
the variables include a 4th leftmost dimension for 'Time', this decorator the variables include a 4th leftmost dimension for 'Time', this decorator
will iterate over all times, call the 3D Fortran routine, and aggregate the will iterate over all times, call the 3D Fortran routine, and aggregate the
results in to a 4D output array. results in to a 4D output array.
It is also important to note that the final output array is allocated It is also important to note that the final output array is allocated
first, and then views are passed to the wrapped function so that values first, and then views are passed to the wrapped function so that values
do not need to get copied in to the final output array. do not need to get copied in to the final output array.
Args: Args:
alg_dtype (:class:`np.dtype` or :obj:`str`): The numpy data type used alg_dtype (:class:`np.dtype` or :obj:`str`): The numpy data type used
in the wrapped function. in the wrapped function.
Returns: Returns:
:class:`numpy.ndarray`: The aggregated uvmet output array that includes :class:`numpy.ndarray`: The aggregated uvmet output array that includes
all extra leftmost dimensions. all extra leftmost dimensions.
""" """
@wrapt.decorator @wrapt.decorator
def func_wrapper(wrapped, instance, args, kwargs): def func_wrapper(wrapped, instance, args, kwargs):
@ -43,64 +43,62 @@ def uvmet_left_iter(alg_dtype=np.float64):
v = args[1] v = args[1]
lat = args[2] lat = args[2]
lon = args[3] lon = args[3]
cen_long = args[4] cen_long = args[4]
cone = args[5] cone = args[5]
orig_dtype = u.dtype orig_dtype = u.dtype
lat_lon_fixed = False lat_lon_fixed = False
if lat.ndim == 2: if lat.ndim == 2:
lat_lon_fixed = True lat_lon_fixed = True
if lon.ndim == 2 and not lat_lon_fixed: if lon.ndim == 2 and not lat_lon_fixed:
raise ValueError("'lat' and 'lon' shape mismatch") raise ValueError("'lat' and 'lon' shape mismatch")
num_left_dims_u = u.ndim - 2 num_left_dims_u = u.ndim - 2
num_left_dims_lat = lat.ndim - 2 num_left_dims_lat = lat.ndim - 2
if (num_left_dims_lat > num_left_dims_u): if (num_left_dims_lat > num_left_dims_u):
raise ValueError("number of 'lat' dimensions is greater than 'u'") raise ValueError("number of 'lat' dimensions is greater than 'u'")
if lat_lon_fixed: if lat_lon_fixed:
mode = 0 # fixed lat/lon mode = 0 # fixed lat/lon
else: else:
if num_left_dims_u == num_left_dims_lat: if num_left_dims_u == num_left_dims_lat:
mode = 1 # lat/lon same as u mode = 1 # lat/lon same as u
else: else:
mode = 2 # probably 3D with 2D lat/lon plus Time mode = 2 # probably 3D with 2D lat/lon plus Time
has_missing = False has_missing = False
u_arr = to_np(u) u_arr = to_np(u)
v_arr = to_np(v) v_arr = to_np(v)
umissing = default_fill(np.float64) umissing = default_fill(np.float64)
if isinstance(u_arr, np.ma.MaskedArray): if isinstance(u_arr, np.ma.MaskedArray):
has_missing = True has_missing = True
umissing = u_arr.fill_value umissing = u_arr.fill_value
vmissing = default_fill(np.float64) vmissing = default_fill(np.float64)
if isinstance(v_arr, np.ma.MaskedArray): if isinstance(v_arr, np.ma.MaskedArray):
has_missing = True has_missing = True
vmissing = v_arr.fill_value vmissing = v_arr.fill_value
uvmetmissing = umissing uvmetmissing = umissing
is_stag = 0 is_stag = 0
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]):
is_stag = 1 is_stag = 1
# Sanity check # Sanity check
if (v.shape[-1] == lat.shape[-1] or v.shape[-2] == lat.shape[-2]): if (v.shape[-1] == lat.shape[-1] or v.shape[-2] == lat.shape[-2]):
raise ValueError("u is staggered but v is not") raise ValueError("u is staggered but v is not")
if (v.shape[-1] != lat.shape[-1] or v.shape[-2] != lat.shape[-2]): if (v.shape[-1] != lat.shape[-1] or v.shape[-2] != lat.shape[-2]):
is_stag = 1 is_stag = 1
# Sanity check # Sanity check
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,
@ -109,44 +107,43 @@ def uvmet_left_iter(alg_dtype=np.float64):
# Initial output is time,nz,2,ny,nx to create contiguous views # Initial output is time,nz,2,ny,nx to create contiguous views
outdims = u.shape[0:num_left_dims_u] outdims = u.shape[0:num_left_dims_u]
extra_dims = tuple(outdims) # Copy the left-most dims for iteration extra_dims = tuple(outdims) # Copy the left-most dims for iteration
outdims += (2,) outdims += (2,)
outdims += lat.shape[-2:] outdims += lat.shape[-2:]
outview_array = np.empty(outdims, alg_dtype) outview_array = np.empty(outdims, alg_dtype)
# Final Output moves the u_v dimension to left side # Final Output moves the u_v dimension to left side
output_dims = (2,) output_dims = (2,)
output_dims += extra_dims output_dims += extra_dims
output_dims += lat.shape[-2:] output_dims += lat.shape[-2:]
output = np.empty(output_dims, orig_dtype) output = np.empty(output_dims, orig_dtype)
for left_idxs in iter_left_indexes(extra_dims): for left_idxs in iter_left_indexes(extra_dims):
left_and_slice_idxs = left_idxs + (slice(None),) left_and_slice_idxs = left_idxs + (slice(None),)
if mode == 0: if mode == 0:
lat_left_and_slice = (slice(None),) lat_left_and_slice = (slice(None),)
elif mode == 1: elif mode == 1:
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]
new_lon = lon[lat_left_and_slice] new_lon = lon[lat_left_and_slice]
outview = outview_array[left_and_slice_idxs] outview = outview_array[left_and_slice_idxs]
# 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
for arg in (new_u, new_v, new_lat, new_lon): for arg in (new_u, new_v, new_lat, new_lon):
@ -154,70 +151,69 @@ def uvmet_left_iter(alg_dtype=np.float64):
if arg.mask.all(): if arg.mask.all():
output[u_output_idxs] = uvmetmissing output[u_output_idxs] = uvmetmissing
output[v_output_idxs] = uvmetmissing output[v_output_idxs] = uvmetmissing
skip_missing = True skip_missing = True
has_missing = True has_missing = True
if skip_missing: if skip_missing:
continue continue
# Call the numerical routine # Call the numerical routine
result = wrapped(new_u, new_v, new_lat, new_lon, cen_long, cone, result = wrapped(new_u, new_v, new_lat, new_lon, cen_long, cone,
isstag=is_stag, has_missing=has_missing, isstag=is_stag, has_missing=has_missing,
umissing=umissing, vmissing=vmissing, umissing=umissing, vmissing=vmissing,
uvmetmissing=uvmetmissing, outview=outview) uvmetmissing=uvmetmissing, outview=outview)
# Make sure the result is the same data as what got passed in # Make sure the result is the same data as what got passed in
# Can delete this once everything works # Can delete this once everything works
if (result.__array_interface__["data"][0] != if (result.__array_interface__["data"][0] !=
outview.__array_interface__["data"][0]): outview.__array_interface__["data"][0]):
raise RuntimeError("output array was copied") raise RuntimeError("output array was copied")
output[u_output_idxs] = ( output[u_output_idxs] = (
outview_array[u_view_idxs].astype(orig_dtype)) outview_array[u_view_idxs].astype(orig_dtype))
output[v_output_idxs] = ( output[v_output_idxs] = (
outview_array[v_view_idxs].astype(orig_dtype)) outview_array[v_view_idxs].astype(orig_dtype))
if has_missing: if has_missing:
output = np.ma.masked_values(output, uvmetmissing) output = np.ma.masked_values(output, uvmetmissing)
return output return output
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.
For example, if a wrapped function works with three-dimensional arrays, but For example, if a wrapped function works with three-dimensional arrays, but
the variables include a 4th leftmost dimension for 'Time', this decorator the variables include a 4th leftmost dimension for 'Time', this decorator
will iterate over all times, call the 3D Fortran routine, and aggregate the will iterate over all times, call the 3D Fortran routine, and aggregate the
results in to a 4D output array. results in to a 4D output array.
It is also important to note that the final output array is allocated It is also important to note that the final output array is allocated
first, and then views are passed to the wrapped function so that values first, and then views are passed to the wrapped function so that values
do not need to get copied in to the final output array. do not need to get copied in to the final output array.
Args: Args:
alg_dtype (:class:`np.dtype` or :obj:`str`): The numpy data type used alg_dtype (:class:`np.dtype` or :obj:`str`): The numpy data type used
in the wrapped function. in the wrapped function.
Returns: Returns:
:class:`numpy.ndarray`: The aggregated cape output array that includes :class:`numpy.ndarray`: The aggregated cape output array that includes
all extra leftmost dimensions. all extra leftmost dimensions.
""" """
@wrapt.decorator @wrapt.decorator
def func_wrapper(wrapped, instance, args, kwargs): def func_wrapper(wrapped, instance, args, kwargs):
# The cape calculations use an ascending vertical pressure coordinate # The cape calculations use an ascending vertical pressure coordinate
new_args = list(args) new_args = list(args)
new_kwargs = dict(kwargs) new_kwargs = dict(kwargs)
p_hpa = args[0] p_hpa = args[0]
tk = args[1] tk = args[1]
qv = args[2] qv = args[2]
@ -227,19 +223,19 @@ def cape_left_iter(alg_dtype=np.float64):
missing = args[6] missing = args[6]
i3dflag = args[7] i3dflag = args[7]
ter_follow = args[8] ter_follow = args[8]
is2d = i3dflag == 0 is2d = i3dflag == 0
# Note: This should still work with DataArrays # Note: This should still work with DataArrays
is1d = np.isscalar(sfp) or np.size(sfp) == 1 is1d = np.isscalar(sfp) or np.size(sfp) == 1
# Make sure sfp and terrain are regular floats for 1D case # Make sure sfp and terrain are regular floats for 1D case
# This should also work with DataArrays # This should also work with DataArrays
if is1d: if is1d:
ter = float(ter) ter = float(ter)
sfp = float(sfp) sfp = float(sfp)
orig_dtype = p_hpa.dtype orig_dtype = p_hpa.dtype
if not is1d: if not is1d:
# Need to order in ascending pressure order # Need to order in ascending pressure order
flip = False flip = False
@ -247,48 +243,48 @@ def cape_left_iter(alg_dtype=np.float64):
top_idxs = list(bot_idxs) top_idxs = list(bot_idxs)
top_idxs[-3] = -1 top_idxs[-3] = -1
top_idxs = tuple(top_idxs) top_idxs = tuple(top_idxs)
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
new_args[3] = ht new_args[3] = ht
num_left_dims = p_hpa.ndim - 3 num_left_dims = p_hpa.ndim - 3
else: else:
# Need to order in ascending pressure order # Need to order in ascending pressure order
flip = False flip = False
if p_hpa[0] > p_hpa[-1]: if p_hpa[0] > p_hpa[-1]:
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])
# Need to make 3D views for the fortran code. # Need to make 3D views for the fortran code.
# Going to make these fortran ordered, since the f_contiguous and # Going to make these fortran ordered, since the f_contiguous and
# c_contiguous flags are broken in numpy 1.11 (always false). This # c_contiguous flags are broken in numpy 1.11 (always false). This
# should work across all numpy versions. # should work across all numpy versions.
new_args[0] = p_hpa.reshape((1, 1, p_hpa.shape[0]), order='F') new_args[0] = p_hpa.reshape((1, 1, p_hpa.shape[0]), order='F')
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
# No special left side iteration, build the output from the cape,cin # No special left side iteration, build the output from the cape,cin
# result # result
if (num_left_dims == 0): if (num_left_dims == 0):
cape, cin = wrapped(*new_args, **new_kwargs) cape, cin = wrapped(*new_args, **new_kwargs)
output_dims = (2,) output_dims = (2,)
if not is1d: if not is1d:
output_dims += p_hpa.shape[-3:] output_dims += p_hpa.shape[-3:]
@ -296,26 +292,26 @@ def cape_left_iter(alg_dtype=np.float64):
output_dims += (p_hpa.shape[0], 1, 1) output_dims += (p_hpa.shape[0], 1, 1)
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
# Initial output is ...,cape_cin,nz,ny,nx to create contiguous views # Initial output is ...,cape_cin,nz,ny,nx to create contiguous views
outdims = p_hpa.shape[0:num_left_dims] outdims = p_hpa.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
outdims += (2,) # cape_cin outdims += (2,) # cape_cin
outdims += p_hpa.shape[-3:] outdims += p_hpa.shape[-3:]
outview_array = np.empty(outdims, alg_dtype) outview_array = np.empty(outdims, alg_dtype)
# Create the output array where the leftmost dim is the product type # Create the output array where the leftmost dim is the product type
output_dims = (2,) output_dims = (2,)
output_dims += extra_dims output_dims += extra_dims
@ -326,14 +322,14 @@ def cape_left_iter(alg_dtype=np.float64):
left_and_slice_idxs = left_idxs + (slice(None),) left_and_slice_idxs = left_idxs + (slice(None),)
cape_idxs = left_idxs + (0, slice(None)) cape_idxs = left_idxs + (0, slice(None))
cin_idxs = left_idxs + (1, slice(None)) cin_idxs = left_idxs + (1, slice(None))
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]
new_args[1] = tk[left_and_slice_idxs] new_args[1] = tk[left_and_slice_idxs]
new_args[2] = qv[left_and_slice_idxs] new_args[2] = qv[left_and_slice_idxs]
@ -342,9 +338,9 @@ def cape_left_iter(alg_dtype=np.float64):
new_args[5] = sfp[left_and_slice_idxs] new_args[5] = sfp[left_and_slice_idxs]
capeview = outview_array[cape_idxs] capeview = outview_array[cape_idxs]
cinview = outview_array[cin_idxs] cinview = outview_array[cin_idxs]
# Skip the possible empty/missing arrays for the join method # Skip the possible empty/missing arrays for the join method
# Note: Masking handled by cape.py or computation.py, so only # Note: Masking handled by cape.py or computation.py, so only
# supply the fill values here. # supply the fill values here.
skip_missing = False skip_missing = False
for arg in (new_args[0:6]): for arg in (new_args[0:6]):
@ -356,25 +352,24 @@ def cape_left_iter(alg_dtype=np.float64):
else: else:
output[cape_output_idxs] = missing output[cape_output_idxs] = missing
output[cin_output_idxs] = missing output[cin_output_idxs] = missing
skip_missing = True skip_missing = True
if skip_missing: if skip_missing:
continue continue
# Call the numerical routine # Call the numerical routine
new_kwargs["capeview"] = capeview new_kwargs["capeview"] = capeview
new_kwargs["cinview"] = cinview new_kwargs["cinview"] = cinview
cape, cin = wrapped(*new_args, **new_kwargs) cape, cin = wrapped(*new_args, **new_kwargs)
# Make sure the result is the same data as what got passed in # Make sure the result is the same data as what got passed in
# Can delete this once everything works # Can delete this once everything works
if (cape.__array_interface__["data"][0] != if (cape.__array_interface__["data"][0] !=
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))
@ -382,82 +377,82 @@ def cape_left_iter(alg_dtype=np.float64):
outview_array[view_cin_reverse_idxs].astype(orig_dtype)) outview_array[view_cin_reverse_idxs].astype(orig_dtype))
else: else:
output[cape_output_idxs] = ( output[cape_output_idxs] = (
outview_array[cape_idxs].astype(orig_dtype)) outview_array[cape_idxs].astype(orig_dtype))
output[cin_output_idxs] = ( output[cin_output_idxs] = (
outview_array[cin_idxs].astype(orig_dtype)) outview_array[cin_idxs].astype(orig_dtype))
return output return output
return func_wrapper return func_wrapper
def cloudfrac_left_iter(alg_dtype=np.float64): def cloudfrac_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
cloud fraction diagnostic. cloud fraction diagnostic.
For example, if a wrapped function works with three-dimensional arrays, but For example, if a wrapped function works with three-dimensional arrays, but
the variables include a 4th leftmost dimension for 'Time', this decorator the variables include a 4th leftmost dimension for 'Time', this decorator
will iterate over all times, call the 3D Fortran routine, and aggregate the will iterate over all times, call the 3D Fortran routine, and aggregate the
results in to a 4D output array. results in to a 4D output array.
It is also important to note that the final output array is allocated It is also important to note that the final output array is allocated
first, and then views are passed to the wrapped function so that values first, and then views are passed to the wrapped function so that values
do not need to get copied in to the final output array. do not need to get copied in to the final output array.
Args: Args:
alg_dtype (:class:`np.dtype` or :obj:`str`): The numpy data type used alg_dtype (:class:`np.dtype` or :obj:`str`): The numpy data type used
in the wrapped function. in the wrapped function.
Returns: Returns:
:class:`numpy.ndarray`: The aggregated cloud fraction output array :class:`numpy.ndarray`: The aggregated cloud fraction output array
that includes all extra leftmost dimensions. that includes all extra leftmost dimensions.
""" """
@wrapt.decorator @wrapt.decorator
def func_wrapper(wrapped, instance, args, kwargs): def func_wrapper(wrapped, instance, args, kwargs):
new_args = list(args) new_args = list(args)
new_kwargs = dict(kwargs) new_kwargs = dict(kwargs)
vert = args[0] vert = args[0]
rh = args[1] rh = args[1]
num_left_dims = vert.ndim - 3 num_left_dims = vert.ndim - 3
orig_dtype = vert.dtype orig_dtype = vert.dtype
# No special left side iteration, build the output from the # No special left side iteration, build the output from the
# low, mid, high results. # low, mid, high results.
if (num_left_dims == 0): if (num_left_dims == 0):
low, mid, high = wrapped(*new_args, **new_kwargs) low, mid, high = wrapped(*new_args, **new_kwargs)
output_dims = (3,) output_dims = (3,)
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 views # Initial output is ...,low_mid_high,nz,ny,nx to create contiguous
# 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
outdims += (3,) # low_mid_high outdims += (3,) # low_mid_high
outdims += vert.shape[-2:] outdims += vert.shape[-2:]
outview_array = np.empty(outdims, alg_dtype) outview_array = np.empty(outdims, alg_dtype)
# Create the output array where the leftmost dim is the cloud type # Create the output array where the leftmost dim is the cloud type
output_dims = (3,) output_dims = (3,)
output_dims += extra_dims output_dims += extra_dims
output_dims += vert.shape[-2:] output_dims += vert.shape[-2:]
output = np.empty(output_dims, orig_dtype) output = np.empty(output_dims, orig_dtype)
has_missing = False has_missing = False
missing = default_fill(np.float64) missing = default_fill(np.float64)
for left_idxs in iter_left_indexes(extra_dims): for left_idxs in iter_left_indexes(extra_dims):
@ -465,16 +460,16 @@ def cloudfrac_left_iter(alg_dtype=np.float64):
low_idxs = left_idxs + (0, slice(None)) low_idxs = left_idxs + (0, slice(None))
mid_idxs = left_idxs + (1, slice(None)) mid_idxs = left_idxs + (1, slice(None))
high_idxs = left_idxs + (2, slice(None)) high_idxs = left_idxs + (2, slice(None))
low_output_idxs = (0,) + left_idxs + (slice(None),) low_output_idxs = (0,) + left_idxs + (slice(None),)
mid_output_idxs = (1,) + left_idxs + (slice(None),) mid_output_idxs = (1,) + left_idxs + (slice(None),)
high_output_idxs = (2,) + left_idxs + (slice(None),) high_output_idxs = (2,) + left_idxs + (slice(None),)
new_args[0] = vert[left_and_slice_idxs] new_args[0] = vert[left_and_slice_idxs]
new_args[1] = rh[left_and_slice_idxs] new_args[1] = rh[left_and_slice_idxs]
# Skip the possible empty/missing arrays for the join method # Skip the possible empty/missing arrays for the join method
# Note: Masking handled by cloudfrac.py or computation.py, so only # Note: Masking handled by cloudfrac.py or computation.py, so only
# supply the fill values here. # supply the fill values here.
skip_missing = False skip_missing = False
for arg in (new_args[0:2]): for arg in (new_args[0:2]):
@ -483,41 +478,41 @@ def cloudfrac_left_iter(alg_dtype=np.float64):
output[low_output_idxs] = missing output[low_output_idxs] = missing
output[mid_output_idxs] = missing output[mid_output_idxs] = missing
output[high_output_idxs] = missing output[high_output_idxs] = missing
skip_missing = True skip_missing = True
has_missing = True has_missing = True
if skip_missing: if skip_missing:
continue continue
lowview = outview_array[low_idxs] lowview = outview_array[low_idxs]
midview = outview_array[mid_idxs] midview = outview_array[mid_idxs]
highview = outview_array[high_idxs] highview = outview_array[high_idxs]
new_kwargs["lowview"] = lowview new_kwargs["lowview"] = lowview
new_kwargs["midview"] = midview new_kwargs["midview"] = midview
new_kwargs["highview"] = highview new_kwargs["highview"] = highview
low, mid, high = wrapped(*new_args, **new_kwargs) low, mid, high = wrapped(*new_args, **new_kwargs)
# Make sure the result is the same data as what got passed in # Make sure the result is the same data as what got passed in
# Can delete this once everything works # Can delete this once everything works
if (low.__array_interface__["data"][0] != if (low.__array_interface__["data"][0] !=
lowview.__array_interface__["data"][0]): lowview.__array_interface__["data"][0]):
raise RuntimeError("output array was copied") raise RuntimeError("output array was copied")
output[low_output_idxs] = ( output[low_output_idxs] = (
outview_array[low_idxs].astype(orig_dtype)) outview_array[low_idxs].astype(orig_dtype))
output[mid_output_idxs] = ( output[mid_output_idxs] = (
outview_array[mid_idxs].astype(orig_dtype)) outview_array[mid_idxs].astype(orig_dtype))
output[high_output_idxs] = ( output[high_output_idxs] = (
outview_array[high_idxs].astype(orig_dtype)) outview_array[high_idxs].astype(orig_dtype))
if has_missing: if has_missing:
output = np.ma.masked_values(output, missing) output = np.ma.masked_values(output, missing)
return output return output
return func_wrapper return func_wrapper
@ -526,95 +521,94 @@ def interplevel_left_iter(is2dlev, alg_dtype=np.float64):
def func_wrapper(wrapped, instance, args, kwargs): def func_wrapper(wrapped, instance, args, kwargs):
new_args = list(args) new_args = list(args)
new_kwargs = dict(kwargs) new_kwargs = dict(kwargs)
field3d = args[0] field3d = args[0]
z = args[1] z = args[1]
levels = args[2] levels = args[2]
num_left_dims = z.ndim - 3 num_left_dims = z.ndim - 3
orig_dtype = field3d.dtype orig_dtype = field3d.dtype
left_dims = z.shape[0:num_left_dims] left_dims = z.shape[0:num_left_dims]
multiproduct = True if field3d.ndim - z.ndim == 1 else False multiproduct = True if field3d.ndim - z.ndim == 1 else False
# No special left side iteration, build the output from the # No special left side iteration, build the output from the
# low, mid, high results. # low, mid, high results.
if (num_left_dims == 0): if (num_left_dims == 0):
if multiproduct: if multiproduct:
if not is2dlev: if not is2dlev:
outshape = (field3d.shape[0:-3] + levels.shape + outshape = (field3d.shape[0:-3] + levels.shape +
field3d.shape[-2:]) field3d.shape[-2:])
else: else:
outshape = (field3d.shape[0:-3] + field3d.shape[-2:]) outshape = (field3d.shape[0:-3] + field3d.shape[-2:])
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)
return output return output
if multiproduct: if multiproduct:
outdims = field3d.shape[0:1] + left_dims outdims = field3d.shape[0:1] + left_dims
else: else:
outdims = left_dims outdims = left_dims
extra_dims = tuple(outdims) extra_dims = tuple(outdims)
if not is2dlev: if not is2dlev:
outdims += levels.shape outdims += levels.shape
outdims += z.shape[-2:] outdims += z.shape[-2:]
outview_array = np.empty(outdims, alg_dtype) outview_array = np.empty(outdims, alg_dtype)
for left_idxs in iter_left_indexes(extra_dims): for left_idxs in iter_left_indexes(extra_dims):
field_out_slice_idxs = left_idxs + (slice(None),) field_out_slice_idxs = left_idxs + (slice(None),)
if multiproduct: if multiproduct:
z_slice_idxs = left_idxs[1:] + (slice(None),) z_slice_idxs = left_idxs[1:] + (slice(None),)
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]
if is2dlev: if is2dlev:
if levels.ndim > 2: if levels.ndim > 2:
new_args[2] = levels[z_slice_idxs] new_args[2] = levels[z_slice_idxs]
new_kwargs["outview"] = outview_array[field_out_slice_idxs] new_kwargs["outview"] = outview_array[field_out_slice_idxs]
_ = wrapped(*new_args, **new_kwargs) _ = wrapped(*new_args, **new_kwargs)
output = outview_array.astype(orig_dtype) output = outview_array.astype(orig_dtype)
return output return output
return func_wrapper return func_wrapper
def check_cape_args(): def check_cape_args():
"""A decorator to check that the cape_3d arguments are valid. """A decorator to check that the cape_3d arguments are valid.
An exception is raised when an invalid argument is found. An exception is raised when an invalid argument is found.
Returns: Returns:
None None
Raises: Raises:
:class:`ValueError`: Raised when an invalid argument is detected. :class:`ValueError`: Raised when an invalid argument is detected.
""" """
@wrapt.decorator @wrapt.decorator
def func_wrapper(wrapped, instance, args, kwargs): def func_wrapper(wrapped, instance, args, kwargs):
p_hpa = args[0] p_hpa = args[0]
tk = args[1] tk = args[1]
qv = args[2] qv = args[2]
@ -624,11 +618,11 @@ def check_cape_args():
missing = args[6] missing = args[6]
i3dflag = args[7] i3dflag = args[7]
ter_follow = args[8] ter_follow = args[8]
is2d = False if i3dflag != 0 else True is2d = False if i3dflag != 0 else True
is1d = ((np.isscalar(sfp) or np.size(sfp) == 1) or is1d = ((np.isscalar(sfp) or np.size(sfp) == 1) or
(np.isscalar(ter) or np.size(ter) == 1)) (np.isscalar(ter) or np.size(ter) == 1))
if not (p_hpa.shape == tk.shape == qv.shape == ht.shape): if not (p_hpa.shape == tk.shape == qv.shape == ht.shape):
raise ValueError("arguments 0, 1, 2, 3 must be the same shape") raise ValueError("arguments 0, 1, 2, 3 must be the same shape")
@ -641,41 +635,41 @@ def check_cape_args():
if np.size(ter) != np.size(sfp): if np.size(ter) != np.size(sfp):
raise ValueError("arguments 4 and 5 must both be scalars or " raise ValueError("arguments 4 and 5 must both be scalars or "
"both be arrays") "both be arrays")
# Only need to test p_hpa since we assured args 0-3 have same ndim # Only need to test p_hpa since we assured args 0-3 have same ndim
if p_hpa.ndim != 1: if p_hpa.ndim != 1:
raise ValueError("arguments 0-3 " raise ValueError("arguments 0-3 "
"must be 1-dimensional when " "must be 1-dimensional when "
"arguments 4 and 5 are scalars") "arguments 4 and 5 are scalars")
return wrapped(*args, **kwargs) return wrapped(*args, **kwargs)
return func_wrapper return func_wrapper
def check_interplevel_args(is2dlev): def check_interplevel_args(is2dlev):
"""A decorator to check that the interplevel arguments are valid. """A decorator to check that the interplevel arguments are valid.
An exception is raised when an invalid argument is found. An exception is raised when an invalid argument is found.
Returns: Returns:
None None
Raises: Raises:
:class:`ValueError`: Raised when an invalid argument is detected. :class:`ValueError`: Raised when an invalid argument is detected.
""" """
@wrapt.decorator @wrapt.decorator
def func_wrapper(wrapped, instance, args, kwargs): def func_wrapper(wrapped, instance, args, kwargs):
field3d = args[0] field3d = args[0]
z = args[1] z = args[1]
levels = args[2] levels = args[2]
multiproduct = True if (field3d.ndim - z.ndim) == 1 else False multiproduct = True if (field3d.ndim - z.ndim) == 1 else False
if not multiproduct: if not multiproduct:
if field3d.shape != z.shape: if field3d.shape != z.shape:
raise ValueError("arguments 0 and 1 must have the same shape") raise ValueError("arguments 0 and 1 must have the same shape")
@ -683,16 +677,15 @@ def check_interplevel_args(is2dlev):
if field3d.shape[1:] != z.shape: if field3d.shape[1:] != z.shape:
raise ValueError("argument 0 and 1 must have same rightmost " raise ValueError("argument 0 and 1 must have same rightmost "
"dimensions") "dimensions")
if is2dlev: if is2dlev:
if levels.ndim != 2: if levels.ndim != 2:
if (levels.shape[0:-2] != z.shape[0:-3] or if (levels.shape[0:-2] != z.shape[0:-3] or
levels.shape[-2:] != z.shape[-2:]): levels.shape[-2:] != z.shape[-2:]):
raise ValueError("argument 1 and 2 must have " raise ValueError("argument 1 and 2 must have "
"the same leftmost and rightmost " "the same leftmost and rightmost "
"dimensions") "dimensions")
return wrapped(*args, **kwargs) return wrapped(*args, **kwargs)
return func_wrapper
return func_wrapper

440
src/wrf/units.py

@ -4,31 +4,31 @@ from .constants import Constants, ConversionFactors
def _apply_conv_fact(var, vartype, var_unit, dest_unit): def _apply_conv_fact(var, vartype, var_unit, dest_unit):
"""Return the variable converted to different units using a conversion """Return the variable converted to different units using a conversion
factor. factor.
Args: Args:
var (:class:`xarray.DataArray` or :class:`numpy.ndarray`): A var (:class:`xarray.DataArray` or :class:`numpy.ndarray`): A
variable. variable.
vartype (:obj:`str`): The type of variable. Choices are: 'wind', vartype (:obj:`str`): The type of variable. Choices are: 'wind',
'pressure', 'temp', or 'height'. 'pressure', 'temp', or 'height'.
var_unit (:obj:`str`): The variable's current units. var_unit (:obj:`str`): The variable's current units.
dest_unit (:obj:`str`): The desired units. dest_unit (:obj:`str`): The desired units.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The variable in :class:`xarray.DataArray` or :class:`numpy.ndarray`: The variable in
the desired units. the desired units.
""" """
if var_unit == dest_unit: if var_unit == dest_unit:
return var return var
# Note, case where var_unit and dest_unit are base unit, should be # Note, case where var_unit and dest_unit are base unit, should be
# handled above # handled above
if var_unit == _BASE_UNITS[vartype]: if var_unit == _BASE_UNITS[vartype]:
return var*(_CONV_FACTORS[vartype]["to_dest"][dest_unit]) return var*(_CONV_FACTORS[vartype]["to_dest"][dest_unit])
@ -36,62 +36,62 @@ def _apply_conv_fact(var, vartype, var_unit, dest_unit):
if dest_unit == _BASE_UNITS[vartype]: if dest_unit == _BASE_UNITS[vartype]:
return var*(_CONV_FACTORS[vartype]["to_base"][var_unit]) return var*(_CONV_FACTORS[vartype]["to_base"][var_unit])
else: else:
return var*(_CONV_FACTORS[vartype]["to_base"][var_unit] * return var*(_CONV_FACTORS[vartype]["to_base"][var_unit] *
_CONV_FACTORS[vartype]["to_dest"][dest_unit]) _CONV_FACTORS[vartype]["to_dest"][dest_unit])
def _to_kelvin(var, var_unit): def _to_kelvin(var, var_unit):
"""Return the variable in Kelvin. """Return the variable in Kelvin.
Args: Args:
var (:class:`xarray.DataArray` or :class:`numpy.ndarray`): A var (:class:`xarray.DataArray` or :class:`numpy.ndarray`): A
variable. variable.
var_unit (:obj:`str`): The variable's current units. var_unit (:obj:`str`): The variable's current units.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The variable in :class:`xarray.DataArray` or :class:`numpy.ndarray`: The variable in
Kelvin. Kelvin.
""" """
if var_unit == "c": if var_unit == "c":
return var + Constants.CELKEL return var + Constants.CELKEL
elif var_unit == "f": elif var_unit == "f":
return (var - 32.0) * (5.0/9.0) + Constants.CELKEL return (var - 32.0) * (5.0/9.0) + Constants.CELKEL
def _k_to_c(var): def _k_to_c(var):
"""Return the variable in Celsius. """Return the variable in Celsius.
Args: Args:
var (:class:`xarray.DataArray` or :class:`numpy.ndarray`): A var (:class:`xarray.DataArray` or :class:`numpy.ndarray`): A
variable in units of Kelvin. variable in units of Kelvin.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The variable in :class:`xarray.DataArray` or :class:`numpy.ndarray`: The variable in
Celsius. Celsius.
""" """
return var - Constants.CELKEL return var - Constants.CELKEL
def _k_to_f(var): def _k_to_f(var):
"""Return the variable in Fahrenheit. """Return the variable in Fahrenheit.
Args: Args:
var (:class:`xarray.DataArray` or :class:`numpy.ndarray`): A var (:class:`xarray.DataArray` or :class:`numpy.ndarray`): A
variable in units of Kelvin. variable in units of Kelvin.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The variable in :class:`xarray.DataArray` or :class:`numpy.ndarray`: The variable in
Fahrenheit. Fahrenheit.
""" """
return 1.8 * _k_to_c(var) + 32.0 return 1.8 * _k_to_c(var) + 32.0
@ -99,25 +99,25 @@ def _k_to_f(var):
def _apply_temp_conv(var, var_unit, dest_unit): def _apply_temp_conv(var, var_unit, dest_unit):
"""Return the variable converted to different units using a temperature """Return the variable converted to different units using a temperature
conversion algorithm. conversion algorithm.
Args: Args:
var (:class:`xarray.DataArray` or :class:`numpy.ndarray`): A var (:class:`xarray.DataArray` or :class:`numpy.ndarray`): A
variable. variable.
var_unit (:obj:`str`): The variable's current units. var_unit (:obj:`str`): The variable's current units.
dest_unit (:obj:`str`): The desired units. dest_unit (:obj:`str`): The desired units.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The variable in :class:`xarray.DataArray` or :class:`numpy.ndarray`: The variable in
the desired units. the desired units.
""" """
if dest_unit == var_unit: if dest_unit == var_unit:
return var return var
if var_unit != _BASE_UNITS["temp"]: if var_unit != _BASE_UNITS["temp"]:
tk = _to_kelvin(var, var_unit) tk = _to_kelvin(var, var_unit)
if dest_unit == _BASE_UNITS["temp"]: if dest_unit == _BASE_UNITS["temp"]:
@ -126,247 +126,237 @@ def _apply_temp_conv(var, var_unit, dest_unit):
return (_TEMP_CONV_METHODS[dest_unit])(tk) return (_TEMP_CONV_METHODS[dest_unit])(tk)
else: else:
return (_TEMP_CONV_METHODS[dest_unit])(var) return (_TEMP_CONV_METHODS[dest_unit])(var)
# 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.
Args: Args:
unit (:obj:`str`): The unit name. unit (:obj:`str`): The unit name.
Returns: Returns:
:obj:`str`: A unit name suitable for dictionary key lookups. :obj:`str`: A unit name suitable for dictionary key lookups.
""" """
cleaned_unit = " ".join(unit.lower().split()) cleaned_unit = " ".join(unit.lower().split())
dealiased = _UNIT_ALIASES.get(cleaned_unit, None) dealiased = _UNIT_ALIASES.get(cleaned_unit, None)
return cleaned_unit if dealiased is None else dealiased return cleaned_unit if dealiased is None else dealiased
def check_units(unit, unit_type): def check_units(unit, unit_type):
"""Raise an exception if the unit name is invalid. """Raise an exception if the unit name is invalid.
Args: Args:
unit (:obj:`str`): The unit name. unit (:obj:`str`): The unit name.
unit_type (:obj:`str`): The type of unit. unit_type (:obj:`str`): The type of unit.
Returns: Returns:
None None
Raises: Raises:
:class:`ValueError`: Raised when the unit name is invalid. :class:`ValueError`: Raised when the unit name is invalid.
""" """
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):
"""Return the variable converted to different units. """Return the variable converted to different units.
Args: Args:
var (:class:`xarray.DataArray` or :class:`numpy.ndarray`): A var (:class:`xarray.DataArray` or :class:`numpy.ndarray`): A
variable. variable.
vartype (:obj:`str`): The type of variable. Choices are: 'wind', vartype (:obj:`str`): The type of variable. Choices are: 'wind',
'pressure', 'temp', or 'height'. 'pressure', 'temp', or 'height'.
var_unit (:obj:`str`): The variable's current units. var_unit (:obj:`str`): The variable's current units.
dest_unit (:obj:`str`): The desired units. dest_unit (:obj:`str`): The desired units.
Returns: Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The variable in :class:`xarray.DataArray` or :class:`numpy.ndarray`: The variable in
the desired units. the desired units.
""" """
u_cleaned = dealias_and_clean_unit(dest_unit) u_cleaned = dealias_and_clean_unit(dest_unit)
if vartype != "temp": if vartype != "temp":
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)

3505
src/wrf/util.py

File diff suppressed because it is too large Load Diff

1
src/wrf/version.py

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

Loading…
Cancel
Save