Пример #1
0
    def test_basic(self):
        """Test we fill in the correct snow falling levels for a simple case"""
        plugin = PhaseChangeLevel(phase_change='snow-sleet')

        plugin.find_extrapolated_falling_level(
            self.max_wb_integral, self.gradients, self.intercepts,
            self.phase_change_level, self.sea_points)
        self.assertArrayAlmostEqual(self.expected_phase_change_level,
                                    self.phase_change_level)
Пример #2
0
 def test_interpolate_edge_case_2d_crater(self):
     """Test that we fill in missing areas under a nan crater edge case."""
     plugin = PhaseChangeLevel(phase_change="snow-sleet", grid_point_radius=1)
     result = plugin._horizontally_interpolate_phase(
         self.phase_change_data_2d_crater,
         self.orography_2d_crater,
         self.max_nbhood_orog_2d_crater,
     )
     self.assertArrayAlmostEqual(result, self.expected_result_2d_crater)
Пример #3
0
    def test_basic(self):
        """Test we find the correct gradient and intercepts for simple case"""
        plugin = PhaseChangeLevel(phase_change="snow-sleet")

        gradients, intercepts = plugin.linear_wet_bulb_fit(
            self.wet_bulb_temperature, self.heights, self.sea_points
        )
        self.assertArrayAlmostEqual(self.expected_gradients, gradients)
        self.assertArrayAlmostEqual(self.expected_intercepts, intercepts)
Пример #4
0
 def test_land_points(self):
     """Test it returns arrays of zeros if points are land."""
     plugin = PhaseChangeLevel(phase_change="snow-sleet")
     sea_points = np.ones((3, 3)) * False
     gradients, intercepts = plugin.linear_wet_bulb_fit(
         self.wet_bulb_temperature, self.heights, sea_points
     )
     self.assertArrayAlmostEqual(np.zeros((3, 3)), gradients)
     self.assertArrayAlmostEqual(np.zeros((3, 3)), intercepts)
Пример #5
0
 def test_outside_range(self):
     """Test method returns an nan if data outside range"""
     plugin = PhaseChangeLevel(phase_change="snow-sleet")
     wb_int_data = self.wb_int_data
     wb_int_data[2, 1, 1] = 70.0
     result = plugin.find_falling_level(
         wb_int_data, self.orog_data, self.height_points
     )
     self.assertTrue(np.isnan(result[1, 1]))
Пример #6
0
 def test_basic(self):
     """Test method returns an array with correct data"""
     plugin = PhaseChangeLevel(phase_change="snow-sleet")
     expected = np.array([[10.0, 7.5], [25.0, 20.5]])
     result = plugin.find_falling_level(
         self.wb_int_data, self.orog_data, self.height_points
     )
     self.assertIsInstance(result, np.ndarray)
     self.assertArrayEqual(result, expected)
Пример #7
0
 def test_error_lat_lon(self):
     """Test the function fails when radius is not zero and grid is lat-lon."""
     cube = self.cube_latlon
     plugin = PhaseChangeLevel(phase_change="snow-sleet", grid_point_radius=1)
     with self.assertRaisesRegex(
         ValueError,
         r"Unable to convert from 'Unit\('degrees'\)' to 'Unit\('metres'\)'.",
     ):
         plugin.find_max_in_nbhood_orography(cube)
Пример #8
0
 def test_null_lat_lon(self):
     """Test the function succeeds and does nothing when radius is zero and grid is
     lat-lon."""
     cube = self.cube_latlon
     plugin = PhaseChangeLevel(phase_change="snow-sleet",
                               grid_point_radius=0)
     expected_data = self.cube.data.copy()
     result = plugin.find_max_in_nbhood_orography(cube)
     self.assertArrayAlmostEqual(result.data, expected_data)
Пример #9
0
 def test_basic(self):
     """Test fills in missing data with orography + highest height"""
     plugin = PhaseChangeLevel(phase_change='snow-sleet')
     self.highest_wb_int[1, 1] = 100.0
     expected = np.array([[1.0, 1.0, 2.0], [1.0, 301.0, 2.0],
                          [1.0, 2.0, 2.0]])
     plugin.fill_in_high_phase_change_falling_levels(
         self.phase_change_level_data, self.orog, self.highest_wb_int,
         self.highest_height)
     self.assertArrayEqual(self.phase_change_level_data, expected)
Пример #10
0
 def test_no_fill_if_conditions_not_met(self):
     """Test it doesn't fill in NaN if the heighest wet bulb integral value
        is less than the threshold."""
     plugin = PhaseChangeLevel(phase_change='snow-sleet')
     expected = np.array([[1.0, 1.0, 2.0], [1.0, np.nan, 2.0],
                          [1.0, 2.0, 2.0]])
     plugin.fill_in_high_phase_change_falling_levels(
         self.phase_change_level_data, self.orog, self.highest_wb_int,
         self.highest_height)
     self.assertArrayEqual(self.phase_change_level_data, expected)
