コード例 #1
0
    def setUp(self):
        """Set up data for testing."""
        self.plugin = GenerateTopographicZoneWeights()
        orography_data = np.array([[[[10., 25.], [75., 100.]]]])
        orography = set_up_cube(orography_data,
                                "altitude",
                                "m",
                                realizations=np.array([0]),
                                y_dimension_length=2,
                                x_dimension_length=2)
        orography = orography[0, 0, ...]
        orography.remove_coord("realization")
        orography.remove_coord("time")
        self.orography = orography

        landmask_data = np.array([[0, 1], [1, 1]])
        landmask = orography.copy(data=landmask_data)
        landmask.rename("land_binary_mask")
        landmask.units = Unit("1")
        self.landmask = landmask
        self.thresholds_dict = {
            'land': {
                'bounds': [[0, 50], [50, 200]],
                'units': 'm'
            }
        }
コード例 #2
0
    def setUp(self):
        """Set up data for testing."""
        self.plugin = GenerateTopographicZoneWeights()
        orography_data = np.array([[10.0, 25.0], [75.0, 100.0]])
        self.orography = set_up_orography_cube(orography_data)

        landmask_data = np.array([[0, 1], [1, 1]], dtype=np.float32)
        landmask = self.orography.copy(data=landmask_data)
        landmask.rename("land_binary_mask")
        landmask.units = Unit("1")
        self.landmask = landmask
        self.thresholds_dict = {"bounds": [[0, 50], [50, 200]], "units": "m"}
コード例 #3
0
class Test_calculate_weights(IrisTest):
    """Test the calculation of weights."""

    def setUp(self):
        """Set up plugin."""
        self.plugin = GenerateTopographicZoneWeights()

    def test_one_point(self):
        """Test when the input array has one point."""
        expected = np.array([0.75])
        points = np.array([125])
        band = [100, 200]
        result = self.plugin.calculate_weights(points, band)
        self.assertIsInstance(result, np.ndarray)
        self.assertArrayAlmostEqual(result, expected)

    def test_multiple_points_matching_points(self):
        """Test when the input array has multiple points, which match the
        midpoint and band limits."""
        expected = np.array([0.5, 1.0, 0.5])
        points = np.array([100, 150, 200])
        band = [100, 200]
        result = self.plugin.calculate_weights(points, band)
        self.assertIsInstance(result, np.ndarray)
        self.assertArrayAlmostEqual(result, expected)

    def test_multiple_points_with_points_not_matching(self):
        """Test when the input array has multiple points, which do not match
        the midpoint and band limits."""
        expected = np.array([0.6, 0.9, 0.6])
        points = np.array([110, 140, 190])
        band = [100, 200]
        result = self.plugin.calculate_weights(points, band)
        self.assertIsInstance(result, np.ndarray)
        self.assertArrayAlmostEqual(result, expected)

    def test_point_beyond_bands(self):
        """Test when the input array has points beyond the band limits.
        The default behaviour is for values beyond the band limits to be 0.5,
        as 0.5 is the value at the band limits."""
        expected = np.array([0.5, 1.0, 0.5])
        points = np.array([90, 150, 210])
        band = [100, 200]
        result = self.plugin.calculate_weights(points, band)
        self.assertIsInstance(result, np.ndarray)
        self.assertArrayAlmostEqual(result, expected)
