Browse Source

Merge branch 'release/1.3.2'

main 1.3.2
Bill Ladwig 7 years ago
parent
commit
5682aa5007
  1. 3
      .circleci/conda_env.yml
  2. 7
      .circleci/config.yml
  3. 10
      README.md
  4. 5
      build_scripts/gnu_no_omp.sh
  5. 7
      build_scripts/gnu_omp.sh
  6. 2
      build_scripts/win_msvc_mingw_no_omp.bat
  7. 2
      build_scripts/win_msvc_mingw_omp.bat
  8. 79
      chey_intel.py
  9. 10
      doc/source/_templates/product_table.txt
  10. 63
      doc/source/contrib.rst
  11. 10
      doc/source/index.rst
  12. 3
      doc/source/internal_api/index.rst
  13. 12
      doc/source/new.rst
  14. 62
      fortran/build_help/sub_sizes.py
  15. 8
      fortran/rip_cape.f90
  16. 36
      fortran/wrf_wind.f90
  17. 144
      setup.py
  18. 21
      src/wrf/__init__.py
  19. 55
      src/wrf/api.py
  20. 139
      src/wrf/cache.py
  21. 2349
      src/wrf/computation.py
  22. 104
      src/wrf/config.py
  23. 43
      src/wrf/constants.py
  24. 243
      src/wrf/coordpair.py
  25. 456
      src/wrf/decorators.py
  26. 56
      src/wrf/destag.py
  27. 1281
      src/wrf/extension.py
  28. 809
      src/wrf/g_cape.py
  29. 688
      src/wrf/g_cloudfrac.py
  30. 149
      src/wrf/g_ctt.py
  31. 228
      src/wrf/g_dbz.py
  32. 191
      src/wrf/g_dewpoint.py
  33. 533
      src/wrf/g_geoht.py
  34. 233
      src/wrf/g_helicity.py
  35. 816
      src/wrf/g_latlon.py
  36. 87
      src/wrf/g_omega.py
  37. 12
      src/wrf/g_precip.py
  38. 183
      src/wrf/g_pressure.py
  39. 95
      src/wrf/g_pw.py
  40. 176
      src/wrf/g_rh.py
  41. 106
      src/wrf/g_slp.py
  42. 623
      src/wrf/g_temp.py
  43. 97
      src/wrf/g_terrain.py
  44. 161
      src/wrf/g_times.py
  45. 1007
      src/wrf/g_uvmet.py
  46. 179
      src/wrf/g_vorticity.py
  47. 989
      src/wrf/g_wind.py
  48. 71
      src/wrf/geobnds.py
  49. 945
      src/wrf/interp.py
  50. 454
      src/wrf/interputils.py
  51. 547
      src/wrf/latlonutils.py
  52. 1841
      src/wrf/metadecorators.py
  53. 988
      src/wrf/projection.py
  54. 13
      src/wrf/projutils.py
  55. 118
      src/wrf/py3compat.py
  56. 494
      src/wrf/routines.py
  57. 463
      src/wrf/specialdec.py
  58. 440
      src/wrf/units.py
  59. 3505
      src/wrf/util.py
  60. 3
      src/wrf/version.py
  61. 32
      test/cachetest.py
  62. 123
      test/ci_tests/make_test_file.py
  63. 208
      test/ci_tests/utests.py
  64. 536
      test/comp_utest.py
  65. 17
      test/ctt_test.py
  66. 10
      test/generator_test.py
  67. 40
      test/misc/extract_one_time.py
  68. 70
      test/misc/loop_and_fill_meta.py
  69. 43
      test/misc/mocktest.py
  70. 188
      test/misc/projtest.py
  71. 58
      test/misc/quiver_test.py
  72. 14
      test/misc/reduce_file.py
  73. 29
      test/misc/snippet.py
  74. 34
      test/misc/varcache.py
  75. 19
      test/misc/viewtest.py
  76. 230
      test/misc/wps.py
  77. 0
      test/ncl/listBug.ncl
  78. 30
      test/ncl/ncl_get_var.ncl
  79. 92
      test/ncl/ncl_vertcross.ncl
  80. 81
      test/ncl/refl10_cross.ncl
  81. 26
      test/ncl/rotated_test.ncl
  82. 21
      test/ncl/test_this.ncl
  83. 0
      test/ncl/test_vinterp.ncl
  84. 416
      test/ncl/wrf_user_vertcross.ncl
  85. 30
      test/snippet.py
  86. 80
      test/test_filevars.py
  87. 103
      test/test_inputs.py
  88. 42
      test/test_multi_cache.py
  89. 91
      test/test_omp.py
  90. 454
      test/test_proj_params.py
  91. 33
      test/test_units.py
  92. 1200
      test/utests.py

3
.circleci/conda_env.yml

@ -2,7 +2,8 @@ name: wrf-python
channels: channels:
- conda-forge - conda-forge
dependencies: dependencies:
- gcc - gcc_linux-64
- gfortran_linux-64
- setuptools - setuptools
- python - python
- numpy - numpy

7
.circleci/config.yml

@ -46,11 +46,14 @@ jobs:
name: build wrf-python name: build wrf-python
command: | command: |
source ~/miniconda3/bin/activate wrf-python source ~/miniconda3/bin/activate wrf-python
unset FFLAGS
unset LDFLAGS
unset CFLAGS
cd fortran/build_help cd fortran/build_help
gfortran -o sizes -fopenmp omp_sizes.f90 $FC -o sizes -fopenmp omp_sizes.f90
LD_LIBRARY_PATH=~/miniconda3/envs/wrf-python/lib python sub_sizes.py LD_LIBRARY_PATH=~/miniconda3/envs/wrf-python/lib python sub_sizes.py
cd .. cd ..
gfortran -E ompgen.F90 -fopenmp -cpp -o omp.f90 $FC -E ompgen.F90 -fopenmp -cpp -o omp.f90
cd .. cd ..
python setup.py config_fc --f90flags="-mtune=generic -fopenmp" build_ext --libraries="gomp" build python setup.py config_fc --f90flags="-mtune=generic -fopenmp" build_ext --libraries="gomp" build
python setup.py install --single-version-externally-managed --record=record.txt python setup.py install --single-version-externally-managed --record=record.txt

10
README.md

@ -23,3 +23,13 @@ Citation
Ladwig, W. (2017). wrf-python (Version x.x.x) [Software]. Boulder, Colorado: UCAR/NCAR. https://doi.org/10.5065/D6W094P1 Ladwig, W. (2017). wrf-python (Version x.x.x) [Software]. Boulder, Colorado: UCAR/NCAR. https://doi.org/10.5065/D6W094P1
Note: The version number x.x.x should be set to the version of wrf-python that you are using. Note: The version number x.x.x should be set to the version of wrf-python that you are using.
--------------------
*The National Center for Atmospheric Research is sponsored by the National
Science Foundation. Any opinions, findings and conclusions or recommendations
expressed in this material do not necessarily reflect the views of the
National Science Foundation.*

5
build_scripts/gnu_no_omp.sh

@ -1,8 +1,9 @@
#!/bin/bash #!/bin/bash
unset LDFLAGS
cd ../fortran cd ../fortran
gfortran -E ompgen.F90 -cpp -o omp.f90 $FC -E ompgen.F90 -cpp -o omp.f90
#f2py *.f90 -m _wrffortran -h wrffortran.pyf --overwrite-signature
cd .. cd ..
python setup.py clean --all python setup.py clean --all

7
build_scripts/gnu_omp.sh

@ -1,12 +1,13 @@
#!/bin/bash #!/bin/bash
unset LDFLAGS
cd ../fortran/build_help cd ../fortran/build_help
gfortran -o sizes -fopenmp omp_sizes.f90 $FC -o sizes -fopenmp omp_sizes.f90
python sub_sizes.py python sub_sizes.py
cd .. cd ..
gfortran -E ompgen.F90 -fopenmp -cpp -o omp.f90 $FC -E ompgen.F90 -fopenmp -cpp -o omp.f90
#f2py *.f90 -m _wrffortran -h wrffortran.pyf --overwrite-signature
cd .. cd ..
python setup.py clean --all python setup.py clean --all

2
build_scripts/win_msvc_mingw_no_omp.bat

@ -1,7 +1,5 @@
cd ../fortran cd ../fortran
CALL gfortran -E ompgen.F90 -cpp -o omp.f90 CALL gfortran -E ompgen.F90 -cpp -o omp.f90
REM Wildcards not working on Windows for some reason
REM CALL f2py -m _wrffortran -h wrffortran.pyf --overwrite-signature wrf_constants.f90 wrf_testfunc.f90 wrf_user.f90 rip_cape.f90 wrf_cloud_fracf.f90 wrf_fctt.f90 wrf_user_dbz.f90 wrf_relhl.f90 calc_uh.f90 wrf_user_latlon_routines.f90 wrf_pvo.f90 eqthecalc.f90 wrf_rip_phys_routines.f90 wrf_pw.f90 wrf_vinterp.f90 wrf_wind.f90 omp.f90
cd .. cd ..
CALL python setup.py clean --all CALL python setup.py clean --all

2
build_scripts/win_msvc_mingw_omp.bat

@ -4,8 +4,6 @@ CALL python sub_sizes.py
cd .. cd ..
CALL gfortran -E ompgen.F90 -cpp -fopenmp -o omp.f90 CALL gfortran -E ompgen.F90 -cpp -fopenmp -o omp.f90
REM Wildcards not working on Windows for some reason
REM CALL f2py -m _wrffortran -h wrffortran.pyf --overwrite-signature wrf_constants.f90 wrf_testfunc.f90 wrf_user.f90 rip_cape.f90 wrf_cloud_fracf.f90 wrf_fctt.f90 wrf_user_dbz.f90 wrf_relhl.f90 calc_uh.f90 wrf_user_latlon_routines.f90 wrf_pvo.f90 eqthecalc.f90 wrf_rip_phys_routines.f90 wrf_pw.f90 wrf_vinterp.f90 wrf_wind.f90 omp.f90
cd .. cd ..
CALL python setup.py clean --all CALL python setup.py clean --all

79
chey_intel.py

@ -13,7 +13,8 @@ compilers = ['IntelFCompiler', 'IntelVisualFCompiler',
def intel_version_match(type): def intel_version_match(type):
# Match against the important stuff in the version string # Match against the important stuff in the version string
return simple_version_match(start=r'Intel.*?Fortran.*?(?:%s).*?Version' % (type,)) return simple_version_match(
start=r'Intel.*?Fortran.*?(?:%s).*?Version'.format(type,))
class BaseIntelFCompiler(FCompiler): class BaseIntelFCompiler(FCompiler):
@ -36,13 +37,13 @@ class IntelFCompiler(BaseIntelFCompiler):
possible_executables = ['ifort', 'ifc'] possible_executables = ['ifort', 'ifc']
executables = { executables = {
'version_cmd' : None, # set by update_executables 'version_cmd': None, # set by update_executables
'compiler_f77' : [None, "-72", "-w90", "-w95"], 'compiler_f77': [None, "-72", "-w90", "-w95"],
'compiler_f90' : [None], 'compiler_f90': [None],
'compiler_fix' : [None, "-FI"], 'compiler_fix': [None, "-FI"],
'linker_so' : ["<F90>", "-shared"], 'linker_so': ["<F90>", "-shared"],
'archiver' : ["ar", "-cr"], 'archiver': ["ar", "-cr"],
'ranlib' : ["ranlib"] 'ranlib': ["ranlib"]
} }
pic_flags = ['-fPIC'] pic_flags = ['-fPIC']
@ -89,13 +90,13 @@ class IntelItaniumFCompiler(IntelFCompiler):
possible_executables = ['ifort', 'efort', 'efc'] possible_executables = ['ifort', 'efort', 'efc']
executables = { executables = {
'version_cmd' : None, 'version_cmd': None,
'compiler_f77' : [None, "-FI", "-w90", "-w95"], 'compiler_f77': [None, "-FI", "-w90", "-w95"],
'compiler_fix' : [None, "-FI"], 'compiler_fix': [None, "-FI"],
'compiler_f90' : [None], 'compiler_f90': [None],
'linker_so' : ['<F90>', "-shared"], 'linker_so': ['<F90>', "-shared"],
'archiver' : ["ar", "-cr"], 'archiver': ["ar", "-cr"],
'ranlib' : ["ranlib"] 'ranlib': ["ranlib"]
} }
@ -104,18 +105,19 @@ class IntelEM64TFCompiler(IntelFCompiler):
compiler_aliases = () compiler_aliases = ()
description = 'Intel Fortran Compiler for 64-bit apps' description = 'Intel Fortran Compiler for 64-bit apps'
version_match = intel_version_match('EM64T-based|Intel\\(R\\) 64|64|IA-64|64-bit') version_match = intel_version_match(
'EM64T-based|Intel\\(R\\) 64|64|IA-64|64-bit')
possible_executables = ['ifort', 'efort', 'efc'] possible_executables = ['ifort', 'efort', 'efc']
executables = { executables = {
'version_cmd' : None, 'version_cmd': None,
'compiler_f77' : [None, "-FI"], 'compiler_f77': [None, "-FI"],
'compiler_fix' : [None, "-FI"], 'compiler_fix': [None, "-FI"],
'compiler_f90' : [None], 'compiler_f90': [None],
'linker_so' : ['<F90>', "-shared"], 'linker_so': ['<F90>', "-shared"],
'archiver' : ["ar", "-cr"], 'archiver': ["ar", "-cr"],
'ranlib' : ["ranlib"] 'ranlib': ["ranlib"]
} }
def get_flags(self): def get_flags(self):
@ -147,13 +149,13 @@ class IntelVisualFCompiler(BaseIntelFCompiler):
possible_executables = ['ifort', 'ifl'] possible_executables = ['ifort', 'ifl']
executables = { executables = {
'version_cmd' : None, 'version_cmd': None,
'compiler_f77' : [None], 'compiler_f77': [None],
'compiler_fix' : [None], 'compiler_fix': [None],
'compiler_f90' : [None], 'compiler_f90': [None],
'linker_so' : [None], 'linker_so': [None],
'archiver' : [ar_exe, "/verbose", "/OUT:"], 'archiver': [ar_exe, "/verbose", "/OUT:"],
'ranlib' : None 'ranlib': None
} }
compile_switch = '/c ' compile_switch = '/c '
@ -163,7 +165,8 @@ class IntelVisualFCompiler(BaseIntelFCompiler):
module_include_switch = '/I' module_include_switch = '/I'
def get_flags(self): def get_flags(self):
opt = ['/nologo', '/MD', '/nbs', '/names:lowercase', '/assume:underscore'] opt = ['/nologo', '/MD', '/nbs', '/names:lowercase',
'/assume:underscore']
return opt return opt
def get_flags_free(self): def get_flags_free(self):
@ -192,13 +195,13 @@ class IntelItaniumVisualFCompiler(IntelVisualFCompiler):
ar_exe = IntelVisualFCompiler.ar_exe ar_exe = IntelVisualFCompiler.ar_exe
executables = { executables = {
'version_cmd' : None, 'version_cmd': None,
'compiler_f77' : [None, "-FI", "-w90", "-w95"], 'compiler_f77': [None, "-FI", "-w90", "-w95"],
'compiler_fix' : [None, "-FI", "-4L72", "-w"], 'compiler_fix': [None, "-FI", "-4L72", "-w"],
'compiler_f90' : [None], 'compiler_f90': [None],
'linker_so' : ['<F90>', "-shared"], 'linker_so': ['<F90>', "-shared"],
'archiver' : [ar_exe, "/verbose", "/OUT:"], 'archiver': [ar_exe, "/verbose", "/OUT:"],
'ranlib' : None 'ranlib': None
} }

10
doc/source/_templates/product_table.txt

@ -245,6 +245,16 @@
| | | | | | | | | |
| | | mi | | | | | mi | |
+--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+
| height_agl | Model Height for Mass Grid (AGL) | m | **units** (str) : Set to desired units. Default is *'m'*. |
| | | | |
| | | km | |
| | | | |
| | | dm | |
| | | | |
| | | ft | |
| | | | |
| | | mi | |
+--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+
| zstag | Model Height for Vertically Staggered Grid | m | **msl** (boolean): Set to False to return AGL values. True is for MSL. Default is *True*. | | zstag | Model Height for Vertically Staggered Grid | m | **msl** (boolean): Set to False to return AGL values. True is for MSL. Default is *True*. |
| | | | | | | | | |
| | | km | **units** (str) : Set to desired units. Default is *'m'*. | | | | km | **units** (str) : Set to desired units. Default is *'m'*. |

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.*

3
doc/source/internal_api/index.rst

@ -23,6 +23,9 @@ The routines below are called internally by :meth:`wrf.getvar`.
wrf.g_dewpoint.get_dp_2m wrf.g_dewpoint.get_dp_2m
wrf.g_geoht.get_geopt wrf.g_geoht.get_geopt
wrf.g_geoht.get_height wrf.g_geoht.get_height
wrf.g_geoht.get_height_agl
wrf.g_geoht.get_stag_geopt
wrf.g_geoht.get_stag_height
wrf.g_helicity.get_srh wrf.g_helicity.get_srh
wrf.g_helicity.get_uh wrf.g_helicity.get_uh
wrf.g_omega.get_omega wrf.g_omega.get_omega

12
doc/source/new.rst

@ -4,6 +4,18 @@ What's New
Releases Releases
------------- -------------
v1.3.2 (February 2019)
^^^^^^^^^^^^^^^^^^^^^^^^^
- Release 1.3.2
- Coordinate name index positions are no longer assumed and are searched
instead. Some users use xarray to rewrite WRF output files, and xarray
might reorder the coordinate name positions.
- Fixed a segfault issue with CAPE when more than 150 vertical levels are
used (e.g. LES runs).
- setup.py will now bootstrap the numpy installation (thanks bbonenfant!).
v1.3.1 (January 2019) v1.3.1 (January 2019)
^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^

62
fortran/build_help/sub_sizes.py

@ -3,55 +3,55 @@ import os
from string import Template from string import Template
import sys import sys
def main(): def main():
print("Running sub_sizes")
print ("Running sub_sizes")
try: try:
result = subprocess.check_output(["./sizes"]) result = subprocess.check_output(["./sizes"])
except: except OSError:
result = subprocess.check_output(["sizes.exe"]) result = subprocess.check_output(["sizes.exe"])
split_result = result.split() split_result = result.split()
if sys.version_info >= (3, ): if sys.version_info >= (3, ):
if split_result[0].strip().decode() != "SIZES": if split_result[0].strip().decode() != "SIZES":
raise ValueError("First line is not SIZES") raise ValueError("First line is not SIZES")
subs = {"FOMP_SCHED_KIND" : split_result[1].strip().decode(), subs = {"FOMP_SCHED_KIND": split_result[1].strip().decode(),
"FOMP_LOCK_KIND" : split_result[2].strip().decode(), "FOMP_LOCK_KIND": split_result[2].strip().decode(),
"FOMP_NEST_LOCK_KIND" : split_result[3].strip().decode(), "FOMP_NEST_LOCK_KIND": split_result[3].strip().decode(),
"FOMP_SCHED_STATIC" : split_result[4].strip().decode(), "FOMP_SCHED_STATIC": split_result[4].strip().decode(),
"FOMP_SCHED_DYNAMIC" : split_result[5].strip().decode(), "FOMP_SCHED_DYNAMIC": split_result[5].strip().decode(),
"FOMP_SCHED_GUIDED" : split_result[6].strip().decode(), "FOMP_SCHED_GUIDED": split_result[6].strip().decode(),
"FOMP_SCHED_AUTO" : split_result[7].strip().decode() "FOMP_SCHED_AUTO": split_result[7].strip().decode()
} }
else: else:
if split_result[0].strip() != "SIZES": if split_result[0].strip() != "SIZES":
raise ValueError("First line is not SIZES") raise ValueError("First line is not SIZES")
subs = {"FOMP_SCHED_KIND" : split_result[1].strip(), subs = {"FOMP_SCHED_KIND": split_result[1].strip(),
"FOMP_LOCK_KIND" : split_result[2].strip(), "FOMP_LOCK_KIND": split_result[2].strip(),
"FOMP_NEST_LOCK_KIND" : split_result[3].strip(), "FOMP_NEST_LOCK_KIND": split_result[3].strip(),
"FOMP_SCHED_STATIC" : split_result[4].strip(), "FOMP_SCHED_STATIC": split_result[4].strip(),
"FOMP_SCHED_DYNAMIC" : split_result[5].strip(), "FOMP_SCHED_DYNAMIC": split_result[5].strip(),
"FOMP_SCHED_GUIDED" : split_result[6].strip(), "FOMP_SCHED_GUIDED": split_result[6].strip(),
"FOMP_SCHED_AUTO" : split_result[7].strip() "FOMP_SCHED_AUTO": split_result[7].strip()
} }
ompgen_temp_path = os.path.join("..", "ompgen.F90.template") ompgen_temp_path = os.path.join("..", "ompgen.F90.template")
ompgen_out_path = os.path.join("..", "ompgen.F90") ompgen_out_path = os.path.join("..", "ompgen.F90")
with open(ompgen_temp_path, "r") as ompgen_in: with open(ompgen_temp_path, "r") as ompgen_in:
ompgen_template = Template(ompgen_in.read()) ompgen_template = Template(ompgen_in.read())
ompgen_string = ompgen_template.substitute(subs) ompgen_string = ompgen_template.substitute(subs)
with open(ompgen_out_path, "w") as ompgen_out: with open(ompgen_out_path, "w") as ompgen_out:
ompgen_out.write(ompgen_string) ompgen_out.write(ompgen_string)
print("End sub_sizes")
print ("End sub_sizes")
if __name__ == "__main__": if __name__ == "__main__":
main() main()

8
fortran/rip_cape.f90

@ -316,7 +316,9 @@ SUBROUTINE DCAPECALC3D(prs,tmk,qvp,ght,ter,sfp,cape,cin,&
REAL(KIND=8) :: facden, qvplift, tmklift, tvenv, tvlift, ghtlift REAL(KIND=8) :: facden, qvplift, tmklift, tvenv, tvlift, ghtlift
REAL(KIND=8) :: eslift, tmkenv, qvpenv, tonpsadiabat REAL(KIND=8) :: eslift, tmkenv, qvpenv, tonpsadiabat
REAL(KIND=8) :: benamin, dz REAL(KIND=8) :: benamin, dz
REAL(KIND=8), DIMENSION(150) :: buoy, zrel, benaccum ! Set a safety factor of 2*mkzh + 1 instead of previously chosen
! 150 levels
REAL(KIND=8), DIMENSION(2*mkzh + 1) :: buoy, zrel, benaccum
REAL(KIND=8), DIMENSION(150) :: psadithte, psadiprs REAL(KIND=8), DIMENSION(150) :: psadithte, psadiprs
REAL(KIND=8), DIMENSION(150,150) :: psaditmk REAL(KIND=8), DIMENSION(150,150) :: psaditmk
LOGICAL :: elfound LOGICAL :: elfound
@ -646,7 +648,9 @@ SUBROUTINE DCAPECALC2D(prs,tmk,qvp,ght,ter,sfp,cape,cin,&
REAL(KIND=8) :: facden, qvplift, tmklift, tvenv, tvlift, ghtlift REAL(KIND=8) :: facden, qvplift, tmklift, tvenv, tvlift, ghtlift
REAL(KIND=8) :: eslift, tmkenv, qvpenv, tonpsadiabat REAL(KIND=8) :: eslift, tmkenv, qvpenv, tonpsadiabat
REAL(KIND=8) :: benamin, dz, pup, pdn REAL(KIND=8) :: benamin, dz, pup, pdn
REAL(KIND=8), DIMENSION(150) :: buoy, zrel, benaccum ! Set a safety factor of 2*mkzh + 1 instead of previously chosen
! 150 levels
REAL(KIND=8), DIMENSION(2*mkzh + 1) :: buoy, zrel, benaccum
REAL(KIND=8), DIMENSION(150) :: psadithte, psadiprs REAL(KIND=8), DIMENSION(150) :: psadithte, psadiprs
REAL(KIND=8), DIMENSION(150,150) :: psaditmk REAL(KIND=8), DIMENSION(150,150) :: psaditmk
LOGICAL :: elfound LOGICAL :: elfound

36
fortran/wrf_wind.f90

@ -1,23 +1,21 @@
! NCLFORTSTART ! NCLFORTSTART
SUBROUTINE DCOMPUTEWSPD(wspd, u, v, nx, ny) SUBROUTINE DCOMPUTEWSPD(wspd, u, v, n)
IMPLICIT NONE IMPLICIT NONE
!f2py threadsafe !f2py threadsafe
!f2py intent(in,out) :: wspd !f2py intent(in,out) :: wspd
INTEGER, INTENT(IN) :: nx, ny INTEGER, INTENT(IN) :: n
REAL(KIND=8), DIMENSION(nx,ny), INTENT(OUT) :: wspd REAL(KIND=8), DIMENSION(n), INTENT(OUT) :: wspd
REAL(KIND=8), DIMENSION(nx,ny), INTENT(IN) :: u, v REAL(KIND=8), DIMENSION(n), INTENT(IN) :: u, v
! NCLEND ! NCLEND
INTEGER i, j INTEGER i
!$OMP PARALLEL DO COLLAPSE(2) SCHEDULE(runtime) !$OMP PARALLEL DO SCHEDULE(runtime)
DO j = 1,ny DO i = 1,n
DO i = 1,nx wspd(i) = SQRT(u(i)*u(i) + v(i)*v(i))
wspd(i,j) = SQRT(u(i,j)*u(i,j) + v(i,j)*v(i,j))
END DO
END DO END DO
!$OMP END PARALLEL DO !$OMP END PARALLEL DO
@ -25,7 +23,7 @@ END SUBROUTINE DCOMPUTEWSPD
! NCLFORTSTART ! NCLFORTSTART
SUBROUTINE DCOMPUTEWDIR(wdir, u, v, nx, ny) SUBROUTINE DCOMPUTEWDIR(wdir, u, v, n)
USE wrf_constants, ONLY : DEG_PER_RAD USE wrf_constants, ONLY : DEG_PER_RAD
IMPLICIT NONE IMPLICIT NONE
@ -33,18 +31,16 @@ SUBROUTINE DCOMPUTEWDIR(wdir, u, v, nx, ny)
!f2py threadsafe !f2py threadsafe
!f2py intent(in,out) :: wdir !f2py intent(in,out) :: wdir
INTEGER, INTENT(IN) :: nx, ny INTEGER, INTENT(IN) :: n
REAL(KIND=8), DIMENSION(nx,ny), INTENT(OUT) :: wdir REAL(KIND=8), DIMENSION(n), INTENT(OUT) :: wdir
REAL(KIND=8), DIMENSION(nx,ny), INTENT(IN) :: u, v REAL(KIND=8), DIMENSION(n), INTENT(IN) :: u, v
! NCLEND ! NCLEND
INTEGER i, j INTEGER i
!$OMP PARALLEL DO COLLAPSE(2) SCHEDULE(runtime) !$OMP PARALLEL DO SCHEDULE(runtime)
DO j = 1,ny DO i = 1,n
DO i = 1,nx wdir(i) = MOD(270.0 - ATAN2(v(i), u(i)) * DEG_PER_RAD, 360.)
wdir(i,j) = MOD(270.0 - ATAN2(v(i,j), u(i,j)) * DEG_PER_RAD, 360.)
END DO
END DO END DO
!$OMP END PARALLEL DO !$OMP END PARALLEL DO

144
setup.py

@ -3,13 +3,21 @@ import sys
import setuptools import setuptools
import socket import socket
# Bootstrap a numpy installation before trying to import it.
import imp
try:
imp.find_module('numpy')
except ImportError:
import subprocess
subprocess.call([sys.executable, '-m', 'pip', 'install', 'numpy'])
if not socket.gethostname().startswith("cheyenne"): if not socket.gethostname().startswith("cheyenne"):
import numpy.distutils.core import numpy.distutils.core
else: else:
import chey_intel import chey_intel
import numpy.distutils.core import numpy.distutils.core
import numpy.distutils.fcompiler.intel import numpy.distutils.fcompiler.intel
numpy.distutils.fcompiler.intel.IntelFCompiler = chey_intel.IntelFCompiler numpy.distutils.fcompiler.intel.IntelFCompiler = chey_intel.IntelFCompiler
numpy.distutils.fcompiler.intel.IntelVisualFCompiler = ( numpy.distutils.fcompiler.intel.IntelVisualFCompiler = (
chey_intel.IntelVisualFCompiler) chey_intel.IntelVisualFCompiler)
@ -23,33 +31,33 @@ else:
chey_intel.IntelEM64TFCompiler) chey_intel.IntelEM64TFCompiler)
ext1 = numpy.distutils.core.Extension( ext1 = numpy.distutils.core.Extension(
name = "wrf._wrffortran", name="wrf._wrffortran",
sources = ["fortran/wrf_constants.f90", sources=["fortran/wrf_constants.f90",
"fortran/wrf_testfunc.f90", "fortran/wrf_testfunc.f90",
"fortran/wrf_user.f90", "fortran/wrf_user.f90",
"fortran/rip_cape.f90", "fortran/rip_cape.f90",
"fortran/wrf_cloud_fracf.f90", "fortran/wrf_cloud_fracf.f90",
"fortran/wrf_fctt.f90", "fortran/wrf_fctt.f90",
"fortran/wrf_user_dbz.f90", "fortran/wrf_user_dbz.f90",
"fortran/wrf_relhl.f90", "fortran/wrf_relhl.f90",
"fortran/calc_uh.f90", "fortran/calc_uh.f90",
"fortran/wrf_user_latlon_routines.f90", "fortran/wrf_user_latlon_routines.f90",
"fortran/wrf_pvo.f90", "fortran/wrf_pvo.f90",
"fortran/eqthecalc.f90", "fortran/eqthecalc.f90",
"fortran/wrf_rip_phys_routines.f90", "fortran/wrf_rip_phys_routines.f90",
"fortran/wrf_pw.f90", "fortran/wrf_pw.f90",
"fortran/wrf_vinterp.f90", "fortran/wrf_vinterp.f90",
"fortran/wrf_wind.f90", "fortran/wrf_wind.f90",
"fortran/omp.f90"] "fortran/omp.f90"]
) )
with open("src/wrf/version.py") as f: with open("src/wrf/version.py") as f:
exec(f.read()) exec(f.read())
on_rtd = os.environ.get("READTHEDOCS", None) == "True" on_rtd = os.environ.get("READTHEDOCS", None) == "True"
#on_rtd=True # on_rtd=True
if on_rtd: if on_rtd:
if sys.version_info < (3,3): if sys.version_info < (3, 3):
requirements = ["mock"] # for python2 and python < 3.3 requirements = ["mock"] # for python2 and python < 3.3
else: else:
requirements = [] # for >= python3.3 requirements = [] # for >= python3.3
@ -59,53 +67,53 @@ else:
# Place install_requires into the text file "requirements.txt" # Place install_requires into the text file "requirements.txt"
with open("requirements.txt") as f2: with open("requirements.txt") as f2:
requirements = f2.read().strip().splitlines() requirements = f2.read().strip().splitlines()
#if sys.version_info < (3,3): # if sys.version_info < (3,3):
# requirements.append("mock") # requirements.append("mock")
ext_modules = [ext1] ext_modules = [ext1]
numpy.distutils.core.setup( numpy.distutils.core.setup(
author = "Bill Ladwig", author="Bill Ladwig",
author_email = "ladwig@ucar.edu", author_email="ladwig@ucar.edu",
description = "Diagnostic and interpolation routines for WRF-ARW data.", description="Diagnostic and interpolation routines for WRF-ARW data.",
long_description = ("A collection of diagnostic and interpolation routines " long_description=("A collection of diagnostic and interpolation "
"to be used with WRF-ARW data.\n\n" "routines to be used with WRF-ARW data.\n\n"
"GitHub Repository:\n\n" "GitHub Repository:\n\n"
"https://github.com/NCAR/wrf-python\n\n" "https://github.com/NCAR/wrf-python\n\n"
"Documentation:\n\n" "Documentation:\n\n"
"http://wrf-python.rtfd.org\n"), "http://wrf-python.rtfd.org\n"),
url = "https://github.com/NCAR/wrf-python", url="https://github.com/NCAR/wrf-python",
keywords = ["python", "wrf-python", "wrf", "forecast", "model", keywords=["python", "wrf-python", "wrf", "forecast", "model",
"weather research and forecasting", "interpolation", "weather research and forecasting", "interpolation",
"plotting", "plots", "meteorology", "nwp", "plotting", "plots", "meteorology", "nwp",
"numerical weather prediction", "diagnostic", "numerical weather prediction", "diagnostic",
"science", "numpy"], "science", "numpy"],
install_requires = requirements, install_requires=requirements,
classifiers = ["Development Status :: 5 - Production/Stable", classifiers=["Development Status :: 5 - Production/Stable",
"Intended Audience :: Science/Research", "Intended Audience :: Science/Research",
"Intended Audience :: Developers", "Intended Audience :: Developers",
"License :: OSI Approved :: Apache Software License", "License :: OSI Approved :: Apache Software License",
"Programming Language :: Fortran", "Programming Language :: Fortran",
"Programming Language :: Python :: 2.7", "Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.7",
"Topic :: Scientific/Engineering :: Atmospheric Science", "Topic :: Scientific/Engineering :: Atmospheric Science",
"Topic :: Software Development", "Topic :: Software Development",
"Operating System :: POSIX", "Operating System :: POSIX",
"Operating System :: Unix", "Operating System :: Unix",
"Operating System :: MacOS", "Operating System :: MacOS",
"Operating System :: Microsoft :: Windows"], "Operating System :: Microsoft :: Windows"],
name = "wrf-python", name="wrf-python",
platforms = ["any"], platforms=["any"],
license = "Apache License 2.0", license="Apache License 2.0",
version = __version__, version=__version__,
packages = setuptools.find_packages("src"), packages=setuptools.find_packages("src"),
ext_modules = ext_modules, ext_modules=ext_modules,
package_dir = {"" : "src"}, package_dir={"": "src"},
download_url = "http://python.org/pypi/wrf-python", download_url="http://python.org/pypi/wrf-python",
package_data={"wrf" : ["data/psadilookup.dat"]}, package_data={"wrf": ["data/psadilookup.dat"]},
scripts=[] scripts=[]
) )

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

1281
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

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

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)

