def __init__(self, precision=0.005, coord_name_to_integrate="height",
                 start_point=None, end_point=None,
                 direction_of_integration="negative"):
        """
        Initialise class.

        Keyword Args:
            precision (float):
                The precision to which the Newton iterator must converge
                before returning wet bulb temperatures.
            coord_name_to_integrate (string):
                Name of the coordinate to be integrated.
            start_point (float or None):
                Point at which to start the integration.
                Default is None. If start_point is None, integration starts
                from the first available point.
            end_point (float or None):
                Point at which to end the integration.
                Default is None. If end_point is None, integration will
                continue until the last available point.
            direction_of_integration (string):
                Description of the direction in which to integrate.
                Options are 'positive' or 'negative'.
                'positive' corresponds to the values within the array
                increasing as the array index increases.
                'negative' corresponds to the values within the array
                decreasing as the array index increases.
        """
        self.wet_bulb_temperature_plugin = (
            WetBulbTemperature(precision=precision))
        self.integration_plugin = Integration(
            coord_name_to_integrate, start_point=start_point,
            end_point=end_point,
            direction_of_integration=direction_of_integration)
        self.coord_name_to_integrate = coord_name_to_integrate
Exemplo n.º 2
0
    def __init__(self,
                 coord_name_to_integrate="height",
                 start_point=None,
                 end_point=None,
                 direction_of_integration="negative"):
        """
        Initialise class.

        Args:
            coord_name_to_integrate (str):
                Name of the coordinate to be integrated.
            start_point (float or None):
                Point at which to start the integration.
                Default is None. If start_point is None, integration starts
                from the first available point.
            end_point (float or None):
                Point at which to end the integration.
                Default is None. If end_point is None, integration will
                continue until the last available point.
            direction_of_integration (str):
                Description of the direction in which to integrate.
                Options are 'positive' or 'negative'.
                'positive' corresponds to the values within the array
                increasing as the array index increases.
                'negative' corresponds to the values within the array
                decreasing as the array index increases.
        """
        self.integration_plugin = Integration(
            coord_name_to_integrate,
            start_point=start_point,
            end_point=end_point,
            direction_of_integration=direction_of_integration)
        self.coord_name_to_integrate = coord_name_to_integrate
 def setUp(self):
     """Set up the cube."""
     height_points = np.array([5.0, 10.0, 20.0])
     cube = _set_up_height_cube(height_points)
     self.plugin_positive = Integration("height", positive_integration=True)
     self.plugin_positive.input_cube = cube.copy()
     self.plugin_negative = Integration("height")
     self.plugin_negative.input_cube = cube.copy()
Exemplo n.º 4
0
class Test_ensure_monotonic_increase_in_chosen_direction(IrisTest):

    """Test the ensure_monotonic_increase_in_chosen_direction method."""

    def setUp(self):
        """Set up the cube."""
        self.ascending_height_points = np.array([5., 10., 20.])
        self.ascending_cube = _set_up_height_cube(
            self.ascending_height_points)
        self.descending_height_points = np.array([20., 10., 5.])
        self.descending_cube = _set_up_height_cube(
            self.descending_height_points, ascending=False)
        self.plugin_positive = Integration("height", positive_integration=True)
        self.plugin_negative = Integration("height")

    def test_ascending_coordinate_positive(self):
        """Test that for a monotonically ascending coordinate, where the
        chosen direction is positive, the resulting coordinate still
        increases monotonically in the positive direction."""
        cube = (
            self.plugin_positive.ensure_monotonic_increase_in_chosen_direction(
                self.ascending_cube))
        self.assertIsInstance(cube, iris.cube.Cube)
        self.assertArrayAlmostEqual(
            cube.coord("height").points, self.ascending_height_points)

    def test_ascending_coordinate_negative(self):
        """Test that for a monotonically ascending coordinate, where the
        chosen direction is negative, the resulting coordinate now decreases
        monotonically in the positive direction."""
        cube = (
            self.plugin_negative.ensure_monotonic_increase_in_chosen_direction(
                self.ascending_cube))
        self.assertIsInstance(cube, iris.cube.Cube)
        self.assertArrayAlmostEqual(
            cube.coord("height").points, self.descending_height_points)

    def test_descending_coordinate_positive(self):
        """Test that for a monotonically ascending coordinate, where the
        chosen direction is positive, the resulting coordinate still
        increases monotonically in the positive direction."""
        cube = (
            self.plugin_positive.ensure_monotonic_increase_in_chosen_direction(
                    self.descending_cube))
        self.assertIsInstance(cube, iris.cube.Cube)
        self.assertArrayAlmostEqual(
            cube.coord("height").points, self.ascending_height_points)

    def test_descending_coordinate_negative(self):
        """Test that for a monotonically ascending coordinate, where the
        chosen direction is negative, the resulting coordinate still decreases
        monotonically in the positive direction."""
        cube = (
            self.plugin_negative.ensure_monotonic_increase_in_chosen_direction(
                    self.descending_cube))
        self.assertIsInstance(cube, iris.cube.Cube)
        self.assertArrayAlmostEqual(
            cube.coord("height").points, self.descending_height_points)
