Browse Source

Fix issues found during testing of static and moving nests.

lon0
Bill Ladwig 7 years ago
parent
commit
06280e5cae
  1. 63
      src/wrf/interp.py
  2. 6
      src/wrf/latlonutils.py
  3. 64
      src/wrf/metadecorators.py
  4. 5
      src/wrf/util.py
  5. 230
      test/utests.py

63
src/wrf/interp.py

@ -272,15 +272,20 @@ def vertcross(field3d, vert, levels=None, missing=default_fill(np.float64),
end_point_xy = None end_point_xy = None
pivot_point_xy = None pivot_point_xy = None
if timeidx is None: if (latlon is True or is_latlon_pair(start_point) or
if (latlon is True or is_latlon_pair(start_point) or is_latlon_pair(pivot_point)):
is_latlon_pair(pivot_point)):
if wrfin is not None:
is_moving = is_moving_domain(wrfin)
else:
is_moving = False
if timeidx is None:
if wrfin is not None: if wrfin is not None:
# Moving nests aren't supported with ALL_TIMES because the # Moving nests aren't supported with ALL_TIMES because the
# domain could move outside of the cross section, which causes # domain could move outside of the line, which causes
# crashes or different line lengths. # crashes or different line lengths.
if is_moving_domain(wrfin): if is_moving:
raise ValueError("Requesting all times with a moving nest " raise ValueError("Requesting all times with a moving nest "
"is not supported when using lat/lon " "is not supported when using lat/lon "
"cross sections because the domain could " "cross sections because the domain could "
@ -288,14 +293,20 @@ def vertcross(field3d, vert, levels=None, missing=default_fill(np.float64),
"You must request each time " "You must request each time "
"individually.") "individually.")
else: else:
# Domain not moving, just use 0
_timeidx = 0 _timeidx = 0
# If using grid coordinates, then don't care about lat/lon # If using grid coordinates, then don't care about lat/lon
# coordinates. Just use 0. # coordinates. Just use 0.
else:
_timeidx = 0
else: else:
_timeidx = 0 if is_moving:
else: _timeidx = timeidx
_timeidx = timeidx else:
# When using non-moving nests, set the time to 0
# to avoid problems downstream
_timeidx = 0
if pivot_point is not None: if pivot_point is not None:
if pivot_point.lat is not None and pivot_point.lon is not None: if pivot_point.lat is not None and pivot_point.lon is not None:
@ -472,15 +483,20 @@ def interpline(field2d, pivot_point=None,
end_point_xy = None end_point_xy = None
pivot_point_xy = None pivot_point_xy = None
if timeidx is None: if (latlon is True or is_latlon_pair(start_point) or
if (latlon is True or is_latlon_pair(start_point) or is_latlon_pair(pivot_point)):
is_latlon_pair(pivot_point)):
if wrfin is not None:
is_moving = is_moving_domain(wrfin)
else:
is_moving = False
if timeidx is None:
if wrfin is not None: if wrfin is not None:
# Moving nests aren't supported with ALL_TIMES because the # Moving nests aren't supported with ALL_TIMES because the
# domain could move outside of the line, which causes # domain could move outside of the line, which causes
# crashes or different line lengths. # crashes or different line lengths.
if is_moving_domain(wrfin): if is_moving:
raise ValueError("Requesting all times with a moving nest " raise ValueError("Requesting all times with a moving nest "
"is not supported when using a lat/lon " "is not supported when using a lat/lon "
"line because the domain could " "line because the domain could "
@ -491,12 +507,17 @@ def interpline(field2d, pivot_point=None,
# Domain not moving, just use 0 # Domain not moving, just use 0
_timeidx = 0 _timeidx = 0
# If using grid coordinates, then don't care about lat/lon # If using grid coordinates, then don't care about lat/lon
# coordinates. Just use 0. # coordinates. Just use 0.
else:
_timeidx = 0
else: else:
_timeidx = 0 if is_moving:
else: _timeidx = timeidx
_timeidx = timeidx else:
# When using non-moving nests, set the time to 0
# to avoid problems downstream
_timeidx = 0
if pivot_point is not None: if pivot_point is not None:
if pivot_point.lat is not None and pivot_point.lon is not None: if pivot_point.lat is not None and pivot_point.lon is not None:

6
src/wrf/latlonutils.py

@ -167,8 +167,8 @@ def _get_proj_params(wrfin, timeidx, stagger, method, squeeze, cache, _key):
# Only need one file and one time if the domain is not moving # Only need one file and one time if the domain is not moving
if not is_moving: if not is_moving:
if is_multi_time_req(timeidx): # Always use the 0th time for non-moving domains to avoid problems
lat_timeidx = 0 lat_timeidx = 0
if is_multi_file(wrfin): if is_multi_file(wrfin):
if not is_mapping(wrfin): if not is_mapping(wrfin):
@ -179,7 +179,7 @@ def _get_proj_params(wrfin, timeidx, stagger, method, squeeze, cache, _key):
key = _key[first_entry] key = _key[first_entry]
return _get_proj_params(wrfin, timeidx, stagger, return _get_proj_params(wrfin, timeidx, stagger,
method, squeeze, cache, key) method, squeeze, cache, key)
xlat = extract_vars(wrfin, lat_timeidx, (latvar,), method, squeeze, cache, xlat = extract_vars(wrfin, lat_timeidx, (latvar,), method, squeeze, cache,
meta=False, _key=_key)[latvar] meta=False, _key=_key)[latvar]
xlon = extract_vars(wrfin, lat_timeidx, (lonvar,), method, squeeze, cache, xlon = extract_vars(wrfin, lat_timeidx, (lonvar,), method, squeeze, cache,

64
src/wrf/metadecorators.py

@ -933,15 +933,20 @@ def _set_cross_meta(wrapped, instance, args, kwargs):
end_point_xy = None end_point_xy = None
pivot_point_xy = None pivot_point_xy = None
if timeidx is None: if (inc_latlon is True or is_latlon_pair(start_point) or
if (inc_latlon is True or is_latlon_pair(start_point) or is_latlon_pair(pivot_point)):
is_latlon_pair(pivot_point)):
if wrfin is not None:
is_moving = is_moving_domain(wrfin)
else:
is_moving = False
if timeidx is None:
if wrfin is not None: if wrfin is not None:
# Moving nests aren't supported with ALL_TIMES because the # Moving nests aren't supported with ALL_TIMES because the
# domain could move outside of the cross section, which causes # domain could move outside of the line, which causes
# crashes or different line lengths. # crashes or different line lengths.
if is_moving_domain(wrfin): if is_moving:
raise ValueError("Requesting all times with a moving nest " raise ValueError("Requesting all times with a moving nest "
"is not supported when using lat/lon " "is not supported when using lat/lon "
"cross sections because the domain could " "cross sections because the domain could "
@ -949,14 +954,20 @@ def _set_cross_meta(wrapped, instance, args, kwargs):
"You must request each time " "You must request each time "
"individually.") "individually.")
else: else:
# Domain not moving, just use 0
_timeidx = 0 _timeidx = 0
# If using grid coordinates, then don't care about lat/lon # If using grid coordinates, then don't care about lat/lon
# coordinates. Just use 0. # coordinates. Just use 0.
else:
_timeidx = 0
else: else:
_timeidx = 0 if is_moving:
else: _timeidx = timeidx
_timeidx = timeidx else:
# When using non-moving nests, set the time to 0
# to avoid problems downstream
_timeidx = 0
if pivot_point is not None: if pivot_point is not None:
if pivot_point.lat is not None and pivot_point.lon is not None: if pivot_point.lat is not None and pivot_point.lon is not None:
@ -1176,15 +1187,20 @@ def _set_line_meta(wrapped, instance, args, kwargs):
end_point_xy = None end_point_xy = None
pivot_point_xy = None pivot_point_xy = None
if timeidx is None: if (inc_latlon is True or is_latlon_pair(start_point) or
if (inc_latlon is True or is_latlon_pair(start_point) or is_latlon_pair(pivot_point)):
is_latlon_pair(pivot_point)):
if wrfin is not None:
is_moving = is_moving_domain(wrfin)
else:
is_moving = False
if timeidx is None:
if wrfin is not None: if wrfin is not None:
# Moving nests aren't supported with ALL_TIMES because the # Moving nests aren't supported with ALL_TIMES because the
# domain could move outside of the line, which causes # domain could move outside of the line, which causes
# crashes or different line lengths. # crashes or different line lengths.
if is_moving_domain(wrfin): if is_moving:
raise ValueError("Requesting all times with a moving nest " raise ValueError("Requesting all times with a moving nest "
"is not supported when using a lat/lon " "is not supported when using a lat/lon "
"line because the domain could " "line because the domain could "
@ -1195,12 +1211,18 @@ def _set_line_meta(wrapped, instance, args, kwargs):
# Domain not moving, just use 0 # Domain not moving, just use 0
_timeidx = 0 _timeidx = 0
# If using grid coordinates, then don't care about lat/lon # If using grid coordinates, then don't care about lat/lon
# coordinates. Just use 0. # coordinates. Just use 0.
else:
_timeidx = 0
else: else:
_timeidx = 0 if is_moving:
else: _timeidx = timeidx
_timeidx = timeidx else:
# When using non-moving nests, set the time to 0
# to avoid problems downstream
_timeidx = 0
if pivot_point is not None: if pivot_point is not None:
if pivot_point.lat is not None and pivot_point.lon is not None: if pivot_point.lat is not None and pivot_point.lon is not None:

5
src/wrf/util.py

@ -3893,7 +3893,10 @@ def is_latlon_pair(pair):
:obj:`bool`: True if the pair is a lat/lon pair. :obj:`bool`: True if the pair is a lat/lon pair.
""" """
return (pair.lat is not None and pair.lon is not None) if pair is not None:
return (pair.lat is not None and pair.lon is not None)
else:
return False

230
test/utests.py

@ -15,9 +15,11 @@ from wrf.util import is_multi_file
NCL_EXE = "/Users/ladwig/miniconda2/envs/ncl_build/bin/ncl" NCL_EXE = "/Users/ladwig/miniconda2/envs/ncl_build/bin/ncl"
NCARG_ROOT = "/Users/ladwig/miniconda2/envs/ncl_build" NCARG_ROOT = "/Users/ladwig/miniconda2/envs/ncl_build"
#TEST_FILE = "/Users/ladwig/Documents/wrf_files/wrfout_d01_2010-06-13_21:00:00" #TEST_FILE = "/Users/ladwig/Documents/wrf_files/wrfout_d01_2010-06-13_21:00:00"
DIR = "/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/moving_nest" DIRS = ["/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/moving_nest",
"/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/static_nest"]
PATTERN = "wrfout_d02_*" PATTERN = "wrfout_d02_*"
REF_NC_FILE = "/tmp/wrftest.nc" REF_NC_FILES = ["/tmp/wrftest_moving.nc", "/tmp/wrftest_static.nc"]
NEST = ["moving", "static"]
# Python 3 # Python 3
if sys.version_info > (3,): if sys.version_info > (3,):
@ -35,19 +37,21 @@ def setUpModule():
this_path = os.path.realpath(__file__) this_path = os.path.realpath(__file__)
ncl_script = os.path.join(os.path.dirname(this_path), ncl_script = os.path.join(os.path.dirname(this_path),
"ncl_get_var.ncl") "ncl_get_var.ncl")
cmd = "%s %s 'dir=\"%s\"' 'pattern=\"%s\"' 'out_file=\"%s\"'" % (NCL_EXE,
ncl_script,
DIR,
PATTERN,
REF_NC_FILE)
print cmd for dir,outfile in zip(DIRS, REF_NC_FILES):
cmd = "%s %s 'dir=\"%s\"' 'pattern=\"%s\"' 'out_file=\"%s\"'" % (
NCL_EXE,
ncl_script,
dir,
PATTERN,
outfile)
print cmd
if not os.path.exists(REF_NC_FILE): if not os.path.exists(outfile):
status = subprocess.call(cmd, shell=True) status = subprocess.call(cmd, shell=True)
if (status != 0): if (status != 0):
raise RuntimeError("NCL script failed. Could not set up test.") raise RuntimeError("NCL script failed. Could not set up test.")
# Using helpful information at: # Using helpful information at:
# http://eli.thegreenplace.net/2014/04/02/dynamically-generating-python-test-cases # http://eli.thegreenplace.net/2014/04/02/dynamically-generating-python-test-cases
@ -377,7 +381,10 @@ def make_interp_test(varname, dir, pattern, referent, multi=False,
# Only do this for the non-multi case, since the domain # Only do this for the non-multi case, since the domain
# might be moving # might be moving
if not multi: if not multi:
if lats.ndim > 2: # moving nest
lats = lats[0,:]
lons = lons[0,:]
ll_point = ll_points(lats, lons) ll_point = ll_points(lats, lons)
pivot = CoordPair(lat=lats[int(lats.shape[-2]/2), pivot = CoordPair(lat=lats[int(lats.shape[-2]/2),
@ -430,7 +437,7 @@ def make_interp_test(varname, dir, pattern, referent, multi=False,
nt.assert_allclose(to_np(ht_cross), nt.assert_allclose(to_np(ht_cross),
to_np(ref_ht_vertcross2), atol=.01) to_np(ref_ht_vertcross2), atol=.01)
idxs = (0, slice(None)) if multi else (slice(None),) idxs = (0, slice(None)) if lats.ndim > 2 else (slice(None),)
start_lat = np.amin(lats[idxs]) + .25*(np.amax(lats[idxs]) - np.amin(lats[idxs])) start_lat = np.amin(lats[idxs]) + .25*(np.amax(lats[idxs]) - np.amin(lats[idxs]))
end_lat = np.amin(lats[idxs]) + .65*(np.amax(lats[idxs]) - np.amin(lats[idxs])) end_lat = np.amin(lats[idxs]) + .65*(np.amax(lats[idxs]) - np.amin(lats[idxs]))
@ -501,8 +508,9 @@ def make_interp_test(varname, dir, pattern, referent, multi=False,
lats = t2.coords["XLAT"] lats = t2.coords["XLAT"]
lons = t2.coords["XLONG"] lons = t2.coords["XLONG"]
if multi: if multi:
lats = lats[0,:] if lats.ndim > 2: # moving nest
lons = lons[0,:] lats = lats[0,:]
lons = lons[0,:]
ll_point = ll_points(lats, lons) ll_point = ll_points(lats, lons)
@ -852,14 +860,27 @@ def make_latlon_test(testid, dir, pattern, referent, single,
nt.assert_allclose(to_np(xy), ref) nt.assert_allclose(to_np(xy), ref)
if xy.ndim > 2:
# Moving nest
is_moving = True
numtimes = xy.shape[-2]
else:
is_moving = False
numtimes = 1
for tidx in range(9): for tidx in range(9):
# Next make sure the 'proj' version works # Next make sure the 'proj' version works
projparams = extract_proj_params(wrfin, timeidx=tidx) projparams = extract_proj_params(wrfin, timeidx=tidx)
xy_proj = ll_to_xy_proj(lats, lons, as_int=False, xy_proj = ll_to_xy_proj(lats, lons, as_int=False,
**projparams) **projparams)
if is_moving:
idxs = (slice(None), tidx, slice(None))
else:
idxs = (slice(None),)
nt.assert_allclose(to_np(xy_proj), to_np(xy[:,tidx,:])) nt.assert_allclose(to_np(xy_proj), to_np(xy[idxs]))
else: else:
# i_s, j_s taken from NCL script, just hard-coding for now # i_s, j_s taken from NCL script, just hard-coding for now
@ -887,13 +908,25 @@ def make_latlon_test(testid, dir, pattern, referent, single,
nt.assert_allclose(to_np(ll), ref) nt.assert_allclose(to_np(ll), ref)
if ll.ndim > 2:
# Moving nest
is_moving = True
numtimes = ll.shape[-2]
else:
is_moving = False
numtimes = 1
for tidx in range(9): for tidx in range(numtimes):
# Next make sure the 'proj' version works # Next make sure the 'proj' version works
projparams = extract_proj_params(wrfin, timeidx=tidx) projparams = extract_proj_params(wrfin, timeidx=tidx)
ll_proj = xy_to_ll_proj(x_s, y_s, **projparams) ll_proj = xy_to_ll_proj(x_s, y_s, **projparams)
nt.assert_allclose(to_np(ll_proj), to_np(ll[:,tidx,:])) if is_moving:
idxs = (slice(None), tidx, slice(None))
else:
idxs = (slice(None),)
nt.assert_allclose(to_np(ll_proj), to_np(ll[idxs]))
return test return test
@ -924,84 +957,85 @@ if __name__ == "__main__":
interp_methods = ["interplevel", "vertcross", "interpline", "vinterp"] interp_methods = ["interplevel", "vertcross", "interpline", "vinterp"]
latlon_tests = ["xy", "ll"] latlon_tests = ["xy", "ll"]
try: for dir, ref_nc_file, nest in zip(DIRS, REF_NC_FILES, NEST):
import netCDF4 try:
except ImportError: import netCDF4
pass except ImportError:
else: pass
for var in wrf_vars: else:
if var in ignore_vars: for var in wrf_vars:
continue if var in ignore_vars:
continue
test_func1 = make_test(var, DIR, PATTERN, REF_NC_FILE)
test_func2 = make_test(var, DIR, PATTERN, REF_NC_FILE, multi=True)
setattr(WRFVarsTest, 'test_{0}'.format(var), test_func1)
setattr(WRFVarsTest, 'test_multi_{0}'.format(var), test_func2)
for method in interp_methods:
test_interp_func1 = make_interp_test(method, DIR, PATTERN,
REF_NC_FILE)
test_interp_func2 = make_interp_test(method, DIR, PATTERN,
REF_NC_FILE, multi=True)
setattr(WRFInterpTest, 'test_{0}'.format(method),
test_interp_func1)
setattr(WRFInterpTest, 'test_multi_{0}'.format(method),
test_interp_func2)
for testid in latlon_tests:
for single in (True, False):
for multi in (True, False):
test_ll_func = make_latlon_test(testid, DIR, PATTERN,
REF_NC_FILE,
single=single,
multi=multi,
pynio=False)
multistr = "" if not multi else "_multi"
singlestr = "_nosingle" if not single else "_single"
test_name = "test_{}{}{}".format(testid, singlestr,
multistr)
setattr(WRFLatLonTest, test_name, test_ll_func)
try: test_func1 = make_test(var, dir, PATTERN, ref_nc_file)
import PyNIO test_func2 = make_test(var, dir, PATTERN, ref_nc_file, multi=True)
except ImportError: setattr(WRFVarsTest, 'test_{0}_{1}'.format(nest,var), test_func1)
pass setattr(WRFVarsTest, 'test_{0}_multi_{1}'.format(nest,var), test_func2)
else:
for var in wrf_vars: for method in interp_methods:
if var in ignore_vars: test_interp_func1 = make_interp_test(method, dir, PATTERN,
continue ref_nc_file)
test_interp_func2 = make_interp_test(method, dir, PATTERN,
test_func1 = make_test(var, DIR, PATTERN, REF_NC_FILE, pynio=True) ref_nc_file, multi=True)
test_func2 = make_test(var, DIR, PATTERN, REF_NC_FILE, multi=True, setattr(WRFInterpTest, 'test_{0}_{1}'.format(nest,method),
pynio=True) test_interp_func1)
setattr(WRFVarsTest, 'test_pynio_{0}'.format(var), test_func1) setattr(WRFInterpTest, 'test_{0}_multi_{1}'.format(nest,method),
setattr(WRFVarsTest, 'test_pynio_multi_{0}'.format(var), test_interp_func2)
test_func2)
for testid in latlon_tests:
for method in interp_methods: for single in (True, False):
test_interp_func1 = make_interp_test(method, DIR, PATTERN, for multi in (True, False):
REF_NC_FILE) test_ll_func = make_latlon_test(testid, dir, PATTERN,
test_interp_func2 = make_interp_test(method, DIR, PATTERN, ref_nc_file,
REF_NC_FILE, multi=True) single=single,
setattr(WRFInterpTest, 'test_pynio_{0}'.format(method), multi=multi,
test_interp_func1) pynio=False)
setattr(WRFInterpTest, 'test_pynio_multi_{0}'.format(method), multistr = "" if not multi else "_multi"
test_interp_func2) singlestr = "_nosingle" if not single else "_single"
test_name = "test_{}_{}{}{}".format(nest, testid, singlestr,
for testid in latlon_tests: multistr)
for single in (True, False): setattr(WRFLatLonTest, test_name, test_ll_func)
for multi in (True, False):
test_ll_func = make_latlon_test(testid, DIR, PATTERN, try:
REF_NC_FILE, import PyNIO
single=single, except ImportError:
multi=multi, pass
pynio=False) else:
multistr = "" if not multi else "_multi" for var in wrf_vars:
singlestr = "_nosingle" if not single else "_single" if var in ignore_vars:
test_name = "test_pynio_{}{}{}".format(testid, continue
singlestr,
multistr) test_func1 = make_test(var, dir, PATTERN, ref_nc_file, pynio=True)
setattr(WRFLatLonTest, test_name, test_ll_func) test_func2 = make_test(var, dir, PATTERN, ref_nc_file, multi=True,
pynio=True)
setattr(WRFVarsTest, 'test_pynio_{0}_{1}'.format(nest,var), test_func1)
setattr(WRFVarsTest, 'test_pynio_{0}_multi_{1}'.format(nest,var),
test_func2)
for method in interp_methods:
test_interp_func1 = make_interp_test(method, dir, PATTERN,
ref_nc_file)
test_interp_func2 = make_interp_test(method, dir, PATTERN,
ref_nc_file, multi=True)
setattr(WRFInterpTest, 'test_pynio_{0}_{1}'.format(nest,method),
test_interp_func1)
setattr(WRFInterpTest, 'test_pynio_{0}_multi_{1}'.format(nest,method),
test_interp_func2)
for testid in latlon_tests:
for single in (True, False):
for multi in (True, False):
test_ll_func = make_latlon_test(testid, dir, PATTERN,
ref_nc_file,
single=single,
multi=multi,
pynio=False)
multistr = "" if not multi else "_multi"
singlestr = "_nosingle" if not single else "_single"
test_name = "test_pynio_{}_{}{}{}".format(nest, testid,
singlestr,
multistr)
setattr(WRFLatLonTest, test_name, test_ll_func)
ut.main() ut.main()
Loading…
Cancel
Save