494
src/wrf/routines.py

@ -2,13 +2,14 @@ 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
from .g_dewpoint import get_dp, get_dp_2m from .g_dewpoint import get_dp, get_dp_2m
from .g_geoht import get_geopt, get_height, get_stag_geopt, get_stag_height from .g_geoht import (get_geopt, get_height, get_stag_geopt, get_stag_height,
get_height_agl)
from .g_helicity import get_srh, get_uh from .g_helicity import get_srh, get_uh
from .g_latlon import get_lat, get_lon from .g_latlon import get_lat, get_lon
from .g_omega import get_omega from .g_omega import get_omega
@ -16,334 +17,339 @@ 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,
"height_agl": get_height_agl,
# 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"], "height_agl": ["units"],
"cin2d_only" : ["missing"], "cape2d_only": ["missing"],
"lcl" : ["missing"], "cin2d_only": ["missing"],
"lfc" : ["missing"], "lcl": ["missing"],
"cape3d_only" : ["missing"], "lfc": ["missing"],
"cin3d_only": ["missing"], "cape3d_only": ["missing"],
"uvmet_wspd" : ["units"], "cin3d_only": ["missing"],
"uvmet_wdir" : ["units"], "uvmet_wspd": ["units"],
"uvmet10_wspd" : ["units"], "uvmet_wdir": ["units"],
"uvmet10_wdir" : ["units"], "uvmet10_wspd": ["units"],
"wspd" : ["units"], "uvmet10_wdir": ["units"],
"wdir" : ["units"], "wspd": ["units"],
"wspd10" : ["units"], "wdir": ["units"],
"wdir10" : ["units"], "wspd10": ["units"],
"low_cloudfrac" : ["vert_type", "low_thresh", "wdir10": ["units"],
"mid_thresh", "high_thresh"], "low_cloudfrac": ["vert_type", "low_thresh",
"mid_cloudfrac" : ["vert_type", "low_thresh", "mid_thresh", "high_thresh"],
"mid_thresh", "high_thresh"], "mid_cloudfrac": ["vert_type", "low_thresh",
"high_cloudfrac" : ["vert_type", "low_thresh", "mid_thresh", "high_thresh"],
"mid_thresh", "high_thresh"], "high_cloudfrac": ["vert_type", "low_thresh",
"default" : [] "mid_thresh", "high_thresh"],
} "default": []
}
_ALIASES = {"cape_2d" : "cape2d",
"cape_3d" : "cape3d", _ALIASES = {"cape_2d": "cape2d",
"eth" : "theta_e", "cape_3d": "cape3d",
"mdbz" : "maxdbz", "eth": "theta_e",
"geopotential" : "geopt", "mdbz": "maxdbz",
"helicity" : "srh", "geopotential": "geopt",
"latitude" : "lat", "helicity": "srh",
"longitude" : "lon", "latitude": "lat",
"omg" : "omega", "longitude": "lon",
"p" : "pres", "omg": "omega",
"rh2" : "rh2m", "p": "pres",
"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

3
src/wrf/version.py

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

32
test/cachetest.py

@ -1,4 +1,4 @@
from __future__ import (absolute_import, division, print_function, from __future__ import (absolute_import, division, print_function,
unicode_literals) unicode_literals)
from threading import Thread from threading import Thread
@ -9,7 +9,7 @@ except ImportError:
from collections import OrderedDict from collections import OrderedDict
import unittest as ut import unittest as ut
import numpy.testing as nt import numpy.testing as nt
from wrf.cache import cache_item, get_cached_item, _get_cache from wrf.cache import cache_item, get_cached_item, _get_cache
from wrf.config import get_cache_size from wrf.config import get_cache_size
@ -20,49 +20,49 @@ class TestThread(Thread):
self.num = num self.num = num
self.q = q self.q = q
super(TestThread, self).__init__() super(TestThread, self).__init__()
def run(self): def run(self):
for i in range(get_cache_size() + 10): for i in range(get_cache_size() + 10):
key = "A" + str(i) key = "A" + str(i)
cache_item(key, "test", i * self.num) cache_item(key, "test", i * self.num)
item = get_cached_item(key, "test") item = get_cached_item(key, "test")
if item != i * self.num: if item != i * self.num:
raise RuntimeError("cache is bogus") raise RuntimeError("cache is bogus")
cache = OrderedDict(_get_cache()) cache = OrderedDict(_get_cache())
self.q.put(cache) self.q.put(cache)
class CacheTest(ut.TestCase): class CacheTest(ut.TestCase):
longMessage = True longMessage = True
def test_thread_local(self): def test_thread_local(self):
q1 = Queue() q1 = Queue()
q2 = Queue() q2 = Queue()
thread1 = TestThread(2, q1) thread1 = TestThread(2, q1)
thread2 = TestThread(40, q2) thread2 = TestThread(40, q2)
thread1.start() thread1.start()
thread2.start() thread2.start()
result1 = q1.get(True, 1) result1 = q1.get(True, 1)
result2 = q2.get(True, 1) result2 = q2.get(True, 1)
thread1.join() thread1.join()
thread2.join() thread2.join()
print(result1) print(result1)
print(result2) print(result2)
# Result 1 and 2 shoudl be different # Result 1 and 2 shoudl be different
self.assertNotEqual(result1, result2) self.assertNotEqual(result1, result2)
# This thread should have no cache # This thread should have no cache
self.assertIsNone(_get_cache()) self.assertIsNone(_get_cache())
if __name__ == "__main__": if __name__ == "__main__":
ut.main() ut.main()

123
test/ci_tests/make_test_file.py

@ -9,19 +9,19 @@ from netCDF4 import Dataset
from wrf import (getvar, interplevel, interpline, vertcross, vinterp, py2round, from wrf import (getvar, interplevel, interpline, vertcross, vinterp, py2round,
CoordPair, ll_to_xy, xy_to_ll, to_np) CoordPair, ll_to_xy, xy_to_ll, to_np)
VARS_TO_KEEP = ("Times", "XLAT", "XLONG", "XLAT_U", "XLAT_V", "XLONG_U", VARS_TO_KEEP = ("Times", "XLAT", "XLONG", "XLAT_U", "XLAT_V", "XLONG_U",
"XLONG_V", "U", "V", "W", "PH", "PHB", "T", "P", "PB", "Q2", "XLONG_V", "U", "V", "W", "PH", "PHB", "T", "P", "PB", "Q2",
"T2", "PSFC", "U10", "V10", "XTIME", "QVAPOR", "QCLOUD", "T2", "PSFC", "U10", "V10", "XTIME", "QVAPOR", "QCLOUD",
"QGRAUP", "QRAIN", "QSNOW", "QICE", "MAPFAC_M", "MAPFAC_U", "QGRAUP", "QRAIN", "QSNOW", "QICE", "MAPFAC_M", "MAPFAC_U",
"MAPFAC_V", "F", "HGT", "RAINC", "RAINSH", "RAINNC") "MAPFAC_V", "F", "HGT", "RAINC", "RAINSH", "RAINNC")
DIMS_TO_TRIM = ("west_east", "south_north", "bottom_top", "bottom_top_stag", DIMS_TO_TRIM = ("west_east", "south_north", "bottom_top", "bottom_top_stag",
"west_east_stag", "south_north_stag") "west_east_stag", "south_north_stag")
WRF_DIAGS = ["avo", "eth", "cape_2d", "cape_3d", "ctt", "dbz", "mdbz", WRF_DIAGS = ["avo", "eth", "cape_2d", "cape_3d", "ctt", "dbz", "mdbz",
"geopt", "helicity", "lat", "lon", "omg", "p", "pressure", "geopt", "helicity", "lat", "lon", "omg", "p", "pressure",
"pvo", "pw", "rh2", "rh", "slp", "ter", "td2", "td", "tc", "pvo", "pw", "rh2", "rh", "slp", "ter", "td2", "td", "tc",
"theta", "tk", "tv", "twb", "updraft_helicity", "ua", "va", "theta", "tk", "tv", "twb", "updraft_helicity", "ua", "va",
"wa", "uvmet10", "uvmet", "z", "cfrac", "zstag"] "wa", "uvmet10", "uvmet", "z", "cfrac", "zstag"]
INTERP_METHS = ["interplevel", "vertcross", "interpline", "vinterp"] INTERP_METHS = ["interplevel", "vertcross", "interpline", "vinterp"]
@ -33,56 +33,56 @@ def copy_and_reduce(opts):
infilename = opts.filename infilename = opts.filename
outfilename = os.path.expanduser( outfilename = os.path.expanduser(
os.path.join(opts.outdir, "ci_test_file.nc")) os.path.join(opts.outdir, "ci_test_file.nc"))
with Dataset(infilename) as infile, Dataset(outfilename, "w") as outfile: with Dataset(infilename) as infile, Dataset(outfilename, "w") as outfile:
# Copy the global attributes # Copy the global attributes
outfile.setncatts(infile.__dict__) outfile.setncatts(infile.__dict__)
# Copy Dimensions # Copy Dimensions
for name, dimension in infile.dimensions.iteritems(): for name, dimension in infile.dimensions.iteritems():
if name in DIMS_TO_TRIM: if name in DIMS_TO_TRIM:
if name.find("_stag") > 0: if name.find("_stag") > 0:
dimsize = (len(dimension) / 2 dimsize = (len(dimension) / 2
if len(dimension) % 2 == 0 if len(dimension) % 2 == 0
else (len(dimension) + 1) / 2) else (len(dimension) + 1) / 2)
else: else:
dimsize = (len(dimension) / 2 dimsize = (len(dimension) / 2
if len(dimension) % 2 == 0 if len(dimension) % 2 == 0
else (len(dimension) - 1) / 2) else (len(dimension) - 1) / 2)
else: else:
dimsize = len(dimension) dimsize = len(dimension)
outfile.createDimension(name, dimsize) outfile.createDimension(name, dimsize)
# Copy Variables # Copy Variables
for name, variable in infile.variables.iteritems(): for name, variable in infile.variables.iteritems():
if name not in VARS_TO_KEEP: if name not in VARS_TO_KEEP:
continue continue
outvar = outfile.createVariable(name, variable.datatype, outvar = outfile.createVariable(name, variable.datatype,
variable.dimensions, zlib=True) variable.dimensions, zlib=True)
in_slices = tuple(slice(0, dimsize) for dimsize in outvar.shape) in_slices = tuple(slice(0, dimsize) for dimsize in outvar.shape)
outvar[:] = variable[in_slices] outvar[:] = variable[in_slices]
outvar.setncatts(infile.variables[name].__dict__) outvar.setncatts(infile.variables[name].__dict__)
def add_to_ncfile(outfile, var, varname): def add_to_ncfile(outfile, var, varname):
dim_d = dict(zip(var.dims, var.shape)) dim_d = dict(zip(var.dims, var.shape))
for dimname, dimsize in dim_d.items(): for dimname, dimsize in dim_d.items():
if dimname not in outfile.dimensions: if dimname not in outfile.dimensions:
outfile.createDimension(dimname, dimsize) outfile.createDimension(dimname, dimsize)
fill_value = None fill_value = None
if "_FillValue" in var.attrs: if "_FillValue" in var.attrs:
fill_value = var.attrs["_FillValue"] fill_value = var.attrs["_FillValue"]
ncvar = outfile.createVariable(varname, var.dtype, var.dims, ncvar = outfile.createVariable(varname, var.dtype, var.dims,
zlib=True, fill_value=fill_value) zlib=True, fill_value=fill_value)
var_vals = to_np(var) var_vals = to_np(var)
ncvar[:] = var_vals[:] ncvar[:] = var_vals[:]
@ -92,78 +92,79 @@ def make_result_file(opts):
os.path.join(opts.outdir, "ci_test_file.nc")) os.path.join(opts.outdir, "ci_test_file.nc"))
outfilename = os.path.expanduser( outfilename = os.path.expanduser(
os.path.join(opts.outdir, "ci_result_file.nc")) os.path.join(opts.outdir, "ci_result_file.nc"))
with Dataset(infilename) as infile, Dataset(outfilename, "w") as outfile: with Dataset(infilename) as infile, Dataset(outfilename, "w") as outfile:
for varname in WRF_DIAGS: for varname in WRF_DIAGS:
var = getvar(infile, varname) var = getvar(infile, varname)
add_to_ncfile(outfile, var, varname) add_to_ncfile(outfile, var, varname)
for interptype in INTERP_METHS: for interptype in INTERP_METHS:
if interptype == "interpline": if interptype == "interpline":
hts = getvar(infile, "z") hts = getvar(infile, "z")
p = getvar(infile, "pressure") p = getvar(infile, "pressure")
hts_850 = interplevel(hts, p, 850.) hts_850 = interplevel(hts, p, 850.)
add_to_ncfile(outfile, hts_850, "interplevel") add_to_ncfile(outfile, hts_850, "interplevel")
if interptype == "vertcross": if interptype == "vertcross":
hts = getvar(infile, "z") hts = getvar(infile, "z")
p = getvar(infile, "pressure") p = getvar(infile, "pressure")
pivot_point = CoordPair(hts.shape[-1] // 2, hts.shape[-2] // 2) pivot_point = CoordPair(hts.shape[-1] // 2, hts.shape[-2] // 2)
ht_cross = vertcross(hts, p, pivot_point=pivot_point, ht_cross = vertcross(hts, p, pivot_point=pivot_point,
angle=90.) angle=90.)
add_to_ncfile(outfile, ht_cross, "vertcross") add_to_ncfile(outfile, ht_cross, "vertcross")
if interptype == "interpline": if interptype == "interpline":
t2 = getvar(infile, "T2") t2 = getvar(infile, "T2")
pivot_point = CoordPair(t2.shape[-1] // 2, t2.shape[-2] // 2) pivot_point = CoordPair(t2.shape[-1] // 2, t2.shape[-2] // 2)
t2_line = interpline(t2, pivot_point=pivot_point, angle=90.0) t2_line = interpline(t2, pivot_point=pivot_point, angle=90.0)
add_to_ncfile(outfile, t2_line, "interpline") add_to_ncfile(outfile, t2_line, "interpline")
if interptype == "vinterp": if interptype == "vinterp":
tk = getvar(infile, "temp", units="k") tk = getvar(infile, "temp", units="k")
interp_levels = [200,300,500,1000] interp_levels = [200, 300, 500, 1000]
field = vinterp(infile, field = vinterp(infile,
field=tk, field=tk,
vert_coord="theta", vert_coord="theta",
interp_levels=interp_levels, interp_levels=interp_levels,
extrapolate=True, extrapolate=True,
field_type="tk", field_type="tk",
log_p=True) log_p=True)
add_to_ncfile(outfile, field, "vinterp") add_to_ncfile(outfile, field, "vinterp")
for latlonmeth in LATLON_METHS: for latlonmeth in LATLON_METHS:
if latlonmeth == "xy": if latlonmeth == "xy":
# Hardcoded values from other unit tests # Hardcoded values from other unit tests
lats = [22.0, 25.0, 27.0] lats = [22.0, 25.0, 27.0]
lons = [-90.0, -87.5, -83.75] lons = [-90.0, -87.5, -83.75]
xy = ll_to_xy(infile, lats[0], lons[0]) xy = ll_to_xy(infile, lats[0], lons[0])
add_to_ncfile(outfile, xy, "xy") add_to_ncfile(outfile, xy, "xy")
else: else:
# Hardcoded from other unit tests # Hardcoded from other unit tests
x_s = np.asarray([10, 50, 90], int) x_s = np.asarray([10, 50, 90], int)
y_s = np.asarray([10, 50, 90], int) y_s = np.asarray([10, 50, 90], int)
ll = xy_to_ll(infile, x_s[0], y_s[0]) ll = xy_to_ll(infile, x_s[0], y_s[0])
add_to_ncfile(outfile, ll, "ll") add_to_ncfile(outfile, ll, "ll")
def main(opts): def main(opts):
copy_and_reduce(opts) copy_and_reduce(opts)
make_result_file(opts) make_result_file(opts)
if __name__ == "__main__": if __name__ == "__main__":
DEFAULT_FILE = ("/Users/ladwig/Documents/wrf_files/" DEFAULT_FILE = ("/Users/ladwig/Documents/wrf_files/"
"wrf_vortex_multi/moving_nest/" "wrf_vortex_multi/moving_nest/"
@ -171,10 +172,10 @@ if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Generate conda test files " parser = argparse.ArgumentParser(description="Generate conda test files "
"for unit testing.") "for unit testing.")
parser.add_argument("-f", "--filename", required=False, parser.add_argument("-f", "--filename", required=False,
default=DEFAULT_FILE, default=DEFAULT_FILE,
help="the WRF test file") help="the WRF test file")
parser.add_argument("-o", "--outdir", required=False, default="./", parser.add_argument("-o", "--outdir", required=False, default="./",
help="the location for the output files") help="the location for the output files")
opts = parser.parse_args() opts = parser.parse_args()
main(opts) main(opts)

208
test/ci_tests/utests.py

@ -1,14 +1,15 @@
import unittest as ut import unittest as ut
import numpy.testing as nt import numpy.testing as nt
import numpy as np import numpy as np
import numpy.ma as ma import numpy.ma as ma
import os, sys import os
import sys
import subprocess import subprocess
from wrf import (getvar, interplevel, interpline, vertcross, vinterp, from wrf import (getvar, interplevel, interpline, vertcross, vinterp,
disable_xarray, xarray_enabled, to_np, disable_xarray, xarray_enabled, to_np,
xy_to_ll, ll_to_xy, xy_to_ll_proj, ll_to_xy_proj, xy_to_ll, ll_to_xy, xy_to_ll_proj, ll_to_xy_proj,
extract_global_attrs, viewitems, CoordPair, extract_global_attrs, viewitems, CoordPair,
omp_get_num_procs, omp_set_num_threads) omp_get_num_procs, omp_set_num_threads)
from wrf.util import is_multi_file from wrf.util import is_multi_file
@ -20,82 +21,82 @@ REF_FILE = "ci_result_file.nc"
if sys.version_info > (3,): if sys.version_info > (3,):
xrange = range xrange = range
# Using helpful information at:
# Using helpful information at:
# http://eli.thegreenplace.net/2014/04/02/dynamically-generating-python-test-cases # http://eli.thegreenplace.net/2014/04/02/dynamically-generating-python-test-cases
def make_test(varname, wrf_in, referent, multi=False, repeat=3, pynio=False): def make_test(varname, wrf_in, referent, multi=False, repeat=3, pynio=False):
def test(self): def test(self):
from netCDF4 import Dataset as NetCDF from netCDF4 import Dataset as NetCDF
timeidx = 0 timeidx = 0
in_wrfnc = NetCDF(wrf_in) in_wrfnc = NetCDF(wrf_in)
refnc = NetCDF(referent) refnc = NetCDF(referent)
# These have a left index that defines the product type # These have a left index that defines the product type
multiproduct = varname in ("uvmet", "uvmet10", "cape_2d", "cape_3d", multiproduct = varname in ("uvmet", "uvmet10", "cape_2d", "cape_3d",
"cfrac") "cfrac")
ref_vals = refnc.variables[varname][:] ref_vals = refnc.variables[varname][:]
if (varname == "tc"): if (varname == "tc"):
my_vals = getvar(in_wrfnc, "temp", timeidx=timeidx, units="c") my_vals = getvar(in_wrfnc, "temp", timeidx=timeidx, units="c")
tol = 1/100. tol = 1/100.
atol = .1 # Note: NCL uses 273.16 as conversion for some reason atol = .1 # Note: NCL uses 273.16 as conversion for some reason
nt.assert_allclose(to_np(my_vals), ref_vals, tol, atol) nt.assert_allclose(to_np(my_vals), ref_vals, tol, atol)
elif (varname == "pw"): elif (varname == "pw"):
my_vals = getvar(in_wrfnc, "pw", timeidx=timeidx) my_vals = getvar(in_wrfnc, "pw", timeidx=timeidx)
tol = .5/100.0 tol = .5/100.0
atol = 0 # NCL uses different constants and doesn't use same atol = 0
# handrolled virtual temp in method
nt.assert_allclose(to_np(my_vals), ref_vals, tol, atol) nt.assert_allclose(to_np(my_vals), ref_vals, tol, atol)
elif (varname == "cape_2d"): elif (varname == "cape_2d"):
cape_2d = getvar(in_wrfnc, varname, timeidx=timeidx) cape_2d = getvar(in_wrfnc, varname, timeidx=timeidx)
tol = 0/100. tol = 0/100.
atol = 200.0 atol = 200.0
# Let's only compare CAPE values until the F90 changes are # Let's only compare CAPE values until the F90 changes are
# merged back in to NCL. The modifications to the R and CP # merged back in to NCL. The modifications to the R and CP
# changes TK enough that non-lifting parcels could lift, thus # changes TK enough that non-lifting parcels could lift, thus
# causing wildly different values in LCL # causing wildly different values in LCL
nt.assert_allclose(to_np(cape_2d[0,:]), ref_vals[0,:], tol, atol) nt.assert_allclose(to_np(cape_2d[0, :]), ref_vals[0, :], tol, atol)
elif (varname == "cape_3d"): elif (varname == "cape_3d"):
cape_3d = getvar(in_wrfnc, varname, timeidx=timeidx) cape_3d = getvar(in_wrfnc, varname, timeidx=timeidx)
# Changing the R and CP constants, while keeping TK within # Changing the R and CP constants, while keeping TK within
# 2%, can lead to some big changes in CAPE. Tolerances # 2%, can lead to some big changes in CAPE. Tolerances
# have been set wide when comparing the with the original # have been set wide when comparing the with the original
# NCL. Change back when the F90 code is merged back with # NCL. Change back when the F90 code is merged back with
# NCL # NCL
tol = 0/100. tol = 0/100.
atol = 200.0 atol = 200.0
#print np.amax(np.abs(to_np(cape_3d[0,:]) - ref_vals[0,:])) # print np.amax(np.abs(to_np(cape_3d[0,:]) - ref_vals[0,:]))
nt.assert_allclose(to_np(cape_3d), ref_vals, tol, atol) nt.assert_allclose(to_np(cape_3d), ref_vals, tol, atol)
else: else:
my_vals = getvar(in_wrfnc, varname, timeidx=timeidx) my_vals = getvar(in_wrfnc, varname, timeidx=timeidx)
tol = 2/100. tol = 2/100.
atol = 0.1 atol = 0.1
#print (np.amax(np.abs(to_np(my_vals) - ref_vals))) # print (np.amax(np.abs(to_np(my_vals) - ref_vals)))
nt.assert_allclose(to_np(my_vals), ref_vals, tol, atol) nt.assert_allclose(to_np(my_vals), ref_vals, tol, atol)
return test return test
def _get_refvals(referent, varname, repeat, multi): def _get_refvals(referent, varname, repeat, multi):
from netCDF4 import Dataset as NetCDF from netCDF4 import Dataset as NetCDF
refnc = NetCDF(referent) refnc = NetCDF(referent)
ref_vals = refnc.variables[varname][:] ref_vals = refnc.variables[varname][:]
return ref_vals return ref_vals
def make_interp_test(varname, wrf_in, referent, multi=False,
def make_interp_test(varname, wrf_in, referent, multi=False,
repeat=3, pynio=False): repeat=3, pynio=False):
def test(self): def test(self):
from netCDF4 import Dataset as NetCDF from netCDF4 import Dataset as NetCDF
timeidx = 0 timeidx = 0
in_wrfnc = NetCDF(wrf_in) in_wrfnc = NetCDF(wrf_in)
if (varname == "interplevel"): if (varname == "interplevel"):
ref_ht_850 = _get_refvals(referent, "interplevel", repeat, multi) ref_ht_850 = _get_refvals(referent, "interplevel", repeat, multi)
hts = getvar(in_wrfnc, "z", timeidx=timeidx) hts = getvar(in_wrfnc, "z", timeidx=timeidx)
@ -103,168 +104,165 @@ def make_interp_test(varname, wrf_in, referent, multi=False,
# Check that it works with numpy arrays # Check that it works with numpy arrays
hts_850 = interplevel(to_np(hts), p, 850) hts_850 = interplevel(to_np(hts), p, 850)
#print (hts_850) # print (hts_850)
hts_850 = interplevel(hts, p, 850) hts_850 = interplevel(hts, p, 850)
nt.assert_allclose(to_np(hts_850), ref_ht_850) nt.assert_allclose(to_np(hts_850), ref_ht_850)
elif (varname == "vertcross"): elif (varname == "vertcross"):
ref_ht_cross = _get_refvals(referent, "vertcross", repeat, multi) ref_ht_cross = _get_refvals(referent, "vertcross", repeat, multi)
hts = getvar(in_wrfnc, "z", timeidx=timeidx) hts = getvar(in_wrfnc, "z", timeidx=timeidx)
p = getvar(in_wrfnc, "pressure", timeidx=timeidx) p = getvar(in_wrfnc, "pressure", timeidx=timeidx)
pivot_point = CoordPair(hts.shape[-1] // 2, hts.shape[-2] // 2) pivot_point = CoordPair(hts.shape[-1] // 2, hts.shape[-2] // 2)
# Check that it works with numpy arrays # Check that it works with numpy arrays
ht_cross = vertcross(to_np(hts), to_np(p), ht_cross = vertcross(to_np(hts), to_np(p),
pivot_point=pivot_point, angle=90.) pivot_point=pivot_point, angle=90.)
#print (ht_cross) # print (ht_cross)
ht_cross = vertcross(hts, p, pivot_point=pivot_point, angle=90.) ht_cross = vertcross(hts, p, pivot_point=pivot_point, angle=90.)
nt.assert_allclose(to_np(ht_cross), ref_ht_cross, rtol=.01) nt.assert_allclose(to_np(ht_cross), ref_ht_cross, rtol=.01)
elif (varname == "interpline"): elif (varname == "interpline"):
ref_t2_line = _get_refvals(referent, "interpline", repeat, multi) ref_t2_line = _get_refvals(referent, "interpline", repeat, multi)
t2 = getvar(in_wrfnc, "T2", timeidx=timeidx) t2 = getvar(in_wrfnc, "T2", timeidx=timeidx)
pivot_point = CoordPair(t2.shape[-1] // 2, t2.shape[-2] // 2) pivot_point = CoordPair(t2.shape[-1] // 2, t2.shape[-2] // 2)
# Check that it works with numpy arrays # Check that it works with numpy arrays
t2_line1 = interpline(to_np(t2), pivot_point=pivot_point, t2_line1 = interpline(to_np(t2), pivot_point=pivot_point,
angle=90.0) angle=90.0)
#print (t2_line1) # print (t2_line1)
t2_line1 = interpline(t2, pivot_point=pivot_point, angle=90.0) t2_line1 = interpline(t2, pivot_point=pivot_point, angle=90.0)
nt.assert_allclose(to_np(t2_line1), ref_t2_line) nt.assert_allclose(to_np(t2_line1), ref_t2_line)
elif (varname == "vinterp"): elif (varname == "vinterp"):
# Tk to theta # Tk to theta
fld_tk_theta = _get_refvals(referent, "vinterp", repeat, multi) fld_tk_theta = _get_refvals(referent, "vinterp", repeat, multi)
fld_tk_theta = np.squeeze(fld_tk_theta) fld_tk_theta = np.squeeze(fld_tk_theta)
tk = getvar(in_wrfnc, "temp", timeidx=timeidx, units="k") tk = getvar(in_wrfnc, "temp", timeidx=timeidx, units="k")
interp_levels = [200,300,500,1000] interp_levels = [200, 300, 500, 1000]
# Check that it works with numpy arrays # Check that it works with numpy arrays
field = vinterp(in_wrfnc, field = vinterp(in_wrfnc,
field=to_np(tk), field=to_np(tk),
vert_coord="theta", vert_coord="theta",
interp_levels=interp_levels, interp_levels=interp_levels,
extrapolate=True, extrapolate=True,
field_type="tk", field_type="tk",
timeidx=timeidx, timeidx=timeidx,
log_p=True) log_p=True)
#print (field) # print (field)
field = vinterp(in_wrfnc, field = vinterp(in_wrfnc,
field=tk, field=tk,
vert_coord="theta", vert_coord="theta",
interp_levels=interp_levels, interp_levels=interp_levels,
extrapolate=True, extrapolate=True,
field_type="tk", field_type="tk",
timeidx=timeidx, timeidx=timeidx,
log_p=True) log_p=True)
tol = 5/100. tol = 5/100.
atol = 0.0001 atol = 0.0001
field = np.squeeze(field) field = np.squeeze(field)
nt.assert_allclose(to_np(field), fld_tk_theta, tol, atol) nt.assert_allclose(to_np(field), fld_tk_theta, tol, atol)
return test return test
def make_latlon_test(testid, wrf_in, referent, single, multi=False, repeat=3, def make_latlon_test(testid, wrf_in, referent, single, multi=False, repeat=3,
pynio=False): pynio=False):
def test(self): def test(self):
from netCDF4 import Dataset as NetCDF from netCDF4 import Dataset as NetCDF
timeidx = 0 timeidx = 0
in_wrfnc = NetCDF(wrf_in) in_wrfnc = NetCDF(wrf_in)
refnc = NetCDF(referent) refnc = NetCDF(referent)
if testid == "xy": if testid == "xy":
# Since this domain is not moving, the reference values are the # Since this domain is not moving, the reference values are the
# same whether there are multiple or single files # same whether there are multiple or single files
ref_vals = refnc.variables["xy"][:] ref_vals = refnc.variables["xy"][:]
# Lats/Lons taken from NCL script, just hard-coding for now # Lats/Lons taken from NCL script, just hard-coding for now
lats = [22.0, 25.0, 27.0] lats = [22.0, 25.0, 27.0]
lons = [-90.0, -87.5, -83.75] lons = [-90.0, -87.5, -83.75]
xy = ll_to_xy(in_wrfnc, lats[0], lons[0]) xy = ll_to_xy(in_wrfnc, lats[0], lons[0])
nt.assert_allclose(to_np(xy), ref_vals) nt.assert_allclose(to_np(xy), ref_vals)
else: else:
# Since this domain is not moving, the reference values are the # Since this domain is not moving, the reference values are the
# same whether there are multiple or single files # same whether there are multiple or single files
ref_vals = refnc.variables["ll"][:] ref_vals = refnc.variables["ll"][:]
# i_s, j_s taken from NCL script, just hard-coding for now # i_s, j_s taken from NCL script, just hard-coding for now
# NCL uses 1-based indexing for this, so need to subtract 1 # NCL uses 1-based indexing for this, so need to subtract 1
x_s = np.asarray([10, 50, 90], int) x_s = np.asarray([10, 50, 90], int)
y_s = np.asarray([10, 50, 90], int) y_s = np.asarray([10, 50, 90], int)
ll = xy_to_ll(in_wrfnc, x_s[0], y_s[0]) ll = xy_to_ll(in_wrfnc, x_s[0], y_s[0])
nt.assert_allclose(to_np(ll), ref_vals) nt.assert_allclose(to_np(ll), ref_vals)
return test return test
class WRFVarsTest(ut.TestCase): class WRFVarsTest(ut.TestCase):
longMessage = True longMessage = True
class WRFInterpTest(ut.TestCase): class WRFInterpTest(ut.TestCase):
longMessage = True longMessage = True
class WRFLatLonTest(ut.TestCase): class WRFLatLonTest(ut.TestCase):
longMessage = True longMessage = True
if __name__ == "__main__": if __name__ == "__main__":
ignore_vars = [] # Not testable yet ignore_vars = [] # Not testable yet
wrf_vars = ["avo", "eth", "cape_2d", "cape_3d", "ctt", "dbz", "mdbz", wrf_vars = ["avo", "eth", "cape_2d", "cape_3d", "ctt", "dbz", "mdbz",
"geopt", "helicity", "lat", "lon", "omg", "p", "pressure", "geopt", "helicity", "lat", "lon", "omg", "p", "pressure",
"pvo", "pw", "rh2", "rh", "slp", "ter", "td2", "td", "tc", "pvo", "pw", "rh2", "rh", "slp", "ter", "td2", "td", "tc",
"theta", "tk", "tv", "twb", "updraft_helicity", "ua", "va", "theta", "tk", "tv", "twb", "updraft_helicity", "ua", "va",
"wa", "uvmet10", "uvmet", "z", "cfrac", "zstag"] "wa", "uvmet10", "uvmet", "z", "cfrac", "zstag"]
interp_methods = ["interplevel", "vertcross", "interpline", "vinterp"] interp_methods = ["interplevel", "vertcross", "interpline", "vinterp"]
latlon_tests = ["xy", "ll"] latlon_tests = ["xy", "ll"]
import netCDF4 import netCDF4
for var in wrf_vars: for var in wrf_vars:
if var in ignore_vars: if var in ignore_vars:
continue continue
test_func1 = make_test(var, TEST_FILE, REF_FILE) test_func1 = make_test(var, TEST_FILE, REF_FILE)
setattr(WRFVarsTest, 'test_{0}'.format(var), test_func1) setattr(WRFVarsTest, 'test_{0}'.format(var), test_func1)
for method in interp_methods: for method in interp_methods:
test_interp_func1 = make_interp_test(method, TEST_FILE, test_interp_func1 = make_interp_test(method, TEST_FILE,
REF_FILE) REF_FILE)
setattr(WRFInterpTest, 'test_{0}'.format(method), setattr(WRFInterpTest, 'test_{0}'.format(method),
test_interp_func1) test_interp_func1)
for testid in latlon_tests: for testid in latlon_tests:
for single in (True,): for single in (True,):
for multi in (False,): for multi in (False,):
test_ll_func = make_latlon_test(testid, TEST_FILE, test_ll_func = make_latlon_test(testid, TEST_FILE,
REF_FILE, REF_FILE,
single=single, multi=multi, single=single, multi=multi,
repeat=3, pynio=False) repeat=3, pynio=False)
multistr = "" if not multi else "_multi" multistr = "" if not multi else "_multi"
singlestr = "_nosingle" if not single else "_single" singlestr = "_nosingle" if not single else "_single"
test_name = "test_{}{}{}".format(testid, singlestr, test_name = "test_{}{}{}".format(testid, singlestr, multistr)
multistr)
setattr(WRFLatLonTest, test_name, test_ll_func) setattr(WRFLatLonTest, test_name, test_ll_func)
ut.main() ut.main()

536
test/comp_utest.py

@ -1,57 +1,68 @@
from math import fabs, log, tan, sin, cos from math import fabs, log, tan, sin, cos
import unittest as ut import unittest as ut
import numpy.testing as nt import numpy.testing as nt
import numpy as np import numpy as np
import numpy.ma as ma import numpy.ma as ma
import os, sys import os
import sys
import subprocess import subprocess
from netCDF4 import Dataset as nc from netCDF4 import Dataset as nc
from wrf import * from wrf import *
NCL_EXE = "/Users/ladwig/nclbuild/6.3.0/bin/ncl" TEST_FILE = ("/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/moving_nest/"
TEST_FILE = "/Users/ladwig/Documents/wrf_files/wrfout_d01_2010-06-13_21:00:00" "wrfout_d02_2005-08-28_00:00:00")
OUT_NC_FILE = "/tmp/wrftest.nc" OUT_NC_FILE = "/tmp/wrftest.nc"
NCFILE = nc(TEST_FILE) NCFILE = nc(TEST_FILE)
NCGROUP = [NCFILE, NCFILE, NCFILE] GROUP_FILES = [
("/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/moving_nest/"
"wrfout_d02_2005-08-28_00:00:00"),
("/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/moving_nest/"
"wrfout_d02_2005-08-28_12:00:00"),
("/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/moving_nest/"
"wrfout_d02_2005-08-29_00:00:00")]
NCGROUP = [nc(file) for file in GROUP_FILES]
# Python 3 # Python 3
if sys.version_info > (3,): if sys.version_info > (3,):
xrange = range xrange = range
ROUTINE_MAP = {"avo": avo,
"eth": eth,
"cape_2d": cape_2d,
"cape_3d": cape_3d,
"ctt": ctt,
"dbz": dbz,
"helicity": srhel,
"omg": omega,
"pvo": pvo,
"pw": pw,
"rh": rh,
"slp": slp,
"td": td,
"tk": tk,
"tv": tvirtual,
"twb": wetbulb,
"updraft_helicity": udhel,
"uvmet": uvmet,
"cloudfrac": cloudfrac}
ROUTINE_MAP = {"avo" : avo,
"eth" : eth,
"cape_2d" : cape_2d,
"cape_3d" : cape_3d,
"ctt" : ctt,
"dbz" : dbz,
"helicity" : srhel,
"omg" : omega,
"pvo" : pvo,
"pw" : pw,
"rh" : rh,
"slp" : slp,
"td" : td,
"tk" : tk,
"tv" : tvirtual,
"twb" : wetbulb,
"updraft_helicity" : udhel,
"uvmet" : uvmet,
"cloudfrac" : cloudfrac}
class ProjectionError(RuntimeError): class ProjectionError(RuntimeError):
pass pass
def get_args(varname, wrfnc, timeidx, method, squeeze): def get_args(varname, wrfnc, timeidx, method, squeeze):
if varname == "avo": if varname == "avo":
ncvars = extract_vars(wrfnc, timeidx, ("U", "V", "MAPFAC_U", varnames = ("U", "V", "MAPFAC_U", "MAPFAC_V", "MAPFAC_M", "F")
"MAPFAC_V", "MAPFAC_M", ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze,
"F"), cache=None, meta=True)
method, squeeze, cache=None, meta=True)
attrs = extract_global_attrs(wrfnc, attrs=("DX", "DY")) attrs = extract_global_attrs(wrfnc, attrs=("DX", "DY"))
u = ncvars["U"] u = ncvars["U"]
v = ncvars["V"] v = ncvars["V"]
@ -59,20 +70,20 @@ def get_args(varname, wrfnc, timeidx, method, squeeze):
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 (u, v, msfu, msfv, msfm, cor, dx, dy) return (u, v, msfu, msfv, msfm, cor, dx, dy)
if varname == "pvo": if varname == "pvo":
ncvars = extract_vars(wrfnc, timeidx, ("U", "V", "T", "P", varnames = ("U", "V", "T", "P", "PB", "MAPFAC_U", "MAPFAC_V",
"PB", "MAPFAC_U", "MAPFAC_M", "F")
"MAPFAC_V", "MAPFAC_M", ncvars = extract_vars(wrfnc, timeidx,
"F"), varnames,
method, squeeze, cache=None, meta=True) method, squeeze, cache=None, meta=True)
attrs = extract_global_attrs(wrfnc, attrs=("DX", "DY")) attrs = extract_global_attrs(wrfnc, attrs=("DX", "DY"))
u = ncvars["U"] u = ncvars["U"]
v = ncvars["V"] v = ncvars["V"]
t = ncvars["T"] t = ncvars["T"]
@ -82,35 +93,35 @@ def get_args(varname, wrfnc, timeidx, method, squeeze):
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 (u, v, full_t, full_p, msfu, msfv, msfm, cor, dx, dy) return (u, v, full_t, full_p, msfu, msfv, msfm, cor, dx, dy)
if varname == "eth": if varname == "eth":
varnames=("T", "P", "PB", "QVAPOR") varnames = ("T", "P", "PB", "QVAPOR")
ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze,
cache=None, meta=True) cache=None, meta=True)
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
tkel = tk(full_p, full_t, meta=False) tkel = tk(full_p, full_t, meta=False)
return (qv, tkel, full_p) return (qv, tkel, full_p)
if varname == "cape_2d": if varname == "cape_2d":
varnames = ("T", "P", "PB", "QVAPOR", "PH","PHB", "HGT", "PSFC") varnames = ("T", "P", "PB", "QVAPOR", "PH", "PHB", "HGT", "PSFC")
ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze,
cache=None, meta=True) cache=None, meta=True)
t = ncvars["T"] t = ncvars["T"]
p = ncvars["P"] p = ncvars["P"]
pb = ncvars["PB"] pb = ncvars["PB"]
@ -119,27 +130,27 @@ def get_args(varname, wrfnc, timeidx, method, squeeze):
phb = ncvars["PHB"] phb = ncvars["PHB"]
ter = ncvars["HGT"] ter = ncvars["HGT"]
psfc = ncvars["PSFC"] psfc = ncvars["PSFC"]
full_t = t + Constants.T_BASE full_t = t + Constants.T_BASE
full_p = p + pb full_p = p + pb
tkel = tk(full_p, full_t, meta=False) tkel = tk(full_p, full_t, meta=False)
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
# Convert pressure to hPa # Convert pressure to hPa
p_hpa = ConversionFactors.PA_TO_HPA * full_p p_hpa = ConversionFactors.PA_TO_HPA * full_p
psfc_hpa = ConversionFactors.PA_TO_HPA * psfc psfc_hpa = ConversionFactors.PA_TO_HPA * psfc
i3dflag = 0 i3dflag = 0
ter_follow = 1 ter_follow = 1
return (p_hpa, tkel, qv, z, ter, psfc_hpa, ter_follow) return (p_hpa, tkel, qv, z, ter, psfc_hpa, ter_follow)
if varname == "cape_3d": if varname == "cape_3d":
varnames = ("T", "P", "PB", "QVAPOR", "PH", "PHB", "HGT", "PSFC") varnames = ("T", "P", "PB", "QVAPOR", "PH", "PHB", "HGT", "PSFC")
ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze,
cache=None, meta=True) cache=None, meta=True)
t = ncvars["T"] t = ncvars["T"]
p = ncvars["P"] p = ncvars["P"]
@ -149,27 +160,27 @@ def get_args(varname, wrfnc, timeidx, method, squeeze):
phb = ncvars["PHB"] phb = ncvars["PHB"]
ter = ncvars["HGT"] ter = ncvars["HGT"]
psfc = ncvars["PSFC"] psfc = ncvars["PSFC"]
full_t = t + Constants.T_BASE full_t = t + Constants.T_BASE
full_p = p + pb full_p = p + pb
tkel = tk(full_p, full_t, meta=False) tkel = tk(full_p, full_t, meta=False)
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
# Convert pressure to hPa # Convert pressure to hPa
p_hpa = ConversionFactors.PA_TO_HPA * full_p p_hpa = ConversionFactors.PA_TO_HPA * full_p
psfc_hpa = ConversionFactors.PA_TO_HPA * psfc psfc_hpa = ConversionFactors.PA_TO_HPA * psfc
i3dflag = 1 i3dflag = 1
ter_follow = 1 ter_follow = 1
return (p_hpa, tkel, qv, z, ter, psfc_hpa, ter_follow) return (p_hpa, tkel, qv, z, ter, psfc_hpa, ter_follow)
if varname == "ctt": if varname == "ctt":
varnames = ("T", "P", "PB", "PH", "PHB", "HGT", "QVAPOR") varnames = ("T", "P", "PB", "PH", "PHB", "HGT", "QVAPOR")
ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze,
cache=None, meta=True) cache=None, meta=True)
t = ncvars["T"] t = ncvars["T"]
p = ncvars["P"] p = ncvars["P"]
@ -177,382 +188,389 @@ def get_args(varname, wrfnc, timeidx, method, squeeze):
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(wrfnc, timeidx, "QICE", icevars = extract_vars(wrfnc, timeidx, "QICE",
method, squeeze, cache=None, meta=False) method, squeeze, cache=None, meta=False)
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(wrfnc, timeidx, "QCLOUD", cldvars = extract_vars(wrfnc, timeidx, "QCLOUD",
method, squeeze, cache=None, meta=False) method, squeeze, cache=None, meta=False)
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
tkel = tk(full_p, full_t, meta=False) tkel = tk(full_p, full_t, meta=False)
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
return (p_hpa, tkel, qv, qcld, ght, ter, qice) return (p_hpa, tkel, qv, qcld, ght, ter, qice)
if varname == "dbz": if varname == "dbz":
varnames = ("T", "P", "PB", "QVAPOR", "QRAIN") varnames = ("T", "P", "PB", "QVAPOR", "QRAIN")
ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze,
cache=None, meta=True) cache=None, meta=True)
t = ncvars["T"] t = ncvars["T"]
p = ncvars["P"] p = ncvars["P"]
pb = ncvars["PB"] pb = ncvars["PB"]
qv = ncvars["QVAPOR"] qv = ncvars["QVAPOR"]
qr = ncvars["QRAIN"] qr = ncvars["QRAIN"]
try: try:
snowvars = extract_vars(wrfnc, timeidx, "QSNOW", snowvars = extract_vars(wrfnc, timeidx, "QSNOW",
method, squeeze, cache=None, meta=False) method, squeeze, cache=None, meta=False)
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(wrfnc, timeidx, "QGRAUP", graupvars = extract_vars(wrfnc, timeidx, "QGRAUP",
method, squeeze, cache=None, meta=False) method, squeeze, cache=None, meta=False)
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
tkel = tk(full_p, full_t, meta=False) tkel = tk(full_p, full_t, meta=False)
return (full_p, tkel, qv, qr, qs, qg) return (full_p, tkel, qv, qr, qs, qg)
if varname == "helicity": if varname == "helicity":
# Top can either be 3000 or 1000 (for 0-1 srh or 0-3 srh) # Top can either be 3000 or 1000 (for 0-1 srh or 0-3 srh)
ncvars = extract_vars(wrfnc, timeidx, ("HGT", "PH", "PHB"), ncvars = extract_vars(wrfnc, timeidx, ("HGT", "PH", "PHB"),
method, squeeze, cache=None, meta=True) method, squeeze, cache=None, meta=True)
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 = "U" varname = "U"
u_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze, u_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze,
cache=None, meta=False) cache=None, meta=False)
u = destagger(u_vars[varname], -1) u = destagger(u_vars[varname], -1)
varname = "V" varname = "V"
v_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze, v_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze,
cache=None, meta=False) cache=None, meta=False)
v = destagger(v_vars[varname], -2) v = destagger(v_vars[varname], -2)
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
return (u, v, z, ter) return (u, v, z, ter)
if varname == "updraft_helicity": if varname == "updraft_helicity":
ncvars = extract_vars(wrfnc, timeidx, ("W", "PH", "PHB", "MAPFAC_M"), varnames = ("W", "PH", "PHB", "MAPFAC_M")
method, squeeze, cache=None, meta=True) ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze,
cache=None, meta=True)
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(wrfnc, attrs=("DX", "DY")) attrs = extract_global_attrs(wrfnc, 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 = "U" varname = "U"
u_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze, u_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze,
cache=None, meta=True) cache=None, meta=True)
u = destagger(u_vars[varname], -1, meta=True) u = destagger(u_vars[varname], -1, meta=True)
varname = "V" varname = "V"
v_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze, v_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze,
cache=None, meta=True) cache=None, meta=True)
v = destagger(v_vars[varname], -2, meta=True) v = destagger(v_vars[varname], -2, meta=True)
zstag = ph + phb zstag = (ph + phb) / Constants.G
return (zstag, mapfct, u, v, wstag, dx, dy) return (zstag, mapfct, u, v, wstag, dx, dy)
if varname == "omg": if varname == "omg":
varnames=("T", "P", "W", "PB", "QVAPOR") varnames = ("T", "P", "W", "PB", "QVAPOR")
ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze,
cache=None, meta=True) cache=None, meta=True)
t = ncvars["T"] t = ncvars["T"]
p = ncvars["P"] p = ncvars["P"]
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
tkel = tk(full_p, full_t, meta=False) tkel = tk(full_p, full_t, meta=False)
return (qv, tkel, wa, full_p) return (qv, tkel, wa, full_p)
if varname == "pw": if varname == "pw":
varnames=("T", "P", "PB", "PH", "PHB", "QVAPOR") varnames = ("T", "P", "PB", "PH", "PHB", "QVAPOR")
ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze,
cache=None, meta=True) cache=None, meta=True)
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"]
# Change this to use real virtual temperature! # Change this to use real virtual temperature!
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
tkel = tk(full_p, full_t, meta=False) tkel = tk(full_p, full_t, meta=False)
return (full_p, tkel, qv, ht) return (full_p, tkel, qv, ht)
if varname == "rh": if varname == "rh":
varnames=("T", "P", "PB", "QVAPOR") varnames = ("T", "P", "PB", "QVAPOR")
ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze,
cache=None, meta=True) cache=None, meta=True)
t = ncvars["T"] t = ncvars["T"]
p = ncvars["P"] p = ncvars["P"]
pb = ncvars["PB"] pb = ncvars["PB"]
qvapor = to_np(ncvars["QVAPOR"]) qvapor = to_np(ncvars["QVAPOR"])
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
tkel = tk(full_p, full_t, meta=False) tkel = tk(full_p, full_t, meta=False)
return (qvapor, full_p, tkel) return (qvapor, full_p, tkel)
if varname == "slp": if varname == "slp":
varnames=("T", "P", "PB", "QVAPOR", "PH", "PHB") varnames = ("T", "P", "PB", "QVAPOR", "PH", "PHB")
ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze,
cache=None, meta=True) cache=None, meta=True)
t = ncvars["T"] t = ncvars["T"]
p = ncvars["P"] p = ncvars["P"]
pb = ncvars["PB"] pb = ncvars["PB"]
qvapor = to_np(ncvars["QVAPOR"]) qvapor = to_np(ncvars["QVAPOR"])
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)
tkel = tk(full_p, full_t, meta=False) tkel = tk(full_p, full_t, meta=False)
return (destag_ph, tkel, full_p, qvapor) return (destag_ph, tkel, full_p, qvapor)
if varname == "td": if varname == "td":
varnames=("P", "PB", "QVAPOR") varnames = ("P", "PB", "QVAPOR")
ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze,
cache=None, meta=True) cache=None, meta=True)
p = ncvars["P"] p = ncvars["P"]
pb = ncvars["PB"] pb = ncvars["PB"]
qvapor = to_np(ncvars["QVAPOR"]) qvapor = to_np(ncvars["QVAPOR"])
# Algorithm requires hPa # Algorithm requires hPa
full_p = .01*(p + pb) full_p = .01*(p + pb)
qvapor[qvapor < 0] = 0 qvapor[qvapor < 0] = 0
return (full_p, qvapor) return (full_p, qvapor)
if varname == "tk": if varname == "tk":
varnames=("T", "P", "PB") varnames = ("T", "P", "PB")
ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze,
cache=None, meta=True) cache=None, meta=True)
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
return (full_p, full_t) return (full_p, full_t)
if varname == "tv": if varname == "tv":
varnames=("T", "P", "PB", "QVAPOR") varnames = ("T", "P", "PB", "QVAPOR")
ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze,
cache=None, meta=True) cache=None, meta=True)
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
tkel = tk(full_p, full_t) tkel = tk(full_p, full_t)
return (tkel, qv) return (tkel, qv)
if varname == "twb": if varname == "twb":
varnames=("T", "P", "PB", "QVAPOR") varnames = ("T", "P", "PB", "QVAPOR")
ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze,
cache=None, meta=True) cache=None, meta=True)
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
tkel = tk(full_p, full_t) tkel = tk(full_p, full_t)
return (full_p, tkel, qv) return (full_p, tkel, qv)
if varname == "uvmet": if varname == "uvmet":
varname = "U" varname = "U"
u_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze, u_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze,
cache=None, meta=True) cache=None, meta=True)
u = destagger(u_vars[varname], -1, meta=True) u = destagger(u_vars[varname], -1, meta=True)
varname = "V" varname = "V"
v_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze, v_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze,
cache=None, meta=True) cache=None, meta=True)
v = destagger(v_vars[varname], -2, meta=True) v = destagger(v_vars[varname], -2, meta=True)
map_proj_attrs = extract_global_attrs(wrfnc, attrs="MAP_PROJ") map_proj_attrs = extract_global_attrs(wrfnc, attrs="MAP_PROJ")
map_proj = map_proj_attrs["MAP_PROJ"] map_proj = map_proj_attrs["MAP_PROJ"]
if map_proj in (0,3,6): if map_proj in (0, 3, 6):
raise ProjectionError("Map projection does not need rotation") raise ProjectionError("Map projection does not need rotation")
elif map_proj in (1,2): elif map_proj in (1, 2):
lat_attrs = extract_global_attrs(wrfnc, attrs=("TRUELAT1", lat_attrs = extract_global_attrs(wrfnc, attrs=("TRUELAT1",
"TRUELAT2")) "TRUELAT2"))
radians_per_degree = Constants.PI/180.0 radians_per_degree = Constants.PI/180.0
# Rotation needed for Lambert and Polar Stereographic # Rotation needed for Lambert and Polar Stereographic
true_lat1 = lat_attrs["TRUELAT1"] true_lat1 = lat_attrs["TRUELAT1"]
true_lat2 = lat_attrs["TRUELAT2"] true_lat2 = lat_attrs["TRUELAT2"]
try: try:
lon_attrs = extract_global_attrs(wrfnc, attrs="STAND_LON") lon_attrs = extract_global_attrs(wrfnc, attrs="STAND_LON")
except AttributeError: except AttributeError:
try: try:
cen_lon_attrs = extract_global_attrs(wrfnc, attrs="CEN_LON") cen_lon_attrs = extract_global_attrs(wrfnc,
attrs="CEN_LON")
except AttributeError: except AttributeError:
raise RuntimeError("longitude attributes not found in NetCDF") raise RuntimeError("longitude attributes not found in "
"NetCDF")
else: else:
cen_lon = cen_lon_attrs["CEN_LON"] cen_lon = cen_lon_attrs["CEN_LON"]
else: else:
cen_lon = lon_attrs["STAND_LON"] cen_lon = lon_attrs["STAND_LON"]
varname = "XLAT" varname = "XLAT"
xlat_var = extract_vars(wrfnc, timeidx, varname, xlat_var = extract_vars(wrfnc, timeidx, varname,
method, squeeze, cache=None, meta=True) method, squeeze, cache=None, meta=True)
lat = xlat_var[varname] lat = xlat_var[varname]
varname = "XLONG" varname = "XLONG"
xlon_var = extract_vars(wrfnc, timeidx, varname, xlon_var = extract_vars(wrfnc, timeidx, varname,
method, squeeze, cache=None, meta=True) method, squeeze, cache=None, meta=True)
lon = xlon_var[varname] lon = xlon_var[varname]
if map_proj == 1: if map_proj == 1:
if((fabs(true_lat1 - true_lat2) > 0.1) and if((fabs(true_lat1 - true_lat2) > 0.1) and
(fabs(true_lat2 - 90.) > 0.1)): (fabs(true_lat2 - 90.) > 0.1)):
cone = (log(cos(true_lat1*radians_per_degree)) cone = (log(cos(true_lat1 * radians_per_degree))
- log(cos(true_lat2*radians_per_degree))) - log(cos(true_lat2 * radians_per_degree)))
cone = (cone / cone = (cone /
(log(tan((45.-fabs(true_lat1/2.))*radians_per_degree)) (log(tan((45.-fabs(true_lat1/2.)) *
- log(tan((45.-fabs(true_lat2/2.))*radians_per_degree)))) radians_per_degree))
- log(tan((45.-fabs(true_lat2/2.)) *
radians_per_degree))))
else: else:
cone = sin(fabs(true_lat1)*radians_per_degree) cone = sin(fabs(true_lat1) * radians_per_degree)
else: else:
cone = 1 cone = 1
return (u, v, lat, lon, cen_lon, cone) return (u, v, lat, lon, cen_lon, cone)
if varname == "cloudfrac": if varname == "cloudfrac":
from wrf.g_geoht import get_height from wrf.g_geoht import get_height
vars = extract_vars(wrfnc, timeidx, ("P", "PB", "QVAPOR", "T"),
method, squeeze, cache=None, meta=True) varnames = ("P", "PB", "QVAPOR", "T")
vars = extract_vars(wrfnc, timeidx, varnames, method, squeeze,
cache=None, meta=True)
p = vars["P"] p = vars["P"]
pb = vars["PB"] pb = vars["PB"]
qv = vars["QVAPOR"] qv = vars["QVAPOR"]
t = vars["T"] t = vars["T"]
geoht_agl = get_height(wrfnc, timeidx, method, squeeze, geoht_agl = get_height(wrfnc, timeidx, method, squeeze,
cache=None, meta=True, msl=False) cache=None, meta=True, msl=False)
full_p = p + pb full_p = p + pb
full_t = t + Constants.T_BASE full_t = t + Constants.T_BASE
tkel = tk(full_p, full_t) tkel = tk(full_p, full_t)
relh = rh(qv, full_p, tkel) relh = rh(qv, full_p, tkel)
return (geoht_agl, relh, 1, 300., 2000., 6000.) return (geoht_agl, relh, 1, 300., 2000., 6000.)
class WRFVarsTest(ut.TestCase): class WRFVarsTest(ut.TestCase):
longMessage = True longMessage = True
def make_func(varname, wrfnc, timeidx, method, squeeze, meta): def make_func(varname, wrfnc, timeidx, method, squeeze, meta):
def func(self): def func(self):
try: try:
args = get_args(varname, wrfnc, timeidx, method, squeeze) args = get_args(varname, wrfnc, timeidx, method, squeeze)
except ProjectionError: # Don't fail for this except ProjectionError: # Don't fail for this
return return
routine = ROUTINE_MAP[varname] routine = ROUTINE_MAP[varname]
kwargs = {"meta" : meta} kwargs = {"meta": meta}
result = routine(*args, **kwargs) result = routine(*args, **kwargs)
ref = getvar(wrfnc, varname, timeidx, method, squeeze, cache=None, ref = getvar(wrfnc, varname, timeidx, method, squeeze, cache=None,
meta=meta) meta=meta)
nt.assert_allclose(to_np(result), to_np(ref)) nt.assert_allclose(to_np(result), to_np(ref))
if meta: if meta:
self.assertEqual(result.dims, ref.dims) self.assertEqual(result.dims, ref.dims)
return func return func
def test_cape3d_1d(wrfnc): def test_cape3d_1d(wrfnc):
def func(self): def func(self):
varnames = ("T", "P", "PB", "QVAPOR", "PH", "PHB", "HGT", "PSFC") varnames = ("T", "P", "PB", "QVAPOR", "PH", "PHB", "HGT", "PSFC")
ncvars = extract_vars(wrfnc, 0, varnames, method="cat", squeeze=True, ncvars = extract_vars(wrfnc, 0, varnames, method="cat", squeeze=True,
cache=None, meta=True) cache=None, meta=True)
t = ncvars["T"] t = ncvars["T"]
p = ncvars["P"] p = ncvars["P"]
pb = ncvars["PB"] pb = ncvars["PB"]
@ -561,9 +579,9 @@ def test_cape3d_1d(wrfnc):
phb = ncvars["PHB"] phb = ncvars["PHB"]
ter = ncvars["HGT"] ter = ncvars["HGT"]
psfc = ncvars["PSFC"] psfc = ncvars["PSFC"]
col_idxs = (slice(None), t.shape[-2]//2, t.shape[-1]//2) col_idxs = (slice(None), t.shape[-2]//2, t.shape[-1]//2)
t = t[col_idxs] t = t[col_idxs]
p = p[col_idxs] p = p[col_idxs]
pb = pb[col_idxs] pb = pb[col_idxs]
@ -572,40 +590,40 @@ def test_cape3d_1d(wrfnc):
phb = phb[col_idxs] phb = phb[col_idxs]
ter = float(ter[col_idxs[1:]]) ter = float(ter[col_idxs[1:]])
psfc = float(psfc[col_idxs[1:]]) psfc = float(psfc[col_idxs[1:]])
full_t = t + Constants.T_BASE full_t = t + Constants.T_BASE
full_p = p + pb full_p = p + pb
tkel = tk(full_p, full_t, meta=False) tkel = tk(full_p, full_t, meta=False)
geopt = ph + phb geopt = ph + phb
geopt_unstag = destagger(geopt, -1) geopt_unstag = destagger(geopt, -1)
z = geopt_unstag/Constants.G z = geopt_unstag/Constants.G
# Convert pressure to hPa # Convert pressure to hPa
p_hpa = ConversionFactors.PA_TO_HPA * full_p p_hpa = ConversionFactors.PA_TO_HPA * full_p
psfc_hpa = ConversionFactors.PA_TO_HPA * psfc psfc_hpa = ConversionFactors.PA_TO_HPA * psfc
i3dflag = 1 i3dflag = 1
ter_follow = 1 ter_follow = 1
result = cape_3d(p_hpa, tkel, qv, z, ter, psfc_hpa, ter_follow) result = cape_3d(p_hpa, tkel, qv, z, ter, psfc_hpa, ter_follow)
ref = getvar(wrfnc, "cape_3d") ref = getvar(wrfnc, "cape_3d")
ref = ref[(slice(None),) + col_idxs] ref = ref[(slice(None),) + col_idxs]
nt.assert_allclose(to_np(result), to_np(ref)) nt.assert_allclose(to_np(result), to_np(ref))
return func return func
def test_cape2d_1d(wrfnc): def test_cape2d_1d(wrfnc):
def func(self): def func(self):
varnames = ("T", "P", "PB", "QVAPOR", "PH", "PHB", "HGT", "PSFC") varnames = ("T", "P", "PB", "QVAPOR", "PH", "PHB", "HGT", "PSFC")
ncvars = extract_vars(wrfnc, 0, varnames, method="cat", squeeze=True, ncvars = extract_vars(wrfnc, 0, varnames, method="cat", squeeze=True,
cache=None, meta=True) cache=None, meta=True)
t = ncvars["T"] t = ncvars["T"]
p = ncvars["P"] p = ncvars["P"]
pb = ncvars["PB"] pb = ncvars["PB"]
@ -614,9 +632,9 @@ def test_cape2d_1d(wrfnc):
phb = ncvars["PHB"] phb = ncvars["PHB"]
ter = ncvars["HGT"] ter = ncvars["HGT"]
psfc = ncvars["PSFC"] psfc = ncvars["PSFC"]
col_idxs = (slice(None), t.shape[-2]//2, t.shape[-1]//2) col_idxs = (slice(None), t.shape[-2]//2, t.shape[-1]//2)
t = t[col_idxs] t = t[col_idxs]
p = p[col_idxs] p = p[col_idxs]
pb = pb[col_idxs] pb = pb[col_idxs]
@ -625,82 +643,74 @@ def test_cape2d_1d(wrfnc):
phb = phb[col_idxs] phb = phb[col_idxs]
ter = float(ter[col_idxs[1:]]) ter = float(ter[col_idxs[1:]])
psfc = float(psfc[col_idxs[1:]]) psfc = float(psfc[col_idxs[1:]])
full_t = t + Constants.T_BASE full_t = t + Constants.T_BASE
full_p = p + pb full_p = p + pb
tkel = tk(full_p, full_t, meta=False) tkel = tk(full_p, full_t, meta=False)
geopt = ph + phb geopt = ph + phb
geopt_unstag = destagger(geopt, -1) geopt_unstag = destagger(geopt, -1)
z = geopt_unstag/Constants.G z = geopt_unstag/Constants.G
# Convert pressure to hPa # Convert pressure to hPa
p_hpa = ConversionFactors.PA_TO_HPA * full_p p_hpa = ConversionFactors.PA_TO_HPA * full_p
psfc_hpa = ConversionFactors.PA_TO_HPA * psfc psfc_hpa = ConversionFactors.PA_TO_HPA * psfc
i3dflag = 0 i3dflag = 0
ter_follow = 1 ter_follow = 1
result = cape_2d(p_hpa, tkel, qv, z, ter, psfc_hpa, ter_follow) result = cape_2d(p_hpa, tkel, qv, z, ter, psfc_hpa, ter_follow)
ref = getvar(wrfnc, "cape_2d") ref = getvar(wrfnc, "cape_2d")
ref = ref[(slice(None),) + col_idxs[1:]] ref = ref[(slice(None),) + col_idxs[1:]]
nt.assert_allclose(to_np(result), to_np(ref)) nt.assert_allclose(to_np(result), to_np(ref))
return func return func
if __name__ == "__main__": if __name__ == "__main__":
from wrf import (omp_set_num_threads, omp_set_schedule, omp_get_schedule, from wrf import (omp_set_num_threads, omp_set_schedule, omp_get_schedule,
omp_set_dynamic, omp_get_num_procs, OMP_SCHED_STATIC) omp_set_dynamic, omp_get_num_procs, OMP_SCHED_STATIC)
omp_set_num_threads(omp_get_num_procs()//2) omp_set_num_threads(omp_get_num_procs()//2)
omp_set_schedule(OMP_SCHED_STATIC, 0) omp_set_schedule(OMP_SCHED_STATIC, 0)
omp_set_dynamic(False) omp_set_dynamic(False)
varnames = ["avo", "eth", "cape_2d", "cape_3d", "ctt", "dbz", "mdbz",
"geopt", "helicity", "lat", "lon", "omg", "p", "pressure",
"pvo", "pw", "rh2", "rh", "slp", "ter", "td2", "td", "tc",
"theta", "tk", "tv", "twb", "updraft_helicity", "ua", "va",
"wa", "uvmet10", "uvmet", "z", "cloudfrac"]
#varnames = ["helicity"]
varnames=["avo", "pvo", "eth", "dbz", "helicity", "updraft_helicity", varnames=["avo", "pvo", "eth", "dbz", "helicity", "updraft_helicity",
"omg", "pw", "rh", "slp", "td", "tk", "tv", "twb", "uvmet", "omg", "pw", "rh", "slp", "td", "tk", "tv", "twb", "uvmet",
"cloudfrac", "ctt"] "cloudfrac", "ctt"]
omp_set_num_threads(omp_get_num_procs()-1) omp_set_num_threads(omp_get_num_procs()-1)
omp_set_schedule(OMP_SCHED_STATIC, 0) omp_set_schedule(OMP_SCHED_STATIC, 0)
omp_set_dynamic(False) omp_set_dynamic(False)
# Turn this one off when not needed, since it's slow # Turn this one off when not needed, since it's slow
varnames += ["cape_2d", "cape_3d"] varnames += ["cape_2d", "cape_3d"]
for varname in varnames: for varname in varnames:
for i,wrfnc in enumerate((NCFILE, NCGROUP)): for i, wrfnc in enumerate((NCFILE, NCGROUP)):
for j,timeidx in enumerate((0, ALL_TIMES)): for j, timeidx in enumerate((0, ALL_TIMES)):
for method in ("cat", "join"): for method in ("cat", "join"):
for squeeze in (True, False): for squeeze in (True, False):
for meta in (True, False): for meta in (True, False):
func = make_func(varname, wrfnc, timeidx, method, func = make_func(varname, wrfnc, timeidx, method,
squeeze, meta) squeeze, meta)
ncname = "single" if i == 0 else "multi" ncname = "single" if i == 0 else "multi"
timename = "t0" if j == 0 else "all" timename = "t0" if j == 0 else "all"
squeeze_name = "squeeze" if squeeze else "nosqueeze" squeeze_name = ("squeeze" if squeeze
else "nosqueeze")
meta_name = "meta" if meta else "nometa" meta_name = "meta" if meta else "nometa"
test_name = "test_{}_{}_{}_{}_{}_{}".format(varname, test_name = "test_{}_{}_{}_{}_{}_{}".format(
ncname, timename, method, varname, ncname, timename, method,
squeeze_name, meta_name) squeeze_name, meta_name)
setattr(WRFVarsTest, test_name, func) setattr(WRFVarsTest, test_name, func)
func = test_cape3d_1d(wrfnc) func = test_cape3d_1d(wrfnc)
setattr(WRFVarsTest, "test_cape3d_1d", func) setattr(WRFVarsTest, "test_cape3d_1d", func)
func = test_cape2d_1d(wrfnc) func = test_cape2d_1d(wrfnc)
setattr(WRFVarsTest, "test_cape2d_1d", func) setattr(WRFVarsTest, "test_cape2d_1d", func)
ut.main() ut.main()

17
test/ctt_test.py

@ -4,10 +4,12 @@ from matplotlib.cm import get_cmap
import cartopy.crs as crs import cartopy.crs as crs
from cartopy.feature import NaturalEarthFeature from cartopy.feature import NaturalEarthFeature
from wrf import to_np, getvar, smooth2d, get_cartopy, cartopy_xlim, cartopy_ylim, latlon_coords from wrf import (to_np, getvar, smooth2d, get_cartopy, cartopy_xlim,
cartopy_ylim, latlon_coords)
# Open the NetCDF file # Open the NetCDF file
ncfile = Dataset("/Users/ladwig/Documents/wrf_files/problem_files/cfrac_bug/wrfout_d02_1987-10-01_00:00:00") ncfile = Dataset("/Users/ladwig/Documents/wrf_files/"
"problem_files/cfrac_bug/wrfout_d02_1987-10-01_00:00:00")
# Get the sea level pressure # Get the sea level pressure
ctt = getvar(ncfile, "ctt") ctt = getvar(ncfile, "ctt")
@ -19,20 +21,23 @@ lats, lons = latlon_coords(ctt)
cart_proj = get_cartopy(ctt) cart_proj = get_cartopy(ctt)
# Create a figure # Create a figure
fig = plt.figure(figsize=(12,9)) fig = plt.figure(figsize=(12, 9))
# Set the GeoAxes to the projection used by WRF # Set the GeoAxes to the projection used by WRF
ax = plt.axes(projection=cart_proj) ax = plt.axes(projection=cart_proj)
# Download and add the states and coastlines # Download and add the states and coastlines
states = NaturalEarthFeature(category='cultural', scale='50m', facecolor='none', states = NaturalEarthFeature(category='cultural', scale='50m',
facecolor='none',
name='admin_1_states_provinces_shp') name='admin_1_states_provinces_shp')
ax.add_feature(states, linewidth=.5) ax.add_feature(states, linewidth=.5)
ax.coastlines('50m', linewidth=0.8) ax.coastlines('50m', linewidth=0.8)
# Make the contour outlines and filled contours for the smoothed sea level pressure. # Make the contour outlines and filled contours for the smoothed sea level
# pressure.
plt.contour(to_np(lons), to_np(lats), to_np(ctt), 10, colors="black", plt.contour(to_np(lons), to_np(lats), to_np(ctt), 10, colors="black",
transform=crs.PlateCarree()) transform=crs.PlateCarree())
plt.contourf(to_np(lons), to_np(lats), to_np(ctt), 10, transform=crs.PlateCarree(), plt.contourf(to_np(lons), to_np(lats), to_np(ctt), 10,
transform=crs.PlateCarree(),
cmap=get_cmap("jet")) cmap=get_cmap("jet"))
# Add a color bar # Add a color bar

10
test/generator_test.py

@ -1,17 +1,19 @@
from __future__ import (absolute_import, division, print_function, unicode_literals) from __future__ import (absolute_import, division, print_function,
unicode_literals)
from wrf import getvar from wrf import getvar
from netCDF4 import Dataset as nc from netCDF4 import Dataset as nc
#ncfile = nc("/Users/ladwig/Documents/wrf_files/wrfout_d01_2016-02-25_18_00_00")
ncfile = nc("/Users/ladwig/Documents/wrf_files/wrfout_d01_2016-10-07_00_00_00") ncfile = nc("/Users/ladwig/Documents/wrf_files/wrfout_d01_2016-10-07_00_00_00")
def gen_seq(): def gen_seq():
wrfseq = [ncfile, ncfile, ncfile] wrfseq = [ncfile, ncfile, ncfile]
for wrf in wrfseq: for wrf in wrfseq:
yield wrf yield wrf
p_gen = getvar(gen_seq(), "P", method="join") p_gen = getvar(gen_seq(), "P", method="join")
print(p_gen) print(p_gen)
del p_gen del p_gen

40
test/misc/extract_one_time.py

@ -0,0 +1,40 @@
from netCDF4 import Dataset
VARS_TO_KEEP = ("Times", "XLAT", "XLONG", "XLAT_U", "XLAT_V", "XLONG_U",
"XLONG_V", "U", "V", "W", "PH", "PHB", "T", "P", "PB", "Q2",
"T2", "PSFC", "U10", "V10", "XTIME", "QVAPOR", "QCLOUD",
"QGRAUP", "QRAIN", "QSNOW", "QICE", "MAPFAC_M", "MAPFAC_U",
"MAPFAC_V", "F", "HGT", "RAINC", "RAINSH", "RAINNC",
"I_RAINC", "I_RAINNC")
INPUT_FILE = "wrfout_d02_2005-08-28_00:00:00"
OUTPUT_FILE = "wrfout_problem_file"
DESIRED_TIME_INDEX = 0
with Dataset(INPUT_FILE) as infile, Dataset(OUTPUT_FILE, mode="w") as outfile:
# Copy the global attributes
outfile.setncatts(infile.__dict__)
# Copy Dimensions
for name, dimension in infile.dimensions.items():
if name != "Time":
dimsize = len(dimension)
outfile.createDimension(name, dimsize)
else:
outfile.createDimension(name, 1)
# Copy Variables
for name, variable in infile.variables.iteritems():
if name not in VARS_TO_KEEP:
continue
outvar = outfile.createVariable(name, variable.datatype,
variable.dimensions, zlib=True)
if len(variable.dimensions) > 1:
outvar[:] = variable[DESIRED_TIME_INDEX, :]
else:
outvar[:] = variable[DESIRED_TIME_INDEX]
outvar.setncatts(variable.__dict__)

70
test/misc/loop_and_fill_meta.py

@ -0,0 +1,70 @@
from __future__ import print_function, division
import os
import numpy as np
from netCDF4 import Dataset
from wrf import getvar, ALL_TIMES, to_np
import xarray
filename_list = ["/Users/ladwig/Documents/wrf_files/"
"wrf_vortex_single/wrfout_d02_2005-08-28_00:00:00",
"/Users/ladwig/Documents/wrf_files/wrf_vortex_single/"
"wrfout_d02_2005-08-28_03:00:00",
"/Users/ladwig/Documents/wrf_files/wrf_vortex_single/"
"wrfout_d02_2005-08-28_06:00:00",
"/Users/ladwig/Documents/wrf_files/wrf_vortex_single/"
"wrfout_d02_2005-08-28_09:00:00"]
result_shape = (4, 1, 96, 96)
# Let's get the first time so we can copy the metadata later
f = Dataset(filename_list[0])
# By setting squeeze to False, you'll get all the dimension names.
z1 = getvar(f, "T2", 0, squeeze=False)
xlat = getvar(f, "XLAT", 0)
xlong = getvar(f, "XLONG", 0)
z_final = np.empty(result_shape, np.float32)
# Modify this number if using more than 1 time per file
times_per_file = 1
data_times = []
xtimes = []
for timeidx in range(result_shape[0]):
# Compute the file index and the time index inside the file
fileidx = timeidx // times_per_file
file_timeidx = timeidx % times_per_file
f = Dataset(filename_list[fileidx])
z = getvar(f, "T2", file_timeidx)
t = getvar(f, "Times", file_timeidx)
xt = getvar(f, "xtimes", file_timeidx)
data_times.append(to_np(t))
xtimes.append(to_np(xt))
z_final[timeidx, :] = z[:]
f.close()
# Let's make the metadata. Dimension names should copy easily if you set
# sqeeze to False, otherwise you can just set them yourself is a tuple of
# dimension names. Since you wanted
# to keep the bottom_top dimension for this 2D variable (which is normally
# removed), I'm doing this manually.
z_dims = ["Time", "bottom_top", "south_north", "west_east"]
# Xarray doesn't copy coordinates easily (it always complains about shape
# mismatches), so do this manually
z_coords = {}
z_coords["Time"] = data_times
z_coords["XTIME"] = ("Time",), xtimes
z_coords["XLAT"] = ("south_north", "west_east"), xlat
z_coords["XLONG"] = ("south_north", "west_east"), xlong
z_name = "T2"
# Attributes copy nicely
z_attrs = {}
z_attrs.update(z1.attrs)
z_with_meta = xarray.DataArray(z_final, coords=z_coords, dims=z_dims,
attrs=z_attrs, name=z_name)

43
test/mocktest.py → test/misc/mocktest.py

@ -6,39 +6,48 @@ try:
except ImportError: except ImportError:
from mock import Mock as MagicMock from mock import Mock as MagicMock
class Mock(MagicMock): class Mock(MagicMock):
@classmethod @classmethod
def __getattr__(cls, name): def __getattr__(cls, name):
return Mock() return Mock()
MOCK_MODULES = ["numpy", "numpy.ma", "xarray", "cartopy",
MOCK_MODULES = ["numpy", "numpy.ma", "xarray", "cartopy",
"pandas", "matplotlib", "netCDF4", "mpl_toolkits.basemap", "pandas", "matplotlib", "netCDF4", "mpl_toolkits.basemap",
"wrf._wrffortran"] "wrf._wrffortran"]
sys.modules.update((mod_name, Mock()) for mod_name in MOCK_MODULES) sys.modules.update((mod_name, Mock()) for mod_name in MOCK_MODULES)
consts = {"DEFAULT_FILL" : 9.9692099683868690E36, consts = {"DEFAULT_FILL": 9.9692099683868690E36,
"DEFAULT_FILL_INT8" : -127, "DEFAULT_FILL_INT8": -127,
"DEFAULT_FILL_INT16" : -32767, "DEFAULT_FILL_INT16": -32767,
"DEFAULT_FILL_INT32" : -2147483647, "DEFAULT_FILL_INT32": -2147483647,
"DEFAULT_FILL_INT64" : -9223372036854775806, "DEFAULT_FILL_INT64": -9223372036854775806,
"DEFAULT_FILL_FLOAT" : 9.9692099683868690E36, "DEFAULT_FILL_FLOAT": 9.9692099683868690E36,
"DEFAULT_FILL_DOUBLE" : 9.9692099683868690E36, "DEFAULT_FILL_DOUBLE": 9.9692099683868690E36,
"fomp_sched_static" : 1, "fomp_sched_static": 1,
"fomp_sched_dynamic" : 2, "fomp_sched_dynamic": 2,
"fomp_sched_guided" : 3, "fomp_sched_guided": 3,
"fomp_sched_auto" : 4} "fomp_sched_auto": 4}
class MockWrfConstants(object): class MockWrfConstants(object):
def __init__(self): def __init__(self):
self.__dict__ = consts self.__dict__ = consts
def mock_asscalar(val): def mock_asscalar(val):
return float(val) return float(val)
sys.modules["wrf._wrffortran"].wrf_constants = MockWrfConstants() sys.modules["wrf._wrffortran"].wrf_constants = MockWrfConstants()
sys.modules["wrf._wrffortran"].omp_constants = MockWrfConstants() sys.modules["wrf._wrffortran"].omp_constants = MockWrfConstants()
sys.modules["numpy"].asscalar = mock_asscalar sys.modules["numpy"].asscalar = mock_asscalar
import wrf try:
print (wrf.get_coord_pairs.__doc__) import wrf
except ImportError:
pass
print(wrf.get_coord_pairs.__doc__)

188
test/projtest.py → test/misc/projtest.py

@ -7,13 +7,15 @@ import matplotlib.cm as cm
from netCDF4 import Dataset as NetCDF from netCDF4 import Dataset as NetCDF
from wrf import get_proj_params
from wrf.projection import getproj, RotatedLatLon, PolarStereographic
PYNGL = True PYNGL = True
try: try:
import Ngl import Ngl
except ImportError: except ImportError:
PYNGL = False PYNGL = False
BASEMAP = True BASEMAP = True
try: try:
import mpl_toolkits.basemap import mpl_toolkits.basemap
@ -25,19 +27,15 @@ try:
from cartopy import crs, feature from cartopy import crs, feature
except ImportError: except ImportError:
CARTOPY = False CARTOPY = False
from wrf import get_proj_params
from wrf.projection import getproj, RotatedLatLon, PolarStereographic
FILE_DIR = "/Users/ladwig/Documents/wrf_files/" FILE_DIR = "/Users/ladwig/Documents/wrf_files/"
WRF_FILES = [ WRF_FILES = [join(FILE_DIR, "norway", "geo_em.d01.nc"),
join(FILE_DIR, "norway", "geo_em.d01.nc"), join(FILE_DIR, "rotated_pole", "EAS_geo_em.d01.nc"),
join(FILE_DIR, "rotated_pole", "EAS_geo_em.d01.nc"), join(FILE_DIR, "rotated_pole", "EUR_geo_em.d01.nc"),
join(FILE_DIR, "rotated_pole", "EUR_geo_em.d01.nc"), join(FILE_DIR, "wrfout_d01_2016-02-25_18_00_00"),
join(FILE_DIR,"wrfout_d01_2016-02-25_18_00_00"), join(FILE_DIR, "wrfout_d01_2008-09-29_23-30-00"),
join(FILE_DIR, "wrfout_d01_2008-09-29_23-30-00"), join(FILE_DIR, "wrfout_d01_2010-06-13_21:00:00")]
join(FILE_DIR, "wrfout_d01_2010-06-13_21:00:00")]
def nz_proj(): def nz_proj():
@ -45,65 +43,68 @@ def nz_proj():
[-32.669853, -32.669853]]) [-32.669853, -32.669853]])
lons = np.array([[163.839595, -179.693502], lons = np.array([[163.839595, -179.693502],
[163.839595, -179.693502]]) [163.839595, -179.693502]])
params = {"MAP_PROJ" : 6, params = {"MAP_PROJ": 6,
"CEN_LAT" : -41.814869, "CEN_LAT": -41.814869,
"CEN_LON" : 179.693502, "CEN_LON": 179.693502,
"TRUELAT1" : 0, "TRUELAT1": 0,
"TRUELAT2": 0, "TRUELAT2": 0,
"MOAD_CEN_LAT" : -41.814869, "MOAD_CEN_LAT": -41.814869,
"STAND_LON" : 180.0 - 179.693502, "STAND_LON": 180.0 - 179.693502,
"POLE_LAT" : 48.185131, "POLE_LAT": 48.185131,
"POLE_LON" : 0.0} "POLE_LON": 0.0}
return lats, lons, RotatedLatLon(lats=lats, lons=lons, **params) return lats, lons, RotatedLatLon(lats=lats, lons=lons, **params)
def argentina_proj(): def argentina_proj():
lats = np.array([[-57.144064, -57.144064], lats = np.array([[-57.144064, -57.144064],
[-21.154470, -21.154470]]) [-21.154470, -21.154470]])
lons = np.array([[-86.893797, -37.089724], lons = np.array([[-86.893797, -37.089724],
[-86.893797, -37.089724]]) [-86.893797, -37.089724]])
params = {"MAP_PROJ" : 6, params = {"MAP_PROJ": 6,
"CEN_LAT" : -39.222954, "CEN_LAT": -39.222954,
"CEN_LON" : -65.980109, "CEN_LON": -65.980109,
"TRUELAT1" : 0, "TRUELAT1": 0,
"TRUELAT2": 0, "TRUELAT2": 0,
"MOAD_CEN_LAT" : -39.222954, "MOAD_CEN_LAT": -39.222954,
"STAND_LON" : 180.0 - -65.980109, "STAND_LON": 180.0 - -65.980109,
"POLE_LAT" : 90 + -39.222954, "POLE_LAT": 90 + -39.222954,
"POLE_LON" : 0.0} "POLE_LON": 0.0}
return lats, lons, RotatedLatLon(lats=lats, lons=lons, **params) return lats, lons, RotatedLatLon(lats=lats, lons=lons, **params)
def south_polar_proj(): def south_polar_proj():
lats = np.array([[-30.0,-30.0], lats = np.array([[-30.0, -30.0],
[-30.0,-30.0]]) [-30.0, -30.0]])
lons = np.array([[-120, 60], lons = np.array([[-120, 60],
[-120, 60]]) [-120, 60]])
params = {"MAP_PROJ" : 2, params = {"MAP_PROJ": 2,
"CEN_LAT" : -90.0, "CEN_LAT": -90.0,
"CEN_LON" : 0, "CEN_LON": 0,
"TRUELAT1" : -10.0, "TRUELAT1": -10.0,
"MOAD_CEN_LAT" : -90.0, "MOAD_CEN_LAT": -90.0,
"STAND_LON" : 0} "STAND_LON": 0}
return lats, lons, PolarStereographic(lats=lats, lons=lons, **params) return lats, lons, PolarStereographic(lats=lats, lons=lons, **params)
def north_polar_proj(): def north_polar_proj():
lats = np.array([[30.0,30.0], lats = np.array([[30.0, 30.0],
[30.0,30.0]]) [30.0, 30.0]])
lons = np.array([[-45, 140], lons = np.array([[-45, 140],
[-45, 140]]) [-45, 140]])
params = {"MAP_PROJ" : 2, params = {"MAP_PROJ": 2,
"CEN_LAT" : 90.0, "CEN_LAT": 90.0,
"CEN_LON" : 10, "CEN_LON": 10,
"TRUELAT1" : 10.0, "TRUELAT1": 10.0,
"MOAD_CEN_LAT" : 90.0, "MOAD_CEN_LAT": 90.0,
"STAND_LON" : 10} "STAND_LON": 10}
return lats, lons, PolarStereographic(lats=lats, lons=lons, **params) return lats, lons, PolarStereographic(lats=lats, lons=lons, **params)
@ -112,21 +113,23 @@ def dateline_rot_proj():
[71.717521, 71.717521]]) [71.717521, 71.717521]])
lons = np.array([[170.332771, -153.456292], lons = np.array([[170.332771, -153.456292],
[170.332771, -153.456292]]) [170.332771, -153.456292]])
params = {"MAP_PROJ" : 6, params = {"MAP_PROJ": 6,
"CEN_LAT" : 66.335764, "CEN_LAT": 66.335764,
"CEN_LON" : -173.143792, "CEN_LON": -173.143792,
"TRUELAT1" : 0, "TRUELAT1": 0,
"TRUELAT2": 0, "TRUELAT2": 0,
"MOAD_CEN_LAT" : 66.335764, "MOAD_CEN_LAT": 66.335764,
"STAND_LON" : 173.143792, "STAND_LON": 173.143792,
"POLE_LAT" : 90.0 - 66.335764, "POLE_LAT": 90.0 - 66.335764,
"POLE_LON" : 180.0} "POLE_LON": 180.0}
return lats, lons, RotatedLatLon(lats=lats, lons=lons, **params) return lats, lons, RotatedLatLon(lats=lats, lons=lons, **params)
class WRFProjTest(ut.TestCase): class WRFProjTest(ut.TestCase):
longMessage = True longMessage = True
def make_test(wrf_file=None, fixed_case=None): def make_test(wrf_file=None, fixed_case=None):
if wrf_file is not None: if wrf_file is not None:
ncfile = NetCDF(wrf_file) ncfile = NetCDF(wrf_file)
@ -144,75 +147,76 @@ def make_test(wrf_file=None, fixed_case=None):
elif fixed_case == "north_polar": elif fixed_case == "north_polar":
lats, lons, proj = north_polar_proj() lats, lons, proj = north_polar_proj()
elif fixed_case == "dateline_rot": elif fixed_case == "dateline_rot":
lats,lons,proj = dateline_rot_proj() lats, lons, proj = dateline_rot_proj()
print ("wrf proj4: {}".format(proj.proj4())) print("wrf proj4: {}".format(proj.proj4()))
if PYNGL: if PYNGL:
# PyNGL plotting # PyNGL plotting
wks_type = bytes("png") wks_type = bytes("png")
wks = Ngl.open_wks(wks_type,bytes("pyngl_{}".format(name_suffix))) wks = Ngl.open_wks(wks_type, bytes("pyngl_{}".format(name_suffix)))
mpres = proj.pyngl() mpres = proj.pyngl()
map = Ngl.map(wks,mpres) map = Ngl.map(wks, mpres)
Ngl.delete_wks(wks) Ngl.delete_wks(wks)
if BASEMAP: if BASEMAP:
# Basemap plotting # Basemap plotting
fig = plt.figure(figsize=(10,10)) fig = plt.figure(figsize=(10, 10))
ax = fig.add_axes([0.1,0.1,0.8,0.8]) ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
# Define and plot the meridians and parallels # Define and plot the meridians and parallels
min_lat = np.amin(lats) min_lat = np.amin(lats)
max_lat = np.amax(lats) max_lat = np.amax(lats)
min_lon = np.amin(lons) min_lon = np.amin(lons)
max_lon = np.amax(lons) max_lon = np.amax(lons)
parallels = np.arange(np.floor(min_lat), np.ceil(max_lat), parallels = np.arange(np.floor(min_lat), np.ceil(max_lat),
(max_lat - min_lat)/5.0) (max_lat - min_lat)/5.0)
meridians = np.arange(np.floor(min_lon), np.ceil(max_lon), meridians = np.arange(np.floor(min_lon), np.ceil(max_lon),
(max_lon - min_lon)/5.0) (max_lon - min_lon)/5.0)
bm = proj.basemap() bm = proj.basemap()
bm.drawcoastlines(linewidth=.5) bm.drawcoastlines(linewidth=.5)
#bm.drawparallels(parallels,labels=[1,1,1,1],fontsize=10) # bm.drawparallels(parallels,labels=[1,1,1,1],fontsize=10)
#bm.drawmeridians(meridians,labels=[1,1,1,1],fontsize=10) # bm.drawmeridians(meridians,labels=[1,1,1,1],fontsize=10)
print ("basemap proj4: {}".format(bm.proj4string)) print("basemap proj4: {}".format(bm.proj4string))
plt.savefig("basemap_{}.png".format(name_suffix)) plt.savefig("basemap_{}.png".format(name_suffix))
plt.close(fig) plt.close(fig)
if CARTOPY: if CARTOPY:
# Cartopy plotting # Cartopy plotting
fig = plt.figure(figsize=(10,10)) fig = plt.figure(figsize=(10, 10))
ax = plt.axes([0.1,0.1,0.8,0.8], projection=proj.cartopy()) ax = plt.axes([0.1, 0.1, 0.8, 0.8], projection=proj.cartopy())
print ("cartopy proj4: {}".format(proj.cartopy().proj4_params)) print("cartopy proj4: {}".format(proj.cartopy().proj4_params))
ax.coastlines('50m', linewidth=0.8) ax.coastlines('50m', linewidth=0.8)
#print proj.x_extents() # print proj.x_extents()
#print proj.y_extents() # print proj.y_extents()
ax.set_xlim(proj.cartopy_xlim()) ax.set_xlim(proj.cartopy_xlim())
ax.set_ylim(proj.cartopy_ylim()) ax.set_ylim(proj.cartopy_ylim())
ax.gridlines() ax.gridlines()
plt.savefig("cartopy_{}.png".format(name_suffix)) plt.savefig("cartopy_{}.png".format(name_suffix))
plt.close(fig) plt.close(fig)
if __name__ == "__main__": if __name__ == "__main__":
for wrf_file in WRF_FILES: for wrf_file in WRF_FILES:
test_func = make_test(wrf_file=wrf_file) test_func = make_test(wrf_file=wrf_file)
setattr(WRFProjTest, "test_proj", test_func) setattr(WRFProjTest, "test_proj", test_func)
test_func2 = make_test(fixed_case="south_rot") test_func2 = make_test(fixed_case="south_rot")
setattr(WRFProjTest, "test_south_rot", test_func2) setattr(WRFProjTest, "test_south_rot", test_func2)
test_func3 = make_test(fixed_case="arg_rot") test_func3 = make_test(fixed_case="arg_rot")
setattr(WRFProjTest, "test_arg_rot", test_func3) setattr(WRFProjTest, "test_arg_rot", test_func3)
test_func4 = make_test(fixed_case="south_polar") test_func4 = make_test(fixed_case="south_polar")
setattr(WRFProjTest, "test_south_polar", test_func4) setattr(WRFProjTest, "test_south_polar", test_func4)
test_func5 = make_test(fixed_case="north_polar") test_func5 = make_test(fixed_case="north_polar")
setattr(WRFProjTest, "test_north_polar", test_func5) setattr(WRFProjTest, "test_north_polar", test_func5)
test_func6 = make_test(fixed_case="dateline_rot") test_func6 = make_test(fixed_case="dateline_rot")
setattr(WRFProjTest, "test_dateline_rot", test_func6) setattr(WRFProjTest, "test_dateline_rot", test_func6)
ut.main() ut.main()