Exemplo n.º 5
0
 def setUp(self):
     """Set up the cube."""
     self.ascending_height_points = np.array([5.0, 10.0, 20.0])
     self.ascending_cube = _set_up_height_cube(self.ascending_height_points)
     self.descending_height_points = np.array([20.0, 10.0, 5.0])
     self.descending_cube = _set_up_height_cube(
         self.descending_height_points, ascending=False)
     self.plugin_positive = Integration("height", positive_integration=True)
     self.plugin_negative = Integration("height")
 def setUp(self):
     """Set up the cube."""
     cube = _set_up_height_cube(np.array([5.0, 10.0, 20.0]))
     self.coord_name = "height"
     data = np.zeros(cube.shape)
     data[0] = np.ones(cube[0].shape, dtype=np.int32)
     data[1] = np.full(cube[1].shape, 2, dtype=np.int32)
     data[2] = np.full(cube[2].shape, 3, dtype=np.int32)
     data[0, 0, 0] = 6
     cube.data = data
     self.cube = cube
     self.plugin = Integration("height")
    def test_end_point_at_bound_negative(self):
        """Test that the resulting cube contains the expected data when a
        end_point is specified, so that only part of the column is
        integrated. In this instance, the end_point of 10 is equal to the
        available height levels [5., 10., 20.]. If the end_point is lower
        than a height level then integration will end. In this example,
        the end_point is equal to a height level, so the layer above the
        end_point is included within the integration.

        For integration in the negative direction (equivalent to
        integrating downwards for a height coordinate), the presense of an
        end_point indicates that the integration may end above the
        lowest height within the column to be integrated."""
        expected = np.array(
            [[[25.00, 25.00, 25.00],
              [25.00, 25.00, 25.00],
              [25.00, 25.00, 25.00]]])
        coord_name = "height"
        end_point = 10.
        direction = "negative"
        result = (
            Integration(
                coord_name, end_point=end_point,
                direction_of_integration=direction
                ).perform_integration(
                      self.negative_upper_bounds_cube,
                      self.negative_lower_bounds_cube,
                      self.negative_integrated_cube))
        self.assertArrayAlmostEqual(
            result.coord("height").points, np.array([10.]))
        self.assertArrayAlmostEqual(result.data, expected)
 def test_end_point_negative(self):
     """Test that the resulting cube contains the expected data when a
     end_point is specified, so that only part of the column is
     integrated. For integration in the negative direction (equivalent to
     integrating downwards for a height coordinate), the presense of an
     end_point indicates that the integration may end above the
     lowest height within the column to be integrated."""
     expected = np.array(
         [[[25.00, 25.00, 25.00],
           [25.00, 25.00, 25.00],
           [25.00, 25.00, 25.00]]])
     coord_name = "height"
     end_point = 8.
     direction = "negative"
     result = (
         Integration(
             coord_name, end_point=end_point,
             direction_of_integration=direction
             ).perform_integration(
                   self.negative_upper_bounds_cube,
                   self.negative_lower_bounds_cube,
                   self.negative_integrated_cube))
     self.assertArrayAlmostEqual(
         result.coord("height").points, np.array([10.]))
     self.assertArrayAlmostEqual(result.data, expected)
Exemplo n.º 9
0
 def test_basic(self):
     """Test that the __repr__ returns the expected string."""
     coord_name = "height"
     result = str(Integration(coord_name))
     msg = ('<Integration: coord_name_to_integrate: height, '
            'start_point: None, end_point: None, '
            'positive_integration: False>')
     self.assertEqual(result, msg)
