Example #1
0
    def __call__(self, cube):
        """
        Regrid this :class:`~iris.cube.Cube` on to the target grid of
        this :class:`LinearRegridder`.

        The given cube must be defined with the same grid as the source
        grid used to create this :class:`LinearRegridder`.

        Args:

        * cube:
            A :class:`~iris.cube.Cube` to be regridded.

        Returns:
            A cube defined with the horizontal dimensions of the target
            and the other dimensions from this cube. The data values of
            this cube will be converted to values on the new grid using
            linear interpolation.

        """
        if eregrid._get_xy_dim_coords(cube) != self._src_grid:
            raise ValueError('The given cube is not defined on the same '
                             'source grid as this regridder.')
        return eregrid.regrid_bilinear_rectilinear_src_and_grid(
            cube, self._target_grid_cube,
            extrapolation_mode=self._extrapolation_mode)
def regrid_conservative_via_esmpy(source_cube, grid_cube):
    """
    Perform a conservative regridding with ESMPy.

    Regrids the data of a source cube onto a new grid defined by a destination
    cube.

    Args:

    * source_cube (:class:`iris.cube.Cube`):
        Source data.  Must have two identifiable horizontal dimension
        coordinates.
    * grid_cube (:class:`iris.cube.Cube`):
        Define the target horizontal grid:  Only the horizontal dimension
        coordinates are actually used.

    Returns:
        A new cube derived from source_cube, regridded onto the specified
        horizontal grid.

    Any additional coordinates which map onto the horizontal dimensions are
    removed, while all other metadata is retained.
    If there are coordinate factories with 2d horizontal reference surfaces,
    the reference surfaces are also regridded, using ordinary bilinear
    interpolation.

    .. note::

        Both source and destination cubes must have two dimension coordinates
        identified with axes 'X' and 'Y' which share a coord_system with a
        Cartopy CRS.
        The grids are defined by :meth:`iris.coords.Coord.contiguous_bounds` of
        these.

    .. note::

        Initialises the ESMF Manager, if it was not already called.
        This implements default Manager operations (e.g. logging).

        To alter this, make a prior call to ESMF.Manager().

    """
    # Get source + target XY coordinate pairs and check they are suitable.
    src_coords = i_regrid._get_xy_dim_coords(source_cube)
    dst_coords = i_regrid._get_xy_dim_coords(grid_cube)
    src_cs = src_coords[0].coord_system
    grid_cs = dst_coords[0].coord_system
    if src_cs is None or grid_cs is None:
        raise ValueError("Both 'src' and 'grid' Cubes must have a"
                         " coordinate system for their rectilinear grid"
                         " coordinates.")

    if src_cs.as_cartopy_crs() is None or grid_cs.as_cartopy_crs() is None:
        raise ValueError("Both 'src' and 'grid' Cubes coord_systems must have "
                         "a valid associated Cartopy CRS.")

    def _valid_units(coord):
        if isinstance(coord.coord_system, (iris.coord_systems.GeogCS,
                                           iris.coord_systems.RotatedGeogCS)):
            valid_units = 'degrees'
        else:
            valid_units = 'm'
        return coord.units == valid_units

    if not all(_valid_units(coord) for coord in src_coords + dst_coords):
        raise ValueError("Unsupported units: must be 'degrees' or 'm'.")

    # Initialise the ESMF manager in case it was not already done.
    ESMF.Manager()

    # Create a data array for the output cube.
    src_dims_xy = [source_cube.coord_dims(coord)[0] for coord in src_coords]
    # Size matches source, except for X+Y dimensions
    dst_shape = np.array(source_cube.shape)
    dst_shape[src_dims_xy] = [coord.shape[0] for coord in dst_coords]
    # NOTE: result array is masked -- fix this afterward if all unmasked
    fullcube_data = np.ma.zeros(dst_shape)

    # Iterate 2d slices over all possible indices of the 'other' dimensions
    all_other_dims = filter(lambda i_dim: i_dim not in src_dims_xy,
                            xrange(source_cube.ndim))
    all_combinations_of_other_inds = np.ndindex(*dst_shape[all_other_dims])
    for other_indices in all_combinations_of_other_inds:
        # Construct a tuple of slices to address the 2d xy field
        slice_indices_array = np.array([slice(None)] * source_cube.ndim)
        slice_indices_array[all_other_dims] = other_indices
        slice_indices_tuple = tuple(slice_indices_array)

        # Get the source data, reformed into the right dimension order, (x,y).
        src_data_2d = source_cube.data[slice_indices_tuple]
        if (src_dims_xy[0] > src_dims_xy[1]):
            src_data_2d = src_data_2d.transpose()

        # Work out whether we have missing data to define a source grid mask.
        if np.ma.is_masked(src_data_2d):
            srcdata_mask = np.ma.getmask(src_data_2d)
        else:
            srcdata_mask = None

        # Construct ESMF Field objects on source and destination grids.
        src_field = _make_esmpy_field(src_coords[0], src_coords[1],
                                      data=src_data_2d, mask=srcdata_mask)
        dst_field = _make_esmpy_field(dst_coords[0], dst_coords[1])

        # Make Field for destination coverage fraction (for missing data calc).
        coverage_field = ESMF.Field(dst_field.grid, 'validmask_dst')

        # Do the actual regrid with ESMF.
        mask_flag_values = np.array([1], dtype=np.int32)
        regrid_method = ESMF.Regrid(src_field, dst_field,
                                    src_mask_values=mask_flag_values,
                                    regrid_method=ESMF.RegridMethod.CONSERVE,
                                    unmapped_action=ESMF.UnmappedAction.IGNORE,
                                    dst_frac_field=coverage_field)
        regrid_method(src_field, dst_field)
        data = dst_field.data

        # Convert destination 'coverage fraction' into a missing-data mask.
        # Set = wherever part of cell goes outside source grid, or overlaps a
        # masked source cell.
        coverage_tolerance_threshold = 1.0 - 1.0e-8
        data.mask = coverage_field.data < coverage_tolerance_threshold

        # Transpose ESMF result dims (X,Y) back to the order of the source
        if (src_dims_xy[0] > src_dims_xy[1]):
            data = data.transpose()

        # Paste regridded slice back into parent array
        fullcube_data[slice_indices_tuple] = data

    # Remove the data mask if completely unused.
    if not np.ma.is_masked(fullcube_data):
        fullcube_data = np.array(fullcube_data)

    # Generate a full 2d sample grid, as required for regridding orography
    # NOTE: as seen in "regrid_bilinear_rectilinear_src_and_grid"
    # TODO: can this not also be wound into the _create_cube method ?
    src_cs = src_coords[0].coord_system
    sample_grid_x, sample_grid_y = i_regrid._sample_grid(src_cs,
                                                         dst_coords[0],
                                                         dst_coords[1])

    # Return result as a new cube based on the source.
    # TODO: please tidy this interface !!!
    return i_regrid._create_cube(
        fullcube_data,
        src=source_cube,
        x_dim=src_dims_xy[0],
        y_dim=src_dims_xy[1],
        src_x_coord=src_coords[0],
        src_y_coord=src_coords[1],
        grid_x_coord=dst_coords[0],
        grid_y_coord=dst_coords[1],
        sample_grid_x=sample_grid_x,
        sample_grid_y=sample_grid_y,
        regrid_callback=i_regrid._regrid_bilinear_array)