58
test/misc/quiver_test.py

@ -0,0 +1,58 @@
from netCDF4 import Dataset
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.cm import get_cmap
import cartopy.crs as crs
from cartopy.feature import NaturalEarthFeature
from wrf import (getvar, interplevel, to_np, latlon_coords, get_cartopy,
cartopy_xlim, cartopy_ylim)
# Open the NetCDF file
ncfile = Dataset("wrfout_d01_2016-10-07_00_00_00")
# Extract the pressure and wind variables
p = getvar(ncfile, "pressure")
# Note: This is m/s.
ua = getvar(ncfile, "ua")
va = getvar(ncfile, "va")
# Interpolate u, and v winds to 950 hPa
u_950 = interplevel(ua, p, 950)
v_950 = interplevel(va, p, 950)
# Get the lat/lon coordinates
lats, lons = latlon_coords(u_950)
# Get the map projection information
cart_proj = get_cartopy(u_950)
# Create the figure
fig = plt.figure(figsize=(12, 9))
ax = plt.axes(projection=cart_proj)
# Download and add the states and coastlines
states = NaturalEarthFeature(category='cultural', scale='50m',
facecolor='none',
name='admin_1_states_provinces_shp')
ax.add_feature(states, linewidth=0.5)
ax.coastlines('50m', linewidth=0.8)
# Add the 950 hPa wind barbs, only plotting every 'thin'ed barb. Adjust thin
# as needed. Also, no scaling is done for the arrows, so you might need to
# mess with the scale argument.
thin = 50
plt.quiver(to_np(lons[::thin, ::thin]), to_np(lats[::thin, ::thin]),
to_np(u_950[::thin, ::thin]), to_np(v_950[::thin, ::thin]),
transform=crs.PlateCarree())
# Set the map bounds
ax.set_xlim(cartopy_xlim(u_950))
ax.set_ylim(cartopy_ylim(v_950))
ax.gridlines()
plt.title("Arrows")
plt.show()