class Test_prepare_for_integration(IrisTest):

    """Test the prepare_for_integration method."""

    def setUp(self):
        """Set up the cube."""
        height_points = np.array([5.0, 10.0, 20.0])
        cube = _set_up_height_cube(height_points)
        self.plugin_positive = Integration("height", positive_integration=True)
        self.plugin_positive.input_cube = cube.copy()
        self.plugin_negative = Integration("height")
        self.plugin_negative.input_cube = cube.copy()

    def test_basic(self):
        """Test that the type of the returned value is as expected and the
        expected number of items are returned."""
        result = self.plugin_negative.prepare_for_integration()
        self.assertIsInstance(result, tuple)
        self.assertEqual(len(result), 2)
        self.assertIsInstance(result[0], iris.cube.Cube)
        self.assertIsInstance(result[1], iris.cube.Cube)

    def test_positive_points(self):
        """Test that the expected coordinate points are returned for each
        cube when the direction of integration is positive."""
        result = self.plugin_positive.prepare_for_integration()
        self.assertArrayAlmostEqual(
            result[0].coord("height").points, np.array([10.0, 20.0])
        )
        self.assertArrayAlmostEqual(
            result[1].coord("height").points, np.array([5.0, 10.0])
        )

    def test_negative_points(self):
        """Test that the expected coordinate points are returned for each
        cube when the direction of integration is negative."""
        self.plugin_negative.input_cube.coord("height").points = np.array(
            [20.0, 10.0, 5.0]
        )
        result = self.plugin_negative.prepare_for_integration()
        self.assertArrayAlmostEqual(
            result[0].coord("height").points, np.array([20.0, 10.0])
        )
        self.assertArrayAlmostEqual(
            result[1].coord("height").points, np.array([10.0, 5.0])
        )
 def test_raise_exception(self):
     """Test that an error is raised, if the direction_of_integration
     is not valid."""
     coord_name = "height"
     direction = "sideways"
     msg = "The specified direction of integration"
     with self.assertRaisesRegexp(ValueError, msg):
         Integration(coord_name, direction_of_integration=direction)
 def test_basic(self):
     """Test that a cube with the points on the chosen coordinate are
     in the expected order."""
     coord_name = "height"
     direction = "negative"
     result = (Integration(
         coord_name, direction_of_integration=direction).process(self.cube))
     self.assertIsInstance(result, iris.cube.Cube)
     self.assertArrayAlmostEqual(
         result.coord("height").points, np.array([10., 5.]))
 def test_descending_coordinate_negative(self):
     """Test that for a monotonically ascending coordinate, where the
     chosen direction is negative, the resulting coordinate still decreases
     monotonically in the positive direction."""
     coord_name = "height"
     direction = "negative"
     cube = (Integration(coord_name, direction_of_integration=direction).
             ensure_monotonic_increase_in_chosen_direction(
                 self.descending_cube))
     self.assertIsInstance(cube, iris.cube.Cube)
     self.assertArrayAlmostEqual(
         cube.coord(coord_name).points, self.descending_height_points)
    def setUp(self):
        """Set up the cubes. One set of cubes for integrating in the positive
        direction and another set of cubes for integrating in the negative
        direction."""
        cube = _set_up_height_cube(np.array([5.0, 10.0, 20.0]))

        data = np.zeros(cube.shape)
        data[0] = np.ones(cube[0].shape, dtype=np.int32)
        data[1] = np.full(cube[1].shape, 2, dtype=np.int32)
        data[2] = np.full(cube[2].shape, 3, dtype=np.int32)
        data[0, 0, 0] = 6
        cube.data = data

        # Cubes for integrating in the positive direction.
        self.positive_upper_bounds_cube = cube[1:, ...]
        self.positive_lower_bounds_cube = cube[:-1, ...]
        self.positive_integrated_cube = cube[1:, ...]
        self.positive_integrated_cube.data = np.zeros(
            self.positive_integrated_cube.shape
        )

        # Cubes for integrating in the negative direction.
        new_cube = cube.copy()
        # Sort cube so that it is in the expected order.
        index = [[2, 1, 0], slice(None), slice(None), slice(None)]
        new_cube = new_cube[tuple(index)]
        self.negative_upper_bounds_cube = new_cube[:-1, ...]
        self.negative_lower_bounds_cube = new_cube[1:, ...]

        self.expected_data_zero_or_negative = np.array(
            [
                [[10.00, 25.00, 25.00], [25.00, 25.00, 25.00], [25.00, 25.00, 25.00]],
                [[30.00, 32.50, 32.50], [32.50, 32.50, 32.50], [32.50, 32.50, 32.50]],
            ]
        )

        self.plugin_positive = Integration("height", positive_integration=True)
        self.plugin_positive.input_cube = cube.copy()
        self.plugin_negative = Integration("height")
        self.plugin_negative.input_cube = cube.copy()
 def test_data(self):
     """Test that the resulting cube contains the expected data following
     vertical integration."""
     expected = np.array([[[[25.00, 25.00, 25.00], [25.00, 25.00, 25.00],
                            [25.00, 25.00, 25.00]]],
                          [[[45.00, 32.50, 32.50], [32.50, 32.50, 32.50],
                            [32.50, 32.50, 32.50]]]])
     coord_name = "height"
     direction = "negative"
     result = (Integration(
         coord_name, direction_of_integration=direction).process(self.cube))
     self.assertArrayAlmostEqual(
         result.coord("height").points, np.array([10., 5.]))
     self.assertArrayAlmostEqual(result.data, expected)
 def test_basic(self):
     """Test that the type of the returned value is as expected and the
     expected number of items are returned."""
     coord_name = "height"
     direction = "negative"
     result = (
         Integration(
             coord_name, direction_of_integration=direction
             ).prepare_for_integration(self.cube))
     self.assertIsInstance(result, tuple)
     self.assertEqual(len(result), 3)
     self.assertIsInstance(result[0], iris.cube.Cube)
     self.assertIsInstance(result[1], iris.cube.Cube)
     self.assertIsInstance(result[2], iris.cube.Cube)
 def test_basic(self):
     """Test that a cube is returned by the perform_integration method with
     the expected coordinate points."""
     coord_name = "height"
     direction = "negative"
     result = (Integration(
         coord_name,
         direction_of_integration=direction).perform_integration(
             self.negative_upper_bounds_cube,
             self.negative_lower_bounds_cube,
             self.negative_integrated_cube))
     self.assertIsInstance(result, iris.cube.Cube)
     self.assertArrayAlmostEqual(
         result.coord("height").points, np.array([5., 10.]))
 def test_positive_points(self):
     """Test that the expected coordinate points are returned for each
     cube when the direction of integration is positive."""
     coord_name = "height"
     direction = "positive"
     result = (
         Integration(
             coord_name, direction_of_integration=direction
             ).prepare_for_integration(self.cube))
     self.assertArrayAlmostEqual(
         result[0].coord("height").points, np.array([10., 20.]))
     self.assertArrayAlmostEqual(
         result[1].coord("height").points, np.array([5., 10.]))
     self.assertArrayAlmostEqual(
         result[2].coord("height").points, np.array([10., 20.]))
 def test_integration_not_performed(self):
     """Test that the expected exception is raise if no integration
     can be performed, as a result of the options selected, for example,
     if the start_point is above the height of any of the levels within
     the cube."""
     coord_name = "height"
     start_point = 25.
     direction = "positive"
     msg = "No integration could be performed for"
     with self.assertRaisesRegexp(ValueError, msg):
         Integration(
             coord_name, start_point=start_point,
             direction_of_integration=direction
             ).perform_integration(
                   self.positive_upper_bounds_cube,
                   self.positive_lower_bounds_cube,
                   self.positive_integrated_cube)
 def test_positive_values_in_data(self):
     """Test that the resulting cube contains the expected data following
     vertical integration."""
     expected = np.array([[[[45.00, 32.50, 32.50], [32.50, 32.50, 32.50],
                            [32.50, 32.50, 32.50]]],
                          [[[25.00, 25.00, 25.00], [25.00, 25.00, 25.00],
                            [25.00, 25.00, 25.00]]]])
     coord_name = "height"
     direction = "negative"
     result = (Integration(
         coord_name,
         direction_of_integration=direction).perform_integration(
             self.negative_upper_bounds_cube,
             self.negative_lower_bounds_cube,
             self.negative_integrated_cube))
     self.assertArrayAlmostEqual(
         result.coord("height").points, np.array([5., 10.]))
     self.assertArrayAlmostEqual(result.data, expected)
 def test_zero_values_in_data(self):
     """Test that the resulting cube contains the expected data following
     vertical integration where some of the values in the data are equal
     to zero. This provides a baseline as the Integration plugin is
     currently restricted so that only positive values contribute towards
     the integral."""
     self.negative_upper_bounds_cube.data[0, :, 0, 0] = 0
     coord_name = "height"
     direction = "negative"
     result = (Integration(
         coord_name,
         direction_of_integration=direction).perform_integration(
             self.negative_upper_bounds_cube,
             self.negative_lower_bounds_cube,
             self.negative_integrated_cube))
     self.assertArrayAlmostEqual(
         result.coord("height").points, np.array([5., 10.]))
     self.assertArrayAlmostEqual(result.data,
                                 self.expected_data_zero_or_negative)
 def test_start_point_negative_direction(self):
     """Test that the resulting cube contains the expected data when a
     start_point is specified, so that only part of the column is
     integrated. For integration in the negative direction (equivalent to
     integrating downwards for the height coordinate in the input cube),
     the presence of a start_point indicates that the integration may start
     below the highest height within the column to be integrated."""
     expected = np.array([[[20.00, 7.50, 7.50], [7.50, 7.50, 7.50],
                           [7.50, 7.50, 7.50]]])
     coord_name = "height"
     start_point = 18.
     direction = "negative"
     result = (Integration(
         coord_name,
         start_point=start_point,
         direction_of_integration=direction).perform_integration(
             self.negative_upper_bounds_cube,
             self.negative_lower_bounds_cube,
             self.negative_integrated_cube))
     self.assertArrayAlmostEqual(
         result.coord("height").points, np.array([5.]))
     self.assertArrayAlmostEqual(result.data, expected)
