示例#1
0
def test_set_record_run_attr_existing_attribute(model_cube, existing,
                                                expected):
    """Test the case in which the cubes already have record_run_attr entries
    and these must be combined to create a new shared attribute. There are three
    tests here:

      - cubes with unique model_run attributes that are combined
      - cubes with distinct but overlapping model_run attributes from which the
        elements are combined without duplicates
      - cubes with identical model_run attributes that are combined such that
        only a single entry is returned.

    The test cubes are only vehicles for the attributes in this test, such that
    the attributes imposed do not necessarily match the other cube metadata."""

    record_run_attr = "mosg__model_run"
    model_id_attr = "mosg__model_configuration"
    cubes = [model_cube[0], model_cube[1]]
    for run_attr, cube in zip(existing, cubes):
        cube.attributes.update({record_run_attr: run_attr})

    set_record_run_attr(cubes, record_run_attr, model_id_attr)

    for cube in cubes:
        assert cube.attributes[record_run_attr] == expected
示例#2
0
    def process(
        self,
        cubes_in: Union[List[Cube], Cube, CubeList],
        cycletime: Optional[str] = None,
    ) -> Cube:
        """
        Prepares merged input cube for cycle and grid blending. Makes updates to
        metadata (attributes and time-type coordinates) ONLY in so far as these are
        needed to ensure inputs can be merged into a single cube.

        Args:
            cubes_in:
                Cubes to be merged.
            cycletime:
                The cycletime in a YYYYMMDDTHHMMZ format e.g. 20171122T0100Z.
                Can be used in rationalise_blend_time_coordinates.

        Returns:
            Merged cube.

        Raises:
            ValueError:
                If self.blend_coord is not present on all cubes (unless
                blending over models)
        """
        cubelist = ([cubes_in.copy()] if isinstance(cubes_in, iris.cube.Cube)
                    else [cube.copy() for cube in cubes_in])

        if self.record_run_attr is not None and self.model_id_attr is not None:
            set_record_run_attr(cubelist, self.record_run_attr,
                                self.model_id_attr)

        if "model" in self.blend_coord:
            cubelist = [self._remove_blend_time(cube) for cube in cubelist]
            cubelist = [
                self._remove_deprecation_warnings(cube) for cube in cubelist
            ]
            if (self.weighting_coord is not None
                    and "forecast_period" in self.weighting_coord):
                # if blending models using weights by forecast period, unify
                # forecast periods (assuming validity times are identical);
                # method returns a new cubelist with copies of input cubes
                cubelist = rebadge_forecasts_as_latest_cycle(
                    cubelist, cycletime=cycletime)

        # check all input cubes have the blend coordinate
        for cube in cubelist:
            if "model" not in self.blend_coord and not cube.coords(
                    self.blend_coord):
                raise ValueError("{} coordinate is not present on all input "
                                 "cubes".format(self.blend_coord))

        # create model ID and model configuration coordinates if blending
        # different models
        if "model" in self.blend_coord and self.model_id_attr is not None:
            self._create_model_coordinates(cubelist)

        # merge resulting cubelist
        result = MergeCubes()(cubelist, check_time_bounds_ranges=True)
        return result
示例#3
0
def test_set_record_run_attr_exception_model_id(model_cube):
    """Test an exception is raised if no model_id_attr is set on the input
    cubes and no existing record_run_attr was present."""
    record_run_attr = "mosg__model_run"
    model_id_attr = "mosg__model_configuration"
    cubes = [model_cube[0], model_cube[1]]

    with pytest.raises(Exception, match="Failure to record run information"):
        set_record_run_attr(cubes, record_run_attr, model_id_attr)
示例#4
0
def test_set_record_run_attr_exception_model_id_unset(model_cube):
    """Test an exception is raised if the model_id_attr argument provided is
    none and the input cubes do not all have an existing record_run_attr."""
    record_run_attr = "mosg__model_run"
    model_id_attr = None
    cubes = [model_cube[0], model_cube[1]]

    with pytest.raises(Exception,
                       match="Not all input cubes contain an existing"):
        set_record_run_attr(cubes, record_run_attr, model_id_attr)
