def setUp(self):
        """Set up and populate a plugin instance"""
        self.plugin = OrographicEnhancement()
        x_coord = DimCoord(np.arange(5), "projection_x_coordinate", units="km")
        y_coord = DimCoord(np.arange(5), "projection_y_coordinate", units="km")

        # this is neighbourhood-processed as part of mask generation
        topography_data = np.array([
            [0.0, 10.0, 20.0, 50.0, 100.0],
            [10.0, 20.0, 50.0, 100.0, 200.0],
            [25.0, 60.0, 80.0, 160.0, 220.0],
            [50.0, 80.0, 100.0, 200.0, 250.0],
            [50.0, 80.0, 100.0, 200.0, 250.0],
        ])
        self.plugin.topography = iris.cube.Cube(
            topography_data,
            long_name="topography",
            units="m",
            dim_coords_and_dims=[(y_coord, 0), (x_coord, 1)],
        )

        humidity_data = np.full((5, 5), 0.9)
        humidity_data[1, 3] = 0.5
        self.plugin.humidity = iris.cube.Cube(
            humidity_data,
            long_name="relhumidity",
            units="1",
            dim_coords_and_dims=[(y_coord, 0), (x_coord, 1)],
        )

        self.plugin.vgradz = np.full((5, 5), 0.01)
        self.plugin.vgradz[3:, :] = 0.0
Example #2
0
    def setUp(self):
        """Set up and populate a plugin instance"""
        x_coord = DimCoord(np.arange(3), 'projection_x_coordinate',
                           units='km')
        y_coord = DimCoord(np.arange(3), 'projection_y_coordinate',
                           units='km')

        temperature = np.array([[277.1, 278.2, 277.7],
                                [278.6, 278.4, 278.9],
                                [278.9, 279.0, 279.6]])
        humidity = np.array([[0.74, 0.85, 0.94],
                             [0.81, 0.82, 0.91],
                             [0.86, 0.93, 0.97]])

        self.plugin = OrographicEnhancement()
        self.plugin.temperature = iris.cube.Cube(
            temperature, long_name="temperature", units="kelvin",
            dim_coords_and_dims=[(y_coord, 0), (x_coord, 1)])
        self.plugin.humidity = iris.cube.Cube(
            humidity, long_name="relhumidity", units="1",
            dim_coords_and_dims=[(y_coord, 0), (x_coord, 1)])
        self.plugin.svp = np.array([[813.6, 878.0, 848.3],
                                    [903.2, 890.5, 922.2],
                                    [922.1, 928.4, 967.9]])

        self.plugin.vgradz = np.array([[0.02, 0.08, 0.2],
                                       [-0.06, 0.12, 0.22],
                                       [0.08, 0.16, 0.23]])

        topography_data = np.full((3, 3), 50., dtype=np.float32)
        self.plugin.topography = iris.cube.Cube(
            topography_data, long_name="topography", units="m",
            dim_coords_and_dims=[(y_coord, 0), (x_coord, 1)])
    def setUp(self):
        """Set up a plugin with wind components"""
        x_coord = DimCoord(3.0 * np.arange(5),
                           "projection_x_coordinate",
                           units="km")
        y_coord = DimCoord(3.0 * np.arange(5),
                           "projection_y_coordinate",
                           units="km")
        uwind = np.full((5, 5), 20.0, dtype=np.float32)
        vwind = np.full((5, 5), 12.0, dtype=np.float32)

        self.plugin = OrographicEnhancement()
        self.plugin.uwind = iris.cube.Cube(
            uwind,
            long_name="grid_eastward_wind",
            units="m s-1",
            dim_coords_and_dims=[(y_coord, 0), (x_coord, 1)],
        )
        self.plugin.vwind = iris.cube.Cube(
            vwind,
            long_name="grid_northward_wind",
            units="m s-1",
            dim_coords_and_dims=[(y_coord, 0), (x_coord, 1)],
        )
        self.plugin.grid_spacing_km = 3.0

        self.point_orogenh = np.array([
            [4.1, 4.6, 5.6, 6.8, 5.5],
            [4.4, 4.6, 5.8, 6.2, 5.5],
            [5.2, 3.0, 3.4, 5.1, 3.3],
            [0.6, 2.0, 1.8, 4.2, 2.5],
            [0.0, 0.0, 0.2, 3.2, 1.8],
        ])
class Test__locate_source_points(IrisTest):
    """Test the _locate_source_points method"""
    def setUp(self):
        """Define input matrices and plugin"""
        self.wind_speed = np.ones((3, 4), dtype=np.float32)
        self.sin_wind_dir = np.full((3, 4), 0.4, dtype=np.float32)
        self.cos_wind_dir = np.full((3, 4), np.sqrt(0.84), dtype=np.float32)
        self.plugin = OrographicEnhancement()
        self.plugin.grid_spacing_km = 3.0

    def test_basic(self):
        """Test location of source points"""
        distance = self.plugin._get_point_distances(self.wind_speed,
                                                    self.cos_wind_dir)
        xsrc, ysrc = self.plugin._locate_source_points(self.wind_speed,
                                                       distance,
                                                       self.sin_wind_dir,
                                                       self.cos_wind_dir)

        expected_xsrc = np.array([
            [[0, 1, 2, 3], [0, 1, 2, 3], [0, 1, 2, 3]],
            [[0, 1, 2, 3], [0, 1, 2, 3], [0, 1, 2, 3]],
            [[0, 0, 1, 2], [0, 0, 1, 2], [0, 0, 1, 2]],
            [[0, 0, 1, 2], [0, 0, 1, 2], [0, 0, 1, 2]],
        ])

        expected_ysrc = np.array([
            [[0, 0, 0, 0], [1, 1, 1, 1], [2, 2, 2, 2]],
            [[0, 0, 0, 0], [0, 0, 0, 0], [1, 1, 1, 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]],
        ])

        self.assertArrayEqual(xsrc, expected_xsrc)
        self.assertArrayEqual(ysrc, expected_ysrc)
 def setUp(self):
     """Define input matrices and plugin"""
     self.wind_speed = np.ones((3, 4), dtype=np.float32)
     self.sin_wind_dir = np.full((3, 4), 0.4, dtype=np.float32)
     self.cos_wind_dir = np.full((3, 4), np.sqrt(0.84), dtype=np.float32)
     self.plugin = OrographicEnhancement()
     self.plugin.grid_spacing_km = 3.0