コード例 #4
0
def process(orography: cli.inputcube,
            land_sea_mask: cli.inputcube = None,
            *,
            bands_config: cli.inputjson = None):
    """Runs topographic weights generation.

    Reads the orography and land_sea_mask fields of a cube. Creates a series of
    topographic zone weights to indicate where an orography point sits within
    the defined topographic bands. If the orography point is in the centre of
    a topographic band, then a single band will have a weight 1.0.
    If the orography point is at the edge of a topographic band, then the
    upper band will have a 0.5 weight whilst the lower band will also have a
    0.5 weight. Otherwise the weight will vary linearly between the centre of
    a topographic band and the edge.

    Args:
        orography (iris.cube.Cube):
            The orography on a standard grid.
        land_sea_mask (iris.cube.Cube):
            Land mask on a standard grid. If provided, sea points will be
            masked and set to the default fill value. If no land mask is
            provided, weights will be generated for sea points as well as land
            in the appropriate topographic band.
        bands_config (dict):
            Definition of orography bands required.
            The expected format of the dictionary is e.g
            {'bounds':[[0, 50], [50, 200]], 'units': 'm'}
            The default dictionary has the following form:
            {'bounds': [[-500., 50.], [50., 100.],
            [100., 150.],[150., 200.], [200., 250.],
            [250., 300.], [300., 400.], [400., 500.],
            [500., 650.],[650., 800.], [800., 950.],
            [950., 6000.]], 'units': 'm'}

    Returns:
        iris.cube.Cube:
            Cube containing the weights depending upon where the orography
            point is within the topographical zones.
    """
    from improver.generate_ancillaries.generate_topographic_zone_weights \
        import GenerateTopographicZoneWeights
    from improver.generate_ancillaries.generate_ancillary import (
        THRESHOLDS_DICT)

    if bands_config is None:
        bands_config = THRESHOLDS_DICT

    if land_sea_mask:
        land_sea_mask = next(land_sea_mask.slices(
            [land_sea_mask.coord(axis='y'), land_sea_mask.coord(axis='x')]))

    orography = next(orography.slices(
        [orography.coord(axis='y'), orography.coord(axis='x')]))

    result = GenerateTopographicZoneWeights().process(
        orography, bands_config, landmask=land_sea_mask)
    return result
コード例 #5
0
def process(landmask, orography, thresholds_dict=None):
    """Runs topographic weights generation.

    Reads the orography and landmask fields of a cube. Creates a series of
    topographic zone weights to indicate where an orography point sits within
    the defined topographic bands. If the orography point is in the centre of
    a topographic band, then a single band will have a weight 1.0.
    If the orography point is at the edge of a topographic band, then the
    upper band will have a 0.5 weight whilst the lower band will also have a
    0.5 weight. Otherwise the weight will vary linearly between the centre of
    a topographic band and the edge.

    Args:
        landmask (iris.cube.Cube):
            Land mask on standard grid. Sea points are masked out in the
            output array
        orography (iris.cube.Cube):
            Orography on standard grid.
        thresholds_dict (dict):
            Definition of orography bands required.
            The expected format of the dictionary is e.g
            {'bounds':[[0, 50], [50, 200]], 'units': 'm'}
            The default dictionary has the following form:
            {'bounds': [[-500., 50.], [50., 100.],
            [100., 150.],[150., 200.], [200., 250.],
            [250., 300.], [300., 400.], [400., 500.],
            [500., 650.],[650., 800.], [800., 950.],
            [950., 6000.]], 'units': 'm'}

    Returns:
        iris.cube.Cube:
            Cube containing the weights depending upon where the orography
            point is within the topographical zones.
    """
    orography = next(
        orography.slices(
            [orography.coord(axis='y'),
             orography.coord(axis='x')]))

    if landmask:
        landmask = next(
            landmask.slices(
                [landmask.coord(axis='y'),
                 landmask.coord(axis='x')]))

    if thresholds_dict is None:
        thresholds_dict = THRESHOLDS_DICT

    result = GenerateTopographicZoneWeights().process(orography,
                                                      thresholds_dict,
                                                      landmask=landmask)
    return result
コード例 #6
0
 def setUp(self):
     """Set up plugin."""
     self.plugin = GenerateTopographicZoneWeights()
