def test_coordinate_not_found(self):
     """Coordinate not found on the cube."""
     nbhooded_cube = self.weights_cube.copy()
     plugin = CollapseMaskedNeighbourhoodCoordinate("kitten", self.weights_cube)
     message = "Expected to find exactly 1 .* coordinate, but found none."
     with self.assertRaisesRegex(CoordinateNotFoundError, message):
         plugin.renormalize_weights(nbhooded_cube)
 def test_normalizing_along_another_axis_with_error(self):
     """Normalizing along another axis, when raises error.
        In this case we are normalizing along an axis where in some places
        the sum along that axis is zero."""
     nbhooded_cube = self.weights_cube.copy()
     plugin = CollapseMaskedNeighbourhoodCoordinate(
         "projection_x_coordinate", self.weights_cube)
     message = "Sum of weights must be > 0.0"
     with self.assertRaisesRegex(ValueError, message):
         plugin.renormalize_weights(nbhooded_cube)
 def test_normalizing_along_another_axis(self):
     """Normalizing along another axis, when this is a valid thing to do.
        This is normalizing along the rows of the input weights."""
     input_weights = np.array(
         [
             [
                 [0.0, 0.7, 0.0, 0.0, 0.0],
                 [0.0, 0.3, 0.0, 0.0, 0.0],
                 [0.3, 0.1, 0.0, 0.0, 0.0],
                 [0.2, 0.0, 0.0, 0.0, 0.0],
                 [0.1, 0.0, 0.0, 0.0, 0.0],
             ],
             [
                 [1.0, 0.0, 1.0, 1.0, 1.0],
                 [1.0, 0.0, 1.0, 1.0, 1.0],
                 [0.0, 0.0, 1.0, 1.0, 1.0],
                 [0.0, 0.0, 0.0, 0.9, 0.1],
                 [1.0, 1.0, 1.0, 0.0, 0.0],
             ],
             [
                 [0.2, 0.0, 0.0, 0.0, 0.0],
                 [0.3, 0.0, 0.0, 0.0, 0.0],
                 [0.1, 0.0, 0.0, 0.0, 0.0],
                 [0.0, 0.0, 0.0, 0.1, 0.0],
                 [0.0, 0.0, 0.0, 0.4, 0.8],
             ],
         ]
     )
     expected_weights = np.array(
         [
             [
                 [0.0, 1.0, 0.0, 0.0, 0.0],
                 [0.0, 1.0, 0.0, 0.0, 0.0],
                 [0.75, 0.25, 0.0, 0.0, 0.0],
                 [1.0, 0.0, 0.0, 0.0, 0.0],
                 [1.0, 0.0, 0.0, 0.0, 0.0],
             ],
             [
                 [0.25, 0.0, 0.25, 0.25, 0.25],
                 [0.25, 0.0, 0.25, 0.25, 0.25],
                 [0.0, 0.0, 0.333333, 0.333333, 0.333333],
                 [0.0, 0.0, 0.0, 0.9, 0.1],
                 [0.333333, 0.333333, 0.333333, 0.0, 0.0],
             ],
             [
                 [1.0, 0.0, 0.0, 0.0, 0.0],
                 [1.0, 0.0, 0.0, 0.0, 0.0],
                 [1.0, 0.0, 0.0, 0.0, 0.0],
                 [0.0, 0.0, 0.0, 1.0, 0.0],
                 [0.0, 0.0, 0.0, 0.333333, 0.666667],
             ],
         ]
     )
     weights_cube = self.weights_cube.copy(input_weights)
     nbhooded_cube = self.weights_cube.copy()
     plugin = CollapseMaskedNeighbourhoodCoordinate(
         "projection_x_coordinate", weights_cube
     )
     weights = plugin.renormalize_weights(nbhooded_cube)
     self.assertArrayAlmostEqual(expected_weights, weights)