class Test_process(IrisTest):

    """Test the process method."""

    def setUp(self):
        """Set up the cube."""
        cube = _set_up_height_cube(np.array([5.0, 10.0, 20.0]))
        self.coord_name = "height"
        data = np.zeros(cube.shape)
        data[0] = np.ones(cube[0].shape, dtype=np.int32)
        data[1] = np.full(cube[1].shape, 2, dtype=np.int32)
        data[2] = np.full(cube[2].shape, 3, dtype=np.int32)
        data[0, 0, 0] = 6
        cube.data = data
        self.cube = cube
        self.plugin = Integration("height")

    def test_basic(self):
        """Test that a cube with the points on the chosen coordinate are
        in the expected order."""
        result = self.plugin.process(self.cube)
        self.assertIsInstance(result, iris.cube.Cube)
        self.assertArrayAlmostEqual(
            result.coord("height").points, np.array([10.0, 5.0])
        )
        self.assertArrayAlmostEqual(
            result.coord("height").bounds, np.array([[10.0, 20.0], [5.0, 10.0]])
        )

    def test_metadata(self):
        """Test that the metadata on the resulting cube is as expected"""
        expected_attributes = generate_mandatory_attributes([self.cube])
        result = self.plugin.process(self.cube)
        self.assertEqual(result.name(), self.cube.name() + "_integral")
        self.assertEqual(result.units, "{} m".format(self.cube.units))
        self.assertDictEqual(result.attributes, expected_attributes)

    def test_data(self):
        """Test that the resulting cube contains the expected data following
        vertical integration."""
        expected = np.array(
            [
                [[25.00, 25.00, 25.00], [25.00, 25.00, 25.00], [25.00, 25.00, 25.00]],
                [[45.00, 32.50, 32.50], [32.50, 32.50, 32.50], [32.50, 32.50, 32.50]],
            ]
        )
        result = self.plugin.process(self.cube)
        self.assertArrayAlmostEqual(
            result.coord("height").points, np.array([10.0, 5.0])
        )
        self.assertArrayAlmostEqual(result.data, expected)

    def test_dimension_preservation(self):
        """Test the result preserves input dimension order when the coordinate
        to integrate is not the first dimension (eg there's a leading
        realization coordinate)
        """
        cube = set_up_variable_cube(280 * np.ones((3, 3, 3), dtype=np.float32))
        cube = add_coordinate(
            cube, np.array([5.0, 10.0, 20.0]), "height", coord_units="m"
        )
        cube.transpose([1, 0, 2, 3])
        expected_coord_order = [coord.name() for coord in cube.coords(dim_coords=True)]
        result = self.plugin.process(cube)
        self.assertEqual(result.coord_dims("height"), (1,))
        result_coord_order = [coord.name() for coord in result.coords(dim_coords=True)]
        self.assertListEqual(result_coord_order, expected_coord_order)