コード例 #7
0
class Test_add_weight_to_upper_adjacent_band(IrisTest):
    """Test for adding weights to the upper adjacent band."""
    def setUp(self):
        """Set up plugin."""
        self.plugin = GenerateTopographicZoneWeights()

    def test_equal_to_max_band_number(self):
        """Test that the results are as expected when the band number is equal
        to the max band number."""
        expected_weights = np.array([[[0.0, 0.0], [1.0, 1.0]]])

        topographic_zone_weights = np.zeros((1, 2, 2))
        orography_band = np.array([[25.0, 50.0], [75.0, 100.0]])
        midpoint = 50.0
        band_number = 0
        max_band_number = 0
        topographic_zone_weights = self.plugin.add_weight_to_upper_adjacent_band(
            topographic_zone_weights,
            orography_band,
            midpoint,
            band_number,
            max_band_number,
        )
        self.assertIsInstance(topographic_zone_weights, np.ndarray)
        self.assertArrayAlmostEqual(topographic_zone_weights, expected_weights)

    def test_not_equal_to_max_band_number(self):
        """Test that the results are as expected when the band number is not
        equal to the max band number."""
        expected_weights = np.array([[[1.0, 1.0], [0.75, 0.5]],
                                     [[0.0, 0.0], [0.25, 0.5]]])
        topographic_zone_weights = np.array([[[1.0, 1.0], [0.75, 0.5]],
                                             [[0.0, 0.0], [0.0, 0.0]]])
        orography_band = np.array([[25.0, 50.0], [75.0, 100.0]])
        midpoint = 50.0
        band_number = 0
        max_band_number = 1
        topographic_zone_weights = self.plugin.add_weight_to_upper_adjacent_band(
            topographic_zone_weights,
            orography_band,
            midpoint,
            band_number,
            max_band_number,
        )
        self.assertIsInstance(topographic_zone_weights, np.ndarray)
        self.assertArrayAlmostEqual(topographic_zone_weights, expected_weights)

    def test_none_above_midpoint(self):
        """Test that the results are as expected when none of the points
        are above the midpoint."""
        expected_weights = np.array([[[0.75, 1.0], [0.65, 0.7]],
                                     [[0.0, 0.0], [0.0, 0.0]]])
        topographic_zone_weights = np.array([[[0.75, 1.0], [0.65, 0.7]],
                                             [[0.0, 0.0], [0.0, 0.0]]])
        orography_band = np.array([[25.0, 50.0], [15.0, 30.0]])
        midpoint = 50.0
        band_number = 0
        max_band_number = 1
        topographic_zone_weights = self.plugin.add_weight_to_upper_adjacent_band(
            topographic_zone_weights,
            orography_band,
            midpoint,
            band_number,
            max_band_number,
        )
        self.assertIsInstance(topographic_zone_weights, np.ndarray)
        self.assertArrayAlmostEqual(topographic_zone_weights, expected_weights)

    def test_all_above_midpoint(self):
        """Test that the results are as expected when all of the points
        are above the midpoint."""
        expected_weights = np.array([[[0.75, 0.7], [0.6, 0.55]],
                                     [[0.25, 0.3], [0.4, 0.45]]])
        topographic_zone_weights = np.array([[[0.75, 0.7], [0.6, 0.55]],
                                             [[0.0, 0.0], [0.0, 0.0]]])
        orography_band = np.array([[75.0, 80.0], [90.0, 95.0]])
        midpoint = 50.0
        band_number = 0
        max_band_number = 1
        topographic_zone_weights = self.plugin.add_weight_to_upper_adjacent_band(
            topographic_zone_weights,
            orography_band,
            midpoint,
            band_number,
            max_band_number,
        )
        self.assertIsInstance(topographic_zone_weights, np.ndarray)
        self.assertArrayAlmostEqual(topographic_zone_weights, expected_weights)
