def test_basic_descending(self): """Test that a constraint is created, if the input coordinates are descending.""" values = [0.1, 0.03] result = create_sorted_lambda_constraint(self.coord_name, values) self.assertIsInstance(result, iris.Constraint) self.assertEqual(list(result._coord_values.keys()), [self.coord_name]) result_cube = self.precip_cube.extract(result) self.assertArrayAlmostEqual(result_cube.data, self.expected_data)
def test_non_default_tolerance(self): """Test that a constraint is created, if the input coordinates are made more fuzzy by use of a non-standard tolerance. The default tolerance is 1.0E-7, here we make it large enough to extract all the available thresholds using two bounds; this is testing an extreme not a desirable use case.""" values = [0.03, 0.6] result = create_sorted_lambda_constraint(self.coord_name, values, tolerance=0.9) self.assertIsInstance(result, iris.Constraint) self.assertEqual(list(result._coord_values.keys()), [self.coord_name]) result_cube = self.precip_cube.extract(result) self.assertArrayAlmostEqual(result_cube.data, self.precip_cube.data)
def create_range_constraint(coord_name: str, value: str) -> Constraint: """ Create a constraint that is representative of a range. Args: coord_name: Name of the coordinate for which the constraint will be created. value: A string containing the range information. It is assumed that the input value is of the form: "[2:10]". Returns: The constraint that has been created to represent the range. """ value = value.replace("[", "").replace("]", "").split(":") constr = create_sorted_lambda_constraint(coord_name, value) return constr
def parse_constraint_list( constraints: List[str], units: Optional[List[str]] = None ) -> Tuple[Constraint, Optional[Dict], Optional[float], Optional[Dict]]: """ For simple constraints of a key=value format, these are passed in as a list of strings and converted to key-value pairs prior to creating the constraints. For more complex constraints, the list of strings given as input are evaluated by parsing for specific identifiers and then the constraints are created as required. The simple key-value pairs and other constraints are merged into a single constraint. Args: constraints: List of string constraints with keys and values split by "=": e.g: ["kw1=val1", "kw2 = val2", "kw3=val3"], where the vals could include ranges e.g. [0:20] or ranges with a step value e.g. [0:20:3]. units: List of units (as strings) corresponding to each coordinate in the list of constraints. One or more "units" may be None, and units may only be associated with coordinate constraints. Returns: - A combination of all the constraints that were supplied. - A dictionary of unit keys and values - A list containing the min and max values for a longitude constraint - A dictionary of coordinate and the step value, i.e. a step of 2 will skip every other point """ if units is None: list_units = len(constraints) * [None] units_dict = None else: if len(units) != len(constraints): msg = "units list must match constraints" raise ValueError(msg) list_units = units units_dict = {} simple_constraints_dict = {} complex_constraints = [] longitude_constraint = None thinning_values = {} for constraint_pair, unit_val in zip(constraints, list_units): key, value = constraint_pair.split("=", 1) key = key.strip(" ") value = value.strip(" ") if ":" in value: range_dict = parse_range_string_to_dict(value) # longitude is a circular coordinate, so needs to be treated in a # different way to a normal constraint if key == "longitude": longitude_constraint = [ FLOAT_DTYPE(range_dict[k]) for k in ["min", "max"] ] else: complex_constraints.append( create_sorted_lambda_constraint( key, [range_dict["min"], range_dict["max"]] ) ) if range_dict.get("step", None): thinning_values[key] = int(range_dict["step"]) else: try: typed_value = literal_eval(value) except ValueError: simple_constraints_dict[key] = value else: simple_constraints_dict[key] = create_constraint(typed_value) if unit_val is not None and unit_val.capitalize() != "None": units_dict[key] = unit_val.strip(" ") if simple_constraints_dict: simple_constraints = Constraint(**simple_constraints_dict) else: simple_constraints = None constraints = simple_constraints for constr in complex_constraints: constraints = constraints & constr return constraints, units_dict, longitude_constraint, thinning_values