class Test_renormalize_weights(IrisTest):
    """Test the renormalize_weights function."""
    def setUp(self):
        """Set up a weights cube and default plugin instance."""
        self.mask = np.array([[[1, 1, 1, 0, 0], [1, 1, 0, 0,
                                                 0], [1, 0, 0, 0, 0],
                               [1, 0, 0, 0, 0], [1, 1, 1, 1, 1]],
                              [[1, 1, 1, 0, 0], [1, 1, 0, 0,
                                                 0], [1, 0, 0, 0, 0],
                               [1, 0, 0, 0, 0], [1, 1, 1, 1, 1]],
                              [[1, 1, 1, 0, 0], [1, 1, 0, 0,
                                                 0], [1, 0, 0, 0, 0],
                               [1, 0, 0, 0, 0], [1, 1, 1, 1, 1]]])
        weights_data = np.array([[[0.8, 0.7, 0.0, 0.0, 0.0],
                                  [0.7, 0.3, 0.0, 0.0, 0.0],
                                  [0.3, 0.1, 0.0, 0.0, 0.0],
                                  [0.0, 0.0, 0.0, 0.0, 0.0],
                                  [0.0, 0.0, 0.0, 0.0, 0.0]],
                                 [[0.2, 0.3, 1.0, 1.0, 1.0],
                                  [0.3, 0.7, 1.0, 1.0, 1.0],
                                  [0.7, 0.9, 1.0, 1.0, 1.0],
                                  [1.0, 1.0, 1.0, 0.9, 0.5],
                                  [1.0, 1.0, 1.0, 0.6, 0.2]],
                                 [[0.0, 0.0, 0.0, 0.0, 0.0],
                                  [0.0, 0.0, 0.0, 0.0, 0.0],
                                  [0.0, 0.0, 0.0, 0.0, 0.0],
                                  [0.0, 0.0, 0.0, 0.1, 0.5],
                                  [0.0, 0.0, 0.0, 0.4, 0.8]]])
        topographic_zone_points = [50, 150, 250]
        topographic_zone_bounds = [[0, 100], [100, 200], [200, 300]]

        weights_cubes = iris.cube.CubeList([])
        for data, point, bounds in zip(weights_data, topographic_zone_points,
                                       topographic_zone_bounds):
            weights_cubes.append(
                set_up_topographic_zone_cube(data,
                                             point,
                                             bounds,
                                             num_grid_points=5))
        self.weights_cube = weights_cubes.merge_cube()
        self.plugin = CollapseMaskedNeighbourhoodCoordinate(
            "topographic_zone", self.weights_cube)

    def test_basic(self):
        """Test weights_cube is still a cube after the function call"""
        nbhooded_cube = self.weights_cube.copy()
        self.plugin.renormalize_weights(nbhooded_cube)
        self.assertIsInstance(self.weights_cube, iris.cube.Cube)

    def test_no_NaNs_in_nbhooded_cube(self):
        """No NaNs in the neighbourhood cube, so no renormalization is
           needed"""
        nbhooded_cube = self.weights_cube.copy()
        expected_weights = self.weights_cube.data.copy()
        self.plugin.renormalize_weights(nbhooded_cube)
        self.assertArrayAlmostEqual(expected_weights, self.weights_cube.data)

    def test_all_NaNs_in_nbhooded_cube(self):
        """Test an error is raised when all NaNs in the nbhood cube so cannot
           have any sensible weights."""
        nbhood_data = np.empty(self.weights_cube.data.shape)
        nbhood_data[:] = np.nan
        nbhooded_cube = self.weights_cube.copy(nbhood_data)
        message = "Sum of weights must be > 0.0"
        with self.assertRaisesRegex(ValueError, message):
            self.plugin.renormalize_weights(nbhooded_cube)

    def test_no_NaNs_in_nbhooded_cube_and_masked_weights(self):
        """No NaNs in the neighbourhood cube, but masked weights."""
        nbhooded_cube = self.weights_cube.copy()

        self.weights_cube.data = np.ma.masked_array(self.weights_cube.data,
                                                    mask=self.mask)
        expected_weights = self.weights_cube.data.copy()
        self.plugin.renormalize_weights(nbhooded_cube)
        self.assertArrayAlmostEqual(expected_weights.data,
                                    self.weights_cube.data.data)
        self.assertArrayAlmostEqual(expected_weights.mask,
                                    self.weights_cube.data.mask)

    def test_some_NaNs_in_nbhooded_cube(self):
        """Some NaNs in the neighbourhood cube, so renormalization is needed"""
        nbhood_data = np.ones((3, 5, 5))
        nbhood_data[0, 0:2, 0] = np.nan
        nbhood_data[2, 2:4, 4] = np.nan

        expected_weights = np.array([[[0.0, 0.7, 0.0, 0.0, 0.0],
                                      [0.0, 0.3, 0.0, 0.0, 0.0],
                                      [0.3, 0.1, 0.0, 0.0, 0.0],
                                      [0.0, 0.0, 0.0, 0.0, 0.0],
                                      [0.0, 0.0, 0.0, 0.0, 0.0]],
                                     [[1.0, 0.3, 1.0, 1.0, 1.0],
                                      [1.0, 0.7, 1.0, 1.0, 1.0],
                                      [0.7, 0.9, 1.0, 1.0, 1.0],
                                      [1.0, 1.0, 1.0, 0.9, 1.0],
                                      [1.0, 1.0, 1.0, 0.6, 0.2]],
                                     [[0.0, 0.0, 0.0, 0.0, 0.0],
                                      [0.0, 0.0, 0.0, 0.0, 0.0],
                                      [0.0, 0.0, 0.0, 0.0, 0.0],
                                      [0.0, 0.0, 0.0, 0.1, 0.0],
                                      [0.0, 0.0, 0.0, 0.4, 0.8]]])
        nbhooded_cube = self.weights_cube.copy(nbhood_data)
        self.plugin.renormalize_weights(nbhooded_cube)
        self.assertArrayAlmostEqual(expected_weights, self.weights_cube.data)

    def test_some_NaNs_in_nbhooded_cube_and_masked_weights(self):
        """Some NaNs in the neighbourhood cube, so renormalization is needed.
           As the points with NaNs in the neighbourhood cube are masked in the
           weights cube then they are not renormalizeed."""
        nbhood_data = np.ones((3, 5, 5))
        nbhood_data[0, 0:2, 0] = np.nan
        nbhood_data[2, 2:4, 4] = np.nan
        nbhooded_cube = self.weights_cube.copy()
        self.weights_cube.data = np.ma.masked_array(self.weights_cube.data,
                                                    mask=self.mask)
        expected_weights = self.weights_cube.data.copy()
        self.plugin.renormalize_weights(nbhooded_cube)
        self.assertArrayAlmostEqual(expected_weights.data,
                                    self.weights_cube.data.data)
        self.assertArrayAlmostEqual(expected_weights.mask,
                                    self.weights_cube.data.mask)

    def test_coordinate_not_found(self):
        """Coordinate not found on the cube."""
        nbhooded_cube = self.weights_cube.copy()
        plugin = CollapseMaskedNeighbourhoodCoordinate("kitten",
                                                       self.weights_cube)
        message = "Expected to find exactly 1 .* coordinate, but found none."
        with self.assertRaisesRegex(CoordinateNotFoundError, message):
            plugin.renormalize_weights(nbhooded_cube)

    def test_normalizing_along_another_axis_with_error(self):
        """Normalizing along another axis, when raises error.
           In this case we are normalizing along an axis where in some places
           the sum along that axis is zero."""
        nbhooded_cube = self.weights_cube.copy()
        plugin = CollapseMaskedNeighbourhoodCoordinate(
            "projection_x_coordinate", self.weights_cube)
        message = "Sum of weights must be > 0.0"
        with self.assertRaisesRegex(ValueError, message):
            plugin.renormalize_weights(nbhooded_cube)

    def test_normalizing_along_another_axis(self):
        """Normalizing along another axis, when this is a valid thing to do.
           This is normalizing along the rows of the input weights."""
        input_weights = np.array([[[0.0, 0.7, 0.0, 0.0, 0.0],
                                   [0.0, 0.3, 0.0, 0.0, 0.0],
                                   [0.3, 0.1, 0.0, 0.0, 0.0],
                                   [0.2, 0.0, 0.0, 0.0, 0.0],
                                   [0.1, 0.0, 0.0, 0.0, 0.0]],
                                  [[1.0, 0.0, 1.0, 1.0, 1.0],
                                   [1.0, 0.0, 1.0, 1.0, 1.0],
                                   [0.0, 0.0, 1.0, 1.0, 1.0],
                                   [0.0, 0.0, 0.0, 0.9, 0.1],
                                   [1.0, 1.0, 1.0, 0.0, 0.0]],
                                  [[0.2, 0.0, 0.0, 0.0, 0.0],
                                   [0.3, 0.0, 0.0, 0.0, 0.0],
                                   [0.1, 0.0, 0.0, 0.0, 0.0],
                                   [0.0, 0.0, 0.0, 0.1, 0.0],
                                   [0.0, 0.0, 0.0, 0.4, 0.8]]])
        expected_weights = np.array([[[0.0, 1.0, 0.0, 0.0, 0.0],
                                      [0.0, 1.0, 0.0, 0.0, 0.0],
                                      [0.75, 0.25, 0.0, 0.0, 0.0],
                                      [1.0, 0.0, 0.0, 0.0, 0.0],
                                      [1.0, 0.0, 0.0, 0.0, 0.0]],
                                     [[0.25, 0.0, 0.25, 0.25, 0.25],
                                      [0.25, 0.0, 0.25, 0.25, 0.25],
                                      [0.0, 0.0, 0.333333, 0.333333, 0.333333],
                                      [0.0, 0.0, 0.0, 0.9, 0.1],
                                      [0.333333, 0.333333, 0.333333, 0.0,
                                       0.0]],
                                     [[1.0, 0.0, 0.0, 0.0, 0.0],
                                      [1.0, 0.0, 0.0, 0.0, 0.0],
                                      [1.0, 0.0, 0.0, 0.0, 0.0],
                                      [0.0, 0.0, 0.0, 1.0, 0.0],
                                      [0.0, 0.0, 0.0, 0.333333, 0.666667]]])
        weights_cube = self.weights_cube.copy(input_weights)
        nbhooded_cube = self.weights_cube.copy()
        plugin = CollapseMaskedNeighbourhoodCoordinate(
            "projection_x_coordinate", weights_cube)
        plugin.renormalize_weights(nbhooded_cube)
        self.assertArrayAlmostEqual(expected_weights, weights_cube.data)