コード例 #8
0
class Test_process(IrisTest):
    """Test the process method."""
    def setUp(self):
        """Set up data for testing."""
        self.plugin = GenerateTopographicZoneWeights()
        orography_data = np.array([[10.0, 25.0], [75.0, 100.0]])
        self.orography = set_up_orography_cube(orography_data)

        landmask_data = np.array([[0, 1], [1, 1]], dtype=np.float32)
        landmask = self.orography.copy(data=landmask_data)
        landmask.rename("land_binary_mask")
        landmask.units = Unit("1")
        self.landmask = landmask
        self.thresholds_dict = {"bounds": [[0, 50], [50, 200]], "units": "m"}

    def test_basic(self):
        """Test that the output is a cube with the expected format."""
        result = self.plugin.process(self.orography, self.thresholds_dict,
                                     self.landmask)
        self.assertIsInstance(result, iris.cube.Cube)
        self.assertEqual(result.name(), "topographic_zone_weights")
        self.assertEqual(result.units, Unit("1"))
        self.assertTrue(result.coord("topographic_zone"))
        self.assertEqual(result.coord("topographic_zone").units, Unit("m"))

    def test_invalid_orography(self):
        """Test that the appropriate exception is raised if the orography has
        more than two dimensions."""
        orography_data = np.array([[[0.0, 25.0], [75.0, 100.0]]])
        orography = set_up_orography_cube(orography_data)
        msg = "The input orography cube should be two-dimensional"
        with self.assertRaisesRegex(InvalidCubeError, msg):
            self.plugin.process(orography, self.thresholds_dict, self.landmask)

    def test_data(self):
        """Test that the result data and mask is as expected."""
        expected_weights_data = np.array(
            [[[1e20, 1.0], [0.33, 0.17]], [[1e20, 0.0], [0.67, 0.83]]],
            dtype=np.float32)
        expected_weights_mask = np.array([[[True, False], [False, False]],
                                          [[True, False], [False, False]]])
        result = self.plugin.process(self.orography, self.thresholds_dict,
                                     self.landmask)
        self.assertIsInstance(result, iris.cube.Cube)
        self.assertArrayAlmostEqual(result.data.data,
                                    expected_weights_data,
                                    decimal=2)
        self.assertArrayAlmostEqual(result.data.mask, expected_weights_mask)

    def test_data_no_mask(self):
        """Test that the result data is as expected, when none of the points
        are masked."""
        expected_weights_data = np.array(
            [[[1.0, 1.0], [0.33, 0.17]], [[0.0, 0.0], [0.67, 0.83]]],
            dtype=np.float32)
        landmask_data = np.array([[1, 1], [1, 1]])
        landmask = self.landmask.copy(landmask_data)
        result = self.plugin.process(self.orography, self.thresholds_dict,
                                     landmask)
        self.assertIsInstance(result, iris.cube.Cube)
        self.assertArrayAlmostEqual(result.data,
                                    expected_weights_data,
                                    decimal=2)

    def test_data_no_mask_input(self):
        """Test that the result data is as expected, when no landsea
           mask is input."""
        expected_weights_data = np.array(
            [[[1.0, 1.0], [0.33, 0.17]], [[0.0, 0.0], [0.67, 0.83]]],
            dtype=np.float32)
        result = self.plugin.process(self.orography, self.thresholds_dict)
        self.assertIsInstance(result, iris.cube.Cube)
        self.assertArrayAlmostEqual(result.data,
                                    expected_weights_data,
                                    decimal=2)

    def test_data_no_mask_input_metatdata(self):
        """Test that the result metadata is as expected, when no landsea
           mask is input."""
        result = self.plugin.process(self.orography, self.thresholds_dict)
        self.assertIsInstance(result, iris.cube.Cube)
        self.assertEqual(
            result.attributes["topographic_zones_include_seapoints"], "True")

    def test_data_no_mask_three_bands(self):
        """Test that the result data is as expected, when none of the points
        are masked and there are three bands defined."""
        orography_data = np.array([[10.0, 40.0, 45.0], [70.0, 80.0, 95.0],
                                   [115.0, 135.0, 145.0]])
        orography = set_up_orography_cube(orography_data)

        landmask_data = np.array([[1, 1, 1], [1, 1, 1], [1, 1, 1]])
        landmask = orography.copy(data=landmask_data)
        landmask.rename("land_binary_mask")
        landmask.units = Unit("1")

        thresholds_dict = {
            "bounds": [[0, 50], [50, 100], [100, 150]],
            "units": "m"
        }
        expected_weights_data = np.array([
            [[1.0, 0.7, 0.6], [0.1, 0.0, 0.0], [0.0, 0.0, 0.0]],
            [[0.0, 0.3, 0.4], [0.9, 0.9, 0.6], [0.2, 0.0, 0.0]],
            [[0.0, 0.0, 0.0], [0.0, 0.1, 0.4], [0.8, 1.0, 1.0]],
        ])
        result = self.plugin.process(orography, thresholds_dict, landmask)
        self.assertIsInstance(result, iris.cube.Cube)
        self.assertArrayAlmostEqual(result.data,
                                    expected_weights_data,
                                    decimal=2)

    def test_different_band_units(self):
        """Test for if the thresholds are specified in a different unit to
        the orography. The thresholds are converted to match the units of the
        orography."""
        expected_weights_data = np.array(
            [[[1e20, 1.0], [0.333, 0.167]], [[1e20, 0.0], [0.67, 0.83]]],
            dtype=np.float32,
        )
        expected_weights_mask = np.array([[[True, False], [False, False]],
                                          [[True, False], [False, False]]])
        thresholds_dict = {"bounds": [[0, 0.05], [0.05, 0.2]], "units": "km"}
        result = self.plugin.process(self.orography, thresholds_dict,
                                     self.landmask)
        self.assertIsInstance(result, iris.cube.Cube)
        self.assertArrayAlmostEqual(result.data.data,
                                    expected_weights_data,
                                    decimal=2)
        self.assertArrayAlmostEqual(result.data.mask, expected_weights_mask)

    def test_one_band_with_orography_in_band(self):
        """Test that if only one band is specified, the results are as
        expected."""
        expected_weights_data = np.array([[1e20, 1.0], [1.0, 1.0]],
                                         dtype=np.float32)
        expected_weights_mask = np.array([[True, False], [False, False]])
        orography_data = np.array([[10.0, 20.0], [30.0, 40.0]],
                                  dtype=np.float32)
        orography = self.orography.copy(data=orography_data)
        thresholds_dict = {"bounds": [[0, 50]], "units": "m"}
        result = self.plugin.process(orography, thresholds_dict, self.landmask)
        self.assertIsInstance(result, iris.cube.Cube)
        self.assertArrayAlmostEqual(result.data.data,
                                    expected_weights_data,
                                    decimal=2)
        self.assertArrayAlmostEqual(result.data.mask, expected_weights_mask)

    @ManageWarnings(record=True)
    def test_warning_if_orography_above_bands(self, warning_list=None):
        """Test that a warning is raised if the orography is greater than the
        maximum band."""
        orography_data = np.array([[60.0, 70.0], [80.0, 90.0]])
        orography = self.orography.copy(data=orography_data)
        thresholds_dict = {"bounds": [[0, 50]], "units": "m"}
        msg = "The maximum orography is greater than the uppermost band"
        self.plugin.process(orography, thresholds_dict, self.landmask)
        self.assertTrue(
            any(item.category == UserWarning for item in warning_list))
        self.assertTrue(any(msg in str(item) for item in warning_list))

    @ManageWarnings(record=True)
    def test_warning_if_orography_below_bands(self, warning_list=None):
        """Test that a warning is raised if the orography is lower than the
        minimum band."""
        orography_data = np.array([[60.0, 70.0], [80.0, 90.0]])
        orography = self.orography.copy(data=orography_data)
        thresholds_dict = {"bounds": [[100, 150]], "units": "m"}
        msg = "The minimum orography is lower than the lowest band"
        self.plugin.process(orography, thresholds_dict, self.landmask)
        self.assertTrue(
            any(item.category == UserWarning for item in warning_list))
        self.assertTrue(any(msg in str(item) for item in warning_list))
