From e4946c675d52fb11e1dfe692072814039382bf83 Mon Sep 17 00:00:00 2001 From: Jon Thielen Date: Wed, 26 Aug 2020 17:41:54 -0500 Subject: [PATCH 1/9] Use PyProj instead of Cartopy for internal coordinate transforms and rename crs coordinate --- conftest.py | 4 +- src/metpy/calc/cross_sections.py | 15 ++++--- src/metpy/calc/tools.py | 16 +++---- src/metpy/interpolate/slices.py | 15 ++++--- src/metpy/plots/mapping.py | 6 +++ src/metpy/xarray.py | 58 ++++++++++++++++++------- tests/calc/test_calc_tools.py | 12 +++--- tests/calc/test_cross_sections.py | 6 +-- tests/calc/test_kinematics.py | 4 +- tests/interpolate/test_slices.py | 12 +++--- tests/test_xarray.py | 71 +++++++++++++++++-------------- tutorials/xarray_tutorial.py | 20 ++++++--- 12 files changed, 144 insertions(+), 95 deletions(-) diff --git a/conftest.py b/conftest.py index 42d2d9e386f..6c981d5791e 100644 --- a/conftest.py +++ b/conftest.py @@ -62,7 +62,7 @@ def cfeature(): @pytest.fixture() def test_da_lonlat(): """Return a DataArray with a lon/lat grid and no time coordinate for use in tests.""" - pytest.importorskip('cartopy') + pytest.importorskip('pyproj') data = numpy.linspace(300, 250, 3 * 4 * 4).reshape((3, 4, 4)) ds = xarray.Dataset( @@ -96,7 +96,7 @@ def test_da_lonlat(): @pytest.fixture() def test_da_xy(): """Return a DataArray with a x/y grid and a time coordinate for use in tests.""" - pytest.importorskip('cartopy') + pytest.importorskip('pyproj') data = numpy.linspace(300, 250, 3 * 3 * 4 * 4).reshape((3, 3, 4, 4)) ds = xarray.Dataset( diff --git a/src/metpy/calc/cross_sections.py b/src/metpy/calc/cross_sections.py index bbae1520762..590f263edf5 100644 --- a/src/metpy/calc/cross_sections.py +++ b/src/metpy/calc/cross_sections.py @@ -36,9 +36,7 @@ def distances_from_cross_section(cross): """ if check_axis(cross.metpy.x, 'longitude') and check_axis(cross.metpy.y, 'latitude'): # Use pyproj to obtain x and y distances - from pyproj import Geod - - g = Geod(cross.metpy.cartopy_crs.proj4_init) + g = cross.metpy.pyproj_crs.get_geod() lon = cross.metpy.x lat = cross.metpy.y @@ -83,10 +81,13 @@ def latitude_from_cross_section(cross): if check_axis(y, 'latitude'): return y else: - import cartopy.crs as ccrs - latitude = ccrs.Geodetic().transform_points(cross.metpy.cartopy_crs, - cross.metpy.x.values, - y.values)[..., 1] + from pyproj import Proj + latitude = Proj(cross.metpy.pyproj_crs)( + cross.metpy.x.values, + y.values, + inverse=True, + radians=False + )[1] latitude = xr.DataArray(latitude * units.degrees_north, coords=y.coords, dims=y.dims) return latitude diff --git a/src/metpy/calc/tools.py b/src/metpy/calc/tools.py index 049316746cb..21cbb09b6c6 100644 --- a/src/metpy/calc/tools.py +++ b/src/metpy/calc/tools.py @@ -763,7 +763,7 @@ def take(indexer): @exporter.export @preprocess_and_wrap() -def lat_lon_grid_deltas(longitude, latitude, x_dim=-1, y_dim=-2, **kwargs): +def lat_lon_grid_deltas(longitude, latitude, x_dim=-1, y_dim=-2, geod=None): r"""Calculate the actual delta between grid points that are in latitude/longitude format. Parameters @@ -778,8 +778,9 @@ def lat_lon_grid_deltas(longitude, latitude, x_dim=-1, y_dim=-2, **kwargs): axis number for the x dimension, defaults to -1. y_dim : int axis number for the y dimesion, defaults to -2. - kwargs - Other keyword arguments to pass to :class:`~pyproj.Geod` + geod : pyproj.Geod or None + PyProj Geod to use for forward azimuth and distance calculations. If None, use a + default spherical ellipsoid. Returns ------- @@ -819,11 +820,10 @@ def lat_lon_grid_deltas(longitude, latitude, x_dim=-1, y_dim=-2, **kwargs): take_y = make_take(latitude.ndim, y_dim) take_x = make_take(latitude.ndim, x_dim) - geod_args = {'ellps': 'sphere'} - if kwargs: - geod_args = kwargs - - g = Geod(**geod_args) + if geod is None: + g = Geod(ellps='sphere') + else: + g = geod forward_az, _, dy = g.inv(longitude[take_y(slice(None, -1))], latitude[take_y(slice(None, -1))], diff --git a/src/metpy/interpolate/slices.py b/src/metpy/interpolate/slices.py index 053644fb2ce..ca204b67530 100644 --- a/src/metpy/interpolate/slices.py +++ b/src/metpy/interpolate/slices.py @@ -74,8 +74,8 @@ def geodesic(crs, start, end, steps): Parameters ---------- - crs: `cartopy.crs` - Cartopy Coordinate Reference System to use for the output + crs: `pyproj.CRS` + PyProj Coordinate Reference System to use for the output start: (2, ) array_like A latitude-longitude pair designating the start point of the geodesic (units are degrees north and degrees east). @@ -96,18 +96,19 @@ def geodesic(crs, start, end, steps): cross_section """ - import cartopy.crs as ccrs - from pyproj import Geod + from pyproj import Proj + + g = crs.get_geod() + p = Proj(crs) # Geod.npts only gives points *in between* the start and end, and we want to include # the endpoints. - g = Geod(crs.proj4_init) geodesic = np.concatenate([ np.array(start[::-1])[None], np.array(g.npts(start[1], start[0], end[1], end[0], steps - 2)), np.array(end[::-1])[None] ]).transpose() - points = crs.transform_points(ccrs.Geodetic(), *geodesic)[:, :2] + points = np.stack(p(geodesic[0], geodesic[1], inverse=False, radians=False), axis=-1) return points @@ -162,7 +163,7 @@ def cross_section(data, start, end, steps=100, interp_type='linear'): # Get the projection and coordinates try: - crs_data = data.metpy.cartopy_crs + crs_data = data.metpy.pyproj_crs x = data.metpy.x except AttributeError: raise ValueError('Data missing required coordinate information. Verify that ' diff --git a/src/metpy/plots/mapping.py b/src/metpy/plots/mapping.py index d8cb9e4162e..9249098d194 100644 --- a/src/metpy/plots/mapping.py +++ b/src/metpy/plots/mapping.py @@ -77,6 +77,12 @@ def to_cartopy(self): return proj_handler(self._attrs, globe) + def to_pyproj(self): + """Convert to a PyProj CRS.""" + import pyproj + + return pyproj.CRS.from_cf(self._attrs) + def to_dict(self): """Get the dictionary of metadata attributes.""" return self._attrs.copy() diff --git a/src/metpy/xarray.py b/src/metpy/xarray.py index 1d2cc5645a2..9beffe38f30 100644 --- a/src/metpy/xarray.py +++ b/src/metpy/xarray.py @@ -213,8 +213,8 @@ def dequantify(self): @property def crs(self): """Return the coordinate reference system (CRS) as a CFProjection object.""" - if 'crs' in self._data_array.coords: - return self._data_array.coords['crs'].item() + if 'metpy_crs' in self._data_array.coords: + return self._data_array.coords['metpy_crs'].item() raise AttributeError('crs attribute is not available.') @property @@ -232,6 +232,11 @@ def cartopy_geodetic(self): """Return the Geodetic CRS associated with the native CRS globe.""" return self.crs.cartopy_geodetic + @property + def pyproj_crs(self): + """Return the coordinate reference system (CRS) as a pyproj object.""" + return self.crs.to_pyproj() + def _fixup_coordinate_map(self, coord_map): """Ensure sure we have coordinate variables in map, not coordinate names.""" new_coord_map = {} @@ -571,7 +576,7 @@ def assign_latitude_longitude(self, force=False): Notes ----- - A valid CRS coordinate must be present. Cartopy is used for the coordinate + A valid CRS coordinate must be present. PyProj is used for the coordinate transformations. """ @@ -607,7 +612,7 @@ def assign_y_x(self, force=False, tolerance=None): Notes ----- - A valid CRS coordinate must be present. Cartopy is used for the coordinate + A valid CRS coordinate must be present. PyProj is used for the coordinate transformations. """ @@ -634,7 +639,7 @@ class MetPyDatasetAccessor: >>> import xarray as xr >>> from metpy.cbook import get_test_data >>> ds = xr.open_dataset(get_test_data('narr_example.nc', False)).metpy.parse_cf() - >>> print(ds['crs'].item()) + >>> print(ds['metpy_crs'].item()) Projection: lambert_conformal_conic """ @@ -676,6 +681,22 @@ def parse_cf(self, varname=None, coordinates=None): var = self._dataset[varname] + # Check for crs conflict + if varname == 'metpy_crs': + warnings.warn( + 'Attempting to parse metpy_crs as a data variable. Unexpected merge conflicts ' + 'may occur.' + ) + elif 'metpy_crs' in var.coords: + try: + assert isinstance(var.coords['metpy_crs'].item(), CFProjection) + except (ValueError, AssertionError): + # Catch non-scalar and non-CFProjection coordinates + warnings.warn( + 'metpy_crs already present as a non-CFProjection coordinate. Unexpected ' + 'merge conflicts may occur.' + ) + # Assign coordinates if the coordinates argument is given if coordinates is not None: var = var.metpy.assign_coordinates(coordinates) @@ -710,7 +731,7 @@ def _has_coord(coord_type): # Rebuild the coordinates of the dataarray, and return quantified DataArray var = self._rebuild_coords(var, crs) if crs is not None: - var = var.assign_coords(coords={'crs': crs}) + var = var.assign_coords(coords={'metpy_crs': crs}) return var def _rebuild_coords(self, var, crs): @@ -796,7 +817,7 @@ def assign_latitude_longitude(self, force=False): Notes ----- - A valid CRS coordinate must be present. Cartopy is used for the coordinate + A valid CRS coordinate must be present. PyProj is used for the coordinate transformations. """ @@ -841,7 +862,7 @@ def assign_y_x(self, force=False, tolerance=None): Notes ----- - A valid CRS coordinate must be present. Cartopy is used for the coordinate + A valid CRS coordinate must be present. PyProj is used for the coordinate transformations. """ @@ -995,14 +1016,16 @@ def _assign_crs(xarray_object, cf_attributes, cf_kwargs): attrs = cf_attributes if cf_attributes is not None else cf_kwargs # Assign crs coordinate to xarray object - return xarray_object.assign_coords(crs=CFProjection(attrs)) + return xarray_object.assign_coords(metpy_crs=CFProjection(attrs)) def _build_latitude_longitude(da): """Build latitude/longitude coordinates from DataArray's y/x coordinates.""" + from pyproj import Proj + y, x = da.metpy.coordinates('y', 'x') xx, yy = np.meshgrid(x.values, y.values) - lonlats = da.metpy.cartopy_geodetic.transform_points(da.metpy.cartopy_crs, xx, yy) + lonlats = np.stack(Proj(da.metpy.pyproj_crs)(xx, yy, inverse=True, radians=False), axis=-1) longitude = xr.DataArray(lonlats[..., 0], dims=(y.name, x.name), coords={y.name: y, x.name: x}, attrs={'units': 'degrees_east', 'standard_name': 'longitude'}) @@ -1014,6 +1037,8 @@ def _build_latitude_longitude(da): def _build_y_x(da, tolerance): """Build y/x coordinates from DataArray's latitude/longitude coordinates.""" + from pyproj import Proj + # Initial sanity checks latitude, longitude = da.metpy.coordinates('latitude', 'longitude') if latitude.dims != longitude.dims: @@ -1023,9 +1048,12 @@ def _build_y_x(da, tolerance): 'must be 2D') # Convert to projected y/x - xxyy = da.metpy.cartopy_crs.transform_points(da.metpy.cartopy_geodetic, - longitude.values, - latitude.values) + xxyy = np.stack(Proj(da.metpy.pyproj_crs)( + longitude.values, + latitude.values, + inverse=False, + radians=False + ), axis=-1) # Handle tolerance tolerance = 1 if tolerance is None else tolerance.m_as('m') @@ -1052,7 +1080,7 @@ def _build_y_x(da, tolerance): else: raise ValueError('Projected y and x coordinates cannot be collapsed to 1D within ' 'tolerance. Verify that your latitude and longitude coordinates ' - 'correpsond to your CRS coordinate.') + 'correspond to your CRS coordinate.') def preprocess_and_wrap(broadcast=None, wrap_like=None, match_unit=False, to_magnitude=False): @@ -1293,7 +1321,7 @@ def grid_deltas_from_dataarray(f, kind='default'): (dx_var, dx_units), (dy_var, dy_units) = ( (xr.Variable(dims=latitude.dims, data=deltas.magnitude), deltas.units) for deltas in lat_lon_grid_deltas(longitude, latitude, x_dim=x_dim, y_dim=y_dim, - initstring=f.metpy.cartopy_crs.proj4_init)) + geod=f.metpy.pyproj_crs.get_geod())) else: # Obtain y/x coordinate differences y, x = f.metpy.coordinates('y', 'x') diff --git a/tests/calc/test_calc_tools.py b/tests/calc/test_calc_tools.py index 299ae865bcd..268dc044861 100644 --- a/tests/calc/test_calc_tools.py +++ b/tests/calc/test_calc_tools.py @@ -498,9 +498,11 @@ def test_lat_lon_grid_deltas_mismatched_shape(): @needs_pyproj def test_lat_lon_grid_deltas_geod_kwargs(): """Test that geod kwargs are overridden by users #774.""" + from pyproj import Geod + lat = np.arange(40, 50, 2.5) lon = np.arange(-100, -90, 2.5) - dx, dy = lat_lon_grid_deltas(lon, lat, a=4370997) + dx, dy = lat_lon_grid_deltas(lon, lat, geod=Geod(a=4370997)) dx_truth = np.array([[146095.76101984, 146095.76101984, 146095.76101984], [140608.9751528, 140608.9751528, 140608.9751528], [134854.56713287, 134854.56713287, 134854.56713287], @@ -997,7 +999,7 @@ def test_first_derivative_xarray_lonlat(test_da_lonlat): coords=(('lat', test_da_lonlat['lat']),) ) _, truth = xr.broadcast(test_da_lonlat, partial) - truth.coords['crs'] = test_da_lonlat['crs'] + truth.coords['metpy_crs'] = test_da_lonlat['metpy_crs'] truth.attrs['units'] = 'kelvin / meter' truth = truth.metpy.quantify() @@ -1051,7 +1053,7 @@ def test_second_derivative_xarray_lonlat(test_da_lonlat): coords=(('lat', test_da_lonlat['lat']),) ) _, truth = xr.broadcast(test_da_lonlat, partial) - truth.coords['crs'] = test_da_lonlat['crs'] + truth.coords['metpy_crs'] = test_da_lonlat['metpy_crs'] truth.attrs['units'] = 'kelvin / meter^2' truth = truth.metpy.quantify() @@ -1079,7 +1081,7 @@ def test_gradient_xarray(test_da_xy): coords=(('isobaric', test_da_xy['isobaric']),) ) _, truth_p = xr.broadcast(test_da_xy, partial) - truth_p.coords['crs'] = test_da_xy['crs'] + truth_p.coords['metpy_crs'] = test_da_xy['metpy_crs'] truth_p.attrs['units'] = 'kelvin / hectopascal' truth_p = truth_p.metpy.quantify() @@ -1163,7 +1165,7 @@ def test_laplacian_xarray_lonlat(test_da_lonlat): coords=(('lat', test_da_lonlat['lat']),) ) _, truth = xr.broadcast(test_da_lonlat, partial) - truth.coords['crs'] = test_da_lonlat['crs'] + truth.coords['metpy_crs'] = test_da_lonlat['metpy_crs'] truth.attrs['units'] = 'kelvin / meter^2' truth = truth.metpy.quantify() diff --git a/tests/calc/test_cross_sections.py b/tests/calc/test_cross_sections.py index 259a25d59d3..c2b09f477d9 100644 --- a/tests/calc/test_cross_sections.py +++ b/tests/calc/test_cross_sections.py @@ -119,7 +119,7 @@ def test_distances_from_cross_section_given_lonlat(test_cross_lonlat): true_x = xr.DataArray( true_x_values * units.meters, coords={ - 'crs': test_cross_lonlat['crs'], + 'metpy_crs': test_cross_lonlat['metpy_crs'], 'lat': test_cross_lonlat['lat'], 'lon': test_cross_lonlat['lon'], 'index': index, @@ -129,7 +129,7 @@ def test_distances_from_cross_section_given_lonlat(test_cross_lonlat): true_y = xr.DataArray( true_y_values * units.meters, coords={ - 'crs': test_cross_lonlat['crs'], + 'metpy_crs': test_cross_lonlat['metpy_crs'], 'lat': test_cross_lonlat['lat'], 'lon': test_cross_lonlat['lon'], 'index': index, @@ -168,7 +168,7 @@ def test_latitude_from_cross_section_given_y(test_cross_xy): true_latitude = xr.DataArray( true_latitude_values * units.degrees_north, coords={ - 'crs': test_cross_xy['crs'], + 'metpy_crs': test_cross_xy['metpy_crs'], 'y': test_cross_xy['y'], 'x': test_cross_xy['x'], 'index': index, diff --git a/tests/calc/test_kinematics.py b/tests/calc/test_kinematics.py index 7ec51385da4..64b83ea94e8 100644 --- a/tests/calc/test_kinematics.py +++ b/tests/calc/test_kinematics.py @@ -16,7 +16,7 @@ total_deformation, vorticity, wind_components) from metpy.constants import g, Re from metpy.testing import (assert_almost_equal, assert_array_almost_equal, assert_array_equal, - get_test_data, needs_cartopy, needs_pyproj) + get_test_data, needs_pyproj) from metpy.units import concatenate, units @@ -1057,7 +1057,7 @@ def test_q_vector_with_static_stability(q_vector_data): @pytest.fixture -@needs_cartopy +@needs_pyproj def data_4d(): """Define 4D data (extracted from Irma GFS example) for testing kinematics functions.""" data = xr.open_dataset(get_test_data('irma_gfs_example.nc', False)) diff --git a/tests/interpolate/test_slices.py b/tests/interpolate/test_slices.py index 5f51a9216a1..d992a928ff0 100644 --- a/tests/interpolate/test_slices.py +++ b/tests/interpolate/test_slices.py @@ -110,7 +110,7 @@ def test_interpolate_to_slice_against_selection(test_ds_lonlat): @needs_cartopy def test_geodesic(test_ds_xy): """Test the geodesic construction.""" - crs = test_ds_xy['temperature'].metpy.cartopy_crs + crs = test_ds_xy['temperature'].metpy.pyproj_crs path = geodesic(crs, (36.46, -112.45), (42.95, -68.74), 7) truth = np.array([[-4.99495719e+05, -1.49986599e+06], [9.84044354e+04, -1.26871737e+06], @@ -150,7 +150,7 @@ def test_cross_section_dataarray_and_linear_interp(test_ds_xy): truth_values_x, name='x', coords={ - 'crs': data['crs'], + 'metpy_crs': data['metpy_crs'], 'y': (['index'], truth_values_y), 'x': (['index'], truth_values_x), 'index': index, @@ -161,7 +161,7 @@ def test_cross_section_dataarray_and_linear_interp(test_ds_xy): truth_values_y, name='y', coords={ - 'crs': data['crs'], + 'metpy_crs': data['metpy_crs'], 'y': (['index'], truth_values_y), 'x': (['index'], truth_values_x), 'index': index, @@ -175,7 +175,7 @@ def test_cross_section_dataarray_and_linear_interp(test_ds_xy): 'time': data['time'], 'isobaric': data['isobaric'], 'index': index, - 'crs': data['crs'], + 'metpy_crs': data['metpy_crs'], 'y': data_truth_y, 'x': data_truth_x }, @@ -216,7 +216,7 @@ def test_cross_section_dataset_and_nearest_interp(test_ds_lonlat): coords={ 'isobaric': test_ds_lonlat['isobaric'], 'index': index, - 'crs': test_ds_lonlat['crs'], + 'metpy_crs': test_ds_lonlat['metpy_crs'], 'lat': (['index'], truth_values_lat), 'lon': (['index'], truth_values_lon) }, @@ -242,7 +242,7 @@ def test_cross_section_error_on_missing_coordinate(test_ds_lonlat): """Test that the proper error is raised with missing coordinate.""" # Use a variable with no crs coordinate data_bad = test_ds_lonlat['temperature'].copy() - del data_bad['crs'] + del data_bad['metpy_crs'] start, end = (30.5, 255.5), (44.5, 274.5) with pytest.raises(ValueError): diff --git a/tests/test_xarray.py b/tests/test_xarray.py index 6fa1537364d..4b6ed788b3b 100644 --- a/tests/test_xarray.py +++ b/tests/test_xarray.py @@ -10,7 +10,7 @@ from metpy.plots.mapping import CFProjection from metpy.testing import (assert_almost_equal, assert_array_almost_equal, assert_array_equal, - get_test_data, needs_cartopy) + get_test_data, needs_pyproj) from metpy.units import DimensionalityError, units from metpy.xarray import ( add_grid_arguments_from_xarray, @@ -219,7 +219,7 @@ def test_missing_grid_mapping(): ds = xr.Dataset({'data': data}) data_var = ds.metpy.parse_cf('data') - assert 'crs' in data_var.coords + assert 'metpy_crs' in data_var.coords def test_missing_grid_mapping_var(caplog): @@ -782,7 +782,7 @@ def test_assign_crs_dataarray_by_argument(test_ds_generic, ccrs): da = test_ds_generic['test'] new_da = da.metpy.assign_crs(sample_cf_attrs) assert isinstance(new_da.metpy.cartopy_crs, ccrs.LambertConformal) - assert new_da['crs'] == CFProjection(sample_cf_attrs) + assert new_da['metpy_crs'] == CFProjection(sample_cf_attrs) def test_assign_crs_dataarray_by_kwargs(test_ds_generic, ccrs): @@ -790,21 +790,21 @@ def test_assign_crs_dataarray_by_kwargs(test_ds_generic, ccrs): da = test_ds_generic['test'] new_da = da.metpy.assign_crs(**sample_cf_attrs) assert isinstance(new_da.metpy.cartopy_crs, ccrs.LambertConformal) - assert new_da['crs'] == CFProjection(sample_cf_attrs) + assert new_da['metpy_crs'] == CFProjection(sample_cf_attrs) def test_assign_crs_dataset_by_argument(test_ds_generic, ccrs): """Test assigning CRS to Dataset by projection dict.""" new_ds = test_ds_generic.metpy.assign_crs(sample_cf_attrs) assert isinstance(new_ds['test'].metpy.cartopy_crs, ccrs.LambertConformal) - assert new_ds['crs'] == CFProjection(sample_cf_attrs) + assert new_ds['metpy_crs'] == CFProjection(sample_cf_attrs) def test_assign_crs_dataset_by_kwargs(test_ds_generic, ccrs): """Test assigning CRS to Dataset by projection kwargs.""" new_ds = test_ds_generic.metpy.assign_crs(**sample_cf_attrs) assert isinstance(new_ds['test'].metpy.cartopy_crs, ccrs.LambertConformal) - assert new_ds['crs'] == CFProjection(sample_cf_attrs) + assert new_ds['metpy_crs'] == CFProjection(sample_cf_attrs) def test_assign_crs_error_with_both_attrs(test_ds_generic): @@ -842,7 +842,7 @@ def test_coord_helper_da_yx(): dims=('y', 'x'), coords={'y': np.linspace(0, 1e5, 3), 'x': np.linspace(-1e5, 0, 3), - 'crs': CFProjection(sample_cf_attrs)}) + 'metpy_crs': CFProjection(sample_cf_attrs)}) @pytest.fixture @@ -874,7 +874,7 @@ def test_coord_helper_da_latlon(): ), dims=('y', 'x') ), - 'crs': CFProjection(sample_cf_attrs) + 'metpy_crs': CFProjection(sample_cf_attrs) } ) @@ -885,7 +885,7 @@ def test_coord_helper_da_dummy_yx(test_coord_helper_da_latlon): return test_coord_helper_da_latlon.assign_coords(y=range(3), x=range(3)) -@needs_cartopy +@needs_pyproj def test_assign_latitude_longitude_basic_dataarray(test_coord_helper_da_yx, test_coord_helper_da_latlon): """Test assign_latitude_longitude in basic usage on DataArray.""" @@ -905,7 +905,7 @@ def test_assign_latitude_longitude_error_existing_dataarray( assert 'Latitude/longitude coordinate(s) are present' in str(exc) -@needs_cartopy +@needs_pyproj def test_assign_latitude_longitude_force_existing_dataarray( test_coord_helper_da_dummy_latlon, test_coord_helper_da_latlon): """Test assign_latitude_longitude with existing coordinates forcing new.""" @@ -917,7 +917,7 @@ def test_assign_latitude_longitude_force_existing_dataarray( lon.values, 3) -@needs_cartopy +@needs_pyproj def test_assign_latitude_longitude_basic_dataset(test_coord_helper_da_yx, test_coord_helper_da_latlon): """Test assign_latitude_longitude in basic usage on Dataset.""" @@ -929,7 +929,7 @@ def test_assign_latitude_longitude_basic_dataset(test_coord_helper_da_yx, lon.values, 3) -@needs_cartopy +@needs_pyproj def test_assign_y_x_basic_dataarray(test_coord_helper_da_yx, test_coord_helper_da_latlon): """Test assign_y_x in basic usage on DataArray.""" new_da = test_coord_helper_da_latlon.metpy.assign_y_x() @@ -946,7 +946,7 @@ def test_assign_y_x_error_existing_dataarray( assert 'y/x coordinate(s) are present' in str(exc) -@needs_cartopy +@needs_pyproj def test_assign_y_x_force_existing_dataarray( test_coord_helper_da_dummy_yx, test_coord_helper_da_yx): """Test assign_y_x with existing coordinates forcing new.""" @@ -956,7 +956,7 @@ def test_assign_y_x_force_existing_dataarray( np.testing.assert_array_almost_equal(test_coord_helper_da_yx['x'].values, x.values, 3) -@needs_cartopy +@needs_pyproj def test_assign_y_x_dataarray_outside_tolerance(test_coord_helper_da_latlon): """Test assign_y_x raises ValueError when tolerance is exceeded on DataArray.""" with pytest.raises(ValueError) as exc: @@ -964,7 +964,7 @@ def test_assign_y_x_dataarray_outside_tolerance(test_coord_helper_da_latlon): assert 'cannot be collapsed to 1D within tolerance' in str(exc) -@needs_cartopy +@needs_pyproj def test_assign_y_x_dataarray_transposed(test_coord_helper_da_yx, test_coord_helper_da_latlon): """Test assign_y_x on DataArray with transposed order.""" new_da = test_coord_helper_da_latlon.transpose(transpose_coords=True).metpy.assign_y_x() @@ -973,7 +973,7 @@ def test_assign_y_x_dataarray_transposed(test_coord_helper_da_yx, test_coord_hel np.testing.assert_array_almost_equal(test_coord_helper_da_yx['x'].values, x.values, 3) -@needs_cartopy +@needs_pyproj def test_assign_y_x_dataset_assumed_order(test_coord_helper_da_yx, test_coord_helper_da_latlon): """Test assign_y_x on Dataset where order must be assumed.""" @@ -1314,31 +1314,36 @@ def test_grid_deltas_from_dataarray_xy(test_da_xy): assert_array_almost_equal(dy, true_dy, 5) +@needs_pyproj def test_grid_deltas_from_dataarray_actual_xy(test_da_xy, ccrs): """Test grid_deltas_from_dataarray with a xy grid and kind='actual'.""" # Construct lon/lat coordinates + from pyproj import Proj y, x = xr.broadcast(*test_da_xy.metpy.coordinates('y', 'x')) - lonlat = (ccrs.Geodetic(test_da_xy.metpy.cartopy_globe) - .transform_points(test_da_xy.metpy.cartopy_crs, x.values, y.values)) - lon = lonlat[..., 0] - lat = lonlat[..., 1] + lon, lat = Proj(test_da_xy.metpy.pyproj_crs)( + x.values, + y.values, + inverse=True, + radians=False + ) test_da_xy = test_da_xy.assign_coords( longitude=xr.DataArray(lon, dims=('y', 'x'), attrs={'units': 'degrees_east'}), latitude=xr.DataArray(lat, dims=('y', 'x'), attrs={'units': 'degrees_north'})) # Actually test calculation dx, dy = grid_deltas_from_dataarray(test_da_xy, kind='actual') - true_dx = [[[[494426.3249766, 493977.6028005, 493044.0656467], - [498740.2046073, 498474.9771064, 497891.6588559], - [500276.2649627, 500256.3440237, 500139.9484845], - [498740.6956936, 499045.0391707, 499542.7244501]]]] * units.m - true_dy = [[[[496862.4106337, 496685.4729999, 496132.0732114, 495137.8882404], - [499774.9676486, 499706.3354977, 499467.5546773, 498965.2587818], - [499750.8962991, 499826.2263137, 500004.4977747, 500150.9897759]]]] * units.m - assert_array_almost_equal(dx, true_dx, 3) - assert_array_almost_equal(dy, true_dy, 3) - - + true_dx = [[[[494152.626, 493704.152, 492771.132], + [498464.118, 498199.037, 497616.042], + [499999.328, 499979.418, 499863.087], + [498464.608, 498768.783, 499266.193]]]] * units.m + true_dy = [[[[496587.363, 496410.523, 495857.430, 494863.795], + [499498.308, 499429.714, 499191.065, 498689.047], + [499474.250, 499549.538, 499727.711, 499874.122]]]] * units.m + assert_array_almost_equal(dx, true_dx, 2) + assert_array_almost_equal(dy, true_dy, 2) + + +@needs_pyproj def test_grid_deltas_from_dataarray_nominal_lonlat(test_da_lonlat): """Test grid_deltas_from_dataarray with a lonlat grid and kind='nominal'.""" dx, dy = grid_deltas_from_dataarray(test_da_lonlat, kind='nominal') @@ -1348,7 +1353,7 @@ def test_grid_deltas_from_dataarray_nominal_lonlat(test_da_lonlat): assert_array_almost_equal(dy, true_dy, 5) -@needs_cartopy +@needs_pyproj def test_grid_deltas_from_dataarray_lonlat_assumed_order(): """Test grid_deltas_from_dataarray when dim order must be assumed.""" # Create test dataarray @@ -1384,7 +1389,7 @@ def test_grid_deltas_from_dataarray_invalid_kind(test_da_xy): grid_deltas_from_dataarray(test_da_xy, kind='invalid') -@needs_cartopy +@needs_pyproj def test_add_grid_arguments_from_dataarray(): """Test the grid argument decorator for adding in arguments from xarray.""" @add_grid_arguments_from_xarray diff --git a/tutorials/xarray_tutorial.py b/tutorials/xarray_tutorial.py index 363eacadc33..2e6b85fbdf3 100644 --- a/tutorials/xarray_tutorial.py +++ b/tutorials/xarray_tutorial.py @@ -151,15 +151,21 @@ # Getting the cartopy coordinate reference system (CRS) of the projection of a DataArray is as # straightforward as using the ``data_var.metpy.cartopy_crs`` property: -data_crs = data['temperature'].metpy.cartopy_crs -print(data_crs) +cartopy_crs = data['temperature'].metpy.cartopy_crs +print(cartopy_crs) + +######################################################################### +# Likewise, the PyProj CRS can be obtained with the ``.pyproj_crs`` property: + +pyproj_crs = data['temperature'].metpy.pyproj_crs +print(pyproj_crs) ######################################################################### # The cartopy ``Globe`` can similarly be accessed via the ``data_var.metpy.cartopy_globe`` # property: -data_globe = data['temperature'].metpy.cartopy_globe -print(data_globe) +cartopy_globe = data['temperature'].metpy.cartopy_globe +print(cartopy_globe) ######################################################################### # Calculations @@ -179,7 +185,7 @@ # As an example, we calculate geostropic wind at 500 hPa below: lat, lon = xr.broadcast(y, x) -dx, dy = mpcalc.lat_lon_grid_deltas(lon, lat, initstring=data_crs.proj4_init) +dx, dy = mpcalc.lat_lon_grid_deltas(lon, lat, geod=pyproj_crs.get_geod()) heights = data['height'].metpy.loc[{'time': time[0], 'vertical': 500. * units.hPa}] u_geo, v_geo = mpcalc.geostrophic_wind(heights, dx, dy, lat) print(u_geo) @@ -242,7 +248,7 @@ # Let's add a projection and coastlines to it ax = plt.axes(projection=ccrs.LambertConformal()) data['height'].metpy.loc[{'time': time[0], - 'vertical': 500. * units.hPa}].plot(ax=ax, transform=data_crs) + 'vertical': 500. * units.hPa}].plot(ax=ax, transform=cartopy_crs) ax.coastlines() plt.show() @@ -254,7 +260,7 @@ data_level = data.metpy.loc[{time.name: time[0], vertical.name: 500. * units.hPa}] # Create the matplotlib figure and axis -fig, ax = plt.subplots(1, 1, figsize=(12, 8), subplot_kw={'projection': data_crs}) +fig, ax = plt.subplots(1, 1, figsize=(12, 8), subplot_kw={'projection': cartopy_crs}) # Plot RH as filled contours rh = ax.contourf(x, y, data_level['relative_humidity'], levels=[70, 80, 90, 100], From da9a17c98c8cf6ebc89f79c257e374e36cb30cd6 Mon Sep 17 00:00:00 2001 From: Ryan May Date: Fri, 2 Oct 2020 13:32:30 -0600 Subject: [PATCH 2/9] API: Make azimuth_range_to_lat_lon take a pyproj.Geod --- src/metpy/calc/tools.py | 24 +++++++++++------------- tests/calc/test_calc_tools.py | 7 ++++--- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/metpy/calc/tools.py b/src/metpy/calc/tools.py index 21cbb09b6c6..15ed2069369 100644 --- a/src/metpy/calc/tools.py +++ b/src/metpy/calc/tools.py @@ -8,6 +8,7 @@ import numpy as np from numpy.core.numeric import normalize_axis_index import numpy.ma as ma +from pyproj import Geod from scipy.spatial import cKDTree import xarray as xr @@ -778,8 +779,8 @@ def lat_lon_grid_deltas(longitude, latitude, x_dim=-1, y_dim=-2, geod=None): axis number for the x dimension, defaults to -1. y_dim : int axis number for the y dimesion, defaults to -2. - geod : pyproj.Geod or None - PyProj Geod to use for forward azimuth and distance calculations. If None, use a + geod : `pyproj.Geod` or ``None`` + PyProj Geod to use for forward azimuth and distance calculations. If ``None``, use a default spherical ellipsoid. Returns @@ -798,8 +799,6 @@ def lat_lon_grid_deltas(longitude, latitude, x_dim=-1, y_dim=-2, geod=None): array-like type). It will also "densify" your data if using Dask or lazy-loading. """ - from pyproj import Geod - # Inputs must be the same number of dimensions if latitude.ndim != longitude.ndim: raise ValueError('Latitude and longitude must have the same number of dimensions.') @@ -842,7 +841,7 @@ def lat_lon_grid_deltas(longitude, latitude, x_dim=-1, y_dim=-2, geod=None): @exporter.export @preprocess_and_wrap() -def azimuth_range_to_lat_lon(azimuths, ranges, center_lon, center_lat, **kwargs): +def azimuth_range_to_lat_lon(azimuths, ranges, center_lon, center_lat, geod=None): """Convert azimuth and range locations in a polar coordinate system to lat/lon coordinates. Pole refers to the origin of the coordinate system. @@ -858,8 +857,9 @@ def azimuth_range_to_lat_lon(azimuths, ranges, center_lon, center_lat, **kwargs) The latitude of the pole in decimal degrees center_lon : float The longitude of the pole in decimal degrees - kwargs - arbitrary keyword arguments to pass to pyproj.Geod (e.g. 'ellps') + geod : `pyproj.Geod` or ``None`` + PyProj Geod to use for forward azimuth and distance calculations. If ``None``, use a + default spherical ellipsoid. Returns ------- @@ -870,12 +870,10 @@ def azimuth_range_to_lat_lon(azimuths, ranges, center_lon, center_lat, **kwargs) Credit to Brian Blaylock for the original implementation. """ - from pyproj import Geod - - geod_args = {'ellps': 'sphere'} - if kwargs: - geod_args = kwargs - g = Geod(**geod_args) + if geod is None: + g = Geod(ellps='sphere') + else: + g = geod rng2d, az2d = np.meshgrid(ranges, azimuths) lats = np.full(az2d.shape, center_lat) diff --git a/tests/calc/test_calc_tools.py b/tests/calc/test_calc_tools.py index 268dc044861..a8b93c2d9e5 100644 --- a/tests/calc/test_calc_tools.py +++ b/tests/calc/test_calc_tools.py @@ -911,13 +911,14 @@ def test_azimuth_range_to_lat_lon(): @needs_pyproj def test_azimuth_range_to_lat_lon_diff_ellps(): - """Test converstion of azimuth and range to lat/lon grid.""" + """Test conversion of azimuth and range to lat/lon grid.""" + from pyproj import Geod + az = [332.2403, 334.6765, 337.2528, 339.73846, 342.26257] rng = [2125., 64625., 127125., 189625., 252125., 314625.] clon = -89.98416666666667 clat = 32.27972222222222 - kwargs = {'ellps': 'WGS84'} - output_lon, output_lat = azimuth_range_to_lat_lon(az, rng, clon, clat, **kwargs) + output_lon, output_lat = azimuth_range_to_lat_lon(az, rng, clon, clat, Geod(ellps='WGS84')) true_lon = [[-89.9946749, -90.3055083, -90.6198256, -90.9377279, -91.2593193, -91.5847066], [-89.9938168, -90.279303, -90.5680603, -90.860187, -91.1557841, From 53776c1ef7b2e022a7a50355ea24e3268355d241 Mon Sep 17 00:00:00 2001 From: Ryan May Date: Fri, 2 Oct 2020 13:50:19 -0600 Subject: [PATCH 3/9] MNT: Make PyProj>=2.3 a required dependency --- ci/Current.txt | 13 +++++++------ ci/Minimum | 13 +++++++------ ci/Prerelease | 5 +++-- ci/extra_requirements.txt | 1 - conftest.py | 15 +++++++-------- docs/installguide.rst | 9 +++++---- setup.cfg | 11 ++++++----- src/metpy/testing.py | 13 ------------- src/metpy/xarray.py | 5 +---- tests/calc/test_calc_tools.py | 17 +++-------------- tests/calc/test_kinematics.py | 8 +------- tests/io/test_gini.py | 3 --- tests/plots/test_declarative.py | 4 +--- tests/test_xarray.py | 14 +------------- 14 files changed, 42 insertions(+), 89 deletions(-) diff --git a/ci/Current.txt b/ci/Current.txt index 01f4829a9f5..46a88ad682a 100644 --- a/ci/Current.txt +++ b/ci/Current.txt @@ -1,10 +1,11 @@ +importlib_metadata==2.0.0 +importlib_resources==3.0.0 matplotlib==3.3.2 numpy==1.19.1 -scipy==1.5.2 +pandas==1.1.3 +pooch==1.2.0 pint==0.16.1 -xarray==0.16.1 +pyproj==2.6.1.post1 +scipy==1.5.2 traitlets==4.3.3 -pooch==1.2.0 -pandas==1.1.3 -importlib_metadata==2.0.0 -importlib_resources==3.0.0 +xarray==0.16.1 diff --git a/ci/Minimum b/ci/Minimum index 44178b76dfc..b9597a41476 100644 --- a/ci/Minimum +++ b/ci/Minimum @@ -1,10 +1,11 @@ +importlib_metadata==1.0.0 +importlib_resources==1.3.0 matplotlib==2.1.0 numpy==1.16.0 -scipy==1.0.0 +pandas==0.22.0 pint==0.10.1 -xarray==0.14.1 -traitlets==4.3.0 pooch==0.1 -pandas==0.22.0 -importlib_metadata==1.0.0 -importlib_resources==1.3.0 +pyproj==2.3.0 +scipy==1.0.0 +traitlets==4.3.0 +xarray==0.14.1 diff --git a/ci/Prerelease b/ci/Prerelease index ff280af5fe0..ce2178a214c 100644 --- a/ci/Prerelease +++ b/ci/Prerelease @@ -1,6 +1,7 @@ matplotlib>=0.0.dev0 numpy>=0.0.dev0 -scipy>=0.0.dev0 -traitlets>=0.0.dev0 pooch>=0.0.dev0 pandas>=0.0.dev0 +pyproj>=0.0.dev0 +scipy>=0.0.dev0 +traitlets>=0.0.dev0 diff --git a/ci/extra_requirements.txt b/ci/extra_requirements.txt index e2080645e89..e4e75c6d077 100644 --- a/ci/extra_requirements.txt +++ b/ci/extra_requirements.txt @@ -1,2 +1 @@ cartopy==0.18.0 -pyproj==2.6.1.post1 diff --git a/conftest.py b/conftest.py index 6c981d5791e..204a1e61987 100644 --- a/conftest.py +++ b/conftest.py @@ -10,6 +10,7 @@ import numpy import pandas import pooch +import pyproj import pytest import scipy import traitlets @@ -25,10 +26,10 @@ def pytest_report_header(config, startdir): """Add dependency information to pytest output.""" return (f'Dep Versions: Matplotlib {matplotlib.__version__}, ' - f'NumPy {numpy.__version__}, SciPy {scipy.__version__}, ' - f'Xarray {xarray.__version__}, Pint {pint.__version__}, ' - f'Pandas {pandas.__version__}, Traitlets {traitlets.__version__}, ' - f'Pooch {pooch.version.full_version}') + f'NumPy {numpy.__version__}, Pandas {pandas.__version__}, ' + f'Pint {pint.__version__}, Pooch {pooch.version.full_version}\n' + f'\tPyProj {pyproj.__version__}, SciPy {scipy.__version__}, ' + f'Traitlets {traitlets.__version__}, Xarray {xarray.__version__}') @pytest.fixture(autouse=True) @@ -45,6 +46,7 @@ def ccrs(): Any testing function/fixture that needs access to ``cartopy.crs`` can simply add this to their parameter list. + """ return pytest.importorskip('cartopy.crs') @@ -55,6 +57,7 @@ def cfeature(): Any testing function/fixture that needs access to ``cartopy.feature`` can simply add this to their parameter list. + """ return pytest.importorskip('cartopy.feature') @@ -62,8 +65,6 @@ def cfeature(): @pytest.fixture() def test_da_lonlat(): """Return a DataArray with a lon/lat grid and no time coordinate for use in tests.""" - pytest.importorskip('pyproj') - data = numpy.linspace(300, 250, 3 * 4 * 4).reshape((3, 4, 4)) ds = xarray.Dataset( {'temperature': (['isobaric', 'lat', 'lon'], data)}, @@ -96,8 +97,6 @@ def test_da_lonlat(): @pytest.fixture() def test_da_xy(): """Return a DataArray with a x/y grid and a time coordinate for use in tests.""" - pytest.importorskip('pyproj') - data = numpy.linspace(300, 250, 3 * 3 * 4 * 4).reshape((3, 3, 4, 4)) ds = xarray.Dataset( {'temperature': (['time', 'isobaric', 'y', 'x'], data), diff --git a/docs/installguide.rst b/docs/installguide.rst index 601d3bc5e37..e8462869ba6 100644 --- a/docs/installguide.rst +++ b/docs/installguide.rst @@ -10,12 +10,13 @@ years. For Python itself, that means supporting the last two minor releases. * matplotlib >= 2.1.0 * numpy >= 1.16.0 -* scipy >= 1.0.0 -* pint >= 0.10.1 * pandas >= 0.22.0 -* xarray >= 0.14.1 -* traitlets >= 4.3.0 +* pint >= 0.10.1 * pooch >= 0.1 +* pyproj >= 2.3.0 +* scipy >= 1.0.0 +* traitlets >= 4.3.0 +* xarray >= 0.14.1 ------------ Installation diff --git a/setup.cfg b/setup.cfg index ecdec2cf20d..1a568d8c7a1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -39,16 +39,17 @@ include_package_data = True setup_requires = setuptools_scm python_requires = >=3.6 install_requires = + importlib_metadata>=1.0.0; python_version < '3.8' + importlib_resources>=1.3.0; python_version < '3.9' matplotlib>=2.1.0 numpy>=1.16.0 - scipy>=1.0 + pandas>=0.22.0 pint>=0.10.1 - xarray>=0.14.1 pooch>=0.1 + pyproj>=2.3.0,<3.0 + scipy>=1.0 traitlets>=4.3.0 - pandas>=0.22.0 - importlib_metadata>=1.0.0; python_version < '3.8' - importlib_resources>=1.3.0; python_version < '3.9' + xarray>=0.14.1 [options.packages.find] where = src diff --git a/src/metpy/testing.py b/src/metpy/testing.py index 103dc28febb..d1556e35411 100644 --- a/src/metpy/testing.py +++ b/src/metpy/testing.py @@ -34,19 +34,6 @@ def wrapped(*args, **kwargs): return wrapped -def needs_pyproj(test_func): - """Decorate a test function or fixture as requiring PyProj. - - Will skip the decorated test, or any test using the decorated fixture, if ``pyproj`` is - unable to be imported. - """ - @functools.wraps(test_func) - def wrapped(*args, **kwargs): - pytest.importorskip('pyproj') - return test_func(*args, **kwargs) - return wrapped - - def get_upper_air_data(date, station): """Get upper air observations from the test data cache. diff --git a/src/metpy/xarray.py b/src/metpy/xarray.py index 9beffe38f30..644d65a3d64 100644 --- a/src/metpy/xarray.py +++ b/src/metpy/xarray.py @@ -22,6 +22,7 @@ import warnings import numpy as np +from pyproj import Proj import xarray as xr from ._vendor.xarray import either_dict_or_kwargs, expanded_indexer, is_dict_like @@ -1021,8 +1022,6 @@ def _assign_crs(xarray_object, cf_attributes, cf_kwargs): def _build_latitude_longitude(da): """Build latitude/longitude coordinates from DataArray's y/x coordinates.""" - from pyproj import Proj - y, x = da.metpy.coordinates('y', 'x') xx, yy = np.meshgrid(x.values, y.values) lonlats = np.stack(Proj(da.metpy.pyproj_crs)(xx, yy, inverse=True, radians=False), axis=-1) @@ -1037,8 +1036,6 @@ def _build_latitude_longitude(da): def _build_y_x(da, tolerance): """Build y/x coordinates from DataArray's latitude/longitude coordinates.""" - from pyproj import Proj - # Initial sanity checks latitude, longitude = da.metpy.coordinates('latitude', 'longitude') if latitude.dims != longitude.dims: diff --git a/tests/calc/test_calc_tools.py b/tests/calc/test_calc_tools.py index a8b93c2d9e5..a3c9be72880 100644 --- a/tests/calc/test_calc_tools.py +++ b/tests/calc/test_calc_tools.py @@ -8,6 +8,7 @@ import numpy as np import numpy.ma as ma import pandas as pd +from pyproj import Geod import pytest import xarray as xr @@ -20,8 +21,7 @@ _greater_or_close, _less_or_close, _next_non_masked_element, _remove_nans, azimuth_range_to_lat_lon, BASE_DEGREE_MULTIPLIER, DIR_STRS, UND) -from metpy.testing import (assert_almost_equal, assert_array_almost_equal, assert_array_equal, - needs_pyproj) +from metpy.testing import (assert_almost_equal, assert_array_almost_equal, assert_array_equal) from metpy.units import units from metpy.xarray import grid_deltas_from_dataarray @@ -422,7 +422,6 @@ def test_get_layer_heights_agl_bottom_no_interp(): assert_array_almost_equal(data_true, data, 6) -@needs_pyproj def test_lat_lon_grid_deltas_1d(): """Test for lat_lon_grid_deltas for variable grid.""" lat = np.arange(40, 50, 2.5) @@ -440,7 +439,6 @@ def test_lat_lon_grid_deltas_1d(): @pytest.mark.parametrize('flip_order', [(False, True)]) -@needs_pyproj def test_lat_lon_grid_deltas_2d(flip_order): """Test for lat_lon_grid_deltas for variable grid with negative delta distances.""" lat = np.arange(40, 50, 2.5) @@ -464,7 +462,6 @@ def test_lat_lon_grid_deltas_2d(flip_order): assert_almost_equal(dy, dy_truth, 4) -@needs_pyproj def test_lat_lon_grid_deltas_extra_dimensions(): """Test for lat_lon_grid_deltas with extra leading dimensions.""" lon, lat = np.meshgrid(np.arange(-100, -90, 2.5), np.arange(40, 50, 2.5)) @@ -483,7 +480,6 @@ def test_lat_lon_grid_deltas_extra_dimensions(): assert_almost_equal(dy, dy_truth, 4) -@needs_pyproj def test_lat_lon_grid_deltas_mismatched_shape(): """Test for lat_lon_grid_deltas for variable grid.""" lat = np.arange(40, 50, 2.5) @@ -495,11 +491,8 @@ def test_lat_lon_grid_deltas_mismatched_shape(): lat_lon_grid_deltas(lon, lat) -@needs_pyproj def test_lat_lon_grid_deltas_geod_kwargs(): """Test that geod kwargs are overridden by users #774.""" - from pyproj import Geod - lat = np.arange(40, 50, 2.5) lon = np.arange(-100, -90, 2.5) dx, dy = lat_lon_grid_deltas(lon, lat, geod=Geod(a=4370997)) @@ -877,9 +870,8 @@ def test_angle_to_direction_level_1(): assert_array_equal(output_dirs, expected_dirs) -@needs_pyproj def test_azimuth_range_to_lat_lon(): - """Test converstion of azimuth and range to lat/lon grid.""" + """Test conversion of azimuth and range to lat/lon grid.""" az = [332.2403, 334.6765, 337.2528, 339.73846, 342.26257] rng = [2125., 64625., 127125., 189625., 252125., 314625.] clon = -89.98416666666667 @@ -909,11 +901,8 @@ def test_azimuth_range_to_lat_lon(): assert_array_almost_equal(output_lat, true_lat, 6) -@needs_pyproj def test_azimuth_range_to_lat_lon_diff_ellps(): """Test conversion of azimuth and range to lat/lon grid.""" - from pyproj import Geod - az = [332.2403, 334.6765, 337.2528, 339.73846, 342.26257] rng = [2125., 64625., 127125., 189625., 252125., 314625.] clon = -89.98416666666667 diff --git a/tests/calc/test_kinematics.py b/tests/calc/test_kinematics.py index 64b83ea94e8..1b0c8482e8b 100644 --- a/tests/calc/test_kinematics.py +++ b/tests/calc/test_kinematics.py @@ -16,7 +16,7 @@ total_deformation, vorticity, wind_components) from metpy.constants import g, Re from metpy.testing import (assert_almost_equal, assert_array_almost_equal, assert_array_equal, - get_test_data, needs_pyproj) + get_test_data) from metpy.units import concatenate, units @@ -466,7 +466,6 @@ def test_absolute_vorticity_asym(): @pytest.fixture -@needs_pyproj def pv_data(): """Test data for all PV testing.""" u = np.array([[[100, 90, 80, 70], @@ -582,7 +581,6 @@ def test_potential_vorticity_baroclinic_wrong_number_of_levels_axis_0(pv_data): pressure[:1, :, :]) -@needs_pyproj def test_potential_vorticity_baroclinic_isentropic_real_data(): """Test potential vorticity calculation with real isentropic data.""" isentlevs = [328, 330, 332] * units.K @@ -677,7 +675,6 @@ def test_potential_vorticity_baroclinic_isentropic_real_data(): assert_almost_equal(pvor, true_pv, 14) -@needs_pyproj def test_potential_vorticity_baroclinic_isobaric_real_data(): """Test potential vorticity calculation with real isentropic data.""" pres = [20000., 25000., 30000.] * units.Pa @@ -786,7 +783,6 @@ def test_potential_vorticity_barotropic(pv_data): assert_almost_equal(pv, truth, 10) -@needs_pyproj def test_inertial_advective_wind_diffluent(): """Test inertial advective wind with a diffluent flow.""" lats = np.array([[50., 50., 50., 50., 50., 50., 50., 50., 50., 50., 50.], @@ -978,7 +974,6 @@ def test_inertial_advective_wind_diffluent(): @pytest.fixture -@needs_pyproj def q_vector_data(): """Define data for use in Q-vector tests.""" speed = np.ones((4, 4)) * 50. * units('knots') @@ -1057,7 +1052,6 @@ def test_q_vector_with_static_stability(q_vector_data): @pytest.fixture -@needs_pyproj def data_4d(): """Define 4D data (extracted from Irma GFS example) for testing kinematics functions.""" data = xr.open_dataset(get_test_data('irma_gfs_example.nc', False)) diff --git a/tests/io/test_gini.py b/tests/io/test_gini.py index 34631853989..b70c333d0e3 100644 --- a/tests/io/test_gini.py +++ b/tests/io/test_gini.py @@ -14,7 +14,6 @@ from metpy.cbook import get_test_data from metpy.io import GiniFile from metpy.io.gini import GiniProjection -from metpy.testing import needs_pyproj logging.getLogger('metpy.io.gini').setLevel(logging.ERROR) @@ -98,7 +97,6 @@ def test_gini_bad_size(): @pytest.mark.parametrize('filename,bounds,data_var,proj_attrs,image,dt', gini_dataset_info, ids=['LCC', 'Stereographic', 'Mercator']) -@needs_pyproj def test_gini_xarray(filename, bounds, data_var, proj_attrs, image, dt): """Test that GINIFile can be passed to XArray as a datastore.""" f = GiniFile(get_test_data(filename)) @@ -134,7 +132,6 @@ def test_gini_xarray(filename, bounds, data_var, proj_attrs, image, dt): assert np.asarray(dt, dtype='datetime64[ms]') == ds.variables['time'] -@needs_pyproj def test_gini_mercator_upper_corner(): """Test that the upper corner of the Mercator coordinates is correct.""" f = GiniFile(get_test_data('HI-REGIONAL_4km_3.9_20160616_1715.gini')) diff --git a/tests/plots/test_declarative.py b/tests/plots/test_declarative.py index 3956ebf583a..186b3303f2c 100644 --- a/tests/plots/test_declarative.py +++ b/tests/plots/test_declarative.py @@ -20,7 +20,7 @@ from metpy.plots import (BarbPlot, ContourPlot, FilledContourPlot, ImagePlot, MapPanel, PanelContainer, PlotObs) # Fixtures to make sure we have the right backend -from metpy.testing import needs_cartopy, needs_pyproj, set_agg_backend # noqa: F401, I202 +from metpy.testing import needs_cartopy, set_agg_backend # noqa: F401, I202 from metpy.units import units @@ -28,7 +28,6 @@ @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.005) -@needs_pyproj @needs_cartopy def test_declarative_image(): """Test making an image plot.""" @@ -324,7 +323,6 @@ def test_colorfill_no_colorbar(cfeature): @pytest.mark.mpl_image_compare(remove_text=True, tolerance=1.23) -@needs_pyproj @needs_cartopy def test_global(): """Test that we can set global extent.""" diff --git a/tests/test_xarray.py b/tests/test_xarray.py index 4b6ed788b3b..d267f8a5e77 100644 --- a/tests/test_xarray.py +++ b/tests/test_xarray.py @@ -10,7 +10,7 @@ from metpy.plots.mapping import CFProjection from metpy.testing import (assert_almost_equal, assert_array_almost_equal, assert_array_equal, - get_test_data, needs_pyproj) + get_test_data) from metpy.units import DimensionalityError, units from metpy.xarray import ( add_grid_arguments_from_xarray, @@ -885,7 +885,6 @@ def test_coord_helper_da_dummy_yx(test_coord_helper_da_latlon): return test_coord_helper_da_latlon.assign_coords(y=range(3), x=range(3)) -@needs_pyproj def test_assign_latitude_longitude_basic_dataarray(test_coord_helper_da_yx, test_coord_helper_da_latlon): """Test assign_latitude_longitude in basic usage on DataArray.""" @@ -905,7 +904,6 @@ def test_assign_latitude_longitude_error_existing_dataarray( assert 'Latitude/longitude coordinate(s) are present' in str(exc) -@needs_pyproj def test_assign_latitude_longitude_force_existing_dataarray( test_coord_helper_da_dummy_latlon, test_coord_helper_da_latlon): """Test assign_latitude_longitude with existing coordinates forcing new.""" @@ -917,7 +915,6 @@ def test_assign_latitude_longitude_force_existing_dataarray( lon.values, 3) -@needs_pyproj def test_assign_latitude_longitude_basic_dataset(test_coord_helper_da_yx, test_coord_helper_da_latlon): """Test assign_latitude_longitude in basic usage on Dataset.""" @@ -929,7 +926,6 @@ def test_assign_latitude_longitude_basic_dataset(test_coord_helper_da_yx, lon.values, 3) -@needs_pyproj def test_assign_y_x_basic_dataarray(test_coord_helper_da_yx, test_coord_helper_da_latlon): """Test assign_y_x in basic usage on DataArray.""" new_da = test_coord_helper_da_latlon.metpy.assign_y_x() @@ -946,7 +942,6 @@ def test_assign_y_x_error_existing_dataarray( assert 'y/x coordinate(s) are present' in str(exc) -@needs_pyproj def test_assign_y_x_force_existing_dataarray( test_coord_helper_da_dummy_yx, test_coord_helper_da_yx): """Test assign_y_x with existing coordinates forcing new.""" @@ -956,7 +951,6 @@ def test_assign_y_x_force_existing_dataarray( np.testing.assert_array_almost_equal(test_coord_helper_da_yx['x'].values, x.values, 3) -@needs_pyproj def test_assign_y_x_dataarray_outside_tolerance(test_coord_helper_da_latlon): """Test assign_y_x raises ValueError when tolerance is exceeded on DataArray.""" with pytest.raises(ValueError) as exc: @@ -964,7 +958,6 @@ def test_assign_y_x_dataarray_outside_tolerance(test_coord_helper_da_latlon): assert 'cannot be collapsed to 1D within tolerance' in str(exc) -@needs_pyproj def test_assign_y_x_dataarray_transposed(test_coord_helper_da_yx, test_coord_helper_da_latlon): """Test assign_y_x on DataArray with transposed order.""" new_da = test_coord_helper_da_latlon.transpose(transpose_coords=True).metpy.assign_y_x() @@ -973,7 +966,6 @@ def test_assign_y_x_dataarray_transposed(test_coord_helper_da_yx, test_coord_hel np.testing.assert_array_almost_equal(test_coord_helper_da_yx['x'].values, x.values, 3) -@needs_pyproj def test_assign_y_x_dataset_assumed_order(test_coord_helper_da_yx, test_coord_helper_da_latlon): """Test assign_y_x on Dataset where order must be assumed.""" @@ -1314,7 +1306,6 @@ def test_grid_deltas_from_dataarray_xy(test_da_xy): assert_array_almost_equal(dy, true_dy, 5) -@needs_pyproj def test_grid_deltas_from_dataarray_actual_xy(test_da_xy, ccrs): """Test grid_deltas_from_dataarray with a xy grid and kind='actual'.""" # Construct lon/lat coordinates @@ -1343,7 +1334,6 @@ def test_grid_deltas_from_dataarray_actual_xy(test_da_xy, ccrs): assert_array_almost_equal(dy, true_dy, 2) -@needs_pyproj def test_grid_deltas_from_dataarray_nominal_lonlat(test_da_lonlat): """Test grid_deltas_from_dataarray with a lonlat grid and kind='nominal'.""" dx, dy = grid_deltas_from_dataarray(test_da_lonlat, kind='nominal') @@ -1353,7 +1343,6 @@ def test_grid_deltas_from_dataarray_nominal_lonlat(test_da_lonlat): assert_array_almost_equal(dy, true_dy, 5) -@needs_pyproj def test_grid_deltas_from_dataarray_lonlat_assumed_order(): """Test grid_deltas_from_dataarray when dim order must be assumed.""" # Create test dataarray @@ -1389,7 +1378,6 @@ def test_grid_deltas_from_dataarray_invalid_kind(test_da_xy): grid_deltas_from_dataarray(test_da_xy, kind='invalid') -@needs_pyproj def test_add_grid_arguments_from_dataarray(): """Test the grid argument decorator for adding in arguments from xarray.""" @add_grid_arguments_from_xarray From 9f7a318130808df7d5ae8c756863e5657b8934e4 Mon Sep 17 00:00:00 2001 From: Ryan May Date: Fri, 2 Oct 2020 14:11:11 -0600 Subject: [PATCH 4/9] MNT: Fix typo in Travis config for uploading coverage Was accidentally trying to upload coverage only when we ran without it. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 09842197af3..66127843027 100644 --- a/.travis.yml +++ b/.travis.yml @@ -88,7 +88,7 @@ script: fi after_script: - - if [[ $TASK != "coverage" ]]; then + - if [[ $TASK == "coverage" ]]; then pip install codecov codacy-coverage; coverage xml; codecov -X gcov -f coverage.xml -e TRAVIS_PYTHON_VERSION; From 204c3fb2c792dd4483d94bb51d82bae4f2d0ff18 Mon Sep 17 00:00:00 2001 From: Ryan May Date: Fri, 2 Oct 2020 14:34:14 -0600 Subject: [PATCH 5/9] MNT: Always upload coverage reports on GitHub Actions Makes it easier to debug issues while PR is still in development. --- .github/workflows/tests-conda.yml | 1 + .github/workflows/tests-pypi.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/tests-conda.yml b/.github/workflows/tests-conda.yml index 3d55e1638c2..dc125e5db94 100644 --- a/.github/workflows/tests-conda.yml +++ b/.github/workflows/tests-conda.yml @@ -108,6 +108,7 @@ jobs: path: test_output/ - name: Upload coverage + if: ${{ always() }} uses: codecov/codecov-action@v1 with: name: conda-${{ matrix.python-version }}-${{ runner.os }} diff --git a/.github/workflows/tests-pypi.yml b/.github/workflows/tests-pypi.yml index 567a420e56c..b07b5907395 100644 --- a/.github/workflows/tests-pypi.yml +++ b/.github/workflows/tests-pypi.yml @@ -126,6 +126,7 @@ jobs: path: test_output/ - name: Upload coverage + if: ${{ always() }} uses: codecov/codecov-action@v1 with: name: pypi-${{ matrix.python-version }}-${{ matrix.dep-versions }}-${{ matrix.no-extras }}-${{ runner.os }} From a46abf58b1d0e725db9c0b69a0dd4dd3a1854cc1 Mon Sep 17 00:00:00 2001 From: Ryan May Date: Mon, 5 Oct 2020 00:45:24 -0600 Subject: [PATCH 6/9] ENH: Add test for .pyproj_crs attribute --- tests/test_xarray.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tests/test_xarray.py b/tests/test_xarray.py index d267f8a5e77..162a80268ed 100644 --- a/tests/test_xarray.py +++ b/tests/test_xarray.py @@ -5,6 +5,7 @@ from collections import OrderedDict import numpy as np +import pyproj import pytest import xarray as xr @@ -63,11 +64,19 @@ def test_var_multidim_no_xy(test_var_multidim_full): def test_projection(test_var, ccrs): """Test getting the proper projection out of the variable.""" crs = test_var.metpy.crs - assert crs['grid_mapping_name'] == 'lambert_conformal_conic' + assert crs['grid_mapping_name'] == 'lambert_conformal_conic' assert isinstance(test_var.metpy.cartopy_crs, ccrs.LambertConformal) +def test_pyproj_projection(test_var): + """Test getting the proper pyproj projection out of the variable.""" + proj = test_var.metpy.pyproj_crs + + assert isinstance(proj, pyproj.CRS) + assert proj.coordinate_operation.method_name == 'Lambert Conic Conformal (1SP)' + + def test_no_projection(test_ds): """Test getting the crs attribute when not available produces a sensible error.""" var = test_ds.lat @@ -1309,9 +1318,8 @@ def test_grid_deltas_from_dataarray_xy(test_da_xy): def test_grid_deltas_from_dataarray_actual_xy(test_da_xy, ccrs): """Test grid_deltas_from_dataarray with a xy grid and kind='actual'.""" # Construct lon/lat coordinates - from pyproj import Proj y, x = xr.broadcast(*test_da_xy.metpy.coordinates('y', 'x')) - lon, lat = Proj(test_da_xy.metpy.pyproj_crs)( + lon, lat = pyproj.Proj(test_da_xy.metpy.pyproj_crs)( x.values, y.values, inverse=True, From ed2cd6c52808589dc03a4ce39aa303db7e9f46c8 Mon Sep 17 00:00:00 2001 From: Ryan May Date: Mon, 5 Oct 2020 23:05:18 -0600 Subject: [PATCH 7/9] MNT: Update tests due to change to PyProj Necessary because we are now properly using the Earth ellipsoid that is present in the data for lat/lon projection. Previously, we were using CartoPy's PlateCarree, which doesn't support globes--thus when we got the spheroid we used the CartoPy default, which is WGS84. Was able to reproduce previous test values by overriding data ellipsoid to specify WGS84. Updating the test values is better long-term than putting a WGS84 hack into the data fixture. --- tests/calc/test_kinematics.py | 1014 ++++++++++++++++----------------- 1 file changed, 492 insertions(+), 522 deletions(-) diff --git a/tests/calc/test_kinematics.py b/tests/calc/test_kinematics.py index 1b0c8482e8b..d3678ab4c76 100644 --- a/tests/calc/test_kinematics.py +++ b/tests/calc/test_kinematics.py @@ -1078,215 +1078,215 @@ def data_4d(): def test_vorticity_4d(data_4d): """Test vorticity on a 4D (time, pressure, y, x) grid.""" vort = vorticity(data_4d.u, data_4d.v) - truth = np.array([[[[-5.83650490e-05, 3.17327814e-05, 4.57268332e-05, 2.00732350e-05], - [2.14368312e-05, 1.95623237e-05, 4.15790182e-05, 6.90274641e-05], - [6.18610861e-05, 6.93600880e-05, 8.36201998e-05, 8.25922654e-05], - [-4.44038452e-05, 1.56487106e-04, 1.42605312e-04, -6.03981765e-05]], - [[-8.26772499e-07, 4.24638141e-05, -1.02560273e-05, 1.40379447e-05], - [1.16882545e-05, 1.06463071e-05, 2.84971990e-05, 6.22850560e-05], - [1.83850591e-05, 7.36387780e-06, 3.76622760e-05, 4.67188878e-05], - [3.19856719e-05, 2.80735317e-05, 3.73822586e-05, 5.40379931e-05]], - [[-2.84629423e-05, 3.82238141e-06, 3.96173636e-06, 5.13752737e-05], - [-2.18549443e-05, 5.28657636e-06, 2.00254459e-05, 3.38246076e-05], - [-1.97810827e-05, -2.51363102e-06, 2.87033130e-05, 3.01975044e-05], - [-2.34149501e-05, -1.82846160e-05, 2.95791089e-05, 3.41817364e-05]]], - [[[-3.66403309e-05, 2.45056689e-05, 8.30552352e-05, 2.42918324e-05], - [3.30814959e-05, 2.30523398e-05, 4.66571426e-05, 7.60789420e-05], - [7.65406561e-05, 4.98489001e-05, 6.61967180e-05, 9.90553670e-05], - [-3.83060455e-06, 8.82014475e-05, 1.11633279e-04, -4.43270102e-05]], - [[-2.47146999e-06, 3.95768075e-05, 3.76682359e-05, 3.79239346e-05], - [-4.60129429e-06, 2.05601660e-05, 2.89144970e-05, 3.01301961e-05], - [1.54778233e-05, 8.05069277e-06, 2.44051429e-05, 7.01730409e-05], - [2.07682219e-05, 2.16790897e-05, 3.38209456e-05, 9.11823021e-05]], - [[-7.12798691e-06, -2.81765143e-06, 1.93675069e-05, 6.21220857e-05], - [-1.80822794e-05, 7.10872010e-06, 1.40635809e-05, 1.80843580e-05], - [-3.01135264e-06, 2.56664766e-06, 2.25038301e-05, 3.69789825e-05], - [-1.48940627e-05, -6.28397440e-06, 3.66706625e-05, 1.13280233e-05]]], - [[[-2.13814161e-05, 3.10846718e-05, 9.42880991e-05, 6.20302960e-05], - [2.83685685e-05, 2.71376067e-05, 4.44470499e-05, 7.94154059e-05], - [7.29341555e-05, 3.07015029e-05, 3.70538789e-05, 7.75632608e-05], - [6.86595116e-05, 2.25094524e-05, 7.15703850e-05, 6.90873953e-05]], - [[-5.70566101e-07, 5.63627148e-05, 2.91960395e-05, 3.62726492e-05], - [-6.17247194e-06, 2.63672993e-05, 3.27525843e-05, 2.87151996e-05], - [8.82121811e-06, 6.46657237e-06, 2.03146030e-05, 4.99274322e-05], - [1.54560972e-05, 1.39161983e-06, -6.92832423e-06, 6.02698395e-05]], - [[3.01573325e-06, 2.95361596e-05, 3.30386503e-05, 4.50206712e-05], - [-3.44201203e-06, 7.98411843e-06, 1.31230998e-05, 1.82704434e-05], - [-5.97302093e-06, -6.76058488e-07, 5.89633276e-06, 1.82494546e-05], - [-2.96985363e-06, 3.86098537e-06, 5.24525482e-06, - 2.72933874e-05]]]]) * units('s^-1') + truth = np.array([[[[-5.84515167e-05, 3.17729585e-05, 4.58261458e-05, 2.01292844e-05], + [2.13352387e-05, 1.96004423e-05, 4.16486823e-05, 6.90445310e-05], + [6.19222139e-05, 6.93601354e-05, 8.36426564e-05, 8.27371956e-05], + [-4.43297956e-05, 1.56381845e-04, 1.42510285e-04, -6.03146997e-05]], + [[-8.68712685e-07, 4.24890902e-05, -1.01652245e-05, 1.40567079e-05], + [1.16955768e-05, 1.06772721e-05, 2.85536762e-05, 6.24020996e-05], + [1.83821861e-05, 7.39881835e-06, 3.76797354e-05, 4.67243318e-05], + [3.19572986e-05, 2.81106117e-05, 3.73957581e-05, 5.39673545e-05]], + [[-2.85395302e-05, 3.84435833e-06, 4.04684576e-06, 5.14302137e-05], + [-2.19027713e-05, 5.30196965e-06, 2.00825604e-05, 3.39049359e-05], + [-1.97989026e-05, -2.49143814e-06, 2.87381603e-05, 3.02743602e-05], + [-2.34066064e-05, -1.82455025e-05, 2.95918539e-05, 3.42233420e-05]]], + [[[-3.66558283e-05, 2.45520394e-05, 8.31163955e-05, 2.44003406e-05], + [3.30393774e-05, 2.30884339e-05, 4.67122293e-05, 7.61002310e-05], + [7.66048678e-05, 4.98785325e-05, 6.62382288e-05, 9.91998869e-05], + [-3.81435328e-06, 8.81953022e-05, 1.11601055e-04, -4.42546076e-05]], + [[-2.51779702e-06, 3.95852220e-05, 3.77219249e-05, 3.79808820e-05], + [-4.63231122e-06, 2.05995207e-05, 2.89780728e-05, 3.01707041e-05], + [1.54931892e-05, 8.07405448e-06, 2.44489167e-05, 7.02383317e-05], + [2.07687143e-05, 2.17030773e-05, 3.38485776e-05, 9.11533757e-05]], + [[-7.17079917e-06, -2.80615398e-06, 1.94218575e-05, 6.22111037e-05], + [-1.81300845e-05, 7.12699895e-06, 1.41049190e-05, 1.80915929e-05], + [-2.99278303e-06, 2.57606747e-06, 2.25304657e-05, 3.70860448e-05], + [-1.48782578e-05, -6.27503290e-06, 3.66662188e-05, 1.14265141e-05]]], + [[[-2.14099419e-05, 3.11255562e-05, 9.43465637e-05, 6.21369629e-05], + [2.83576779e-05, 2.71698609e-05, 4.45208755e-05, 7.95114352e-05], + [7.29890624e-05, 3.07600211e-05, 3.71063624e-05, 7.76394608e-05], + [6.87038548e-05, 2.25751156e-05, 7.15889230e-05, 6.91611419e-05]], + [[-5.87372735e-07, 5.63433054e-05, 2.92457668e-05, 3.63765486e-05], + [-6.20728063e-06, 2.63907904e-05, 3.27952358e-05, 2.87436180e-05], + [8.81938816e-06, 6.49991429e-06, 2.03676451e-05, 4.99729397e-05], + [1.54604020e-05, 1.42654642e-06, -6.85425498e-06, 6.03129247e-05]], + [[2.98152979e-06, 2.95129295e-05, 3.30665600e-05, 4.51175504e-05], + [-3.45169373e-06, 7.99863229e-06, 1.31503178e-05, 1.82983904e-05], + [-5.96640905e-06, -6.58389207e-07, 5.92924140e-06, 1.82929244e-05], + [-2.94758645e-06, 3.86196289e-06, 5.27851664e-06, + 2.73814460e-05]]]]) * units('s^-1') assert_array_almost_equal(vort.data, truth, 12) def test_divergence_4d(data_4d): """Test divergence on a 4D (time, pressure, y, x) grid.""" div = divergence(data_4d.u, data_4d.v) - truth = np.array([[[[-8.43705083e-06, -5.42243991e-06, 1.42553766e-05, 2.81311077e-05], - [2.95334911e-05, -8.91904163e-06, 1.18532270e-05, -6.26196756e-06], - [-4.63583096e-05, -2.10525265e-05, 1.32571075e-05, 4.76118929e-05], - [-3.36862002e-05, 1.49431136e-05, -4.23301144e-05, 3.93742169e-05]], - [[1.69160375e-05, 1.54447811e-06, 4.11350021e-05, -5.08238612e-05], - [2.27239404e-05, -3.97652811e-06, 5.32329400e-06, 1.75756955e-05], - [-6.16991733e-06, -1.77132521e-06, -1.46585782e-05, 2.66081211e-06], - [-3.06896618e-05, -8.23671871e-06, -2.56998533e-05, -2.79187158e-05]], - [[-1.47210200e-05, -2.26015888e-05, -2.10309987e-05, -3.88000930e-05], - [2.87880179e-06, -9.97852896e-06, -1.02741993e-06, 4.53302920e-06], - [1.48614763e-05, 3.64899207e-06, 5.07255670e-06, 5.85619901e-06], - [1.12477665e-05, 1.61231699e-05, 1.13583495e-05, 3.92603086e-06]]], - [[[-3.95659837e-06, -8.16293111e-06, 6.64912076e-06, 4.82899740e-05], - [2.29285761e-05, -7.41730432e-07, 4.88714205e-06, -3.26931553e-05], - [-3.27492305e-05, -9.30212918e-06, 1.79834485e-06, 2.53811573e-05], - [-7.03628352e-05, 1.59599812e-05, -5.03928715e-05, 3.02766722e-05]], - [[1.67050619e-05, 1.24336555e-05, -2.22683301e-05, -1.89873955e-05], - [1.75618966e-05, -1.79165561e-06, 4.00327550e-06, -5.57201491e-06], - [4.02631582e-06, -5.29814574e-06, 3.59245019e-06, 4.16299189e-06], - [-3.62032526e-06, 4.00602251e-06, -2.17495860e-05, 2.93910418e-05]], - [[-6.50182516e-06, -3.06444044e-07, -4.68103153e-05, 6.54271734e-06], - [-9.22409986e-07, -6.80509227e-06, 1.57428914e-06, 2.13528516e-06], - [5.77636627e-06, -2.32628120e-06, 1.63766780e-05, 1.30647979e-05], - [-3.84054963e-06, 1.81368329e-05, -2.12456769e-08, 1.39177255e-05]]], - [[[-3.09706856e-06, -1.58174014e-05, 1.11042898e-05, 1.01863872e-05], - [2.22554541e-05, -2.18115261e-06, 2.95538179e-06, -1.27314237e-05], - [-2.01806928e-06, -1.44611342e-05, -1.60090851e-06, 1.51875027e-05], - [-7.45883555e-05, -2.17664690e-05, -6.59935118e-06, -9.51280586e-06]], - [[-2.84262294e-06, 1.83370481e-05, -1.60080375e-05, 5.94414530e-06], - [2.21275530e-05, 2.08417698e-06, -8.62049532e-06, -4.83025078e-06], - [6.84132907e-06, -1.74271778e-06, 4.96579520e-06, -8.14051216e-06], - [-5.91652815e-06, -1.27122109e-06, 1.33424166e-05, 2.89090443e-05]], - [[3.56617289e-06, -3.61628942e-06, -2.14971623e-05, 9.09440121e-06], - [1.15504071e-05, -2.35438670e-07, -1.00682327e-05, -7.83169489e-06], - [1.74820166e-06, -4.85659616e-07, 6.34687163e-06, -9.27089944e-06], - [9.23766788e-07, -2.85241737e-06, 1.68475020e-05, - -5.70982211e-06]]]]) * units('s^-1') + truth = np.array([[[[-8.37608702e-06, -5.39336397e-06, 1.42603335e-05, 2.81027853e-05], + [2.95370985e-05, -8.88467654e-06, 1.18747200e-05, -6.20723902e-06], + [-4.64906358e-05, -2.10620465e-05, 1.33445184e-05, 4.77361610e-05], + [-3.35504768e-05, 1.49909469e-05, -4.22891356e-05, 3.92159433e-05]], + [[1.69393752e-05, 1.56905500e-06, 4.11012845e-05, -5.08416562e-05], + [2.27792270e-05, -3.97105867e-06, 5.31878772e-06, 1.75998805e-05], + [-6.17322438e-06, -1.74803086e-06, -1.46149939e-05, 2.66339797e-06], + [-3.07336112e-05, -8.20592276e-06, -2.56044746e-05, -2.78477951e-05]], + [[-1.46864164e-05, -2.25693290e-05, -2.10250150e-05, -3.88307255e-05], + [2.90827814e-06, -9.96431223e-06, -1.02191776e-06, 4.55537027e-06], + [1.48872763e-05, 3.66494030e-06, 5.06711902e-06, 5.82930016e-06], + [1.12436632e-05, 1.61499510e-05, 1.13636149e-05, 3.85414061e-06]]], + [[[-3.88892057e-06, -8.14255274e-06, 6.65050488e-06, 4.82691326e-05], + [2.29332150e-05, -6.95362743e-07, 4.90498323e-06, -3.27034929e-05], + [-3.28375966e-05, -9.30193297e-06, 1.89455614e-06, 2.55048066e-05], + [-7.03452094e-05, 1.59780333e-05, -5.02908248e-05, 3.01991417e-05]], + [[1.67133745e-05, 1.24417427e-05, -2.22432790e-05, -1.89957283e-05], + [1.76022853e-05, -1.76730982e-06, 3.99751017e-06, -5.57126626e-06], + [4.04526025e-06, -5.27586187e-06, 3.61323452e-06, 4.18352106e-06], + [-3.61713767e-06, 4.02190371e-06, -2.16650827e-05, 2.94848150e-05]], + [[-6.47746172e-06, -2.98975077e-07, -4.67842312e-05, 6.49628794e-06], + [-9.24534558e-07, -6.79275746e-06, 1.57736990e-06, 2.15325190e-06], + [5.79213453e-06, -2.31793357e-06, 1.63764017e-05, 1.30886984e-05], + [-3.81688990e-06, 1.81508167e-05, -1.74511070e-08, 1.38462853e-05]]], + [[[-3.04155688e-06, -1.57905256e-05, 1.11057616e-05, 1.02005872e-05], + [2.22682490e-05, -2.13398663e-06, 2.98041246e-06, -1.27487570e-05], + [-2.03870559e-06, -1.44407167e-05, -1.54275894e-06, 1.52970546e-05], + [-7.46492084e-05, -2.17403382e-05, -6.50948158e-06, -9.39708598e-06]], + [[-2.87798173e-06, 1.83342079e-05, -1.59741420e-05, 5.93365940e-06], + [2.21780902e-05, 2.10396122e-06, -8.61898987e-06, -4.81985056e-06], + [6.86376938e-06, -1.71394859e-06, 4.97940406e-06, -8.12845764e-06], + [-5.91535752e-06, -1.25897243e-06, 1.33746621e-05, 2.89926447e-05]], + [[3.56301627e-06, -3.60303780e-06, -2.14805401e-05, 9.05879471e-06], + [1.15765605e-05, -2.20007656e-07, -1.00689171e-05, -7.85316340e-06], + [1.76295477e-06, -4.68035973e-07, 6.34634343e-06, -9.26903305e-06], + [9.56906212e-07, -2.83017535e-06, 1.68342294e-05, + -5.69798533e-06]]]]) * units('s^-1') assert_array_almost_equal(div.data, truth, 12) def test_shearing_deformation_4d(data_4d): """Test shearing_deformation on a 4D (time, pressure, y, x) grid.""" shdef = shearing_deformation(data_4d.u, data_4d.v) - truth = np.array([[[[-2.32353766e-05, 3.38638896e-06, 2.68355706e-05, 1.06560395e-05], - [-6.40834716e-05, 1.01157390e-05, 1.72783215e-05, -2.41362735e-05], - [6.69848680e-07, -1.89007571e-05, -1.40877214e-05, 3.71581119e-05], - [6.36114984e-05, -1.08233745e-04, -9.64601102e-05, 7.32813538e-05]], - [[-2.42214688e-05, -1.01851671e-05, 5.54461375e-05, -3.51796268e-07], - [-2.71001778e-06, 9.30533079e-06, 2.03843188e-05, 3.34828205e-05], - [-7.27917172e-06, 1.72622100e-05, -5.55179147e-06, -1.31598103e-05], - [-2.51927242e-05, 9.17056498e-06, -2.24631811e-06, -5.35695138e-05]], - [[-2.57651839e-05, 1.01219942e-05, 4.53600390e-05, 5.28799494e-07], - [-1.55543764e-05, 6.18561775e-06, 2.36187660e-05, 2.52821250e-05], - [-2.67211842e-06, 1.14466225e-05, 7.99438411e-06, 3.06434113e-05], - [1.17029675e-05, 2.71857215e-05, -1.93883537e-06, 1.03237850e-05]]], - [[[5.36878109e-06, 1.03810936e-05, -1.74133759e-05, 3.67019082e-05], - [-3.68224309e-05, 8.02234002e-06, 2.97256283e-06, -2.41548002e-05], - [-1.03217116e-07, -3.55295991e-07, 1.04215491e-06, 3.06178781e-05], - [1.78849776e-05, -3.14217684e-05, -5.31904971e-05, 6.33706906e-05]], - [[-2.54172738e-05, -1.89184694e-05, 7.52187239e-06, 7.78263224e-06], - [-1.58491234e-05, 8.86850501e-06, 1.94682285e-05, 6.28154949e-06], - [2.87101909e-06, 1.02981542e-05, 1.49483081e-05, 1.11896135e-05], - [-6.24536223e-06, 5.02422684e-06, 3.65739211e-06, -4.43343790e-05]], - [[-1.97278534e-05, 7.98223535e-06, 1.93668724e-05, 1.17314603e-05], - [-1.80800662e-05, 7.10682312e-06, 1.58638789e-05, -7.11095379e-06], - [1.04957568e-05, 3.46915772e-06, 7.19233197e-06, 4.14864918e-05], - [1.30201499e-05, 7.22093417e-06, -1.46521398e-05, 5.00427528e-05]]], - [[[-8.31494391e-06, 2.74335132e-06, -2.40497584e-05, 1.75042968e-05], - [-1.88915366e-05, 3.28864180e-06, 1.34127052e-05, 1.23621458e-05], - [4.61243898e-07, 1.85506704e-05, 1.67855055e-05, 9.59377214e-06], - [6.06292303e-06, 2.92575008e-05, -1.44159217e-05, 2.17975694e-05]], - [[-1.00168319e-05, -4.57753168e-05, 8.50542316e-06, 3.44821467e-05], - [-1.65225222e-05, -4.66989095e-06, 6.65190573e-06, 1.71105129e-06], - [-4.23400810e-06, 1.50208950e-05, 1.80731201e-05, 5.36054473e-06], - [-2.10443897e-06, 1.80502669e-05, 4.39381815e-05, 5.78573040e-06]], - [[-2.08335492e-05, -3.03108476e-05, -3.85875023e-06, 2.70252773e-05], - [-4.78804481e-06, 1.24351472e-06, 6.82854110e-06, 5.67152289e-06], - [5.73158894e-06, 1.05747791e-05, 1.53497021e-05, 1.55510561e-05], - [1.23394357e-05, -1.98706807e-06, 1.56020711e-05, - 3.89964205e-05]]]]) * units('s^-1') + truth = np.array([[[[-2.33792381e-05, 3.44534094e-06, 2.69410760e-05, 1.06867281e-05], + [-6.40972431e-05, 1.01579031e-05, 1.73678734e-05, -2.40319045e-05], + [7.70545354e-07, -1.87702202e-05, -1.39302341e-05, 3.73230852e-05], + [6.35849225e-05, -1.08009221e-04, -9.62510298e-05, 7.32297192e-05]], + [[-2.42502310e-05, -1.01193319e-05, 5.54828905e-05, -3.31928326e-07], + [-2.69305297e-06, 9.32833730e-06, 2.04600718e-05, 3.36248400e-05], + [-7.24755760e-06, 1.72909996e-05, -5.48615182e-06, -1.30784063e-05], + [-2.51475614e-05, 9.22553765e-06, -2.17297542e-06, -5.34977173e-05]], + [[-2.58416628e-05, 1.01393773e-05, 4.54141476e-05, 6.20366322e-07], + [-1.56077459e-05, 6.20125807e-06, 2.36797141e-05, 2.53616873e-05], + [-2.71240538e-06, 1.14475474e-05, 8.05450723e-06, 3.07240065e-05], + [1.16656764e-05, 2.71686080e-05, -1.88326452e-06, 1.03921795e-05]]], + [[[5.29600994e-06, 1.04331961e-05, -1.72892524e-05, 3.67655639e-05], + [-3.67904320e-05, 8.07030650e-06, 3.05173020e-06, -2.40356283e-05], + [3.03845109e-08, -2.56843275e-07, 1.17465234e-06, 3.08089412e-05], + [1.79034632e-05, -3.12752861e-05, -5.30138255e-05, 6.33453564e-05]], + [[-2.54496668e-05, -1.88685727e-05, 7.59573914e-06, 7.85469836e-06], + [-1.58734272e-05, 8.90875832e-06, 1.95355336e-05, 6.33953947e-06], + [2.90313838e-06, 1.03222777e-05, 1.50063775e-05, 1.13348820e-05], + [-6.20995986e-06, 5.06623932e-06, 3.72239179e-06, -4.41896630e-05]], + [[-1.97608457e-05, 7.98531569e-06, 1.94218554e-05, 1.18509048e-05], + [-1.81300845e-05, 7.12699895e-06, 1.59034980e-05, -7.08850441e-06], + [1.04965562e-05, 3.47535804e-06, 7.24254745e-06, 4.15824912e-05], + [1.29997134e-05, 7.21430847e-06, -1.45932750e-05, 5.00959463e-05]]], + [[[-8.37024044e-06, 2.79795154e-06, -2.39099649e-05, 1.76221280e-05], + [-1.88550094e-05, 3.33869412e-06, 1.34953970e-05, 1.25143854e-05], + [5.96277806e-07, 1.86196124e-05, 1.68723536e-05, 9.74312685e-06], + [6.20326426e-06, 2.93197852e-05, -1.42931965e-05, 2.19484546e-05]], + [[-1.00299098e-05, -4.57260229e-05, 8.56211376e-06, 3.45779631e-05], + [-1.65491061e-05, -4.63468810e-06, 6.71584791e-06, 1.76493950e-06], + [-4.22030685e-06, 1.50431608e-05, 1.81194219e-05, 5.45811766e-06], + [-2.07574370e-06, 1.80633930e-05, 4.39555860e-05, 5.90590854e-06]], + [[-2.08496392e-05, -3.02898043e-05, -3.80429538e-06, 2.71317584e-05], + [-4.80062637e-06, 1.25396267e-06, 6.85529455e-06, 5.70834171e-06], + [5.72435226e-06, 1.05827268e-05, 1.53717763e-05, 1.55950591e-05], + [1.23403264e-05, -1.98341401e-06, 1.56203357e-05, + 3.90722041e-05]]]]) * units('s^-1') assert_array_almost_equal(shdef.data, truth, 12) def test_stretching_deformation_4d(data_4d): """Test stretching_deformation on a 4D (time, pressure, y, x) grid.""" stdef = stretching_deformation(data_4d.u, data_4d.v) - truth = np.array([[[[3.47764258e-05, 2.24655678e-05, -5.99204286e-06, -2.81311151e-05], - [-1.00806414e-05, 2.43815624e-05, 5.10566770e-06, 3.02039392e-05], - [-5.93889988e-05, 4.15227142e-06, 3.93751112e-05, 5.52382202e-05], - [8.92010023e-05, 1.85531529e-05, 3.60056433e-05, -1.03321628e-04]], - [[2.96761128e-06, 1.36910447e-05, -4.34590663e-05, 1.80287489e-05], - [1.86757141e-05, 5.47290208e-06, -9.06422655e-06, 8.11203944e-06], - [1.34111946e-07, 1.26357749e-05, 2.72130530e-05, -3.62654235e-06], - [-1.35816208e-05, 1.87775033e-05, 5.84933984e-05, 5.04057146e-05]], - [[2.89236233e-05, 3.31889843e-05, 1.58664147e-05, 5.74675794e-06], - [1.68234432e-05, 1.52158343e-05, 5.26714302e-06, 1.21764691e-05], - [8.55744700e-06, 7.69832273e-06, -4.83112472e-06, -1.57549242e-05], - [-5.86025733e-06, 8.47198887e-06, -3.49088418e-07, -3.92962147e-05]]], - [[[3.69582950e-05, 1.86470363e-05, -2.80150634e-06, -3.51977497e-05], - [-6.46847071e-06, 2.69781373e-05, 6.95914436e-06, 5.98289917e-06], - [-4.00667252e-05, 5.05292624e-06, 4.75021137e-05, 6.24518714e-05], - [3.67260262e-05, 2.68548943e-06, 7.10293796e-05, -5.79403685e-05]], - [[-5.34297827e-06, -1.07157183e-06, 2.58835391e-05, 7.10885516e-06], - [1.26149579e-05, 1.35132438e-05, -2.75629799e-06, 4.32503739e-06], - [8.52816121e-06, 1.49554343e-05, 6.30626925e-06, 9.11577765e-06], - [2.68336326e-06, 5.36356177e-06, 5.47773716e-05, 4.06465999e-05]], - [[1.73474509e-05, 5.98748586e-06, 4.13875024e-05, -2.90086556e-05], - [4.23620645e-07, 1.02966295e-05, 5.15938898e-06, 7.09234799e-06], - [5.32951540e-06, 6.67206038e-06, -7.92655166e-06, 1.03541254e-05], - [1.46155702e-05, 1.33856872e-07, 4.47179844e-06, -4.46031161e-05]]], - [[[3.02111317e-05, 2.69212573e-05, -4.64855995e-06, 2.98329788e-06], - [-2.05441981e-06, 2.88664710e-05, 1.15095603e-05, -3.72867092e-06], - [-1.41578887e-05, 1.61511593e-05, 3.08142052e-05, 5.12063542e-05], - [-4.81886157e-06, 1.96583134e-05, 4.92309570e-05, 6.43248731e-05]], - [[-1.85904008e-05, -1.13648603e-05, 2.94359552e-05, -8.00997936e-06], - [1.62793512e-05, 8.39043368e-06, 7.12412372e-06, 7.32420819e-06], - [9.09319575e-06, 1.67115147e-05, 5.41579051e-06, 1.03134035e-05], - [2.63717343e-06, 5.48753335e-06, 1.28924212e-05, 3.38671775e-05]], - [[-4.08263202e-06, 1.03302483e-05, 2.30465372e-05, -2.51046121e-05], - [8.40123079e-06, 9.21367536e-06, 6.57669664e-06, -9.62598559e-06], - [6.70192818e-06, 9.41865112e-06, -1.75966046e-06, 4.68368828e-06], - [1.75811596e-05, 1.24562416e-05, -1.28654291e-05, - 7.34949445e-06]]]]) * units('s^-1') + truth = np.array([[[[3.47898088e-05, 2.24845986e-05, -5.97367530e-06, -2.81027927e-05], + [-1.00316265e-05, 2.43890252e-05, 5.13005043e-06, 3.02139765e-05], + [-5.95303373e-05, 4.11805509e-06, 3.94239079e-05, 5.53801191e-05], + [8.92024896e-05, 1.85881092e-05, 3.59490328e-05, -1.03321407e-04]], + [[3.00039817e-06, 1.37094723e-05, -4.34319088e-05, 1.79539749e-05], + [1.87324184e-05, 5.47148050e-06, -9.06983993e-06, 8.15734277e-06], + [1.21798873e-07, 1.26405968e-05, 2.72019585e-05, -3.63162743e-06], + [-1.36470926e-05, 1.87727600e-05, 5.84790724e-05, 5.03903728e-05]], + [[2.89291086e-05, 3.31866090e-05, 1.58458533e-05, 5.68409251e-06], + [1.68472637e-05, 1.52157851e-05, 5.27310978e-06, 1.21993291e-05], + [8.59225306e-06, 7.71174035e-06, -4.82506223e-06, -1.57536424e-05], + [-5.84283826e-06, 8.50599727e-06, -3.27143224e-07, -3.93117456e-05]]], + [[[3.69837694e-05, 1.86562509e-05, -2.79203000e-06, -3.51399535e-05], + [-6.42858314e-06, 2.70027422e-05, 6.97334875e-06, 5.92098244e-06], + [-4.01668004e-05, 5.04173347e-06, 4.75334876e-05, 6.25555261e-05], + [3.66252634e-05, 2.71352154e-06, 7.09783382e-05, -5.79312118e-05]], + [[-5.31921974e-06, -1.04758793e-06, 2.58686924e-05, 7.08365906e-06], + [1.26562011e-05, 1.35206063e-05, -2.74715944e-06, 4.32091552e-06], + [8.54170666e-06, 1.49581427e-05, 6.31110194e-06, 9.12961275e-06], + [2.67785986e-06, 5.37083849e-06, 5.47744998e-05, 4.07259321e-05]], + [[1.73537008e-05, 5.99605247e-06, 4.13461116e-05, -2.90256397e-05], + [4.24395934e-07, 1.02937398e-05, 5.17452359e-06, 7.09934306e-06], + [5.34248818e-06, 6.67495925e-06, -7.90440717e-06, 1.03908310e-05], + [1.46185421e-05, 1.65031056e-07, 4.47900388e-06, -4.46075180e-05]]], + [[[3.02321534e-05, 2.69257238e-05, -4.63180943e-06, 3.00627122e-06], + [-2.01256850e-06, 2.88914919e-05, 1.15236589e-05, -3.75586415e-06], + [-1.41791143e-05, 1.61351154e-05, 3.08316570e-05, 5.12686237e-05], + [-4.95427192e-06, 1.96269721e-05, 4.92464559e-05, 6.43446270e-05]], + [[-1.86155399e-05, -1.13423401e-05, 2.94399620e-05, -8.00532458e-06], + [1.63327091e-05, 8.39898448e-06, 7.11857042e-06, 7.32055442e-06], + [9.11199258e-06, 1.67214834e-05, 5.42904828e-06, 1.03069722e-05], + [2.62789752e-06, 5.48570575e-06, 1.29250179e-05, 3.39387353e-05]], + [[-4.08093319e-06, 1.03359478e-05, 2.30342884e-05, -2.51141968e-05], + [8.42904887e-06, 9.22253152e-06, 6.56793595e-06, -9.65174212e-06], + [6.70904325e-06, 9.42414527e-06, -1.74726096e-06, 4.66995059e-06], + [1.75937571e-05, 1.24577364e-05, -1.28423144e-05, + 7.34171029e-06]]]]) * units('s^-1') assert_array_almost_equal(stdef.data, truth, 12) def test_total_deformation_4d(data_4d): """Test total_deformation on a 4D (time, pressure, y, x) grid.""" totdef = total_deformation(data_4d.u, data_4d.v) - truth = np.array([[[[4.18244250e-05, 2.27193611e-05, 2.74964075e-05, 3.00817356e-05], - [6.48714934e-05, 2.63967566e-05, 1.80168876e-05, 3.86631303e-05], - [5.93927763e-05, 1.93514851e-05, 4.18194127e-05, 6.65731647e-05], - [1.09559306e-04, 1.09812399e-04, 1.02960960e-04, 1.26670895e-04]], - [[2.44025874e-05, 1.70640656e-05, 7.04483116e-05, 1.80321809e-05], - [1.88713140e-05, 1.07954545e-05, 2.23087574e-05, 3.44514797e-05], - [7.28040706e-06, 2.13926787e-05, 2.77735961e-05, 1.36503632e-05], - [2.86205132e-05, 2.08972221e-05, 5.85365151e-05, 7.35556175e-05]], - [[3.87352641e-05, 3.46981764e-05, 4.80549296e-05, 5.77103594e-06], - [2.29121554e-05, 1.64250869e-05, 2.41989442e-05, 2.80615795e-05], - [8.96493815e-06, 1.37945402e-05, 9.34076781e-06, 3.44562954e-05], - [1.30882414e-05, 2.84752182e-05, 1.97001150e-06, 4.06297062e-05]]], - [[[3.73462098e-05, 2.13419556e-05, 1.76372928e-05, 5.08518598e-05], - [3.73862612e-05, 2.81456539e-05, 7.56741832e-06, 2.48847233e-05], - [4.00668582e-05, 5.06540214e-06, 4.75135443e-05, 6.95535096e-05], - [4.08493993e-05, 3.15363185e-05, 8.87378259e-05, 8.58657716e-05]], - [[2.59727785e-05, 1.89487929e-05, 2.69543347e-05, 1.05406445e-05], - [2.02566502e-05, 1.61634816e-05, 1.96623777e-05, 7.62652034e-06], - [8.99846010e-06, 1.81581110e-05, 1.62240854e-05, 1.44327701e-05], - [6.79742509e-06, 7.34919385e-06, 5.48993347e-05, 6.01471798e-05]], - [[2.62701780e-05, 9.97827981e-06, 4.56946507e-05, 3.12910413e-05], - [1.80850283e-05, 1.25110957e-05, 1.66817850e-05, 1.00432596e-05], - [1.17713485e-05, 7.52006948e-06, 1.07032640e-05, 4.27590565e-05], - [1.95739418e-05, 7.22217474e-06, 1.53193401e-05, 6.70351779e-05]]], - [[[3.13344980e-05, 2.70606739e-05, 2.44948972e-05, 1.77567021e-05], - [1.90029154e-05, 2.90531980e-05, 1.76740102e-05, 1.29122281e-05], - [1.41654000e-05, 2.45964900e-05, 3.50894348e-05, 5.20973241e-05], - [7.74470545e-06, 3.52484133e-05, 5.12982059e-05, 6.79177688e-05]], - [[2.11172897e-05, 4.71650260e-05, 3.06401319e-05, 3.54002572e-05], - [2.31950645e-05, 9.60246108e-06, 9.74684506e-06, 7.52141756e-06], - [1.00306048e-05, 2.24700247e-05, 1.88671263e-05, 1.16233270e-05], - [3.37392161e-06, 1.88659788e-05, 4.57905920e-05, 3.43578287e-05]], - [[2.12298059e-05, 3.20228280e-05, 2.33673454e-05, 3.68864089e-05], - [9.66985273e-06, 9.29721155e-06, 9.48060716e-06, 1.11725454e-05], - [8.81855731e-06, 1.41611066e-05, 1.54502349e-05, 1.62410677e-05], - [2.14792655e-05, 1.26137383e-05, 2.02223611e-05, - 3.96829419e-05]]]]) * units('s^-1') + truth = np.array([[[[4.19156244e-05, 2.27470339e-05, 2.75954049e-05, 3.00661456e-05], + [6.48775008e-05, 2.64198324e-05, 1.81096782e-05, 3.86059168e-05], + [5.95353239e-05, 1.92166476e-05, 4.18126289e-05, 6.67830089e-05], + [1.09545089e-04, 1.09597033e-04, 1.02745286e-04, 1.26640850e-04]], + [[2.44351405e-05, 1.70396746e-05, 7.04604984e-05, 1.79570429e-05], + [1.89250108e-05, 1.08145724e-05, 2.23802711e-05, 3.46001750e-05], + [7.24858097e-06, 2.14187617e-05, 2.77496740e-05, 1.35732616e-05], + [2.86119377e-05, 2.09171476e-05, 5.85194303e-05, 7.34928257e-05]], + [[3.87902676e-05, 3.47009797e-05, 4.80992294e-05, 5.71784592e-06], + [2.29658884e-05, 1.64309378e-05, 2.42597310e-05, 2.81431841e-05], + [9.01021396e-06, 1.38027998e-05, 9.38915929e-06, 3.45274069e-05], + [1.30470980e-05, 2.84690226e-05, 1.91146748e-06, 4.06621536e-05]]], + [[[3.73610348e-05, 2.13753895e-05, 1.75132430e-05, 5.08578708e-05], + [3.73478589e-05, 2.81829369e-05, 7.61187559e-06, 2.47541807e-05], + [4.01668119e-05, 5.04827148e-06, 4.75479995e-05, 6.97308017e-05], + [4.07669464e-05, 3.13927813e-05, 8.85911406e-05, 8.58408963e-05]], + [[2.59996085e-05, 1.88976315e-05, 2.69607956e-05, 1.05770748e-05], + [2.03013575e-05, 1.61917500e-05, 1.97277459e-05, 7.67203178e-06], + [9.02158329e-06, 1.81740323e-05, 1.62794771e-05, 1.45543595e-05], + [6.76273132e-06, 7.38327075e-06, 5.49008382e-05, 6.00943247e-05]], + [[2.62990866e-05, 9.98588563e-06, 4.56805146e-05, 3.13517416e-05], + [1.81350510e-05, 1.25201914e-05, 1.67241425e-05, 1.00323261e-05], + [1.17779401e-05, 7.52550294e-06, 1.07207344e-05, 4.28610889e-05], + [1.95625745e-05, 7.21619581e-06, 1.52651613e-05, 6.70778242e-05]]], + [[[3.13694760e-05, 2.70707062e-05, 2.43544673e-05, 1.78767184e-05], + [1.89621152e-05, 2.90837615e-05, 1.77459983e-05, 1.30658470e-05], + [1.41916465e-05, 2.46380177e-05, 3.51463709e-05, 5.21862079e-05], + [7.93884738e-06, 3.52826847e-05, 5.12787372e-05, 6.79850401e-05]], + [[2.11456240e-05, 4.71117592e-05, 3.06597644e-05, 3.54925451e-05], + [2.32514580e-05, 9.59287621e-06, 9.78655496e-06, 7.53030733e-06], + [1.00418822e-05, 2.24923252e-05, 1.89152853e-05, 1.16629638e-05], + [3.34881431e-06, 1.88780066e-05, 4.58164777e-05, 3.44487664e-05]], + [[2.12452693e-05, 3.20047506e-05, 2.33463296e-05, 3.69710047e-05], + [9.70025146e-06, 9.30739007e-06, 9.49383200e-06, 1.12134424e-05], + [8.81926698e-06, 1.41706959e-05, 1.54707604e-05, 1.62792600e-05], + [2.14900894e-05, 1.26146394e-05, 2.02217686e-05, + 3.97559787e-05]]]]) * units('s^-1') assert_array_almost_equal(totdef.data, truth, 12) @@ -1299,158 +1299,157 @@ def test_frontogenesis_4d(data_4d): 'latitude', 'longitude' ) - truth = np.array([[[[4.23682195e-10, -6.42818314e-12, -2.16491106e-10, -3.81845902e-10], - [-5.28632893e-10, -6.99413155e-12, -4.77775880e-11, 2.95949984e-10], - [7.82193227e-10, 3.55234312e-10, 2.14592821e-11, -5.20704165e-10], - [-3.51045184e-10, 2.06780694e-10, 1.68485199e-09, -1.46174872e-09]], - [[-7.24768625e-11, 1.07136516e-10, -1.33696585e-10, 3.43097590e-10], - [-5.01911031e-11, 2.14208730e-11, -4.73005145e-11, 9.54558115e-11], - [4.78125962e-11, 6.95838402e-11, 3.53993328e-10, -7.14986278e-11], - [6.14372837e-10, 1.41441177e-10, 8.45358549e-10, 1.36583089e-09]], - [[2.06344624e-11, 3.21877016e-10, 5.56930831e-10, 1.42479782e-10], - [9.85250671e-11, 1.06837011e-10, 5.71410578e-11, -4.75266666e-12], - [-6.42402291e-11, -2.11239598e-11, -1.21400141e-11, 2.11343115e-10], - [-6.98848390e-11, -4.11958693e-11, -1.75826411e-10, -1.78597026e-10]]], - [[[1.75135966e-10, -1.28928980e-11, -5.23466009e-11, -3.77702045e-10], - [-1.89751794e-10, -2.39181519e-11, 1.11100290e-11, 3.27299708e-10], - [5.03778532e-10, 6.01896046e-11, 2.52803022e-10, 2.63665975e-10], - [8.58126215e-10, -1.03984888e-10, 1.36888801e-09, -2.55913184e-11]], - [[-4.65433709e-11, -4.29058715e-11, 1.37345453e-10, 1.99587747e-10], - [-7.54157254e-11, 3.17296645e-11, 1.98329738e-11, 7.03907102e-11], - [2.07597572e-11, 9.76879251e-11, 3.64976958e-11, 9.05618888e-11], - [1.08070144e-10, 4.28175872e-12, 7.19436313e-10, -4.06647884e-10]], - [[3.53005192e-11, 6.97543696e-12, 7.69214961e-10, 1.72736149e-10], - [8.60386822e-11, 6.45426820e-11, 5.98938946e-11, -3.48393954e-11], - [-8.04261595e-11, 3.35202076e-11, -6.74886256e-11, 2.13321809e-10], - [3.19647867e-12, -1.19646868e-10, -3.89001592e-11, 2.32044147e-10]]], - [[[-1.06347076e-10, 1.43182380e-10, -1.67871828e-10, 7.55627348e-12], - [-2.10533376e-11, -1.36653603e-11, 5.14204938e-11, 4.52818256e-11], - [9.35607979e-11, -1.90340153e-11, 5.14752944e-11, 3.57906394e-10], - [1.47969288e-09, -6.40884777e-11, -2.00932232e-10, 3.79333810e-10]], - [[1.39959107e-10, -3.68301267e-10, 2.35651001e-10, 1.53240049e-10], - [-2.59723933e-10, 3.90319800e-11, 9.15095885e-11, 3.55206479e-11], - [6.07203827e-12, 8.14448434e-11, 2.37589779e-11, 1.56707972e-10], - [6.01077213e-11, 1.43024438e-11, 2.19842020e-10, 6.06611960e-12]], - [[5.76777660e-11, 1.50880981e-10, 9.80083831e-11, 1.37735770e-10], - [-5.69004435e-11, 1.61334326e-11, 8.32223545e-11, 1.07345248e-10], - [-5.82285173e-11, 1.03267739e-12, 9.19171693e-12, 1.73823741e-10], - [-2.33302976e-11, 1.01795295e-10, 4.19754683e-12, - 5.18286088e-10]]]]) * units('K/m/s') + truth = np.array([[[[4.23682388e-10, -6.60428594e-12, -2.16700227e-10, -3.80960666e-10], + [-5.28427593e-10, -7.11496293e-12, -4.77951513e-11, 2.94985981e-10], + [7.86953679e-10, 3.54196972e-10, 2.07842740e-11, -5.25487973e-10], + [-3.52111258e-10, 2.06421077e-10, 1.67986422e-09, -1.45950592e-09]], + [[-7.31728965e-11, 1.06892315e-10, -1.33453527e-10, 3.42647921e-10], + [-5.05805666e-11, 2.12238918e-11, -4.71306612e-11, 9.62250022e-11], + [4.76933273e-11, 6.94586917e-11, 3.53139630e-10, -7.14834221e-11], + [6.14587969e-10, 1.41091788e-10, 8.42714362e-10, 1.36031856e-09]], + [[2.05113794e-11, 3.21339794e-10, 5.56947831e-10, 1.43142115e-10], + [9.85782985e-11, 1.06721561e-10, 5.73106405e-11, -5.03368922e-12], + [-6.43122987e-11, -2.12772736e-11, -1.17352480e-11, 2.13297934e-10], + [-6.97155996e-11, -4.10739462e-11, -1.75156002e-10, -1.76167917e-10]]], + [[[1.74719456e-10, -1.35620544e-11, -5.23975776e-11, -3.77740716e-10], + [-1.89498320e-10, -2.40570704e-11, 1.09765802e-11, 3.26582884e-10], + [5.05760395e-10, 5.96930313e-11, 2.51806496e-10, 2.62326483e-10], + [8.55597272e-10, -1.03839677e-10, 1.36437001e-09, -2.55279252e-11]], + [[-4.68143046e-11, -4.29566800e-11, 1.37326379e-10, 2.00212822e-10], + [-7.60292021e-11, 3.13481943e-11, 2.02636812e-11, 7.07310188e-11], + [2.07073318e-11, 9.74536122e-11, 3.64495220e-11, 9.11599007e-11], + [1.07707226e-10, 4.27961436e-12, 7.17400120e-10, -4.07742791e-10]], + [[3.51033086e-11, 6.86914537e-12, 7.68630167e-10, 1.73824937e-10], + [8.63644951e-11, 6.43950959e-11, 6.01335884e-11, -3.49684748e-11], + [-8.06772168e-11, 3.34221310e-11, -6.70871076e-11, 2.13933933e-10], + [2.77857293e-12, -1.19419804e-10, -3.88340891e-11, 2.35051688e-10]]], + [[[-1.06920260e-10, 1.42163009e-10, -1.67670634e-10, 7.77738130e-12], + [-2.14431980e-11, -1.40383248e-11, 5.12326588e-11, 4.47136472e-11], + [9.29690678e-11, -1.91237280e-11, 5.11911088e-11, 3.57423744e-10], + [1.48172065e-09, -6.47936247e-11, -2.02021163e-10, 3.76309534e-10]], + [[1.40697485e-10, -3.68197137e-10, 2.35522920e-10, 1.53804948e-10], + [-2.61409796e-10, 3.88149869e-11, 9.17155132e-11, 3.56335985e-11], + [6.05095218e-12, 8.10937994e-11, 2.38586262e-11, 1.57114763e-10], + [5.98536934e-11, 1.42709122e-11, 2.20296991e-10, 6.13222348e-12]], + [[5.77582222e-11, 1.50846336e-10, 9.79419525e-11, 1.38512768e-10], + [-5.73091526e-11, 1.59416672e-11, 8.32303219e-11, 1.08035832e-10], + [-5.84859130e-11, 7.43545248e-13, 9.37957614e-12, 1.74102020e-10], + [-2.38469755e-11, 1.01414977e-10, 4.18826651e-12, + 5.18914848e-10]]]]) * units('K/m/s') assert_array_almost_equal(frnt.data, truth, 16) def test_geostrophic_wind_4d(data_4d): """Test geostrophic_wind on a 4D (time, pressure, y, x) grid.""" u_g, v_g = geostrophic_wind(data_4d.height) - u_g_truth = np.array([[[[4.40351577, 12.52087174, 20.6458988, 3.17057524], - [14.11461945, 17.13672114, 22.06686549, 28.28270102], - [24.47454294, 22.86342357, 31.74065923, 41.48130088], - [35.59988608, 29.85398309, 50.68045123, 41.40714946]], - [[7.36263117, 11.15525254, 15.36136167, 8.9043257], - [8.36777239, 12.52326604, 13.39382587, 14.3316852], - [10.38214971, 13.05048176, 16.57141998, 20.60619861], - [13.53264547, 12.63889256, 25.51465438, 27.85194657]], - [[5.7558101, 8.87403945, 12.12136937, 6.95914488], - [5.63469768, 9.23443489, 9.46733739, 9.64257854], - [5.15675792, 8.93121196, 10.1444189, 10.03116286], - [4.2776387, 7.88423455, 14.54891694, 7.85445617]]], - [[[2.56196227, 12.12574931, 18.89599304, 9.31367555], - [11.14381317, 16.08241758, 22.90372798, 23.24530036], - [21.19957068, 18.21200016, 27.48622742, 37.93750724], - [32.9424023, 18.30589852, 32.72832352, 53.53662491]], - [[5.89065665, 10.24260864, 13.9982738, 7.63017804], - [7.7319368, 12.49290588, 13.8820136, 12.98573492], - [9.40028538, 12.48920597, 15.31293349, 18.73778037], - [10.88139947, 9.96423153, 18.47901734, 24.95509777]], - [[5.3790145, 9.32173797, 9.01530817, 3.68853401], - [5.42563016, 8.9380796, 9.35289746, 9.01581846], - [4.95407268, 8.35221797, 9.30403934, 11.10242176], - [3.90093595, 7.537516, 8.82237876, 9.57266969]]], - [[[4.077062, 9.91350661, 14.63954845, 11.45133198], - [9.22655905, 15.4118828, 20.8655254, 20.36949692], - [17.30011986, 16.30232673, 23.25100815, 32.47299215], - [28.67482075, 12.0424244, 21.34996542, 48.18503321]], - [[4.67946573, 7.67735341, 7.67258169, 7.43729616], - [6.37289243, 10.60261239, 12.10568057, 11.53061917], - [7.78079442, 11.18649202, 14.92802562, 16.19084404], - [8.87459397, 9.15376375, 15.95950978, 21.50271574]], - [[4.07034519, 6.49914852, 4.9842596, 5.1120617], - [4.20250871, 6.7603074, 8.51019946, 8.51714298], - [3.85757805, 6.9373935, 9.8250342, 10.52736698], - [2.97756024, 7.02083208, 8.67190524, - 10.98516411]]]]) * units('m/s') - v_g_truth = np.array([[[[-2.34336304e+01, -1.93589800e+01, -7.42980465e+00, - 1.23538955e+01], - [-2.05343103e+01, -1.59281972e+01, -7.22778771e+00, - 5.56691831e+00], - [-2.12483061e+01, -1.50276890e+01, -1.26159708e+00, - 2.00499696e+01], - [-2.82673839e+01, -1.22322398e+01, 2.74929719e+00, - 1.66772271e+01]], - [[-2.11572490e+01, -1.57068398e+01, -7.16428821e+00, - 4.47040576e+00], - [-1.85233793e+01, -1.38641633e+01, -7.23745352e+00, - 1.35674995e+00], - [-1.48069287e+01, -1.29873005e+01, -6.19402168e+00, - 5.57290769e+00], - [-1.63708722e+01, -1.07203268e+01, -3.25405588e+00, - 6.02794043e+00]], - [[-1.83721994e+01, -1.51434535e+01, -8.30361332e+00, - 2.14732109e+00], - [-1.60334603e+01, -1.37004633e+01, -8.52272656e+00, - -5.00249972e-01], - [-1.25811419e+01, -1.30858045e+01, -8.11893604e+00, - 2.31946331e+00], - [-1.07972595e+01, -1.12050147e+01, -8.05482698e+00, - -1.34669618e+00]]], - [[[-2.47128002e+01, -2.06093912e+01, -7.53605837e+00, - 1.45071982e+01], - [-2.04618167e+01, -1.66379272e+01, -6.94777385e+00, - 8.60864325e+00], - [-2.03847527e+01, -1.41640171e+01, -3.58588785e+00, - 1.13496351e+01], - [-3.06442215e+01, -1.34818877e+01, 3.63145087e+00, - 2.06957942e+01]], - [[-2.20117576e+01, -1.60592509e+01, -6.79979611e+00, - 5.76660686e+00], - [-1.88926841e+01, -1.40452204e+01, -7.10711240e+00, - 1.92164004e+00], - [-1.49428085e+01, -1.27155409e+01, -6.56034626e+00, - 3.52277542e+00], - [-1.56847892e+01, -1.10535722e+01, -3.82991450e+00, - 5.98618380e+00]], - [[-1.89418619e+01, -1.48982095e+01, -8.32871820e+00, - 7.66612047e-01], - [-1.57997569e+01, -1.38337366e+01, -9.12720817e+00, - -1.68017155e+00], - [-1.34002412e+01, -1.27868867e+01, -8.32854573e+00, - -2.52183180e-02], - [-1.10305552e+01, -1.16852908e+01, -7.77451018e+00, - 7.01786569e-01]]], - [[[-2.87198561e+01, -2.07541862e+01, -7.39120444e+00, - 1.13690891e+01], - [-2.50727626e+01, -1.76277299e+01, -6.48428638e+00, - 8.35756789e+00], - [-2.15686958e+01, -1.43774917e+01, -4.66795064e+00, - 7.55992752e+00], - [-3.08303915e+01, -1.46678239e+01, 2.17589132e+00, - 1.97007540e+01]], - [[-2.14034948e+01, -1.55089179e+01, -7.19566930e+00, - 3.53625110e+00], - [-1.85643117e+01, -1.42866005e+01, -7.10227950e+00, - 2.98865138e+00], - [-1.52824784e+01, -1.23952994e+01, -6.71565437e+00, - 1.75645659e+00], - [-1.53343446e+01, -1.06296646e+01, -4.49885811e+00, - 3.05807491e+00]], - [[-1.62094234e+01, -1.41161427e+01, -9.20541452e+00, - -1.47723905e+00], - [-1.41272620e+01, -1.33895366e+01, -9.16198151e+00, - -1.44459685e+00], - [-1.29925870e+01, -1.17892453e+01, -8.27421454e+00, - -2.44749477e+00], - [-1.08991833e+01, -1.03581717e+01, -7.35501458e+00, - -1.88971184e+00]]]]) * units('m/s') + u_g_truth = np.array([[[[4.4048682, 12.51692258, 20.6372888, 3.17769076], + [14.10194272, 17.12263389, 22.04954728, 28.25627227], + [24.44520364, 22.83658626, 31.70185292, 41.43474924], + [35.55078527, 29.81195711, 50.61167797, 41.34530902]], + [[7.35972965, 11.1508039, 15.35393025, 8.90224418], + [8.36112058, 12.51333565, 13.38382857, 14.31961908], + [10.36996705, 13.0359012, 16.55131816, 20.5818523, ], + [13.51358869, 12.61987535, 25.47981594, 27.81300202]], + [[5.75323442, 8.87025383, 12.11513202, 6.9569899], + [5.63036347, 9.22723021, 9.46050042, 9.6346362], + [5.15111673, 8.92136198, 10.13229278, 10.02026762], + [4.27093343, 7.87208428, 14.5287988, 7.84193975]]], + [[[2.56374289, 12.12175071, 18.88903041, 9.31429628], + [11.13363838, 16.0692652, 22.88529273, 23.22479772], + [21.17380408, 18.19154086, 27.4544941, 37.89230504], + [32.89749307, 18.27860521, 32.68137119, 53.46237373]], + [[5.88868673, 10.23886093, 13.99207011, 7.62863328], + [7.72562462, 12.48283865, 13.87130247, 12.9747224], + [9.38948486, 12.47560991, 15.29521325, 18.71570391], + [10.86569379, 9.94843902, 18.45258217, 24.92010393]], + [[5.37666159, 9.31750301, 9.01145261, 3.6887154], + [5.42142711, 8.93123924, 9.34560535, 9.00788023], + [4.9486882, 8.34297898, 9.29367604, 11.09021549], + [3.89472979, 7.52596773, 8.80903347, 9.55782342]]], + [[[4.07701203, 9.91100477, 14.63521206, 11.44931207], + [9.21849021, 15.39896866, 20.84826281, 20.3521286], + [17.27879226, 16.28474129, 23.22522698, 32.4339051], + [28.63614846, 12.02289896, 21.31740279, 48.11881204]], + [[4.67797906, 7.67496412, 7.67070558, 7.4354085], + [6.3676578, 10.5938839, 12.09551605, 11.52096098], + [7.77187678, 11.17427574, 14.91109545, 16.17177845], + [8.86174332, 9.13936002, 15.93605997, 21.47254661]], + [[4.06859757, 6.49637507, 4.98325985, 5.1109647], + [4.19923572, 6.75503352, 8.50297947, 8.50993959], + [3.85339539, 6.92959206, 9.81419868, 10.5154729], + [2.97279544, 7.01038155, 8.65854052, 10.9689316]]]]) * units('m/s') + v_g_truth = np.array([[[[-2.34997753e+01, -1.94136235e+01, -7.45077637e+00, + 1.23887662e+01], + [-2.05898579e+01, -1.59712848e+01, -7.24733971e+00, + 5.58197747e+00], + [-2.13032949e+01, -1.50665793e+01, -1.26486198e+00, + 2.01018571e+01], + [-2.83372497e+01, -1.22624731e+01, 2.75609237e+00, + 1.67184466e+01]], + [[-2.12169685e+01, -1.57511747e+01, -7.18451047e+00, + 4.48302414e+00], + [-1.85734872e+01, -1.39016674e+01, -7.25703167e+00, + 1.36042011e+00], + [-1.48452478e+01, -1.30209105e+01, -6.21005126e+00, + 5.58732988e+00], + [-1.64113345e+01, -1.07468232e+01, -3.26209862e+00, + 6.04283912e+00]], + [[-1.84240576e+01, -1.51861981e+01, -8.32705150e+00, + 2.15338222e+00], + [-1.60768326e+01, -1.37375247e+01, -8.54578152e+00, + -5.01603207e-01], + [-1.26137008e+01, -1.31196694e+01, -8.13994713e+00, + 2.32546588e+00], + [-1.08239460e+01, -1.12327091e+01, -8.07473534e+00, + -1.35002468e+00]]], + [[[-2.47825558e+01, -2.06675642e+01, -7.55733001e+00, + 1.45481469e+01], + [-2.05171683e+01, -1.66829347e+01, -6.96656838e+00, + 8.63193062e+00], + [-2.04375067e+01, -1.42006723e+01, -3.59516781e+00, + 1.13790069e+01], + [-3.07199620e+01, -1.35152096e+01, 3.64042638e+00, + 2.07469460e+01]], + [[-2.20738890e+01, -1.61045805e+01, -6.81898954e+00, + 5.78288395e+00], + [-1.89437910e+01, -1.40832144e+01, -7.12633797e+00, + 1.92683830e+00], + [-1.49814792e+01, -1.27484476e+01, -6.57732385e+00, + 3.53189205e+00], + [-1.57235558e+01, -1.10808922e+01, -3.83938054e+00, + 6.00097928e+00]], + [[-1.89953281e+01, -1.49402619e+01, -8.35222723e+00, + 7.68775922e-01], + [-1.58424970e+01, -1.38711585e+01, -9.15189832e+00, + -1.68471661e+00], + [-1.34349198e+01, -1.28199780e+01, -8.35009927e+00, + -2.52835808e-02], + [-1.10578184e+01, -1.17141722e+01, -7.79372570e+00, + 7.03521108e-01]]], + [[[-2.88009221e+01, -2.08127679e+01, -7.41206720e+00, + 1.14011801e+01], + [-2.51405873e+01, -1.76754149e+01, -6.50182713e+00, + 8.38017608e+00], + [-2.16245136e+01, -1.44146994e+01, -4.68003089e+00, + 7.57949195e+00], + [-3.09065921e+01, -1.47040769e+01, 2.18126927e+00, + 1.97494465e+01]], + [[-2.14639093e+01, -1.55526942e+01, -7.21598014e+00, + 3.54623269e+00], + [-1.86145303e+01, -1.43252474e+01, -7.12149199e+00, + 2.99673603e+00], + [-1.53220281e+01, -1.24273773e+01, -6.73303389e+00, + 1.76100214e+00], + [-1.53722451e+01, -1.06559370e+01, -4.50997751e+00, + 3.06563326e+00]], + [[-1.62551769e+01, -1.41559875e+01, -9.23139816e+00, + -1.48140877e+00], + [-1.41654778e+01, -1.34257568e+01, -9.18676573e+00, + -1.44850466e+00], + [-1.30262107e+01, -1.18197548e+01, -8.29562748e+00, + -2.45382867e+00], + [-1.09261218e+01, -1.03837731e+01, -7.37319328e+00, + -1.89438246e+00]]]]) * units('m/s') assert_array_almost_equal(u_g.data, u_g_truth, 6) assert_array_almost_equal(v_g.data, v_g_truth, 6) @@ -1459,115 +1458,86 @@ def test_inertial_advective_wind_4d(data_4d): """Test inertial_advective_wind on a 4D (time, pressure, y, x) grid.""" u_g, v_g = geostrophic_wind(data_4d.height) u_i, v_i = inertial_advective_wind(u_g, v_g, u_g, v_g) - u_i_truth = np.array([[[[-4.74579332, -6.36486064, -7.20354171, -11.08307751], - [-1.88515129, -4.33855679, -6.82871465, -9.38096911], - [2.308649, -6.93391208, -14.06293133, -20.60786775], - [-0.92388354, -13.76737076, -17.9039117, -23.71419254]], - [[-2.60558413, -3.48755492, -3.62050089, -4.18871134], - [-3.36965812, -2.57689219, -2.66529828, -3.34582207], - [-0.56309499, -2.3322732, -4.37379768, -6.6663065], - [1.70092943, -3.59623514, -5.94640587, -7.50380432]], - [[-1.60508844, -2.30572073, -2.39044749, -2.59511279], - [-2.18854472, -1.47967397, -1.57319604, -2.24386278], - [-1.10582176, -1.24627092, -2.02075175, -3.314856], - [-0.25911941, -1.62294229, -1.75103256, -1.21885814]]], - [[[-6.69345313, -6.73506869, -7.9082287, -12.43972804], - [-2.21048835, -5.05651724, -7.72691754, -11.18333726], - [2.66904547, -4.81530785, -9.54984823, -12.89835729], - [8.55752862, -7.70089375, -12.37978952, -10.22208691]], - [[-3.17485999, -3.54021424, -3.54593593, -4.29515483], - [-3.68981249, -2.85516457, -2.76603925, -3.31604629], - [-1.16624451, -2.17242275, -3.57186768, -5.25444633], - [1.41851647, -2.44637201, -4.63693023, -6.09680756]], - [[-3.2219496, -1.90321215, -1.16750878, -1.08832287], - [-2.0239913, -1.38273223, -1.39926438, -1.92743159], - [-1.31353175, -1.15761322, -1.72857968, -2.81015813], - [-0.96137414, -0.94030556, -1.52657711, -2.56457651]]], - [[[-5.10794084, -5.32937859, -5.93090309, -8.05663994], - [-5.25295525, -6.02259284, -7.06582462, -9.0763472], - [0.32747247, -4.38931301, -7.24210551, -8.856658], - [11.82591067, -3.51482111, -8.18935835, -3.90270871]], - [[-2.9420404, -1.93269048, -1.78193608, -2.21710641], - [-2.96678921, -2.48380116, -2.64978243, -3.39496054], - [-1.42507824, -2.23090734, -3.01660858, -3.95003961], - [0.38000295, -2.10863221, -3.40584443, -4.06614801]], - [[-1.84525414, -0.73542408, -0.62568812, -1.18458192], - [-0.90497548, -1.10518325, -1.44073904, -1.95278103], - [-0.97196521, -1.22914653, -1.48019684, -1.79349709], - [-1.29544691, -0.9808466, -1.24778616, - -1.95945874]]]]) * units('m/s') - v_i_truth = np.array([[[[1.03108918e+01, 5.87304544e+00, -3.23865690e+00, - -1.88225987e+01], - [9.87187503e+00, 5.33610060e+00, 4.80874417e+00, - 3.92484555e-02], - [6.37856912e+00, 6.46296166e+00, 8.14267044e+00, - 4.37232518e+00], - [-1.30385124e+00, 1.01032585e+01, 4.20243238e+00, - -1.97934081e+01]], - [[1.10360108e+00, 2.30280536e+00, -1.82078930e+00, - -3.54284012e+00], - [2.43663102e+00, 1.35818636e+00, 4.92919838e-01, - -9.85544117e-03], - [2.33985677e+00, 1.03370035e+00, 3.28069921e+00, - 4.50046765e-01], - [2.93689077e-01, 1.43848430e+00, 6.69758269e+00, - -4.27897434e+00]], - [[4.77869846e-01, 1.14482717e+00, -1.82404796e+00, - -1.95731131e+00], - [5.19464097e-01, 4.52949199e-01, -3.26412809e-01, - 6.88744088e-02], - [2.51097720e-01, 1.43448773e-01, 1.08982754e+00, - -9.69963394e-02], - [-3.37860948e-01, 2.48187099e-01, 2.41935519e+00, - -2.84847302e+00]]], - [[[9.00342804e+00, 6.74193832e+00, 5.48141003e-01, - -1.25005172e+01], - [9.56628265e+00, 4.57654669e+00, 3.34479904e+00, - -7.13103555e+00], - [5.46655351e+00, 2.14241047e+00, 7.51934330e+00, - 2.43229680e+00], - [-5.48082957e+00, -6.46852260e-01, 1.34334674e+01, - 1.61485491e+01]], - [[2.49375451e+00, 3.34815514e+00, -7.09673457e-01, - -3.42185701e+00], - [2.69963182e+00, 1.64621317e+00, 2.91799176e-01, - -1.12584231e+00], - [1.83462164e+00, 1.71608154e-01, 1.87927013e+00, - 7.54482898e-01], - [-4.86175507e-01, -1.06374611e+00, 4.20283383e+00, - 1.54789418e+00]], - [[1.05175282e+00, 2.36715709e-01, -4.35406547e-01, - -9.39935118e-01], - [5.26821709e-01, 1.34167595e-01, 6.74485663e-02, - 1.18351992e-01], - [9.51152970e-02, 3.63519903e-02, 2.14587938e-01, - 6.10557463e-01], - [-2.42904366e-01, -5.80309556e-02, -3.63185957e-02, - 2.28010678e-01]]], - [[[5.18112516e+00, 8.23347995e+00, 2.85922078e+00, - -5.58457816e+00], - [8.85157651e+00, 4.70839103e+00, 2.51314815e+00, - -5.64246393e+00], - [7.54770787e+00, 8.21372199e-02, 4.70293099e+00, - 3.47174970e+00], - [-1.92174464e+00, -5.91657547e+00, 1.00629730e+01, - 2.62854305e+01]], - [[2.20347520e+00, 3.00714687e+00, 1.59377661e+00, - -6.41826692e-01], - [2.15604582e+00, 1.86128202e+00, 1.28260457e+00, - -1.03918888e+00], - [1.50501488e+00, 5.74547239e-01, 1.52092784e+00, - -3.94591487e-01], - [2.83614456e-02, -8.95222937e-01, 2.49176874e+00, - 1.81097696e+00]], - [[6.98668139e-01, 2.56635250e-01, 1.74332893e+00, - 3.79321436e-01], - [2.39593746e-01, 4.88748160e-01, 1.16884612e+00, - -7.54110131e-03], - [-6.40285805e-02, 5.82931602e-01, 4.67005716e-01, - 3.76288542e-02], - [-2.10896883e-01, 5.17706856e-01, -4.13562541e-01, - 6.96975860e-01]]]]) * units('m/s') + u_i_truth = np.array([[[[-4.77165787, -6.39928757, -7.24239774, -11.14139847], + [-1.8967587, -4.36028755, -6.86016435, -9.424228], + [2.31421679, -6.96263439, -14.11859275, -20.68976199], + [-0.92900951, -13.81722973, -17.96832023, -23.80435234]], + [[-2.62194257, -3.50676725, -3.63961746, -4.21059159], + [-3.38684408, -2.58995365, -2.67792148, -3.36122749], + [-0.56740802, -2.34244481, -4.39126012, -6.69284736], + [1.70715454, -3.60961021, -5.96780511, -7.53107716]], + [[-1.61558735, -2.31867093, -2.40316115, -2.60870259], + [-2.19984407, -1.48762908, -1.58089856, -2.2541336], + [-1.11136338, -1.25207315, -2.02918744, -3.32828099], + [-0.26028196, -1.62956357, -1.75756959, -1.22270124]]], + [[[-6.72938857, -6.77202159, -7.95073037, -12.50625533], + [-2.22377841, -5.0815521, -7.76259189, -11.23523285], + [2.67551814, -4.83617581, -9.58820051, -12.95106032], + [8.58739912, -7.72793742, -12.42304341, -10.25891257]], + [[-3.19431927, -3.55990592, -3.56474965, -4.31772693], + [-3.70858471, -2.86947801, -2.77907873, -3.331319], + [-1.17292465, -2.182095, -3.58631575, -5.27553824], + [1.4236791, -2.45544962, -4.65344893, -6.11853894]], + [[-3.24030343, -1.91423726, -1.1742268, -1.09439772], + [-2.03479751, -1.39015234, -1.40603089, -1.93610702], + [-1.31981448, -1.16318518, -1.73599486, -2.82161648], + [-0.96540565, -0.94432034, -1.53211138, -2.57328907]]], + [[[-5.13892702, -5.35990209, -5.96305829, -8.10039371], + [-5.28049715, -6.05189422, -7.09840362, -9.11834812], + [0.32358269, -4.40891596, -7.27141143, -8.89305721], + [11.86892255, -3.52631413, -8.21707342, -3.9149252]], + [[-2.95997348, -1.94436814, -1.79187921, -2.22918106], + [-2.98223302, -2.49621136, -2.66214712, -3.41052605], + [-1.43265094, -2.2408268, -3.02891598, -3.9658998], + [0.38112998, -2.11641585, -3.417963, -4.08044633]], + [[-1.85590971, -0.74052267, -0.62971895, -1.19099569], + [-0.91035149, -1.11111857, -1.44768616, -1.96172425], + [-0.97667565, -1.23489465, -1.48658447, -1.80074616], + [-1.30083552, -0.98479841, -1.25235639, + -1.96633294]]]]) * units('m/s') + v_i_truth = np.array([[[[1.03230312e+01, 5.87882109e+00, -3.24343027e+00, -1.88483470e+01], + [9.87647721e+00, 5.33706213e+00, 4.80929670e+00, 3.63063183e-02], + [6.37603821e+00, 6.45974507e+00, 8.14449487e+00, 4.38722620e+00], + [-1.31406689e+00, 1.00969188e+01, 4.19901525e+00, + -1.97739544e+01]], + [[1.10383561e+00, 2.30354462e+00, -1.82374723e+00, -3.54809094e+00], + [2.43631993e+00, 1.35723724e+00, 4.91193534e-01, -1.02997771e-02], + [2.33864366e+00, 1.03130947e+00, 3.27949769e+00, 4.52250225e-01], + [2.90865168e-01, 1.43496262e+00, 6.69604741e+00, -4.27768358e+00]], + [[4.77255548e-01, 1.14453826e+00, -1.82710412e+00, -1.96018490e+00], + [5.18797941e-01, 4.51757453e-01, -3.28462782e-01, 6.84789970e-02], + [2.50176678e-01, 1.41538500e-01, 1.08853845e+00, -9.62071225e-02], + [-3.39224824e-01, 2.45760327e-01, 2.41856776e+00, + -2.84808630e+00]]], + [[[9.01508187e+00, 6.74751069e+00, 5.47135566e-01, -1.25176087e+01], + [9.57125782e+00, 4.57776586e+00, 3.34524473e+00, -7.13601695e+00], + [5.46543202e+00, 2.13979774e+00, 7.51931363e+00, 2.43567533e+00], + [-5.48910344e+00, -6.52697336e-01, 1.34309575e+01, + 1.61565561e+01]], + [[2.49548039e+00, 3.34982501e+00, -7.11777553e-01, -3.42687086e+00], + [2.70007988e+00, 1.64584666e+00, 2.90292095e-01, -1.12712093e+00], + [1.83356146e+00, 1.69401994e-01, 1.87788933e+00, 7.55385123e-01], + [-4.89203395e-01, -1.06751808e+00, 4.20107093e+00, + 1.54893157e+00]], + [[1.05193589e+00, 2.35318468e-01, -4.37301952e-01, -9.41622628e-01], + [5.26337352e-01, 1.32572812e-01, 6.61575719e-02, 1.18009862e-01], + [9.40801497e-02, 3.45333939e-02, 2.13427873e-01, 6.10855423e-01], + [-2.44339907e-01, -6.01035575e-02, -3.78806842e-02, + 2.28008249e-01]]], + [[[5.18811867e+00, 8.23959428e+00, 2.86095202e+00, -5.59181418e+00], + [8.85485851e+00, 4.71028978e+00, 2.51387570e+00, -5.64507599e+00], + [7.54725519e+00, 7.98206363e-02, 4.70219106e+00, 3.47217441e+00], + [-1.92815930e+00, -5.92302637e+00, 1.00607869e+01, + 2.62899914e+01]], + [[2.20504999e+00, 3.00861548e+00, 1.59466025e+00, -6.42397860e-01], + [2.15641722e+00, 1.86132244e+00, 1.28263500e+00, -1.03958535e+00], + [1.50404596e+00, 5.72947187e-01, 1.51990698e+00, -3.94664336e-01], + [2.57832794e-02, -8.98652226e-01, 2.48959124e+00, 1.81170400e+00]], + [[6.98702092e-01, 2.55782733e-01, 1.74430100e+00, 3.79660759e-01], + [2.39131800e-01, 4.87869781e-01, 1.16903247e+00, -7.66523806e-03], + [-6.48734332e-02, 5.81810137e-01, 4.66189458e-01, 3.71854388e-02], + [-2.11996986e-01, 5.16093087e-01, -4.15633085e-01, + 6.96457035e-01]]]]) * units('m/s') assert_array_almost_equal(u_i.data, u_i_truth, 6) assert_array_almost_equal(v_i.data, v_i_truth, 6) @@ -1576,87 +1546,87 @@ def test_q_vector_4d(data_4d): """Test q_vector on a 4D (time, pressure, y, x) grid.""" u_g, v_g = geostrophic_wind(data_4d.height) q1, q2 = q_vector(u_g, v_g, data_4d.temperature, data_4d.pressure) - q1_truth = np.array([[[[-8.98245364e-13, 2.03803219e-13, 2.88874668e-12, 2.18043424e-12], - [4.37446820e-13, 1.21145200e-13, 1.51859353e-12, 3.82803347e-12], - [-1.20538030e-12, 2.27477298e-12, 3.47570178e-12, 3.03123012e-12], - [-1.51597275e-12, 8.02915408e-12, 7.71292472e-12, -2.22078527e-12]], - [[5.72960497e-13, 1.04264321e-12, -1.75695523e-13, 1.20745997e-12], - [2.94807953e-13, 5.80261767e-13, 6.23668595e-13, 7.31474131e-13], - [-4.04218965e-14, 3.24794013e-13, 1.39539675e-12, 2.82242029e-12], - [3.27509076e-13, 5.61307677e-13, 1.13454829e-12, 4.63551274e-12]], - [[2.23877015e-13, 5.77177907e-13, 1.62133659e-12, 5.43858376e-13], - [2.65333917e-13, 2.41006445e-13, 3.72510595e-13, 7.35822030e-13], - [6.56644633e-14, 1.99773842e-13, 5.20573457e-13, 1.69706608e-12], - [4.15915138e-14, 1.19910880e-13, 1.03632944e-12, 1.99561829e-12]]], - [[[-2.68870846e-13, 1.35977736e-12, 4.17548337e-12, 1.50465522e-12], - [4.62457018e-14, 1.25888111e-13, 2.15928418e-12, 4.70656495e-12], - [-1.25393137e-12, 9.54737370e-13, 1.48443002e-12, 2.12375621e-12], - [-2.93284658e-12, 6.06555344e-12, 4.21151397e-12, -2.12250513e-12]], - [[4.23461674e-13, 1.39393686e-13, 5.89509120e-13, 2.55041326e-12], - [5.73125714e-13, 5.60965341e-13, 7.65040451e-13, 9.49571939e-13], - [2.17153819e-14, 3.97023968e-13, 1.09194718e-12, 1.90731542e-12], - [1.45101233e-13, 1.79588608e-13, 1.03018848e-12, 3.62186462e-12]], - [[5.32674437e-13, 5.13465061e-13, 1.15582657e-12, 1.04827520e-12], - [2.77261345e-13, 2.33645555e-13, 4.59592371e-13, 5.34293340e-13], - [1.47376125e-13, 1.95746242e-13, 3.45854003e-13, 7.47741411e-13], - [-2.14078421e-14, 1.75226662e-13, 4.85424103e-13, 1.10808035e-12]]], - [[[6.41348753e-13, 1.88256910e-12, 5.21213092e-12, 2.07707653e-12], - [1.30753737e-12, 4.77125469e-13, 2.15204760e-12, 3.07374453e-12], - [-2.30546806e-13, 2.49929428e-13, 8.82215204e-14, 2.45990265e-12], - [-7.25812141e-12, 8.47072439e-13, -2.06762495e-12, - -4.40132129e-12]], - [[6.03705941e-13, -6.71320661e-13, 9.10543636e-13, 5.82480651e-13], - [9.54081741e-13, 6.11781160e-13, 6.95995265e-13, 8.67169047e-13], - [7.86580678e-14, 5.27405484e-13, 7.45800341e-13, 1.33965768e-12], - [2.22480631e-13, -1.98920384e-13, 8.56608245e-13, 1.59793218e-12]], - [[4.47195537e-13, 2.18235390e-13, 3.30926531e-13, -4.06675908e-14], - [1.70517246e-13, 2.18234962e-13, 3.78622612e-13, 5.03962144e-13], - [2.59462161e-13, 2.65626826e-13, 2.04642555e-13, 6.02812047e-13], - [1.69339642e-13, 2.91716502e-13, -1.20043003e-14, - 4.43770388e-13]]]]) * units('m^2 kg^-1 s^-1') - q2_truth = np.array([[[[3.33980776e-12, -1.32969763e-13, 1.01454470e-12, 6.02652581e-12], - [2.52898242e-13, -1.71069245e-13, -8.24708561e-13, 1.66384429e-13], - [-3.50646511e-12, -1.68929195e-12, 7.76215111e-13, 1.54486058e-12], - [-1.75492099e-12, -3.86524071e-12, -1.89368596e-12, - -5.14689517e-12]], - [[-2.09848775e-13, -6.25683634e-13, -1.40009292e-13, 1.08972893e-12], - [-2.58259284e-13, -2.67211578e-13, -6.41928957e-14, 5.90625597e-13], - [-2.73346325e-13, -2.28248227e-13, -4.76577835e-13, - -8.48559875e-13], - [1.21003124e-12, -5.10541546e-13, 6.35947149e-14, 2.44893915e-12]], - [[-6.72309334e-14, -3.56791270e-13, -4.13553842e-14, 3.81212108e-13], - [-3.55860413e-13, -1.22880634e-13, -3.19443665e-14, - -4.71232601e-14], - [-2.82289531e-13, -1.20965929e-13, 1.14160715e-13, -6.85113982e-14], - [5.17465531e-14, -4.61129211e-13, 5.33793701e-13, 1.28285338e-12]]], - [[[1.71894904e-12, -1.35675428e-12, 1.48328005e-13, 3.22454170e-12], - [-2.12666583e-13, -1.17551681e-13, -6.93968059e-13, 1.76659826e-12], - [-2.67906914e-12, -3.78250861e-13, -9.88730956e-13, 2.88200442e-12], - [1.48225123e-12, 2.15004833e-13, -4.84554577e-12, 2.77242999e-12]], - [[-3.09626209e-13, -2.52138997e-13, 4.58311589e-14, 2.03206766e-12], - [-3.95662347e-13, -2.99828956e-13, 1.08715446e-14, 1.06096030e-12], - [-2.46502471e-13, -2.43524217e-13, -3.81250581e-13, - -1.70270366e-13], - [8.12479206e-13, -1.38629628e-13, -8.05591138e-13, - -7.80286006e-13]], - [[-2.19626566e-13, -1.52852503e-13, 4.07706963e-13, 1.52323163e-12], - [-2.56235985e-13, -1.20817691e-13, 6.51260820e-15, 3.49591511e-13], - [-2.44063890e-13, -1.21871642e-13, -9.09798480e-14, - -1.59903476e-13], - [-2.47929201e-13, -1.77269110e-13, -1.12991330e-13, - -6.06795348e-13]]], - [[[-6.48288201e-13, -1.96951432e-12, -5.53508048e-13, 1.94507133e-12], - [-2.00769011e-12, -3.72469047e-13, -4.59116219e-13, 1.11322705e-13], - [-3.83507643e-12, 1.18054543e-13, -4.24001455e-13, -5.88688871e-13], - [-1.84528711e-12, 1.54974343e-12, -7.36123184e-13, 1.06256777e-13]], - [[-4.58487019e-13, -1.89124158e-13, 2.58416604e-13, 8.14652306e-13], - [-6.09664269e-13, -3.51509413e-13, 2.39576397e-13, 5.80539044e-13], - [-1.68850738e-13, -3.49553817e-13, -2.26470205e-13, 7.79989044e-13], - [2.23081718e-13, 1.20195366e-13, -1.01508013e-12, -2.15527487e-13]], - [[-1.68054338e-13, -5.06878852e-14, 2.77697698e-13, 8.37521961e-13], - [-1.39462599e-13, -1.36628363e-13, 3.13920124e-14, 4.55413406e-13], - [-1.06658890e-13, -2.19817426e-13, -8.35968065e-14, 1.88190788e-13], - [-2.27182863e-13, -2.74607819e-13, -1.10587309e-13, - -3.88915866e-13]]]]) * units('m^2 kg^-1 s^-1') + q1_truth = np.array([[[[-9.02684270e-13, 2.04906965e-13, 2.90366741e-12, 2.19304520e-12], + [4.39259469e-13, 1.21664810e-13, 1.52570637e-12, 3.84499568e-12], + [-1.20961682e-12, 2.28334568e-12, 3.48876764e-12, 3.04353683e-12], + [-1.52298016e-12, 8.05872598e-12, 7.74115167e-12, -2.23036948e-12]], + [[5.76052684e-13, 1.04797925e-12, -1.76250215e-13, 1.21374024e-12], + [2.96159390e-13, 5.82994320e-13, 6.26425486e-13, 7.35027599e-13], + [-4.05458639e-14, 3.26100111e-13, 1.40096964e-12, 2.83322883e-12], + [3.28501677e-13, 5.63278420e-13, 1.13853072e-12, 4.65264045e-12]], + [[2.25120252e-13, 5.80121595e-13, 1.62940948e-12, 5.46851323e-13], + [2.66540809e-13, 2.42144848e-13, 3.74380714e-13, 7.39064640e-13], + [6.59374356e-14, 2.00559760e-13, 5.22478916e-13, 1.70369853e-12], + [4.17124258e-14, 1.20339974e-13, 1.04017356e-12, 2.00285625e-12]]], + [[[-2.70235442e-13, 1.36656387e-12, 4.19692633e-12, 1.51457512e-12], + [4.64050107e-14, 1.26573416e-13, 2.16942269e-12, 4.72745728e-12], + [-1.25821951e-12, 9.58231778e-13, 1.49027307e-12, 2.13360636e-12], + [-2.94458687e-12, 6.08808030e-12, 4.22668460e-12, -2.13178006e-12]], + [[4.25758843e-13, 1.40346565e-13, 5.92764154e-13, 2.56329392e-12], + [5.75744868e-13, 5.63479482e-13, 7.68528359e-13, 9.54169673e-13], + [2.17989465e-14, 3.98672857e-13, 1.09630992e-12, 1.91458466e-12], + [1.45472393e-13, 1.80092943e-13, 1.03379050e-12, 3.63517612e-12]], + [[5.35452418e-13, 5.16110645e-13, 1.16167143e-12, 1.05362388e-12], + [2.78553810e-13, 2.34739333e-13, 4.61792157e-13, 5.36758839e-13], + [1.47975123e-13, 1.96526691e-13, 3.47222939e-13, 7.50706946e-13], + [-2.14929942e-14, 1.75848584e-13, 4.87164306e-13, 1.11205431e-12]]], + [[[6.44882146e-13, 1.89203548e-12, 5.23849406e-12, 2.08882463e-12], + [1.31350246e-12, 4.79492775e-13, 2.16235780e-12, 3.08756991e-12], + [-2.30468810e-13, 2.50903749e-13, 8.88048363e-14, 2.47021777e-12], + [-7.28610348e-12, 8.50128944e-13, -2.07551923e-12, + -4.41869472e-12]], + [[6.06909454e-13, -6.74302573e-13, 9.15261604e-13, 5.85674558e-13], + [9.58472982e-13, 6.14413858e-13, 6.99238896e-13, 8.71127400e-13], + [7.89621985e-14, 5.29651074e-13, 7.48742263e-13, 1.34494118e-12], + [2.23181345e-13, -1.99880485e-13, 8.59667557e-13, 1.60364542e-12]], + [[4.49485378e-13, 2.19403888e-13, 3.32682107e-13, -4.06788877e-14], + [1.71334107e-13, 2.19234639e-13, 3.80362792e-13, 5.06206489e-13], + [2.60485983e-13, 2.66689509e-13, 2.05507268e-13, 6.05236747e-13], + [1.69953806e-13, 2.92743451e-13, -1.21101740e-14, + 4.45255696e-13]]]]) * units('m^2 kg^-1 s^-1') + q2_truth = np.array([[[[3.34398414e-12, -1.32578962e-13, 1.01530245e-12, 6.03460412e-12], + [2.51655551e-13, -1.71080181e-13, -8.25450865e-13, 1.68941987e-13], + [-3.50610571e-12, -1.68900418e-12, 7.74142051e-13, 1.53842636e-12], + [-1.75486540e-12, -3.86629371e-12, -1.89184780e-12, + -5.15338594e-12]], + [[-2.09878631e-13, -6.26922694e-13, -1.40170277e-13, 1.09139148e-12], + [-2.58443408e-13, -2.67657189e-13, -6.44319215e-14, 5.90804763e-13], + [-2.73875193e-13, -2.28517322e-13, -4.76883863e-13, + -8.48746443e-13], + [1.21044640e-12, -5.10676858e-13, 6.32812733e-14, 2.44933519e-12]], + [[-6.72809694e-14, -3.57593424e-13, -4.18326571e-14, 3.81509257e-13], + [-3.56312152e-13, -1.23269564e-13, -3.21698576e-14, + -4.69401174e-14], + [-2.82461704e-13, -1.21007762e-13, 1.13823760e-13, -6.93485412e-14], + [5.19806694e-14, -4.61314808e-13, 5.33326094e-13, 1.28209513e-12]]], + [[[1.72127539e-12, -1.35818611e-12, 1.48111017e-13, 3.22882115e-12], + [-2.13631818e-13, -1.17550571e-13, -6.94644658e-13, 1.76893456e-12], + [-2.67966931e-12, -3.78148042e-13, -9.90360068e-13, 2.87997878e-12], + [1.48322304e-12, 2.15101840e-13, -4.84581616e-12, 2.77231259e-12]], + [[-3.09742331e-13, -2.52155554e-13, 4.57591777e-14, 2.03457093e-12], + [-3.95777463e-13, -3.00202455e-13, 1.05082591e-14, 1.06140347e-12], + [-2.46969363e-13, -2.43836368e-13, -3.81515859e-13, + -1.70412444e-13], + [8.12844940e-13, -1.38633850e-13, -8.06173908e-13, + -7.80955396e-13]], + [[-2.19923258e-13, -1.53282385e-13, 4.07809333e-13, 1.52462097e-12], + [-2.56567476e-13, -1.21124223e-13, 6.28491470e-15, 3.49835200e-13], + [-2.44172367e-13, -1.22026447e-13, -9.12989545e-14, + -1.60305594e-13], + [-2.47776092e-13, -1.77361553e-13, -1.13326400e-13, + -6.07726254e-13]]], + [[[-6.49332840e-13, -1.97186697e-12, -5.54805109e-13, 1.94760968e-12], + [-2.00917113e-12, -3.72825112e-13, -4.59780632e-13, 1.12445112e-13], + [-3.83584827e-12, 1.18455212e-13, -4.24969207e-13, -5.88484873e-13], + [-1.84313287e-12, 1.55136757e-12, -7.38157445e-13, 1.03689734e-13]], + [[-4.58924792e-13, -1.88627007e-13, 2.58408535e-13, 8.15237426e-13], + [-6.09787117e-13, -3.51901418e-13, 2.39399294e-13, 5.80646992e-13], + [-1.69168847e-13, -3.49955041e-13, -2.26671298e-13, 7.79694360e-13], + [2.23293556e-13, 1.20382150e-13, -1.01583327e-12, -2.16626822e-13]], + [[-1.68178414e-13, -5.08196191e-14, 2.77786052e-13, 8.38012650e-13], + [-1.39619960e-13, -1.36786251e-13, 3.12305194e-14, 4.55426142e-13], + [-1.06649917e-13, -2.19937033e-13, -8.38223242e-14, 1.87904895e-13], + [-2.27100932e-13, -2.74536001e-13, -1.10779552e-13, + -3.90314768e-13]]]]) * units('m^2 kg^-1 s^-1') assert_array_almost_equal(q1.data, q1_truth, 18) assert_array_almost_equal(q2.data, q2_truth, 18) From 3686ea70865ab646b0b6f556a4542a4255127b4a Mon Sep 17 00:00:00 2001 From: Ryan May Date: Mon, 5 Oct 2020 23:58:12 -0600 Subject: [PATCH 8/9] MNT: Don't use deprecated functions in GitHub Actions Apparently these have been changed for security reasons: https://github.blog/changelog/2020-10-01-github-actions-deprecating-set-env-and-add-path-commands/ --- .github/workflows/docs.yml | 4 ++-- .github/workflows/linting.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 043cd2e1963..c6423ac75a2 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -98,7 +98,7 @@ jobs: - name: Enable linkchecker for PRs if: ${{ github.event_name == 'pull_request' && matrix.check-links == true }} - run: echo "::set-env name=LINKCHECKER::linkcheck" + run: echo "LINKCHECKER=linkcheck" >> $GITHUB_ENV - name: Build docs run: | @@ -120,7 +120,7 @@ jobs: # branch that's not master (which is confined to n.nn.x above) or on a tag. - name: Set doc version if: ${{ github.event_name != 'push' || !contains(github.ref, 'master') }} - run: echo "::set-env name=DOC_VERSION::v$(python -c 'import metpy; print(metpy.__version__.rsplit(".", maxsplit=2)[0])')" + run: echo "DOC_VERSION=v$(python -c 'import metpy; print(metpy.__version__.rsplit(".", maxsplit=2)[0])')" >> $GITHUB_ENV - name: Upload to GitHub Pages if: ${{ github.event_name != 'pull_request' && matrix.experimental == false }} diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index aea49d47d98..a69ae81dfbc 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -25,7 +25,7 @@ jobs: curl -sfL \ https://github.com/reviewdog/reviewdog/raw/master/install.sh | \ sh -s -- -b $HOME/bin - echo ::add-path::$HOME/bin + echo "$HOME/bin" >> $GITHUB_PATH - name: Run flake8 env: From ee2117db996534d066baf4d549bcd94eff626a3b Mon Sep 17 00:00:00 2001 From: Ryan May Date: Tue, 6 Oct 2020 23:26:07 -0600 Subject: [PATCH 9/9] TST: Add some tests for warnings in xarray handling --- src/metpy/xarray.py | 15 ++++++--------- tests/test_xarray.py | 24 ++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/src/metpy/xarray.py b/src/metpy/xarray.py index 644d65a3d64..960ba3c5ea3 100644 --- a/src/metpy/xarray.py +++ b/src/metpy/xarray.py @@ -688,15 +688,12 @@ def parse_cf(self, varname=None, coordinates=None): 'Attempting to parse metpy_crs as a data variable. Unexpected merge conflicts ' 'may occur.' ) - elif 'metpy_crs' in var.coords: - try: - assert isinstance(var.coords['metpy_crs'].item(), CFProjection) - except (ValueError, AssertionError): - # Catch non-scalar and non-CFProjection coordinates - warnings.warn( - 'metpy_crs already present as a non-CFProjection coordinate. Unexpected ' - 'merge conflicts may occur.' - ) + elif 'metpy_crs' in var.coords and (var.coords['metpy_crs'].size > 1 or not isinstance( + var.coords['metpy_crs'].item(), CFProjection)): + warnings.warn( + 'metpy_crs already present as a non-CFProjection coordinate. Unexpected ' + 'merge conflicts may occur.' + ) # Assign coordinates if the coordinates argument is given if coordinates is not None: diff --git a/tests/test_xarray.py b/tests/test_xarray.py index 162a80268ed..0ef261b2b31 100644 --- a/tests/test_xarray.py +++ b/tests/test_xarray.py @@ -248,6 +248,30 @@ def test_missing_grid_mapping_var(caplog): assert 'Could not find' in caplog.text +def test_parsecf_crs(): + """Test calling `parse_cf` with the metpy_crs variable.""" + ds = xr.Dataset({'metpy_crs': xr.DataArray(1)}) + + with pytest.warns(UserWarning, match='Attempting to parse metpy_crs'): + ds.metpy.parse_cf('metpy_crs') + + +def test_parsecf_existing_scalar_crs(): + """Test calling `parse_cf` on a variable with an existing scalar metpy_crs coordinate.""" + ds = xr.Dataset({'data': xr.DataArray(1, coords=dict(metpy_crs=1))}) + + with pytest.warns(UserWarning, match='metpy_crs already present'): + ds.metpy.parse_cf('data') + + +def test_parsecf_existing_vector_crs(): + """Test calling `parse_cf` on a variable with an existing vector metpy_crs coordinate.""" + ds = xr.Dataset({'data': xr.DataArray(1, dims=('metpy_crs',), coords=(np.ones(3),))}) + + with pytest.warns(UserWarning, match='metpy_crs already present'): + ds.metpy.parse_cf('data') + + def test_preprocess_and_wrap_only_preprocessing(): """Test xarray preprocessing and wrapping decorator for only preprocessing.""" data = xr.DataArray(np.ones(3), attrs={'units': 'km'})