14
test/reduce_file.py → test/misc/reduce_file.py

@ -13,22 +13,22 @@ for att_name in in_nc.ncattrs():
# Copy Dimensions, but modify the vertical dimensions # Copy Dimensions, but modify the vertical dimensions
for dimname, dim in in_nc.dimensions.iteritems(): for dimname, dim in in_nc.dimensions.iteritems():
out_nc.createDimension(dimname, len(dim)) out_nc.createDimension(dimname, len(dim))
# Copy Variables and their Attributes, using the reduced vertical dimension # Copy Variables and their Attributes, using the reduced vertical dimension
for varname, var in in_nc.variables.iteritems(): for varname, var in in_nc.variables.iteritems():
if varname in ("T2", "XLAT", "XLONG", "XTIME"): if varname in ("T2", "XLAT", "XLONG", "XTIME"):
datatype = var.datatype datatype = var.datatype
dimensions = var.dimensions dimensions = var.dimensions
shape = var.shape shape = var.shape
new_shape = shape new_shape = shape
new_var = out_nc.createVariable(varname, datatype, dimensions) new_var = out_nc.createVariable(varname, datatype, dimensions)
new_var[:] = var[:] new_var[:] = var[:]
for att_name in var.ncattrs(): for att_name in var.ncattrs():
setattr(new_var, att_name, getattr(var, att_name)) setattr(new_var, att_name, getattr(var, att_name))
in_nc.close() in_nc.close()
out_nc.close() out_nc.close()

