Example #1
0
 def test_diagnostic_len_match(self):
     """Test diagnostic fields, thresholds and conditions are same len."""
     tree = wxcode_decision_tree_global()
     for node in tree:
         query = tree[node]
         diag_len = len(expand_nested_lists(query, 'diagnostic_fields'))
         thres_len = len(expand_nested_lists(query,
                                             'diagnostic_thresholds'))
         cond_len = len(expand_nested_lists(query, 'diagnostic_conditions'))
         self.assertEqual(diag_len, thres_len)
         self.assertEqual(diag_len, cond_len)
Example #2
0
 def test_list_of_lists(self):
     """Returns a expanded list if given a list of lists."""
     result = expand_nested_lists(self.dictionary, "list_of_lists")
     for val in result:
         self.assertEqual(val, "a")
Example #3
0
 def test_simple_list(self):
     """Testexpand_nested_lists returns a expanded list if given a list."""
     result = expand_nested_lists(self.dictionary, "list")
     for val in result:
         self.assertEqual(val, "a")
Example #4
0
 def test_basic(self):
     """Test that the expand_nested_lists returns a list."""
     result = expand_nested_lists(self.dictionary, "list")
     self.assertIsInstance(result, list)
Example #5
0
    def check_input_cubes(self, cubes: CubeList) -> Optional[Dict[str, Any]]:
        """
        Check that the input cubes contain all the diagnostics and thresholds
        required by the decision tree.  Sets self.coord_named_threshold to
        "True" if threshold-type coordinates have the name "threshold" (as
        opposed to the standard name of the diagnostic), for backward
        compatibility.

        Args:
            cubes:
                A CubeList containing the input diagnostic cubes.

        Returns:
            A dictionary of (keyword) nodes names where the diagnostic
            data is missing and (values) node associated with
            diagnostic_missing_action.

        Raises:
            IOError:
                Raises an IOError if any of the required input data is missing.
                The error includes details of which fields are missing.
        """
        optional_node_data_missing = {}
        missing_data = []
        for key, query in self.queries.items():
            diagnostics = get_parameter_names(
                expand_nested_lists(query, "diagnostic_fields")
            )
            thresholds = expand_nested_lists(query, "diagnostic_thresholds")
            conditions = expand_nested_lists(query, "diagnostic_conditions")
            for diagnostic, threshold, condition in zip(
                diagnostics, thresholds, conditions
            ):

                # First we check the diagnostic name and units, performing
                # a conversion is required and possible.
                test_condition = iris.Constraint(name=diagnostic)
                matched_cube = cubes.extract(test_condition)
                if not matched_cube:
                    if "diagnostic_missing_action" in query:
                        optional_node_data_missing.update(
                            {key: query[query["diagnostic_missing_action"]]}
                        )
                    else:
                        missing_data.append([diagnostic, threshold, condition])
                    continue

                cube_threshold_units = find_threshold_coordinate(matched_cube[0]).units
                threshold.convert_units(cube_threshold_units)

                # Then we check if the required threshold is present in the
                # cube, and that the thresholding is relative to it correctly.
                threshold = threshold.points.item()
                threshold_name = find_threshold_coordinate(matched_cube[0]).name()

                # Set flag to check for old threshold coordinate names
                if threshold_name == "threshold" and not self.coord_named_threshold:
                    self.coord_named_threshold = True

                # Check threshold == 0.0
                if abs(threshold) < self.float_abs_tolerance:
                    coord_constraint = {
                        threshold_name: lambda cell: np.isclose(
                            cell.point, 0, rtol=0, atol=self.float_abs_tolerance
                        )
                    }
                else:
                    coord_constraint = {
                        threshold_name: lambda cell: np.isclose(
                            cell.point, threshold, rtol=self.float_tolerance, atol=0
                        )
                    }

                # Checks whether the spp__relative_to_threshold attribute is above
                # or below a threshold and and compares to the diagnostic_condition.
                test_condition = iris.Constraint(
                    coord_values=coord_constraint,
                    cube_func=lambda cube: (
                        probability_is_above_or_below(cube) == condition
                    ),
                )
                matched_threshold = matched_cube.extract(test_condition)
                if not matched_threshold:
                    missing_data.append([diagnostic, threshold, condition])

        if missing_data:
            msg = (
                "Weather Symbols input cubes are missing"
                " the following required"
                " input fields:\n"
            )
            dyn_msg = "name: {}, threshold: {}, " "spp__relative_to_threshold: {}\n"
            for item in missing_data:
                msg = msg + dyn_msg.format(*item)
            raise IOError(msg)

        if not optional_node_data_missing:
            optional_node_data_missing = None
        return optional_node_data_missing
