def test_invalid_mdtol():
    """
    Test initialisation of :func:`esmf_regrid.experimental.unstructured_scheme.GridToMeshESMFRegridder`.

    Checks that an error is raised when mdtol is out of range.
    """
    tgt = _flat_mesh_cube()

    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)

    with pytest.raises(ValueError):
        _ = GridToMeshESMFRegridder(src, tgt, mdtol=2)
    with pytest.raises(ValueError):
        _ = GridToMeshESMFRegridder(src, tgt, mdtol=-1)
def test_bilinear():
    """
    Basic test for :func:`esmf_regrid.experimental.unstructured_scheme.GridToMeshESMFRegridder`.

    Tests with method="bilinear".
    """
    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)
    face_tgt = _gridlike_mesh_cube(n_lons, n_lats, location="face")
    node_tgt = _gridlike_mesh_cube(n_lons, n_lats, location="node")

    src = _add_metadata(src)
    src.data[:] = 1  # Ensure all data in the source is one.
    face_regridder = GridToMeshESMFRegridder(src, face_tgt, method="bilinear")
    node_regridder = GridToMeshESMFRegridder(src, node_tgt, method="bilinear")

    assert face_regridder.regridder.method == "bilinear"
    assert node_regridder.regridder.method == "bilinear"

    face_expected_data = np.ones_like(face_tgt.data)
    node_expected_data = np.ones_like(node_tgt.data)
    face_result = face_regridder(src)
    node_result = node_regridder(src)

    # Lenient check for data.
    assert np.allclose(face_expected_data, face_result.data)
    assert np.allclose(node_expected_data, node_result.data)

    # Check metadata and scalar coords.
    face_expected_cube = _add_metadata(face_tgt)
    node_expected_cube = _add_metadata(node_tgt)
    face_expected_cube.data = face_result.data
    node_expected_cube.data = node_result.data
    assert face_expected_cube == face_result
    assert node_expected_cube == node_result
def test_flat_cubes():
    """
    Basic test for :func:`esmf_regrid.experimental.unstructured_scheme.GridToMeshESMFRegridder`.

    Tests with flat cubes as input (a 2D grid cube and a 1D mesh cube).
    """
    tgt = _flat_mesh_cube()

    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)
    # Ensure data in the target grid is different to the expected data.
    # i.e. target grid data is all zero, expected data is all one
    tgt.data[:] = 0

    src = _add_metadata(src)
    src.data[:] = 1  # Ensure all data in the source is one.
    regridder = GridToMeshESMFRegridder(src, tgt)
    result = regridder(src)
    src_T = src.copy()
    src_T.transpose()
    result_transposed = regridder(src_T)

    expected_data = np.ones([n_lats, n_lons])
    expected_cube = _add_metadata(tgt)

    # Lenient check for data.
    assert np.allclose(expected_data, result.data)
    assert np.allclose(expected_data, result_transposed.data)

    # Check metadata and scalar coords.
    expected_cube.data = result.data
    assert expected_cube == result
    expected_cube.data = result_transposed.data
    assert expected_cube == result_transposed
def test_extra_dims():
    """
    Test for :func:`esmf_regrid.schemes.regrid_rectilinear_to_rectilinear`.

    Tests the handling of extra dimensions and metadata. Ensures that proper
    coordinates, attributes, names and units are copied over.
    """
    h = 2
    t = 4
    e = 6
    src_lats = 3
    src_lons = 5

    tgt_lats = 5
    tgt_lons = 3

    lon_bounds = (-180, 180)
    lat_bounds = (-90, 90)

    src_grid = _grid_cube(
        src_lons,
        src_lats,
        lon_bounds,
        lat_bounds,
    )
    tgt_grid = _grid_cube(
        tgt_lons,
        tgt_lats,
        lon_bounds,
        lat_bounds,
    )

    height = DimCoord(np.arange(h), standard_name="height")
    time = DimCoord(np.arange(t), standard_name="time")
    extra = AuxCoord(np.arange(e), long_name="extra dim")
    spanning = AuxCoord(np.ones([h, t, e]), long_name="spanning dim")

    src_data = np.empty([h, src_lats, t, src_lons, e])
    src_data[:] = np.arange(t * h * e).reshape([h, t, e])[:, np.newaxis, :,
                                                          np.newaxis, :]

    src_cube = Cube(src_data)
    src_cube.add_dim_coord(height, 0)
    src_cube.add_dim_coord(src_grid.coord("latitude"), 1)
    src_cube.add_dim_coord(time, 2)
    src_cube.add_dim_coord(src_grid.coord("longitude"), 3)
    src_cube.add_aux_coord(extra, 4)
    src_cube.add_aux_coord(spanning, [0, 2, 4])

    def _add_metadata(cube):
        result = cube.copy()
        result.units = "K"
        result.attributes = {"a": 1}
        result.standard_name = "air_temperature"
        scalar_height = AuxCoord([5], units="m", standard_name="height")
        scalar_time = DimCoord([10], units="s", standard_name="time")
        result.add_aux_coord(scalar_height)
        result.add_aux_coord(scalar_time)
        return result

    src_cube = _add_metadata(src_cube)

    result = regrid_rectilinear_to_rectilinear(src_cube, tgt_grid)

    expected_data = np.empty([h, tgt_lats, t, tgt_lons, e])
    expected_data[:] = np.arange(t * h * e).reshape([h, t,
                                                     e])[:, np.newaxis, :,
                                                         np.newaxis, :]

    expected_cube = Cube(expected_data)
    expected_cube.add_dim_coord(height, 0)
    expected_cube.add_dim_coord(tgt_grid.coord("latitude"), 1)
    expected_cube.add_dim_coord(time, 2)
    expected_cube.add_dim_coord(tgt_grid.coord("longitude"), 3)
    expected_cube.add_aux_coord(extra, 4)
    expected_cube.add_aux_coord(spanning, [0, 2, 4])
    expected_cube = _add_metadata(expected_cube)

    # Lenient check for data.
    assert np.allclose(expected_data, result.data)

    # Check metadata and coords.
    result.data = expected_data
    assert expected_cube == result
Beispiel #5
0
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))