コード例 #9
0
class Test_add_weight_to_lower_adjacent_band(IrisTest):
    """Test for adding weights to the lower adjacent band."""
    def setUp(self):
        """Set up plugin."""
        self.plugin = GenerateTopographicZoneWeights()

    def test_equal_to_zeroth_band_number(self):
        """Test that the results are as expected when the band number is equal
        to the zeroth band."""
        expected_weights = np.array([[[1.0, 0.0], [0.0, 0.0]]])

        topographic_zone_weights = np.zeros((1, 2, 2))
        orography_band = np.array([[25., 50.], [75., 100.]])
        midpoint = 50.
        band_number = 0
        topographic_zone_weights = (
            self.plugin.add_weight_to_lower_adjacent_band(
                topographic_zone_weights, orography_band, midpoint,
                band_number))
        self.assertIsInstance(topographic_zone_weights, np.ndarray)
        self.assertArrayAlmostEqual(topographic_zone_weights, expected_weights)

    def test_not_equal_to_zeroth_band_number(self):
        """Test that the results are as expected when the band number is not
        equal to the zeroth band number."""
        expected_weights = np.array([[[0.25, 0.0], [0.0, 0.0]],
                                     [[0.75, 1.0], [1.0, 1.0]]])
        topographic_zone_weights = np.array([[[0.0, 0.0], [0.0, 0.0]],
                                             [[0.75, 1.0], [1.0, 1.0]]])
        orography_band = np.array([[25., 50.], [75., 100.]])
        midpoint = 50.
        band_number = 1
        topographic_zone_weights = (
            self.plugin.add_weight_to_lower_adjacent_band(
                topographic_zone_weights, orography_band, midpoint,
                band_number))
        self.assertIsInstance(topographic_zone_weights, np.ndarray)
        self.assertArrayAlmostEqual(topographic_zone_weights, expected_weights)

    def test_none_below_midpoint(self):
        """Test that the results are as expected when none of the points
        are below the midpoint."""
        expected_weights = np.array([[[0.0, 0.0], [0.0, 0.0]],
                                     [[1.0, 1.0], [1.0, 1.0]]])
        topographic_zone_weights = np.array([[[0.0, 0.0], [0.0, 0.0]],
                                             [[1.0, 1.0], [1.0, 1.0]]])
        orography_band = np.array([[75., 50.], [85., 70.]])
        midpoint = 50.
        band_number = 1
        topographic_zone_weights = (
            self.plugin.add_weight_to_lower_adjacent_band(
                topographic_zone_weights, orography_band, midpoint,
                band_number))
        self.assertIsInstance(topographic_zone_weights, np.ndarray)
        self.assertArrayAlmostEqual(topographic_zone_weights, expected_weights)

    def test_all_below_midpoint(self):
        """Test that the results are as expected when all of the points
        are below the midpoint."""
        expected_weights = np.array([[[0.25, 0.3], [0.4, 0.45]],
                                     [[0.75, 0.7], [0.6, 0.55]]])
        topographic_zone_weights = np.array([[[0.0, 0.0], [0.0, 0.0]],
                                             [[0.75, 0.7], [0.6, 0.55]]])
        orography_band = np.array([[25., 20.], [10., 5.]])
        midpoint = 50.
        band_number = 1
        topographic_zone_weights = (
            self.plugin.add_weight_to_lower_adjacent_band(
                topographic_zone_weights, orography_band, midpoint,
                band_number))
        self.assertIsInstance(topographic_zone_weights, np.ndarray)
        self.assertArrayAlmostEqual(topographic_zone_weights, expected_weights)