29
test/misc/snippet.py

@ -0,0 +1,29 @@
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
def main():
bm = Basemap(projection="rotpole",
o_lat_p=36.0,
o_lon_p=180.0,
llcrnrlat=-10.590603,
urcrnrlat=46.591976,
llcrnrlon=-139.08585,
urcrnrlon=22.661009,
lon_0=-106.0,
rsphere=6370000,
resolution='l')
fig = plt.figure(figsize=(8, 8))
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
bm.drawcoastlines(linewidth=.5)
print(bm.proj4string)
plt.savefig("basemap_map.png")
plt.close(fig)
if __name__ == "__main__":
main()

34
test/varcache.py → test/misc/varcache.py

@ -4,33 +4,37 @@ import time
from netCDF4 import Dataset from netCDF4 import Dataset
from wrf import getvar, ALL_TIMES, extract_vars from wrf import getvar, ALL_TIMES, extract_vars
wrf_filenames = ["/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/moving_nest/wrfout_d02_2005-08-28_00:00:00", wrf_filenames = ["/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/"
"/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/moving_nest/wrfout_d02_2005-08-28_12:00:00", "moving_nest/wrfout_d02_2005-08-28_00:00:00",
"/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/moving_nest/wrfout_d02_2005-08-29_00:00:00"] "/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/"
"moving_nest/wrfout_d02_2005-08-28_12:00:00",
"/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/"
"moving_nest/wrfout_d02_2005-08-29_00:00:00"]
wrfin = [Dataset(x) for x in wrf_filenames] wrfin = [Dataset(x) for x in wrf_filenames]
my_cache = extract_vars(wrfin, ALL_TIMES, ("P", "PB", "PH", "PHB", "T", "QVAPOR", "HGT", "U", "V", "W", "PSFC")) my_cache = extract_vars(wrfin, ALL_TIMES,
("P", "PB", "PH", "PHB", "T", "QVAPOR", "HGT", "U",
"V", "W", "PSFC"))
start = time.time() start = time.time()
for var in ("avo", "eth", "cape_2d", "cape_3d", "ctt", "dbz", "mdbz", for var in ("avo", "eth", "cape_2d", "cape_3d", "ctt", "dbz", "mdbz",
"geopt", "helicity", "lat", "lon", "omg", "p", "pressure", "geopt", "helicity", "lat", "lon", "omg", "p", "pressure",
"pvo", "pw", "rh2", "rh", "slp", "ter", "td2", "td", "tc", "pvo", "pw", "rh2", "rh", "slp", "ter", "td2", "td", "tc",
"theta", "tk", "tv", "twb", "updraft_helicity", "ua", "va", "theta", "tk", "tv", "twb", "updraft_helicity", "ua", "va",
"wa", "uvmet10", "uvmet", "z", "cfrac", "zstag", "geopt_stag"): "wa", "uvmet10", "uvmet", "z", "cfrac", "zstag", "geopt_stag"):
v = getvar(wrfin, var, ALL_TIMES) v = getvar(wrfin, var, ALL_TIMES)
end = time.time() end = time.time()
print ("Time taken without variable cache: ", (end-start)) print("Time taken without variable cache: ", (end-start))
start = time.time() start = time.time()
for var in ("avo", "eth", "cape_2d", "cape_3d", "ctt", "dbz", "mdbz", for var in ("avo", "eth", "cape_2d", "cape_3d", "ctt", "dbz", "mdbz",
"geopt", "helicity", "lat", "lon", "omg", "p", "pressure", "geopt", "helicity", "lat", "lon", "omg", "p", "pressure",
"pvo", "pw", "rh2", "rh", "slp", "ter", "td2", "td", "tc", "pvo", "pw", "rh2", "rh", "slp", "ter", "td2", "td", "tc",
"theta", "tk", "tv", "twb", "updraft_helicity", "ua", "va", "theta", "tk", "tv", "twb", "updraft_helicity", "ua", "va",
"wa", "uvmet10", "uvmet", "z", "cfrac", "zstag", "geopt_stag"): "wa", "uvmet10", "uvmet", "z", "cfrac", "zstag", "geopt_stag"):
v = getvar(wrfin, var, ALL_TIMES, cache=my_cache) v = getvar(wrfin, var, ALL_TIMES, cache=my_cache)
end = time.time() end = time.time()
print ("Time taken with variable cache: ", (end-start)) print("Time taken with variable cache: ", (end-start))

19
test/viewtest.py → test/misc/viewtest.py

@ -1,26 +1,25 @@
# Test snippet for f2py
import numpy as np import numpy as np
import wrf._wrffortran import wrf._wrffortran
errlen = int(wrf._wrffortran.constants.errlen) errlen = int(wrf._wrffortran.constants.errlen)
a = np.ones((3, 3, 3))
a = np.ones((3,3,3)) b = np.zeros((3, 3, 3, 3))
b = np.zeros((3,3,3,3))
c = np.zeros(errlen, "c") c = np.zeros(errlen, "c")
errstat = np.array(0) errstat = np.array(0)
errmsg = np.zeros(errlen, "c") errmsg = np.zeros(errlen, "c")
c[:] = "Test String" c[:] = "Test String"
for i in xrange(2): for i in xrange(2):
outview = b[i,:] outview = b[i, :]
outview = outview.T outview = outview.T
q = wrf._wrffortran.testfunc(a,outview,c,errstat=errstat,errmsg=errmsg) q = wrf._wrffortran.testfunc(a, outview, c, errstat=errstat,
print errstat errmsg=errmsg)
print(errstat)
str_bytes = (bytes(char).decode("utf-8") for char in errmsg[:]) str_bytes = (bytes(char).decode("utf-8") for char in errmsg[:])
print repr(errmsg) print(repr(errmsg))
print "".join(str_bytes).strip() print("".join(str_bytes).strip())