class Test_perform_integration(IrisTest):

    """Test the perform_integration method."""

    def setUp(self):
        """Set up the cubes. One set of cubes for integrating in the positive
        direction and another set of cubes for integrating in the negative
        direction."""
        cube = _set_up_height_cube(np.array([5.0, 10.0, 20.0]))

        data = np.zeros(cube.shape)
        data[0] = np.ones(cube[0].shape, dtype=np.int32)
        data[1] = np.full(cube[1].shape, 2, dtype=np.int32)
        data[2] = np.full(cube[2].shape, 3, dtype=np.int32)
        data[0, 0, 0] = 6
        cube.data = data

        # Cubes for integrating in the positive direction.
        self.positive_upper_bounds_cube = cube[1:, ...]
        self.positive_lower_bounds_cube = cube[:-1, ...]
        self.positive_integrated_cube = cube[1:, ...]
        self.positive_integrated_cube.data = np.zeros(
            self.positive_integrated_cube.shape
        )

        # Cubes for integrating in the negative direction.
        new_cube = cube.copy()
        # Sort cube so that it is in the expected order.
        index = [[2, 1, 0], slice(None), slice(None), slice(None)]
        new_cube = new_cube[tuple(index)]
        self.negative_upper_bounds_cube = new_cube[:-1, ...]
        self.negative_lower_bounds_cube = new_cube[1:, ...]

        self.expected_data_zero_or_negative = np.array(
            [
                [[10.00, 25.00, 25.00], [25.00, 25.00, 25.00], [25.00, 25.00, 25.00]],
                [[30.00, 32.50, 32.50], [32.50, 32.50, 32.50], [32.50, 32.50, 32.50]],
            ]
        )

        self.plugin_positive = Integration("height", positive_integration=True)
        self.plugin_positive.input_cube = cube.copy()
        self.plugin_negative = Integration("height")
        self.plugin_negative.input_cube = cube.copy()

    def test_basic(self):
        """Test that a cube is returned by the perform_integration method with
        the expected coordinate points."""
        result = self.plugin_negative.perform_integration(
            self.negative_upper_bounds_cube, self.negative_lower_bounds_cube
        )
        self.assertIsInstance(result, iris.cube.Cube)
        self.assertArrayAlmostEqual(
            result.coord("height").points, np.array([10.0, 5.0])
        )
        self.assertArrayAlmostEqual(
            result.coord("height").bounds, np.array([[10.0, 20.0], [5.0, 10.0]])
        )

    def test_positive_values_in_data(self):
        """Test that the resulting cube contains the expected data following
        vertical integration."""
        expected = np.array(
            [
                [[25.00, 25.00, 25.00], [25.00, 25.00, 25.00], [25.00, 25.00, 25.00]],
                [[45.00, 32.50, 32.50], [32.50, 32.50, 32.50], [32.50, 32.50, 32.50]],
            ]
        )
        result = self.plugin_negative.perform_integration(
            self.negative_upper_bounds_cube, self.negative_lower_bounds_cube
        )
        self.assertArrayAlmostEqual(
            result.coord("height").points, np.array([10.0, 5.0])
        )
        self.assertArrayAlmostEqual(result.data, expected)

    def test_zero_values_in_data(self):
        """Test that the resulting cube contains the expected data following
        vertical integration where some of the values in the data are equal
        to zero. This provides a baseline as the Integration plugin is
        currently restricted so that only positive values contribute towards
        the integral."""
        self.negative_upper_bounds_cube.data[0, 0, 0] = 0
        result = self.plugin_negative.perform_integration(
            self.negative_upper_bounds_cube, self.negative_lower_bounds_cube
        )
        self.assertArrayAlmostEqual(
            result.coord("height").points, np.array([10.0, 5.0])
        )
        self.assertArrayAlmostEqual(result.data, self.expected_data_zero_or_negative)

    def test_negative_and_positive_values_in_data(self):
        """Test that the resulting cube contains the expected data following
        vertical integration where some of the values in the data are negative.
        This shows that both zero values and negative values have no impact
        on the integration as the Integration plugin is currently
        restricted so that only positive values contribute towards the
        integral."""
        self.negative_upper_bounds_cube.data[0, 0, 0] = -1
        result = self.plugin_negative.perform_integration(
            self.negative_upper_bounds_cube, self.negative_lower_bounds_cube
        )
        self.assertArrayAlmostEqual(
            result.coord("height").points, np.array([10.0, 5.0])
        )
        self.assertArrayAlmostEqual(result.data, self.expected_data_zero_or_negative)

    def test_start_point_positive_direction(self):
        """Test that the resulting cube contains the expected data when a
        start_point is specified, so that only part of the column is
        integrated. For integration in the positive direction (equivalent to
        integrating downwards for the height coordinate in the input cube),
        the presence of a start_point indicates that the integration may start
        above the lowest height within the column to be integrated."""
        expected = np.array(
            [[[25.00, 25.00, 25.00], [25.00, 25.00, 25.00], [25.00, 25.00, 25.00]]]
        )
        self.plugin_positive.start_point = 8.0
        result = self.plugin_positive.perform_integration(
            self.positive_upper_bounds_cube, self.positive_lower_bounds_cube
        )
        self.assertArrayAlmostEqual(result.coord("height").points, np.array([20.0]))
        self.assertArrayAlmostEqual(result.data, expected)

    def test_start_point_negative_direction(self):
        """Test that the resulting cube contains the expected data when a
        start_point is specified, so that only part of the column is
        integrated. For integration in the negative direction (equivalent to
        integrating downwards for the height coordinate in the input cube),
        the presence of a start_point indicates that the integration may start
        below the highest height within the column to be integrated."""
        expected = np.array(
            [[[20.00, 7.50, 7.50], [7.50, 7.50, 7.50], [7.50, 7.50, 7.50]]]
        )
        self.plugin_negative.start_point = 18.0
        result = self.plugin_negative.perform_integration(
            self.negative_upper_bounds_cube, self.negative_lower_bounds_cube
        )
        self.assertArrayAlmostEqual(result.coord("height").points, np.array([5.0]))
        self.assertArrayAlmostEqual(result.data, expected)

    def test_end_point_positive_direction(self):
        """Test that the resulting cube contains the expected data when a
        end_point is specified, so that only part of the column is
        integrated. For integration in the positive direction (equivalent to
        integrating downwards for the height coordinate in the input cube),
        the presence of an end_point indicates that the integration may end
        below the highest height within the column to be integrated."""
        expected = np.array(
            [[[20.00, 7.50, 7.50], [7.50, 7.50, 7.50], [7.50, 7.50, 7.50]]]
        )
        self.plugin_positive.end_point = 18.0
        result = self.plugin_positive.perform_integration(
            self.positive_upper_bounds_cube, self.positive_lower_bounds_cube
        )
        self.assertArrayAlmostEqual(result.coord("height").points, np.array([10.0]))
        self.assertArrayAlmostEqual(result.data, expected)

    def test_end_point_negative_direction(self):
        """Test that the resulting cube contains the expected data when a
        end_point is specified, so that only part of the column is
        integrated. For integration in the negative direction (equivalent to
        integrating downwards for the height coordinate in the input cube),
        the presence of an end_point indicates that the integration may end
        above the lowest height within the column to be integrated."""
        expected = np.array(
            [[[25.00, 25.00, 25.00], [25.00, 25.00, 25.00], [25.00, 25.00, 25.00]]]
        )
        self.plugin_negative.end_point = 8.0
        result = self.plugin_negative.perform_integration(
            self.negative_upper_bounds_cube, self.negative_lower_bounds_cube
        )
        self.assertArrayAlmostEqual(result.coord("height").points, np.array([10.0]))
        self.assertArrayAlmostEqual(result.data, expected)

    def test_start_point_at_bound_positive_direction(self):
        """Test that the resulting cube contains the expected data when a
        start_point is specified, so that only part of the column is
        integrated. In this instance, the start_point of 10 is equal to the
        available height levels [5., 10., 20.]. If the start_point is greater
        than a height level then integration will start from the next layer
        in the vertical. In this example, the start_point is equal to a height
        level, so the layer above the start_point is included within the
        integration.

        For integration in the positive direction (equivalent to
        integrating downwards for the height coordinate in the input cube),
        the presence of a start_point indicates that the integration may start
        above the lowest height within the column to be integrated."""
        expected = np.array(
            [[[25.00, 25.00, 25.00], [25.00, 25.00, 25.00], [25.00, 25.00, 25.00]]]
        )
        self.plugin_positive.start_point = 10.0
        result = self.plugin_positive.perform_integration(
            self.positive_upper_bounds_cube, self.positive_lower_bounds_cube
        )
        self.assertArrayAlmostEqual(result.coord("height").points, np.array([20.0]))
        self.assertArrayAlmostEqual(result.data, expected)

    def test_end_point_at_bound_negative_direction(self):
        """Test that the resulting cube contains the expected data when a
        end_point is specified, so that only part of the column is
        integrated. In this instance, the end_point of 10 is equal to the
        available height levels [5., 10., 20.]. If the end_point is lower
        than a height level then integration will end. In this example,
        the end_point is equal to a height level, so the layer above the
        end_point is included within the integration.

        For integration in the negative direction (equivalent to
        integrating downwards for the height coordinate in the input cube),
        the presence of an end_point indicates that the integration may end
        above the lowest height within the column to be integrated."""
        expected = np.array(
            [[[25.00, 25.00, 25.00], [25.00, 25.00, 25.00], [25.00, 25.00, 25.00]]]
        )
        self.plugin_negative.end_point = 10.0
        result = self.plugin_negative.perform_integration(
            self.negative_upper_bounds_cube, self.negative_lower_bounds_cube
        )
        self.assertArrayAlmostEqual(result.coord("height").points, np.array([10.0]))
        self.assertArrayAlmostEqual(result.data, expected)

    def test_integration_not_performed(self):
        """Test that the expected exception is raise if no integration
        can be performed, for example if the selected levels are out of
        the dataset range."""
        self.plugin_positive.start_point = 25.0
        msg = "No integration could be performed for"
        with self.assertRaisesRegex(ValueError, msg):
            self.plugin_positive.perform_integration(
                self.positive_upper_bounds_cube, self.positive_lower_bounds_cube
            )