示例#5
0
    def process(self, cubes: CubeList) -> Cube:
        """Calculate the modal weather code, with handling for edge cases.

        Args:
            cubes:
                A list of weather code cubes at different times. A modal
                code will be calculated over the time coordinate to return
                the most comon code, which is taken to be the best
                representation of the whole period.

        Returns:
            A single weather code cube with time bounds that span those of
            the input weather code cubes.
        """
        # Set the record_run attribute on all cubes. This will survive the
        # merge and be present on the output.
        if self.record_run_attr:
            set_record_run_attr(cubes, self.record_run_attr,
                                self.model_id_attr)

        cube = MergeCubes()(cubes)
        self._unify_day_and_night(cube)

        # Handle case in which a single time is provided.
        if len(cube.coord("time").points) == 1:
            result = cube
            result.add_cell_method(self.mode_cell_method)
        else:
            result = cube.collapsed("time", self.aggregator_instance)
        self._set_blended_times(result)

        if self.model_id_attr:
            # Update contributing models
            contributing_models = set()
            for source_cube in cubes:
                for model in source_cube.attributes[self.model_id_attr].split(
                        " "):
                    contributing_models.update([model])
            result.attributes[self.model_id_attr] = " ".join(
                sorted(list(contributing_models)))

        # Handle any unset points where it was hard to determine a suitable mode
        if (result.data == UNSET_CODE_INDICATOR).any():
            self._group_codes(result, cube)

        return result
示例#6
0
def test_set_record_run_attr_mixed_inputs(model_cube):
    """Test use case where the record_run_attr is constructed from one cube
    with an existing record_run_attr, and one where other information on the
    cube is used."""

    record_run_attr = "mosg__model_run"
    model_id_attr = "mosg__model_configuration"
    cubes = [model_cube[0], model_cube[1]]
    cubes[0].attributes.update({record_run_attr: "uk_det:20171110T0000Z:"})
    cubes[1].attributes = {
        model_id_attr: cubes[1].coord("model_configuration").points[0]
    }
    expected = "uk_det:20171110T0000Z:\nuk_ens:20171110T0100Z:"

    set_record_run_attr(cubes, record_run_attr, model_id_attr)

    for cube in cubes:
        assert cube.attributes[record_run_attr] == expected
示例#7
0
def test_set_record_run_attr_basic(model_cube, indices, expected):
    """Test use case where the record_run_attr is constructed from other
    information on the cubes. There are two tests here:

      - cubes with unique attributes that are combined
      - cubes with identical attributes attributes that are combined such that
        only a single entry is returned."""

    record_run_attr = "mosg__model_run"
    model_id_attr = "mosg__model_configuration"
    cubes = [model_cube[index] for index in indices]
    for cube in cubes:
        cube.attributes = {
            model_id_attr: cube.coord("model_configuration").points[0]
        }

    set_record_run_attr(cubes, record_run_attr, model_id_attr)

    for cube in cubes:
        assert cube.attributes[record_run_attr] == expected
示例#8
0
    def create_symbol_cube(self, cubes: Union[List[Cube], CubeList]) -> Cube:
        """
        Create an empty weather symbol cube

        Args:
            cubes:
                List of input cubes used to generate weather symbols

        Returns:
            A cube with suitable metadata to describe the weather symbols
            that will fill it and data initiated with the value -1 to allow
            any unset points to be readily identified.
        """
        threshold_coord = find_threshold_coordinate(self.template_cube)
        template_cube = next(self.template_cube.slices_over([threshold_coord
                                                             ])).copy()
        # remove coordinates and bounds that do not apply to weather symbols
        template_cube.remove_coord(threshold_coord)

        mandatory_attributes = generate_mandatory_attributes(cubes)
        optional_attributes = weather_code_attributes()
        if self.model_id_attr:
            optional_attributes.update(
                update_model_id_attr_attribute(cubes, self.model_id_attr))
        if self.record_run_attr:
            set_record_run_attr(cubes, self.record_run_attr,
                                self.model_id_attr)
            optional_attributes.update({
                self.record_run_attr:
                cubes[0].attributes[self.record_run_attr]
            })

        symbols = create_new_diagnostic_cube(
            "weather_code",
            "1",
            template_cube,
            mandatory_attributes,
            optional_attributes=optional_attributes,
            data=np.ma.masked_all_like(template_cube.data).astype(np.int32),
        )
        return symbols