def check_input_cubes(self, cubes):
        """
        Check that the input cubes contain all the diagnostics and thresholds
        required by the decision tree.

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

        Raises:
            IOError:
                Raises an IOError if any of the required input data is missing.
                The error includes details of which fields are missing.
        """
        missing_data = []
        for query in self.queries.itervalues():
            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:
                    missing_data.append([diagnostic, threshold, condition])
                    continue
                else:
                    cube_threshold_units = (
                        matched_cube[0].coord('threshold').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()
                test_condition = (iris.Constraint(
                    threshold=lambda cell:
                    (threshold *
                     (1. - self.float_tolerance) < cell < threshold *
                     (1. + self.float_tolerance)),
                    cube_func=lambda cube:
                    (cube.attributes['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: {}, relative_to_threshold: {}\n'
            for item in missing_data:
                msg = msg + dyn_msg.format(*item)
            raise IOError(msg)
        return
Exemple #2
0
 def test_diagnostic_len_match(self):
     """Test diagnostic fields, thresholds and conditions are same len."""
     tree = wxcode_decision_tree()
     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)
Exemple #3
0
def interrogate_decision_tree(wxtree):
    """
    Obtain a list of necessary inputs from the decision tree as it is currently
    defined. Return a list of the diagnostic names, the thresholds needed,
    and whether they are thresholded above or below these values. This output
    is used to create the CLI help, informing the user of the necessary inputs.

    Returns:
        list of str:
            Returns a list of strings, an entry for each diagnostic required,
            including threshold details.
    """

    # Get current weather symbol decision tree and populate a list of
    # required inputs for printing.
    if wxtree == 'high_resolution':
        queries = wxcode_decision_tree()
    elif wxtree == 'global':
        queries = wxcode_decision_tree_global()

    # Diagnostic names and threshold values.
    requirements = {}
    # How the data has been thresholded relative to these thresholds.
    relative = {}

    for query in queries.values():
        diagnostics = expand_nested_lists(query, 'diagnostic_fields')
        for index, diagnostic in enumerate(diagnostics):
            if diagnostic not in requirements:
                requirements[diagnostic] = []
                relative[diagnostic] = []
            requirements[diagnostic].extend(
                expand_nested_lists(query, 'diagnostic_thresholds')[index])
            relative[diagnostic].append(
                expand_nested_lists(query, 'diagnostic_conditions')[index])

    # Create a list of formatted strings that will be printed as part of the
    # CLI help.
    output = []
    for requirement in requirements:
        entries = np.array([entry for entry in requirements[requirement]])
        relations = np.array([entry for entry in relative[requirement]])
        _, thresholds = np.unique(np.array(
            [item.points.item() for item in entries]),
                                  return_index=True)
        output.append('{}; thresholds: {}'.format(
            requirement, ', '.join([
                '{} ({})'.format(str(threshold.points.item()),
                                 str(threshold.units)) for threshold, relation
                in zip(entries[thresholds], relations[thresholds])
            ])))
    return output
 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.assertEquals(val, 'a')
 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.assertEquals(val, 'a')
 def test_basic(self):
     """Test that the expand_nested_lists returns a list."""
     result = expand_nested_lists(self.dictionary, 'list')
     self.assertIsInstance(result, list)
Exemple #7
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.

        Raises:
            IOError:
                Raises an IOError if any of the required input data is missing.
                The error includes details of which fields are missing.
        """
        missing_data = []
        for query in self.queries.values():
            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:
                    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

                test_condition = (iris.Constraint(
                    coord_values={
                        threshold_name:
                        lambda cell:
                        (threshold *
                         (1. - self.float_tolerance) < cell < threshold *
                         (1. + self.float_tolerance))
                    },
                    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)
        return