コード例 #10
0
def main(argv=None):
    """Load in arguments and get going."""
    parser = ArgParser(
        description=('Reads input orography and landmask fields. Creates '
                     'a series of topographic zone weights to indicate '
                     'where an orography point sits within the defined '
                     'topographic bands. If the orography point is in the '
                     'centre of a topographic band, then a single band will '
                     'have a weight of 1.0. If the orography point is at the '
                     'edge of a topographic band, then the upper band will '
                     'have a 0.5 weight whilst the lower band will also have '
                     'a 0.5 weight. Otherwise, the weight will vary linearly '
                     'between the centre of a topographic band and the edge.'))
    parser.add_argument('input_filepath_standard_orography',
                        metavar='INPUT_FILE_STANDARD_OROGRAPHY',
                        help=('A path to an input NetCDF orography file to '
                              'be processed'))
    parser.add_argument('output_filepath', metavar='OUTPUT_FILE',
                        help='The output path for the processed NetCDF.')
    parser.add_argument('--input_filepath_landmask', metavar='INPUT_FILE_LAND',
                        help=('A path to an input NetCDF land mask file to be '
                              'processed. If provided, sea points will be '
                              'masked and set to the default fill value. If '
                              'no land mask is provided, weights will be '
                              'generated for sea points as well as land, '
                              'included in the appropriate topographic band.'))

    parser.add_argument('--force', dest='force', default=False,
                        action='store_true',
                        help=('If keyword is set (i.e. True), ancillaries '
                              'will be generated even if doing so will '
                              'overwrite existing files'))
    parser.add_argument('--thresholds_filepath',
                        metavar='THRESHOLDS_FILEPATH',
                        default=None,
                        help=("The path to a json file which can be used "
                              "to set the number and size of topographic "
                              "bounds. If unset a default bounds dictionary"
                              " will be used:"
                              "{'bounds': [[-500., 50.], [50., 100.], "
                              "[100., 150.],[150., 200.], [200., 250.], "
                              "[250., 300.], [300., 400.], [400., 500.], "
                              "[500., 650.],[650., 800.], [800., 950.], "
                              "[950., 6000.]], 'units': 'm'}"))
    args = parser.parse_args(args=argv)

    if args.thresholds_filepath:
        with open(args.thresholds_filepath, 'r') as filehandle:
            thresholds_dict = json.loads(filehandle.read())
    else:
        thresholds_dict = THRESHOLDS_DICT

    if not os.path.exists(args.output_filepath) or args.force:
        orography = load_cube(args.input_filepath_standard_orography)
        orography = next(orography.slices([orography.coord(axis='y'),
                                           orography.coord(axis='x')]))

        landmask = None
        if args.input_filepath_landmask:
            try:
                landmask = load_cube(args.input_filepath_landmask)
            except IOError as err:
                msg = ("Loading land mask has been unsuccessful: {}. "
                       "This may be because the land mask could not be "
                       "located in {}; run "
                       'improver-generate-landmask-ancillary first.').format(
                           err, args.input_filepath_landmask)
                raise IOError(msg)

            landmask = next(landmask.slices([landmask.coord(axis='y'),
                                             landmask.coord(axis='x')]))

        result = GenerateTopographicZoneWeights().process(
            orography, thresholds_dict, landmask=landmask)
        save_netcdf(result, args.output_filepath)
    else:
        print('File already exists here: ', args.output_filepath)
