Browse Source

Added the jinja patch for 1.21 conda-build so it works with numpy distutils. Added code for the threadlocal caching. Added a cached test. Modified cloudfrac to match what has been committed to NCL. Modified the NCL test script to add cloud fraction.

main
Bill Ladwig 9 years ago
parent
commit
1f731e8b25
  1. 9
      conda_recipe/meta.yaml
  2. 0
      fortran/wrf_cloud_fracf.f90
  3. 22
      fortran/wrffortran.pyf
  4. 152
      ncl_reference/WRFUserARW.ncl
  5. 183
      ncl_reference/jinja_context.py
  6. 4
      setup.py
  7. 52
      src/wrf/cache.py
  8. 76
      src/wrf/config.py
  9. 14
      src/wrf/metadecorators.py
  10. 3
      src/wrf/routines.py
  11. 49
      src/wrf/util.py
  12. 65
      test/cachetest.py
  13. 2
      test/ncl_get_var.ncl
  14. 47
      test/utests.py

9
conda_recipe/meta.yaml

@ -1,13 +1,16 @@
# For the version to work, the patch in ncl_reference needs to be applied
# to the installed conda-build. Otherwise, it needs to be set manually until
# continuum can release a fix.
package: package:
name: wrf-python name: wrf-python
version: "0.0.1" version: {{ load_setup_py_data().version }}
source: source:
git_url: git@github.com:NCAR/wrf-python.git git_url: git@github.com:NCAR/wrf-python.git
git_rev: prebeta.0.1
build: build:
number: 5 number: 1
detect_binary_files_with_prefix: true detect_binary_files_with_prefix: true
requirements: requirements:

0
fortran/cloud_fracf.f90 → fortran/wrf_cloud_fracf.f90

22
fortran/wrffortran.pyf

@ -22,17 +22,6 @@ python module _wrffortran ! in
real(kind=8) dimension(nx,ny,nz),intent(inout),depend(nx,ny,nz) :: tem1 real(kind=8) dimension(nx,ny,nz),intent(inout),depend(nx,ny,nz) :: tem1
real(kind=8) dimension(nx,ny,nz),intent(inout),depend(nx,ny,nz) :: tem2 real(kind=8) dimension(nx,ny,nz),intent(inout),depend(nx,ny,nz) :: tem2
end subroutine dcalcuh end subroutine dcalcuh
subroutine dcloudfrac(pres,rh,lowc,midc,highc,nz,ns,ew) ! in :_wrffortran:cloud_fracf.f90
threadsafe
real(kind=8) dimension(ew,ns,nz),intent(in) :: pres
real(kind=8) dimension(ew,ns,nz),intent(in),depend(ew,ns,nz) :: rh
real(kind=8) dimension(ew,ns),intent(out,in),depend(ew,ns) :: lowc
real(kind=8) dimension(ew,ns),intent(out,in),depend(ew,ns) :: midc
real(kind=8) dimension(ew,ns),intent(out,in),depend(ew,ns) :: highc
integer, optional,check(shape(pres,2)==nz),depend(pres) :: nz=shape(pres,2)
integer, optional,check(shape(pres,1)==ns),depend(pres) :: ns=shape(pres,1)
integer, optional,check(shape(pres,0)==ew),depend(pres) :: ew=shape(pres,0)
end subroutine dcloudfrac
subroutine deqthecalc(qvp,tmk,prs,eth,miy,mjx,mkzh) ! in :_wrffortran:eqthecalc.f90 subroutine deqthecalc(qvp,tmk,prs,eth,miy,mjx,mkzh) ! in :_wrffortran:eqthecalc.f90
threadsafe threadsafe
use wrf_constants, only: tlclc2,tlclc3,tlclc1,tlclc4,eps,thtecon3,gammamd,thtecon1,gamma,thtecon2 use wrf_constants, only: tlclc2,tlclc3,tlclc1,tlclc4,eps,thtecon3,gammamd,thtecon1,gamma,thtecon2
@ -104,6 +93,17 @@ python module _wrffortran ! in
integer intent(inout) :: errstat integer intent(inout) :: errstat
character*(*) intent(inout) :: errmsg character*(*) intent(inout) :: errmsg
end subroutine dcapecalc3d end subroutine dcapecalc3d
subroutine dcloudfrac(pres,rh,lowc,midc,highc,nz,ns,ew) ! in :_wrffortran:wrf_cloud_fracf.f90
threadsafe
real(kind=8) dimension(ew,ns,nz),intent(in) :: pres
real(kind=8) dimension(ew,ns,nz),intent(in),depend(ew,ns,nz) :: rh
real(kind=8) dimension(ew,ns),intent(out,in),depend(ew,ns) :: lowc
real(kind=8) dimension(ew,ns),intent(out,in),depend(ew,ns) :: midc
real(kind=8) dimension(ew,ns),intent(out,in),depend(ew,ns) :: highc
integer, optional,check(shape(pres,2)==nz),depend(pres) :: nz=shape(pres,2)
integer, optional,check(shape(pres,1)==ns),depend(pres) :: ns=shape(pres,1)
integer, optional,check(shape(pres,0)==ew),depend(pres) :: ew=shape(pres,0)
end subroutine dcloudfrac
module wrf_constants ! in :_wrffortran:wrf_constants.f90 module wrf_constants ! in :_wrffortran:wrf_constants.f90
real(kind=8), parameter,optional :: wrf_earth_radius=6370000.d0 real(kind=8), parameter,optional :: wrf_earth_radius=6370000.d0
real(kind=8), parameter,optional :: rhowat=1000.d0 real(kind=8), parameter,optional :: rhowat=1000.d0