Exemplo n.º 25
0
class WetBulbTemperatureIntegral(BasePlugin):
    """Calculate a wet-bulb temperature integral."""
    def __init__(self,
                 coord_name_to_integrate="height",
                 start_point=None,
                 end_point=None,
                 direction_of_integration="negative"):
        """
        Initialise class.

        Args:
            coord_name_to_integrate (str):
                Name of the coordinate to be integrated.
            start_point (float or None):
                Point at which to start the integration.
                Default is None. If start_point is None, integration starts
                from the first available point.
            end_point (float or None):
                Point at which to end the integration.
                Default is None. If end_point is None, integration will
                continue until the last available point.
            direction_of_integration (str):
                Description of the direction in which to integrate.
                Options are 'positive' or 'negative'.
                'positive' corresponds to the values within the array
                increasing as the array index increases.
                'negative' corresponds to the values within the array
                decreasing as the array index increases.
        """
        self.integration_plugin = Integration(
            coord_name_to_integrate,
            start_point=start_point,
            end_point=end_point,
            direction_of_integration=direction_of_integration)
        self.coord_name_to_integrate = coord_name_to_integrate

    def __repr__(self):
        """Represent the configured plugin instance as a string."""
        result = ('<WetBulbTemperatureIntegral: {}>'.format(
            self.integration_plugin))
        return result

    def process(self, wet_bulb_temperature):
        """
        Calculate the vertical integal of wet bulb temperature from the input
        wet bulb temperatures on height levels.

        Args:
            wet_bulb_temperature (iris.cube.Cube):
                Cube of wet bulb temperatures of height levels.

        Returns:
            wet_bulb_temperature_integral (iris.cube.Cube):
                Cube of wet bulb temperature integral (Kelvin-metres).
        """
        # Touch the data to ensure it is not lazy
        # otherwise vertical interpolation is slow
        # pylint: disable=pointless-statement
        wet_bulb_temperature.data
        # Convert to Celsius
        wet_bulb_temperature.convert_units('celsius')
        # Integrate.
        wet_bulb_temperature_integral = (
            self.integration_plugin.process(wet_bulb_temperature))
        wet_bulb_temperature_integral.rename("wet_bulb_temperature_integral")
        units_string = "K {}".format(
            wet_bulb_temperature.coord(self.coord_name_to_integrate).units)
        wet_bulb_temperature_integral.units = Unit(units_string)
        return wet_bulb_temperature_integral
