diff --git a/src/wrf/interp.py b/src/wrf/interp.py index 81e5eb1..23fb4ca 100755 --- a/src/wrf/interp.py +++ b/src/wrf/interp.py @@ -272,15 +272,20 @@ def vertcross(field3d, vert, levels=None, missing=default_fill(np.float64), end_point_xy = None pivot_point_xy = None - if timeidx is None: - if (latlon is True or is_latlon_pair(start_point) or - is_latlon_pair(pivot_point)): - + if (latlon is True or is_latlon_pair(start_point) or + is_latlon_pair(pivot_point)): + + if wrfin is not None: + is_moving = is_moving_domain(wrfin) + else: + is_moving = False + + if timeidx is None: if wrfin is not None: # Moving nests aren't supported with ALL_TIMES because the - # domain could move outside of the cross section, which causes + # domain could move outside of the line, which causes # crashes or different line lengths. - if is_moving_domain(wrfin): + if is_moving: raise ValueError("Requesting all times with a moving nest " "is not supported when using lat/lon " "cross sections because the domain could " @@ -288,14 +293,20 @@ def vertcross(field3d, vert, levels=None, missing=default_fill(np.float64), "You must request each time " "individually.") else: + # Domain not moving, just use 0 _timeidx = 0 - # If using grid coordinates, then don't care about lat/lon - # coordinates. Just use 0. + # If using grid coordinates, then don't care about lat/lon + # coordinates. Just use 0. + else: + _timeidx = 0 else: - _timeidx = 0 - else: - _timeidx = timeidx + if is_moving: + _timeidx = timeidx + else: + # When using non-moving nests, set the time to 0 + # to avoid problems downstream + _timeidx = 0 if pivot_point is not None: if pivot_point.lat is not None and pivot_point.lon is not None: @@ -472,15 +483,20 @@ def interpline(field2d, pivot_point=None, end_point_xy = None pivot_point_xy = None - if timeidx is None: - if (latlon is True or is_latlon_pair(start_point) or - is_latlon_pair(pivot_point)): - + if (latlon is True or is_latlon_pair(start_point) or + is_latlon_pair(pivot_point)): + + if wrfin is not None: + is_moving = is_moving_domain(wrfin) + else: + is_moving = False + + if timeidx is None: if wrfin is not None: # Moving nests aren't supported with ALL_TIMES because the # domain could move outside of the line, which causes # crashes or different line lengths. - if is_moving_domain(wrfin): + if is_moving: raise ValueError("Requesting all times with a moving nest " "is not supported when using a lat/lon " "line because the domain could " @@ -491,12 +507,17 @@ def interpline(field2d, pivot_point=None, # Domain not moving, just use 0 _timeidx = 0 - # If using grid coordinates, then don't care about lat/lon - # coordinates. Just use 0. + # If using grid coordinates, then don't care about lat/lon + # coordinates. Just use 0. + else: + _timeidx = 0 else: - _timeidx = 0 - else: - _timeidx = timeidx + if is_moving: + _timeidx = timeidx + else: + # When using non-moving nests, set the time to 0 + # to avoid problems downstream + _timeidx = 0 if pivot_point is not None: if pivot_point.lat is not None and pivot_point.lon is not None: diff --git a/src/wrf/latlonutils.py b/src/wrf/latlonutils.py index adf27ab..e22c1a4 100644 --- a/src/wrf/latlonutils.py +++ b/src/wrf/latlonutils.py @@ -167,8 +167,8 @@ def _get_proj_params(wrfin, timeidx, stagger, method, squeeze, cache, _key): # Only need one file and one time if the domain is not moving if not is_moving: - if is_multi_time_req(timeidx): - lat_timeidx = 0 + # Always use the 0th time for non-moving domains to avoid problems + lat_timeidx = 0 if is_multi_file(wrfin): if not is_mapping(wrfin): @@ -179,7 +179,7 @@ def _get_proj_params(wrfin, timeidx, stagger, method, squeeze, cache, _key): key = _key[first_entry] return _get_proj_params(wrfin, timeidx, stagger, method, squeeze, cache, key) - + xlat = extract_vars(wrfin, lat_timeidx, (latvar,), method, squeeze, cache, meta=False, _key=_key)[latvar] xlon = extract_vars(wrfin, lat_timeidx, (lonvar,), method, squeeze, cache, diff --git a/src/wrf/metadecorators.py b/src/wrf/metadecorators.py index c8ff1b0..ea00d75 100644 --- a/src/wrf/metadecorators.py +++ b/src/wrf/metadecorators.py @@ -933,15 +933,20 @@ def _set_cross_meta(wrapped, instance, args, kwargs): end_point_xy = None pivot_point_xy = None - if timeidx is None: - if (inc_latlon is True or is_latlon_pair(start_point) or - is_latlon_pair(pivot_point)): - + if (inc_latlon is True or is_latlon_pair(start_point) or + is_latlon_pair(pivot_point)): + + if wrfin is not None: + is_moving = is_moving_domain(wrfin) + else: + is_moving = False + + if timeidx is None: if wrfin is not None: # Moving nests aren't supported with ALL_TIMES because the - # domain could move outside of the cross section, which causes + # domain could move outside of the line, which causes # crashes or different line lengths. - if is_moving_domain(wrfin): + if is_moving: raise ValueError("Requesting all times with a moving nest " "is not supported when using lat/lon " "cross sections because the domain could " @@ -949,14 +954,20 @@ def _set_cross_meta(wrapped, instance, args, kwargs): "You must request each time " "individually.") else: + # Domain not moving, just use 0 _timeidx = 0 - # If using grid coordinates, then don't care about lat/lon - # coordinates. Just use 0. + # If using grid coordinates, then don't care about lat/lon + # coordinates. Just use 0. + else: + _timeidx = 0 else: - _timeidx = 0 - else: - _timeidx = timeidx + if is_moving: + _timeidx = timeidx + else: + # When using non-moving nests, set the time to 0 + # to avoid problems downstream + _timeidx = 0 if pivot_point is not None: if pivot_point.lat is not None and pivot_point.lon is not None: @@ -1176,15 +1187,20 @@ def _set_line_meta(wrapped, instance, args, kwargs): end_point_xy = None pivot_point_xy = None - if timeidx is None: - if (inc_latlon is True or is_latlon_pair(start_point) or - is_latlon_pair(pivot_point)): - + if (inc_latlon is True or is_latlon_pair(start_point) or + is_latlon_pair(pivot_point)): + + if wrfin is not None: + is_moving = is_moving_domain(wrfin) + else: + is_moving = False + + if timeidx is None: if wrfin is not None: # Moving nests aren't supported with ALL_TIMES because the # domain could move outside of the line, which causes # crashes or different line lengths. - if is_moving_domain(wrfin): + if is_moving: raise ValueError("Requesting all times with a moving nest " "is not supported when using a lat/lon " "line because the domain could " @@ -1195,12 +1211,18 @@ def _set_line_meta(wrapped, instance, args, kwargs): # Domain not moving, just use 0 _timeidx = 0 - # If using grid coordinates, then don't care about lat/lon - # coordinates. Just use 0. + # If using grid coordinates, then don't care about lat/lon + # coordinates. Just use 0. + else: + _timeidx = 0 else: - _timeidx = 0 - else: - _timeidx = timeidx + if is_moving: + _timeidx = timeidx + else: + # When using non-moving nests, set the time to 0 + # to avoid problems downstream + _timeidx = 0 + if pivot_point is not None: if pivot_point.lat is not None and pivot_point.lon is not None: diff --git a/src/wrf/util.py b/src/wrf/util.py index b9431be..67d30eb 100644 --- a/src/wrf/util.py +++ b/src/wrf/util.py @@ -3893,7 +3893,10 @@ def is_latlon_pair(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 diff --git a/test/utests.py b/test/utests.py index 64f54fb..699d287 100644 --- a/test/utests.py +++ b/test/utests.py @@ -15,9 +15,11 @@ from wrf.util import is_multi_file NCL_EXE = "/Users/ladwig/miniconda2/envs/ncl_build/bin/ncl" NCARG_ROOT = "/Users/ladwig/miniconda2/envs/ncl_build" #TEST_FILE = "/Users/ladwig/Documents/wrf_files/wrfout_d01_2010-06-13_21:00:00" -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_*" -REF_NC_FILE = "/tmp/wrftest.nc" +REF_NC_FILES = ["/tmp/wrftest_moving.nc", "/tmp/wrftest_static.nc"] +NEST = ["moving", "static"] # Python 3 if sys.version_info > (3,): @@ -35,19 +37,21 @@ def setUpModule(): this_path = os.path.realpath(__file__) ncl_script = os.path.join(os.path.dirname(this_path), "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): - status = subprocess.call(cmd, shell=True) - if (status != 0): - raise RuntimeError("NCL script failed. Could not set up test.") + if not os.path.exists(outfile): + status = subprocess.call(cmd, shell=True) + if (status != 0): + raise RuntimeError("NCL script failed. Could not set up test.") # Using helpful information at: # http://eli.thegreenplace.net/2014/04/02/dynamically-generating-python-test-cases @@ -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 # might be moving if not multi: - + if lats.ndim > 2: # moving nest + lats = lats[0,:] + lons = lons[0,:] + ll_point = ll_points(lats, lons) pivot = CoordPair(lat=lats[int(lats.shape[-2]/2), @@ -430,7 +437,7 @@ def make_interp_test(varname, dir, pattern, referent, multi=False, nt.assert_allclose(to_np(ht_cross), 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])) 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"] lons = t2.coords["XLONG"] if multi: - lats = lats[0,:] - lons = lons[0,:] + if lats.ndim > 2: # moving nest + lats = lats[0,:] + lons = lons[0,:] 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) + if xy.ndim > 2: + # Moving nest + is_moving = True + numtimes = xy.shape[-2] + else: + is_moving = False + numtimes = 1 + for tidx in range(9): # Next make sure the 'proj' version works projparams = extract_proj_params(wrfin, timeidx=tidx) xy_proj = ll_to_xy_proj(lats, lons, as_int=False, **projparams) + + if is_moving: + idxs = (slice(None), tidx, slice(None)) + else: + idxs = (slice(None),) - nt.assert_allclose(to_np(xy_proj), to_np(xy[:,tidx,:])) + nt.assert_allclose(to_np(xy_proj), to_np(xy[idxs])) else: # 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) + 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 projparams = extract_proj_params(wrfin, timeidx=tidx) 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 @@ -924,84 +957,85 @@ if __name__ == "__main__": interp_methods = ["interplevel", "vertcross", "interpline", "vinterp"] latlon_tests = ["xy", "ll"] - try: - import netCDF4 - except ImportError: - pass - else: - for var in wrf_vars: - if var in ignore_vars: - continue - - test_func1 = make_test(var, 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) + for dir, ref_nc_file, nest in zip(DIRS, REF_NC_FILES, NEST): + try: + import netCDF4 + except ImportError: + pass + else: + for var in wrf_vars: + if var in ignore_vars: + continue - try: - import PyNIO - except ImportError: - pass - else: - for var in wrf_vars: - if var in ignore_vars: - continue - - test_func1 = make_test(var, DIR, PATTERN, REF_NC_FILE, pynio=True) - test_func2 = make_test(var, DIR, PATTERN, REF_NC_FILE, multi=True, - pynio=True) - setattr(WRFVarsTest, 'test_pynio_{0}'.format(var), test_func1) - setattr(WRFVarsTest, 'test_pynio_multi_{0}'.format(var), - test_func2) - - for method in interp_methods: - test_interp_func1 = make_interp_test(method, DIR, PATTERN, - REF_NC_FILE) - test_interp_func2 = make_interp_test(method, DIR, PATTERN, - REF_NC_FILE, multi=True) - setattr(WRFInterpTest, 'test_pynio_{0}'.format(method), - test_interp_func1) - setattr(WRFInterpTest, 'test_pynio_multi_{0}'.format(method), - test_interp_func2) - - for testid in latlon_tests: - for single in (True, False): - for multi in (True, False): - test_ll_func = make_latlon_test(testid, 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(testid, - singlestr, - multistr) - setattr(WRFLatLonTest, test_name, test_ll_func) + test_func1 = make_test(var, dir, PATTERN, ref_nc_file) + test_func2 = make_test(var, dir, PATTERN, ref_nc_file, multi=True) + setattr(WRFVarsTest, 'test_{0}_{1}'.format(nest,var), test_func1) + setattr(WRFVarsTest, 'test_{0}_multi_{1}'.format(nest,var), test_func2) + + for method in interp_methods: + test_interp_func1 = make_interp_test(method, dir, PATTERN, + ref_nc_file) + test_interp_func2 = make_interp_test(method, dir, PATTERN, + ref_nc_file, multi=True) + setattr(WRFInterpTest, 'test_{0}_{1}'.format(nest,method), + test_interp_func1) + setattr(WRFInterpTest, 'test_{0}_multi_{1}'.format(nest,method), + test_interp_func2) + + for testid in latlon_tests: + for single in (True, False): + for multi in (True, False): + test_ll_func = make_latlon_test(testid, dir, PATTERN, + ref_nc_file, + single=single, + multi=multi, + pynio=False) + multistr = "" if not multi else "_multi" + singlestr = "_nosingle" if not single else "_single" + test_name = "test_{}_{}{}{}".format(nest, testid, singlestr, + multistr) + setattr(WRFLatLonTest, test_name, test_ll_func) + + try: + import PyNIO + except ImportError: + pass + else: + for var in wrf_vars: + if var in ignore_vars: + continue + + test_func1 = make_test(var, dir, PATTERN, ref_nc_file, pynio=True) + test_func2 = make_test(var, dir, PATTERN, ref_nc_file, multi=True, + pynio=True) + setattr(WRFVarsTest, 'test_pynio_{0}_{1}'.format(nest,var), test_func1) + setattr(WRFVarsTest, 'test_pynio_{0}_multi_{1}'.format(nest,var), + test_func2) + + for method in interp_methods: + test_interp_func1 = make_interp_test(method, dir, PATTERN, + ref_nc_file) + test_interp_func2 = make_interp_test(method, dir, PATTERN, + ref_nc_file, multi=True) + setattr(WRFInterpTest, 'test_pynio_{0}_{1}'.format(nest,method), + test_interp_func1) + setattr(WRFInterpTest, 'test_pynio_{0}_multi_{1}'.format(nest,method), + test_interp_func2) + + for testid in latlon_tests: + for single in (True, False): + for multi in (True, False): + test_ll_func = make_latlon_test(testid, dir, PATTERN, + ref_nc_file, + single=single, + multi=multi, + pynio=False) + multistr = "" if not multi else "_multi" + singlestr = "_nosingle" if not single else "_single" + test_name = "test_pynio_{}_{}{}{}".format(nest, testid, + singlestr, + multistr) + setattr(WRFLatLonTest, test_name, test_ll_func) ut.main() \ No newline at end of file