Example #6
0
 def setUp(self):
     """Define input matrices and plugin"""
     self.wind_speed = np.ones((3, 4), dtype=np.float32)
     sin_wind_dir = np.linspace(0, 1, 12).reshape(3, 4)
     cos_wind_dir = np.sqrt(1. - np.square(sin_wind_dir))
     self.max_sin_cos = np.where(abs(sin_wind_dir) > abs(cos_wind_dir),
                                 abs(sin_wind_dir), abs(cos_wind_dir))
     self.plugin = OrographicEnhancement()
     self.plugin.grid_spacing_km = 3.
class Test__point_orogenh(IrisTest):
    """Test the _point_orogenh method"""
    def setUp(self):
        """Set up and populate a plugin instance"""
        x_coord = DimCoord(np.arange(3), 'projection_x_coordinate', units='km')
        y_coord = DimCoord(np.arange(3), 'projection_y_coordinate', units='km')

        temperature = np.array([[277.1, 278.2, 277.7], [278.6, 278.4, 278.9],
                                [278.9, 279.0, 279.6]])
        humidity = np.array([[0.74, 0.85, 0.94], [0.81, 0.82, 0.91],
                             [0.86, 0.93, 0.97]])
        svp = np.array([[813.6, 878.0, 848.3], [903.2, 890.5, 922.2],
                        [922.1, 928.4, 967.9]])

        self.plugin = OrographicEnhancement()
        self.plugin.temperature = iris.cube.Cube(temperature,
                                                 long_name="temperature",
                                                 units="kelvin",
                                                 dim_coords_and_dims=[
                                                     (y_coord, 0), (x_coord, 1)
                                                 ])
        self.plugin.humidity = iris.cube.Cube(humidity,
                                              long_name="relhumidity",
                                              units="1",
                                              dim_coords_and_dims=[
                                                  (y_coord, 0), (x_coord, 1)
                                              ])
        self.plugin.svp = iris.cube.Cube(
            svp,
            long_name="saturation_vapour_pressure",
            units="Pa",
            dim_coords_and_dims=[(y_coord, 0), (x_coord, 1)])

        self.plugin.vgradz = np.array([[0.02, 0.08, 0.2], [-0.06, 0.12, 0.22],
                                       [0.08, 0.16, 0.23]])

        topography_data = np.full((3, 3), 50., dtype=np.float32)
        self.plugin.topography = iris.cube.Cube(topography_data,
                                                long_name="topography",
                                                units="m",
                                                dim_coords_and_dims=[
                                                    (y_coord, 0), (x_coord, 1)
                                                ])

    def test_basic(self):
        """Test output is an array"""
        result = self.plugin._point_orogenh()
        self.assertIsInstance(result, np.ndarray)

    def test_values(self):
        """Test output values are as expected"""
        expected_values = np.array([[0., 1.67372072, 4.47886658],
                                    [0., 2.45468903, 5.1627059],
                                    [1.77400422, 3.86162901, 6.02323198]])
        result = self.plugin._point_orogenh()
        self.assertArrayAlmostEqual(result, expected_values)
class Test__add_upstream_component(IrisTest):
    """Test the _add_upstream_component method"""
    def setUp(self):
        """Set up a plugin with wind components"""
        x_coord = DimCoord(3.0 * np.arange(5),
                           "projection_x_coordinate",
                           units="km")
        y_coord = DimCoord(3.0 * np.arange(5),
                           "projection_y_coordinate",
                           units="km")
        uwind = np.full((5, 5), 20.0, dtype=np.float32)
        vwind = np.full((5, 5), 12.0, dtype=np.float32)

        self.plugin = OrographicEnhancement()
        self.plugin.uwind = iris.cube.Cube(
            uwind,
            long_name="grid_eastward_wind",
            units="m s-1",
            dim_coords_and_dims=[(y_coord, 0), (x_coord, 1)],
        )
        self.plugin.vwind = iris.cube.Cube(
            vwind,
            long_name="grid_northward_wind",
            units="m s-1",
            dim_coords_and_dims=[(y_coord, 0), (x_coord, 1)],
        )
        self.plugin.grid_spacing_km = 3.0

        self.point_orogenh = np.array([
            [4.1, 4.6, 5.6, 6.8, 5.5],
            [4.4, 4.6, 5.8, 6.2, 5.5],
            [5.2, 3.0, 3.4, 5.1, 3.3],
            [0.6, 2.0, 1.8, 4.2, 2.5],
            [0.0, 0.0, 0.2, 3.2, 1.8],
        ])

    def test_basic(self):
        """Test output is an array"""
        result = self.plugin._add_upstream_component(self.point_orogenh)
        self.assertIsInstance(result, np.ndarray)

    def test_values(self):
        """Test output values are sensible"""
        expected_values = np.array([
            [0.953865, 1.039876, 1.241070, 1.506976, 1.355637],
            [1.005472, 1.039876, 1.275474, 1.403762, 1.355637],
            [1.161275, 0.782825, 0.863303, 1.226206, 0.942638],
            [0.418468, 0.659300, 0.496544, 0.927728, 0.735382],
            [0.036423, 0.036423, 0.152506, 0.660092, 0.558801],
        ])

        result = self.plugin._add_upstream_component(self.point_orogenh)
        self.assertArrayAlmostEqual(result, expected_values)
 def setUp(self):
     """Set up input cubes"""
     temperature = np.arange(6).reshape(2, 3)
     self.temperature_cube = set_up_variable_cube(temperature)
     orography = np.array([[20., 30., 40., 30., 25., 25.],
                           [30., 50., 80., 60., 50., 45.],
                           [50., 65., 90., 70., 60., 50.],
                           [45., 60., 85., 65., 55., 45.]])
     orography_cube = set_up_orography_cube(orography)
     self.plugin = OrographicEnhancement()
     self.plugin.topography = sort_coord_in_cube(
         orography_cube, orography_cube.coord(axis='y'))
 def setUp(self):
     """Set up an input cube"""
     self.plugin = OrographicEnhancement()
     data = np.array([[200.0, 450.0, 850.0], [320.0, 500.0, 1000.0],
                      [230.0, 600.0, 900.0]])
     x_coord = DimCoord(np.arange(3), "projection_x_coordinate", units="km")
     y_coord = DimCoord(np.arange(3), "projection_y_coordinate", units="km")
     self.plugin.topography = iris.cube.Cube(
         data,
         long_name="topography",
         units="m",
         dim_coords_and_dims=[(y_coord, 0), (x_coord, 1)],
     )