230
test/misc/wps.py

@ -0,0 +1,230 @@
# Hastily made script to read WPS intermediate files
from __future__ import print_function
import struct
import numpy as np
# Number of bytes used at the start and end of a fortran record to
# indicate the record size
SIZE_BYTES = 4
class WPSData(object):
def __init__(self, ifv=None, hdate=None, xfcst=None, map_source=None,
field=None, units=None, desc=None, xlvl=None, nx=None,
ny=None, iproj=None, startloc=None, startlat=None,
startlon=None, deltalat=None, deltalon=None, nlats=None,
dx=None, dy=None, xlonc=None, truelat1=None, truelat2=None,
earth_radius=None, is_wind_earth_rel=None, slab=None):
self.ifv = ifv
self.hdate = hdate
self.xfcst = xfcst
self.map_source = map_source
self.field = field
self.units = units
self.desc = desc
self.xlvl = xlvl
self.nx = nx
self.ny = ny
self.iproj = iproj
self.startloc = startloc
self.startlat = startlat
self.startlon = startlon
self.deltalat = deltalat
self.deltalon = deltalon
self.nlats = nlats
self.dx = dx
self.dy = dy
self.xlonc = xlonc
self.truelat1 = truelat1
self.truelat2 = truelat2
self.earth_radius = earth_radius
self.is_wind_earth_rel = is_wind_earth_rel
self.slab = slab
def _parse_record1(data):
result = {}
result["ifv"] = struct.unpack(">i", data)
return result
def _parse_record2(data):
result = {}
parsed = struct.unpack(">24sf32s9s25s46sfiii", data)
result["hdate"] = parsed[0]
result["xfcst"] = parsed[1]
result["map_source"] = parsed[2]
result["field"] = parsed[3]
result["units"] = parsed[4]
result["desc"] = parsed[5]
result["xlvl"] = parsed[6]
result["nx"] = parsed[7]
result["ny"] = parsed[8]
result["iproj"] = parsed[9]
return result
def _parse_record3(data, iproj):
result = {}
if iproj == 0:
fmt = ">8sfffff"
parsed = struct.unpack(fmt, data)
result["startloc"] = parsed[0]
result["startlat"] = parsed[1]
result["startlon"] = parsed[2]
result["deltalat"] = parsed[3]
result["deltalon"] = parsed[4]
result["earth_radius"] = parsed[5]
elif iproj == 1:
fmt = ">8sffffff"
parsed = struct.unpack(fmt, data)
result["startloc"] = parsed[0]
result["startlat"] = parsed[1]
result["startlon"] = parsed[2]
result["dx"] = parsed[3]
result["dy"] = parsed[4]
result["truelat1"] = parsed[5]
result["earth_radius"] = parsed[6]
elif iproj == 3:
fmt = ">8sffffffff"
parsed = struct.unpack(fmt, data)
result["startloc"] = parsed[0]
result["startlat"] = parsed[1]
result["startlon"] = parsed[2]
result["dx"] = parsed[3]
result["dy"] = parsed[4]
result["xlonc"] = parsed[5]
result["truelat1"] = parsed[6]
result["truelat2"] = parsed[7]
result["earth_radius"] = parsed[8]
elif iproj == 4:
fmt = ">8sfffff"
parsed = struct.unpack(fmt, data)
result["startloc"] = parsed[0]
result["startlat"] = parsed[1]
result["startlon"] = parsed[2]
result["nlats"] = parsed[3]
result["deltalon"] = parsed[4]
result["earth_radius"] = parsed[5]
elif iproj == 5:
fmt = ">8sfffffff"
parsed = struct.unpack(fmt, data)
result["startloc"] = parsed[0]
result["startlat"] = parsed[1]
result["startlon"] = parsed[2]
result["dx"] = parsed[3]
result["dy"] = parsed[4]
result["xlonc"] = parsed[5]
result["truelat1"] = parsed[6]
result["earth_radius"] = parsed[7]
return result
def _parse_record4(data):
result = {}
result["is_wind_earth_rel"] = struct.unpack(">i", data)
return result
def _parse_record5(data, nx, ny):
result = {}
size = nx * ny
fmt = ">{}f".format(size)
parsed = struct.unpack(fmt, data)
arr = np.array(parsed, dtype=np.float32)
result["slab"] = arr.reshape((nx, ny), order="F")
return result
_PARSE_MAP = {0: _parse_record1,
1: _parse_record2,
2: _parse_record3,
3: _parse_record4,
4: _parse_record5}
def parse_record(record_idx, data, iproj=None, nx=None, ny=None):
if record_idx == 0 or record_idx == 1 or record_idx == 3:
return _PARSE_MAP[record_idx](data)
elif record_idx == 2:
return _PARSE_MAP[record_idx](data, iproj)
elif record_idx == 4:
return _PARSE_MAP[record_idx](data, nx, ny)
def read_wps(wps_file, field=None):
with open(wps_file, "rb") as f:
wps_params = {}
record_list = []
raw_data = f.read()
record_size_idx = 0
end_of_file_idx = len(raw_data) - 1
while True:
iproj = None
nx = None
ny = None
keep_record = True
for record_idx in range(5):
# Each record has the size (in SIZE_BYTES bytes) at the
# start and end of each record. This might be compiler
# dependent though, so this might need to be modified.
# Also, the WPS files are stored big endian.
record_size = struct.unpack(
">i",
raw_data[record_size_idx: record_size_idx + SIZE_BYTES])
record_start = record_size_idx + SIZE_BYTES
record_end = record_start + record_size[0]
record_data = raw_data[record_start:record_end]
parsed_record = parse_record(record_idx, record_data, iproj,
nx, ny)
try:
field_name = parsed_record["field"].strip()
except KeyError:
pass
else:
if field is not None:
if field_name != field:
keep_record = False
try:
iproj = parsed_record["iproj"]
except KeyError:
pass
try:
nx = parsed_record["nx"]
except KeyError:
pass
try:
ny = parsed_record["ny"]
except KeyError:
pass
wps_params.update(parsed_record)
record_size_idx = record_end + SIZE_BYTES
if keep_record:
record_list.append(WPSData(**wps_params))
# Repeat for all record slabs
if record_end + SIZE_BYTES > end_of_file_idx:
break
return record_list

0
test/listBug.ncl → test/ncl/listBug.ncl

30
test/ncl_get_var.ncl → test/ncl/ncl_get_var.ncl

@ -32,7 +32,9 @@
"geopt", "helicity", "lat", "lon", "omg", "p", "pressure", \ "geopt", "helicity", "lat", "lon", "omg", "p", "pressure", \
"pvo", "pw", "rh2", "rh", "slp", "ter", "td2", "td", "tc", \ "pvo", "pw", "rh2", "rh", "slp", "ter", "td2", "td", "tc", \
"theta", "tk", "tv", "twb", "updraft_helicity", "ua", "va", \ "theta", "tk", "tv", "twb", "updraft_helicity", "ua", "va", \
"wa", "uvmet10", "uvmet", "z", "cfrac", "height_agl" /] "wa", "uvmet10", "uvmet", "z", "cfrac", "height_agl", \
"wspd_wdir", "wspd_wdir10", "uvmet_wspd_wdir", \
"uvmet10_wspd_wdir" /]
unique_dimname_list = NewList("fifo") unique_dimname_list = NewList("fifo")
unique_dimsize_list = NewList("fifo") unique_dimsize_list = NewList("fifo")
@ -87,7 +89,7 @@
xopt@timeidx = time xopt@timeidx = time
xopt@linecoords = True xopt@linecoords = True
ht_vertcross1 = wrf_user_vertcross(z, p, pivot, xopt) ht_vertcross1 = wrf_user_vert_cross(z, p, pivot, xopt)
fout->ht_vertcross1 = ht_vertcross1 fout->ht_vertcross1 = ht_vertcross1
@ -100,7 +102,7 @@
xopt@timeidx = time xopt@timeidx = time
xopt@linecoords = True xopt@linecoords = True
ht_vertcross2 = wrf_user_vertcross(z, p, pivot, xopt) ht_vertcross2 = wrf_user_vert_cross(z, p, pivot, xopt)
ht_vertcross2!1 = "vertical2" ht_vertcross2!1 = "vertical2"
ht_vertcross2!2 = "cross_line_idx2" ht_vertcross2!2 = "cross_line_idx2"
@ -131,7 +133,7 @@
xopt@linecoords = True xopt@linecoords = True
xopt@autolevels = 1000 xopt@autolevels = 1000
ht_vertcross3 = wrf_user_vertcross(z, p, start_end, xopt) ht_vertcross3 = wrf_user_vert_cross(z, p, start_end, xopt)
ht_vertcross3!0 = "Time" ht_vertcross3!0 = "Time"
ht_vertcross3!1 = "vertical3" ht_vertcross3!1 = "vertical3"
@ -150,7 +152,7 @@
p_var := p(i,:,:,:) p_var := p(i,:,:,:)
z_var := z(i,:,:,:) z_var := z(i,:,:,:)
ht_vertcross := wrf_user_vertcross(z_var, p_var, start_end, xopt) ht_vertcross := wrf_user_vert_cross(z_var, p_var, start_end, xopt)
dim0name = sprinti("vertical_t%i",i) dim0name = sprinti("vertical_t%i",i)
dim1name = sprinti("cross_line_idx_t%i",i) dim1name = sprinti("cross_line_idx_t%i",i)
@ -190,8 +192,8 @@
plev := 500. ; 500 MB plev := 500. ; 500 MB
hlev := 5000 ; 5000 m hlev := 5000 ; 5000 m
z2_500 = wrf_user_interplevel(z,p,plev,False) z2_500 = wrf_user_interp_level(z,p,plev,False)
p2_5000 = wrf_user_interplevel(p,z,hlev,False) p2_5000 = wrf_user_interp_level(p,z,hlev,False)
fout->z2_500 = z2_500 fout->z2_500 = z2_500
fout->p2_5000 = p2_5000 fout->p2_5000 = p2_5000
@ -199,8 +201,8 @@
plev := (/1000., 850., 500., 250./) plev := (/1000., 850., 500., 250./)
hlev := (/500., 2500., 5000., 10000. /) hlev := (/500., 2500., 5000., 10000. /)
z2_multi = wrf_user_interplevel(z,p,plev,False) z2_multi = wrf_user_interp_level(z,p,plev,False)
p2_multi = wrf_user_interplevel(p,z,hlev,False) p2_multi = wrf_user_interp_level(p,z,hlev,False)
fout->z2_multi = z2_multi fout->z2_multi = z2_multi
fout->p2_multi = p2_multi fout->p2_multi = p2_multi
@ -208,7 +210,7 @@
pblh = wrf_user_getvar(input_file, "PBLH", time) pblh = wrf_user_getvar(input_file, "PBLH", time)
opts := False opts := False
opts@inc2dlevs = True opts@inc2dlevs = True
p_lev2d = wrf_user_interplevel(p, z, pblh, opts) p_lev2d = wrf_user_interp_level(p, z, pblh, opts)
fout->p_lev2d = p_lev2d fout->p_lev2d = p_lev2d
@ -234,7 +236,7 @@
xopt@timeidx = 0 xopt@timeidx = 0
xopt@linecoords = True xopt@linecoords = True
t2_line2 = wrf_user_interpline(t2, pivot, xopt) t2_line2 = wrf_user_interp_line(t2, pivot, xopt)
fout->t2_line2 = t2_line2 fout->t2_line2 = t2_line2
@ -257,7 +259,7 @@
xopt@timeidx = 0 xopt@timeidx = 0
xopt@linecoords = True xopt@linecoords = True
t2_line3 = wrf_user_interpline(t2, start_end, xopt) t2_line3 = wrf_user_interp_line(t2, start_end, xopt)
t2_line3!1 = "line_idx_t2_line3" t2_line3!1 = "line_idx_t2_line3"
fout->t2_line3 = t2_line3 fout->t2_line3 = t2_line3
@ -270,7 +272,7 @@
name = sprinti("t2_line_t%i", i) name = sprinti("t2_line_t%i", i)
dim0name = sprinti("lineidx_t%i",i) dim0name = sprinti("lineidx_t%i",i)
var := t2(i,:,:) var := t2(i,:,:)
t2_line := wrf_user_interpline(var, start_end, xopt) t2_line := wrf_user_interp_line(var, start_end, xopt)
t2_line!0 = dim0name t2_line!0 = dim0name
fout->$name$ = t2_line fout->$name$ = t2_line
end do end do
@ -286,7 +288,7 @@
xopt@timeidx = 0 xopt@timeidx = 0
xopt@linecoords = True xopt@linecoords = True
t2_line4 = wrf_user_interpline(t2, start_end, xopt) t2_line4 = wrf_user_interp_line(t2, start_end, xopt)
t2_line4!0 = "t2_line4_idx" t2_line4!0 = "t2_line4_idx"
fout->t2_line4 = t2_line4 fout->t2_line4 = t2_line4

92
test/ncl/ncl_vertcross.ncl

@ -0,0 +1,92 @@
input_file = addfile("/Users/ladwig/Documents/wrf_files/wrfout_d02_2010-06-13_21:00:00.nc", "r")
z = wrf_user_getvar(input_file, "z", 0) ; grid point height
p = wrf_user_getvar(input_file, "pressure", 0) ; total pressure
dimsz = dimsizes(z)
pivot = (/ (dimsz(2)-1)/2, (dimsz(1)-1)/2 /) ; pivot point is center of domain
; For the new cross section routine
xopt = True
xopt@use_pivot = True
xopt@angle = 45.0
;xopt@levels =
;xopt@latlon =
xopt@file_handle = input_file
;xopt@timeidx =
xopt@linecoords = True
ht_vertcross = wrf_user_vertcross(z, p, pivot, xopt)
printVarSummary(ht_vertcross)
print(min(ht_vertcross@lats))
print(min(ht_vertcross@lons))
print(max(ht_vertcross@lats))
print(max(ht_vertcross@lons))
xopt@use_pivot = False
xopt@angle = 0.0
;xopt@levels =
xopt@latlon = True
xopt@file_handle = input_file
xopt@timeidx = 0
xopt@linecoords = True
loc_param = (/-104.3632, 32.8562, -95.15308, 40.06575 /) ; pivot point is center of domain
ht_vertcross2 = wrf_user_vertcross(z, p, loc_param, xopt)
printVarSummary(ht_vertcross2)
print(min(ht_vertcross2@lats))
print(min(ht_vertcross2@lons))
print(max(ht_vertcross2@lats))
print(max(ht_vertcross2@lons))
print(ht_vertcross2@lats(190))
print(ht_vertcross2@lons(190))
xopt@use_pivot = True
xopt@angle = 45.0
;xopt@levels =
xopt@latlon = True
xopt@file_handle = input_file
xopt@timeidx = 0
xopt@linecoords = True
loc_param := (/-99.98572, 36.54949 /) ; pivot point is center of domain
ht_vertcross3 = wrf_user_vertcross(z, p, loc_param, xopt)
printVarSummary(ht_vertcross3)
print(min(ht_vertcross3@lats))
print(min(ht_vertcross3@lons))
print(max(ht_vertcross3@lats))
print(max(ht_vertcross3@lons))
xopt@use_pivot = True
xopt@angle = 45.0
xopt@levels = (/1000., 850., 700., 500., 250. /)
xopt@latlon = True
xopt@file_handle = input_file
xopt@timeidx = 0
xopt@linecoords = True
loc_param := (/-99.98572, 36.54949 /) ; pivot point is center of domain
ht_vertcross4 = wrf_user_vertcross(z, p, loc_param, xopt)
printVarSummary(ht_vertcross4)
print(min(ht_vertcross4@lats))
print(min(ht_vertcross4@lons))
print(max(ht_vertcross4@lats))
print(max(ht_vertcross4@lons))
o = True
o@returnInt = False
o@useTime = 0
l = wrf_user_ll_to_xy(input_file, -99.98572, 36.54949, o)
print(l)
l1 = wrf_user_xy_to_ll(input_file, l(1), l(0), o)
print(l1)

81
test/ncl/refl10_cross.ncl

@ -0,0 +1,81 @@
a = addfile("wrfout_d03_2017-04-03_06:00:00_ctrl","r")
time = 0
refl_10cm = wrf_user_getvar(a,"REFL_10CM",time)
z = wrf_user_getvar(a, "z", time)
lat = wrf_user_getvar(a, "lat", time)
lon = wrf_user_getvar(a, "lon", time)
; convert the lat/lon to x,y
start_lat = 20.9
start_lon = 92.5
end_lat = 29.2
end_lon = 92.5
opt = True
start_ij = wrf_user_ll_to_ij(a, start_lon, start_lat, opt)
start_ij = start_ij - 1
end_ij = wrf_user_ll_to_ij(a, end_lon, end_lat, opt)
end_ij = end_ij - 1
start_end = (/start_ij(0), start_ij(1), end_ij(0), end_ij(1)/)
lat_line = wrf_user_intrp2d(lat,start_end,0.0,True)
nlat = dimsizes(lat_line)
lon_line = wrf_user_intrp2d(lon,start_end,0.0,True)
refl_cross = wrf_user_intrp3d(refl_10cm,z,"v",start_end,0.,True)
; Need to make a vertical coordinate by using the same code as the
; cross section
; Currently, the vertical coordinate is not set, so let's do it
; manually. This will be fixed in the next version of NCL.
; If you want to set these levels yourself, you'll need to copy the
; code I sent before and manually set the levels in the cross section
; routine, then do it again here.
z_max = max(z)
z_min = 0.
dz = 0.01 * z_max
nlevels = tointeger( z_max/dz )
z_var2d = new( (/nlevels/), typeof(z))
z_var2d(0) = z_min
do i=1, nlevels-1
z_var2d(i) = z_var2d(0)+i*dz
end do
refl_cross&Vertical = z_var2d
wks = gsn_open_wks("png","cross")
cmap := read_colormap_file("BlAqGrYeOrReVi200")
cmap(0,:) = (/0,0,0,0/) ; make first color fully transparent
resx = True
resx@gsnMaximize = True
resx@lbLabelAutoStride = True ; default v6.1.0
resx@cnFillOn = True ; turn on color fill
resx@cnLinesOn = False ; turn lines on/off ; True is default
resx@cnLineLabelsOn = False ; turn line labels on/off ; True is default
resx@cnFillPalette = cmap
nLabels = 8 ; arbitrary
resx@tmXBLabels = new(nLabels,"string")
resx@tmXBMode = "Explicit"
resx@tmXBValues := toint(fspan(0,nlat-1,nLabels))
do i=0,nLabels-1
x = lon_line(i)
y = lat_line(i)
resx@tmXBLabels(i) = sprintf("%5.1f", y)+"~C~"+sprintf("%5.1f", x)
end do
resx@tiMainString = "Full South-North Grid Line X-Section"
plot1 = gsn_csm_contour(wks, refl_cross, resx )

26
test/ncl/rotated_test.ncl

@ -0,0 +1,26 @@
;---Open file and calculate slp.
a = addfile("/Users/ladwig/Documents/wrf_files/rotated_pole_test.nc","r")
t2 = wrf_user_getvar(a,"T2",0)
;---Start the graphics
wks = gsn_open_wks("x11","wrf")
;---Set some graphical resources
res = True
res@gsnMaximize = True
res@cnFillOn = True
res@tfDoNDCOverlay = True ; This is necessary if you don't
; set sfXArray/sfYArray
;---Add additional map resources
res = wrf_map_resources(a,res)
;---Change some of the resources that were set (these were set to "gray")
res@mpGeophysicalLineColor = "black"
res@mpNationalLineColor = "black"
res@mpUSStateLineColor = "black"
plot = gsn_csm_contour_map(wks,t2,res)

21
test/ncl/test_this.ncl

@ -0,0 +1,21 @@
ifils = systemfunc ("ls /Users/ladwig/Documents/wrf_files/wrf_vortex_single/wrfout_d02_*")
print(ifils)
a = addfiles(ifils, "r")
;a = addfile("/Users/ladwig/Documents/wrf_files/wrfout_d01_2010-06-13_21:00:00.nc", "r")
lats := (/22.0, 25.0, 27.0 /)
lons := (/-90.0, -87.5, -83.75 /)
opt = True
opt@useTime = -1
opt@returnI = False
xy = wrf_user_ll_to_xy(a, lons, lats, opt)
print(xy)
x_s = (/10, 50, 90 /)
y_s = (/10, 50, 90 /)
ll = wrf_user_xy_to_ll(a, x_s, y_s, opt)
print(ll)

0
test/test_vinterp.ncl → test/ncl/test_vinterp.ncl

416
test/ncl/wrf_user_vertcross.ncl

