diff --git a/.gitignore b/.gitignore index 868dff8..042b9bd 100644 --- a/.gitignore +++ b/.gitignore @@ -46,4 +46,10 @@ doc/build doc/source/internal_api/generated doc/source/user_api/generated test/ipynb/.ipynb_checkpoints +fortran/omp.f90 +fortran/wrffortran.pyf +fortran/*.mod +build +.settings +src/wrf_python.egg-info diff --git a/build_scripts/gnu_no_omp.sh b/build_scripts/gnu_no_omp.sh new file mode 100755 index 0000000..6f73784 --- /dev/null +++ b/build_scripts/gnu_no_omp.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +cd ../fortran +gfortran -E ompgen.F90 -cpp -o omp.f90 +f2py *.f90 -m _wrffortran -h wrffortran.pyf --overwrite-signature +cd .. +python setup.py clean --all +python setup.py config_fc --f90flags="-mtune=generic" build_ext build diff --git a/build_scripts/gnu_omp.sh b/build_scripts/gnu_omp.sh new file mode 100755 index 0000000..cf66f27 --- /dev/null +++ b/build_scripts/gnu_omp.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +cd ../fortran +gfortran -E ompgen.F90 -fopenmp -cpp -o omp.f90 +f2py *.f90 -m _wrffortran -h wrffortran.pyf --overwrite-signature --no-wrap-functions +cd .. +python setup.py clean --all +python setup.py config_fc --f90flags="-mtune=generic -fopenmp" build_ext --libraries="gomp" build + diff --git a/doc/source/_templates/product_table.txt b/doc/source/_templates/product_table.txt index 8e92724..c49c3ac 100644 --- a/doc/source/_templates/product_table.txt +++ b/doc/source/_templates/product_table.txt @@ -1,240 +1,256 @@ -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| Variable Name | Description | Available Units | Additional Keyword Arguments | -+====================+===============================================================+=============================+===============================================================================================+ -| avo | Absolute Vorticity | 10-5 s-1 | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| eth/theta_e | Equivalent Potential Temperature | K | **units** (str) : Set to desired units. Default is *'K'*. | -| | | | | -| | | degC | | -| | | | | -| | | 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_3d | 3D cape and cin | J kg-1 | **missing** (float): Fill value for output only | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| ctt | Cloud Top Temperature | degC | **units** (str) : Set to desired units. Default is *'degC'*. | -| | | | | -| | | K | | -| | | | | -| | | degF | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| cloudfrac | Cloud Fraction | % | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| dbz | Reflectivity | dBZ | **do_variant** (boolean): Set to True to enable variant calculation. Default is *False*. | -| | | | | -| | | | **do_liqskin** (boolean): Set to True to enable liquid skin calculation. Default is *False*. | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| mdbz | Maximum Reflectivity | dBZ | **do_variant** (boolean): Set to True to enable variant calculation. Default is *False*. | -| | | | | -| | | | **do_liqskin** (boolean): Set to True to enable liquid skin calculation. Default is *False*. | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| geopt/geopotential | Full Model Geopotential | m2 s-2 | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| helicity | Storm Relative Helicity | m2 s-2 | **top** (float): The top level for the calculation in meters. Default is *3000.0*. | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| lat | Latitude | decimal degrees | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| lon | Longitude | decimal degrees | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| omg/omega | Omega | Pa s-1 | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| p/pres | Full Model Pressure | Pa | **units** (str) : Set to desired units. Default is *'Pa'*. | -| | | | | -| | (in specified units) | hPa | | -| | | | | -| | | mb | | -| | | | | -| | | torr | | -| | | | | -| | | mmhg | | -| | | | | -| | | atm | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| pressure | Full Model Pressure (hPa) | hPa | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| pvo | Potential Vorticity | PVU | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| pw | Precipitable Water | kg m-2 | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| rh | Relative Humidity | % | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| rh2 | 2m Relative Humidity | % | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| slp | Sea Level Pressure | hPa | **units** (str) : Set to desired units. Default is *'hPa'*. | -| | | | | -| | | hPa | | -| | | | | -| | | mb | | -| | | | | -| | | torr | | -| | | | | -| | | mmhg | | -| | | | | -| | | atm | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| ter | Model Terrain Height | m | **units** (str) : Set to desired units. Default is *'m'*. | -| | | | | -| | | km | | -| | | | | -| | | dm | | -| | | | | -| | | ft | | -| | | | | -| | | mi | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| td2 | 2m Dew Point Temperature | degC | **units** (str) : Set to desired units. Default is *'degC'*. | -| | | | | -| | | K | | -| | | | | -| | | degF | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| td | Dew Point Temperature | degC | **units** (str) : Set to desired units. Default is *'degC'*. | -| | | | | -| | | K | | -| | | | | -| | | degF | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| tc | Temperature in Celsius | degC | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| th/theta | Potential Temperature | K | **units** (str) : Set to desired units. Default is *'K'*. | -| | | | | -| | | degC | | -| | | | | -| | | degF | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| temp | Temperature (in specified units) | K | **units** (str) : Set to desired units. Default is *'K'*. | -| | | | | -| | | degC | | -| | | | | -| | | degF | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| tk | Temperature in Kelvin | K | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| times | Times in the File or Sequence | | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| xtimes | XTIME Coordinate | minutes since | | -| | | | | -| | (if applicable) | start of | | -| | | | | -| | | model run | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| tv | Virtual Temperature | K | **units** (str) : Set to desired units. Default is *'K'*. | -| | | | | -| | | degC | | -| | | | | -| | | degF | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| twb | Wet Bulb Temperature | K | **units** (str) : Set to desired units. Default is *'K'*. | -| | | | | -| | | degC | | -| | | | | -| | | degF | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| updraft_helicity | Updraft Helicity | m2 s-2 | **bottom** (float): The bottom level for the calculation in meters. Default is *2000.0*. | -| | | | | -| | | | **top** (float): The top level for the calculation in meters. Default is *5000.0*. | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| ua | U-component of Wind on Mass Points | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | -| | | | | -| | | km h-1 | | -| | | | | -| | | mi h-1 | | -| | | | | -| | | kt | | -| | | | | -| | | ft s-1 | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| va | V-component of Wind on Mass Points | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | -| | | | | -| | | km h-1 | | -| | | | | -| | | mi h-1 | | -| | | | | -| | | kt | | -| | | | | -| | | ft s-1 | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| wa | W-component of Wind on Mass Points | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | -| | | | | -| | | km h-1 | | -| | | | | -| | | mi h-1 | | -| | | | | -| | | kt | | -| | | | | -| | | ft s-1 | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| uvmet10 | 10 m U and V Components of Wind | 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 | U and V Components of Wind | 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_wdir | Wind Speed and Direction (wind_from_direction) | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | -| | | | | -| | in Grid Coordinates | km h-1 | | -| | | | | -| | | mi h-1 | | -| | | | | -| | | kt | | -| | | | | -| | | ft s-1 | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| wspd_wdir10 | 10m Wind Speed and Direction (wind_from_direction) | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | -| | | | | -| | in Grid Coordinates | km h-1 | | -| | | | | -| | | mi h-1 | | -| | | | | -| | | kt | | -| | | | | -| | | ft s-1 | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| uvmet_wspd_wdir | Wind Speed and Direction (wind_from_direction) | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | -| | | | | -| | Rotated to Earth Coordinates | km h-1 | | -| | | | | -| | | mi h-1 | | -| | | | | -| | | kt | | -| | | | | -| | | ft s-1 | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| uvmet10_wspd_wdir | 10m Wind Speed and Direction (wind_from_direction) | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | -| | | | | -| | Rotated to Earth Coordinates | km h-1 | | -| | | | | -| | | mi h-1 | | -| | | | | -| | | kt | | -| | | | | -| | | ft s-1 | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| z/height | Full Model Height on Mass Levels | m | **msl** (boolean): Set to False to return AGL values. True is for MSL. Default is *True*. | -| | | | | -| | | km | **units** (str) : Set to desired units. Default is *'m'*. | -| | | | | -| | | dm | | -| | | | | -| | | ft | | -| | | | | -| | | mi | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ - - ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Variable Name | Description | Available Units | Additional Keyword Arguments | ++====================+===============================================================+=============================+=========================================================================================================================================================+ +| avo | Absolute Vorticity | 10-5 s-1 | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| eth/theta_e | Equivalent Potential Temperature | K | **units** (str) : Set to desired units. Default is *'K'*. | +| | | | | +| | | degC | | +| | | | | +| | | 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_3d | 3D cape and cin | J kg-1 | **missing** (float): Fill value for output only | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ctt | Cloud Top Temperature | degC | **units** (str) : Set to desired units. Default is *'degC'*. | +| | | | | +| | | K | | +| | | | | +| | | degF | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| cloudfrac | Cloud Fraction | % | **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) | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| dbz | Reflectivity | dBZ | **do_variant** (boolean): Set to True to enable variant calculation. Default is *False*. | +| | | | | +| | | | **do_liqskin** (boolean): Set to True to enable liquid skin calculation. Default is *False*. | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| mdbz | Maximum Reflectivity | dBZ | **do_variant** (boolean): Set to True to enable variant calculation. Default is *False*. | +| | | | | +| | | | **do_liqskin** (boolean): Set to True to enable liquid skin calculation. Default is *False*. | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| geopt/geopotential | Geopotential for the Mass Grid | m2 s-2 | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| geopt_stag | Geopotential for the Vertically Staggered Grid | m2 s-2 | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| helicity | Storm Relative Helicity | m2 s-2 | **top** (float): The top level for the calculation in meters. Default is *3000.0*. | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| lat | Latitude | decimal degrees | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| lon | Longitude | decimal degrees | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| omg/omega | Omega | Pa s-1 | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| p/pres | Full Model Pressure | Pa | **units** (str) : Set to desired units. Default is *'Pa'*. | +| | | | | +| | (in specified units) | hPa | | +| | | | | +| | | mb | | +| | | | | +| | | torr | | +| | | | | +| | | mmhg | | +| | | | | +| | | atm | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| pressure | Full Model Pressure (hPa) | hPa | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| pvo | Potential Vorticity | PVU | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| pw | Precipitable Water | kg m-2 | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| rh | Relative Humidity | % | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| rh2 | 2m Relative Humidity | % | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| slp | Sea Level Pressure | hPa | **units** (str) : Set to desired units. Default is *'hPa'*. | +| | | | | +| | | hPa | | +| | | | | +| | | mb | | +| | | | | +| | | torr | | +| | | | | +| | | mmhg | | +| | | | | +| | | atm | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ter | Model Terrain Height | m | **units** (str) : Set to desired units. Default is *'m'*. | +| | | | | +| | | km | | +| | | | | +| | | dm | | +| | | | | +| | | ft | | +| | | | | +| | | mi | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| td2 | 2m Dew Point Temperature | degC | **units** (str) : Set to desired units. Default is *'degC'*. | +| | | | | +| | | K | | +| | | | | +| | | degF | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| td | Dew Point Temperature | degC | **units** (str) : Set to desired units. Default is *'degC'*. | +| | | | | +| | | K | | +| | | | | +| | | degF | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| tc | Temperature in Celsius | degC | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| th/theta | Potential Temperature | K | **units** (str) : Set to desired units. Default is *'K'*. | +| | | | | +| | | degC | | +| | | | | +| | | degF | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| temp | Temperature (in specified units) | K | **units** (str) : Set to desired units. Default is *'K'*. | +| | | | | +| | | degC | | +| | | | | +| | | degF | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| tk | Temperature in Kelvin | K | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| times | Times in the File or Sequence | | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| xtimes | XTIME Coordinate | minutes since | | +| | | | | +| | (if applicable) | start of | | +| | | | | +| | | model run | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| tv | Virtual Temperature | K | **units** (str) : Set to desired units. Default is *'K'*. | +| | | | | +| | | degC | | +| | | | | +| | | degF | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| twb | Wet Bulb Temperature | K | **units** (str) : Set to desired units. Default is *'K'*. | +| | | | | +| | | degC | | +| | | | | +| | | degF | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| updraft_helicity | Updraft Helicity | m2 s-2 | **bottom** (float): The bottom level for the calculation in meters. Default is *2000.0*. | +| | | | | +| | | | **top** (float): The top level for the calculation in meters. Default is *5000.0*. | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ua | U-component of Wind on Mass Points | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | +| | | | | +| | | km h-1 | | +| | | | | +| | | mi h-1 | | +| | | | | +| | | kt | | +| | | | | +| | | ft s-1 | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| va | V-component of Wind on Mass Points | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | +| | | | | +| | | km h-1 | | +| | | | | +| | | mi h-1 | | +| | | | | +| | | kt | | +| | | | | +| | | ft s-1 | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| wa | W-component of Wind on Mass Points | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | +| | | | | +| | | km h-1 | | +| | | | | +| | | mi h-1 | | +| | | | | +| | | kt | | +| | | | | +| | | ft s-1 | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| uvmet10 | 10 m U and V Components of Wind | 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 | U and V Components of Wind | 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_wdir | Wind Speed and Direction (wind_from_direction) | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | +| | | | | +| | in Grid Coordinates | km h-1 | | +| | | | | +| | | mi h-1 | | +| | | | | +| | | kt | | +| | | | | +| | | ft s-1 | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| wspd_wdir10 | 10m Wind Speed and Direction (wind_from_direction) | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | +| | | | | +| | in Grid Coordinates | km h-1 | | +| | | | | +| | | mi h-1 | | +| | | | | +| | | kt | | +| | | | | +| | | ft s-1 | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| uvmet_wspd_wdir | Wind Speed and Direction (wind_from_direction) | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | +| | | | | +| | Rotated to Earth Coordinates | km h-1 | | +| | | | | +| | | mi h-1 | | +| | | | | +| | | kt | | +| | | | | +| | | ft s-1 | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| uvmet10_wspd_wdir | 10m Wind Speed and Direction (wind_from_direction) | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | +| | | | | +| | Rotated to Earth Coordinates | km h-1 | | +| | | | | +| | | mi h-1 | | +| | | | | +| | | kt | | +| | | | | +| | | ft s-1 | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| z/height | Model Height for Mass Grid | m | **msl** (boolean): Set to False to return AGL values. True is for MSL. Default is *True*. | +| | | | | +| | | km | **units** (str) : Set to desired units. Default is *'m'*. | +| | | | | +| | | dm | | +| | | | | +| | | ft | | +| | | | | +| | | mi | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| zstag | Model Height for Vertically Staggered Grid | m | **msl** (boolean): Set to False to return AGL values. True is for MSL. Default is *True*. | +| | | | | +| | | km | **units** (str) : Set to desired units. Default is *'m'*. | +| | | | | +| | | dm | | +| | | | | +| | | ft | | +| | | | | +| | | mi | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ diff --git a/doc/source/basic_usage.rst b/doc/source/basic_usage.rst index 3bd5eba..145eed4 100644 --- a/doc/source/basic_usage.rst +++ b/doc/source/basic_usage.rst @@ -1,6 +1,36 @@ How To Use ============ +Introduction +--------------- + +The API for wrf-python can be summarized as a variable computation/extraction +routine, several interpolation routines, and a few plotting helper utilities. +The API is kept as simple as possible to help minimize the +learning curve for new programmers, students, and scientists. In the future, +we plan to extend xarray for programmers desiring a more object oriented API, +but this remains a work in progress. + +The five most commonly used routines can be summarized as: + +- :meth:`wrf.getvar` - Extracts WRF-ARW NetCDF variables and + computes diagnostic variables that WRF does not compute (e.g. storm + relative helicity). This is the routine that you will use most often. + +- :meth:`wrf.interplevel` - Interpolates a three-dimensional field to a + horizontal plane at a specified level using simple (fast) linear + interpolation (e.g. 850 hPa temperature). + +- :meth:`wrf.vertcross` - Interpolates a three-dimensional field to a vertical + plane through a user-specified horizontal line (i.e. a cross section). + +- :meth:`wrf.interpline` - Interpolates a two-dimensional field to a + user-specified line. + +- :meth:`wrf.vinterp` - Interpolates a three-dimensional field to + user-specified 'surface' levels (e.g. theta-e levels). This is a smarter, + albeit slower, version of :meth:`wrf.interplevel`. + Basic Usage ---------------- @@ -10,8 +40,8 @@ Computing Diagnostic Variables ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The primary use for the :meth:`wrf.getvar` function is to return diagnostic -variables that require a calculation, since WRF does not produce these variables -natively. These diagnostics include CAPE, storm relative helicity, +variables that require a calculation, since WRF does not produce these +variables natively. These diagnostics include CAPE, storm relative helicity, omega, sea level pressure, etc. A table of all available diagnostics can be found here: :ref:`diagnostic-table`. @@ -355,9 +385,9 @@ Result: pole_lon=0.0) -Note how the 'Time' dimension was replaced with the 'file' dimension, due to the -numpy's automatic squeezing of the single 'Time' dimension. To maintain the -'Time' dimension, set the *squeeze* parameter to False. +Note how the 'Time' dimension was replaced with the 'file' dimension, due to +numpy's automatic squeezing of the single element 'Time' dimension. To maintain +the 'Time' dimension, set the *squeeze* parameter to False. .. code-block:: python @@ -1614,3 +1644,460 @@ Result: ] +.. _using_omp: + +Using OpenMP +------------------------- + +Beginning in version 1.1, the Fortran computational routines in wrf-python make +use of OpenMP directives. OpenMP enables the calculations to use multiple CPU +cores, which can improve performance. In order to use OpenMP features, +wrf-python has to be compiled with OpenMP enabled (most pre-built binary +installations will have this enabled). + +The Fortran computational routines have all been built using runtime +scheduling, instead of compile time scheduling, so that the user can choose the +scheduler type within their Python application. By default, the scheduling +type is set to :data:`wrf.OMP_SCHED_STATIC` using only 1 CPU core, so +wrf-python will behave similarly to the non-OpenMP built versions. For the most +part, the difference between the scheduling types is minimal, with the exception +being the :data:`wrf.OMP_SCHED_DYNAMIC` scheduler that is much slower due to +the additional overhead associated with it. For new users, using the default +scheduler should be sufficient. + + +Verifying that OpenMP is Enabled +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +To take advantage of the performance improvements offered by OpenMP, wrf-python +needs to have been compiled with OpenMP features enabled. The example below +shows how you can determine if OpenMP is enabled in your build of wrf-python. + +.. code-block:: python + + from __future__ import print_function + + from wrf import omp_enabled + + print(omp_enabled()) + + +Result: + +.. code-block:: none + + True + + +Determining the Number of Available Processors +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The example below shows how you can get the maximum number of processors +that are available on your system. + +.. code-block:: python + + from __future__ import print_function + + from wrf import omp_get_num_procs + + print(omp_get_num_procs()) + + +Result: + +.. code-block:: none + + 8 + + +Specifying the Number of Threads +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +To enable multicore support via OpenMP, specifying the maximum number +of OpenMP threads (i.e. CPU cores) is the only step that you need to take. + +In the example below, :meth:`wrf.omp_set_num_threads` is used to set the +maximum number of threads to use, and :meth:`wrf.omp_get_max_threads` is used +to retrieve (and print) the maximum number of threads used. + +.. note:: + + Although there is an OpenMP routine named :meth:`wrf.omp_get_num_threads`, + this routine will always return 1 when called from the sequential part of + the program. Use :meth:`wrf.omp_get_max_threads` to return the value set by + :meth:`wrf.omp_set_num_threads`. + +.. code-block:: python + + from __future__ import print_function + + from wrf import omp_set_num_threads, omp_get_max_threads + + omp_set_num_threads(4) + + print (omp_get_max_threads()) + + +Result: + +.. code-block:: none + + 4 + +Setting a Different Scheduler Type +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When an OpenMP directive is encountered in the Fortran code, a scheduler is +used to determine how the work is divided among the threads. All of the +Fortran routines are compiled to use a 'runtime' scheduler, which indicates +that the scheduler type (from the four listed below) is to be chosen at +runtime (i.e. inside a Python script) + +By default, the scheduler chosen is the :data:`wrf.OMP_SCHED_STATIC` scheduler, +which should be sufficient for most users. However, OpenMP and wrf-python +include the following options for the scheduler type: + +- :data:`wrf.OMP_SCHED_STATIC` +- :data:`wrf.OMP_SCHED_DYNAMIC` +- :data:`wrf.OMP_SCHED_GUIDED` +- :data:`wrf.OMP_SCHED_AUTO` + +Refer to the +`OpenMP Specification `_. +for more information about these scheduler types. In local testing, +:data:`wrf.OMP_SCHED_GUIDED` produced the best results, but +differences between :data:`wrf.OMP_SCHED_STATIC`, +:data:`wrf.OMP_SCHED_GUIDED`, and +:data:`wrf.OMP_SCHED_AUTO` were minor. However, +:data:`wrf.OMP_SCHED_DYNAMIC` produced noticeably slower results +due to the overhead of using a dynamic scheduler. + +When setting a scheduler type, the :meth:`wrf.omp_set_schedule` takes two +arguments. The first is the scheduler type (one from the list above), and the +second optional argument is a modifier, which is usually referred as the chunk +size. If the modifier/chunk_size is set to 0, then the OpenMP default +implementation is used. For :data:`wrf.OMP_SCHED_AUTO`, the +modifier is ignored. + +If you are new to OpenMP and all this sounds confusing, don't worry about +setting a scheduler type. The default static scheduler will be good enough. + +In the example below, the scheduler type is set to +:data:`wrf.OMP_SCHED_GUIDED` and uses the default chunk size of 0. The +scheduler type is then read back using :meth:`wrf.omp_get_schedule` +and printed. + +.. code-block:: python + + from __future__ import print_function + + from wrf import omp_set_schedule, omp_get_schedule, OMP_SCHED_GUIDED + + omp_set_schedule(OMP_SCHED_GUIDED, 0) + + sched, modifier = omp_get_schedule() + + print(sched, modifier) + + +Result: + +.. code-block:: none + + 3 1 + +Notice that the printed scheduler type (*sched* variable) is set to a +value of 3, which is the actual integer constant value for the +:data:`wrf.OMP_SCHED_GUIDED` scheduler type. The *modifier* is returned as a +value of 1, which is different than the 0 that was supplied to the +:meth:`wrf.omp_set_schedule` routine. This is because the 0 tells OpenMP to use +its own default value for the scheduler, which is 1 for this type of scheduler. + +.. _performance: + +Performance Tips +-------------------- + +Memory Issues with :data:`wrf.ALL_TIMES` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The use of :data:`wrf.ALL_TIMES` for the *timeidx* parameter to +:meth:`wrf.getvar` is convenient for computing diagnostic variables across +multiple files/times, but there is something that users should be aware of. +When :data:`wrf.ALL_TIMES` is set as the *timeidx* argument, all arrays used +in the computation are extracted for all times before the computation +is started. This can cause serious memory issues on smaller hardware systems +like laptops. + +In this example, the user wants to use a data set that is 289 x 39 x 300 x 300 +and compute z for the entire data set. The user is using a laptop with +8 GB of memory. + +.. code-block:: python + + from netCDF4 import Dataset + from wrf import getvar, ALL_TIMES + + file_list = [Dataset("/path/to/file1"), Dataset("/path/to/file2"),...] + z = getvar(file_list, "z", ALL_TIMES) + +Five hours later, the computation finished. What happened? + +In wrf-python, all of the computational routines use 8-byte REAL variables so +that both the 4-byte and 8-byte version of WRF output can be used. The +calculation for z extracts three variables (P, PHB, and HGT) and returns a +fourth array (RESULT). The RESULT will get cut in half to 4-byte REALs +after the computation, but needs an 8-byte REAL when the result is computed. + +Let's look at the approximate amount memory needed: + +**P**: 289 x 39 x 300 x 300 x 8 = 8,115,120,000 bytes (~8 GB!) + +**PHB**: 289 x 39 x 300 x 300 x 8 = 8,115,120,000 bytes (~8 GB!) + +**HGT**: 289 x 300 x 300 x 8 = 208,080,000 (~208 MB) + +**RESULT**: 289 x 39 x 300 x 300 x 8 = 8,115,120,000 bytes (~8 GB!) + +Yikes! So, in order to do this calculation using :data:`wrf.ALL_TIMES` as +the *timeidx*, over 24.2 GB are needed for this one calculation. When the +laptop runs out of memory, it begins using the hard drive for swap memory, +which runs hundreds of times slower than real memory. + +To fix this situation, it is better to allocate the output array yourself and +run the calculation for each time step in a loop +("loop-and-fill"). The required memory requirements change to: + +(Note: only need to store the result in a 4-byte REAL) + +**FINAL_RESULT**: 289 x 39 x 300 x 300 x 4 = 4,057560,000 bytes (~4 GB) + +(Note: the numbers below are for each loop iteration) + +**P**: 39 x 300 x 300 x 8 = 28,080,000 bytes (~28 MB) + +**PHB**: 39 x 300 x 300 x 8 = 28,080,000 bytes (~28 MB) + +**HGT**: 300 x 300 x 8 = 720,000 bytes (720 KB) + +**RESULT**: 39 x 300 x 300 x 8 = 28,080,000 bytes (~28 MB) + +Since the memory for the computation is deleted after each +loop iteration, the total memory usage drops to approximately 4.1 GB. + +The moral of the story is that you need to make sure that your system has +enough memory to extract everything it needs up front if you want to use +:data:`wrf.ALL_TIMES`, otherwise it is better to "loop-and-fill" yourself. + +Here is an example of the "loop-and-fill" technique: + +.. code-block:: python + + from __future__ import print_function, division + + import numpy as np + from netCDF4 import Dataset + from wrf import getvar, ALL_TIMES + + filename_list = ["/path/to/file1", "/path/to/file2",...] + + # Result shape (hard coded for this example) + result_shape = (289, 39, 300, 300) + + # Only need 4-byte floats + z_final = np.empty(result_shape, np.float32) + + # Modify this number if using more than 1 time per file + times_per_file = 1 + + for timeidx in range(result_shape[0]): + # Compute the file index and the time index inside the file + fileidx = timeidx // times_per_file + file_timeidx = timeidx % times_per_file + + f = Dataset(filename_list[fileidx]) + z = getvar(f, "z", file_timeidx) + + z_final[timeidx,:] = z[:] + f.close() + + +The *cache* Argument for :meth:`wrf.getvar` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If you have read through the documentation, you may have noticed that the +:meth:`wrf.getvar` routine contains a *cache* argument. What is this for? + +Internally, if metadata is turned on, a variable is extracted from the NetCDF +file and its metadata is copied to form the result's metadata. Often this +variable is one of the computation's function arguments, so rather than +spend time extracting the variable again for the computation, it is placed +in a cache (dictionary) and passed on to the computational function. + +What isn't widely known is that this cache argument can also be supplied by +end users wishing to speed up their application. This can be useful in +situations where numerous calculations are being performed on the same +data set. For many algorithms, the time needed to extract the arrays from the +NetCDF file is on par with the time needed to perform the calculation. If you +are computing numerous diagnostics, extracting the variables up front allows +you to only pay this extraction penalty once, rather than inside of each call +to :meth:`wrf.getvar`. + +The cache is nothing more than a dictionary where each key is the variable +name (e.g. "P") and the value is the :class:`xarray.DataArray` or +:class:`numpy.ndarray` variable. Creating the cache dictionary is easy, +since the :meth:`wrf.extract_vars` routine returns a dictionary for a +sequence of variables. + +.. note:: + + The *timeidx* parameter supplied to :meth:`extract_vars` + must be the same *timeidx* parameter that you plan to use for + :meth:`wrf.getvar`. Otherwise, it will crash with dimension mismatch errors. + +Some common variables that you can use to create an effective cache are: P, PB, +PH, PHB, T, QVAPOR, HGT, PSFC, U, V, W. + +Below is an example showing the same computation done with and without the +cache. The execution time is printed. The hardware used is a 2.8 GHz Intel Core +i7, which contains 4 CPU cores with 2 hyper threads (8 total threads). This +will be interpreted as 8 CPUs for OpenMP. + +.. code-block:: python + + from __future__ import print_function + + import time + from netCDF4 import Dataset + from wrf import getvar, ALL_TIMES, extract_vars + + # The first two files contain four times, the last file contains only one. + wrf_filenames = ["/path/to/wrfout_d02_2005-08-28_00:00:00", + "/path/to/wrfout_d02_2005-08-28_12:00:00", + "/path/to/wrfout_d02_2005-08-29_00:00:00"] + + wrfin = [Dataset(x) for x in wrf_filenames] + + start = time.time() + my_cache = extract_vars(wrfin, ALL_TIMES, ("P", "PSFC", "PB", "PH", "PHB", + "T", "QVAPOR", "HGT", "U", "V", + "W")) + end = time.time() + print ("Time taken to build cache: ", (end-start), "s") + + 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", "zstag", "geopt_stag") + + # No cache + start = time.time() + for var in vars: + v = getvar(wrfin, var, ALL_TIMES) + end = time.time() + no_cache_time = (end-start) + + print ("Time taken without variable cache: ", no_cache_time, "s") + + # With a cache + start = time.time() + for var in vars: + v = getvar(wrfin, var, ALL_TIMES, cache=my_cache) + end = time.time() + cache_time = (end-start) + + print ("Time taken with variable cache: ", cache_time, "s") + + improvement = ((no_cache_time-cache_time)/no_cache_time) * 100 + print ("The cache decreased computation time by: ", improvement, "%") + + +Result: + +.. code-block:: none + + Time taken to build cache: 0.28154706955 s + Time taken without variable cache: 11.0905270576 s + Time taken with variable cache: 8.25931215286 s + The cache decreased computation time by: 25.5282268378 % + +By removing the repeated extraction of common variables in the +:meth:`wrf.getvar` routine, for the single threaded case, the computation +time has been reduced by 25.5% in this particular example. + +Things get more interesting when OpenMP is turned on and set to use the +maximum number of processors (in this case 8 threads are used). + +.. code-block:: python + + from __future__ import print_function + + import time + from netCDF4 import Dataset + from wrf import (getvar, ALL_TIMES, extract_vars, + omp_set_num_threads, omp_get_num_procs) + + # The first two files contain four times, the last file contains only one. + wrf_filenames = ["/path/to/wrfout_d02_2005-08-28_00:00:00", + "/path/to/wrfout_d02_2005-08-28_12:00:00", + "/path/to/wrfout_d02_2005-08-29_00:00:00"] + + wrfin = [Dataset(x) for x in wrf_filenames] + + start = time.time() + my_cache = extract_vars(wrfin, ALL_TIMES, ("P", "PSFC", "PB", "PH", "PHB", + "T", "QVAPOR", "HGT", "U", "V", + "W")) + end = time.time() + print ("Time taken to build cache: ", (end-start), "s") + + omp_set_num_threads(omp_get_num_procs()) + + 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", "zstag", "geopt_stag") + + # No cache + start = time.time() + for var in vars: + v = getvar(wrfin, var, ALL_TIMES) + end = time.time() + no_cache_time = (end-start) + + print ("Time taken without variable cache: ", no_cache_time, "s") + + # With a cache + start = time.time() + for var in vars: + v = getvar(wrfin, var, ALL_TIMES, cache=my_cache) + end = time.time() + cache_time = (end-start) + + print ("Time taken with variable cache: ", cache_time, "s") + + improvement = ((no_cache_time-cache_time)/no_cache_time) * 100 + print ("The cache decreased computation time by: ", improvement, "%") + +Result: + +.. code-block:: none + + Time taken to build cache: 0.2700548172 s + Time taken without variable cache: 6.02652812004 s + Time taken with variable cache: 3.27777099609 s + The cache decreased computation time by: 45.6109565772 % + +In this example, 4 CPU cores (8 total threads) are used. When the cache is +used, the computation time drops by 45%, so almost half the time was spent +simply extracting variables from the NetCDF file. When compared to the +11.09 s needed to compute the single threaded case with no variable cache, the +computation time drops by roughly 70% (compared to 45% with 8 threads but +no cache). + +In summary, if you are computing a lot of diagnostic variables, consider using +the *cache* argument to improve performance, particularly if you want to +maximize your multithreaded performance with OpenMP. diff --git a/doc/source/citation.rst b/doc/source/citation.rst new file mode 100644 index 0000000..89c35c4 --- /dev/null +++ b/doc/source/citation.rst @@ -0,0 +1,26 @@ +.. _citation: + +Citation +================= + +WRF-Python has a Digital Object Identifier (DOI), which is a persistent +identifier for web-based resources. The wrf-python DOI, when used in URL form, +https://doi.org/10.5065/D6W094P1, provides a persistent link to the wrf-python +Github page. The benefit of DOIs is that they are widely accepted by academic +publishers as citable locators for scholarly objects. + +If you author a paper that involves data analysis with wrf-python, or +visualizations created with wrf-python, we would like to ask you to please +cite wrf-python. This helps us better understand the impact of the software on +the scientific community, which in turns helps us maintain support for the +effort. + +You can cite wrf-python using the following citation: + +.. code-block:: none + + Ladwig, W. (2017). wrf-python (Version x.x.x) [Software]. Boulder, Colorado: UCAR/NCAR. https://doi.org/10.5065/D6W094P1 + +.. note:: + + The version number x.x.x should be set to the version of wrf-python that you are using. \ No newline at end of file diff --git a/doc/source/conf.py b/doc/source/conf.py index 76aa4d0..b248434 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -23,14 +23,24 @@ except ImportError: class Mock(MagicMock): @classmethod def __getattr__(cls, name): - return Mock() + return MagicMock() MOCK_MODULES = ["numpy", "numpy.ma", "xarray", "cartopy", "pandas", "matplotlib", "netCDF4", "mpl_toolkits.basemap", "wrf._wrffortran"] sys.modules.update((mod_name, Mock()) for mod_name in MOCK_MODULES) -consts = {"DEFAULT_FILL" : 9.9692099683868690E36} +consts = {"DEFAULT_FILL" : 9.9692099683868690E36, + "DEFAULT_FILL_INT8" : -127, + "DEFAULT_FILL_INT16" : -32767, + "DEFAULT_FILL_INT32" : -2147483647, + "DEFAULT_FILL_INT64" : -9223372036854775806, + "DEFAULT_FILL_FLOAT" : 9.9692099683868690E36, + "DEFAULT_FILL_DOUBLE" : 9.9692099683868690E36, + "fomp_sched_static" : 1, + "fomp_sched_dynamic" : 2, + "fomp_sched_guided" : 3, + "fomp_sched_auto" : 4} class MockWrfConstants(object): def __init__(self): @@ -40,6 +50,8 @@ def mock_asscalar(val): return float(val) sys.modules["wrf._wrffortran"].wrf_constants = MockWrfConstants() +sys.modules["wrf._wrffortran"].omp_constants = MockWrfConstants() + sys.modules["numpy"].asscalar = mock_asscalar @@ -106,6 +118,7 @@ author = u'Bill Ladwig' # built documents. # # The short X.Y version. + import wrf version = wrf.__version__ # The full version, including alpha/beta/rc tags. diff --git a/doc/source/faq.rst b/doc/source/faq.rst index 74468d0..2c1cd28 100644 --- a/doc/source/faq.rst +++ b/doc/source/faq.rst @@ -61,6 +61,13 @@ In a future release of wrf-python, direct support for Dataset objects will be added and this will no longer be necessary. +Why is wrf-python taking hours to run? +--------------------------------------------- + +The most likely culprit is insufficient memory for the calculation you are +trying to perform. + +See :ref:`performance` for more information. diff --git a/doc/source/index.rst b/doc/source/index.rst index c0ae7c1..f43090a 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -45,8 +45,9 @@ Documentation ./api ./faq ./support + ./citation ./license - ./workshop + ./tutorial Indices and tables diff --git a/doc/source/internal_api/index.rst b/doc/source/internal_api/index.rst index 11fa312..a41775b 100644 --- a/doc/source/internal_api/index.rst +++ b/doc/source/internal_api/index.rst @@ -13,46 +13,46 @@ The routines below are called internally by :meth:`wrf.getvar`. :nosignatures: :toctree: ./generated/ - wrf.cape.get_2dcape - wrf.cape.get_3dcape - wrf.cloudfrac.get_cloudfrac - wrf.ctt.get_ctt - wrf.dbz.get_dbz - wrf.dbz.get_max_dbz - wrf.dewpoint.get_dp - wrf.dewpoint.get_dp_2m - wrf.geoht.get_geopt - wrf.geoht.get_height - wrf.helicity.get_srh - wrf.helicity.get_uh - wrf.omega.get_omega - wrf.pressure.get_pressure - wrf.pressure.get_pressure_hpa - wrf.pw.get_pw - wrf.rh.get_rh - wrf.rh.get_rh_2m - wrf.slp.get_slp - wrf.temp.get_theta - wrf.temp.get_temp - wrf.temp.get_eth - wrf.temp.get_tv - wrf.temp.get_tw - wrf.temp.get_tk - wrf.temp.get_tc - wrf.terrain.get_terrain - wrf.times.get_times - wrf.times.get_xtimes - wrf.uvmet.get_uvmet - wrf.uvmet.get_uvmet10 - wrf.uvmet.get_uvmet_wspd_wdir - wrf.uvmet.get_uvmet10_wspd_wdir - wrf.vorticity.get_avo - wrf.vorticity.get_pvo - wrf.wind.get_u_destag - wrf.wind.get_v_destag - wrf.wind.get_w_destag - wrf.wind.get_destag_wspd_wdir - wrf.wind.get_destag_wspd_wdir10 + wrf.g_cape.get_2dcape + wrf.g_cape.get_3dcape + wrf.g_cloudfrac.get_cloudfrac + wrf.g_ctt.get_ctt + wrf.g_dbz.get_dbz + wrf.g_dbz.get_max_dbz + wrf.g_dewpoint.get_dp + wrf.g_dewpoint.get_dp_2m + wrf.g_geoht.get_geopt + wrf.g_geoht.get_height + wrf.g_helicity.get_srh + wrf.g_helicity.get_uh + wrf.g_omega.get_omega + wrf.g_pressure.get_pressure + wrf.g_pressure.get_pressure_hpa + wrf.g_pw.get_pw + wrf.g_rh.get_rh + wrf.g_rh.get_rh_2m + wrf.g_slp.get_slp + wrf.g_temp.get_theta + wrf.g_temp.get_temp + wrf.g_temp.get_eth + wrf.g_temp.get_tv + wrf.g_temp.get_tw + wrf.g_temp.get_tk + wrf.g_temp.get_tc + wrf.g_terrain.get_terrain + wrf.g_times.get_times + wrf.g_times.get_xtimes + wrf.g_uvmet.get_uvmet + wrf.g_uvmet.get_uvmet10 + wrf.g_uvmet.get_uvmet_wspd_wdir + wrf.g_uvmet.get_uvmet10_wspd_wdir + wrf.g_vorticity.get_avo + wrf.g_vorticity.get_pvo + wrf.g_wind.get_u_destag + wrf.g_wind.get_v_destag + wrf.g_wind.get_w_destag + wrf.g_wind.get_destag_wspd_wdir + wrf.g_wind.get_destag_wspd_wdir10 ------------------------- diff --git a/doc/source/new.rst b/doc/source/new.rst index aec8cbe..e2ee510 100644 --- a/doc/source/new.rst +++ b/doc/source/new.rst @@ -4,6 +4,48 @@ What's New Releases ------------- +v1.1.0 +^^^^^^^^^^^^^^ + +- Release 1.1.0 +- Computational routines now support multiple cores using OpenMP. See + :ref:`using_omp` for details on how to use this new feature. +- The CAPE routines should be noticeably faster, even in the single threaded + case (thank you supreethms1809!). +- :meth:`wrf.getvar` now works correctly with non-gridded NetCDF variables +- The cloud fraction diagnostic has changed: + - Users can now select their own cloud threshold levels, and can choose + between a vertical coordinate defined as height (AGL), height (MSL), or + pressure. + - The default vertical coordinate type has been changed to be height (AGL). + This ensures that clouds appear over mountainous regions. If you need + the old behavior, set the *vert_type* argument to 'pressure'. + - Fixed a bug involving the cloud threshold search algorithm, where if the + surface was higher than the threshold for a cloud level, the algorithm + would use whatever was there before (uninitialized variable bug). This + caused some interesting visualization issues when plotted. Now, whenever + the surface is above a cloud level threshold, a fill value is used to + indicate that data is unavailable for that location. +- The cartopy object for LambertConformal should now work correctly in the + southern hemisphere. +- Fixed a bug with the PolarStereographic projection missing a geobounds + argument (thank you hanschen!). +- Renamed the modules containing the 'get_product' routines used + by :meth:`wrf.getvar` to avoid naming conflicts with the raw computational + routine names. Users should be using :meth:`wrf.getvar` instead of these + routines, but for those that imported the 'get_product' routines + directly, you will need to modify your code. +- Fixed a uniqueness issue with the internal coordinate cache that was causing + crashes when input data is changed to a different file in a jupyter notebook + cell. +- Added code to better support building wheels on Windows (thank you letmaik!) +- Improved support for scipy.io.netcdf objects. +- Added a new 'zstag' diagnostic that returns the height values for the + vertically staggered grid. +- A DOI is now available for wrf-python. Please cite wrf-python if you are + using it for your research. (See :ref:`citation`) + + v1.0.5 ^^^^^^^^^^^^^^ diff --git a/doc/source/tutorial.rst b/doc/source/tutorial.rst new file mode 100644 index 0000000..29b68b5 --- /dev/null +++ b/doc/source/tutorial.rst @@ -0,0 +1,25 @@ +Tutorials +============= + +NCAR occasionally provides tutorials for wrf-python at various times +throughout the year. + +Below are the links to the upcoming and past tutorials. + +Upcoming Tutorials +--------------------- + +.. toctree:: + :maxdepth: 1 + + tutorials/tutorial_03_2018.rst + + +Past Tutorials +------------------ + +.. toctree:: + :maxdepth: 1 + + tutorials/wrf_workshop_2017.rst + diff --git a/doc/source/tutorials/tutorial_03_2018.rst b/doc/source/tutorials/tutorial_03_2018.rst new file mode 100644 index 0000000..dfaea94 --- /dev/null +++ b/doc/source/tutorials/tutorial_03_2018.rst @@ -0,0 +1,77 @@ +WRF-Python Tutorial 2018 +========================= + +NCAR will be providing a four hour tutorial for wrf-python on Wednesday, March +7, 2018. The tutorial is free, but seating is limited to only 16 students, so +registration is required. + +The tutorial will take place at NCAR's corporate training center in Boulder, +Colorado. + +`Corporate Technical Training Center `_ +3085 Center Green Drive, Building CG-2, Room #3024 +Boulder, Colorado + +Overview +-------------- + +This tutorial provides an introduction to wrf-python. The tutorial is beginner +friendly for new users of wrf-python, but this is not an introduction to the Python +programming language (see :ref:`prereq`). Due to limited seating, if you +do not have any previous experience with Python, please do not register +for this tutorial. + +Students are encouraged to bring their own data sets, but data will be provided +if this is not an option. Students will be provided a jupyter notebook workbook +which can be modified to accommodate their data. + +Topics include: + +- How to install wrf-python via conda +- A brief introduction to jupyter notebook +- Overview of WRF data files +- WRF-Python basics +- Plotting with cartopy +- Overview of OpenMP features and other performance tips +- Open lab for students + + +Registration +--------------- + +The registration form is here: + +`Registration Form `_ + +Registration consists of a brief survey, which will help give the instructors +a brief overview of your background and will help tailor the tutorial to +your expectations. + +.. _prereq: + +Prerequisites +--------------- + +This tutorial assumes that you have basic knowledge of how to type commands +in to a command terminal using your preferred operating system. You +should know some basic directory commands like *cd*, *mkdir*, *cp*, *mv*. + +This tutorial assumes that you have prior experience programming in Python. +Below is a list of some Python concepts that you will see in the examples, +but don't worry if you aren't familiar with everything. + +- Opening a Python interpreter and entering commands. +- Importing packages via the import statement. +- Familiarity with some of the basic Python types: str, list, tuple, dict, bool, float, int, None. +- Creating a list, tuple, or dict with "[ ]", "( )", "{ }" syntax (e.g. my_list = [1,2,3,4,5]). +- Accessing dict/list/tuple items with the "x[ ]" syntax (e.g. my_list_item = my_list[0]). +- Slicing str/list/tuple with the ":" syntax (e.g. my_slice = my_list[1:3]). +- Using object methods and attributes with the "x.y" syntax (e.g. my_list.append(6)). +- Calling functions (e.g. result = some_function(x, y)) +- Familiarity with numpy would be helpful, as only a very brief introduction + is provided. +- Familiarity with matplotlib would be helpful, as only a very brief + introduction is provided. + + + diff --git a/doc/source/workshop.rst b/doc/source/tutorials/wrf_workshop_2017.rst similarity index 100% rename from doc/source/workshop.rst rename to doc/source/tutorials/wrf_workshop_2017.rst diff --git a/doc/source/user_api/index.rst b/doc/source/user_api/index.rst index 17b72e0..0832ca5 100644 --- a/doc/source/user_api/index.rst +++ b/doc/source/user_api/index.rst @@ -74,7 +74,6 @@ the array object to a compiled extension. :toctree: ./generated/ wrf.to_np - Variable Extraction Routines ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -110,7 +109,7 @@ The routines below are used to assist with plotting. wrf.get_pyngl wrf.cartopy_xlim wrf.cartopy_ylim - + Raw Diagnostic Routines ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -154,6 +153,82 @@ sure they are removed before calling these routines. wrf.omega wrf.pw +OpenMP Runtime Library Routines +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The routines below are the OpenMP runtime libraries that have been wrapped +for wrf-python. The entire library (OpenMP 3.x) has been wrapped, but many of +the routines are only useful inside of an OpenMP thread, so they aren't useful +from inside the Python interpreter. Also, the Fortran code in wrf-python is +fairly simple in terms of threading, so features like nested threads aren't +used. The documentation below is split in to the useful OpenMP functions and +the less useful functions. + +The documentation for each routine was taken directly from the +`OpenMP Specification `_. +Read the specification for more details about these routines. + +Useful OpenMP Routines +***************************** + +The routines below are useful when called from within a Python program. These +routines handle setting the number of threads, setting up the scheduler, +and timing. + +It is also important to note that the OpenMP directives within the Fortran +code all specify a runtime scheduler. This means that the user can control +the type of scheduling to use from within their Python application by using the +routines below. + +.. autosummary:: + :nosignatures: + :toctree: ./generated/ + + wrf.omp_enabled + wrf.omp_set_num_threads + wrf.omp_get_max_threads + wrf.omp_get_num_procs + wrf.omp_set_dynamic + wrf.omp_get_dynamic + wrf.omp_set_schedule + wrf.omp_get_schedule + wrf.omp_get_thread_limit + wrf.omp_get_wtime + wrf.omp_get_wtick + +Less Useful OpenMP Routines +******************************* + +The routines below are less useful because wrf-python does not use nested +parallelism and some of the routines are only applicable when called from +within an OpenMP thread. + +.. autosummary:: + :nosignatures: + :toctree: ./generated/ + + wrf.omp_get_num_threads + wrf.omp_get_thread_num + wrf.omp_in_parallel + wrf.omp_set_nested + wrf.omp_get_nested + wrf.omp_set_max_active_levels + wrf.omp_get_max_active_levels + wrf.omp_get_level + wrf.omp_get_ancestor_thread_num + wrf.omp_get_team_size + wrf.omp_get_active_level + wrf.omp_in_final + wrf.omp_init_lock + wrf.omp_init_nest_lock + wrf.omp_destroy_lock + wrf.omp_destroy_nest_lock + wrf.omp_set_lock + wrf.omp_set_nest_lock + wrf.omp_unset_lock + wrf.omp_unset_nest_lock + wrf.omp_test_lock + wrf.omp_test_nest_lock Configuration Routines ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -182,6 +257,7 @@ in one place. wrf.disable_pyngl wrf.set_cache_size wrf.get_cache_size + wrf.omp_enabled Miscellaneous Routines @@ -247,7 +323,7 @@ use a single point for an (x, y) or (lat, lon) location. wrf.CoordPair CoordPair Methods -~~~~~~~~~~~~~~~~~~~~~~~ +************************ .. autosummary:: :nosignatures: @@ -274,7 +350,7 @@ The classes below are used to hold the projection information in the 'projection' entry within a :attr:`xarray.DataArray.attrs` attribute. Projection Base Class -~~~~~~~~~~~~~~~~~~~~~~~~ +****************************** The base class for all map projection types. @@ -285,7 +361,7 @@ The base class for all map projection types. wrf.WrfProj Projection Base Class Methods -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +*********************************** The class methods for all projection types. @@ -303,7 +379,7 @@ The class methods for all projection types. Projection Subclasses -~~~~~~~~~~~~~~~~~~~~~~~~ +***************************** See :class:`wrf.WrfProj` for methods and attributes. diff --git a/fortran/calc_uh.f90 b/fortran/calc_uh.f90 index a893189..72acfe6 100644 --- a/fortran/calc_uh.f90 +++ b/fortran/calc_uh.f90 @@ -47,7 +47,7 @@ SUBROUTINE DCALCUH(nx, ny, nz, nzp1, zp, mapfct, dx, dy, uhmnhgt, uhmxhgt, us, & ! Misc local variables INTEGER :: i, j, k, kbot, ktop - REAL(KIND=8) :: twodx, twody, wgtlw, sum, wmean, wsum, wavg + REAL(KIND=8) :: twodx, twody, wgtlw, sum, wmean, wsum !,wavg REAL(KIND=8) :: helbot, heltop, wbot, wtop REAL(KIND=8) :: zbot, ztop @@ -61,21 +61,29 @@ SUBROUTINE DCALCUH(nx, ny, nz, nzp1, zp, mapfct, dx, dy, uhmnhgt, uhmxhgt, us, & twodx = 2.0*dx twody = 2.0*dy + + !$OMP PARALLEL + + !$OMP DO COLLAPSE(3) SCHEDULE(runtime) DO k=2,nz-2 DO j=2,ny-1 DO i=2,nx-1 - wavg = 0.5*(w(i,j,k)+w(i,j,k+1)) - tem1(i,j,k) = wavg*((vs(i+1,j,k) - vs(i-1,j,k))/(twodx*mapfct(i,j)) - & - (us(i,j+1,k) - us(i,j-1,k))/(twody*mapfct(i,j))) + !wavg = 0.5*(w(i,j,k)+w(i,j,k+1)) + tem1(i,j,k) = (0.5*(w(i,j,k)+w(i,j,k+1)))*((vs(i+1,j,k) - & + vs(i-1,j,k))/(twodx*mapfct(i,j)) - & + (us(i,j+1,k) - us(i,j-1,k))/(twody*mapfct(i,j))) tem2(i,j,k) = 0.5*(zp(i,j,k) + zp(i,j,k+1)) END DO END DO END DO + !$OMP END DO ! Integrate over depth uhminhgt to uhmxhgt AGL ! ! WRITE(6,'(a,f12.1,a,f12.1,a)') & ! 'Calculating UH from ',uhmnhgt,' to ',uhmxhgt,' m AGL' + !$OMP DO COLLAPSE(2) PRIVATE(i, j, k, zbot, ztop, kbot, ktop, & + !$OMP wgtlw, wbot, wtop, wsum, wmean, sum, helbot, heltop) SCHEDULE(runtime) DO j=2,ny-2 DO i=2,nx-2 zbot = zp(i,j,2) + uhmnhgt @@ -142,6 +150,9 @@ SUBROUTINE DCALCUH(nx, ny, nz, nzp1, zp, mapfct, dx, dy, uhmnhgt, uhmxhgt, us, & END IF END DO END DO + !$OMP END DO + + !$OMP END PARALLEL uh = uh*1000. ! Scale according to Kain et al. (2008) diff --git a/fortran/eqthecalc.f90 b/fortran/eqthecalc.f90 index a4cb6ba..65c069d 100644 --- a/fortran/eqthecalc.f90 +++ b/fortran/eqthecalc.f90 @@ -32,6 +32,8 @@ SUBROUTINE DEQTHECALC(qvp, tmk, prs, eth, miy, mjx, mkzh) REAL(KIND=8) :: tlcl INTEGER :: i, j, k + !$OMP PARALLEL DO COLLAPSE(3) PRIVATE(i, j, k, q, t, p, e, tlcl) & + !$OMP SCHEDULE(runtime) DO k = 1,mkzh DO j = 1,mjx DO i = 1,miy @@ -40,11 +42,12 @@ SUBROUTINE DEQTHECALC(qvp, tmk, prs, eth, miy, mjx, mkzh) p = prs(i,j,k)/100. e = q*p/(EPS + q) tlcl = TLCLC1/(LOG(t**TLCLC2/e) - TLCLC3) + TLCLC4 - eth(i,j,k) = t*(1000.D0/p)**(GAMMA*(1.D0 + GAMMAMD*q))* & + eth(i,j,k) = tmk(i,j,k)*(1000.D0/p)**(GAMMA*(1.D0 + GAMMAMD*q))* & EXP((THTECON1/tlcl - THTECON2)*q*(1.D0 + THTECON3*q)) END DO END DO END DO + !$OMP END PARALLEL DO RETURN diff --git a/fortran/omp.f90 b/fortran/omp.f90 new file mode 100644 index 0000000..dce6b1d --- /dev/null +++ b/fortran/omp.f90 @@ -0,0 +1,465 @@ + +MODULE omp_constants + INTEGER, PARAMETER :: fomp_sched_kind = 4 + INTEGER, PARAMETER :: fomp_lock_kind = 4 + INTEGER, PARAMETER :: fomp_nest_lock_kind = 8 + INTEGER(KIND=fomp_sched_kind), PARAMETER :: fomp_sched_static = 1 + INTEGER(KIND=fomp_sched_kind), PARAMETER :: fomp_sched_dynamic = 2 + INTEGER(KIND=fomp_sched_kind), PARAMETER :: fomp_sched_guided = 3 + INTEGER(KIND=fomp_sched_kind), PARAMETER :: fomp_sched_auto = 4 +END MODULE omp_constants + + +FUNCTION fomp_enabled() + + IMPLICIT NONE + + !f2py threadsafe + + LOGICAL :: fomp_enabled + + fomp_enabled = .FALSE. + +END FUNCTION fomp_enabled + + +SUBROUTINE fomp_set_num_threads(num_threads) + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER, INTENT(IN) :: num_threads + IF (.FALSE.) PRINT *, num_threads + +END SUBROUTINE fomp_set_num_threads + + +FUNCTION fomp_get_num_threads() + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER :: fomp_get_num_threads + + fomp_get_num_threads = -1 + +END FUNCTION fomp_get_num_threads + + +FUNCTION fomp_get_max_threads() + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER :: fomp_get_max_threads + + fomp_get_max_threads = -1 + +END FUNCTION fomp_get_max_threads + + +FUNCTION fomp_get_thread_num() + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER :: fomp_get_thread_num + + fomp_get_thread_num = -1 + +END FUNCTION fomp_get_thread_num + + +FUNCTION fomp_get_num_procs() + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER :: fomp_get_num_procs + + fomp_get_num_procs = -1 + +END FUNCTION fomp_get_num_procs + + +FUNCTION fomp_in_parallel() + + IMPLICIT NONE + + !f2py threadsafe + + LOGICAL :: fomp_in_parallel + + fomp_in_parallel = .FALSE. + +END FUNCTION fomp_in_parallel + + +SUBROUTINE fomp_set_dynamic(dynamic_threads) + + IMPLICIT NONE + + !f2py threadsafe + + LOGICAL, INTENT(IN) :: dynamic_threads + IF (.FALSE.) PRINT *, dynamic_threads + +END SUBROUTINE fomp_set_dynamic + + +FUNCTION fomp_get_dynamic() + + IMPLICIT NONE + + !f2py threadsafe + + LOGICAL :: fomp_get_dynamic + + fomp_get_dynamic = .FALSE. + +END FUNCTION fomp_get_dynamic + + +SUBROUTINE fomp_set_nested(nested) + + IMPLICIT NONE + + !f2py threadsafe + + LOGICAL, INTENT(IN) :: nested + IF (.FALSE.) PRINT *, nested + +END SUBROUTINE fomp_set_nested + + +FUNCTION fomp_get_nested() + + IMPLICIT NONE + + !f2py threadsafe + + LOGICAL :: fomp_get_nested + + fomp_get_nested = .FALSE. + +END FUNCTION fomp_get_nested + + +SUBROUTINE fomp_set_schedule(kind, modifier) + + USE omp_constants, ONLY : fomp_sched_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_sched_kind), INTENT(IN) :: kind + INTEGER, INTENT(IN) :: modifier + IF (.FALSE.) PRINT *, kind, modifier + +END SUBROUTINE fomp_set_schedule + + +SUBROUTINE fomp_get_schedule(kind, modifier) + + USE omp_constants, ONLY : fomp_sched_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_sched_kind), INTENT(OUT) :: kind + INTEGER, INTENT(OUT) :: modifier + + kind = -1 + modifier = -1 + +END SUBROUTINE fomp_get_schedule + + +FUNCTION fomp_get_thread_limit() + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER :: fomp_get_thread_limit + + fomp_get_thread_limit = -1 + +END FUNCTION fomp_get_thread_limit + + +SUBROUTINE fomp_set_max_active_levels(max_levels) + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER, INTENT(IN) :: max_levels + IF (.FALSE.) PRINT *, max_levels + +END SUBROUTINE fomp_set_max_active_levels + + +FUNCTION fomp_get_max_active_levels() + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER :: fomp_get_max_active_levels + + fomp_get_max_active_levels = -1 + +END FUNCTION fomp_get_max_active_levels + + +FUNCTION fomp_get_level() + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER :: fomp_get_level + + fomp_get_level = -1 + +END FUNCTION fomp_get_level + + +FUNCTION fomp_get_ancestor_thread_num(level) + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER, INTENT(IN) :: level + INTEGER :: fomp_get_ancestor_thread_num + IF (.FALSE.) PRINT *, level + + fomp_get_ancestor_thread_num = -1 + +END FUNCTION fomp_get_ancestor_thread_num + + +FUNCTION fomp_get_team_size(level) + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER, INTENT(IN) :: level + INTEGER :: fomp_get_team_size + IF (.FALSE.) PRINT *, level + + fomp_get_team_size = -1 + +END FUNCTION fomp_get_team_size + + +FUNCTION fomp_get_active_level() + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER :: fomp_get_active_level + + fomp_get_active_level = -1 + +END FUNCTION fomp_get_active_level + + +FUNCTION fomp_in_final() + + IMPLICIT NONE + + !f2py threadsafe + + LOGICAL :: fomp_in_final + + fomp_in_final = .FALSE. + +END FUNCTION fomp_in_final + + +SUBROUTINE fomp_init_lock(svar) + + USE omp_constants, ONLY : fomp_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_lock_kind), INTENT(OUT) :: svar + + svar = -1 + +END SUBROUTINE fomp_init_lock + + +SUBROUTINE fomp_init_nest_lock(nvar) + + USE omp_constants, ONLY : fomp_nest_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_nest_lock_kind), INTENT(OUT) :: nvar + + nvar = -1 + +END SUBROUTINE fomp_init_nest_lock + + +SUBROUTINE fomp_destroy_lock(svar) + + USE omp_constants, ONLY : fomp_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_lock_kind), INTENT(INOUT) :: svar + IF (.FALSE.) PRINT *, svar + +END SUBROUTINE fomp_destroy_lock + + +SUBROUTINE fomp_destroy_nest_lock(nvar) + + USE omp_constants, ONLY : fomp_nest_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_nest_lock_kind), INTENT(INOUT) :: nvar + IF (.FALSE.) PRINT *, nvar + +END SUBROUTINE fomp_destroy_nest_lock + + +SUBROUTINE fomp_set_lock(svar) + + USE omp_constants, ONLY : fomp_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_lock_kind), INTENT(INOUT) :: svar + IF (.FALSE.) PRINT *, svar + +END SUBROUTINE fomp_set_lock + + +SUBROUTINE fomp_set_nest_lock(nvar) + + USE omp_constants, ONLY : fomp_nest_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_nest_lock_kind), INTENT(INOUT) :: nvar + IF (.FALSE.) PRINT *, nvar + +END SUBROUTINE fomp_set_nest_lock + + +SUBROUTINE fomp_unset_lock(svar) + + USE omp_constants, ONLY : fomp_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_lock_kind), INTENT(INOUT) :: svar + IF (.FALSE.) PRINT *, svar + +END SUBROUTINE fomp_unset_lock + + +SUBROUTINE fomp_unset_nest_lock(nvar) + + USE omp_constants, ONLY : fomp_nest_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_nest_lock_kind), INTENT(INOUT) :: nvar + IF (.FALSE.) PRINT *, nvar + +END SUBROUTINE fomp_unset_nest_lock + + +FUNCTION fomp_test_lock(svar) + + USE omp_constants, ONLY : fomp_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_lock_kind), INTENT(INOUT) :: svar + LOGICAL :: fomp_test_lock + IF (.FALSE.) PRINT *, svar + + fomp_test_lock = .FALSE. + +END FUNCTION fomp_test_lock + + +FUNCTION fomp_test_nest_lock(nvar) + + USE omp_constants, ONLY : fomp_nest_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_nest_lock_kind), INTENT(INOUT) :: nvar + INTEGER :: fomp_test_nest_lock + IF (.FALSE.) PRINT *, nvar + + fomp_test_nest_lock = -1 + +END FUNCTION fomp_test_nest_lock + + +FUNCTION fomp_get_wtime() + + IMPLICIT NONE + + !f2py threadsafe + + REAL (KIND=8) :: fomp_get_wtime + + fomp_get_wtime = -1 + +END FUNCTION fomp_get_wtime + + +FUNCTION fomp_get_wtick() + + IMPLICIT NONE + + !f2py threadsafe + + REAL (KIND=8) :: fomp_get_wtick + + fomp_get_wtick = -1 + +END FUNCTION fomp_get_wtick + + diff --git a/fortran/ompgen.F90 b/fortran/ompgen.F90 new file mode 100644 index 0000000..6ae48e1 --- /dev/null +++ b/fortran/ompgen.F90 @@ -0,0 +1,753 @@ +MODULE omp_constants +#ifdef _OPENMP + USE omp_lib + ! f2py can't figure this out without me making a map of these kinds to + ! c types, so we're going to have to hard code the kinds below. + !INTEGER, PARAMETER :: fomp_sched_kind = omp_sched_kind + !INTEGER, PARAMETER :: fomp_nest_lock_kind = omp_nest_lock_kind + !INTEGER, PARAMETER :: fomp_lock_kind = omp_lock_kind + + ! Note: Defining these specifically is the only way I can get f2py to + ! work without doing the same thing in a mapping file. The values below + ! are for GNU on 64bit. This may not be entirely correct for non-GNU + ! compilers. In particular, it will be the locks that segfault/crash if + ! this is wrong. +#if defined(__GFORTRAN__) +#if (__SIZEOF_POINTER__ == 8) + INTEGER, PARAMETER :: fomp_sched_kind = 4 + INTEGER, PARAMETER :: fomp_lock_kind = 8 + INTEGER, PARAMETER :: fomp_nest_lock_kind = 8 +#else + INTEGER, PARAMETER :: fomp_sched_kind = 4 + INTEGER, PARAMETER :: fomp_lock_kind = 4 + INTEGER, PARAMETER :: fomp_nest_lock_kind = 4 +#endif +#elif defined(__INTEL_COMPILER_BUILD_DATE) +#if defined(__x86_64__) || defined(_M_X64) + INTEGER, PARAMETER :: fomp_sched_kind = 4 + INTEGER, PARAMETER :: fomp_lock_kind = 8 + INTEGER, PARAMETER :: fomp_nest_lock_kind = 8 +#else + INTEGER, PARAMETER :: fomp_sched_kind = 4 + INTEGER, PARAMETER :: fomp_lock_kind = 4 + INTEGER, PARAMETER :: fomp_nest_lock_kind = 4 +#endif +#elif defined(__PGI) +#if defined(__x86_64__) + INTEGER, PARAMETER :: fomp_sched_kind = 4 + INTEGER, PARAMETER :: fomp_lock_kind = 8 + INTEGER, PARAMETER :: fomp_nest_lock_kind = 8 +#else + INTEGER, PARAMETER :: fomp_sched_kind = 4 + INTEGER, PARAMETER :: fomp_lock_kind = 4 + INTEGER, PARAMETER :: fomp_nest_lock_kind = 4 +#endif +#endif + + INTEGER(KIND=fomp_sched_kind), PARAMETER :: fomp_sched_static = omp_sched_static + INTEGER(KIND=fomp_sched_kind), PARAMETER :: fomp_sched_dynamic = omp_sched_dynamic + INTEGER(KIND=fomp_sched_kind), PARAMETER :: fomp_sched_guided = omp_sched_guided + INTEGER(KIND=fomp_sched_kind), PARAMETER :: fomp_sched_auto = omp_sched_auto +#else + INTEGER, PARAMETER :: fomp_sched_kind = 4 + INTEGER, PARAMETER :: fomp_lock_kind = 4 + INTEGER, PARAMETER :: fomp_nest_lock_kind = 8 + INTEGER(KIND=fomp_sched_kind), PARAMETER :: fomp_sched_static = 1 + INTEGER(KIND=fomp_sched_kind), PARAMETER :: fomp_sched_dynamic = 2 + INTEGER(KIND=fomp_sched_kind), PARAMETER :: fomp_sched_guided = 3 + INTEGER(KIND=fomp_sched_kind), PARAMETER :: fomp_sched_auto = 4 +#endif + +END MODULE omp_constants + + +FUNCTION fomp_enabled() + + IMPLICIT NONE + + !f2py threadsafe + + LOGICAL :: fomp_enabled + +#ifdef _OPENMP + fomp_enabled = .TRUE. +#else + fomp_enabled = .FALSE. +#endif + +END FUNCTION fomp_enabled + + +SUBROUTINE fomp_set_num_threads(num_threads) +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER, INTENT(IN) :: num_threads + +#ifdef _OPENMP + CALL omp_set_num_threads(num_threads) +#else + IF (.FALSE.) PRINT *, num_threads +#endif + + +END SUBROUTINE fomp_set_num_threads + + +FUNCTION fomp_get_num_threads() +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER :: fomp_get_num_threads + +#ifdef _OPENMP + fomp_get_num_threads = omp_get_num_threads() +#else + fomp_get_num_threads = -1 +#endif + +END FUNCTION fomp_get_num_threads + + +FUNCTION fomp_get_max_threads() +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER :: fomp_get_max_threads + +#ifdef _OPENMP + fomp_get_max_threads = omp_get_max_threads() +#else + fomp_get_max_threads = -1 +#endif + +END FUNCTION fomp_get_max_threads + + +FUNCTION fomp_get_thread_num() +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER :: fomp_get_thread_num + +#ifdef _OPENMP + fomp_get_thread_num = omp_get_thread_num() +#else + fomp_get_thread_num = -1 +#endif + +END FUNCTION fomp_get_thread_num + + +FUNCTION fomp_get_num_procs() +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER :: fomp_get_num_procs + +#ifdef _OPENMP + fomp_get_num_procs = omp_get_num_procs() +#else + fomp_get_num_procs = -1 +#endif + +END FUNCTION fomp_get_num_procs + + +FUNCTION fomp_in_parallel() +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + LOGICAL :: fomp_in_parallel + +#ifdef _OPENMP + fomp_in_parallel = omp_in_parallel() +#else + fomp_in_parallel = .FALSE. +#endif + +END FUNCTION fomp_in_parallel + + +SUBROUTINE fomp_set_dynamic(dynamic_threads) +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + LOGICAL, INTENT(IN) :: dynamic_threads + +#ifdef _OPENMP + CALL omp_set_dynamic(dynamic_threads) +#else + IF (.FALSE.) PRINT *, dynamic_threads +#endif + +END SUBROUTINE fomp_set_dynamic + + +FUNCTION fomp_get_dynamic() +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + LOGICAL :: fomp_get_dynamic + +#ifdef _OPENMP + fomp_get_dynamic = omp_get_dynamic() +#else + fomp_get_dynamic = .FALSE. +#endif + +END FUNCTION fomp_get_dynamic + + +SUBROUTINE fomp_set_nested(nested) +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + LOGICAL, INTENT(IN) :: nested + +#ifdef _OPENMP + CALL omp_set_nested(nested) +#else + IF (.FALSE.) PRINT *, nested +#endif + +END SUBROUTINE fomp_set_nested + + +FUNCTION fomp_get_nested() +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + LOGICAL :: fomp_get_nested + +#ifdef _OPENMP + fomp_get_nested = omp_get_nested() +#else + fomp_get_nested = .FALSE. +#endif + +END FUNCTION fomp_get_nested + + +SUBROUTINE fomp_set_schedule(kind, modifier) +#ifdef _OPENMP + USE omp_lib +#endif + USE omp_constants, ONLY : fomp_sched_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_sched_kind), INTENT(IN) :: kind + INTEGER, INTENT(IN) :: modifier + +#ifdef _OPENMP + CALL omp_set_schedule(kind, modifier) +#else + IF (.FALSE.) PRINT *, kind, modifier +#endif + +END SUBROUTINE fomp_set_schedule + + +SUBROUTINE fomp_get_schedule(kind, modifier) +#ifdef _OPENMP + USE omp_lib +#endif + USE omp_constants, ONLY : fomp_sched_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_sched_kind), INTENT(OUT) :: kind + INTEGER, INTENT(OUT) :: modifier + +#ifdef _OPENMP + CALL omp_get_schedule(kind, modifier) +#else + kind = -1 + modifier = -1 +#endif + +END SUBROUTINE fomp_get_schedule + + +FUNCTION fomp_get_thread_limit() +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER :: fomp_get_thread_limit + +#ifdef _OPENMP + fomp_get_thread_limit = omp_get_thread_limit() +#else + fomp_get_thread_limit = -1 +#endif + +END FUNCTION fomp_get_thread_limit + + +SUBROUTINE fomp_set_max_active_levels(max_levels) +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER, INTENT(IN) :: max_levels + +#ifdef _OPENMP + CALL omp_set_max_active_levels(max_levels) +#else + IF (.FALSE.) PRINT *, max_levels +#endif + +END SUBROUTINE fomp_set_max_active_levels + + +FUNCTION fomp_get_max_active_levels() +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER :: fomp_get_max_active_levels + +#ifdef _OPENMP + fomp_get_max_active_levels = omp_get_max_active_levels() +#else + fomp_get_max_active_levels = -1 +#endif + +END FUNCTION fomp_get_max_active_levels + + +FUNCTION fomp_get_level() +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER :: fomp_get_level + +#ifdef _OPENMP + fomp_get_level = omp_get_level() +#else + fomp_get_level = -1 +#endif + +END FUNCTION fomp_get_level + + +FUNCTION fomp_get_ancestor_thread_num(level) +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER, INTENT(IN) :: level + INTEGER :: fomp_get_ancestor_thread_num + +#ifdef _OPENMP + fomp_get_ancestor_thread_num = omp_get_ancestor_thread_num(level) +#else + IF (.FALSE.) PRINT *, level + fomp_get_ancestor_thread_num = -1 +#endif + +END FUNCTION fomp_get_ancestor_thread_num + + +FUNCTION fomp_get_team_size(level) +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER, INTENT(IN) :: level + INTEGER :: fomp_get_team_size + +#ifdef _OPENMP + fomp_get_team_size = omp_get_team_size(level) +#else + IF (.FALSE.) PRINT *, level + fomp_get_team_size = -1 +#endif + +END FUNCTION fomp_get_team_size + + +FUNCTION fomp_get_active_level() +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER :: fomp_get_active_level + +#ifdef _OPENMP + fomp_get_active_level = omp_get_active_level() +#else + fomp_get_active_level = -1 +#endif + +END FUNCTION fomp_get_active_level + + +FUNCTION fomp_in_final() +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + LOGICAL :: fomp_in_final + +#ifdef _OPENMP + fomp_in_final = omp_in_final() +#else + fomp_in_final = .FALSE. +#endif + +END FUNCTION fomp_in_final + + +SUBROUTINE fomp_init_lock(svar) +#ifdef _OPENMP + USE omp_lib +#endif + USE omp_constants, ONLY : fomp_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_lock_kind), INTENT(OUT) :: svar + +#ifdef _OPENMP + CALL omp_init_lock(svar) +#else + svar = -1 +#endif + +END SUBROUTINE fomp_init_lock + + +SUBROUTINE fomp_init_nest_lock(nvar) +#ifdef _OPENMP + USE omp_lib +#endif + USE omp_constants, ONLY : fomp_nest_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_nest_lock_kind), INTENT(OUT) :: nvar + +#ifdef _OPENMP + CALL omp_init_nest_lock(nvar) +#else + nvar = -1 +#endif + +END SUBROUTINE fomp_init_nest_lock + + +SUBROUTINE fomp_destroy_lock(svar) +#ifdef _OPENMP + USE omp_lib +#endif + USE omp_constants, ONLY : fomp_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_lock_kind), INTENT(INOUT) :: svar + +#ifdef _OPENMP + CALL omp_destroy_lock(svar) +#else + IF (.FALSE.) PRINT *, svar +#endif + + +END SUBROUTINE fomp_destroy_lock + + +SUBROUTINE fomp_destroy_nest_lock(nvar) +#ifdef _OPENMP + USE omp_lib +#endif + USE omp_constants, ONLY : fomp_nest_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_nest_lock_kind), INTENT(INOUT) :: nvar + +#ifdef _OPENMP + CALL omp_destroy_nest_lock(nvar) +#else + IF (.FALSE.) PRINT *, nvar +#endif + +END SUBROUTINE fomp_destroy_nest_lock + + +SUBROUTINE fomp_set_lock(svar) +#ifdef _OPENMP + USE omp_lib +#endif + USE omp_constants, ONLY : fomp_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_lock_kind), INTENT(INOUT) :: svar + +#ifdef _OPENMP + CALL omp_set_lock(svar) +#else + IF (.FALSE.) PRINT *, svar +#endif + +END SUBROUTINE fomp_set_lock + + +SUBROUTINE fomp_set_nest_lock(nvar) +#ifdef _OPENMP + USE omp_lib +#endif + USE omp_constants, ONLY : fomp_nest_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_nest_lock_kind), INTENT(INOUT) :: nvar + +#ifdef _OPENMP + CALL omp_set_nest_lock(nvar) +#else + IF (.FALSE.) PRINT *, nvar +#endif + +END SUBROUTINE fomp_set_nest_lock + + +SUBROUTINE fomp_unset_lock(svar) +#ifdef _OPENMP + USE omp_lib +#endif + USE omp_constants, ONLY : fomp_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_lock_kind), INTENT(INOUT) :: svar + +#ifdef _OPENMP + CALL omp_unset_lock(svar) +#else + IF (.FALSE.) PRINT *, svar +#endif + +END SUBROUTINE fomp_unset_lock + + +SUBROUTINE fomp_unset_nest_lock(nvar) +#ifdef _OPENMP + USE omp_lib +#endif + USE omp_constants, ONLY : fomp_nest_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_nest_lock_kind), INTENT(INOUT) :: nvar + +#ifdef _OPENMP + CALL omp_unset_nest_lock(nvar) +#else + IF (.FALSE.) PRINT *, nvar +#endif + +END SUBROUTINE fomp_unset_nest_lock + + +FUNCTION fomp_test_lock(svar) +#ifdef _OPENMP + USE omp_lib +#endif + USE omp_constants, ONLY : fomp_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_lock_kind), INTENT(INOUT) :: svar + LOGICAL :: fomp_test_lock + +#ifdef _OPENMP + fomp_test_lock = omp_test_lock(svar) +#else + IF (.FALSE.) PRINT *, svar + fomp_test_lock = .FALSE. +#endif + + + +END FUNCTION fomp_test_lock + + +FUNCTION fomp_test_nest_lock(nvar) +#ifdef _OPENMP + USE omp_lib +#endif + USE omp_constants, ONLY : fomp_nest_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_nest_lock_kind), INTENT(INOUT) :: nvar + INTEGER :: fomp_test_nest_lock + +#ifdef _OPENMP + fomp_test_nest_lock = omp_test_nest_lock(nvar) +#else + IF (.FALSE.) PRINT *, nvar + fomp_test_nest_lock = -1 +#endif + + + +END FUNCTION fomp_test_nest_lock + + +FUNCTION fomp_get_wtime() +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + REAL (KIND=8) :: fomp_get_wtime + +#ifdef _OPENMP + fomp_get_wtime = omp_get_wtime() +#else + fomp_get_wtime = -1 +#endif + + + +END FUNCTION fomp_get_wtime + + +FUNCTION fomp_get_wtick() +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + REAL (KIND=8) :: fomp_get_wtick + +#ifdef _OPENMP + fomp_get_wtick = omp_get_wtick() +#else + fomp_get_wtick = -1 +#endif + + + +END FUNCTION fomp_get_wtick + + diff --git a/fortran/rip_cape.f90 b/fortran/rip_cape.f90 index 08cf9a0..c92be3f 100644 --- a/fortran/rip_cape.f90 +++ b/fortran/rip_cape.f90 @@ -37,9 +37,9 @@ END FUNCTION TVIRTUAL REAL(KIND=8) FUNCTION TONPSADIABAT(thte, prs, psadithte, psadiprs, psaditmk, gamma,& errstat, errmsg) USE wrf_constants, ONLY : ALGERR - +!!$OMP DECLARE SIMD (TONPSADIABAT) +!!uniform(thte,prs,psadithte,psadiprs,psaditmk) !f2py threadsafe - !f2py intent(in,out) :: cape, cin IMPLICIT NONE REAL(KIND=8), INTENT(IN) :: thte @@ -57,8 +57,9 @@ REAL(KIND=8) FUNCTION TONPSADIABAT(thte, prs, psadithte, psadiprs, psaditmk, gam REAL(KIND=8) :: fracjt2 REAL(KIND=8) :: fracip REAL(KIND=8) :: fracip2 - - INTEGER :: ip, ipch, jt, jtch + + INTEGER :: l1, h1, mid1, rang1, l2, h2, mid2, rang2 + INTEGER :: ip, jt ! This function gives the temperature (in K) on a moist adiabat ! (specified by thte in K) given pressure in hPa. It uses a @@ -78,22 +79,53 @@ REAL(KIND=8) FUNCTION TONPSADIABAT(thte, prs, psadithte, psadiprs, psaditmk, gam ! Otherwise, look for the given thte/prs point in the lookup table. jt = -1 - DO jtch = 1, 150-1 - IF (thte .GE. psadithte(jtch) .AND. thte .LT. psadithte(jtch+1)) THEN - jt = jtch - EXIT - !GO TO 213 + l1 = 1 + h1 = 149 + rang1 = h1 - l1 + mid1 = (h1 + l1) / 2 + DO WHILE(rang1 .GT. 1) + IF (thte .GE. psadithte(mid1)) THEN + l1 = mid1 + ELSE + h1 = mid1 END IF + rang1 = h1 - l1 + mid1 = (h1 + l1) / 2 END DO + jt = l1 + + ! DO jtch = 1, 150-1 + ! IF (thte .GE. psadithte(jtch) .AND. thte .LT. psadithte(jtch+1)) THEN + ! jt = jtch + ! EXIT + ! !GO TO 213 + ! END IF + ! END DO ip = -1 - DO ipch = 1, 150-1 - IF (prs .LE. psadiprs(ipch) .AND. prs .GT. psadiprs(ipch+1)) THEN - ip = ipch - EXIT - !GO TO 215 + l2 = 1 + h2 = 149 + rang2 = h2 - l2 + mid2 = (h2 + l2) / 2 + DO WHILE(rang2 .GT. 1) + IF (prs .LE. psadiprs(mid2)) THEN + l2 = mid2 + ELSE + h2 = mid2 END IF + rang2 = h2 - l2 + mid2 = (h2 + l2) / 2 END DO + ip = l2 + + ! ip = -1 + ! DO ipch = 1, 150-1 + ! IF (prs .LE. psadiprs(ipch) .AND. prs .GT. psadiprs(ipch+1)) THEN + ! ip = ipch + ! EXIT + ! !GO TO 215 + ! END IF + ! END DO IF (jt .EQ. -1 .OR. ip .EQ. -1) THEN ! Set the error and return @@ -185,35 +217,36 @@ END SUBROUTINE DLOOKUP_TABLE ! which case it assumes the lower bounding pressure level is as far ! below the lowest vertical level as the upper bounding pressure ! level is above. -SUBROUTINE DPFCALC(prs, sfp, pf, miy, mjx, mkzh, ter_follow) +SUBROUTINE DPFCALC(prs, sfp, pf, mix, mjy, mkzh, ter_follow) - REAL(KIND=8), DIMENSION(miy,mjx,mkzh), INTENT(IN) :: prs - REAL(KIND=8), DIMENSION(miy,mjx), INTENT(IN) :: sfp - REAL(KIND=8), DIMENSION(miy,mjx,mkzh), INTENT(OUT) :: pf - INTEGER, INTENT(IN) :: ter_follow,miy,mjx,mkzh + REAL(KIND=8), DIMENSION(mkzh,mix,mjy), INTENT(IN) :: prs + REAL(KIND=8), DIMENSION(mix,mjy), INTENT(IN) :: sfp + REAL(KIND=8), DIMENSION(mkzh,mix,mjy), INTENT(OUT) :: pf + INTEGER, INTENT(IN) :: ter_follow,mix,mjy,mkzh INTEGER :: i,j,k - - ! do j=1,mjx-1 Artifact of MM5 - DO j = 1,mjx - ! do i=1,miy-1 staggered grid - DO i = 1,miy - DO k = 1,mkzh - IF (k .EQ. mkzh) THEN - ! terrain-following data - IF (ter_follow .EQ. 1) THEN - pf(i,j,k) = sfp(i,j) - ! pressure-level data - ELSE - pf(i,j,k) = .5D0 * (3.D0*prs(i,j,k) - prs(i,j,k-1)) - END IF - ELSE - pf(i,j,k) = .5D0 * (prs(i,j,k+1) + prs(i,j,k)) - END IF - END DO - END DO + + !$OMP PARALLEL DO COLLAPSE(3) SCHEDULE(runtime) + DO j = 1,mjy + DO i = 1,mix + DO k = 1,mkzh + IF (k .EQ. mkzh) THEN + ! terrain-following data + IF (ter_follow .EQ. 1) THEN + pf(k,i,j) = sfp(i,j) + ! pressure-level data + ELSE + pf(k,i,j) = .5D0 * (3.D0*prs(k,i,j) - prs(k-1,i,j)) + END IF + ELSE + pf(k,i,j) = .5D0 * (prs(k+1,i,j) + prs(k,i,j)) + END IF + END DO + END DO END DO + !$OMP END PARALLEL DO + RETURN END SUBROUTINE DPFCALC @@ -224,50 +257,367 @@ END SUBROUTINE DPFCALC ! ! !DESCRIPTION: ! -! If i3dflag=1, this routine calculates CAPE and CIN (in m**2/s**2, +! This routine calculates CAPE and CIN (in m**2/s**2, ! or J/kg) for every grid point in the entire 3D domain (treating -! each grid point as a parcel). If i3dflag=0, then it -! calculates CAPE and CIN only for the parcel with max theta-e in +! each grid point as a parcel). +! + + +! Important! The z-indexes must be arranged so that mkzh (max z-index) is the +! surface pressure. So, pressure must be ordered in ascending order before +! calling this routine. Other variables must be ordered the same (p,tk,q,z). + +! Also, be advised that missing data values are not checked during the computation. +! Also also, Pressure must be hPa + +! NCLFORTSTART +SUBROUTINE DCAPECALC3D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& + cmsg,mix,mjy,mkzh,ter_follow,& + psafile, errstat, errmsg) + USE wrf_constants, ONLY : CELKEL, G, EZERO, ESLCON1, ESLCON2, & + EPS, RD, CP, GAMMA, CPMD, RGASMD, GAMMAMD, TLCLC1, & + TLCLC2, TLCLC3, TLCLC4, THTECON1, THTECON2, THTECON3 + + !USE omp_lib + IMPLICIT NONE + + !f2py threadsafe + !f2py intent(in,out) :: cape, cin + + INTEGER, INTENT(IN) :: mix, mjy, mkzh, ter_follow + REAL(KIND=8), DIMENSION(mix,mjy,mkzh), INTENT(IN) :: prs + REAL(KIND=8), DIMENSION(mix,mjy,mkzh), INTENT(IN) :: tmk + REAL(KIND=8), DIMENSION(mix,mjy,mkzh), INTENT(IN) :: qvp + REAL(KIND=8), DIMENSION(mix,mjy,mkzh), INTENT(IN) :: ght + REAL(KIND=8), DIMENSION(mix,mjy), INTENT(IN) :: ter + REAL(KIND=8), DIMENSION(mix,mjy), INTENT(IN) ::sfp + REAL(KIND=8), DIMENSION(mix,mjy,mkzh), INTENT(OUT) :: cape + REAL(KIND=8), DIMENSION(mix,mjy,mkzh), INTENT(OUT) :: cin + REAL(KIND=8), INTENT(IN) :: cmsg + CHARACTER(LEN=*), INTENT(IN) :: psafile + INTEGER, INTENT(INOUT) :: errstat + CHARACTER(LEN=*), INTENT(INOUT) :: errmsg + +! NCLFORTEND + + ! local variables + INTEGER :: i, j, k, ilcl, kel, kk, klcl, klev, klfc, kmax, kpar + REAL(KIND=8) :: tlcl, zlcl + REAL(KIND=8) :: ethpari, qvppari, tmkpari + REAL(KIND=8) :: facden, qvplift, tmklift, tvenv, tvlift, ghtlift + REAL(KIND=8) :: eslift, tmkenv, qvpenv, tonpsadiabat + REAL(KIND=8) :: benamin, dz + REAL(KIND=8), DIMENSION(150) :: buoy, zrel, benaccum + REAL(KIND=8), DIMENSION(mkzh,mix,mjy) :: prsf + REAL(KIND=8), DIMENSION(150) :: psadithte, psadiprs + REAL(KIND=8), DIMENSION(150,150) :: psaditmk + LOGICAL :: elfound + + REAL(KIND=8), DIMENSION(mkzh,mix,mjy) :: prs_new + REAL(KIND=8), DIMENSION(mkzh,mix,mjy) :: tmk_new + REAL(KIND=8), DIMENSION(mkzh,mix,mjy) :: qvp_new + REAL(KIND=8), DIMENSION(mkzh,mix,mjy) :: ght_new + + ! To remove compiler warnings + tmkpari = 0 + qvppari = 0 + klev = 0 + klcl = 0 + kel = 0 + IF (.FALSE.) PRINT *,ter + + + ! the comments were taken from a mark stoelinga email, 23 apr 2007, + ! in response to a user getting the "outside of lookup table bounds" + ! error message. + + ! tmkpari - initial temperature of parcel, k + ! values of 300 okay. (not sure how much from this you can stray.) + + ! prspari - initial pressure of parcel, hpa + ! values of 980 okay. (not sure how much from this you can stray.) + + ! thtecon1, thtecon2, thtecon3 + ! these are all constants, the first in k and the other two have + ! no units. values of 3376, 2.54, and 0.81 were stated as being + ! okay. + + ! tlcl - the temperature at the parcel's lifted condensation level, k + ! should be a reasonable atmospheric temperature around 250-300 k + ! (398 is "way too high") + + ! qvppari - the initial water vapor mixing ratio of the parcel, + ! kg/kg (should range from 0.000 to 0.025) + ! + + ! calculated the pressure at full sigma levels (a set of pressure + ! levels that bound the layers represented by the vertical grid points) + + !$OMP PARALLEL DO COLLAPSE(3) SCHEDULE(runtime) + DO j = 1,mjy + DO i = 1,mix + DO k = 1,mkzh + prs_new(k,i,j) = prs(i,j,k) + tmk_new(k,i,j) = tmk(i,j,k) + qvp_new(k,i,j) = qvp(i,j,k) + ght_new(k,i,j) = ght(i,j,k) + END DO + END DO + END DO + !$OMP END PARALLEL DO + + CALL DPFCALC(prs_new, sfp, prsf, mix, mjy, mkzh, ter_follow) + + ! before looping, set lookup table for getting temperature on + ! a pseudoadiabat. + + CALL DLOOKUP_TABLE(psadithte, psadiprs, psaditmk, psafile, errstat, errmsg) + + IF (errstat .NE. 0) THEN + RETURN + END IF + + !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(tlcl, ethpari, & + !$OMP zlcl, kk, ilcl, klcl, tmklift, tvenv, tvlift, ghtlift, & + !$OMP facden, tmkenv, qvpenv, eslift, qvplift, buoy, benamin, & + !$OMP benaccum, zrel, kmax, dz, elfound, & + !$OMP kel, klfc, & + !$OMP i, j, k, kpar) SCHEDULE(runtime) + DO j = 1,mjy + DO i = 1,mix + cape(i,j,1) = 0.D0 + cin(i,j,1) = 0.D0 + + !!$OMP SIMD + DO kpar = 2, mkzh + + ! Calculate temperature and moisture properties of parcel + ! (note, qvppari and tmkpari already calculated above for 2d case.) + + tlcl = TLCLC1/(LOG(tmk_new(kpar,i,j)**TLCLC2/(MAX(1.D-20,qvp_new(kpar,i,j)*prs_new(kpar,i,j)/ & + (EPS + qvp_new(kpar,i,j))))) - TLCLC3) + TLCLC4 + + ethpari = tmk_new(kpar,i,j)*(1000.D0/prs_new(kpar,i,j))**(GAMMA*(1.D0 + GAMMAMD*qvp_new(kpar,i,j)))* & + EXP((THTECON1/tlcl - THTECON2)*qvp_new(kpar,i,j)*(1.D0 + THTECON3*qvp_new(kpar,i,j))) + + zlcl = ght_new(kpar,i,j) + (tmk_new(kpar,i,j) - tlcl)/(G/CP * (1.D0 + CPMD*qvp_new(kpar,i,j))) + + ! Calculate buoyancy and relative height of lifted parcel at + ! all levels, and store in bottom up arrays. add a level at the lcl, + ! and at all points where buoyancy is zero. + ! + ! For arrays that go bottom to top + kk = 0 + ilcl = 0 + + IF (ght_new(kpar,i,j) .GE. zlcl) THEN + ! Initial parcel already saturated or supersaturated. + ilcl = 2 + klcl = 1 + END IF + + !!$OMP SIMD lastprivate(qvplift,tmklift,ghtlift,tvlift,tmkenv,qvpenv,tvenv,eslift,facden) + DO k = kpar,1,-1 + ! For arrays that go bottom to top + kk = kk + 1 + + ! Model level is below lcl + IF (ght_new(k,i,j) .LT. zlcl) THEN + tmklift = tmk_new(kpar,i,j) - G/(CP * (1.D0 + CPMD*qvp_new(kpar,i,j)))*& + (ght_new(k,i,j) - ght_new(kpar,i,j)) + tvenv = tmk_new(k,i,j)*(EPS + qvp_new(k,i,j))/(EPS*(1.D0 + qvp_new(k,i,j))) + tvlift = tmklift*(EPS + qvp_new(kpar,i,j))/(EPS*(1.D0 + qvp_new(kpar,i,j))) + ghtlift = ght_new(k,i,j) + ELSE IF (ght(i,j,k) .GE. zlcl .AND. ilcl .EQ. 0) THEN + ! This model level and previous model level straddle the lcl, + ! so first create a new level in the bottom-up array, at the lcl. + facden = 1.0/(ght_new(k,i,j) - ght_new(k+1,i,j)) + tmkenv = tmk_new(k+1,i,j)*((ght_new(k,i,j)-zlcl)*facden) + tmk_new(k,i,j)*& + ((zlcl-ght_new(k+1,i,j))*facden) + qvpenv = qvp_new(k+1,i,j)*((ght_new(k,i,j)-zlcl)*facden) + qvp_new(k,i,j)*& + ((zlcl-ght_new(k+1,i,j))*facden) + tvenv = tmkenv* (EPS + qvpenv) / (EPS * (1.D0 + qvpenv)) + tvlift = tlcl* (EPS + qvp_new(kpar,i,j)) / (EPS *(1.D0 + qvp_new(kpar,i,j))) + ghtlift = zlcl + ilcl = 1 + ELSE + tmklift = TONPSADIABAT(ethpari, prs_new(k,i,j), psadithte, psadiprs,& + psaditmk, GAMMA, errstat, errmsg) + eslift = EZERO*EXP(ESLCON1*(tmklift - CELKEL)/(tmklift - ESLCON2)) + qvplift = EPS*eslift/(prs_new(k,i,j) - eslift) + tvenv = tmk_new(k,i,j) * (EPS + qvp_new(k,i,j)) / (EPS * (1.D0 + qvp_new(k,i,j))) + tvlift = tmklift*(EPS + qvplift) / (EPS * (1.D0 + qvplift)) + ghtlift = ght_new(k,i,j) + END IF + ! Buoyancy + buoy(kk) = G*(tvlift - tvenv)/tvenv + zrel(kk) = ghtlift - ght_new(kpar,i,j) + IF ((kk .GT. 1) .AND. (buoy(kk)*buoy(kk-1) .LT. 0.0D0)) THEN + ! Parcel ascent curve crosses sounding curve, so create a new level + ! in the bottom-up array at the crossing. + kk = kk + 1 + buoy(kk) = buoy(kk-1) + zrel(kk) = zrel(kk-1) + buoy(kk-1) = 0.D0 + zrel(kk-1) = zrel(kk-2) + buoy(kk-2)/& + (buoy(kk-2) - buoy(kk))*(zrel(kk) - zrel(kk-2)) + END IF + IF (ilcl .EQ. 1) THEN + klcl = kk + ilcl = 2 + CYCLE + END IF + + END DO + + kmax = kk + ! IF (kmax .GT. 150) THEN + ! print *,'kmax got too big' + ! errstat = ALGERR + ! WRITE(errmsg, *) 'capecalc3d: kmax got too big. kmax=',kmax + ! RETURN + ! END IF + + ! If no lcl was found, set klcl to kmax. it is probably not really + ! at kmax, but this will make the rest of the routine behave + ! properly. + IF (ilcl .EQ. 0) klcl=kmax + + ! Get the accumulated buoyant energy from the parcel's starting + ! point, at all levels up to the top level. + benaccum(1) = 0.0D0 + benamin = 9d9 + DO k = 2,kmax + dz = zrel(k) - zrel(k-1) + benaccum(k) = benaccum(k-1) + .5D0*dz*(buoy(k-1) + buoy(k)) + IF (benaccum(k) .LT. benamin) THEN + benamin = benaccum(k) + END IF + END DO + ! Determine equilibrium level (el), which we define as the highest + ! level of non-negative buoyancy above the lcl. note, this may be + ! the top level if the parcel is still buoyant there. + + elfound = .FALSE. + DO k = kmax,klcl,-1 + IF (buoy(k) .GE. 0.D0) THEN + ! k of equilibrium level + kel = k + elfound = .TRUE. + EXIT + END IF + END DO + + ! If we got through that loop, then there is no non-negative + ! buoyancy above the lcl in the sounding. in these situations, + ! both cape and cin will be set to -0.1 j/kg. (see below about + ! missing values in v6.1.0). also, where cape is + ! non-zero, cape and cin will be set to a minimum of +0.1 j/kg, so + ! that the zero contour in either the cin or cape fields will + ! circumscribe regions of non-zero cape. + + ! In v6.1.0 of ncl, we added a _fillvalue attribute to the return + ! value of this function. at that time we decided to change -0.1 + ! to a more appropriate missing value, which is passed into this + ! routine as cmsg. + + IF (.NOT. elfound) THEN + !print *,'el not found' + cape(i,j,kpar) = cmsg + cin(i,j,kpar) = cmsg + klfc = kmax + CYCLE + END IF + + ! If there is an equilibrium level, then cape is positive. we'll + ! define the level of free convection (lfc) as the point below the + ! el, but at or above the lcl, where accumulated buoyant energy is a + ! minimum. the net positive area (accumulated buoyant energy) from + ! the lfc up to the el will be defined as the cape, and the net + ! negative area (negative of accumulated buoyant energy) from the + ! parcel starting point to the lfc will be defined as the convective + ! inhibition (cin). + + ! First get the lfc according to the above definition. + benamin = 9D9 + klfc = kmax + DO k = klcl,kel + IF (benaccum(k) .LT. benamin) THEN + benamin = benaccum(k) + klfc = k + END IF + END DO + + ! Now we can assign values to cape and cin + + cape(i,j,kpar) = MAX(benaccum(kel)-benamin, 0.1D0) + cin(i,j,kpar) = MAX(-benamin, 0.1D0) + + ! cin is uninteresting when cape is small (< 100 j/kg), so set + ! cin to -0.1 (see note about missing values in v6.1.0) in + ! that case. + + ! In v6.1.0 of ncl, we added a _fillvalue attribute to the return + ! value of this function. at that time we decided to change -0.1 + ! to a more appropriate missing value, which is passed into this + ! routine as cmsg. + + IF (cape(i,j,kpar) .LT. 100.D0) cin(i,j,kpar) = cmsg + + END DO + END DO + END DO + !$OMP END PARALLEL DO + + RETURN +END SUBROUTINE DCAPECALC3D + +!====================================================================== +! +! !IROUTINE: capecalc2d -- Calculate CAPE and CIN +! +! !DESCRIPTION: +! +! Calculates CAPE and CIN only for the parcel with max theta-e in ! the column, (i.e. something akin to Colman's MCAPE). By "parcel", ! we mean a 500-m deep parcel, with actual temperature and moisture ! averaged over that depth. ! -! In the case of i3dflag=0, ! CAPE and CIN are 2D fields that are placed in the k=mkzh slabs of -! the cape and cin arrays. Also, if i3dflag=0, LCL and LFC heights +! the cape and cin arrays. Also, LCL and LFC heights ! are put in the k=mkzh-1 and k=mkzh-2 slabs of the cin array. ! - - ! Important! The z-indexes must be arranged so that mkzh (max z-index) is the ! surface pressure. So, pressure must be ordered in ascending order before ! calling this routine. Other variables must be ordered the same (p,tk,q,z). -! Also, be advised that missing data values are not checked during the computation. +! Also, be advised that missing data values are not checked during the +! computation. ! Also also, Pressure must be hPa ! NCLFORTSTART -SUBROUTINE DCAPECALC3D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& - cmsg,miy,mjx,mkzh,i3dflag,ter_follow,& +SUBROUTINE DCAPECALC2D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& + cmsg,mix,mjy,mkzh,ter_follow,& psafile, errstat, errmsg) - USE wrf_constants, ONLY : ALGERR, CELKEL, G, EZERO, ESLCON1, ESLCON2, & + USE wrf_constants, ONLY : CELKEL, G, EZERO, ESLCON1, ESLCON2, & EPS, RD, CP, GAMMA, CPMD, RGASMD, GAMMAMD, TLCLC1, & TLCLC2, TLCLC3, TLCLC4, THTECON1, THTECON2, THTECON3 + !USE omp_lib IMPLICIT NONE !f2py threadsafe !f2py intent(in,out) :: cape, cin - INTEGER, INTENT(IN) :: miy, mjx, mkzh, i3dflag, ter_follow - REAL(KIND=8), DIMENSION(miy,mjx,mkzh), INTENT(IN) :: prs - REAL(KIND=8), DIMENSION(miy,mjx,mkzh), INTENT(IN) :: tmk - REAL(KIND=8), DIMENSION(miy,mjx,mkzh), INTENT(IN) :: qvp - REAL(KIND=8), DIMENSION(miy,mjx,mkzh), INTENT(IN) :: ght - REAL(KIND=8), DIMENSION(miy,mjx), INTENT(IN) :: ter - REAL(KIND=8), DIMENSION(miy,mjx), INTENT(IN) ::sfp - REAL(KIND=8), DIMENSION(miy,mjx,mkzh), INTENT(OUT) :: cape - REAL(KIND=8), DIMENSION(miy,mjx,mkzh), INTENT(OUT) :: cin + INTEGER, INTENT(IN) :: mix, mjy, mkzh, ter_follow + REAL(KIND=8), DIMENSION(mix,mjy,mkzh), INTENT(IN) :: prs + REAL(KIND=8), DIMENSION(mix,mjy,mkzh), INTENT(IN) :: tmk + REAL(KIND=8), DIMENSION(mix,mjy,mkzh), INTENT(IN) :: qvp + REAL(KIND=8), DIMENSION(mix,mjy,mkzh), INTENT(IN) :: ght + REAL(KIND=8), DIMENSION(mix,mjy), INTENT(IN) :: ter + REAL(KIND=8), DIMENSION(mix,mjy), INTENT(IN) ::sfp + REAL(KIND=8), DIMENSION(mix,mjy,mkzh), INTENT(OUT) :: cape + REAL(KIND=8), DIMENSION(mix,mjy,mkzh), INTENT(OUT) :: cin REAL(KIND=8), INTENT(IN) :: cmsg CHARACTER(LEN=*), INTENT(IN) :: psafile INTEGER, INTENT(INOUT) :: errstat @@ -275,26 +625,34 @@ SUBROUTINE DCAPECALC3D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& ! NCLFORTEND + ! local variables INTEGER :: i, j, k, ilcl, kel, kk, klcl, klev, klfc, kmax, kpar, kpar1, kpar2 - REAL(KIND=8) :: davg, ethmax, q, t, p, e, eth, tlcl, zlcl + REAL(KIND=8) :: ethmax, p, e, tlcl, zlcl REAL(KIND=8) :: pavg, tvirtual, p1, p2, pp1, pp2, th, totthe, totqvp, totprs - REAL(KIND=8) :: cpm, deltap, ethpari, gammam, ghtpari, qvppari, prspari, tmkpari - REAL(KIND=8) :: facden, fac1, fac2, qvplift, tmklift, tvenv, tvlift, ghtlift + REAL(KIND=8) :: cpm, deltap, ethpari, gammam, qvppari, tmkpari + REAL(KIND=8) :: facden, qvplift, tmklift, tvenv, tvlift, ghtlift REAL(KIND=8) :: eslift, tmkenv, qvpenv, tonpsadiabat REAL(KIND=8) :: benamin, dz, pup, pdn REAL(KIND=8), DIMENSION(150) :: buoy, zrel, benaccum - REAL(KIND=8), DIMENSION(miy,mjx,mkzh) :: prsf + REAL(KIND=8), DIMENSION(mkzh,mix,mjy) :: prsf REAL(KIND=8), DIMENSION(150) :: psadithte, psadiprs REAL(KIND=8), DIMENSION(150,150) :: psaditmk LOGICAL :: elfound + REAL(KIND=8), DIMENSION(mkzh) :: eth_temp + REAL(KIND=8), DIMENSION(mkzh,mix,mjy) :: prs_new + REAL(KIND=8), DIMENSION(mkzh,mix,mjy) :: tmk_new + REAL(KIND=8), DIMENSION(mkzh,mix,mjy) :: qvp_new + REAL(KIND=8), DIMENSION(mkzh,mix,mjy) :: ght_new ! To remove compiler warnings + errstat = 0 tmkpari = 0 qvppari = 0 klev = 0 klcl = 0 kel = 0 + deltap = 0 ! the comments were taken from a mark stoelinga email, 23 apr 2007, @@ -320,10 +678,23 @@ SUBROUTINE DCAPECALC3D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& ! kg/kg (should range from 0.000 to 0.025) ! + !$OMP PARALLEL DO COLLAPSE(3) SCHEDULE(runtime) + DO j = 1,mjy + DO i = 1,mix + DO k = 1,mkzh + prs_new(k,i,j) = prs(i,j,k) + tmk_new(k,i,j) = tmk(i,j,k) + qvp_new(k,i,j) = qvp(i,j,k) + ght_new(k,i,j) = ght(i,j,k) + END DO + END DO + END DO + !$OMP END PARALLEL DO + + ! calculated the pressure at full sigma levels (a set of pressure ! levels that bound the layers represented by the vertical grid points) - - CALL DPFCALC(prs, sfp, prsf, miy, mjx, mkzh, ter_follow) + CALL DPFCALC(prs_new, sfp, prsf, mix, mjy, mkzh, ter_follow) ! before looping, set lookup table for getting temperature on ! a pseudoadiabat. @@ -334,284 +705,272 @@ SUBROUTINE DCAPECALC3D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& RETURN END IF - ! do j=1,mjx-1 - DO j = 1,mjx - ! do i=1,miy-1 - DO i = 1,miy - cape(i,j,1) = 0.d0 - cin(i,j,1) = 0.d0 - - IF (i3dflag .EQ. 1) THEN - kpar1 = 2 - kpar2 = mkzh - ELSE - ! find parcel with max theta-e in lowest 3 km agl. - ethmax = -1.d0 - DO k = mkzh,1,-1 - IF (ght(i,j,k)-ter(i,j) .LT. 3000.d0) THEN - q = MAX(qvp(i,j,k), 1.d-15) - t = tmk(i,j,k) - p = prs(i,j,k) - e = q*p/(EPS + q) - tlcl = TLCLC1 / (LOG(t**TLCLC2/e)-TLCLC3) + TLCLC4 - eth = t * (1000.d0/p)**(GAMMA*(1.d0 + GAMMAMD*q))*& - EXP((THTECON1/tlcl - THTECON2)*q*(1.d0 + THTECON3*q)) - IF (eth .GT. ethmax) THEN - klev = k - ethmax = eth - END IF - END IF - END DO - kpar1 = klev - kpar2 = klev - - ! Establish average properties of that parcel - ! (over depth of approximately davg meters) - - ! davg=.1 - davg = 500.d0 - pavg = davg*prs(i,j,kpar1)*& - G/(RD*tvirtual(tmk(i,j,kpar1), qvp(i,j,kpar1))) - p2 = MIN(prs(i,j,kpar1)+.5d0*pavg, prsf(i,j,mkzh)) - p1 = p2 - pavg - totthe = 0.D0 - totqvp = 0.D0 - totprs = 0.D0 - DO k = mkzh,2,-1 - IF (prsf(i,j,k) .LE. p1) EXIT !GOTO 35 - IF (prsf(i,j,k-1) .GE. p2) CYCLE !GOTO 34 - p = prs(i,j,k) - pup = prsf(i,j,k) - pdn = prsf(i,j,k-1) - q = MAX(qvp(i,j,k),1.D-15) - th = tmk(i,j,k)*(1000.D0/p)**(GAMMA*(1.D0 + GAMMAMD*q)) - pp1 = MAX(p1,pdn) - pp2 = MIN(p2,pup) - IF (pp2 .GT. pp1) THEN - deltap = pp2 - pp1 - totqvp = totqvp + q*deltap - totthe = totthe + th*deltap - totprs = totprs + deltap - END IF -! 34 CONTINUE - END DO -! 35 CONTINUE - qvppari = totqvp/totprs - tmkpari = (totthe/totprs)*& - (prs(i,j,kpar1)/1000.D0)**(GAMMA*(1.D0+GAMMAMD*qvp(i,j,kpar1))) - END IF - - DO kpar = kpar1, kpar2 - - ! Calculate temperature and moisture properties of parcel - ! (note, qvppari and tmkpari already calculated above for 2d case.) - - IF (i3dflag .EQ. 1) THEN - qvppari = qvp(i,j,kpar) - tmkpari = tmk(i,j,kpar) - END IF - prspari = prs(i,j,kpar) - ghtpari = ght(i,j,kpar) - gammam = GAMMA * (1.D0 + GAMMAMD*qvppari) - cpm = CP * (1.D0 + CPMD*qvppari) - - e = MAX(1.D-20,qvppari*prspari/(EPS + qvppari)) - tlcl = TLCLC1/(LOG(tmkpari**TLCLC2/e) - TLCLC3) + TLCLC4 - ethpari = tmkpari*(1000.D0/prspari)**(GAMMA*(1.D0 + GAMMAMD*qvppari))*& - EXP((THTECON1/tlcl - THTECON2)*qvppari*(1.D0 + THTECON3*qvppari)) - zlcl = ghtpari + (tmkpari - tlcl)/(G/cpm) - - ! Calculate buoyancy and relative height of lifted parcel at - ! all levels, and store in bottom up arrays. add a level at the lcl, - ! and at all points where buoyancy is zero. - ! - ! For arrays that go bottom to top - kk = 0 - ilcl = 0 - - IF (ghtpari .GE. zlcl) THEN - ! Initial parcel already saturated or supersaturated. - ilcl = 2 - klcl = 1 - END IF - - k = kpar - DO WHILE (k .GE. 1)!k = kpar, 1, -1 - !DO k = kpar, 1, -1 - ! For arrays that go bottom to top -! 33 kk = kk + 1 - kk = kk + 1 - - ! Model level is below lcl - IF (ght(i,j,k) .LT. zlcl) THEN - qvplift = qvppari - tmklift = tmkpari - G/cpm*(ght(i,j,k) - ghtpari) - tvenv = tvirtual(tmk(i,j,k), qvp(i,j,k)) - tvlift = tvirtual(tmklift, qvplift) - ghtlift = ght(i,j,k) - ELSE IF (ght(i,j,k) .GE. zlcl .AND. ilcl .EQ. 0) THEN - ! This model level and previous model level straddle the lcl, - ! so first create a new level in the bottom-up array, at the lcl. - tmklift = tlcl - qvplift = qvppari - facden = ght(i,j,k) - ght(i,j,k+1) - fac1 = (zlcl-ght(i,j,k+1))/facden - fac2 = (ght(i,j,k)-zlcl)/facden - tmkenv = tmk(i,j,k+1)*fac2 + tmk(i,j,k)*fac1 - qvpenv = qvp(i,j,k+1)*fac2 + qvp(i,j,k)*fac1 - tvenv = tvirtual(tmkenv, qvpenv) - tvlift = tvirtual(tmklift, qvplift) - ghtlift = zlcl - ilcl = 1 - ELSE - tmklift = TONPSADIABAT(ethpari, prs(i,j,k), psadithte, psadiprs,& - psaditmk, GAMMA, errstat, errmsg) - eslift = EZERO*EXP(ESLCON1*(tmklift - CELKEL)/(tmklift - ESLCON2)) - qvplift = EPS*eslift/(prs(i,j,k) - eslift) - tvenv = tvirtual(tmk(i,j,k), qvp(i,j,k)) - tvlift = tvirtual(tmklift, qvplift) - ghtlift = ght(i,j,k) - END IF - ! Buoyancy - buoy(kk) = G*(tvlift - tvenv)/tvenv - zrel(kk) = ghtlift - ghtpari - - IF ((kk .GT. 1) .AND. (buoy(kk)*buoy(kk-1) .LT. 0.0D0)) THEN - ! Parcel ascent curve crosses sounding curve, so create a new level - ! in the bottom-up array at the crossing. - kk = kk + 1 - buoy(kk) = buoy(kk-1) - zrel(kk) = zrel(kk-1) - buoy(kk-1) = 0.D0 - zrel(kk-1) = zrel(kk-2) + buoy(kk-2)/& - (buoy(kk-2) - buoy(kk))*(zrel(kk) - zrel(kk-2)) - END IF - - IF (ilcl .EQ. 1) THEN - klcl = kk - ilcl = 2 - !GOTO 33 - CYCLE - END IF - - k = k - 1 - END DO - - kmax = kk - IF (kmax .GT. 150) THEN - errstat = ALGERR - WRITE(errmsg, *) 'capecalc3d: kmax got too big. kmax=',kmax - RETURN - END IF - - ! If no lcl was found, set klcl to kmax. it is probably not really - ! at kmax, but this will make the rest of the routine behave - ! properly. - IF (ilcl .EQ. 0) klcl=kmax - - ! Get the accumulated buoyant energy from the parcel's starting - ! point, at all levels up to the top level. - benaccum(1) = 0.0D0 - benamin = 9d9 - DO k = 2,kmax - dz = zrel(k) - zrel(k-1) - benaccum(k) = benaccum(k-1) + .5D0*dz*(buoy(k-1) + buoy(k)) - IF (benaccum(k) .LT. benamin) THEN - benamin = benaccum(k) - END IF - END DO - - ! Determine equilibrium level (el), which we define as the highest - ! level of non-negative buoyancy above the lcl. note, this may be - ! the top level if the parcel is still buoyant there. - - elfound = .FALSE. - DO k = kmax,klcl,-1 - IF (buoy(k) .GE. 0.D0) THEN - ! k of equilibrium level - kel = k - elfound = .TRUE. - EXIT - !GOTO 50 - END IF - END DO - - ! If we got through that loop, then there is no non-negative - ! buoyancy above the lcl in the sounding. in these situations, - ! both cape and cin will be set to -0.1 j/kg. (see below about - ! missing values in v6.1.0). also, where cape is - ! non-zero, cape and cin will be set to a minimum of +0.1 j/kg, so - ! that the zero contour in either the cin or cape fields will - ! circumscribe regions of non-zero cape. - - ! In v6.1.0 of ncl, we added a _fillvalue attribute to the return - ! value of this function. at that time we decided to change -0.1 - ! to a more appropriate missing value, which is passed into this - ! routine as cmsg. - - ! cape(i,j,kpar) = -0.1D0 - ! cin(i,j,kpar) = -0.1D0 - IF (.NOT. elfound) THEN - cape(i,j,kpar) = cmsg - cin(i,j,kpar) = cmsg - klfc = kmax - CYCLE - END IF - -! GOTO 102 - -! 50 CONTINUE - - ! If there is an equilibrium level, then cape is positive. we'll - ! define the level of free convection (lfc) as the point below the - ! el, but at or above the lcl, where accumulated buoyant energy is a - ! minimum. the net positive area (accumulated buoyant energy) from - ! the lfc up to the el will be defined as the cape, and the net - ! negative area (negative of accumulated buoyant energy) from the - ! parcel starting point to the lfc will be defined as the convective - ! inhibition (cin). - - ! First get the lfc according to the above definition. - benamin = 9D9 - klfc = kmax - DO k = klcl,kel - IF (benaccum(k) .LT. benamin) THEN - benamin = benaccum(k) - klfc = k - END IF - END DO - - ! Now we can assign values to cape and cin - - cape(i,j,kpar) = MAX(benaccum(kel)-benamin, 0.1D0) - cin(i,j,kpar) = MAX(-benamin, 0.1D0) - - ! cin is uninteresting when cape is small (< 100 j/kg), so set - ! cin to -0.1 (see note about missing values in v6.1.0) in - ! that case. - - ! In v6.1.0 of ncl, we added a _fillvalue attribute to the return - ! value of this function. at that time we decided to change -0.1 - ! to a more appropriate missing value, which is passed into this - ! routine as cmsg. - - ! IF (cape(i,j,kpar).lt.100.D0) cin(i,j,kpar) = -0.1D0 - IF (cape(i,j,kpar) .LT. 100.D0) cin(i,j,kpar) = cmsg -! 102 CONTINUE - - END DO - - IF (i3dflag .EQ. 0) THEN - cape(i,j,mkzh) = cape(i,j,kpar1) - cin(i,j,mkzh) = cin(i,j,kpar1) - ! meters agl - cin(i,j,mkzh-1) = zrel(klcl) + ghtpari - ter(i,j) - ! meters agl - cin(i,j,mkzh-2) = zrel(klfc) + ghtpari - ter(i,j) - - ENDIF - END DO + !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(tlcl, ethpari, & + !$OMP zlcl, kk, ilcl, klcl, tmklift, tvenv, tvlift, ghtlift, & + !$OMP facden, tmkenv, qvpenv, eslift, qvplift, buoy, benamin, & + !$OMP benaccum, zrel, kmax, dz, elfound, & + !$OMP kel, klfc, pavg, p2, p1, totthe, totqvp, totprs, & + !$OMP i, j, k, kpar, kpar1, kpar2, qvppari, tmkpari, p, pup, pdn, th, & + !$OMP pp1, pp2, ethmax, eth_temp, klev) SCHEDULE(runtime) + DO j = 1,mjy + DO i = 1,mix + cape(i,j,1) = 0.D0 + cin(i,j,1) = 0.D0 + ! find parcel with max theta-e in lowest 3 km agl. + ethmax = -1.D0 + eth_temp = -1.D0 + DO k = 1, mkzh + IF (ght_new(k,i,j)-ter(i,j) .LT. 3000.D0) THEN + tlcl = TLCLC1 / (LOG(tmk_new(k,i,j)**TLCLC2/& + (MAX(qvp_new(k,i,j), 1.d-15)*prs_new(k,i,j)/(EPS+MAX(qvp_new(k,i,j), 1.d-15))))-TLCLC3)+& + TLCLC4 + eth_temp(k) = tmk_new(k,i,j) * (1000.D0/prs_new(k,i,j))**& + (GAMMA*(1.D0 + GAMMAMD*(MAX(qvp_new(k,i,j), 1.d-15))))*& + EXP((THTECON1/tlcl - THTECON2)*(MAX(qvp_new(k,i,j), 1.d-15))*& + (1.D0 + THTECON3*(MAX(qvp_new(k,i,j), 1.d-15)))) + END IF + END DO + klev = mkzh + DO k = 1,mkzh + IF (eth_temp(k) .GT. ethmax) THEN + klev = k + ethmax = eth_temp(k) + END IF + END DO + + kpar1 = klev + kpar2 = klev + + + ! Establish average properties of that parcel + ! (over depth of approximately davg meters) + + !davg = 500.D0 + pavg = 500.D0 * prs_new(kpar1,i,j)*& + G/(RD*tvirtual(tmk_new(kpar1,i,j), qvp_new(kpar1,i,j))) + p2 = MIN(prs_new(kpar1,i,j)+.5d0*pavg, prsf(mkzh,i,j)) + p1 = p2 - pavg + totthe = 0.D0 + totqvp = 0.D0 + totprs = 0.D0 + DO k = mkzh,2,-1 + IF (prsf(k,i,j) .LE. p1) EXIT !GOTO 35 + IF (prsf(k-1,i,j) .GE. p2) CYCLE !GOTO 34 + p = prs_new(k,i,j) + pup = prsf(k,i,j) + pdn = prsf(k-1,i,j) + !q = MAX(qvp_new(k,i,j),1.D-15) + th = tmk_new(k,i,j)*(1000.D0/prs_new(k,i,j))**(GAMMA*(1.D0 + GAMMAMD*MAX(qvp_new(k,i,j),1.D-15))) + pp1 = MAX(p1,pdn) + pp2 = MIN(p2,pup) + IF (pp2 .GT. pp1) THEN + ! deltap = pp2 - pp1 + totqvp = totqvp + MAX(qvp_new(k,i,j),1.D-15)*(pp2 - pp1) + totthe = totthe + th*(pp2 - pp1) + totprs = totprs + (pp2 - pp1) + END IF + END DO + qvppari = totqvp/totprs + tmkpari = (totthe/totprs)*& + (prs_new(kpar1,i,j)/1000.D0)**(GAMMA*(1.D0+GAMMAMD*qvp_new(kpar1,i,j))) + + DO kpar = kpar1, kpar2 + + ! Calculate temperature and moisture properties of parcel + ! (note, qvppari and tmkpari already calculated above for 2d + ! case.) + + !prspari = prs_new(kpar,i,j) + !ghtpari = ght_new(kpar,i,j) + gammam = GAMMA * (1.D0 + GAMMAMD*qvppari) + cpm = CP * (1.D0 + CPMD*qvppari) + + e = MAX(1.D-20,qvppari*prs_new(kpar,i,j)/(EPS + qvppari)) + tlcl = TLCLC1/(LOG(tmkpari**TLCLC2/e) - TLCLC3) + TLCLC4 + ethpari = tmkpari*(1000.D0/prs_new(kpar,i,j))**(GAMMA*(1.D0 + GAMMAMD*qvppari))*& + EXP((THTECON1/tlcl - THTECON2)*qvppari*(1.D0 + THTECON3*qvppari)) + zlcl = ght_new(kpar,i,j) + (tmkpari - tlcl)/(G/cpm) + + ! Calculate buoyancy and relative height of lifted parcel at + ! all levels, and store in bottom up arrays. add a level at the + ! lcl, + ! and at all points where buoyancy is zero. + ! + ! + ! For arrays that go bottom to top + kk = 0 + ilcl = 0 + + IF (ght_new(kpar,i,j) .GE. zlcl) THEN + ! Initial parcel already saturated or supersaturated. + ilcl = 2 + klcl = 1 + END IF + + k = kpar + DO k = kpar,1,-1 + ! For arrays that go bottom to top + kk = kk + 1 + + ! Model level is below lcl + IF (ght_new(k,i,j) .LT. zlcl) THEN + tmklift = tmk_new(kpar,i,j) - G/(CP * (1.D0 + CPMD*qvp_new(kpar,i,j)))*& + (ght_new(k,i,j) - ght_new(kpar,i,j)) + tvenv = tmk_new(k,i,j)*(EPS + qvp_new(k,i,j))/(EPS*(1.D0 + qvp_new(k,i,j))) + tvlift = tmklift*(EPS + qvp_new(kpar,i,j))/(EPS*(1.D0 + qvp_new(kpar,i,j))) + ghtlift = ght_new(k,i,j) + ELSE IF (ght(i,j,k) .GE. zlcl .AND. ilcl .EQ. 0) THEN + ! This model level and previous model level straddle the lcl, + ! so first create a new level in the bottom-up array, at the lcl. + facden = 1/(ght_new(k,i,j) - ght_new(k+1,i,j)) + tmkenv = tmk_new(k+1,i,j)*((ght_new(k,i,j)-zlcl)*facden) + tmk_new(k,i,j)*& + ((zlcl-ght_new(k+1,i,j))*facden) + qvpenv = qvp_new(k+1,i,j)*((ght_new(k,i,j)-zlcl)*facden) + qvp_new(k,i,j)*& + ((zlcl-ght_new(k+1,i,j))*facden) + tvenv = tmkenv* (EPS + qvpenv) / (EPS * (1.D0 + qvpenv)) + tvlift = tlcl* (EPS + qvp_new(kpar,i,j)) / (EPS *(1.D0 + qvp_new(kpar,i,j))) + ghtlift = zlcl + ilcl = 1 + ELSE + tmklift = TONPSADIABAT(ethpari, prs_new(k,i,j), psadithte, psadiprs,& + psaditmk, GAMMA, errstat, errmsg) + eslift = EZERO*EXP(ESLCON1*(tmklift - CELKEL)/(tmklift - ESLCON2)) + qvplift = EPS*eslift/(prs_new(k,i,j) - eslift) + tvenv = tmk_new(k,i,j) * (EPS + qvp_new(k,i,j)) / (EPS * (1.D0 + qvp_new(k,i,j))) + tvlift = tmklift*(EPS + qvplift) / (EPS * (1.D0 + qvplift)) + ghtlift = ght_new(k,i,j) + END IF + ! Buoyancy + buoy(kk) = G*(tvlift - tvenv)/tvenv + zrel(kk) = ghtlift - ght_new(kpar,i,j) + IF ((kk .GT. 1) .AND. (buoy(kk)*buoy(kk-1) .LT. 0.0D0)) THEN + ! Parcel ascent curve crosses sounding curve, so create a new level + ! in the bottom-up array at the crossing. + kk = kk + 1 + buoy(kk) = buoy(kk-1) + zrel(kk) = zrel(kk-1) + buoy(kk-1) = 0.D0 + zrel(kk-1) = zrel(kk-2) + buoy(kk-2)/& + (buoy(kk-2) - buoy(kk))*(zrel(kk) - zrel(kk-2)) + END IF + IF (ilcl .EQ. 1) THEN + klcl = kk + ilcl = 2 + CYCLE + END IF + END DO + + kmax = kk + ! IF (kmax .GT. 150) THEN + ! errstat = ALGERR + ! WRITE(errmsg, *) 'capecalc3d: kmax got too big. kmax=',kmax + ! RETURN + ! END IF + + ! If no lcl was found, set klcl to kmax. it is probably not + ! really + ! at kmax, but this will make the rest of the routine behave + ! properly. + IF (ilcl .EQ. 0) klcl=kmax + + ! Get the accumulated buoyant energy from the parcel's starting + ! point, at all levels up to the top level. + benaccum(1) = 0.0D0 + benamin = 9d9 + DO k = 2,kmax + dz = zrel(k) - zrel(k-1) + benaccum(k) = benaccum(k-1) + .5D0*dz*(buoy(k-1) + buoy(k)) + IF (benaccum(k) .LT. benamin) THEN + benamin = benaccum(k) + END IF + END DO + ! Determine equilibrium level (el), which we define as the highest + ! level of non-negative buoyancy above the lcl. note, this may be + ! the top level if the parcel is still buoyant there. + + elfound = .FALSE. + DO k = kmax,klcl,-1 + IF (buoy(k) .GE. 0.D0) THEN + ! k of equilibrium level + kel = k + elfound = .TRUE. + EXIT + END IF + END DO + + ! If we got through that loop, then there is no non-negative + ! buoyancy above the lcl in the sounding. in these situations, + ! both cape and cin will be set to -0.1 j/kg. (see below about + ! missing values in v6.1.0). also, where cape is + ! non-zero, cape and cin will be set to a minimum of +0.1 j/kg, so + ! that the zero contour in either the cin or cape fields will + ! circumscribe regions of non-zero cape. + ! In v6.1.0 of ncl, we added a _fillvalue attribute to the return + ! value of this function. at that time we decided to change -0.1 + ! to a more appropriate missing value, which is passed into this + ! routine as cmsg. + + IF (.NOT. elfound) THEN + cape(i,j,kpar) = cmsg + cin(i,j,kpar) = cmsg + klfc = kmax + CYCLE + END IF + + + ! If there is an equilibrium level, then cape is positive. + ! we'll + ! define the level of free convection (lfc) as the point below + ! the + ! el, but at or above the lcl, where accumulated buoyant energy + ! is a + ! minimum. the net positive area (accumulated buoyant energy) + ! from + ! the lfc up to the el will be defined as the cape, and the net + ! negative area (negative of accumulated buoyant energy) from + ! the + ! parcel starting point to the lfc will be defined as the + ! convective + ! inhibition (cin). + + ! First get the lfc according to the above definition. + benamin = 9D9 + klfc = kmax + DO k = klcl,kel + IF (benaccum(k) .LT. benamin) THEN + benamin = benaccum(k) + klfc = k + END IF + END DO + + ! Now we can assign values to cape and cin + + cape(i,j,kpar) = MAX(benaccum(kel)-benamin, 0.1D0) + cin(i,j,kpar) = MAX(-benamin, 0.1D0) + + ! cin is uninteresting when cape is small (< 100 j/kg), so set + ! cin to -0.1 (see note about missing values in v6.1.0) in + ! that case. + + ! In v6.1.0 of ncl, we added a _fillvalue attribute to the return + ! value of this function. at that time we decided to change -0.1 + ! to a more appropriate missing value, which is passed into this + ! routine as cmsg. + + IF (cape(i,j,kpar) .LT. 100.D0) cin(i,j,kpar) = cmsg + + END DO + + cape(i,j,mkzh) = cape(i,j,kpar1) + cin(i,j,mkzh) = cin(i,j,kpar1) + ! meters agl + cin(i,j,mkzh-1) = zrel(klcl) + ght_new(kpar1,i,j) - ter(i,j) + ! meters agl + cin(i,j,mkzh-2) = zrel(klfc) + ght_new(kpar1,i,j) - ter(i,j) + + END DO END DO + !$OMP END PARALLEL DO RETURN -END SUBROUTINE DCAPECALC3D +END SUBROUTINE DCAPECALC2D diff --git a/fortran/wrf_cloud_fracf.f90 b/fortran/wrf_cloud_fracf.f90 index 61cbe4c..92ae745 100644 --- a/fortran/wrf_cloud_fracf.f90 +++ b/fortran/wrf_cloud_fracf.f90 @@ -18,7 +18,12 @@ SUBROUTINE DCLOUDFRAC(pres, rh, lowc, midc, highc, nz, ns, ew) kchi = 0 kcmi = 0 kclo = 0 + lowc = 0 + midc = 0 + highc = 0 + !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i, j, k, kchi, kcmi, kclo) & + !$OMP SCHEDULE(runtime) DO j = 1,ns DO i = 1,ew DO k = 1,nz-1 @@ -27,30 +32,125 @@ SUBROUTINE DCLOUDFRAC(pres, rh, lowc, midc, highc, nz, ns, ew) IF ( pres(i,j,k) .GT. 45000. ) kchi=k END DO - DO k = 1,nz-1 - IF (k .GE. kclo .AND. k .LT. kcmi) THEN - lowc(i,j) = MAX(rh(i,j,k), lowc(i,j)) - ELSE IF (k .GE. kcmi .AND. k .LT. kchi) THEN ! mid cloud - midc(i,j) = MAX(rh(i,j,k), midc(i,j)) - ELSE if (k .GE. kchi) THEN ! high cloud - highc(i,j) = MAX(rh(i,j,k), highc(i,j)) - END IF - END DO + DO k = 1,nz-1 + IF (k .GE. kclo .AND. k .LT. kcmi) THEN + lowc(i,j) = MAX(rh(i,j,k), lowc(i,j)) + ELSE IF (k .GE. kcmi .AND. k .LT. kchi) THEN ! mid cloud + midc(i,j) = MAX(rh(i,j,k), midc(i,j)) + ELSE if (k .GE. kchi) THEN ! high cloud + highc(i,j) = MAX(rh(i,j,k), highc(i,j)) + END IF + END DO - lowc(i,j) = 4.0*lowc(i,j)/100. - 3.0 - midc(i,j) = 4.0*midc(i,j)/100. - 3.0 - highc(i,j) = 2.5*highc(i,j)/100. - 1.5 + lowc(i,j) = 4.0*lowc(i,j)/100. - 3.0 + midc(i,j) = 4.0*midc(i,j)/100. - 3.0 + highc(i,j) = 2.5*highc(i,j)/100. - 1.5 - lowc(i,j) = MIN(lowc(i,j), 1.0) - lowc(i,j) = MAX(lowc(i,j), 0.0) - midc(i,j) = MIN(midc(i,j), 1.0) - midc(i,j) = MAX(midc(i,j), 0.0) - highc(i,j) = MIN(highc(i,j), 1.0) - highc(i,j) = MAX(highc(i,j), 0.0) + lowc(i,j) = MIN(lowc(i,j), 1.0) + lowc(i,j) = MAX(lowc(i,j), 0.0) + midc(i,j) = MIN(midc(i,j), 1.0) + midc(i,j) = MAX(midc(i,j), 0.0) + highc(i,j) = MIN(highc(i,j), 1.0) + highc(i,j) = MAX(highc(i,j), 0.0) END DO END DO + !$OMP END PARALLEL DO RETURN END SUBROUTINE DCLOUDFRAC + + +! NCLFORTSTART +SUBROUTINE DCLOUDFRAC2(vert, rh, vert_inc_w_height, low_thresh, mid_thresh, & + high_thresh, msg, lowc, midc, highc, nz, ns, ew) + IMPLICIT NONE + + !f2py threadsafe + !f2py intent(in,out) :: lowc, midc, highc + + INTEGER nz, ns, ew + REAL(KIND=8), DIMENSION(ew, ns, nz), INTENT(IN) :: rh, vert + REAL(KIND=8), INTENT(IN) :: low_thresh, mid_thresh, high_thresh, msg + INTEGER, INTENT(IN) :: vert_inc_w_height + REAL(KIND=8), DIMENSION(ew, ns), INTENT(OUT) :: lowc, midc, highc + +! NCLEND + + INTEGER i, j, k + INTEGER kchi, kcmi, kclo + + ! Initialize the output + lowc = 0 + midc = 0 + highc = 0 + + !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i, j, k, kchi, kcmi, kclo) & + !$OMP SCHEDULE(runtime) + DO j = 1,ns + DO i = 1,ew + ! A value of -1 means 'not found'. This is needed to handle + ! the mountains, where the level thresholds are below the lowest + ! model level. + kchi = -1 + kcmi = -1 + kclo = -1 + + IF (vert_inc_w_height .NE. 0) THEN ! Vert coord increase with height + DO k = 1,nz + IF (vert(i,j,k) .LT. low_thresh) kclo=k + IF (vert(i,j,k) .LT. mid_thresh) kcmi=k + IF (vert(i,j,k) .LT. high_thresh) kchi=k + END DO + ELSE ! Vert coord decrease with height + DO k = 1,nz + IF (vert(i,j,k) .GT. low_thresh) kclo=k + IF (vert(i,j,k) .GT. mid_thresh) kcmi=k + IF (vert(i,j,k) .GT. high_thresh) kchi=k + END DO + ENDIF + + DO k = 1,nz + IF (k .GE. kclo .AND. k .LT. kcmi) THEN + lowc(i,j) = MAX(rh(i,j,k), lowc(i,j)) + ELSE IF (k .GE. kcmi .AND. k .LT. kchi) THEN ! mid cloud + midc(i,j) = MAX(rh(i,j,k), midc(i,j)) + ELSE if (k .GE. kchi) THEN ! high cloud + highc(i,j) = MAX(rh(i,j,k), highc(i,j)) + END IF + END DO + + ! Only do this when a cloud threshold is in the model vertical + ! domain, otherwise it will be set to missing + IF (kclo .GE. 1) THEN + lowc(i,j) = 4.0*lowc(i,j)/100. - 3.0 + lowc(i,j) = MIN(lowc(i,j), 1.0) + lowc(i,j) = MAX(lowc(i,j), 0.0) + ELSE + lowc(i,j) = msg + END IF + + IF (kcmi .GE. 1) THEN + midc(i,j) = 4.0*midc(i,j)/100. - 3.0 + midc(i,j) = MIN(midc(i,j), 1.0) + midc(i,j) = MAX(midc(i,j), 0.0) + ELSE + midc(i,j) = msg + END IF + + IF (kchi .GE. 1) THEN + highc(i,j) = 2.5*highc(i,j)/100. - 1.5 + highc(i,j) = MIN(highc(i,j), 1.0) + highc(i,j) = MAX(highc(i,j), 0.0) + ELSE + highc(i,j) = msg + END IF + + END DO + END DO + !$OMP END PARALLEL DO + + RETURN + +END SUBROUTINE DCLOUDFRAC2 diff --git a/fortran/wrf_constants.f90 b/fortran/wrf_constants.f90 index 617e63f..336c0e3 100644 --- a/fortran/wrf_constants.f90 +++ b/fortran/wrf_constants.f90 @@ -9,7 +9,15 @@ MODULE wrf_constants REAL(KIND=8), PARAMETER :: PI = 3.1415926535897932384626433D0 REAL(KIND=8), PARAMETER :: RAD_PER_DEG = PI/180.D0 REAL(KIND=8), PARAMETER :: DEG_PER_RAD = 180.D0/PI - REAL(KIND=8), PARAMETER :: DEFAULT_FILL = 9.9692099683868690D36 + REAL(KIND=8), PARAMETER :: DEFAULT_FILL = 9.9692099683868690E36 + INTEGER(KIND=1), PARAMETER :: DEFAULT_FILL_INT8 = -127 + INTEGER(KIND=2), PARAMETER :: DEFAULT_FILL_INT16 = -32767 + INTEGER(KIND=4), PARAMETER :: DEFAULT_FILL_INT32 = -2147483647 + INTEGER(KIND=8), PARAMETER :: DEFAULT_FILL_INT64 = INT(-9223372036854775806D0, KIND=8) + REAL(KIND=4), PARAMETER :: DEFAULT_FILL_FLOAT = 9.9692099683868690E36 + REAL(KIND=8), PARAMETER :: DEFAULT_FILL_DOUBLE = 9.9692099683868690E36 + CHARACTER(LEN=1), PARAMETER :: DEFAULT_FILL_CHAR = ACHAR(0) + REAL(KIND=8), PARAMETER :: P1000MB = 100000.D0 ! j/k/kg diff --git a/fortran/wrf_fctt.f90 b/fortran/wrf_fctt.f90 index 97da1b9..efc59b6 100644 --- a/fortran/wrf_fctt.f90 +++ b/fortran/wrf_fctt.f90 @@ -18,23 +18,15 @@ SUBROUTINE wrfcttcalc(prs, tk, qci, qcw, qvp, ght, ter, ctt, haveqci, nz, ns, ew ! LOCAL VARIABLES INTEGER i,j,k,ripk - !INTEGER :: mjx,miy,mkzh - REAL(KIND=8) :: vt, opdepthu, opdepthd, dp - REAL(KIND=8) :: ratmix, arg1, arg2, agl_hgt - REAL(KIND=8) :: fac, prsctt - !REAL(KIND=8) :: eps,ussalr,rgas,grav,abscoefi,abscoef,celkel,wrfout - !REAL(KIND=8) :: ght(ew,ns,nz),stuff(ew,ns) - !REAL(KIND=8), DIMENSION(ew,ns,nz) :: pf(ns,ew,nz),p1,p2 + REAL(KIND=8) :: opdepthu, opdepthd, dp, arg1, fac, prsctt, ratmix + REAL(KIND=8) :: arg2, agl_hgt, vt REAL(KIND=8), DIMENSION(ew,ns,nz) :: pf REAL(KIND=8) :: p1, p2 - !mjx = ew - !miy = ns - !mkzh = nz + !$OMP PARALLEL - prsctt = 0 ! removes the warning - -! Calculate the surface pressure + ! Calculate the surface pressure + !$OMP DO COLLAPSE(2) SCHEDULE(runtime) DO j=1,ns DO i=1,ew ratmix = .001D0*qvp(i,j,1) @@ -46,7 +38,9 @@ SUBROUTINE wrfcttcalc(prs, tk, qci, qcw, qvp, ght, ter, ctt, haveqci, nz, ns, ew pf(i,j,nz) = prs(i,j,1)*(vt/(vt + USSALR*(agl_hgt)))**(arg1) END DO END DO + !$OMP END DO + !$OMP DO COLLAPSE(3) SCHEDULE(runtime) DO k=1,nz-1 DO j=1,ns DO i=1,ew @@ -55,26 +49,27 @@ SUBROUTINE wrfcttcalc(prs, tk, qci, qcw, qvp, ght, ter, ctt, haveqci, nz, ns, ew END DO END DO END DO + !$OMP END DO + !$OMP DO COLLAPSE(2) PRIVATE(i, j, k, ripk, opdepthd, opdepthu, & + !$OMP prsctt, dp, p1, p2, fac, arg1) SCHEDULE(runtime) DO j=1,ns DO i=1,ew opdepthd = 0.D0 k = 0 + prsctt = 0 -! Integrate downward from model top, calculating path at full -! model vertical levels. - -!20 opdepthu=opdepthd + ! Integrate downward from model top, calculating path at full + ! model vertical levels. DO k=1, nz opdepthu = opdepthd - !k=k+1 ripk = nz - k + 1 - IF (k .EQ. 1) THEN - dp = 200.D0*(pf(i,j,1) - prs(i,j,nz)) ! should be in Pa - ELSE + IF (k .NE. 1) THEN dp = 100.D0*(pf(i,j,k) - pf(i,j,k-1)) ! should be in Pa + ELSE + dp = 200.D0*(pf(i,j,1) - prs(i,j,nz)) ! should be in Pa END IF IF (haveqci .EQ. 0) then @@ -89,7 +84,6 @@ SUBROUTINE wrfcttcalc(prs, tk, qci, qcw, qvp, ght, ter, ctt, haveqci, nz, ns, ew END IF IF (opdepthd .LT. 1. .AND. k .LT. nz) THEN - !GOTO 20 CYCLE ELSE IF (opdepthd .LT. 1. .AND. k .EQ. nz) THEN @@ -111,15 +105,14 @@ SUBROUTINE wrfcttcalc(prs, tk, qci, qcw, qvp, ght, ter, ctt, haveqci, nz, ns, ew fac = (prsctt - p1)/(p2 - p1) arg1 = fac*(tk(i,j,ripk) - tk(i,j,ripk+1)) - CELKEL ctt(i,j) = tk(i,j,ripk+1) + arg1 - !GOTO 40 EXIT END IF END DO END DO END DO -! 30 CONTINUE -! 40 CONTINUE -! 190 CONTINUE + !$OMP END DO + + !$OMP END PARALLEL RETURN END SUBROUTINE wrfcttcalc diff --git a/fortran/wrf_pvo.f90 b/fortran/wrf_pvo.f90 index 209a257..0a4e788 100644 --- a/fortran/wrf_pvo.f90 +++ b/fortran/wrf_pvo.f90 @@ -23,29 +23,29 @@ SUBROUTINE DCOMPUTEABSVORT(av, u, v, msfu, msfv, msft, cor, dx, dy, nx, ny, nz,& REAL(KIND=8) :: dsy, dsx, dudy, dvdx, avort REAL(KIND=8) :: mm - ! PRINT*,'nx,ny,nz,nxp1,nyp1' - ! PRINT*,nx,ny,nz,nxp1,nyp1 + !$OMP PARALLEL DO COLLAPSE(3) PRIVATE(i, j, k, jp1, jm1, ip1, im1, & + !$OMP dsx, dsy, mm, dudy, dvdx, avort) SCHEDULE(runtime) DO k = 1,nz DO j = 1,ny - jp1 = MIN(j+1, ny) - jm1 = MAX(j-1, 1) DO i = 1,nx + jp1 = MIN(j+1, ny) + jm1 = MAX(j-1, 1) ip1 = MIN(i+1, nx) im1 = MAX(i-1, 1) - ! PRINT *,jp1,jm1,ip1,im1 dsx = (ip1 - im1) * dx dsy = (jp1 - jm1) * dy mm = msft(i,j)*msft(i,j) - ! PRINT *,j,i,u(i,jp1,k),msfu(i,jp1),u(i,jp1,k)/msfu(i,jp1) dudy = 0.5D0*(u(i,jp1,k)/msfu(i,jp1) + u(i+1,jp1,k)/msfu(i+1,jp1) - & u(i,jm1,k)/msfu(i,jm1) - u(i+1,jm1,k)/msfu(i+1,jm1))/dsy*mm dvdx = 0.5D0*(v(ip1,j,k)/msfv(ip1,j) + v(ip1,j+1,k)/msfv(ip1,j+1) - & v(im1,j,k)/msfv(im1,j) - v(im1,j+1,k)/msfv(im1,j+1))/dsx*mm avort = dvdx - dudy + cor(i,j) av(i,j,k) = avort*1.D5 + END DO END DO END DO + !$OMP END PARALLEL DO RETURN @@ -80,22 +80,23 @@ SUBROUTINE DCOMPUTEPV(pv, u, v, theta, prs, msfu, msfv, msft, cor, dx, dy, nx, & REAL(KIND=8) :: dsy, dsx, dp, dudy, dvdx, dudp, dvdp, dthdp, avort REAL(KIND=8) :: dthdx, dthdy, mm - ! PRINT*,'nx,ny,nz,nxp1,nyp1' - ! PRINT*,nx,ny,nz,nxp1,nyp1 + !$OMP PARALLEL DO COLLAPSE(3) PRIVATE(i, j, k, kp1, km1, jp1, jm1, ip1, & + !$OMP im1, dsx, dsy, mm, dudy, dvdx, avort, & + !$OMP dp, dudp, dvdp, dthdp, dthdx, dthdy) SCHEDULE(runtime) DO k = 1,nz - kp1 = MIN(k+1, nz) - km1 = MAX(k-1, 1) DO J = 1,ny - jp1 = MIN(j+1, ny) - jm1 = MAX(j-1, 1) DO i = 1,nx + kp1 = MIN(k+1, nz) + km1 = MAX(k-1, 1) + jp1 = MIN(j+1, ny) + jm1 = MAX(j-1, 1) ip1 = MIN(i+1, nx) im1 = MAX(i-1, 1) - ! PRINT *,jp1,jm1,ip1,im1 + dsx = (ip1 - im1)*dx dsy = (jp1 - jm1)*dy mm = msft(i,j)*msft(i,j) - ! PRINT *,j,i,u(i,jp1,k),msfu(i,jp1),u(i,jp1,k)/msfu(i,jp1) + dudy = 0.5D0*(u(i,jp1,k)/msfu(i,jp1) + u(i+1,jp1,k)/msfu(i+1,jp1) - & u(i,jm1,k)/msfu(i,jm1) - u(i+1,jm1,k)/msfu(i+1,jm1))/dsy*mm dvdx = 0.5D0*(v(ip1,j,k)/msfv(ip1,j) + v(ip1,j+1,k)/msfv(ip1,j+1) - & @@ -108,14 +109,11 @@ SUBROUTINE DCOMPUTEPV(pv, u, v, theta, prs, msfu, msfv, msft, cor, dx, dy, nx, & dthdx = (theta(ip1,j,k) - theta(im1,j,k))/dsx*msft(i,j) dthdy = (theta(i,jp1,k) - theta(i,jm1,k))/dsy*msft(i,j) pv(i,j,k) = -G*(dthdp*avort - dvdp*dthdx + dudp*dthdy)*10000.D0 - ! if(i.eq.300 .and. j.eq.300) then - ! PRINT*,'avort,dudp,dvdp,dthdp,dthdx,dthdy,pv' - ! PRINT*,avort,dudp,dvdp,dthdp,dthdx,dthdy,pv(i,j,k) - ! endif pv(i,j,k) = pv(i,j,k)*1.D2 END DO END DO END DO + !$OMP END PARALLEL DO RETURN diff --git a/fortran/wrf_pw.f90 b/fortran/wrf_pw.f90 index ba19ffb..bd80876 100644 --- a/fortran/wrf_pw.f90 +++ b/fortran/wrf_pw.f90 @@ -18,14 +18,21 @@ SUBROUTINE DCOMPUTEPW(p, tv, qv, ht, pw, nx, ny, nz, nzh) !REAL(KIND=8),PARAMETER :: R=287.06 pw = 0 + + !$OMP PARALLEL + DO k=1,nz + !$OMP DO COLLAPSE(2) SCHEDULE(runtime) DO j=1,ny DO i=1,nx pw(i,j) = pw(i,j) + ((p(i,j,k)/(RD*tv(i,j,k)))*qv(i,j,k)*(ht(i,j,k+1) - ht(i,j,k))) END DO END DO + !$OMP END DO END DO + !$OMP END PARALLEL + RETURN END SUBROUTINE DCOMPUTEPW diff --git a/fortran/wrf_relhl.f90 b/fortran/wrf_relhl.f90 index 9efeec8..b3e562f 100644 --- a/fortran/wrf_relhl.f90 +++ b/fortran/wrf_relhl.f90 @@ -56,10 +56,10 @@ SUBROUTINE DCALRELHL(u, v, ght, ter, top, sreh, miy, mjx, mkzh) INTEGER :: i, j, k, k10, k3, ktop !REAL(KIND=8), PARAMETER :: DTR=PI/180.d0, DPR=180.d0/PI - !DO j = 1, mjx-1 + !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i,j,k,k10,k3,ktop, cu, cv, x, & + !$OMP sum, dh, sdh, su, sv, ua, va, asp, adr, bsp, bdr) SCHEDULE(runtime) DO j=1, mjx DO i=1, miy - !DO i=1, miy-1 sdh = 0.D0 su = 0.D0 sv = 0.D0 @@ -82,12 +82,14 @@ SUBROUTINE DCALRELHL(u, v, ght, ter, top, sreh, miy, mjx, mkzh) IF (k10 .EQ. 0) THEN k10 = 2 ENDIF + DO k = k3, k10, -1 dh = ght(i,j,k-1) - ght(i,j,k) sdh = sdh + dh su = su + 0.5D0*dh*(u(i,j,k-1) + u(i,j,k)) sv = sv + 0.5D0*dh*(v(i,j,k-1) + v(i,j,k)) END DO + ua = su/sdh va = sv/sdh asp = SQRT(ua*ua + va*va) @@ -96,11 +98,13 @@ SUBROUTINE DCALRELHL(u, v, ght, ter, top, sreh, miy, mjx, mkzh) ELSE adr = DEG_PER_RAD * (PI + ATAN2(ua,va)) ENDIF + bsp = 0.75D0*asp bdr = adr + 30.D0 IF (bdr .GT. 360.D0) THEN bdr = bdr - 360.D0 ENDIF + cu = -bsp*SIN(bdr * RAD_PER_DEG) cv = -bsp*COS(bdr * RAD_PER_DEG) sum = 0.D0 @@ -112,6 +116,7 @@ SUBROUTINE DCALRELHL(u, v, ght, ter, top, sreh, miy, mjx, mkzh) sreh(i,j) = -sum END DO END DO + !$OMP END PARALLEL DO RETURN diff --git a/fortran/wrf_rip_phys_routines.f90 b/fortran/wrf_rip_phys_routines.f90 index 350fb75..51af679 100644 --- a/fortran/wrf_rip_phys_routines.f90 +++ b/fortran/wrf_rip_phys_routines.f90 @@ -50,13 +50,23 @@ SUBROUTINE WETBULBCALC(prs, tmk, qvp, twb, nx, ny, nz, psafile, errstat, errmsg) !NCLEND INTEGER :: i, j, k - INTEGER :: jtch, jt, ipch, ip + INTEGER :: jt, ip REAL(KIND=8) :: q, t, p, e, tlcl, eth REAL(KIND=8) :: fracip, fracip2, fracjt, fracjt2 REAL(KIND=8), DIMENSION(150) :: PSADITHTE, PSADIPRS REAL(KIND=8), DIMENSION(150,150) :: PSADITMK REAL(KIND=8) :: tonpsadiabat + INTEGER :: l1, h1, mid1, rang1, l2, h2, mid2, rang2 + INTEGER :: errcnt1, errcnt2, bad_i, bad_j, bad_k + REAL(KIND=8) :: bad_p, bad_eth + !INTEGER :: ip, ipch, jt, jtch + + errcnt1 = 0 + errcnt2 = 0 + bad_i = -1 + bad_j = -1 + bad_k = -1 ! Before looping, set lookup table for getting temperature on ! a pseudoadiabat. @@ -67,6 +77,10 @@ SUBROUTINE WETBULBCALC(prs, tmk, qvp, twb, nx, ny, nz, psafile, errstat, errmsg) RETURN END IF + !$OMP PARALLEL DO COLLAPSE(3) PRIVATE (i, j, k, jt, ip, q, t, p, e, tlcl, & + !$OMP eth, fracip, fracip2, fracjt, fracjt2, l1, h1, mid1, rang1, l2, h2, & + !$OMP mid2, rang2, tonpsadiabat) REDUCTION(+:errcnt1, errcnt2) & + !$OMP SCHEDULE(runtime) DO k=1,nz DO j=1,ny DO i=1,nx @@ -92,26 +106,51 @@ SUBROUTINE WETBULBCALC(prs, tmk, qvp, twb, nx, ny, nz, psafile, errstat, errmsg) tonpsadiabat = eth*(p/1000.)**GAMMA ELSE ! Otherwise, look for the given thte/prs point in the lookup table. - jt=-1 - DO jtch=1,150-1 - IF (eth .GE. PSADITHTE(jtch) .AND. eth .LT. PSADITHTE(jtch+1)) THEN - jt = jtch - EXIT - ENDIF + jt = -1 + l1 = 1 + h1 = 149 + rang1 = h1 - l1 + mid1 = (h1 + l1) / 2 + DO WHILE(rang1 .GT. 1) + IF (eth .GE. psadithte(mid1)) THEN + l1 = mid1 + ELSE + h1 = mid1 + END IF + rang1 = h1 - l1 + mid1 = (h1 + l1) / 2 END DO - - ip=-1 - DO ipch=1,150-1 - IF (p .LE. PSADIPRS(ipch) .AND. p .GT. PSADIPRS(ipch+1)) THEN - ip = ipch - EXIT - ENDIF + jt = l1 + + ip = -1 + l2 = 1 + h2 = 149 + rang2 = h2 - l2 + mid2 = (h2 + l2) / 2 + DO WHILE(rang2 .GT. 1) + IF (p .LE. psadiprs(mid2)) THEN + l2 = mid2 + ELSE + h2 = mid2 + END IF + rang2 = h2 - l2 + mid2 = (h2 + l2) / 2 END DO + ip = l2 IF (jt .EQ. -1 .OR. ip .EQ. -1) THEN - errstat = ALGERR - WRITE(errmsg, *) "Outside of lookup table bounds. prs,thte=", p, eth - RETURN + errcnt1 = errcnt1 + 1 + !$OMP CRITICAL + ! Only do this the first time + IF (bad_i .EQ. -1) THEN + bad_i = i + bad_j = j + bad_k = k + bad_p = p + bad_eth = eth + END IF + !$OMP END CRITICAL + CYCLE ENDIF fracjt = (eth - PSADITHTE(jt))/(PSADITHTE(jt+1) - PSADITHTE(jt)) @@ -119,12 +158,21 @@ SUBROUTINE WETBULBCALC(prs, tmk, qvp, twb, nx, ny, nz, psafile, errstat, errmsg) fracip = (PSADIPRS(ip) - p)/(PSADIPRS(ip) - PSADIPRS(ip+1)) fracip2 = 1. - fracip + IF (PSADITMK(ip,jt) .GT. 1e9 .OR. PSADITMK(ip+1,jt) .GT. 1e9 .OR. & PSADITMK(ip,jt+1) .GT. 1e9 .OR. PSADITMK(ip+1,jt+1) .GT. 1e9) THEN - !PRINT*,'Tried to access missing tmperature in lookup table.' - errstat = ALGERR - WRITE(errmsg, *) "Prs and Thte probably unreasonable. prs, thte=", p, eth - RETURN + errcnt2 = errcnt2 + 1 + !$OMP CRITICAL + ! Only do this the first time + IF (bad_i .EQ. -1) THEN + bad_i = i + bad_j = j + bad_k = k + bad_p = p + bad_eth = eth + END IF + !$OMP END CRITICAL + CYCLE ENDIF tonpsadiabat = fracip2*fracjt2*PSADITMK(ip,jt) + & @@ -139,6 +187,20 @@ SUBROUTINE WETBULBCALC(prs, tmk, qvp, twb, nx, ny, nz, psafile, errstat, errmsg) END DO END DO + !$OMP END PARALLEL DO + + IF (errcnt1 > 0) THEN + errstat = ALGERR + WRITE(errmsg, *) "Outside of lookup table bounds. i=", bad_i, ",j=", bad_j, ",k=", bad_k, ",p=", bad_p, ",eth=", bad_eth + RETURN + END IF + + IF (errcnt2 > 0) THEN + errstat = ALGERR + WRITE(errmsg, *) "PRS and THTE unreasonable. i=", bad_i, ",j=", bad_j, ",k=", bad_k, ",p=", bad_p, ",eth=", bad_eth + RETURN + END IF + RETURN END SUBROUTINE WETBULBCALC @@ -195,6 +257,7 @@ SUBROUTINE OMGCALC(qvp, tmk, www, prs, omg, mx, my, mz) INTEGER :: i, j, k !REAL(KIND=8), PARAMETER :: GRAV=9.81, RGAS=287.04, EPS=0.622 + !$OMP PARALLEL DO COLLAPSE(3) SCHEDULE(runtime) DO k=1,mz DO j=1,my DO i=1,mx @@ -204,6 +267,7 @@ SUBROUTINE OMGCALC(qvp, tmk, www, prs, omg, mx, my, mz) END DO END DO END DO + !$OMP END PARALLEL DO RETURN @@ -255,6 +319,7 @@ SUBROUTINE VIRTUAL_TEMP(temp, ratmix, tv, nx, ny, nz) INTEGER :: i,j,k !REAL(KIND=8),PARAMETER :: EPS = 0.622D0 + !$OMP PARALLEL DO COLLAPSE(3) SCHEDULE(runtime) DO k=1,nz DO j=1,ny DO i=1,nx @@ -262,6 +327,7 @@ SUBROUTINE VIRTUAL_TEMP(temp, ratmix, tv, nx, ny, nz) END DO END DO END DO + !$OMP END PARALLEL DO RETURN diff --git a/fortran/wrf_user.f90 b/fortran/wrf_user.f90 index 4bb7297..1bfc82c 100644 --- a/fortran/wrf_user.f90 +++ b/fortran/wrf_user.f90 @@ -16,13 +16,15 @@ SUBROUTINE DCOMPUTEPI(pi, pressure, nx, ny, nz) !REAL(KIND=8), PARAMETER :: P1000MB=100000.D0, R_D=287.D0, CP=7.D0*R_D/2.D0 + !$OMP PARALLEL DO COLLAPSE(3) SCHEDULE(runtime) DO k = 1,nz - DO j = 1,ny - DO i = 1,nx - pi(i,j,k) = (pressure(i,j,k)/P1000MB)**(RD/CP) - END DO - END DO + DO j = 1,ny + DO i = 1,nx + pi(i,j,k) = (pressure(i,j,k)/P1000MB)**(RD/CP) + END DO + END DO END DO + !$OMP END PARALLEL DO END SUBROUTINE DCOMPUTEPI @@ -37,7 +39,7 @@ SUBROUTINE DCOMPUTETK(tk, pressure, theta, nx) !f2py intent(in,out) :: tk INTEGER, INTENT(IN) :: nx - REAL(KIND=8) :: pi + !REAL(KIND=8) :: pi REAL(KIND=8), DIMENSION(nx), INTENT(IN) :: pressure REAL(KIND=8), DIMENSION(nx), INTENT(IN) :: theta REAL(KIND=8), DIMENSION(nx), INTENT(OUT) :: tk @@ -48,10 +50,13 @@ SUBROUTINE DCOMPUTETK(tk, pressure, theta, nx) !REAL(KIND=8), PARAMETER :: P1000MB=100000.D0, RD=287.D0, CP=7.D0*RD/2.D0 + !$OMP PARALLEL DO SCHEDULE(runtime) DO i = 1,nx - pi = (pressure(i)/P1000MB)**(RD/CP) - tk(i) = pi*theta(i) + !pi = (pressure(i)/P1000MB)**(RD/CP) + !tk(i) = pi * theta(i) + tk(i) = (pressure(i)/P1000MB)**(RD/CP) * theta(i) END DO + !$OMP END PARALLEL DO RETURN @@ -76,9 +81,7 @@ SUBROUTINE DINTERP3DZ(data3d, out2d, zdata, desiredloc, nx, ny, nz, missingval) INTEGER :: i,j,kp,ip,im LOGICAL :: dointerp - REAL(KIND=8) :: height,w1,w2 - - height = desiredloc + REAL(KIND=8) :: w1,w2 ! does vertical coordinate increase or decrease with increasing k? ! set offset appropriately @@ -90,6 +93,8 @@ 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 FIRSTPRIVATE(ip,im) SCHEDULE(runtime) DO i = 1,nx DO j = 1,ny ! Initialize to missing. Was initially hard-coded to -999999. @@ -98,17 +103,17 @@ SUBROUTINE DINTERP3DZ(data3d, out2d, zdata, desiredloc, nx, ny, nz, missingval) kp = nz DO WHILE ((.NOT. dointerp) .AND. (kp >= 2)) - IF (((zdata(i,j,kp-im) < height) .AND. (zdata(i,j,kp-ip) > height))) THEN - w2 = (height - zdata(i,j,kp-im))/(zdata(i,j,kp-ip) - zdata(i,j,kp-im)) + 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) = 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 + !$OMP END PARALLEL DO RETURN @@ -195,20 +200,23 @@ SUBROUTINE DINTERP2DXY(v3d, v2d, xy, nx, ny, nz, nxy) INTEGER :: i, j, k, ij REAL(KIND=8) :: w11, w12, w21, w22, wx, wy + !$OMP PARALLEL DO PRIVATE(i,j,k,ij,w11,w12,w21,w22,wx,wy) & + !$OMP SCHEDULE(runtime) DO ij = 1,nxy - i = MAX(1,MIN(nx-1,INT(xy(1,ij)+1))) - j = MAX(1,MIN(ny-1,INT(xy(2,ij)+1))) - wx = DBLE(i+1) - (xy(1,ij)+1) - wy = DBLE(j+1) - (xy(2,ij)+1) - w11 = wx*wy - w21 = (1.D0-wx)*wy - w12 = wx*(1.D0-wy) - w22 = (1.D0-wx)* (1.D0-wy) - DO k = 1,nz - v2d(ij,k) = w11*v3d(i,j,k) + w21*v3d(i+1,j,k) + & - w12*v3d(i,j+1,k) + w22*v3d(i+1,j+1,k) - END DO + i = MAX(1,MIN(nx-1,INT(xy(1,ij)+1))) + j = MAX(1,MIN(ny-1,INT(xy(2,ij)+1))) + wx = DBLE(i+1) - (xy(1,ij)+1) + wy = DBLE(j+1) - (xy(2,ij)+1) + w11 = wx*wy + w21 = (1.D0-wx)*wy + w12 = wx*(1.D0-wy) + w22 = (1.D0-wx)* (1.D0-wy) + DO k = 1,nz + v2d(ij,k) = w11*v3d(i,j,k) + w21*v3d(i+1,j,k) + & + w12*v3d(i,j+1,k) + w22*v3d(i+1,j+1,k) + END DO END DO + !$OMP END PARALLEL DO RETURN @@ -241,27 +249,30 @@ SUBROUTINE DINTERP1D(v_in, v_out, z_in, z_out, vmsg, nz_in, nz_out) ip = 0 im = 1 IF (z_in(1) .GT. z_in(nz_in)) THEN - ip = 1 - im = 0 + ip = 1 + im = 0 END IF + !$OMP PARALLEL DO PRIVATE(kp, k, interp, height, w1, w2) & + !$OMP FIRSTPRIVATE(ip, im) SCHEDULE(runtime) DO k = 1,nz_out - v_out(k) = vmsg - - interp = .FALSE. - kp = nz_in - height = z_out(k) - - DO WHILE ((.NOT. interp) .AND. (kp .GE. 2)) - IF (((z_in(kp-im) .LE. height) .AND. (z_in(kp-ip) .GT. height))) THEN - w2 = (height - z_in(kp-im))/(z_in(kp-ip) - z_in(kp-im)) - w1 = 1.D0 - w2 - v_out(k) = w1*v_in(kp-im) + w2*v_in(kp-ip) - interp = .TRUE. - END IF - kp = kp - 1 - END DO + v_out(k) = vmsg + + interp = .FALSE. + kp = nz_in + height = z_out(k) + + DO WHILE ((.NOT. interp) .AND. (kp .GE. 2)) + IF (((z_in(kp-im) .LE. height) .AND. (z_in(kp-ip) .GT. height))) THEN + w2 = (height - z_in(kp-im))/(z_in(kp-ip) - z_in(kp-im)) + w1 = 1.D0 - w2 + v_out(k) = w1*v_in(kp-im) + w2*v_in(kp-ip) + interp = .TRUE. + END IF + kp = kp - 1 + END DO END DO + !$OMP END PARALLEL DO RETURN @@ -316,10 +327,12 @@ SUBROUTINE DCOMPUTESEAPRS(nx, ny, nz, z, t, p, q, sea_level_pressure, & INTEGER :: i, j, k INTEGER :: klo, khi + INTEGER :: errcnt, bad_i, bad_j + REAL(KIND=8) :: bad_sfp REAL(KIND=8) :: plo, phi, tlo, thi, zlo, zhi REAL(KIND=8) :: p_at_pconst, t_at_pconst, z_at_pconst - REAL(KIND=8) :: z_half_lowest + !REAL(KIND=8) :: z_half_lowest LOGICAL :: l1, l2, l3, found @@ -329,7 +342,13 @@ SUBROUTINE DCOMPUTESEAPRS(nx, ny, nz, z, t, p, q, sea_level_pressure, & ! heating cycle in the pressure field. errstat = 0 + errcnt = 0 + bad_i = -1 + bad_j = -1 + bad_sfp = -1 + !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i,j,k,found) REDUCTION(+:errcnt) & + !$OMP SCHEDULE(runtime) DO j = 1,ny DO i = 1,nx level(i,j) = -1 @@ -345,22 +364,30 @@ SUBROUTINE DCOMPUTESEAPRS(nx, ny, nz, z, t, p, q, sea_level_pressure, & END DO IF (level(i,j) == -1) THEN - errstat = ALGERR - errmsg = "Error in finding 100 hPa up" - RETURN - - !PRINT '(A,I4,A)','Troubles finding level ', NINT(PCONST)/100,' above ground.' - !PRINT '(A,I4,A,I4,A)','Problems first occur at (',I,',',J,')' - !PRINT '(A,F6.1,A)','Surface pressure = ',p(i,j,1)/100,' hPa.' - !STOP 'Error in finding 100 hPa up' - + errcnt = errcnt + 1 + !$OMP CRITICAL + ! Only do this the first time + IF (bad_i .EQ. -1) THEN + bad_i = i + bad_j = j + bad_sfp = p(i,j,1) / 100. + END IF + !$OMP END CRITICAL END IF END DO END DO + !$OMP END PARALLEL DO + + IF (errcnt > 0) THEN + errstat = ALGERR + WRITE(errmsg,*) "Error in finding 100 hPa up. i=", bad_i, "j=", bad_j, "sfc_p=", bad_sfp + RETURN + END IF ! Get temperature PCONST Pa above surface. Use this to extrapolate ! the temperature at the surface and down to sea level. - + !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i,j,klo,khi) REDUCTION(+:errcnt) & + !$OMP SCHEDULE(runtime) DO j = 1,ny DO i = 1,nx @@ -368,39 +395,43 @@ SUBROUTINE DCOMPUTESEAPRS(nx, ny, nz, z, t, p, q, sea_level_pressure, & khi = MIN(klo+1, nz-1) IF (klo == khi) THEN - errstat = ALGERR - errmsg = "Error trapping levels" - RETURN - - !PRINT '(A)','Trapping levels are weird.' - !PRINT '(A,I3,A,I3,A)','klo = ',klo,', khi = ',khi,': and they should not be equal.' - !STOP 'Error trapping levels' + errcnt = errcnt + 1 + !$OMP CRITICAL + IF (bad_i .EQ. -1) THEN + bad_i = i + bad_j = j + END IF + !$OMP END CRITICAL END IF plo = p(i,j,klo) phi = p(i,j,khi) tlo = t(i,j,klo)*(1.D0 + 0.608D0*q(i,j,klo)) thi = t(i,j,khi)*(1.D0 + 0.608D0*q(i,j,khi)) - ! zlo = zetahalf(klo)/ztop*(ztop-terrain(i,j))+terrain(i,j) - ! zhi = zetahalf(khi)/ztop*(ztop-terrain(i,j))+terrain(i,j) zlo = z(i,j,klo) zhi = z(i,j,khi) - p_at_pconst = p(i,j,1) - PCONST t_at_pconst = thi - (thi-tlo)*LOG(p_at_pconst/phi)*LOG(plo/phi) z_at_pconst = zhi - (zhi-zlo)*LOG(p_at_pconst/phi)*LOG(plo/phi) - + ! t_surf(i,j) = t_at_pconst * (p(i,j,1)/p_at_pconst)**(USSALR*RD/G) t_sea_level(i,j) = t_at_pconst + USSALR*z_at_pconst - END DO END DO + !$OMP END PARALLEL DO + + IF (errcnt > 0) THEN + errstat = ALGERR + WRITE(errmsg,*) "Error trapping levels at i=", bad_i, "j=", bad_j + RETURN + END IF ! If we follow a traditional computation, there is a correction to the ! sea level temperature if both the surface and sea level ! temperatures are *too* hot. IF (ridiculous_mm5_test) THEN + !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(l1,l2,l3) SCHEDULE(runtime) DO j = 1,ny DO i = 1,nx l1 = t_sea_level(i,j) < TC @@ -413,21 +444,23 @@ SUBROUTINE DCOMPUTESEAPRS(nx, ny, nz, z, t, p, q, sea_level_pressure, & END IF END DO END DO + !$OMP END PARALLEL DO END IF ! The grand finale: ta da! + !$OMP PARALLEL DO COLLAPSE(2) SCHEDULE(runtime) DO j = 1,ny DO i = 1,nx - ! z_half_lowest=zetahalf(1)/ztop*(ztop-terrain(i,j))+terrain(i,j) - z_half_lowest = z(i,j,1) + !z_half_lowest = z(i,j,1) ! Convert to hPa in this step, by multiplying by 0.01. The original ! Fortran routine didn't do this, but the NCL script that called it ! did, so we moved it here. - sea_level_pressure(i,j) = 0.01*(p(i,j,1)*EXP((2.D0*G*z_half_lowest)/& + sea_level_pressure(i,j) = 0.01*(p(i,j,1)*EXP((2.D0*G*z(i,j,1))/& (RD*(t_sea_level(i,j) + t_surf(i,j))))) END DO END DO + !$OMP END PARALLEL DO ! PRINT *,'sea pres input at weird location i=20,j=1,k=1' ! PRINT *,'t=',t(20,1,1),t(20,2,1),t(20,3,1) @@ -464,12 +497,15 @@ SUBROUTINE DFILTER2D(a, b, nx, ny, it, missing) INTEGER :: i, j, iter DO iter=1,it + !$OMP PARALLEL DO COLLAPSE(2) SCHEDULE(runtime) DO j=1,ny DO i = 1,nx b(i,j) = a(i,j) END DO END DO + !$OMP END PARALLEL DO + !$OMP PARALLEL DO COLLAPSE(2) SCHEDULE(runtime) DO j=2,ny-1 DO i=1,nx IF (b(i,j-1) .EQ. missing .OR. b(i,j) .EQ. missing .OR. & @@ -480,7 +516,9 @@ SUBROUTINE DFILTER2D(a, b, nx, ny, it, missing) END IF END DO END DO + !$OMP END PARALLEL DO + !$OMP PARALLEL DO COLLAPSE(2) SCHEDULE(runtime) DO j=1,ny DO i=2,nx-1 IF (b(i-1,j) .EQ. missing .OR. b(i,j) .EQ. missing .OR. & @@ -491,6 +529,7 @@ SUBROUTINE DFILTER2D(a, b, nx, ny, it, missing) END IF END DO END DO + !$OMP END PARALLEL DO ! do j=1,ny ! do i=1,nx ! b(i,j) = a(i,j) @@ -533,13 +572,18 @@ SUBROUTINE FILTER2D(a, b, nx, ny, it, missing) INTEGER :: i, j, iter + !$OMP PARALLEL + DO iter=1,it + !$OMP DO COLLAPSE(2) SCHEDULE(runtime) DO j=1,ny DO i = 1,nx b(i,j) = a(i,j) END DO END DO + !$OMP END DO + !$OMP DO COLLAPSE(2) SCHEDULE(runtime) DO j=2,ny-1 DO i=1,nx IF (b(i,j-1) .EQ. missing .OR. b(i,j) .EQ. missing .OR. & @@ -550,7 +594,9 @@ SUBROUTINE FILTER2D(a, b, nx, ny, it, missing) END IF END DO END DO + !$OMP END DO + !$OMP DO COLLAPSE(2) SCHEDULE(runtime) DO j=1,ny DO i=2,nx-1 IF (b(i-1,j) .EQ. missing .OR. b(i,j) .EQ. missing .OR. & @@ -561,6 +607,7 @@ SUBROUTINE FILTER2D(a, b, nx, ny, it, missing) END IF END DO END DO + !$OMP END DO ! do j=1,ny ! do i=1,nx ! b(i,j) = a(i,j) @@ -578,6 +625,8 @@ SUBROUTINE FILTER2D(a, b, nx, ny, it, missing) ! enddo END DO + !$OMP END PARALLEL + RETURN END SUBROUTINE FILTER2D @@ -601,6 +650,7 @@ SUBROUTINE DCOMPUTERH(qv, p, t, rh, nx) INTEGER :: i REAL(KIND=8) :: qvs,es,pressure,temperature + !$OMP PARALLEL DO PRIVATE(qvs, es, pressure, temperature) SCHEDULE(runtime) DO i = 1,nx pressure = p(i) temperature = t(i) @@ -612,6 +662,7 @@ SUBROUTINE DCOMPUTERH(qv, p, t, rh, nx) ! rh(i) = 100.*qv(i)/qvs rh(i) = 100.D0*MAX(MIN(qv(i)/qvs, 1.0D0), 0.0D0) END DO + !$OMP END PARALLEL DO RETURN @@ -707,11 +758,14 @@ SUBROUTINE DCOMPUTEUVMET(u, v, uvmet, longca,longcb,flong,flat, & ! NCLEND INTEGER :: i,j - REAL(KIND=8) :: uk, vk + !REAL(KIND=8) :: uk, vk ! msg stands for missing value in this code ! WRITE (6,FMT=*) ' in compute_uvmet ',NX,NY,NXP1,NYP1,ISTAG + !$OMP PARALLEL + + !$OMP DO COLLAPSE(2) SCHEDULE(runtime) DO j = 1,ny DO i = 1,nx @@ -733,34 +787,81 @@ SUBROUTINE DCOMPUTEUVMET(u, v, uvmet, longca,longcb,flong,flat, & END DO END DO + !$OMP END DO - ! WRITE (6,FMT=*) " computing velocities " - DO j = 1,ny - DO i = 1,nx - IF (istag.EQ.1) THEN - IF (is_msg_val .AND. (u(i,j) .EQ. umsg .OR. v(i,j) .EQ. vmsg & - .OR. u(i+1,j) .EQ. umsg .OR. v(i,j+1) .EQ. vmsg)) THEN - uvmet(i,j,1) = uvmetmsg - uvmet(i,j,2) = uvmetmsg - ELSE - uk = 0.5D0*(u(i,j) + u(i+1,j)) - vk = 0.5D0*(v(i,j) + v(i,j+1)) - uvmet(i,j,1) = vk*longcb(i,j) + uk*longca(i,j) - uvmet(i,j,2) = vk*longca(i,j) - uk*longcb(i,j) - END IF - ELSE - IF (is_msg_val .AND. (u(i,j) .EQ. umsg .OR. v(i,j) .EQ. vmsg)) THEN - uvmet(i,j,1) = uvmetmsg - uvmet(i,j,2) = uvmetmsg - ELSE - uk = u(i,j) - vk = v(i,j) - uvmet(i,j,1) = vk*longcb(i,j) + uk*longca(i,j) - uvmet(i,j,2) = vk*longca(i,j) - uk*longcb(i,j) - END IF - END IF - END DO - END DO + ! Note: Intentionally removed as many IF statements as possible from loops + ! to improve vectorization. + + IF (istag .EQ. 0) THEN ! Not staggered + IF (.NOT. is_msg_val) THEN ! No missing values used + !$OMP DO COLLAPSE(2) SCHEDULE(runtime) + DO j = 1,ny + DO i = 1,nx + uvmet(i,j,1) = v(i,j)*longcb(i,j) + u(i,j)*longca(i,j) + uvmet(i,j,2) = v(i,j)*longca(i,j) - u(i,j)*longcb(i,j) + END DO + END DO + !$OMP END DO + ELSE ! Missing values used + !$OMP DO COLLAPSE(2) SCHEDULE(runtime) + DO j = 1,ny + DO i = 1,nx + IF ((u(i,j) .NE. umsg .AND. v(i,j) .NE. vmsg)) THEN + uvmet(i,j,1) = v(i,j)*longcb(i,j) + u(i,j)*longca(i,j) + uvmet(i,j,2) = v(i,j)*longca(i,j) - u(i,j)*longcb(i,j) + ELSE + uvmet(i,j,1) = uvmetmsg + uvmet(i,j,2) = uvmetmsg + END IF + END DO + END DO + !$OMP END DO + END IF + ELSE ! Staggered + IF (.NOT. is_msg_val) THEN ! No missing values used + !$OMP DO COLLAPSE(2) SCHEDULE(runtime) + DO j = 1,ny + DO i = 1,nx + ! This is the more readable version. + !uk = 0.5D0*(u(i,j) + u(i+1,j)) + !vk = 0.5D0*(v(i,j) + v(i,j+1)) + !uvmet(i,j,1) = vk*longcb(i,j) + uk*longca(i,j) + !uvmet(i,j,2) = vk*longca(i,j) - uk*longcb(i,j) + + uvmet(i,j,1) = (0.5D0*(v(i,j) + v(i,j+1)))*longcb(i,j) + & + (0.5D0*(u(i,j) + u(i+1,j)))*longca(i,j) + uvmet(i,j,2) = (0.5D0*(v(i,j) + v(i,j+1)))*longca(i,j) - & + (0.5D0*(u(i,j) + u(i+1,j)))*longcb(i,j) + + END DO + END DO + !$OMP END DO + ELSE ! Missing values used + !$OMP DO COLLAPSE(2) SCHEDULE(runtime) + DO j = 1,ny + DO i = 1,nx + IF (u(i,j) .NE. umsg .AND. v(i,j) .NE. vmsg .AND. u(i+1,j) .NE. umsg .AND. v(i,j+1) .NE. vmsg) THEN + ! This is the more readable version. + !uk = 0.5D0*(u(i,j) + u(i+1,j)) + !vk = 0.5D0*(v(i,j) + v(i,j+1)) + !uvmet(i,j,1) = vk*longcb(i,j) + uk*longca(i,j) + !uvmet(i,j,2) = vk*longca(i,j) - uk*longcb(i,j) + + uvmet(i,j,1) = (0.5D0*(v(i,j) + v(i,j+1)))*longcb(i,j) + & + (0.5D0*(u(i,j) + u(i+1,j)))*longca(i,j) + uvmet(i,j,2) = (0.5D0*(v(i,j) + v(i,j+1)))*longca(i,j) - & + (0.5D0*(u(i,j) + u(i+1,j)))*longcb(i,j) + ELSE + uvmet(i,j,1) = uvmetmsg + uvmet(i,j,2) = uvmetmsg + END IF + END DO + END DO + !$OMP END DO + END IF + END IF + + !$OMP END PARALLEL RETURN @@ -791,15 +892,17 @@ SUBROUTINE DCOMPUTETD(td, pressure, qv_in, nx) INTEGER :: i + !$OMP PARALLEL DO PRIVATE(i,qv,tdc) SCHEDULE(runtime) DO i = 1,nx - qv = MAX(qv_in(i), 0.D0) - ! vapor pressure - tdc = qv*pressure(i)/(.622D0 + qv) + qv = MAX(qv_in(i), 0.D0) + ! vapor pressure + tdc = qv*pressure(i)/(.622D0 + qv) - ! avoid problems near zero - tdc = MAX(tdc, 0.001D0) - td(i) = (243.5D0*LOG(tdc) - 440.8D0)/(19.48D0 - LOG(tdc)) + ! avoid problems near zero + tdc = MAX(tdc, 0.001D0) + td(i) = (243.5D0*LOG(tdc) - 440.8D0)/(19.48D0 - LOG(tdc)) END DO + !$OMP END PARALLEL DO RETURN @@ -825,11 +928,7 @@ SUBROUTINE DCOMPUTEICLW(iclw, pressure, qc_in, nx, ny, nz) REAL(KIND=8), PARAMETER :: GG = 1000.D0/G INTEGER i,j,k - DO j = 1,ny - DO i = 1,nx - iclw(i,j) = 0.D0 - END DO - END DO + iclw = 0 DO j = 3,ny - 2 DO i = 3,nx - 2 diff --git a/fortran/wrf_user_dbz.f90 b/fortran/wrf_user_dbz.f90 index d1151f0..2e5e17b 100644 --- a/fortran/wrf_user_dbz.f90 +++ b/fortran/wrf_user_dbz.f90 @@ -77,7 +77,10 @@ SUBROUTINE CALCDBZ(prs, tmk, qvp, qra, qsn, qgr, sn0, ivarint, iliqskin, dbz, nx REAL(KIND=8), PARAMETER :: RN0_S = 2.D7 REAL(KIND=8), PARAMETER :: RN0_G = 4.D6 + !$OMP PARALLEL + ! Force all Q arrays to be 0.0 or greater. + !$OMP DO COLLAPSE(3) SCHEDULE(runtime) DO k = 1,nz DO j = 1,ny DO i = 1,nx @@ -96,10 +99,12 @@ SUBROUTINE CALCDBZ(prs, tmk, qvp, qra, qsn, qgr, sn0, ivarint, iliqskin, dbz, nx END DO END DO END DO + !$OMP END DO ! Input pressure is Pa, but we need hPa in calculations IF (sn0 .EQ. 0) THEN + !$OMP DO COLLAPSE(3) SCHEDULE(runtime) DO k = 1,nz DO j = 1,ny DO i = 1,nx @@ -110,12 +115,17 @@ SUBROUTINE CALCDBZ(prs, tmk, qvp, qra, qsn, qgr, sn0, ivarint, iliqskin, dbz, nx END DO END DO END DO + !$OMP END DO END IF factor_r = GAMMA_SEVEN*1.D18*(1.D0/(PI*RHO_R))**1.75D0 factor_s = GAMMA_SEVEN*1.D18*(1.D0/(PI*RHO_S))**1.75D0*(RHO_S/RHOWAT)**2*ALPHA factor_g = GAMMA_SEVEN*1.D18*(1.D0/(PI*RHO_G))**1.75D0*(RHO_G/RHOWAT)**2*ALPHA + + !$OMP DO COLLAPSE(3) PRIVATE(i, j, k, temp_c, virtual_t, gonv, ronv, sonv, & + !$OMP factorb_g, factorb_s, rhoair, z_e) & + !$OMP FIRSTPRIVATE(factor_r, factor_s, factor_g) SCHEDULE(runtime) DO k = 1,nz DO j = 1,ny DO i = 1,nx @@ -171,6 +181,9 @@ SUBROUTINE CALCDBZ(prs, tmk, qvp, qra, qsn, qgr, sn0, ivarint, iliqskin, dbz, nx END DO END DO END DO + !$OMP END DO + + !$OMP END PARALLEL RETURN diff --git a/fortran/wrf_vinterp.f90 b/fortran/wrf_vinterp.f90 index c9bebb0..c34efd7 100644 --- a/fortran/wrf_vinterp.f90 +++ b/fortran/wrf_vinterp.f90 @@ -14,17 +14,19 @@ SUBROUTINE wrf_monotonic(out, in, lvprs, cor, idir, delta, ew, ns, nz, icorsw) REAL(KIND=8), INTENT(IN) :: delta REAL(KIND=8), DIMENSION(ew,ns,nz), INTENT(INOUT) :: in REAL(KIND=8), DIMENSION(ew,ns,nz), INTENT(OUT) :: out - REAL(KIND=8), DIMENSION(ew,ns,nz) :: lvprs - REAL(KIND=8), DIMENSION(ew,ns) :: cor + REAL(KIND=8), DIMENSION(ew,ns,nz), INTENT(IN) :: lvprs + REAL(KIND=8), DIMENSION(ew,ns), INTENT(IN) :: cor -!NCLEND - INTEGER :: i, j, k, ripk, k300 +!NCLEND - k300 = 1 ! removes the warning + INTEGER :: i, j, k, k300 + !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i, j, k, k300) SCHEDULE(runtime) DO j=1,ns DO i=1,ew + k300 = -1 + IF (icorsw .EQ. 1 .AND. cor(i,j) .LT. 0.) THEN DO k=1,nz in(i,j,k) = -in(i,j,k) @@ -33,14 +35,19 @@ SUBROUTINE wrf_monotonic(out, in, lvprs, cor, idir, delta, ew, ns, nz, icorsw) ! First find k index that is at or below (height-wise) ! the 300 hPa level. - DO k = 1,nz - ripk = nz-k+1 + DO k = 1,nz-1 IF (lvprs(i,j,k) .LE. 300.D0) THEN k300 = k EXIT END IF END DO + ! If the search fails for some reason, use the second to last + ! k index + IF (k300 .EQ. -1) THEN + k300 = nz-1 + END IF + DO k = k300,1,-1 IF (idir .EQ. 1) THEN out(i,j,k) = MIN(in(i,j,k), in(i,j,k+1) + delta) @@ -58,6 +65,7 @@ SUBROUTINE wrf_monotonic(out, in, lvprs, cor, idir, delta, ew, ns, nz, icorsw) END DO END DO END DO + !$OMP END PARALLEL DO RETURN @@ -65,7 +73,7 @@ END SUBROUTINE wrf_monotonic !NCLFORTSTART -FUNCTION wrf_intrp_value(wvalp0, wvalp1, vlev, vcp0, vcp1, icase, errstat, errmsg) +FUNCTION wrf_intrp_value(wvalp0, wvalp1, vlev, vcp0, vcp1, icase, errstat) USE wrf_constants, ONLY : ALGERR, SCLHT IMPLICIT NONE @@ -75,7 +83,6 @@ FUNCTION wrf_intrp_value(wvalp0, wvalp1, vlev, vcp0, vcp1, icase, errstat, errms INTEGER, INTENT(IN) :: icase REAL(KIND=8), INTENT(IN) :: wvalp0, wvalp1, vlev, vcp0, vcp1 INTEGER, INTENT(INOUT) :: errstat - CHARACTER(LEN=*), INTENT(INOUT) :: errmsg REAL(KIND=8) :: wrf_intrp_value !NCLEND @@ -83,10 +90,6 @@ FUNCTION wrf_intrp_value(wvalp0, wvalp1, vlev, vcp0, vcp1, icase, errstat, errms REAL(KIND=8) :: valp0, valp1, rvalue REAL(KIND=8) :: chkdiff - !REAL(KIND=8), PARAMETER :: RGAS=287.04d0 - !REAL(KIND=8), PARAMETER :: USSALR=0.0065d0 - !REAL(KIND=8), PARAMETER :: SCLHT=RGAS*256.d0/9.81d0 - errstat = 0 valp0 = wvalp0 @@ -99,7 +102,7 @@ FUNCTION wrf_intrp_value(wvalp0, wvalp1, vlev, vcp0, vcp1, icase, errstat, errms chkdiff = vcp1 - vcp0 IF(chkdiff .EQ. 0) THEN errstat = ALGERR - errmsg = "bad difference in vcp's" + !errmsg = "bad difference in vcp's" wrf_intrp_value = 0 RETURN !PRINT *,"bad difference in vcp's" @@ -152,6 +155,7 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& INTEGER :: nreqlvs, ripk !njx,niy INTEGER :: i, j, k, kupper !itriv INTEGER :: ifound, isign !miy,mjx + INTEGER :: log_errcnt, interp_errcnt, interp_errstat REAL(KIND=8), DIMENSION(ew,ns) :: tempout REAL(KIND=8) :: rlevel, vlev, diff REAL(KIND=8) :: tmpvlev @@ -166,33 +170,15 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& REAL(KIND=8) :: tlcl, gammam CHARACTER(LEN=1) :: cvcord - !REAL(KIND=8), PARAMETER :: RGAS = 287.04d0 !J/K/kg - !REAL(KIND=8), PARAMETER :: RGASMD = .608d0 - !REAL(KIND=8), PARAMETER :: USSALR = .0065d0 ! deg C per m - !REAL(KIND=8), PARAMETER :: SCLHT = RGAS*256.d0/9.81d0 - !REAL(KIND=8), PARAMETER :: EPS = 0.622d0 - !REAL(KIND=8), PARAMETER :: RCONST = -9.81d0/(RGAS * USSALR) - !REAL(KIND=8), PARAMETER :: EXPON = RGAS*USSALR/9.81d0 - !REAL(KIND=8), PARAMETER :: EXPONI = 1./EXPON - !REAL(KIND=8), PARAMETER :: TLCLC1 = 2840.d0 - !REAL(KIND=8), PARAMETER :: TLCLC2 = 3.5d0 - !REAL(KIND=8), PARAMETER :: TLCLC3 = 4.805d0 - !REAL(KIND=8), PARAMETER :: TLCLC4 = 55.d0 - !REAL(KIND=8), PARAMETER :: THTECON1 = 3376.d0 ! K - !REAL(KIND=8), PARAMETER :: THTECON2 = 2.54d0 - !REAL(KIND=8), PARAMETER :: THTECON3 = 0.81d0 - !REAL(KIND=8), PARAMETER :: CP = 1004.d0 - !REAL(KIND=8), PARAMETER :: CPMD = 0.887d0 - !REAL(KIND=8), PARAMETER :: GAMMA = RGAS/CP - !REAL(KIND=8), PARAMETER :: GAMMAMD = RGASMD-CPMD - !REAL(KIND=8), PARAMETER :: CELKEL = 273.16d0 - ! Removes the warnings for uninitialized variables cvcord = '' plev = 0 zlev = 0 vlev = 0 errstat = 0 + interp_errcnt = 0 + interp_errstat = 0 + log_errcnt = 0 IF (vcor .EQ. 1) THEN cvcord = 'p' @@ -202,17 +188,6 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& cvcord = 't' END IF - !miy = ns - !mjx = ew - !njx = ew - !niy = ns - - DO j = 1,ns - DO i = 1,ew - tempout(i,j) = rmsg - END DO - END DO - DO nreqlvs = 1,numlevels IF (cvcord .EQ. 'z') THEN ! Convert rlevel to meters from km @@ -224,8 +199,16 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& vlev = interp_levels(nreqlvs) END IF + !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i, j, k, ifound, & + !$OMP ripk, vcp1, vcp0, valp0, valp1, tmpvlev, interp_errstat, & + !$OMP vclhsl, vctophsl, diff, isign, plhsl, zlhsl, ezlhsl, tlhsl, & + !$OMP zsurf, qvapor, psurf, psurfsm, ezsurf, plev, ezlev, zlev, & + !$OMP ptarget, dpmin, kupper, pbot, zbot, pratio, tbotextrap, & + !$OMP vt, tlev, gammam, e, tlcl) REDUCTION (+:log_errcnt, interp_errcnt) & + !$OMP SCHEDULE(runtime) DO j=1,ns DO i=1,ew + tempout(i,j) = rmsg ! Get the interpolated value that is within the model domain ifound = 0 DO k = 1,nz-1 @@ -245,35 +228,34 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& IF (logp .EQ. 1) THEN vcp1 = LOG(vcp1) vcp0 = LOG(vcp0) - IF (vlev .EQ. 0.0D0) THEN - errstat = ALGERR - WRITE(errmsg, *) "Pres=0. Unable to take log of 0." - RETURN - !PRINT *,"Pressure value = 0" - !PRINT *,"Unable to take log of 0" - !STOP + IF (vlev .NE. 0.0D0) THEN + tmpvlev = LOG(vlev) + ELSE + log_errcnt = log_errcnt + 1 + tmpvlev = rmsg END IF - tmpvlev = LOG(vlev) ELSE tmpvlev = vlev END IF - tempout(i,j) = wrf_intrp_value(valp0, valp1, tmpvlev, vcp0, & - vcp1, icase, errstat, errmsg) - IF (errstat .NE. 0) THEN - RETURN - END IF - ! print *,"one ",i,j,tempout(i,j) - ifound = 1 + IF (tmpvlev .NE. rmsg) THEN + tempout(i,j) = wrf_intrp_value(valp0, valp1, tmpvlev, vcp0, & + vcp1, icase, interp_errstat) + + IF (interp_errstat .NE. 0) THEN + tempout(i,j) = rmsg + interp_errcnt = interp_errcnt + 1 + END IF + + ifound = 1 + END IF END IF - !GOTO 115 ! EXIT + EXIT END IF END DO !end for the k loop - !115 CONTINUE IF (ifound .EQ. 1) THEN !Grid point is in the model domain - !GOTO 333 ! CYCLE CYCLE END IF @@ -281,7 +263,6 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& !all values above or below the model level to rmsg. IF (extrap .EQ. 0) THEN tempout(i,j) = rmsg - !GOTO 333 ! CYCLE CYCLE END IF @@ -296,8 +277,6 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& IF (isign*vlev .GE. isign*vctophsl) THEN ! Assign the highest model level to the out array tempout(i,j) = datain(i,j,nz) - ! print *,"at warn",i,j,tempout(i,j) - !GOTO 333 ! CYCLE CYCLE END IF @@ -307,7 +286,6 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& IF (datain(i,j,1) .EQ. rmsg) THEN tempout(i,j) = rmsg - !GOTO 333 ! CYCLE CYCLE END IF @@ -351,7 +329,6 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& zlev = -SCLHT*LOG(ezlev) IF (icase .EQ. 2) THEN tempout(i,j) = zlev - !GOTO 333 ! CYCLE CYCLE END IF @@ -362,7 +339,6 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& psurf + (ezsurf - ezlev)*plhsl)/(ezsurf - ezlhsl) IF (icase .EQ. 1) THEN tempout(i,j) = plev - !GOTO 333 ! CYCLE CYCLE END IF END IF @@ -374,12 +350,11 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& ripk = nz-k+1 dp = ABS((pres(i,j,ripk) * 0.01D0) - ptarget) IF (dp .GT. dpmin) THEN - !GOTO 334 ! EXIT EXIT END IF dpmin = MIN(dpmin, dp) END DO - !334 + kupper = k-1 ripk = nz - kupper + 1 @@ -394,7 +369,6 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& zlev = zbot + vt/USSALR*(1. - (vlev/pbot)**EXPON) IF (icase .EQ. 2) THEN tempout(i,j) = zlev - !GOTO 333 ! CYCLE CYCLE END IF ELSE IF (cvcord .EQ. 'z') THEN @@ -402,7 +376,6 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& plev = pbot*(1. + USSALR/vt*(zbot - zlev))**EXPONI IF (icase .EQ. 1) THEN tempout(i,j) = plev - !GOTO 333 ! CYCLE CYCLE END IF END IF @@ -434,13 +407,27 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& END DO END DO + !$OMP END PARALLEL DO + + IF (log_errcnt > 0) THEN + errstat = ALGERR + WRITE(errmsg, *) "Pres=0. Unable to take log of 0." + RETURN + END IF + + IF (interp_errcnt > 0) THEN + errstat = ALGERR + WRITE(errmsg, *) "bad difference in vcp's" + RETURN + END IF - ! print *,"----done----",interp_levels(nreqlvs) + !$OMP PARALLEL DO COLLAPSE(2) SCHEDULE(runtime) DO j = 1,ns DO i = 1,ew dataout(i,j,nreqlvs) = tempout(i,j) END DO END DO + !$OMP END PARALLEL DO END DO !end for the nreqlvs diff --git a/fortran/wrf_wind.f90 b/fortran/wrf_wind.f90 index b08c019..1d93d20 100644 --- a/fortran/wrf_wind.f90 +++ b/fortran/wrf_wind.f90 @@ -13,11 +13,13 @@ SUBROUTINE DCOMPUTEWSPD(wspd, u, v, nx, ny) INTEGER i, j + !$OMP PARALLEL DO COLLAPSE(2) SCHEDULE(runtime) DO j = 1,ny DO i = 1,nx wspd(i,j) = SQRT(u(i,j)*u(i,j) + v(i,j)*v(i,j)) END DO END DO + !$OMP END PARALLEL DO END SUBROUTINE DCOMPUTEWSPD @@ -38,11 +40,13 @@ SUBROUTINE DCOMPUTEWDIR(wdir, u, v, nx, ny) INTEGER i, j + !$OMP PARALLEL DO COLLAPSE(2) SCHEDULE(runtime) DO j = 1,ny DO i = 1,nx wdir(i,j) = MOD(270.0 - ATAN2(v(i,j), u(i,j)) * DEG_PER_RAD, 360.) END DO END DO + !$OMP END PARALLEL DO END SUBROUTINE DCOMPUTEWDIR diff --git a/fortran/wrffortran.pyf b/fortran/wrffortran.pyf index bf99a1e..7211ff6 100644 --- a/fortran/wrffortran.pyf +++ b/fortran/wrffortran.pyf @@ -33,6 +33,165 @@ python module _wrffortran ! in 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 @@ -63,36 +222,55 @@ python module _wrffortran ! in integer intent(inout) :: errstat character*(*) intent(inout) :: errmsg end subroutine dlookup_table - subroutine dpfcalc(prs,sfp,pf,miy,mjx,mkzh,ter_follow) ! in :_wrffortran:rip_cape.f90 - real(kind=8) dimension(miy,mjx,mkzh),intent(in) :: prs - real(kind=8) dimension(miy,mjx),intent(in),depend(miy,mjx) :: sfp - real(kind=8) dimension(miy,mjx,mkzh),intent(out),depend(miy,mjx,mkzh) :: pf - integer, optional,intent(in),check(shape(prs,0)==miy),depend(prs) :: miy=shape(prs,0) - integer, optional,intent(in),check(shape(prs,1)==mjx),depend(prs) :: mjx=shape(prs,1) - integer, optional,intent(in),check(shape(prs,2)==mkzh),depend(prs) :: mkzh=shape(prs,2) + 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,cmsg,miy,mjx,mkzh,i3dflag,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,algerr,ezero,thtecon2 - real(kind=8) dimension(miy,mjx,mkzh),intent(in) :: prs - 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) :: qvp - 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) dimension(miy,mjx),intent(in),depend(miy,mjx) :: sfp - real(kind=8) dimension(miy,mjx,mkzh),intent(out,in),depend(miy,mjx,mkzh) :: cape - real(kind=8) dimension(miy,mjx,mkzh),intent(out,in),depend(miy,mjx,mkzh) :: cin + subroutine dcapecalc3d(prs,tmk,qvp,ght,ter,sfp,cape,cin,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) intent(in) :: cmsg - integer, optional,intent(in),check(shape(prs,0)==miy),depend(prs) :: miy=shape(prs,0) - integer, optional,intent(in),check(shape(prs,1)==mjx),depend(prs) :: mjx=shape(prs,1) + 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) :: i3dflag 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,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) 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 @@ -104,8 +282,27 @@ python module _wrffortran ! in 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 @@ -115,6 +312,7 @@ python module _wrffortran ! in 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.96920996839e+36 real(kind=8), parameter,optional :: rd=287.d0 real(kind=8), parameter,optional :: abscoef=.145d0 real(kind=8), parameter,optional :: celkel=273.15d0 @@ -122,16 +320,18 @@ python module _wrffortran ! in 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 :: default_fill=9.96920996839e+36 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 @@ -141,9 +341,10 @@ python module _wrffortran ! in real(kind=8), parameter,optional :: rgasmd=.608d0 real(kind=8), parameter,optional :: g=9.81d0 integer, optional :: errlen=512 - real(kind=8), parameter,optional :: eps=0.622d0 + 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 @@ -508,8 +709,8 @@ python module _wrffortran ! in 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),depend(ew,ns,nz) :: lvprs - real(kind=8) dimension(ew,ns),depend(ew,ns) :: cor + 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) @@ -517,7 +718,7 @@ python module _wrffortran ! in 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,errmsg) ! in :_wrffortran:wrf_vinterp.f90 + 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 @@ -527,7 +728,6 @@ python module _wrffortran ! in real(kind=8) intent(in) :: vcp1 integer intent(in) :: icase integer intent(inout) :: errstat - character*(*) intent(inout) :: errmsg 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,rmsg,errstat,errmsg) ! in :_wrffortran:wrf_vinterp.f90 diff --git a/setup.py b/setup.py index 8f42dad..41ec9e6 100755 --- a/setup.py +++ b/setup.py @@ -22,6 +22,7 @@ ext1 = numpy.distutils.core.Extension( "fortran/wrf_pw.f90", "fortran/wrf_vinterp.f90", "fortran/wrf_wind.f90", + "fortran/omp.f90", "fortran/wrffortran.pyf"] ) diff --git a/src/wrf/api.py b/src/wrf/api.py index c5dec8c..660c9d6 100644 --- a/src/wrf/api.py +++ b/src/wrf/api.py @@ -2,17 +2,36 @@ from .config import (xarray_enabled, disable_xarray, enable_xarray, cartopy_enabled, disable_cartopy, enable_cartopy, basemap_enabled, disable_basemap, enable_basemap, pyngl_enabled, enable_pyngl, disable_pyngl, - set_cache_size, get_cache_size) -from .constants import ALL_TIMES, Constants, ConversionFactors, ProjectionTypes + set_cache_size, get_cache_size, omp_enabled) +from .constants import (ALL_TIMES, Constants, ConversionFactors, + ProjectionTypes, default_fill, + OMP_SCHED_STATIC, OMP_SCHED_DYNAMIC, + OMP_SCHED_GUIDED, OMP_SCHED_AUTO) from .destag import destagger from .routines import getvar from .computation import (xy, interp1d, interp2dxy, interpz3d, slp, tk, td, rh, uvmet, smooth2d, cape_2d, cape_3d, cloudfrac, ctt, dbz, srhel, udhel, avo, pvo, eth, wetbulb, tvirtual, omega, pw) -from .extension import DiagnosticError +from .extension import (DiagnosticError, omp_set_num_threads, + omp_get_num_threads, + omp_get_max_threads, omp_get_thread_num, + omp_get_num_procs, omp_in_parallel, + omp_set_dynamic, omp_get_dynamic, omp_set_nested, + omp_get_nested, omp_set_schedule, + omp_get_schedule, omp_get_thread_limit, + omp_set_max_active_levels, + omp_get_max_active_levels, omp_get_level, + omp_get_ancestor_thread_num, omp_get_team_size, + omp_get_active_level, omp_in_final, + omp_init_lock, omp_init_nest_lock, + omp_destroy_lock, omp_destroy_nest_lock, + omp_set_lock, omp_set_nest_lock, + omp_unset_lock, omp_unset_nest_lock, + omp_test_lock, omp_test_nest_lock, + omp_get_wtime, omp_get_wtick) from .interp import (interplevel, vertcross, interpline, vinterp) -from .latlon import (xy_to_ll, ll_to_xy, xy_to_ll_proj, ll_to_xy_proj) +from .g_latlon import (xy_to_ll, ll_to_xy, xy_to_ll_proj, ll_to_xy_proj) from .py3compat import (viewitems, viewkeys, viewvalues, isstr, py2round, py3range, ucode) from .util import (to_np, extract_global_attrs, is_standard_wrf_var, @@ -41,15 +60,33 @@ __all__ += ["xarray_enabled", "disable_xarray", "enable_xarray", "cartopy_enabled", "disable_cartopy", "enable_cartopy", "basemap_enabled", "disable_basemap", "enable_basemap", "pyngl_enabled", "enable_pyngl", "disable_pyngl", - "set_cache_size", "get_cache_size"] -__all__ += ["ALL_TIMES", "Constants", "ConversionFactors", "ProjectionTypes"] + "set_cache_size", "get_cache_size", "omp_enabled"] +__all__ += ["ALL_TIMES", "Constants", "ConversionFactors", "ProjectionTypes", + "default_fill", "OMP_SCHED_STATIC", "OMP_SCHED_DYNAMIC", + "OMP_SCHED_GUIDED", "OMP_SCHED_AUTO"] __all__ += ["destagger"] __all__ += ["getvar"] __all__ += ["xy", "interp1d", "interp2dxy", "interpz3d", "slp", "tk", "td", "rh", "uvmet", "smooth2d", "cape_2d", "cape_3d", "cloudfrac", "ctt", "dbz", "srhel", "udhel", "avo", "pvo", "eth", "wetbulb", "tvirtual", "omega", "pw"] -__all__ += ["DiagnosticError"] +__all__ += ["DiagnosticError", "omp_set_num_threads", + "omp_get_num_threads", + "omp_get_max_threads", "omp_get_thread_num", + "omp_get_num_procs", "omp_in_parallel", + "omp_set_dynamic", "omp_get_dynamic", "omp_set_nested", + "omp_get_nested", "omp_set_schedule", + "omp_get_schedule", "omp_get_thread_limit", + "omp_set_max_active_levels", + "omp_get_max_active_levels", "omp_get_level", + "omp_get_ancestor_thread_num", "omp_get_team_size", + "omp_get_active_level", "omp_in_final", + "omp_init_lock", "omp_init_nest_lock", + "omp_destroy_lock", "omp_destroy_nest_lock", + "omp_set_lock", "omp_set_nest_lock", + "omp_unset_lock", "omp_unset_nest_lock", + "omp_test_lock", "omp_test_nest_lock", + "omp_get_wtime", "omp_get_wtick"] __all__ += ["interplevel", "vertcross", "interpline", "vinterp"] __all__ += ["xy_to_ll", "ll_to_xy", "xy_to_ll_proj", "ll_to_xy_proj"] __all__ += ["viewitems", "viewkeys", "viewvalues", "isstr", "py2round", diff --git a/src/wrf/cloudfrac.py b/src/wrf/cloudfrac.py deleted file mode 100644 index 75b500f..0000000 --- a/src/wrf/cloudfrac.py +++ /dev/null @@ -1,88 +0,0 @@ -from __future__ import (absolute_import, division, print_function, - unicode_literals) - -from .constants import Constants -from .extension import _tk, _rh, _cloudfrac -from .metadecorators import set_cloudfrac_metadata -from .util import extract_vars - -@set_cloudfrac_metadata() -def get_cloudfrac(wrfin, timeidx=0, method="cat", squeeze=True, - cache=None, meta=True, _key=None): - """Return the cloud fraction. - - The leftmost dimension of the returned array represents three different - quantities: - - - return_val[0,...] will contain LOW level cloud fraction - - return_val[1,...] will contain MID level cloud fraction - - return_val[2,...] will contain HIGH level cloud fraction - - 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. - - Returns: - :class:`xarray.DataArray` or :class:`numpy.ndarray`: The - cloud fraction array whose leftmost dimension is 3 (LOW=0, MID=1, - HIGH=2). - 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. - - """ - - ncvars = extract_vars(wrfin, timeidx, ("P", "PB", "QVAPOR", "T"), - method, squeeze, cache, meta=False, - _key=_key) - - p = ncvars["P"] - pb = ncvars["PB"] - qv = ncvars["QVAPOR"] - t = ncvars["T"] - - full_p = p + pb - full_t = t + Constants.T_BASE - - tk = _tk(full_p, full_t) - rh = _rh(qv, full_p, tk) - - return _cloudfrac(full_p, rh) diff --git a/src/wrf/computation.py b/src/wrf/computation.py index 81e37dd..3973709 100644 --- a/src/wrf/computation.py +++ b/src/wrf/computation.py @@ -4,7 +4,7 @@ from __future__ import (absolute_import, division, print_function, import numpy as np import numpy.ma as ma -from .constants import Constants +from .constants import default_fill from .extension import (_interpz3d, _interp2dxy, _interp1d, _slp, _tk, _td, _rh, _uvmet, _smooth2d, _cape, _cloudfrac, _ctt, _dbz, _srhel, _udhel, _avo, _pvo, _eth, _wetbulb, _tv, @@ -104,7 +104,7 @@ def xy(field, pivot_point=None, angle=None, start_point=None, end_point=None, @set_interp_metadata("1d") -def interp1d(field, z_in, z_out, missing=Constants.DEFAULT_FILL, +def interp1d(field, z_in, z_out, missing=default_fill(np.float64), meta=True): """Return the linear interpolation of a one-dimensional variable. @@ -128,7 +128,7 @@ def interp1d(field, z_in, z_out, missing=Constants.DEFAULT_FILL, to. Must be the same type as *z_in*. missing (:obj:`float`, optional): The fill value to use for the - output. Default is :data:`wrf.Constants.DEFAULT_FILL`. + output. Default is :data:`wrf.default_fill(np.float64)`. meta (:obj:`bool`, optional): Set to False to disable metadata and return :class:`numpy.ndarray` instead of @@ -251,7 +251,7 @@ def interp2dxy(field3d, xy, meta=True): @set_interp_metadata("horiz") -def interpz3d(field3d, vert, desiredlev, missing=Constants.DEFAULT_FILL, +def interpz3d(field3d, vert, desiredlev, missing=default_fill(np.float64), meta=True): """Return the field interpolated to a specified pressure or height level. @@ -280,7 +280,7 @@ def interpz3d(field3d, vert, desiredlev, missing=Constants.DEFAULT_FILL, 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.Constants.DEFAULT_FILL`. + Default is :data:`wrf.default_fill(numpy.float64)`. meta (:obj:`bool`): Set to False to disable metadata and return :class:`numpy.ndarray` instead of @@ -713,7 +713,7 @@ def smooth2d(field, passes, meta=True): @set_cape_alg_metadata(is2d=True, copyarg="pres_hpa") def cape_2d(pres_hpa, tkel, qv, height, terrain, psfc_hpa, ter_follow, - missing=Constants.DEFAULT_FILL, meta=True): + missing=default_fill(np.float64), meta=True): """Return the two-dimensional CAPE, CIN, LCL, and LFC. This function calculates the maximum convective available potential @@ -790,7 +790,7 @@ def cape_2d(pres_hpa, tkel, qv, height, terrain, psfc_hpa, ter_follow, False for pressure level data. missing (:obj:`float`, optional): The fill value to use for the - output. Default is :data:`wrf.Constants.DEFAULT_FILL`. + output. Default is :data:`wrf.default_fill(numpy.float64)`. meta (:obj:`bool`): Set to False to disable metadata and return :class:`numpy.ndarray` instead of @@ -843,7 +843,7 @@ def cape_2d(pres_hpa, tkel, qv, height, terrain, psfc_hpa, ter_follow, @set_cape_alg_metadata(is2d=False, copyarg="pres_hpa") def cape_3d(pres_hpa, tkel, qv, height, terrain, psfc_hpa, ter_follow, - missing=Constants.DEFAULT_FILL, meta=True): + missing=default_fill(np.float64), meta=True): """Return the three-dimensional CAPE and CIN. This function calculates the maximum convective available potential @@ -926,7 +926,7 @@ def cape_3d(pres_hpa, tkel, qv, height, terrain, psfc_hpa, ter_follow, False for pressure level data. missing (:obj:`float`, optional): The fill value to use for the - output. Default is :data:`wrf.Constants.DEFAULT_FILL`. + output. Default is :data:`wrf.default_fill(numpy.float64)`. meta (:obj:`bool`): Set to False to disable metadata and return :class:`numpy.ndarray` instead of @@ -962,8 +962,9 @@ def cape_3d(pres_hpa, tkel, qv, height, terrain, psfc_hpa, ter_follow, return ma.masked_values(cape_cin, missing) -@set_cloudfrac_alg_metadata(copyarg="pres") -def cloudfrac(pres, relh, meta=True): +@set_cloudfrac_alg_metadata(copyarg="vert") +def cloudfrac(vert, relh, vert_inc_w_height, low_thresh, mid_thresh, + high_thresh, missing=default_fill(np.float64), meta=True): """Return the cloud fraction. The leftmost dimension of the returned array represents three different @@ -973,14 +974,22 @@ def cloudfrac(pres, relh, meta=True): - return_val[1,...] will contain MID level cloud fraction - return_val[2,...] will contain HIGH level cloud fraction + The *low_thresh*, *mid_thresh*, and *high_threshold* paramters specify the + low, mid, and high cloud levels in the same units as *vert*. + + In mountainous regions, there is a possibility + that the lowest WRF level will be higher than the low_cloud or mid_cloud + threshold. When this happens, a fill value will be used in the output at + that location. + This is the raw computational algorithm and does not extract any variables from WRF output files. Use :meth:`wrf.getvar` to both extract and compute diagnostic variables. Args: - pres (:class:`xarray.DataArray` or :class:`numpy.ndarray`): Full - pressure (perturbation + base state pressure) in [Pa], with the + vert (:class:`xarray.DataArray` or :class:`numpy.ndarray`): The + vertical coordinate variable (usually pressure or height) with the rightmost dimensions as bottom_top x south_north x west_east Note: @@ -990,8 +999,21 @@ def cloudfrac(pres, relh, meta=True): dimension names to the output. Otherwise, default names will be used. - relh (:class:`xarray.DataArray` or :class:`numpy.ndarray`) Relative - humidity with the same dimensionality as *pres* + relh (:class:`xarray.DataArray` or :class:`numpy.ndarray`): Relative + humidity with the same dimensionality as *vert* + + vert_inc_w_height (:obj:`int`): Set to 1 if the vertical coordinate + values increase with height (height values). Set to 0 if the + vertical coordinate values decrease with height (pressure values). + + low_thresh (:obj:`float`): The bottom vertical threshold for what is + considered a low cloud. + + mid_thresh (:obj:`float`): The bottom vertical threshold for what is + considered a mid level cloud. + + high_thresh (:obj:`float`): The bottom vertical threshold for what is + considered a high cloud. meta (:obj:`bool`): Set to False to disable metadata and return :class:`numpy.ndarray` instead of @@ -1016,7 +1038,10 @@ def cloudfrac(pres, relh, meta=True): :meth:`wrf.getvar`, :meth:`wrf.rh` """ - return _cloudfrac(pres, relh) + cfrac = _cloudfrac(vert, relh, vert_inc_w_height, low_thresh, mid_thresh, + high_thresh, missing) + + return ma.masked_values(cfrac, missing) @set_alg_metadata(2, "pres_hpa", refvarndims=3, diff --git a/src/wrf/config.py b/src/wrf/config.py index c7f28b2..d22d5de 100644 --- a/src/wrf/config.py +++ b/src/wrf/config.py @@ -2,35 +2,68 @@ from __future__ import (absolute_import, division, print_function, unicode_literals) from threading import local +import wrapt + +from ._wrffortran import (fomp_enabled, fomp_set_num_threads, + fomp_set_schedule, fomp_set_dynamic, + omp_constants) _local_config = local() -_local_config.xarray_enabled = True -_local_config.cartopy_enabled = True -_local_config.basemap_enabled = True -_local_config.pyngl_enabled = True -_local_config.cache_size = 20 - -try: - from xarray import DataArray -except ImportError: - _local_config.xarray_enabled = False - -try: - from cartopy import crs -except ImportError: - _local_config.cartopy_enabled = False + +def _init_local(): + global _local_config -try: - from mpl_toolkits.basemap import Basemap -except ImportError: - _local_config.basemap_enabled = False + _local_config.xarray_enabled = True + _local_config.cartopy_enabled = True + _local_config.basemap_enabled = True + _local_config.pyngl_enabled = True + _local_config.cache_size = 20 + _local_config.initialized = True -try: - from Ngl import Resources -except ImportError: - _local_config.pyngl_enabled = False + try: + from xarray import DataArray + except ImportError: + _local_config.xarray_enabled = False + + try: + from cartopy import crs + except ImportError: + _local_config.cartopy_enabled = False + + try: + from mpl_toolkits.basemap import Basemap + except ImportError: + _local_config.basemap_enabled = False + + try: + from Ngl import Resources + except ImportError: + _local_config.pyngl_enabled = False + + +# Initialize the main thread's configuration +_init_local() + + +def init_local(): + """A decorator that initializes thread local data if necessary.""" + @wrapt.decorator + def func_wrapper(wrapped, instance, args, kwargs): + global _local_config + try: + init = _local_config.init + except AttributeError: + _init_local() + else: + if not init: + _init_local() + + return wrapped(*args, **kwargs) + + return func_wrapper +@init_local() def xarray_enabled(): """Return True if xarray is installed and enabled. @@ -43,18 +76,21 @@ def xarray_enabled(): return _local_config.xarray_enabled +@init_local() def disable_xarray(): """Disable xarray.""" global _local_config _local_config.xarray_enabled = False - + +@init_local() def enable_xarray(): """Enable xarray.""" global _local_config _local_config.xarray_enabled = True - + +@init_local() def cartopy_enabled(): """Return True if cartopy is installed and enabled. @@ -67,18 +103,21 @@ def cartopy_enabled(): return _local_config.cartopy_enabled +@init_local() def enable_cartopy(): """Enable cartopy.""" global _local_config _local_config.cartopy_enabled = True - + +@init_local() def disable_cartopy(): """Disable cartopy.""" global _local_config _local_config.cartopy_enabled = True - + +@init_local() def basemap_enabled(): """Return True if basemap is installed and enabled. @@ -91,17 +130,21 @@ def basemap_enabled(): return _local_config.basemap_enabled +@init_local() def enable_basemap(): """Enable basemap.""" global _local_config _local_config.basemap_enabled = True - + +@init_local() def disable_basemap(): """Disable basemap.""" global _local_config _local_config.basemap_enabled = True - + + +@init_local() def pyngl_enabled(): """Return True if pyngl is installed and enabled. @@ -114,18 +157,21 @@ def pyngl_enabled(): return _local_config.pyngl_enabled +@init_local() def enable_pyngl(): """Enable pyngl.""" global _local_config _local_config.pyngl_enabled = True - + +@init_local() def disable_pyngl(): """Disable pyngl.""" global _local_config _local_config.pyngl_enabled = True - + +@init_local() def set_cache_size(size): """Set the maximum number of items that the threadlocal cache can retain. @@ -143,7 +189,8 @@ def set_cache_size(size): global _local_config _local_config.cache_size = size - + +@init_local() def get_cache_size(): """Return the maximum number of items that the threadlocal cache can retain. @@ -155,5 +202,24 @@ def get_cache_size(): global _local_config return int(_local_config.cache_size) + +def omp_enabled(): + """Return True if OpenMP is enabled. + + OpenMP is only enabled if compiled with OpenMP features. + + Returns: + + :obj:`bool`: True if OpenMP is enabled, otherwise False. + + """ + + return True if fomp_enabled() else False +# Set OpenMP to use 1 thread, static scheduler, and no dynamic +# Note: Using the raw extension functions here to prevent possible +# circular import problems in the future. +fomp_set_num_threads(1) +fomp_set_schedule(omp_constants.fomp_sched_static, 0) +fomp_set_dynamic(False) diff --git a/src/wrf/constants.py b/src/wrf/constants.py index c8270db..6a6dc90 100755 --- a/src/wrf/constants.py +++ b/src/wrf/constants.py @@ -1,10 +1,12 @@ from __future__ import (absolute_import, division, print_function, unicode_literals) +from sys import version_info +import struct import numpy as np from .py3compat import viewitems -from wrf._wrffortran import wrf_constants +from ._wrffortran import wrf_constants, omp_constants #: Indicates that all times should be used in a diagnostic routine. ALL_TIMES = None @@ -15,6 +17,12 @@ class Constants(object): for key,val in viewitems(wrf_constants.__dict__): setattr(Constants, key.upper(), np.asscalar(val)) +OMP_SCHED_STATIC = omp_constants.fomp_sched_static +OMP_SCHED_DYNAMIC = omp_constants.fomp_sched_dynamic +OMP_SCHED_GUIDED = omp_constants.fomp_sched_guided +OMP_SCHED_AUTO = omp_constants.fomp_sched_auto + + class ConversionFactors(object): PA_TO_HPA = .01 PA_TO_TORR = 760.0/101325.0 @@ -29,11 +37,49 @@ class ConversionFactors(object): M_TO_FT = 3.28084 M_TO_MILES = .000621371 + class ProjectionTypes(object): ZERO = 0 LAMBERT_CONFORMAL = 1 POLAR_STEREOGRAPHIC = 2 MERCATOR = 3 LAT_LON = 6 + +# Create the default fill mapping based on type. +_DEFAULT_FILL_MAP = {None: Constants.DEFAULT_FILL, + np.dtype(np.bool_) : False, + np.dtype(np.intc) : Constants.DEFAULT_FILL_INT32, # Usually true + np.dtype(np.int8) : Constants.DEFAULT_FILL_INT8, + np.dtype(np.uint8) : 255, + np.dtype(np.int16) : Constants.DEFAULT_FILL_INT16, + np.dtype(np.uint16) : 65535, + np.dtype(np.int32) : Constants.DEFAULT_FILL_INT32, + np.dtype(np.uint32) : 4294967295, + np.dtype(np.int64) : Constants.DEFAULT_FILL_INT64, + np.dtype(np.uint64) : 18446744073709551614, + np.dtype(np.float_) : Constants.DEFAULT_FILL_DOUBLE, + np.dtype(np.float32) : Constants.DEFAULT_FILL_FLOAT, + np.dtype(np.float64) : Constants.DEFAULT_FILL_DOUBLE + } + +if version_info >= (3, ): + _DEFAULT_FILL_MAP[np.int_] = Constants.DEFAULT_FILL_INT64 +else: + _DEFAULT_FILL_MAP[np.int_] = Constants.DEFAULT_FILL_INT32 + +if (struct.calcsize("P") == 8): + _DEFAULT_FILL_MAP[np.intp] = Constants.DEFAULT_FILL_INT64 +else: + _DEFAULT_FILL_MAP[np.intp] = Constants.DEFAULT_FILL_INT32 + + +# Add the integers based on python 2.x or 3.x +def default_fill(dtype=None): + dt = np.dtype(dtype) if dtype is not None else None + return _DEFAULT_FILL_MAP.get(dt, 0) + + + + \ No newline at end of file diff --git a/src/wrf/decorators.py b/src/wrf/decorators.py index 406dcec..7b1797f 100644 --- a/src/wrf/decorators.py +++ b/src/wrf/decorators.py @@ -11,7 +11,7 @@ from .units import do_conversion, check_units, dealias_and_clean_unit from .util import iter_left_indexes, from_args, to_np, combine_dims from .py3compat import viewitems, viewvalues, isstr from .config import xarray_enabled -from .constants import Constants +from .constants import default_fill if xarray_enabled(): from xarray import DataArray @@ -201,7 +201,7 @@ def left_iteration(ref_var_expected_dims, if all_masked: for output in viewvalues(outd): output[left_and_slice_idxs] = ( - Constants.DEFAULT_FILL) + default_fill(np.float64)) skip_missing = True mask_output = True break @@ -240,9 +240,9 @@ def left_iteration(ref_var_expected_dims, # Mostly when used with join if mask_output: if isinstance(output, np.ndarray): - output = ma.masked_values(output, Constants.DEFAULT_FILL) + output = ma.masked_values(output, default_fill(np.float64)) else: - output = tuple(ma.masked_values(arr, Constants.DEFAULT_FILL) + output = tuple(ma.masked_values(arr, default_fill(np.float64)) for arr in output) return output diff --git a/src/wrf/extension.py b/src/wrf/extension.py index 5f3fcea..3862c85 100755 --- a/src/wrf/extension.py +++ b/src/wrf/extension.py @@ -3,16 +3,32 @@ from __future__ import (absolute_import, division, print_function, import numpy as np -from .constants import Constants +from .constants import Constants, default_fill -from ._wrffortran import (dcomputetk, dinterp3dz, dinterp2dxy, dinterp1d, +from wrf._wrffortran import (dcomputetk, dinterp3dz, dinterp2dxy, dinterp1d, dcomputeseaprs, dfilter2d, dcomputerh, dcomputeuvmet, - dcomputetd, dcapecalc3d, dcloudfrac, wrfcttcalc, - calcdbz, dcalrelhl, dcalcuh, dcomputepv, + dcomputetd, dcapecalc2d, dcapecalc3d, dcloudfrac2, + wrfcttcalc, calcdbz, dcalrelhl, dcalcuh, dcomputepv, dcomputeabsvort, dlltoij, dijtoll, deqthecalc, omgcalc, virtual_temp, wetbulbcalc, dcomputepw, wrf_monotonic, wrf_vintrp, dcomputewspd, - dcomputewdir) + dcomputewdir, + fomp_set_num_threads, fomp_get_num_threads, + fomp_get_max_threads, fomp_get_thread_num, + fomp_get_num_procs, fomp_in_parallel, + fomp_set_dynamic, fomp_get_dynamic, fomp_set_nested, + fomp_get_nested, fomp_set_schedule, + fomp_get_schedule, fomp_get_thread_limit, + fomp_set_max_active_levels, + fomp_get_max_active_levels, fomp_get_level, + fomp_get_ancestor_thread_num, fomp_get_team_size, + fomp_get_active_level, fomp_in_final, + fomp_init_lock, fomp_init_nest_lock, + fomp_destroy_lock, fomp_destroy_nest_lock, + fomp_set_lock, fomp_set_nest_lock, + fomp_unset_lock, fomp_unset_nest_lock, + fomp_test_lock, fomp_test_nest_lock, + fomp_get_wtime, fomp_get_wtick) from .decorators import (left_iteration, cast_type, extract_and_transpose, check_args) @@ -24,7 +40,7 @@ from .specialdec import (uvmet_left_iter, cape_left_iter, class DiagnosticError(Exception): """Raised when an error occurs in a diagnostic routine.""" def __init__(self, message=None): - """Initialize a :class:`wrf.DiagnosticError` objection. + """Initialize a :class:`wrf.DiagnosticError` object. Args: @@ -365,8 +381,8 @@ def _eth(qv, tk, p, outview=None): @cast_type(arg_idxs=(0,1,2,3)) @extract_and_transpose() def _uvmet(u, v, lat, lon, cen_long, cone, isstag=0, has_missing=False, - umissing=Constants.DEFAULT_FILL, vmissing=Constants.DEFAULT_FILL, - uvmetmissing=Constants.DEFAULT_FILL, outview=None): + umissing=default_fill(np.float64), vmissing=default_fill(np.float64), + uvmetmissing=default_fill(np.float64), outview=None): """Wrapper for dcomputeuvmet. Located in wrf_user.f90. @@ -597,8 +613,13 @@ def _cape(p_hpa, tk, qv, ht, ter, sfp, missing, i3dflag, ter_follow, errstat = np.array(0) errmsg = np.zeros(Constants.ERRLEN, "c") + if i3dflag: + cape_routine = dcapecalc3d + else: + cape_routine = dcapecalc2d + # note that p_hpa, tk, qv, and ht have the vertical flipped - result = dcapecalc3d(p_hpa, + result = cape_routine(p_hpa, tk, qv, ht, @@ -607,7 +628,6 @@ def _cape(p_hpa, tk, qv, ht, ter, sfp, missing, i3dflag, ter_follow, capeview, cinview, missing, - i3dflag, ter_follow, psafile, errstat, @@ -620,28 +640,34 @@ def _cape(p_hpa, tk, qv, ht, ter, sfp, missing, i3dflag, ter_follow, @check_args(0, 3, (3,3)) @cloudfrac_left_iter() -@cast_type(arg_idxs=(0, 1), outviews=("lowview", "medview", "highview")) -@extract_and_transpose(outviews=("lowview", "medview", "hightview")) -def _cloudfrac(p, rh, lowview=None, medview=None, highview=None): - """Wrapper for dcloudfrace. +@cast_type(arg_idxs=(0, 1), outviews=("lowview", "midview", "highview")) +@extract_and_transpose(outviews=("lowview", "midview", "highview")) +def _cloudfrac(vert, rh, vert_inc_w_height, low_thresh, mid_thresh, + high_thresh, missing, lowview=None, midview=None, highview=None): + """Wrapper for dcloudfrac2. Located in wrf_cloud_fracf.f90. """ if lowview is None: - lowview = np.zeros(p.shape[0:2], p.dtype, order="F") + lowview = np.zeros(vert.shape[0:2], vert.dtype, order="F") - if medview is None: - medview = np.zeros(p.shape[0:2], p.dtype, order="F") + if midview is None: + midview = np.zeros(vert.shape[0:2], vert.dtype, order="F") if highview is None: - highview = np.zeros(p.shape[0:2], p.dtype, order="F") - - result = dcloudfrac(p, - rh, - lowview, - medview, - highview) + highview = np.zeros(vert.shape[0:2], vert.dtype, order="F") + + result = dcloudfrac2(vert, + rh, + vert_inc_w_height, + low_thresh, + mid_thresh, + high_thresh, + missing, + lowview, + midview, + highview) return result @@ -769,7 +795,7 @@ def _smooth2d(field, passes, outview=None): if isinstance(field, np.ma.MaskedArray): missing = field.fill_value else: - missing = Constants.DEFAULT_FILL + missing = default_fill(np.float64) if outview is None: outview = field.copy(order="A") @@ -898,5 +924,686 @@ def _wdir(u, v, outview=None): v) return result + + +# OpenMP runtime wrappers + +def omp_set_num_threads(num_threads): + """Specify the number of threads to use. + + The omp_set_num_threads routine affects the number of threads to be used + for subsequent parallel regions that do not specify a num_threads + clause, by setting the value of the first element of the nthreads-var + ICV of the current task. + + Args: + + num_threads (a positive :obj:`int`): The number of threads. Must be + positive. + + Returns: + + None. + + """ + if num_threads < 0: + raise ValueError("'num_threads' must be a positive integer.") + + fomp_set_num_threads(num_threads) + + +def omp_get_num_threads(): + """Return the number of threads in the current team. + + The omp_get_num_threads routine returns the number of threads in the + team executing the parallel region to which the routine region binds. + If called from the sequential part of a program, this routine returns 1. + + Note: + + This function always returns 1 when called from within Python. + + Returns: + + :obj:`int`: The number of threads in the current team. + + See Also: + + :meth:`wrf.omp_get_max_threads`, :meth:`wrf.omp_set_num_threads` + + """ + return fomp_get_num_threads() + + +def omp_get_max_threads(): + """Return the maximum number of threads that can be used in a parallel \ + region. + + The omp_get_max_threads routine returns an upper bound on the number of + threads that could be used to form a new team if a parallel construct + without a num_threads clause were encountered after execution returns from + this routine. + + Returns: + + :obj:`int`: The number of threads in the current team. + + See Also: + + :meth:`wrf.omp_set_num_threads` + + """ + return fomp_get_max_threads() + + +def omp_get_thread_num(): + """Return the thread number, within the current team, of the \ + calling thread. + + The omp_get_thread_num routine returns the thread number of the calling + thread, within the team executing the parallel region to which the routine + region binds. The thread number is an integer between 0 and one less than + the value returned by omp_get_num_threads, inclusive. The thread number of + the master thread of the team is 0. The routine returns 0 if it is called + from the sequential part of a program. + + Note: + + This function always returns 0 when called from within Python. + + Returns: + + :obj:`int`: The thread number. + + See Also: + + :meth:`wrf.omp_get_num_procs` + + """ + return fomp_get_thread_num() + + +def omp_get_num_procs(): + """Return the number of processors on the device. + + The omp_get_num_procs routine returns the number of processors that are + available to the device at the time the routine is called. This value may + change between the time that it is determined by the omp_get_num_procs + routine and the time that it is read in the calling context due to system + actions outside the control of the OpenMP implementation. + + Returns: + + :obj:`int`: The number of processors. + + """ + return fomp_get_num_procs() + + +def omp_in_parallel(): + """Return 1 if the active-levels-var ICV is greater than zero; \ + otherwise, return 0. + + The effect of the omp_in_parallel routine is to return 1 if the current + task is enclosed by an active parallel region, and the parallel region is + enclosed by the outermost initial task region on the device; otherwise it + returns 0. + + Note: + + This function always returns 0 when called from within Python. + + Returns: + + :obj:`int`: Returns 1 if the active-levels-var ICV is greater than + zero. Otherwise, it returns 0. + + """ + return fomp_in_parallel() + + +def omp_set_dynamic(dynamic_threads): + """Enable or disable dynamic adjustment of the number of threads \ + available for the execution of subsequent parallel regions by setting the \ + value of the dyn-var ICV. + + For implementations that support dynamic adjustment of the number of + threads, if the argument to omp_set_dynamic evaluates to True, dynamic + adjustment is enabled for the current task; otherwise, dynamic adjustment + is disabled for the current task. For implementations that do not support + dynamic adjustment of the number of threads this routine has no effect: + the value of dyn-var remains false. + + Args: + + dynamic_threads (:obj:`bool`): Set to True to support the dynamic + adjustment of the number of threads. Otherwise, set to False. + + Returns: + + None. + + See Also: + + :meth:`wrf.omp_get_dynamic` + + """ + fomp_set_dynamic(dynamic_threads) + + +def omp_get_dynamic(): + """Return the value of the dyn-var ICV, which determines whether \ + dynamic adjustment of the number of threads is enabled or disabled. + + This routine returns 1 if dynamic adjustment of the number of threads + is enabled for the current task; it returns 0, otherwise. If an + implementation does not support dynamic adjustment of the + number of threads, then this routine always returns 0. + + Returns: + + :obj:`int`: Returns 1 if dynamic thread adjustment is enabled, 0 + if disabled. + + See Also: + + :meth:`wrf.omp_set_dynamic` + + """ + return fomp_get_dynamic() + + +def omp_set_nested(nested): + """Enable or disable nested parallelism, by setting the nest-var ICV + + For implementations that support nested parallelism, if the argument to + omp_set_nested evaluates to True, nested parallelism is enabled for the + current task; otherwise, nested parallelism is disabled for the current + task. For implementations that do not support nested parallelism, this + routine has no effect: the value of nest-var remains False. + + Args: + + dynamic_threads (:obj:`bool`): Set to True to support nested + parallelism, otherwise False. + + Returns: + + None + + See Also: + + :meth:`wrf.omp_get_nested` + + """ + fomp_set_nested(nested) + + +def omp_get_nested(): + """Return the value of the nest-var ICV, which determines if nested \ + parallelism is enabled or disabled + + This routine returns 1 if nested parallelism is enabled for the current + task; it returns 0, otherwise. If an implementation does not support + nested parallelism, this routine always returns 0. + + Returns: + + :obj:`int`: Returns 1 if nested parallelism is enabled, otherwise 0. + + See Also: + + :meth:`wrf.omp_set_nested` + + """ + return fomp_get_nested() + + +def omp_set_schedule(kind, modifier=0): + """Set the schedule that is applied when *runtime* is used as \ + schedule kind, by setting the value of the run-sched-var ICV. + + The effect of this routine is to set the value of the run-sched-var ICV + of the current task to the values specified in the two arguments. The + schedule is set to the schedule type specified by the first argument kind. + It can be any of the standard schedule types or any other implementation + specific one. For the schedule types static, dynamic, and guided the + chunk_size is set to the value of the second argument, or to the default + chunk_size if the value of the second argument is less than 1; for the + schedule type auto the second argument has no meaning; for implementation + specific schedule types, the values and associated meanings of the second + argument are implementation defined. + + Args: + + kind (:obj:`int`): Must be :data:`wrf.OMP_SCHED_STATIC`, + :data:`wrf.OMP_SCHED_DYNAMIC`, :data:`wrf.OMP_SCHED_GUIDED`, + or :data:`wrf.OMP_SCHED_AUTO`. + + modifier(:obj:`int`): An implementation specific value, depending on + the choice for *kind*. This parameter is alternatively named + chunk_size in some OpenMP documentation. Default is 0, which + means the OpenMP implementation will use its default value. + + Returns: + + None + + See Also: + + :meth:`wrf.omp_get_schedule` + + """ + fomp_set_schedule(kind, modifier) + + +def omp_get_schedule(): + """Return the schedule that is applied when the runtime schedule is used. + + This routine returns the run-sched-var ICV in the task to which the routine + binds. The first item is the schedule kind, which will be one of + :data:`wrf.OMP_SCHED_STATIC`, :data:`wrf.OMP_SCHED_DYNAMIC`, + :data:`wrf.OMP_SCHED_GUIDED`, or :data:`wrf.OMP_SCHED_AUTO`. The second + item returned is the modifier, which is often named chunk_size in + OpenMP documentation. + + Returns: + + :obj:`tuple`: The first item is an :obj:`int` for the schedule *kind*. + The second items is :obj:`int` for the *modifier* (chunk_size). + + See Also: + + :meth:`wrf.omp_set_schedule` + + """ + return fomp_get_schedule() + + +def omp_get_thread_limit(): + """Return the maximum number of OpenMP threads available to participate \ + in the current contention group. + + The omp_get_thread_limit routine returns the value of the thread-limit-var + ICV. + + Returns: + + :obj:`int`: The number of OpenMP threads available to participate in + the current contention group. + + See Also: + + :meth:`wrf.omp_get_max_threads` + + """ + return fomp_get_thread_limit() + + +def omp_set_max_active_levels(max_levels): + """Limit the number of nested active parallel regions on the device, \ + by setting the max-active-levels-var ICV. + + The effect of this routine is to set the value of the max-active-levels-var + ICV to the value specified in the argument. If the number of parallel + levels requested exceeds the number of levels of parallelism supported by + the implementation, the value of the max-active-levels-var ICV will be set + to the number of parallel levels supported by the implementation. This + routine has the described effect only when called from a sequential part + of the program. When called from within an explicit parallel region, the + effect of this routine is implementation defined. + + Args: + + max_levels (:obj:`int`): The maximum number of nested active parallel + regions. + + Returns: + + None. + + See Also: + + :meth:`wrf.omp_get_max_active_levels` + + """ + fomp_set_max_active_levels(max_levels) + + +def omp_get_max_active_levels(): + """Return the value of the max-active-levels-var ICV, which determines \ + the maximum number of nested active parallel regions on the device + + The omp_get_max_active_levels routine returns the value of the + max-active-levels-var ICV, which determines the maximum number of nested + active parallel regions on the device. + + Returns: + + :obj:`int`: The maximum number of nested active parallel regions. + + See Also: + + :meth:`wrf.omp_set_max_active_levels` + + """ + return fomp_get_max_active_levels() + + +def omp_get_level(): + """Return the value of the levels-var ICV. + + The effect of the omp_get_level routine is to return the number of nested + parallel regions (whether active or inactive) enclosing the current task + such that all of the parallel regions are enclosed by the outermost initial + task region on the current device. + + Returns: + + :obj:`int`: The number of nested parallel regions. + + See Also: + + :meth:`wrf.omp_get_active_level` + + """ + return fomp_get_level() + + +def omp_get_ancestor_thread_num(level): + """Return, for a given nested level of the current thread, the thread \ + number of the ancestor of the current thread. + + The omp_get_ancestor_thread_num routine returns the thread number of the + ancestor at a given nest level of the current thread or the thread number + of the current thread. If the requested nest level is outside the range of + 0 and the nest level of the current thread, as returned by the + omp_get_level routine, the routine returns -1. + + Args: + + level (:obj:`int`): The nested level of the current thread. + + Returns: + + :obj:`int`: The thread number of the ancestor at a given nest level + of the current thread. + + See Also: + + :meth:`wrf.omp_get_max_active_levels`, :meth:`wrf.omp_get_level` + + """ + return fomp_get_ancestor_thread_num(level) + + +def omp_get_team_size(level): + """Return, for a given nested level of the current thread, the size \ + of the thread team to which the ancestor or the current thread belongs + + The omp_get_team_size routine returns the size of the thread team to which + the ancestor or the current thread belongs. If the requested nested level + is outside the range of 0 and the nested level of the current thread, as + returned by the omp_get_level routine, the routine returns -1. Inactive + parallel regions are regarded like active parallel regions executed with + one thread. + + Args: + + level (:obj:`int`): The nested level of the current thread. + + Returns: + + :obj:`int`: The size of the thread team. + + See Also: + + :meth:`wrf.omp_get_ancestor_thread_num` + + """ + return fomp_get_team_size(level) + + +def omp_get_active_level(): + """Return the value of the active-level-vars ICV. + + The effect of the omp_get_active_level routine is to return the number of + nested, active parallel regions enclosing the current task such that all + of the parallel regions are enclosed by the outermost initial task region + on the current device. + + Returns: + + :obj:`int`: The number of nested activate parallel regions. + + See Also: + + :meth:`wrf.omp_get_team_size` + + """ + return fomp_get_active_level() + + +def omp_in_final(): + """Return 1 (True) if the routine is executed in a final task region; \ + otherwise, it returns 0 (False). + + Returns: + + :obj:`int`: Return 1 if the routine is executed in a final task + region, 0 otherwise. + + """ + return fomp_in_final() + + +def omp_init_lock(): + """Initialize a simple OpenMP lock. + + Returns: + + :obj:`int`: An integer representing the lock. + + See Also: + + :meth:`wrf.omp_init_nest_lock`, :meth:`wrf.omp_destroy_lock` + + """ + return fomp_init_lock() + + +def omp_init_nest_lock(): + """Initialize a nestable OpenMP lock. + + Returns: + + :obj:`int`: An integer representing the nestable lock. + + See Also: + + :meth:`wrf.omp_init_lock` + + """ + return fomp_init_nest_lock() + + +def omp_destroy_lock(svar): + """Destroy a simple OpenMP lock. + + This sets the lock to an uninitialized state. + + Args: + + svar (:obj:`int`): An integer representing the lock. + + See Also: + + :meth:`wrf.omp_destroy_nest_lock`, :meth:`wrf.omp_init_lock` + + """ + fomp_destroy_lock(svar) + + +def omp_destroy_nest_lock(nvar): + """Destroy a nestable OpenMP lock. + + This sets the lock to an uninitialized state. + + Args: + + nvar (:obj:`int`): An integer representing the nestable lock. + + See Also: + + :meth:`wrf.omp_destroy_lock`, :meth:`wrf.omp_init_nest_lock` + + """ + fomp_destroy_nest_lock(nvar) + + +def omp_set_lock(svar): + """Set a simple OpenMP lock. + + Args: + + svar (:obj:`int`): An integer representing the lock. + + See Also: + + :meth:`wrf.omp_unset_lock`, :meth:`wrf.omp_set_nest_lock` + + """ + fomp_set_lock(svar) + + +def omp_set_nest_lock(nvar): + """Set a nestable OpenMP lock. + + Args: + + nvar (:obj:`int`): An integer representing the nestable lock. + + See Also: + + :meth:`wrf.omp_unset_nest_lock`, :meth:`wrf.omp_set_lock` + + """ + fomp_set_nest_lock(nvar) + + +def omp_unset_lock(svar): + """Unset a simple OpenMP lock. + + Args: + + svar (:obj:`int`): An integer representing the simple lock. + + See Also: + + :meth:`wrf.omp_unset_nest_lock`, :meth:`wrf.omp_set_lock` + + """ + fomp_unset_lock(svar) + + +def omp_unset_nest_lock(nvar): + """Unset a nestable OpenMP lock. + + Args: + + nvar (:obj:`int`): An integer representing the nestable lock. + + See Also: + + :meth:`wrf.omp_set_nest_lock`, :meth:`wrf.omp_unset_lock` + + """ + fomp_unset_nest_lock(nvar) + + +def omp_test_lock(svar): + """Test a simple OpenMP lock. + + This method attempts to set the lock, but does not suspend execution. + + Args: + + svar (:obj:`int`): An integer representing the simple lock. + + Returns: + + :obj:`int`: Returns 1 (True) if the lock is successfully set, + otherwise 0 (False). + + See Also: + + :meth:`wrf.test_nest_lock`, :meth:`wrf.omp_set_lock` + + """ + return fomp_test_lock(svar) + + +def omp_test_nest_lock(nvar): + """Test a nestable OpenMP lock. + + This method attempts to set the lock, but does not suspend execution. + + Args: + + nvar (:obj:`int`): An integer representing the simple lock. + + Returns: + + :obj:`int`: Returns the nesting count if successful, + otherwise 0 (False). + + See Also: + + :meth:`wrf.test_lock`, :meth:`wrf.omp_set_nest_lock` + + """ + return fomp_test_nest_lock(nvar) + + +def omp_get_wtime(): + """Return elapsed wall clock time in seconds. + + The omp_get_wtime routine returns a value equal to the elapsed wall clock + time in seconds since some “time in the past”. The actual + “time in the past” is arbitrary, but it is guaranteed not to change during + the execution of the application program. The time returned is a + “per-thread time”, so it is not required to be globally consistent across + all threads participating in an application. + + Returns: + + :obj:`float`: Returns the wall clock time in seconds. + + See Also: + + :meth:`wrf.omp_get_wtick` + + """ + return fomp_get_wtime() + + +def omp_get_wtick(): + """Return the precision of the timer used by :meth:`wrf.omp_get_wtime`. + + The omp_get_wtick routine returns a value equal to the number of + seconds between successive clock ticks of the timer used by + :meth:`wrf.omp_get_wtime`. + + Returns: + + :obj:`float`: Returns the precision of the timer. + + See Also: + + :meth:`wrf.omp_get_wtime` + + """ + return fomp_get_wtick() + diff --git a/src/wrf/cape.py b/src/wrf/g_cape.py similarity index 96% rename from src/wrf/cape.py rename to src/wrf/g_cape.py index 7cb7efd..5f5ef10 100755 --- a/src/wrf/cape.py +++ b/src/wrf/g_cape.py @@ -6,13 +6,13 @@ import numpy.ma as ma from .extension import _tk, _cape from .destag import destagger -from .constants import Constants, ConversionFactors +from .constants import default_fill, Constants, ConversionFactors from .util import extract_vars 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=Constants.DEFAULT_FILL): + meta=True, _key=None, missing=default_fill(np.float64)): """Return the 2d fields of CAPE, CIN, LCL, and LFC. The leftmost dimension of the returned array represents four different @@ -66,7 +66,7 @@ def get_2dcape(wrfin, timeidx=0, method="cat", squeeze=True, cache=None, purposes only. Default is None. missing (:obj:`float`): The fill value to use for the output. - Default is :data:`wrf.Constants.DEFAULT_FILL`. + Default is :data:`wrf.default_fill(np.float64)`. Returns: :class:`xarray.DataArray` or :class:`numpy.ndarray`: The @@ -130,7 +130,7 @@ def get_2dcape(wrfin, timeidx=0, method="cat", squeeze=True, cache=None, @set_cape_metadata(is2d=False) def get_3dcape(wrfin, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, - _key=None, missing=Constants.DEFAULT_FILL): + _key=None, missing=default_fill(np.float64)): """Return the three-dimensional CAPE and CIN. The leftmost dimension of the returned array represents two different @@ -182,7 +182,7 @@ def get_3dcape(wrfin, timeidx=0, method="cat", purposes only. Default is None. missing (:obj:`float`): The fill value to use for the output. - Default is :data:`wrf.Constants.DEFAULT_FILL`. + Default is :data:`wrf.default_fill(np.float64)`. Returns: :class:`xarray.DataArray` or :class:`numpy.ndarray`: The diff --git a/src/wrf/g_cloudfrac.py b/src/wrf/g_cloudfrac.py new file mode 100644 index 0000000..2417dfa --- /dev/null +++ b/src/wrf/g_cloudfrac.py @@ -0,0 +1,164 @@ +from __future__ import (absolute_import, division, print_function, + unicode_literals) + +import numpy as np +import numpy.ma as ma + +from .constants import Constants, default_fill +from .extension import _tk, _rh, _cloudfrac +from .metadecorators import set_cloudfrac_metadata +from .util import extract_vars +from .g_geoht import _get_geoht + + +@set_cloudfrac_metadata() +def get_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 low, mid, and high level clouds. + + The leftmost dimension of the returned array represents three different + quantities: + + - return_val[0,...] will contain LOW level cloud fraction + - return_val[1,...] will contain MID level cloud fraction + - return_val[2,...] will contain HIGH level cloud fraction + + 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 whose leftmost dimension is 3 (LOW=0, MID=1, + HIGH=2). + 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. + + """ + + ncvars = extract_vars(wrfin, timeidx, ("P", "PB", "QVAPOR", "T"), + method, squeeze, cache, meta=False, + _key=_key) + + p = ncvars["P"] + pb = ncvars["PB"] + qv = ncvars["QVAPOR"] + t = ncvars["T"] + + full_p = p + pb + full_t = t + Constants.T_BASE + + tk = _tk(full_p, full_t) + rh = _rh(qv, full_p, tk) + + if vert_type.lower() == "pres" or vert_type.lower() == "pressure": + v_coord = full_p + _low_thresh = 97000. if low_thresh is None else low_thresh + _mid_thresh = 80000. if mid_thresh is None else mid_thresh + _high_thresh = 45000. if high_thresh is None else high_thresh + vert_inc_w_height = 0 + elif (vert_type.lower() == "height_msl" + or vert_type.lower() == "height_agl"): + is_msl = vert_type.lower() == "height_msl" + v_coord = _get_geoht(wrfin, timeidx, method, squeeze, + cache, meta=False, _key=_key, height=True, + msl=is_msl) + _low_thresh = 300. if low_thresh is None else low_thresh + _mid_thresh = 2000. if mid_thresh is None else mid_thresh + _high_thresh = 6000. if high_thresh is None else high_thresh + vert_inc_w_height = 1 + else: + raise ValueError("'vert_type' must be 'pres', 'height_msl', " + "or 'height_agl'") + + cfrac = _cloudfrac(v_coord, rh, vert_inc_w_height, + _low_thresh, _mid_thresh, _high_thresh, missing) + + return ma.masked_values(cfrac, missing) + diff --git a/src/wrf/ctt.py b/src/wrf/g_ctt.py similarity index 100% rename from src/wrf/ctt.py rename to src/wrf/g_ctt.py diff --git a/src/wrf/dbz.py b/src/wrf/g_dbz.py similarity index 100% rename from src/wrf/dbz.py rename to src/wrf/g_dbz.py diff --git a/src/wrf/dewpoint.py b/src/wrf/g_dewpoint.py similarity index 100% rename from src/wrf/dewpoint.py rename to src/wrf/g_dewpoint.py diff --git a/src/wrf/geoht.py b/src/wrf/g_geoht.py similarity index 61% rename from src/wrf/geoht.py rename to src/wrf/g_geoht.py index 98d9ceb..d9c7624 100755 --- a/src/wrf/geoht.py +++ b/src/wrf/g_geoht.py @@ -1,6 +1,8 @@ from __future__ import (absolute_import, division, print_function, unicode_literals) +import warnings + from .constants import Constants from .destag import destagger from .decorators import convert_units @@ -9,7 +11,7 @@ from .util import extract_vars, either def _get_geoht(wrfin, timeidx, method="cat", squeeze=True, cache=None, meta=True, _key=None, - height=True, msl=True): + height=True, msl=True, stag=False): """Return the geopotential or geopotential height. If *height* is False, then geopotential is returned in units of @@ -67,6 +69,9 @@ def _get_geoht(wrfin, timeidx, method="cat", squeeze=True, as Mean Sea Level (MSL). Set to False to return the geopotential height as Above Ground Level (AGL) by subtracting the terrain height. Default is True. + + stag (:obj:`bool`, optional): Set to True to use the vertical + staggered grid, rather than the mass grid. Default is False. Returns: :class:`xarray.DataArray` or :class:`numpy.ndarray`: The @@ -86,14 +91,21 @@ def _get_geoht(wrfin, timeidx, method="cat", squeeze=True, phb = ph_vars["PHB"] hgt = ph_vars["HGT"] geopt = ph + phb - geopt_unstag = destagger(geopt, -3) + if not stag: + geopt_unstag = destagger(geopt, -3) + else: + geopt_unstag = geopt else: ght_vars = extract_vars(wrfin, timeidx, ("GHT", "HGT_M"), method, squeeze, cache, meta=False, _key=_key) geopt_unstag = ght_vars["GHT"] * Constants.G hgt = ght_vars["HGT_M"] - + + if stag: + warnings.warn("file contains no vertically staggered geopotential " + "height variable, returning unstaggered result " + "instead" ) if height: if msl: return geopt_unstag / Constants.G @@ -110,7 +122,7 @@ def _get_geoht(wrfin, timeidx, method="cat", squeeze=True, return geopt_unstag -@set_height_metadata(geopt=True) +@set_height_metadata(geopt=True, stag=False) def get_geopt(wrfin, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None): """Return the geopotential. @@ -171,7 +183,7 @@ def get_geopt(wrfin, timeidx=0, method="cat", squeeze=True, cache=None, False, True) -@set_height_metadata(geopt=False) +@set_height_metadata(geopt=False, stag=False) @convert_units("height", "m") def get_height(wrfin, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None, @@ -245,3 +257,139 @@ def get_height(wrfin, timeidx=0, method="cat", squeeze=True, return _get_geoht(wrfin, timeidx, method, squeeze, cache, meta, _key, True, msl) + +@set_height_metadata(geopt=True, stag=True) +def get_stag_geopt(wrfin, timeidx=0, method="cat", squeeze=True, cache=None, + meta=True, _key=None): + """Return the geopotential for the vertically staggered grid. + + The geopotential is returned in units of [m2 s-2]. + + 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. + + Returns: + :class:`xarray.DataArray` or :class:`numpy.ndarray`: The + geopotential. + If xarray is enabled and the *meta* parameter is True, then the result + will be a :class:`xarray.DataArray` object. Otherwise, the result will + be a :class:`numpy.ndarray` object with no metadata. + + """ + return _get_geoht(wrfin, timeidx, method, squeeze, cache, meta, _key, + False, True, stag=True) + + +@set_height_metadata(geopt=False, stag=True) +@convert_units("height", "m") +def get_stag_height(wrfin, timeidx=0, method="cat", squeeze=True, + cache=None, meta=True, _key=None, + msl=True, units="m"): + """Return the geopotential height for the vertically staggered grid. + + If *msl* is True, then geopotential height is returned as Mean Sea Level + (MSL). If *msl* is False, then geopotential height is returned as + Above Ground Level (AGL) by subtracting the terrain height. + + This functions extracts the necessary variables from the NetCDF file + object in order to perform the calculation. + + Args: + + wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ + iterable): WRF-ARW NetCDF + data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` + or an iterable sequence of the aforementioned types. + + timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The + desired time index. This value can be a positive integer, + negative integer, or + :data:`wrf.ALL_TIMES` (an alias for None) to return + all times in the file or sequence. The default is 0. + + method (:obj:`str`, optional): The aggregation method to use for + sequences. Must be either 'cat' or 'join'. + 'cat' combines the data along the Time dimension. + 'join' creates a new dimension for the file index. + The default is 'cat'. + + squeeze (:obj:`bool`, optional): Set to False to prevent dimensions + with a size of 1 from being automatically removed from the shape + of the output. Default is True. + + cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) + that can be used to supply pre-extracted NetCDF variables to the + computational routines. It is primarily used for internal + purposes, but can also be used to improve performance by + eliminating the need to repeatedly extract the same variables + used in multiple diagnostics calculations, particularly when using + large sequences of files. + Default is None. + + meta (:obj:`bool`, optional): Set to False to disable metadata and + return :class:`numpy.ndarray` instead of + :class:`xarray.DataArray`. Default is True. + + _key (:obj:`int`, optional): A caching key. This is used for internal + purposes only. Default is None. + + msl (:obj:`bool`, optional): Set to True to return geopotential height + as Mean Sea Level (MSL). Set to False to return the + geopotential height as Above Ground Level (AGL) by subtracting + the terrain height. Default is True. + + units (:obj:`str`): The desired units. Refer to the :meth:`getvar` + product table for a list of available units for 'z'. Default + is 'm'. + + Returns: + :class:`xarray.DataArray` or :class:`numpy.ndarray`: The + geopotential height. + If xarray is enabled and the *meta* parameter is True, then the result + will be a :class:`xarray.DataArray` object. Otherwise, the result will + be a :class:`numpy.ndarray` object with no metadata. + + """ + + return _get_geoht(wrfin, timeidx, method, squeeze, cache, meta, _key, + True, msl, stag=True) + \ No newline at end of file diff --git a/src/wrf/helicity.py b/src/wrf/g_helicity.py similarity index 100% rename from src/wrf/helicity.py rename to src/wrf/g_helicity.py diff --git a/src/wrf/latlon.py b/src/wrf/g_latlon.py similarity index 100% rename from src/wrf/latlon.py rename to src/wrf/g_latlon.py diff --git a/src/wrf/omega.py b/src/wrf/g_omega.py similarity index 100% rename from src/wrf/omega.py rename to src/wrf/g_omega.py diff --git a/src/wrf/precip.py b/src/wrf/g_precip.py similarity index 100% rename from src/wrf/precip.py rename to src/wrf/g_precip.py diff --git a/src/wrf/pressure.py b/src/wrf/g_pressure.py similarity index 100% rename from src/wrf/pressure.py rename to src/wrf/g_pressure.py diff --git a/src/wrf/pw.py b/src/wrf/g_pw.py similarity index 100% rename from src/wrf/pw.py rename to src/wrf/g_pw.py diff --git a/src/wrf/rh.py b/src/wrf/g_rh.py similarity index 100% rename from src/wrf/rh.py rename to src/wrf/g_rh.py diff --git a/src/wrf/slp.py b/src/wrf/g_slp.py similarity index 100% rename from src/wrf/slp.py rename to src/wrf/g_slp.py diff --git a/src/wrf/temp.py b/src/wrf/g_temp.py similarity index 100% rename from src/wrf/temp.py rename to src/wrf/g_temp.py diff --git a/src/wrf/terrain.py b/src/wrf/g_terrain.py similarity index 100% rename from src/wrf/terrain.py rename to src/wrf/g_terrain.py diff --git a/src/wrf/times.py b/src/wrf/g_times.py similarity index 100% rename from src/wrf/times.py rename to src/wrf/g_times.py diff --git a/src/wrf/uvmet.py b/src/wrf/g_uvmet.py similarity index 99% rename from src/wrf/uvmet.py rename to src/wrf/g_uvmet.py index 6dba5e6..5ef19a3 100755 --- a/src/wrf/uvmet.py +++ b/src/wrf/g_uvmet.py @@ -9,7 +9,7 @@ import numpy as np from .extension import _uvmet from .destag import destagger from .constants import Constants -from .wind import _calc_wspd_wdir +from .g_wind import _calc_wspd_wdir from .decorators import convert_units from .metadecorators import set_wind_metadata from .util import extract_vars, extract_global_attrs, either diff --git a/src/wrf/vorticity.py b/src/wrf/g_vorticity.py similarity index 100% rename from src/wrf/vorticity.py rename to src/wrf/g_vorticity.py diff --git a/src/wrf/wind.py b/src/wrf/g_wind.py similarity index 100% rename from src/wrf/wind.py rename to src/wrf/g_wind.py diff --git a/src/wrf/interp.py b/src/wrf/interp.py index a2e67aa..fcd3a8e 100755 --- a/src/wrf/interp.py +++ b/src/wrf/interp.py @@ -11,16 +11,16 @@ from .metadecorators import set_interp_metadata from .util import extract_vars, is_staggered, get_id, to_np from .py3compat import py3range from .interputils import get_xy, get_xy_z_params, to_xy_coords -from .constants import Constants, ConversionFactors -from .terrain import get_terrain -from .geoht import get_height -from .temp import get_theta, get_temp, get_eth -from .pressure import get_pressure +from .constants import Constants, default_fill, ConversionFactors +from wrf.g_terrain import get_terrain +from wrf.g_geoht import get_height +from wrf.g_temp import get_theta, get_temp, get_eth +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=Constants.DEFAULT_FILL, +def interplevel(field3d, vert, desiredlev, missing=default_fill(np.float64), meta=True): """Return the three-dimensional field interpolated to a horizontal plane at the specified vertical level. @@ -40,7 +40,7 @@ def interplevel(field3d, vert, desiredlev, missing=Constants.DEFAULT_FILL, 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.Constants.DEFAULT_FILL`. + Default is :data:`wrf.default_fill(numpy.float64)`. meta (:obj:`bool`): Set to False to disable metadata and return :class:`numpy.ndarray` instead of @@ -90,7 +90,7 @@ def interplevel(field3d, vert, desiredlev, missing=Constants.DEFAULT_FILL, @set_interp_metadata("cross") -def vertcross(field3d, vert, levels=None, missing=Constants.DEFAULT_FILL, +def vertcross(field3d, vert, levels=None, missing=default_fill(np.float64), wrfin=None, timeidx=0, stagger=None, projection=None, pivot_point=None, angle=None, start_point=None, end_point=None, @@ -133,7 +133,7 @@ def vertcross(field3d, vert, levels=None, missing=Constants.DEFAULT_FILL, Default is None. missing (:obj:`float`): The fill value to use for the output. - Default is :data:`wrf.Constants.DEFAULT_FILL`. + Default is :data:`wrf.default_fill(numpy.float64)`. wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ iterable, optional): WRF-ARW NetCDF @@ -197,7 +197,16 @@ def vertcross(field3d, vert, levels=None, missing=Constants.DEFAULT_FILL, latlon (:obj:`bool`, optional): Set to True to also interpolate the two-dimensional latitude and longitude coordinates along the same horizontal line and include this information in the metadata - (if enabled). This can be helpful for plotting. Default is False. + (if enabled). This can be helpful for plotting. Default is False. + + Note: + + Currently, *field3d* must be of type :class:`xarray.DataArray` + and contain coordinate information in order to generate the + latitude and longitude coordinates along the line if + *latlon* is set to True. Otherwise, a warning will be issued, + and the latitude and longitude information will not be + present. cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) that can be used to supply pre-extracted NetCDF variables to the @@ -365,6 +374,15 @@ def interpline(field2d, pivot_point=None, horizontal line and include this information in the metadata (if enabled). This can be helpful for plotting. Default is False. + Note: + + Currently, *field2d* must be of type :class:`xarray.DataArray` + and contain coordinate information in order to generate the + latitude and longitude coordinates along the line if + *latlon* is set to True. Otherwise, a warning will be issued, + and the latitude and longitude information will not be + present. + 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 @@ -584,9 +602,6 @@ def vinterp(wrfin, field, vert_coord, interp_levels, extrapolate=False, terht = get_terrain(wrfin, timeidx, units="m", method=method, squeeze=squeeze, cache=cache, meta=False, _key=_key) - t = get_theta(wrfin, timeidx, units="k", - method=method, squeeze=squeeze, cache=cache, - meta=False, _key=_key) tk = get_temp(wrfin, timeidx, units="k", method=method, squeeze=squeeze, cache=cache, meta=False, _key=_key) @@ -596,9 +611,6 @@ def vinterp(wrfin, field, vert_coord, interp_levels, extrapolate=False, ght = get_height(wrfin, timeidx, msl=True, units="m", method=method, squeeze=squeeze, cache=cache, meta=False, _key=_key) - ht_agl = get_height(wrfin, timeidx, msl=False, units="m", - method=method, squeeze=squeeze, cache=cache, - meta=False, _key=_key) smsfp = _smooth2d(sfp, 3) @@ -613,10 +625,18 @@ def vinterp(wrfin, field, vert_coord, interp_levels, extrapolate=False, vcord_array = np.exp(-ght/sclht) elif vert_coord == "ght_agl": + ht_agl = get_height(wrfin, timeidx, msl=False, units="m", + method=method, squeeze=squeeze, cache=cache, + meta=False, _key=_key) + vcor = 3 vcord_array = np.exp(-ht_agl/sclht) elif vert_coord in ("theta", "th"): + t = get_theta(wrfin, timeidx, units="k", + method=method, squeeze=squeeze, cache=cache, + meta=False, _key=_key) + vcor = 4 idir = 1 icorsw = 0 @@ -651,7 +671,7 @@ def vinterp(wrfin, field, vert_coord, interp_levels, extrapolate=False, if isinstance(field, ma.MaskedArray): missing = field.fill_value else: - missing = Constants.DEFAULT_FILL + missing = default_fill(np.float64) if (field.shape != p.shape): raise ValueError("'field' shape does not match other variable shapes. " diff --git a/src/wrf/interputils.py b/src/wrf/interputils.py index 114df41..fb2768f 100644 --- a/src/wrf/interputils.py +++ b/src/wrf/interputils.py @@ -158,23 +158,24 @@ def _calc_xy(xdim, ydim, pivot_point=None, angle=None, 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 + x1 = xdim - 1 if ( y1 > ydim-1): - y1 = ydim + y1 = ydim - 1 else: raise ValueError("invalid start/end or pivot/angle arguments") dx = x1 - x0 dy = y1 - y0 distance = (dx*dx + dy*dy)**0.5 - npts = int(distance) - dxy = distance/npts + npts = int(distance) + 1 xy = np.zeros((npts,2), "float") - dx = dx/npts - dy = dy/npts + dx = dx/(npts-1) + dy = dy/(npts-1) for i in py3range(npts): xy[i,0] = x0 + i*dx diff --git a/src/wrf/metadecorators.py b/src/wrf/metadecorators.py index e34b71d..0549220 100644 --- a/src/wrf/metadecorators.py +++ b/src/wrf/metadecorators.py @@ -1,5 +1,6 @@ from __future__ import (absolute_import, division, print_function, unicode_literals) +import warnings import wrapt from collections import OrderedDict @@ -322,6 +323,7 @@ def set_wind_metadata(copy_varname, name, description, return func_wrapper + def set_cape_metadata(is2d): """A decorator that sets the metadata for a wrapped CAPE function's output. @@ -468,14 +470,22 @@ def set_cloudfrac_metadata(): return wrapped(*args, **kwargs) argvars = from_args(wrapped, ("wrfin", "timeidx", "method", "squeeze", - "cache", "_key"), - *args, **kwargs) + "cache", "_key", "vert_type", + "low_thresh", "mid_thresh", + "high_thresh", "missing"), + *args, **kwargs) wrfin = argvars["wrfin"] timeidx = argvars["timeidx"] method = argvars["method"] squeeze = argvars["squeeze"] cache = argvars["cache"] _key = argvars["_key"] + vert_type = argvars["vert_type"] + low_thresh = argvars["low_thresh"] + mid_thresh = argvars["mid_thresh"] + high_thresh = argvars["high_thresh"] + missing = argvars["missing"] + if cache is None: cache = {} @@ -499,6 +509,20 @@ def set_cloudfrac_metadata(): outattrs.update(copy_var.attrs) outdimnames = [None] * result.ndim + # For printing units + unitstr = ("Pa" if vert_type.lower() == "pres" + or vert_type.lower() == "pressure" else "m") + + # For setting the threholds in metdata + if vert_type.lower() == "pres" or vert_type.lower() == "pressure": + _low_thresh = 97000. if low_thresh is None else low_thresh + _mid_thresh = 80000. if mid_thresh is None else mid_thresh + _high_thresh = 45000. if high_thresh is None else high_thresh + elif vert_type.lower() == "height_msl" or "height_agl": + _low_thresh = 300. if low_thresh is None else low_thresh + _mid_thresh = 2000. if mid_thresh is None else mid_thresh + _high_thresh = 6000. if high_thresh is None else high_thresh + # Right dims outdimnames[-2:] = copy_var.dims[-2:] # Left dims @@ -507,6 +531,11 @@ def set_cloudfrac_metadata(): outattrs["description"] = "low, mid, high clouds" outattrs["MemoryOrder"] = "XY" outattrs["units"] = "%" + outattrs["low_thresh"] = "{} {}".format(_low_thresh, unitstr) + outattrs["mid_thresh"] = "{} {}".format(_mid_thresh, unitstr) + outattrs["high_thresh"] = "{} {}".format(_high_thresh, unitstr) + outattrs["_FillValue"] = missing + outattrs["missing_value"] = missing outname = "cloudfrac" # xarray doesn't line up coordinate dimensions based on @@ -529,6 +558,7 @@ def set_cloudfrac_metadata(): return func_wrapper + def set_latlon_metadata(xy=False): """A decorator that sets the metadata for a wrapped latlon function's output. @@ -613,8 +643,9 @@ def set_latlon_metadata(xy=False): return da return func_wrapper - -def set_height_metadata(geopt=False): + + +def set_height_metadata(geopt=False, stag=False): """A decorator that sets the metadata for a wrapped height function's output. @@ -629,6 +660,9 @@ def set_height_metadata(geopt=False): returns geopotential. Set to True if the wrapped function returns geopotential height. Default is False. + stag (:obj:`bool`, optional): Set to True to use the vertical + staggered grid, rather than the mass grid. Default is False. + Returns: :class:`xarray.DataArray` or :class:`numpy.ndarray`: The wrapped @@ -664,9 +698,17 @@ def set_height_metadata(geopt=False): if cache is None: cache = {} + is_met_em = False # For height, either copy the met_em GHT variable or copy and modify # pressure (which has the same dims as destaggered height) - ht_metadata_varname = either("P", "GHT")(wrfin) + if not stag: + ht_metadata_varname = either("P", "GHT")(wrfin) + else: + ht_metadata_varname = either("PH", "GHT")(wrfin) + + if ht_metadata_varname == "GHT": + is_met_em = True + ht_var = extract_vars(wrfin, timeidx, ht_metadata_varname, method, squeeze, cache, meta=True, _key=_key) @@ -692,18 +734,28 @@ def set_height_metadata(geopt=False): if geopt: outname = "geopt" outattrs["units"] = "m2 s-2" - outattrs["description"] = "full model geopotential" + if not stag or is_met_em: + outattrs["description"] = "geopotential (mass grid)" + else: + outattrs["description"] = ("geopotential (vertically " + "staggered grid)") else: outname = "height" if msl else "height_agl" outattrs["units"] = units height_type = "MSL" if msl else "AGL" - outattrs["description"] = "model height ({})".format(height_type) + if not stag or is_met_em: + outattrs["description"] = ("model height - [{}] " + "(mass grid)".format(height_type)) + else: + outattrs["description"] = ("model height - [{}] (vertically " + "staggered grid)".format(height_type)) return DataArray(result, name=outname, dims=outdimnames, coords=outcoords, attrs=outattrs) return func_wrapper + def _set_horiz_meta(wrapped, instance, args, kwargs): """A decorator implementation that sets the metadata for a wrapped horizontal interpolation function. @@ -798,7 +850,8 @@ def _set_horiz_meta(wrapped, instance, args, kwargs): return DataArray(result, name=outname, dims=outdimnames, coords=outcoords, attrs=outattrs) - + + def _set_cross_meta(wrapped, instance, args, kwargs): """A decorator implementation that sets the metadata for a wrapped cross \ section interpolation function. @@ -990,6 +1043,8 @@ def _set_cross_meta(wrapped, instance, args, kwargs): outcoords["xy_loc"] = (loc_dimnames, latlon_loc) else: + warnings.warn("'latlon' is set to True, but 'field3d' " + " contains no coordinate information") outcoords["xy_loc"] = ("cross_line_idx", np.asarray(tuple( CoordPair(xy[i,0], xy[i,1]) for i in py3range(xy.shape[-2])))) @@ -1002,6 +1057,10 @@ def _set_cross_meta(wrapped, instance, args, kwargs): outcoords["vertical"] = z_var2d[:] else: + if inc_latlon: + warnings.warn("'latlon' is set to True, but 'field3d' is " + "not of type xarray.DataArray and contains no " + "coordinate information") outname = "field3d_cross" outattrs = OrderedDict() @@ -1012,7 +1071,6 @@ def _set_cross_meta(wrapped, instance, args, kwargs): return DataArray(result, name=outname, dims=outdimnames, coords=outcoords, attrs=outattrs) - def _set_line_meta(wrapped, instance, args, kwargs): """A decorator implementation that sets the metadata for a wrapped line @@ -1198,6 +1256,8 @@ def _set_line_meta(wrapped, instance, args, kwargs): outcoords["xy_loc"] = (loc_dimnames, latlon_loc) else: + warnings.warn("'latlon' is set to True, but 'field2d' " + "contains no coordinate information") outcoords["xy_loc"] = ("line_idx", np.asarray(tuple( CoordPair(xy[i,0], xy[i,1]) for i in py3range(xy.shape[-2])))) @@ -1208,6 +1268,10 @@ def _set_line_meta(wrapped, instance, args, kwargs): for i in py3range(xy.shape[-2])))) else: + if inc_latlon: + warnings.warn("'latlon' is set to True, but 'field2d' is " + "not of type xarray.DataArray and contains no " + "coordinate information") outname = "field2d_line" outattrs = OrderedDict() @@ -1770,6 +1834,7 @@ def set_alg_metadata(alg_ndims, refvarname, return func_wrapper + def set_smooth_metdata(): @wrapt.decorator def func_wrapper(wrapped, instance, args, kwargs): @@ -1994,14 +2059,14 @@ def set_cape_alg_metadata(is2d, copyarg="pres_hpa"): return func_wrapper -def set_cloudfrac_alg_metadata(copyarg="pres"): +def set_cloudfrac_alg_metadata(copyarg="vert"): """A decorator that sets the metadata for the wrapped raw cloud fraction diagnostic function. Args: copyarg (:obj:`str`): The wrapped function argument to use for - copying dimension names. Default is 'pres'. + copying dimension names. Default is 'vert'. Returns: @@ -2025,6 +2090,16 @@ def set_cloudfrac_alg_metadata(copyarg="pres"): result = wrapped(*args, **kwargs) + argvals = from_args(wrapped, (copyarg, "low_thresh", + "mid_thresh", "high_thresh", + "missing"), + *args, **kwargs) + cp = argvals[copyarg] + low_thresh = argvals["low_thresh"] + mid_thresh = argvals["mid_thresh"] + high_thresh = argvals["high_thresh"] + missing = argvals["missing"] + # Default dimension names outdims = ["dim_{}".format(i) for i in py3range(result.ndim)] @@ -2034,15 +2109,17 @@ def set_cloudfrac_alg_metadata(copyarg="pres"): outattrs["description"] = "low, mid, high clouds" outattrs["units"] = "%" outattrs["MemoryOrder"] = "XY" + outattrs["low_thresh"] = low_thresh + outattrs["mid_thresh"] = mid_thresh + outattrs["high_thresh"] = high_thresh + outattrs["_FillValue"] = missing + outattrs["missing_value"] = missing - - p = from_args(wrapped, copyarg, *args, **kwargs)[copyarg] - - if isinstance(p, DataArray): + if isinstance(cp, DataArray): # Right dims - outdims[-2:] = p.dims[-2:] + outdims[-2:] = cp.dims[-2:] # Left dims - outdims[1:-2] = p.dims[0:-3] + outdims[1:-2] = cp.dims[0:-3] outcoords = {} diff --git a/src/wrf/projection.py b/src/wrf/projection.py index f8b95d5..d5b6670 100644 --- a/src/wrf/projection.py +++ b/src/wrf/projection.py @@ -574,12 +574,16 @@ class LambertConformal(WrfProj): def _cartopy(self): if not cartopy_enabled(): return None - + + # Set cutoff to -30 for NH, +30.0 for SH. + cutoff = -30.0 if self.moad_cen_lat >= 0 else 30.0 + _cartopy = crs.LambertConformal( central_longitude = self.stand_lon, central_latitude = self.moad_cen_lat, standard_parallels = self._std_parallels, - globe = self._globe()) + globe = self._globe(), + cutoff = cutoff) return _cartopy @@ -787,7 +791,7 @@ class PolarStereographic(WrfProj): return _pyngl - def _basemap(self, **kwargs): + def _basemap(self, geobounds, **kwargs): if not basemap_enabled(): return None diff --git a/src/wrf/routines.py b/src/wrf/routines.py index b72423b..5442956 100644 --- a/src/wrf/routines.py +++ b/src/wrf/routines.py @@ -3,27 +3,27 @@ from __future__ import (absolute_import, division, print_function, from .util import (get_iterable, is_standard_wrf_var, extract_vars, viewkeys, get_id) -from .cape import get_2dcape, get_3dcape -from .ctt import get_ctt -from .dbz import get_dbz, get_max_dbz -from .dewpoint import get_dp, get_dp_2m -from .geoht import get_geopt, get_height -from .helicity import get_srh, get_uh -from .latlon import get_lat, get_lon -from .omega import get_omega -from .pressure import get_pressure, get_pressure_hpa -from .pw import get_pw -from .rh import get_rh, get_rh_2m -from .slp import get_slp -from .temp import get_tc, get_eth, get_temp, get_theta, get_tk, get_tv, get_tw -from .terrain import get_terrain -from .uvmet import (get_uvmet, get_uvmet10, get_uvmet10_wspd_wdir, +from .g_cape import get_2dcape, get_3dcape +from .g_ctt import get_ctt +from .g_dbz import get_dbz, get_max_dbz +from .g_dewpoint import get_dp, get_dp_2m +from .g_geoht import get_geopt, get_height, get_stag_geopt, get_stag_height +from .g_helicity import get_srh, get_uh +from .g_latlon import get_lat, get_lon +from .g_omega import get_omega +from .g_pressure import get_pressure, get_pressure_hpa +from .g_pw import get_pw +from .g_rh import get_rh, get_rh_2m +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) -from .vorticity import get_avo, get_pvo -from .wind import (get_destag_wspd_wdir, get_destag_wspd_wdir10, +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) -from .times import get_times -from .cloudfrac import get_cloudfrac +from .g_times import get_times +from .g_cloudfrac import get_cloudfrac # func is the function to call. kargs are required arguments that should @@ -68,7 +68,9 @@ _FUNC_MAP = {"cape2d" : get_2dcape, "uvmet_wspd_wdir" : get_uvmet_wspd_wdir, "uvmet10_wspd_wdir" : get_uvmet10_wspd_wdir, "ctt" : get_ctt, - "cloudfrac" : get_cloudfrac + "cloudfrac" : get_cloudfrac, + "geopt_stag" : get_stag_geopt, + "zstag" : get_stag_height } _VALID_KARGS = {"cape2d" : ["missing"], @@ -111,7 +113,10 @@ _VALID_KARGS = {"cape2d" : ["missing"], "uvmet_wspd_wdir" : ["units"], "uvmet10_wspd_wdir" : ["units"], "ctt" : [], - "cloudfrac" : [], + "cloudfrac" : ["vert_type", "low_thresh", + "mid_thresh", "high_thresh"], + "geopt_stag" : [], + "zstag" : ["msl", "units"], "default" : [] } diff --git a/src/wrf/specialdec.py b/src/wrf/specialdec.py index c093ca5..f72899b 100644 --- a/src/wrf/specialdec.py +++ b/src/wrf/specialdec.py @@ -7,7 +7,7 @@ import wrapt from .util import iter_left_indexes, to_np from .config import xarray_enabled -from .constants import Constants +from .constants import default_fill if xarray_enabled(): from xarray import DataArray @@ -74,12 +74,12 @@ def uvmet_left_iter(alg_dtype=np.float64): v_arr = to_np(v) - umissing = Constants.DEFAULT_FILL + umissing = default_fill(np.float64) if isinstance(u_arr, np.ma.MaskedArray): has_missing = True umissing = u_arr.fill_value - vmissing = Constants.DEFAULT_FILL + vmissing = default_fill(np.float64) if isinstance(v_arr, np.ma.MaskedArray): has_missing = True vmissing = v_arr.fill_value @@ -383,6 +383,7 @@ def cape_left_iter(alg_dtype=np.float64): return func_wrapper + def cloudfrac_left_iter(alg_dtype=np.float64): """A decorator to handle iterating over the leftmost dimensions for the cloud fraction diagnostic. @@ -409,72 +410,71 @@ def cloudfrac_left_iter(alg_dtype=np.float64): """ @wrapt.decorator def func_wrapper(wrapped, instance, args, kwargs): - # The cape calculations use an ascending vertical pressure coordinate new_args = list(args) new_kwargs = dict(kwargs) - p = args[0] + vert = args[0] rh = args[1] - num_left_dims = p.ndim - 3 - orig_dtype = p.dtype + num_left_dims = vert.ndim - 3 + orig_dtype = vert.dtype - # No special left side iteration, build the output from the cape,cin - # result + # No special left side iteration, build the output from the + # low, mid, high results. if (num_left_dims == 0): - low, med, high = wrapped(*new_args, **new_kwargs) + low, mid, high = wrapped(*new_args, **new_kwargs) output_dims = (3,) - output_dims += p.shape[-2:] + output_dims += vert.shape[-2:] output = np.empty(output_dims, orig_dtype) output[0,:] = low[:] - output[1,:] = med[:] + output[1,:] = mid[:] output[2,:] = high[:] return output - # Initial output is ...,cape_cin,nz,ny,nx to create contiguous views - outdims = p.shape[0:num_left_dims] + # Initial output is ...,low_mid_high,nz,ny,nx to create contiguous views + outdims = vert.shape[0:num_left_dims] extra_dims = tuple(outdims) # Copy the left-most dims for iteration outdims += (3,) # low_mid_high - outdims += p.shape[-2:] + outdims += vert.shape[-2:] outview_array = np.empty(outdims, alg_dtype) # Create the output array where the leftmost dim is the cloud type output_dims = (3,) output_dims += extra_dims - output_dims += p.shape[-2:] + output_dims += vert.shape[-2:] output = np.empty(output_dims, orig_dtype) has_missing = False - missing = Constants.DEFAULT_FILL + missing = default_fill(np.float64) for left_idxs in iter_left_indexes(extra_dims): left_and_slice_idxs = left_idxs + (slice(None),) low_idxs = left_idxs + (0, slice(None)) - med_idxs = left_idxs + (1, slice(None)) + mid_idxs = left_idxs + (1, slice(None)) high_idxs = left_idxs + (2, slice(None)) low_output_idxs = (0,) + left_idxs + (slice(None),) - med_output_idxs = (1,) + left_idxs + (slice(None),) + mid_output_idxs = (1,) + left_idxs + (slice(None),) high_output_idxs = (2,) + left_idxs + (slice(None),) - new_args[0] = p[left_and_slice_idxs] + new_args[0] = vert[left_and_slice_idxs] new_args[1] = rh[left_and_slice_idxs] # Skip the possible empty/missing arrays for the join method - # Note: Masking handled by cape.py or computation.py, so only + # Note: Masking handled by cloudfrac.py or computation.py, so only # supply the fill values here. skip_missing = False for arg in (new_args[0:2]): if isinstance(arg, np.ma.MaskedArray): if arg.mask.all(): output[low_output_idxs] = missing - output[med_output_idxs] = missing + output[mid_output_idxs] = missing output[high_output_idxs] = missing skip_missing = True @@ -484,14 +484,14 @@ def cloudfrac_left_iter(alg_dtype=np.float64): continue lowview = outview_array[low_idxs] - medview = outview_array[med_idxs] + midview = outview_array[mid_idxs] highview = outview_array[high_idxs] new_kwargs["lowview"] = lowview - new_kwargs["medview"] = medview + new_kwargs["midview"] = midview new_kwargs["highview"] = highview - low, med, high = wrapped(*new_args, **new_kwargs) + low, mid, high = wrapped(*new_args, **new_kwargs) # Make sure the result is the same data as what got passed in # Can delete this once everything works @@ -501,8 +501,8 @@ def cloudfrac_left_iter(alg_dtype=np.float64): output[low_output_idxs] = ( outview_array[low_idxs].astype(orig_dtype)) - output[med_output_idxs] = ( - outview_array[med_idxs].astype(orig_dtype)) + output[mid_output_idxs] = ( + outview_array[mid_idxs].astype(orig_dtype)) output[high_output_idxs] = ( outview_array[high_idxs].astype(orig_dtype)) diff --git a/src/wrf/util.py b/src/wrf/util.py index 6c520e1..22cc2d6 100644 --- a/src/wrf/util.py +++ b/src/wrf/util.py @@ -30,7 +30,7 @@ import numpy as np import numpy.ma as ma from .config import xarray_enabled -from .constants import Constants, ALL_TIMES +from .constants import default_fill, ALL_TIMES from .py3compat import (viewitems, viewkeys, isstr, py3range, ucode) from .cache import cache_item, get_cached_item from .geobnds import GeoBounds, NullGeoBounds @@ -39,7 +39,6 @@ from .projection import getproj if xarray_enabled(): from xarray import DataArray - from pandas import NaT _COORD_PAIR_MAP = {"XLAT" : ("XLAT", "XLONG"), @@ -209,11 +208,30 @@ def _generator_copy(gen): module = getmodule(gen.gi_frame) if module is not None: - res = module.get(funcname)(**argvals.locals) + try: + try: + argd = {key:argvals.locals[key] for key in argvals.args} + res = module.get(funcname)(**argd) + except AttributeError: + res = getattr(module, funcname)(**argd) + except: + # This is the old way it used to work, but it looks like this was + # fixed by Python. + try: + res = module.get(funcname)(**argvals.locals) + except AttributeError: + res = getattr(module, funcname)(**argvals.locals) else: # Created in jupyter or the python interpreter import __main__ - res = getattr(__main__, funcname)(**argvals.locals) + + try: + argd = {key:argvals.locals[key] for key in argvals.args} + res = getattr(__main__, funcname)(**argd) + except: + # This was the old way it used to work, but appears to have + # been fixed by Python. + res = getattr(__main__, funcname)(**argvals.locals) return res @@ -727,8 +745,12 @@ def is_moving_domain(wrfin, varname=None, latvar=either("XLAT", "XLAT_M"), # to be a shortcut in the netcdf files. if varname is not None: try: - coord_names = getattr(first_wrfnc.variables[varname], - "coordinates").split() + coord_str = getattr(first_wrfnc.variables[varname], "coordinates") + # scipy.io.netcdf stores attributes as bytes rather than str + if isinstance(coord_str, str): + coord_names = coord_str.split() + else: + coord_names = coord_str.decode().split() except AttributeError: # Variable doesn't have a coordinates attribute, use the # arguments @@ -878,7 +900,17 @@ def extract_dim(wrfin, dim): d = wrfin.dimensions[dim] if not isinstance(d, int): - return len(d) #netCDF4 + try: + return len(d) #netCDF4 + except TypeError: #scipy.io.netcdf + # Scipy can't handled unlimited dimensions, so now we have to + # figure it out + try: + s = wrfin.variables["P"].shape + return s[-4] + except: + raise ValueError("unsupported NetCDF reader") + return d # PyNIO @@ -1078,6 +1110,7 @@ def _find_max_time_size(wrfseq): return max_times + def _get_coord_names(wrfin, varname): # Need only the first real file @@ -1141,7 +1174,10 @@ def _get_coord_names(wrfin, varname): lat_coord = "XLAT" lon_coord = "XLONG" else: - coord_names = coord_attr.split() + if isinstance(coord_attr, str): + coord_names = coord_attr.split() + else: + coord_names = coord_attr.decode().split() lon_coord = coord_names[0] lat_coord = coord_names[1] @@ -1200,16 +1236,46 @@ def _build_data_array(wrfnc, varname, timeidx, is_moving_domain, is_multifile, multitime = is_multi_time_req(timeidx) time_idx_or_slice = timeidx if not multitime else slice(None) var = wrfnc.variables[varname] - data = var[time_idx_or_slice, :] + if len(var.shape) > 1: + data = var[time_idx_or_slice, :] + else: + data = var[time_idx_or_slice] # Want to preserve the time dimension if not multitime: - data = data[np.newaxis, :] + if len(var.shape) > 1: + data = data[np.newaxis, :] + else: + data = data[np.newaxis] + + attrs = OrderedDict() + for dkey, val in viewitems(var.__dict__): + # scipy.io adds these but don't want them + if dkey in ("data", "_shape", "_size", "_typecode", "_attributes", + "maskandscale", "dimensions"): + continue + + _dkey = dkey if isinstance(dkey, str) else dkey.decode() + if isstr(val): + _val = val + else: + if isinstance(val, bytes): + _val = val.decode() # scipy.io.netcdf + else: + _val = val + + attrs[_dkey] = _val - attrs = OrderedDict(var.__dict__) dimnames = var.dimensions[-data.ndim:] - lat_coord, lon_coord, time_coord = _get_coord_names(wrfnc, varname) + lat_coord = lon_coord = time_coord = None + + try: + if dimnames[-2] == "south_north" and dimnames[-1] == "west_east": + lat_coord, lon_coord, time_coord = _get_coord_names(wrfnc, varname) + except IndexError: + pass + coords = OrderedDict() @@ -1288,10 +1354,9 @@ def _build_data_array(wrfnc, varname, timeidx, is_moving_domain, is_multifile, coords[time_coord] = (lon_coord_dims[0], [time_coord_vals[timeidx]]) - proj_params = get_proj_params(wrfnc) - proj = getproj(**proj_params) - attrs["projection"] = proj - + proj_params = get_proj_params(wrfnc) + proj = getproj(**proj_params) + attrs["projection"] = proj if dimnames[0] == "Time": t = extract_times(wrfnc, timeidx, meta=False, do_xtime=False) @@ -1355,8 +1420,13 @@ def _find_forward(wrfseq, varname, timeidx, is_moving, meta, _key): return _build_data_array(wrfnc, varname, filetimeidx, is_moving, True, _key) else: - result = wrfnc.variables[varname][filetimeidx, :] - return result[np.newaxis, :] # So that nosqueeze works + var = wrfnc.variables[varname] + if len(var.shape) > 1: + result = var[filetimeidx, :] + return result[np.newaxis, :] # So that nosqueeze works + else: + result = var[filetimeidx] + return result[np.newaxis] # So that nosqueeze works else: comboidx += numtimes @@ -1555,7 +1625,10 @@ def _cat_files(wrfseq, varname, timeidx, is_moving, squeeze, meta, _key): startidx = 0 endidx = numtimes - outdata[startidx:endidx, :] = first_var[:] + if first_var.ndim > 1: + outdata[startidx:endidx, :] = first_var[:] + else: + outdata[startidx:endidx] = first_var[:] if xarray_enabled() and meta: latname, lonname, timename = _find_coord_names(first_var.coords) @@ -1614,7 +1687,10 @@ def _cat_files(wrfseq, varname, timeidx, is_moving, squeeze, meta, _key): endidx = startidx + numtimes - outdata[startidx:endidx, :] = vardata[:] + if vardata.ndim > 1: + outdata[startidx:endidx, :] = vardata[:] + else: + outdata[startidx:endidx] = vardata[:] if xarray_enabled() and meta: if timename is not None and not timecached: @@ -1776,7 +1852,8 @@ def _join_files(wrfseq, varname, timeidx, is_moving, meta, _key): if xarray_enabled() and meta: first_var = _build_data_array(wrfnc, varname, ALL_TIMES, is_moving, True, _key) - time_coord = np.full((numfiles, maxtimes), int(NaT), "datetime64[ns]") + time_coord = np.full((numfiles, maxtimes), np.datetime64("NaT"), + "datetime64[ns]") time_coord[file_idx, 0:numtimes] = first_var.coords["Time"][:] else: first_var = wrfnc.variables[varname][:] @@ -1791,8 +1868,11 @@ def _join_files(wrfseq, varname, timeidx, is_moving, meta, _key): outdims += first_var.shape[1:] # For join, always need to start with full masked values - outdata = np.full(outdims, Constants.DEFAULT_FILL, first_var.dtype) - outdata[file_idx, 0:numtimes, :] = first_var[:] + outdata = np.full(outdims, default_fill(first_var.dtype), first_var.dtype) + if first_var.ndim > 1: + outdata[file_idx, 0:numtimes, :] = first_var[:] + else: + outdata[file_idx, 0:numtimes] = first_var[:] # Create the secondary coordinate arrays if xarray_enabled() and meta: @@ -1814,8 +1894,9 @@ def _join_files(wrfseq, varname, timeidx, is_moving, meta, _key): if timename is not None: outxtimes = get_cached_item(_key, timekey) if outxtimes is None: - outxtimes = np.full(outdims[0:2], Constants.DEFAULT_FILL, - first_var.dtype) + outxtimes = np.full(outdims[0:2], + default_fill(first_var.dtype), + first_var.dtype) outxtimes[file_idx, 0:numtimes] = first_var.coords[timename][:] else: timecached = True @@ -1824,8 +1905,9 @@ def _join_files(wrfseq, varname, timeidx, is_moving, meta, _key): if latname is not None: outlats = get_cached_item(_key, latkey) if outlats is None: - outlats = np.full(outcoorddims, Constants.DEFAULT_FILL, - first_var.dtype) + outlats = np.full(outcoorddims, + default_fill(first_var.dtype), + first_var.dtype) outlats[file_idx, 0:numtimes, :] = ( first_var.coords[latname][:]) else: @@ -1834,8 +1916,9 @@ def _join_files(wrfseq, varname, timeidx, is_moving, meta, _key): if lonname is not None: outlons = get_cached_item(_key, lonkey) if outlons is None: - outlons = np.full(outcoorddims, Constants.DEFAULT_FILL, - first_var.dtype) + outlons = np.full(outcoorddims, + default_fill(first_var.dtype), + first_var.dtype) outlons[file_idx, 0:numtimes, :] = ( first_var.coords[lonname][:]) else: @@ -1855,8 +1938,11 @@ def _join_files(wrfseq, varname, timeidx, is_moving, meta, _key): if not multitime: outvar = outvar[np.newaxis, :] - - outdata[file_idx, 0:numtimes, :] = outvar[:] + + if outvar.ndim > 1: + outdata[file_idx, 0:numtimes, :] = outvar[:] + else: + outdata[file_idx, 0:numtimes] = outvar[:] if xarray_enabled() and meta: # For join, the times are a function of fileidx @@ -1885,7 +1971,7 @@ def _join_files(wrfseq, varname, timeidx, is_moving, meta, _key): # then a mask array is needed to flag all the missing arrays with # missing values if file_times_less_than_max: - outdata = np.ma.masked_values(outdata, Constants.DEFAULT_FILL) + outdata = np.ma.masked_values(outdata, default_fill(outdata.dtype)) if xarray_enabled() and meta: # Cache the coords if applicable @@ -1912,8 +1998,8 @@ def _join_files(wrfseq, varname, timeidx, is_moving, meta, _key): outcoords["datetime"] = outdimnames[0:2], time_coord if isinstance(outdata, np.ma.MaskedArray): - outattrs["_FillValue"] = Constants.DEFAULT_FILL - outattrs["missing_value"] = Constants.DEFAULT_FILL + outattrs["_FillValue"] = default_fill(outdata.dtype) + outattrs["missing_value"] = default_fill(outdata.dtype) if timename is not None: outxtimes = outxtimes[:, time_idx_or_slice] @@ -2146,9 +2232,6 @@ def extract_vars(wrfin, timeidx, varnames, method="cat", squeeze=True, negative integer, or :data:`wrf.ALL_TIMES` (an alias for None) to return all times in the file or sequence. The default is 0. - - is_moving (:obj:`bool`): A boolean type that indicates if the - sequence is a moving nest. method (:obj:`str`, optional): The aggregation method to use for sequences. Must be either 'cat' or 'join'. @@ -2359,15 +2442,35 @@ def extract_times(wrfin, timeidx, method="cat", squeeze=True, cache=None, else: wrf_list = wrfin + dt = "datetime64[ns]" if not do_xtime else np.float64 + fill_value = (np.datetime64('NaT') if not do_xtime else + default_fill(np.float64)) + try: if method.lower() == "cat": time_list = [file_time for wrf_file in wrf_list for file_time in _file_times(wrf_file, do_xtime)] + time_arr = np.asarray(time_list, dtype=dt) + elif method.lower() == "join": time_list = [[file_time for file_time in _file_times(wrf_file, do_xtime)] - for wrf_file in wrf_list] + for wrf_file in wrf_list] + + num_rows = len(time_list) + num_cols = len(time_list[0]) + + time_arr = np.full((num_rows, num_cols), fill_value, dtype=dt) + for i,row in enumerate(time_list): + if len(row) == num_cols: + time_arr[i,:] = row[:] + else: + for j,val in enumerate(row): + time_arr[i,j] = val + + time_arr = ma.masked_values(time_arr, fill_value) + else: raise ValueError("invalid method argument '{}'".format(method)) except KeyError: @@ -2381,6 +2484,8 @@ def extract_times(wrfin, timeidx, method="cat", squeeze=True, cache=None, outdimnames = ["Time"] else: outdimnames = ["fileidx", "Time"] + outattrs["missing_value"] = fill_value + outattrs["_FillValue"] = fill_value if not do_xtime: outname = "times" @@ -2393,11 +2498,12 @@ def extract_times(wrfin, timeidx, method="cat", squeeze=True, cache=None, outname = "XTIME" - outarr = DataArray(time_list, name=outname, coords=outcoords, + + outarr = DataArray(time_arr, name=outname, coords=outcoords, dims=outdimnames, attrs=outattrs) else: - outarr = np.asarray(time_list, dtype="datetime64[ns]") + outarr = time_arr if not multitime: return outarr[timeidx] @@ -2583,26 +2689,6 @@ def get_proj_params(wrfin):#, timeidx=0, varname=None): "DX", "DY")) return proj_params -# multitime = is_multi_time_req(timeidx) -# if not multitime: -# time_idx_or_slice = timeidx -# else: -# time_idx_or_slice = slice(None) -# -# if varname is not None: -# if not is_coordvar(varname): -# coord_names = getattr(wrfin.variables[varname], -# "coordinates").split() -# lon_coord = coord_names[0] -# lat_coord = coord_names[1] -# else: -# lat_coord, lon_coord = get_coord_pairs(varname) -# else: -# lat_coord, lon_coord = latlon_coordvars(wrfin.variables) -# -# return (wrfin.variables[lat_coord][time_idx_or_slice,:], -# wrfin.variables[lon_coord][time_idx_or_slice,:], -# proj_params) def from_args(func, argnames, *args, **kwargs): @@ -2936,18 +3022,40 @@ def psafilepath(): return os.path.join(os.path.dirname(__file__), "data", "psadilookup.dat") -def get_id(obj): - """Return the object id. +def get_filepath(obj): + + try: + path = obj.filepath() + except AttributeError: + try: + path = obj.file.path + except: + # Let's make up a filename from the first file time + found = False + times = extract_times(obj, None, meta=False, do_xtime=False) + for t in times: + path = "wrfout_{}".format(str(t)) + found = True + break + + if not found: + raise ValueError("file contains no path information") + + return path + +def get_id(obj, prefix=''): + """Return the cache id. - The object id is used as a caching key for various routines. If the + The cache id is used as a caching key for various routines. If the object type is a mapping, then the result will also be a - mapping of each key to the object id for the value. Otherwise, only the - object id is returned. + mapping of each key to the object id for the value. Args: obj (:obj:`object`): Any object type. + prefix (:obj:`str`): A string to help with recursive calls. + Returns: :obj:`int` or :obj:`dict`: If the *obj* parameter is not a mapping, @@ -2955,12 +3063,18 @@ def get_id(obj): key to the object id for the value is returned. """ + if not is_multi_file(obj): + return hash(prefix + get_filepath(obj)) + + # For sequences, the hashing string will be the list ID and the + # path for the first file in the sequence if not is_mapping(obj): - return id(obj) + _next = next(iter(obj)) + return get_id(_next, prefix + str(id(obj))) # For each key in the mapping, recursively call get_id until # until a non-mapping is found - return {key : get_id(val) for key,val in viewitems(obj)} + return {key : get_id(val, prefix) for key,val in viewitems(obj)} def geo_bounds(var=None, wrfin=None, varname=None, timeidx=0, method="cat", diff --git a/src/wrf/version.py b/src/wrf/version.py index c55bfe3..aebf1b8 100644 --- a/src/wrf/version.py +++ b/src/wrf/version.py @@ -1,2 +1,2 @@ -__version__ = "1.0.5" +__version__ = "1.1.0" diff --git a/test/cachetest.py b/test/cachetest.py index cc7efee..fad5e88 100644 --- a/test/cachetest.py +++ b/test/cachetest.py @@ -2,7 +2,10 @@ from __future__ import (absolute_import, division, print_function, unicode_literals) from threading import Thread -from Queue import Queue +try: + from Queue import Queue +except ImportError: + from queue import Queue from collections import OrderedDict import unittest as ut @@ -62,4 +65,4 @@ class CacheTest(ut.TestCase): if __name__ == "__main__": - ut.main() \ No newline at end of file + ut.main() diff --git a/test/comp_utest.py b/test/comp_utest.py index 846466f..6af64d2 100644 --- a/test/comp_utest.py +++ b/test/comp_utest.py @@ -512,7 +512,7 @@ def get_args(varname, wrfnc, timeidx, method, squeeze): tkel = tk(full_p, full_t) relh = rh(qv, full_p, tkel) - return (full_p, relh) + return (full_p, relh, 0, 97000., 80000., 45000.) class WRFVarsTest(ut.TestCase): @@ -538,7 +538,7 @@ def make_func(varname, wrfnc, timeidx, method, squeeze, meta): if meta: self.assertEqual(result.dims, ref.dims) - + return func diff --git a/test/generator_test.py b/test/generator_test.py new file mode 100644 index 0000000..6a45d89 --- /dev/null +++ b/test/generator_test.py @@ -0,0 +1,17 @@ +from __future__ import (absolute_import, division, print_function, unicode_literals) + +from wrf import getvar +from netCDF4 import Dataset as nc +#ncfile = nc("/Users/ladwig/Documents/wrf_files/wrfout_d01_2016-02-25_18_00_00") +ncfile = nc("/Users/ladwig/Documents/wrf_files/wrfout_d01_2016-10-07_00_00_00") + +def gen_seq(): + wrfseq = [ncfile, ncfile, ncfile] + for wrf in wrfseq: + yield wrf + +p_gen = getvar(gen_seq(), "P", method="join") + +print(p_gen) +del p_gen + diff --git a/test/ipynb/Doc_Examples.ipynb b/test/ipynb/Doc_Examples.ipynb index 1006a79..4b8d794 100644 --- a/test/ipynb/Doc_Examples.ipynb +++ b/test/ipynb/Doc_Examples.ipynb @@ -2,10 +2,7 @@ "cells": [ { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "# Cartopy Examples" ] @@ -13,11 +10,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "%matplotlib inline" @@ -27,9 +20,6 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false, - "deletable": true, - "editable": true, "scrolled": false }, "outputs": [], @@ -115,11 +105,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "%matplotlib inline\n", @@ -203,9 +189,6 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false, - "deletable": true, - "editable": true, "scrolled": false }, "outputs": [], @@ -343,9 +326,6 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false, - "deletable": true, - "editable": true, "scrolled": false }, "outputs": [], @@ -411,11 +391,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "%matplotlib inline\n", @@ -484,11 +460,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "from __future__ import (absolute_import, division, print_function, unicode_literals)\n", @@ -568,11 +540,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "from __future__ import print_function\n", @@ -606,11 +574,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "from __future__ import print_function\n", @@ -631,10 +595,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "# Basemap Examples" ] @@ -642,11 +603,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "\n", @@ -704,11 +661,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "%matplotlib inline\n", @@ -781,11 +734,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "%matplotlib inline\n", @@ -921,11 +870,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "from __future__ import print_function\n", @@ -960,11 +905,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "from __future__ import print_function\n", @@ -986,11 +927,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "# SLP\n", @@ -1041,11 +978,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "from __future__ import print_function\n", @@ -1079,11 +1012,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "from __future__ import print_function\n", @@ -1101,6 +1030,233 @@ "\n", "print (bounds)" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# OpenMP Routines" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import print_function\n", + "\n", + "from wrf import omp_enabled\n", + "\n", + "print(omp_enabled())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import print_function\n", + "\n", + "from wrf import omp_get_num_procs\n", + "\n", + "print(omp_get_num_procs())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import print_function\n", + "\n", + "from wrf import omp_set_num_threads, omp_get_max_threads\n", + "\n", + "omp_set_num_threads(4)\n", + "\n", + "print(omp_get_max_threads())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import print_function\n", + "\n", + "from wrf import omp_set_schedule, omp_get_schedule, OMP_SCHED_GUIDED\n", + "\n", + "omp_set_schedule(OMP_SCHED_GUIDED, 0)\n", + "\n", + "sched, modifier = omp_get_schedule()\n", + "\n", + "print(sched, modifier)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Loop and Fill Technique" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import print_function, division\n", + "\n", + "import numpy as np\n", + "from netCDF4 import Dataset\n", + "from wrf import getvar, ALL_TIMES\n", + "\n", + "filename_list = [\"/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/wrfout_d02_2005-08-28_00:00:00\",\n", + " \"/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/wrfout_d02_2005-08-28_12:00:00\", \n", + " \"/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/wrfout_d02_2005-08-29_00:00:00\"]\n", + "\n", + "# Result shape (hardcoded for this example, modify as necessary)\n", + "result_shape = (9, 29, 96, 96)\n", + "\n", + "# Only need 4-byte floats\n", + "z_final = np.empty(result_shape, np.float32)\n", + "\n", + "# Modify this number if using more than 1 time per file\n", + "times_per_file = 4\n", + "\n", + "for timeidx in xrange(result_shape[0]):\n", + " # Compute the file index and the time index inside the file\n", + " fileidx = timeidx // times_per_file\n", + " file_timeidx = timeidx % times_per_file\n", + "\n", + " f = Dataset(filename_list[fileidx]) \n", + " z = getvar(f, \"z\", file_timeidx)\n", + "\n", + " z_final[timeidx,:] = z[:]\n", + " f.close()\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Using the cache argument" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import print_function\n", + "\n", + "import time\n", + "from netCDF4 import Dataset\n", + "from wrf import getvar, ALL_TIMES, extract_vars\n", + "\n", + "wrf_filenames = [\"/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/wrfout_d02_2005-08-28_00:00:00\",\n", + " \"/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/wrfout_d02_2005-08-28_12:00:00\", \n", + " \"/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/wrfout_d02_2005-08-29_00:00:00\"]\n", + "\n", + "wrfin = [Dataset(x) for x in wrf_filenames]\n", + "\n", + "start = time.time()\n", + "my_cache = extract_vars(wrfin, ALL_TIMES, (\"P\", \"PSFC\", \"PB\", \"PH\", \"PHB\", \"T\", \"QVAPOR\", \n", + " \"HGT\", \"U\", \"V\", \"W\"))\n", + "end = time.time()\n", + "print (\"Time taken to build cache: \", (end-start), \"s\")\n", + "\n", + "vars = (\"avo\", \"eth\", \"cape_2d\", \"cape_3d\", \"ctt\", \"dbz\", \"mdbz\", \n", + " \"geopt\", \"helicity\", \"lat\", \"lon\", \"omg\", \"p\", \"pressure\", \n", + " \"pvo\", \"pw\", \"rh2\", \"rh\", \"slp\", \"ter\", \"td2\", \"td\", \"tc\", \n", + " \"theta\", \"tk\", \"tv\", \"twb\", \"updraft_helicity\", \"ua\", \"va\", \n", + " \"wa\", \"uvmet10\", \"uvmet\", \"z\", \"cfrac\", \"zstag\", \"geopt_stag\")\n", + "\n", + "start = time.time()\n", + "for var in vars:\n", + " v = getvar(wrfin, var, ALL_TIMES)\n", + "end = time.time()\n", + "no_cache_time = (end-start)\n", + "\n", + "print (\"Time taken without variable cache: \", no_cache_time, \"s\")\n", + "\n", + "start = time.time()\n", + "for var in vars:\n", + " v = getvar(wrfin, var, ALL_TIMES, cache=my_cache)\n", + "end = time.time()\n", + "cache_time = (end-start)\n", + "\n", + "print (\"Time taken with variable cache: \", cache_time, \"s\")\n", + "\n", + "improvement = ((no_cache_time-cache_time)/no_cache_time) * 100 \n", + "print (\"The cache decreased computation time by: \", improvement, \"%\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Using the cache argument with OpenMP" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import print_function\n", + "\n", + "import time\n", + "from netCDF4 import Dataset\n", + "from wrf import getvar, ALL_TIMES, extract_vars, omp_set_num_threads, omp_get_num_procs\n", + "\n", + "wrf_filenames = [\"/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/wrfout_d02_2005-08-28_00:00:00\",\n", + " \"/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/wrfout_d02_2005-08-28_12:00:00\", \n", + " \"/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/wrfout_d02_2005-08-29_00:00:00\"]\n", + "\n", + "wrfin = [Dataset(x) for x in wrf_filenames]\n", + "\n", + "start = time.time()\n", + "my_cache = extract_vars(wrfin, ALL_TIMES, (\"P\", \"PSFC\", \"PB\", \"PH\", \"PHB\", \"T\", \"QVAPOR\", \n", + " \"HGT\", \"U\", \"V\", \"W\"))\n", + "end = time.time()\n", + "print (\"Time taken to build cache: \", (end-start), \"s\")\n", + "\n", + "omp_set_num_threads(omp_get_num_procs())\n", + "\n", + "vars = (\"avo\", \"eth\", \"cape_2d\", \"cape_3d\", \"ctt\", \"dbz\", \"mdbz\", \n", + " \"geopt\", \"helicity\", \"lat\", \"lon\", \"omg\", \"p\", \"pressure\", \n", + " \"pvo\", \"pw\", \"rh2\", \"rh\", \"slp\", \"ter\", \"td2\", \"td\", \"tc\", \n", + " \"theta\", \"tk\", \"tv\", \"twb\", \"updraft_helicity\", \"ua\", \"va\", \n", + " \"wa\", \"uvmet10\", \"uvmet\", \"z\", \"cfrac\", \"zstag\", \"geopt_stag\")\n", + "\n", + "start = time.time()\n", + "for var in vars:\n", + " v = getvar(wrfin, var, ALL_TIMES)\n", + "end = time.time()\n", + "no_cache_time = (end-start)\n", + "\n", + "print (\"Time taken without variable cache: \", no_cache_time, \"s\")\n", + "\n", + "start = time.time()\n", + "for var in vars:\n", + " v = getvar(wrfin, var, ALL_TIMES, cache=my_cache)\n", + "end = time.time()\n", + "cache_time = (end-start)\n", + "\n", + "print (\"Time taken with variable cache: \", cache_time, \"s\")\n", + "\n", + "improvement = ((no_cache_time-cache_time)/no_cache_time) * 100 \n", + "print (\"The cache decreased computation time by: \", improvement, \"%\")\n", + "\n", + "omp_set_num_threads(1)" + ] } ], "metadata": { @@ -1119,7 +1275,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", - "version": "2.7.12" + "version": "2.7.13" } }, "nbformat": 4, diff --git a/test/ipynb/WRF_python_demo.ipynb b/test/ipynb/WRF_python_demo.ipynb index 8df9d49..977aaed 100644 --- a/test/ipynb/WRF_python_demo.ipynb +++ b/test/ipynb/WRF_python_demo.ipynb @@ -2,10 +2,7 @@ "cells": [ { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "# 1.0 Basic Variable Extraction" ] @@ -13,11 +10,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "from __future__ import (absolute_import, division, print_function, unicode_literals)\n", @@ -32,9 +25,6 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false, - "deletable": true, - "editable": true, "scrolled": false }, "outputs": [], @@ -45,10 +35,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "### 1.0.1 DataArray attributes: 'dims', 'coords', 'attrs'" ] @@ -56,11 +43,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "print(\"dims: \", p.dims)\n", @@ -71,10 +54,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "### 1.0.2 Removing implicit 'squeeze' behavior to preserve single sized dimensions" ] @@ -82,11 +62,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "p_nosqueeze = getvar(ncfile, \"P\", timeidx=0, squeeze=False)\n", @@ -95,10 +71,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "### 1.0.3 Single element metadata" ] @@ -106,11 +79,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "print (p_nosqueeze[0,0,100,200])\n", @@ -119,10 +88,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "### 1.0.4 Disabling/Enabling xarray" ] @@ -130,11 +96,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "from wrf import disable_xarray, enable_xarray\n", @@ -157,20 +119,14 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "# 2.0 Sequences of Input Files " ] }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "## 2.0.1 Combining via the 'cat' method" ] @@ -178,11 +134,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "from wrf import ALL_TIMES\n", @@ -196,10 +148,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "## 2.0.2 Combining via the 'join' method" ] @@ -207,11 +156,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "p_join = getvar(wrflist, \"P\", timeidx=ALL_TIMES, method=\"join\")\n", @@ -220,10 +165,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "Note how the Time dimension was replaced with the file dimension, due to the 'squeezing' of the Time dimension.\n", "\n", @@ -234,11 +176,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "from wrf import ALL_TIMES\n", @@ -249,10 +187,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "## 2.0.3 Dictionary Sequences" ] @@ -260,11 +195,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "wrf_dict = {\"label1\" : [ncfile, ncfile],\n", @@ -276,10 +207,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "## 2.0.4 Generator Sequences" ] @@ -287,11 +215,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "def gen_seq():\n", @@ -306,10 +230,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "## 2.0.5 Custom Iterable Classes" ] @@ -317,11 +238,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "class FileGen(object):\n", @@ -334,7 +251,7 @@ " return self\n", " \n", " def next(self):\n", - " if self._i >= self._total:\n", + " if self._i > self._total:\n", " raise StopIteration\n", " else:\n", " val = self.ncfile[self._i]\n", @@ -347,7 +264,7 @@ "\n", "obj_gen = FileGen(ncfile, 3)\n", "\n", - "p_obj_gen = getvar(gen_seq(), \"P\", method=\"join\", squeeze=False)\n", + "p_obj_gen = getvar(obj_gen, \"P\", method=\"join\", squeeze=False)\n", "print(p_obj_gen)\n", "\n", "del p_obj_gen\n", @@ -356,10 +273,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "# 3.0 WRF Variable Computational Routines" ] @@ -367,11 +281,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "wrf_vars = [\"avo\", \"eth\", \"cape_2d\", \"cape_3d\", \"ctt\", \"dbz\", \"mdbz\", \n", @@ -390,10 +300,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "(Note all of the NaNs in the above routines which produce missing values (e.g. cape_2d). xarray always converts all masked_array missing values to NaN in order to work with pandas. To get back the original missing values in a numpy masked_array, you need to use the 'to_np' method from wrf.)" ] @@ -401,11 +308,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "from wrf import to_np\n", @@ -417,11 +320,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "keys = [x for x in vard.keys()]\n", @@ -431,20 +330,14 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "## 3.1 Interpolation Routines" ] }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "### 3.1.1 Horizontal Level Interpolation" ] @@ -452,11 +345,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "# 500 MB Heights\n", @@ -472,10 +361,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "### 3.1.2 Vertical Cross Section Interpolation" ] @@ -483,11 +369,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "# Pressure using pivot and angle\n", @@ -515,11 +397,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "# Pressure using pivot and angle\n", @@ -570,11 +448,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "# Pressure using pivot and angle\n", @@ -626,10 +500,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "### 3.1.3 Interpolate 2D Variable to a Line" ] @@ -637,11 +508,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "# T2 using pivot and angle\n", @@ -685,10 +552,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "### 3.1.4 Vertical Coordinate Interpolation" ] @@ -696,11 +560,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "from wrf import vinterp, getvar\n", @@ -782,10 +642,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "## 3.2 Lat/Lon to X/Y Routines" ] @@ -793,11 +650,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "from wrf.latlon import xy_to_ll, ll_to_xy \n", @@ -835,9 +688,7 @@ { "cell_type": "markdown", "metadata": { - "collapsed": true, - "deletable": true, - "editable": true + "collapsed": true }, "source": [ "# 4.0 Plotting with Cartopy" @@ -846,11 +697,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "%matplotlib inline" @@ -859,11 +706,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "# SLP\n", @@ -905,11 +748,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "# SLP\n", @@ -986,11 +825,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "# 500 MB Heights and Winds\n", @@ -1043,9 +878,6 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false, - "deletable": true, - "editable": true, "scrolled": false }, "outputs": [], @@ -1075,11 +907,7 @@ }, { "cell_type": "markdown", - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "# Multi-time Moving Domain Files" ] @@ -1087,11 +915,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "import os\n", @@ -1111,11 +935,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "p = getvar(ncfiles, \"ctt\", timeidx=ALL_TIMES)" @@ -1124,11 +944,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "print (p)\n", @@ -1138,11 +954,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "print (p.attrs[\"projection\"])\n" @@ -1151,11 +963,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "ncfiles[2].variables[\"XTIME\"][:]\n" @@ -1164,11 +972,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "p = getvar(ncfiles, \"P\", timeidx=None, method=\"cat\", meta=True, squeeze=True)\n" @@ -1177,11 +981,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "print (p)" @@ -1190,11 +990,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "print (type(p.coords[\"Time\"]))" @@ -1203,11 +999,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "import datetime\n", @@ -1220,11 +1012,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "wrf_vars = [\"avo\", \"eth\", \"cape_2d\", \"cape_3d\", \"ctt\", \"dbz\", \"mdbz\", \n", @@ -1249,11 +1037,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "import os\n", @@ -1290,11 +1074,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "import os\n", @@ -1335,11 +1115,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "from wrf import getvar\n" @@ -1348,22 +1124,14 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "from wrf.latlon import xy_to_ll, ll_to_xy \n", @@ -1390,11 +1158,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "from glob import glob\n", @@ -1435,11 +1199,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [] } @@ -1460,9 +1220,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", - "version": "2.7.12" + "version": "2.7.13" } }, "nbformat": 4, - "nbformat_minor": 0 + "nbformat_minor": 1 } diff --git a/test/listBug.ncl b/test/listBug.ncl index 49b81f0..8e9745a 100644 --- a/test/listBug.ncl +++ b/test/listBug.ncl @@ -1,6 +1,18 @@ +; Bug1: This segfaults l = NewList("fifo") name = "foo" ListAppend(l, (/name/)) print(l) print(l[0]) name = "bar" + +; Bug2 Variables disappear +a = addfile("/Users/ladwig/Documents/wrf_files/wrfout_d02_2010-06-13_21:00:00.nc", "r") +b := wrf_user_getvar(a, "slp", -1) +c = NewList("fifo") +ListAppend(c, (/b/)) +b := wrf_user_getvar(a, "rh", -1) +ListAppend(c, (/b/)) + +print(c[0]) +print(c[1]) ; Variables start disappearing diff --git a/test/mocktest.py b/test/mocktest.py new file mode 100644 index 0000000..211936f --- /dev/null +++ b/test/mocktest.py @@ -0,0 +1,44 @@ +import sys +import os + +try: + from unittest.mock import MagicMock +except ImportError: + from mock import Mock as MagicMock + +class Mock(MagicMock): + @classmethod + def __getattr__(cls, name): + return Mock() + +MOCK_MODULES = ["numpy", "numpy.ma", "xarray", "cartopy", + "pandas", "matplotlib", "netCDF4", "mpl_toolkits.basemap", + "wrf._wrffortran"] +sys.modules.update((mod_name, Mock()) for mod_name in MOCK_MODULES) + +consts = {"DEFAULT_FILL" : 9.9692099683868690E36, + "DEFAULT_FILL_INT8" : -127, + "DEFAULT_FILL_INT16" : -32767, + "DEFAULT_FILL_INT32" : -2147483647, + "DEFAULT_FILL_INT64" : -9223372036854775806, + "DEFAULT_FILL_FLOAT" : 9.9692099683868690E36, + "DEFAULT_FILL_DOUBLE" : 9.9692099683868690E36, + "fomp_sched_static" : 1, + "fomp_sched_dynamic" : 2, + "fomp_sched_guided" : 3, + "fomp_sched_auto" : 4} + +class MockWrfConstants(object): + def __init__(self): + self.__dict__ = consts + +def mock_asscalar(val): + return float(val) + +sys.modules["wrf._wrffortran"].wrf_constants = MockWrfConstants() +sys.modules["wrf._wrffortran"].omp_constants = MockWrfConstants() + +sys.modules["numpy"].asscalar = mock_asscalar + +import wrf +print (wrf.get_coord_pairs.__doc__) diff --git a/test/test_filevars.py b/test/test_filevars.py new file mode 100644 index 0000000..dc816c3 --- /dev/null +++ b/test/test_filevars.py @@ -0,0 +1,77 @@ +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 getvar, ALL_TIMES + +TEST_DIR = "/Users/ladwig/Documents/wrf_files/wrf_vortex_multi" +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"] +TEST_FILES = [os.path.join(TEST_DIR, x) for x in TEST_FILENAMES] + +# Python 3 +if sys.version_info > (3,): + xrange = range + + +class WRFFileVarsTest(ut.TestCase): + longMessage = True + +def make_test(ncfiles, varname): + def test(self): + #import time + #very_start = time.time() + #start = time.time() + t1 = getvar(ncfiles, varname, 0) + + #end = time.time() + #print ("t1: ", start-end) + #start = time.time() + t2 = getvar(ncfiles, varname, 0, meta=False) + #end = time.time() + #print ("t2: ", start-end) + #start = time.time() + t3 = getvar(ncfiles, varname, ALL_TIMES) + #end = time.time() + #print ("t3: ", start-end) + #start = time.time() + t4 = getvar(ncfiles, varname, ALL_TIMES, meta=False) + #end = time.time() + #print ("t4: ", start-end) + #start = time.time() + t5 = getvar(ncfiles, varname, ALL_TIMES, method="join") + #end = time.time() + #print ("t5: ", start-end) + #start = time.time() + t6 = getvar(ncfiles, varname, ALL_TIMES, method="join", meta=False) + #end = time.time() + #print ("t6: ", start-end) + #start = time.time() + + #print ("Total Time: ", (end-start)) + return test + + +if __name__ == "__main__": + from netCDF4 import Dataset + ncfiles = [Dataset(x) for x in TEST_FILES] + + #import scipy.io + #ncfiles = [scipy.io.netcdf.netcdf_file(x) for x in TEST_FILES] + + file_vars = ncfiles[0].variables.keys() + + ignore_vars = [] + + for var in file_vars: + if var in ignore_vars: + continue + + test_func1 = make_test(ncfiles, var) + setattr(WRFFileVarsTest, 'test_{0}'.format(var), test_func1) + + ut.main() \ No newline at end of file diff --git a/test/test_omp.py b/test/test_omp.py new file mode 100644 index 0000000..ab239c9 --- /dev/null +++ b/test/test_omp.py @@ -0,0 +1,121 @@ +from __future__ import (absolute_import, division, print_function, + unicode_literals) + +import unittest as ut +import numpy.testing as nt + +from wrf import (omp_set_num_threads, omp_get_num_threads, + omp_get_max_threads, omp_get_thread_num, + omp_get_num_procs, omp_in_parallel, + omp_set_dynamic, omp_get_dynamic, omp_set_nested, + omp_get_nested, omp_set_schedule, + omp_get_schedule, omp_get_thread_limit, + omp_set_max_active_levels, + omp_get_max_active_levels, omp_get_level, + omp_get_ancestor_thread_num, omp_get_team_size, + omp_get_active_level, omp_in_final, + omp_init_lock, omp_init_nest_lock, + omp_destroy_lock, omp_destroy_nest_lock, + omp_set_lock, omp_set_nest_lock, + omp_unset_lock, omp_unset_nest_lock, + omp_test_lock, omp_test_nest_lock, + omp_get_wtime, omp_get_wtick) +from wrf import Constants + + +class OmpTest(ut.TestCase): + longMessage = True + + def test_locks(self): + l = omp_init_lock() + omp_set_lock(l) + omp_unset_lock(l) + omp_test_lock(l) + omp_destroy_lock(l) + + nl = omp_init_nest_lock() + omp_set_nest_lock(nl) + omp_unset_nest_lock(nl) + omp_test_nest_lock(nl) + omp_destroy_nest_lock(nl) + + + def test_thread_set(self): + omp_set_num_threads(4) + max_threads = omp_get_max_threads() + self.assertEqual(max_threads, 4) + + num_threads = omp_get_num_threads() + self.assertEqual(num_threads, 1) # Always 1 outside of parallel region + + thread_num = omp_get_thread_num() + self.assertEqual(thread_num, 0) # Always 0 outside of parallel region + num_procs = omp_get_num_procs() + in_parallel = omp_in_parallel() + self.assertFalse(in_parallel) # Always False outside of parallel region + + limit = omp_get_thread_limit() + + + def test_dynamic(self): + omp_set_dynamic(True) + dynamic = omp_get_dynamic() + self.assertTrue(dynamic) + + omp_set_dynamic(False) + dynamic = omp_get_dynamic() + self.assertFalse(dynamic) + + def test_nested(self): + omp_set_nested(True) + nested = omp_get_nested() + self.assertTrue(nested) + + omp_set_nested(False) + nested = omp_get_nested() + self.assertFalse(nested) + + + def test_schedule(self): + omp_set_schedule(Constants.OMP_SCHED_STATIC, 100000) + kind, modifier = omp_get_schedule() + self.assertEqual(kind, Constants.OMP_SCHED_STATIC) + self.assertEqual(modifier, 100000) + + omp_set_schedule(Constants.OMP_SCHED_DYNAMIC, 10000) + kind, modifier = omp_get_schedule() + self.assertEqual(kind, Constants.OMP_SCHED_DYNAMIC) + self.assertEqual(modifier, 10000) + + omp_set_schedule(Constants.OMP_SCHED_GUIDED, 100) + kind, modifier = omp_get_schedule() + self.assertEqual(kind, Constants.OMP_SCHED_GUIDED) + self.assertEqual(modifier, 100) + + omp_set_schedule(Constants.OMP_SCHED_AUTO, 10) + kind, modifier = omp_get_schedule() + self.assertEqual(kind, Constants.OMP_SCHED_AUTO) + self.assertNotEqual(modifier, 10) # The modifier argument is ignored, + # so it will be set to the previous + # value of 100. + + + def test_team_level(self): + omp_set_max_active_levels(10) + active_levels = omp_get_max_active_levels() + self.assertEqual(active_levels, 10) + + level = omp_get_level() + ancestor_thread = omp_get_ancestor_thread_num(level) + team_size = omp_get_team_size(level) + active_level = omp_get_active_level() + in_final = omp_in_final() + + + def test_time(self): + wtime = omp_get_wtime() + wtick = omp_get_wtick() + +if __name__ == "__main__": + ut.main() + \ No newline at end of file diff --git a/test/utests.py b/test/utests.py index 234239a..9c9ba12 100644 --- a/test/utests.py +++ b/test/utests.py @@ -93,57 +93,71 @@ def make_test(varname, wrf_in, referent, multi=False, repeat=3, pynio=False): multiproduct = varname in ("uvmet", "uvmet10", "cape_2d", "cape_3d", "cfrac") + # These varnames don't have NCL functions to test against + ignore_referent = ("zstag", "geopt_stag") - 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 not in ignore_referent: + if not multi: + ref_vals = refnc.variables[varname][:] else: - ref_vals = ma.asarray(np.zeros(new_dims, data.dtype)) - - for i in xrange(repeat): - if not multiproduct: - ref_vals[i,:] = data[:] + 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 masked: - ref_vals.mask[i,:] = data.mask[:] - + if not masked: + ref_vals = np.zeros(new_dims, data.dtype) else: - for prod in xrange(ref_vals.shape[0]): - ref_vals[prod,i,:] = data[prod,:] - + 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[prod,i,:] = data.mask[prod,:] + 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,:] if (varname == "tc"): my_vals = getvar(in_wrfnc, "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 == "cfrac"): + # Change the vert_type to height_agl when NCL gets updated. + my_vals = getvar(in_wrfnc, "cfrac", timeidx=timeidx, + vert_type="pres") + 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) tol = .5/100.0 atol = 0 # NCL uses different constants and doesn't use same # handrolled virtual temp in method - nt.assert_allclose(to_np(my_vals), ref_vals, tol, atol) + try: + nt.assert_allclose(to_np(my_vals), ref_vals, tol, atol) + except AssertionError: + print (np.amax(np.abs(to_np(my_vals) - ref_vals))) + raise elif (varname == "cape_2d"): cape_2d = getvar(in_wrfnc, varname, timeidx=timeidx) tol = 0/100. @@ -165,12 +179,20 @@ 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) + # 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) tol = 2/100. atol = 0.1 #print (np.amax(np.abs(to_np(my_vals) - ref_vals))) - nt.assert_allclose(to_np(my_vals), ref_vals, tol, atol) + try: + nt.assert_allclose(to_np(my_vals), ref_vals, tol, atol) + except: + print (np.amax(np.abs(to_np(my_vals) - ref_vals))) + raise return test @@ -259,23 +281,28 @@ def make_interp_test(varname, wrf_in, referent, multi=False, p = getvar(in_wrfnc, "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) ht_cross = vertcross(hts, p, pivot_point=pivot_point, angle=90.) - - nt.assert_allclose(to_np(ht_cross), ref_ht_cross, rtol=.01) + + # 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) # Test opposite p_cross1 = vertcross(p,hts,pivot_point=pivot_point, angle=90.0) - - nt.assert_allclose(to_np(p_cross1), + + nt.assert_allclose(to_np(p_cross1)[...,0:-1], 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) - + + p_cross2 = vertcross(p,hts,start_point=start_point, end_point=end_point) - + nt.assert_allclose(to_np(p_cross1), to_np(p_cross2)) @@ -286,9 +313,12 @@ def make_interp_test(varname, wrf_in, referent, multi=False, t2 = getvar(in_wrfnc, "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) t2_line1 = interpline(t2, pivot_point=pivot_point, angle=90.0) - nt.assert_allclose(to_np(t2_line1), ref_t2_line) + # Note: After NCL is fixed, remove the slice. + nt.assert_allclose(to_np(t2_line1)[...,0:-1], ref_t2_line) # Test point to point start_point = CoordPair(0, t2.shape[-2]/2) @@ -599,12 +629,18 @@ class WRFLatLonTest(ut.TestCase): if __name__ == "__main__": + from wrf import (omp_set_num_threads, omp_set_schedule, omp_get_schedule, + omp_set_dynamic, OMP_SCHED_STATIC) + omp_set_num_threads(8) + omp_set_schedule(OMP_SCHED_STATIC, 0) + omp_set_dynamic(False) + ignore_vars = [] # Not testable yet 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", "zstag", "geopt_stag"] interp_methods = ["interplevel", "vertcross", "interpline", "vinterp"] latlon_tests = ["xy", "ll"] diff --git a/test/varcache.py b/test/varcache.py new file mode 100644 index 0000000..1e0fe3d --- /dev/null +++ b/test/varcache.py @@ -0,0 +1,36 @@ +from __future__ import print_function + +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"] + +wrfin = [Dataset(x) for x in wrf_filenames] + +my_cache = extract_vars(wrfin, ALL_TIMES, ("P", "PB", "PH", "PHB", "T", "QVAPOR", "HGT", "U", "V", "W", "PSFC")) + +start = time.time() +for var in ("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", "zstag", "geopt_stag"): + v = getvar(wrfin, var, ALL_TIMES) +end = time.time() + +print ("Time taken without variable cache: ", (end-start)) + +start = time.time() +for var in ("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", "zstag", "geopt_stag"): + v = getvar(wrfin, var, ALL_TIMES, cache=my_cache) +end = time.time() + +print ("Time taken with variable cache: ", (end-start)) +