class Test__generate_mask(IrisTest):
    """Test the _generate_mask method"""
    def setUp(self):
        """Set up and populate a plugin instance"""
        self.plugin = OrographicEnhancement()
        x_coord = DimCoord(np.arange(5), "projection_x_coordinate", units="km")
        y_coord = DimCoord(np.arange(5), "projection_y_coordinate", units="km")

        # this is neighbourhood-processed as part of mask generation
        topography_data = np.array([
            [0.0, 10.0, 20.0, 50.0, 100.0],
            [10.0, 20.0, 50.0, 100.0, 200.0],
            [25.0, 60.0, 80.0, 160.0, 220.0],
            [50.0, 80.0, 100.0, 200.0, 250.0],
            [50.0, 80.0, 100.0, 200.0, 250.0],
        ])
        self.plugin.topography = iris.cube.Cube(
            topography_data,
            long_name="topography",
            units="m",
            dim_coords_and_dims=[(y_coord, 0), (x_coord, 1)],
        )

        humidity_data = np.full((5, 5), 0.9)
        humidity_data[1, 3] = 0.5
        self.plugin.humidity = iris.cube.Cube(
            humidity_data,
            long_name="relhumidity",
            units="1",
            dim_coords_and_dims=[(y_coord, 0), (x_coord, 1)],
        )

        self.plugin.vgradz = np.full((5, 5), 0.01)
        self.plugin.vgradz[3:, :] = 0.0

    def test_basic(self):
        """Test output is array"""
        result = self.plugin._generate_mask()
        self.assertIsInstance(result, np.ndarray)

    def test_values(self):
        """Test output mask is correct"""
        expected_output = np.full((5, 5), False, dtype=bool)
        expected_output[0, :2] = True  # orography too low
        expected_output[1, 3] = True  # humidity too low
        expected_output[3:, :] = True  # vgradz too low
        result = self.plugin._generate_mask()
        self.assertArrayEqual(result, expected_output)
Example #12
0
def process(temperature, humidity, pressure, wind_speed, wind_dir, orography):
    """Calculate orograhpic enhancement

    Uses the ResolveWindComponents() and OrographicEnhancement() plugins.
    Outputs data on the high resolution orography grid.

    Args:
        temperature (iris.cube.Cube):
             Cube containing temperature at top of boundary layer.
        humidity (iris.cube.Cube):
            Cube containing relative humidity at top of boundary layer.
        pressure (iris.cube.Cube):
            Cube containing pressure at top of boundary layer.
        wind_speed (iris.cube.Cube):
            Cube containing wind speed values.
        wind_dir (iris.cube.Cube):
            Cube containing wind direction values relative to true north.
        orography (iris.cube.Cube):
            Cube containing height of orography above sea level on high
            resolution (1 km) UKPP domain grid.

    Returns:
        iris.cube.Cube:
            Precipitation enhancement due to orography on the high resolution
            input orography grid.
    """
    # resolve u and v wind components
    u_wind, v_wind = ResolveWindComponents().process(wind_speed, wind_dir)
    # calculate orographic enhancement
    return OrographicEnhancement().process(temperature, humidity, pressure,
                                           u_wind, v_wind, orography)
class Test__compute_weighted_values(IrisTest):
    """Test the _compute_weighted_values method"""
    def setUp(self):
        """Set up plugin and some inputs"""
        self.plugin = OrographicEnhancement()
        self.plugin.grid_spacing_km = 3.0

        self.point_orogenh = np.array([
            [4.1, 4.6, 5.6, 6.8, 5.5],
            [4.4, 4.6, 5.8, 6.2, 5.5],
            [5.2, 3.0, 3.4, 5.1, 3.3],
            [0.6, 2.0, 1.8, 4.2, 2.5],
            [0.0, 0.0, 0.2, 3.2, 1.8],
        ])

        self.wind_speed = np.full((5, 5), 25.0, dtype=np.float32)
        sin_wind_dir = np.full((5, 5), 0.4, dtype=np.float32)
        cos_wind_dir = np.full((5, 5), np.sqrt(0.84), dtype=np.float32)
        self.distance = self.plugin._get_point_distances(
            self.wind_speed, cos_wind_dir)
        self.xsrc, self.ysrc = self.plugin._locate_source_points(
            self.wind_speed, self.distance, sin_wind_dir, cos_wind_dir)

    def test_basic(self):
        """Test output is two arrays"""
        orogenh, weights = self.plugin._compute_weighted_values(
            self.point_orogenh, self.xsrc, self.ysrc, self.distance,
            self.wind_speed)
        self.assertIsInstance(orogenh, np.ndarray)
        self.assertIsInstance(weights, np.ndarray)

    def test_values(self):
        """Test values are as expected"""
        expected_orogenh = np.array([
            [6.0531969, 6.7725644, 8.2301264, 9.9942646, 8.1690931],
            [6.3531971, 6.7725644, 8.4301271, 9.3942642, 8.1690931],
            [7.2848172, 5.1725645, 6.1178742, 8.0310230, 5.9690924],
            [3.0469213, 3.4817038, 3.4649093, 6.6558237, 4.1816435],
            [0.4585612, 1.0727906, 1.1036499, 5.1721582, 3.0895371],
        ])
        expected_weights = np.full((5, 5), 1.4763895, dtype=np.float32)
        orogenh, weights = self.plugin._compute_weighted_values(
            self.point_orogenh, self.xsrc, self.ysrc, self.distance,
            self.wind_speed)
        self.assertArrayAlmostEqual(orogenh, expected_orogenh)
        self.assertArrayAlmostEqual(weights, expected_weights)
    def setUp(self):
        """Set up plugin and some inputs"""
        self.plugin = OrographicEnhancement()
        self.plugin.grid_spacing_km = 3.

        self.point_orogenh = np.array([[4.1, 4.6, 5.6, 6.8, 5.5],
                                       [4.4, 4.6, 5.8, 6.2, 5.5],
                                       [5.2, 3.0, 3.4, 5.1, 3.3],
                                       [0.6, 2.0, 1.8, 4.2, 2.5],
                                       [0.0, 0.0, 0.2, 3.2, 1.8]])

        self.wind_speed = np.full((5, 5), 25., dtype=np.float32)
        sin_wind_dir = np.full((5, 5), 0.4, dtype=np.float32)
        cos_wind_dir = np.full((5, 5), np.sqrt(0.84), dtype=np.float32)
        self.distance = self.plugin._get_point_distances(
            self.wind_speed, cos_wind_dir)
        self.xsrc, self.ysrc = self.plugin._locate_source_points(
            self.wind_speed, self.distance, sin_wind_dir, cos_wind_dir)