Exemplo n.º 26
0
class WetBulbTemperatureIntegral(object):
    """Calculate  a wet-bulb temperature integral."""
    def __init__(self,
                 precision=0.005,
                 coord_name_to_integrate="height",
                 start_point=None,
                 end_point=None,
                 direction_of_integration="negative"):
        """
        Initialise class.

        Keyword Args:
            precision (float):
                The precision to which the Newton iterator must converge
                before returning wet bulb temperatures.
            coord_name_to_integrate (string):
                Name of the coordinate to be integrated.
            start_point (float or None):
                Point at which to start the integration.
                Default is None. If start_point is None, integration starts
                from the first available point.
            end_point (float or None):
                Point at which to end the integration.
                Default is None. If end_point is None, integration will
                continue until the last available point.
            direction_of_integration (string):
                Description of the direction in which to integrate.
                Options are 'positive' or 'negative'.
                'positive' corresponds to the values within the array
                increasing as the array index increases.
                'negative' corresponds to the values within the array
                decreasing as the array index increases.
        """
        self.wet_bulb_temperature_plugin = (WetBulbTemperature(
            precision=precision))
        self.integration_plugin = Integration(
            coord_name_to_integrate,
            start_point=start_point,
            end_point=end_point,
            direction_of_integration=direction_of_integration)
        self.coord_name_to_integrate = coord_name_to_integrate

    def __repr__(self):
        """Represent the configured plugin instance as a string."""
        result = ('<WetBulbTemperatureIntegral: {}, {}>'.format(
            self.wet_bulb_temperature_plugin, self.integration_plugin))
        return result

    def process(self, temperature, relative_humidity, pressure):
        """
        Calculate the wet bulb temperature integral by firstly calculating
        the wet bulb temperature from the inputs provided, and then
        calculating the vertical integral of the wet bulb temperature.

        Args:
            temperature (iris.cube.Cube):
                Cube of air temperatures (K).
            relative_humidity (iris.cube.Cube):
                Cube of relative humidities (%, converted to fractional).
            pressure (iris.cube.Cube):
                Cube of air pressures (Pa).

        Returns:
            wet_bulb_temperature_integral (iris.cube.Cube):
                Cube of wet bulb temperature integral (Kelvin-metres).
        """
        # Calculate wet-bulb temperature.
        wet_bulb_temperature = (self.wet_bulb_temperature_plugin.process(
            temperature, relative_humidity, pressure))
        # Convert to Celsius
        wet_bulb_temperature.convert_units('celsius')
        # Integrate.
        wet_bulb_temperature_integral = (
            self.integration_plugin.process(wet_bulb_temperature))
        wet_bulb_temperature_integral.rename("wet_bulb_temperature_integral")
        units_string = "K {}".format(
            wet_bulb_temperature.coord(self.coord_name_to_integrate).units)
        wet_bulb_temperature_integral.units = Unit(units_string)
        return wet_bulb_temperature_integral
Exemplo n.º 27
0
 def __init__(self):
     """Initialise class."""
     self.integration_plugin = Integration("height")