@ -0,0 +1,416 @@
;--------------------------------------------------------------------------------
undef("wrf_user_ll_to_xy")
function wrf_user_ll_to_xy( file_handle, longitude:numeric, latitude:numeric, \
opts_args:logical )
; This is the same as wrf_user_ll_to_ij, but returns 0-based indexes
begin
;
; As of NCL V6.0.0, wrf_user_ll_to_ij can now handle a file
; or a list of files.
;
if(typeof(file_handle).eq."file") then
ISFILE = True
nc_file = file_handle
elseif(typeof(file_handle).eq."list") then
ISFILE = False
nc_file = file_handle[0]
else
print("wrf_user_ll_to_xy: Error: the first argument must be a file or a list of files opened with addfile or addfiles")
return
end if
opts = opts_args
useT = get_res_value(opts,"useTime",0)
returnI= get_res_value(opts,"returnInt",True)
res = True
res@MAP_PROJ = nc_file@MAP_PROJ
res@TRUELAT1 = nc_file@TRUELAT1
res@TRUELAT2 = nc_file@TRUELAT2
res@STAND_LON = nc_file@STAND_LON
res@DX = nc_file@DX
res@DY = nc_file@DY
if (res@MAP_PROJ .eq. 6) then
res@POLE_LAT = nc_file@POLE_LAT
res@POLE_LON = nc_file@POLE_LON
res@LATINC = (res@DY*360.)/2.0/3.141592653589793/6370000.
res@LONINC = (res@DX*360.)/2.0/3.141592653589793/6370000.
else
res@POLE_LAT = 90.0
res@POLE_LON = 0.0
res@LATINC = 0.0
res@LONINC = 0.0
end if
if(isfilevar(nc_file,"XLAT"))
if(ISFILE) then
XLAT = nc_file->XLAT(useT,:,:)
XLONG = nc_file->XLONG(useT,:,:)
else
XLAT = file_handle[:]->XLAT
XLONG = file_handle[:]->XLONG
end if
else
if(ISFILE) then
XLAT = nc_file->XLAT_M(useT,:,:)
XLONG = nc_file->XLONG_M(useT,:,:)
else
XLAT = file_handle[:]->XLAT_M
XLONG = file_handle[:]->XLONG_M
end if
end if
if(dimsizes(dimsizes(XLAT)).eq.2) then
; Rank 2
res@REF_LAT = XLAT(0,0)
res@REF_LON = XLONG(0,0)
else
; Rank 3
res@REF_LAT = XLAT(useT,0,0)
res@REF_LON = XLONG(useT,0,0)
end if
res@KNOWNI = 1.0
res@KNOWNJ = 1.0
loc = wrf_ll_to_ij (longitude, latitude, res)
loc = loc - 1
if (dimsizes(dimsizes(loc)) .eq. 1) then
loc!0 = "x_y"
elseif (dimsizes(dimsizes(loc)) .eq. 2) then
loc!0 = "x_y"
loc!1 = "idx"
else ; Not currently supported
loc!0 = "x_y"
loc!1 = "domain_idx"
loc!2 = "idx"
end if
if ( returnI ) then
loci = new(dimsizes(loc),integer)
;loci@_FillValue = default_fillvalue("integer") ; was -999
loci = tointeger(loc + .5)
loci!0 = loc!0
return(loci)
else
return(loc)
end if
end
;--------------------------------------------------------------------------------
undef("wrf_user_xy_to_ll")
function wrf_user_xy_to_ll( file_handle, x:numeric, y:numeric, \
opts_args:logical )
begin
;
; As of NCL V6.0.0, wrf_user_ll_to_ij can now handle a file
; or a list of files.
;
if(typeof(file_handle).eq."file") then
ISFILE = True
nc_file = file_handle
elseif(typeof(file_handle).eq."list") then
ISFILE = False
nc_file = file_handle[0]
else
print("wrf_user_xy_to_ll: Error: the first argument must be a file or a list of files opened with addfile or addfiles")
return
end if
opts = opts_args
useT = get_res_value(opts,"useTime",0)
res = True
res@MAP_PROJ = nc_file@MAP_PROJ
res@TRUELAT1 = nc_file@TRUELAT1
res@TRUELAT2 = nc_file@TRUELAT2
res@STAND_LON = nc_file@STAND_LON
res@DX = nc_file@DX
res@DY = nc_file@DY
if (res@MAP_PROJ .eq. 6) then
res@POLE_LAT = nc_file@POLE_LAT
res@POLE_LON = nc_file@POLE_LON
res@LATINC = (res@DY*360.)/2.0/3.141592653589793/6370000.
res@LONINC = (res@DX*360.)/2.0/3.141592653589793/6370000.
else
res@POLE_LAT = 90.0
res@POLE_LON = 0.0
res@LATINC = 0.0
res@LONINC = 0.0
end if
if(isfilevar(nc_file,"XLAT")) then
if(ISFILE) then
XLAT = nc_file->XLAT(useT,:,:)
XLONG = nc_file->XLONG(useT,:,:)
else
XLAT = file_handle[:]->XLAT
XLONG = file_handle[:]->XLONG
end if
else
if(ISFILE) then
XLAT = nc_file->XLAT_M(useT,:,:)
XLONG = nc_file->XLONG_M(useT,:,:)
else
XLAT = file_handle[:]->XLAT_M
XLONG = file_handle[:]->XLONG_M
end if
end if
if(dimsizes(dimsizes(XLAT)).eq.2) then
; Rank 2
res@REF_LAT = XLAT(0,0)
res@REF_LON = XLONG(0,0)
else
; Rank 3
res@REF_LAT = XLAT(useT,0,0)
res@REF_LON = XLONG(useT,0,0)
end if
res@KNOWNI = 1.0
res@KNOWNJ = 1.0
; Convert to 1-based indexes for Fortran
new_x = x + 1
new_y = y + 1
loc = wrf_ij_to_ll (new_x,new_y,res)
if (dimsizes(dimsizes(loc)) .eq. 1) then
loc!0 = "lon_lat"
elseif (dimsizes(dimsizes(loc)) .eq. 2) then
loc!0 = "lon_lat"
loc!1 = "idx"
else ; Not currently supported
loc!0 = "lon_lat"
loc!1 = "domain_idx"
loc!2 = "idx"
end if
return(loc)
end
;--------------------------------------------------------------------------------
undef("wrf_user_vertcross")
function wrf_user_vertcross(var3d:numeric, z_in:numeric, \
loc_param:numeric, opts:logical )
; var3d - 3d field to interpolate (all input fields must be unstaggered)
; z_in - interpolate to this field (either p/z)
; loc_param - an array of 4 values representing the start point and end point
; for the cross section (start_x, start_y, end_x, end_y) OR a single
; point when opt@use_pivot is True representing the pivot point.
; The values can be in grid coordinates or lat/lon coordinates
; (start_x = start_lon, start_y = start_lat, ...). If using
; lat/lon coordinates, then opt@latlon must be True.
; opts - optional arguments
; use_pivot - set to True to indicate that loc_param and angle are used,
; otherwise loc_param is set to 4 values to indicate a start and
; end point
; angle - an angle for vertical plots - 90 represent a WE cross section,
; ignored if use_pivot is False.
; levels - the vertical levels to use in the same units as z_in. Set to
; False to automatically generate the number of levels specified
; by autolevels.
; latlon - set to True if the values in loc_param are latitude and longitude
; values rather than grid values
; file_handle - must be set to a file handle when latlon is True or
; linecoords is True, otherwise this is ignored.
; timeidx - the time index to use for moving nests when latlon is True. Set
; to 0 if the nest is not moving.
; linecoords - set to True to include the latitude and longitude coordinates
; for the cross section line in the output attributes.
; autolevels - set to the desired number of levels when levels are
; selected automatically (default 100).
begin
use_pivot = get_res_value(opts, "use_pivot", False)
angle = get_res_value(opts, "angle", 0.0)
levels = get_res_value(opts, "levels", new(1,integer))
latlon = get_res_value(opts, "latlon", False)
file_handle = get_res_value(opts, "file_handle", 0)
timeidx = get_res_value(opts, "timeidx", 0)
linecoords = get_res_value(opts, "linecoords", False)
nlevels = get_res_value(opts, "autolevels", 100)
dims = dimsizes(var3d)
nd = dimsizes(dims)
dimX = dims(nd-1)
dimY = dims(nd-2)
dimZ = dims(nd-3)
if ( nd .eq. 4 ) then
z = z_in(0,:,:,:)
else
z = z_in
end if
; Convert latlon to xy coordinates
if (use_pivot) then
if (latlon) then
opt = True
opt@returnInt = True
opt@useTime = timeidx
ij := wrf_user_ll_to_xy(file_handle, loc_param(0), loc_param(1), opt)
start_x = ij(0)
start_y = ij(1)
else
start_x = loc_param(0)
start_y = loc_param(1)
end if
else
if (latlon) then
opt = True
opt@returnInt = True
opt@useTime = timeidx
ij := wrf_user_ll_to_xy(file_handle, (/ loc_param(0), loc_param(2) /), (/ loc_param(1), loc_param(3) /), opt)
start_x = ij(0,0)
start_y = ij(1,0)
end_x = ij(0,1)
end_y = ij(1,1)
else
start_x = loc_param(0)
start_y = loc_param(1)
end_x = loc_param(2)
end_y = loc_param(3)
end if
end if
; get the lat/lons along the cross section line if requested
; set the cross section line coordinates if requested
if (linecoords) then
latname = "XLAT"
lonname = "XLONG"
if(.not. isfilevar(file_handle,"XLAT")) then
if(isfilevar(file_handle,"XLAT_M")) then
latname = "XLAT_M"
lonname = "XLONG_M"
end if
end if
latvar = _get_wrf_var(file_handle, latname, timeidx)
lonvar = _get_wrf_var(file_handle, lonname, timeidx)
if (use_pivot) then
loc := (/start_x, start_y/)
linelats = wrf_user_intrp2d(latvar, loc, angle, False)
linelons = wrf_user_intrp2d(lonvar, loc, angle, False)
else
loc := (/start_x, start_y, end_x, end_y /)
linelats = wrf_user_intrp2d(latvar, loc, angle, True)
linelons = wrf_user_intrp2d(lonvar, loc, angle, True)
end if
end if
; set vertical cross section
; Note for wrf_user_set_xy, opt is False when pivot and angle used.
if (use_pivot) then
xy = wrf_user_set_xy( z, start_x, start_y, \ ; assumes 0-based indexing in v6.5.0
0.0, 0.0, angle, False )
else
xy = wrf_user_set_xy( z, start_x, start_y, \ ; assumes 0-based indexing in v6.5.0
end_x, end_y, \
angle, True)
end if
xp = dimsizes(xy)
; first we interp z
var2dz = wrf_interp_2d_xy( z, xy)
; interp to constant z grid
if (all(ismissing(levels))) then
if(var2dz(0,0) .gt. var2dz(1,0) ) then ; monotonically decreasing coordinate
z_max = floor(max(z)/10)*10 ; bottom value
z_min = ceil(min(z)/10)*10 ; top value
dz = (1.0/nlevels) * (z_max - z_min)
;nlevels = tointeger( (z_max-z_min)/dz)
z_var2d = new( (/nlevels/), typeof(z))
z_var2d(0) = z_max
dz = -dz
else
z_max = max(z)
z_min = 0.
dz = (1.0/nlevels) * z_max
;nlevels = tointeger( z_max/dz )
z_var2d = new( (/nlevels/), typeof(z))
z_var2d(0) = z_min
end if
do i=1, nlevels-1
z_var2d(i) = z_var2d(0)+i*dz
end do
else
z_var2d = levels
nlevels = dimsizes(z_var2d)
end if
; interp the variable
if ( dimsizes(dims) .eq. 4 ) then
var2d = new( (/dims(0), nlevels, xp(0)/), typeof(var2dz))
do it = 0,dims(0)-1
var2dtmp = wrf_interp_2d_xy( var3d(it,:,:,:), xy)
do i=0,xp(0)-1
var2d(it,:,i) = wrf_interp_1d( var2dtmp(:,i), var2dz(:,i), z_var2d)
end do
end do
var2d!0 = var3d!0
var2d!1 = "vertical"
var2d!2 = "cross_line_idx"
else
var2d = new( (/nlevels, xp(0)/), typeof(var2dz))
var2dtmp = wrf_interp_2d_xy( var3d, xy)
do i=0,xp(0)-1
var2d(:,i) = wrf_interp_1d( var2dtmp(:,i), var2dz(:,i), z_var2d)
end do
var2d!0 = "vertical"
var2d!1 = "cross_line_idx"
end if
st_x = tointeger(xy(0,0)) ; + 1 (removed 1-based indexing in 6.5.0)
st_y = tointeger(xy(0,1)) ; + 1
ed_x = tointeger(xy(xp(0)-1,0)) ; + 1
ed_y = tointeger(xy(xp(0)-1,1)) ; + 1
if (.not. use_pivot) then
var2d@Orientation = "Cross-Section: (" + \
st_x + "," + st_y + ") to (" + \
ed_x + "," + ed_y + ")"
else
var2d@Orientation = "Cross-Section: (" + \
st_x + "," + st_y + ") to (" + \
ed_x + "," + ed_y + ") ; center=(" + \
start_x + "," + start_y + \
") ; angle=" + angle
end if
if (linecoords) then
var2d@lats = linelats
var2d@lons = linelons
end if
var2d&vertical = z_var2d
return(var2d)
end

30
test/snippet.py

@ -1,30 +0,0 @@
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
def main():
bm = Basemap(projection = "rotpole",
o_lat_p = 36.0,
o_lon_p = 180.0,
llcrnrlat = -10.590603,
urcrnrlat = 46.591976,
llcrnrlon = -139.08585,
urcrnrlon = 22.661009,
lon_0 = -106.0,
rsphere = 6370000,
resolution = 'l')
fig = plt.figure(figsize=(8,8))
ax = fig.add_axes([0.1,0.1,0.8,0.8])
bm.drawcoastlines(linewidth=.5)
print bm.proj4string
plt.savefig("basemap_map.png")
plt.close(fig)
if __name__ == "__main__":
main()

80
test/test_filevars.py

@ -1,77 +1,79 @@
import unittest as ut import unittest as ut
import numpy.testing as nt import numpy.testing as nt
import numpy as np import numpy as np
import numpy.ma as ma import numpy.ma as ma
import os, sys import os
import sys
import subprocess import subprocess
from wrf import getvar, ALL_TIMES from wrf import getvar, ALL_TIMES
TEST_DIR = "/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/moving_nest" TEST_DIR = "/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/moving_nest"
TEST_FILENAMES = ["wrfout_d02_2005-08-28_00:00:00", TEST_FILENAMES = ["wrfout_d02_2005-08-28_00:00:00",
"wrfout_d02_2005-08-28_12:00:00", "wrfout_d02_2005-08-28_12:00:00",
"wrfout_d02_2005-08-29_00:00:00"] "wrfout_d02_2005-08-29_00:00:00"]
TEST_FILES = [os.path.join(TEST_DIR, x) for x in TEST_FILENAMES] TEST_FILES = [os.path.join(TEST_DIR, x) for x in TEST_FILENAMES]
# Python 3 # Python 3
if sys.version_info > (3,): if sys.version_info > (3,):
xrange = range xrange = range
class WRFFileVarsTest(ut.TestCase): class WRFFileVarsTest(ut.TestCase):
longMessage = True longMessage = True
def make_test(ncfiles, varname): def make_test(ncfiles, varname):
def test(self): def test(self):
#import time # import time
#very_start = time.time() # very_start = time.time()
#start = time.time() # start = time.time()
t1 = getvar(ncfiles, varname, 0) t1 = getvar(ncfiles, varname, 0)
#end = time.time() # end = time.time()
#print ("t1: ", start-end) # print("t1: ", start-end)
#start = time.time() # start = time.time()
t2 = getvar(ncfiles, varname, 0, meta=False) t2 = getvar(ncfiles, varname, 0, meta=False)
#end = time.time() # end = time.time()
#print ("t2: ", start-end) # print("t2: ", start-end)
#start = time.time() # start = time.time()
t3 = getvar(ncfiles, varname, ALL_TIMES) t3 = getvar(ncfiles, varname, ALL_TIMES)
#end = time.time() # end = time.time()
#print ("t3: ", start-end) # print("t3: ", start-end)
#start = time.time() # start = time.time()
t4 = getvar(ncfiles, varname, ALL_TIMES, meta=False) t4 = getvar(ncfiles, varname, ALL_TIMES, meta=False)
#end = time.time() # end = time.time()
#print ("t4: ", start-end) # print("t4: ", start-end)
#start = time.time() # start = time.time()
t5 = getvar(ncfiles, varname, ALL_TIMES, method="join") t5 = getvar(ncfiles, varname, ALL_TIMES, method="join")
#end = time.time() # end = time.time()
#print ("t5: ", start-end) # print("t5: ", start-end)
#start = time.time() # start = time.time()
t6 = getvar(ncfiles, varname, ALL_TIMES, method="join", meta=False) t6 = getvar(ncfiles, varname, ALL_TIMES, method="join", meta=False)
#end = time.time() # end = time.time()
#print ("t6: ", start-end) # print("t6: ", start-end)
#start = time.time() # start = time.time()
#print ("Total Time: ", (end-start)) # print("Total Time: ", (end-start))
return test return test
if __name__ == "__main__": if __name__ == "__main__":
from netCDF4 import Dataset from netCDF4 import Dataset
ncfiles = [Dataset(x) for x in TEST_FILES] ncfiles = [Dataset(x) for x in TEST_FILES]
#import scipy.io # import scipy.io
#ncfiles = [scipy.io.netcdf.netcdf_file(x) for x in TEST_FILES] # ncfiles = [scipy.io.netcdf.netcdf_file(x) for x in TEST_FILES]
file_vars = ncfiles[0].variables.keys() file_vars = ncfiles[0].variables.keys()
ignore_vars = [] ignore_vars = []
for var in file_vars: for var in file_vars:
if var in ignore_vars: if var in ignore_vars:
continue continue
test_func1 = make_test(ncfiles, var) test_func1 = make_test(ncfiles, var)
setattr(WRFFileVarsTest, 'test_{0}'.format(var), test_func1) setattr(WRFFileVarsTest, 'test_{0}'.format(var), test_func1)
ut.main() ut.main()

103
test/test_inputs.py

@ -1,5 +1,5 @@
import unittest as ut import unittest as ut
import numpy.testing as nt import numpy.testing as nt
import numpy as np import numpy as np
import numpy.ma as ma import numpy.ma as ma
import os import os
@ -17,20 +17,21 @@ TEST_FILES = [os.path.join(IN_DIR, "wrfout_d02_2005-08-28_00:00:00"),
os.path.join(IN_DIR, "wrfout_d02_2005-08-28_12:00:00"), os.path.join(IN_DIR, "wrfout_d02_2005-08-28_12:00:00"),
os.path.join(IN_DIR, "wrfout_d02_2005-08-29_00:00:00")] os.path.join(IN_DIR, "wrfout_d02_2005-08-29_00:00:00")]
def wrfin_gen(wrf_in): def wrfin_gen(wrf_in):
for x in wrf_in: for x in wrf_in:
yield x yield x
class wrf_in_iter_class(object): class wrf_in_iter_class(object):
def __init__(self, wrf_in): def __init__(self, wrf_in):
self._wrf_in = wrf_in self._wrf_in = wrf_in
self._total = len(wrf_in) self._total = len(wrf_in)
self._i = 0 self._i = 0
def __iter__(self): def __iter__(self):
return self return self
def next(self): def next(self):
if self._i >= self._total: if self._i >= self._total:
raise StopIteration raise StopIteration
@ -38,7 +39,7 @@ class wrf_in_iter_class(object):
result = self._wrf_in[self._i] result = self._wrf_in[self._i]
self._i += 1 self._i += 1
return result return result
# Python 3 # Python 3
def __next__(self): def __next__(self):
return self.next() return self.next()
@ -48,34 +49,34 @@ def make_test(varname, wrf_in):
def test(self): def test(self):
x = getvar(wrf_in, varname, 0) x = getvar(wrf_in, varname, 0)
xa = getvar(wrf_in, varname, None) xa = getvar(wrf_in, varname, None)
return test return test
def make_interp_test(varname, wrf_in): def make_interp_test(varname, wrf_in):
def test(self): def test(self):
# Only testing vinterp since other interpolation just use variables # Only testing vinterp since other interpolation just use variables
if (varname == "vinterp"): if (varname == "vinterp"):
for timeidx in (0, None): for timeidx in (0, None):
eth = getvar(wrf_in, "eth", timeidx=timeidx) eth = getvar(wrf_in, "eth", timeidx=timeidx)
interp_levels = [850,500,5] interp_levels = [850, 500, 5]
field = vinterp(wrf_in, field = vinterp(wrf_in,
field=eth, field=eth,
vert_coord="pressure", vert_coord="pressure",
interp_levels=interp_levels, interp_levels=interp_levels,
extrapolate=True, extrapolate=True,
field_type="theta-e", field_type="theta-e",
timeidx=timeidx, timeidx=timeidx,
log_p=True) log_p=True)
else: else:
pass pass
return test return test
def make_latlon_test(testid, wrf_in): def make_latlon_test(testid, wrf_in):
def test(self): def test(self):
if testid == "xy_out": if testid == "xy_out":
# Lats/Lons taken from NCL script, just hard-coding for now # Lats/Lons taken from NCL script, just hard-coding for now
lats = [-55, -60, -65] lats = [-55, -60, -65]
@ -87,42 +88,44 @@ def make_latlon_test(testid, wrf_in):
j_s = np.asarray([10, 100, 150], int) j_s = np.asarray([10, 100, 150], int)
ll = xy_to_ll(wrf_in, i_s, j_s, timeidx=0) ll = xy_to_ll(wrf_in, i_s, j_s, timeidx=0)
ll = xy_to_ll(wrf_in, i_s, j_s, timeidx=None) ll = xy_to_ll(wrf_in, i_s, j_s, timeidx=None)
return test return test
class WRFVarsTest(ut.TestCase): class WRFVarsTest(ut.TestCase):
longMessage = True longMessage = True
class WRFInterpTest(ut.TestCase): class WRFInterpTest(ut.TestCase):
longMessage = True longMessage = True
class WRFLatLonTest(ut.TestCase): class WRFLatLonTest(ut.TestCase):
longMessage = True longMessage = True
if __name__ == "__main__": if __name__ == "__main__":
from wrf import (omp_set_num_threads, omp_set_schedule, omp_get_schedule, from wrf import (omp_set_num_threads, omp_set_schedule, omp_get_schedule,
omp_set_dynamic, omp_get_num_procs, OMP_SCHED_STATIC) omp_set_dynamic, omp_get_num_procs, OMP_SCHED_STATIC)
omp_set_num_threads(omp_get_num_procs()-1) omp_set_num_threads(omp_get_num_procs()-1)
omp_set_schedule(OMP_SCHED_STATIC, 0) omp_set_schedule(OMP_SCHED_STATIC, 0)
omp_set_dynamic(False) omp_set_dynamic(False)
ignore_vars = [] # Not testable yet ignore_vars = [] # Not testable yet
wrf_vars = ["avo", "eth", "cape_2d", "cape_3d", "ctt", "dbz", "mdbz", wrf_vars = ["avo", "eth", "cape_2d", "cape_3d", "ctt", "dbz", "mdbz",
"geopt", "helicity", "lat", "lon", "omg", "p", "pressure", "geopt", "helicity", "lat", "lon", "omg", "p", "pressure",
"pvo", "pw", "rh2", "rh", "slp", "ter", "td2", "td", "tc", "pvo", "pw", "rh2", "rh", "slp", "ter", "td2", "td", "tc",
"theta", "tk", "tv", "twb", "updraft_helicity", "ua", "va", "theta", "tk", "tv", "twb", "updraft_helicity", "ua", "va",
"wa", "uvmet10", "uvmet", "z", "cfrac", "zstag", "geopt_stag"] "wa", "uvmet10", "uvmet", "z", "cfrac", "zstag", "geopt_stag"]
interp_methods = ["interplevel", "vertcross", "interpline", "vinterp"] interp_methods = ["interplevel", "vertcross", "interpline", "vinterp"]
latlon_tests = ["xy_out", "ll_out"] latlon_tests = ["xy_out", "ll_out"]
for nc_lib in ("netcdf4", "pynio", "scipy"): for nc_lib in ("netcdf4", "pynio", "scipy"):
if nc_lib == "netcdf4": if nc_lib == "netcdf4":
try: try:
from netCDF4 import Dataset from netCDF4 import Dataset
except ImportError: except ImportError:
print ("netcdf4-python not installed") print("netcdf4-python not installed")
continue continue
else: else:
test_in = [Dataset(x) for x in TEST_FILES] test_in = [Dataset(x) for x in TEST_FILES]
@ -130,52 +133,46 @@ if __name__ == "__main__":
try: try:
from Nio import open_file from Nio import open_file
except ImportError: except ImportError:
print ("PyNIO not installed") print("PyNIO not installed")
continue continue
else: else:
test_in = [open_file(x +".nc", "r") for x in TEST_FILES] test_in = [open_file(x + ".nc", "r") for x in TEST_FILES]
elif nc_lib == "scipy": elif nc_lib == "scipy":
try: try:
from scipy.io.netcdf import netcdf_file from scipy.io.netcdf import netcdf_file
except ImportError: except ImportError:
print ("scipy.io.netcdf not installed") print("scipy.io.netcdf not installed")
else: else:
test_in = [netcdf_file(x, mmap=False) for x in TEST_FILES] test_in = [netcdf_file(x, mmap=False) for x in TEST_FILES]
input0 = test_in[0] input0 = test_in[0]
input1 = test_in input1 = test_in
input2 = tuple(input1) input2 = tuple(input1)
input3 = wrfin_gen(test_in) input3 = wrfin_gen(test_in)
input4 = wrf_in_iter_class(test_in) input4 = wrf_in_iter_class(test_in)
input5 = {"A" : input1, input5 = {"A": input1,
"B" : input2} "B": input2}
input6 = {"A" : {"AA" : input1}, input6 = {"A": {"AA": input1},
"B" : {"BB" : input2}} "B": {"BB": input2}}
for i,input in enumerate((input0, input1, input2, for i, input in enumerate((input0, input1, input2,
input3, input4, input5, input6)): input3, input4, input5, input6)):
for var in wrf_vars: for var in wrf_vars:
if var in ignore_vars: if var in ignore_vars:
continue continue
test_func1 = make_test(var, input) test_func1 = make_test(var, input)
setattr(WRFVarsTest, "test_{0}_input{1}_{2}".format(nc_lib, setattr(WRFVarsTest, "test_{0}_input{1}_{2}".format(
i, var), nc_lib, i, var), test_func1)
test_func1)
for method in interp_methods: for method in interp_methods:
test_interp_func1 = make_interp_test(method, input) test_interp_func1 = make_interp_test(method, input)
setattr(WRFInterpTest, setattr(WRFInterpTest, "test_{0}_input{1}_{2}".format(
"test_{0}_input{1}_{2}".format(nc_lib, nc_lib, i, method), test_interp_func1)
i, method),
test_interp_func1)
for testid in latlon_tests: for testid in latlon_tests:
test_ll_func = make_latlon_test(testid, input) test_ll_func = make_latlon_test(testid, input)
test_name = "test_{0}_input{1}_{2}".format(nc_lib, i, testid) test_name = "test_{0}_input{1}_{2}".format(nc_lib, i, testid)
setattr(WRFLatLonTest, test_name, test_ll_func) setattr(WRFLatLonTest, test_name, test_ll_func)
ut.main() ut.main()

42
test/test_multi_cache.py

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

91
test/test_omp.py