Example #15
0
def process(temperature: cli.inputcube,
            humidity: cli.inputcube,
            pressure: cli.inputcube,
            wind_speed: cli.inputcube,
            wind_direction: cli.inputcube,
            orography: cli.inputcube,
            *,
            boundary_height: float = 1000.0,
            boundary_height_units='m'):
    """Calculate orographic enhancement

    Uses the ResolveWindComponents() and OrographicEnhancement() plugins.
    Outputs data on the high resolution orography grid.

    Args:
        temperature (iris.cube.Cube):
             Cube containing temperature at top of boundary layer.
        humidity (iris.cube.Cube):
            Cube containing relative humidity at top of boundary layer.
        pressure (iris.cube.Cube):
            Cube containing pressure at top of boundary layer.
        wind_speed (iris.cube.Cube):
            Cube containing wind speed values.
        wind_direction (iris.cube.Cube):
            Cube containing wind direction values relative to true north.
        orography (iris.cube.Cube):
            Cube containing height of orography above sea level on high
            resolution (1 km) UKPP domain grid.
        boundary_height (float):
            Model height level to extract variables for calculating orographic
            enhancement, as proxy for the boundary layer.
        boundary_height_units (str):
            Units of the boundary height specified for extracting model levels.

    Returns:
        iris.cube.Cube:
            Precipitation enhancement due to orography on the high resolution
            input orography grid.
    """
    from improver.orographic_enhancement import OrographicEnhancement
    from improver.wind_calculations.wind_components import \
        ResolveWindComponents

    constraint_info = (boundary_height, boundary_height_units)

    temperature = extract_and_check(temperature, *constraint_info)
    humidity = extract_and_check(humidity, *constraint_info)
    pressure = extract_and_check(pressure, *constraint_info)
    wind_speed = extract_and_check(wind_speed, *constraint_info)
    wind_direction = extract_and_check(wind_direction, *constraint_info)

    # resolve u and v wind components
    u_wind, v_wind = ResolveWindComponents().process(wind_speed,
                                                     wind_direction)
    # calculate orographic enhancement
    return OrographicEnhancement().process(temperature, humidity, pressure,
                                           u_wind, v_wind, orography)
    def setUp(self):
        """Set up a plugin instance, data array and cubes"""
        self.plugin = OrographicEnhancement()
        topography = set_up_orography_cube(np.zeros((3, 4), dtype=np.float32))
        self.plugin.topography = sort_coord_in_cube(topography,
                                                    topography.coord(axis='y'))

        self.temperature = set_up_variable_cube(np.full((2, 4), 280.15),
                                                units='kelvin',
                                                xo=398000.)
        self.temperature.attributes['institution'] = 'Met Office'
        self.temperature.attributes['source'] = 'Met Office Unified Model'
        self.temperature.attributes['mosg__grid_type'] = 'standard'
        self.temperature.attributes['mosg__grid_version'] = '1.2.0'
        self.temperature.attributes['mosg__grid_domain'] = 'uk_extended'
        self.temperature.attributes['mosg__model_configuration'] = 'uk_det'

        self.orogenh = np.array([[1.1, 1.2, 1.5, 1.4], [1.0, 1.3, 1.4, 1.6],
                                 [0.8, 0.9, 1.2, 0.9]])
class Test__get_point_distances(IrisTest):
    """Test the _get_point_distances function"""
    def setUp(self):
        """Define input matrices and plugin"""
        self.wind_speed = np.ones((3, 4), dtype=np.float32)
        sin_wind_dir = np.linspace(0, 1, 12).reshape(3, 4)
        cos_wind_dir = np.sqrt(1.0 - np.square(sin_wind_dir))
        self.max_sin_cos = np.where(
            abs(sin_wind_dir) > abs(cos_wind_dir), abs(sin_wind_dir),
            abs(cos_wind_dir))
        self.plugin = OrographicEnhancement()
        self.plugin.grid_spacing_km = 3.0

    def test_basic(self):
        """Test the function returns an array of the expected shape"""
        distance = self.plugin._get_point_distances(self.wind_speed,
                                                    self.max_sin_cos)
        self.assertIsInstance(distance, np.ndarray)
        self.assertSequenceEqual(distance.shape, (5, 3, 4))

    def test_values_with_nans(self):
        """Test for expected values including nans"""
        slice_0 = np.zeros((3, 4), dtype=np.float32)
        slice_1 = np.array([
            [1.0, 1.00415802, 1.01695037, 1.03940225],
            [1.07349002, 1.12268281, 1.1931175, 1.2963624],
            [1.375, 1.22222221, 1.10000002, 1.0],
        ])
        slice_2 = 2.0 * slice_1
        slice_3 = 3.0 * slice_1
        slice_3[1, 3] = np.nan
        slice_3[2, 0] = np.nan
        slice_4 = np.full_like(slice_0, np.nan)
        slice_4[0, 0] = 4.0
        slice_4[-1, -1] = 4.0
        expected_data = np.array([slice_0, slice_1, slice_2, slice_3, slice_4])

        distance = self.plugin._get_point_distances(self.wind_speed,
                                                    self.max_sin_cos)
        np.testing.assert_allclose(distance, expected_data, equal_nan=True)