コード例 #11
0
class Test_process(IrisTest):
    """Test the process method."""
    def setUp(self):
        """Set up data for testing."""
        self.plugin = GenerateTopographicZoneWeights()
        orography_data = np.array([[[[10., 25.], [75., 100.]]]])
        orography = set_up_cube(orography_data,
                                "altitude",
                                "m",
                                realizations=np.array([0]),
                                y_dimension_length=2,
                                x_dimension_length=2)
        orography = orography[0, 0, ...]
        orography.remove_coord("realization")
        orography.remove_coord("time")
        self.orography = orography

        landmask_data = np.array([[0, 1], [1, 1]])
        landmask = orography.copy(data=landmask_data)
        landmask.rename("land_binary_mask")
        landmask.units = Unit("1")
        self.landmask = landmask
        self.thresholds_dict = {
            'land': {
                'bounds': [[0, 50], [50, 200]],
                'units': 'm'
            }
        }

    def test_basic(self):
        """Test that the output is a cube with the expected format."""
        result = self.plugin.process(self.orography, self.landmask,
                                     self.thresholds_dict)
        self.assertIsInstance(result, iris.cube.Cube)
        self.assertEqual(result.name(), "topographic_zone_weights")
        self.assertEqual(result.units, Unit("1"))
        self.assertTrue(result.coord("topographic_zone"))
        self.assertEqual(result.coord("topographic_zone").units, Unit("m"))

    def test_invalid_orography(self):
        """Test that the appropriate exception is raised if the orography has
        more than two dimensions."""
        orography_data = np.array([[[[0., 25.], [75., 100.]]]])
        orography = set_up_cube(orography_data,
                                "altitude",
                                "m",
                                realizations=np.array([0]),
                                y_dimension_length=2,
                                x_dimension_length=2)
        msg = "The input orography cube should be two-dimensional"
        with self.assertRaisesRegexp(InvalidCubeError, msg):
            self.plugin.process(orography, self.landmask, self.thresholds_dict)

    def test_invalid_bands(self):
        """Test for if the thresholds_dict has an invalid key."""
        thresholds_dict = {
            'invalid': {
                'bounds': [[0, 50], [50, 200]],
                'units': 'm'
            }
        }
        msg = "'land'"
        with self.assertRaisesRegexp(KeyError, msg):
            self.plugin.process(self.orography, self.landmask, thresholds_dict)

    def test_data(self):
        """Test that the result data and mask is as expected."""
        expected_weights_data = np.array([[[1e20, 1.0], [0.33, 0.17]],
                                          [[1e20, 0.0], [0.67, 0.83]]])
        expected_weights_mask = np.array([[[True, False], [False, False]],
                                          [[True, False], [False, False]]])
        result = self.plugin.process(self.orography, self.landmask,
                                     self.thresholds_dict)
        self.assertIsInstance(result, iris.cube.Cube)
        self.assertArrayAlmostEqual(result.data.data,
                                    expected_weights_data,
                                    decimal=2)
        self.assertArrayAlmostEqual(result.data.mask, expected_weights_mask)

    def test_data_no_mask(self):
        """Test that the result data is as expected, when none of the points
        are masked."""
        expected_weights_data = np.array([[[1.0, 1.0], [0.33, 0.17]],
                                          [[0.0, 0.0], [0.67, 0.83]]])
        landmask_data = np.array([[1, 1], [1, 1]])
        landmask = self.landmask.copy(landmask_data)
        result = self.plugin.process(self.orography, landmask,
                                     self.thresholds_dict)
        self.assertIsInstance(result, iris.cube.Cube)
        self.assertArrayAlmostEqual(result.data,
                                    expected_weights_data,
                                    decimal=2)

    def test_data_no_mask_three_bands(self):
        """Test that the result data is as expected, when none of the points
        are masked and there are three bands defined."""
        orography_data = np.array([[[[10., 40., 45.], [70., 80., 95.],
                                     [115., 135., 145.]]]])
        orography = set_up_cube(orography_data,
                                "altitude",
                                "m",
                                realizations=np.array([0]),
                                y_dimension_length=3,
                                x_dimension_length=3)
        orography = orography[0, 0, ...]
        orography.remove_coord("realization")
        orography.remove_coord("time")

        landmask_data = np.array([[1, 1, 1], [1, 1, 1], [1, 1, 1]])
        landmask = orography.copy(data=landmask_data)
        landmask.rename("land_binary_mask")
        landmask.units = Unit("1")

        thresholds_dict = {
            'land': {
                'bounds': [[0, 50], [50, 100], [100, 150]],
                'units': 'm'
            }
        }
        expected_weights_data = np.array([[[1.0, 0.7, 0.6], [0.1, 0.0, 0.0],
                                           [0.0, 0.0, 0.0]],
                                          [[0.0, 0.3, 0.4], [0.9, 0.9, 0.6],
                                           [0.2, 0.0, 0.0]],
                                          [[0.0, 0.0, 0.0], [0.0, 0.1, 0.4],
                                           [0.8, 1.0, 1.0]]])
        result = self.plugin.process(orography, landmask, thresholds_dict)
        self.assertIsInstance(result, iris.cube.Cube)
        self.assertArrayAlmostEqual(result.data,
                                    expected_weights_data,
                                    decimal=2)

    def test_different_band_units(self):
        """Test for if the thresholds are specified in a different unit to
        the orography. The thresholds are converted to match the units of the
        orography."""
        expected_weights_data = np.array([[[1e20, 1.0], [0.33, 0.17]],
                                          [[1e20, 0.0], [0.67, 0.83]]])
        expected_weights_mask = np.array([[[True, False], [False, False]],
                                          [[True, False], [False, False]]])
        thresholds_dict = {
            'land': {
                'bounds': [[0, 0.05], [0.05, 0.2]],
                'units': 'km'
            }
        }
        result = self.plugin.process(self.orography, self.landmask,
                                     thresholds_dict)
        self.assertIsInstance(result, iris.cube.Cube)
        self.assertArrayAlmostEqual(result.data.data,
                                    expected_weights_data,
                                    decimal=2)
        self.assertArrayAlmostEqual(result.data.mask, expected_weights_mask)

    def test_one_band_with_orography_in_band(self):
        """Test that if only one band is specified, the results are as
        expected."""
        expected_weights_data = np.array([[1e20, 1.0], [1.0, 1.0]])
        expected_weights_mask = np.array([[True, False], [False, False]])
        orography_data = np.array([[10., 20.], [30., 40.]])
        orography = self.orography.copy(data=orography_data)
        thresholds_dict = {'land': {'bounds': [[0, 50]], 'units': 'm'}}
        result = self.plugin.process(orography, self.landmask, thresholds_dict)
        self.assertIsInstance(result, iris.cube.Cube)
        self.assertArrayAlmostEqual(result.data.data,
                                    expected_weights_data,
                                    decimal=2)
        self.assertArrayAlmostEqual(result.data.mask, expected_weights_mask)

    def test_warning_if_orography_above_bands(self):
        """Test that a warning is raised if the orography is greater than the
        maximum band."""
        orography_data = np.array([[60., 70.], [80., 90.]])
        orography = self.orography.copy(data=orography_data)
        thresholds_dict = {'land': {'bounds': [[0, 50]], 'units': 'm'}}
        msg = "The maximum orography is greater than the uppermost band"
        with warnings.catch_warnings(record=True) as warning_list:
            self.plugin.process(orography, self.landmask, thresholds_dict)
            self.assertTrue(
                any(item.category == UserWarning for item in warning_list))
            self.assertTrue(any(msg in str(item) for item in warning_list))

    def test_warning_if_orography_below_bands(self):
        """Test that a warning is raised if the orography is lower than the
        minimum band."""
        orography_data = np.array([[60., 70.], [80., 90.]])
        orography = self.orography.copy(data=orography_data)
        thresholds_dict = {'land': {'bounds': [[100, 150]], 'units': 'm'}}
        msg = "The minimum orography is lower than the lowest band"
        with warnings.catch_warnings(record=True) as warning_list:
            self.plugin.process(orography, self.landmask, thresholds_dict)
            self.assertTrue(
                any(item.category == UserWarning for item in warning_list))
            self.assertTrue(any(msg in str(item) for item in warning_list))