Example #3
0
def regrid_conservative_via_esmpy(source_cube, grid_cube):
    """
    Perform a conservative regridding with ESMPy.

    Regrids the data of a source cube onto a new grid defined by a destination
    cube.

    Args:

    * source_cube (:class:`iris.cube.Cube`):
        Source data.  Must have two identifiable horizontal dimension
        coordinates.
    * grid_cube (:class:`iris.cube.Cube`):
        Define the target horizontal grid:  Only the horizontal dimension
        coordinates are actually used.

    Returns:
        A new cube derived from source_cube, regridded onto the specified
        horizontal grid.

    Any additional coordinates which map onto the horizontal dimensions are
    removed, while all other metadata is retained.
    If there are coordinate factories with 2d horizontal reference surfaces,
    the reference surfaces are also regridded, using ordinary bilinear
    interpolation.

    .. note::

        Both source and destination cubes must have two dimension coordinates
        identified with axes 'X' and 'Y' which share a coord_system with a
        Cartopy CRS.
        The grids are defined by :meth:`iris.coords.Coord.contiguous_bounds` of
        these.

    .. note::

        Initialises the ESMF Manager, if it was not already called.
        This implements default Manager operations (e.g. logging).

        To alter this, make a prior call to ESMF.Manager().

    """
    # Get source + target XY coordinate pairs and check they are suitable.
    src_coords = i_regrid._get_xy_dim_coords(source_cube)
    dst_coords = i_regrid._get_xy_dim_coords(grid_cube)
    src_cs = src_coords[0].coord_system
    grid_cs = dst_coords[0].coord_system
    if src_cs is None or grid_cs is None:
        raise ValueError("Both 'src' and 'grid' Cubes must have a"
                         " coordinate system for their rectilinear grid"
                         " coordinates.")

    if src_cs.as_cartopy_crs() is None or grid_cs.as_cartopy_crs() is None:
        raise ValueError("Both 'src' and 'grid' Cubes coord_systems must have "
                         "a valid associated Cartopy CRS.")

    def _valid_units(coord):
        if isinstance(
                coord.coord_system,
            (iris.coord_systems.GeogCS, iris.coord_systems.RotatedGeogCS)):
            valid_units = 'degrees'
        else:
            valid_units = 'm'
        return coord.units == valid_units

    if not all(_valid_units(coord) for coord in src_coords + dst_coords):
        raise ValueError("Unsupported units: must be 'degrees' or 'm'.")

    # Initialise the ESMF manager in case it was not already done.
    ESMF.Manager()

    # Create a data array for the output cube.
    src_dims_xy = [source_cube.coord_dims(coord)[0] for coord in src_coords]
    # Size matches source, except for X+Y dimensions
    dst_shape = np.array(source_cube.shape)
    dst_shape[src_dims_xy] = [coord.shape[0] for coord in dst_coords]
    # NOTE: result array is masked -- fix this afterward if all unmasked
    fullcube_data = np.ma.zeros(dst_shape)

    # Iterate 2d slices over all possible indices of the 'other' dimensions
    all_other_dims = filter(lambda i_dim: i_dim not in src_dims_xy,
                            xrange(source_cube.ndim))
    all_combinations_of_other_inds = np.ndindex(*dst_shape[all_other_dims])
    for other_indices in all_combinations_of_other_inds:
        # Construct a tuple of slices to address the 2d xy field
        slice_indices_array = np.array([slice(None)] * source_cube.ndim)
        slice_indices_array[all_other_dims] = other_indices
        slice_indices_tuple = tuple(slice_indices_array)

        # Get the source data, reformed into the right dimension order, (x,y).
        src_data_2d = source_cube.data[slice_indices_tuple]
        if (src_dims_xy[0] > src_dims_xy[1]):
            src_data_2d = src_data_2d.transpose()

        # Work out whether we have missing data to define a source grid mask.
        if np.ma.is_masked(src_data_2d):
            srcdata_mask = np.ma.getmask(src_data_2d)
        else:
            srcdata_mask = None

        # Construct ESMF Field objects on source and destination grids.
        src_field = _make_esmpy_field(src_coords[0],
                                      src_coords[1],
                                      data=src_data_2d,
                                      mask=srcdata_mask)
        dst_field = _make_esmpy_field(dst_coords[0], dst_coords[1])

        # Make Field for destination coverage fraction (for missing data calc).
        coverage_field = ESMF.Field(dst_field.grid, 'validmask_dst')

        # Do the actual regrid with ESMF.
        mask_flag_values = np.array([1], dtype=np.int32)
        regrid_method = ESMF.Regrid(src_field,
                                    dst_field,
                                    src_mask_values=mask_flag_values,
                                    regrid_method=ESMF.RegridMethod.CONSERVE,
                                    unmapped_action=ESMF.UnmappedAction.IGNORE,
                                    dst_frac_field=coverage_field)
        regrid_method(src_field, dst_field)
        data = dst_field.data

        # Convert destination 'coverage fraction' into a missing-data mask.
        # Set = wherever part of cell goes outside source grid, or overlaps a
        # masked source cell.
        coverage_tolerance_threshold = 1.0 - 1.0e-8
        data.mask = coverage_field.data < coverage_tolerance_threshold

        # Transpose ESMF result dims (X,Y) back to the order of the source
        if (src_dims_xy[0] > src_dims_xy[1]):
            data = data.transpose()

        # Paste regridded slice back into parent array
        fullcube_data[slice_indices_tuple] = data

    # Remove the data mask if completely unused.
    if not np.ma.is_masked(fullcube_data):
        fullcube_data = np.array(fullcube_data)

    # Generate a full 2d sample grid, as required for regridding orography
    # NOTE: as seen in "regrid_bilinear_rectilinear_src_and_grid"
    # TODO: can this not also be wound into the _create_cube method ?
    src_cs = src_coords[0].coord_system
    sample_grid_x, sample_grid_y = i_regrid._sample_grid(
        src_cs, dst_coords[0], dst_coords[1])

    # Return result as a new cube based on the source.
    # TODO: please tidy this interface !!!
    return i_regrid._create_cube(
        fullcube_data,
        src=source_cube,
        x_dim=src_dims_xy[0],
        y_dim=src_dims_xy[1],
        src_x_coord=src_coords[0],
        src_y_coord=src_coords[1],
        grid_x_coord=dst_coords[0],
        grid_y_coord=dst_coords[1],
        sample_grid_x=sample_grid_x,
        sample_grid_y=sample_grid_y,
        regrid_callback=i_regrid._regrid_bilinear_array)