152
ncl_reference/WRFUserARW.ncl

@ -894,7 +894,7 @@ begin
uvmet(1,:,:) = (/v(:,:)/) uvmet(1,:,:) = (/v(:,:)/)
end if end if
copy_VarAtts_except(u,uvmet,(/"description","units"/)) copy_VarAtts_except(u,uvmet,(/"description","units"/))
uvmet@description = " u,v met velocity" uvmet@description = "u,v met velocity"
uvmet!0 = "u_v" uvmet!0 = "u_v"
do n=0,dimsizes(dims)-1 ; Copy dimension names do n=0,dimsizes(dims)-1 ; Copy dimension names
if(isdimnamed(u,n)) if(isdimnamed(u,n))
@ -950,7 +950,7 @@ begin
end if end if
if( (variable .eq. "uvmet10") ) then if( (variable .eq. "uvmet10") ) then
uvmet@description = " u10,v10 met velocity" uvmet@description = "u10,v10 met velocity"
end if end if
return(uvmet) return(uvmet)
@ -1377,8 +1377,69 @@ begin
return(cape) return(cape)
end if end if
if( any( variable .eq. "cfrac" )) then
print("calculating wrf cloud fraction")
if(typeof(file_handle).eq."file") then
ISFILE = True
nc_file = file_handle
else if(typeof(file_handle).eq."list") then
ISFILE = False
nc_file = file_handle[0]
else
print("wrf_user_getvar: error: the first argument must be a file or a list of files opened with addfile or addfiles")
return
end if
end if
time_in = time
if ( time .eq. -1 ) then
if(ISFILE) then
P = nc_file->P
PB = nc_file->PB
rh = wrf_user_getvar(nc_file,"rh",-1)
else
P = file_handle[:]->P
PB = file_handle[:]->PB
rh = wrf_user_getvar(file_handle,"rh",-1)
end if
else
if(ISFILE) then
P = nc_file->P(time_in,:,:,:)
PB = nc_file->PB(time_in,:,:,:)
rh = wrf_user_getvar(file_handle,"rh",time_in)
else
P = file_handle[:]->P(time_in,:,:,:)
PB = file_handle[:]->PB(time_in,:,:,:)
rh = wrf_user_getvar(file_handle,"rh",time_in)
end if
end if
pres = (P + PB)
thedims = dimsizes(pres)
if(time .eq. -1) then
nt = thedims(0)
nz = thedims(1)
ns = thedims(2)
ew = thedims(3)
else
nt = 1
nz = thedims(0)
ns = thedims(1)
ew = thedims(2)
end if
out_array = wrf_cloud_fraction(pres,rh)
out_array@units = "%"
out_array@description = "Low, Mid, High Clouds"
return(out_array)
end if ;variable is cfrac
if( any( variable .eq. (/"pw"/) ) ) then if( any( variable .eq. (/"pw"/) ) ) then
;Precipitable Water ;Precipitable Water
print("calculating precipitable water") print("calculating precipitable water")
@ -2172,6 +2233,7 @@ begin
field_type = str_lower(get_res_value_keep(opts,"field_type","none")) field_type = str_lower(get_res_value_keep(opts,"field_type","none"))
log_of_Pressure = get_res_value_keep(opts,"logP",False) log_of_Pressure = get_res_value_keep(opts,"logP",False)
debug = get_res_value_keep(opts,"debug",False) debug = get_res_value_keep(opts,"debug",False)
timeidx = get_res_value_keep(opts,"time",-1)
valid_field_types = (/"none","pressure","pres","p","z","t","ght"/) valid_field_types = (/"none","pressure","pres","p","z","t","ght"/)
if(.not.any(field_type.eq.valid_field_types)) then if(.not.any(field_type.eq.valid_field_types)) then
@ -2237,29 +2299,23 @@ begin
;We will need some basic fields for the interpolation ;We will need some basic fields for the interpolation
;regardless of the field requested. Get all time periods ;regardless of the field requested. Get all time periods
;of the fields. ;of the fields.
P = _get_wrf_var(file_handle,"P",-1) + _get_wrf_var(file_handle,"PB",-1) P = _get_wrf_var(file_handle,"P",timeidx) + _get_wrf_var(file_handle,"PB",timeidx)
qvp = _get_wrf_var(file_handle,"QVAPOR",-1) qvp = _get_wrf_var(file_handle,"QVAPOR",timeidx)
terht = _get_wrf_var(file_handle,"HGT",-1) terht = _get_wrf_var(file_handle,"HGT",timeidx)
sfp = _get_wrf_var(file_handle,"PSFC",-1) * 0.01 sfp = _get_wrf_var(file_handle,"PSFC",timeidx) * 0.01
Pdims = dimsizes(P) Pdims = dimsizes(P)
if(ISFILE) then ght = wrf_user_getvar(file_handle,"height",timeidx)
ght = wrf_user_getvar(nc_file,"height",-1) tk = wrf_user_getvar(file_handle,"tk",timeidx)
tk = wrf_user_getvar(nc_file,"tk",-1) smsfp = sfp
else wrf_smooth_2d(smsfp,3)
tmpz = file_handle[:]->PH
PHB = file_handle[:]->PHB
tmpz = (tmpz + PHB)/9.81
ght = wrf_user_unstagger(tmpz,"Z")
T = file_handle[:]->T
T = T + 300.
tk = wrf_tk( P , T )
end if
smsfp = sfp
wrf_smooth_2d(smsfp,3)
;Initialize an array for the vertical coordinate ;Initialize an array for the vertical coordinate
ntimes = Pdims(0) if (timeidx .lt. 0) then
ntimes = Pdims(0)
else
ntimes = 1
end if
;Get the vertical coordinate type ;Get the vertical coordinate type
vcor = 0 vcor = 0
@ -2276,14 +2332,26 @@ begin
if(vert_coordinate .eq. "ght_agl") then if(vert_coordinate .eq. "ght_agl") then
vcor = 3 vcor = 3
rtemp = new( (/nz,ns,ew/),float)
vcord_array = new((/ntimes,nz,ns,ew/),float) if (timeidx .lt. 0) then
do it = 0, ntimes - 1 rtemp = new( (/nz,ns,ew/),float)
do ilev = 0,nz-1 vcord_array = new((/ntimes,nz,ns,ew/),float)
rtemp(ilev,:,:) = ght(it,ilev,:,:) - terht(0,:,:) do it = 0, ntimes - 1
end do do ilev = 0,nz-1
vcord_array(it,:,:,:) = exp(-rtemp/sclht) rtemp(ilev,:,:) = ght(it,ilev,:,:) - terht(it,:,:)
end do end do
vcord_array(it,:,:,:) = exp(-rtemp/sclht)
end do
else
rtemp = new((/nz,ns,ew/),float)
vcord_array = new((/nz,ns,ew/), float)
do ilev = 0,nz-1
rtemp(ilev,:,:) = ght(ilev,:,:) - terht(:,:)
end do
vcord_array(:,:,:) = exp(-rtemp/sclht)
end if
delete(rtemp) delete(rtemp)
end if end if
@ -2291,14 +2359,9 @@ begin
vcor = 4 vcor = 4
idir = 1 idir = 1
icorsw = 0 icorsw = 0
delta = 0.01 delta = 0.01
if(ISFILE) then coriolis = _get_wrf_var(file_handle, "F", timeidx)
coriolis = nc_file->F(0,:,:) theta = wrf_user_getvar(file_handle,"theta",timeidx)
theta = wrf_user_getvar(nc_file,"theta",-1)
else
coriolis = file_handle[0]->F(0,:,:)
theta = T
end if
preshPa = P * 0.01 preshPa = P * 0.01
vcord_array = wrf_monotonic(theta,preshPa,coriolis,idir,delta,icorsw) vcord_array = wrf_monotonic(theta,preshPa,coriolis,idir,delta,icorsw)
; ;
@ -2313,13 +2376,8 @@ begin
icorsw = 0 icorsw = 0
idir = 1 idir = 1
delta = 0.01 delta = 0.01
if(ISFILE) then coriolis = _get_wrf_var(file_handle, "F", timeidx)
coriolis = nc_file->F(0,:,:) eqpot = wrf_user_getvar(file_handle,"eth",timeidx)
eqpot = wrf_user_getvar(nc_file,"eth",-1)
else
coriolis = file_handle[0]->F(0,:,:)
eqpot = wrf_eth ( qvp, tk, P )
end if
preshPa = P * 0.01 preshPa = P * 0.01
vcord_array = wrf_monotonic(eqpot,preshPa,coriolis,idir,delta,icorsw) vcord_array = wrf_monotonic(eqpot,preshPa,coriolis,idir,delta,icorsw)
@ -2331,8 +2389,8 @@ begin
if(debug) then if(debug) then
print("icase = " + icase + " extrap = " + extrap + " vcor = " + vcor + " logP = " + logP) print("icase = " + icase + " extrap = " + extrap + " vcor = " + vcor + " logP = " + logP)
end if end if
field_out = wrf_vintrp(field,P,tk,qvp,ght,terht(0,:,:),sfp,smsfp,\ field_out = wrf_vintrp(field,P,tk,qvp,ght,terht,sfp,smsfp,\
vcord_array,interp_levels,icase,extrap,vcor,logP) vcord_array,interp_levels,icase,extrap,vcor,logP)
; Add metadata to return array ; Add metadata to return array
@ -2341,7 +2399,7 @@ begin
; Add new levels as a coordinate array ; Add new levels as a coordinate array
lev_field = num_field_dims-3 lev_field = num_field_dims-3
field_out!lev_field = "interp_levels" field_out!lev_field = "interp_levels"
field_out&$field_out!lev_field$ = interp_levels(::-1) field_out&$field_out!lev_field$ = interp_levels(:)
field_out@vert_interp_type = vert_coordinate field_out@vert_interp_type = vert_coordinate
return(field_out) return(field_out)