Example #6
0
    def check_input_cubes(self, cubes):
        """
        Check that the input cubes contain all the diagnostics and thresholds
        required by the decision tree.  Sets self.coord_named_threshold to
        "True" if threshold-type coordinates have the name "threshold" (as
        opposed to the standard name of the diagnostic), for backward
        compatibility.

        Args:
            cubes (iris.cube.CubeList):
                A CubeList containing the input diagnostic cubes.

        Returns:
            dict or None:
                A dictionary of (keyword) nodes names where the diagnostic
                data is missing and (values) node associated with
                diagnostic_missing_action.

        Raises:
            IOError:
                Raises an IOError if any of the required input data is missing.
                The error includes details of which fields are missing.
        """
        optional_node_data_missing = {}
        missing_data = []
        for key, query in self.queries.items():
            diagnostics = expand_nested_lists(query, 'diagnostic_fields')
            thresholds = expand_nested_lists(query, 'diagnostic_thresholds')
            conditions = expand_nested_lists(query, 'diagnostic_conditions')
            for diagnostic, threshold, condition in zip(
                    diagnostics, thresholds, conditions):

                # First we check the diagnostic name and units, performing
                # a conversion is required and possible.
                test_condition = (iris.Constraint(name=diagnostic))
                matched_cube = cubes.extract(test_condition)
                if not matched_cube:
                    if 'diagnostic_missing_action' in query:
                        optional_node_data_missing.update(
                            {key: query[query['diagnostic_missing_action']]})
                    else:
                        missing_data.append([diagnostic, threshold, condition])
                    continue
                else:
                    cube_threshold_units = (find_threshold_coordinate(
                        matched_cube[0]).units)
                    threshold.convert_units(cube_threshold_units)

                # Then we check if the required threshold is present in the
                # cube, and that the thresholding is relative to it correctly.
                threshold = threshold.points.item()
                threshold_name = find_threshold_coordinate(
                    matched_cube[0]).name()

                # Check cube and threshold coordinate names match according to
                # expected convention.  If not, add to exception dictionary.
                if extract_diagnostic_name(diagnostic) != threshold_name:
                    self.threshold_coord_names[diagnostic] = (threshold_name)

                # Set flag to check for old threshold coordinate names
                if (threshold_name == "threshold"
                        and not self.coord_named_threshold):
                    self.coord_named_threshold = True

                # Check threshold == 0.0
                if abs(threshold) < self.float_abs_tolerance:
                    coord_constraint = {
                        threshold_name:
                        lambda cell: (-self.float_abs_tolerance < cell < self.
                                      float_abs_tolerance)
                    }
                else:
                    coord_constraint = {
                        threshold_name:
                        lambda cell:
                        (threshold *
                         (1. - self.float_tolerance) < cell < threshold *
                         (1. + self.float_tolerance))
                    }
                test_condition = (iris.Constraint(
                    coord_values=coord_constraint,
                    cube_func=lambda cube:
                    (find_threshold_coordinate(cube).attributes[
                        'spp__relative_to_threshold'] == condition)))
                matched_threshold = matched_cube.extract(test_condition)
                if not matched_threshold:
                    missing_data.append([diagnostic, threshold, condition])

        if missing_data:
            msg = ('Weather Symbols input cubes are missing'
                   ' the following required'
                   ' input fields:\n')
            dyn_msg = ('name: {}, threshold: {}, '
                       'spp__relative_to_threshold: {}\n')
            for item in missing_data:
                msg = msg + dyn_msg.format(*item)
            raise IOError(msg)

        if not optional_node_data_missing:
            optional_node_data_missing = None
        return optional_node_data_missing