class Test__orography_gradients(IrisTest):
    """Test the _orography_gradients method"""
    def setUp(self):
        """Set up an input cube"""
        self.plugin = OrographicEnhancement()
        data = np.array([[200.0, 450.0, 850.0], [320.0, 500.0, 1000.0],
                         [230.0, 600.0, 900.0]])
        x_coord = DimCoord(np.arange(3), "projection_x_coordinate", units="km")
        y_coord = DimCoord(np.arange(3), "projection_y_coordinate", units="km")
        self.plugin.topography = iris.cube.Cube(
            data,
            long_name="topography",
            units="m",
            dim_coords_and_dims=[(y_coord, 0), (x_coord, 1)],
        )

    def test_basic(self):
        """Test outputs are cubes"""
        gradx, grady = self.plugin._orography_gradients()
        self.assertIsInstance(gradx, iris.cube.Cube)
        self.assertIsInstance(grady, iris.cube.Cube)

    def test_values(self):
        """Test output values and units"""
        expected_gradx = np.array([
            [0.12333333, 0.33, 0.53666667],
            [0.2, 0.33333333, 0.46666667],
            [0.27666667, 0.33666667, 0.39666667],
        ])

        expected_grady = np.array([
            [0.15833333, 0.175, 0.19166667],
            [0.035, 0.03833333, 0.04166667],
            [-0.08833333, -0.09833333, -0.10833333],
        ])
        gradx, grady = self.plugin._orography_gradients()
        self.assertArrayAlmostEqual(gradx.data, expected_gradx)
        self.assertArrayAlmostEqual(grady.data, expected_grady)
        for cube in [gradx, grady]:
            self.assertEqual(cube.units, "1")
Example #19
0
    def test_basic(self):
        """Test initialisation with no arguments"""
        plugin = OrographicEnhancement()
        self.assertAlmostEqual(plugin.orog_thresh_m, 20.)
        self.assertAlmostEqual(plugin.rh_thresh_ratio, 0.8)
        self.assertAlmostEqual(plugin.vgradz_thresh_ms, 0.0005)
        self.assertAlmostEqual(plugin.upstream_range_of_influence_km, 15.)
        self.assertAlmostEqual(plugin.efficiency_factor, 0.23265)
        self.assertAlmostEqual(plugin.cloud_lifetime_s, 102.)

        none_type_attributes = [
            'topography', 'temperature', 'humidity', 'pressure',
            'uwind', 'vwind', 'svp', 'vgradz', 'grid_spacing_km']
        for attr in none_type_attributes:
            self.assertIsNone(getattr(plugin, attr))
    def setUp(self):
        """Set up a plugin instance, data array and cubes"""
        self.plugin = OrographicEnhancement()
        topography = set_up_orography_cube(np.zeros((3, 4), dtype=np.float32))
        self.plugin.topography = sort_coord_in_cube(topography,
                                                    topography.coord(axis="y"))

        t_attributes = {
            "institution": "Met Office",
            "source": "Met Office Unified Model",
            "mosg__grid_type": "standard",
            "mosg__grid_version": "1.2.0",
            "mosg__grid_domain": "uk_extended",
            "mosg__model_configuration": "uk_det",
        }
        self.temperature = set_up_variable_cube(
            np.full((2, 4), 280.15, dtype=np.float32),
            units="kelvin",
            xo=398000.0,
            attributes=t_attributes,
        )

        self.orogenh = np.array([[1.1, 1.2, 1.5, 1.4], [1.0, 1.3, 1.4, 1.6],
                                 [0.8, 0.9, 1.2, 0.9]])
    def setUp(self):
        """Set up input cubes"""
        temperature = np.arange(6).reshape(2, 3)
        self.temperature = set_up_variable_cube(temperature)
        humidity = np.arange(0.75, 0.86, 0.02).reshape(2, 3)
        self.humidity = set_up_variable_cube(humidity, 'relhumidity', '1')
        pressure = np.arange(820, 921, 20).reshape(2, 3)
        self.pressure = set_up_variable_cube(pressure, 'pressure', 'hPa')
        uwind = np.full((2, 3), 20., dtype=np.float32)
        self.uwind = set_up_variable_cube(uwind, 'wind-u', 'knots')
        vwind = np.full((2, 3), 12., dtype=np.float32)
        self.vwind = set_up_variable_cube(vwind, 'wind-v', 'knots')

        orography = np.array([[20., 30., 40., 30., 25., 25.],
                              [30., 50., 80., 60., 50., 45.],
                              [50., 65., 90., 70., 60., 50.],
                              [45., 60., 85., 65., 55., 45.]])
        self.orography_cube = set_up_orography_cube(orography)
        self.plugin = OrographicEnhancement()
    def setUp(self):
        """Set up input cubes"""
        temperature = np.arange(6).reshape(2, 3)
        self.temperature = set_up_variable_cube(temperature)
        humidity = np.arange(0.75, 0.86, 0.02).reshape(2, 3)
        self.humidity = set_up_variable_cube(humidity, "relhumidity", "1")
        pressure = np.arange(820, 921, 20).reshape(2, 3)
        self.pressure = set_up_variable_cube(pressure, "pressure", "hPa")
        uwind = np.full((2, 3), 20.0, dtype=np.float32)
        self.uwind = set_up_variable_cube(uwind, "wind-u", "knots")
        vwind = np.full((2, 3), 12.0, dtype=np.float32)
        self.vwind = set_up_variable_cube(vwind, "wind-v", "knots")

        orography = np.array([
            [20.0, 30.0, 40.0, 30.0, 25.0, 25.0],
            [30.0, 50.0, 80.0, 60.0, 50.0, 45.0],
            [50.0, 65.0, 90.0, 70.0, 60.0, 50.0],
            [45.0, 60.0, 85.0, 65.0, 55.0, 45.0],
        ])
        self.orography_cube = set_up_orography_cube(orography)
        self.plugin = OrographicEnhancement()