183
ncl_reference/jinja_context.py

@ -0,0 +1,183 @@
'''
Created on Jan 16, 2014
@author: sean
'''
from __future__ import absolute_import, division, print_function
from functools import partial
import json
import logging
import os
import sys
import jinja2
from .conda_interface import PY3
from .environ import get_dict as get_environ
from .metadata import select_lines, ns_cfg
from .source import WORK_DIR
log = logging.getLogger(__file__)
class UndefinedNeverFail(jinja2.Undefined):
"""
A class for Undefined jinja variables.
This is even less strict than the default jinja2.Undefined class,
because it permits things like {{ MY_UNDEFINED_VAR[:2] }} and
{{ MY_UNDEFINED_VAR|int }}. This can mask lots of errors in jinja templates, so it
should only be used for a first-pass parse, when you plan on running a 'strict'
second pass later.
"""
all_undefined_names = []
def __init__(self, hint=None, obj=jinja2.runtime.missing, name=None,
exc=jinja2.exceptions.UndefinedError):
UndefinedNeverFail.all_undefined_names.append(name)
jinja2.Undefined.__init__(self, hint, obj, name, exc)
__add__ = __radd__ = __mul__ = __rmul__ = __div__ = __rdiv__ = \
__truediv__ = __rtruediv__ = __floordiv__ = __rfloordiv__ = \
__mod__ = __rmod__ = __pos__ = __neg__ = __call__ = \
__getitem__ = __lt__ = __le__ = __gt__ = __ge__ = \
__complex__ = __pow__ = __rpow__ = \
lambda self, *args, **kwargs: UndefinedNeverFail(hint=self._undefined_hint,
obj=self._undefined_obj,
name=self._undefined_name,
exc=self._undefined_exception)
__str__ = __repr__ = \
lambda *args, **kwargs: u''
__int__ = lambda _: 0
__float__ = lambda _: 0.0
def __getattr__(self, k):
try:
return object.__getattr__(self, k)
except AttributeError:
return UndefinedNeverFail(hint=self._undefined_hint,
obj=self._undefined_obj,
name=self._undefined_name + '.' + k,
exc=self._undefined_exception)
class FilteredLoader(jinja2.BaseLoader):
"""
A pass-through for the given loader, except that the loaded source is
filtered according to any metadata selectors in the source text.
"""
def __init__(self, unfiltered_loader):
self._unfiltered_loader = unfiltered_loader
self.list_templates = unfiltered_loader.list_templates
def get_source(self, environment, template):
contents, filename, uptodate = self._unfiltered_loader.get_source(environment,
template)
return select_lines(contents, ns_cfg()), filename, uptodate
def load_setup_py_data(setup_file='setup.py', from_recipe_dir=False, recipe_dir=None,
unload_modules=None, fail_on_error=False):
_setuptools_data = {}
def setup(**kw):
_setuptools_data.update(kw)
import setuptools
import distutils.core
try:
import numpy.distutils.core
except ImportError:
do_numpy = False
else:
do_numpy = True
cd_to_work = False
if from_recipe_dir and recipe_dir:
setup_file = os.path.abspath(os.path.join(recipe_dir, setup_file))
elif os.path.exists(WORK_DIR):
cd_to_work = True
cwd = os.getcwd()
os.chdir(WORK_DIR)
if not os.path.isabs(setup_file):
setup_file = os.path.join(WORK_DIR, setup_file)
# this is very important - or else if versioneer or otherwise is in the start folder,
# things will pick up the wrong versioneer/whatever!
sys.path.insert(0, WORK_DIR)
else:
log.debug("Did not find setup.py file in manually specified location, and source "
"not downloaded yet.")
return {}
# Patch setuptools, distutils
setuptools_setup = setuptools.setup
distutils_setup = distutils.core.setup
setuptools.setup = distutils.core.setup = setup
if do_numpy:
numpy_setup = numpy.distutils.core.setup
numpy.distutils.core.setup = setup
ns = {
'__name__': '__main__',
'__doc__': None,
'__file__': setup_file,
}
try:
code = compile(open(setup_file).read(), setup_file, 'exec', dont_inherit=1)
exec(code, ns, ns)
distutils.core.setup = distutils_setup
setuptools.setup = setuptools_setup
if do_numpy:
numpy.distutils.core.setup = numpy_setup
# this happens if setup.py is used in load_setup_py_data, but source is not yet downloaded
except:
raise
finally:
if cd_to_work:
os.chdir(cwd)
del sys.path[0]
return _setuptools_data
def load_setuptools(setup_file='setup.py', from_recipe_dir=False, recipe_dir=None,
unload_modules=None, fail_on_error=False):
log.warn("Deprecation notice: the load_setuptools function has been renamed to "
"load_setup_py_data. load_setuptools will be removed in a future release.")
return load_setup_py_data(setup_file=setup_file, from_recipe_dir=from_recipe_dir,
recipe_dir=recipe_dir, unload_modules=unload_modules,
fail_on_error=fail_on_error)
def load_npm():
# json module expects bytes in Python 2 and str in Python 3.
mode_dict = {'mode': 'r', 'encoding': 'utf-8'} if PY3 else {'mode': 'rb'}
with open('package.json', **mode_dict) as pkg:
return json.load(pkg)
def context_processor(initial_metadata, recipe_dir):
"""
Return a dictionary to use as context for jinja templates.
initial_metadata: Augment the context with values from this MetaData object.
Used to bootstrap metadata contents via multiple parsing passes.
"""
ctx = get_environ(m=initial_metadata)
environ = dict(os.environ)
environ.update(get_environ(m=initial_metadata))
ctx.update(
load_setup_py_data=partial(load_setup_py_data, recipe_dir=recipe_dir),
# maintain old alias for backwards compatibility:
load_setuptools=partial(load_setuptools, recipe_dir=recipe_dir),
load_npm=load_npm,
environ=environ)
return ctx