Пример #11
0
 def test_gradients_zero(self):
     """Test we do nothing if all gradients are zero"""
     plugin = PhaseChangeLevel(phase_change='snow-sleet')
     gradients = np.zeros((3, 3))
     plugin.find_extrapolated_falling_level(
         self.max_wb_integral, gradients, self.intercepts,
         self.phase_change_level, self.sea_points)
     expected_phase_change_level = np.ones((3, 3))*np.nan
     self.assertArrayAlmostEqual(expected_phase_change_level,
                                 self.phase_change_level)
Пример #12
0
 def test_interpolate_edge_case_2d_crater_grid_point_radius_2(self):
     """Test filling in missing areas under a radius 2 nan crater edge case."""
     plugin = PhaseChangeLevel(phase_change="snow-sleet", grid_point_radius=2)
     max_nbhood_orog = np.full(
         (9, 9), 900.0
     )  # Determined from the grid point radius increase.
     max_nbhood_orog[4, 4] = 600.0
     result = plugin._horizontally_interpolate_phase(
         self.phase_change_data_2d_crater, self.orography_2d_crater, max_nbhood_orog
     )
     self.assertArrayAlmostEqual(result, self.expected_result_2d_crater)
Пример #13
0
 def test_all_above_threshold(self):
     """Test it doesn't change points that are all above the threshold"""
     plugin = PhaseChangeLevel(phase_change='snow-sleet')
     self.max_wb_integral[0, 1] = 100
     self.phase_change_level[0, 1] = 100
     self.expected_phase_change_level[0, 1] = 100
     plugin.fill_in_sea_points(self.phase_change_level, self.land_sea,
                               self.max_wb_integral,
                               self.wet_bulb_temperature, self.heights)
     self.assertArrayAlmostEqual(self.phase_change_level.data,
                                 self.expected_phase_change_level)
Пример #14
0
 def setUp(self):
     """ Set up arrays for testing."""
     self.phase_change_level_data = np.array([[1.0, 1.0, 2.0],
                                              [1.0, np.nan, 2.0],
                                              [1.0, 2.0, 2.0]])
     self.orog_data = np.array([[6.0, 6.0, 6.0],
                                [6.0, 7.0, 6.0],
                                [6.0, 6.0, 6.0]])
     self.max_in_nbhood_orog = np.array([[7.0, 7.0, 7.0],
                                         [7.0, 7.0, 7.0],
                                         [7.0, 7.0, 7.0]])
     self.plugin = PhaseChangeLevel(phase_change='snow-sleet')
Пример #15
0
 def test_interpolate_edge_case_2d_nan_peak(self):
     """Test that we fill in missing areas under a nan-peaked edge case."""
     plugin = PhaseChangeLevel(phase_change="snow-sleet", grid_point_radius=1)
     phase_change_data = self.phase_change_data_2d.copy()
     phase_change_data[2][2] = np.nan  # Peak is also nan.
     result = plugin._horizontally_interpolate_phase(
         phase_change_data, self.orography_2d, self.max_nbhood_orog_2d
     )
     expected_result = self.expected_result_2d.copy()
     expected_result[2][2] = self.orography_2d[2][2]
     expected_result[2][3] = self.orography_2d[2][3]
     self.assertArrayAlmostEqual(result, expected_result)
 def test_basic(self):
     """Test it fills in the points it's meant to."""
     plugin = PhaseChangeLevel(phase_change="snow-sleet")
     plugin.fill_in_sea_points(
         self.phase_change_level,
         self.land_sea,
         self.max_wb_integral,
         self.wet_bulb_temperature,
         self.heights,
     )
     self.assertArrayAlmostEqual(self.phase_change_level.data,
                                 self.expected_phase_change_level)
Пример #17
0
 def test_snow_sleet_phase_change_reorder_cubes(self):
     """Same test as test_snow_sleet_phase_change but the cubes are in a
     different order"""
     self.orog.data[1, 1] = 100.0
     result = PhaseChangeLevel(phase_change='snow-sleet').process(
         CubeList([
             self.wet_bulb_integral_cube, self.wet_bulb_temperature_cube,
             self.orog, self.land_sea
         ]))
     self.assertIsInstance(result, iris.cube.Cube)
     self.assertEqual(result.name(), "altitude_of_snow_falling_level")
     self.assertEqual(result.units, Unit('m'))
     self.assertArrayAlmostEqual(result.data, self.expected_snow_sleet)
 def test_no_sea(self):
     """Test it only fills in sea points, and ignores a land point"""
     plugin = PhaseChangeLevel(phase_change="snow-sleet")
     expected = np.ones((3, 3)) * np.nan
     land_sea = np.ones((3, 3))
     plugin.fill_in_sea_points(
         self.phase_change_level,
         land_sea,
         self.max_wb_integral,
         self.wet_bulb_temperature,
         self.heights,
     )
     self.assertArrayAlmostEqual(self.phase_change_level.data, expected)