def process(temperature, humidity, pressure, wind_speed, wind_dir, orography):
    """Calculate orograhpic enhancement

    Uses the ResolveWindComponents() and OrographicEnhancement() plugins.
    Outputs data on the high resolution orography grid and regrided to the
    coarser resolution of the input diagnostic variables.

    Args:
        temperature (iris.cube.Cube):
             Cube containing temperature at top of boundary layer.
        humidity (iris.cube.Cube):
            Cube containing relative humidity at top of boundary layer.
        pressure (iris.cube.Cube):
            Cube containing pressure at top of boundary layer.
        wind_speed (iris.cube.Cube):
            Cube containing wind speed values.
        wind_dir (iris.cube.Cube):
            Cube containing wind direction values relative to true north.
        orography (iris.cube.Cube):
            Cube containing height of orography above sea level on high
            resolution (1 km) UKPP domain grid.

    Returns:
        (tuple): tuple containing:
                **orogenh_high_res** (iris.cube.Cube):
                    Precipitation enhancement due to orography in mm/h on the
                    UK standard grid, padded with masked up np.nans where
                    outside the UKPP domain.
                **orogenh_standard** (iris.cube.Cube):
                    Precipitation enhancement due to orography in mm/h on
                    the 1km Transverse Mercator UKPP grid domain.
    """
    # resolve u and v wind components
    u_wind, v_wind = ResolveWindComponents().process(wind_speed, wind_dir)
    # calculate orographic enhancement
    orogenh_high_res, orogenh_standard = OrographicEnhancement().process(
        temperature, humidity, pressure, u_wind, v_wind, orography)
    return orogenh_high_res, orogenh_standard
Example #24
0
class Test__create_output_cubes(IrisTest):
    """Test the _create_output_cube method"""
    def setUp(self):
        """Set up a plugin instance, data array and cubes"""
        self.plugin = OrographicEnhancement()
        topography = set_up_orography_cube(np.zeros((3, 4), dtype=np.float32))
        self.plugin.topography = sort_coord_in_cube(topography,
                                                    topography.coord(axis='y'))

        self.temperature = set_up_variable_cube(np.full((2, 4), 280.15),
                                                units='kelvin',
                                                xo=398000.)
        self.temperature.attributes['institution'] = 'Met Office'
        self.temperature.attributes['source'] = 'Met Office Unified Model'
        self.temperature.attributes['mosg__grid_type'] = 'standard'
        self.temperature.attributes['mosg__grid_version'] = '1.2.0'
        self.temperature.attributes['mosg__grid_domain'] = 'uk_extended'
        self.temperature.attributes['mosg__model_configuration'] = 'uk_det'

        self.orogenh = np.array([[1.1, 1.2, 1.5, 1.4], [1.0, 1.3, 1.4, 1.6],
                                 [0.8, 0.9, 1.2, 0.9]])

    def test_basic(self):
        """Test that the cube is returned with float32 coords"""
        output = self.plugin._create_output_cube(self.orogenh,
                                                 self.temperature)

        self.assertIsInstance(output, iris.cube.Cube)
        for coord in output.coords(dim_coords=True):
            self.assertEqual(coord.points.dtype, 'float32')

    def test_values(self):
        """Test the cube is changed only in units (to m s-1)"""
        original_converted = 2.7777778e-07 * self.orogenh

        output = self.plugin._create_output_cube(self.orogenh,
                                                 self.temperature)
        self.assertArrayAlmostEqual(output.data, original_converted)

    def test_metadata(self):
        """Check output metadata on cube is as expected"""
        hi_res_attributes = self.temperature.attributes
        for key, val in self.plugin.topography.attributes.items():
            hi_res_attributes[key] = val

        output = self.plugin._create_output_cube(self.orogenh,
                                                 self.temperature)

        for axis in ['x', 'y']:
            self.assertEqual(output.coord(axis=axis),
                             self.plugin.topography.coord(axis=axis))

        self.assertEqual(output.name(), 'orographic_enhancement')
        self.assertEqual(output.units, 'm s-1')
        for t_coord in ['time', 'forecast_period', 'forecast_reference_time']:
            self.assertEqual(output.coord(t_coord),
                             self.temperature.coord(t_coord))
        self.assertDictEqual(output.attributes, hi_res_attributes)

    def test_grid_metadata(self):
        """Test specific grid and model metadata inheritance"""
        output = self.plugin._create_output_cube(self.orogenh,
                                                 self.temperature)

        for attr in [
                'mosg__grid_type', 'mosg__grid_version', 'mosg__grid_domain'
        ]:
            self.assertEqual(output.attributes[attr],
                             self.plugin.topography.attributes[attr])

        self.assertEqual(
            output.attributes['mosg__model_configuration'],
            self.temperature.attributes['mosg__model_configuration'])
