def test_check_coordinate_name(self): """ Test that the utility returns an iris.cube.Cube with an ensemble_member_id coordinate. """ rename_coordinate(self.cube, "realization", "ensemble_member_id") self.assertTrue(self.cube.coord("ensemble_member_id"))
def test_absent_original_coord(self): """ Test that the utility returns an iris.cube.Cube after the renaming was not successful, as the original coordinate was not found in the cube. """ rename_coordinate(self.cube, "fake", "ensemble_member_id") self.assertFalse(self.cube.coords("ensemble_member_id"))
def test_check_type_error(self): """ Test that a TyoeError is raised, if the input variable is not an iris.cube.Cube. """ fake_cube = "fake" msg = "A Cube or CubeList is not provided for renaming" with self.assertRaisesRegexp(TypeError, msg): rename_coordinate(fake_cube, "realization", "ensemble_member_id")
def test_basic_cubelist(self): """ Test that the utility returns an iris.cube.CubeList and that the cubes in the cubelist have an ensemble_member_id coordinate. """ cube1 = self.cube.copy() cube2 = self.cube.copy() cubes = iris.cube.CubeList([cube1, cube2]) rename_coordinate(cubes, "realization", "ensemble_member_id") self.assertIsInstance(cubes, iris.cube.CubeList) for cube in cubes: self.assertTrue(cube.coord("ensemble_member_id"))
def apply_params_entry(self): """ Wrapping function to calculate the forecast predictor and forecast variance prior to applying coefficients to the current forecast. Returns: (tuple) : tuple containing: **calibrated_forecast_predictor** (CubeList): CubeList containing both the calibrated version of the ensemble predictor, either the ensemble mean/members. **calibrated_forecast_variance** (CubeList): CubeList containing both the calibrated version of the ensemble variance, either the ensemble mean/members. **calibrated_forecast_coefficients** (CubeList): CubeList containing both the coefficients for calibrating the ensemble. """ # Ensure predictor_of_mean_flag is valid. check_predictor_of_mean_flag(self.predictor_of_mean_flag) rename_coordinate( self.current_forecast, "ensemble_member_id", "realization") current_forecast_cubes = concatenate_cubes( self.current_forecast) if self.predictor_of_mean_flag.lower() in ["mean"]: forecast_predictors = current_forecast_cubes.collapsed( "realization", iris.analysis.MEAN) elif self.predictor_of_mean_flag.lower() in ["members"]: forecast_predictors = current_forecast_cubes forecast_vars = current_forecast_cubes.collapsed( "realization", iris.analysis.VARIANCE) (calibrated_forecast_predictor, calibrated_forecast_var, calibrated_forecast_coefficients) = self._apply_params( forecast_predictors, forecast_vars, self.optimised_coeffs, self.coeff_names, self.predictor_of_mean_flag) return (calibrated_forecast_predictor, calibrated_forecast_var, calibrated_forecast_coefficients)
def estimate_coefficients_for_ngr(self, current_forecast, historic_forecast, truth): """ Using Nonhomogeneous Gaussian Regression/Ensemble Model Output Statistics, estimate the required coefficients from historical forecasts. The main contents of this method is: 1. Metadata checks to ensure that the current forecast, historic forecast and truth exist in a form that can be processed. 2. Loop through times within the concatenated current forecast cube: 1. Extract the desired forecast period from the historic forecasts to match the current forecasts. Apply unit conversion to ensure that historic forecasts have the desired units for calibration. 2. Extract the relevant truth to co-incide with the time within the historic forecasts. Apply unit conversion to ensure that the truth has the desired units for calibration. 3. Calculate mean and variance. 4. Calculate initial guess at coefficient values by performing a linear regression, if requested, otherwise default values are used. 5. Perform minimisation. Args: current_forecast (Iris Cube or CubeList): The cube containing the current forecast. historical_forecast (Iris Cube or CubeList): The cube or cubelist containing the historical forecasts used for calibration. truth (Iris Cube or CubeList): The cube or cubelist containing the truth used for calibration. Returns: (tuple): tuple containing: **optimised_coeffs** (Dictionary): Dictionary containing a list of the optimised coefficients for each date. **coeff_names** (List): The name of each coefficient. """ def convert_to_cubelist(cubes, cube_type="forecast"): """ Convert cube to cubelist, if necessary. Args: cubes (Iris Cube or Iris CubeList): Cube to be converted to CubeList. cube_type (String): String to describe the cube, which is being converted to a CubeList. Raises ------ TypeError: The input cube is not an Iris cube. """ if not isinstance(cubes, iris.cube.CubeList): cubes = iris.cube.CubeList([cubes]) for cube in cubes: if not isinstance(cube, iris.cube.Cube): msg = ("The input data within the {} " "is not an Iris Cube.".format(cube_type)) raise TypeError(msg) return cubes # Ensure predictor_of_mean_flag is valid. check_predictor_of_mean_flag(self.predictor_of_mean_flag) # Setting default values for optimised_coeffs and coeff_names. optimised_coeffs = {} coeff_names = ["gamma", "delta", "a", "beta"] # Set default values for whether there are NaN values within the # initial guess. nan_in_initial_guess = False for var in [current_forecast, historic_forecast, truth]: if (isinstance(var, iris.cube.Cube) or isinstance(var, iris.cube.CubeList)): current_forecast_cubes = current_forecast historic_forecast_cubes = historic_forecast truth_cubes = truth else: msg = ("{} is not a Cube or CubeList." "Returning default values for optimised_coeffs {} " "and coeff_names {}.").format(var, optimised_coeffs, coeff_names) warnings.warn(msg) return optimised_coeffs, coeff_names current_forecast_cubes = (convert_to_cubelist( current_forecast_cubes, cube_type="current forecast")) historic_forecast_cubes = (convert_to_cubelist( historic_forecast_cubes, cube_type="historic forecast")) truth_cubes = convert_to_cubelist(truth_cubes, cube_type="truth") if (len(current_forecast_cubes) == 0 or len(historic_forecast_cubes) == 0 or len(truth_cubes) == 0): msg = ("Insufficient input data present to estimate " "coefficients using NGR. " "\nNumber of current_forecast_cubes: {}" "\nNumber of historic_forecast_cubes: {}" "\nNumber of truth_cubes: {}".format( len(current_forecast_cubes), len(historic_forecast_cubes), len(truth_cubes))) warnings.warn(msg) return optimised_coeffs, coeff_names rename_coordinate(current_forecast_cubes, "ensemble_member_id", "realization") rename_coordinate(historic_forecast_cubes, "ensemble_member_id", "realization") current_forecast_cubes = concatenate_cubes(current_forecast_cubes) historic_forecast_cubes = concatenate_cubes(historic_forecast_cubes) truth_cubes = concatenate_cubes(truth_cubes) for current_forecast_cube in current_forecast_cubes.slices_over( "time"): date = unit.num2date( current_forecast_cube.coord("time").points, current_forecast_cube.coord("time").units.name, current_forecast_cube.coord("time").units.calendar)[0] # Extract desired forecast_period from historic_forecast_cubes. forecast_period_constr = iris.Constraint( forecast_period=current_forecast_cube.coord( "forecast_period").points) historic_forecast_cube = historic_forecast_cubes.extract( forecast_period_constr) # Extract truth matching the time of the historic forecast. truth_constr = iris.Constraint( forecast_reference_time=historic_forecast_cube.coord( "time").points) truth_cube = truth_cubes.extract(truth_constr) if truth_cube is None: msg = ("Unable to calibrate for the time points {} " "as no truth data is available." "Moving on to try to calibrate " "next time point.".format( historic_forecast_cube.coord("time").points)) warnings.warn(msg) continue # Make sure inputs have the same units. historic_forecast_cube.convert_units(self.desired_units) truth_cube.convert_units(self.desired_units) if self.predictor_of_mean_flag.lower() in ["mean"]: no_of_members = None forecast_predictor = historic_forecast_cube.collapsed( "realization", iris.analysis.MEAN) elif self.predictor_of_mean_flag.lower() in ["members"]: no_of_members = len( historic_forecast_cube.coord("realization").points) forecast_predictor = historic_forecast_cube forecast_var = historic_forecast_cube.collapsed( "realization", iris.analysis.VARIANCE) # Computing initial guess for EMOS coefficients # If no initial guess from a previous iteration, or if there # are NaNs in the initial guess, calculate an initial guess. if "initial_guess" not in locals() or nan_in_initial_guess: initial_guess = self.compute_initial_guess( truth_cube, forecast_predictor, self.predictor_of_mean_flag, self.ESTIMATE_COEFFICIENTS_FROM_LINEAR_MODEL_FLAG, no_of_members=no_of_members) if np.any(np.isnan(initial_guess)): nan_in_initial_guess = True if not nan_in_initial_guess: # Need to access the x attribute returned by the # minimisation function. optimised_coeffs[date] = ( self.minimiser.crps_minimiser_wrapper( initial_guess, forecast_predictor, truth_cube, forecast_var, self.predictor_of_mean_flag, self.distribution.lower())) initial_guess = optimised_coeffs[date] else: optimised_coeffs[date] = initial_guess return optimised_coeffs, coeff_names
def test_basic_cube(self): """Test that the utility returns an iris.cube.Cube.""" rename_coordinate(self.cube, "realization", "ensemble_member_id") self.assertIsInstance(self.cube, iris.cube.Cube)