From c2fa5405fb793a2a0c3220ad800d00a7ad8562b8 Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Wed, 14 Sep 2016 16:31:38 -0600 Subject: [PATCH] Added new unit aliases to better match udunits, if we ever go down that route. Changed temperature to use Kelvin as base unit, so speed should improve. Added unit test for units to make sure they all run --- src/wrf/cape.py | 17 ---- src/wrf/computation.py | 12 +-- src/wrf/ctt.py | 2 +- src/wrf/dbz.py | 4 +- src/wrf/decorators.py | 5 +- src/wrf/dewpoint.py | 4 +- src/wrf/helicity.py | 4 +- src/wrf/metadecorators.py | 6 +- src/wrf/omega.py | 2 +- src/wrf/pressure.py | 4 +- src/wrf/rh.py | 4 +- src/wrf/slp.py | 2 +- src/wrf/temp.py | 16 ++-- src/wrf/units.py | 173 ++++++++++++++++++++++++++++++-------- src/wrf/uvmet.py | 12 +-- src/wrf/wind.py | 20 ++--- test/test_units.py | 56 ++++++++++++ 17 files changed, 243 insertions(+), 100 deletions(-) create mode 100644 test/test_units.py diff --git a/src/wrf/cape.py b/src/wrf/cape.py index e86872d..f7daf65 100755 --- a/src/wrf/cape.py +++ b/src/wrf/cape.py @@ -4,22 +4,12 @@ from __future__ import (absolute_import, division, print_function, import numpy as np import numpy.ma as ma -#from .extension import computetk,computecape from .extension import _tk, _cape from .destag import destagger from .constants import Constants, ConversionFactors from .util import extract_vars from .metadecorators import set_cape_metadata - -#@copy_and_set_metadata(copy_varname="T", -# name="cape_2d", -# dimnames=combine_with("T", remove_dims=("bottom_top",), -# insert_before="south_north", -# new_dimnames=["mcape_mcin_lcl_lfc"]), -# description="mcape ; mcin ; lcl ; lfc", -# units="J/kg ; J/kg ; m ; m", -# MemoryOrder="XY") @set_cape_metadata(is2d=True) def get_2dcape(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None, missing=Constants.DEFAULT_FILL): @@ -74,13 +64,6 @@ def get_2dcape(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None, return ma.masked_values(result, missing) -#@copy_and_set_metadata(copy_varname="T", name="cape_3d", -# dimnames=combine_with("T", -# insert_before="bottom_top", -# new_dimnames=["cape_cin"]), -# description="cape ; cin", -# units="J kg-1 ; J kg-1", -# MemoryOrder="XY") @set_cape_metadata(is2d=False) def get_3dcape(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, diff --git a/src/wrf/computation.py b/src/wrf/computation.py index 7cd1408..3fe0b5c 100644 --- a/src/wrf/computation.py +++ b/src/wrf/computation.py @@ -38,7 +38,7 @@ def interpz3d(field3d, z, desiredloc, missingval=Constants.DEFAULT_FILL, return _interpz3d(field3d, z, desiredloc, missingval) -@set_alg_metadata(2, "pres", refvarndims=3, units="hpa", +@set_alg_metadata(2, "pres", refvarndims=3, units="hPa", description="sea level pressure") def slp(height, tkel, pres, qv, meta=True): return _slp(height, tkel, pres, qv) @@ -139,8 +139,8 @@ def ctt(pres_hpa, tkel, qv, qcld, height, terrain, qice=None, meta=True): @set_alg_metadata(3, "pres", units="dBZ", description="radar reflectivity") -def dbz(pres, tkel, qv, qr, qs=None, qg=None, use_varint=False, use_liqskin=False, - meta=True): +def dbz(pres, tkel, qv, qr, qs=None, qg=None, use_varint=False, + use_liqskin=False, meta=True): if qs is None: qs = np.zeros(qv.shape, qv.dtype) @@ -155,7 +155,7 @@ def dbz(pres, tkel, qv, qr, qs=None, qg=None, use_varint=False, use_liqskin=Fals return _dbz(pres, tkel, qv, qr, qs, qg, sn0, ivarint, iliqskin) -@set_alg_metadata(2, "terrain", units="m-2/s-2", +@set_alg_metadata(2, "terrain", units="m2 s-2", description="storm relative helicity") def srhel(u, v, z, terrain, top=3000.0, meta=True): # u, v get swapped in vertical @@ -166,7 +166,7 @@ def srhel(u, v, z, terrain, top=3000.0, meta=True): return _srhel(_u, _v, _z, terrain, top) -@set_alg_metadata(2, "u", refvarndims=3, units="m-2/s-2", +@set_alg_metadata(2, "u", refvarndims=3, units="m2 s-2", description="updraft helicity") def udhel(zstag, mapfct, u, v, wstag, dx, dy, bottom=2000.0, top=5000.0, meta=True): @@ -205,7 +205,7 @@ def tvirtual(tkel, qv, meta=True): return _tv(tkel, qv) -@set_alg_metadata(3, "qv", units="Pa/s", +@set_alg_metadata(3, "qv", units="Pa s-1", description="omega") def omega(qv, tkel, w, pres, meta=True): return _omega(qv, tkel, w, pres) diff --git a/src/wrf/ctt.py b/src/wrf/ctt.py index 3e4f8a5..f0d671b 100644 --- a/src/wrf/ctt.py +++ b/src/wrf/ctt.py @@ -19,7 +19,7 @@ from .util import extract_vars @convert_units("temp", "c") def get_ctt(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None, - units="c"): + units="degC"): """Return the cloud top temperature. """ diff --git a/src/wrf/dbz.py b/src/wrf/dbz.py index 4f75d53..8315557 100755 --- a/src/wrf/dbz.py +++ b/src/wrf/dbz.py @@ -12,7 +12,7 @@ from .metadecorators import copy_and_set_metadata @copy_and_set_metadata(copy_varname="T", name="dbz", description="radar reflectivity", - units="dBz") + units="dBZ") def get_dbz(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None, use_varint=False, use_liqskin=False): @@ -68,7 +68,7 @@ def get_dbz(wrfnc, timeidx=0, method="cat", @copy_and_set_metadata(copy_varname="T", name="max_dbz", remove_dims=("bottom_top",), description="maximum radar reflectivity", - units="dBz", + units="dBZ", MemoryOrder="XY") def get_max_dbz(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None, diff --git a/src/wrf/decorators.py b/src/wrf/decorators.py index 2ab3979..c13af3b 100644 --- a/src/wrf/decorators.py +++ b/src/wrf/decorators.py @@ -7,7 +7,7 @@ import wrapt import numpy as np import numpy.ma as ma -from .units import do_conversion, check_units +from .units import do_conversion, check_units, dealias_and_clean_unit from .util import iter_left_indexes, from_args, npvalues, combine_dims from .py3compat import viewitems, viewvalues, isstr from .config import xarray_enabled @@ -29,7 +29,8 @@ def convert_units(unit_type, alg_unit): def func_wrapper(wrapped, instance, args, kwargs): desired_units = from_args(wrapped, "units", *args, **kwargs)["units"] - check_units(desired_units, unit_type) + u_cleaned = dealias_and_clean_unit(desired_units) + check_units(u_cleaned, unit_type) # Unit conversion done here return do_conversion(wrapped(*args, **kwargs), unit_type, diff --git a/src/wrf/dewpoint.py b/src/wrf/dewpoint.py index 67187dc..95bc4b9 100755 --- a/src/wrf/dewpoint.py +++ b/src/wrf/dewpoint.py @@ -12,7 +12,7 @@ from .util import extract_vars description="dew point temperature") @convert_units("temp", "c") def get_dp(wrfnc, timeidx=0, method="cat", squeeze=True, - cache=None, meta=True, _key=None, units="c"): + cache=None, meta=True, _key=None, units="degC"): varnames=("P", "PB", "QVAPOR") ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache, @@ -33,7 +33,7 @@ def get_dp(wrfnc, timeidx=0, method="cat", squeeze=True, description="2m dew point temperature") @convert_units("temp", "c") def get_dp_2m(wrfnc, timeidx=0, method="cat", squeeze=True, - cache=None, meta=True, _key=None, units="c"): + cache=None, meta=True, _key=None, units="degC"): varnames=("PSFC", "Q2") ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache, meta=False, _key=_key) diff --git a/src/wrf/helicity.py b/src/wrf/helicity.py index 0ab45bc..67335ff 100755 --- a/src/wrf/helicity.py +++ b/src/wrf/helicity.py @@ -11,7 +11,7 @@ from .metadecorators import copy_and_set_metadata @copy_and_set_metadata(copy_varname="HGT", name="srh", description="storm relative helicity", - units="m-2/s-2") + units="m2 s-2") def get_srh(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None, top=3000.0): # Top can either be 3000 or 1000 (for 0-1 srh or 0-3 srh) @@ -51,7 +51,7 @@ def get_srh(wrfnc, timeidx=0, method="cat", squeeze=True, @copy_and_set_metadata(copy_varname="MAPFAC_M", name="updraft_helicity", description="updraft helicity", - units="m-2/s-2") + units="m2 s-2") def get_uh(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None, bottom=2000.0, top=5000.0): diff --git a/src/wrf/metadecorators.py b/src/wrf/metadecorators.py index c4fbba7..996e1ce 100644 --- a/src/wrf/metadecorators.py +++ b/src/wrf/metadecorators.py @@ -286,7 +286,7 @@ def set_cape_metadata(is2d): outdimnames[0] = "mcape_mcin_lcl_lfc" outattrs["description"] = "mcape ; mcin ; lcl ; lfc" outattrs["MemoryOrder"] = "XY" - outattrs["units"] = "J/kg ; J/kg ; m ; m" + outattrs["units"] = "J kg-1 ; J kg-1 ; m ; m" outname = "cape_2d" else: # Right dims @@ -1215,7 +1215,7 @@ def set_alg_metadata(alg_ndims, refvarname, return func_wrapper -def set_uvmet_alg_metadata(units="mps", description="earth rotated u,v", +def set_uvmet_alg_metadata(units="m s-1", description="earth rotated u,v", latarg="lat", windarg="u"): @wrapt.decorator @@ -1287,7 +1287,7 @@ def set_cape_alg_metadata(is2d, copyarg="pres_hpa"): if is2d: outname = "cape_2d" outattrs["description"] = "mcape ; mcin ; lcl ; lfc" - outattrs["units"] = "J/kg ; J/kg ; m ; m" + outattrs["units"] = "J kg-1 ; J kg-1 ; m ; m" outattrs["MemoryOrder"] = "XY" else: outname = "cape_3d" diff --git a/src/wrf/omega.py b/src/wrf/omega.py index 61dccb7..f602c69 100755 --- a/src/wrf/omega.py +++ b/src/wrf/omega.py @@ -10,7 +10,7 @@ from .metadecorators import copy_and_set_metadata @copy_and_set_metadata(copy_varname="T", name="omega", description="omega", - units="Pa/s") + units="Pa s-1") def get_omega(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None): varnames=("T", "P", "W", "PB", "QVAPOR") diff --git a/src/wrf/pressure.py b/src/wrf/pressure.py index 0cc19bf..4d960b1 100755 --- a/src/wrf/pressure.py +++ b/src/wrf/pressure.py @@ -11,7 +11,7 @@ from .util import extract_vars, either @convert_units("pressure", "pa") def get_pressure(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None, - units="pa"): + units="Pa"): varname = either("P", "PRES")(wrfnc) if varname == "P": @@ -30,7 +30,7 @@ def get_pressure(wrfnc, timeidx=0, method="cat", squeeze=True, def get_pressure_hpa(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None, - units="hpa"): + units="hPa"): return get_pressure(wrfnc, timeidx, method, squeeze, cache, meta, _key, units) diff --git a/src/wrf/rh.py b/src/wrf/rh.py index e2d8c5f..fc2292b 100755 --- a/src/wrf/rh.py +++ b/src/wrf/rh.py @@ -10,7 +10,7 @@ from .metadecorators import copy_and_set_metadata @copy_and_set_metadata(copy_varname="T", name="rh", description="relative humidity", - delete_attrs=("units",)) + units="%") def get_rh(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None): varnames=("T", "P", "PB", "QVAPOR") @@ -32,7 +32,7 @@ def get_rh(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None, @copy_and_set_metadata(copy_varname="T2", name="rh2", description="2m relative humidity", - delete_attrs=("units",)) + units="%") def get_rh_2m(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None): varnames=("T2", "PSFC", "Q2") diff --git a/src/wrf/slp.py b/src/wrf/slp.py index e585a94..d0263f2 100755 --- a/src/wrf/slp.py +++ b/src/wrf/slp.py @@ -17,7 +17,7 @@ from .util import extract_vars @convert_units("pressure", "hpa") def get_slp(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None, - units="hpa"): + units="hPa"): varnames=("T", "P", "PB", "QVAPOR", "PH", "PHB") ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache, meta=False, _key=_key) diff --git a/src/wrf/temp.py b/src/wrf/temp.py index fa0776f..029bf1a 100755 --- a/src/wrf/temp.py +++ b/src/wrf/temp.py @@ -13,7 +13,7 @@ from .util import extract_vars @convert_units("temp", "k") def get_theta(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None, - units="k"): + units="K"): varnames = ("T",) ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache, @@ -29,7 +29,7 @@ def get_theta(wrfnc, timeidx=0, method="cat", squeeze=True, @convert_units("temp", "k") def get_temp(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None, - units="k"): + units="K"): """Return the temperature in Kelvin or Celsius""" varnames=("T", "P", "PB") @@ -48,10 +48,10 @@ def get_temp(wrfnc, timeidx=0, method="cat", squeeze=True, @copy_and_set_metadata(copy_varname="T", name="theta_e", description="equivalent potential temperature") -@convert_units("temp", "k") +@convert_units("temp", "K") def get_eth(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None, - units="k"): + units="K"): "Return equivalent potential temperature (Theta-e) in Kelvin" varnames=("T", "P", "PB", "QVAPOR") @@ -76,7 +76,7 @@ def get_eth(wrfnc, timeidx=0, method="cat", squeeze=True, @convert_units("temp", "k") def get_tv(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None, - units="k"): + units="K"): "Return the virtual temperature (tv) in Kelvin or Celsius" varnames=("T", "P", "PB", "QVAPOR") @@ -102,7 +102,7 @@ def get_tv(wrfnc, timeidx=0, method="cat", squeeze=True, @convert_units("temp", "k") def get_tw(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None, - units="k"): + units="K"): "Return the wetbulb temperature (tw)" varnames=("T", "P", "PB", "QVAPOR") @@ -125,13 +125,13 @@ def get_tw(wrfnc, timeidx=0, method="cat", squeeze=True, def get_tk(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None): return get_temp(wrfnc, timeidx, method, squeeze, cache, meta, _key, - units="k") + units="K") def get_tc(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None): return get_temp(wrfnc, timeidx, method, squeeze, cache, meta, _key, - units="c") + units="degC") diff --git a/src/wrf/units.py b/src/wrf/units.py index 153a4bd..b0a0734 100755 --- a/src/wrf/units.py +++ b/src/wrf/units.py @@ -12,7 +12,7 @@ def _apply_conv_fact(var, vartype, var_unit, dest_unit): # Note, case where var_unit and dest_unit are base unit, should be # handled above if var_unit == _BASE_UNITS[vartype]: - return var * _CONV_FACTORS[vartype]["to_dest"][dest_unit] + return var*(_CONV_FACTORS[vartype]["to_dest"][dest_unit]) else: if dest_unit == _BASE_UNITS[vartype]: return var*(_CONV_FACTORS[vartype]["to_base"][var_unit]) @@ -21,51 +21,149 @@ def _apply_conv_fact(var, vartype, var_unit, dest_unit): _CONV_FACTORS[vartype]["to_dest"][dest_unit]) -def _to_celsius(var, var_unit): - if var_unit == "k": - return var - Constants.CELKEL +def _to_kelvin(var, var_unit): + if var_unit == "c": + return var + Constants.CELKEL elif var_unit == "f": - return (var - 32.0) * (5.0/9.0) + return (var - 32.0) * (5.0/9.0) + Constants.CELKEL -def _c_to_k(var): - return var + Constants.TCK0 +def _k_to_c(var): + return var - Constants.CELKEL -def _c_to_f(var): - return 1.8 * var + 32.0 +def _k_to_f(var): + return 1.8 * _k_to_c(var) + 32.0 -# Temperature is a more complicated operation so requres functions +# Temperature is a more complicated operation so requires functions def _apply_temp_conv(var, var_unit, dest_unit): if dest_unit == var_unit: return var if var_unit != _BASE_UNITS["temp"]: - tc = _to_celsius(var, var_unit) + tk = _to_kelvin(var, var_unit) if dest_unit == _BASE_UNITS["temp"]: - return tc + return tk else: - return (_TEMP_CONV_METHODS[dest_unit])(tc) + return (_TEMP_CONV_METHODS[dest_unit])(tk) else: return (_TEMP_CONV_METHODS[dest_unit])(var) + + +_UNIT_ALIASES = {"mps" : "m s-1", + "m/s" : "m s-1", + "ms-1" : "m s-1", + "meters_per_second" : "m s-1", + "metres_per_second" : "m s-1", + "knots" : "kt", + "knot" : "kt", + "kts" : "kt", + "kn" : "kt", + "miles_per_hour" : "mi h-1", + "mih-1" : "mi h-1", + "mph" : "mi h-1", + "mi/h" : "mi h-1", + "kmph" : "km h-1", + "kmh-1" : "km h-1", + "km/h" : "km h-1", + "kilometers_per_hour" : "km h-1", + "kilometres_per_hour" : "km h-1", + "ft/s" : "ft s-1", + "ft/sec" : "ft s-1", + "fps" : "ft s-1", + "fs-1" : "ft s-1", + "feet_per_second" : "ft s-1", + + "pascal" : "pa", + "pascals" : "pa", + "hecto_pascal" : "hpa", + "hecto_pascals" : "hpa", + "millibar" : "mb", + "millibars" : "mb", + "mbar" : "mb", + + "kelvin" : "k", + "degree_kelvin" : "k", + "degrees_kelvin" : "k", + "degree_k" : "k", + "degrees_k" : "k", + "degreek" : "k", + "degreesk" : "k", + "degk" : "k", + "degsk" : "k", + "deg_k" : "k", + "degs_k" : "k", + "deg k" : "k", + "degs k" : "k", + + "celsius" : "c", + "degree_celsius" : "c", + "degrees_celsius" : "c", + "degree_c" : "c", + "degrees_c" : "c", + "degreec" : "c", + "degreesc" : "c", + "degc" : "c", + "degsc" : "c", + "deg_c" : "c", + "degs_c" : "c", + "deg c" : "c", + "degs c" : "c", + + "fahrenheit" : "f", + "degree_fahrenheit" : "f", + "degrees_fahrenheit" : "f", + "degree_f" : "f", + "degrees_f" : "f", + "degreef" : "f", + "degreesf" : "f", + "degf" : "f", + "degsf" : "f", + "deg_f" : "f", + "degs_f" : "f", + "deg f" : "f", + "degs f" : "f", + + "meter" : "m", + "meters" : "m", + "metre" : "m", + "metres" : "m", + "kilometer" : "km", + "kilometers" : "km", + "dekameter" : "dm", + "dekameters" : "dm", + "decameter" : "dm", + "decameters" : "dm", + "dekametre" : "dm", + "dekametres" : "dm", + "decametre" : "dm", + "decametres" : "dm", + "dam" : "dm", + "dkm" : "dm", + "feet" : "ft", + "foot" : "ft", + "mile" : "mi", + "miles" : "mi" + + } -_VALID_UNITS = {"wind" : ["mps", "kts", "mph", "kmph", "fps"], +_VALID_UNITS = {"wind" : ["m s-1", "kt", "mi h-1", "km h-1", "ft s-1"], "pressure" : ["pa", "hpa", "mb", "torr", "mmhg", "atm"], "temp" : ["k", "f", "c"], - "height" : ["m", "km", "dm", "ft", "miles"] + "height" : ["m", "km", "dm", "ft", "mi"] } -_WIND_BASE_FACTORS = {"kts" : ConversionFactors.MPS_TO_KTS, - "kmph" : ConversionFactors.MPS_TO_KMPH, - "mph" : ConversionFactors.MPS_TO_MPH, - "fps" : ConversionFactors.MPS_TO_FPS +_WIND_BASE_FACTORS = {"kt" : ConversionFactors.MPS_TO_KTS, + "km h-1" : ConversionFactors.MPS_TO_KMPH, + "mi h-1" : ConversionFactors.MPS_TO_MPH, + "ft s-1" : ConversionFactors.MPS_TO_FPS } -_WIND_TOBASE_FACTORS = {"kts" : 1.0/ConversionFactors.MPS_TO_KTS, - "kmph" : 1.0/ConversionFactors.MPS_TO_KMPH, - "mph" : 1.0/ConversionFactors.MPS_TO_MPH, - "fps" : 1.0/ConversionFactors.MPS_TO_FPS +_WIND_TOBASE_FACTORS = {"kt" : 1.0/ConversionFactors.MPS_TO_KTS, + "km h-1" : 1.0/ConversionFactors.MPS_TO_KMPH, + "mi h-1" : 1.0/ConversionFactors.MPS_TO_MPH, + "ft s-1" : 1.0/ConversionFactors.MPS_TO_FPS } _PRES_BASE_FACTORS = {"hpa" : ConversionFactors.PA_TO_HPA, @@ -85,19 +183,18 @@ _PRES_TOBASE_FACTORS = {"hpa" : 1.0/ConversionFactors.PA_TO_HPA, _HEIGHT_BASE_FACTORS = {"km" : ConversionFactors.M_TO_KM, "dm" : ConversionFactors.M_TO_DM, "ft" : ConversionFactors.M_TO_FT, - "miles" : ConversionFactors.M_TO_MILES + "mi" : ConversionFactors.M_TO_MILES } _HEIGHT_TOBASE_FACTORS = {"km" : 1.0/ConversionFactors.M_TO_KM, "dm" : 1.0/ConversionFactors.M_TO_DM, "ft" : 1.0/ConversionFactors.M_TO_FT, - "miles" : 1.0/ConversionFactors.M_TO_MILES - + "mi" : 1.0/ConversionFactors.M_TO_MILES } -_BASE_UNITS = {"wind" : "mps", +_BASE_UNITS = {"wind" : "m s-1", "pressure" : "pa", - "temp" : "c", + "temp" : "k", "height" : "m" } @@ -109,23 +206,29 @@ _CONV_FACTORS = {"wind" : {"to_dest" : _WIND_BASE_FACTORS, "to_base" : _HEIGHT_TOBASE_FACTORS} } -_TEMP_CONV_METHODS = {"k" : _c_to_k, - "f" : _c_to_f +_TEMP_CONV_METHODS = {"c" : _k_to_c, + "f" : _k_to_f } +def dealias_and_clean_unit(unit): + cleaned_unit = " ".join(unit.lower().split()) + dealiased = _UNIT_ALIASES.get(cleaned_unit, None) + + return cleaned_unit if dealiased is None else dealiased + def check_units(unit, unit_type): - unitl = unit.lower() - if unitl not in _VALID_UNITS[unit_type]: + u_cleaned = dealias_and_clean_unit(unit) + if u_cleaned not in _VALID_UNITS[unit_type]: raise ValueError("invalid unit type '%s'" % unit) def do_conversion(var, vartype, var_unit, dest_unit): + u_cleaned = dealias_and_clean_unit(dest_unit) if vartype != "temp": - return _apply_conv_fact(var, vartype, - var_unit.lower(), dest_unit.lower()) + return _apply_conv_fact(var, vartype, var_unit.lower(), u_cleaned) else: - return _apply_temp_conv(var, var_unit.lower(), dest_unit.lower()) + return _apply_temp_conv(var, var_unit.lower(), u_cleaned) diff --git a/src/wrf/uvmet.py b/src/wrf/uvmet.py index 362844b..0007e82 100755 --- a/src/wrf/uvmet.py +++ b/src/wrf/uvmet.py @@ -15,10 +15,10 @@ from .metadecorators import set_wind_metadata from .util import extract_vars, extract_global_attrs, either -@convert_units("wind", "mps") +@convert_units("wind", "m s-1") def _get_uvmet(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None, - ten_m=False, units ="mps"): + ten_m=False, units ="m s-1"): """ Return a tuple of u,v with the winds rotated in to earth space""" if not ten_m: @@ -135,7 +135,7 @@ def _get_uvmet(wrfnc, timeidx=0, method="cat", squeeze=True, wspd_wdir=False) def get_uvmet(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None, - units="mps"): + units="m s-1"): return _get_uvmet(wrfnc, timeidx, method, squeeze, cache, meta, _key, False, units) @@ -148,7 +148,7 @@ def get_uvmet(wrfnc, timeidx=0, method="cat", squeeze=True, wspd_wdir=False) def get_uvmet10(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None, - units="mps"): + units="m s-1"): return _get_uvmet(wrfnc, timeidx, method, squeeze, cache, meta, _key, True, units) @@ -161,7 +161,7 @@ def get_uvmet10(wrfnc, timeidx=0, method="cat", squeeze=True, wspd_wdir=True) def get_uvmet_wspd_wdir(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None, - units="mps"): + units="m s-1"): uvmet = _get_uvmet(wrfnc, timeidx, method, squeeze, cache, meta, _key, False, units) @@ -177,7 +177,7 @@ def get_uvmet_wspd_wdir(wrfnc, timeidx=0, method="cat", squeeze=True, wspd_wdir=True) def get_uvmet10_wspd_wdir(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None, - units="mps"): + units="m s-1"): uvmet10 = _get_uvmet(wrfnc, timeidx, method, squeeze, cache, meta, _key, True, units) diff --git a/src/wrf/wind.py b/src/wrf/wind.py index 22de953..c5dd57f 100755 --- a/src/wrf/wind.py +++ b/src/wrf/wind.py @@ -10,8 +10,8 @@ from .decorators import convert_units from .metadecorators import set_wind_metadata -@convert_units("wind", "mps") -def _calc_wspd(u, v, units="mps"): +@convert_units("wind", "m s-1") +def _calc_wspd(u, v, units="m s-1"): return np.sqrt(u**2 + v**2) @@ -50,10 +50,10 @@ def _calc_wspd_wdir(u, v, two_d, units): wind_ncvar=True, two_d=False, wspd_wdir=False) -@convert_units("wind", "mps") +@convert_units("wind", "m s-1") def get_u_destag(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None, - units="mps"): + units="m s-1"): varname = either("U", "UU")(wrfnc) u_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze, cache, meta=False, _key=_key) @@ -68,10 +68,10 @@ def get_u_destag(wrfnc, timeidx=0, method="cat", squeeze=True, two_d=False, wind_ncvar=True, wspd_wdir=False) -@convert_units("wind", "mps") +@convert_units("wind", "m s-1") def get_v_destag(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None, - units="mps"): + units="m s-1"): varname = either("V", "VV")(wrfnc) v_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze, cache, meta=False, _key=_key) @@ -86,10 +86,10 @@ def get_v_destag(wrfnc, timeidx=0, method="cat", squeeze=True, two_d=False, wind_ncvar=True, wspd_wdir=False) -@convert_units("wind", "mps") +@convert_units("wind", "m s-1") def get_w_destag(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None, - units="mps"): + units="m s-1"): w_vars = extract_vars(wrfnc, timeidx, "W", method, squeeze, cache, meta=False, _key=_key) w = destagger(w_vars["W"], -3) @@ -103,7 +103,7 @@ def get_w_destag(wrfnc, timeidx=0, method="cat", squeeze=True, wspd_wdir=True) def get_destag_wspd_wdir(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None, - units="mps"): + units="m s-1"): varname = either("U", "UU")(wrfnc) u_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze, cache, meta=False, _key=_key) @@ -124,7 +124,7 @@ def get_destag_wspd_wdir(wrfnc, timeidx=0, method="cat", wspd_wdir=True) def get_destag_wspd_wdir10(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None, - units="mps"): + units="m s-1"): varname = either("U10", "UU")(wrfnc) u_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze, cache, diff --git a/test/test_units.py b/test/test_units.py new file mode 100644 index 0000000..40c07eb --- /dev/null +++ b/test/test_units.py @@ -0,0 +1,56 @@ +import sys +import unittest as ut + +import numpy.testing as nt +import numpy as np +import numpy.ma as ma +from netCDF4 import Dataset as nc + +from wrf import getvar + +TEST_FILE = "/Users/ladwig/Documents/wrf_files/wrfout_d01_2010-06-13_21:00:00" + +# Python 3 +if sys.version_info > (3,): + xrange = range + + +class TestUnits(ut.TestCase): + longMessage = True + + def test_temp_units(self): + wrfnc = nc(TEST_FILE) + + for units in ("K", "degC", "degF"): + var = getvar(wrfnc, "temp", units=units) + + self.assertEqual(var.attrs["units"], units) + + def test_wind_units(self): + wrfnc = nc(TEST_FILE) + + for units in ("m s-1", "kt", "mi h-1", "km h-1", "ft s-1"): + var = getvar(wrfnc, "uvmet", units=units) + + self.assertEqual(var.attrs["units"], units) + + def test_pres_units(self): + wrfnc = nc(TEST_FILE) + + for units in ("Pa", "hPa", "mb", "torr", "mmHg", "atm"): + var = getvar(wrfnc, "slp", units=units) + + self.assertEqual(var.attrs["units"], units) + + def test_height_units(self): + wrfnc = nc(TEST_FILE) + + for units in ("m", "km", "dam", "ft", "mi"): + var = getvar(wrfnc, "z", units=units) + + self.assertEqual(var.attrs["units"], units) + + + +if __name__ == "__main__": + ut.main() \ No newline at end of file