Пример #19
0
 def test_snow_sleet_phase_change(self):
     """Test that process returns a cube with the right name, units and
     values. In this instance the phase change is from snow to sleet. The
     returned level is consistent across the field, despite a high point
     that sits above the snow falling level."""
     self.orog.data[1, 1] = 100.0
     result = PhaseChangeLevel(phase_change='snow-sleet').process(
         self.wet_bulb_temperature_cube, self.wet_bulb_integral_cube,
         self.orog, self.land_sea)
     expected = np.ones((2, 3, 3), dtype=np.float32) * 66.88566
     self.assertIsInstance(result, iris.cube.Cube)
     self.assertEqual(result.name(), "altitude_of_snow_falling_level")
     self.assertEqual(result.units, Unit('m'))
     self.assertArrayAlmostEqual(result.data, expected)
Пример #20
0
 def test_interpolation_from_sea_points(self):
     """Test that the phase change level process returns a cube
     containing the expected data. In this case there is a single
     non-sea-level point in the orography. The snow falling level is below
     the surface of the sea, so for the single high point falling level is
     interpolated from the surrounding sea-level points."""
     orog = self.orog
     orog.data = np.zeros_like(orog.data)
     orog.data[2, 2] = 100.0
     land_sea = self.land_sea
     land_sea.data[1, 1] = 1
     result = PhaseChangeLevel(
         phase_change="snow-sleet", grid_point_radius=1
     ).process(
         CubeList(
             [
                 self.wet_bulb_temperature_cube,
                 self.wet_bulb_integral_cube,
                 orog,
                 land_sea,
             ]
         )
     )
     expected = self.expected_snow_sleet - 1
     expected[:, 2, 2] += 1
     self.assertIsInstance(result, iris.cube.Cube)
     self.assertArrayAlmostEqual(result.data, expected)
 def test_basic(self):
     """Test that the __repr__ returns the expected string."""
     result = str(PhaseChangeLevel(phase_change="snow-sleet"))
     msg = ("<PhaseChangeLevel: "
            "falling_level_threshold:90.0,"
            " grid_point_radius: 2>")
     self.assertEqual(result, msg)
Пример #22
0
def process(
    *cubes: cli.inputcube_nolazy,
    phase_change,
    grid_point_radius=2,
    horizontal_interpolation=True,
):
    """Height of precipitation phase change relative to sea level.

    Calculated as a continuous 2D field by finding the height above sea level
    at which the integral of wet bulb temperature matches an empirical
    threshold that is expected to correspond with the phase change.

    Args:
        cubes (iris.cube.CubeList or list of iris.cube.Cube):
            containing:
                wet_bulb_temperature (iris.cube.Cube):
                    Cube of wet bulb temperatures on height levels.
                wet_bulb_integral (iris.cube.Cube):
                    Cube of wet bulb temperature integrals calculated
                    vertically downwards to height levels.
                orography (iris.cube.Cube):
                    Cube of the orography height in m.
                land_sea_mask (iris.cube.Cube):
                    Cube containing the binary land-sea mask. Land points are
                    set to 1, sea points are set to 0.
        phase_change (str):
            The desired phase change for which the altitude should be
            returned. Options are:

                snow-sleet - the melting of snow to sleet.
                sleet-rain - the melting of sleet to rain.
                hail-rain - the melting of hail to rain.
        grid_point_radius (int):
            The radius in grid points used to calculate the maximum
            height of the orography in a neighbourhood to determine points that
            should be excluded from interpolation for being too close to the
            orographic feature where high-resolution models can give highly
            localised results. Zero uses central point only (neighbourhood is disabled).
            One uses central point and one in each direction. Two goes two points etc.

        horizontal_interpolation (bool):
            If True apply horizontal interpolation to fill in holes in
            the returned phase-change-level that occur because the level
            falls below the orography. If False these areas will be masked.

    Returns:
        iris.cube.Cube:
            Processed Cube of phase change altitude relative to sea level.
    """
    from improver.psychrometric_calculations.psychrometric_calculations import (
        PhaseChangeLevel, )

    plugin = PhaseChangeLevel(
        phase_change=phase_change,
        grid_point_radius=grid_point_radius,
        horizontal_interpolation=horizontal_interpolation,
    )
    result = plugin(cubes)
    return result
