def setup_cache(self): SYNTH_DATA_DIR = Path().cwd() / "tmp_data" SYNTH_DATA_DIR.mkdir(exist_ok=True) file = str(SYNTH_DATA_DIR.joinpath("chunked_cube.nc")) lon_bounds = (-180, 180) lat_bounds = (-90, 90) n_lons_src = 100 n_lats_src = 200 n_lons_tgt = 20 n_lats_tgt = 40 h = 2000 # Rotated coord systems prevent pickling of the regridder so are # removed for the time being. grid = _grid_cube( n_lons_src, n_lats_src, lon_bounds, lat_bounds, # alt_coord_system=True, ) tgt = _grid_cube(n_lons_tgt, n_lats_tgt, lon_bounds, lat_bounds) chunk_size = [n_lats_src, n_lons_src, 10] src_data = da.ones([n_lats_src, n_lons_src, h], chunks=chunk_size) src = Cube(src_data) src.add_dim_coord(grid.coord("latitude"), 0) src.add_dim_coord(grid.coord("longitude"), 1) iris.save(src, file, chunksizes=chunk_size) # Construct regridder with a loaded version of the grid for consistency. loaded_src = iris.load_cube(file) regridder = ESMFAreaWeightedRegridder(loaded_src, tgt) return regridder, file
def test_dim_switching(): """ Test calling of :func:`esmf_regrid.schemes.ESMFAreaWeightedRegridder`. Checks that the regridder accepts a cube with dimensions in a different order than the cube which initialised it. Checks that dimension order is inherited from the cube in the calling function in both cases. """ n_lons = 6 n_lats = 5 lon_bounds = (-180, 180) lat_bounds = (-90, 90) src = _grid_cube(n_lons, n_lats, lon_bounds, lat_bounds, circular=True) tgt = _grid_cube(n_lons, n_lats, lon_bounds, lat_bounds, circular=True) regridder = ESMFAreaWeightedRegridder(src, tgt) unswitched_result = regridder(src) src_switched = src.copy() src_switched.transpose() switched_result = regridder(src_switched) assert unswitched_result.coord( dimensions=(0, )).standard_name == "latitude" assert unswitched_result.coord( dimensions=(1, )).standard_name == "longitude" assert switched_result.coord(dimensions=(0, )).standard_name == "longitude" assert switched_result.coord(dimensions=(1, )).standard_name == "latitude"
def setup(self, type): lon_bounds = (-180, 180) lat_bounds = (-90, 90) n_lons_src = 20 n_lats_src = 40 n_lons_tgt = 20 n_lats_tgt = 40 h = 100 if type == "large source": n_lons_src = 100 n_lats_src = 200 if type == "large target": n_lons_tgt = 100 n_lats_tgt = 200 grid = _grid_cube( n_lons_src, n_lats_src, lon_bounds, lat_bounds, alt_coord_system=(type == "mixed"), ) tgt = _grid_cube(n_lons_tgt, n_lats_tgt, lon_bounds, lat_bounds) src_data = np.arange(n_lats_src * n_lons_src * h).reshape( [n_lats_src, n_lons_src, h]) src = Cube(src_data) src.add_dim_coord(grid.coord("latitude"), 0) src.add_dim_coord(grid.coord("longitude"), 1) self.regridder = ESMFAreaWeightedRegridder(src, tgt) self.src = src
def test_invalid_mdtol(): """ Test initialisation of :func:`esmf_regrid.schemes.ESMFAreaWeightedRegridder`. Checks that an error is raised when mdtol is out of range. """ n_lons = 6 n_lats = 5 lon_bounds = (-180, 180) lat_bounds = (-90, 90) src = _grid_cube(n_lons, n_lats, lon_bounds, lat_bounds, circular=True) tgt = _grid_cube(n_lons, n_lats, lon_bounds, lat_bounds, circular=True) match = "Value for mdtol must be in range 0 - 1, got " with pytest.raises(ValueError, match=match): _ = ESMFAreaWeightedRegridder(src, tgt, mdtol=2) with pytest.raises(ValueError, match=match): _ = ESMFAreaWeightedRegridder(src, tgt, mdtol=-1)
def test_curvilinear_equivalence(): """ Test initialisation of :func:`esmf_regrid.schemes.ESMFAreaWeightedRegridder`. Checks that equivalent curvilinear and rectilinear coordinates give the same results. """ n_lons_src = 6 n_lons_tgt = 3 n_lats_src = 4 n_lats_tgt = 2 lon_bounds = (-180, 180) lat_bounds = (-90, 90) grid_src = _grid_cube(n_lons_src, n_lats_src, lon_bounds, lat_bounds, circular=True) grid_tgt = _grid_cube(n_lons_tgt, n_lats_tgt, lon_bounds, lat_bounds, circular=True) curv_src = _curvilinear_cube(n_lons_src, n_lats_src, lon_bounds, lat_bounds) curv_tgt = _curvilinear_cube(n_lons_tgt, n_lats_tgt, lon_bounds, lat_bounds) grid_to_grid = ESMFAreaWeightedRegridder(grid_src, grid_tgt) grid_to_curv = ESMFAreaWeightedRegridder(grid_src, curv_tgt) curv_to_grid = ESMFAreaWeightedRegridder(curv_src, grid_tgt) curv_to_curv = ESMFAreaWeightedRegridder(curv_src, curv_tgt) def extract_weights(regridder): return regridder.regridder.weight_matrix.todense() for regridder in [grid_to_curv, curv_to_grid, curv_to_curv]: assert np.allclose(extract_weights(grid_to_grid), extract_weights(regridder))
def test_differing_grids(): """ Test calling of :func:`esmf_regrid.schemes.ESMFAreaWeightedRegridder`. Checks that the regridder raises an error when given a cube with a different grid to the one it was initialised with. """ n_lons = 6 n_lats = 5 lon_bounds = (-180, 180) lat_bounds = (-90, 90) src_init = _grid_cube(n_lons, n_lats, lon_bounds, lat_bounds, circular=True) tgt = _grid_cube(n_lons, n_lats, lon_bounds, lat_bounds, circular=True) n_lons_dif = 7 src_dif_coord = _grid_cube(n_lons_dif, n_lats, lon_bounds, lat_bounds, circular=True) src_dif_circ = _grid_cube(n_lons, n_lats, lon_bounds, lat_bounds, circular=False) regridder = ESMFAreaWeightedRegridder(src_init, tgt) msg = "The given cube is not defined on the same source grid as this regridder." with pytest.raises(ValueError, match=msg): _ = regridder(src_dif_coord) with pytest.raises(ValueError, match=msg): _ = regridder(src_dif_circ)
def test_unit_equivalence(): """ Test initialisation of :func:`esmf_regrid.schemes.ESMFAreaWeightedRegridder`. Checks that equivalent coordinates in degrees and radians give the same results. """ n_lons_src = 6 n_lons_tgt = 3 n_lats_src = 4 n_lats_tgt = 2 lon_bounds = (-180, 180) lat_bounds = (-90, 90) lon_rad_bounds = (-np.pi, np.pi) lat_rad_bounds = (-np.pi / 2, np.pi / 2) def rad_coords(cube): cube.coord("latitude").units = Unit("radians") cube.coord("longitude").units = Unit("radians") grid_src = _grid_cube(n_lons_src, n_lats_src, lon_bounds, lat_bounds, circular=True) grid_src_rad = _grid_cube(n_lons_src, n_lats_src, lon_rad_bounds, lat_rad_bounds, circular=True) rad_coords(grid_src_rad) grid_tgt = _grid_cube(n_lons_tgt, n_lats_tgt, lon_bounds, lat_bounds, circular=True) grid_tgt_rad = _grid_cube(n_lons_tgt, n_lats_tgt, lon_rad_bounds, lat_rad_bounds, circular=True) rad_coords(grid_tgt_rad) curv_src = _curvilinear_cube(n_lons_src, n_lats_src, lon_bounds, lat_bounds) curv_src_rad = _curvilinear_cube(n_lons_src, n_lats_src, lon_rad_bounds, lat_rad_bounds) rad_coords(curv_src_rad) curv_tgt = _curvilinear_cube(n_lons_tgt, n_lats_tgt, lon_bounds, lat_bounds) curv_tgt_rad = _curvilinear_cube(n_lons_tgt, n_lats_tgt, lon_rad_bounds, lat_rad_bounds) rad_coords(curv_tgt_rad) grid_to_grid = ESMFAreaWeightedRegridder(grid_src, grid_tgt) grid_rad_to_grid = ESMFAreaWeightedRegridder(grid_src_rad, grid_tgt) grid_rad_to_curv = ESMFAreaWeightedRegridder(grid_src_rad, curv_tgt) curv_to_grid_rad = ESMFAreaWeightedRegridder(curv_src, grid_tgt_rad) curv_rad_to_grid = ESMFAreaWeightedRegridder(curv_src_rad, grid_tgt) curv_to_curv_rad = ESMFAreaWeightedRegridder(curv_src, curv_tgt_rad) def extract_weights(regridder): return regridder.regridder.weight_matrix.todense() for regridder in [ grid_rad_to_grid, grid_rad_to_curv, curv_to_grid_rad, curv_rad_to_grid, curv_to_curv_rad, ]: assert np.allclose(extract_weights(grid_to_grid), extract_weights(regridder))