def test_simple_missing_data(self): """ Check for missing data handling. Should mask cells that either .. (a) go partly outside the source grid (b) partially overlap masked source data """ c1, c2 = self.stock_c1_c2 # regrid from c2 to c1 -- should mask all the edges... c2_to_c1 = regrid_conservative_via_esmpy(c2, c1) self.assertArrayEqual( c2_to_c1.data.mask, [[True, True, True, True, True], [True, False, False, False, True], [True, False, False, False, True], [True, False, False, False, True], [True, True, True, True, True] ]) # do same with a particular point masked c2m = c2.copy() c2m.data = np.ma.array(c2m.data) c2m.data[1, 1] = np.ma.masked c2m_to_c1 = regrid_conservative_via_esmpy(c2m, c1) self.assertArrayEqual( c2m_to_c1.data.mask, [[True, True, True, True, True], [True, True, True, False, True], [True, True, True, False, True], [ True, False, False, False, True ], [True, True, True, True, True]])
def test_simple_missing_data(self): """ Check for missing data handling. Should mask cells that either .. (a) go partly outside the source grid (b) partially overlap masked source data """ c1, c2 = self.stock_c1_c2 # regrid from c2 to c1 -- should mask all the edges... c2_to_c1 = regrid_conservative_via_esmpy(c2, c1) self.assertArrayEqual(c2_to_c1.data.mask, [[True, True, True, True, True], [True, False, False, False, True], [True, False, False, False, True], [True, False, False, False, True], [True, True, True, True, True]]) # do same with a particular point masked c2m = c2.copy() c2m.data = np.ma.array(c2m.data) c2m.data[1, 1] = np.ma.masked c2m_to_c1 = regrid_conservative_via_esmpy(c2m, c1) self.assertArrayEqual(c2m_to_c1.data.mask, [[True, True, True, True, True], [True, True, True, False, True], [True, True, True, False, True], [True, False, False, False, True], [True, True, True, True, True]])
def test_polar_areas(self): """ Test area-conserving regrid between different grids. Grids have overlapping areas in the same (lat-lon) coordinate system. Cells are highly non-square (near the pole). """ # Like test_basic_area, but not symmetrical + bigger overall errors. shape1 = (5, 5) xlims1, ylims1 = ((-2, 2), (84, 88)) c1 = _make_test_cube(shape1, xlims1, ylims1) c1.data[:] = 0.0 c1.data[2, 2] = 1.0 c1_areasum = _cube_area_sum(c1) shape2 = (4, 4) xlims2, ylims2 = ((-1.5, 1.5), (84.5, 87.5)) c2 = _make_test_cube(shape2, xlims2, ylims2) c2.data[:] = 0.0 c1to2 = regrid_conservative_via_esmpy(c1, c2) # check for expected pattern d_expect = np.array( [ [0.0, 0.0, 0.0, 0.0], [0.0, 0.23614, 0.23614, 0.0], [0.0, 0.26784, 0.26784, 0.0], [0.0, 0.0, 0.0, 0.0], ] ) self.assertArrayAllClose(c1to2.data, d_expect, rtol=5.0e-5) # check sums c1to2_areasum = _cube_area_sum(c1to2) self.assertArrayAllClose(c1to2_areasum, c1_areasum) # # transform back again ... # c1to2to1 = regrid_conservative_via_esmpy(c1to2, c1) # check values d_expect = np.array( [ [0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.056091, 0.112181, 0.056091, 0.0], [0.0, 0.125499, 0.250998, 0.125499, 0.0], [0.0, 0.072534, 0.145067, 0.072534, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0], ] ) self.assertArrayAllClose(c1to2to1.data, d_expect, atol=0.0005) # check sums c1to2to1_areasum = _cube_area_sum(c1to2to1) self.assertArrayAllClose(c1to2to1_areasum, c1_areasum)
def test_multidimensional(self): """ Check valid operation on a multidimensional cube. Calculation should repeat across multiple dimensions. Any attached orography is interpolated. NOTE: in future, extra dimensions may be passed through to ESMF: At present, it repeats the calculation on 2d slices. So we check that at least the results are equivalent (as it's quite easy to do). """ # Get some higher-dimensional test data c1 = istk.realistic_4d() # Chop down to small size, and mask some data c1 = c1[:3, :4, :16, :12] c1.data[:, 2, :, :] = np.ma.masked c1.data[1, 1, 3:9, 4:7] = np.ma.masked # Give it a slightly more challenging indexing order: tzyx --> xzty c1.transpose((3, 1, 0, 2)) # Construct a (coarser) target grid of about the same extent c1_cs = c1.coord(axis="x").coord_system xlims = _minmax(c1.coord(axis="x").contiguous_bounds()) ylims = _minmax(c1.coord(axis="y").contiguous_bounds()) # Reduce the dimensions slightly to avoid NaNs in regridded orography delta = 0.05 # || NOTE: this is *not* a small amount. Think there is a bug. # || NOTE: See https://github.com/SciTools/iris/issues/458 xlims = np.interp([delta, 1.0 - delta], [0, 1], xlims) ylims = np.interp([delta, 1.0 - delta], [0, 1], ylims) pole_latlon = ( c1_cs.grid_north_pole_latitude, c1_cs.grid_north_pole_longitude, ) c2 = _make_test_cube((7, 8), xlims, ylims, pole_latlon=pole_latlon) # regrid onto new grid c1_to_c2 = regrid_conservative_via_esmpy(c1, c2) # check that all the original coords exist in the new cube # NOTE: this also effectively confirms we haven't lost the orography def list_coord_names(cube): return sorted([coord.name() for coord in cube.coords()]) self.assertEqual(list_coord_names(c1_to_c2), list_coord_names(c1)) # check that each xy 'slice' has same values as if done on its own. for i_p, i_t in np.ndindex(c1.shape[1:3]): c1_slice = c1[:, i_p, i_t] c2_slice = regrid_conservative_via_esmpy(c1_slice, c2) subcube = c1_to_c2[:, i_p, i_t] self.assertEqual(subcube, c2_slice) # check all other metadata self.assertEqual(c1_to_c2.metadata, c1.metadata)
def test_longitude_wraps(self): """Check results are independent of where the grid 'seams' are.""" # First repeat global regrid calculation from 'test_global'. shape1 = (8, 6) xlim1 = 180.0 * (shape1[0] - 1) / shape1[0] ylim1 = 90.0 * (shape1[1] - 1) / shape1[1] xlims1 = (-xlim1, xlim1) ylims1 = (-ylim1, ylim1) c1 = _make_test_cube(shape1, xlims1, ylims1) # Create a small, plausible global array (see test_global). basedata = np.array([ [1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 4, 4, 4, 2, 2, 1], [2, 1, 4, 4, 4, 2, 2, 2], [2, 5, 5, 1, 1, 1, 5, 5], [5, 5, 5, 1, 1, 1, 5, 5], [5, 5, 5, 5, 5, 5, 5, 5], ]) c1.data[:] = basedata shape2 = (14, 11) xlim2 = 180.0 * (shape2[0] - 1) / shape2[0] ylim2 = 90.0 * (shape2[1] - 1) / shape2[1] xlims_2 = (-xlim2, xlim2) ylims_2 = (-ylim2, ylim2) c2 = _make_test_cube(shape2, xlims_2, ylims_2, pole_latlon=(47.4, 25.7)) # Perform regridding c1toc2 = regrid_conservative_via_esmpy(c1, c2) # Now redo with dst longitudes rotated, so 'seam' is somewhere else. x2_shift_steps = shape2[0] // 3 xlims2_shifted = np.array(xlims_2) + 360.0 * x2_shift_steps / shape2[0] c2_shifted = _make_test_cube(shape2, xlims2_shifted, ylims_2, pole_latlon=(47.4, 25.7)) c1toc2_shifted = regrid_conservative_via_esmpy(c1, c2_shifted) # Show that results are the same, when output rolled by same amount rolled_data = np.roll(c1toc2_shifted.data, x2_shift_steps, axis=1) self.assertArrayAllClose(rolled_data, c1toc2.data) # Repeat with rolled *source* data : result should be identical x1_shift_steps = shape1[0] // 3 x_shift_degrees = 360.0 * x1_shift_steps / shape1[0] xlims1_shifted = [x - x_shift_degrees for x in xlims1] c1_shifted = _make_test_cube(shape1, xlims1_shifted, ylims1) c1_shifted.data[:] = np.roll(basedata, x1_shift_steps, axis=1) c1shifted_toc2 = regrid_conservative_via_esmpy(c1_shifted, c2) self.assertEqual(c1shifted_toc2, c1toc2)
def test_multidimensional(self): """ Check valid operation on a multidimensional cube. Calculation should repeat across multiple dimensions. Any attached orography is interpolated. NOTE: in future, extra dimensions may be passed through to ESMF: At present, it repeats the calculation on 2d slices. So we check that at least the results are equivalent (as it's quite easy to do). """ # Get some higher-dimensional test data c1 = istk.realistic_4d() # Chop down to small size, and mask some data c1 = c1[:3, :4, :16, :12] c1.data[:, 2, :, :] = np.ma.masked c1.data[1, 1, 3:9, 4:7] = np.ma.masked # Give it a slightly more challenging indexing order: tzyx --> xzty c1.transpose((3, 1, 0, 2)) # Construct a (coarser) target grid of about the same extent c1_cs = c1.coord(axis='x').coord_system xlims = _minmax(c1.coord(axis='x').contiguous_bounds()) ylims = _minmax(c1.coord(axis='y').contiguous_bounds()) # Reduce the dimensions slightly to avoid NaNs in regridded orography delta = 0.05 # NOTE: this is *not* a small amount. Think there is a bug. # NOTE: See https://github.com/SciTools/iris/issues/458 xlims = np.interp([delta, 1.0 - delta], [0, 1], xlims) ylims = np.interp([delta, 1.0 - delta], [0, 1], ylims) pole_latlon = (c1_cs.grid_north_pole_latitude, c1_cs.grid_north_pole_longitude) c2 = _make_test_cube((7, 8), xlims, ylims, pole_latlon=pole_latlon) # regrid onto new grid c1_to_c2 = regrid_conservative_via_esmpy(c1, c2) # check that all the original coords exist in the new cube # NOTE: this also effectively confirms we haven't lost the orography def list_coord_names(cube): return sorted([coord.name() for coord in cube.coords()]) self.assertEqual(list_coord_names(c1_to_c2), list_coord_names(c1)) # check that each xy 'slice' has same values as if done on its own. for i_p, i_t in np.ndindex(c1.shape[1:3]): c1_slice = c1[:, i_p, i_t] c2_slice = regrid_conservative_via_esmpy(c1_slice, c2) subcube = c1_to_c2[:, i_p, i_t] self.assertEqual(subcube, c2_slice) # check all other metadata self.assertEqual(c1_to_c2.metadata, c1.metadata)
def test_simple_areas(self): """ Test area-conserving regrid between simple "near-square" grids. Grids have overlapping areas in the same (lat-lon) coordinate system. Grids are "nearly flat" lat-lon spaces (small ranges near the equator). """ c1, c2 = self.stock_c1_c2 c1_areasum = self.stock_c1_areasum # main regrid c1to2 = regrid_conservative_via_esmpy(c1, c2) c1to2_areasum = _cube_area_sum(c1to2) # Check expected result (Cartesian equivalent, so not exact). d_expect = np.array( [ [0.00, 0.00, 0.00, 0.00], [0.00, 0.25, 0.25, 0.00], [0.00, 0.25, 0.25, 0.00], [0.00, 0.00, 0.00, 0.00], ] ) # Numbers are slightly off (~0.25000952). This is expected. self.assertArrayAllClose(c1to2.data, d_expect, rtol=5.0e-5) # check that the area sums are equivalent, simple total is a bit off self.assertArrayAllClose(c1to2_areasum, c1_areasum) # # regrid back onto original grid again ... # c1to2to1 = regrid_conservative_via_esmpy(c1to2, c1) c1to2to1_areasum = _cube_area_sum(c1to2to1) # Check expected result (Cartesian/exact difference now greater) d_expect = np.array( [ [0.0, 0.0000, 0.0000, 0.0000, 0.0], [0.0, 0.0625, 0.1250, 0.0625, 0.0], [0.0, 0.1250, 0.2500, 0.1250, 0.0], [0.0, 0.0625, 0.1250, 0.0625, 0.0], [0.0, 0.0000, 0.0000, 0.0000, 0.0], ] ) self.assertArrayAllClose(c1to2to1.data, d_expect, atol=0.00002) # check area sums again self.assertArrayAllClose(c1to2to1_areasum, c1_areasum)
def test_polar_areas(self): """ Test area-conserving regrid between different grids. Grids have overlapping areas in the same (lat-lon) coordinate system. Cells are highly non-square (near the pole). """ # Like test_basic_area, but not symmetrical + bigger overall errors. shape1 = (5, 5) xlims1, ylims1 = ((-2, 2), (84, 88)) c1 = _make_test_cube(shape1, xlims1, ylims1) c1.data[:] = 0.0 c1.data[2, 2] = 1.0 c1_areasum = _cube_area_sum(c1) shape2 = (4, 4) xlims2, ylims2 = ((-1.5, 1.5), (84.5, 87.5)) c2 = _make_test_cube(shape2, xlims2, ylims2) c2.data[:] = 0.0 c1to2 = regrid_conservative_via_esmpy(c1, c2) # check for expected pattern d_expect = np.array([[0.0, 0.0, 0.0, 0.0], [0.0, 0.23614, 0.23614, 0.0], [0.0, 0.26784, 0.26784, 0.0], [0.0, 0.0, 0.0, 0.0]]) self.assertArrayAllClose(c1to2.data, d_expect, rtol=5.0e-5) # check sums c1to2_areasum = _cube_area_sum(c1to2) self.assertArrayAllClose(c1to2_areasum, c1_areasum) # # transform back again ... # c1to2to1 = regrid_conservative_via_esmpy(c1to2, c1) # check values d_expect = np.array([[0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.056091, 0.112181, 0.056091, 0.0], [0.0, 0.125499, 0.250998, 0.125499, 0.0], [0.0, 0.072534, 0.145067, 0.072534, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0]]) self.assertArrayAllClose(c1to2to1.data, d_expect, atol=0.0005) # check sums c1to2to1_areasum = _cube_area_sum(c1to2to1) self.assertArrayAllClose(c1to2to1_areasum, c1_areasum)
def test_longitude_wraps(self): """Check results are independent of where the grid 'seams' are.""" # First repeat global regrid calculation from 'test_global'. shape1 = (8, 6) xlim1 = 180.0 * (shape1[0] - 1) / shape1[0] ylim1 = 90.0 * (shape1[1] - 1) / shape1[1] xlims1 = (-xlim1, xlim1) ylims1 = (-ylim1, ylim1) c1 = _make_test_cube(shape1, xlims1, ylims1) # Create a small, plausible global array (see test_global). basedata = np.array( [[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 4, 4, 4, 2, 2, 1], [2, 1, 4, 4, 4, 2, 2, 2], [2, 5, 5, 1, 1, 1, 5, 5], [5, 5, 5, 1, 1, 1, 5, 5], [5, 5, 5, 5, 5, 5, 5, 5]]) c1.data[:] = basedata shape2 = (14, 11) xlim2 = 180.0 * (shape2[0] - 1) / shape2[0] ylim2 = 90.0 * (shape2[1] - 1) / shape2[1] xlims_2 = (-xlim2, xlim2) ylims_2 = (-ylim2, ylim2) c2 = _make_test_cube(shape2, xlims_2, ylims_2, pole_latlon=(47.4, 25.7)) # Perform regridding c1toc2 = regrid_conservative_via_esmpy(c1, c2) # Now redo with dst longitudes rotated, so 'seam' is somewhere else. x2_shift_steps = int(shape2[0] / 3) xlims2_shifted = np.array(xlims_2) + 360.0 * x2_shift_steps / shape2[0] c2_shifted = _make_test_cube(shape2, xlims2_shifted, ylims_2, pole_latlon=(47.4, 25.7)) c1toc2_shifted = regrid_conservative_via_esmpy(c1, c2_shifted) # Show that results are the same, when output rolled by same amount rolled_data = np.roll(c1toc2_shifted.data, x2_shift_steps, axis=1) self.assertArrayAllClose(rolled_data, c1toc2.data) # Repeat with rolled *source* data : result should be identical x1_shift_steps = int(shape1[0] / 3) x_shift_degrees = 360.0 * x1_shift_steps / shape1[0] xlims1_shifted = [x - x_shift_degrees for x in xlims1] c1_shifted = _make_test_cube(shape1, xlims1_shifted, ylims1) c1_shifted.data[:] = np.roll(basedata, x1_shift_steps, axis=1) c1shifted_toc2 = regrid_conservative_via_esmpy(c1_shifted, c2) self.assertEqual(c1shifted_toc2, c1toc2)
def regrid(hires_cube, regridding, rotate=False): lowres_cube = None startdt = datetime.datetime.now() if regridding == 'iris.regrid.linear': lowres_cube = hires_cube.regrid(lowres_grid, iris.analysis.Linear()) elif regridding == 'iris.regrid.linear.nan': lowres_cube = hires_cube.regrid(lowres_grid, iris.analysis.Linear('nan')) elif regridding == 'iris.regrid.AreaWeighted': if rotate: print 'Doesnt work with changing coordinate systems' else: lowres_cube = hires_cube.regrid(lowres_grid, iris.analysis.AreaWeighted()) elif regridding == 'iris.experimental.area_weighted': if rotate: print 'Doesnt work with changing coordinate systems' else: lowres_cube = iris.experimental.regrid.regrid_area_weighted_rectilinear_src_and_grid( hires_cube, lowres_grid) elif regridding == 'iris.esmpy': lowres_cube = regrid_conservative_via_esmpy(hires_cube, lowres_grid) elif regridding == 'tidl.pp_regrid': lowres_cube = get_cube_from_file('lowres_ppregrid.pp') elif regridding == 'tidl.pp_regrid_avg': lowres_cube = get_cube_from_file('lowres_ppregridavg.pp') enddt = datetime.datetime.now() deltadt = (enddt - startdt).total_seconds() return lowres_cube, deltadt
def test_global_collapse(self): # Test regridding global data to a single cell. # Fetch 'standard' testcube data c1, _ = self.stock_c1_c2 c1_areasum = self.stock_c1_areasum # Condense entire globe onto a single cell x_coord_2 = iris.coords.DimCoord([0.0], bounds=[-180.0, 180.0], standard_name='longitude', units='degrees', coord_system=_PLAIN_GEODETIC_CS) y_coord_2 = iris.coords.DimCoord([0.0], bounds=[-90.0, 90.0], standard_name='latitude', units='degrees', coord_system=_PLAIN_GEODETIC_CS) c2 = iris.cube.Cube([[0.0]]) c2.add_dim_coord(y_coord_2, 0) c2.add_dim_coord(x_coord_2, 1) # NOTE: at present, this causes an error inside ESMF ... context = self.assertRaises(NameError) global_cell_supported = False if global_cell_supported: context = _donothing_context_manager() with context: c1_to_global = regrid_conservative_via_esmpy(c1, c2) # Check the total area sum is still the same self.assertArrayAllClose(c1_to_global.data[0, 0], c1_areasum)
def test_fail_no_cs(self): # Test error when one coordinate has no coord_system. shape1 = (5, 5) xlims1, ylims1 = ((-2, 2), (-2, 2)) c1 = _make_test_cube(shape1, xlims1, ylims1) c1.data[:] = 0.0 c1.data[2, 2] = 1.0 shape2 = (4, 4) xlims2, ylims2 = ((-1.5, 1.5), (-1.5, 1.5)) c2 = _make_test_cube(shape2, xlims2, ylims2) c2.data[:] = 0.0 c2.coord('latitude').coord_system = None with self.assertRaises(ValueError): regrid_conservative_via_esmpy(c1, c2)
def test_global(self): # Test global regridding. # Compute basic test data cubes. shape1 = (8, 6) xlim1 = 180.0 * (shape1[0] - 1) / shape1[0] ylim1 = 90.0 * (shape1[1] - 1) / shape1[1] c1 = _make_test_cube(shape1, (-xlim1, xlim1), (-ylim1, ylim1)) # Create a small, plausible global array: # - top + bottom rows all the same # - left + right columns "mostly close" for checking across the seam basedata = np.array([[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 4, 4, 4, 2, 2, 1], [2, 1, 4, 4, 4, 2, 2, 2], [2, 5, 5, 1, 1, 1, 5, 5], [5, 5, 5, 1, 1, 1, 5, 5], [5, 5, 5, 5, 5, 5, 5, 5]]) c1.data[:] = basedata # Create a rotated grid to regrid this onto. shape2 = (14, 11) xlim2 = 180.0 * (shape2[0] - 1) / shape2[0] ylim2 = 90.0 * (shape2[1] - 1) / shape2[1] c2 = _make_test_cube(shape2, (-xlim2, xlim2), (-ylim2, ylim2), pole_latlon=(47.4, 25.7)) # Perform regridding c1toc2 = regrid_conservative_via_esmpy(c1, c2) # Check that before+after area-sums match fairly well c1_areasum = _cube_area_sum(c1) c1toc2_areasum = _cube_area_sum(c1toc2) self.assertArrayAllClose(c1toc2_areasum, c1_areasum, rtol=0.006)
def test_global(self): # Test global regridding. # Compute basic test data cubes. shape1 = (8, 6) xlim1 = 180.0 * (shape1[0] - 1) / shape1[0] ylim1 = 90.0 * (shape1[1] - 1) / shape1[1] c1 = _make_test_cube(shape1, (-xlim1, xlim1), (-ylim1, ylim1)) # Create a small, plausible global array: # - top + bottom rows all the same # - left + right columns "mostly close" for checking across the seam basedata = np.array( [[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 4, 4, 4, 2, 2, 1], [2, 1, 4, 4, 4, 2, 2, 2], [2, 5, 5, 1, 1, 1, 5, 5], [5, 5, 5, 1, 1, 1, 5, 5], [5, 5, 5, 5, 5, 5, 5, 5]]) c1.data[:] = basedata # Create a rotated grid to regrid this onto. shape2 = (14, 11) xlim2 = 180.0 * (shape2[0] - 1) / shape2[0] ylim2 = 90.0 * (shape2[1] - 1) / shape2[1] c2 = _make_test_cube(shape2, (-xlim2, xlim2), (-ylim2, ylim2), pole_latlon=(47.4, 25.7)) # Perform regridding c1toc2 = regrid_conservative_via_esmpy(c1, c2) # Check that before+after area-sums match fairly well c1_areasum = _cube_area_sum(c1) c1toc2_areasum = _cube_area_sum(c1toc2) self.assertArrayAllClose(c1toc2_areasum, c1_areasum, rtol=0.006)
def test_xy_transposed(self): # Test effects of transposing X and Y in src/dst data. c1, c2 = self.stock_c1_c2 testcube_xy = self.stock_regrid_c1toc2 # Check that transposed data produces transposed results # - i.e. regrid(data^T)^T == regrid(data) c1_yx = c1.copy() c1_yx.transpose() testcube_yx = regrid_conservative_via_esmpy(c1_yx, c2) testcube_yx.transpose() self.assertEqual(testcube_yx, testcube_xy) # Check that transposing destination does nothing c2_yx = c2.copy() c2_yx.transpose() testcube_dst_transpose = regrid_conservative_via_esmpy(c1, c2_yx) self.assertEqual(testcube_dst_transpose, testcube_xy)
def test_simple_areas(self): """ Test area-conserving regrid between simple "near-square" grids. Grids have overlapping areas in the same (lat-lon) coordinate system. Grids are "nearly flat" lat-lon spaces (small ranges near the equator). """ c1, c2 = self.stock_c1_c2 c1_areasum = self.stock_c1_areasum # main regrid c1to2 = regrid_conservative_via_esmpy(c1, c2) c1to2_areasum = _cube_area_sum(c1to2) # Check expected result (Cartesian equivalent, so not exact). d_expect = np.array([[0.00, 0.00, 0.00, 0.00], [0.00, 0.25, 0.25, 0.00], [0.00, 0.25, 0.25, 0.00], [0.00, 0.00, 0.00, 0.00]]) # Numbers are slightly off (~0.25000952). This is expected. self.assertArrayAllClose(c1to2.data, d_expect, rtol=5.0e-5) # check that the area sums are equivalent, simple total is a bit off self.assertArrayAllClose(c1to2_areasum, c1_areasum) # # regrid back onto original grid again ... # c1to2to1 = regrid_conservative_via_esmpy(c1to2, c1) c1to2to1_areasum = _cube_area_sum(c1to2to1) # Check expected result (Cartesian/exact difference now greater) d_expect = np.array([[0.0, 0.0000, 0.0000, 0.0000, 0.0], [0.0, 0.0625, 0.1250, 0.0625, 0.0], [0.0, 0.1250, 0.2500, 0.1250, 0.0], [0.0, 0.0625, 0.1250, 0.0625, 0.0], [0.0, 0.0000, 0.0000, 0.0000, 0.0]]) self.assertArrayAllClose(c1to2to1.data, d_expect, atol=0.00002) # check area sums again self.assertArrayAllClose(c1to2to1_areasum, c1_areasum)
def test_fail_different_cs(self): # Test error when either src or dst coords have different # coord_systems. shape1 = (5, 5) xlims1, ylims1 = ((-2, 2), (-2, 2)) shape2 = (4, 4) xlims2, ylims2 = ((-1.5, 1.5), (-1.5, 1.5)) # Check basic regrid between these is ok. c1 = _make_test_cube(shape1, xlims1, ylims1, pole_latlon=(45.0, 35.0)) c2 = _make_test_cube(shape2, xlims2, ylims2) regrid_conservative_via_esmpy(c1, c2) # Replace the coord_system one of the source coords + check this fails. c1.coord('grid_longitude').coord_system = \ c2.coord('longitude').coord_system with self.assertRaises(ValueError): regrid_conservative_via_esmpy(c1, c2) # Repeat with target coordinate fiddled. c1 = _make_test_cube(shape1, xlims1, ylims1, pole_latlon=(45.0, 35.0)) c2 = _make_test_cube(shape2, xlims2, ylims2) c2.coord('latitude').coord_system = \ c1.coord('grid_latitude').coord_system with self.assertRaises(ValueError): regrid_conservative_via_esmpy(c1, c2)
def setUp(self): # Compute basic test data cubes. shape1 = (5, 5) xlims1, ylims1 = ((-2, 2), (-2, 2)) c1 = _make_test_cube(shape1, xlims1, ylims1) c1.data[:] = 0.0 c1.data[2, 2] = 1.0 shape2 = (4, 4) xlims2, ylims2 = ((-1.5, 1.5), (-1.5, 1.5)) c2 = _make_test_cube(shape2, xlims2, ylims2) c2.data[:] = 0.0 # Save timesaving pre-computed bits self.stock_c1_c2 = (c1, c2) self.stock_regrid_c1toc2 = regrid_conservative_via_esmpy(c1, c2) self.stock_c1_areasum = _cube_area_sum(c1)
def test_rotated(self): """ Test area-weighted regrid on more complex area. Use two mutually rotated grids, of similar area + same dims. Only a small central region in each is non-zero, which maps entirely inside the other region. So the area-sum totals should match exactly. """ # create source test cube on rotated form pole_lat = 53.4 pole_lon = -173.2 deg_swing = 35.3 pole_lon += deg_swing c1_nx = 9 + 6 c1_ny = 7 + 6 c1_xlims = -60.0, 60.0 c1_ylims = -45.0, 20.0 c1_xlims = [x - deg_swing for x in c1_xlims] c1 = _make_test_cube((c1_nx, c1_ny), c1_xlims, c1_ylims, pole_latlon=(pole_lat, pole_lon)) c1.data[3:-3, 3:-3] = np.array([ [100, 100, 100, 100, 100, 100, 100, 100, 100], [100, 100, 100, 100, 100, 100, 100, 100, 100], [100, 100, 199, 199, 199, 199, 100, 100, 100], [100, 100, 100, 100, 199, 199, 100, 100, 100], [100, 100, 100, 100, 199, 199, 199, 100, 100], [100, 100, 100, 100, 100, 100, 100, 100, 100], [100, 100, 100, 100, 100, 100, 100, 100, 100]], dtype=np.float) c1_areasum = _cube_area_sum(c1) # construct target cube to receive nx2 = 9 + 6 ny2 = 7 + 6 c2_xlims = -100.0, 120.0 c2_ylims = -20.0, 50.0 c2 = _make_test_cube((nx2, ny2), c2_xlims, c2_ylims) c2.data = np.ma.array(c2.data, mask=True) # perform regrid c1to2 = regrid_conservative_via_esmpy(c1, c2) # check we have zeros (or nearly) all around the edge.. c1toc2_zeros = np.ma.array(c1to2.data) c1toc2_zeros[c1toc2_zeros.mask] = 0.0 c1toc2_zeros = np.abs(c1toc2_zeros.mask) < 1.0e-6 self.assertArrayEqual(c1toc2_zeros[0, :], True) self.assertArrayEqual(c1toc2_zeros[-1, :], True) self.assertArrayEqual(c1toc2_zeros[:, 0], True) self.assertArrayEqual(c1toc2_zeros[:, -1], True) # check the area-sum operation c1to2_areasum = _cube_area_sum(c1to2) self.assertArrayAllClose(c1to2_areasum, c1_areasum, rtol=0.004) # # Now repeat, transforming backwards ... # c1.data = np.ma.array(c1.data, mask=True) c2.data[:] = 0.0 c2.data[5:-5, 5:-5] = np.array([ [199, 199, 199, 199, 100], [100, 100, 199, 199, 100], [100, 100, 199, 199, 199]], dtype=np.float) c2_areasum = _cube_area_sum(c2) c2toc1 = regrid_conservative_via_esmpy(c2, c1) # check we have zeros (or nearly) all around the edge.. c2toc1_zeros = np.ma.array(c2toc1.data) c2toc1_zeros[c2toc1_zeros.mask] = 0.0 c2toc1_zeros = np.abs(c2toc1_zeros.mask) < 1.0e-6 self.assertArrayEqual(c2toc1_zeros[0, :], True) self.assertArrayEqual(c2toc1_zeros[-1, :], True) self.assertArrayEqual(c2toc1_zeros[:, 0], True) self.assertArrayEqual(c2toc1_zeros[:, -1], True) # check the area-sum operation c2toc1_areasum = _cube_area_sum(c2toc1) self.assertArrayAllClose(c2toc1_areasum, c2_areasum, rtol=0.004)
def test_same_grid(self): # Test regridding onto the identical grid. # Use regrid with self as target. c1, _ = self.stock_c1_c2 testcube = regrid_conservative_via_esmpy(c1, c1) self.assertEqual(testcube, c1)
def test_single_cells(self): # Test handling of single-cell grids. # Fetch 'standard' testcube data c1, c2 = self.stock_c1_c2 c1_areasum = self.stock_c1_areasum # # At present NxN -> 1x1 "in-place" doesn't seem to work properly # - result cell has missing-data ? # # Condense entire region into a single cell in the c1 grid xlims1 = _minmax(c1.coord(axis='x').bounds) ylims1 = _minmax(c1.coord(axis='y').bounds) x_c1x1 = iris.coords.DimCoord(xlims1[0], bounds=xlims1, standard_name='longitude', units='degrees', coord_system=_PLAIN_GEODETIC_CS) y_c1x1 = iris.coords.DimCoord(ylims1[0], bounds=ylims1, standard_name='latitude', units='degrees', coord_system=_PLAIN_GEODETIC_CS) c1x1_gridcube = iris.cube.Cube([[0.0]]) c1x1_gridcube.add_dim_coord(y_c1x1, 0) c1x1_gridcube.add_dim_coord(x_c1x1, 1) c1x1 = regrid_conservative_via_esmpy(c1, c1x1_gridcube) c1x1_areasum = _cube_area_sum(c1x1) # Check the total area sum is still the same condense_to_1x1_supported = False # NOTE: currently disabled (ESMF gets this wrong) # NOTE ALSO: call hits numpy 1.7 bug in testing.assert_array_compare. if condense_to_1x1_supported: self.assertArrayAllClose(c1x1_areasum, c1_areasum) # Condense entire region onto a single cell covering the area of 'c2' xlims2 = _minmax(c2.coord(axis='x').bounds) ylims2 = _minmax(c2.coord(axis='y').bounds) x_c2x1 = iris.coords.DimCoord(xlims2[0], bounds=xlims2, standard_name='longitude', units=iris.unit.Unit('degrees'), coord_system=_PLAIN_GEODETIC_CS) y_c2x1 = iris.coords.DimCoord(ylims2[0], bounds=ylims2, standard_name='latitude', units=iris.unit.Unit('degrees'), coord_system=_PLAIN_GEODETIC_CS) c2x1_gridcube = iris.cube.Cube([[0.0]]) c2x1_gridcube.add_dim_coord(y_c2x1, 0) c2x1_gridcube.add_dim_coord(x_c2x1, 1) c1_to_c2x1 = regrid_conservative_via_esmpy(c1, c2x1_gridcube) # Check the total area sum is still the same c1_to_c2x1_areasum = _cube_area_sum(c1_to_c2x1) self.assertArrayAllClose(c1_to_c2x1_areasum, c1_areasum, 0.0004) # 1x1 -> NxN : regrid single cell to NxN grid # construct a single-cell approximation to 'c1' with the same area sum. # NOTE: can't use _make_cube (see docstring) c1x1 = c1.copy()[0:1, 0:1] xlims1 = _minmax(c1.coord(axis='x').bounds) ylims1 = _minmax(c1.coord(axis='y').bounds) c1x1.coord(axis='x').bounds = xlims1 c1x1.coord(axis='y').bounds = ylims1 # Assign data mean as single cell value : Maybe not exact, but "close" c1x1.data[0, 0] = np.mean(c1.data) # Regrid this back onto the original NxN grid c1x1_to_c1 = regrid_conservative_via_esmpy(c1x1, c1) c1x1_to_c1_areasum = _cube_area_sum(c1x1_to_c1) # Check that area sum is ~unchanged, as expected self.assertArrayAllClose(c1x1_to_c1_areasum, c1_areasum, 0.0004) # Check 1x1 -> 1x1 # NOTE: can *only* get any result with a fully overlapping cell, so # just regrid onto self c1x1toself = regrid_conservative_via_esmpy(c1x1, c1x1) c1x1toself_areasum = _cube_area_sum(c1x1toself) self.assertArrayAllClose(c1x1toself_areasum, c1_areasum, 0.0004)
def test_single_cells(self): # Test handling of single-cell grids. # Fetch 'standard' testcube data c1, c2 = self.stock_c1_c2 c1_areasum = self.stock_c1_areasum # # At present NxN -> 1x1 "in-place" doesn't seem to work properly # - result cell has missing-data ? # # Condense entire region into a single cell in the c1 grid xlims1 = _minmax(c1.coord(axis='x').bounds) ylims1 = _minmax(c1.coord(axis='y').bounds) x_c1x1 = iris.coords.DimCoord(xlims1[0], bounds=xlims1, standard_name='longitude', units='degrees', coord_system=_PLAIN_GEODETIC_CS) y_c1x1 = iris.coords.DimCoord(ylims1[0], bounds=ylims1, standard_name='latitude', units='degrees', coord_system=_PLAIN_GEODETIC_CS) c1x1_gridcube = iris.cube.Cube([[0.0]]) c1x1_gridcube.add_dim_coord(y_c1x1, 0) c1x1_gridcube.add_dim_coord(x_c1x1, 1) c1x1 = regrid_conservative_via_esmpy(c1, c1x1_gridcube) c1x1_areasum = _cube_area_sum(c1x1) # Check the total area sum is still the same # NOTE: at present, this causes an error inside ESMF ... context = self.assertRaises(AssertionError) condense_to_1x1_supported = False if condense_to_1x1_supported: context = _donothing_context_manager() with context: self.assertArrayAllClose(c1x1_areasum, c1_areasum) # Condense entire region onto a single cell covering the area of 'c2' xlims2 = _minmax(c2.coord(axis='x').bounds) ylims2 = _minmax(c2.coord(axis='y').bounds) x_c2x1 = iris.coords.DimCoord(xlims2[0], bounds=xlims2, standard_name='longitude', units=iris.unit.Unit('degrees'), coord_system=_PLAIN_GEODETIC_CS) y_c2x1 = iris.coords.DimCoord(ylims2[0], bounds=ylims2, standard_name='latitude', units=iris.unit.Unit('degrees'), coord_system=_PLAIN_GEODETIC_CS) c2x1_gridcube = iris.cube.Cube([[0.0]]) c2x1_gridcube.add_dim_coord(y_c2x1, 0) c2x1_gridcube.add_dim_coord(x_c2x1, 1) c1_to_c2x1 = regrid_conservative_via_esmpy(c1, c2x1_gridcube) # Check the total area sum is still the same c1_to_c2x1_areasum = _cube_area_sum(c1_to_c2x1) self.assertArrayAllClose(c1_to_c2x1_areasum, c1_areasum, 0.0004) # 1x1 -> NxN : regrid single cell to NxN grid # construct a single-cell approximation to 'c1' with the same area sum. # NOTE: can't use _make_cube (see docstring) c1x1 = c1.copy()[0:1, 0:1] xlims1 = _minmax(c1.coord(axis='x').bounds) ylims1 = _minmax(c1.coord(axis='y').bounds) c1x1.coord(axis='x').bounds = xlims1 c1x1.coord(axis='y').bounds = ylims1 # Assign data mean as single cell value : Maybe not exact, but "close" c1x1.data[0, 0] = np.mean(c1.data) # Regrid this back onto the original NxN grid c1x1_to_c1 = regrid_conservative_via_esmpy(c1x1, c1) c1x1_to_c1_areasum = _cube_area_sum(c1x1_to_c1) # Check that area sum is ~unchanged, as expected self.assertArrayAllClose(c1x1_to_c1_areasum, c1_areasum, 0.0004) # Check 1x1 -> 1x1 # NOTE: can *only* get any result with a fully overlapping cell, so # just regrid onto self c1x1toself = regrid_conservative_via_esmpy(c1x1, c1x1) c1x1toself_areasum = _cube_area_sum(c1x1toself) self.assertArrayAllClose(c1x1toself_areasum, c1_areasum, 0.0004)
import iris from iris.experimental.regrid_conservative import regrid_conservative_via_esmpy import matplotlib.pyplot as plt import numpy as np if __name__ == "__main__": source = iris.load_cube("pp_unrot.pp") target = iris.load_cube("pp_rotgrid.pp") regridded = regrid_conservative_via_esmpy(source, target) regridded_crs = regridded.coord(axis='x').coord_system.as_cartopy_crs() source_crs = source.coord(axis='x').coord_system.as_cartopy_crs() source_proj = source.coord(axis='x').coord_system.as_cartopy_projection() # Which data got masked? masked = [] for ndi in np.ndindex(*regridded.data.shape): if regridded.data.mask[ndi]: masked.append(ndi) print "regridded.shape", regridded.shape print "masked items at", masked # Plot the source ax = plt.axes(projection=source_proj) xx, yy = np.meshgrid( source.coord(axis='x').points, source.coord(axis='y').points) plt.scatter(xx.flat, yy.flat, c='g') # Plot the masked point in the source projection
import iris from iris.experimental.regrid_conservative import regrid_conservative_via_esmpy import matplotlib.pyplot as plt import numpy as np if __name__ == "__main__": source = iris.load_cube("pp_unrot.pp") target = iris.load_cube("pp_rotgrid.pp") regridded = regrid_conservative_via_esmpy(source, target) regridded_crs = regridded.coord(axis='x').coord_system.as_cartopy_crs() source_crs = source.coord(axis='x').coord_system.as_cartopy_crs() source_proj = source.coord(axis='x').coord_system.as_cartopy_projection() # Which data got masked? masked = [] for ndi in np.ndindex(*regridded.data.shape): if regridded.data.mask[ndi]: masked.append(ndi) print "regridded.shape", regridded.shape print "masked items at", masked # Plot the source ax = plt.axes(projection=source_proj) xx, yy = np.meshgrid(source.coord(axis='x').points, source.coord(axis='y').points) plt.scatter(xx.flat, yy.flat, c='g') # Plot the masked point in the source projection for ndi in masked:
def test_rotated(self): """ Test area-weighted regrid on more complex area. Use two mutually rotated grids, of similar area + same dims. Only a small central region in each is non-zero, which maps entirely inside the other region. So the area-sum totals should match exactly. """ # create source test cube on rotated form pole_lat = 53.4 pole_lon = -173.2 deg_swing = 35.3 pole_lon += deg_swing c1_nx = 9 + 6 c1_ny = 7 + 6 c1_xlims = -60.0, 60.0 c1_ylims = -45.0, 20.0 c1_xlims = [x - deg_swing for x in c1_xlims] c1 = _make_test_cube((c1_nx, c1_ny), c1_xlims, c1_ylims, pole_latlon=(pole_lat, pole_lon)) c1.data[3:-3, 3:-3] = np.array( [[100, 100, 100, 100, 100, 100, 100, 100, 100], [100, 100, 100, 100, 100, 100, 100, 100, 100], [100, 100, 199, 199, 199, 199, 100, 100, 100], [100, 100, 100, 100, 199, 199, 100, 100, 100], [100, 100, 100, 100, 199, 199, 199, 100, 100], [100, 100, 100, 100, 100, 100, 100, 100, 100], [100, 100, 100, 100, 100, 100, 100, 100, 100]], dtype=np.float) c1_areasum = _cube_area_sum(c1) # construct target cube to receive nx2 = 9 + 6 ny2 = 7 + 6 c2_xlims = -100.0, 120.0 c2_ylims = -20.0, 50.0 c2 = _make_test_cube((nx2, ny2), c2_xlims, c2_ylims) c2.data = np.ma.array(c2.data, mask=True) # perform regrid c1to2 = regrid_conservative_via_esmpy(c1, c2) # check we have zeros (or nearly) all around the edge.. c1toc2_zeros = np.ma.array(c1to2.data) c1toc2_zeros[c1toc2_zeros.mask] = 0.0 c1toc2_zeros = np.abs(c1toc2_zeros.mask) < 1.0e-6 self.assertArrayEqual(c1toc2_zeros[0, :], True) self.assertArrayEqual(c1toc2_zeros[-1, :], True) self.assertArrayEqual(c1toc2_zeros[:, 0], True) self.assertArrayEqual(c1toc2_zeros[:, -1], True) # check the area-sum operation c1to2_areasum = _cube_area_sum(c1to2) self.assertArrayAllClose(c1to2_areasum, c1_areasum, rtol=0.004) # # Now repeat, transforming backwards ... # c1.data = np.ma.array(c1.data, mask=True) c2.data[:] = 0.0 c2.data[5:-5, 5:-5] = np.array( [[199, 199, 199, 199, 100], [100, 100, 199, 199, 100], [100, 100, 199, 199, 199]], dtype=np.float) c2_areasum = _cube_area_sum(c2) c2toc1 = regrid_conservative_via_esmpy(c2, c1) # check we have zeros (or nearly) all around the edge.. c2toc1_zeros = np.ma.array(c2toc1.data) c2toc1_zeros[c2toc1_zeros.mask] = 0.0 c2toc1_zeros = np.abs(c2toc1_zeros.mask) < 1.0e-6 self.assertArrayEqual(c2toc1_zeros[0, :], True) self.assertArrayEqual(c2toc1_zeros[-1, :], True) self.assertArrayEqual(c2toc1_zeros[:, 0], True) self.assertArrayEqual(c2toc1_zeros[:, -1], True) # check the area-sum operation c2toc1_areasum = _cube_area_sum(c2toc1) self.assertArrayAllClose(c2toc1_areasum, c2_areasum, rtol=0.004)
def test_missing_data_rotated(self): """ Check missing-data handling between different coordinate systems. Regrid between mutually rotated lat/lon systems, and check results for missing data due to grid edge overlap, and source-data masking. """ for do_add_missing in (False, True): # create source test cube on rotated form pole_lat = 53.4 pole_lon = -173.2 deg_swing = 35.3 pole_lon += deg_swing c1_nx = 9 + 6 c1_ny = 7 + 6 c1_xlims = -60.0, 60.0 c1_ylims = -45.0, 20.0 c1_xlims = [x - deg_swing for x in c1_xlims] c1 = _make_test_cube((c1_nx, c1_ny), c1_xlims, c1_ylims, pole_latlon=(pole_lat, pole_lon)) c1.data = np.ma.array(c1.data, mask=False) c1.data[3:-3, 3:-3] = np.ma.array( [[100, 100, 100, 100, 100, 100, 100, 100, 100], [100, 100, 100, 100, 100, 100, 100, 100, 100], [100, 100, 199, 199, 199, 199, 100, 100, 100], [100, 100, 100, 100, 199, 199, 100, 100, 100], [100, 100, 100, 100, 199, 199, 199, 100, 100], [100, 100, 100, 100, 100, 100, 100, 100, 100], [100, 100, 100, 100, 100, 100, 100, 100, 100]], dtype=np.float) if do_add_missing: c1.data = np.ma.array(c1.data) c1.data[7, 7] = np.ma.masked c1.data[3:5, 10:12] = np.ma.masked # construct target cube to receive nx2 = 9 + 6 ny2 = 7 + 6 c2_xlims = -80.0, 80.0 c2_ylims = -20.0, 50.0 c2 = _make_test_cube((nx2, ny2), c2_xlims, c2_ylims) c2.data = np.ma.array(c2.data, mask=True) # perform regrid + snapshot test results c1toc2 = regrid_conservative_via_esmpy(c1, c2) # check masking of result is as expected # (generated by inspecting plot of how src+dst grids overlap) expected_mask_valuemap = np.array( # KEY: 0=masked, 7=present, 5=masked with masked datapoints [[0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 7, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0], [0, 0, 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0], [0, 0, 0, 7, 7, 7, 7, 7, 7, 7, 5, 5, 7, 0, 0], [0, 0, 7, 7, 7, 7, 7, 7, 7, 7, 5, 5, 7, 0, 0], [0, 0, 0, 7, 7, 7, 7, 7, 7, 7, 5, 5, 7, 0, 0], [0, 0, 0, 7, 7, 7, 7, 5, 5, 7, 7, 7, 7, 0, 0], [0, 0, 0, 0, 7, 7, 7, 5, 5, 7, 7, 7, 7, 0, 0], [0, 0, 0, 0, 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0], [0, 0, 0, 0, 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0], [0, 0, 0, 0, 0, 0, 7, 7, 7, 7, 7, 7, 7, 7, 0], [0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 7, 7, 7, 7, 0]]) if do_add_missing: expected_mask = expected_mask_valuemap < 7 else: expected_mask = expected_mask_valuemap == 0 actual_mask = c1toc2.data.mask self.assertArrayEqual(actual_mask, expected_mask) if not do_add_missing: # check preservation of area-sums # NOTE: does *not* work with missing data, even theoretically, # as the 'missing areas' are not the same. c1_areasum = _cube_area_sum(c1) c1to2_areasum = _cube_area_sum(c1toc2) self.assertArrayAllClose(c1_areasum, c1to2_areasum, rtol=0.003)
def test_missing_data_rotated(self): """ Check missing-data handling between different coordinate systems. Regrid between mutually rotated lat/lon systems, and check results for missing data due to grid edge overlap, and source-data masking. """ for do_add_missing in (False, True): # create source test cube on rotated form pole_lat = 53.4 pole_lon = -173.2 deg_swing = 35.3 pole_lon += deg_swing c1_nx = 9 + 6 c1_ny = 7 + 6 c1_xlims = -60.0, 60.0 c1_ylims = -45.0, 20.0 c1_xlims = [x - deg_swing for x in c1_xlims] c1 = _make_test_cube((c1_nx, c1_ny), c1_xlims, c1_ylims, pole_latlon=(pole_lat, pole_lon)) c1.data = np.ma.array(c1.data, mask=False) c1.data[3:-3, 3:-3] = np.ma.array([ [100, 100, 100, 100, 100, 100, 100, 100, 100], [100, 100, 100, 100, 100, 100, 100, 100, 100], [100, 100, 199, 199, 199, 199, 100, 100, 100], [100, 100, 100, 100, 199, 199, 100, 100, 100], [100, 100, 100, 100, 199, 199, 199, 100, 100], [100, 100, 100, 100, 100, 100, 100, 100, 100], [100, 100, 100, 100, 100, 100, 100, 100, 100]], dtype=np.float) if do_add_missing: c1.data = np.ma.array(c1.data) c1.data[7, 7] = np.ma.masked c1.data[3:5, 10:12] = np.ma.masked # construct target cube to receive nx2 = 9 + 6 ny2 = 7 + 6 c2_xlims = -80.0, 80.0 c2_ylims = -20.0, 50.0 c2 = _make_test_cube((nx2, ny2), c2_xlims, c2_ylims) c2.data = np.ma.array(c2.data, mask=True) # perform regrid + snapshot test results c1toc2 = regrid_conservative_via_esmpy(c1, c2) # check masking of result is as expected # (generated by inspecting plot of how src+dst grids overlap) expected_mask_valuemap = np.array( # KEY: 0=masked, 7=present, 5=masked with masked datapoints [[0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 7, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0], [0, 0, 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0], [0, 0, 0, 7, 7, 7, 7, 7, 7, 7, 5, 5, 7, 0, 0], [0, 0, 7, 7, 7, 7, 7, 7, 7, 7, 5, 5, 7, 0, 0], [0, 0, 0, 7, 7, 7, 7, 7, 7, 7, 5, 5, 7, 0, 0], [0, 0, 0, 7, 7, 7, 7, 5, 5, 7, 7, 7, 7, 0, 0], [0, 0, 0, 0, 7, 7, 7, 5, 5, 7, 7, 7, 7, 0, 0], [0, 0, 0, 0, 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0], [0, 0, 0, 0, 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0], [0, 0, 0, 0, 0, 0, 7, 7, 7, 7, 7, 7, 7, 7, 0], [0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 7, 7, 7, 7, 0]]) if do_add_missing: expected_mask = expected_mask_valuemap < 7 else: expected_mask = expected_mask_valuemap == 0 actual_mask = c1toc2.data.mask self.assertArrayEqual(actual_mask, expected_mask) if not do_add_missing: # check preservation of area-sums # NOTE: does *not* work with missing data, even theoretically, # as the 'missing areas' are not the same. c1_areasum = _cube_area_sum(c1) c1to2_areasum = _cube_area_sum(c1toc2) self.assertArrayAllClose(c1_areasum, c1to2_areasum, rtol=0.003)