@ -1,16 +1,16 @@
from __future__ import (absolute_import, division, print_function, from __future__ import (absolute_import, division, print_function,
unicode_literals) unicode_literals)
import unittest as ut import unittest as ut
import numpy.testing as nt import numpy.testing as nt
from wrf import (omp_set_num_threads, omp_get_num_threads, from wrf import (omp_set_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,
@ -20,103 +20,102 @@ from wrf import (omp_set_num_threads, omp_get_num_threads,
omp_unset_lock, omp_unset_nest_lock, omp_unset_lock, omp_unset_nest_lock,
omp_test_lock, omp_test_nest_lock, omp_test_lock, omp_test_nest_lock,
omp_get_wtime, omp_get_wtick, omp_get_wtime, omp_get_wtick,
OMP_SCHED_STATIC, OMP_SCHED_DYNAMIC, OMP_SCHED_STATIC, OMP_SCHED_DYNAMIC,
OMP_SCHED_GUIDED, OMP_SCHED_AUTO) OMP_SCHED_GUIDED, OMP_SCHED_AUTO)
class OmpTest(ut.TestCase): class OmpTest(ut.TestCase):
longMessage = True longMessage = True
def test_locks(self): def test_locks(self):
l = omp_init_lock() lk = omp_init_lock()
omp_set_lock(l) omp_set_lock(lk)
omp_unset_lock(l) omp_unset_lock(lk)
omp_test_lock(l) omp_test_lock(lk)
omp_destroy_lock(l) omp_destroy_lock(lk)
nl = omp_init_nest_lock() nl = omp_init_nest_lock()
omp_set_nest_lock(nl) omp_set_nest_lock(nl)
omp_unset_nest_lock(nl) omp_unset_nest_lock(nl)
omp_test_nest_lock(nl) omp_test_nest_lock(nl)
omp_destroy_nest_lock(nl) omp_destroy_nest_lock(nl)
def test_thread_set(self): def test_thread_set(self):
omp_set_num_threads(4) omp_set_num_threads(4)
max_threads = omp_get_max_threads() max_threads = omp_get_max_threads()
self.assertEqual(max_threads, 4) self.assertEqual(max_threads, 4)
num_threads = omp_get_num_threads() num_threads = omp_get_num_threads()
self.assertEqual(num_threads, 1) # Always 1 outside of parallel region # Always 1 outside of parallel region
self.assertEqual(num_threads, 1)
thread_num = omp_get_thread_num() thread_num = omp_get_thread_num()
self.assertEqual(thread_num, 0) # Always 0 outside of parallel region # Always 0 outside of parallel region
self.assertEqual(thread_num, 0)
num_procs = omp_get_num_procs() num_procs = omp_get_num_procs()
in_parallel = omp_in_parallel() in_parallel = omp_in_parallel()
self.assertFalse(in_parallel) # Always False outside of parallel region # Always False outside of parallel region
self.assertFalse(in_parallel)
limit = omp_get_thread_limit() limit = omp_get_thread_limit()
def test_dynamic(self): def test_dynamic(self):
omp_set_dynamic(True) omp_set_dynamic(True)
dynamic = omp_get_dynamic() dynamic = omp_get_dynamic()
self.assertTrue(dynamic) self.assertTrue(dynamic)
omp_set_dynamic(False) omp_set_dynamic(False)
dynamic = omp_get_dynamic() dynamic = omp_get_dynamic()
self.assertFalse(dynamic) self.assertFalse(dynamic)
def test_nested(self): def test_nested(self):
omp_set_nested(True) omp_set_nested(True)
nested = omp_get_nested() nested = omp_get_nested()
self.assertTrue(nested) self.assertTrue(nested)
omp_set_nested(False) omp_set_nested(False)
nested = omp_get_nested() nested = omp_get_nested()
self.assertFalse(nested) self.assertFalse(nested)
def test_schedule(self): def test_schedule(self):
omp_set_schedule(OMP_SCHED_STATIC, 100000) omp_set_schedule(OMP_SCHED_STATIC, 100000)
kind, modifier = omp_get_schedule() kind, modifier = omp_get_schedule()
self.assertEqual(kind, OMP_SCHED_STATIC) self.assertEqual(kind, OMP_SCHED_STATIC)
self.assertEqual(modifier, 100000) self.assertEqual(modifier, 100000)
omp_set_schedule(OMP_SCHED_DYNAMIC, 10000) omp_set_schedule(OMP_SCHED_DYNAMIC, 10000)
kind, modifier = omp_get_schedule() kind, modifier = omp_get_schedule()
self.assertEqual(kind, OMP_SCHED_DYNAMIC) self.assertEqual(kind, OMP_SCHED_DYNAMIC)
self.assertEqual(modifier, 10000) self.assertEqual(modifier, 10000)
omp_set_schedule(OMP_SCHED_GUIDED, 100) omp_set_schedule(OMP_SCHED_GUIDED, 100)
kind, modifier = omp_get_schedule() kind, modifier = omp_get_schedule()
self.assertEqual(kind, OMP_SCHED_GUIDED) self.assertEqual(kind, OMP_SCHED_GUIDED)
self.assertEqual(modifier, 100) self.assertEqual(modifier, 100)
omp_set_schedule(OMP_SCHED_AUTO, 10) omp_set_schedule(OMP_SCHED_AUTO, 10)
kind, modifier = omp_get_schedule() kind, modifier = omp_get_schedule()
self.assertEqual(kind, OMP_SCHED_AUTO) self.assertEqual(kind, OMP_SCHED_AUTO)
self.assertNotEqual(modifier, 10) # The modifier argument is ignored, # The modifier argument is ignored,
# so it will be set to the previous # so it will be set to the previous
# value of 100. # value of 100.
self.assertNotEqual(modifier, 10)
def test_team_level(self): def test_team_level(self):
omp_set_max_active_levels(10) omp_set_max_active_levels(10)
active_levels = omp_get_max_active_levels() active_levels = omp_get_max_active_levels()
self.assertEqual(active_levels, 10) self.assertEqual(active_levels, 10)
level = omp_get_level() level = omp_get_level()
ancestor_thread = omp_get_ancestor_thread_num(level) ancestor_thread = omp_get_ancestor_thread_num(level)
team_size = omp_get_team_size(level) team_size = omp_get_team_size(level)
active_level = omp_get_active_level() active_level = omp_get_active_level()
in_final = omp_in_final() in_final = omp_in_final()
def test_time(self): def test_time(self):
wtime = omp_get_wtime() wtime = omp_get_wtime()
wtick = omp_get_wtick() wtick = omp_get_wtick()
if __name__ == "__main__": if __name__ == "__main__":
ut.main() ut.main()

454
test/test_proj_params.py

@ -1,314 +1,302 @@
import unittest as ut import unittest as ut
import numpy.testing as nt import numpy.testing as nt
import numpy as np import numpy as np
import numpy.ma as ma import numpy.ma as ma
import os, sys import os
import sys
import subprocess import subprocess
from wrf import xy_to_ll_proj, ll_to_xy_proj, to_np from wrf import xy_to_ll_proj, ll_to_xy_proj, to_np
class WRFLatLonProjTest(ut.TestCase): class WRFLatLonProjTest(ut.TestCase):
longMessage = True longMessage = True
def make_test(xy_or_ll_out): def make_test(xy_or_ll_out):
def test(self): def test(self):
# Python 3 # Python 3
if sys.version_info > (3,): if sys.version_info > (3, ):
assert_raises_regex = self.assertRaisesRegex assert_raises_regex = self.assertRaisesRegex
xrange = range xrange = range
else: else:
assert_raises_regex = self.assertRaisesRegexp assert_raises_regex = self.assertRaisesRegexp
if xy_or_ll_out == "xy": if xy_or_ll_out == "xy":
# Test the required failures # Test the required failures
with assert_raises_regex(ValueError, ".*map_proj.*" ): with assert_raises_regex(ValueError, ".*map_proj.*"):
ll_to_xy_proj(30,-110) ll_to_xy_proj(30, -110)
with assert_raises_regex(ValueError, ".*ref_lat.*" ): with assert_raises_regex(ValueError, ".*ref_lat.*"):
ll_to_xy_proj(30,-110, map_proj=1) ll_to_xy_proj(30, -110, map_proj=1)
with assert_raises_regex(ValueError, ".*ref_lon.*" ): with assert_raises_regex(ValueError, ".*ref_lon.*"):
ll_to_xy_proj(30,-110, map_proj=1, ref_lat=45) ll_to_xy_proj(30, -110, map_proj=1, ref_lat=45)
with assert_raises_regex(ValueError, ".*known_x.*" ): with assert_raises_regex(ValueError, ".*known_x.*"):
ll_to_xy_proj(30,-110, map_proj=1, ref_lat=45.0, ll_to_xy_proj(30, -110, map_proj=1, ref_lat=45.0,
ref_lon=-120.) ref_lon=-120.)
with assert_raises_regex(ValueError, ".*known_y.*" ): with assert_raises_regex(ValueError, ".*known_y.*"):
ll_to_xy_proj(30,-110, map_proj=1, ref_lat=45.0, ll_to_xy_proj(30, -110, map_proj=1, ref_lat=45.0,
ref_lon=-120., known_x=1) ref_lon=-120., known_x=1)
with assert_raises_regex(ValueError, ".*dx.*" ): with assert_raises_regex(ValueError, ".*dx.*"):
ll_to_xy_proj(30,-110, map_proj=1, ref_lat=45.0, ll_to_xy_proj(30, -110, map_proj=1, ref_lat=45.0,
ref_lon=-120., known_x=0, known_y=0) ref_lon=-120., known_x=0, known_y=0)
####### Now test the projections # Now test the projections
# Lambert Conformal - truelat1, stand_lon required # Lambert Conformal - truelat1, stand_lon required
with assert_raises_regex(ValueError, ".*truelat1.*" ): with assert_raises_regex(ValueError, ".*truelat1.*"):
ll_to_xy_proj(30,-110, map_proj=1, ref_lat=45.0, ll_to_xy_proj(30, -110, map_proj=1, ref_lat=45.0,
ref_lon=-120., known_x=0, known_y=0, ref_lon=-120., known_x=0, known_y=0,
dx=3000.) dx=3000.)
with assert_raises_regex(ValueError, ".*stand_lon.*" ): with assert_raises_regex(ValueError, ".*stand_lon.*"):
ll_to_xy_proj(30,-110, map_proj=1, ref_lat=45.0, ll_to_xy_proj(30, -110, map_proj=1, ref_lat=45.0,
ref_lon=-120., known_x=0, known_y=0, ref_lon=-120., known_x=0, known_y=0,
dx=3000., dx=3000.,
truelat1=60.) truelat1=60.)
# Make sure it runs with all params set vs not # Make sure it runs with all params set vs not
p_all = ll_to_xy_proj(28., -89., map_proj=1, ref_lat=17.803, p_all = ll_to_xy_proj(28., -89., map_proj=1, ref_lat=17.803,
ref_lon=-100.7747, known_x=0, known_y=0, ref_lon=-100.7747, known_x=0, known_y=0,
dx=30000., truelat1=30., truelat2=30., dx=30000., truelat1=30., truelat2=30.,
stand_lon=-89., pole_lat=90., pole_lon=0.) stand_lon=-89., pole_lat=90., pole_lon=0.)
p_def = ll_to_xy_proj(28., -89., map_proj=1, ref_lat=17.803,
p_def = ll_to_xy_proj(28., -89., map_proj=1, ref_lat=17.803, ref_lon=-100.7747, known_x=0, known_y=0,
ref_lon=-100.7747, known_x=0, known_y=0, dx=30000., truelat1=30., stand_lon=-89.)
dx=30000., truelat1=30.,
stand_lon=-89.)
nt.assert_allclose(to_np(p_all), to_np(p_def)) nt.assert_allclose(to_np(p_all), to_np(p_def))
# Polar Stereographic - truelat1, stand_lon # Polar Stereographic - truelat1, stand_lon
with assert_raises_regex(ValueError, ".*truelat1.*" ): with assert_raises_regex(ValueError, ".*truelat1.*"):
ll_to_xy_proj(30,-110, map_proj=2, ref_lat=45.0, ll_to_xy_proj(30, -110, map_proj=2, ref_lat=45.0,
ref_lon=-120., known_x=0, known_y=0, ref_lon=-120., known_x=0, known_y=0,
dx=3000.) dx=3000.)
with assert_raises_regex(ValueError, ".*stand_lon.*" ): with assert_raises_regex(ValueError, ".*stand_lon.*"):
ll_to_xy_proj(30,-110, map_proj=2, ref_lat=45.0, ll_to_xy_proj(30, -110, map_proj=2, ref_lat=45.0,
ref_lon=-120., known_x=0, known_y=0, ref_lon=-120., known_x=0, known_y=0,
dx=3000., dx=3000.,
truelat1=60.) truelat1=60.)
p_all = ll_to_xy_proj(28., -89., map_proj=2, ref_lat=17.933, p_all = ll_to_xy_proj(28., -89., map_proj=2, ref_lat=17.933,
ref_lon=-100.0735, known_x=0, known_y=0, ref_lon=-100.0735, known_x=0, known_y=0,
dx=30000., truelat1=30., truelat2=30., dx=30000., truelat1=30., truelat2=30.,
stand_lon=-89., pole_lat=90., pole_lon=0.) stand_lon=-89., pole_lat=90., pole_lon=0.)
p_def = ll_to_xy_proj(28., -89., map_proj=2, ref_lat=17.933,
p_def = ll_to_xy_proj(28., -89., map_proj=2, ref_lat=17.933, ref_lon=-100.0735, known_x=0, known_y=0,
ref_lon=-100.0735, known_x=0, known_y=0, dx=30000., truelat1=30., stand_lon=-89.)
dx=30000., truelat1=30.,
stand_lon=-89.)
nt.assert_allclose(to_np(p_all), to_np(p_def)) nt.assert_allclose(to_np(p_all), to_np(p_def))
# Mercator - truelat1 # Mercator - truelat1
with assert_raises_regex(ValueError, ".*truelat1.*" ): with assert_raises_regex(ValueError, ".*truelat1.*"):
ll_to_xy_proj(30,-110, map_proj=2, ref_lat=45.0, ll_to_xy_proj(30, -110, map_proj=2, ref_lat=45.0,
ref_lon=-120., known_x=0, known_y=0, ref_lon=-120., known_x=0, known_y=0,
dx=3000.) dx=3000.)
p_all = ll_to_xy_proj(28., -89., map_proj=3, ref_lat=19.1075, p_all = ll_to_xy_proj(28., -89., map_proj=3, ref_lat=19.1075,
ref_lon=-101.008, known_x=0, known_y=0, ref_lon=-101.008, known_x=0, known_y=0,
dx=30000., truelat1=30., truelat2=30., dx=30000., truelat1=30., truelat2=30.,
stand_lon=-89., pole_lat=90., pole_lon=0.) stand_lon=-89., pole_lat=90., pole_lon=0.)
p_def = ll_to_xy_proj(28., -89., map_proj=3, ref_lat=19.1075,
p_def = ll_to_xy_proj(28., -89., map_proj=3, ref_lat=19.1075, ref_lon=-101.008, known_x=0, known_y=0,
ref_lon=-101.008, known_x=0, known_y=0, dx=30000., truelat1=30.)
dx=30000., truelat1=30.)
nt.assert_allclose(to_np(p_all), to_np(p_def)) nt.assert_allclose(to_np(p_all), to_np(p_def))
# Lat/lon - stand_lon, dy, pole_lat, pole_lon # Lat/lon - stand_lon, dy, pole_lat, pole_lon
with assert_raises_regex(ValueError, ".*stand_lon.*" ): with assert_raises_regex(ValueError, ".*stand_lon.*"):
ll_to_xy_proj(30,-110, map_proj=6, ref_lat=45.0, ll_to_xy_proj(30, -110, map_proj=6, ref_lat=45.0,
ref_lon=-120., known_x=0, known_y=0, ref_lon=-120., known_x=0, known_y=0,
dx=.2698388) dx=.2698388)
with assert_raises_regex(ValueError, ".*dy.*" ): with assert_raises_regex(ValueError, ".*dy.*"):
ll_to_xy_proj(30,-110, map_proj=6, ref_lat=45.0, ll_to_xy_proj(30, -110, map_proj=6, ref_lat=45.0,
ref_lon=-120., known_x=0, known_y=0, ref_lon=-120., known_x=0, known_y=0,
stand_lon=89.0, stand_lon=89.0,
dx=.2698388) dx=.2698388)
with assert_raises_regex(ValueError, ".*pole_lat.*" ): with assert_raises_regex(ValueError, ".*pole_lat.*"):
ll_to_xy_proj(30,-110, map_proj=6, ref_lat=45.0, ll_to_xy_proj(30, -110, map_proj=6, ref_lat=45.0,
ref_lon=-120., known_x=0, known_y=0, ref_lon=-120., known_x=0, known_y=0,
stand_lon=89.0, stand_lon=89.0,
dx=.2698388, dy=.2698388) dx=.2698388, dy=.2698388)
with assert_raises_regex(ValueError, ".*pole_lon.*" ): with assert_raises_regex(ValueError, ".*pole_lon.*"):
ll_to_xy_proj(30,-110, map_proj=6, ref_lat=45.0, ll_to_xy_proj(30, -110, map_proj=6, ref_lat=45.0,
ref_lon=-120., known_x=0, known_y=0, ref_lon=-120., known_x=0, known_y=0,
stand_lon=89.0, stand_lon=89.0,
dx=.2698388, dy=.2698388, dx=.2698388, dy=.2698388,
pole_lat=62.0) pole_lat=62.0)
p_all = ll_to_xy_proj(28.,-89., map_proj=6, ref_lat=17.6759, p_all = ll_to_xy_proj(28., -89., map_proj=6, ref_lat=17.6759,
ref_lon=-101.4286, known_x=0, known_y=0, ref_lon=-101.4286, known_x=0, known_y=0,
dx=30000, dy=30000, dx=30000, dy=30000,
truelat1=30., truelat2=30., truelat1=30., truelat2=30.,
stand_lon=-89., pole_lat=62.0, stand_lon=-89., pole_lat=62.0,
pole_lon=180.) pole_lon=180.)
p_def = ll_to_xy_proj(28., -89., map_proj=6, ref_lat=17.6759,
p_def = ll_to_xy_proj(28., -89., map_proj=6, ref_lat=17.6759, ref_lon=-101.4286, known_x=0, known_y=0,
ref_lon=-101.4286, known_x=0, known_y=0, stand_lon=-89.,
stand_lon=-89., dx=30000, dy=30000, pole_lat=62.0,
dx=30000, dy=30000, pole_lat=62.0, pole_lon=180.)
pole_lon=180.)
nt.assert_allclose(to_np(p_all), to_np(p_def)) nt.assert_allclose(to_np(p_all), to_np(p_def))
if xy_or_ll_out == "ll": if xy_or_ll_out == "ll":
# Test the required failures # Test the required failures
with assert_raises_regex(ValueError, ".*map_proj.*" ): with assert_raises_regex(ValueError, ".*map_proj.*"):
xy_to_ll_proj(45, 50) xy_to_ll_proj(45, 50)
with assert_raises_regex(ValueError, ".*ref_lat.*" ): with assert_raises_regex(ValueError, ".*ref_lat.*"):
xy_to_ll_proj(45, 50, map_proj=1) xy_to_ll_proj(45, 50, map_proj=1)
with assert_raises_regex(ValueError, ".*ref_lon.*" ): with assert_raises_regex(ValueError, ".*ref_lon.*"):
xy_to_ll_proj(45, 50, map_proj=1, ref_lat=45) xy_to_ll_proj(45, 50, map_proj=1, ref_lat=45)
with assert_raises_regex(ValueError, ".*known_x.*" ): with assert_raises_regex(ValueError, ".*known_x.*"):
xy_to_ll_proj(45, 50, map_proj=1, ref_lat=45.0, xy_to_ll_proj(45, 50, map_proj=1, ref_lat=45.0,
ref_lon=-120.) ref_lon=-120.)
with assert_raises_regex(ValueError, ".*known_y.*" ): with assert_raises_regex(ValueError, ".*known_y.*"):
xy_to_ll_proj(45, 50, map_proj=1, ref_lat=45.0, xy_to_ll_proj(45, 50, map_proj=1, ref_lat=45.0,
ref_lon=-120., known_x=1) ref_lon=-120., known_x=1)
with assert_raises_regex(ValueError, ".*dx.*" ): with assert_raises_regex(ValueError, ".*dx.*"):
xy_to_ll_proj(45, 50, map_proj=1, ref_lat=45.0, xy_to_ll_proj(45, 50, map_proj=1, ref_lat=45.0,
ref_lon=-120., known_x=0, known_y=0) ref_lon=-120., known_x=0, known_y=0)
####### Now test the projections # Now test the projections
# Lambert Conformal - truelat1, stand_lon required # Lambert Conformal - truelat1, stand_lon required
with assert_raises_regex(ValueError, ".*truelat1.*" ): with assert_raises_regex(ValueError, ".*truelat1.*"):
xy_to_ll_proj(45, 50, map_proj=1, ref_lat=45.0, xy_to_ll_proj(45, 50, map_proj=1, ref_lat=45.0,
ref_lon=-120., known_x=0, known_y=0, ref_lon=-120., known_x=0, known_y=0,
dx=3000.) dx=3000.)
with assert_raises_regex(ValueError, ".*stand_lon.*" ): with assert_raises_regex(ValueError, ".*stand_lon.*"):
xy_to_ll_proj(45, 50, map_proj=1, ref_lat=45.0, xy_to_ll_proj(45, 50, map_proj=1, ref_lat=45.0,
ref_lon=-120., known_x=0, known_y=0, ref_lon=-120., known_x=0, known_y=0,
dx=3000., dx=3000.,
truelat1=60.) truelat1=60.)
# Make sure it runs with all params set vs not # Make sure it runs with all params set vs not
p_all = xy_to_ll_proj(45, 50, map_proj=1, ref_lat=17.803, p_all = xy_to_ll_proj(45, 50, map_proj=1, ref_lat=17.803,
ref_lon=-100.7747, known_x=0, known_y=0, ref_lon=-100.7747, known_x=0, known_y=0,
dx=30000., truelat1=30., truelat2=30., dx=30000., truelat1=30., truelat2=30.,
stand_lon=-89., pole_lat=90., pole_lon=0.) stand_lon=-89., pole_lat=90., pole_lon=0.)
p_def = xy_to_ll_proj(45, 50, map_proj=1, ref_lat=17.803,
p_def = xy_to_ll_proj(45, 50, map_proj=1, ref_lat=17.803, ref_lon=-100.7747, known_x=0, known_y=0,
ref_lon=-100.7747, known_x=0, known_y=0, dx=30000., truelat1=30.,
dx=30000., truelat1=30., stand_lon=-89.)
stand_lon=-89.)
nt.assert_allclose(to_np(p_all), to_np(p_def)) nt.assert_allclose(to_np(p_all), to_np(p_def))
# Polar Stereographic - truelat1, stand_lon # Polar Stereographic - truelat1, stand_lon
with assert_raises_regex(ValueError, ".*truelat1.*" ): with assert_raises_regex(ValueError, ".*truelat1.*"):
xy_to_ll_proj(45, 50, map_proj=2, ref_lat=45.0, xy_to_ll_proj(45, 50, map_proj=2, ref_lat=45.0,
ref_lon=-120., known_x=0, known_y=0, ref_lon=-120., known_x=0, known_y=0,
dx=3000.) dx=3000.)
with assert_raises_regex(ValueError, ".*stand_lon.*" ): with assert_raises_regex(ValueError, ".*stand_lon.*"):
xy_to_ll_proj(45, 50, map_proj=2, ref_lat=45.0, xy_to_ll_proj(45, 50, map_proj=2, ref_lat=45.0,
ref_lon=-120., known_x=0, known_y=0, ref_lon=-120., known_x=0, known_y=0,
dx=3000., dx=3000.,
truelat1=60.) truelat1=60.)
p_all = xy_to_ll_proj(45, 50, map_proj=2, ref_lat=17.933, p_all = xy_to_ll_proj(45, 50, map_proj=2, ref_lat=17.933,
ref_lon=-100.0735, known_x=0, known_y=0, ref_lon=-100.0735, known_x=0, known_y=0,
dx=30000., truelat1=30., truelat2=30., dx=30000., truelat1=30., truelat2=30.,
stand_lon=-89., pole_lat=90., pole_lon=0.) stand_lon=-89., pole_lat=90., pole_lon=0.)
p_def = xy_to_ll_proj(45, 50, map_proj=2, ref_lat=17.933,
p_def = xy_to_ll_proj(45, 50, map_proj=2, ref_lat=17.933, ref_lon=-100.0735, known_x=0, known_y=0,
ref_lon=-100.0735, known_x=0, known_y=0, dx=30000., truelat1=30.,
dx=30000., truelat1=30., stand_lon=-89.)
stand_lon=-89.)
nt.assert_allclose(to_np(p_all), to_np(p_def)) nt.assert_allclose(to_np(p_all), to_np(p_def))
# Mercator - truelat1 # Mercator - truelat1
with assert_raises_regex(ValueError, ".*truelat1.*" ): with assert_raises_regex(ValueError, ".*truelat1.*"):
xy_to_ll_proj(45, 50, map_proj=2, ref_lat=45.0, xy_to_ll_proj(45, 50, map_proj=2, ref_lat=45.0,
ref_lon=-120., known_x=0, known_y=0, ref_lon=-120., known_x=0, known_y=0,
dx=3000.) dx=3000.)
p_all = xy_to_ll_proj(45, 50, map_proj=3, ref_lat=19.1075, p_all = xy_to_ll_proj(45, 50, map_proj=3, ref_lat=19.1075,
ref_lon=-101.008, known_x=0, known_y=0, ref_lon=-101.008, known_x=0, known_y=0,
dx=30000., truelat1=30., truelat2=30., dx=30000., truelat1=30., truelat2=30.,
stand_lon=-89., pole_lat=90., pole_lon=0.) stand_lon=-89., pole_lat=90., pole_lon=0.)
p_def = xy_to_ll_proj(45, 50, map_proj=3, ref_lat=19.1075,
p_def = xy_to_ll_proj(45, 50, map_proj=3, ref_lat=19.1075, ref_lon=-101.008, known_x=0, known_y=0,
ref_lon=-101.008, known_x=0, known_y=0, dx=30000., truelat1=30.)
dx=30000., truelat1=30.)
nt.assert_allclose(to_np(p_all), to_np(p_def)) nt.assert_allclose(to_np(p_all), to_np(p_def))
# Lat/lon - stand_lon, dy, pole_lat, pole_lon # Lat/lon - stand_lon, dy, pole_lat, pole_lon
with assert_raises_regex(ValueError, ".*stand_lon.*" ): with assert_raises_regex(ValueError, ".*stand_lon.*"):
xy_to_ll_proj(45, 50, map_proj=6, ref_lat=45.0, xy_to_ll_proj(45, 50, map_proj=6, ref_lat=45.0,
ref_lon=-120., known_x=0, known_y=0, ref_lon=-120., known_x=0, known_y=0,
dx=.2698388) dx=.2698388)
with assert_raises_regex(ValueError, ".*dy.*" ): with assert_raises_regex(ValueError, ".*dy.*"):
xy_to_ll_proj(45, 50, map_proj=6, ref_lat=45.0, xy_to_ll_proj(45, 50, map_proj=6, ref_lat=45.0,
ref_lon=-120., known_x=0, known_y=0, ref_lon=-120., known_x=0, known_y=0,
stand_lon=89.0, stand_lon=89.0,
dx=.2698388) dx=.2698388)
with assert_raises_regex(ValueError, ".*pole_lat.*" ): with assert_raises_regex(ValueError, ".*pole_lat.*"):
xy_to_ll_proj(45, 50, map_proj=6, ref_lat=45.0, xy_to_ll_proj(45, 50, map_proj=6, ref_lat=45.0,
ref_lon=-120., known_x=0, known_y=0, ref_lon=-120., known_x=0, known_y=0,
stand_lon=89.0, stand_lon=89.0,
dx=.2698388, dy=.2698388) dx=.2698388, dy=.2698388)
with assert_raises_regex(ValueError, ".*pole_lon.*" ): with assert_raises_regex(ValueError, ".*pole_lon.*"):
xy_to_ll_proj(45, 50, map_proj=6, ref_lat=45.0, xy_to_ll_proj(45, 50, map_proj=6, ref_lat=45.0,
ref_lon=-120., known_x=0, known_y=0, ref_lon=-120., known_x=0, known_y=0,
stand_lon=89.0, stand_lon=89.0,
dx=.2698388, dy=.2698388, dx=.2698388, dy=.2698388,
pole_lat=62.0) pole_lat=62.0)
p_all = xy_to_ll_proj(64, 40, map_proj=6, ref_lat=17.6759, p_all = xy_to_ll_proj(64, 40, map_proj=6, ref_lat=17.6759,
ref_lon=-101.4286, known_x=0, known_y=0, ref_lon=-101.4286, known_x=0, known_y=0,
dx=30000, dy=30000, dx=30000, dy=30000,
truelat1=30., truelat2=30., truelat1=30., truelat2=30.,
stand_lon=-89., pole_lat=62.0, stand_lon=-89., pole_lat=62.0,
pole_lon=180.) pole_lon=180.)
p_def = xy_to_ll_proj(64, 40, map_proj=6, ref_lat=17.6759,
p_def = xy_to_ll_proj(64, 40, map_proj=6, ref_lat=17.6759, ref_lon=-101.4286, known_x=0, known_y=0,
ref_lon=-101.4286, known_x=0, known_y=0, stand_lon=-89.,
stand_lon=-89., dx=30000, dy=30000, pole_lat=62.0,
dx=30000, dy=30000, pole_lat=62.0, pole_lon=180.)
pole_lon=180.)
nt.assert_allclose(to_np(p_all), to_np(p_def)) nt.assert_allclose(to_np(p_all), to_np(p_def))
return test return test
if __name__ == "__main__": if __name__ == "__main__":
for v in ("xy", "ll"): for v in ("xy", "ll"):
test_func = make_test(v) test_func = make_test(v)
setattr(WRFLatLonProjTest, 'test_{0}'.format(v), test_func) setattr(WRFLatLonProjTest, 'test_{0}'.format(v), test_func)
ut.main() ut.main()

33
test/test_units.py

@ -1,7 +1,7 @@
import sys import sys
import unittest as ut import unittest as ut
import numpy.testing as nt import numpy.testing as nt
import numpy as np import numpy as np
import numpy.ma as ma import numpy.ma as ma
from netCDF4 import Dataset as nc from netCDF4 import Dataset as nc
@ -13,44 +13,43 @@ TEST_FILE = "/Users/ladwig/Documents/wrf_files/wrfout_d01_2010-06-13_21:00:00"
# Python 3 # Python 3
if sys.version_info > (3,): if sys.version_info > (3,):
xrange = range xrange = range
class TestUnits(ut.TestCase): class TestUnits(ut.TestCase):
longMessage = True longMessage = True
def test_temp_units(self): def test_temp_units(self):
wrfnc = nc(TEST_FILE) wrfnc = nc(TEST_FILE)
for units in ("K", "degC", "degF"): for units in ("K", "degC", "degF"):
var = getvar(wrfnc, "temp", units=units) var = getvar(wrfnc, "temp", units=units)
self.assertEqual(var.attrs["units"], units) self.assertEqual(var.attrs["units"], units)
def test_wind_units(self): def test_wind_units(self):
wrfnc = nc(TEST_FILE) wrfnc = nc(TEST_FILE)
for units in ("m s-1", "kt", "mi h-1", "km h-1", "ft s-1"): for units in ("m s-1", "kt", "mi h-1", "km h-1", "ft s-1"):
var = getvar(wrfnc, "uvmet", units=units) var = getvar(wrfnc, "uvmet", units=units)
self.assertEqual(var.attrs["units"], units) self.assertEqual(var.attrs["units"], units)
def test_pres_units(self): def test_pres_units(self):
wrfnc = nc(TEST_FILE) wrfnc = nc(TEST_FILE)
for units in ("Pa", "hPa", "mb", "torr", "mmHg", "atm"): for units in ("Pa", "hPa", "mb", "torr", "mmHg", "atm"):
var = getvar(wrfnc, "slp", units=units) var = getvar(wrfnc, "slp", units=units)
self.assertEqual(var.attrs["units"], units) self.assertEqual(var.attrs["units"], units)
def test_height_units(self): def test_height_units(self):
wrfnc = nc(TEST_FILE) wrfnc = nc(TEST_FILE)
for units in ("m", "km", "dam", "ft", "mi"): for units in ("m", "km", "dam", "ft", "mi"):
var = getvar(wrfnc, "z", units=units) var = getvar(wrfnc, "z", units=units)
self.assertEqual(var.attrs["units"], units) self.assertEqual(var.attrs["units"], units)
if __name__ == "__main__": if __name__ == "__main__":
ut.main() ut.main()

1200
test/utests.py

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save