class Test__create_output_cube(IrisTest):
    """Test the _create_output_cube method"""
    def setUp(self):
        """Set up a plugin instance, data array and cubes"""
        self.plugin = OrographicEnhancement()
        topography = set_up_orography_cube(np.zeros((3, 4), dtype=np.float32))
        self.plugin.topography = sort_coord_in_cube(topography,
                                                    topography.coord(axis="y"))

        t_attributes = {
            "institution": "Met Office",
            "source": "Met Office Unified Model",
            "mosg__grid_type": "standard",
            "mosg__grid_version": "1.2.0",
            "mosg__grid_domain": "uk_extended",
            "mosg__model_configuration": "uk_det",
        }
        self.temperature = set_up_variable_cube(
            np.full((2, 4), 280.15, dtype=np.float32),
            units="kelvin",
            xo=398000.0,
            attributes=t_attributes,
        )

        self.orogenh = np.array([[1.1, 1.2, 1.5, 1.4], [1.0, 1.3, 1.4, 1.6],
                                 [0.8, 0.9, 1.2, 0.9]])

    def test_basic(self):
        """Test that the cube is returned with float32 coords"""
        output = self.plugin._create_output_cube(self.orogenh,
                                                 self.temperature)

        self.assertIsInstance(output, iris.cube.Cube)
        for coord in output.coords(dim_coords=True):
            self.assertEqual(coord.points.dtype, "float32")

    def test_values(self):
        """Test the cube is changed only in units (to m s-1)"""
        original_converted = 2.7777778e-07 * self.orogenh

        output = self.plugin._create_output_cube(self.orogenh,
                                                 self.temperature)
        self.assertArrayAlmostEqual(output.data, original_converted)

    def test_metadata(self):
        """Check output metadata on cube is as expected"""
        expected_attributes = {
            "title": MANDATORY_ATTRIBUTE_DEFAULTS["title"],
            "source": self.temperature.attributes["source"],
            "institution": self.temperature.attributes["institution"],
        }
        for attr in MOSG_GRID_ATTRIBUTES:
            expected_attributes[attr] = self.plugin.topography.attributes[attr]

        output = self.plugin._create_output_cube(self.orogenh,
                                                 self.temperature)
        for axis in ["x", "y"]:
            self.assertEqual(output.coord(axis=axis),
                             self.plugin.topography.coord(axis=axis))

        self.assertEqual(output.name(), "orographic_enhancement")
        self.assertEqual(output.units, "m s-1")
        for t_coord in ["time", "forecast_period", "forecast_reference_time"]:
            self.assertEqual(output.coord(t_coord),
                             self.temperature.coord(t_coord))
        self.assertDictEqual(output.attributes, expected_attributes)
def main(argv=None):
    """Calculate orographic enhancement of precipitation from model pressure,
    temperature, relative humidity and wind input files"""

    parser = ArgParser(description='Calculate orographic enhancement using the'
                       ' ResolveWindComponents() and OrographicEnhancement() '
                       'plugins. Outputs data on the high resolution orography'
                       ' grid and regridded to the coarser resolution of the '
                       'input diagnostic variables.')

    parser.add_argument('temperature_filepath',
                        metavar='TEMPERATURE_FILEPATH',
                        help='Full path to input NetCDF file of temperature on'
                        ' height levels')
    parser.add_argument('humidity_filepath',
                        metavar='HUMIDITY_FILEPATH',
                        help='Full path to input NetCDF file of relative '
                        'humidity on height levels')
    parser.add_argument('pressure_filepath',
                        metavar='PRESSURE_FILEPATH',
                        help='Full path to input NetCDF file of pressure on '
                        'height levels')
    parser.add_argument('windspeed_filepath',
                        metavar='WINDSPEED_FILEPATH',
                        help='Full path to input NetCDF file of wind speed on '
                        'height levels')
    parser.add_argument('winddir_filepath',
                        metavar='WINDDIR_FILEPATH',
                        help='Full path to input NetCDF file of wind direction'
                        ' on height levels')
    parser.add_argument('orography_filepath',
                        metavar='OROGRAPHY_FILEPATH',
                        help='Full path to input NetCDF high resolution '
                        'orography ancillary. This should be on the same or a '
                        'finer resolution grid than the input variables, and '
                        'defines the grid on which the orographic enhancement '
                        'will be calculated.')
    parser.add_argument('output_dir',
                        metavar='OUTPUT_DIR',
                        help='Directory '
                        'to write output orographic enhancement files')
    parser.add_argument('--boundary_height',
                        type=float,
                        default=1000.,
                        help='Model height level to extract variables for '
                        'calculating orographic enhancement, as proxy for '
                        'the boundary layer.')
    parser.add_argument('--boundary_height_units',
                        type=str,
                        default='m',
                        help='Units of the boundary height specified for '
                        'extracting model levels.')

    args = parser.parse_args(args=argv)

    constraint_info = (args.boundary_height, args.boundary_height_units)

    temperature = load_and_extract(args.temperature_filepath, *constraint_info)
    humidity = load_and_extract(args.humidity_filepath, *constraint_info)
    pressure = load_and_extract(args.pressure_filepath, *constraint_info)
    wind_speed = load_and_extract(args.windspeed_filepath, *constraint_info)
    wind_dir = load_and_extract(args.winddir_filepath, *constraint_info)

    # resolve u and v wind components
    uwind, vwind = ResolveWindComponents().process(wind_speed, wind_dir)

    # load high resolution orography
    orography = load_cube(args.orography_filepath)

    # calculate orographic enhancement
    orogenh_high_res, orogenh_standard = OrographicEnhancement().process(
        temperature, humidity, pressure, uwind, vwind, orography)

    # generate file names
    fname_standard = os.path.join(args.output_dir,
                                  generate_file_name(orogenh_standard))
    fname_high_res = os.path.join(
        args.output_dir,
        generate_file_name(orogenh_high_res,
                           parameter="orographic_enhancement_high_resolution"))

    # save output files
    save_netcdf(orogenh_standard, fname_standard)
    save_netcdf(orogenh_high_res, fname_high_res)