Пример #23
0
 def test_snow_sleet_phase_change(self):
     """Test that process returns a cube with the right name, units and
     values. In this instance the phase change is from snow to sleet. The
     returned level has three values, all above orography."""
     result = PhaseChangeLevel(phase_change="snow-sleet").process(
         CubeList([
             self.wet_bulb_temperature_cube,
             self.wet_bulb_integral_cube,
             self.orog,
             self.land_sea,
         ]))
     self.assertIsInstance(result, iris.cube.Cube)
     self.assertEqual(result.name(), "altitude_of_snow_falling_level")
     self.assertEqual(result.units, Unit("m"))
     self.assertArrayAlmostEqual(result.data, self.expected_snow_sleet)
     if hasattr(result.data, "mask"):
         self.assertFalse(result.data.mask.any())
Пример #24
0
 def test_sleet_rain_phase_change(self):
     """Test that process returns a cube with the right name, units and
     values. In this instance the phase change is from sleet to rain. Note
     that the wet bulb temperature integral values are doubled such that the
     rain threshold is reached above the surface. The returned level is
     consistent across the field, despite a high point that sits above the
     rain falling level."""
     self.orog.data[1, 1] = 100.0
     self.wet_bulb_integral_cube = self.wet_bulb_integral_cube * 2.
     result = PhaseChangeLevel(phase_change='sleet-rain').process(
         self.wet_bulb_temperature_cube, self.wet_bulb_integral_cube,
         self.orog, self.land_sea)
     expected = np.ones((2, 3, 3), dtype=np.float32) * 49.178673
     self.assertIsInstance(result, iris.cube.Cube)
     self.assertEqual(result.name(), "altitude_of_rain_falling_level")
     self.assertEqual(result.units, Unit('m'))
     self.assertArrayAlmostEqual(result.data, expected)
    def test_unknown_phase_change(self):
        """Test that the __init__ method raised an exception for an unknown
        phase change argument."""

        phase_change = "kittens-puppies"
        msg = ("Unknown phase change 'kittens-puppies' requested.\n"
               "Available options are: snow-sleet, sleet-rain")

        with self.assertRaisesRegex(ValueError, msg):
            PhaseChangeLevel(phase_change)
    def test_sleet_rain(self):
        """Test that the __init__ method configures the plugin as expected
        for the sleet_rain phase change."""

        phase_change = "sleet-rain"
        plugin = PhaseChangeLevel(phase_change, grid_point_radius=3)

        self.assertEqual(plugin.falling_level_threshold, 202.5)
        self.assertEqual(plugin.phase_change_name, "rain_falling")
        self.assertEqual(plugin.grid_point_radius, 3)
    def test_snow_sleet(self):
        """Test that the __init__ method configures the plugin as expected
        for the snow-sleet phase change."""

        phase_change = "snow-sleet"
        plugin = PhaseChangeLevel(phase_change, grid_point_radius=3)

        self.assertEqual(plugin.falling_level_threshold, 90.0)
        self.assertEqual(plugin.phase_change_name, "snow_falling")
        self.assertEqual(plugin.grid_point_radius, 3)
Пример #28
0
 def test_too_many_cubes(self):
     """Tests that an error is raised if there are too many cubes."""
     msg = "Expected 4"
     with self.assertRaisesRegex(ValueError, msg):
         PhaseChangeLevel(phase_change='snow-sleet').process(
             CubeList([
                 self.wet_bulb_temperature_cube,
                 self.wet_bulb_integral_cube, self.orog, self.land_sea,
                 self.orog
             ]))
Пример #29
0
 def test_inverted_input_cube(self):
     """Test that the phase change level process returns a cube
     containing the expected data when the height coordinate is in
     ascending order rather than the expected descending order."""
     self.orog.data[1, 1] = 100.0
     result = PhaseChangeLevel(phase_change='snow-sleet').process(
         CubeList([
             self.wet_bulb_temperature_cube, self.wet_bulb_integral_cube,
             self.orog, self.land_sea
         ]))
     self.assertArrayAlmostEqual(result.data, self.expected_snow_sleet)
Пример #30
0
 def test_inverted_input_cube(self):
     """Test that the phase change level process returns a cube
     containing the expected data when the height coordinate is in
     ascending order rather than the expected descending order."""
     self.orog.data[1, 1] = 100.0
     result = PhaseChangeLevel(phase_change='snow-sleet').process(
         self.wet_bulb_temperature_cube,
         self.wet_bulb_integral_cube_inverted,
         self.orog, self.land_sea)
     expected = np.ones((2, 3, 3), dtype=np.float32) * 66.88566
     self.assertArrayAlmostEqual(result.data, expected)