4
setup.py

@ -8,7 +8,7 @@ ext1 = numpy.distutils.core.Extension(
"fortran/wrf_testfunc.f90", "fortran/wrf_testfunc.f90",
"fortran/wrf_user.f90", "fortran/wrf_user.f90",
"fortran/rip_cape.f90", "fortran/rip_cape.f90",
"fortran/cloud_fracf.f90", "fortran/wrf_cloud_fracf.f90",
"fortran/wrf_fctt.f90", "fortran/wrf_fctt.f90",
"fortran/wrf_user_dbz.f90", "fortran/wrf_user_dbz.f90",
"fortran/wrf_relhl.f90", "fortran/wrf_relhl.f90",
@ -22,7 +22,7 @@ ext1 = numpy.distutils.core.Extension(
"fortran/wrffortran.pyf"] "fortran/wrffortran.pyf"]
) )
with open('src/wrf/version.py') as f: with open("src/wrf/version.py") as f:
exec(f.read()) exec(f.read())
requirements = [ requirements = [

52
src/wrf/cache.py

@ -0,0 +1,52 @@
from __future__ import (absolute_import, division, print_function,
unicode_literals)
from threading import local
from collections import OrderedDict
from .config import get_cache_size
_local_storage = local()
def cache_item(key, product, value):
global _local_storage
try:
cache = _local_storage.cache
except AttributeError:
_local_storage.cache = OrderedDict()
cache = _local_storage.cache
try:
prod_dict = cache[key]
except KeyError:
if len(cache) >= get_cache_size():
cache.popitem(last=False) # Remove the oldest dataset
cache[key] = OrderedDict()
prod_dict = cache[key]
cache[key][product] = value
def get_cached_item(key, product):
cache = getattr(_local_storage, "cache", None)
if cache is None:
return None
prod_dict = cache.get(key, None)
if prod_dict is None:
return None
return prod_dict.get(product, None)
def _get_cache():
return getattr(_local_storage, "cache", None)

76
src/wrf/config.py

@ -4,75 +4,87 @@ from __future__ import (absolute_import, division, print_function,
try: try:
from xarray import DataArray from xarray import DataArray
except ImportError: except ImportError:
_XARRAY_ENABLED = False _xarray_enabled = False
else: else:
_XARRAY_ENABLED = True _xarray_enabled = True
try: try:
from cartopy import crs from cartopy import crs
except ImportError: except ImportError:
_CARTOPY_ENABLED = False _cartopy_enabled = False
else: else:
_CARTOPY_ENABLED = True _cartopy_enabled = True
try: try:
from mpl_toolkits.basemap import Basemap from mpl_toolkits.basemap import Basemap
except ImportError: except ImportError:
_BASEMAP_ENABLED = False _basemap_enabled = False
else: else:
_BASEMAP_ENABLED = True _basemap_enabled = True
try: try:
from Ngl import Resources from Ngl import Resources
except ImportError: except ImportError:
_PYNGL_ENABLED = False _pyngl_enabled = False
else: else:
_PYNGL_ENABLED = True _pyngl_enabled = True
_cache_size = 5
def xarray_enabled(): def xarray_enabled():
global _XARRAY_ENABLED global _xarray_enabled
return _XARRAY_ENABLED return _xarray_enabled
def disable_xarray(): def disable_xarray():
global _XARRAY_ENABLED global _xarray_enabled
_XARRAY_ENABLED = False _xarray_enabled = False
def enable_xarray(): def enable_xarray():
global _XARRAY_ENABLED global _xarray_enabled
_XARRAY_ENABLED = True _xarray_enabled = True
def cartopy_enabled(): def cartopy_enabled():
global _CARTOPY_ENABLED global _cartopy_enabled
return _CARTOPY_ENABLED return _cartopy_enabled
def enable_cartopy(): def enable_cartopy():
global _CARTOPY_ENABLED global _cartopy_enabled
_CARTOPY_ENABLED = True _cartopy_enabled = True
def disable_cartopy(): def disable_cartopy():
global _CARTOPY_ENABLED global _cartopy_enabled
_CARTOPY_ENABLED = True _cartopy_enabled = True
def basemap_enabled(): def basemap_enabled():
global _BASEMAP_ENABLED global _basemap_enabled
return _BASEMAP_ENABLED return _basemap_enabled
def enable_basemap(): def enable_basemap():
global _BASEMAP_ENABLED global _basemap_enabled
_BASEMAP_ENABLED = True _basemap_enabled = True
def disable_basemap(): def disable_basemap():
global _BASEMAP_ENABLED global _basemap_enabled
_BASEMAP_ENABLED = True _basemap_enabled = True
def pyngl_enabled(): def pyngl_enabled():
global _PYNGL_ENABLED global _pyngl_enabled
return _PYNGL_ENABLED return _pyngl_enabled
def enable_pyngl(): def enable_pyngl():
global _PYNGL_ENABLED global _pyngl_enabled
_PYNGL_ENABLED = True _pyngl_enabled = True
def disable_pyngl(): def disable_pyngl():
global _PYNGL_ENABLED global _pyngl_enabled
_PYNGL_ENABLED = True _pyngl_enabled = True
def set_cache_size(size):
global _cache_size
_cache_size = size
def get_cache_size():
return int(_cache_size)

14
src/wrf/metadecorators.py

@ -369,8 +369,8 @@ def set_cloudfrac_metadata():
outdimnames[-2:] = copy_var.dims[-2:] outdimnames[-2:] = copy_var.dims[-2:]
# Left dims # Left dims
outdimnames[1:-2] = copy_var.dims[0:-3] outdimnames[1:-2] = copy_var.dims[0:-3]
outdimnames[0] = "low_med_high" outdimnames[0] = "low_mid_high"
outattrs["description"] = "low, med, high clouds" outattrs["description"] = "low, mid, high clouds"
outattrs["MemoryOrder"] = "XY" outattrs["MemoryOrder"] = "XY"
outattrs["units"] = "%" outattrs["units"] = "%"
outname = "cloudfrac" outname = "cloudfrac"
@ -388,7 +388,7 @@ def set_cloudfrac_metadata():
elif key == "Time": elif key == "Time":
outcoords[key] = npvalues(dataarray) outcoords[key] = npvalues(dataarray)
outcoords["low_med_high"] = ["low", "med", "high"] outcoords["low_mid_high"] = ["low", "mid", "high"]
return DataArray(result, name=outname, coords=outcoords, return DataArray(result, name=outname, coords=outcoords,
dims=outdimnames, attrs=outattrs) dims=outdimnames, attrs=outattrs)
@ -1342,7 +1342,7 @@ def set_cloudfrac_alg_metadata(copyarg="pres"):
outattrs = OrderedDict() outattrs = OrderedDict()
outname = "cloudfrac" outname = "cloudfrac"
outattrs["description"] = "low, med, high clouds" outattrs["description"] = "low, mid, high clouds"
outattrs["units"] = "%" outattrs["units"] = "%"
outattrs["MemoryOrder"] = "XY" outattrs["MemoryOrder"] = "XY"
@ -1357,9 +1357,9 @@ def set_cloudfrac_alg_metadata(copyarg="pres"):
outcoords = {} outcoords = {}
# Left-most is always cape_cin or cape_cin_lcl_lfc # Left-most is always low_mid_high
outdims[0] = "low_med_high" outdims[0] = "low_mid_high"
outcoords["low_med_high"] = ["low", "med", "high"] outcoords["low_mid_high"] = ["low", "mid", "high"]
out = DataArray(result, name=outname, dims=outdims, coords=outcoords, out = DataArray(result, name=outname, dims=outdims, coords=outcoords,
attrs=outattrs) attrs=outattrs)

3
src/wrf/routines.py

@ -129,7 +129,8 @@ _ALIASES = {"cape_2d" : "cape2d",
"ter" : "terrain", "ter" : "terrain",
"updraft_helicity" : "uhel", "updraft_helicity" : "uhel",
"td" : "dp", "td" : "dp",
"td2" : "dp2m" "td2" : "dp2m",
"cfrac" : "cloudfrac"
} }
class ArgumentError(Exception): class ArgumentError(Exception):

49
src/wrf/util.py

@ -325,18 +325,20 @@ def is_moving_domain(wrfseq, varname=None, latvar=either("XLAT", "XLAT_M"),
# Quick check on pressure coordinates, bypassing the need to search the # Quick check on pressure coordinates, bypassing the need to search the
# domain corner points # domain corner points
try: # try:
coord_names = getattr(first_wrfnc.variables["P"], # coord_names = getattr(first_wrfnc.variables["P"],
"coordinates").split() # "coordinates").split()
except KeyError: # except KeyError:
pass # pass
else: # else:
if "XTIME" in coord_names: # if "XTIME" in coord_names:
return True # return True
else: # else:
return False # return False
# The long way of checking all lat/lon corner points # The long way of checking all lat/lon corner points
# There doesn't appear to be a shortcut, so this should probably
# be stored in a cache somewhere
if varname is not None: if varname is not None:
try: try:
coord_names = getattr(first_wrfnc.variables[varname], coord_names = getattr(first_wrfnc.variables[varname],
@ -349,18 +351,18 @@ def is_moving_domain(wrfseq, varname=None, latvar=either("XLAT", "XLAT_M"),
else: else:
# If the XTIME variable is found to be a coordinate variable, # If the XTIME variable is found to be a coordinate variable,
# then it's a moving domain file # then it's a moving domain file
try: # try:
xtime_coord = coord_names[2] # xtime_coord = coord_names[2]
except IndexError: # except IndexError:
# XTIME is not a coordinate variable, if the variable is in the # # XTIME is not a coordinate variable, if the variable is in the
# file, then this is not a moving domain file # # file, then this is not a moving domain file
if "XTIME" in first_wrfnc.variables: # if "XTIME" in first_wrfnc.variables:
return False # return False
#
else: # else:
# XTIME is a coordinate, so this is a moving domain file # # XTIME is a coordinate, so this is a moving domain file
if xtime_coord == "XTIME": # if xtime_coord == "XTIME":
return True # return True
lon_coord = coord_names[0] lon_coord = coord_names[0]
lat_coord = coord_names[1] lat_coord = coord_names[1]
@ -395,8 +397,9 @@ def is_moving_domain(wrfseq, varname=None, latvar=either("XLAT", "XLAT_M"),
else: else:
if _corners_moved(wrfnc, ll_corner, ur_corner, if _corners_moved(wrfnc, ll_corner, ur_corner,
lat_coord, lon_coord): lat_coord, lon_coord):
return True return True
return False return False
def _get_global_attr(wrfnc, attr): def _get_global_attr(wrfnc, attr):

65
test/cachetest.py

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

2
test/ncl_get_var.ncl

@ -22,7 +22,7 @@
"geopt", "helicity", "lat", "lon", "omg", "p", "pressure", \ "geopt", "helicity", "lat", "lon", "omg", "p", "pressure", \
"pvo", "pw", "rh2", "rh", "slp", "ter", "td2", "td", "tc", \ "pvo", "pw", "rh2", "rh", "slp", "ter", "td2", "td", "tc", \
"theta", "tk", "tv", "twb", "updraft_helicity", "ua", "va", \ "theta", "tk", "tv", "twb", "updraft_helicity", "ua", "va", \
"wa", "uvmet10", "uvmet", "z"/] "wa", "uvmet10", "uvmet", "z", "cfrac"/]
unique_dimname_list = NewList("fifo") unique_dimname_list = NewList("fifo")
unique_dimsize_list = NewList("fifo") unique_dimsize_list = NewList("fifo")

47
test/utests.py

@ -82,16 +82,15 @@ def make_test(varname, wrf_in, referent, multi=False, repeat=3, pynio=False):
wrf_file = wrf_in wrf_file = wrf_in
nc = Nio.open_file(wrf_file) nc = Nio.open_file(wrf_file)
in_wrfnc = [nc for i in xrange(repeat)] in_wrfnc = [nc for i in xrange(repeat)]
# Note: remove this after cloudfrac is included in NCL
# For now just make sure it runs
if varname == "cloudfrac":
my_vals = getvar(in_wrfnc, "cloudfrac", timeidx=timeidx)
return
refnc = NetCDF(referent) refnc = NetCDF(referent)
# These have a left index that defines the product type
multiproduct = varname in ("uvmet", "uvmet10", "cape_2d", "cape_3d",
"cfrac")
if not multi: if not multi:
ref_vals = refnc.variables[varname][:] ref_vals = refnc.variables[varname][:]
else: else:
@ -104,6 +103,8 @@ def make_test(varname, wrf_in, referent, multi=False, repeat=3, pynio=False):
new_dims = [2] + [repeat] + [x for x in data.shape[1:]] new_dims = [2] + [repeat] + [x for x in data.shape[1:]]
elif (varname == "cape_2d"): elif (varname == "cape_2d"):
new_dims = [4] + [repeat] + [x for x in data.shape[1:]] 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 masked=False
@ -114,34 +115,20 @@ def make_test(varname, wrf_in, referent, multi=False, repeat=3, pynio=False):
ref_vals = np.zeros(new_dims, data.dtype) ref_vals = np.zeros(new_dims, data.dtype)
else: else:
ref_vals = ma.asarray(np.zeros(new_dims, data.dtype)) ref_vals = ma.asarray(np.zeros(new_dims, data.dtype))
for i in xrange(repeat): for i in xrange(repeat):
if (varname != "uvmet" and varname != "uvmet10" if not multiproduct:
and varname != "cape_2d" and varname != "cape_3d"):
ref_vals[i,:] = data[:] ref_vals[i,:] = data[:]
if masked: if masked:
ref_vals.mask[i,:] = data.mask[:] ref_vals.mask[i,:] = data.mask[:]
elif (varname == "uvmet" or varname == "uvmet10"
or varname=="cape_3d"): else:
ref_vals[0, i, :] = data[0,:] for prod in xrange(ref_vals.shape[0]):
ref_vals[1, i, :] = data[1,:] ref_vals[prod,i,:] = data[prod,:]
if masked: if masked:
ref_vals.mask[0,i,:] = data.mask[0,:] ref_vals.mask[prod,i,:] = data.mask[prod,:]
ref_vals.mask[1,i,:] = data.mask[1,:]
elif varname == "cape_2d":
ref_vals[0, i, :] = data[0,:]
ref_vals[1, i, :] = data[1,:]
ref_vals[2, i, :] = data[2,:]
ref_vals[3, i, :] = data[3,:]
if masked:
ref_vals.mask[0,i,:] = data.mask[0,:]
ref_vals.mask[1,i,:] = data.mask[1,:]
ref_vals.mask[2,i,:] = data.mask[2,:]
ref_vals.mask[3,i,:] = data.mask[3,:]
if (varname == "tc"): if (varname == "tc"):
my_vals = getvar(in_wrfnc, "temp", timeidx=timeidx, units="c") my_vals = getvar(in_wrfnc, "temp", timeidx=timeidx, units="c")
@ -483,7 +470,7 @@ if __name__ == "__main__":
"geopt", "helicity", "lat", "lon", "omg", "p", "pressure", "geopt", "helicity", "lat", "lon", "omg", "p", "pressure",
"pvo", "pw", "rh2", "rh", "slp", "ter", "td2", "td", "tc", "pvo", "pw", "rh2", "rh", "slp", "ter", "td2", "td", "tc",
"theta", "tk", "tv", "twb", "updraft_helicity", "ua", "va", "theta", "tk", "tv", "twb", "updraft_helicity", "ua", "va",
"wa", "uvmet10", "uvmet", "z", "cloudfrac"] "wa", "uvmet10", "uvmet", "z", "cfrac"]
interp_methods = ["interplevel", "vertcross", "interpline", "vinterp"] interp_methods = ["interplevel", "vertcross", "interpline", "vinterp"]
try: try:

Loading…
Cancel
Save