class Test__create_output_cubes(IrisTest):
    """Test the _create_output_cubes method"""
    def setUp(self):
        """Set up a plugin instance, data array and cubes"""
        self.plugin = OrographicEnhancement()
        topography = set_up_orography_cube(np.zeros((3, 4), dtype=np.float32))
        self.plugin.topography = sort_coord_in_cube(topography,
                                                    topography.coord(axis='y'))

        self.temperature = set_up_variable_cube(np.full((2, 4), 280.15),
                                                units='kelvin',
                                                xo=398000.)
        self.temperature.attributes['institution'] = 'Met Office'
        self.temperature.attributes['source'] = 'Met Office Unified Model'
        self.temperature.attributes['mosg__grid_type'] = 'standard'
        self.temperature.attributes['mosg__grid_version'] = '1.2.0'
        self.temperature.attributes['mosg__grid_domain'] = 'uk_extended'
        self.temperature.attributes['mosg__model_configuration'] = 'uk_det'

        self.orogenh = np.array([[1.1, 1.2, 1.5, 1.4], [1.0, 1.3, 1.4, 1.6],
                                 [0.8, 0.9, 1.2, 0.9]])

    def test_basic(self):
        """Test that two cubes are returned with float32 coords"""
        output, regridded_output = self.plugin._create_output_cubes(
            self.orogenh, self.temperature)
        for cube in [output, regridded_output]:
            self.assertIsInstance(cube, iris.cube.Cube)
            for coord in cube.coords(dim_coords=True):
                self.assertEqual(coord.points.dtype, 'float32')

    def test_values(self):
        """Test first cube is unchanged and regridded output cube is masked
        as expected"""
        expected_data = np.array([[np.nan, 1.0, 1.4, np.nan],
                                  [np.nan, np.nan, np.nan, np.nan]])
        expected_mask = np.where(np.isfinite(expected_data), False, True)
        output, regridded_output = self.plugin._create_output_cubes(
            self.orogenh, self.temperature)
        self.assertArrayAlmostEqual(output.data, self.orogenh)
        self.assertTrue(
            np.allclose(regridded_output.data.data,
                        expected_data,
                        equal_nan=True))
        self.assertArrayEqual(regridded_output.data.mask, expected_mask)

    def test_metadata(self):
        """Check output metadata on both cubes is as expected"""
        hi_res_attributes = self.temperature.attributes
        for key, val in self.plugin.topography.attributes.items():
            hi_res_attributes[key] = val

        tref = sort_coord_in_cube(self.temperature,
                                  self.temperature.coord(axis='y'))
        output, regridded_output = self.plugin._create_output_cubes(
            self.orogenh, self.temperature)

        for axis in ['x', 'y']:
            self.assertEqual(output.coord(axis=axis),
                             self.plugin.topography.coord(axis=axis))
            self.assertEqual(regridded_output.coord(axis=axis),
                             tref.coord(axis=axis))

        for cube in [output, regridded_output]:
            self.assertEqual(cube.name(), 'orographic_enhancement')
            self.assertEqual(cube.units, 'mm h-1')
            for t_coord in [
                    'time', 'forecast_period', 'forecast_reference_time'
            ]:
                self.assertEqual(cube.coord(t_coord),
                                 self.temperature.coord(t_coord))
        self.assertDictEqual(regridded_output.attributes,
                             self.temperature.attributes)
        self.assertDictEqual(output.attributes, hi_res_attributes)

    def test_grid_metadata(self):
        """Test specific grid and model metadata inheritance"""
        output, regridded_output = self.plugin._create_output_cubes(
            self.orogenh, self.temperature)

        for attr in [
                'mosg__grid_type', 'mosg__grid_version', 'mosg__grid_domain'
        ]:
            self.assertEqual(regridded_output.attributes[attr],
                             self.temperature.attributes[attr])
            self.assertEqual(output.attributes[attr],
                             self.plugin.topography.attributes[attr])

        for cube in [output, regridded_output]:
            self.assertEqual(
                cube.attributes['mosg__model_configuration'],
                self.temperature.attributes['mosg__model_configuration'])
 def test_basic(self):
     """Test string representation of plugin"""
     plugin = OrographicEnhancement()
     self.assertEqual(str(plugin), "<OrographicEnhancement()>")
class Test__regrid_variable(IrisTest):
    """Test the _regrid_variable method"""
    def setUp(self):
        """Set up input cubes"""
        temperature = np.arange(6).reshape(2, 3)
        self.temperature_cube = set_up_variable_cube(temperature)
        orography = np.array([
            [20.0, 30.0, 40.0, 30.0, 25.0, 25.0],
            [30.0, 50.0, 80.0, 60.0, 50.0, 45.0],
            [50.0, 65.0, 90.0, 70.0, 60.0, 50.0],
            [45.0, 60.0, 85.0, 65.0, 55.0, 45.0],
        ])
        orography_cube = set_up_orography_cube(orography)
        self.plugin = OrographicEnhancement()
        self.plugin.topography = sort_coord_in_cube(
            orography_cube, orography_cube.coord(axis="y"))

    def test_basic(self):
        """Test cube of the correct shape and type is returned"""
        expected_data = np.array([
            [4.5, 5.0, 5.5, 6.0, 6.5, 7.0],
            [3.0, 3.5, 4.0, 4.5, 5.0, 5.5],
            [1.5, 2.0, 2.5, 3.0, 3.5, 4.0],
            [0.0, 0.5, 1.0, 1.5, 2.0, 2.5],
        ])
        result = self.plugin._regrid_variable(self.temperature_cube, "degC")
        self.assertIsInstance(result, iris.cube.Cube)
        self.assertArrayAlmostEqual(result.data, expected_data)
        self.assertEqual(result.data.dtype, "float32")

    def test_axis_inversion(self):
        """Test axes are output in ascending order"""
        result = self.plugin._regrid_variable(self.temperature_cube, "degC")
        x_points = result.coord(axis="x").points
        y_points = result.coord(axis="y").points
        self.assertTrue(x_points[1] > x_points[0])
        self.assertTrue(y_points[1] > y_points[0])

    def test_unit_conversion(self):
        """Test units are correctly converted"""
        expected_data = np.array(
            [
                [277.65, 278.15, 278.65, 279.15, 279.65, 280.15],
                [276.15, 276.65, 277.15, 277.65, 278.15, 278.65],
                [274.65, 275.15, 275.65, 276.15, 276.65, 277.15],
                [273.15, 273.65, 274.15, 274.65, 275.15, 275.65],
            ],
            dtype=np.float32,
        )
        result = self.plugin._regrid_variable(self.temperature_cube, "kelvin")
        self.assertEqual(result.units, "kelvin")
        self.assertArrayAlmostEqual(result.data, expected_data)

    def test_null(self):
        """Test cube is unchanged if axes and grid are already correct"""
        correct_cube = self.plugin.topography.copy()
        result = self.plugin._regrid_variable(correct_cube, "m")
        self.assertArrayAlmostEqual(result.data, correct_cube.data)
        self.assertEqual(result.metadata, correct_cube.metadata)

    def test_input_unchanged(self):
        """Test the input cube is not modified in place"""
        reference_cube = self.temperature_cube.copy()
        _ = self.plugin._regrid_variable(self.temperature_cube, "degC")
        self.assertArrayAlmostEqual(self.temperature_cube.data,
                                    reference_cube.data)
        self.assertEqual(self.temperature_cube.metadata,
                         reference_cube.metadata)