diff --git a/.circleci/conda_env.yml b/.circleci/conda_env.yml new file mode 100644 index 0000000..fbedd3f --- /dev/null +++ b/.circleci/conda_env.yml @@ -0,0 +1,11 @@ +name: wrf-python +channels: + - conda-forge +dependencies: + - gcc + - setuptools + - python + - numpy + - wrapt + - xarray + - netcdf4 diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..45ba0c7 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,73 @@ +# Python CircleCI 2.0 configuration file +# +# Check https://circleci.com/docs/2.0/language-python/ for more details +# +version: 2 +jobs: + build: + docker: + # specify the version you desire here + # use `-browsers` prefix for selenium tests, e.g. `3.6.1-browsers` + - image: circleci/python:3.6.1 + + # Specify service dependencies here if necessary + # CircleCI maintains a library of pre-built images + # documented at https://circleci.com/docs/2.0/circleci-images/ + # - image: circleci/postgres:9.4 + + working_directory: ~/repo + + steps: + - checkout + + # Download and cache dependencies + - restore_cache: + keys: + - v1-dependencies-{{ checksum ".circleci/conda_env.yml" }} + + - run: + name: install conda and dependencies + command: | + test -d ~/miniconda3 && exit 0 + echo "Installing a fresh version of Miniconda." + MINICONDA_URL="https://repo.continuum.io/miniconda" + MINICONDA_FILE="Miniconda3-latest-Linux-x86_64.sh" + curl -L -O "${MINICONDA_URL}/${MINICONDA_FILE}" + bash $MINICONDA_FILE -b + source ~/miniconda3/bin/activate root + conda env create -f .circleci/conda_env.yml + + - save_cache: + paths: + - ~/miniconda3 + key: v1-dependencies-{{ checksum ".circleci/conda_env.yml" }} + + - run: + name: build wrf-python + command: | + source ~/miniconda3/bin/activate wrf-python + cd fortran/build_help + gfortran -o sizes -fopenmp omp_sizes.f90 + LD_LIBRARY_PATH=~/miniconda3/envs/wrf-python/lib python sub_sizes.py + cd .. + gfortran -E ompgen.F90 -fopenmp -cpp -o omp.f90 + cd .. + 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 + + + # run tests! + # this example uses Django's built-in test-runner + # other common Python testing frameworks include pytest and nose + # https://pytest.org + # https://nose.readthedocs.io + - run: + name: run tests + command: | + source ~/miniconda3/bin/activate wrf-python + cd test/ci_tests + python utests.py + + - store_artifacts: + path: test-reports + destination: test-reports diff --git a/LICENSE b/LICENSE index 376dd79..525a044 100644 --- a/LICENSE +++ b/LICENSE @@ -1,32 +1,14 @@ -PLEASE READ THIS SOFTWARE LICENSE ("LICENSE") CAREFULLY BEFORE USING THE -SOFTWARE. BY USING THE SOFTWARE, YOU ARE AGREEING TO BE BOUND BY ALL OF THE -TERMS OF THIS LICENSE. IF YOU DO NOT AGREE TO THE TERMS OF THIS LICENSE, -DO NOT USE THE SOFTWARE. +Copyright 2016 University Corporation for Atmospheric Research -Copyright © 2016 the University Corporation for Atmospheric Research ("UCAR"). -All rights reserved. Developed by NCAR's Computational and Information Systems -Laboratory, UCAR, www2.cisl.ucar.edu. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at -Redistribution and use of the Software in source and binary forms, with or -without modification, is permitted provided that the following conditions -are met: + http://www.apache.org/licenses/LICENSE-2.0 -- Neither the names of NCAR's Computational and Information Systems Laboratory, - the University Corporation for Atmospheric Research, nor the names of its - sponsors or contributors may be used to endorse or promote products derived - from this Software without specific prior written permission. +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. -- Redistributions of source code must retain the above copyright notices, this - list of conditions, and the disclaimer below. - -- Redistributions in binary form must reproduce the above copyright notice, this - list of conditions, and the disclaimer below in the documentation and/or other - materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING, BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. diff --git a/build_scripts/chey_intel_mkl_omp.sh b/build_scripts/chey_intel_mkl_omp.sh index ee40614..c6b014f 100644 --- a/build_scripts/chey_intel_mkl_omp.sh +++ b/build_scripts/chey_intel_mkl_omp.sh @@ -7,7 +7,7 @@ python sub_sizes.py cd .. ifort ompgen.F90 -qopenmp -fpp -save-temps > /dev/null 2>&1 mv ompgen.i90 omp.f90 -f2py *.f90 -m _wrffortran -h wrffortran.pyf --overwrite-signature +#f2py *.f90 -m _wrffortran -h wrffortran.pyf --overwrite-signature cd .. python setup.py clean --all diff --git a/build_scripts/gnu_no_omp.sh b/build_scripts/gnu_no_omp.sh index b87b529..917c2da 100755 --- a/build_scripts/gnu_no_omp.sh +++ b/build_scripts/gnu_no_omp.sh @@ -2,7 +2,7 @@ cd ../fortran gfortran -E ompgen.F90 -cpp -o omp.f90 -f2py *.f90 -m _wrffortran -h wrffortran.pyf --overwrite-signature +#f2py *.f90 -m _wrffortran -h wrffortran.pyf --overwrite-signature cd .. python setup.py clean --all diff --git a/build_scripts/gnu_omp.sh b/build_scripts/gnu_omp.sh index 75310ad..3f97567 100755 --- a/build_scripts/gnu_omp.sh +++ b/build_scripts/gnu_omp.sh @@ -6,7 +6,7 @@ python sub_sizes.py cd .. gfortran -E ompgen.F90 -fopenmp -cpp -o omp.f90 -f2py *.f90 -m _wrffortran -h wrffortran.pyf --overwrite-signature +#f2py *.f90 -m _wrffortran -h wrffortran.pyf --overwrite-signature cd .. python setup.py clean --all diff --git a/build_scripts/win_mingw_no_omp.bat b/build_scripts/win_mingw_no_omp.bat index 9e1223c..f72d8de 100644 --- a/build_scripts/win_mingw_no_omp.bat +++ b/build_scripts/win_mingw_no_omp.bat @@ -1,7 +1,7 @@ cd ../fortran CALL gfortran -E ompgen.F90 -cpp -o omp.f90 REM Wildcards not working on Windows for some reason -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 +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 .. CALL python setup.py clean --all diff --git a/build_scripts/win_mingw_omp.bat b/build_scripts/win_mingw_omp.bat index a5fa4c3..84dbdba 100644 --- a/build_scripts/win_mingw_omp.bat +++ b/build_scripts/win_mingw_omp.bat @@ -5,7 +5,7 @@ CALL python sub_sizes.py cd .. CALL gfortran -E ompgen.F90 -cpp -fopenmp -o omp.f90 REM Wildcards not working on Windows for some reason -CALLf2py -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 +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 .. CALL python setup.py clean --all diff --git a/build_scripts/win_msvc_mingw_no_omp.bat b/build_scripts/win_msvc_mingw_no_omp.bat index 6fa132b..b789008 100644 --- a/build_scripts/win_msvc_mingw_no_omp.bat +++ b/build_scripts/win_msvc_mingw_no_omp.bat @@ -1,7 +1,7 @@ cd ../fortran CALL gfortran -E ompgen.F90 -cpp -o omp.f90 REM Wildcards not working on Windows for some reason -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 +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 .. CALL python setup.py clean --all diff --git a/build_scripts/win_msvc_mingw_omp.bat b/build_scripts/win_msvc_mingw_omp.bat index f4d1c5d..2582a22 100644 --- a/build_scripts/win_msvc_mingw_omp.bat +++ b/build_scripts/win_msvc_mingw_omp.bat @@ -5,7 +5,7 @@ CALL python sub_sizes.py cd .. CALL gfortran -E ompgen.F90 -cpp -fopenmp -o omp.f90 REM Wildcards not working on Windows for some reason -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 +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 .. CALL python setup.py clean --all diff --git a/doc/source/_static/images/basemap_500.png b/doc/source/_static/images/basemap_500.png index 5d9d86e..71c8ce9 100644 Binary files a/doc/source/_static/images/basemap_500.png and b/doc/source/_static/images/basemap_500.png differ diff --git a/doc/source/_static/images/basemap_front.png b/doc/source/_static/images/basemap_front.png index c2eb6a6..3282dc4 100644 Binary files a/doc/source/_static/images/basemap_front.png and b/doc/source/_static/images/basemap_front.png differ diff --git a/doc/source/_static/images/basemap_slp.png b/doc/source/_static/images/basemap_slp.png index afd8258..5977ce7 100644 Binary files a/doc/source/_static/images/basemap_slp.png and b/doc/source/_static/images/basemap_slp.png differ diff --git a/doc/source/_static/images/cartopy_500.png b/doc/source/_static/images/cartopy_500.png index cc012f8..fec8167 100644 Binary files a/doc/source/_static/images/cartopy_500.png and b/doc/source/_static/images/cartopy_500.png differ diff --git a/doc/source/_static/images/cartopy_cross.png b/doc/source/_static/images/cartopy_cross.png index fc7297e..f558ba3 100644 Binary files a/doc/source/_static/images/cartopy_cross.png and b/doc/source/_static/images/cartopy_cross.png differ diff --git a/doc/source/_static/images/cartopy_slp.png b/doc/source/_static/images/cartopy_slp.png index 9195c61..5d86502 100644 Binary files a/doc/source/_static/images/cartopy_slp.png and b/doc/source/_static/images/cartopy_slp.png differ diff --git a/doc/source/_static/images/cross_mtns.png b/doc/source/_static/images/cross_mtns.png new file mode 100644 index 0000000..967b460 Binary files /dev/null and b/doc/source/_static/images/cross_mtns.png differ diff --git a/doc/source/_static/images/matthew.png b/doc/source/_static/images/matthew.png index 2224b15..a989be6 100644 Binary files a/doc/source/_static/images/matthew.png and b/doc/source/_static/images/matthew.png differ diff --git a/doc/source/_static/images/matthew_cross.png b/doc/source/_static/images/matthew_cross.png index 81fe199..b86f5ec 100644 Binary files a/doc/source/_static/images/matthew_cross.png and b/doc/source/_static/images/matthew_cross.png differ diff --git a/doc/source/_templates/product_table.txt b/doc/source/_templates/product_table.txt index b2ee337..e3fe65b 100644 --- a/doc/source/_templates/product_table.txt +++ b/doc/source/_templates/product_table.txt @@ -9,9 +9,9 @@ | | | | | | | | degF | | +--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ -| cape_2d | 2D cape (mcape/mcin/lcl/lfc) | J kg-1 ; J kg-1 ; m ; m | **missing** (float): Fill value for output only | +| cape_2d | 2D CAPE (MCAPE/MCIN/LCL/LFC) | J kg-1 ; J kg-1 ; m ; m | **missing** (float): Fill value for output only | +--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ -| cape_3d | 3D cape and cin | J kg-1 | **missing** (float): Fill value for output only | +| cape_3d | 3D CAPE and CIN | J kg-1 | **missing** (float): Fill value for output only | +--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ | ctt | Cloud Top Temperature | degC | **fill_nocloud** (boolean): Set to True to use fill values for cloud free regions rather than surface temperature. Default is *False*. | | | | | | diff --git a/doc/source/_templates/subproducts.txt b/doc/source/_templates/subproducts.txt new file mode 100644 index 0000000..a13b2db --- /dev/null +++ b/doc/source/_templates/subproducts.txt @@ -0,0 +1,119 @@ ++--------------------+----------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Variable Name | Calculated From | Description | Available Units | Additional Keyword Arguments | ++====================+======================+===============================================================+=============================+=========================================================================================================================================================+ +| mcape | cape_2d | 2D Max CAPE | J kg-1 | **missing** (float): Fill value for output only | ++--------------------+----------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| mcin | cape_2d | 2D Max CIN | J kg-1 | **missing** (float): Fill value for output only | ++--------------------+----------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| lcl | cape_2d | 2D Lifted Condensation Level | J kg-1 | **missing** (float): Fill value for output only | ++--------------------+----------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| lfc | cape_2d | 2D Level of Free Convection | J kg-1 | **missing** (float): Fill value for output only | ++--------------------+----------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| cape3d_only | cape_3d | 3D CAPE | J kg-1 | **missing** (float): Fill value for output only | ++--------------------+----------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| cin3d_only | cape_3d | 3D CIN | J kg-1 | **missing** (float): Fill value for output only | ++--------------------+----------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| low_cloudfrac | cloudfrac | Cloud Fraction for Low Clouds | % | **vert_type** (str): The vertical coordinate type for the cloud thresholds. Must be 'height_agl', 'height_msl', or 'pres'. Default is 'height_agl'. | +| | | | | | +| | | | | **low_thresh** (float): The low cloud threshold (meters for 'height_agl' and 'height_msl', pascals for 'pres'). Default is 300 m (97000 Pa) | +| | | | | | +| | | | | **mid_thresh** (float): The mid cloud threshold (meters for 'height_agl' and 'height_msl', pascals for 'pres'). Default is 2000 m (80000 Pa) | +| | | | | | +| | | | | **high_thresh** (float): The high cloud threshold (meters for 'height_agl' and 'height_msl', pascals for 'pres'). Default is 6000 m (45000 Pa) | ++--------------------+----------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| mid_cloudfrac | cloudfrac | Cloud Fraction for Mid Clouds | % | **vert_type** (str): The vertical coordinate type for the cloud thresholds. Must be 'height_agl', 'height_msl', or 'pres'. Default is 'height_agl'. | +| | | | | | +| | | | | **low_thresh** (float): The low cloud threshold (meters for 'height_agl' and 'height_msl', pascals for 'pres'). Default is 300 m (97000 Pa) | +| | | | | | +| | | | | **mid_thresh** (float): The mid cloud threshold (meters for 'height_agl' and 'height_msl', pascals for 'pres'). Default is 2000 m (80000 Pa) | +| | | | | | +| | | | | **high_thresh** (float): The high cloud threshold (meters for 'height_agl' and 'height_msl', pascals for 'pres'). Default is 6000 m (45000 Pa) | ++--------------------+----------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| high_cloudfrac | cloudfrac | Cloud Fraction for High Clouds | % | **vert_type** (str): The vertical coordinate type for the cloud thresholds. Must be 'height_agl', 'height_msl', or 'pres'. Default is 'height_agl'. | +| | | | | | +| | | | | **low_thresh** (float): The low cloud threshold (meters for 'height_agl' and 'height_msl', pascals for 'pres'). Default is 300 m (97000 Pa) | +| | | | | | +| | | | | **mid_thresh** (float): The mid cloud threshold (meters for 'height_agl' and 'height_msl', pascals for 'pres'). Default is 2000 m (80000 Pa) | +| | | | | | +| | | | | **high_thresh** (float): The high cloud threshold (meters for 'height_agl' and 'height_msl', pascals for 'pres'). Default is 6000 m (45000 Pa) | ++--------------------+----------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| uvmet_wspd | uvmet_wspd_wdir | Wind Speed | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | +| | | | | | +| | | Rotated to Earth Coordinates | km h-1 | | +| | | | | | +| | | | mi h-1 | | +| | | | | | +| | | | kt | | +| | | | | | +| | | | ft s-1 | | ++--------------------+----------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| uvmet_wdir | uvmet_wspd_wdir | Wind 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 | | +| | | | | | +| | | | mi h-1 | | +| | | | | | +| | | | kt | | +| | | | | | +| | | | ft s-1 | | ++--------------------+----------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| uvmet10_wspd | uvmet10_wspd_wdir | 10m Wind Speed | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | +| | | | | | +| | | Rotated to Earth Coordinates | km h-1 | | +| | | | | | +| | | | mi h-1 | | +| | | | | | +| | | | kt | | +| | | | | | +| | | | ft s-1 | | ++--------------------+----------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| uvmet10_wdir | uvmet10_wspd_wdir | 10m Wind 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 | | +| | | | | | +| | | | mi h-1 | | +| | | | | | +| | | | kt | | +| | | | | | +| | | | ft s-1 | | ++--------------------+----------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| wspd | wspd_wdir | Wind Speed | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | +| | | | | | +| | | in Grid Coordinates | km h-1 | | +| | | | | | +| | | | mi h-1 | | +| | | | | | +| | | | kt | | +| | | | | | +| | | | ft s-1 | | ++--------------------+----------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| wdir | wspd_wdir | Wind Direction (wind_from_direction) | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | +| | | | | | +| | | in Grid Coordinates | km h-1 | | +| | | | | | +| | | | mi h-1 | | +| | | | | | +| | | | kt | | +| | | | | | +| | | | ft s-1 | | ++--------------------+----------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| wspd10 | wspd_wdir10 | 10m Wind Speed | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | +| | | | | | +| | | in Grid Coordinates | km h-1 | | +| | | | | | +| | | | mi h-1 | | +| | | | | | +| | | | kt | | +| | | | | | +| | | | ft s-1 | | ++--------------------+----------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| wdir10 | wspd_wdir10 | 10m Wind Direction (wind_from_direction) | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | +| | | | | | +| | | in Grid Coordinates | km h-1 | | +| | | | | | +| | | | mi h-1 | | +| | | | | | +| | | | kt | | +| | | | | | +| | | | ft s-1 | | ++--------------------+----------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ \ No newline at end of file diff --git a/doc/source/conf.py b/doc/source/conf.py index b248434..7d339dc 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -75,15 +75,18 @@ extensions = [ 'sphinx.ext.napoleon', 'sphinx.ext.autosummary', 'sphinx.ext.intersphinx', + 'sphinx.ext.mathjax' ] +#mathjax_path = "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-MML-AM_CHTML" + intersphinx_mapping = { 'matplotlib': ('http://matplotlib.org/', None), 'basemap' : ('http://matplotlib.org/basemap/', None), - 'cartopy' : ( - 'http://scitools.org.uk/cartopy/docs/latest/index.html', None), 'python': ('http://docs.python.org/3/', None), 'numpy': ('http://docs.scipy.org/doc/numpy/', None), + 'scipy' : + ('https://docs.scipy.org/doc/scipy/reference/', None), 'xarray': ('http://xarray.pydata.org/en/stable/', None), 'wrapt': ('http://wrapt.readthedocs.io/en/latest/', None) diff --git a/doc/source/diagnostics.rst b/doc/source/diagnostics.rst index 88e364b..c706ee6 100644 --- a/doc/source/diagnostics.rst +++ b/doc/source/diagnostics.rst @@ -5,3 +5,17 @@ Table of Available Diagnostics .. include:: _templates/product_table.txt + +.. _subdiagnostic-table: + +Table of Subproduct Diagnostics +---------------------------------- + +Some diagnostics (e.g. cape_2d) include multiple products in its +output. These products have been broken out in to individual diagnostics +to help those utilities that are unable to work with multiple outputs. +These individual diagnostics can be requested like any other diagnostic +using :meth:`wrf.getvar`. These are summarized in the table below. + + +.. include:: _templates/subproducts.txt diff --git a/doc/source/internal_api/index.rst b/doc/source/internal_api/index.rst index a41775b..cfd3c7c 100644 --- a/doc/source/internal_api/index.rst +++ b/doc/source/internal_api/index.rst @@ -53,6 +53,23 @@ The routines below are called internally by :meth:`wrf.getvar`. wrf.g_wind.get_w_destag wrf.g_wind.get_destag_wspd_wdir wrf.g_wind.get_destag_wspd_wdir10 + wrf.g_wind.get_destag_wspd + wrf.g_wind.get_destag_wdir + wrf.g_wind.get_destag_wspd10 + wrf.g_wind.get_destag_wdir10 + wrf.g_uvmet.get_uvmet_wspd + wrf.g_uvmet.get_uvmet_wdir + wrf.g_uvmet.get_uvmet10_wspd + wrf.g_uvmet.get_uvmet10_wdir + wrf.g_cloudfrac.get_low_cloudfrac + wrf.g_cloudfrac.get_mid_cloudfrac + wrf.g_cloudfrac.get_high_cloudfrac + wrf.g_cape.get_cape2d_only + wrf.g_cape.get_cin2d_only + wrf.g_cape.get_lcl + wrf.g_cape.get_lfc + wrf.g_cape.get_3dcape_only + wrf.g_cape.get_3dcin_only ------------------------- diff --git a/doc/source/new.rst b/doc/source/new.rst index 7e2d87f..2bc87d0 100644 --- a/doc/source/new.rst +++ b/doc/source/new.rst @@ -4,8 +4,52 @@ What's New Releases ------------- -v1.2.0 -^^^^^^^^^^^^^^ +v1.3.0 (December 2018) +^^^^^^^^^^^^^^^^^^^^^^^^^ + +- Release 1.3.0 +- Fixed FutureWarning issue with destag routine (thank you honnorat!) +- Fixed computational problems with updraft_helicity, and values are no longer + scaled by 1000. +- Removed version constraints for wrapt and setuptools. +- Fixed xarray being a hard dependency. +- Fixed unit issues with vinterp when pressure values are extracted below + ground. Also added support for height fields in km and pressure fields in + hPa. The documentation has been improved. +- Fixed the smooth2d routine so that it actually works. It never worked + correctly before (nor did it work in NCL). Users can now specify the + center weight of the kernel and the documentation has been updated to + describe how it works. +- Fixed the storm relative helicity algorithm so that it works in the southern + hemisphere. The raw algorithm now requires latitude input if used + in the southern hemisphere, otherwise the northern hemisphere is assumed. +- Fixed an issue with the latest version of cartopy 0.17 (thanks honnorat!) +- Fixed an issue where invalid keyword arguments weren't throwing errors when + extracting standard WRF variables. +- Fixed minor issues related to moving nests when using line interpolation and + vertical cross sections. It is still an error to request all times when + using lat/lon coordinates with a moving nest, but otherwise knows how to + run when all times are requested. This never really worked quite right. +- Removed the pyf file from setup.py since it is generated via the build + system. +- Added an autolevels parameter for the vertical cross section so that users + can specify the number of vertical levels to use if they don't want to + specify them manually. +- The interplevel routine has been improved. Users can now specify a single + level, multiple levels, or a 2D array (e.g. PBLH) to interpolate to. + Performance has been improved when interpolating a multiple product + field like wspd_wdir. +- Products that produce multiple outputs can now have the outputs requested + individually. See :ref:`subdiagnostic-table` for a list of what is available. +- Much of this version of wrf-python has been back ported to NCL in the + upcoming 6.6.0 release. The diagnostics should produce the same results + in both packages. +- Now released under the Apache 2.0 license. + + + +v1.2.0 (May 2018) +^^^^^^^^^^^^^^^^^^^^^^^^^ - Release 1.2.0 - Previous versions of wrf-python promoted the strings used in xarray (e.g. @@ -17,8 +61,8 @@ v1.2.0 errors, so we've decided to bump the major version number. -v1.1.3 -^^^^^^^^^^^^^^ +v1.1.3 (March 2018) +^^^^^^^^^^^^^^^^^^^^^^^^^ - Release 1.1.3 - Fixed/Enhanced the cloud top temperature diagnostic. @@ -39,15 +83,15 @@ v1.1.3 cape_3d. -v1.1.2 -^^^^^^^^^^^^^^ +v1.1.2 (February 2018) +^^^^^^^^^^^^^^^^^^^^^^^^^^ - Release 1.1.2 - Fix OpenMP directive issue with cloud top temperature. -v1.1.1 -^^^^^^^^^^^^^^ +v1.1.1 (February 2018) +^^^^^^^^^^^^^^^^^^^^^^^^^^ - Release 1.1.1 - Added script for building on Cheyenne with maxed out Intel settings, which @@ -59,8 +103,8 @@ v1.1.1 - Fix cape_2d private variable bug when running with multiple CPUs. -v1.1.0 -^^^^^^^^^^^^^^ +v1.1.0 (January 2018) +^^^^^^^^^^^^^^^^^^^^^^^^^ - Release 1.1.0 - Computational routines now support multiple cores using OpenMP. See @@ -107,15 +151,15 @@ v1.1.0 services like AppVeyor. -v1.0.5 -^^^^^^^^^^^^^^ +v1.0.5 (September 2017) +^^^^^^^^^^^^^^^^^^^^^^^^^^ - Release 1.0.5 - Reduced the CI test file sizes by half. -v1.0.4 -^^^^^^^^^^^^^^ +v1.0.4 (September 2017) +^^^^^^^^^^^^^^^^^^^^^^^^ - Release 1.0.4 - Fix warnings with CI tests which were caused by fill values being written @@ -123,23 +167,25 @@ v1.0.4 - Added the __eq__ operator to the WrfProj projection base class. - Fixed array order issue when using the raw CAPE routine with 1D arrays. -v1.0.3 -^^^^^^^^^^^^^^ + +v1.0.3 (June 2017) +^^^^^^^^^^^^^^^^^^^^^ - Relase 1.0.3 - Fixed an issue with the cartopy Mercator subclass where the xlimits were being calculated to the same value (or very close), causing blank plots. -v1.0.2 -^^^^^^^^^^^^^^ + +v1.0.2 (May 2017) +^^^^^^^^^^^^^^^^^^^^^ - Release 1.0.2 - Fixed issue with the wspd_wdir product types when sequences of files are used. -v1.0.1 -^^^^^^^^^^^^^ +v1.0.1 (March 2017) +^^^^^^^^^^^^^^^^^^^^^ - Release 1.0.1 - Fixed issue with initialization of PolarStereographic and LatLon map @@ -149,8 +195,8 @@ v1.0.1 so wrf-python should as well. -v1.0.0 -^^^^^^^^^^^^^ +v1.0.0 (March 2017) +^^^^^^^^^^^^^^^^^^^^^ - Release 1.0.0. - Fixed issue with not being able to set the thread-local coordinate cache to @@ -163,80 +209,6 @@ v1.0.0 column of data. -Beta Releases --------------- - -v1.0b3 -^^^^^^^^^^^^^ - -- Beta release 3. -- Improvements made for conda-forge integration testing. -- Fixed an incorrectly initialized variable issue with vinterp. This issue - mainly impacts the unit tests for continuous integration testing with - conda-forge, since the data set used for these tests is heavily cropped. -- Back-ported the inspect.BoundArguments.apply_defaults so that Python 3.4 - works. Windows users that want to try out wrf-python with Python 3.4 - can use the bladwig conda channel to get it. - -v1.0b2 -^^^^^^^^^^^^^^ - -- Beta release 2. -- xarray 0.9 no longer includes default index dimensions in the coordinate - mappings. This was causing a crash in the routines that cause a reduction - in dimension shape, mainly the interpolation routines. This has been - fixed. -- Documentation updated to show the new output from xarray. - -v1.0b1 -^^^^^^^^^^^^^ - -- Beta release 1. -- Added more packaging boilerplate. -- Note: Currently unable to build with Python 3.5 on Windows, due to - issues with distutils, numpy distutils, and mingw compiler. Will attempt - to find a workaround before the next release. Windows users should use - Python 2.7 or Python 3.4 for now. - - ----------------- - -Alpha Releases ----------------- - -v1.0a3 -^^^^^^^^^^^^ - -- Alpha release 3. -- Added docstrings. -- The mapping API has changed. - - The projection attributes are no longer arrays for moving domains. - - Utility functions have been added for extracting geobounds. It is now - easier to get map projection objects from sliced variables. - - Utility functions have been added for getting cartopy, basemap, and pyngl - objects. - - Users should no longer need to use xarray attributes directly -- Now uses CoordPair for cross sections so that lat/lon can be used instead of - raw x,y grid coordinates. -- Renamed npvalues to to_np which is more intuitive. -- Fixed issue with generator expressions. -- Renamed some functions and arguments. - - -------------- - - -Known Issues --------------- - -v1.0.0 -^^^^^^^^ - -- Currently unable to build on Windows with Python 3.5+ using open source - mingw compiler. The mingwpy project is working on resolving the - incompatibilities between mingw and Visual Studio 2015 that was used to - build Python 3.5+. Numpy 1.13 also has improved f2py support for - Python 3.5+ on Windows, so this will be revisited when it is released. diff --git a/doc/source/plot.rst b/doc/source/plot.rst index 13f229d..6c572e0 100644 --- a/doc/source/plot.rst +++ b/doc/source/plot.rst @@ -10,11 +10,11 @@ for a future release. Matplotlib With Cartopy ------------------------- -Cartopy is becoming the main tool for base mapping with matplotlib, but you should -be aware of a few shortcomings when working with WRF data. +Cartopy is becoming the main tool for base mapping with matplotlib, but you +should be aware of a few shortcomings when working with WRF data. -- The builtin transformations of coordinates when calling the contouring functions - do not work correctly with the rotated pole projection. The +- The builtin transformations of coordinates when calling the contouring + functions do not work correctly with the rotated pole projection. The transform_points method needs to be called manually on the latitude and longitude arrays. @@ -40,7 +40,8 @@ Plotting a Two-dimensional Field import cartopy.crs as crs 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 ncfile = Dataset("wrfout_d01_2016-10-07_00_00_00") @@ -48,8 +49,9 @@ Plotting a Two-dimensional Field # Get the sea level pressure slp = getvar(ncfile, "slp") - # Smooth the sea level pressure since it tends to be noisy near the mountains - smooth_slp = smooth2d(slp, 3) + # Smooth the sea level pressure since it tends to be noisy near the + # mountains + smooth_slp = smooth2d(slp, 3, cenweight=4) # Get the latitude and longitude points lats, lons = latlon_coords(slp) @@ -58,26 +60,29 @@ Plotting a Two-dimensional Field cart_proj = get_cartopy(slp) # Create a figure - fig = plt.figure(figsize=(12,9)) + fig = plt.figure(figsize=(12,6)) # Set the GeoAxes to the projection used by WRF 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=.5) + states = NaturalEarthFeature(category="cultural", scale="50m", + facecolor="none", + name="admin_1_states_provinces_shp") + ax.add_feature(states, linewidth=.5, edgecolor="black") 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(smooth_slp), 10, colors="black", transform=crs.PlateCarree()) - plt.contourf(to_np(lons), to_np(lats), to_np(smooth_slp), 10, transform=crs.PlateCarree(), + plt.contourf(to_np(lons), to_np(lats), to_np(smooth_slp), 10, + transform=crs.PlateCarree(), cmap=get_cmap("jet")) # Add a color bar - plt.colorbar(ax=ax, shrink=.62) + plt.colorbar(ax=ax, shrink=.98) - # Set the map limits. Not really necessary, but used for demonstration. + # Set the map bounds ax.set_xlim(cartopy_xlim(smooth_slp)) ax.set_ylim(cartopy_ylim(smooth_slp)) @@ -105,7 +110,8 @@ Horizontal Interpolation to a Pressure Level 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 + 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") @@ -134,27 +140,31 @@ Horizontal Interpolation to a Pressure Level 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) + states = NaturalEarthFeature(category="cultural", scale="50m", + facecolor="none", + name="admin_1_states_provinces_shp") + ax.add_feature(states, linewidth=0.5, edgecolor="black") ax.coastlines('50m', linewidth=0.8) # Add the 500 hPa geopotential height contours levels = np.arange(520., 580., 6.) - contours = plt.contour(to_np(lons), to_np(lats), to_np(ht_500), levels=levels, colors="black", + contours = plt.contour(to_np(lons), to_np(lats), to_np(ht_500), + levels=levels, colors="black", transform=crs.PlateCarree()) plt.clabel(contours, inline=1, fontsize=10, fmt="%i") # Add the wind speed contours levels = [25, 30, 35, 40, 50, 60, 70, 80, 90, 100, 110, 120] - wspd_contours = plt.contourf(to_np(lons), to_np(lats), to_np(wspd_500), levels=levels, + wspd_contours = plt.contourf(to_np(lons), to_np(lats), to_np(wspd_500), + levels=levels, cmap=get_cmap("rainbow"), transform=crs.PlateCarree()) plt.colorbar(wspd_contours, ax=ax, orientation="horizontal", pad=.05) # Add the 500 hPa wind barbs, only plotting every 125th data point. - plt.barbs(to_np(lons[::125,::125]), to_np(lats[::125,::125]), to_np(u_500[::125, ::125]), - to_np(v_500[::125, ::125]), transform=crs.PlateCarree(), length=6) + plt.barbs(to_np(lons[::125,::125]), to_np(lats[::125,::125]), + to_np(u_500[::125, ::125]), to_np(v_500[::125, ::125]), + transform=crs.PlateCarree(), length=6) # Set the map bounds ax.set_xlim(cartopy_xlim(ht_500)) @@ -187,8 +197,8 @@ plot, see :ref:`cross_example`. import cartopy.feature as cfeature from netCDF4 import Dataset - from wrf import (getvar, to_np, vertcross, smooth2d, CoordPair, GeoBounds, get_cartopy, - latlon_coords, cartopy_xlim, cartopy_ylim) + from wrf import (getvar, to_np, vertcross, smooth2d, CoordPair, GeoBounds, + get_cartopy, latlon_coords, cartopy_xlim, cartopy_ylim) # Open the NetCDF file ncfile = Dataset("wrfout_d01_2016-10-07_00_00_00") @@ -206,12 +216,13 @@ plot, see :ref:`cross_example`. start_point = CoordPair(lat=26.76, lon=-80.0) end_point = CoordPair(lat=26.76, lon=-77.8) - # Compute the vertical cross-section interpolation. Also, include the lat/lon - # points along the cross-section in the metadata by setting latlon to True. - z_cross = vertcross(Z, z, wrfin=ncfile, start_point=start_point, end_point=end_point, - latlon=True, meta=True) - wspd_cross = vertcross(wspd, z, wrfin=ncfile, start_point=start_point, end_point=end_point, - latlon=True, meta=True) + # Compute the vertical cross-section interpolation. Also, include the + # lat/lon points along the cross-section in the metadata by setting latlon + # to True. + z_cross = vertcross(Z, z, wrfin=ncfile, start_point=start_point, + end_point=end_point, latlon=True, meta=True) + wspd_cross = vertcross(wspd, z, wrfin=ncfile, start_point=start_point, + end_point=end_point, latlon=True, meta=True) dbz_cross = 10.0 * np.log10(z_cross) # Get the lat/lon points @@ -221,31 +232,37 @@ plot, see :ref:`cross_example`. cart_proj = get_cartopy(slp) # Create a figure that will have 3 subplots - fig = plt.figure(figsize=(10,7)) + fig = plt.figure(figsize=(12,9)) ax_ctt = fig.add_subplot(1,2,1,projection=cart_proj) ax_wspd = fig.add_subplot(2,2,2) ax_dbz = fig.add_subplot(2,2,4) # Download and create the states, land, and oceans using cartopy features - states = cfeature.NaturalEarthFeature(category='cultural', scale='50m', facecolor='none', + states = cfeature.NaturalEarthFeature(category='cultural', scale='50m', + facecolor='none', name='admin_1_states_provinces_shp') - land = cfeature.NaturalEarthFeature(category='physical', name='land', scale='50m', + land = cfeature.NaturalEarthFeature(category='physical', name='land', + scale='50m', facecolor=cfeature.COLORS['land']) - ocean = cfeature.NaturalEarthFeature(category='physical', name='ocean', scale='50m', + ocean = cfeature.NaturalEarthFeature(category='physical', name='ocean', + scale='50m', facecolor=cfeature.COLORS['water']) # Make the pressure contours contour_levels = [960, 965, 970, 975, 980, 990] - c1 = ax_ctt.contour(lons, lats, to_np(smooth_slp), levels=contour_levels, colors="white", - transform=crs.PlateCarree(), zorder=3, linewidths=1.0) + c1 = ax_ctt.contour(lons, lats, to_np(smooth_slp), levels=contour_levels, + colors="white", transform=crs.PlateCarree(), zorder=3, + linewidths=1.0) # Create the filled cloud top temperature contours contour_levels = [-80.0, -70.0, -60, -50, -40, -30, -20, -10, 0, 10] - ctt_contours = ax_ctt.contourf(to_np(lons), to_np(lats), to_np(ctt), contour_levels, - cmap=get_cmap("Greys"), transform=crs.PlateCarree(), zorder=2) + ctt_contours = ax_ctt.contourf(to_np(lons), to_np(lats), to_np(ctt), + contour_levels, cmap=get_cmap("Greys"), + transform=crs.PlateCarree(), zorder=2) - ax_ctt.plot([start_point.lon, end_point.lon], [start_point.lat, end_point.lat], - color="yellow", marker="o", transform=crs.PlateCarree(), zorder=3) + ax_ctt.plot([start_point.lon, end_point.lon], + [start_point.lat, end_point.lat], color="yellow", marker="o", + transform=crs.PlateCarree(), zorder=3) # Create the color bar for cloud top temperature cb_ctt = fig.colorbar(ctt_contours, ax=ax_ctt, shrink=.60) @@ -271,7 +288,8 @@ plot, see :ref:`cross_example`. # Make the contour plot for dbz levels = [5 + 5*n for n in range(15)] - dbz_contours = ax_dbz.contourf(to_np(dbz_cross), levels=levels, cmap=get_cmap("jet")) + dbz_contours = ax_dbz.contourf(to_np(dbz_cross), levels=levels, + cmap=get_cmap("jet")) cb_dbz = fig.colorbar(dbz_contours, ax=ax_dbz) cb_dbz.ax.tick_params(labelsize=5) @@ -308,9 +326,9 @@ plot, see :ref:`cross_example`. Matplotlib with Basemap ----------------------- -Although basemap is in maintenance mode only and becoming deprecated, it is still -widely used by many programmers. Cartopy is becoming the preferred package for -mapping, however it suffers from growing pains in some areas +Although basemap is in maintenance mode only and becoming deprecated, it is +still widely used by many programmers. Cartopy is becoming the preferred +package for mapping, however it suffers from growing pains in some areas (can't use latitude/longitude labels for many map projections). If you run in to these issues, basemap is likely to accomplish what you need. @@ -337,8 +355,9 @@ Plotting a Two-Dimensional Field # Get the sea level pressure slp = getvar(ncfile, "slp") - # Smooth the sea level pressure since it tends to be noisy near the mountains - smooth_slp = smooth2d(slp, 3) + # Smooth the sea level pressure since it tends to be noisy near the + # mountains + smooth_slp = smooth2d(slp, 3, cenweight=4) # Get the latitude and longitude points lats, lons = latlon_coords(slp) @@ -354,8 +373,9 @@ Plotting a Two-Dimensional Field bm.drawstates(linewidth=0.25) bm.drawcountries(linewidth=0.25) - # Convert the lats and lons to x and y. Make sure you convert the lats and lons to - # numpy arrays via to_np, or basemap crashes with an undefined RuntimeError. + # Convert the lats and lons to x and y. Make sure you convert the lats and + # lons to numpy arrays via to_np, or basemap crashes with an undefined + # RuntimeError. x, y = bm(to_np(lons), to_np(lats)) # Draw the contours and filled contours @@ -458,7 +478,8 @@ plot, see :ref:`cross_example`. from matplotlib.cm import get_cmap from netCDF4 import Dataset - from wrf import getvar, to_np, vertcross, smooth2d, CoordPair, get_basemap, latlon_coords + from wrf import (getvar, to_np, vertcross, smooth2d, CoordPair, + get_basemap, latlon_coords) # Open the NetCDF file ncfile = Dataset("wrfout_d01_2016-10-07_00_00_00") @@ -476,19 +497,20 @@ plot, see :ref:`cross_example`. start_point = CoordPair(lat=26.76, lon=-80.0) end_point = CoordPair(lat=26.76, lon=-77.8) - # Compute the vertical cross-section interpolation. Also, include the lat/lon points - # along the cross-section in the metadata by setting latlon to True. - z_cross = vertcross(Z, z, wrfin=ncfile, start_point=start_point, end_point=end_point, - latlon=True, meta=True) - wspd_cross = vertcross(wspd, z, wrfin=ncfile, start_point=start_point, end_point=end_point, - latlon=True, meta=True) + # Compute the vertical cross-section interpolation. Also, include the + # lat/lon points along the cross-section in the metadata by setting latlon + # to True. + z_cross = vertcross(Z, z, wrfin=ncfile, start_point=start_point, + end_point=end_point, latlon=True, meta=True) + wspd_cross = vertcross(wspd, z, wrfin=ncfile, start_point=start_point, + end_point=end_point, latlon=True, meta=True) dbz_cross = 10.0 * np.log10(z_cross) # Get the latitude and longitude points lats, lons = latlon_coords(slp) # Create the figure that will have 3 subplots - fig = plt.figure(figsize=(10,7)) + fig = plt.figure(figsize=(12,9)) ax_ctt = fig.add_subplot(1,2,1) ax_wspd = fig.add_subplot(2,2,2) ax_dbz = fig.add_subplot(2,2,4) @@ -501,15 +523,16 @@ plot, see :ref:`cross_example`. # Make the pressure contours contour_levels = [960, 965, 970, 975, 980, 990] - c1 = bm.contour(x, y, to_np(smooth_slp), levels=contour_levels, colors="white", - zorder=3, linewidths=1.0, ax=ax_ctt) + c1 = bm.contour(x, y, to_np(smooth_slp), levels=contour_levels, + colors="white", zorder=3, linewidths=1.0, ax=ax_ctt) # Create the filled cloud top temperature contours contour_levels = [-80.0, -70.0, -60, -50, -40, -30, -20, -10, 0, 10] - ctt_contours = bm.contourf(x, y, to_np(ctt), contour_levels, cmap=get_cmap("Greys"), - zorder=2, ax=ax_ctt) + ctt_contours = bm.contourf(x, y, to_np(ctt), contour_levels, + cmap=get_cmap("Greys"), zorder=2, ax=ax_ctt) - point_x, point_y = bm([start_point.lon, end_point.lon], [start_point.lat, end_point.lat]) + point_x, point_y = bm([start_point.lon, end_point.lon], + [start_point.lat, end_point.lat]) bm.plot([point_x[0], point_x[1]], [point_y[0], point_y[1]], color="yellow", marker="o", zorder=3, ax=ax_ctt) @@ -522,8 +545,12 @@ plot, see :ref:`cross_example`. bm.drawstates(linewidth=0.25, ax=ax_ctt) bm.drawcountries(linewidth=0.25, ax=ax_ctt) bm.fillcontinents(color=np.array([ 0.9375 , 0.9375 , 0.859375]), - ax=ax_ctt, lake_color=np.array([ 0.59375 , 0.71484375, 0.8828125 ])) - bm.drawmapboundary(fill_color=np.array([ 0.59375 , 0.71484375, 0.8828125 ]), ax=ax_ctt) + ax=ax_ctt, + lake_color=np.array([0.59375 , + 0.71484375, + 0.8828125 ])) + bm.drawmapboundary(fill_color=np.array([ 0.59375 , 0.71484375, 0.8828125 ]), + ax=ax_ctt) # Draw Parallels parallels = np.arange(np.amin(lats), 30., 2.5) @@ -547,7 +574,8 @@ plot, see :ref:`cross_example`. # Make the contour plot for dbz levels = [5 + 5*n for n in range(15)] - dbz_contours = ax_dbz.contourf(to_np(dbz_cross), levels=levels, cmap=get_cmap("jet")) + dbz_contours = ax_dbz.contourf(to_np(dbz_cross), levels=levels, + cmap=get_cmap("jet")) cb_dbz = fig.colorbar(dbz_contours, ax=ax_dbz) cb_dbz.ax.tick_params(labelsize=5) @@ -583,6 +611,7 @@ plot, see :ref:`cross_example`. .. _cross_example: + Vertical Cross Section ------------------------------- @@ -616,10 +645,10 @@ plotted using the standard matplotlib API. start_point = CoordPair(lat=26.76, lon=-80.0) end_point = CoordPair(lat=26.76, lon=-77.8) - # Compute the vertical cross-section interpolation. Also, include the lat/lon - # points along the cross-section. - wspd_cross = vertcross(wspd, z, wrfin=ncfile, start_point=start_point, end_point=end_point, - latlon=True, meta=True) + # Compute the vertical cross-section interpolation. Also, include the + # lat/lon points along the cross-section. + wspd_cross = vertcross(wspd, z, wrfin=ncfile, start_point=start_point, + end_point=end_point, latlon=True, meta=True) # Create the figure fig = plt.figure(figsize=(12,6)) @@ -634,7 +663,8 @@ plotted using the standard matplotlib API. # Set the x-ticks to use latitude and longitude labels. coord_pairs = to_np(wspd_cross.coords["xy_loc"]) x_ticks = np.arange(coord_pairs.shape[0]) - x_labels = [pair.latlon_str(fmt="{:.2f}, {:.2f}") for pair in to_np(coord_pairs)] + x_labels = [pair.latlon_str(fmt="{:.2f}, {:.2f}") + for pair in to_np(coord_pairs)] ax.set_xticks(x_ticks[::20]) ax.set_xticklabels(x_labels[::20], rotation=45, fontsize=8) @@ -651,4 +681,142 @@ plotted using the standard matplotlib API. plt.title("Vertical Cross Section of Wind Speed (kt)") plt.show() + + +Cross Section with Mountains +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The example below shows how to make a cross section with the mountainous +terrain filled. + +.. image:: _static/images/cross_mtns.png + :scale: 100% + :align: center + +.. code-block:: python + + import numpy as np + from matplotlib import pyplot + from matplotlib.cm import get_cmap + from matplotlib.colors import from_levels_and_colors + from cartopy import crs + from cartopy.feature import NaturalEarthFeature, COLORS + from netCDF4 import Dataset + from wrf import (getvar, to_np, get_cartopy, latlon_coords, vertcross, + cartopy_xlim, cartopy_ylim, interpline, CoordPair) + + wrf_file = Dataset("wrfout_d01_2010-06-04_00:00:00") + + # Define the cross section start and end points + cross_start = CoordPair(lat=43.5, lon=-116.5) + cross_end = CoordPair(lat=43.5, lon=-114) + + # Get the WRF variables + ht = getvar(wrf_file, "z", timeidx=-1) + ter = getvar(wrf_file, "ter", timeidx=-1) + dbz = getvar(wrf_file, "dbz", timeidx=-1) + max_dbz = getvar(wrf_file, "mdbz", timeidx=-1) + Z = 10**(dbz/10.) # Use linear Z for interpolation + + # Compute the vertical cross-section interpolation. Also, include the + # lat/lon points along the cross-section in the metadata by setting latlon + # to True. + z_cross = vertcross(Z, ht, wrfin=wrf_file, + start_point=cross_start, + end_point=cross_end, + latlon=True, meta=True) + + # Convert back to dBz after interpolation + dbz_cross = 10.0 * np.log10(z_cross) + + # Add back the attributes that xarray dropped from the operations above + dbz_cross.attrs.update(z_cross.attrs) + dbz_cross.attrs["description"] = "radar reflectivity cross section" + dbz_cross.attrs["units"] = "dBZ" + + # To remove the slight gap between the dbz contours and terrain due to the + # contouring of gridded data, a new vertical grid spacing, and model grid + # staggering, fill in the lower grid cells with the first non-missing value + # for each column. + + # Make a copy of the z cross data. Let's use regular numpy arrays for this. + dbz_cross_filled = np.ma.copy(to_np(dbz_cross)) + + # For each cross section column, find the first index with non-missing + # values and copy these to the missing elements below. + for i in range(dbz_cross_filled.shape[-1]): + column_vals = dbz_cross_filled[:,i] + # Let's find the lowest index that isn't filled. The nonzero function + # finds all unmasked values greater than 0. Since 0 is a valid value + # for dBZ, let's change that threshold to be -200 dBZ instead. + first_idx = int(np.transpose((column_vals > -200).nonzero())[0]) + dbz_cross_filled[0:first_idx, i] = dbz_cross_filled[first_idx, i] + + # Get the terrain heights along the cross section line + ter_line = interpline(ter, wrfin=wrf_file, start_point=cross_start, + end_point=cross_end) + + # Get the lat/lon points + lats, lons = latlon_coords(dbz) + + # Get the cartopy projection object + cart_proj = get_cartopy(dbz) + + # Create the figure + fig = pyplot.figure(figsize=(8,6)) + ax_cross = pyplot.axes() + + dbz_levels = np.arange(5., 75., 5.) + + # Create the color table found on NWS pages. + dbz_rgb = np.array([[4,233,231], + [1,159,244], [3,0,244], + [2,253,2], [1,197,1], + [0,142,0], [253,248,2], + [229,188,0], [253,149,0], + [253,0,0], [212,0,0], + [188,0,0],[248,0,253], + [152,84,198]], np.float32) / 255.0 + + dbz_map, dbz_norm = from_levels_and_colors(dbz_levels, dbz_rgb, + extend="max") + + # Make the cross section plot for dbz + dbz_levels = np.arange(5.,75.,5.) + xs = np.arange(0, dbz_cross.shape[-1], 1) + ys = to_np(dbz_cross.coords["vertical"]) + dbz_contours = ax_cross.contourf(xs, + ys, + to_np(dbz_cross_filled), + levels=dbz_levels, + cmap=dbz_map, + norm=dbz_norm, + extend="max") + # Add the color bar + cb_dbz = fig.colorbar(dbz_contours, ax=ax_cross) + cb_dbz.ax.tick_params(labelsize=8) + + # Fill in the mountain area + ht_fill = ax_cross.fill_between(xs, 0, to_np(ter_line), + facecolor="saddlebrown") + + # Set the x-ticks to use latitude and longitude labels + coord_pairs = to_np(dbz_cross.coords["xy_loc"]) + x_ticks = np.arange(coord_pairs.shape[0]) + x_labels = [pair.latlon_str() for pair in to_np(coord_pairs)] + + # Set the desired number of x ticks below + num_ticks = 5 + thin = int((len(x_ticks) / num_ticks) + .5) + ax_cross.set_xticks(x_ticks[::thin]) + ax_cross.set_xticklabels(x_labels[::thin], rotation=45, fontsize=8) + + # Set the x-axis and y-axis labels + ax_cross.set_xlabel("Latitude, Longitude", fontsize=12) + ax_cross.set_ylabel("Height (m)", fontsize=12) + + # Add a title + ax_cross.set_title("Cross-Section of Reflectivity (dBZ)", {"fontsize" : 14}) + + pyplot.show() diff --git a/doc/source/tutorial.rst b/doc/source/tutorial.rst index 297b35b..3e1d08b 100644 --- a/doc/source/tutorial.rst +++ b/doc/source/tutorial.rst @@ -12,7 +12,6 @@ Upcoming Tutorials .. toctree:: :maxdepth: 1 - tutorials/boise_2018.rst Past Tutorials @@ -21,8 +20,11 @@ Past Tutorials .. toctree:: :maxdepth: 1 - tutorials/wrf_workshop_2017.rst - tutorials/tutorial_03_2018.rst + tutorials/boise_2018.rst tutorials/wrf_workshop_2018.rst + tutorials/tutorial_03_2018.rst + tutorials/wrf_workshop_2017.rst + + diff --git a/fortran/calc_uh.f90 b/fortran/calc_uh.f90 index 72acfe6..d66ef06 100644 --- a/fortran/calc_uh.f90 +++ b/fortran/calc_uh.f90 @@ -154,8 +154,6 @@ SUBROUTINE DCALCUH(nx, ny, nz, nzp1, zp, mapfct, dx, dy, uhmnhgt, uhmxhgt, us, & !$OMP END PARALLEL - uh = uh*1000. ! Scale according to Kain et al. (2008) - RETURN END SUBROUTINE DCALCUH diff --git a/fortran/wrf_constants.f90 b/fortran/wrf_constants.f90 index eb2e9d8..0864502 100644 --- a/fortran/wrf_constants.f90 +++ b/fortran/wrf_constants.f90 @@ -1,8 +1,8 @@ ! These are chosen to match the wrf module_model_constants.F where ! applicable MODULE wrf_constants - INTEGER :: ERRLEN=512 - INTEGER :: ALGERR=64 + INTEGER, PARAMETER :: ERRLEN=512 + INTEGER, PARAMETER :: ALGERR=64 REAL(KIND=8), PARAMETER :: WRF_EARTH_RADIUS = 6370000.D0 REAL(KIND=8), PARAMETER :: T_BASE = 300.0D0 diff --git a/fortran/wrf_relhl.f90 b/fortran/wrf_relhl.f90 index b3e562f..aa9d3f4 100644 --- a/fortran/wrf_relhl.f90 +++ b/fortran/wrf_relhl.f90 @@ -31,7 +31,7 @@ ! *************************************************************** ! NCLFORTSTART -SUBROUTINE DCALRELHL(u, v, ght, ter, top, sreh, miy, mjx, mkzh) +SUBROUTINE DCALRELHL(u, v, ght, ter, lat, top, sreh, miy, mjx, mkzh) USE wrf_constants, ONLY : PI, RAD_PER_DEG, DEG_PER_RAD IMPLICIT NONE @@ -43,6 +43,7 @@ SUBROUTINE DCALRELHL(u, v, ght, ter, top, sreh, miy, mjx, mkzh) REAL(KIND=8), DIMENSION(miy,mjx,mkzh), INTENT(IN) :: u, v, ght REAL(KIND=8), INTENT(IN) :: top REAL(KIND=8), DIMENSION(miy,mjx), INTENT(IN) :: ter + REAL(KIND=8), DIMENSION(miy,mjx), INTENT(IN) :: lat REAL(KIND=8), DIMENSION(miy,mjx), INTENT(OUT) :: sreh ! NCLEND @@ -100,7 +101,13 @@ SUBROUTINE DCALRELHL(u, v, ght, ter, top, sreh, miy, mjx, mkzh) ENDIF bsp = 0.75D0*asp - bdr = adr + 30.D0 + + IF (lat(i,j) .GE. 0) THEN ! Northern hemisphern + bdr = adr + 30.D0 + ELSE ! Southern hemisphere + bdr = adr - 30.D0 + END IF + IF (bdr .GT. 360.D0) THEN bdr = bdr - 360.D0 ENDIF diff --git a/fortran/wrf_user.f90 b/fortran/wrf_user.f90 index e0bfac8..48963b7 100644 --- a/fortran/wrf_user.f90 +++ b/fortran/wrf_user.f90 @@ -64,7 +64,67 @@ END SUBROUTINE DCOMPUTETK ! NCLFORTSTART -SUBROUTINE DINTERP3DZ(data3d, out2d, zdata, desiredloc, nx, ny, nz, missingval) +SUBROUTINE DINTERP3DZ(data3d, out2d, zdata, levels, nx, ny, nz, nlev, missingval) + IMPLICIT NONE + + !f2py threadsafe + !f2py intent(in,out) :: out2d + + INTEGER, INTENT(IN) :: nx, ny, nz, nlev + REAL(KIND=8), DIMENSION(nx,ny,nz), INTENT(IN) :: data3d + REAL(KIND=8), DIMENSION(nx,ny,nlev), INTENT(OUT) :: out2d + REAL(KIND=8), DIMENSION(nx,ny,nz), INTENT(IN) :: zdata + REAL(KIND=8), DIMENSION(nlev), INTENT(IN) :: levels + REAL(KIND=8), INTENT(IN) :: missingval + +! NCLEND + + INTEGER :: i,j,kp,ip,im,lev + LOGICAL :: dointerp + REAL(KIND=8) :: w1,w2,desiredloc + + ! does vertical coordinate increase or decrease with increasing k? + ! set offset appropriately + + ip = 0 + im = 1 + IF (zdata(1,1,1) .GT. zdata(1,1,nz)) THEN + ip = 1 + im = 0 + END IF + + !$OMP PARALLEL DO COLLAPSE(3) PRIVATE(i,j,lev,kp,dointerp,w1,w2,desiredloc) & + !$OMP FIRSTPRIVATE(ip,im) SCHEDULE(runtime) + DO lev = 1,nlev + DO i = 1,nx + DO j = 1,ny + ! Initialize to missing. Was initially hard-coded to -999999. + out2d(i,j,lev) = missingval + dointerp = .FALSE. + kp = nz + desiredloc = levels(lev) + + DO WHILE ((.NOT. dointerp) .AND. (kp >= 2)) + IF (((zdata(i,j,kp-im) < desiredloc) .AND. (zdata(i,j,kp-ip) > desiredloc))) THEN + w2 = (desiredloc - zdata(i,j,kp-im))/(zdata(i,j,kp-ip) - zdata(i,j,kp-im)) + w1 = 1.D0 - w2 + out2d(i,j,lev) = w1*data3d(i,j,kp-im) + w2*data3d(i,j,kp-ip) + dointerp = .TRUE. + END IF + kp = kp - 1 + END DO + END DO + END DO + END DO + !$OMP END PARALLEL DO + + RETURN + +END SUBROUTINE DINTERP3DZ + + +! NCLFORTSTART +SUBROUTINE DINTERP3DZ_2DLEV(data3d, out2d, zdata, levs2d, nx, ny, nz, missingval) IMPLICIT NONE !f2py threadsafe @@ -74,14 +134,14 @@ SUBROUTINE DINTERP3DZ(data3d, out2d, zdata, desiredloc, nx, ny, nz, missingval) REAL(KIND=8), DIMENSION(nx,ny,nz), INTENT(IN) :: data3d REAL(KIND=8), DIMENSION(nx,ny), INTENT(OUT) :: out2d REAL(KIND=8), DIMENSION(nx,ny,nz), INTENT(IN) :: zdata - REAL(KIND=8), INTENT(IN) :: desiredloc + REAL(KIND=8), DIMENSION(nx,ny), INTENT(IN) :: levs2d REAL(KIND=8), INTENT(IN) :: missingval ! NCLEND INTEGER :: i,j,kp,ip,im LOGICAL :: dointerp - REAL(KIND=8) :: w1,w2 + REAL(KIND=8) :: w1,w2,desiredloc ! does vertical coordinate increase or decrease with increasing k? ! set offset appropriately @@ -93,7 +153,7 @@ SUBROUTINE DINTERP3DZ(data3d, out2d, zdata, desiredloc, nx, ny, nz, missingval) im = 0 END IF - !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i,j,kp,dointerp,w1,w2) & + !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i,j,kp,dointerp,w1,w2,desiredloc) & !$OMP FIRSTPRIVATE(ip,im) SCHEDULE(runtime) DO i = 1,nx DO j = 1,ny @@ -101,6 +161,7 @@ SUBROUTINE DINTERP3DZ(data3d, out2d, zdata, desiredloc, nx, ny, nz, missingval) out2d(i,j) = missingval dointerp = .FALSE. kp = nz + desiredloc = levs2d(i,j) DO WHILE ((.NOT. dointerp) .AND. (kp >= 2)) IF (((zdata(i,j,kp-im) < desiredloc) .AND. (zdata(i,j,kp-ip) > desiredloc))) THEN @@ -117,7 +178,7 @@ SUBROUTINE DINTERP3DZ(data3d, out2d, zdata, desiredloc, nx, ny, nz, missingval) RETURN -END SUBROUTINE DINTERP3DZ +END SUBROUTINE DINTERP3DZ_2DLEV ! PORT DZSTAG HERE @@ -479,7 +540,7 @@ END SUBROUTINE DCOMPUTESEAPRS ! must make the same change below to filter2d. ! NCLFORTSTART -SUBROUTINE DFILTER2D(a, b, nx, ny, it, missing) +SUBROUTINE DFILTER2D(a, b, nx, ny, it, missing, cenweight) IMPLICIT NONE @@ -488,14 +549,16 @@ SUBROUTINE DFILTER2D(a, b, nx, ny, it, missing) INTEGER, INTENT(IN) :: nx, ny, it REAL(KIND=8), DIMENSION(nx, ny), INTENT(INOUT) :: a - REAL(KIND=8), INTENT(IN) :: missing + REAL(KIND=8), INTENT(IN) :: missing, cenweight REAL(KIND=8), DIMENSION(nx, ny), INTENT(INOUT) :: b ! NCLEND - REAL(KIND=8), PARAMETER :: COEF=0.25D0 - INTEGER :: i, j, iter + REAL(KIND=8) :: cenmult, coef + + cenmult = (cenweight) / 2. + coef = 1.0 / (4. + cenweight) DO iter=1,it !$OMP PARALLEL DO COLLAPSE(2) SCHEDULE(runtime) @@ -508,25 +571,25 @@ SUBROUTINE DFILTER2D(a, b, nx, ny, it, missing) !$OMP PARALLEL DO COLLAPSE(2) SCHEDULE(runtime) DO j=2,ny-1 - DO i=1,nx + DO i=2,nx-1 IF (b(i,j-1) .EQ. missing .OR. b(i,j) .EQ. missing .OR. & b(i,j+1) .EQ. missing) THEN a(i,j) = a(i,j) ELSE - a(i,j) = a(i,j) + COEF*(b(i,j-1) - 2*b(i,j) + b(i,j+1)) + a(i,j) = coef*(b(i,j-1) + cenmult*b(i,j) + b(i,j+1)) END IF END DO END DO !$OMP END PARALLEL DO !$OMP PARALLEL DO COLLAPSE(2) SCHEDULE(runtime) - DO j=1,ny + DO j=2,ny-1 DO i=2,nx-1 IF (b(i-1,j) .EQ. missing .OR. b(i,j) .EQ. missing .OR. & b(i+1,j) .EQ. missing) THEN a(i,j) = a(i,j) ELSE - a(i,j) = a(i,j) + COEF*(b(i-1,j) - 2*b(i,j) + b(i+1,j)) + a(i,j) = a(i,j) + coef*(b(i-1,j) + cenmult*b(i,j) + b(i+1,j)) END IF END DO END DO @@ -556,7 +619,7 @@ END SUBROUTINE DFILTER2D ! must make the same change below to dfilter2d. ! NCLFORTSTART -SUBROUTINE FILTER2D(a, b, nx, ny, it, missing) +SUBROUTINE FILTER2D(a, b, nx, ny, it, missing, cenweight) IMPLICIT NONE !f2py threadsafe @@ -564,14 +627,17 @@ SUBROUTINE FILTER2D(a, b, nx, ny, it, missing) INTEGER, INTENT(IN) :: nx, ny, it REAL(KIND=4), DIMENSION(nx, ny), INTENT(INOUT) :: a - REAL(KIND=4), INTENT(IN) :: missing + REAL(KIND=4), INTENT(IN) :: missing, cenweight REAL(KIND=4), DIMENSION(nx, ny), INTENT(INOUT) :: b ! NCLEND - REAL(KIND=4), PARAMETER :: COEF=0.25 - + !REAL(KIND=8), PARAMETER :: COEF = .125 INTEGER :: i, j, iter + REAL(KIND=8) :: cenmult, coef + + cenmult = (cenweight) / 2. + coef = 1.0 / (4. + cenweight) !$OMP PARALLEL @@ -586,25 +652,25 @@ SUBROUTINE FILTER2D(a, b, nx, ny, it, missing) !$OMP DO COLLAPSE(2) SCHEDULE(runtime) DO j=2,ny-1 - DO i=1,nx + DO i=2,nx-1 IF (b(i,j-1) .EQ. missing .OR. b(i,j) .EQ. missing .OR. & b(i,j+1) .EQ. missing) THEN a(i,j) = a(i,j) ELSE - a(i,j) = a(i,j) + COEF*(b(i,j-1) - 2*b(i,j) + b(i,j+1)) + a(i,j) = coef*(b(i,j-1) + cenmult*b(i,j) + b(i,j+1)) END IF END DO END DO !$OMP END DO !$OMP DO COLLAPSE(2) SCHEDULE(runtime) - DO j=1,ny + DO j=2,ny-1 DO i=2,nx-1 IF (b(i-1,j) .EQ. missing .OR. b(i,j) .EQ. missing .OR. & b(i+1,j) .EQ. missing) THEN a(i,j) = a(i,j) ELSE - a(i,j) = a(i,j) + COEF*(b(i-1,j) - 2*b(i,j)+b(i+1,j)) + a(i,j) = a(i,j) + coef*(b(i-1,j) + cenmult*b(i,j) + b(i+1,j)) END IF END DO END DO diff --git a/fortran/wrf_vinterp.f90 b/fortran/wrf_vinterp.f90 index d81270b..9b7f94e 100644 --- a/fortran/wrf_vinterp.f90 +++ b/fortran/wrf_vinterp.f90 @@ -340,7 +340,7 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& plev = ((ezlev - ezlhsl)*& psurf + (ezsurf - ezlev)*plhsl)/(ezsurf - ezlhsl) IF (icase .EQ. 1) THEN - tempout(i,j) = plev + tempout(i,j) = plev * 100. CYCLE END IF END IF @@ -377,7 +377,7 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& zlev = -sclht*LOG(vlev) plev = pbot*(1. + USSALR/vt*(zbot - zlev))**EXPONI IF (icase .EQ. 1) THEN - tempout(i,j) = plev + tempout(i,j) = plev * 100. CYCLE END IF END IF @@ -395,7 +395,7 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& ! Potential temperature - theta ELSE IF (icase .EQ. 5) THEN tempout(i,j) = tlev*(1000.D0/plev)**gammam - ! extraolation for equivalent potential temperature + ! extrapolation for equivalent potential temperature ELSE IF (icase .EQ. 6) THEN e = qvapor*plev/(EPS + qvapor) tlcl = TLCLC1/(LOG(tlev**TLCLC2/e) - TLCLC3) + TLCLC4 diff --git a/fortran/wrffortran.pyf b/fortran/wrffortran.pyf deleted file mode 100644 index 15340e1..0000000 --- a/fortran/wrffortran.pyf +++ /dev/null @@ -1,795 +0,0 @@ -! -*- f90 -*- -! Note: the context of this file is case sensitive. - -python module _wrffortran ! in - interface ! in :_wrffortran - subroutine dcalcuh(nx,ny,nz,nzp1,zp,mapfct,dx,dy,uhmnhgt,uhmxhgt,us,vs,w,uh,tem1,tem2) ! in :_wrffortran:calc_uh.f90 - threadsafe - integer, optional,intent(in),check(shape(zp,0)==nx),depend(zp) :: nx=shape(zp,0) - integer, optional,intent(in),check(shape(zp,1)==ny),depend(zp) :: ny=shape(zp,1) - integer, optional,intent(in),check(shape(us,2)==nz),depend(us) :: nz=shape(us,2) - integer, optional,intent(in),check(shape(zp,2)==nzp1),depend(zp) :: nzp1=shape(zp,2) - real(kind=8) dimension(nx,ny,nzp1),intent(in) :: zp - real(kind=8) dimension(nx,ny),intent(in),depend(nx,ny) :: mapfct - real(kind=8) intent(in) :: dx - real(kind=8) intent(in) :: dy - real(kind=8) intent(in) :: uhmnhgt - real(kind=8) intent(in) :: uhmxhgt - real(kind=8) dimension(nx,ny,nz),intent(in),depend(nx,ny) :: us - real(kind=8) dimension(nx,ny,nz),intent(in),depend(nx,ny,nz) :: vs - real(kind=8) dimension(nx,ny,nzp1),intent(in),depend(nx,ny,nzp1) :: w - real(kind=8) dimension(nx,ny),intent(out,in),depend(nx,ny) :: uh - real(kind=8) dimension(nx,ny,nz),intent(inout),depend(nx,ny,nz) :: tem1 - real(kind=8) dimension(nx,ny,nz),intent(inout),depend(nx,ny,nz) :: tem2 - end subroutine dcalcuh - subroutine deqthecalc(qvp,tmk,prs,eth,miy,mjx,mkzh) ! in :_wrffortran:eqthecalc.f90 - threadsafe - use wrf_constants, only: tlclc2,tlclc3,tlclc1,tlclc4,eps,thtecon3,gammamd,thtecon1,gamma,thtecon2 - real(kind=8) dimension(miy,mjx,mkzh),intent(in) :: qvp - real(kind=8) dimension(miy,mjx,mkzh),intent(in),depend(miy,mjx,mkzh) :: tmk - real(kind=8) dimension(miy,mjx,mkzh),intent(in),depend(miy,mjx,mkzh) :: prs - real(kind=8) dimension(miy,mjx,mkzh),intent(out,in),depend(miy,mjx,mkzh) :: eth - integer, optional,intent(in),check(shape(qvp,0)==miy),depend(qvp) :: miy=shape(qvp,0) - integer, optional,intent(in),check(shape(qvp,1)==mjx),depend(qvp) :: mjx=shape(qvp,1) - integer, optional,intent(in),check(shape(qvp,2)==mkzh),depend(qvp) :: mkzh=shape(qvp,2) - end subroutine deqthecalc - module omp_constants ! in :_wrffortran:omp.f90 - integer, parameter,optional :: fomp_nest_lock_kind=8 - integer(kind=4), parameter,optional :: fomp_sched_dynamic=2 - integer(kind=4), parameter,optional :: fomp_sched_guided=3 - integer, parameter,optional :: fomp_sched_kind=4 - integer(kind=4), parameter,optional :: fomp_sched_static=1 - integer, parameter,optional :: fomp_lock_kind=4 - integer(kind=4), parameter,optional :: fomp_sched_auto=4 - end module omp_constants - function fomp_enabled() ! in :_wrffortran:omp.f90 - threadsafe - logical :: fomp_enabled - end function fomp_enabled - subroutine fomp_set_num_threads(num_threads) ! in :_wrffortran:omp.f90 - threadsafe - integer intent(in) :: num_threads - end subroutine fomp_set_num_threads - function fomp_get_num_threads() ! in :_wrffortran:omp.f90 - threadsafe - integer :: fomp_get_num_threads - end function fomp_get_num_threads - function fomp_get_max_threads() ! in :_wrffortran:omp.f90 - threadsafe - integer :: fomp_get_max_threads - end function fomp_get_max_threads - function fomp_get_thread_num() ! in :_wrffortran:omp.f90 - threadsafe - integer :: fomp_get_thread_num - end function fomp_get_thread_num - function fomp_get_num_procs() ! in :_wrffortran:omp.f90 - threadsafe - integer :: fomp_get_num_procs - end function fomp_get_num_procs - function fomp_in_parallel() ! in :_wrffortran:omp.f90 - threadsafe - logical :: fomp_in_parallel - end function fomp_in_parallel - subroutine fomp_set_dynamic(dynamic_threads) ! in :_wrffortran:omp.f90 - threadsafe - logical intent(in) :: dynamic_threads - end subroutine fomp_set_dynamic - function fomp_get_dynamic() ! in :_wrffortran:omp.f90 - threadsafe - logical :: fomp_get_dynamic - end function fomp_get_dynamic - subroutine fomp_set_nested(nested) ! in :_wrffortran:omp.f90 - threadsafe - logical intent(in) :: nested - end subroutine fomp_set_nested - function fomp_get_nested() ! in :_wrffortran:omp.f90 - threadsafe - logical :: fomp_get_nested - end function fomp_get_nested - subroutine fomp_set_schedule(kind,modifier) ! in :_wrffortran:omp.f90 - threadsafe - use omp_constants, only: fomp_sched_kind - integer(kind=4) intent(in) :: kind - integer intent(in) :: modifier - end subroutine fomp_set_schedule - subroutine fomp_get_schedule(kind,modifier) ! in :_wrffortran:omp.f90 - threadsafe - use omp_constants, only: fomp_sched_kind - integer(kind=4) intent(out) :: kind - integer intent(out) :: modifier - end subroutine fomp_get_schedule - function fomp_get_thread_limit() ! in :_wrffortran:omp.f90 - threadsafe - integer :: fomp_get_thread_limit - end function fomp_get_thread_limit - subroutine fomp_set_max_active_levels(max_levels) ! in :_wrffortran:omp.f90 - threadsafe - integer intent(in) :: max_levels - end subroutine fomp_set_max_active_levels - function fomp_get_max_active_levels() ! in :_wrffortran:omp.f90 - threadsafe - integer :: fomp_get_max_active_levels - end function fomp_get_max_active_levels - function fomp_get_level() ! in :_wrffortran:omp.f90 - threadsafe - integer :: fomp_get_level - end function fomp_get_level - function fomp_get_ancestor_thread_num(level) ! in :_wrffortran:omp.f90 - threadsafe - integer intent(in) :: level - integer :: fomp_get_ancestor_thread_num - end function fomp_get_ancestor_thread_num - function fomp_get_team_size(level) ! in :_wrffortran:omp.f90 - threadsafe - integer intent(in) :: level - integer :: fomp_get_team_size - end function fomp_get_team_size - function fomp_get_active_level() ! in :_wrffortran:omp.f90 - threadsafe - integer :: fomp_get_active_level - end function fomp_get_active_level - function fomp_in_final() ! in :_wrffortran:omp.f90 - threadsafe - logical :: fomp_in_final - end function fomp_in_final - subroutine fomp_init_lock(svar) ! in :_wrffortran:omp.f90 - threadsafe - use omp_constants, only: fomp_lock_kind - integer(kind=4) intent(out) :: svar - end subroutine fomp_init_lock - subroutine fomp_init_nest_lock(nvar) ! in :_wrffortran:omp.f90 - threadsafe - use omp_constants, only: fomp_nest_lock_kind - integer(kind=8) intent(out) :: nvar - end subroutine fomp_init_nest_lock - subroutine fomp_destroy_lock(svar) ! in :_wrffortran:omp.f90 - threadsafe - use omp_constants, only: fomp_lock_kind - integer(kind=4) intent(inout) :: svar - end subroutine fomp_destroy_lock - subroutine fomp_destroy_nest_lock(nvar) ! in :_wrffortran:omp.f90 - threadsafe - use omp_constants, only: fomp_nest_lock_kind - integer(kind=8) intent(inout) :: nvar - end subroutine fomp_destroy_nest_lock - subroutine fomp_set_lock(svar) ! in :_wrffortran:omp.f90 - threadsafe - use omp_constants, only: fomp_lock_kind - integer(kind=4) intent(inout) :: svar - end subroutine fomp_set_lock - subroutine fomp_set_nest_lock(nvar) ! in :_wrffortran:omp.f90 - threadsafe - use omp_constants, only: fomp_nest_lock_kind - integer(kind=8) intent(inout) :: nvar - end subroutine fomp_set_nest_lock - subroutine fomp_unset_lock(svar) ! in :_wrffortran:omp.f90 - threadsafe - use omp_constants, only: fomp_lock_kind - integer(kind=4) intent(inout) :: svar - end subroutine fomp_unset_lock - subroutine fomp_unset_nest_lock(nvar) ! in :_wrffortran:omp.f90 - threadsafe - use omp_constants, only: fomp_nest_lock_kind - integer(kind=8) intent(inout) :: nvar - end subroutine fomp_unset_nest_lock - function fomp_test_lock(svar) ! in :_wrffortran:omp.f90 - threadsafe - use omp_constants, only: fomp_lock_kind - integer(kind=4) intent(inout) :: svar - logical :: fomp_test_lock - end function fomp_test_lock - function fomp_test_nest_lock(nvar) ! in :_wrffortran:omp.f90 - threadsafe - use omp_constants, only: fomp_nest_lock_kind - integer(kind=8) intent(inout) :: nvar - integer :: fomp_test_nest_lock - end function fomp_test_nest_lock - function fomp_get_wtime() ! in :_wrffortran:omp.f90 - threadsafe - real(kind=8) :: fomp_get_wtime - end function fomp_get_wtime - function fomp_get_wtick() ! in :_wrffortran:omp.f90 - threadsafe - real(kind=8) :: fomp_get_wtick - end function fomp_get_wtick - function tvirtual(temp,ratmix) ! in :_wrffortran:rip_cape.f90 - threadsafe - use wrf_constants, only: eps - real(kind=8) intent(in) :: temp - real(kind=8) intent(in) :: ratmix - real(kind=8) :: tvirtual - end function tvirtual - function tonpsadiabat(thte,prs,psadithte,psadiprs,psaditmk,gamma,errstat,errmsg) ! in :_wrffortran:rip_cape.f90 - threadsafe - use wrf_constants, only: algerr - real(kind=8) intent(in) :: thte - real(kind=8) intent(in) :: prs - real(kind=8) dimension(150),intent(in) :: psadithte - real(kind=8) dimension(150),intent(in) :: psadiprs - real(kind=8) dimension(150,150),intent(in) :: psaditmk - real(kind=8) intent(in) :: gamma - integer intent(inout) :: errstat - character*(*) intent(inout) :: errmsg - real(kind=8) :: tonpsadiabat - end function tonpsadiabat - subroutine dlookup_table(psadithte,psadiprs,psaditmk,fname,errstat,errmsg) ! in :_wrffortran:rip_cape.f90 - threadsafe - use wrf_constants, only: algerr - real(kind=8) dimension(150),intent(inout) :: psadithte - real(kind=8) dimension(150),intent(inout) :: psadiprs - real(kind=8) dimension(150,150),intent(inout) :: psaditmk - character*(*) intent(in) :: fname - integer intent(inout) :: errstat - character*(*) intent(inout) :: errmsg - end subroutine dlookup_table - subroutine dpfcalc(prs,sfp,pf,mix,mjy,mkzh,ter_follow) ! in :_wrffortran:rip_cape.f90 - real(kind=8) dimension(mkzh,mix,mjy),intent(in) :: prs - real(kind=8) dimension(mix,mjy),intent(in),depend(mix,mjy) :: sfp - real(kind=8) dimension(mkzh,mix,mjy),intent(out),depend(mkzh,mix,mjy) :: pf - integer, optional,intent(in),check(shape(prs,1)==mix),depend(prs) :: mix=shape(prs,1) - integer, optional,intent(in),check(shape(prs,2)==mjy),depend(prs) :: mjy=shape(prs,2) - integer, optional,intent(in),check(shape(prs,0)==mkzh),depend(prs) :: mkzh=shape(prs,0) - integer intent(in) :: ter_follow - end subroutine dpfcalc - subroutine dcapecalc3d(prs,tmk,qvp,ght,ter,sfp,cape,cin,prsf,prs_new,tmk_new,qvp_new,ght_new,cmsg,mix,mjy,mkzh,ter_follow,psafile,errstat,errmsg) ! in :_wrffortran:rip_cape.f90 - threadsafe - use wrf_constants, only: tlclc2,gamma,tlclc1,rgasmd,tlclc4,g,tlclc3,thtecon3,eps,rd,cpmd,celkel,gammamd,eslcon2,eslcon1,cp,thtecon1,ezero,thtecon2 - real(kind=8) dimension(mix,mjy,mkzh),intent(in) :: prs - real(kind=8) dimension(mix,mjy,mkzh),intent(in),depend(mix,mjy,mkzh) :: tmk - real(kind=8) dimension(mix,mjy,mkzh),intent(in),depend(mix,mjy,mkzh) :: qvp - real(kind=8) dimension(mix,mjy,mkzh),intent(in),depend(mix,mjy,mkzh) :: ght - real(kind=8) dimension(mix,mjy),intent(in),depend(mix,mjy) :: ter - real(kind=8) dimension(mix,mjy),intent(in),depend(mix,mjy) :: sfp - real(kind=8) dimension(mix,mjy,mkzh),intent(out,in),depend(mix,mjy,mkzh) :: cape - real(kind=8) dimension(mix,mjy,mkzh),intent(out,in),depend(mix,mjy,mkzh) :: cin - real(kind=8) dimension(mkzh,mix,mjy),intent(inout),depend(mkzh,mix,mjy) :: prsf - real(kind=8) dimension(mkzh,mix,mjy),intent(inout),depend(mkzh,mix,mjy) :: prs_new - real(kind=8) dimension(mkzh,mix,mjy),intent(inout),depend(mkzh,mix,mjy) :: tmk_new - real(kind=8) dimension(mkzh,mix,mjy),intent(inout),depend(mkzh,mix,mjy) :: qvp_new - real(kind=8) dimension(mkzh,mix,mjy),intent(inout),depend(mkzh,mix,mjy) :: ght_new - real(kind=8) intent(in) :: cmsg - integer, optional,intent(in),check(shape(prs,0)==mix),depend(prs) :: mix=shape(prs,0) - integer, optional,intent(in),check(shape(prs,1)==mjy),depend(prs) :: mjy=shape(prs,1) - integer, optional,intent(in),check(shape(prs,2)==mkzh),depend(prs) :: mkzh=shape(prs,2) - integer intent(in) :: ter_follow - character*(*) intent(in) :: psafile - integer intent(inout) :: errstat - character*(*) intent(inout) :: errmsg - end subroutine dcapecalc3d - subroutine dcapecalc2d(prs,tmk,qvp,ght,ter,sfp,cape,cin,prsf,prs_new,tmk_new,qvp_new,ght_new,cmsg,mix,mjy,mkzh,ter_follow,psafile,errstat,errmsg) ! in :_wrffortran:rip_cape.f90 - threadsafe - use wrf_constants, only: tlclc2,gamma,tlclc1,rgasmd,tlclc4,g,tlclc3,thtecon3,eps,rd,cpmd,celkel,gammamd,eslcon2,eslcon1,cp,thtecon1,ezero,thtecon2 - real(kind=8) dimension(mix,mjy,mkzh),intent(in) :: prs - real(kind=8) dimension(mix,mjy,mkzh),intent(in),depend(mix,mjy,mkzh) :: tmk - real(kind=8) dimension(mix,mjy,mkzh),intent(in),depend(mix,mjy,mkzh) :: qvp - real(kind=8) dimension(mix,mjy,mkzh),intent(in),depend(mix,mjy,mkzh) :: ght - real(kind=8) dimension(mix,mjy),intent(in),depend(mix,mjy) :: ter - real(kind=8) dimension(mix,mjy),intent(in),depend(mix,mjy) :: sfp - real(kind=8) dimension(mix,mjy,mkzh),intent(out,in),depend(mix,mjy,mkzh) :: cape - real(kind=8) dimension(mix,mjy,mkzh),intent(out,in),depend(mix,mjy,mkzh) :: cin - real(kind=8) dimension(mkzh,mix,mjy),intent(inout),depend(mkzh,mix,mjy) :: prsf - real(kind=8) dimension(mkzh,mix,mjy),intent(inout),depend(mkzh,mix,mjy) :: prs_new - real(kind=8) dimension(mkzh,mix,mjy),intent(inout),depend(mkzh,mix,mjy) :: tmk_new - real(kind=8) dimension(mkzh,mix,mjy),intent(inout),depend(mkzh,mix,mjy) :: qvp_new - real(kind=8) dimension(mkzh,mix,mjy),intent(inout),depend(mkzh,mix,mjy) :: ght_new - real(kind=8) intent(in) :: cmsg - integer, optional,intent(in),check(shape(prs,0)==mix),depend(prs) :: mix=shape(prs,0) - integer, optional,intent(in),check(shape(prs,1)==mjy),depend(prs) :: mjy=shape(prs,1) - integer, optional,intent(in),check(shape(prs,2)==mkzh),depend(prs) :: mkzh=shape(prs,2) - integer intent(in) :: ter_follow - character*(*) intent(in) :: psafile - integer intent(inout) :: errstat - character*(*) intent(inout) :: errmsg - end subroutine dcapecalc2d - subroutine dcloudfrac(pres,rh,lowc,midc,highc,nz,ns,ew) ! in :_wrffortran:wrf_cloud_fracf.f90 - threadsafe - real(kind=8) dimension(ew,ns,nz),intent(in) :: pres - real(kind=8) dimension(ew,ns,nz),intent(in),depend(ew,ns,nz) :: rh - real(kind=8) dimension(ew,ns),intent(out,in),depend(ew,ns) :: lowc - real(kind=8) dimension(ew,ns),intent(out,in),depend(ew,ns) :: midc - real(kind=8) dimension(ew,ns),intent(out,in),depend(ew,ns) :: highc - integer, optional,check(shape(pres,2)==nz),depend(pres) :: nz=shape(pres,2) - integer, optional,check(shape(pres,1)==ns),depend(pres) :: ns=shape(pres,1) - integer, optional,check(shape(pres,0)==ew),depend(pres) :: ew=shape(pres,0) - end subroutine dcloudfrac - subroutine dcloudfrac2(vert,rh,vert_inc_w_height,low_thresh,mid_thresh,high_thresh,msg,lowc,midc,highc,nz,ns,ew) ! in :_wrffortran:wrf_cloud_fracf.f90 - threadsafe - real(kind=8) dimension(ew,ns,nz),intent(in) :: vert - real(kind=8) dimension(ew,ns,nz),intent(in),depend(ew,ns,nz) :: rh - integer intent(in) :: vert_inc_w_height - real(kind=8) intent(in) :: low_thresh - real(kind=8) intent(in) :: mid_thresh - real(kind=8) intent(in) :: high_thresh - real(kind=8) intent(in) :: msg - real(kind=8) dimension(ew,ns),intent(out,in),depend(ew,ns) :: lowc - real(kind=8) dimension(ew,ns),intent(out,in),depend(ew,ns) :: midc - real(kind=8) dimension(ew,ns),intent(out,in),depend(ew,ns) :: highc - integer, optional,check(shape(vert,2)==nz),depend(vert) :: nz=shape(vert,2) - integer, optional,check(shape(vert,1)==ns),depend(vert) :: ns=shape(vert,1) - integer, optional,check(shape(vert,0)==ew),depend(vert) :: ew=shape(vert,0) - end subroutine dcloudfrac2 - module wrf_constants ! in :_wrffortran:wrf_constants.f90 - real(kind=4), parameter,optional :: default_fill_float=9.96920996839e+36 - integer(kind=8), parameter,optional :: default_fill_int64=int(-9223372036854775806d0, kind=8) - real(kind=8), parameter,optional :: wrf_earth_radius=6370000.d0 - character(len=1), parameter,optional :: default_fill_char=achar(0) - real(kind=8), parameter,optional :: rhowat=1000.d0 - real(kind=8), parameter,optional :: t_base=300.0d0 - real(kind=8), parameter,optional :: cp=1004.5d0 - real(kind=8), parameter,optional :: thtecon1=3376.d0 - real(kind=8), parameter,optional :: thtecon3=.81d0 - real(kind=8), parameter,optional :: thtecon2=2.54d0 - real(kind=8), parameter,optional :: p1000mb=100000.d0 - real(kind=8), parameter,optional :: rv=461.6d0 - real(kind=8), parameter,optional,depend(pi) :: rad_per_deg=pi/180.d0 - real(kind=8), parameter,optional :: default_fill_double=9.9692099683868690d36 - real(kind=8), parameter,optional :: rd=287.d0 - real(kind=8), parameter,optional :: abscoef=.145d0 - real(kind=8), parameter,optional :: celkel=273.15d0 - real(kind=8), parameter,optional :: abscoefi=.272d0 - real(kind=8), parameter,optional :: eslcon2=29.65d0 - real(kind=8), parameter,optional :: eslcon1=17.67d0 - real(kind=8), parameter,optional :: pi=3.1415926535897932384626433d0 - integer(kind=1), parameter,optional :: default_fill_int8=-127 - real(kind=8), parameter,optional :: tlclc2=3.5d0 - real(kind=8), parameter,optional :: tlclc3=4.805d0 - real(kind=8), parameter,optional :: rho_g=400.d0 - real(kind=8), parameter,optional :: tlclc1=2840.d0 - real(kind=8), parameter,optional :: tlclc4=55.d0 - integer(kind=2), parameter,optional :: default_fill_int16=-32767 - real(kind=8), parameter,optional,depend(pi) :: deg_per_rad=180.d0/pi - real(kind=8), parameter,optional :: cpmd=.887d0 - real(kind=8), parameter,optional,depend(rd,g) :: sclht=rd*256.d0/g - real(kind=8), parameter,optional :: ussalr=0.0065d0 - real(kind=8), parameter,optional :: default_fill=9.9692099683868690d36 - real(kind=8), parameter,optional :: rho_s=100.d0 - real(kind=8), parameter,optional,depend(rhowat) :: rho_r=1000.0 - real(kind=8), parameter,optional :: alpha=0.224d0 - real(kind=8), parameter,optional :: celkel_triple=273.16d0 - real(kind=8), parameter,optional :: ezero=6.112d0 - real(kind=8), parameter,optional,depend(rd,ussalr,g) :: expon=0.190163098879 - real(kind=8), parameter,optional :: rgasmd=.608d0 - real(kind=8), parameter,optional :: g=9.81d0 - integer, optional :: errlen=512 - integer(kind=4), parameter,optional :: default_fill_int32=-2147483647 - real(kind=8), parameter,optional :: gamma_seven=720.d0 - real(kind=8), parameter,optional,depend(cpmd,rgasmd) :: gammamd=-0.279 - real(kind=8), parameter,optional :: eps=0.622d0 - integer, optional :: algerr=64 - real(kind=8), parameter,optional,depend(cp,rd) :: gamma=0.285714285714 - real(kind=8), parameter,optional,depend(expon,rd,ussalr,g) :: exponi=5.25864379523 - end module wrf_constants - subroutine wrfcttcalc(prs,tk,qci,qcw,qvp,ght,ter,ctt,pf,haveqci,fill_nocloud,missing,opt_thresh,nz,ns,ew) ! in :_wrffortran:wrf_fctt.f90 - threadsafe - use wrf_constants, only: g,eps,rd,ussalr,abscoefi,abscoef,celkel - real(kind=8) dimension(ew,ns,nz),intent(in) :: prs - real(kind=8) dimension(ew,ns,nz),intent(in),depend(ew,ns,nz) :: tk - real(kind=8) dimension(ew,ns,nz),intent(in),depend(ew,ns,nz) :: qci - real(kind=8) dimension(ew,ns,nz),intent(in),depend(ew,ns,nz) :: qcw - real(kind=8) dimension(ew,ns,nz),intent(in),depend(ew,ns,nz) :: qvp - real(kind=8) dimension(ew,ns,nz),intent(in),depend(ew,ns,nz) :: ght - real(kind=8) dimension(ew,ns),intent(in),depend(ew,ns) :: ter - real(kind=8) dimension(ew,ns),intent(out,in),depend(ew,ns) :: ctt - real(kind=8) dimension(ew,ns,nz),intent(inout),depend(ew,ns,nz) :: pf - integer intent(in) :: haveqci - integer intent(in) :: fill_nocloud - real(kind=8) intent(in) :: missing - real(kind=8) intent(in) :: opt_thresh - integer, optional,intent(in),check(shape(prs,2)==nz),depend(prs) :: nz=shape(prs,2) - integer, optional,intent(in),check(shape(prs,1)==ns),depend(prs) :: ns=shape(prs,1) - integer, optional,intent(in),check(shape(prs,0)==ew),depend(prs) :: ew=shape(prs,0) - end subroutine wrfcttcalc - subroutine dcomputeabsvort(av,u,v,msfu,msfv,msft,cor,dx,dy,nx,ny,nz,nxp1,nyp1) ! in :_wrffortran:wrf_pvo.f90 - threadsafe - real(kind=8) dimension(nx,ny,nz),intent(out,in) :: av - real(kind=8) dimension(nxp1,ny,nz),intent(in),depend(ny,nz) :: u - real(kind=8) dimension(nx,nyp1,nz),intent(in),depend(nx,nz) :: v - real(kind=8) dimension(nxp1,ny),intent(in),depend(nxp1,ny) :: msfu - real(kind=8) dimension(nx,nyp1),intent(in),depend(nx,nyp1) :: msfv - real(kind=8) dimension(nx,ny),intent(in),depend(nx,ny) :: msft - real(kind=8) dimension(nx,ny),intent(in),depend(nx,ny) :: cor - real(kind=8) :: dx - real(kind=8) :: dy - integer, optional,intent(in),check(shape(av,0)==nx),depend(av) :: nx=shape(av,0) - integer, optional,intent(in),check(shape(av,1)==ny),depend(av) :: ny=shape(av,1) - integer, optional,intent(in),check(shape(av,2)==nz),depend(av) :: nz=shape(av,2) - integer, optional,intent(in),check(shape(u,0)==nxp1),depend(u) :: nxp1=shape(u,0) - integer, optional,intent(in),check(shape(v,1)==nyp1),depend(v) :: nyp1=shape(v,1) - end subroutine dcomputeabsvort - subroutine dcomputepv(pv,u,v,theta,prs,msfu,msfv,msft,cor,dx,dy,nx,ny,nz,nxp1,nyp1) ! in :_wrffortran:wrf_pvo.f90 - threadsafe - use wrf_constants, only: g - real(kind=8) dimension(nx,ny,nz),intent(out,in) :: pv - real(kind=8) dimension(nxp1,ny,nz),intent(in),depend(ny,nz) :: u - real(kind=8) dimension(nx,nyp1,nz),intent(in),depend(nx,nz) :: v - real(kind=8) dimension(nx,ny,nz),intent(in),depend(nx,ny,nz) :: theta - real(kind=8) dimension(nx,ny,nz),intent(in),depend(nx,ny,nz) :: prs - real(kind=8) dimension(nxp1,ny),intent(in),depend(nxp1,ny) :: msfu - real(kind=8) dimension(nx,nyp1),intent(in),depend(nx,nyp1) :: msfv - real(kind=8) dimension(nx,ny),intent(in),depend(nx,ny) :: msft - real(kind=8) dimension(nx,ny),intent(in),depend(nx,ny) :: cor - real(kind=8) :: dx - real(kind=8) :: dy - integer, optional,intent(in),check(shape(pv,0)==nx),depend(pv) :: nx=shape(pv,0) - integer, optional,intent(in),check(shape(pv,1)==ny),depend(pv) :: ny=shape(pv,1) - integer, optional,intent(in),check(shape(pv,2)==nz),depend(pv) :: nz=shape(pv,2) - integer, optional,intent(in),check(shape(u,0)==nxp1),depend(u) :: nxp1=shape(u,0) - integer, optional,intent(in),check(shape(v,1)==nyp1),depend(v) :: nyp1=shape(v,1) - end subroutine dcomputepv - subroutine dcomputepw(p,tv,qv,ht,pw,nx,ny,nz,nzh) ! in :_wrffortran:wrf_pw.f90 - threadsafe - use wrf_constants, only: rd - real(kind=8) dimension(nx,ny,nz),intent(in) :: p - real(kind=8) dimension(nx,ny,nz),intent(in),depend(nx,ny,nz) :: tv - real(kind=8) dimension(nx,ny,nz),intent(in),depend(nx,ny,nz) :: qv - real(kind=8) dimension(nx,ny,nzh),intent(in),depend(nx,ny) :: ht - real(kind=8) dimension(nx,ny),intent(out,in),depend(nx,ny) :: pw - integer, optional,intent(in),check(shape(p,0)==nx),depend(p) :: nx=shape(p,0) - integer, optional,intent(in),check(shape(p,1)==ny),depend(p) :: ny=shape(p,1) - integer, optional,intent(in),check(shape(p,2)==nz),depend(p) :: nz=shape(p,2) - integer, optional,intent(in),check(shape(ht,2)==nzh),depend(ht) :: nzh=shape(ht,2) - end subroutine dcomputepw - subroutine dcalrelhl(u,v,ght,ter,top,sreh,miy,mjx,mkzh) ! in :_wrffortran:wrf_relhl.f90 - threadsafe - use wrf_constants, only: rad_per_deg,deg_per_rad,pi - real(kind=8) dimension(miy,mjx,mkzh),intent(in) :: u - real(kind=8) dimension(miy,mjx,mkzh),intent(in),depend(miy,mjx,mkzh) :: v - real(kind=8) dimension(miy,mjx,mkzh),intent(in),depend(miy,mjx,mkzh) :: ght - real(kind=8) dimension(miy,mjx),intent(in),depend(miy,mjx) :: ter - real(kind=8) intent(in) :: top - real(kind=8) dimension(miy,mjx),intent(out,in),depend(miy,mjx) :: sreh - integer, optional,intent(in),check(shape(u,0)==miy),depend(u) :: miy=shape(u,0) - integer, optional,intent(in),check(shape(u,1)==mjx),depend(u) :: mjx=shape(u,1) - integer, optional,intent(in),check(shape(u,2)==mkzh),depend(u) :: mkzh=shape(u,2) - end subroutine dcalrelhl - subroutine wetbulbcalc(prs,tmk,qvp,twb,nx,ny,nz,psafile,errstat,errmsg) ! in :_wrffortran:wrf_rip_phys_routines.f90 - threadsafe - use wrf_constants, only: tlclc2,tlclc3,tlclc1,tlclc4,eps,thtecon3,gammamd,thtecon1,algerr,gamma,thtecon2 - real(kind=8) dimension(nx,ny,nz),intent(in) :: prs - real(kind=8) dimension(nx,ny,nz),intent(in),depend(nx,ny,nz) :: tmk - real(kind=8) dimension(nx,ny,nz),intent(in),depend(nx,ny,nz) :: qvp - real(kind=8) dimension(nx,ny,nz),intent(out,in),depend(nx,ny,nz) :: twb - integer, optional,intent(in),check(shape(prs,0)==nx),depend(prs) :: nx=shape(prs,0) - integer, optional,intent(in),check(shape(prs,1)==ny),depend(prs) :: ny=shape(prs,1) - integer, optional,intent(in),check(shape(prs,2)==nz),depend(prs) :: nz=shape(prs,2) - character*(*) intent(in) :: psafile - integer intent(inout) :: errstat - character*(*) intent(inout) :: errmsg - end subroutine wetbulbcalc - subroutine omgcalc(qvp,tmk,www,prs,omg,mx,my,mz) ! in :_wrffortran:wrf_rip_phys_routines.f90 - threadsafe - use wrf_constants, only: rd,eps,g - real(kind=8) dimension(mx,my,mz),intent(in) :: qvp - real(kind=8) dimension(mx,my,mz),intent(in),depend(mx,my,mz) :: tmk - real(kind=8) dimension(mx,my,mz),intent(in),depend(mx,my,mz) :: www - real(kind=8) dimension(mx,my,mz),intent(in),depend(mx,my,mz) :: prs - real(kind=8) dimension(mx,my,mz),intent(out,in),depend(mx,my,mz) :: omg - integer, optional,intent(in),check(shape(qvp,0)==mx),depend(qvp) :: mx=shape(qvp,0) - integer, optional,intent(in),check(shape(qvp,1)==my),depend(qvp) :: my=shape(qvp,1) - integer, optional,intent(in),check(shape(qvp,2)==mz),depend(qvp) :: mz=shape(qvp,2) - end subroutine omgcalc - subroutine virtual_temp(temp,ratmix,tv,nx,ny,nz) ! in :_wrffortran:wrf_rip_phys_routines.f90 - threadsafe - use wrf_constants, only: eps - real(kind=8) dimension(nx,ny,nz),intent(in) :: temp - real(kind=8) dimension(nx,ny,nz),intent(in),depend(nx,ny,nz) :: ratmix - real(kind=8) dimension(nx,ny,nz),intent(out,in),depend(nx,ny,nz) :: tv - integer, optional,intent(in),check(shape(temp,0)==nx),depend(temp) :: nx=shape(temp,0) - integer, optional,intent(in),check(shape(temp,1)==ny),depend(temp) :: ny=shape(temp,1) - integer, optional,intent(in),check(shape(temp,2)==nz),depend(temp) :: nz=shape(temp,2) - end subroutine virtual_temp - subroutine testfunc(a,b,c,nx,ny,nz,errstat,errmsg) ! in :_wrffortran:wrf_testfunc.f90 - use wrf_constants, only: algerr - real(kind=8) dimension(nx,ny,nz),intent(in) :: a - real(kind=8) dimension(nx,ny,nz),intent(out,in),depend(nx,ny,nz) :: b - character*(*) intent(in) :: c - integer, optional,intent(in),check(shape(a,0)==nx),depend(a) :: nx=shape(a,0) - integer, optional,intent(in),check(shape(a,1)==ny),depend(a) :: ny=shape(a,1) - integer, optional,intent(in),check(shape(a,2)==nz),depend(a) :: nz=shape(a,2) - integer intent(inout) :: errstat - character*(*) intent(inout) :: errmsg - end subroutine testfunc - subroutine dcomputepi(pi,pressure,nx,ny,nz) ! in :_wrffortran:wrf_user.f90 - threadsafe - use wrf_constants, only: rd,p1000mb,cp - real(kind=8) dimension(nx,ny,nz),intent(out,in) :: pi - real(kind=8) dimension(nx,ny,nz),intent(in),depend(nx,ny,nz) :: pressure - integer, optional,intent(in),check(shape(pi,0)==nx),depend(pi) :: nx=shape(pi,0) - integer, optional,intent(in),check(shape(pi,1)==ny),depend(pi) :: ny=shape(pi,1) - integer, optional,intent(in),check(shape(pi,2)==nz),depend(pi) :: nz=shape(pi,2) - end subroutine dcomputepi - subroutine dcomputetk(tk,pressure,theta,nx) ! in :_wrffortran:wrf_user.f90 - threadsafe - use wrf_constants, only: rd,p1000mb,cp - real(kind=8) dimension(nx),intent(out,in) :: tk - real(kind=8) dimension(nx),intent(in),depend(nx) :: pressure - real(kind=8) dimension(nx),intent(in),depend(nx) :: theta - integer, optional,intent(in),check(len(tk)>=nx),depend(tk) :: nx=len(tk) - end subroutine dcomputetk - subroutine dinterp3dz(data3d,out2d,zdata,desiredloc,nx,ny,nz,missingval) ! in :_wrffortran:wrf_user.f90 - threadsafe - real(kind=8) dimension(nx,ny,nz),intent(in) :: data3d - real(kind=8) dimension(nx,ny),intent(out,in),depend(nx,ny) :: out2d - real(kind=8) dimension(nx,ny,nz),intent(in),depend(nx,ny,nz) :: zdata - real(kind=8) intent(in) :: desiredloc - integer, optional,intent(in),check(shape(data3d,0)==nx),depend(data3d) :: nx=shape(data3d,0) - integer, optional,intent(in),check(shape(data3d,1)==ny),depend(data3d) :: ny=shape(data3d,1) - integer, optional,intent(in),check(shape(data3d,2)==nz),depend(data3d) :: nz=shape(data3d,2) - real(kind=8) intent(in) :: missingval - end subroutine dinterp3dz - subroutine dzstag(znew,nx,ny,nz,z,nxz,nyz,nzz,terrain) ! in :_wrffortran:wrf_user.f90 - threadsafe - real(kind=8) dimension(nx,ny,nz),intent(out,in) :: znew - integer, optional,intent(in),check(shape(znew,0)==nx),depend(znew) :: nx=shape(znew,0) - integer, optional,intent(in),check(shape(znew,1)==ny),depend(znew) :: ny=shape(znew,1) - integer, optional,intent(in),check(shape(znew,2)==nz),depend(znew) :: nz=shape(znew,2) - real(kind=8) dimension(nxz,nyz,nzz),intent(in) :: z - integer, optional,intent(in),check(shape(z,0)==nxz),depend(z) :: nxz=shape(z,0) - integer, optional,intent(in),check(shape(z,1)==nyz),depend(z) :: nyz=shape(z,1) - integer, optional,intent(in),check(shape(z,2)==nzz),depend(z) :: nzz=shape(z,2) - real(kind=8) dimension(nxz,nyz),intent(in),depend(nxz,nyz) :: terrain - end subroutine dzstag - subroutine dinterp2dxy(v3d,v2d,xy,nx,ny,nz,nxy) ! in :_wrffortran:wrf_user.f90 - threadsafe - real(kind=8) dimension(nx,ny,nz),intent(in) :: v3d - real(kind=8) dimension(nxy,nz),intent(out,in),depend(nz) :: v2d - real(kind=8) dimension(2,nxy),intent(in),depend(nxy) :: xy - integer, optional,intent(in),check(shape(v3d,0)==nx),depend(v3d) :: nx=shape(v3d,0) - integer, optional,intent(in),check(shape(v3d,1)==ny),depend(v3d) :: ny=shape(v3d,1) - integer, optional,intent(in),check(shape(v3d,2)==nz),depend(v3d) :: nz=shape(v3d,2) - integer, optional,intent(in),check(shape(v2d,0)==nxy),depend(v2d) :: nxy=shape(v2d,0) - end subroutine dinterp2dxy - subroutine dinterp1d(v_in,v_out,z_in,z_out,vmsg,nz_in,nz_out) ! in :_wrffortran:wrf_user.f90 - threadsafe - real(kind=8) dimension(nz_in),intent(in) :: v_in - real(kind=8) dimension(nz_out),intent(out,in) :: v_out - real(kind=8) dimension(nz_in),intent(in),depend(nz_in) :: z_in - real(kind=8) dimension(nz_out),intent(in),depend(nz_out) :: z_out - real(kind=8) intent(in) :: vmsg - integer, optional,intent(in),check(len(v_in)>=nz_in),depend(v_in) :: nz_in=len(v_in) - integer, optional,intent(in),check(len(v_out)>=nz_out),depend(v_out) :: nz_out=len(v_out) - end subroutine dinterp1d - subroutine dcomputeseaprs(nx,ny,nz,z,t,p,q,sea_level_pressure,t_sea_level,t_surf,level,errstat,errmsg) ! in :_wrffortran:wrf_user.f90 - threadsafe - use wrf_constants, only: rd,ussalr,algerr,g - integer, optional,intent(in),check(shape(z,0)==nx),depend(z) :: nx=shape(z,0) - integer, optional,intent(in),check(shape(z,1)==ny),depend(z) :: ny=shape(z,1) - integer, optional,intent(in),check(shape(z,2)==nz),depend(z) :: nz=shape(z,2) - real(kind=8) dimension(nx,ny,nz),intent(in) :: z - real(kind=8) dimension(nx,ny,nz),intent(in),depend(nx,ny,nz) :: t - real(kind=8) dimension(nx,ny,nz),intent(in),depend(nx,ny,nz) :: p - real(kind=8) dimension(nx,ny,nz),intent(in),depend(nx,ny,nz) :: q - real(kind=8) dimension(nx,ny),intent(out,in),depend(nx,ny) :: sea_level_pressure - real(kind=8) dimension(nx,ny),intent(inout),depend(nx,ny) :: t_sea_level - real(kind=8) dimension(nx,ny),intent(inout),depend(nx,ny) :: t_surf - integer dimension(nx,ny),intent(inout),depend(nx,ny) :: level - integer intent(inout) :: errstat - character*(*) intent(inout) :: errmsg - end subroutine dcomputeseaprs - subroutine dfilter2d(a,b,nx,ny,it,missing) ! in :_wrffortran:wrf_user.f90 - threadsafe - real(kind=8) dimension(nx,ny),intent(in,out) :: a - real(kind=8) dimension(nx,ny),intent(inout),depend(nx,ny) :: b - integer, optional,intent(in),check(shape(a,0)==nx),depend(a) :: nx=shape(a,0) - integer, optional,intent(in),check(shape(a,1)==ny),depend(a) :: ny=shape(a,1) - integer intent(in) :: it - real(kind=8) intent(in) :: missing - end subroutine dfilter2d - subroutine filter2d(a,b,nx,ny,it,missing) ! in :_wrffortran:wrf_user.f90 - threadsafe - real(kind=4) dimension(nx,ny),intent(in,out) :: a - real(kind=4) dimension(nx,ny),intent(inout),depend(nx,ny) :: b - integer, optional,intent(in),check(shape(a,0)==nx),depend(a) :: nx=shape(a,0) - integer, optional,intent(in),check(shape(a,1)==ny),depend(a) :: ny=shape(a,1) - integer intent(in) :: it - real(kind=4) intent(in) :: missing - end subroutine filter2d - subroutine dcomputerh(qv,p,t,rh,nx) ! in :_wrffortran:wrf_user.f90 - threadsafe - use wrf_constants, only: rv,eps,rd,ezero,eslcon2,eslcon1,celkel - real(kind=8) dimension(nx),intent(in) :: qv - real(kind=8) dimension(nx),intent(in),depend(nx) :: p - real(kind=8) dimension(nx),intent(in),depend(nx) :: t - real(kind=8) dimension(nx),intent(out,in),depend(nx) :: rh - integer, optional,intent(in),check(len(qv)>=nx),depend(qv) :: nx=len(qv) - end subroutine dcomputerh - subroutine dgetijlatlong(lat_array,long_array,lat,longitude,ii,jj,nx,ny,imsg) ! in :_wrffortran:wrf_user.f90 - threadsafe - real(kind=8) dimension(nx,ny),intent(in) :: lat_array - real(kind=8) dimension(nx,ny),intent(in),depend(nx,ny) :: long_array - real(kind=8) :: lat - real(kind=8) :: longitude - integer intent(out,in) :: ii - integer intent(out,in) :: jj - integer, optional,intent(in),check(shape(lat_array,0)==nx),depend(lat_array) :: nx=shape(lat_array,0) - integer, optional,intent(in),check(shape(lat_array,1)==ny),depend(lat_array) :: ny=shape(lat_array,1) - integer intent(in) :: imsg - end subroutine dgetijlatlong - subroutine dcomputeuvmet(u,v,uvmet,longca,longcb,flong,flat,cen_long,cone,rpd,nx,ny,nxp1,nyp1,istag,is_msg_val,umsg,vmsg,uvmetmsg) ! in :_wrffortran:wrf_user.f90 - threadsafe - real(kind=8) dimension(nxp1,ny),intent(in) :: u - real(kind=8) dimension(nx,nyp1),intent(in) :: v - real(kind=8) dimension(nx,ny,2),intent(out,in),depend(nx,ny) :: uvmet - real(kind=8) dimension(nx,ny),intent(inout),depend(nx,ny) :: longca - real(kind=8) dimension(nx,ny),intent(inout),depend(nx,ny) :: longcb - real(kind=8) dimension(nx,ny),intent(in),depend(nx,ny) :: flong - real(kind=8) dimension(nx,ny),intent(in),depend(nx,ny) :: flat - real(kind=8) intent(in) :: cen_long - real(kind=8) intent(in) :: cone - real(kind=8) intent(in) :: rpd - integer, optional,intent(in),check(shape(v,0)==nx),depend(v) :: nx=shape(v,0) - integer, optional,intent(in),check(shape(u,1)==ny),depend(u) :: ny=shape(u,1) - integer, optional,intent(in),check(shape(u,0)==nxp1),depend(u) :: nxp1=shape(u,0) - integer, optional,intent(in),check(shape(v,1)==nyp1),depend(v) :: nyp1=shape(v,1) - integer intent(in) :: istag - logical intent(in) :: is_msg_val - real(kind=8) intent(in) :: umsg - real(kind=8) intent(in) :: vmsg - real(kind=8) intent(in) :: uvmetmsg - end subroutine dcomputeuvmet - subroutine dcomputetd(td,pressure,qv_in,nx) ! in :_wrffortran:wrf_user.f90 - threadsafe - real(kind=8) dimension(nx),intent(out,in) :: td - real(kind=8) dimension(nx),intent(in),depend(nx) :: pressure - real(kind=8) dimension(nx),intent(in),depend(nx) :: qv_in - integer, optional,intent(in),check(len(td)>=nx),depend(td) :: nx=len(td) - end subroutine dcomputetd - subroutine dcomputeiclw(iclw,pressure,qc_in,nx,ny,nz) ! in :_wrffortran:wrf_user.f90 - threadsafe - use wrf_constants, only: g - real(kind=8) dimension(nx,ny),intent(out,in) :: iclw - real(kind=8) dimension(nx,ny,nz),intent(in),depend(nx,ny) :: pressure - real(kind=8) dimension(nx,ny,nz),intent(in),depend(nx,ny,nz) :: qc_in - integer, optional,intent(in),check(shape(iclw,0)==nx),depend(iclw) :: nx=shape(iclw,0) - integer, optional,intent(in),check(shape(iclw,1)==ny),depend(iclw) :: ny=shape(iclw,1) - integer, optional,intent(in),check(shape(pressure,2)==nz),depend(pressure) :: nz=shape(pressure,2) - end subroutine dcomputeiclw - subroutine calcdbz(prs,tmk,qvp,qra,qsn,qgr,sn0,ivarint,iliqskin,dbz,nx,ny,nz) ! in :_wrffortran:wrf_user_dbz.f90 - threadsafe - use wrf_constants, only: rho_g,gamma_seven,rho_r,rd,rho_s,alpha,rhowat,pi,celkel - real(kind=8) dimension(nx,ny,nz),intent(in) :: prs - real(kind=8) dimension(nx,ny,nz),intent(in),depend(nx,ny,nz) :: tmk - real(kind=8) dimension(nx,ny,nz),intent(inout),depend(nx,ny,nz) :: qvp - real(kind=8) dimension(nx,ny,nz),intent(inout),depend(nx,ny,nz) :: qra - real(kind=8) dimension(nx,ny,nz),intent(inout),depend(nx,ny,nz) :: qsn - real(kind=8) dimension(nx,ny,nz),intent(inout),depend(nx,ny,nz) :: qgr - integer intent(in) :: sn0 - integer intent(in) :: ivarint - integer intent(in) :: iliqskin - real(kind=8) dimension(nx,ny,nz),intent(out,in),depend(nx,ny,nz) :: dbz - integer, optional,intent(in),check(shape(prs,0)==nx),depend(prs) :: nx=shape(prs,0) - integer, optional,intent(in),check(shape(prs,1)==ny),depend(prs) :: ny=shape(prs,1) - integer, optional,intent(in),check(shape(prs,2)==nz),depend(prs) :: nz=shape(prs,2) - end subroutine calcdbz - subroutine rotatecoords(ilat,ilon,olat,olon,lat_np,lon_np,lon_0,direction) ! in :_wrffortran:wrf_user_latlon_routines.f90 - threadsafe - use wrf_constants, only: rad_per_deg,deg_per_rad,pi - real(kind=8) intent(in) :: ilat - real(kind=8) intent(in) :: ilon - real(kind=8) intent(out,in) :: olat - real(kind=8) intent(out,in) :: olon - real(kind=8) intent(in) :: lat_np - real(kind=8) intent(in) :: lon_np - real(kind=8) intent(in) :: lon_0 - integer intent(in) :: direction - end subroutine rotatecoords - subroutine dlltoij(map_proj,truelat1,truelat2,stdlon,lat1,lon1,pole_lat,pole_lon,knowni,knownj,dx,dy,latinc,loninc,lat,lon,loc,errstat,errmsg) ! in :_wrffortran:wrf_user_latlon_routines.f90 - threadsafe - use wrf_constants, only: rad_per_deg,deg_per_rad,pi,algerr,wrf_earth_radius - integer intent(in) :: map_proj - real(kind=8) intent(inout) :: truelat1 - real(kind=8) intent(inout) :: truelat2 - real(kind=8) intent(in) :: stdlon - real(kind=8) intent(in) :: lat1 - real(kind=8) intent(in) :: lon1 - real(kind=8) intent(in) :: pole_lat - real(kind=8) intent(in) :: pole_lon - real(kind=8) intent(in) :: knowni - real(kind=8) intent(in) :: knownj - real(kind=8) intent(in) :: dx - real(kind=8) intent(in) :: dy - real(kind=8) intent(in) :: latinc - real(kind=8) intent(in) :: loninc - real(kind=8) intent(inout) :: lat - real(kind=8) intent(inout) :: lon - real(kind=8) dimension(2),intent(out,in) :: loc - integer intent(inout) :: errstat - character*(*) intent(inout) :: errmsg - end subroutine dlltoij - subroutine dijtoll(map_proj,truelat1,truelat2,stdlon,lat1,lon1,pole_lat,pole_lon,knowni,knownj,dx,dy,latinc,loninc,ai,aj,loc,errstat,errmsg) ! in :_wrffortran:wrf_user_latlon_routines.f90 - threadsafe - use wrf_constants, only: rad_per_deg,deg_per_rad,pi,algerr,wrf_earth_radius - integer intent(in) :: map_proj - real(kind=8) intent(inout) :: truelat1 - real(kind=8) intent(inout) :: truelat2 - real(kind=8) intent(in) :: stdlon - real(kind=8) intent(in) :: lat1 - real(kind=8) intent(in) :: lon1 - real(kind=8) intent(in) :: pole_lat - real(kind=8) intent(in) :: pole_lon - real(kind=8) intent(in) :: knowni - real(kind=8) intent(in) :: knownj - real(kind=8) intent(in) :: dx - real(kind=8) intent(in) :: dy - real(kind=8) intent(in) :: latinc - real(kind=8) intent(in) :: loninc - real(kind=8) intent(in) :: ai - real(kind=8) intent(in) :: aj - real(kind=8) dimension(2),intent(out,in) :: loc - integer intent(inout) :: errstat - character*(*) intent(inout) :: errmsg - end subroutine dijtoll - subroutine wrf_monotonic(out,in,lvprs,cor,idir,delta,ew,ns,nz,icorsw) ! in :_wrffortran:wrf_vinterp.f90 - threadsafe - real(kind=8) dimension(ew,ns,nz),intent(out,in) :: out - real(kind=8) dimension(ew,ns,nz),intent(inout),depend(ew,ns,nz) :: in - real(kind=8) dimension(ew,ns,nz),intent(in),depend(ew,ns,nz) :: lvprs - real(kind=8) dimension(ew,ns),intent(in),depend(ew,ns) :: cor - integer intent(in) :: idir - real(kind=8) intent(in) :: delta - integer, optional,intent(in),check(shape(out,0)==ew),depend(out) :: ew=shape(out,0) - integer, optional,intent(in),check(shape(out,1)==ns),depend(out) :: ns=shape(out,1) - integer, optional,intent(in),check(shape(out,2)==nz),depend(out) :: nz=shape(out,2) - integer intent(in) :: icorsw - end subroutine wrf_monotonic - function wrf_intrp_value(wvalp0,wvalp1,vlev,vcp0,vcp1,icase,errstat) ! in :_wrffortran:wrf_vinterp.f90 - threadsafe - use wrf_constants, only: sclht,algerr - real(kind=8) intent(in) :: wvalp0 - real(kind=8) intent(in) :: wvalp1 - real(kind=8) intent(in) :: vlev - real(kind=8) intent(in) :: vcp0 - real(kind=8) intent(in) :: vcp1 - integer intent(in) :: icase - integer intent(inout) :: errstat - real(kind=8) :: wrf_intrp_value - end function wrf_intrp_value - subroutine wrf_vintrp(datain,dataout,pres,tk,qvp,ght,terrain,sfp,smsfp,vcarray,interp_levels,numlevels,icase,ew,ns,nz,extrap,vcor,logp,tempout,rmsg,errstat,errmsg) ! in :_wrffortran:wrf_vinterp.f90 - threadsafe - use wrf_constants, only: tlclc2,tlclc3,sclht,tlclc4,thtecon3,eps,tlclc1,celkel,exponi,gammamd,ussalr,expon,thtecon1,algerr,gamma,thtecon2 - real(kind=8) dimension(ew,ns,nz),intent(in) :: datain - real(kind=8) dimension(ew,ns,numlevels),intent(out,in),depend(ew,ns) :: dataout - real(kind=8) dimension(ew,ns,nz),intent(in),depend(ew,ns,nz) :: pres - real(kind=8) dimension(ew,ns,nz),intent(in),depend(ew,ns,nz) :: tk - real(kind=8) dimension(ew,ns,nz),intent(in),depend(ew,ns,nz) :: qvp - real(kind=8) dimension(ew,ns,nz),intent(in),depend(ew,ns,nz) :: ght - real(kind=8) dimension(ew,ns),intent(in),depend(ew,ns) :: terrain - real(kind=8) dimension(ew,ns),intent(in),depend(ew,ns) :: sfp - real(kind=8) dimension(ew,ns),intent(in),depend(ew,ns) :: smsfp - real(kind=8) dimension(ew,ns,nz),intent(in),depend(ew,ns,nz) :: vcarray - real(kind=8) dimension(numlevels),intent(in),depend(numlevels) :: interp_levels - integer, optional,intent(in),check(shape(dataout,2)==numlevels),depend(dataout) :: numlevels=shape(dataout,2) - integer intent(in) :: icase - integer, optional,intent(in),check(shape(datain,0)==ew),depend(datain) :: ew=shape(datain,0) - integer, optional,intent(in),check(shape(datain,1)==ns),depend(datain) :: ns=shape(datain,1) - integer, optional,intent(in),check(shape(datain,2)==nz),depend(datain) :: nz=shape(datain,2) - integer intent(in) :: extrap - integer intent(in) :: vcor - integer intent(in) :: logp - real(kind=8) dimension(ew,ns),intent(inout),depend(ew,ns) :: tempout - real(kind=8) intent(in) :: rmsg - 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 - -! This file was auto-generated with f2py (version:2). -! See http://cens.ioc.ee/projects/f2py2e/ diff --git a/requirements.txt b/requirements.txt index b044c9f..67027d4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ numpy>=1.11 -wrapt>=1.10 -setuptools>=38.0 +wrapt +setuptools diff --git a/setup.py b/setup.py index de39044..3964f41 100755 --- a/setup.py +++ b/setup.py @@ -40,8 +40,7 @@ ext1 = numpy.distutils.core.Extension( "fortran/wrf_pw.f90", "fortran/wrf_vinterp.f90", "fortran/wrf_wind.f90", - "fortran/omp.f90", - "fortran/wrffortran.pyf"] + "fortran/omp.f90"] ) with open("src/wrf/version.py") as f: @@ -86,12 +85,13 @@ numpy.distutils.core.setup( classifiers = ["Development Status :: 5 - Production/Stable", "Intended Audience :: Science/Research", "Intended Audience :: Developers", - "License :: OSI Approved", + "License :: OSI Approved :: Apache Software License", "Programming Language :: Fortran", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", "Topic :: Scientific/Engineering :: Atmospheric Science", "Topic :: Software Development", "Operating System :: POSIX", @@ -100,7 +100,7 @@ numpy.distutils.core.setup( "Operating System :: Microsoft :: Windows"], name = "wrf-python", platforms = ["any"], - license = "OSI Approved", + license = "Apache License 2.0", version = __version__, packages = setuptools.find_packages("src"), ext_modules = ext_modules, diff --git a/src/wrf/computation.py b/src/wrf/computation.py index 68486b2..f75ae87 100644 --- a/src/wrf/computation.py +++ b/src/wrf/computation.py @@ -677,21 +677,38 @@ def uvmet(u, v, lat, lon, cen_long, cone, meta=True, units="m s-1"): @set_smooth_metdata() -def smooth2d(field, passes, meta=True): +def smooth2d(field, passes, cenweight=2.0, meta=True): """Return the field smoothed. - This routine does not modify the original data. + The smoothing kernel applied is: + + .. math:: + + \\frac{1}{4 + cenweight} * \\begin{bmatrix} + 0 & 1 & 0 \\\\ + 1 & cenweight & 1 \\\\ + 0 & 1 & 0 + \\end{bmatrix} + + Data values along the borders are left unchanged. This routine does not + modify the original data supplied by the *field* parameter.. + + If you need more general purpose multidimensional filtering tools, + try the :meth:`scipy.ndimage.convolve` method. Args: field (:class:`xarray.DataArray` or :class:`numpy.ndarray`): The field to smooth, which must be at least two dimensions. Missing/fill values will be ignored as long as the type is either a - :class:`numpy.ma.MaskedArray or a :class:`xarray.DataArray` with + :class:`numpy.ma.MaskedArray` or a :class:`xarray.DataArray` with a *_FillValue* attribute. passes (:obj:`int`): The number of smoothing passes. + cenweight (:obj:`float`, optional): The weight to apply to the + center of the smoothing kernel. Default is 2.0. + meta (:obj:`bool`): Set to False to disable metadata and return :class:`numpy.ndarray` instead of :class:`xarray.DataArray`. Default is True. @@ -705,9 +722,13 @@ def smooth2d(field, passes, meta=True): be either a :class:`numpy.ndarray` or a :class:`numpy.ma.MaskedArray` depending on the type for *field*. + See Also: + + :meth:`scipy.ndimage.convolve` + """ - return _smooth2d(field, passes) + return _smooth2d(field, passes, cenweight) @set_cape_alg_metadata(is2d=True, copyarg="pres_hpa") @@ -1277,7 +1298,7 @@ def dbz(pres, tkel, qv, qr, qs=None, qg=None, use_varint=False, @set_alg_metadata(2, "terrain", units="m2 s-2", description="storm relative helicity") -def srhel(u, v, height, terrain, top=3000.0, meta=True): +def srhel(u, v, height, terrain, top=3000.0, lats=None, meta=True): """Return the storm relative helicity. This function calculates storm relative helicity from WRF ARW output. @@ -1323,6 +1344,11 @@ def srhel(u, v, height, terrain, top=3000.0, meta=True): top (:obj:`float`): The height of the layer below which helicity is calculated (meters above ground level). + + lats (:class:`xarray.DataArray` or :class:`numpy.ndarray`, optional): + Array of latitudes. This is required if any (or all) of your + domain is in the southern hemisphere. If not provided, the + northern hemisphere is assumed. Default is None. meta (:obj:`bool`): Set to False to disable metadata and return :class:`numpy.ndarray` instead of @@ -1352,7 +1378,12 @@ def srhel(u, v, height, terrain, top=3000.0, meta=True): _v = np.ascontiguousarray(v[...,::-1,:,:]) _height = np.ascontiguousarray(height[...,::-1,:,:]) - return _srhel(_u, _v, _height, terrain, top) + if lats is None: + _lats = np.ones_like(terrain) + else: + _lats = lats + + return _srhel(_u, _v, _height, terrain, _lats, top) @set_alg_metadata(2, "u", refvarndims=3, units="m2 s-2", diff --git a/src/wrf/destag.py b/src/wrf/destag.py index dda151a..21d688d 100755 --- a/src/wrf/destag.py +++ b/src/wrf/destag.py @@ -57,7 +57,7 @@ def destagger(var, stagger_dim, meta=False): dim_ranges_1[stagger_dim] = slice1 dim_ranges_2[stagger_dim] = slice2 - result = .5*(var[dim_ranges_1] + var[dim_ranges_2]) + result = .5*(var[tuple(dim_ranges_1)] + var[tuple(dim_ranges_2)]) return result diff --git a/src/wrf/extension.py b/src/wrf/extension.py index d7636c5..84803ef 100755 --- a/src/wrf/extension.py +++ b/src/wrf/extension.py @@ -11,7 +11,7 @@ from wrf._wrffortran import (dcomputetk, dinterp3dz, dinterp2dxy, dinterp1d, dcomputeabsvort, dlltoij, dijtoll, deqthecalc, omgcalc, virtual_temp, wetbulbcalc, dcomputepw, wrf_monotonic, wrf_vintrp, dcomputewspd, - dcomputewdir, + dcomputewdir, dinterp3dz_2dlev, fomp_set_num_threads, fomp_get_num_threads, fomp_get_max_threads, fomp_get_thread_num, fomp_get_num_procs, fomp_in_parallel, @@ -34,7 +34,8 @@ from .decorators import (left_iteration, cast_type, from .util import combine_dims, npbytes_to_str, psafilepath from .py3compat import py3range from .specialdec import (uvmet_left_iter, cape_left_iter, - cloudfrac_left_iter, check_cape_args) + cloudfrac_left_iter, check_cape_args, + interplevel_left_iter, check_interplevel_args) class DiagnosticError(Exception): """Raised when an error occurs in a diagnostic routine.""" @@ -73,9 +74,9 @@ class DiagnosticError(Exception): # below assume that Fortran-ordered views are being used. This allows # f2py to pass the array pointers directly to the Fortran routine. -@check_args(0, 3, (3, 3, None, None)) -@left_iteration(3, 2, ref_var_idx=0, ignore_args=(2,3)) -@cast_type(arg_idxs=(0,1)) +@check_interplevel_args(is2dlev=False) +@interplevel_left_iter(is2dlev=False) +@cast_type(arg_idxs=(0,1,2)) @extract_and_transpose() def _interpz3d(field3d, z, desiredloc, missingval, outview=None): """Wrapper for dinterp3dz. @@ -83,9 +84,10 @@ def _interpz3d(field3d, z, desiredloc, missingval, outview=None): Located in wrf_user.f90. """ - if outview is None: - outview = np.empty(field3d.shape[0:2], np.float64, order="F") - + if outview is None: + outshape = field3d.shape[0:2] + desiredloc.shape + outview = np.empty(outshape, np.float64, order="F") + result = dinterp3dz(field3d, outview, z, @@ -94,6 +96,27 @@ def _interpz3d(field3d, z, desiredloc, missingval, outview=None): return result +@check_interplevel_args(is2dlev=True) +@interplevel_left_iter(is2dlev=True) +@cast_type(arg_idxs=(0,1,2)) +@extract_and_transpose() +def _interpz3d_lev2d(field3d, z, lev2d, missingval, outview=None): + """Wrapper for dinterp3dz. + + Located in wrf_user.f90. + + """ + if outview is None: + outview = np.empty(field3d.shape[0:2], np.float64, order="F") + + result = dinterp3dz_2dlev(field3d, + outview, + z, + lev2d, + missingval) + return result + + @check_args(0, 3, (3,)) @left_iteration(3, combine_dims([(0,-3),(1,-2)]), ref_var_idx=0, ignore_args=(1,)) @@ -486,11 +509,11 @@ def _wetbulb(p, tk, qv, psafile=psafilepath(), outview=None): return result -@check_args(0, 3, (3,3,3,2)) -@left_iteration(3, 2, ref_var_idx=0, ignore_args=(4,)) -@cast_type(arg_idxs=(0,1,2,3)) +@check_args(0, 3, (3,3,3,2,2)) +@left_iteration(3, 2, ref_var_idx=0, ignore_args=(5,)) +@cast_type(arg_idxs=(0,1,2,3,4)) @extract_and_transpose() -def _srhel(u, v, z, ter, top, outview=None): +def _srhel(u, v, z, ter, lats, top, outview=None): """Wrapper for dcalrelhl. Located in wrf_relhl.f90. @@ -503,6 +526,7 @@ def _srhel(u, v, z, ter, top, outview=None): v, z, ter, + lats, top, outview) @@ -799,10 +823,10 @@ def _ctt(p_hpa, tk, qice, qcld, qv, ght, ter, haveqci, fill_nocloud, @check_args(0, 2, (2,)) -@left_iteration(2, 2, ref_var_idx=0, ignore_args=(1,)) +@left_iteration(2, 2, ref_var_idx=0, ignore_args=(1,2)) @cast_type(arg_idxs=(0,)) @extract_and_transpose() -def _smooth2d(field, passes, outview=None): +def _smooth2d(field, passes, cenweight, outview=None): """Wrapper for dfilter2d. Located in wrf_user.f90. @@ -826,7 +850,8 @@ def _smooth2d(field, passes, outview=None): dfilter2d(outview, field_tmp, passes, - missing) + missing, + cenweight) return outview diff --git a/src/wrf/g_cape.py b/src/wrf/g_cape.py index 697ca0d..d2ce0e7 100755 --- a/src/wrf/g_cape.py +++ b/src/wrf/g_cape.py @@ -12,7 +12,7 @@ from .metadecorators import set_cape_metadata @set_cape_metadata(is2d=True) def get_2dcape(wrfin, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None, missing=default_fill(np.float64)): - """Return the 2d fields of MCAPE, MCIN, LCL, and LFC. + """Return the two-dimensional fields of MCAPE, MCIN, LCL, and LFC. The leftmost dimension of the returned array represents four different quantities: @@ -223,6 +223,412 @@ def get_3dcape(wrfin, timeidx=0, method="cat", return ma.masked_values(cape_cin, missing) + + +def get_cape2d_only(wrfin, timeidx=0, method="cat", squeeze=True, cache=None, + meta=True, _key=None, missing=default_fill(np.float64)): + """Return the two-dimensional field of MCAPE (Max Convective Available + Potential Energy). + + 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. + + missing (:obj:`float`): The fill value to use for the output. + Default is :data:`wrf.default_fill(np.float64)`. + + Returns: + :class:`xarray.DataArray` or :class:`numpy.ndarray`: The + 2D MCAPE field. + 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. + + """ + result = get_2dcape(wrfin, timeidx, method, squeeze, cache, + meta, _key, missing)[0,:] + + if meta: + result.attrs["description"] = "mcape" + result.attrs["units"] = "J kg-1" + + return result + + +def get_cin2d_only(wrfin, timeidx=0, method="cat", squeeze=True, cache=None, + meta=True, _key=None, missing=default_fill(np.float64)): + """Return the two-dimensional field of MCIN (Max Convective Inhibition). + + 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. + + missing (:obj:`float`): The fill value to use for the output. + Default is :data:`wrf.default_fill(np.float64)`. + + Returns: + :class:`xarray.DataArray` or :class:`numpy.ndarray`: The + 2D MCIN field. + 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. + + """ + result = get_2dcape(wrfin, timeidx, method, squeeze, cache, + meta, _key, missing)[1,:] + + if meta: + result.attrs["description"] = "mcin" + result.attrs["units"] = "J kg-1" + + return result + + +def get_lcl(wrfin, timeidx=0, method="cat", squeeze=True, cache=None, + meta=True, _key=None, missing=default_fill(np.float64)): + """Return the two-dimensional field of LCL (Lifted Condensation Level). + + 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. + + missing (:obj:`float`): The fill value to use for the output. + Default is :data:`wrf.default_fill(np.float64)`. + + Returns: + :class:`xarray.DataArray` or :class:`numpy.ndarray`: The + 2D LCL field. + 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. + + """ + result = get_2dcape(wrfin, timeidx, method, squeeze, cache, + meta, _key, missing)[2,:] + + if meta: + result.attrs["description"] = "lcl" + result.attrs["units"] = "m" + + return result + + +def get_lfc(wrfin, timeidx=0, method="cat", squeeze=True, cache=None, + meta=True, _key=None, missing=default_fill(np.float64)): + """Return the two-dimensional field of LFC (Level of Free Convection). + + 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. + + missing (:obj:`float`): The fill value to use for the output. + Default is :data:`wrf.default_fill(np.float64)`. + + Returns: + :class:`xarray.DataArray` or :class:`numpy.ndarray`: The + 2D LFC field. + 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. + + """ + result = get_2dcape(wrfin, timeidx, method, squeeze, cache, + meta, _key, missing)[3,:] + + if meta: + result.attrs["description"] = "lfc" + result.attrs["units"] = "m" + + return result + + +def get_3dcape_only(wrfin, timeidx=0, method="cat", + squeeze=True, cache=None, meta=True, + _key=None, missing=default_fill(np.float64)): + """Return the three-dimensional field of CAPE (Convective Available + Potential Energy). + + 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. + + missing (:obj:`float`): The fill value to use for the output. + Default is :data:`wrf.default_fill(np.float64)`. + + Returns: + :class:`xarray.DataArray` or :class:`numpy.ndarray`: The + 3D CAPE field. + 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. + + """ + result = get_3dcape(wrfin, timeidx, method, squeeze, cache, meta, + _key, missing)[0,:] + + if meta: + result.attrs["description"] = "cape" + result.attrs["units"] = "J kg-1" + + return result + + +def get_3dcin_only(wrfin, timeidx=0, method="cat", + squeeze=True, cache=None, meta=True, + _key=None, missing=default_fill(np.float64)): + """Return the three-dimensional field of CIN (Convective Inhibition). + + 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. + + missing (:obj:`float`): The fill value to use for the output. + Default is :data:`wrf.default_fill(np.float64)`. + + Returns: + :class:`xarray.DataArray` or :class:`numpy.ndarray`: The + 3D CIN field. + 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. + + """ + result = get_3dcape(wrfin, timeidx, method, squeeze, cache, meta, + _key, missing)[1,:] + + if meta: + result.attrs["description"] = "cin" + result.attrs["units"] = "J kg-1" + + return result \ No newline at end of file diff --git a/src/wrf/g_cloudfrac.py b/src/wrf/g_cloudfrac.py index bb9368d..49af329 100644 --- a/src/wrf/g_cloudfrac.py +++ b/src/wrf/g_cloudfrac.py @@ -161,3 +161,338 @@ def get_cloudfrac(wrfin, timeidx=0, method="cat", squeeze=True, return ma.masked_values(cfrac, missing) + +def get_low_cloudfrac(wrfin, timeidx=0, method="cat", squeeze=True, + cache=None, meta=True, _key=None, + vert_type="height_agl", low_thresh=None, mid_thresh=None, + high_thresh=None, missing=default_fill(np.float64)): + """Return the cloud fraction for the low level clouds. + + If the vertical coordinate type is 'height_agl' or 'height_msl', the + default cloud levels are defined as: + + 300 m <= low_cloud < 2000 m + 2000 m <= mid_cloud < 6000 m + 6000 m <= high_cloud + + For 'pressure', the default cloud levels are defined as: + + 97000 Pa <= low_cloud < 80000 Pa + 80000 Pa <= mid_cloud < 45000 Pa + 45000 Pa <= high_cloud + + Note that the default low cloud levels are chosen to + 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 + 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 + *low_thresh* needs to be set to values that are slightly greater than + (less than) the lowest height (pressure) values. + + 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 + mid_cloud threshold, particularly for mountainous regions. When this + happens, a fill value will be used in the output. + + 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. + + vert_type (:obj:`str`, optional): The type of vertical coordinate used + to determine cloud type thresholds. Must be 'height_agl', + 'height_msl', or 'pres'. The default is 'height_agl'. + + low_thresh (:obj:`float`, optional): The lower bound for what 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 + default is 300 m. + + mid_thresh (:obj:`float`, optional): The lower bound for what is + considered a mid level cloud. If *vert_type* is 'pres', the + default is 80000 Pa. If *vert_type* is 'height_agl' or + 'height_msl', then the default is 2000 m. + + high_thresh (:obj:`float`, optional): The lower bound for what is + considered a high level cloud. If *vert_type* is 'pres', the + default is 45000 Pa. If *vert_type* is 'height_agl' or + 'height_msl', then the default is 6000 m. + + Returns: + :class:`xarray.DataArray` or :class:`numpy.ndarray`: The + cloud fraction array for low level clouds. + 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. + + """ + result = get_cloudfrac(wrfin, timeidx, method, squeeze, + cache, meta, _key, + vert_type, low_thresh, mid_thresh, + high_thresh, missing)[0,:] + + if meta: + result.attrs["description"] = "low clouds" + + return result + + +def get_mid_cloudfrac(wrfin, timeidx=0, method="cat", squeeze=True, + cache=None, meta=True, _key=None, + vert_type="height_agl", low_thresh=None, mid_thresh=None, + high_thresh=None, missing=default_fill(np.float64)): + """Return the cloud fraction for the mid level clouds. + + If the vertical coordinate type is 'height_agl' or 'height_msl', the + default cloud levels are defined as: + + 300 m <= low_cloud < 2000 m + 2000 m <= mid_cloud < 6000 m + 6000 m <= high_cloud + + For 'pressure', the default cloud levels are defined as: + + 97000 Pa <= low_cloud < 80000 Pa + 80000 Pa <= mid_cloud < 45000 Pa + 45000 Pa <= high_cloud + + Note that the default low cloud levels are chosen to + 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 + 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 + *low_thresh* needs to be set to values that are slightly greater than + (less than) the lowest height (pressure) values. + + 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 + mid_cloud threshold, particularly for mountainous regions. When this + happens, a fill value will be used in the output. + + 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. + + vert_type (:obj:`str`, optional): The type of vertical coordinate used + to determine cloud type thresholds. Must be 'height_agl', + 'height_msl', or 'pres'. The default is 'height_agl'. + + low_thresh (:obj:`float`, optional): The lower bound for what 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 + default is 300 m. + + mid_thresh (:obj:`float`, optional): The lower bound for what is + considered a mid level cloud. If *vert_type* is 'pres', the + default is 80000 Pa. If *vert_type* is 'height_agl' or + 'height_msl', then the default is 2000 m. + + high_thresh (:obj:`float`, optional): The lower bound for what is + considered a high level cloud. If *vert_type* is 'pres', the + default is 45000 Pa. If *vert_type* is 'height_agl' or + 'height_msl', then the default is 6000 m. + + Returns: + :class:`xarray.DataArray` or :class:`numpy.ndarray`: The + cloud fraction array for mid level clouds. + 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. + + """ + result = get_cloudfrac(wrfin, timeidx, method, squeeze, + cache, meta, _key, + vert_type, low_thresh, mid_thresh, + high_thresh, missing)[1,:] + + if meta: + result.attrs["description"] = "mid clouds" + + return result + + +def get_high_cloudfrac(wrfin, timeidx=0, method="cat", squeeze=True, + cache=None, meta=True, _key=None, + vert_type="height_agl", low_thresh=None, mid_thresh=None, + high_thresh=None, missing=default_fill(np.float64)): + """Return the cloud fraction for the high level clouds. + + If the vertical coordinate type is 'height_agl' or 'height_msl', the + default cloud levels are defined as: + + 300 m <= low_cloud < 2000 m + 2000 m <= mid_cloud < 6000 m + 6000 m <= high_cloud + + For 'pressure', the default cloud levels are defined as: + + 97000 Pa <= low_cloud < 80000 Pa + 80000 Pa <= mid_cloud < 45000 Pa + 45000 Pa <= high_cloud + + Note that the default low cloud levels are chosen to + 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 + 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 + *low_thresh* needs to be set to values that are slightly greater than + (less than) the lowest height (pressure) values. + + 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 + mid_cloud threshold, particularly for mountainous regions. When this + happens, a fill value will be used in the output. + + 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. + + vert_type (:obj:`str`, optional): The type of vertical coordinate used + to determine cloud type thresholds. Must be 'height_agl', + 'height_msl', or 'pres'. The default is 'height_agl'. + + low_thresh (:obj:`float`, optional): The lower bound for what 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 + default is 300 m. + + mid_thresh (:obj:`float`, optional): The lower bound for what is + considered a mid level cloud. If *vert_type* is 'pres', the + default is 80000 Pa. If *vert_type* is 'height_agl' or + 'height_msl', then the default is 2000 m. + + high_thresh (:obj:`float`, optional): The lower bound for what is + considered a high level cloud. If *vert_type* is 'pres', the + default is 45000 Pa. If *vert_type* is 'height_agl' or + 'height_msl', then the default is 6000 m. + + Returns: + :class:`xarray.DataArray` or :class:`numpy.ndarray`: The + cloud fraction array for high level clouds. + 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. + + """ + result = get_cloudfrac(wrfin, timeidx, method, squeeze, + cache, meta, _key, + vert_type, low_thresh, mid_thresh, + high_thresh, missing)[2,:] + + if meta: + result.attrs["description"] = "high clouds" + + return result diff --git a/src/wrf/g_helicity.py b/src/wrf/g_helicity.py index 0429aef..5ae17ab 100755 --- a/src/wrf/g_helicity.py +++ b/src/wrf/g_helicity.py @@ -7,6 +7,7 @@ from .extension import _srhel, _udhel from .destag import destagger from .util import extract_vars, extract_global_attrs, either from .metadecorators import copy_and_set_metadata +from .g_latlon import get_lat @copy_and_set_metadata(copy_varname="HGT", name="srh", description="storm relative helicity", @@ -72,6 +73,9 @@ def get_srh(wrfin, timeidx=0, method="cat", squeeze=True, """ # Top can either be 3000 or 1000 (for 0-1 srh or 0-3 srh) + lats = get_lat(wrfin, timeidx, method, squeeze, + cache, meta=False, _key=_key, stagger=None) + ncvars = extract_vars(wrfin, timeidx, ("HGT", "PH", "PHB"), method, squeeze, cache, meta=False, _key=_key) @@ -101,7 +105,7 @@ def get_srh(wrfin, timeidx=0, method="cat", squeeze=True, v1 = np.ascontiguousarray(v[...,::-1,:,:]) z1 = np.ascontiguousarray(z[...,::-1,:,:]) - srh = _srhel(u1, v1, z1, ter, top) + srh = _srhel(u1, v1, z1, ter, lats, top) return srh @@ -197,7 +201,7 @@ def get_uh(wrfin, timeidx=0, method="cat", squeeze=True, meta=False, _key=_key) v = destagger(v_vars[varname], -2) - zp = ph + phb + zp = (ph + phb) / Constants.G uh = _udhel(zp, mapfct, u, v, wstag, dx, dy, bottom, top) diff --git a/src/wrf/g_latlon.py b/src/wrf/g_latlon.py index 5d2dc08..03aef1f 100755 --- a/src/wrf/g_latlon.py +++ b/src/wrf/g_latlon.py @@ -3,14 +3,17 @@ from __future__ import (absolute_import, division, print_function) from collections import OrderedDict import numpy as np -from xarray import DataArray from .util import extract_vars, get_id, get_iterable, is_mapping, to_np from .py3compat import viewkeys from .latlonutils import _lat_varname, _lon_varname, _ll_to_xy, _xy_to_ll from .metadecorators import set_latlon_metadata +from .constants import Constants, ProjectionTypes from .config import xarray_enabled +if xarray_enabled(): + from xarray import DataArray + def get_lat(wrfin, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None, @@ -365,8 +368,8 @@ def ll_to_xy(wrfin, latitude, longitude, timeidx=0, - 'v': Use the same staggered grid as the v wind component, which has a staggered south_north (y) dimension. - as_int (:obj:`bool`): Set to True to return the x,y values as - :obj:`int`, otherwise they will be returned as :obj:`float`. + as_int (:obj:`bool`): Set to False to return the x,y values as + :obj:`float`, otherwise they will be returned as :obj:`int`. Returns: :class:`xarray.DataArray` or :class:`numpy.ndarray`: The @@ -384,7 +387,128 @@ def ll_to_xy(wrfin, latitude, longitude, timeidx=0, return _ll_to_xy(latitude, longitude, _wrfin, timeidx, stagger, "cat", squeeze, None, _key, as_int, **{}) + + +def _set_defaults(projparams): + """Check projection parameters and set defaults. + + Throws an exception if projection parameters required by WPS are not + provided, along with any other parameters required by the map projection + transformation routines. + + For parameters not used by the projection type, defaults are used so that + the None values don't pass through to fortran downstream. + + Args: + + projparams (:obj:`dict`): Projection parameters dictionary. + + Returns: + :obj:`dict`: The projection parameters with default values inserted + where applicable. + + """ + _params = dict(projparams) + + map_proj = _params.get("map_proj") + # All projections require these arguments + if map_proj is None: + raise ValueError("'map_proj' cannot be None") + + if _params.get("ref_lat") is None: + raise ValueError("'ref_lat' cannot be None") + + if _params.get("ref_lon") is None: + raise ValueError("'ref_lon' cannot be None") + + if _params.get("known_x") is None: + raise ValueError("'known_x' cannot be None") + + if _params.get("known_y") is None: + raise ValueError("'known_y' cannot be None") + + if _params.get("dx") is None: + raise ValueError("'dx' cannot be None") + + # Requires truelat1,stand_lon, truelat2, dx, dy + if map_proj == ProjectionTypes.LAMBERT_CONFORMAL: + if _params.get("truelat1") is None: + raise ValueError("'truelat1' cannot be None") + + if _params.get("stand_lon") is None: + raise ValueError("'stand_lon' cannot be None") + + if _params.get("truelat2") is None: + _params["truelat2"] = _params["truelat1"] + + # Requires truelat1, stand_lon + elif map_proj == ProjectionTypes.POLAR_STEREOGRAPHIC: + if _params.get("truelat1") is None: + raise ValueError("'truelat1' cannot be None") + + if _params.get("stand_lon") is None: + raise ValueError("'stand_lon' cannot be None") + + # Requires truelat1 + elif map_proj == ProjectionTypes.MERCATOR: + if _params.get("truelat1") is None: + raise ValueError("'truelat1' cannot be None") + + if _params.get("stand_lon") is None: + _params["stand_lon"] = 0.0 + + # Requires pole_lat, pole_lon, stand_lon + elif map_proj == ProjectionTypes.LAT_LON: + if _params.get("stand_lon") is None: + raise ValueError("'stand_lon' cannot be None") + + if _params.get("dy") is None: + raise ValueError("'dy' cannot be None") + + if _params.get("pole_lat") is None: + raise ValueError("'pole_lat' cannot be None") + + if _params.get("pole_lon") is None: + raise ValueError("'pole_lon' cannot be None") + + if _params.get("latinc") is None: + _params["latinc"] = ((_params["dy"]*360.0)/2.0 / + Constants.PI/Constants.WRF_EARTH_RADIUS) + + if _params.get("loninc") is None: + _params["loninc"] = ((_params["dx"]*360.0)/2.0 / + Constants.PI/Constants.WRF_EARTH_RADIUS) + + else: + raise ValueError("invalid 'map_proj' value of {}".format(map_proj)) + # Set these to defaults if not used so that the Fortran routines + # don't crash + if _params.get("truelat1") is None: + _params["truelat1"] = 0. + + if _params.get("truelat2") is None: + _params["truelat2"] = 0. + + if _params.get("pole_lat") is None: + _params["pole_lat"] = 90.0 + + if _params.get("pole_lon") is None: + _params["pole_lon"] = 0.0 + + if _params.get("dx") is None: + _params["dx"] = 0.0 + + if _params.get("dy") is None: + _params["dy"] = 0.0 + + if _params.get("latinc") is None: + _params["latinc"] = 0. + + if _params.get("loninc") is None: + _params["loninc"] = 0. + + return _params @set_latlon_metadata(xy=True) @@ -421,19 +545,21 @@ def ll_to_xy_proj(latitude, longitude, meta=True, squeeze=True, as_int=True, return :class:`numpy.ndarray` instead of :class:`xarray.DataArray`. Default is True. - as_int (:obj:`bool`): Set to True to return the x,y values as - :obj:`int`, otherwise they will be returned as :obj:`float`. + as_int (:obj:`bool`): Set to False to return the x,y values as + :obj:`float`, otherwise they will be returned as :obj:`int`. + Default is True. map_proj (:obj:`int`): Model projection [1=Lambert Conformal, 2=Polar Stereographic, 3=Mercator, 6=Lat-Lon]. Required. - truelat1 (:obj:`float`): True latitude 1. Required for + truelat1 (:obj:`float`): Latitude of true scale 1. Required for map_proj = 1, 2, 3 (defaults to 0 otherwise). - truelat2 (:obj:`float`): True latitude 2. Optional for + truelat2 (:obj:`float`): Latitude of true scale 2. Optional for map_proj = 1 (defaults to 0 otherwise). - stand_lon (:obj:`float`): Standard longitude. Required. + stand_lon (:obj:`float`): Standard longitude. Required for *map_proj* = + 1, 2, 6 (defaults to 0 otherwise). ref_lat (:obj:`float`): A reference latitude. Required. @@ -445,25 +571,25 @@ def ll_to_xy_proj(latitude, longitude, meta=True, squeeze=True, as_int=True, known_y (:obj:`float`): The known y-coordinate associated with *ref_lat*. Required. - pole_lat (:obj:`float`): Pole latitude. Optional for - *map_proj* = 6 (defaults to 90 otherwise). + pole_lat (:obj:`float`): Pole latitude. Required for + *map_proj* = 6 (use 90 for no rotation). - pole_lon (:obj:`float`): Pole longitude. Optional for - *map_proj* = 6 (defaults to 0 otherwise). + pole_lon (:obj:`float`): Pole longitude. Required for + *map_proj* = 6 (use 0 for no rotation). dx (:obj:`float`): The x spacing in meters at the true latitude. - Required for *map_proj* = 1, 2, 3 (defaults to 0 otherwise). + Required for all map projections. dy (:obj:`float`) - The y spacing in meters at the true latitude. - Required for *map_proj* = 1, 2, 3 (defaults to 0 otherwise). + Required for *map_proj* = 6 (defaults to 0 otherwise). - latinc (:obj:`float`): Required for *map_proj* = 6. Defined as: + latinc (:obj:`float`): Optional for *map_proj* = 6. Default is: .. code-block:: python latinc = (dy*360.0)/2.0/Constants.PI/Constants.WRF_EARTH_RADIUS - loninc (:obj:`float`): Required for *map_proj* = 6. Defined as: + loninc (:obj:`float`): Optional for *map_proj* = 6. Default is: .. code-block:: python @@ -478,11 +604,13 @@ def ll_to_xy_proj(latitude, longitude, meta=True, squeeze=True, as_int=True, """ loc = locals() - projparams = {name : loc[name] for name in ("map_proj", "truelat1", + _projparams = {name : loc[name] for name in ("map_proj", "truelat1", "truelat2", "stand_lon", "ref_lat", "ref_lon", "pole_lat", "pole_lon", "known_x", "known_y", "dx", "dy", "latinc", "loninc")} + + projparams = _set_defaults(_projparams) return _ll_to_xy(latitude, longitude, None, 0, True, "cat", squeeze, None, None, as_int, **projparams) @@ -589,19 +717,17 @@ def xy_to_ll_proj(x, y, meta=True, squeeze=True, map_proj=None, truelat1=None, return :class:`numpy.ndarray` instead of :class:`xarray.DataArray`. Default is True. - as_int (:obj:`bool`): Set to True to return the x,y values as - :obj:`int`, otherwise they will be returned as :obj:`float`. - map_proj (:obj:`int`): Model projection [1=Lambert Conformal, 2=Polar Stereographic, 3=Mercator, 6=Lat-Lon]. Required. - truelat1 (:obj:`float`): True latitude 1. Required for + truelat1 (:obj:`float`): Latitude of true scale 1. Required for map_proj = 1, 2, 3 (defaults to 0 otherwise). - truelat2 (:obj:`float`): True latitude 2. Optional for + truelat2 (:obj:`float`): Latitude of true scale 2. Optional for map_proj = 1 (defaults to 0 otherwise). - stand_lon (:obj:`float`): Standard longitude. Required. + stand_lon (:obj:`float`): Standard longitude. Required for *map_proj* = + 1, 2, 6 (defaults to 0 otherwise). ref_lat (:obj:`float`): A reference latitude. Required. @@ -613,25 +739,25 @@ def xy_to_ll_proj(x, y, meta=True, squeeze=True, map_proj=None, truelat1=None, known_y (:obj:`float`): The known y-coordinate associated with *ref_lat*. Required. - pole_lat (:obj:`float`): Pole latitude. Optional for - *map_proj* = 6 (defaults to 90 otherwise). + pole_lat (:obj:`float`): Pole latitude. Required for + *map_proj* = 6 (use 90 for no rotation). - pole_lon (:obj:`float`): Pole longitude. Optional for - *map_proj* = 6 (defaults to 0 otherwise). + pole_lon (:obj:`float`): Pole longitude. Required for + *map_proj* = 6 (use 0 for no rotation). dx (:obj:`float`): The x spacing in meters at the true latitude. - Required for *map_proj* = 1, 2, 3 (defaults to 0 otherwise). + Required for all map projections. dy (:obj:`float`) - The y spacing in meters at the true latitude. - Required for *map_proj* = 1, 2, 3 (defaults to 0 otherwise). + Required for *map_proj* = 6 (defaults to 0 otherwise). - latinc (:obj:`float`): Required for *map_proj* = 6. Defined as: + latinc (:obj:`float`): Optional for *map_proj* = 6. Default is: .. code-block:: python latinc = (dy*360.0)/2.0/Constants.PI/Constants.WRF_EARTH_RADIUS - loninc (:obj:`float`): Required for *map_proj* = 6. Defined as: + loninc (:obj:`float`): Optional for *map_proj* = 6. Default is: .. code-block:: python @@ -646,11 +772,13 @@ def xy_to_ll_proj(x, y, meta=True, squeeze=True, map_proj=None, truelat1=None, be a :class:`numpy.ndarray` object with no metadata. """ loc = locals() - projparams = {name : loc[name] for name in ("map_proj", "truelat1", + _projparams = {name : loc[name] for name in ("map_proj", "truelat1", "truelat2", "stand_lon", "ref_lat", "ref_lon", "pole_lat", "pole_lon", "known_x", "known_y", "dx", "dy", "latinc", "loninc")} + + projparams = _set_defaults(_projparams) return _xy_to_ll(x, y, None, 0, None, "cat", squeeze, None, None, **projparams) diff --git a/src/wrf/g_uvmet.py b/src/wrf/g_uvmet.py index e1d7108..dc1d0e0 100755 --- a/src/wrf/g_uvmet.py +++ b/src/wrf/g_uvmet.py @@ -520,7 +520,287 @@ def get_uvmet10_wspd_wdir(wrfin, timeidx=0, method="cat", squeeze=True, return _calc_wspd_wdir(uvmet10[0,...,:,:], uvmet10[1,...,:,:], True, units) +def get_uvmet_wspd(wrfin, timeidx=0, method="cat", squeeze=True, + cache=None, meta=True, _key=None, + units="m s-1"): + """Return the wind speed for the wind rotated to earth coordinates. + + This function should be used when comparing with observations. + + This function 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 'uvmet_wspd_wdir'. + Default is 'm s-1'. + + Returns: + + :class:`xarray.DataArray` or :class:`numpy.ndarray`: The wind speed + for the wind rotated to earth coordinates. 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. + + """ + result = get_uvmet_wspd_wdir(wrfin, timeidx, method, squeeze, + cache, meta, _key, units)[0,:] + + if meta: + result.attrs["description"] = "earth rotated wspd" + + return result + + +def get_uvmet_wdir(wrfin, timeidx=0, method="cat", squeeze=True, + cache=None, meta=True, _key=None, + units="m s-1"): + """Return the wind direction for the wind rotated to earth coordinates. + + This function should be used when comparing with observations. + + This function 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 'uvmet_wspd_wdir'. + Default is 'm s-1'. + + Returns: + + :class:`xarray.DataArray` or :class:`numpy.ndarray`: The wind direction + for the wind rotated to earth coordinates. 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. + + """ + result = get_uvmet_wspd_wdir(wrfin, timeidx, method, squeeze, + cache, meta, _key, units)[1,:] + + if meta: + result.attrs["description"] = "earth rotated wdir" + + return result + + +def get_uvmet10_wspd(wrfin, timeidx=0, method="cat", squeeze=True, + cache=None, meta=True, _key=None, + units="m s-1"): + """Return the wind speed for the 10m wind rotated to earth coordinates. + + This function should be used when comparing with observations. + + This function 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 'uvmet_wspd_wdir'. + Default is 'm s-1'. + + Returns: + + :class:`xarray.DataArray` or :class:`numpy.ndarray`: The wind speed + for the 10m wind rotated to earth coordinates. 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. + + """ + result = get_uvmet10_wspd_wdir(wrfin, timeidx, method, squeeze, + cache, meta, _key, units)[0,:] + if meta: + result.attrs["description"] = "10m earth rotated wspd" + + return result + + +def get_uvmet10_wdir(wrfin, timeidx=0, method="cat", squeeze=True, + cache=None, meta=True, _key=None, + units="m s-1"): + """Return the wind direction for the 10m wind rotated to earth coordinates. + + This function should be used when comparing with observations. + + This function 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 'uvmet_wspd_wdir'. + Default is 'm s-1'. + + Returns: + + :class:`xarray.DataArray` or :class:`numpy.ndarray`: The wind direction + for the 10m wind rotated to earth coordinates. 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. + + """ + result = get_uvmet10_wspd_wdir(wrfin, timeidx, method, squeeze, + cache, meta, _key, units)[1,:] + + if meta: + result.attrs["description"] = "10m earth rotated wdir" + + return result \ No newline at end of file diff --git a/src/wrf/g_wind.py b/src/wrf/g_wind.py index 40f6fec..9edaeea 100755 --- a/src/wrf/g_wind.py +++ b/src/wrf/g_wind.py @@ -332,7 +332,7 @@ def get_destag_wspd_wdir(wrfin, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None, units="m s-1"): """Return the wind speed and wind direction for the wind in the projected - coordinate space. + coordinate (i.e. grid) space. The wind speed and direction from this function will be relative to the grid. This function should not be used to compare with observations, @@ -423,7 +423,7 @@ def get_destag_wspd_wdir10(wrfin, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None, units="m s-1"): """Return the wind speed and wind direction for the 10m wind in - projected coordinate space. + projected coordinate (i.e. grid) space. The wind speed and direction from this function will be relative to the grid. This function should not be used to compare with observations, @@ -507,3 +507,281 @@ def get_destag_wspd_wdir10(wrfin, timeidx=0, method="cat", return _calc_wspd_wdir(u, v, True, units) + +def get_destag_wspd(wrfin, timeidx=0, method="cat", + squeeze=True, cache=None, meta=True, _key=None, + units="m s-1"): + """Return the wind speed in the projected coordinate (i.e. grid) space. + + This function 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 'wspd_wdir'. + Default is 'm s-1'. + + Returns: + + :class:`xarray.DataArray` or :class:`numpy.ndarray`: The wind + speed in projected space. 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. + + """ + result = get_destag_wspd_wdir(wrfin, timeidx, method, squeeze, cache, + meta, _key, units)[0,:] + + if meta: + result.attrs["description"] = "wspd in projection space" + + return result + + +def get_destag_wdir(wrfin, timeidx=0, method="cat", + squeeze=True, cache=None, meta=True, _key=None, + units="m s-1"): + """Return the wind direction in the projected coordinate (i.e. grid) space. + + This function 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 'wspd_wdir'. + Default is 'm s-1'. + + Returns: + + :class:`xarray.DataArray` or :class:`numpy.ndarray`: The wind + direction in projected space. 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. + + """ + result = get_destag_wspd_wdir(wrfin, timeidx, method, squeeze, cache, + meta, _key, units)[1,:] + + if meta: + result.attrs["description"] = "wdir in projection space" + + return result + + +def get_destag_wspd10(wrfin, timeidx=0, method="cat", + squeeze=True, cache=None, meta=True, _key=None, + units="m s-1"): + """Return the wind speed for the 10m wind in projected coordinate + (i.e. grid) space. + + This function 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 + 'wspd_wdir10'. Default is 'm s-1'. + + Returns: + + :class:`xarray.DataArray` or :class:`numpy.ndarray`: The wind speed + for the 10m wind in projected space. 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. + + """ + result = get_destag_wspd_wdir10(wrfin, timeidx, method, + squeeze, cache, meta, _key, units)[0,:] + + if meta: + result.attrs["description"] = "10m wspd in projection space" + + return result + + +def get_destag_wdir10(wrfin, timeidx=0, method="cat", + squeeze=True, cache=None, meta=True, _key=None, + units="m s-1"): + """Return the wind direction for the 10m wind in projected coordinate + (i.e. grid) space. + + This function 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 + 'wspd_wdir10'. Default is 'm s-1'. + + Returns: + + :class:`xarray.DataArray` or :class:`numpy.ndarray`: The wind direction + for the 10m wind in projected space. 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. + + """ + result = get_destag_wspd_wdir10(wrfin, timeidx, method, + squeeze, cache, meta, _key, units)[1,:] + + if meta: + result.attrs["description"] = "10m wdir in projection space" + + return result + diff --git a/src/wrf/interp.py b/src/wrf/interp.py index 7c92179..d875fd1 100755 --- a/src/wrf/interp.py +++ b/src/wrf/interp.py @@ -4,10 +4,11 @@ import numpy as np import numpy.ma as ma from .extension import (_interpz3d, _vertcross, _interpline, _smooth2d, - _monotonic, _vintrp) + _monotonic, _vintrp, _interpz3d_lev2d) from .metadecorators import set_interp_metadata -from .util import extract_vars, is_staggered, get_id, to_np, get_iterable +from .util import (extract_vars, is_staggered, get_id, to_np, get_iterable, + is_moving_domain, is_latlon_pair) from .py3compat import py3range from .interputils import get_xy, get_xy_z_params, to_xy_coords from .constants import Constants, default_fill, ConversionFactors @@ -20,7 +21,7 @@ from wrf.g_pressure import get_pressure # Note: Extension decorator is good enough to handle left dims @set_interp_metadata("horiz") def interplevel(field3d, vert, desiredlev, missing=default_fill(np.float64), - meta=True): + squeeze=True, meta=True): """Return the three-dimensional field interpolated to a horizontal plane at the specified vertical level. @@ -35,11 +36,20 @@ def interplevel(field3d, vert, desiredlev, missing=default_fill(np.float64), pressure or height. This array must have the same dimensionality as *field3d*. - desiredlev (:obj:`float`): The desired vertical level. + desiredlev (:obj:`float`, 1D sequence, or :class:`numpy.ndarray`): The + desired vertical level(s). This can be a single value (e.g. 500), + a sequence of values (e.g. [1000, 850, 700, 500, 250]), or a + multidimensional array where the right two dimensions (ny x nx) + must match *field3d*, and any leftmost dimensions match + field3d.shape[:-3] (e.g. planetary boundary layer). Must be in the same units as the *vert* parameter. missing (:obj:`float`): The fill value to use for the output. Default is :data:`wrf.default_fill(numpy.float64)`. + + 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. meta (:obj:`bool`): Set to False to disable metadata and return :class:`numpy.ndarray` instead of @@ -66,26 +76,34 @@ def interplevel(field3d, vert, desiredlev, missing=default_fill(np.float64), p = getvar(wrfin, "pressure") ht = getvar(wrfin, "z", units="dm") + pblh = getvar(wrfin, "PBLH") ht_500 = interplevel(ht, p, 500.0) + + """ - # Some fields like uvmet have an extra left dimension for the product - # type, we'll handle that iteration here. - multi = True if field3d.ndim - vert.ndim == 1 else False - if not multi: - result = _interpz3d(field3d, vert, desiredlev, missing) + _desiredlev = np.asarray(desiredlev) + if _desiredlev.ndim == 0: + _desiredlev = np.array([desiredlev], np.float64) + levsare2d = False else: - outshape = field3d.shape[0:-3] + field3d.shape[-2:] - result = np.empty(outshape, dtype=field3d.dtype) - - for i in py3range(field3d.shape[0]): - result[i,:] = ( - _interpz3d(field3d[i,:], vert, desiredlev, missing)[:]) - - return ma.masked_values (result, missing) + levsare2d = _desiredlev.ndim >= 2 + + if not levsare2d: + result = _interpz3d(field3d, vert, _desiredlev, missing) + else: + result = _interpz3d_lev2d(field3d, vert, _desiredlev, missing) + + masked = ma.masked_values (result, missing) + + if not meta: + if squeeze: + return masked.squeeze() + + return masked @set_interp_metadata("cross") @@ -94,7 +112,7 @@ def vertcross(field3d, vert, levels=None, missing=default_fill(np.float64), ll_point=None, pivot_point=None, angle=None, start_point=None, end_point=None, - latlon=False, cache=None, meta=True): + latlon=False, autolevels=100, cache=None, meta=True): """Return the vertical cross section for a three-dimensional field. The cross section is defined by a horizontal line through the domain. @@ -213,6 +231,10 @@ def vertcross(field3d, vert, levels=None, missing=default_fill(np.float64), *latlon* is set to True. Otherwise, a warning will be issued, and the latitude and longitude information will not be present. + + autolevels(:obj:`int`, optional): The number of evenly spaced + automatically chosen vertical levels to use when *levels* + is None. Default is 100. cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) that can be used to supply pre-extracted NetCDF variables to the @@ -236,10 +258,6 @@ def vertcross(field3d, vert, levels=None, missing=default_fill(np.float64), :class:`numpy.ndarray` object with no metadata. """ - if timeidx is None: - raise ValueError("'timeidx' must be a positive or negative integer") - - # Some fields like uvmet have an extra left dimension for the product # type, we'll handle that iteration here. multi = True if field3d.ndim - vert.ndim == 1 else False @@ -254,9 +272,45 @@ def vertcross(field3d, vert, levels=None, missing=default_fill(np.float64), end_point_xy = None pivot_point_xy = None + if (latlon is True or is_latlon_pair(start_point) or + is_latlon_pair(pivot_point)): + + if wrfin is not None: + is_moving = is_moving_domain(wrfin) + else: + is_moving = False + + if timeidx is None: + if wrfin is not None: + # Moving nests aren't supported with ALL_TIMES because the + # domain could move outside of the line, which causes + # crashes or different line lengths. + if is_moving: + raise ValueError("Requesting all times with a moving nest " + "is not supported when using lat/lon " + "cross sections because the domain could " + "move outside of the cross section. " + "You must request each time " + "individually.") + else: + # Domain not moving, just use 0 + _timeidx = 0 + + # If using grid coordinates, then don't care about lat/lon + # coordinates. Just use 0. + else: + _timeidx = 0 + else: + if is_moving: + _timeidx = timeidx + else: + # When using non-moving nests, set the time to 0 + # to avoid problems downstream + _timeidx = 0 + if pivot_point is not None: if pivot_point.lat is not None and pivot_point.lon is not None: - xy_coords = to_xy_coords(pivot_point, wrfin, timeidx, + xy_coords = to_xy_coords(pivot_point, wrfin, _timeidx, stagger, projection, ll_point) pivot_point_xy = (xy_coords.x, xy_coords.y) else: @@ -264,14 +318,14 @@ def vertcross(field3d, vert, levels=None, missing=default_fill(np.float64), if start_point is not None and end_point is not None: if start_point.lat is not None and start_point.lon is not None: - xy_coords = to_xy_coords(start_point, wrfin, timeidx, + xy_coords = to_xy_coords(start_point, wrfin, _timeidx, stagger, projection, ll_point) start_point_xy = (xy_coords.x, xy_coords.y) else: start_point_xy = (start_point.x, start_point.y) if end_point.lat is not None and end_point.lon is not None: - xy_coords = to_xy_coords(end_point, wrfin, timeidx, + xy_coords = to_xy_coords(end_point, wrfin, _timeidx, stagger, projection, ll_point) end_point_xy = (xy_coords.x, xy_coords.y) else: @@ -279,7 +333,8 @@ def vertcross(field3d, vert, levels=None, missing=default_fill(np.float64), xy, var2dz, z_var2d = get_xy_z_params(to_np(vert), pivot_point_xy, angle, start_point_xy, - end_point_xy, levels) + end_point_xy, levels, + autolevels) if not multi: result = _vertcross(field3d, xy, var2dz, z_var2d, missing) @@ -295,10 +350,9 @@ def vertcross(field3d, vert, levels=None, missing=default_fill(np.float64), @set_interp_metadata("line") -def interpline(field2d, pivot_point=None, - wrfin=None, timeidx=0, stagger=None, projection=None, +def interpline(field2d, wrfin=None, timeidx=0, stagger=None, projection=None, ll_point=None, - angle=None, start_point=None, + pivot_point=None, angle=None, start_point=None, end_point=None, latlon=False, cache=None, meta=True): """Return the two-dimensional field interpolated along a line. @@ -420,8 +474,6 @@ def interpline(field2d, pivot_point=None, """ - if timeidx is None: - raise ValueError("'timeidx' must be a positive or negative integer") try: xy = cache["xy"] @@ -430,9 +482,45 @@ def interpline(field2d, pivot_point=None, end_point_xy = None pivot_point_xy = None + if (latlon is True or is_latlon_pair(start_point) or + is_latlon_pair(pivot_point)): + + if wrfin is not None: + is_moving = is_moving_domain(wrfin) + else: + is_moving = False + + if timeidx is None: + if wrfin is not None: + # Moving nests aren't supported with ALL_TIMES because the + # domain could move outside of the line, which causes + # crashes or different line lengths. + if is_moving: + raise ValueError("Requesting all times with a moving nest " + "is not supported when using a lat/lon " + "line because the domain could " + "move outside of line. " + "You must request each time " + "individually.") + else: + # Domain not moving, just use 0 + _timeidx = 0 + + # If using grid coordinates, then don't care about lat/lon + # coordinates. Just use 0. + else: + _timeidx = 0 + else: + if is_moving: + _timeidx = timeidx + else: + # When using non-moving nests, set the time to 0 + # to avoid problems downstream + _timeidx = 0 + if pivot_point is not None: if pivot_point.lat is not None and pivot_point.lon is not None: - xy_coords = to_xy_coords(pivot_point, wrfin, timeidx, + xy_coords = to_xy_coords(pivot_point, wrfin, _timeidx, stagger, projection, ll_point) pivot_point_xy = (xy_coords.x, xy_coords.y) else: @@ -440,22 +528,22 @@ def interpline(field2d, pivot_point=None, if start_point is not None and end_point is not None: if start_point.lat is not None and start_point.lon is not None: - xy_coords = to_xy_coords(start_point, wrfin, timeidx, + xy_coords = to_xy_coords(start_point, wrfin, _timeidx, stagger, projection, ll_point) start_point_xy = (xy_coords.x, xy_coords.y) else: start_point_xy = (start_point.x, start_point.y) if end_point.lat is not None and end_point.lon is not None: - xy_coords = to_xy_coords(end_point, wrfin, timeidx, + xy_coords = to_xy_coords(end_point, wrfin, _timeidx, stagger, projection, ll_point) end_point_xy = (xy_coords.x, xy_coords.y) else: end_point_xy = (end_point.x, end_point.y) - + xy = get_xy(field2d, pivot_point_xy, angle, start_point_xy, end_point_xy) - + return _interpline(field2d, xy) @@ -488,25 +576,38 @@ def vinterp(wrfin, field, vert_coord, interp_levels, extrapolate=False, [K] interp_levels (sequence): A 1D sequence of vertical levels to - interpolate to. + interpolate to. Values must be in the same units as specified + above for the *vert_coord* parameter. extrapolate (:obj:`bool`, optional): Set to True to extrapolate - values below ground. Default is False. + values below ground. This is only performed when *vert_coord* is + a pressure or height type, and the *field_type* is a pressure type + (with height vertical coordinate), a height type (with pressure as + the vertical coordinate), or a temperature type (with either height + or pressure as the vertical coordinate). If those conditions are + not met, or *field_type* is None, then the lowest model level + will be used. Extrapolation is performed using standard atmosphere. + Default is False. field_type (:obj:`str`, optional): The type of field. Default is None. Valid strings are: * 'none': None - * 'pressure', 'pres', 'p': pressure - * 'z', 'ght': geopotential height + * 'pressure', 'pres', 'p': pressure [Pa] + * 'pressure_hpa', 'pres_hpa', 'p_hpa': pressure [hPa] + * 'z', 'ght': geopotential height [m] + * 'z_km', 'ght_km': geopotential height [km] * 'tc': temperature [degC] * 'tk': temperature [K] * 'theta', 'th': potential temperature [K] * 'theta-e', 'thetae', 'eth': equivalent potential temperature - log_p (:obj:`bool`, optional): Use the log of the pressure for - interpolation instead of pressure. Default is False. + log_p (:obj:`bool`, optional): Set to True to use the log of the + vertical coordinate for interpolation. This is mainly intended + for pressure vertical coordinate types, but note that the log + will still be taken for any vertical coordinate type when + this is set to True. Default is False. timeidx (:obj:`int`, optional): The time index to use when extracting auxiallary variables used in @@ -556,16 +657,22 @@ def vinterp(wrfin, field, vert_coord, interp_levels, extrapolate=False, valid_coords = ("pressure", "pres", "p", "ght_msl", "ght_agl", "theta", "th", "theta-e", "thetae", "eth") - valid_field_types = ("none", "pressure", "pres", "p", "z", + valid_field_types = ("none", "pressure", "pres", "p", + 'pressure_hpa', 'pres_hpa', 'p_hpa', "z", "tc", "tk", "theta", "th", "theta-e", "thetae", - "eth", "ght") + "eth", "ght", 'z_km', 'ght_km') icase_lookup = {"none" : 0, "p" : 1, "pres" : 1, "pressure" : 1, + "p_hpa" : 1, + "pres_hpa" : 1, + "pressure_hpa" : 1, "z" : 2, "ght" : 2, + "z_km" : 2, + "ght_km" : 2, "tc" : 3, "tk" : 4, "theta" : 5, @@ -574,6 +681,22 @@ def vinterp(wrfin, field, vert_coord, interp_levels, extrapolate=False, "thetae" : 6, "eth" : 6} + in_unitmap = {"p_hpa" : 1.0/ConversionFactors.PA_TO_HPA, + "pres_hpa" : 1.0/ConversionFactors.PA_TO_HPA, + "pressure_hpa" : 1.0/ConversionFactors.PA_TO_HPA, + "z_km" : 1.0/ConversionFactors.M_TO_KM, + "ght_km" : 1.0/ConversionFactors.M_TO_KM, + + } + + out_unitmap = {"p_hpa" : ConversionFactors.PA_TO_HPA, + "pres_hpa" : ConversionFactors.PA_TO_HPA, + "pressure_hpa" : ConversionFactors.PA_TO_HPA, + "z_km" : ConversionFactors.M_TO_KM, + "ght_km" : ConversionFactors.M_TO_KM, + + } + # These constants match what's in the fortran code. rgas = Constants.RD ussalr = Constants.USSALR @@ -583,18 +706,21 @@ def vinterp(wrfin, field, vert_coord, interp_levels, extrapolate=False, if not isinstance(interp_levels, np.ndarray): interp_levels = np.asarray(interp_levels, np.float64) - # TODO: Check if field is staggered + if len(interp_levels) == 0: + raise ValueError("'interp_levels' contains no values") + + # Check if field is staggered if is_staggered(_wrfin, field): - raise RuntimeError("Please unstagger field in the vertical") + raise ValueError("Please unstagger field in the vertical") # Check for valid coord if vert_coord not in valid_coords: - raise RuntimeError("'%s' is not a valid vertical " + raise ValueError("'%s' is not a valid vertical " "coordinate type" % vert_coord) # Check for valid field type if field_type not in valid_field_types: - raise RuntimeError("'%s' is not a valid field type" % field_type) + raise ValueError("'%s' is not a valid field type" % field_type) log_p_int = 1 if log_p else 0 @@ -605,14 +731,12 @@ def vinterp(wrfin, field, vert_coord, interp_levels, extrapolate=False, extrap = 1 icase = icase_lookup[field_type] - # Extract vriables - #timeidx = -1 # Should this be an argument? - ncvars = extract_vars(_wrfin, timeidx, ("PSFC", "QVAPOR", "F"), + # Extract variables + ncvars = extract_vars(_wrfin, timeidx, ("PSFC", "QVAPOR"), method, squeeze, cache, meta=False, _key=_key) sfp = ncvars["PSFC"] * ConversionFactors.PA_TO_HPA qv = ncvars["QVAPOR"] - coriolis = ncvars["F"] terht = get_terrain(_wrfin, timeidx, units="m", method=method, squeeze=squeeze, cache=cache, @@ -627,7 +751,7 @@ def vinterp(wrfin, field, vert_coord, interp_levels, extrapolate=False, method=method, squeeze=squeeze, cache=cache, meta=False, _key=_key) - smsfp = _smooth2d(sfp, 3) + smsfp = _smooth2d(sfp, 3, 2.0) vcor = 0 @@ -652,6 +776,10 @@ def vinterp(wrfin, field, vert_coord, interp_levels, extrapolate=False, method=method, squeeze=squeeze, cache=cache, meta=False, _key=_key) + coriolis = extract_vars(_wrfin, timeidx, "F", + method, squeeze, cache, meta=False, + _key=_key)["F"] + vcor = 4 idir = 1 icorsw = 0 @@ -675,6 +803,10 @@ def vinterp(wrfin, field, vert_coord, interp_levels, extrapolate=False, eth = get_eth(_wrfin, timeidx, method=method, squeeze=squeeze, cache=cache, meta=False, _key=_key) + coriolis = extract_vars(_wrfin, timeidx, "F", + method, squeeze, cache, meta=False, + _key=_key)["F"] + p_hpa = p * ConversionFactors.PA_TO_HPA vcord_array = _monotonic(eth, p_hpa, coriolis, idir, delta, icorsw) @@ -693,12 +825,29 @@ def vinterp(wrfin, field, vert_coord, interp_levels, extrapolate=False, "Verify that the 'timeidx' parameter matches the " "same value used when extracting the 'field' " "variable.") - - res = _vintrp(field, p, tk, qv, ght, terht, sfp, smsfp, + + # Some field types are in different units than the Fortran routine + # expects + + conv_factor = in_unitmap.get(field_type) + + if conv_factor is not None: + field_ = field * conv_factor + else: + field_ = field + + res = _vintrp(field_, p, tk, qv, ght, terht, sfp, smsfp, vcord_array, interp_levels, icase, extrap, vcor, log_p_int, missing) - return ma.masked_values(res, missing) + conv_factor = out_unitmap.get(field_type) + + if conv_factor is not None: + res_ = res * conv_factor + else: + res_ = res + + return ma.masked_values(res_, missing) diff --git a/src/wrf/interputils.py b/src/wrf/interputils.py index 44a6ab7..8e04c86 100644 --- a/src/wrf/interputils.py +++ b/src/wrf/interputils.py @@ -156,13 +156,6 @@ def _calc_xy(xdim, ydim, pivot_point=None, angle=None, if x1 >= xdim or y1 >= ydim: raise ValueError("end_point {} is outside of domain " "with shape {}".format(end_point, (xdim, ydim))) - - # From the original NCL code, but the error above will be thrown - # instead. - if ( x1 > xdim-1 ): - x1 = xdim - 1 - if ( y1 > ydim-1): - y1 = ydim - 1 else: raise ValueError("invalid start/end or pivot/angle arguments") @@ -185,7 +178,7 @@ def _calc_xy(xdim, ydim, pivot_point=None, angle=None, def get_xy_z_params(z, pivot_point=None, angle=None, start_point=None, end_point=None, - levels=None): + levels=None, autolevels=100): """Return the cross section parameters. This function returns the xy horizontal cross section line coordinates, @@ -224,6 +217,10 @@ def get_xy_z_params(z, pivot_point=None, angle=None, vertical levels in the output array. If None, a fixed set of vertical levels is provided. Default is None. + autolevels(:obj:`int`, optional): The number of evenly spaced + automatically chosen vertical levels to use when *levels* + is None. Default is 100. + Returns: :obj:`tuple`: A tuple containing the xy horizontal cross section @@ -248,20 +245,18 @@ def get_xy_z_params(z, pivot_point=None, angle=None, if(var2dz[idx1] > var2dz[idx2]): # monotonically decreasing coordinate z_max = floor(np.amax(z)/10) * 10 # bottom value z_min = ceil(np.amin(z)/10) * 10 # top value - dz = 10 - nlevels = int((z_max - z_min)/dz) - z_var2d = np.zeros((nlevels), dtype=z.dtype) + dz = (1.0/autolevels) * (z_max - z_min) + z_var2d = np.zeros((autolevels), dtype=z.dtype) z_var2d[0] = z_max dz = -dz else: z_max = np.amax(z) z_min = 0. - dz = 0.01*z_max - nlevels = int(z_max/dz) - z_var2d = np.zeros((nlevels), dtype=z.dtype) + dz = (1.0/autolevels)*z_max + z_var2d = np.zeros((autolevels), dtype=z.dtype) z_var2d[0] = z_min - for i in py3range(1,nlevels): + for i in py3range(1,autolevels): z_var2d[i] = z_var2d[0] + i*dz else: z_var2d = np.asarray(levels, z.dtype) diff --git a/src/wrf/latlonutils.py b/src/wrf/latlonutils.py index adf27ab..505b204 100644 --- a/src/wrf/latlonutils.py +++ b/src/wrf/latlonutils.py @@ -167,8 +167,8 @@ def _get_proj_params(wrfin, timeidx, stagger, method, squeeze, cache, _key): # Only need one file and one time if the domain is not moving if not is_moving: - if is_multi_time_req(timeidx): - lat_timeidx = 0 + # Always use the 0th time for non-moving domains to avoid problems + lat_timeidx = 0 if is_multi_file(wrfin): if not is_mapping(wrfin): @@ -179,7 +179,7 @@ def _get_proj_params(wrfin, timeidx, stagger, method, squeeze, cache, _key): key = _key[first_entry] return _get_proj_params(wrfin, timeidx, stagger, method, squeeze, cache, key) - + xlat = extract_vars(wrfin, lat_timeidx, (latvar,), method, squeeze, cache, meta=False, _key=_key)[latvar] xlon = extract_vars(wrfin, lat_timeidx, (lonvar,), method, squeeze, cache, @@ -233,7 +233,6 @@ def _kwarg_proj_params(**projparams): # Sanity checks # Required args for all projections for name, var in viewitems({"MAP_PROJ" : map_proj, - "STAND_LON" : stdlon, "REF_LAT" : ref_lat, "REF_LON" : ref_lon, "KNOWN_X" : known_x, diff --git a/src/wrf/metadecorators.py b/src/wrf/metadecorators.py index 573c7fc..ea00d75 100644 --- a/src/wrf/metadecorators.py +++ b/src/wrf/metadecorators.py @@ -9,7 +9,8 @@ import numpy.ma as ma from .extension import _interpline from .util import (extract_vars, either, from_args, arg_location, is_coordvar, latlon_coordvars, to_np, - from_var, iter_left_indexes, is_mapping) + from_var, iter_left_indexes, is_mapping, + is_moving_domain, is_latlon_pair) from .coordpair import CoordPair from .py3compat import viewkeys, viewitems, py3range from .interputils import get_xy_z_params, get_xy, to_xy_coords @@ -792,16 +793,21 @@ def _set_horiz_meta(wrapped, instance, args, kwargs): """ argvars = from_args(wrapped, ("field3d", "vert", "desiredlev", - "missing"), + "missing", "squeeze"), *args, **kwargs) field3d = argvars["field3d"] z = argvars["vert"] - desiredloc = argvars["desiredlev"] + desiredlev = argvars["desiredlev"] + _desiredlev = np.asarray(desiredlev) missingval = argvars["missing"] + squeeze = argvars["squeeze"] result = wrapped(*args, **kwargs) + levsare2d = _desiredlev.ndim >= 2 + multiproduct = field3d.ndim - z.ndim == 1 + # Defaults, in case the data isn't a DataArray outname = None outdimnames = None @@ -812,34 +818,45 @@ def _set_horiz_meta(wrapped, instance, args, kwargs): vert_units = None if isinstance(z, DataArray): vert_units = z.attrs.get("units", None) - - # If we have no metadata to start with, only set the level - levelstr = ("{0} {1}".format(desiredloc, vert_units) - if vert_units is not None - else "{0}".format(desiredloc)) - - name_levelstr = ("{0}_{1}".format(desiredloc, vert_units) - if vert_units is not None - else "{0}".format(desiredloc)) + if isinstance(field3d, DataArray): outcoords = OrderedDict() outdimnames = list(field3d.dims) outcoords.update(field3d.coords) - outdimnames.remove(field3d.dims[-3]) + + del outdimnames[-3] + try: del outcoords[field3d.dims[-3]] except KeyError: pass # xarray 0.9 + + if not levsare2d: + outdimnames.insert(-2, "level") + if _desiredlev.ndim == 0: + outcoords["level"] = [desiredlev] + else: + outcoords["level"] = _desiredlev + else: + if (_desiredlev.ndim == 2): + outcoords["level"] = field3d.dims[-2:], _desiredlev[:] + else: + if multiproduct: + d = field3d.dims[1:-3] + field3d.dims[-2:] + else: + d = field3d.dims[0:-3] + field3d.dims[-2:] + outcoords["level"] = d, _desiredlev[:] + outattrs.update(field3d.attrs) - outname = "{0}_{1}".format(field3d.name, name_levelstr) + outname = "{0}_interp".format(field3d.name) else: - outname = "field3d_{0}".format(name_levelstr) - - outattrs["level"] = levelstr + outname = "field3d_interp" + outattrs["missing_value"] = missingval outattrs["_FillValue"] = missingval + outattrs["vert_units"] = vert_units for key in ("MemoryOrder", "description"): try: @@ -847,8 +864,10 @@ def _set_horiz_meta(wrapped, instance, args, kwargs): except KeyError: pass - return DataArray(result, name=outname, dims=outdimnames, - coords=outcoords, attrs=outattrs) + da = DataArray(result, name=outname, dims=outdimnames, + coords=outcoords, attrs=outattrs) + + return da.squeeze() if squeeze else da def _set_cross_meta(wrapped, instance, args, kwargs): @@ -889,7 +908,7 @@ def _set_cross_meta(wrapped, instance, args, kwargs): "latlon", "missing", "wrfin", "timeidx", "stagger", "projection", "ll_point", "pivot_point", "angle", - "start_point", "end_point", + "start_point", "end_point", "autolevels", "cache"), *args, **kwargs) @@ -907,15 +926,52 @@ def _set_cross_meta(wrapped, instance, args, kwargs): angle = argvars["angle"] start_point = argvars["start_point"] end_point = argvars["end_point"] + autolevels = argvars["autolevels"] cache = argvars["cache"] start_point_xy = None end_point_xy = None pivot_point_xy = None + if (inc_latlon is True or is_latlon_pair(start_point) or + is_latlon_pair(pivot_point)): + + if wrfin is not None: + is_moving = is_moving_domain(wrfin) + else: + is_moving = False + + if timeidx is None: + if wrfin is not None: + # Moving nests aren't supported with ALL_TIMES because the + # domain could move outside of the line, which causes + # crashes or different line lengths. + if is_moving: + raise ValueError("Requesting all times with a moving nest " + "is not supported when using lat/lon " + "cross sections because the domain could " + "move outside of the cross section. " + "You must request each time " + "individually.") + else: + # Domain not moving, just use 0 + _timeidx = 0 + + # If using grid coordinates, then don't care about lat/lon + # coordinates. Just use 0. + else: + _timeidx = 0 + else: + if is_moving: + _timeidx = timeidx + else: + # When using non-moving nests, set the time to 0 + # to avoid problems downstream + _timeidx = 0 + if pivot_point is not None: if pivot_point.lat is not None and pivot_point.lon is not None: - xy_coords = to_xy_coords(pivot_point, wrfin, timeidx, + xy_coords = to_xy_coords(pivot_point, wrfin, _timeidx, stagger, projection, ll_point) pivot_point_xy = (xy_coords.x, xy_coords.y) else: @@ -923,21 +979,21 @@ def _set_cross_meta(wrapped, instance, args, kwargs): if start_point is not None and end_point is not None: if start_point.lat is not None and start_point.lon is not None: - xy_coords = to_xy_coords(start_point, wrfin, timeidx, + xy_coords = to_xy_coords(start_point, wrfin, _timeidx, stagger, projection, ll_point) start_point_xy = (xy_coords.x, xy_coords.y) else: start_point_xy = (start_point.x, start_point.y) if end_point.lat is not None and end_point.lon is not None: - xy_coords = to_xy_coords(end_point, wrfin, timeidx, + xy_coords = to_xy_coords(end_point, wrfin, _timeidx, stagger, projection, ll_point) end_point_xy = (xy_coords.x, xy_coords.y) else: end_point_xy = (end_point.x, end_point.y) xy, var2dz, z_var2d = get_xy_z_params(to_np(z), pivot_point_xy, angle, - start_point_xy, end_point_xy, levels) + start_point_xy, end_point_xy, levels, autolevels) # Make a copy so we don't modify a user supplied cache if cache is not None: @@ -1130,10 +1186,47 @@ def _set_line_meta(wrapped, instance, args, kwargs): start_point_xy = None end_point_xy = None pivot_point_xy = None + + if (inc_latlon is True or is_latlon_pair(start_point) or + is_latlon_pair(pivot_point)): + + if wrfin is not None: + is_moving = is_moving_domain(wrfin) + else: + is_moving = False + + if timeidx is None: + if wrfin is not None: + # Moving nests aren't supported with ALL_TIMES because the + # domain could move outside of the line, which causes + # crashes or different line lengths. + if is_moving: + raise ValueError("Requesting all times with a moving nest " + "is not supported when using a lat/lon " + "line because the domain could " + "move outside of line. " + "You must request each time " + "individually.") + else: + # Domain not moving, just use 0 + _timeidx = 0 + + # If using grid coordinates, then don't care about lat/lon + # coordinates. Just use 0. + else: + _timeidx = 0 + else: + if is_moving: + _timeidx = timeidx + else: + # When using non-moving nests, set the time to 0 + # to avoid problems downstream + _timeidx = 0 + if pivot_point is not None: if pivot_point.lat is not None and pivot_point.lon is not None: - xy_coords = to_xy_coords(pivot_point, wrfin, timeidx, + xy_coords = to_xy_coords(pivot_point, wrfin, _timeidx, stagger, projection, ll_point) pivot_point_xy = (xy_coords.x, xy_coords.y) else: @@ -1142,19 +1235,20 @@ def _set_line_meta(wrapped, instance, args, kwargs): if start_point is not None and end_point is not None: if start_point.lat is not None and start_point.lon is not None: - xy_coords = to_xy_coords(start_point, wrfin, timeidx, + xy_coords = to_xy_coords(start_point, wrfin, _timeidx, stagger, projection, ll_point) start_point_xy = (xy_coords.x, xy_coords.y) else: start_point_xy = (start_point.x, start_point.y) if end_point.lat is not None and end_point.lon is not None: - xy_coords = to_xy_coords(end_point, wrfin, timeidx, + xy_coords = to_xy_coords(end_point, wrfin, _timeidx, stagger, projection, ll_point) end_point_xy = (xy_coords.x, xy_coords.y) else: end_point_xy = (end_point.x, end_point.y) + xy = get_xy(field2d, pivot_point_xy, angle, start_point_xy, end_point_xy) # Make a copy so we don't modify a user supplied cache @@ -1645,7 +1739,7 @@ def set_interp_metadata(interp_type): """ @wrapt.decorator def func_wrapper(wrapped, instance, args, kwargs): - do_meta = from_args(wrapped, ("meta",), *args, **kwargs)["meta"] + do_meta = from_args(wrapped, ("meta"), *args, **kwargs)["meta"] if do_meta is None: do_meta = True @@ -1839,11 +1933,12 @@ def set_smooth_metdata(): if not xarray_enabled() or not do_meta: return wrapped(*args, **kwargs) - argvars = from_args(wrapped, ("field", "passes"), + argvars = from_args(wrapped, ("field", "passes", "cenweight"), *args, **kwargs) field = argvars["field"] passes = argvars["passes"] + cenweight = argvars["cenweight"] result = wrapped(*args, **kwargs) @@ -1858,6 +1953,7 @@ def set_smooth_metdata(): outcoords.update(field.coords) outattrs.update(field.attrs) outattrs["passes"] = passes + outattrs["cenweight"] = cenweight if isinstance(result, ma.MaskedArray): outattrs["_FillValue"] = result.fill_value diff --git a/src/wrf/projection.py b/src/wrf/projection.py index 61fd6b5..b7cd2d7 100644 --- a/src/wrf/projection.py +++ b/src/wrf/projection.py @@ -78,25 +78,43 @@ if cartopy_enabled(): self._xlimits = tuple(xlimits) self._ylimits = tuple(limits[..., 1]) + # Compatibility with cartopy >= 0.17 + self._x_limits = self._xlimits + self._y_limits = self._ylimits + self._threshold = np.diff(self.x_limits)[0] / 720 -def _ismissing(val): - """Return True if a value is None, greater than 90.0, or less than -90. +def _ismissing(val, islat=True): + """Return True if a value is None or out of bounds. - This function is used to check for invalid latitude values. + This function is used to check for invalid latitude/longitude values. Args: val (numeric): A numeric value. + islat (:obj:`bool`): Set to False if checking for longitude values. + Returns: - :obj:`bool`: True if the value is None, greater than 90.0, or less - than -90.0. Otherwise, False is returned. + :obj:`bool`: True if the value is None, or an out of bounds value. """ - return val is None or val > 90. or val < -90. + if islat: + if val is None: + return True + + if math.fabs(val) > 90.: + return True + else: + if val is None: + return True + + if math.fabs(val) > 360.: + return True + + return False class WrfProj(object): @@ -291,7 +309,8 @@ class WrfProj(object): return (None if not cartopy_enabled() else crs.Globe(ellipse=None, semimajor_axis=Constants.WRF_EARTH_RADIUS, - semiminor_axis=Constants.WRF_EARTH_RADIUS)) + semiminor_axis=Constants.WRF_EARTH_RADIUS, + nadgrids="@null")) def cartopy_xlim(self, geobounds): """Return the x extents in projected coordinates for cartopy. @@ -592,7 +611,7 @@ class LambertConformal(WrfProj): else self.truelat2) _proj4 = ("+proj=lcc +units=meters +a={} +b={} +lat_1={} " - "+lat_2={} +lat_0={} +lon_0={}".format( + "+lat_2={} +lat_0={} +lon_0={} +nadgrids=@null".format( Constants.WRF_EARTH_RADIUS, Constants.WRF_EARTH_RADIUS, self.truelat1, @@ -635,6 +654,9 @@ class Mercator(WrfProj): if self.truelat1 == 0. or _ismissing(self.truelat1) else self.truelat1) + self._stand_lon = (0. if _ismissing(self.stand_lon, islat=False) + else self.stand_lon) + def _cf_params(self): @@ -654,7 +676,7 @@ class Mercator(WrfProj): _pyngl.mpProjection = "Mercator" _pyngl.mpDataBaseVersion = "MediumRes" _pyngl.mpCenterLatF = 0.0 - _pyngl.mpCenterLonF = self.stand_lon + _pyngl.mpCenterLonF = self._stand_lon _pyngl.mpLimitMode = "Corners" _pyngl.mpLeftCornerLonF = geobounds.bottom_left.lon @@ -673,7 +695,7 @@ class Mercator(WrfProj): return None local_kwargs = dict(projection = "merc", - lon_0 = self.stand_lon, + lon_0 = self._stand_lon, lat_0 = self.moad_cen_lat, lat_ts = self._lat_ts, llcrnrlat = geobounds.bottom_left.lat, @@ -695,12 +717,12 @@ class Mercator(WrfProj): if self._lat_ts == 0.0: _cartopy = crs.Mercator( - central_longitude = self.stand_lon, + central_longitude = self._stand_lon, globe = self._globe()) else: _cartopy = MercatorWithLatTS( - central_longitude = self.stand_lon, + central_longitude = self._stand_lon, latitude_true_scale = self._lat_ts, globe = self._globe()) @@ -710,10 +732,10 @@ class Mercator(WrfProj): def _proj4(self): _proj4 = ("+proj=merc +units=meters +a={} +b={} " - "+lon_0={} +lat_ts={}".format( + "+lon_0={} +lat_ts={} +nadgrids=@null".format( Constants.WRF_EARTH_RADIUS, Constants.WRF_EARTH_RADIUS, - self.stand_lon, + self._stand_lon, self._lat_ts)) return _proj4 @@ -824,7 +846,7 @@ class PolarStereographic(WrfProj): def _proj4(self): _proj4 = ("+proj=stere +units=meters +a={} +b={} " - "+lat0={} +lon_0={} +lat_ts={}".format( + "+lat0={} +lon_0={} +lat_ts={} +nadgrids=@null".format( Constants.WRF_EARTH_RADIUS, Constants.WRF_EARTH_RADIUS, self._hemi, @@ -918,7 +940,7 @@ class LatLon(WrfProj): return None _cartopy = crs.PlateCarree(central_longitude=self.stand_lon, - globe=self._globe()) + globe=self._globe()) return _cartopy @@ -930,7 +952,7 @@ class LatLon(WrfProj): def _proj4(self): _proj4 = ("+proj=eqc +units=meters +a={} +b={} " - "+lon_0={}".format(Constants.WRF_EARTH_RADIUS, + "+lon_0={} +nadgrids=@null".format(Constants.WRF_EARTH_RADIUS, Constants.WRF_EARTH_RADIUS, self.stand_lon)) return _proj4 diff --git a/src/wrf/routines.py b/src/wrf/routines.py index ba5569c..aef3697 100644 --- a/src/wrf/routines.py +++ b/src/wrf/routines.py @@ -2,7 +2,9 @@ from __future__ import (absolute_import, division, print_function) from .util import (get_iterable, is_standard_wrf_var, extract_vars, viewkeys, get_id) -from .g_cape import get_2dcape, get_3dcape +from .g_cape import (get_2dcape, get_3dcape, get_cape2d_only, + get_cin2d_only, get_lcl, get_lfc, get_3dcape_only, + get_3dcin_only) from .g_ctt import get_ctt from .g_dbz import get_dbz, get_max_dbz from .g_dewpoint import get_dp, get_dp_2m @@ -17,12 +19,16 @@ 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_terrain import get_terrain from .g_uvmet import (get_uvmet, get_uvmet10, get_uvmet10_wspd_wdir, - get_uvmet_wspd_wdir) + get_uvmet_wspd_wdir, get_uvmet_wspd, get_uvmet_wdir, + get_uvmet10_wspd, get_uvmet10_wdir) from .g_vorticity import get_avo, get_pvo 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_wdir10) from .g_times import get_times, get_xtimes -from .g_cloudfrac import get_cloudfrac +from .g_cloudfrac import (get_cloudfrac, get_low_cloudfrac, get_mid_cloudfrac, + get_high_cloudfrac) # func is the function to call. kargs are required arguments that should @@ -70,7 +76,25 @@ _FUNC_MAP = {"cape2d" : get_2dcape, "ctt" : get_ctt, "cloudfrac" : get_cloudfrac, "geopt_stag" : get_stag_geopt, - "zstag" : get_stag_height + "zstag" : get_stag_height, + # Diagnostics below are extracted from multi-product diagnostics + "cape2d_only" : get_cape2d_only, + "cin2d_only" : get_cin2d_only, + "lcl" : get_lcl, + "lfc" : get_lfc, + "cape3d_only" : get_3dcape_only, + "cin3d_only": get_3dcin_only, + "uvmet_wspd" : get_uvmet_wspd, + "uvmet_wdir" : get_uvmet_wdir, + "uvmet10_wspd" : get_uvmet10_wspd, + "uvmet10_wdir" : get_uvmet10_wdir, + "wspd" : get_destag_wspd, + "wdir" : get_destag_wdir, + "wspd10" : get_destag_wspd10, + "wdir10" : get_destag_wdir10, + "low_cloudfrac" : get_low_cloudfrac, + "mid_cloudfrac" : get_mid_cloudfrac, + "high_cloudfrac" : get_high_cloudfrac } _VALID_KARGS = {"cape2d" : ["missing"], @@ -118,6 +142,26 @@ _VALID_KARGS = {"cape2d" : ["missing"], "mid_thresh", "high_thresh"], "geopt_stag" : [], "zstag" : ["msl", "units"], + "cape2d_only" : ["missing"], + "cin2d_only" : ["missing"], + "lcl" : ["missing"], + "lfc" : ["missing"], + "cape3d_only" : ["missing"], + "cin3d_only": ["missing"], + "uvmet_wspd" : ["units"], + "uvmet_wdir" : ["units"], + "uvmet10_wspd" : ["units"], + "uvmet10_wdir" : ["units"], + "wspd" : ["units"], + "wdir" : ["units"], + "wspd10" : ["units"], + "wdir10" : ["units"], + "low_cloudfrac" : ["vert_type", "low_thresh", + "mid_thresh", "high_thresh"], + "mid_cloudfrac" : ["vert_type", "low_thresh", + "mid_thresh", "high_thresh"], + "high_cloudfrac" : ["vert_type", "low_thresh", + "mid_thresh", "high_thresh"], "default" : [] } @@ -140,7 +184,14 @@ _ALIASES = {"cape_2d" : "cape2d", "cfrac" : "cloudfrac", "wspd_wdir_uvmet" : "uvmet_wspd_wdir", "wspd_wdir_uvmet10" : "uvmet10_wspd_wdir", - "th" : "theta" + "th" : "theta", + "low_cfrac" : "low_cloudfrac", + "mid_cfrac" : "mid_cloudfrac", + "high_cfrac" : "high_cloudfrac", + "wspd_uvmet" : "uvmet_wspd" , + "wdir_uvmet" : "uvmet_wdir" , + "wspd_uvmet10" : "uvmet10_wspd" , + "wdir_uvmet10" : "uvmet10_wdir" , } class ArgumentError(Exception): @@ -160,8 +211,12 @@ def _undo_alias(alias): def _check_kargs(var, kargs): for arg in viewkeys(kargs): if arg not in _VALID_KARGS[var]: - raise ValueError("'%s' is an invalid keyword " - "argument for '%s'" % (arg, var)) + if var != "default": + raise ValueError("'{}' is an invalid keyword " + "argument for '{}'".format(arg, var)) + else: + raise ValueError("'{}' is an invalid keyword " + "argument".format(arg)) def getvar(wrfin, varname, timeidx=0, @@ -277,6 +332,7 @@ def getvar(wrfin, varname, timeidx=0, wrfin = get_iterable(wrfin) if is_standard_wrf_var(wrfin, varname) and varname != "Times": + _check_kargs("default", kwargs) return extract_vars(wrfin, timeidx, varname, method, squeeze, cache, meta, _key)[varname] elif varname == "Times": diff --git a/src/wrf/specialdec.py b/src/wrf/specialdec.py index 9c70eee..3845da2 100644 --- a/src/wrf/specialdec.py +++ b/src/wrf/specialdec.py @@ -5,6 +5,7 @@ import numpy as np import wrapt from .util import iter_left_indexes, to_np +from .py3compat import py3range from .config import xarray_enabled from .constants import default_fill @@ -520,6 +521,83 @@ def cloudfrac_left_iter(alg_dtype=np.float64): return func_wrapper +def interplevel_left_iter(is2dlev, alg_dtype=np.float64): + @wrapt.decorator + def func_wrapper(wrapped, instance, args, kwargs): + new_args = list(args) + new_kwargs = dict(kwargs) + + field3d = args[0] + z = args[1] + levels = args[2] + + num_left_dims = z.ndim - 3 + orig_dtype = field3d.dtype + left_dims = z.shape[0:num_left_dims] + multiproduct = True if field3d.ndim - z.ndim == 1 else False + + # No special left side iteration, build the output from the + # low, mid, high results. + if (num_left_dims == 0): + if multiproduct: + if not is2dlev: + outshape = (field3d.shape[0:-3] + levels.shape + + field3d.shape[-2:]) + else: + outshape = (field3d.shape[0:-3] + field3d.shape[-2:]) + + output = np.empty(outshape, dtype=alg_dtype) + for i in py3range(field3d.shape[0]): + new_args[0] = field3d[i,:] + new_kwargs["outview"] = output[i,:] + _ = wrapped(*new_args, **new_kwargs) + else: + output = wrapped(*args, **kwargs) + + return output + + if multiproduct: + outdims = field3d.shape[0:1] + left_dims + else: + outdims = left_dims + + extra_dims = tuple(outdims) + + if not is2dlev: + outdims += levels.shape + + outdims += z.shape[-2:] + + outview_array = np.empty(outdims, alg_dtype) + + for left_idxs in iter_left_indexes(extra_dims): + + field_out_slice_idxs = left_idxs + (slice(None),) + + if multiproduct: + z_slice_idxs = left_idxs[1:] + (slice(None),) + else: + z_slice_idxs = left_idxs + (slice(None),) + + + new_args[0] = field3d[field_out_slice_idxs] + new_args[1] = z[z_slice_idxs] + + if is2dlev: + if levels.ndim > 2: + new_args[2] = levels[z_slice_idxs] + + new_kwargs["outview"] = outview_array[field_out_slice_idxs] + + _ = wrapped(*new_args, **new_kwargs) + + output = outview_array.astype(orig_dtype) + + return output + + return func_wrapper + + def check_cape_args(): """A decorator to check that the cape_3d arguments are valid. @@ -574,3 +652,47 @@ def check_cape_args(): return func_wrapper + +def check_interplevel_args(is2dlev): + """A decorator to check that the interplevel arguments are valid. + + An exception is raised when an invalid argument is found. + + Returns: + + None + + Raises: + + :class:`ValueError`: Raised when an invalid argument is detected. + + """ + @wrapt.decorator + def func_wrapper(wrapped, instance, args, kwargs): + + field3d = args[0] + z = args[1] + levels = args[2] + + multiproduct = True if (field3d.ndim - z.ndim) == 1 else False + + if not multiproduct: + if field3d.shape != z.shape: + raise ValueError("arguments 0 and 1 must have the same shape") + else: + if field3d.shape[1:] != z.shape: + raise ValueError("argument 0 and 1 must have same rightmost " + "dimensions") + + if is2dlev: + if levels.ndim != 2: + if (levels.shape[0:-2] != z.shape[0:-3] or + levels.shape[-2:] != z.shape[-2:]): + raise ValueError("argument 1 and 2 must have " + "the same leftmost and rightmost " + "dimensions") + + return wrapped(*args, **kwargs) + + return func_wrapper + diff --git a/src/wrf/util.py b/src/wrf/util.py index be174b4..6a297d0 100644 --- a/src/wrf/util.py +++ b/src/wrf/util.py @@ -3392,7 +3392,7 @@ def latlon_coords(var, as_np=False): var (:class:`xarray.DataArray`): A variable. - to_np (:obj:`bool`): Set to True to return the coordinates as + as_np (:obj:`bool`): Set to True to return the coordinates as :class:`numpy.ndarray` objects instead of :class:`xarray.DataArray` objects. @@ -3880,13 +3880,24 @@ def pairs_to_latlon(pairs): return lats, lons - +def is_latlon_pair(pair): + """Return True if the :class:`wrf.CoordPair` is a lat/lon pair + + Args: + + pair (:class:`wrf.CoordPair`): A single :class:`wrf.CoordPair` object. + + Returns: + + :obj:`bool`: True if the pair is a lat/lon pair. + + """ + if pair is not None: + return (pair.lat is not None and pair.lon is not None) + else: + return False - - - - diff --git a/src/wrf/version.py b/src/wrf/version.py index c1afe52..1794957 100644 --- a/src/wrf/version.py +++ b/src/wrf/version.py @@ -1,2 +1,2 @@ -__version__ = "1.2.0" +__version__ = "1.3.0" diff --git a/test/ci_tests/ci_result_file.nc b/test/ci_tests/ci_result_file.nc index e15d100..de544e1 100644 Binary files a/test/ci_tests/ci_result_file.nc and b/test/ci_tests/ci_result_file.nc differ diff --git a/test/ci_tests/ci_test_file.nc b/test/ci_tests/ci_test_file.nc index b0f8945..5765888 100644 Binary files a/test/ci_tests/ci_test_file.nc and b/test/ci_tests/ci_test_file.nc differ diff --git a/test/ci_tests/make_test_file.py b/test/ci_tests/make_test_file.py index fcdfac3..0b1bfd0 100644 --- a/test/ci_tests/make_test_file.py +++ b/test/ci_tests/make_test_file.py @@ -146,17 +146,17 @@ def make_result_file(opts): for latlonmeth in LATLON_METHS: if latlonmeth == "xy": # Hardcoded values from other unit tests - lats = [-55, -60, -65] - lons = [25, 30, 35] + lats = [22.0, 25.0, 27.0] + lons = [-90.0, -87.5, -83.75] xy = ll_to_xy(infile, lats[0], lons[0]) add_to_ncfile(outfile, xy, "xy") else: # Hardcoded from other unit tests - i_s = np.asarray([10, 100, 150], int) - 1 - j_s = np.asarray([10, 100, 150], int) - 1 + x_s = np.asarray([10, 50, 90], int) + y_s = np.asarray([10, 50, 90], int) - ll = xy_to_ll(infile, i_s[0], j_s[0]) + ll = xy_to_ll(infile, x_s[0], y_s[0]) add_to_ncfile(outfile, ll, "ll") def main(opts): @@ -166,7 +166,8 @@ def main(opts): if __name__ == "__main__": DEFAULT_FILE = ("/Users/ladwig/Documents/wrf_files/" - "wrf_vortex_multi/wrfout_d02_2005-08-28_12:00:00") + "wrf_vortex_multi/moving_nest/" + "wrfout_d02_2005-08-28_12:00:00") parser = argparse.ArgumentParser(description="Generate conda test files " "for unit testing.") parser.add_argument("-f", "--filename", required=False, diff --git a/test/ci_tests/utests.py b/test/ci_tests/utests.py index a61cf5c..67affd5 100644 --- a/test/ci_tests/utests.py +++ b/test/ci_tests/utests.py @@ -193,8 +193,8 @@ def make_latlon_test(testid, wrf_in, referent, single, multi=False, repeat=3, # same whether there are multiple or single files ref_vals = refnc.variables["xy"][:] # Lats/Lons taken from NCL script, just hard-coding for now - lats = [-55, -60, -65] - lons = [25, 30, 35] + lats = [22.0, 25.0, 27.0] + lons = [-90.0, -87.5, -83.75] xy = ll_to_xy(in_wrfnc, lats[0], lons[0]) @@ -208,10 +208,10 @@ def make_latlon_test(testid, wrf_in, referent, single, multi=False, repeat=3, # 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 - i_s = np.asarray([10, 100, 150], int) - 1 - j_s = np.asarray([10, 100, 150], int) - 1 + x_s = np.asarray([10, 50, 90], int) + y_s = np.asarray([10, 50, 90], int) - ll = xy_to_ll(in_wrfnc, i_s[0], j_s[0]) + ll = xy_to_ll(in_wrfnc, x_s[0], y_s[0]) nt.assert_allclose(to_np(ll), ref_vals) diff --git a/test/ipynb/Doc_Examples.ipynb b/test/ipynb/Doc_Examples.ipynb index 81ba5bd..3c9c02f 100644 --- a/test/ipynb/Doc_Examples.ipynb +++ b/test/ipynb/Doc_Examples.ipynb @@ -108,8 +108,6 @@ "metadata": {}, "outputs": [], "source": [ - "%matplotlib inline\n", - "\n", "from __future__ import (absolute_import, division, print_function, unicode_literals)\n", "\n", "import numpy as np\n", @@ -146,11 +144,11 @@ "levels = [5 + 5*n for n in range(15)]\n", "\n", "# Make the contour plot\n", - "a = axes[0].contourf(to_np(wspd_cross))\n", + "a = axes[0].contourf(to_np(wspd_cross), cmap=get_cmap(\"jet\"))\n", "# Add the color bar\n", "fig.colorbar(a, ax=axes[0])\n", "\n", - "b = axes[1].contourf(to_np(dbz_cross), levels=levels)\n", + "b = axes[1].contourf(to_np(dbz_cross), levels=levels, cmap=get_cmap(\"jet\"))\n", "fig.colorbar(b, ax=axes[1])\n", "\n", "# Set the x-ticks to use latitude and longitude labels.\n", @@ -185,6 +183,13 @@ "plt.show()" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Cartopy Panel Plot" + ] + }, { "cell_type": "code", "execution_count": null, @@ -193,8 +198,6 @@ }, "outputs": [], "source": [ - "%matplotlib inline\n", - "\n", "from __future__ import (absolute_import, division, print_function, unicode_literals)\n", "\n", "import numpy as np\n", @@ -236,7 +239,7 @@ "cart_proj = get_cartopy(slp)\n", "\n", "# Create a figure that will have 3 subplots\n", - "fig = plt.figure(figsize=(10,7))\n", + "fig = plt.figure(figsize=(12,9))\n", "ax_ctt = fig.add_subplot(1,2,1,projection=cart_proj)\n", "ax_wspd = fig.add_subplot(2,2,2)\n", "ax_dbz = fig.add_subplot(2,2,4)\n", @@ -282,13 +285,13 @@ "wspd_contours = ax_wspd.contourf(to_np(wspd_cross), cmap=get_cmap(\"jet\"))\n", "# Add the color bar\n", "cb_wspd = fig.colorbar(wspd_contours, ax=ax_wspd)\n", - "cb_wspd.ax.tick_params(labelsize=5)\n", + "cb_wspd.ax.tick_params(labelsize=6)\n", "\n", "# Make the contour plot for dbz\n", "levels = [5 + 5*n for n in range(15)]\n", "dbz_contours = ax_dbz.contourf(to_np(dbz_cross), levels=levels, cmap=get_cmap(\"jet\"))\n", "cb_dbz = fig.colorbar(dbz_contours, ax=ax_dbz)\n", - "cb_dbz.ax.tick_params(labelsize=5)\n", + "cb_dbz.ax.tick_params(labelsize=6)\n", "\n", "# Set the x-ticks to use latitude and longitude labels\n", "coord_pairs = to_np(dbz_cross.coords[\"xy_loc\"])\n", @@ -297,25 +300,25 @@ "ax_wspd.set_xticks(x_ticks[::20])\n", "ax_wspd.set_xticklabels([], rotation=45)\n", "ax_dbz.set_xticks(x_ticks[::20])\n", - "ax_dbz.set_xticklabels(x_labels[::20], rotation=45, fontsize=4) \n", + "ax_dbz.set_xticklabels(x_labels[::20], rotation=45, fontsize=6) \n", "\n", "# Set the y-ticks to be height\n", "vert_vals = to_np(dbz_cross.coords[\"vertical\"])\n", "v_ticks = np.arange(vert_vals.shape[0])\n", "ax_wspd.set_yticks(v_ticks[::20])\n", - "ax_wspd.set_yticklabels(vert_vals[::20], fontsize=4) \n", + "ax_wspd.set_yticklabels(vert_vals[::20], fontsize=6) \n", "ax_dbz.set_yticks(v_ticks[::20])\n", - "ax_dbz.set_yticklabels(vert_vals[::20], fontsize=4) \n", + "ax_dbz.set_yticklabels(vert_vals[::20], fontsize=6) \n", "\n", "# Set the x-axis and y-axis labels\n", - "ax_dbz.set_xlabel(\"Latitude, Longitude\", fontsize=5)\n", - "ax_wspd.set_ylabel(\"Height (m)\", fontsize=5)\n", - "ax_dbz.set_ylabel(\"Height (m)\", fontsize=5)\n", + "ax_dbz.set_xlabel(\"Latitude, Longitude\", fontsize=8)\n", + "ax_wspd.set_ylabel(\"Height (m)\", fontsize=8)\n", + "ax_dbz.set_ylabel(\"Height (m)\", fontsize=8)\n", "\n", "# Add a title\n", - "ax_ctt.set_title(\"Cloud Top Temperature (degC)\", {\"fontsize\" : 7})\n", - "ax_wspd.set_title(\"Cross-Section of Wind Speed (kt)\", {\"fontsize\" : 7})\n", - "ax_dbz.set_title(\"Cross-Section of Reflectivity (dBZ)\", {\"fontsize\" : 7})\n", + "ax_ctt.set_title(\"Cloud Top Temperature (degC)\", {\"fontsize\" : 10})\n", + "ax_wspd.set_title(\"Cross-Section of Wind Speed (kt)\", {\"fontsize\" : 10})\n", + "ax_dbz.set_title(\"Cross-Section of Reflectivity (dBZ)\", {\"fontsize\" : 10})\n", "\n", "plt.savefig(\"/Users/ladwig/Documents/workspace/wrf_python/doc/source/_static/images/matthew.png\",\n", " transparent=True, bbox_inches=\"tight\")\n", @@ -330,8 +333,6 @@ }, "outputs": [], "source": [ - "%matplotlib inline\n", - "# SLP\n", "from __future__ import (absolute_import, division, print_function, unicode_literals)\n", " \n", "from netCDF4 import Dataset \n", @@ -349,7 +350,7 @@ "slp = getvar(ncfile, \"slp\")\n", "\n", "# Smooth the sea level pressure since it tends to be noisy near the mountains\n", - "smooth_slp = smooth2d(slp, 3)\n", + "smooth_slp = smooth2d(slp, 3, cenweight=4)\n", "\n", "# Get the latitude and longitude points\n", "lats, lons = latlon_coords(slp)\n", @@ -358,22 +359,22 @@ "cart_proj = get_cartopy(slp)\n", "\n", "# Create a figure\n", - "fig = plt.figure(figsize=(12,9))\n", + "fig = plt.figure(figsize=(12,6.0))\n", "# Set the GeoAxes to the projection used by WRF\n", "ax = plt.axes(projection=cart_proj)\n", "\n", "# Download and add the states and coastlines\n", "states = NaturalEarthFeature(category='cultural', scale='50m', facecolor='none',\n", " name='admin_1_states_provinces_shp')\n", - "ax.add_feature(states, linewidth=.5)\n", - "ax.coastlines('50m', linewidth=0.8)\n", + "ax.add_feature(states, linewidth=.5, edgecolor=\"black\")\n", + "ax.coastlines('50m', linewidth=0.8, zorder=3)\n", "\n", "# Make the contour outlines and filled contours for the smoothed sea level pressure.\n", "plt.contour(to_np(lons), to_np(lats), to_np(smooth_slp), 10, colors=\"black\", transform=crs.PlateCarree())\n", "plt.contourf(to_np(lons), to_np(lats), to_np(smooth_slp), 10, transform=crs.PlateCarree(), cmap=get_cmap(\"jet\"))\n", "\n", "# Add a color bar\n", - "plt.colorbar(ax=ax, shrink=.62)\n", + "plt.colorbar(ax=ax, shrink=.98)\n", "\n", "# Set the map limits. Not really necessary, but used for demonstration.\n", "ax.set_xlim(cartopy_xlim(smooth_slp))\n", @@ -388,14 +389,19 @@ " transparent=True, bbox_inches=\"tight\")" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Vertical Cross Section" + ] + }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "%matplotlib inline\n", - "\n", "from __future__ import (absolute_import, division, print_function, unicode_literals)\n", "\n", "import numpy as np\n", @@ -457,6 +463,151 @@ "plt.show()" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Vertical Cross Section with Mountains" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "from matplotlib import pyplot\n", + "from matplotlib.cm import get_cmap\n", + "from matplotlib.colors import from_levels_and_colors\n", + "from cartopy import crs\n", + "from cartopy.feature import NaturalEarthFeature, COLORS\n", + "from netCDF4 import Dataset\n", + "from wrf import (getvar, to_np, get_cartopy, latlon_coords, vertcross,\n", + " cartopy_xlim, cartopy_ylim, interpline, CoordPair)\n", + "\n", + "wrf_file = Dataset(\"/Users/ladwig/Documents/wrf_files/boise_tutorial/orig/wrfout_d01_2010-06-04_00:00:00\")\n", + "\n", + "# Define the cross section start and end points\n", + "cross_start = CoordPair(lat=43.5, lon=-116.5)\n", + "cross_end = CoordPair(lat=43.5, lon=-114)\n", + "\n", + "# Get the WRF variables\n", + "ht = getvar(wrf_file, \"z\", timeidx=-1)\n", + "ter = getvar(wrf_file, \"ter\", timeidx=-1)\n", + "dbz = getvar(wrf_file, \"dbz\", timeidx=-1)\n", + "max_dbz = getvar(wrf_file, \"mdbz\", timeidx=-1)\n", + "Z = 10**(dbz/10.) # Use linear Z for interpolation\n", + "\n", + "# Compute the vertical cross-section interpolation. Also, include the lat/lon\n", + "# points along the cross-section in the metadata by setting latlon to True.\n", + "z_cross = vertcross(Z, ht, wrfin=wrf_file, \n", + " start_point=cross_start, \n", + " end_point=cross_end,\n", + " latlon=True, meta=True)\n", + "\n", + "# Convert back to dBz after interpolation\n", + "dbz_cross = 10.0 * np.log10(z_cross)\n", + "\n", + "# Add back the attributes that xarray dropped from the operations above\n", + "dbz_cross.attrs.update(z_cross.attrs)\n", + "dbz_cross.attrs[\"description\"] = \"radar reflectivity cross section\"\n", + "dbz_cross.attrs[\"units\"] = \"dBZ\"\n", + "\n", + "# To remove the slight gap between the dbz and terrain due to contouring, the new vertical \n", + "# grid spacing, and grid staggering, let's fill in the lower grid cells with the first non-missing \n", + "# value for each column.\n", + "\n", + "# Make a copy of the z cross data. Let's use regular numpy arrays for this.\n", + "dbz_cross_filled = np.ma.copy(to_np(dbz_cross))\n", + "\n", + "# For each cross section column, find the first index with non-missing values and copy\n", + "# these to the missing elements below.\n", + "for i in range(dbz_cross_filled.shape[-1]):\n", + " column_vals = dbz_cross_filled[:,i]\n", + " # Let's find the lowest index that isn't filled. The nonzero function \n", + " # finds all unmasked values greater than 0. Since 0 is a valid value\n", + " # for dBZ, let's change that threshold to be -200 dBZ instead. \n", + " first_idx = int(np.transpose((column_vals > -200).nonzero())[0])\n", + " dbz_cross_filled[0:first_idx, i] = dbz_cross_filled[first_idx, i]\n", + " \n", + "# Get the terrain heights along the cross section line\n", + "ter_line = interpline(ter, wrfin=wrf_file, start_point=cross_start, end_point=cross_end)\n", + "\n", + "# Get the lat/lon points\n", + "lats, lons = latlon_coords(dbz)\n", + "\n", + "# Get the cartopy projection object\n", + "cart_proj = get_cartopy(dbz)\n", + "\n", + "# Create a figure that will have 2 subplots (1 row, 2 columns)\n", + "fig = pyplot.figure(figsize=(12,9))\n", + "ax_cross = pyplot.axes()\n", + "\n", + "dbz_levels = np.arange(5., 75., 5.)\n", + "\n", + "# This is the NWS color table.\n", + "dbz_rgb = np.array([[4,233,231],\n", + " [1,159,244], [3,0,244],\n", + " [2,253,2], [1,197,1],\n", + " [0,142,0], [253,248,2],\n", + " [229,188,0], [253,149,0],\n", + " [253,0,0], [212,0,0],\n", + " [188,0,0],[248,0,253],\n", + " [152,84,198]], np.float32) / 255.0\n", + " \n", + "dbz_map, dbz_norm = from_levels_and_colors(dbz_levels, dbz_rgb, extend=\"max\")\n", + "\n", + "# Make the cross section plot for dbz\n", + "dbz_levels = np.arange(5.,75.,5.)\n", + "xs = np.arange(0, dbz_cross.shape[-1], 1)\n", + "ys = to_np(dbz_cross.coords[\"vertical\"])\n", + "dbz_contours = ax_cross.contourf(xs, \n", + " ys, \n", + " to_np(dbz_cross_filled), \n", + " levels=dbz_levels,\n", + " cmap=dbz_map, \n", + " norm=dbz_norm, \n", + " extend=\"max\")\n", + "cb_dbz = fig.colorbar(dbz_contours, ax=ax_cross)\n", + "cb_dbz.ax.tick_params(labelsize=8)\n", + "\n", + "# Fill in the mountain area\n", + "ht_fill = ax_cross.fill_between(xs, 0, to_np(ter_line), facecolor=\"saddlebrown\")\n", + "\n", + "# Set the x-ticks to use latitude and longitude labels\n", + "coord_pairs = to_np(dbz_cross.coords[\"xy_loc\"])\n", + "x_ticks = np.arange(coord_pairs.shape[0])\n", + "x_labels = [pair.latlon_str() for pair in to_np(coord_pairs)]\n", + "\n", + "# Set the desired number of x ticks below\n", + "num_ticks = 5\n", + "thin = int((len(x_ticks) / num_ticks) + .5)\n", + "ax_cross.set_xticks(x_ticks[::thin])\n", + "ax_cross.set_xticklabels(x_labels[::thin], rotation=45, fontsize=8)\n", + "\n", + "# Set the x-axis and y-axis labels\n", + "ax_cross.set_xlabel(\"Latitude, Longitude\", fontsize=12)\n", + "ax_cross.set_ylabel(\"Height (m)\", fontsize=12)\n", + "\n", + "# Add a title\n", + "ax_cross.set_title(\"Cross-Section of Reflectivity (dBZ)\", {\"fontsize\" : 14})\n", + "\n", + "plt.savefig(\"/Users/ladwig/Documents/workspace/wrf_python/doc/source/_static/images/cross_mtns.png\",\n", + " transparent=True, bbox_inches=\"tight\")\n", + "\n", + "pyplot.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Wind Barbs" + ] + }, { "cell_type": "code", "execution_count": null, @@ -503,7 +654,7 @@ "# Download and add the states and coastlines\n", "states = NaturalEarthFeature(category='cultural', scale='50m', facecolor='none',\n", " name='admin_1_states_provinces_shp')\n", - "ax.add_feature(states, linewidth=0.5)\n", + "ax.add_feature(states, linewidth=0.5, edgecolor=\"black\")\n", "ax.coastlines('50m', linewidth=0.8)\n", "\n", "# Add the 500 hPa geopotential height contours\n", @@ -623,7 +774,7 @@ "slp = getvar(ncfile, \"slp\")\n", "\n", "# Smooth the sea level pressure since it tends to be noisy near the mountains\n", - "smooth_slp = smooth2d(slp, 3)\n", + "smooth_slp = smooth2d(slp, 3, cenweight=4)\n", "\n", "# Get the latitude and longitude points\n", "lats, lons = latlon_coords(slp)\n", @@ -632,7 +783,7 @@ "bm = get_basemap(slp)\n", "\n", "# Create a figure\n", - "fig = plt.figure(figsize=(12,9))\n", + "fig = plt.figure(figsize=(12,6))\n", "\n", "# Add geographic outlines\n", "bm.drawcoastlines(linewidth=0.25)\n", @@ -648,7 +799,7 @@ "bm.contourf(x, y, to_np(smooth_slp), 10, cmap=get_cmap(\"jet\"))\n", "\n", "# Add a color bar\n", - "plt.colorbar(shrink=.62)\n", + "plt.colorbar(shrink=.98)\n", "\n", "plt.title(\"Sea Level Pressure (hPa)\")\n", "\n", @@ -664,7 +815,6 @@ "metadata": {}, "outputs": [], "source": [ - "%matplotlib inline\n", "from __future__ import (absolute_import, division, print_function, unicode_literals)\n", "\n", "from netCDF4 import Dataset \n", @@ -731,14 +881,19 @@ "plt.show()" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Basemap Panel Plot" + ] + }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "%matplotlib inline\n", - "\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "from matplotlib.cm import get_cmap\n", @@ -772,7 +927,7 @@ "lats, lons = latlon_coords(slp)\n", "\n", "# Create the figure that will have 3 subplots\n", - "fig = plt.figure(figsize=(10,7))\n", + "fig = plt.figure(figsize=(12,9))\n", "ax_ctt = fig.add_subplot(1,2,1)\n", "ax_wspd = fig.add_subplot(2,2,2)\n", "ax_dbz = fig.add_subplot(2,2,4)\n", @@ -827,13 +982,13 @@ "wspd_contours = ax_wspd.contourf(to_np(wspd_cross), cmap=get_cmap(\"jet\"))\n", "# Add the color bar\n", "cb_wspd = fig.colorbar(wspd_contours, ax=ax_wspd)\n", - "cb_wspd.ax.tick_params(labelsize=5)\n", + "cb_wspd.ax.tick_params(labelsize=6)\n", "\n", "# Make the contour plot for dbz\n", "levels = [5 + 5*n for n in range(15)]\n", "dbz_contours = ax_dbz.contourf(to_np(dbz_cross), levels=levels, cmap=get_cmap(\"jet\"))\n", "cb_dbz = fig.colorbar(dbz_contours, ax=ax_dbz)\n", - "cb_dbz.ax.tick_params(labelsize=5)\n", + "cb_dbz.ax.tick_params(labelsize=6)\n", "\n", "# Set the x-ticks to use latitude and longitude labels.\n", "coord_pairs = to_np(dbz_cross.coords[\"xy_loc\"])\n", @@ -842,25 +997,25 @@ "ax_wspd.set_xticks(x_ticks[::20])\n", "ax_wspd.set_xticklabels([], rotation=45)\n", "ax_dbz.set_xticks(x_ticks[::20])\n", - "ax_dbz.set_xticklabels(x_labels[::20], rotation=45, fontsize=4) \n", + "ax_dbz.set_xticklabels(x_labels[::20], rotation=45, fontsize=6) \n", "\n", "# Set the y-ticks to be height.\n", "vert_vals = to_np(dbz_cross.coords[\"vertical\"])\n", "v_ticks = np.arange(vert_vals.shape[0])\n", "ax_wspd.set_yticks(v_ticks[::20])\n", - "ax_wspd.set_yticklabels(vert_vals[::20], fontsize=4) \n", + "ax_wspd.set_yticklabels(vert_vals[::20], fontsize=6) \n", "ax_dbz.set_yticks(v_ticks[::20])\n", - "ax_dbz.set_yticklabels(vert_vals[::20], fontsize=4) \n", + "ax_dbz.set_yticklabels(vert_vals[::20], fontsize=6) \n", "\n", "# Set the x-axis and y-axis labels\n", - "ax_dbz.set_xlabel(\"Latitude, Longitude\", fontsize=5)\n", - "ax_wspd.set_ylabel(\"Height (m)\", fontsize=5)\n", - "ax_dbz.set_ylabel(\"Height (m)\", fontsize=5)\n", + "ax_dbz.set_xlabel(\"Latitude, Longitude\", fontsize=8)\n", + "ax_wspd.set_ylabel(\"Height (m)\", fontsize=8)\n", + "ax_dbz.set_ylabel(\"Height (m)\", fontsize=8)\n", "\n", "# Add titles\n", - "ax_ctt.set_title(\"Cloud Top Temperature (degC)\", {\"fontsize\" : 7})\n", - "ax_wspd.set_title(\"Cross-Section of Wind Speed (kt)\", {\"fontsize\" : 7})\n", - "ax_dbz.set_title(\"Cross-Section of Reflectivity (dBZ)\", {\"fontsize\" : 7})\n", + "ax_ctt.set_title(\"Cloud Top Temperature (degC)\", {\"fontsize\" : 10})\n", + "ax_wspd.set_title(\"Cross-Section of Wind Speed (kt)\", {\"fontsize\" : 10})\n", + "ax_dbz.set_title(\"Cross-Section of Reflectivity (dBZ)\", {\"fontsize\" : 10})\n", "\n", "plt.savefig(\"/Users/ladwig/Documents/workspace/wrf_python/doc/source/_static/images/basemap_front.png\",\n", " transparent=False, bbox_inches=\"tight\")\n", @@ -1261,21 +1416,21 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 2", + "display_name": "Python 3", "language": "python", - "name": "python2" + "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", - "version": 2 + "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.14" + "pygments_lexer": "ipython3", + "version": "3.6.7" } }, "nbformat": 4, diff --git a/test/ipynb/WRF_python_demo.ipynb b/test/ipynb/WRF_python_demo.ipynb index 8f073d0..229c6ed 100644 --- a/test/ipynb/WRF_python_demo.ipynb +++ b/test/ipynb/WRF_python_demo.ipynb @@ -271,6 +271,182 @@ " " ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import tempfile\n", + "import glob\n", + "import shutil\n", + "import os\n", + "from netCDF4 import Dataset\n", + "from wrf import getvar, ll_to_xy, CoordPair, GeoBounds\n", + "\n", + "\n", + "class FileReduce(object):\n", + " def __init__(self, filenames, geobounds, tempdir=None, delete=True, reuse=True):\n", + " \"\"\"An iterable object for cutting out geographic domains.\n", + " \n", + " Args:\n", + " \n", + " filenames (sequence): A sequence of full paths to the WRF files\n", + " \n", + " geobounds (GeoBounds): A GeoBounds object defining the region of interest\n", + " \n", + " tempdir (str): The location to store the temporary cropped data files. If None, tempfile.mkdtemp is used.\n", + " \n", + " delete (bool): Set to True to delete the cropped files when FileReduce is garbage collected.\n", + " \n", + " reuse (bool): Set to True when you want to resuse the files that were previously converted. *tempdir* \n", + " must be set to a specific directory that contains the converted files.\n", + " \n", + " \"\"\"\n", + " self._filenames = filenames\n", + " self._i = 0\n", + " self._geobounds = geobounds\n", + " self._delete = delete\n", + " self._cache = set()\n", + " self._own_data = True\n", + " self._reuse = reuse\n", + " \n", + " if tempdir is not None:\n", + " if not os.path.exists(tempdir):\n", + " os.makedirs(tempdir)\n", + " self._tempdir = tempdir\n", + " if self._reuse:\n", + " self._cache = set((os.path.join(self._tempdir, name) \n", + " for name in os.listdir(self._tempdir)))\n", + " else:\n", + " self._tempdir = tempfile.mkdtemp()\n", + "\n", + " print (\"temporary directory is: {}\".format(self._tempdir))\n", + " self._prev = None\n", + " self._set_extents()\n", + " \n", + " def _set_extents(self):\n", + " fname = list(self._filenames)[0]\n", + " with Dataset(fname) as ncfile:\n", + " lons = [self._geobounds.bottom_left.lon, self._geobounds.top_right.lon]\n", + " lats = [self._geobounds.bottom_left.lat, self._geobounds.top_right.lat]\n", + " orig_west_east = len(ncfile.dimensions[\"west_east\"])\n", + " orig_south_north = len(ncfile.dimensions[\"south_north\"])\n", + " \n", + " \n", + " # Note: Not handling the moving nest here\n", + " # Extra points included around the boundaries to ensure domain is fully included\n", + " x_y = ll_to_xy(ncfile, lats, lons, meta=False)\n", + " self._start_x = 0 if x_y[0,0] == 0 else x_y[0,0] - 1\n", + " self._end_x = orig_west_east - 1 if x_y[0,1] >= orig_west_east - 1 else x_y[0,1] + 1\n", + " self._start_y = 0 if x_y[1,0] == 0 else x_y[1,0] - 1\n", + " self._end_y = orig_south_north if x_y[1,1] >= orig_south_north - 1 else x_y[1,1] + 1\n", + " \n", + " self._west_east = self._end_x - self._start_x + 1\n", + " self._west_east_stag = self._west_east + 1\n", + " self._south_north = self._end_y - self._start_y + 1\n", + " self._south_north_stag = self._south_north + 1\n", + " \n", + " \n", + " def __iter__(self):\n", + " return self\n", + " \n", + " def __copy__(self):\n", + " cp = type(self).__new__(self.__class__)\n", + " cp.__dict__.update(self.__dict__)\n", + " cp._own_data = False\n", + " cp._delete = False\n", + " \n", + " return cp\n", + " \n", + " def __del__(self):\n", + " if self._delete:\n", + " shutil.rmtree(self._tempdir)\n", + " \n", + " def reduce(self, fname):\n", + " outfilename = os.path.join(self._tempdir, \"reduced_\" + os.path.basename(fname))\n", + " \n", + " # WRF-Python can iterate over sequences several times during a 'getvar', so a cache is used to \n", + " if outfilename in self._cache:\n", + " return Dataset(outfilename)\n", + " \n", + " # New dimension sizes\n", + " dim_d = {\"west_east\" : self._west_east,\n", + " \"west_east_stag\" : self._west_east_stag,\n", + " \"south_north\" : self._south_north,\n", + " \"south_north_stag\" : self._south_north_stag\n", + " }\n", + " \n", + " # Data slice sizes for the 2D dimensions\n", + " slice_d = {\"west_east\" : slice(self._start_x, self._end_x + 1),\n", + " \"west_east_stag\" : slice(self._start_x, self._end_x + 2),\n", + " \"south_north\" : slice(self._start_y, self._end_y + 1),\n", + " \"south_north_stag\" : slice(self._start_y, self._end_y + 2)\n", + " }\n", + " \n", + " with Dataset(fname) as infile, Dataset(outfilename, mode=\"w\") as outfile:\n", + " print (\"reduce getting called!\")\n", + " \n", + " # Copy the global attributes\n", + " outfile.setncatts(infile.__dict__)\n", + "\n", + " # Copy Dimensions, limiting south_north and west_east to desired domain\n", + " for name, dimension in infile.dimensions.items():\n", + " dimsize = dim_d.get(name, len(dimension))\n", + " outfile.createDimension(name, dimsize)\n", + "\n", + " # Copy Variables \n", + " for name, variable in infile.variables.iteritems():\n", + " \n", + " new_slices = tuple((slice_d.get(dimname, slice(None)) for dimname in variable.dimensions))\n", + "\n", + " outvar = outfile.createVariable(name, variable.datatype, variable.dimensions)\n", + "\n", + " outvar[:] = variable[new_slices]\n", + "\n", + " outvar.setncatts(variable.__dict__)\n", + " \n", + " \n", + " result = Dataset(outfilename)\n", + " \n", + " self._cache.add(outfilename)\n", + " \n", + " return result\n", + " \n", + " \n", + " def next(self):\n", + " if self._i >= len(self._filenames):\n", + " if self._prev is not None:\n", + " self._prev.close()\n", + " raise StopIteration\n", + " else:\n", + " fname = self._filenames[self._i]\n", + " reduced_file = self.reduce(fname)\n", + " if self._prev is not None:\n", + " self._prev.close()\n", + " self._prev = reduced_file\n", + " \n", + " self._i += 1\n", + " \n", + " return reduced_file\n", + " \n", + " # Python 3\n", + " def __next__(self):\n", + " return self.next()\n", + "\n", + "ll = CoordPair(lat=24.0, lon=-87.)\n", + "ur = CoordPair(lat=27.0, lon=-84)\n", + "bounds = GeoBounds(ll, ur)\n", + "reduced_files = FileReduce(glob.glob(\"/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/moving_nest/wrfout_d02*\"),\n", + " bounds, tempdir=\"/Users/ladwig/mytemp\", delete=False, reuse=True)\n", + "\n", + "slp = getvar(reduced_files, \"slp\")\n", + "print(slp)\n", + "\n", + "\n", + "del (reduced_files)\n" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -922,7 +1098,7 @@ "from wrf import getvar, ALL_TIMES\n", "from netCDF4 import Dataset as nc\n", "\n", - "dir = \"/Users/ladwig/Documents/wrf_files/wrf_vortex_multi\"\n", + "dir = \"/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/moving_nest\"\n", "ncfilenames = [os.path.join(dir, x) for x in os.listdir(dir) if x.find(\"_d02_\") > 0]\n", "ncfiles = [nc(x) for x in ncfilenames]\n", "\n", @@ -1031,6 +1207,8 @@ "#vard = {varname: getvar(ncfiles, varname, method=\"join\", squeeze=False) for varname in wrf_vars}\n", "for varname in wrf_vars:\n", " print(vard[varname])\n", + " \n", + "# NOTE: Warnings below are due to \"join\" and the fill values used since the last file only contains 1 time step.\n", " " ] }, @@ -1044,7 +1222,7 @@ "from wrf import getvar\n", "from netCDF4 import Dataset as nc\n", "\n", - "dir = \"/Users/ladwig/Documents/wrf_files/wrf_vortex_multi\"\n", + "dir = \"/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/moving_nest\"\n", "ncfilenames = [os.path.join(dir, x) for x in os.listdir(dir) if x.find(\"_d02_\") > 0]\n", "ncfiles = [nc(x) for x in ncfilenames]\n", "\n", @@ -1081,7 +1259,7 @@ "from wrf import getvar\n", "from netCDF4 import Dataset as nc\n", "\n", - "dir = \"/Users/ladwig/Documents/wrf_files/wrf_vortex_multi\"\n", + "dir = \"/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/moving_nest\"\n", "ncfilenames = [os.path.join(dir, x) for x in os.listdir(dir) if x.find(\"_d02_\") > 0]\n", "ncfiles = [nc(x) for x in ncfilenames]\n", "\n", @@ -1112,6 +1290,13 @@ "del t2_line, t2" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, { "cell_type": "code", "execution_count": null, @@ -1165,7 +1350,7 @@ "from wrf import getvar, ALL_TIMES, geo_bounds, get_cartopy, get_basemap, get_pyngl, cartopy_xlim, cartopy_ylim\n", "from netCDF4 import Dataset as nc\n", "\n", - "wrf_filenames = glob(\"/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/wrfout_d02_*\")\n", + "wrf_filenames = glob(\"/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/moving_nest/wrfout_d02_*\")\n", "ncfiles = [nc(x) for x in wrf_filenames]\n", "\n", "slp = getvar(ncfiles, \"slp\", timeidx=ALL_TIMES)\n", @@ -1241,7 +1426,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.4" + "version": "3.6.7" } }, "nbformat": 4, diff --git a/test/ncl_get_var.ncl b/test/ncl_get_var.ncl index 25fd969..a7b8aa5 100644 --- a/test/ncl_get_var.ncl +++ b/test/ncl_get_var.ncl @@ -4,25 +4,35 @@ ;system("printenv") - if (.not. isvar("in_file")) then - in_file = "/Users/ladwig/Documents/wrf_files/wrfout_d02_2010-06-13_21:00:00.nc" + if (.not. isvar("dir")) then + dir = "/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/moving_nest" + ;in_file = "/Users/ladwig/Documents/wrf_files/wrfout_d01_2010-06-13_21:00:00.nc" + ;in_file = "/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/moving_nest/wrfout_d02_2005-08-28_00:00:00.nc" end if + if (.not. isvar("pattern")) then + pattern = "wrfout_d02_*" + end if + if (.not. isvar("out_file")) then out_file = "/tmp/wrftest.nc" end if - input_file = addfile(in_file,"r") + + pat = dir + "/" + pattern + cmd = "ls " + pat + fils = systemfunc (cmd) ; file paths + input_file = addfiles(fils,"r") system("/bin/rm -f " + out_file) ; remove if exists fout = addfile(out_file, "c") - time = 0 + time = -1 wrf_vars = [/"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", "cfrac"/] + "wa", "uvmet10", "uvmet", "z", "cfrac", "height_agl" /] unique_dimname_list = NewList("fifo") unique_dimsize_list = NewList("fifo") @@ -40,148 +50,253 @@ ; variables with unique names. This is the reason for all of the ; name attribute stuff below. do i = 0, ListCount(wrf_vars) - 1 - print("working on: " + wrf_vars[i]) v := wrf_user_getvar(input_file, wrf_vars[i], time) - - ;if (wrf_vars[i] .eq. "avo") then - ; print(v) - ;end if - - ; pw is written in pure NCL and does not contain dimension names - ; so manually creating the dimension names here - if (wrf_vars[i] .eq. "pw") then - dim_names := (/"south_north", "west_east"/) - dim_sizes := dimsizes(v) - else - dim_names := getvardims(v) - dim_sizes := dimsizes(v) - end if - - vardata@$wrf_vars[i]$ := v - vardimnamedata@$wrf_vars[i]$ := dim_names - ListAppend(vardata_list,vardata@$wrf_vars[i]$) - ListAppend(full_vardimname_list, vardimnamedata@$wrf_vars[i]$) - ;print(vardata_list) - - dimname=True - dimsize=True - - ; Determine the unique dimensions names, which will be used when - ; creating the output NetCDF file - do j=0, dimsizes(dim_sizes)-1 - ;print(dim_names) - ;print(dim_names(j)) - - name_id = sprintf("dimname_%i",i*j) - size_id = sprintf("dimsize_%i",i*j) - - dimname@$name_id$ = dim_names(j) - dimsize@$size_id$ = dim_sizes(j) - - has_name = False - do k=0, ListCount(unique_dimname_list)-1 - if ((/unique_dimname_list[k]/) .eq. (/dimname@$name_id$/)) then - has_name = True - end if - end do - - if (.not. has_name) then - ;print("inserting: " + dimname@$name_id$) - ListAppend(unique_dimname_list, dimname@$name_id$) - ListAppend(unique_dimsize_list, dimsize@$size_id$) - end if - - end do + fout->$wrf_vars[i]$ = v end do - - setfileoption(fout,"DefineMode",True) - - ; Set global attributes - f_att = True ; assign file attributes - f_att@title = "NCL generated netCDF file" - f_att@Conventions = "None" - fileattdef(fout, f_att) ; copy file attributes - - ; Set up the NetCDF dimensions - d_names = new(ListCount(unique_dimname_list), string) - d_sizes = new(ListCount(unique_dimname_list), integer) - d_unlim = new(ListCount(unique_dimname_list), logical) - - ; Note: Need to do this copy since NCL can't coerce the list data to - ; array data - do i=0, ListCount(unique_dimname_list) - 1 - d_names(i) = unique_dimname_list[i] - d_sizes(i) = unique_dimsize_list[i] - d_unlim(i) = False - end do - - filedimdef(fout, d_names, d_sizes, d_unlim) - ; Save the variables to the NetCDF file - do i=0, ListCount(vardata_list)-1 - d := vardata_list[i] - filevardef(fout, wrf_vars[i], typeof(d), full_vardimname_list[i]) - filevarattdef(fout,wrf_vars[i], d) - fout->$wrf_vars[i]$ = (/d/) - end do ; Do the interpolation routines manually - ; 3D vertical cross section - z = wrf_user_getvar(input_file, "z", 0) ; grid point height - p = wrf_user_getvar(input_file, "pressure", 0) ; total pressure + ;;;;;;;;;;;;;;;;;;; 3D vertical cross section + time = -1 + + z := wrf_user_getvar(input_file, "z", time) ; grid point height + p := wrf_user_getvar(input_file, "pressure", time) ; total pressure + dimsz = dimsizes(z) - pivot = (/ dimsz(2)/2, dimsz(1)/2 /) ; pivot point is center of domain + pivot = (/ dimsz(3)/2, dimsz(2)/2 /) ; pivot point is center of domain ht_cross = wrf_user_intrp3d(z,p,"v",pivot,90.0,False) + p_cross = wrf_user_intrp3d(p,z,"v",pivot,90.0,False) + p_cross!0 = "Vertical_p" + p_cross!1 = "Horizontal_p" + + fout->ht_cross = ht_cross + fout->p_cross = p_cross + + + time = 0 + + ; For the new cross section routine + xopt = True + xopt@use_pivot = True + xopt@angle = 90.0 + xopt@file_handle = input_file + xopt@timeidx = time + xopt@linecoords = True + + ht_vertcross1 = wrf_user_vertcross(z, p, pivot, xopt) + + fout->ht_vertcross1 = ht_vertcross1 + + ; For the new cross section routine + xopt := True + xopt@use_pivot = True + xopt@angle = 90.0 + xopt@levels = (/1000., 850., 700., 500., 250./) + xopt@file_handle = input_file + xopt@timeidx = time + xopt@linecoords = True + + ht_vertcross2 = wrf_user_vertcross(z, p, pivot, xopt) + ht_vertcross2!1 = "vertical2" + ht_vertcross2!2 = "cross_line_idx2" + + fout->ht_vertcross2 = ht_vertcross2 - ht_cross_dims = dimsizes(ht_cross) - p_cross_dims = dimsizes(p_cross) + ; Can only use a single time for lat/lon version at this time - ; 3D horizontal interpolation + vertdims = dimsizes(ht_vertcross2) + htdims = dimsizes(z) + + lats = wrf_user_getvar(input_file, "lat", 0) + lons = wrf_user_getvar(input_file, "lon", 0) + + start_lat = min(lats) + .25d*(max(lats) - min(lats)) + end_lat = min(lats) + .65d*(max(lats) - min(lats)) + + start_lon = min(lons) + .25d*(max(lons) - min(lons)) + end_lon = min(lons) + .65d*(max(lons) - min(lons)) + + start_end = (/ start_lon, start_lat, end_lon, end_lat /) + + ; For the new cross section routine + xopt := True + xopt@use_pivot = False + xopt@latlon = True + xopt@file_handle = input_file + xopt@timeidx = 0 + xopt@linecoords = True + xopt@autolevels = 1000 + + ht_vertcross3 = wrf_user_vertcross(z, p, start_end, xopt) + + ht_vertcross3!0 = "Time" + ht_vertcross3!1 = "vertical3" + ht_vertcross3!2 = "cross_line_idx3" + + fout->ht_vertcross3 = ht_vertcross3 + + ; Test the moving nest with lat/lon over time + + times = wrf_user_getvar(input_file, "times", -1) + ntimes = dimsizes(times) + + do i=0,ntimes-1 + xopt@timeidx = i + name = sprinti("ht_vertcross_t%i", i) + p_var := p(i,:,:,:) + z_var := z(i,:,:,:) + + ht_vertcross := wrf_user_vertcross(z_var, p_var, start_end, xopt) + + dim0name = sprinti("vertical_t%i",i) + dim1name = sprinti("cross_line_idx_t%i",i) + ht_vertcross!0 = dim0name + ht_vertcross!1 = dim1name + + fout->$name$ = ht_vertcross + end do + + ;;;;;;;;;;;;;;;;;;;;;;;; 3D horizontal interpolation + + time = -1 + + z := wrf_user_getvar(input_file, "z", time) ; grid point height + p := wrf_user_getvar(input_file, "pressure", time) ; total pressure + + ; First, check backwards compat plev = 500. ; 500 MB + hlev = 5000; ; 5000 m + z_500 = wrf_user_intrp3d(z,p,"h",plev,0.,False) - z_500_dims = dimsizes(z_500) + p_5000 = wrf_user_intrp3d(p,z,"h",hlev,0.,False) + + fout->z_500 = z_500 + fout->p_5000 = p_5000 + + plev := (/1000., 850., 500., 250./) + hlev := (/500., 2500., 5000., 10000. /) + z_multi = wrf_user_intrp3d(z,p,"h",plev,0.,False) + p_multi = wrf_user_intrp3d(p,z,"h",hlev,0.,False) + + fout->z_multi = z_multi + fout->p_multi = p_multi + + ; Now check the new routine + + plev := 500. ; 500 MB + hlev := 5000 ; 5000 m + + z2_500 = wrf_user_interplevel(z,p,plev,False) + p2_5000 = wrf_user_interplevel(p,z,hlev,False) + fout->z2_500 = z2_500 + fout->p2_5000 = p2_5000 - ; 2D interpolation along line - t2 = wrf_user_getvar(input_file, "T2", 0) + + plev := (/1000., 850., 500., 250./) + hlev := (/500., 2500., 5000., 10000. /) + z2_multi = wrf_user_interplevel(z,p,plev,False) + p2_multi = wrf_user_interplevel(p,z,hlev,False) + + fout->z2_multi = z2_multi + fout->p2_multi = p2_multi + + pblh = wrf_user_getvar(input_file, "PBLH", time) + opts := False + opts@inc2dlevs = True + p_lev2d = wrf_user_interplevel(p, z, pblh, opts) + + fout->p_lev2d = p_lev2d + + + ;;;;;;;;;;;;;;;;;;;;;;;; 2D interpolation along line + + time = -1 + + t2 = wrf_user_getvar(input_file, "T2", time) dimst2 = dimsizes(t2) - pivot = (/ dimst2(1)/2, dimst2(0)/2 /) + pivot = (/ dimst2(2)/2, dimst2(1)/2 /) t2_line = wrf_user_intrp2d(t2, pivot, 90.0, False) - t2_line_dims = dimsizes(t2_line) - - filedimdef(fout, (/"ht_cross_vert", "ht_cross_horiz", "p_cross_vert", "p_cross_horiz"/), \ - (/ht_cross_dims(0), ht_cross_dims(1), p_cross_dims(0), p_cross_dims(1)/), \ - (/False,False,False,False/)) - filedimdef(fout, (/"z500_vert", "z500_horiz"/), \ - (/z_500_dims(0), z_500_dims(1) /), \ - (/False, False/)) - filedimdef(fout, (/"t2_line_horiz"/), \ - (/t2_line_dims(0) /), \ - (/False/)) - - filevardef(fout, "ht_cross", typeof(ht_cross), (/"ht_cross_vert", "ht_cross_horiz"/)) - filevardef(fout, "p_cross", typeof(p_cross), (/"p_cross_vert", "p_cross_horiz"/)) - filevardef(fout, "z_500", typeof(z_500), (/"z500_vert", "z500_horiz"/)) - filevardef(fout, "t2_line", typeof(t2_line), (/"t2_line_horiz"/)) - - filevarattdef(fout,"ht_cross", ht_cross) - filevarattdef(fout,"p_cross", p_cross) - filevarattdef(fout, "z_500", z_500) - filevarattdef(fout, "t2_line", t2_line) - fout->ht_cross = (/ht_cross/) - fout->p_cross = (/p_cross/) - fout->z_500 = (/z_500/) - fout->t2_line = (/t2_line/) - - - ; 3D interpolation to new vertical coordinates + + fout->t2_line = t2_line + + ; For the new interplevel routine + xopt := True + xopt@use_pivot = True + xopt@angle = 90.0 + xopt@latlon = False + xopt@file_handle = input_file + xopt@timeidx = 0 + xopt@linecoords = True + + t2_line2 = wrf_user_interpline(t2, pivot, xopt) + + fout->t2_line2 = t2_line2 + + lats = wrf_user_getvar(input_file, "lat", 0) + lons = wrf_user_getvar(input_file, "lon", 0) + + start_lat = min(lats) + .25d*(max(lats) - min(lats)) + end_lat = min(lats) + .65d*(max(lats) - min(lats)) + + start_lon = min(lons) + .25d*(max(lons) - min(lons)) + end_lon = min(lons) + .65d*(max(lons) - min(lons)) + + start_end = (/ start_lon, start_lat, end_lon, end_lat /) + + ; For the new line routine + xopt := True + xopt@use_pivot = False + xopt@latlon = True + xopt@file_handle = input_file + xopt@timeidx = 0 + xopt@linecoords = True + + t2_line3 = wrf_user_interpline(t2, start_end, xopt) + t2_line3!1 = "line_idx_t2_line3" + + fout->t2_line3 = t2_line3 + + times = wrf_user_getvar(input_file, "times", -1) + ntimes = dimsizes(times) + + do i=0,ntimes-1 + xopt@timeidx = i + name = sprinti("t2_line_t%i", i) + dim0name = sprinti("lineidx_t%i",i) + var := t2(i,:,:) + t2_line := wrf_user_interpline(var, start_end, xopt) + t2_line!0 = dim0name + fout->$name$ = t2_line + end do + + ; Make sure the 1 time case still works + t2 := wrf_user_getvar(input_file, "T2", 0) + + ; For the new line routine + xopt := True + xopt@use_pivot = False + xopt@latlon = True + xopt@file_handle = input_file + xopt@timeidx = 0 + xopt@linecoords = True + + t2_line4 = wrf_user_interpline(t2, start_end, xopt) + t2_line4!0 = "t2_line4_idx" + + fout->t2_line4 = t2_line4 + + + ;;;;;;;;;;;;;;;;;;;;;;; 3D interpolation to new vertical coordinates + time = -1 + ; interp t to theta - fld1 = wrf_user_getvar(input_file, "tk", -1) + fld1 = wrf_user_getvar(input_file, "tk", time) vert_coord = "theta" interp_levels = (/200,300,500,1000/) @@ -191,32 +306,17 @@ opts@logP = True fld1_intrp = wrf_user_vert_interp(input_file,fld1,vert_coord,interp_levels,opts) + fld1_intrp!1 = "interp_levels1" - fld1_dims = dimsizes(fld1_intrp) - - filedimdef(fout, (/"fld1_time", "fld1_levs", "fld1_sn", "fld1_we"/), \ - (/fld1_dims(0), fld1_dims(1), fld1_dims(2), fld1_dims(3)/), \ - (/False,False,False,False/)) - - filevardef(fout, "fld_tk_theta", typeof(fld1_intrp), (/"fld1_time", "fld1_levs", "fld1_sn", "fld1_we"/)) - filevarattdef(fout,"fld_tk_theta", fld1_intrp) - fout->fld_tk_theta = (/fld1_intrp/) + fout->fld_tk_theta = fld1_intrp ; interp t to theta-e fld2 = fld1 vert_coord := "theta-e" fld2_intrp = wrf_user_vert_interp(input_file,fld2,vert_coord,interp_levels,opts) + fld2_intrp!1 = "interp_levels2" - - fld2_dims = dimsizes(fld2_intrp) - - filedimdef(fout, (/"fld2_time", "fld2_levs", "fld2_sn", "fld2_we"/), \ - (/fld2_dims(0), fld2_dims(1), fld2_dims(2), fld2_dims(3)/), \ - (/False,False,False,False/)) - - filevardef(fout, "fld_tk_theta_e", typeof(fld2_intrp), (/"fld2_time", "fld2_levs", "fld2_sn", "fld2_we"/)) - filevarattdef(fout,"fld_tk_theta_e", fld2_intrp) - fout->fld_tk_theta_e = (/fld2_intrp/) + fout->fld_tk_theta_e = fld2_intrp ; interp t to pressure @@ -224,34 +324,19 @@ vert_coord := "pressure" interp_levels := (/850,500/) fld3_intrp = wrf_user_vert_interp(input_file,fld3,vert_coord,interp_levels,opts) + fld3_intrp!1 = "interp_levels3" + fout->fld_tk_pres = fld3_intrp - fld3_dims = dimsizes(fld3_intrp) - - filedimdef(fout, (/"fld3_time", "fld3_levs", "fld3_sn", "fld3_we"/), \ - (/fld3_dims(0), fld3_dims(1), fld3_dims(2), fld3_dims(3)/), \ - (/False,False,False,False/)) - - filevardef(fout, "fld_tk_pres", typeof(fld3_intrp), (/"fld3_time", "fld3_levs", "fld3_sn", "fld3_we"/)) - filevarattdef(fout,"fld_tk_pres", fld3_intrp) - fout->fld_tk_pres = (/fld3_intrp/) ; interp t to ght_msl fld4 = fld1 vert_coord := "ght_msl" interp_levels := (/1,2/) fld4_intrp = wrf_user_vert_interp(input_file,fld4,vert_coord,interp_levels,opts) + fld4_intrp!1 = "interp_levels4" - - fld4_dims = dimsizes(fld4_intrp) - - filedimdef(fout, (/"fld4_time", "fld4_levs", "fld4_sn", "fld4_we"/), \ - (/fld4_dims(0), fld4_dims(1), fld4_dims(2), fld4_dims(3)/), \ - (/False,False,False,False/)) - - filevardef(fout, "fld_tk_ght_msl", typeof(fld4_intrp), (/"fld4_time", "fld4_levs", "fld4_sn", "fld4_we"/)) - filevarattdef(fout,"fld_tk_ght_msl", fld4_intrp) - fout->fld_tk_ght_msl = (/fld4_intrp/) + fout->fld_tk_ght_msl = fld4_intrp ; interp t to ght_agl @@ -259,99 +344,69 @@ vert_coord := "ght_agl" interp_levels := (/1,2/) fld5_intrp = wrf_user_vert_interp(input_file,fld1,vert_coord,interp_levels,opts) + fld5_intrp!1 = "interp_levels5" - - fld5_dims = dimsizes(fld5_intrp) - - filedimdef(fout, (/"fld5_time", "fld5_levs", "fld5_sn", "fld5_we"/), \ - (/fld5_dims(0), fld5_dims(1), fld5_dims(2), fld5_dims(3)/), \ - (/False,False,False,False/)) - - filevardef(fout, "fld_tk_ght_agl", typeof(fld5_intrp), (/"fld5_time", "fld5_levs", "fld5_sn", "fld5_we"/)) - filevarattdef(fout,"fld_tk_ght_agl", fld5_intrp) - fout->fld_tk_ght_agl = (/fld5_intrp/) + fout->fld_tk_ght_agl = fld5_intrp ; interp ht to pres - fld6 = wrf_user_getvar(input_file, "height", -1) + fld6 = wrf_user_getvar(input_file, "height", time) vert_coord := "pressure" opts@field_type = "ght" interp_levels := (/500,50/) fld6_intrp = wrf_user_vert_interp(input_file,fld6,vert_coord,interp_levels,opts) + fld6_intrp!1 = "interp_levels6" - - fld6_dims = dimsizes(fld6_intrp) - - filedimdef(fout, (/"fld6_time", "fld6_levs", "fld6_sn", "fld6_we"/), \ - (/fld6_dims(0), fld6_dims(1), fld6_dims(2), fld6_dims(3)/), \ - (/False,False,False,False/)) - - filevardef(fout, "fld_ht_pres", typeof(fld6_intrp), (/"fld6_time", "fld6_levs", "fld6_sn", "fld6_we"/)) - filevarattdef(fout,"fld_ht_pres", fld6_intrp) - fout->fld_ht_pres = (/fld6_intrp/) + fout->fld_ht_pres = fld6_intrp ; interp pres to theta - fld7 = wrf_user_getvar(input_file, "pressure", -1) + fld7 = wrf_user_getvar(input_file, "pressure", time) vert_coord := "theta" opts@field_type = "pressure" interp_levels := (/200,300,500,1000/) fld7_intrp = wrf_user_vert_interp(input_file,fld7,vert_coord,interp_levels,opts) + fld7_intrp!1 = "interp_levels7" - - fld7_dims = dimsizes(fld7_intrp) - - filedimdef(fout, (/"fld7_time", "fld7_levs", "fld7_sn", "fld7_we"/), \ - (/fld7_dims(0), fld7_dims(1), fld7_dims(2), fld7_dims(3)/), \ - (/False,False,False,False/)) - - filevardef(fout, "fld_pres_theta", typeof(fld7_intrp), (/"fld7_time", "fld7_levs", "fld7_sn", "fld7_we"/)) - filevarattdef(fout,"fld_pres_theta", fld7_intrp) fout->fld_pres_theta = (/fld7_intrp/) + ; interp theta-e to pressure - fld8 = wrf_user_getvar(input_file, "eth", -1) + fld8 = wrf_user_getvar(input_file, "eth", time) vert_coord := "pressure" opts@field_type = "T" interp_levels := (/850,500,5/) fld8_intrp = wrf_user_vert_interp(input_file,fld8,vert_coord,interp_levels,opts) + fld8_intrp!1 = "interp_levels8" + + fout->fld_thetae_pres = fld8_intrp + + + ;;;;;;;;;;;;;;;;;;; lat/lon to x/y and x/y to lat/lon routines + + lats := (/22.0, 25.0, 27.0 /) + lons := (/-90.0, -87.5, -83.75 /) + x_s = (/10, 50, 90 /) + y_s = (/10, 50, 90 /) + + opt = True + opt@useTime = -1 + opt@returnInt = False + + xy1 = wrf_user_ll_to_xy(input_file, lons, lats, opt) + ll1 = wrf_user_xy_to_ll(input_file, x_s, y_s, opt) + + fout->xy1 = xy1 + fout->ll1 = ll1 + + opt = True + opt@useTime = 8 + opt@returnInt = True + xy2 = wrf_user_ll_to_xy(input_file, lons, lats, opt) + ll2 = wrf_user_xy_to_ll(input_file, x_s, y_s, opt) - fld8_dims = dimsizes(fld8_intrp) - - filedimdef(fout, (/"fld8_time", "fld8_levs", "fld8_sn", "fld8_we"/), \ - (/fld8_dims(0), fld8_dims(1), fld8_dims(2), fld8_dims(3)/), \ - (/False,False,False,False/)) - - filevardef(fout, "fld_thetae_pres", typeof(fld8_intrp), (/"fld8_time", "fld8_levs", "fld8_sn", "fld8_we"/)) - filevarattdef(fout,"fld_thetae_pres", fld8_intrp) - fout->fld_thetae_pres = (/fld8_intrp/) - - ; lat/lon to x/y and x/y to lat/lon routines - lats = (/-55, -60, -65 /) - lons = (/25, 30, 35 /) - i_s = (/10, 100, 150 /) - j_s = (/10, 100, 150 /) - ij = wrf_user_ll_to_ij(input_file, lons, lats, True) - ll = wrf_user_ij_to_ll(input_file, i_s, j_s, True) - - ij_dims = dimsizes(ij) - ll_dims = dimsizes(ll) - - filedimdef(fout, (/"i_j", "ij_idx"/), \ - (/ij_dims(0), ij_dims(1)/), \ - (/False,False/)) - - filedimdef(fout, (/"lat_lon", "ll_idx"/), \ - (/ll_dims(0), ll_dims(1)/), \ - (/False,False/)) - - filevardef(fout, "ij", typeof(ij), (/"i_j", "ij_idx"/)) - filevardef(fout, "ll", typeof(ll), (/"lat_lon", "ll_idx"/)) - filevarattdef(fout,"ij", ij) - filevarattdef(fout,"ll", ll) - - fout->ij = (/ij/) - fout->ll = (/ll/) + fout->xy2 = xy2 + fout->ll2 = ll2 delete(fout) diff --git a/test/plot_tests/d01/avo.png b/test/plot_tests/d01/avo.png new file mode 100644 index 0000000..39857dc Binary files /dev/null and b/test/plot_tests/d01/avo.png differ diff --git a/test/plot_tests/d01/cape2d_only.png b/test/plot_tests/d01/cape2d_only.png new file mode 100644 index 0000000..32e5d66 Binary files /dev/null and b/test/plot_tests/d01/cape2d_only.png differ diff --git a/test/plot_tests/d01/cape3d_only.png b/test/plot_tests/d01/cape3d_only.png new file mode 100644 index 0000000..e75f5a4 Binary files /dev/null and b/test/plot_tests/d01/cape3d_only.png differ diff --git a/test/plot_tests/d01/cape_2d.png b/test/plot_tests/d01/cape_2d.png new file mode 100644 index 0000000..e33aee5 Binary files /dev/null and b/test/plot_tests/d01/cape_2d.png differ diff --git a/test/plot_tests/d01/cape_3d.png b/test/plot_tests/d01/cape_3d.png new file mode 100644 index 0000000..c0aef7b Binary files /dev/null and b/test/plot_tests/d01/cape_3d.png differ diff --git a/test/plot_tests/d01/cfrac.png b/test/plot_tests/d01/cfrac.png new file mode 100644 index 0000000..7956e08 Binary files /dev/null and b/test/plot_tests/d01/cfrac.png differ diff --git a/test/plot_tests/d01/cin2d_only.png b/test/plot_tests/d01/cin2d_only.png new file mode 100644 index 0000000..dab42c2 Binary files /dev/null and b/test/plot_tests/d01/cin2d_only.png differ diff --git a/test/plot_tests/d01/cin3d_only.png b/test/plot_tests/d01/cin3d_only.png new file mode 100644 index 0000000..4762554 Binary files /dev/null and b/test/plot_tests/d01/cin3d_only.png differ diff --git a/test/plot_tests/d01/ctt.png b/test/plot_tests/d01/ctt.png new file mode 100644 index 0000000..5bcce25 Binary files /dev/null and b/test/plot_tests/d01/ctt.png differ diff --git a/test/plot_tests/d01/dbz.png b/test/plot_tests/d01/dbz.png new file mode 100644 index 0000000..1edb65f Binary files /dev/null and b/test/plot_tests/d01/dbz.png differ diff --git a/test/plot_tests/d01/eth.png b/test/plot_tests/d01/eth.png new file mode 100644 index 0000000..1148bce Binary files /dev/null and b/test/plot_tests/d01/eth.png differ diff --git a/test/plot_tests/d01/geopt.png b/test/plot_tests/d01/geopt.png new file mode 100644 index 0000000..d00f785 Binary files /dev/null and b/test/plot_tests/d01/geopt.png differ diff --git a/test/plot_tests/d01/helicity.png b/test/plot_tests/d01/helicity.png new file mode 100644 index 0000000..c7bd18d Binary files /dev/null and b/test/plot_tests/d01/helicity.png differ diff --git a/test/plot_tests/d01/high_cfrac.png b/test/plot_tests/d01/high_cfrac.png new file mode 100644 index 0000000..0a89b18 Binary files /dev/null and b/test/plot_tests/d01/high_cfrac.png differ diff --git a/test/plot_tests/d01/lat.png b/test/plot_tests/d01/lat.png new file mode 100644 index 0000000..3fadc3b Binary files /dev/null and b/test/plot_tests/d01/lat.png differ diff --git a/test/plot_tests/d01/lcl.png b/test/plot_tests/d01/lcl.png new file mode 100644 index 0000000..6a7d334 Binary files /dev/null and b/test/plot_tests/d01/lcl.png differ diff --git a/test/plot_tests/d01/lfc.png b/test/plot_tests/d01/lfc.png new file mode 100644 index 0000000..7c8e29c Binary files /dev/null and b/test/plot_tests/d01/lfc.png differ diff --git a/test/plot_tests/d01/lon.png b/test/plot_tests/d01/lon.png new file mode 100644 index 0000000..5d5a12f Binary files /dev/null and b/test/plot_tests/d01/lon.png differ diff --git a/test/plot_tests/d01/low_cfrac.png b/test/plot_tests/d01/low_cfrac.png new file mode 100644 index 0000000..a434292 Binary files /dev/null and b/test/plot_tests/d01/low_cfrac.png differ diff --git a/test/plot_tests/d01/mdbz.png b/test/plot_tests/d01/mdbz.png new file mode 100644 index 0000000..83a1cc6 Binary files /dev/null and b/test/plot_tests/d01/mdbz.png differ diff --git a/test/plot_tests/d01/mid_cfrac.png b/test/plot_tests/d01/mid_cfrac.png new file mode 100644 index 0000000..7596ccf Binary files /dev/null and b/test/plot_tests/d01/mid_cfrac.png differ diff --git a/test/plot_tests/d01/omg.png b/test/plot_tests/d01/omg.png new file mode 100644 index 0000000..0d2d6ea Binary files /dev/null and b/test/plot_tests/d01/omg.png differ diff --git a/test/plot_tests/d01/p.png b/test/plot_tests/d01/p.png new file mode 100644 index 0000000..9748bcd Binary files /dev/null and b/test/plot_tests/d01/p.png differ diff --git a/test/plot_tests/d01/pressure.png b/test/plot_tests/d01/pressure.png new file mode 100644 index 0000000..cb0b294 Binary files /dev/null and b/test/plot_tests/d01/pressure.png differ diff --git a/test/plot_tests/d01/pvo.png b/test/plot_tests/d01/pvo.png new file mode 100644 index 0000000..2de679c Binary files /dev/null and b/test/plot_tests/d01/pvo.png differ diff --git a/test/plot_tests/d01/pw.png b/test/plot_tests/d01/pw.png new file mode 100644 index 0000000..258b005 Binary files /dev/null and b/test/plot_tests/d01/pw.png differ diff --git a/test/plot_tests/d01/rh.png b/test/plot_tests/d01/rh.png new file mode 100644 index 0000000..877a8fa Binary files /dev/null and b/test/plot_tests/d01/rh.png differ diff --git a/test/plot_tests/d01/rh2.png b/test/plot_tests/d01/rh2.png new file mode 100644 index 0000000..d1b1332 Binary files /dev/null and b/test/plot_tests/d01/rh2.png differ diff --git a/test/plot_tests/d01/slp.png b/test/plot_tests/d01/slp.png new file mode 100644 index 0000000..e0cd91d Binary files /dev/null and b/test/plot_tests/d01/slp.png differ diff --git a/test/plot_tests/d01/tc.png b/test/plot_tests/d01/tc.png new file mode 100644 index 0000000..faee47d Binary files /dev/null and b/test/plot_tests/d01/tc.png differ diff --git a/test/plot_tests/d01/td.png b/test/plot_tests/d01/td.png new file mode 100644 index 0000000..3bb6fa5 Binary files /dev/null and b/test/plot_tests/d01/td.png differ diff --git a/test/plot_tests/d01/td2.png b/test/plot_tests/d01/td2.png new file mode 100644 index 0000000..a929270 Binary files /dev/null and b/test/plot_tests/d01/td2.png differ diff --git a/test/plot_tests/d01/ter.png b/test/plot_tests/d01/ter.png new file mode 100644 index 0000000..14a0f95 Binary files /dev/null and b/test/plot_tests/d01/ter.png differ diff --git a/test/plot_tests/d01/theta.png b/test/plot_tests/d01/theta.png new file mode 100644 index 0000000..6098f91 Binary files /dev/null and b/test/plot_tests/d01/theta.png differ diff --git a/test/plot_tests/d01/tk.png b/test/plot_tests/d01/tk.png new file mode 100644 index 0000000..c6527c8 Binary files /dev/null and b/test/plot_tests/d01/tk.png differ diff --git a/test/plot_tests/d01/tv.png b/test/plot_tests/d01/tv.png new file mode 100644 index 0000000..a95b99a Binary files /dev/null and b/test/plot_tests/d01/tv.png differ diff --git a/test/plot_tests/d01/twb.png b/test/plot_tests/d01/twb.png new file mode 100644 index 0000000..93ab3df Binary files /dev/null and b/test/plot_tests/d01/twb.png differ diff --git a/test/plot_tests/d01/ua.png b/test/plot_tests/d01/ua.png new file mode 100644 index 0000000..504ad13 Binary files /dev/null and b/test/plot_tests/d01/ua.png differ diff --git a/test/plot_tests/d01/updraft_helicity.png b/test/plot_tests/d01/updraft_helicity.png new file mode 100644 index 0000000..07c6a85 Binary files /dev/null and b/test/plot_tests/d01/updraft_helicity.png differ diff --git a/test/plot_tests/d01/uvmet.png b/test/plot_tests/d01/uvmet.png new file mode 100644 index 0000000..45b6005 Binary files /dev/null and b/test/plot_tests/d01/uvmet.png differ diff --git a/test/plot_tests/d01/uvmet10.png b/test/plot_tests/d01/uvmet10.png new file mode 100644 index 0000000..2cdd7d8 Binary files /dev/null and b/test/plot_tests/d01/uvmet10.png differ diff --git a/test/plot_tests/d01/uvmet10_wdir.png b/test/plot_tests/d01/uvmet10_wdir.png new file mode 100644 index 0000000..ee38ca0 Binary files /dev/null and b/test/plot_tests/d01/uvmet10_wdir.png differ diff --git a/test/plot_tests/d01/uvmet10_wspd.png b/test/plot_tests/d01/uvmet10_wspd.png new file mode 100644 index 0000000..31a0487 Binary files /dev/null and b/test/plot_tests/d01/uvmet10_wspd.png differ diff --git a/test/plot_tests/d01/uvmet10_wspd_wdir.png b/test/plot_tests/d01/uvmet10_wspd_wdir.png new file mode 100644 index 0000000..8049757 Binary files /dev/null and b/test/plot_tests/d01/uvmet10_wspd_wdir.png differ diff --git a/test/plot_tests/d01/uvmet_wdir.png b/test/plot_tests/d01/uvmet_wdir.png new file mode 100644 index 0000000..98e7796 Binary files /dev/null and b/test/plot_tests/d01/uvmet_wdir.png differ diff --git a/test/plot_tests/d01/uvmet_wspd.png b/test/plot_tests/d01/uvmet_wspd.png new file mode 100644 index 0000000..d5ba724 Binary files /dev/null and b/test/plot_tests/d01/uvmet_wspd.png differ diff --git a/test/plot_tests/d01/uvmet_wspd_wdir.png b/test/plot_tests/d01/uvmet_wspd_wdir.png new file mode 100644 index 0000000..990e67f Binary files /dev/null and b/test/plot_tests/d01/uvmet_wspd_wdir.png differ diff --git a/test/plot_tests/d01/va.png b/test/plot_tests/d01/va.png new file mode 100644 index 0000000..67b93ad Binary files /dev/null and b/test/plot_tests/d01/va.png differ diff --git a/test/plot_tests/d01/wa.png b/test/plot_tests/d01/wa.png new file mode 100644 index 0000000..db64184 Binary files /dev/null and b/test/plot_tests/d01/wa.png differ diff --git a/test/plot_tests/d01/wdir.png b/test/plot_tests/d01/wdir.png new file mode 100644 index 0000000..adaaf5c Binary files /dev/null and b/test/plot_tests/d01/wdir.png differ diff --git a/test/plot_tests/d01/wdir10.png b/test/plot_tests/d01/wdir10.png new file mode 100644 index 0000000..892a299 Binary files /dev/null and b/test/plot_tests/d01/wdir10.png differ diff --git a/test/plot_tests/d01/wspd.png b/test/plot_tests/d01/wspd.png new file mode 100644 index 0000000..86c2e66 Binary files /dev/null and b/test/plot_tests/d01/wspd.png differ diff --git a/test/plot_tests/d01/wspd10.png b/test/plot_tests/d01/wspd10.png new file mode 100644 index 0000000..74b24d6 Binary files /dev/null and b/test/plot_tests/d01/wspd10.png differ diff --git a/test/plot_tests/d01/wspd_wdir.png b/test/plot_tests/d01/wspd_wdir.png new file mode 100644 index 0000000..cee99db Binary files /dev/null and b/test/plot_tests/d01/wspd_wdir.png differ diff --git a/test/plot_tests/d01/wspd_wdir10.png b/test/plot_tests/d01/wspd_wdir10.png new file mode 100644 index 0000000..cd19a97 Binary files /dev/null and b/test/plot_tests/d01/wspd_wdir10.png differ diff --git a/test/plot_tests/d01/z.png b/test/plot_tests/d01/z.png new file mode 100644 index 0000000..438fe98 Binary files /dev/null and b/test/plot_tests/d01/z.png differ diff --git a/test/plot_tests/d02/avo.png b/test/plot_tests/d02/avo.png new file mode 100644 index 0000000..3076c89 Binary files /dev/null and b/test/plot_tests/d02/avo.png differ diff --git a/test/plot_tests/d02/cape2d_only.png b/test/plot_tests/d02/cape2d_only.png new file mode 100644 index 0000000..f0b43ab Binary files /dev/null and b/test/plot_tests/d02/cape2d_only.png differ diff --git a/test/plot_tests/d02/cape3d_only.png b/test/plot_tests/d02/cape3d_only.png new file mode 100644 index 0000000..fc0c178 Binary files /dev/null and b/test/plot_tests/d02/cape3d_only.png differ diff --git a/test/plot_tests/d02/cape_2d.png b/test/plot_tests/d02/cape_2d.png new file mode 100644 index 0000000..ba4a6af Binary files /dev/null and b/test/plot_tests/d02/cape_2d.png differ diff --git a/test/plot_tests/d02/cape_3d.png b/test/plot_tests/d02/cape_3d.png new file mode 100644 index 0000000..278a970 Binary files /dev/null and b/test/plot_tests/d02/cape_3d.png differ diff --git a/test/plot_tests/d02/cfrac.png b/test/plot_tests/d02/cfrac.png new file mode 100644 index 0000000..b1b52ad Binary files /dev/null and b/test/plot_tests/d02/cfrac.png differ diff --git a/test/plot_tests/d02/cin2d_only.png b/test/plot_tests/d02/cin2d_only.png new file mode 100644 index 0000000..c5cb133 Binary files /dev/null and b/test/plot_tests/d02/cin2d_only.png differ diff --git a/test/plot_tests/d02/cin3d_only.png b/test/plot_tests/d02/cin3d_only.png new file mode 100644 index 0000000..4f5e771 Binary files /dev/null and b/test/plot_tests/d02/cin3d_only.png differ diff --git a/test/plot_tests/d02/ctt.png b/test/plot_tests/d02/ctt.png new file mode 100644 index 0000000..4bf763e Binary files /dev/null and b/test/plot_tests/d02/ctt.png differ diff --git a/test/plot_tests/d02/dbz.png b/test/plot_tests/d02/dbz.png new file mode 100644 index 0000000..4767c37 Binary files /dev/null and b/test/plot_tests/d02/dbz.png differ diff --git a/test/plot_tests/d02/eth.png b/test/plot_tests/d02/eth.png new file mode 100644 index 0000000..df19200 Binary files /dev/null and b/test/plot_tests/d02/eth.png differ diff --git a/test/plot_tests/d02/geopt.png b/test/plot_tests/d02/geopt.png new file mode 100644 index 0000000..43e8e1e Binary files /dev/null and b/test/plot_tests/d02/geopt.png differ diff --git a/test/plot_tests/d02/helicity.png b/test/plot_tests/d02/helicity.png new file mode 100644 index 0000000..544e521 Binary files /dev/null and b/test/plot_tests/d02/helicity.png differ diff --git a/test/plot_tests/d02/high_cfrac.png b/test/plot_tests/d02/high_cfrac.png new file mode 100644 index 0000000..ef92109 Binary files /dev/null and b/test/plot_tests/d02/high_cfrac.png differ diff --git a/test/plot_tests/d02/lat.png b/test/plot_tests/d02/lat.png new file mode 100644 index 0000000..bb4e184 Binary files /dev/null and b/test/plot_tests/d02/lat.png differ diff --git a/test/plot_tests/d02/lcl.png b/test/plot_tests/d02/lcl.png new file mode 100644 index 0000000..a2efe53 Binary files /dev/null and b/test/plot_tests/d02/lcl.png differ diff --git a/test/plot_tests/d02/lfc.png b/test/plot_tests/d02/lfc.png new file mode 100644 index 0000000..0a41fe8 Binary files /dev/null and b/test/plot_tests/d02/lfc.png differ diff --git a/test/plot_tests/d02/lon.png b/test/plot_tests/d02/lon.png new file mode 100644 index 0000000..e8491f8 Binary files /dev/null and b/test/plot_tests/d02/lon.png differ diff --git a/test/plot_tests/d02/low_cfrac.png b/test/plot_tests/d02/low_cfrac.png new file mode 100644 index 0000000..e2d3a88 Binary files /dev/null and b/test/plot_tests/d02/low_cfrac.png differ diff --git a/test/plot_tests/d02/mdbz.png b/test/plot_tests/d02/mdbz.png new file mode 100644 index 0000000..e1eb827 Binary files /dev/null and b/test/plot_tests/d02/mdbz.png differ diff --git a/test/plot_tests/d02/mid_cfrac.png b/test/plot_tests/d02/mid_cfrac.png new file mode 100644 index 0000000..628524a Binary files /dev/null and b/test/plot_tests/d02/mid_cfrac.png differ diff --git a/test/plot_tests/d02/omg.png b/test/plot_tests/d02/omg.png new file mode 100644 index 0000000..22f689f Binary files /dev/null and b/test/plot_tests/d02/omg.png differ diff --git a/test/plot_tests/d02/p.png b/test/plot_tests/d02/p.png new file mode 100644 index 0000000..9e10cc7 Binary files /dev/null and b/test/plot_tests/d02/p.png differ diff --git a/test/plot_tests/d02/pressure.png b/test/plot_tests/d02/pressure.png new file mode 100644 index 0000000..bc8f399 Binary files /dev/null and b/test/plot_tests/d02/pressure.png differ diff --git a/test/plot_tests/d02/pvo.png b/test/plot_tests/d02/pvo.png new file mode 100644 index 0000000..f44c5ca Binary files /dev/null and b/test/plot_tests/d02/pvo.png differ diff --git a/test/plot_tests/d02/pw.png b/test/plot_tests/d02/pw.png new file mode 100644 index 0000000..e00142e Binary files /dev/null and b/test/plot_tests/d02/pw.png differ diff --git a/test/plot_tests/d02/rh.png b/test/plot_tests/d02/rh.png new file mode 100644 index 0000000..5a3523f Binary files /dev/null and b/test/plot_tests/d02/rh.png differ diff --git a/test/plot_tests/d02/rh2.png b/test/plot_tests/d02/rh2.png new file mode 100644 index 0000000..375536e Binary files /dev/null and b/test/plot_tests/d02/rh2.png differ diff --git a/test/plot_tests/d02/slp.png b/test/plot_tests/d02/slp.png new file mode 100644 index 0000000..4a961b9 Binary files /dev/null and b/test/plot_tests/d02/slp.png differ diff --git a/test/plot_tests/d02/tc.png b/test/plot_tests/d02/tc.png new file mode 100644 index 0000000..0bf677f Binary files /dev/null and b/test/plot_tests/d02/tc.png differ diff --git a/test/plot_tests/d02/td.png b/test/plot_tests/d02/td.png new file mode 100644 index 0000000..5c7a46e Binary files /dev/null and b/test/plot_tests/d02/td.png differ diff --git a/test/plot_tests/d02/td2.png b/test/plot_tests/d02/td2.png new file mode 100644 index 0000000..81e11a3 Binary files /dev/null and b/test/plot_tests/d02/td2.png differ diff --git a/test/plot_tests/d02/ter.png b/test/plot_tests/d02/ter.png new file mode 100644 index 0000000..ff7d7aa Binary files /dev/null and b/test/plot_tests/d02/ter.png differ diff --git a/test/plot_tests/d02/theta.png b/test/plot_tests/d02/theta.png new file mode 100644 index 0000000..807bdde Binary files /dev/null and b/test/plot_tests/d02/theta.png differ diff --git a/test/plot_tests/d02/tk.png b/test/plot_tests/d02/tk.png new file mode 100644 index 0000000..38c3e46 Binary files /dev/null and b/test/plot_tests/d02/tk.png differ diff --git a/test/plot_tests/d02/tv.png b/test/plot_tests/d02/tv.png new file mode 100644 index 0000000..1bc03e7 Binary files /dev/null and b/test/plot_tests/d02/tv.png differ diff --git a/test/plot_tests/d02/twb.png b/test/plot_tests/d02/twb.png new file mode 100644 index 0000000..10378ae Binary files /dev/null and b/test/plot_tests/d02/twb.png differ diff --git a/test/plot_tests/d02/ua.png b/test/plot_tests/d02/ua.png new file mode 100644 index 0000000..38cd0ca Binary files /dev/null and b/test/plot_tests/d02/ua.png differ diff --git a/test/plot_tests/d02/updraft_helicity.png b/test/plot_tests/d02/updraft_helicity.png new file mode 100644 index 0000000..0d17880 Binary files /dev/null and b/test/plot_tests/d02/updraft_helicity.png differ diff --git a/test/plot_tests/d02/uvmet.png b/test/plot_tests/d02/uvmet.png new file mode 100644 index 0000000..3a4179d Binary files /dev/null and b/test/plot_tests/d02/uvmet.png differ diff --git a/test/plot_tests/d02/uvmet10.png b/test/plot_tests/d02/uvmet10.png new file mode 100644 index 0000000..3265962 Binary files /dev/null and b/test/plot_tests/d02/uvmet10.png differ diff --git a/test/plot_tests/d02/uvmet10_wdir.png b/test/plot_tests/d02/uvmet10_wdir.png new file mode 100644 index 0000000..32ff17c Binary files /dev/null and b/test/plot_tests/d02/uvmet10_wdir.png differ diff --git a/test/plot_tests/d02/uvmet10_wspd.png b/test/plot_tests/d02/uvmet10_wspd.png new file mode 100644 index 0000000..f014caf Binary files /dev/null and b/test/plot_tests/d02/uvmet10_wspd.png differ diff --git a/test/plot_tests/d02/uvmet10_wspd_wdir.png b/test/plot_tests/d02/uvmet10_wspd_wdir.png new file mode 100644 index 0000000..c296931 Binary files /dev/null and b/test/plot_tests/d02/uvmet10_wspd_wdir.png differ diff --git a/test/plot_tests/d02/uvmet_wdir.png b/test/plot_tests/d02/uvmet_wdir.png new file mode 100644 index 0000000..ae44362 Binary files /dev/null and b/test/plot_tests/d02/uvmet_wdir.png differ diff --git a/test/plot_tests/d02/uvmet_wspd.png b/test/plot_tests/d02/uvmet_wspd.png new file mode 100644 index 0000000..85dd630 Binary files /dev/null and b/test/plot_tests/d02/uvmet_wspd.png differ diff --git a/test/plot_tests/d02/uvmet_wspd_wdir.png b/test/plot_tests/d02/uvmet_wspd_wdir.png new file mode 100644 index 0000000..b09b76f Binary files /dev/null and b/test/plot_tests/d02/uvmet_wspd_wdir.png differ diff --git a/test/plot_tests/d02/va.png b/test/plot_tests/d02/va.png new file mode 100644 index 0000000..672e18e Binary files /dev/null and b/test/plot_tests/d02/va.png differ diff --git a/test/plot_tests/d02/wa.png b/test/plot_tests/d02/wa.png new file mode 100644 index 0000000..1f8a72f Binary files /dev/null and b/test/plot_tests/d02/wa.png differ diff --git a/test/plot_tests/d02/wdir.png b/test/plot_tests/d02/wdir.png new file mode 100644 index 0000000..c1039f3 Binary files /dev/null and b/test/plot_tests/d02/wdir.png differ diff --git a/test/plot_tests/d02/wdir10.png b/test/plot_tests/d02/wdir10.png new file mode 100644 index 0000000..18c9ddd Binary files /dev/null and b/test/plot_tests/d02/wdir10.png differ diff --git a/test/plot_tests/d02/wspd.png b/test/plot_tests/d02/wspd.png new file mode 100644 index 0000000..3600356 Binary files /dev/null and b/test/plot_tests/d02/wspd.png differ diff --git a/test/plot_tests/d02/wspd10.png b/test/plot_tests/d02/wspd10.png new file mode 100644 index 0000000..ed8c32a Binary files /dev/null and b/test/plot_tests/d02/wspd10.png differ diff --git a/test/plot_tests/d02/wspd_wdir.png b/test/plot_tests/d02/wspd_wdir.png new file mode 100644 index 0000000..64311f6 Binary files /dev/null and b/test/plot_tests/d02/wspd_wdir.png differ diff --git a/test/plot_tests/d02/wspd_wdir10.png b/test/plot_tests/d02/wspd_wdir10.png new file mode 100644 index 0000000..027e5b6 Binary files /dev/null and b/test/plot_tests/d02/wspd_wdir10.png differ diff --git a/test/plot_tests/d02/z.png b/test/plot_tests/d02/z.png new file mode 100644 index 0000000..bd97ce3 Binary files /dev/null and b/test/plot_tests/d02/z.png differ diff --git a/test/plot_tests/plot_all.ipynb b/test/plot_tests/plot_all.ipynb new file mode 100644 index 0000000..75ef2f8 --- /dev/null +++ b/test/plot_tests/plot_all.ipynb @@ -0,0 +1,372 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import print_function\n", + "\n", + "# This jupyter notebook command inserts matplotlib graphics in \n", + "# to the workbook\n", + "%matplotlib inline\n", + "\n", + "# Modify these to point to your own files\n", + "WRF_DIRECTORY = \"/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/moving_nest\"\n", + "\n", + "WRF_FILES = [\"wrfout_d02_2005-08-28_00:00:00\",\n", + " \"wrfout_d02_2005-08-28_12:00:00\",\n", + " \"wrfout_d02_2005-08-29_00:00:00\"]\n", + "\n", + "\n", + "# Do not modify the code below this line\n", + "#------------------------------------------------------\n", + "# Turn off annoying warnings\n", + "import warnings\n", + "warnings.filterwarnings('ignore')\n", + "\n", + "# Make sure the environment is good\n", + "import numpy\n", + "import cartopy\n", + "import matplotlib\n", + "from netCDF4 import Dataset\n", + "from xarray import DataArray\n", + "from wrf import (getvar, interplevel, vertcross, \n", + " vinterp, ALL_TIMES)\n", + "import os\n", + "\n", + "_WRF_FILES = [os.path.abspath(os.path.expanduser(\n", + " os.path.join(WRF_DIRECTORY, f))) for f in WRF_FILES]\n", + "\n", + "# Check that the WRF files exist\n", + "try:\n", + " for f in _WRF_FILES:\n", + " if not os.path.exists(f):\n", + " raise ValueError(\"{} does not exist. \"\n", + " \"Check for typos or incorrect directory.\".format(f))\n", + "except ValueError:\n", + " # If the directory ended up in the zip file, then \n", + " # another 'wrf_tutorial_data' directory might be \n", + " # used.\n", + " WRF_DIRECTORY = os.path.join(WRF_DIRECTORY, \"wrf_tutorial_data\")\n", + " _WRF_FILES = [os.path.abspath(os.path.expanduser(\n", + " os.path.join(WRF_DIRECTORY, f))) for f in WRF_FILES]\n", + " for f in _WRF_FILES:\n", + " if not os.path.exists(f):\n", + " raise\n", + "\n", + " \n", + "# Create functions so that the WRF files only need\n", + "# to be specified using the WRF_FILES global above\n", + "def single_wrf_file():\n", + " global _WRF_FILES\n", + " return _WRF_FILES[0]\n", + "\n", + "def multiple_wrf_files():\n", + " global _WRF_FILES\n", + " return _WRF_FILES\n", + "\n", + "def save_fig(diagname):\n", + " f = single_wrf_file()\n", + " if f.find(\"_d01_\") > 0:\n", + " fout = \"{}.png\".format(os.path.join(os.path.abspath(\".\"), \"d01\", diagname))\n", + " else:\n", + " fout = \"{}.png\".format(os.path.join(os.path.abspath(\".\"), \"d02\", diagname))\n", + " \n", + " matplotlib.pyplot.savefig(fout)\n", + " \n", + "\n", + "print(\"All tests passed!\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def get_kwargs(diagname):\n", + " kwargs = {}\n", + " if diagname == \"ctt\":\n", + " kwargs = {\"fill_nocloud\" : True}\n", + " \n", + " return kwargs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def contour_levels(diagname, diag, numlevels=15):\n", + " levels = numlevels\n", + " extend = \"neither\"\n", + " if diagname == \"ter\":\n", + " levels = numpy.arange(10.,4000.,250.)\n", + " elif diagname == \"avo\":\n", + " levels = numpy.arange(10.,75.,5.)\n", + " extend=\"max\"\n", + " elif diagname == \"eth\":\n", + " levels = numpy.arange(270.,400.,10.)\n", + " elif diagname == \"cape_2d\":\n", + " levels = numpy.arange(200.,4000.,250.)\n", + " extend = \"max\"\n", + " elif diagname == \"cape_3d\":\n", + " levels = numpy.arange(200.,4000.,250.)\n", + " extend = \"max\"\n", + " elif diagname == \"ctt\":\n", + " # Note: The MP scheme doesn't produce cloud water so this\n", + " # is just surface temperature\n", + " levels = numpy.arange(-100, 20., 5.0)\n", + " extend = \"both\"\n", + " elif diagname == \"dbz\":\n", + " #pass\n", + " levels = numpy.arange(15.,75.,5.)\n", + " extend = \"max\"\n", + " elif diagname == \"mdbz\":\n", + " levels = numpy.arange(15.,75.,5.)\n", + " extend = \"max\"\n", + " elif diagname == \"geopt\":\n", + " pass\n", + " elif diagname == \"helicity\":\n", + " levels = numpy.arange(100,500,25)\n", + " extend=\"max\"\n", + " elif diagname == \"lat\":\n", + " pass\n", + " elif diagname == \"lon\":\n", + " pass\n", + " elif diagname == \"omg\":\n", + " pass\n", + " elif diagname == \"p\":\n", + " levels = numpy.arange(95000.,103000.,250.)\n", + " extend = \"min\"\n", + " elif diagname == \"pressure\":\n", + " levels = numpy.arange(950.,1030.,2.5)\n", + " extend = \"min\"\n", + " elif diagname == \"pvo\":\n", + " levels = numpy.arange(.5,5.,.25)\n", + " elif diagname == \"pw\":\n", + " #pass\n", + " levels = numpy.arange(0.1,100,10)\n", + " elif diagname == \"rh2\":\n", + " levels = numpy.arange(50,101,5)\n", + " elif diagname == \"rh\":\n", + " levels = numpy.arange(50.,101.,5)\n", + " elif diagname == \"slp\":\n", + " levels = numpy.arange(950.,1030.,2.5)\n", + " extend = \"min\"\n", + " elif diagname == \"td2\":\n", + " levels = numpy.arange(10,40,5)\n", + " extend = \"max\"\n", + " elif diagname == \"td\":\n", + " levels = numpy.arange(10,40,5)\n", + " extend = \"max\"\n", + " elif diagname == \"tc\":\n", + " levels = numpy.arange(10,40,5)\n", + " extend = \"max\"\n", + " elif diagname == \"theta\":\n", + " levels = numpy.arange(270.,350.,2.5)\n", + " elif diagname == \"tk\":\n", + " levels = numpy.arange(270.,350.,5.)\n", + " elif diagname == \"tv\":\n", + " levels = numpy.arange(270.,350.,5.)\n", + " #levels = numpy.arange(10,40,5)\n", + " elif diagname == \"twb\":\n", + " levels = numpy.arange(270.,350.,5.) \n", + " elif diagname == \"updraft_helicity\":\n", + " pass\n", + " #levels = numpy.arange(1,100,5)\n", + " elif diagname == \"ua\":\n", + " levels = numpy.arange(-40,40,5)\n", + " extend = \"both\"\n", + " elif diagname == \"va\":\n", + " levels = numpy.arange(-40,40,5)\n", + " extend = \"both\"\n", + " elif diagname == \"wa\":\n", + " pass\n", + " #levels = numpy.arange(0.1,40,5)\n", + " elif diagname == \"uvmet10\":\n", + " levels = numpy.arange(-40,40,5)\n", + " extend = \"both\"\n", + " elif diagname == \"uvmet\":\n", + " levels = numpy.arange(-40,40,5)\n", + " extend = \"both\"\n", + " elif diagname == \"z\":\n", + " levels = numpy.arange(0,200,10)\n", + " extend=\"max\"\n", + " elif diagname == \"cfrac\":\n", + " levels = numpy.arange(0.0,1.1,.2)\n", + " elif diagname == \"wspd_wdir\":\n", + " levels = numpy.arange(-40,40,5)\n", + " extend = \"both\"\n", + " elif diagname == \"wspd_wdir10\":\n", + " levels = numpy.arange(-40,40,5)\n", + " extend = \"both\"\n", + " elif diagname == \"uvmet_wspd_wdir\":\n", + " levels = numpy.arange(-40,40,5)\n", + " extend = \"both\"\n", + " elif diagname == \"uvmet10_wspd_wdir\":\n", + " levels = numpy.arange(-40,40,5)\n", + " extend = \"both\"\n", + " elif diagname ==\"cape2d_only\":\n", + " levels = numpy.arange(200.,4000.,250.)\n", + " extend = \"max\"\n", + " elif diagname ==\"cin2d_only\": \n", + " levels = numpy.arange(10.,200.,10.)\n", + " extend = \"max\"\n", + " elif diagname ==\"lcl\":\n", + " levels = numpy.arange(50.,2000.,50.)\n", + " extend = \"max\"\n", + " elif diagname ==\"lfc\": \n", + " levels = numpy.arange(100.,5000.,100.)\n", + " extend = \"max\"\n", + " elif diagname ==\"cape3d_only\": \n", + " levels = numpy.arange(200.,4000.,250.)\n", + " extend = \"max\"\n", + " elif diagname ==\"cin3d_only\": \n", + " levels = levels = numpy.arange(10.,200.,10.)\n", + " extend = \"max\"\n", + " elif diagname ==\"uvmet_wspd\": \n", + " levels = numpy.arange(-40,40,5)\n", + " extend = \"both\"\n", + " elif diagname ==\"uvmet_wdir\": \n", + " levels = numpy.arange(0,370,10)\n", + " elif diagname ==\"uvmet10_wspd\": \n", + " levels = numpy.arange(-40,40,5)\n", + " extend = \"both\"\n", + " elif diagname ==\"uvmet10_wdir\": \n", + " levels = numpy.arange(0,370,10)\n", + " elif diagname ==\"wspd\": \n", + " levels = numpy.arange(-40,40,5)\n", + " extend = \"both\"\n", + " elif diagname ==\"wdir\": \n", + " levels = numpy.arange(0,370,10)\n", + " elif diagname ==\"wspd10\": \n", + " levels = numpy.arange(-40,40,5)\n", + " extend = \"both\"\n", + " elif diagname ==\"wdir10\": \n", + " levels = numpy.arange(0,370,10)\n", + " elif diagname ==\"low_cfrac\":\n", + " levels = numpy.arange(0.0,1.1,.2)\n", + " elif diagname ==\"mid_cfrac\": \n", + " levels = numpy.arange(0.0,1.1,.2)\n", + " elif diagname ==\"high_cfrac\":\n", + " levels = numpy.arange(0.0,1.1,.2)\n", + " \n", + " return levels, extend" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "import numpy\n", + "from matplotlib import pyplot\n", + "from matplotlib.cm import get_cmap\n", + "from cartopy import crs\n", + "from cartopy.feature import NaturalEarthFeature\n", + "from netCDF4 import Dataset\n", + "from wrf import getvar, to_np, get_cartopy, latlon_coords, cartopy_xlim, cartopy_ylim, Constants\n", + "\n", + "file_path = single_wrf_file()\n", + "wrf_file = Dataset(file_path)\n", + "\n", + "for diagname in (\"ter\",\"avo\", \"eth\", \"cape_2d\", \"cape_3d\", \"ctt\", \"dbz\", \"mdbz\", \n", + " \"geopt\", \"helicity\", \"lat\", \"lon\", \"omg\", \"p\", \"pressure\", \n", + " \"pvo\", \"pw\", \"rh2\", \"rh\", \"slp\", \"td2\", \"td\", \"tc\",\n", + " \"theta\", \"tk\", \"tv\", \"twb\", \"updraft_helicity\", \"ua\", \"va\", \n", + " \"wa\", \"uvmet10\", \"uvmet\", \"z\", \"wspd_wdir\", \"wspd_wdir10\",\n", + " \"uvmet_wspd_wdir\", \"uvmet10_wspd_wdir\", \"cfrac\",\n", + " \"cape2d_only\",\"cin2d_only\", \"lcl\", \"lfc\", \"cape3d_only\", \"cin3d_only\",\"uvmet_wspd\", \n", + " \"uvmet_wdir\",\"uvmet10_wspd\", \"uvmet10_wdir\",\"wspd\", \"wdir\", \"wspd10\", \"wdir10\",\n", + " \"low_cfrac\", \"mid_cfrac\", \"high_cfrac\"):\n", + " \n", + " # Get the terrain height\n", + " print(diagname)\n", + " kwargs = get_kwargs(diagname)\n", + " diag = getvar(wrf_file, diagname, timeidx=3, **kwargs)\n", + "\n", + " if diag.ndim == 3:\n", + " diag = diag[0,:]\n", + " elif diag.ndim == 4:\n", + " diag = diag[0,0,:]\n", + "\n", + " # Get the cartopy object and the lat,lon coords\n", + " cart_proj = get_cartopy(diag)\n", + " lats, lons = latlon_coords(diag)\n", + " \n", + "\n", + " # Create a figure and get the GetAxes object\n", + " fig = pyplot.figure(figsize=(10, 7.5))\n", + " ax = pyplot.axes(projection=cart_proj)\n", + "\n", + " # Download and add the states and coastlines\n", + " # See the cartopy documentation for more on this.\n", + " states = NaturalEarthFeature(category='cultural', \n", + " scale='50m', \n", + " facecolor='none',\n", + " name='admin_1_states_provinces_shp')\n", + "\n", + " ax.add_feature(states, linewidth=.5, edgecolor='black', zorder=3)\n", + " ax.coastlines('50m', linewidth=.8, color='black', zorder=4)\n", + "\n", + " # Set the contour levels\n", + " levels, extend = contour_levels(diagname, diag, numlevels=15)\n", + "\n", + " # Make the contour lines and fill them.\n", + " pyplot.contour(to_np(lons), to_np(lats), \n", + " to_np(diag), levels=levels, \n", + " colors=\"black\",\n", + " transform=crs.PlateCarree())\n", + " pyplot.contourf(to_np(lons), to_np(lats), \n", + " to_np(diag), levels=levels,\n", + " transform=crs.PlateCarree(),\n", + " extend=extend,\n", + " cmap=get_cmap(\"jet\"))\n", + "\n", + " ax.set_xlim(cartopy_xlim(diag))\n", + " ax.set_ylim(cartopy_ylim(diag))\n", + " \n", + " # Add a color bar. The shrink often needs to be set \n", + " # by trial and error.\n", + " cb = pyplot.colorbar(ax=ax, shrink=.99)\n", + " \n", + " pyplot.title(diagname)\n", + " \n", + " # Uncomment this to build the sample images\n", + " #save_fig(diagname)\n", + " \n", + " pyplot.show()\n", + "\n", + "\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.7" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/test/test_filevars.py b/test/test_filevars.py index dc816c3..1cf3c82 100644 --- a/test/test_filevars.py +++ b/test/test_filevars.py @@ -7,7 +7,7 @@ import subprocess from wrf import getvar, ALL_TIMES -TEST_DIR = "/Users/ladwig/Documents/wrf_files/wrf_vortex_multi" +TEST_DIR = "/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/moving_nest" TEST_FILENAMES = ["wrfout_d02_2005-08-28_00:00:00", "wrfout_d02_2005-08-28_12:00:00", "wrfout_d02_2005-08-29_00:00:00"] diff --git a/test/test_inputs.py b/test/test_inputs.py index 1fa986e..7271681 100644 --- a/test/test_inputs.py +++ b/test/test_inputs.py @@ -12,7 +12,7 @@ from wrf import (getvar, interplevel, interpline, vertcross, vinterp, extract_global_attrs, viewitems, CoordPair, ll_points) from wrf.util import is_multi_file -IN_DIR = "/Users/ladwig/Documents/wrf_files/wrf_vortex_multi" +IN_DIR = "/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/moving_nest" 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-29_00:00:00")] diff --git a/test/test_multi_cache.py b/test/test_multi_cache.py index 80c1c7d..963fca0 100644 --- a/test/test_multi_cache.py +++ b/test/test_multi_cache.py @@ -5,8 +5,8 @@ from netCDF4 import Dataset as nc #a = nc("/Users/ladwig/Documents/wrf_files/wrf_vortex_single/wrfout_d02_2005-08-28_00:00:00") #b = nc("/Users/ladwig/Documents/wrf_files/wrf_vortex_single/wrfout_d02_2005-08-28_03:00:00") -a = nc("/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/wrfout_d02_2005-08-28_00:00:00") -b = nc("/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/wrfout_d02_2005-08-28_12: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/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]} } } } } t1 = time.time() diff --git a/test/test_proj_params.py b/test/test_proj_params.py new file mode 100644 index 0000000..77a0ec7 --- /dev/null +++ b/test/test_proj_params.py @@ -0,0 +1,314 @@ +import unittest as ut +import numpy.testing as nt +import numpy as np +import numpy.ma as ma +import os, sys +import subprocess + +from wrf import xy_to_ll_proj, ll_to_xy_proj, to_np + + +class WRFLatLonProjTest(ut.TestCase): + longMessage = True + +def make_test(xy_or_ll_out): + def test(self): + + # Python 3 + if sys.version_info > (3,): + assert_raises_regex = self.assertRaisesRegex + xrange = range + else: + assert_raises_regex = self.assertRaisesRegexp + + if xy_or_ll_out == "xy": + # Test the required failures + with assert_raises_regex(ValueError, ".*map_proj.*" ): + ll_to_xy_proj(30,-110) + + with assert_raises_regex(ValueError, ".*ref_lat.*" ): + ll_to_xy_proj(30,-110, map_proj=1) + + with assert_raises_regex(ValueError, ".*ref_lon.*" ): + ll_to_xy_proj(30,-110, map_proj=1, ref_lat=45) + + with assert_raises_regex(ValueError, ".*known_x.*" ): + ll_to_xy_proj(30,-110, map_proj=1, ref_lat=45.0, + ref_lon=-120.) + + with assert_raises_regex(ValueError, ".*known_y.*" ): + ll_to_xy_proj(30,-110, map_proj=1, ref_lat=45.0, + ref_lon=-120., known_x=1) + + with assert_raises_regex(ValueError, ".*dx.*" ): + ll_to_xy_proj(30,-110, map_proj=1, ref_lat=45.0, + ref_lon=-120., known_x=0, known_y=0) + + ####### Now test the projections + + # Lambert Conformal - truelat1, stand_lon required + with assert_raises_regex(ValueError, ".*truelat1.*" ): + ll_to_xy_proj(30,-110, map_proj=1, ref_lat=45.0, + ref_lon=-120., known_x=0, known_y=0, + dx=3000.) + + with assert_raises_regex(ValueError, ".*stand_lon.*" ): + ll_to_xy_proj(30,-110, map_proj=1, ref_lat=45.0, + ref_lon=-120., known_x=0, known_y=0, + dx=3000., + truelat1=60.) + + # 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, + ref_lon=-100.7747, known_x=0, known_y=0, + dx=30000., truelat1=30., truelat2=30., + stand_lon=-89., pole_lat=90., pole_lon=0.) + + + 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, + dx=30000., truelat1=30., + stand_lon=-89.) + + nt.assert_allclose(to_np(p_all), to_np(p_def)) + + # Polar Stereographic - truelat1, stand_lon + + with assert_raises_regex(ValueError, ".*truelat1.*" ): + ll_to_xy_proj(30,-110, map_proj=2, ref_lat=45.0, + ref_lon=-120., known_x=0, known_y=0, + dx=3000.) + + with assert_raises_regex(ValueError, ".*stand_lon.*" ): + ll_to_xy_proj(30,-110, map_proj=2, ref_lat=45.0, + ref_lon=-120., known_x=0, known_y=0, + dx=3000., + truelat1=60.) + + 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, + dx=30000., truelat1=30., truelat2=30., + stand_lon=-89., pole_lat=90., pole_lon=0.) + + + 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, + dx=30000., truelat1=30., + stand_lon=-89.) + + nt.assert_allclose(to_np(p_all), to_np(p_def)) + + + # Mercator - truelat1 + + with assert_raises_regex(ValueError, ".*truelat1.*" ): + ll_to_xy_proj(30,-110, map_proj=2, ref_lat=45.0, + ref_lon=-120., known_x=0, known_y=0, + dx=3000.) + + 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, + dx=30000., truelat1=30., truelat2=30., + stand_lon=-89., pole_lat=90., pole_lon=0.) + + + 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, + dx=30000., truelat1=30.) + + nt.assert_allclose(to_np(p_all), to_np(p_def)) + + # Lat/lon - stand_lon, dy, pole_lat, pole_lon + + with assert_raises_regex(ValueError, ".*stand_lon.*" ): + ll_to_xy_proj(30,-110, map_proj=6, ref_lat=45.0, + ref_lon=-120., known_x=0, known_y=0, + dx=.2698388) + + with assert_raises_regex(ValueError, ".*dy.*" ): + ll_to_xy_proj(30,-110, map_proj=6, ref_lat=45.0, + ref_lon=-120., known_x=0, known_y=0, + stand_lon=89.0, + dx=.2698388) + + with assert_raises_regex(ValueError, ".*pole_lat.*" ): + ll_to_xy_proj(30,-110, map_proj=6, ref_lat=45.0, + ref_lon=-120., known_x=0, known_y=0, + stand_lon=89.0, + dx=.2698388, dy=.2698388) + + with assert_raises_regex(ValueError, ".*pole_lon.*" ): + ll_to_xy_proj(30,-110, map_proj=6, ref_lat=45.0, + ref_lon=-120., known_x=0, known_y=0, + stand_lon=89.0, + dx=.2698388, dy=.2698388, + pole_lat=62.0) + + 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, + dx=30000, dy=30000, + truelat1=30., truelat2=30., + stand_lon=-89., pole_lat=62.0, + pole_lon=180.) + + + 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, + stand_lon=-89., + dx=30000, dy=30000, pole_lat=62.0, + pole_lon=180.) + + nt.assert_allclose(to_np(p_all), to_np(p_def)) + + + if xy_or_ll_out == "ll": + + # Test the required failures + with assert_raises_regex(ValueError, ".*map_proj.*" ): + xy_to_ll_proj(45, 50) + + with assert_raises_regex(ValueError, ".*ref_lat.*" ): + xy_to_ll_proj(45, 50, map_proj=1) + + with assert_raises_regex(ValueError, ".*ref_lon.*" ): + xy_to_ll_proj(45, 50, map_proj=1, ref_lat=45) + + with assert_raises_regex(ValueError, ".*known_x.*" ): + xy_to_ll_proj(45, 50, map_proj=1, ref_lat=45.0, + ref_lon=-120.) + + with assert_raises_regex(ValueError, ".*known_y.*" ): + xy_to_ll_proj(45, 50, map_proj=1, ref_lat=45.0, + ref_lon=-120., known_x=1) + + with assert_raises_regex(ValueError, ".*dx.*" ): + xy_to_ll_proj(45, 50, map_proj=1, ref_lat=45.0, + ref_lon=-120., known_x=0, known_y=0) + + ####### Now test the projections + + # Lambert Conformal - truelat1, stand_lon required + with assert_raises_regex(ValueError, ".*truelat1.*" ): + xy_to_ll_proj(45, 50, map_proj=1, ref_lat=45.0, + ref_lon=-120., known_x=0, known_y=0, + dx=3000.) + + with assert_raises_regex(ValueError, ".*stand_lon.*" ): + xy_to_ll_proj(45, 50, map_proj=1, ref_lat=45.0, + ref_lon=-120., known_x=0, known_y=0, + dx=3000., + truelat1=60.) + + # 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, + ref_lon=-100.7747, known_x=0, known_y=0, + dx=30000., truelat1=30., truelat2=30., + stand_lon=-89., pole_lat=90., pole_lon=0.) + + + 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, + dx=30000., truelat1=30., + stand_lon=-89.) + + nt.assert_allclose(to_np(p_all), to_np(p_def)) + + # Polar Stereographic - truelat1, stand_lon + + with assert_raises_regex(ValueError, ".*truelat1.*" ): + xy_to_ll_proj(45, 50, map_proj=2, ref_lat=45.0, + ref_lon=-120., known_x=0, known_y=0, + dx=3000.) + + with assert_raises_regex(ValueError, ".*stand_lon.*" ): + xy_to_ll_proj(45, 50, map_proj=2, ref_lat=45.0, + ref_lon=-120., known_x=0, known_y=0, + dx=3000., + truelat1=60.) + + 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, + dx=30000., truelat1=30., truelat2=30., + stand_lon=-89., pole_lat=90., pole_lon=0.) + + + 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, + dx=30000., truelat1=30., + stand_lon=-89.) + + nt.assert_allclose(to_np(p_all), to_np(p_def)) + + + # Mercator - truelat1 + + with assert_raises_regex(ValueError, ".*truelat1.*" ): + xy_to_ll_proj(45, 50, map_proj=2, ref_lat=45.0, + ref_lon=-120., known_x=0, known_y=0, + dx=3000.) + + 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, + dx=30000., truelat1=30., truelat2=30., + stand_lon=-89., pole_lat=90., pole_lon=0.) + + + 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, + dx=30000., truelat1=30.) + + nt.assert_allclose(to_np(p_all), to_np(p_def)) + + # Lat/lon - stand_lon, dy, pole_lat, pole_lon + + with assert_raises_regex(ValueError, ".*stand_lon.*" ): + xy_to_ll_proj(45, 50, map_proj=6, ref_lat=45.0, + ref_lon=-120., known_x=0, known_y=0, + dx=.2698388) + + with assert_raises_regex(ValueError, ".*dy.*" ): + xy_to_ll_proj(45, 50, map_proj=6, ref_lat=45.0, + ref_lon=-120., known_x=0, known_y=0, + stand_lon=89.0, + dx=.2698388) + + with assert_raises_regex(ValueError, ".*pole_lat.*" ): + xy_to_ll_proj(45, 50, map_proj=6, ref_lat=45.0, + ref_lon=-120., known_x=0, known_y=0, + stand_lon=89.0, + dx=.2698388, dy=.2698388) + + with assert_raises_regex(ValueError, ".*pole_lon.*" ): + xy_to_ll_proj(45, 50, map_proj=6, ref_lat=45.0, + ref_lon=-120., known_x=0, known_y=0, + stand_lon=89.0, + dx=.2698388, dy=.2698388, + pole_lat=62.0) + + 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, + dx=30000, dy=30000, + truelat1=30., truelat2=30., + stand_lon=-89., pole_lat=62.0, + pole_lon=180.) + + + 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, + stand_lon=-89., + dx=30000, dy=30000, pole_lat=62.0, + pole_lon=180.) + + nt.assert_allclose(to_np(p_all), to_np(p_def)) + + return test + + +if __name__ == "__main__": + + for v in ("xy", "ll"): + test_func = make_test(v) + setattr(WRFLatLonProjTest, 'test_{0}'.format(v), test_func) + + ut.main() + \ No newline at end of file diff --git a/test/utests.py b/test/utests.py index ed1d452..bf5359a 100644 --- a/test/utests.py +++ b/test/utests.py @@ -4,6 +4,7 @@ import numpy as np import numpy.ma as ma import os, sys import subprocess +import glob from wrf import (getvar, interplevel, interpline, vertcross, vinterp, disable_xarray, xarray_enabled, to_np, @@ -11,44 +12,51 @@ from wrf import (getvar, interplevel, interpline, vertcross, vinterp, extract_global_attrs, viewitems, CoordPair, ll_points) from wrf.util import is_multi_file -NCL_EXE = "/Users/ladwig/nclbuild/6.3.0/bin/ncl" -TEST_FILE = "/Users/ladwig/Documents/wrf_files/wrfout_d01_2010-06-13_21:00:00" -OUT_NC_FILE = "/tmp/wrftest.nc" +NCL_EXE = "/Users/ladwig/miniconda2/envs/ncl_build/bin/ncl" +NCARG_ROOT = "/Users/ladwig/miniconda2/envs/ncl_build" +#TEST_FILE = "/Users/ladwig/Documents/wrf_files/wrfout_d01_2010-06-13_21:00:00" +DIRS = ["/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/moving_nest", + "/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/static_nest"] +PATTERN = "wrfout_d02_*" +REF_NC_FILES = ["/tmp/wrftest_moving.nc", "/tmp/wrftest_static.nc"] +NEST = ["moving", "static"] # Python 3 if sys.version_info > (3,): xrange = range def setUpModule(): - ncarg_root = os.environ.get("NCARG_ROOT", None) - if ncarg_root is None: - raise RuntimeError("$NCARG_ROOT environment variable not set") - + #ncarg_root = os.environ.get("NCARG_ROOT", None) + #if ncarg_root is None: + # raise RuntimeError("$NCARG_ROOT environment variable not set") + os.environ["NCARG_ROOT"] = NCARG_ROOT + os.environ["NCARG_NCARG"] = os.path.join(NCARG_ROOT, "lib", "ncarg") + os.environ["OMP_NUM_THREADS"] = "4" + + this_path = os.path.realpath(__file__) ncl_script = os.path.join(os.path.dirname(this_path), "ncl_get_var.ncl") - ncfile = TEST_FILE + ".nc" # NCL requires extension - # This needs to be set when PyNIO is installed, since PyNIOs data does - # not contain the dat file for the CAPE calcluations - os.environ["NCARG_NCARG"] = os.path.join(os.environ["NCARG_ROOT"], - "lib", "ncarg") - cmd = "%s %s 'in_file=\"%s\"' 'out_file=\"%s\"'" % (NCL_EXE, - ncl_script, - ncfile, - OUT_NC_FILE) + for dir,outfile in zip(DIRS, REF_NC_FILES): + cmd = "%s %s 'dir=\"%s\"' 'pattern=\"%s\"' 'out_file=\"%s\"'" % ( + NCL_EXE, + ncl_script, + dir, + PATTERN, + outfile) - #print cmd + print(cmd) - if not os.path.exists(OUT_NC_FILE): - status = subprocess.call(cmd, shell=True) - if (status != 0): - raise RuntimeError("NCL script failed. Could not set up test.") + if not os.path.exists(outfile): + status = subprocess.call(cmd, shell=True) + if (status != 0): + raise RuntimeError("NCL script failed. Could not set up test.") # Using helpful information at: # 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, dir, pattern, referent, multi=False, pynio=False): def test(self): try: @@ -57,99 +65,77 @@ def make_test(varname, wrf_in, referent, multi=False, repeat=3, pynio=False): pass try: - from PyNIO import Nio + import Nio except: pass - if not multi: - timeidx = 0 - if not pynio: - in_wrfnc = NetCDF(wrf_in) - else: - # Note: Python doesn't like it if you reassign an outer scoped - # variable (wrf_in in this case) - if not wrf_in.endswith(".nc"): - wrf_file = wrf_in + ".nc" - else: - wrf_file = wrf_in - in_wrfnc = Nio.open_file(wrf_file) - else: - timeidx = None + timeidx = 0 if not multi else None + pat = os.path.join(dir, pattern) + wrf_files = glob.glob(pat) + wrf_files.sort() + + wrfin = [] + for fname in wrf_files: if not pynio: - nc = NetCDF(wrf_in) - in_wrfnc = [nc for i in xrange(repeat)] + f = NetCDF(fname) + try: + f.set_always_mask(False) + except: + pass + wrfin.append(f) else: - if not wrf_in.endswith(".nc"): - wrf_file = wrf_in + ".nc" + if not fname.endswith(".nc"): + _fname = fname + ".nc" else: - wrf_file = wrf_in - nc = Nio.open_file(wrf_file) - in_wrfnc = [nc for i in xrange(repeat)] - - + _fname = fname + f = Nio.open_file(_fname) + wrfin.append(f) + refnc = NetCDF(referent) + try: + refnc.set_auto_mask(False) + except: + pass # These have a left index that defines the product type multiproduct = varname in ("uvmet", "uvmet10", "cape_2d", "cape_3d", "cfrac") + multi2d = ("uvmet10", "cape_2d", "cfrac") + multi3d = ("uvmet", "cape_3d") # These varnames don't have NCL functions to test against ignore_referent = ("zstag", "geopt_stag") if varname not in ignore_referent: if not multi: - ref_vals = refnc.variables[varname][:] - else: - data = refnc.variables[varname][:] - if (varname != "uvmet" and varname != "uvmet10" - and varname != "cape_2d" and varname != "cape_3d"): - new_dims = [repeat] + [x for x in data.shape] - elif (varname == "uvmet" or varname == "uvmet10" - or varname == "cape_3d"): - new_dims = [2] + [repeat] + [x for x in data.shape[1:]] - elif (varname == "cape_2d"): - new_dims = [4] + [repeat] + [x for x in data.shape[1:]] - elif (varname == "cfrac"): - new_dims = [3] + [repeat] + [x for x in data.shape[1:]] - - - masked=False - if (isinstance(data, ma.core.MaskedArray)): - masked=True - - if not masked: - ref_vals = np.zeros(new_dims, data.dtype) + if varname in multi2d: + ref_vals = refnc.variables[varname][...,0,:,:] + elif varname in multi3d: + ref_vals = refnc.variables[varname][...,0,:,:,:] else: - ref_vals = ma.asarray(np.zeros(new_dims, data.dtype)) - - for i in xrange(repeat): - if not multiproduct: - ref_vals[i,:] = data[:] - - if masked: - ref_vals.mask[i,:] = data.mask[:] - - else: - for prod in xrange(ref_vals.shape[0]): - ref_vals[prod,i,:] = data[prod,:] - - if masked: - ref_vals.mask[prod,i,:] = data.mask[prod,:] + ref_vals = refnc.variables[varname][0,:] + else: + ref_vals = refnc.variables[varname][:] if (varname == "tc"): - my_vals = getvar(in_wrfnc, "temp", timeidx=timeidx, units="c") + my_vals = getvar(wrfin, "temp", timeidx=timeidx, units="c") + tol = 1/100. + atol = .1 # Note: NCL uses 273.16 as conversion for some reason + nt.assert_allclose(to_np(my_vals), ref_vals, tol, atol) + elif (varname == "height_agl"): + # Change the vert_type to height_agl when NCL gets updated. + my_vals = getvar(wrfin, "z", timeidx=timeidx, msl=False) tol = 1/100. atol = .1 # Note: NCL uses 273.16 as conversion for some reason nt.assert_allclose(to_np(my_vals), ref_vals, tol, atol) elif (varname == "cfrac"): # Change the vert_type to height_agl when NCL gets updated. - my_vals = getvar(in_wrfnc, "cfrac", timeidx=timeidx, - vert_type="pres") + my_vals = getvar(wrfin, "cfrac", timeidx=timeidx) tol = 1/100. atol = .1 # Note: NCL uses 273.16 as conversion for some reason nt.assert_allclose(to_np(my_vals), ref_vals, tol, atol) elif (varname == "pw"): - my_vals = getvar(in_wrfnc, "pw", timeidx=timeidx) + my_vals = getvar(wrfin, "pw", timeidx=timeidx) tol = .5/100.0 atol = 0 # NCL uses different constants and doesn't use same # handrolled virtual temp in method @@ -159,7 +145,7 @@ def make_test(varname, wrf_in, referent, multi=False, repeat=3, pynio=False): print (np.amax(np.abs(to_np(my_vals) - ref_vals))) raise elif (varname == "cape_2d"): - cape_2d = getvar(in_wrfnc, varname, timeidx=timeidx) + cape_2d = getvar(wrfin, varname, timeidx=timeidx) tol = 0/100. atol = 200.0 # Let's only compare CAPE values until the F90 changes are @@ -168,7 +154,7 @@ def make_test(varname, wrf_in, referent, multi=False, repeat=3, pynio=False): # causing wildly different values in LCL nt.assert_allclose(to_np(cape_2d[0,:]), ref_vals[0,:], tol, atol) elif (varname == "cape_3d"): - cape_3d = getvar(in_wrfnc, varname, timeidx=timeidx) + cape_3d = getvar(wrfin, varname, timeidx=timeidx) # Changing the R and CP constants, while keeping TK within # 2%, can lead to some big changes in CAPE. Tolerances # have been set wide when comparing the with the original @@ -180,55 +166,65 @@ def make_test(varname, wrf_in, referent, multi=False, repeat=3, pynio=False): #print np.amax(np.abs(to_np(cape_3d[0,:]) - ref_vals[0,:])) nt.assert_allclose(to_np(cape_3d), ref_vals, tol, atol) elif (varname == "zstag" or varname == "geopt_stag"): - v = getvar(in_wrfnc, varname, timeidx=timeidx) + v = getvar(wrfin, varname, timeidx=timeidx) # For now, only make sure it runs without crashing since no NCL # to compare with yet. else: - my_vals = getvar(in_wrfnc, varname, timeidx=timeidx) + my_vals = getvar(wrfin, varname, timeidx=timeidx) tol = 2/100. atol = 0.1 #print (np.amax(np.abs(to_np(my_vals) - ref_vals))) try: nt.assert_allclose(to_np(my_vals), ref_vals, tol, atol) except: - print (np.amax(np.abs(to_np(my_vals) - ref_vals))) + absdiff = np.abs(to_np(my_vals) - ref_vals) + maxdiff = np.amax(absdiff) + print(maxdiff) + print(np.argwhere(absdiff == maxdiff)) + raise return test -def _get_refvals(referent, varname, repeat, multi): +def _get_refvals(referent, varname, multi): try: from netCDF4 import Dataset as NetCDF except: pass refnc = NetCDF(referent) + try: + pass + #refnc.set_auto_mask(False) + except: + pass + + multi2d = ("uvmet10", "cape_2d", "cfrac") + multi3d = ("uvmet", "cape_3d") + interpline = ("t2_line","t2_line2", "t2_line3") if not multi: - ref_vals = refnc.variables[varname][:] - else: - data = refnc.variables[varname][:] - new_dims = [repeat] + [x for x in data.shape] - masked=False - if (isinstance(data, ma.core.MaskedArray)): - masked=True - - if not masked: - ref_vals = np.zeros(new_dims, data.dtype) + if varname in multi2d: + ref_vals = refnc.variables[varname][...,0,:,:] + elif varname in multi3d: + ref_vals = refnc.variables[varname][...,0,:,:,:] else: - ref_vals = ma.asarray(np.zeros(new_dims, data.dtype)) - - for i in xrange(repeat): - ref_vals[i,:] = data[:] - - if masked: - ref_vals.mask[i,:] = data.mask[:] + v = refnc.variables[varname][:] + if v.ndim == 2: + if varname in interpline: + ref_vals = v[0,:] + else: + ref_vals = v + else: + ref_vals = v[0,:] + else: + ref_vals = refnc.variables[varname][:] return ref_vals -def make_interp_test(varname, wrf_in, referent, multi=False, - repeat=3, pynio=False): +def make_interp_test(varname, dir, pattern, referent, multi=False, + pynio=False): def test(self): try: from netCDF4 import Dataset as NetCDF @@ -236,91 +232,185 @@ def make_interp_test(varname, wrf_in, referent, multi=False, pass try: - from PyNIO import Nio + import Nio except: pass - if not multi: - timeidx = 0 - if not pynio: - in_wrfnc = NetCDF(wrf_in) - else: - # Note: Python doesn't like it if you reassign an outer scoped - # variable (wrf_in in this case) - if not wrf_in.endswith(".nc"): - wrf_file = wrf_in + ".nc" - else: - wrf_file = wrf_in - in_wrfnc = Nio.open_file(wrf_file) - else: - timeidx = None + timeidx = 0 if not multi else None + pat = os.path.join(dir, pattern) + wrf_files = glob.glob(pat) + wrf_files.sort() + + wrfin = [] + for fname in wrf_files: if not pynio: - nc = NetCDF(wrf_in) - in_wrfnc = [nc for i in xrange(repeat)] + f = NetCDF(fname) + try: + f.set_always_mask(False) + except: + pass + wrfin.append(f) else: - if not wrf_in.endswith(".nc"): - wrf_file = wrf_in + ".nc" + if not fname.endswith(".nc"): + _fname = fname + ".nc" else: - wrf_file = wrf_in - nc = Nio.open_file(wrf_file) - in_wrfnc = [nc for i in xrange(repeat)] + _fname = fname + f = Nio.open_file(_fname) + wrfin.append(f) if (varname == "interplevel"): - ref_ht_500 = _get_refvals(referent, "z_500", repeat, multi) - hts = getvar(in_wrfnc, "z", timeidx=timeidx) - p = getvar(in_wrfnc, "pressure", timeidx=timeidx) + ref_ht_500 = _get_refvals(referent, "z_500", multi) + ref_p_5000 = _get_refvals(referent, "p_5000", multi) + ref_ht_multi = _get_refvals(referent, "z_multi", multi) + ref_p_multi = _get_refvals(referent, "p_multi", multi) + + ref_ht2_500 = _get_refvals(referent, "z2_500", multi) + ref_p2_5000 = _get_refvals(referent, "p2_5000", multi) + ref_ht2_multi = _get_refvals(referent, "z2_multi", multi) + ref_p2_multi = _get_refvals(referent, "p2_multi", multi) + + ref_p_lev2d = _get_refvals(referent, "p_lev2d", multi) + + hts = getvar(wrfin, "z", timeidx=timeidx) + p = getvar(wrfin, "pressure", timeidx=timeidx) + wspd_wdir = getvar(wrfin, "wspd_wdir", timeidx=timeidx) # Make sure the numpy versions work first hts_500 = interplevel(to_np(hts), to_np(p), 500) hts_500 = interplevel(hts, p, 500) + # Note: the '*2*' versions in the reference are testing + # against the new version of interplevel in NCL nt.assert_allclose(to_np(hts_500), ref_ht_500) + nt.assert_allclose(to_np(hts_500), ref_ht2_500) + + # Make sure the numpy versions work first + p_5000 = interplevel(to_np(p), to_np(hts), 5000) + p_5000 = interplevel(p, hts, 5000) + + + nt.assert_allclose(to_np(p_5000), ref_p_5000) + nt.assert_allclose(to_np(p_5000), ref_p2_5000) + + hts_multi= interplevel(to_np(hts), to_np(p), + [1000., 850., 500., 250.]) + hts_multi = interplevel(hts, p, [1000., 850., 500., 250.]) + + nt.assert_allclose(to_np(hts_multi), ref_ht_multi) + nt.assert_allclose(to_np(hts_multi), ref_ht2_multi) + + p_multi= interplevel(to_np(p), to_np(hts), + [500., 2500., 5000., 10000. ]) + p_multi = interplevel(p, hts, [500., 2500., 5000., 10000. ]) + + nt.assert_allclose(to_np(p_multi), ref_p_multi) + nt.assert_allclose(to_np(p_multi), ref_p2_multi) + + pblh = getvar(wrfin, "PBLH", timeidx=timeidx) + p_lev2d = interplevel(to_np(p), to_np(hts), to_np(pblh)) + p_lev2d = interplevel(p, hts, pblh) + nt.assert_allclose(to_np(p_lev2d), ref_p_lev2d) + + # Just make sure these run below + wspd_wdir_500 = interplevel(to_np(wspd_wdir), to_np(p), 500) + wspd_wdir_500 = interplevel(wspd_wdir, p, 500) + #print(wspd_wdir_500) + + wspd_wdir_multi= interplevel(to_np(wspd_wdir), + to_np(p), [1000,500,250]) + wdpd_wdir_multi = interplevel(wspd_wdir, p, [1000,500,250]) + + + wspd_wdir_pblh = interplevel(to_np(wspd_wdir), to_np(hts), pblh) + wspd_wdir_pblh = interplevel(wspd_wdir, hts, pblh) + + if multi: + wspd_wdir_pblh_2 = interplevel(to_np(wspd_wdir), + to_np(hts), pblh[0,:]) + wspd_wdir_pblh_2 = interplevel(wspd_wdir, hts, pblh[0,:]) + + # Since PBLH doesn't change in this case, it should match + # the 0 time from previous computation. Note that this + # only works when the data has 1 time step that is repeated. + # If you use a different case with multiple times, + # this will probably fail. + nt.assert_allclose(to_np(wspd_wdir_pblh_2[:,0,:]), + to_np(wspd_wdir_pblh[:,0,:])) + + nt.assert_allclose(to_np(wspd_wdir_pblh_2[:,-1,:]), + to_np(wspd_wdir_pblh[:,0,:])) + elif (varname == "vertcross"): - ref_ht_cross = _get_refvals(referent, "ht_cross", repeat, multi) - ref_p_cross = _get_refvals(referent, "p_cross", repeat, multi) + ref_ht_cross = _get_refvals(referent, "ht_cross", multi) + ref_p_cross = _get_refvals(referent, "p_cross", multi) + ref_ht_vertcross1 = _get_refvals(referent, "ht_vertcross1", + multi) + ref_ht_vertcross2 = _get_refvals(referent, "ht_vertcross2", + multi) + ref_ht_vertcross3 = _get_refvals(referent, "ht_vertcross3", + multi) - hts = getvar(in_wrfnc, "z", timeidx=timeidx) - p = getvar(in_wrfnc, "pressure", timeidx=timeidx) + hts = getvar(wrfin, "z", timeidx=timeidx) + p = getvar(wrfin, "pressure", timeidx=timeidx) pivot_point = CoordPair(hts.shape[-1] / 2, hts.shape[-2] / 2) - #ht_cross = vertcross(to_np(hts), p, pivot_point=pivot_point, - # angle=90., latlon=True) + + # Beginning in wrf-python 1.3, users can select number of levels. + # Originally, for pressure, dz was 10, so let's back calculate + # the number of levels. + p_max = np.floor(np.amax(p)/10) * 10 # bottom value + p_min = np.ceil(np.amin(p)/10) * 10 # top value + + p_autolevels = int((p_max - p_min) /10) + # Make sure the numpy versions work first + ht_cross = vertcross(to_np(hts), to_np(p), - pivot_point=pivot_point, angle=90.) - ht_cross = vertcross(hts, p, pivot_point=pivot_point, angle=90.) + pivot_point=pivot_point, angle=90., + autolevels=p_autolevels) - # Note: Until the bug is fixed in NCL, the wrf-python cross - # sections will contain one extra point - nt.assert_allclose(to_np(ht_cross)[...,0:-1], ref_ht_cross, - rtol=.01) + ht_cross = vertcross(hts, p, pivot_point=pivot_point, angle=90., + autolevels=p_autolevels) + + nt.assert_allclose(to_np(ht_cross), ref_ht_cross, rtol=.01) - # Test the manual projection method with lat/lon lats = hts.coords["XLAT"] lons = hts.coords["XLONG"] - ll_point = ll_points(lats, lons) - pivot = CoordPair(lat=lats[int(lats.shape[-2]/2), + + # Test the manual projection method with lat/lon + # Only do this for the non-multi case, since the domain + # might be moving + if not multi: + if lats.ndim > 2: # moving nest + lats = lats[0,:] + lons = lons[0,:] + + ll_point = ll_points(lats, lons) + + pivot = CoordPair(lat=lats[int(lats.shape[-2]/2), int(lats.shape[-1]/2)], - lon=lons[int(lons.shape[-2]/2), + lon=lons[int(lons.shape[-2]/2), int(lons.shape[-1]/2)]) - v1 = vertcross(hts,p,wrfin=in_wrfnc,pivot_point=pivot_point, + + v1 = vertcross(hts,p,wrfin=wrfin,pivot_point=pivot_point, angle=90.0) - v2 = vertcross(hts,p,projection=hts.attrs["projection"], + v2 = vertcross(hts,p,projection=hts.attrs["projection"], ll_point=ll_point, pivot_point=pivot_point, angle=90.) - nt.assert_allclose(to_np(v1), to_np(v2), rtol=.01) + nt.assert_allclose(to_np(v1), to_np(v2), rtol=.01) # Test opposite + p_cross1 = vertcross(p,hts,pivot_point=pivot_point, angle=90.0) - nt.assert_allclose(to_np(p_cross1)[...,0:-1], + nt.assert_allclose(to_np(p_cross1), ref_p_cross, rtol=.01) # Test point to point start_point = CoordPair(0, hts.shape[-2]/2) - end_point = CoordPair(-1,hts.shape[-2]/2) + end_point = CoordPair(-1, hts.shape[-2]/2) p_cross2 = vertcross(p,hts,start_point=start_point, @@ -328,34 +418,111 @@ def make_interp_test(varname, wrf_in, referent, multi=False, nt.assert_allclose(to_np(p_cross1), to_np(p_cross2)) + + # Check the new vertcross routine + pivot_point = CoordPair(hts.shape[-1] / 2, hts.shape[-2] / 2) + ht_cross = vertcross(hts, p, + pivot_point=pivot_point, angle=90., + latlon=True) + + nt.assert_allclose(to_np(ht_cross), + to_np(ref_ht_vertcross1), atol=.01) + + + + levels = [1000., 850., 700., 500., 250.] + ht_cross = vertcross(hts, p, + pivot_point=pivot_point, angle=90., + levels=levels, latlon=True) + + nt.assert_allclose(to_np(ht_cross), + to_np(ref_ht_vertcross2), atol=.01) + + idxs = (0, slice(None)) if lats.ndim > 2 else (slice(None),) + + start_lat = np.amin(lats[idxs]) + .25*(np.amax(lats[idxs]) - np.amin(lats[idxs])) + end_lat = np.amin(lats[idxs]) + .65*(np.amax(lats[idxs]) - np.amin(lats[idxs])) + + start_lon = np.amin(lons[idxs]) + .25*(np.amax(lons[idxs]) - np.amin(lons[idxs])) + end_lon = np.amin(lons[idxs]) + .65*(np.amax(lons[idxs]) - np.amin(lons[idxs])) + + start_point = CoordPair(lat=start_lat, lon=start_lon) + end_point = CoordPair(lat=end_lat, lon=end_lon) + + ll_point = ll_points(lats[idxs], lons[idxs]) + + ht_cross = vertcross(hts, p, + start_point=start_point, + end_point=end_point, + projection=hts.attrs["projection"], + ll_point=ll_point, + latlon=True, + autolevels=1000) + + + nt.assert_allclose(to_np(ht_cross), + to_np(ref_ht_vertcross3), + rtol=.01) + + if multi: + ntimes = hts.shape[0] + + for t in range(ntimes): + hts = getvar(wrfin, "z", timeidx=t) + p = getvar(wrfin, "pressure", timeidx=t) + + ht_cross = vertcross(hts, p, + start_point=start_point, + end_point=end_point, + wrfin=wrfin, + timeidx=t, + latlon=True, + autolevels=1000) + + refname = "ht_vertcross_t{}".format(t) + ref_ht_vertcross = _get_refvals(referent, refname, False) + + nt.assert_allclose(to_np(ht_cross), + to_np(ref_ht_vertcross),rtol=.02) + elif (varname == "interpline"): - ref_t2_line = _get_refvals(referent, "t2_line", repeat, multi) + ref_t2_line = _get_refvals(referent, "t2_line", multi) + ref_t2_line2 = _get_refvals(referent, "t2_line2", multi) + ref_t2_line3 = _get_refvals(referent, "t2_line3", multi) - t2 = getvar(in_wrfnc, "T2", timeidx=timeidx) + t2 = getvar(wrfin, "T2", timeidx=timeidx) pivot_point = CoordPair(t2.shape[-1] / 2, t2.shape[-2] / 2) - #t2_line1 = interpline(to_np(t2), pivot_point=pivot_point, - # angle=90.0, latlon=True) # Make sure the numpy version works t2_line1 = interpline(to_np(t2), pivot_point=pivot_point, angle=90.0) t2_line1 = interpline(t2, pivot_point=pivot_point, angle=90.0) - # Note: After NCL is fixed, remove the slice. - nt.assert_allclose(to_np(t2_line1)[...,0:-1], ref_t2_line) + nt.assert_allclose(to_np(t2_line1), ref_t2_line) + + # Test the new NCL wrf_user_interplevel result + nt.assert_allclose(to_np(t2_line1), ref_t2_line2) # Test the manual projection method with lat/lon lats = t2.coords["XLAT"] lons = t2.coords["XLONG"] + if multi: + if lats.ndim > 2: # moving nest + lats = lats[0,:] + lons = lons[0,:] + ll_point = ll_points(lats, lons) + pivot = CoordPair(lat=lats[int(lats.shape[-2]/2), int(lats.shape[-1]/2)], lon=lons[int(lons.shape[-2]/2), int(lons.shape[-1]/2)]) - l1 = interpline(t2,wrfin=in_wrfnc,pivot_point=pivot_point, + + l1 = interpline(t2,wrfin=wrfin,pivot_point=pivot_point, angle=90.0) + l2 = interpline(t2,projection=t2.attrs["projection"], ll_point=ll_point, pivot_point=pivot_point, angle=90.) @@ -369,17 +536,74 @@ def make_interp_test(varname, wrf_in, referent, multi=False, end_point=end_point) nt.assert_allclose(to_np(t2_line1), to_np(t2_line2)) + + # Now test the start/end with lat/lon points + + start_lat = float(np.amin(lats) + .25*(np.amax(lats) + - np.amin(lats))) + end_lat = float(np.amin(lats) + .65*(np.amax(lats) + - np.amin(lats))) + + start_lon = float(np.amin(lons) + .25*(np.amax(lons) + - np.amin(lons))) + end_lon = float(np.amin(lons) + .65*(np.amax(lons) + - np.amin(lons))) + + start_point = CoordPair(lat=start_lat, lon=start_lon) + end_point = CoordPair(lat=end_lat, lon=end_lon) + + t2_line3 = interpline(t2,wrfin=wrfin,timeidx=0, + start_point=start_point, + end_point=end_point,latlon=True) + + + nt.assert_allclose(to_np(t2_line3), ref_t2_line3, rtol=.01) + + # Test all time steps + if multi: + refnc = NetCDF(referent) + ntimes = t2.shape[0] + + for t in range(ntimes): + t2 = getvar(wrfin, "T2", timeidx=t) + + line = interpline(t2,wrfin=wrfin,timeidx=t, + start_point=start_point, + end_point=end_point,latlon=True) + + refname = "t2_line_t{}".format(t) + refline = refnc.variables[refname][:] + + nt.assert_allclose(to_np(line), + to_np(refline),rtol=.005) + + refnc.close() + + # Test NCLs single time case + if not multi: + refnc = NetCDF(referent) + ref_t2_line4 = refnc.variables["t2_line4"][:] + + t2 = getvar(wrfin, "T2", timeidx=0) + line = interpline(t2,wrfin=wrfin,timeidx=0, + start_point=start_point, + end_point=end_point,latlon=True) + + nt.assert_allclose(to_np(line), + to_np(ref_t2_line4),rtol=.005) + refnc.close() + elif (varname == "vinterp"): # Tk to theta - fld_tk_theta = _get_refvals(referent, "fld_tk_theta", repeat, multi) + fld_tk_theta = _get_refvals(referent, "fld_tk_theta", multi) fld_tk_theta = np.squeeze(fld_tk_theta) - tk = getvar(in_wrfnc, "temp", timeidx=timeidx, units="k") + tk = getvar(wrfin, "temp", timeidx=timeidx, units="k") interp_levels = [200,300,500,1000] # Make sure the numpy version works - field = vinterp(in_wrfnc, + field = vinterp(wrfin, field=to_np(tk), vert_coord="theta", interp_levels=interp_levels, @@ -388,7 +612,7 @@ def make_interp_test(varname, wrf_in, referent, multi=False, timeidx=timeidx, log_p=True) - field = vinterp(in_wrfnc, + field = vinterp(wrfin, field=tk, vert_coord="theta", interp_levels=interp_levels, @@ -405,12 +629,12 @@ def make_interp_test(varname, wrf_in, referent, multi=False, nt.assert_allclose(to_np(field), fld_tk_theta, tol, atol) # Tk to theta-e - fld_tk_theta_e = _get_refvals(referent, "fld_tk_theta_e", repeat, multi) + fld_tk_theta_e = _get_refvals(referent, "fld_tk_theta_e", multi) fld_tk_theta_e = np.squeeze(fld_tk_theta_e) interp_levels = [200,300,500,1000] - field = vinterp(in_wrfnc, + field = vinterp(wrfin, field=tk, vert_coord="theta-e", interp_levels=interp_levels, @@ -427,12 +651,12 @@ def make_interp_test(varname, wrf_in, referent, multi=False, nt.assert_allclose(to_np(field), fld_tk_theta_e, tol, atol) # Tk to pressure - fld_tk_pres = _get_refvals(referent, "fld_tk_pres", repeat, multi) + fld_tk_pres = _get_refvals(referent, "fld_tk_pres", multi) fld_tk_pres = np.squeeze(fld_tk_pres) interp_levels = [850,500] - field = vinterp(in_wrfnc, + field = vinterp(wrfin, field=tk, vert_coord="pressure", interp_levels=interp_levels, @@ -447,11 +671,11 @@ def make_interp_test(varname, wrf_in, referent, multi=False, nt.assert_allclose(to_np(field), fld_tk_pres, tol, atol) # Tk to geoht_msl - fld_tk_ght_msl = _get_refvals(referent, "fld_tk_ght_msl", repeat, multi) + fld_tk_ght_msl = _get_refvals(referent, "fld_tk_ght_msl", multi) fld_tk_ght_msl = np.squeeze(fld_tk_ght_msl) interp_levels = [1,2] - field = vinterp(in_wrfnc, + field = vinterp(wrfin, field=tk, vert_coord="ght_msl", interp_levels=interp_levels, @@ -465,11 +689,11 @@ def make_interp_test(varname, wrf_in, referent, multi=False, nt.assert_allclose(to_np(field), fld_tk_ght_msl, tol, atol) # Tk to geoht_agl - fld_tk_ght_agl = _get_refvals(referent, "fld_tk_ght_agl", repeat, multi) + fld_tk_ght_agl = _get_refvals(referent, "fld_tk_ght_agl", multi) fld_tk_ght_agl = np.squeeze(fld_tk_ght_agl) interp_levels = [1,2] - field = vinterp(in_wrfnc, + field = vinterp(wrfin, field=tk, vert_coord="ght_agl", interp_levels=interp_levels, @@ -483,12 +707,12 @@ def make_interp_test(varname, wrf_in, referent, multi=False, nt.assert_allclose(to_np(field), fld_tk_ght_agl, tol, atol) # Hgt to pressure - fld_ht_pres = _get_refvals(referent, "fld_ht_pres", repeat, multi) + fld_ht_pres = _get_refvals(referent, "fld_ht_pres", multi) fld_ht_pres = np.squeeze(fld_ht_pres) - z = getvar(in_wrfnc, "height", timeidx=timeidx, units="m") + z = getvar(wrfin, "height", timeidx=timeidx, units="m") interp_levels = [500,50] - field = vinterp(in_wrfnc, + field = vinterp(wrfin, field=z, vert_coord="pressure", interp_levels=interp_levels, @@ -502,12 +726,12 @@ def make_interp_test(varname, wrf_in, referent, multi=False, nt.assert_allclose(to_np(field), fld_ht_pres, tol, atol) # Pressure to theta - fld_pres_theta = _get_refvals(referent, "fld_pres_theta", repeat, multi) + fld_pres_theta = _get_refvals(referent, "fld_pres_theta", multi) fld_pres_theta = np.squeeze(fld_pres_theta) - p = getvar(in_wrfnc, "pressure", timeidx=timeidx) + p = getvar(wrfin, "pressure", timeidx=timeidx) interp_levels = [200,300,500,1000] - field = vinterp(in_wrfnc, + field = vinterp(wrfin, field=p, vert_coord="theta", interp_levels=interp_levels, @@ -521,12 +745,12 @@ def make_interp_test(varname, wrf_in, referent, multi=False, nt.assert_allclose(to_np(field), fld_pres_theta, tol, atol) # Theta-e to pres - fld_thetae_pres = _get_refvals(referent, "fld_thetae_pres", repeat, multi) + fld_thetae_pres = _get_refvals(referent, "fld_thetae_pres", multi) fld_thetae_pres = np.squeeze(fld_thetae_pres) - eth = getvar(in_wrfnc, "eth", timeidx=timeidx) + eth = getvar(wrfin, "eth", timeidx=timeidx) interp_levels = [850,500,5] - field = vinterp(in_wrfnc, + field = vinterp(wrfin, field=eth, vert_coord="pressure", interp_levels=interp_levels, @@ -541,25 +765,31 @@ def make_interp_test(varname, wrf_in, referent, multi=False, return test -def extract_proj_params(wrfnc): +def extract_proj_params(wrfnc, timeidx=0): attrs = extract_global_attrs(wrfnc, ("MAP_PROJ", "TRUELAT1", "TRUELAT2", "STAND_LON", "POLE_LAT", "POLE_LON", "DX", "DY")) result = {key.lower(): val for key,val in viewitems(attrs)} + _timeidx = timeidx if is_multi_file(wrfnc): - wrfnc = wrfnc[0] + wrfnc0 = wrfnc[0] + num_times_per_file = len(wrfnc0.dimensions["Time"]) + file_idx = timeidx // num_times_per_file + _timeidx = timeidx % num_times_per_file + + wrfnc = wrfnc[file_idx] result["known_x"] = 0 result["known_y"] = 0 - result["ref_lat"] = wrfnc.variables["XLAT"][0,0,0] - result["ref_lon"] = wrfnc.variables["XLONG"][0,0,0] + result["ref_lat"] = wrfnc.variables["XLAT"][_timeidx,0,0] + result["ref_lon"] = wrfnc.variables["XLONG"][_timeidx,0,0] return result -def make_latlon_test(testid, wrf_in, referent, single, multi=False, repeat=3, - pynio=False): +def make_latlon_test(testid, dir, pattern, referent, single, + multi=False, pynio=False): def test(self): try: from netCDF4 import Dataset as NetCDF @@ -567,105 +797,141 @@ def make_latlon_test(testid, wrf_in, referent, single, multi=False, repeat=3, pass try: - from PyNIO import Nio + import Nio except: pass - if not multi: - timeidx = 0 - if not pynio: - in_wrfnc = NetCDF(wrf_in) - else: - # Note: Python doesn't like it if you reassign an outer scoped - # variable (wrf_in in this case) - if not wrf_in.endswith(".nc"): - wrf_file = wrf_in + ".nc" - else: - wrf_file = wrf_in - in_wrfnc = Nio.open_file(wrf_file) - else: - timeidx = None + timeidx = 0 if not multi else None + pat = os.path.join(dir, pattern) + wrf_files = glob.glob(pat) + wrf_files.sort() + + refnc = NetCDF(referent) + try: + refnc.set_always_mask(False) + except: + pass + + wrfin = [] + for fname in wrf_files: if not pynio: - nc = NetCDF(wrf_in) - in_wrfnc = [nc for i in xrange(repeat)] + f = NetCDF(fname) + try: + f.set_auto_mask(False) + except: + pass + wrfin.append(f) else: - if not wrf_in.endswith(".nc"): - wrf_file = wrf_in + ".nc" + if not fname.endswith(".nc"): + _fname = fname + ".nc" else: - wrf_file = wrf_in - nc = Nio.open_file(wrf_file) - in_wrfnc = [nc for i in xrange(repeat)] - - refnc = NetCDF(referent) + _fname = fname + f = Nio.open_file(_fname) + wrfin.append(f) if testid == "xy": - # Since this domain is not moving, the reference values are the - # same whether there are multiple or single files - ref_vals = refnc.variables["ij"][:] + # Lats/Lons taken from NCL script, just hard-coding for now - lats = [-55, -60, -65] - lons = [25, 30, 35] + lats = [22.0, 25.0, 27.0] + lons = [-90.0, -87.5, -83.75] # Just call with a single lat/lon if single: - xy = ll_to_xy(in_wrfnc, lats[0], lons[0]) - xy = xy + 1 # NCL uses fortran indexing + timeidx = 8 + ref_vals = refnc.variables["xy2"][:] + + xy = ll_to_xy(wrfin, lats[0], lons[0], timeidx=timeidx, + as_int=True) ref = ref_vals[:,0] nt.assert_allclose(to_np(xy), ref) # Next make sure the 'proj' version works - projparams = extract_proj_params(in_wrfnc) - xy_proj = ll_to_xy_proj(lats[0], lons[0], **projparams) + projparams = extract_proj_params(wrfin, timeidx=timeidx) + xy_proj = ll_to_xy_proj(lats[0], lons[0], + as_int=True, + **projparams) - nt.assert_allclose(to_np(xy_proj), to_np(xy-1)) + nt.assert_allclose(to_np(xy_proj), to_np(xy)) else: - xy = ll_to_xy(in_wrfnc, lats, lons) - xy = xy + 1 # NCL uses fortran indexing + ref_vals = refnc.variables["xy1"][:] + xy = ll_to_xy(wrfin, lats, lons, timeidx=None, as_int=False) + ref = ref_vals[:] nt.assert_allclose(to_np(xy), ref) - # Next make sure the 'proj' version works - projparams = extract_proj_params(in_wrfnc) - xy_proj = ll_to_xy_proj(lats, lons, **projparams) + if xy.ndim > 2: + # Moving nest + is_moving = True + numtimes = xy.shape[-2] + else: + is_moving = False + numtimes = 1 - nt.assert_allclose(to_np(xy_proj), to_np(xy-1)) + for tidx in range(9): + + # Next make sure the 'proj' version works + projparams = extract_proj_params(wrfin, timeidx=tidx) + xy_proj = ll_to_xy_proj(lats, lons, as_int=False, + **projparams) + + if is_moving: + idxs = (slice(None), tidx, slice(None)) + else: + idxs = (slice(None),) + + nt.assert_allclose(to_np(xy_proj), to_np(xy[idxs])) else: - # Since this domain is not moving, the reference values are the - # same whether there are multiple or single files - ref_vals = refnc.variables["ll"][:] - # 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 - i_s = np.asarray([10, 100, 150], int) - 1 - j_s = np.asarray([10, 100, 150], int) - 1 + x_s = np.asarray([10, 50, 90], int) + y_s = np.asarray([10, 50, 90], int) if single: - ll = xy_to_ll(in_wrfnc, i_s[0], j_s[0]) + timeidx = 8 + ref_vals = refnc.variables["ll2"][:] + ll = xy_to_ll(wrfin, x_s[0], y_s[0], timeidx=timeidx) ref = ref_vals[::-1,0] nt.assert_allclose(to_np(ll), ref) # Next make sure the 'proj' version works - projparams = extract_proj_params(in_wrfnc) - ll_proj = xy_to_ll_proj(i_s[0], j_s[0], **projparams) + projparams = extract_proj_params(wrfin, timeidx=8) + ll_proj = xy_to_ll_proj(x_s[0], y_s[0], **projparams) nt.assert_allclose(to_np(ll_proj), to_np(ll)) + + else: - ll = xy_to_ll(in_wrfnc, i_s, j_s) + ref_vals = refnc.variables["ll1"][:] + ll = xy_to_ll(wrfin, x_s, y_s, timeidx=None) ref = ref_vals[::-1,:] nt.assert_allclose(to_np(ll), ref) - # Next make sure the 'proj' version works - projparams = extract_proj_params(in_wrfnc) - ll_proj = xy_to_ll_proj(i_s, j_s, **projparams) + if ll.ndim > 2: + # Moving nest + is_moving = True + numtimes = ll.shape[-2] + else: + is_moving = False + numtimes = 1 - nt.assert_allclose(to_np(ll_proj), to_np(ll)) + for tidx in range(numtimes): + # Next make sure the 'proj' version works + projparams = extract_proj_params(wrfin, timeidx=tidx) + ll_proj = xy_to_ll_proj(x_s, y_s, **projparams) + + if is_moving: + idxs = (slice(None), tidx, slice(None)) + else: + idxs = (slice(None),) + + nt.assert_allclose(to_np(ll_proj), to_np(ll[idxs])) return test @@ -691,86 +957,90 @@ if __name__ == "__main__": "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", "cfrac", "zstag", "geopt_stag"] + "wa", "uvmet10", "uvmet", "z", "cfrac", "zstag", "geopt_stag", + "height_agl"] interp_methods = ["interplevel", "vertcross", "interpline", "vinterp"] latlon_tests = ["xy", "ll"] - try: - import netCDF4 - except ImportError: - pass - else: - for var in wrf_vars: - if var in ignore_vars: - continue - - test_func1 = make_test(var, TEST_FILE, OUT_NC_FILE) - test_func2 = make_test(var, TEST_FILE, OUT_NC_FILE, multi=True) - setattr(WRFVarsTest, 'test_{0}'.format(var), test_func1) - setattr(WRFVarsTest, 'test_multi_{0}'.format(var), test_func2) - - for method in interp_methods: - test_interp_func1 = make_interp_test(method, TEST_FILE, - OUT_NC_FILE) - test_interp_func2 = make_interp_test(method, TEST_FILE, - OUT_NC_FILE, multi=True) - setattr(WRFInterpTest, 'test_{0}'.format(method), - test_interp_func1) - setattr(WRFInterpTest, 'test_multi_{0}'.format(method), - test_interp_func2) - - for testid in latlon_tests: - for single in (True, False): - for multi in (True, False): - test_ll_func = make_latlon_test(testid, TEST_FILE, - OUT_NC_FILE, - single=single, multi=multi, - repeat=3, pynio=False) - multistr = "" if not multi else "_multi" - singlestr = "_nosingle" if not single else "_single" - test_name = "test_{}{}{}".format(testid, singlestr, - multistr) - setattr(WRFLatLonTest, test_name, test_ll_func) + for dir, ref_nc_file, nest in zip(DIRS, REF_NC_FILES, NEST): + try: + import netCDF4 + except ImportError: + pass + else: + for var in wrf_vars: + if var in ignore_vars: + continue - try: - import PyNIO - except ImportError: - pass - else: - for var in wrf_vars: - if var in ignore_vars: - continue - - test_func1 = make_test(var, TEST_FILE, OUT_NC_FILE, pynio=True) - test_func2 = make_test(var, TEST_FILE, OUT_NC_FILE, multi=True, - pynio=True) - setattr(WRFVarsTest, 'test_pynio_{0}'.format(var), test_func1) - setattr(WRFVarsTest, 'test_pynio_multi_{0}'.format(var), - test_func2) - - for method in interp_methods: - test_interp_func1 = make_interp_test(method, TEST_FILE, - OUT_NC_FILE) - test_interp_func2 = make_interp_test(method, TEST_FILE, - OUT_NC_FILE, multi=True) - setattr(WRFInterpTest, 'test_pynio_{0}'.format(method), - test_interp_func1) - setattr(WRFInterpTest, 'test_pynio_multi_{0}'.format(method), - test_interp_func2) - - for testid in latlon_tests: - for single in (True, False): - for multi in (True, False): - test_ll_func = make_latlon_test(testid, TEST_FILE, - OUT_NC_FILE, - single=single, multi=multi, - repeat=3, pynio=False) - multistr = "" if not multi else "_multi" - singlestr = "_nosingle" if not single else "_single" - test_name = "test_pynio_{}{}{}".format(testid, - singlestr, - multistr) - setattr(WRFLatLonTest, test_name, test_ll_func) + test_func1 = make_test(var, dir, PATTERN, ref_nc_file) + test_func2 = make_test(var, dir, PATTERN, ref_nc_file, multi=True) + setattr(WRFVarsTest, 'test_{0}_{1}'.format(nest,var), test_func1) + setattr(WRFVarsTest, 'test_{0}_multi_{1}'.format(nest,var), test_func2) + + for method in interp_methods: + test_interp_func1 = make_interp_test(method, dir, PATTERN, + ref_nc_file) + test_interp_func2 = make_interp_test(method, dir, PATTERN, + ref_nc_file, multi=True) + setattr(WRFInterpTest, 'test_{0}_{1}'.format(nest,method), + test_interp_func1) + setattr(WRFInterpTest, 'test_{0}_multi_{1}'.format(nest,method), + test_interp_func2) + + for testid in latlon_tests: + for single in (True, False): + for multi in (True, False): + test_ll_func = make_latlon_test(testid, dir, PATTERN, + ref_nc_file, + single=single, + multi=multi, + pynio=False) + multistr = "" if not multi else "_multi" + singlestr = "_nosingle" if not single else "_single" + test_name = "test_{}_{}{}{}".format(nest, testid, singlestr, + multistr) + setattr(WRFLatLonTest, test_name, test_ll_func) + + try: + import PyNIO + except ImportError: + pass + else: + for var in wrf_vars: + if var in ignore_vars: + continue + + test_func1 = make_test(var, dir, PATTERN, ref_nc_file, pynio=True) + test_func2 = make_test(var, dir, PATTERN, ref_nc_file, multi=True, + pynio=True) + setattr(WRFVarsTest, 'test_pynio_{0}_{1}'.format(nest,var), test_func1) + setattr(WRFVarsTest, 'test_pynio_{0}_multi_{1}'.format(nest,var), + test_func2) + + for method in interp_methods: + test_interp_func1 = make_interp_test(method, dir, PATTERN, + ref_nc_file) + test_interp_func2 = make_interp_test(method, dir, PATTERN, + ref_nc_file, multi=True) + setattr(WRFInterpTest, 'test_pynio_{0}_{1}'.format(nest,method), + test_interp_func1) + setattr(WRFInterpTest, 'test_pynio_{0}_multi_{1}'.format(nest,method), + test_interp_func2) + + for testid in latlon_tests: + for single in (True, False): + for multi in (True, False): + test_ll_func = make_latlon_test(testid, dir, PATTERN, + ref_nc_file, + single=single, + multi=multi, + pynio=False) + multistr = "" if not multi else "_multi" + singlestr = "_nosingle" if not single else "_single" + test_name = "test_pynio_{}_{}{}{}".format(nest, testid, + singlestr, + multistr) + setattr(WRFLatLonTest, test_name, test_ll_func) ut.main() \ No newline at end of file diff --git a/test/varcache.py b/test/varcache.py index 1e0fe3d..f8ba15d 100644 --- a/test/varcache.py +++ b/test/varcache.py @@ -4,9 +4,9 @@ import time from netCDF4 import Dataset from wrf import getvar, ALL_TIMES, extract_vars -wrf_filenames = ["/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/wrfout_d02_2005-08-28_00:00:00", - "/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/wrfout_d02_2005-08-28_12:00:00", - "/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/wrfout_d02_2005-08-29_00:00:00"] +wrf_filenames = ["/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"] wrfin = [Dataset(x) for x in wrf_filenames]