diff --git a/doc/source/_templates/product_table.txt b/doc/source/_templates/product_table.txt index 9f0748c..6a25df5 100644 --- a/doc/source/_templates/product_table.txt +++ b/doc/source/_templates/product_table.txt @@ -183,7 +183,7 @@ | | | | | | | | ft s-1 | | +--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| wspd_wdir | Wind Speed and Direction | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | +| wspd_wdir | Wind Speed and Direction (wind_from_direction) | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | | | | | | | | in Grid Coordinates | km h-1 | | | | | | | @@ -193,7 +193,7 @@ | | | | | | | | ft s-1 | | +--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| wspd_wdir10 | 10m Wind Speed and Direction | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | +| wspd_wdir10 | 10m Wind Speed and Direction (wind_from_direction) | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | | | | | | | | in Grid Coordinates | km h-1 | | | | | | | @@ -203,7 +203,7 @@ | | | | | | | | ft s-1 | | +--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| uvmet_wspd_wdir | Wind Speed and Direction | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | +| uvmet_wspd_wdir | Wind Speed and Direction (wind_from_direction) | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | | | | | | | | Rotated to Earth Coordinates | km h-1 | | | | | | | @@ -213,7 +213,7 @@ | | | | | | | | ft s-1 | | +--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| uvmet10_wspd_wdir | 10m Wind Speed and Direction | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | +| uvmet10_wspd_wdir | 10m Wind Speed and Direction (wind_from_direction) | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | | | | | | | | Rotated to Earth Coordinates | km h-1 | | | | | | | @@ -223,7 +223,7 @@ | | | | | | | | ft s-1 | | +--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| z/height | Full Model Height | m | **msl** (boolean): Set to False to return AGL values. True is for MSL. Default is *True*. | +| z/height | Full Model Height on Mass Levels | 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'*. | | | | | | diff --git a/doc/source/index.rst b/doc/source/index.rst index f9c4168..c0ae7c1 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -46,6 +46,7 @@ Documentation ./faq ./support ./license + ./workshop Indices and tables diff --git a/doc/source/new.rst b/doc/source/new.rst index 905a3c3..b71844c 100644 --- a/doc/source/new.rst +++ b/doc/source/new.rst @@ -4,6 +4,14 @@ What's New Releases ------------- +v1.0.2 +^^^^^^^^^^^^^^ + +- Release 1.0.2 +- Fixed issue with the wspd_wdir product types when sequences of files are + used. + + v1.0.1 ^^^^^^^^^^^^^ diff --git a/doc/source/workshop.rst b/doc/source/workshop.rst new file mode 100644 index 0000000..97af0fc --- /dev/null +++ b/doc/source/workshop.rst @@ -0,0 +1,302 @@ +WRF Workshop 2017 +===================== + +Welcome wrf-python tutorial attendees! + +The instructions below should be completed prior to arriving at the tutorial. +There will not be enough time to do this during the tutorial. + +Prerequisites +--------------- + +This tutorial assumes that you have basic knowledge of how to type commands +in to a command terminal using your preferred operating system. The student +should know some basic directory commands like *cd*, *mkdir*, *cp*, *mv*. + +Regarding Python, to understand the examples in this tutorial, the student +should have some experience with Python basics. This includes: + +- Opening a Python interpreter and entering commands. +- Importing packages via the import statement. +- Using the basic Python types: str, list, tuple, dict, boolean, numeric, None. +- Creating a list, tuple, or dict with "[ ]", "( )", "{ }" syntax. +- Accessing dict/list/tuple items with the "x[ ]" syntax. +- Slicing str/list/tuple with the ":" syntax. +- Using object methods and attributes with the "x.y" syntax. +- Familiarity with numpy would be helpful, as only a very brief introduction + is provided. +- Familiarity with matplotlib would be helpful, as only a very brief + introduction is provided. + +If a student is completely new to Python, that shouldn't be a problem, since +most of the examples consist of basic container types and function calls. It +would be helpful to look at some introductory material before arriving at the +tutorial. If you've programmed before, picking up Python is fairly easy. + +Here are some links: + +https://www.learnpython.org/ + +https://developers.google.com/edu/python/ + + +Step 1: Open a Command Terminal +-------------------------------- + +To begin, you will first need to know how to open a command line terminal for +your operating system. + +For Windows: + +.. code-block:: none + + WINDOWS + r + type cmd in the run window + +For Mac: + +.. code-block:: none + + Finder -> Applications -> Utilities -> Terminal + +For Linux: + +.. code-block:: none + + Try one of the following: + + CTRL + ALT + T + CTRL + ALT + F2 + + +Step 2: Download Miniconda +---------------------------- + +For this tutorial, you will need to download and install Miniconda. We are +going to use Python 2.7, but it should also work with Python 3.5+. However, +due to limitations with open source compilers on conda-forge, only Python 2.7 +is available for Windows. + +**What is Miniconda?** + +If you have used the Anaconda distribution for Python before, then you will be +familiar with Miniconda. The Anaconda Python distribution includes numerous +scientific packages out of box, which can be difficult for users to build and +install. More importantly, Anaconda includes the conda package manager. + +The conda package manager is a utility (similar to yum or apt-get) that installs +packages from a repository of pre-compiled Python packages. These repositories +are called channels. Conda makes it easy for Python users to install and +uninstall packages, and also can be used to create isolated Python environments +(more on that later). + +Miniconda is a bare bones implementation of Anaconda and only includes the +conda package manager. Since we are going to use the conda-forge channel to +install our scientific packages, Miniconda avoids any complications between +packages provided by Anaconda and conda-forge. + +Please use the appropriate link below to download Miniconda for your operating +system. + +.. note:: + + 64-bit OS recommended + +`Win64 `_ + +`Mac `_ + +`Linux `_ + +For more information, see: https://conda.io/miniconda.html + + +Step 3: Install Miniconda +---------------------------- + +Windows: + + 1. Browse to the directory where you downloaded Miniconda2-latest-Windows-x86_64.exe. + 2. Double click on Miniconda2-latest-Windows-x86_64.exe. + 3. Follow the instructions. + 4. Restart your command terminal. + +Mac and Linux: + + For Mac and Linux, the installer is a bash script. + + 1. Using a terminal, you need to execute the shell script that you downloaded by + doing:: + + bash /path/to/Miniconda2-latest-MacOSX-x86_64.sh [Mac] + + bash /path/to/Miniconda2-latest-Linux-x86_64.sh [Linux] + + 2. Follow the instructions. + + 3. At the end of the installation, it will ask if you want to add the + miniconda2 path to your bash environment. If you are unsure what to do, + you should say "yes". If you say "no", we're going to assume you know + what you are doing. + + If you said "yes", then once you restart your shell, the miniconda2 Python + will be found instead of the system Python when you type the "python" + command. If you want to undo this later, then you can edit + either ~/.bash_profile or ~/.bashrc (depending on OS used) and + comment out the line that looks similar to:: + + # added by Miniconda2 4.1.11 installer + export PATH="/path/to/miniconda2/bin:$PATH" + + 4. Restart your command terminal. + +.. note:: + + If you have already installed another Python distribution, like Enthought + Canopy, you will need to comment out any PATH entries for that distribution + in your .bashrc or .bash_profile. Otherwise, your shell environment may + pick to wrong Python installation. You can use the command "which python" + to verify the Python installation being used. + + + +Step 4: Set Up the Conda Environment +-------------------------------------- + +If you are new to the conda package manager, one of the nice features of conda +is that you can create isolated Python environments that prevent package +incompatibilities. This is similar to the virtualenv package that some +Python users may be familiar with. However, conda is not compatible with +virtualenv, so only use conda environments when working with conda. + +The name of our conda environment for this tutorial is: **tutorial_2017**. + +Follow the instructions below to create the tutorial_2017 environment. + + 1. Open a command terminal if you haven't done so. + + 2. [Linux and Mac Users Only] The conda package manager only works with bash, + so if bash is not your current shell, type:: + + bash + + 3. Add the conda-forge channel to your conda package manager. + + Type or copy this command in to your command terminal:: + + conda config --add channels conda-forge + + .. note:: + + Conda-forge is a community driven collection of packages that are + continually tested to ensure compatibility. We highly recommend using + conda-forge when working with conda. See https://conda-forge.github.io/ + for more details on this excellent project. + + 4. Create the conda environment for the tutorial. + + Type or copy this command in to your command terminal:: + + conda create -n tutorial_2017 python=2.7 matplotlib=1.5.3 cartopy netcdf4 jupyter git wrf-python + + Type "y" when prompted. It will take several minutes to install everything. + + This command creates an isolated Python environment named *tutorial_2017*, and installs + the python interpreter, matplotlib, cartopy, netcdf4, jupyter, git, and wrf-python + packages. + + .. note:: + + In this tutorial, we need to use matplotlib v1.5.3 due to some issues with cartopy, which + should be fixed in a later version of cartopy. Be sure to supply the version number as + indicated in the command above. + + 5. Activate the conda environment. + + To activate the tutorial_2017 Python environment, type the following + in to the command terminal:: + + source activate tutorial_2017. + + You should see (tutorial_2017) on your command prompt. + + To deactivate your conda environment, type the following in to the + command terminal:: + + source deactivate + + +Step 5: Download the Student Workbook +--------------------------------------- + +The student workbook for the tutorial is available on GitHub. The tutorial_2017 +conda environment includes the git application needed to download the repository. + +These instructions download the tutorial in to your home directory. If you want +to place the tutorial in to another directory, we're going to assume you know +how to do this yourself. + +To download the student workbook, follow these instructions: + + 1. Activate the tutorial_2017 conda environment following the instructions + in the previous step (*source activate tutorial_2017*). + + 2. Change your working directory to the home directory by typing the + following command in to the command terminal: + + For Linux and Mac:: + + cd ~ + + For Windows:: + + cd %HOMEPATH% + + 3. Download the git repository for the tutorial by typing the following + in to the command terminal:: + + git checkout https://github.com/NCAR/wrf_python_tutorial + + 4. There may be additional changes to the tutorial after you have downloaded + it. To pull down the latest changes, type the following in to the + command terminal: + + For Linux and Mac:: + + source activate tutorial_2017 + + cd ~/wrf_python_tutorial + + git pull + + For Windows:: + + source activate tutorial_2017 + + cd %HOMEPATH%\wrf_python_tutorial + + git pull + + +Step 6: Obtain WRF Output Files +---------------------------------- + +For this tutorial, we strongly recommend that you use your own WRF output files. +The tutorial includes an easy way to point to your own data files. The WRF +output files should all be from the same WRF run and use the same domain. +If your files are located on another system (e.g. yellowstone), then copy 2 or +3 of these files to your local computer prior to the tutorial. + +If you do not have any of your own WRF output files, then you can download the +instructor data files from a link that should have been provided to you in an +email prior to the tutorial. + + +Getting Help +---------------- + +If you experience problems during this installation, please send a question +to the :ref:`google-group` support mailing list. + + +We look forward to seeing you at the tutorial! diff --git a/fortran/wrf_wind.f90 b/fortran/wrf_wind.f90 new file mode 100644 index 0000000..b08c019 --- /dev/null +++ b/fortran/wrf_wind.f90 @@ -0,0 +1,48 @@ +! NCLFORTSTART +SUBROUTINE DCOMPUTEWSPD(wspd, u, v, nx, ny) + + IMPLICIT NONE + + !f2py threadsafe + !f2py intent(in,out) :: wspd + + INTEGER, INTENT(IN) :: nx, ny + REAL(KIND=8), DIMENSION(nx,ny), INTENT(OUT) :: wspd + REAL(KIND=8), DIMENSION(nx,ny), INTENT(IN) :: u, v +! NCLEND + + INTEGER i, j + + DO j = 1,ny + DO i = 1,nx + wspd(i,j) = SQRT(u(i,j)*u(i,j) + v(i,j)*v(i,j)) + END DO + END DO + +END SUBROUTINE DCOMPUTEWSPD + + +! NCLFORTSTART +SUBROUTINE DCOMPUTEWDIR(wdir, u, v, nx, ny) + USE wrf_constants, ONLY : DEG_PER_RAD + + IMPLICIT NONE + + !f2py threadsafe + !f2py intent(in,out) :: wdir + + INTEGER, INTENT(IN) :: nx, ny + REAL(KIND=8), DIMENSION(nx,ny), INTENT(OUT) :: wdir + REAL(KIND=8), DIMENSION(nx,ny), INTENT(IN) :: u, v +! NCLEND + + INTEGER i, j + + DO j = 1,ny + DO i = 1,nx + wdir(i,j) = MOD(270.0 - ATAN2(v(i,j), u(i,j)) * DEG_PER_RAD, 360.) + END DO + END DO + +END SUBROUTINE DCOMPUTEWDIR + diff --git a/fortran/wrffortran.pyf b/fortran/wrffortran.pyf index 1c652a5..bf99a1e 100644 --- a/fortran/wrffortran.pyf +++ b/fortran/wrffortran.pyf @@ -556,6 +556,23 @@ python module _wrffortran ! in integer intent(inout) :: errstat character*(*) intent(inout) :: errmsg end subroutine wrf_vintrp + subroutine dcomputewspd(wspd,u,v,nx,ny) ! in :_wrffortran:wrf_wind.f90 + threadsafe + real(kind=8) dimension(nx,ny),intent(out,in) :: wspd + real(kind=8) dimension(nx,ny),intent(in),depend(nx,ny) :: u + real(kind=8) dimension(nx,ny),intent(in),depend(nx,ny) :: v + integer, optional,intent(in),check(shape(wspd,0)==nx),depend(wspd) :: nx=shape(wspd,0) + integer, optional,intent(in),check(shape(wspd,1)==ny),depend(wspd) :: ny=shape(wspd,1) + end subroutine dcomputewspd + subroutine dcomputewdir(wdir,u,v,nx,ny) ! in :_wrffortran:wrf_wind.f90 + threadsafe + use wrf_constants, only: deg_per_rad + real(kind=8) dimension(nx,ny),intent(out,in) :: wdir + real(kind=8) dimension(nx,ny),intent(in),depend(nx,ny) :: u + real(kind=8) dimension(nx,ny),intent(in),depend(nx,ny) :: v + integer, optional,intent(in),check(shape(wdir,0)==nx),depend(wdir) :: nx=shape(wdir,0) + integer, optional,intent(in),check(shape(wdir,1)==ny),depend(wdir) :: ny=shape(wdir,1) + end subroutine dcomputewdir end interface end python module _wrffortran diff --git a/setup.py b/setup.py index 41d0b6e..8f42dad 100755 --- a/setup.py +++ b/setup.py @@ -21,6 +21,7 @@ ext1 = numpy.distutils.core.Extension( "fortran/wrf_rip_phys_routines.f90", "fortran/wrf_pw.f90", "fortran/wrf_vinterp.f90", + "fortran/wrf_wind.f90", "fortran/wrffortran.pyf"] ) diff --git a/src/wrf/extension.py b/src/wrf/extension.py index 2b86650..5f3fcea 100755 --- a/src/wrf/extension.py +++ b/src/wrf/extension.py @@ -11,7 +11,8 @@ from ._wrffortran import (dcomputetk, dinterp3dz, dinterp2dxy, dinterp1d, calcdbz, dcalrelhl, dcalcuh, dcomputepv, dcomputeabsvort, dlltoij, dijtoll, deqthecalc, omgcalc, virtual_temp, wetbulbcalc, dcomputepw, - wrf_monotonic, wrf_vintrp) + wrf_monotonic, wrf_vintrp, dcomputewspd, + dcomputewdir) from .decorators import (left_iteration, cast_type, extract_and_transpose, check_args) @@ -858,5 +859,44 @@ def _vintrp(field, pres, tk, qvp, ght, terrain, sfp, smsfp, raise DiagnosticError("".join(npbytes_to_str(errmsg)).strip()) return result + +@check_args(0, 2, (2,2)) +@left_iteration(2, 2, ref_var_idx=0) +@cast_type(arg_idxs=(0,1)) +@extract_and_transpose() +def _wspd(u, v, outview=None): + """Wrapper for dcomputewspd. + + Located in wrf_wind.f90. + + """ + if outview is None: + outview = np.empty_like(u) + + result = dcomputewspd(outview, + u, + v) + + return result + + +@check_args(0, 2, (2,2)) +@left_iteration(2, 2, ref_var_idx=0) +@cast_type(arg_idxs=(0,1)) +@extract_and_transpose() +def _wdir(u, v, outview=None): + """Wrapper for dcomputewdir. + + Located in wrf_wind.f90. + + """ + if outview is None: + outview = np.empty_like(u) + + result = dcomputewdir(outview, + u, + v) + + return result diff --git a/src/wrf/specialdec.py b/src/wrf/specialdec.py index 8bbdb41..ecf7a49 100644 --- a/src/wrf/specialdec.py +++ b/src/wrf/specialdec.py @@ -151,6 +151,20 @@ def uvmet_left_iter(alg_dtype=np.float64): new_lon = lon[lat_left_and_slice] outview = outview_array[left_and_slice_idxs] + # Skip the possible empty/missing arrays for the join method + skip_missing = False + for arg in (new_u, new_v, new_lat, new_lon): + if isinstance(arg, np.ma.MaskedArray): + if arg.mask.all(): + output[u_output_idxs] = uvmetmissing + output[v_output_idxs] = uvmetmissing + + skip_missing = True + has_missing = True + + if skip_missing: + continue + # Call the numerical routine result = wrapped(new_u, new_v, new_lat, new_lon, cen_long, cone, isstag=is_stag, has_missing=has_missing, @@ -324,6 +338,25 @@ def cape_left_iter(alg_dtype=np.float64): capeview = outview_array[cape_idxs] cinview = outview_array[cin_idxs] + # Skip the possible empty/missing arrays for the join method + # Note: Masking handled by cape.py or computation.py, so only + # supply the fill values here. + skip_missing = False + for arg in (new_args[0:6]): + if isinstance(arg, np.ma.MaskedArray): + if arg.mask.all(): + if flip and not is2d: + output[cape_output_idxs] = missing + output[cin_output_idxs] = missing + else: + output[cape_output_idxs] = missing + output[cin_output_idxs] = missing + + skip_missing = True + + if skip_missing: + continue + # Call the numerical routine new_kwargs["capeview"] = capeview new_kwargs["cinview"] = cinview @@ -420,6 +453,8 @@ def cloudfrac_left_iter(alg_dtype=np.float64): output_dims += p.shape[-2:] output = np.empty(output_dims, orig_dtype) + has_missing = False + missing = Constants.DEFAULT_FILL for left_idxs in iter_left_indexes(extra_dims): left_and_slice_idxs = left_idxs + (slice(None),) low_idxs = left_idxs + (0, slice(None)) @@ -433,6 +468,23 @@ def cloudfrac_left_iter(alg_dtype=np.float64): new_args[0] = p[left_and_slice_idxs] new_args[1] = rh[left_and_slice_idxs] + # Skip the possible empty/missing arrays for the join method + # Note: Masking handled by cape.py or computation.py, so only + # supply the fill values here. + skip_missing = False + for arg in (new_args[0:2]): + if isinstance(arg, np.ma.MaskedArray): + if arg.mask.all(): + output[low_output_idxs] = missing + output[med_output_idxs] = missing + output[high_output_idxs] = missing + + skip_missing = True + has_missing = True + + if skip_missing: + continue + lowview = outview_array[low_idxs] medview = outview_array[med_idxs] highview = outview_array[high_idxs] @@ -456,6 +508,9 @@ def cloudfrac_left_iter(alg_dtype=np.float64): output[high_output_idxs] = ( outview_array[high_idxs].astype(orig_dtype)) + if has_missing: + output = np.ma.masked_values(output, missing) + return output return func_wrapper diff --git a/src/wrf/uvmet.py b/src/wrf/uvmet.py index e30a51c..6dba5e6 100755 --- a/src/wrf/uvmet.py +++ b/src/wrf/uvmet.py @@ -134,9 +134,16 @@ def _get_uvmet(wrfin, timeidx=0, method="cat", squeeze=True, # For 3D array, this makes (0,...,:,:,:) and (1,...,:,:,:) idx0 = (0,) + (Ellipsis,) + (slice(None),)*(-end_idx) idx1 = (1,) + (Ellipsis,) + (slice(None),)*(-end_idx) - - result[idx0] = u[:] - result[idx1] = v[:] + + try: + fill = u.fill_value + except AttributeError: + result[idx0] = u[:] + result[idx1] = v[:] + else: + result[idx0] = np.ma.filled(u[:], fill) + result[idx1] = np.ma.filled(v[:], fill) + result = np.ma.masked_values(result, fill) return result elif map_proj in (1,2): diff --git a/src/wrf/version.py b/src/wrf/version.py index f24c16c..73a2e28 100644 --- a/src/wrf/version.py +++ b/src/wrf/version.py @@ -1,2 +1,2 @@ -__version__ = "1.0.1" +__version__ = "1.0.2" diff --git a/src/wrf/wind.py b/src/wrf/wind.py index 9976f60..b04246d 100755 --- a/src/wrf/wind.py +++ b/src/wrf/wind.py @@ -3,7 +3,7 @@ from __future__ import (absolute_import, division, print_function, import numpy as np -from .constants import Constants +from .extension import _wspd, _wdir from .destag import destagger from .util import extract_vars, either from .decorators import convert_units @@ -29,7 +29,7 @@ def _calc_wspd(u, v, units="m s-1"): :class:`numpy.ndarray`: The wind speed. """ - return np.sqrt(u**2 + v**2) + return _wspd(u, v) def _calc_wdir(u, v): @@ -46,8 +46,7 @@ def _calc_wdir(u, v): :class:`numpy.ndarray`: The wind direction. """ - wdir = 270.0 - np.arctan2(v,u) * (180.0/Constants.PI) - return np.remainder(wdir, 360.0) + return _wdir(u, v) def _calc_wspd_wdir(u, v, two_d, units): @@ -79,26 +78,35 @@ def _calc_wspd_wdir(u, v, two_d, units): leftmost dimension is 2 (0=WSPD, 1=WDIR). """ + wspd = _calc_wspd(u, v, units) wdir = _calc_wdir(u, v) + + try: + fill = wspd.fill_value + except AttributeError: + fill = None idx_end = -2 if two_d else -3 - outdims = list(wspd.shape[0:idx_end]) + [2] + list(wspd.shape[idx_end:]) - + outdims = [2] + list(wspd.shape[0:idx_end]) + list(wspd.shape[idx_end:]) + result = np.zeros(outdims, wspd.dtype) idxs0 = ((0,Ellipsis, slice(None), slice(None), slice(None)) if not two_d else - (1, Ellipsis, slice(None), slice(None))) + (0, Ellipsis, slice(None), slice(None))) idxs1 = ((1, Ellipsis, slice(None), slice(None), slice(None)) if not two_d else - (0, Ellipsis, slice(None), slice(None))) + (1, Ellipsis, slice(None), slice(None))) result[idxs0] = wspd[:] result[idxs1] = wdir[:] + if fill is not None: + result = np.ma.masked_equal(result, fill) + return result