def test_coefficient_values_for_fake_distribution(self):
        """Ensure the appropriate error is raised if the minimisation function
        requested is not available."""
        distribution = "fake"

        plugin = Plugin(distribution, self.current_cycle)
        msg = "Distribution requested"
        with self.assertRaisesRegex(KeyError, msg):
            plugin.process(self.historic_temperature_forecast_cube,
                           self.temperature_truth_cube)
Exemplo n.º 2
0
    def test_non_matching_units(self):
        """Test that an exception is raised if the historic forecasts and truth
        have non matching units."""
        self.historic_temperature_forecast_cube.convert_units("Fahrenheit")

        plugin = Plugin(self.distribution, self.current_cycle)

        msg = "The historic forecast units"
        with self.assertRaisesRegex(ValueError, msg):
            plugin.process(self.historic_temperature_forecast_cube,
                           self.temperature_truth_cube)
Exemplo n.º 3
0
 def test_basic(self):
     """Ensure that the optimised_coefficients are returned as a cube,
     with the expected number of coefficients."""
     plugin = Plugin(self.distribution, self.current_cycle)
     result = plugin.process(self.historic_temperature_forecast_cube,
                             self.temperature_truth_cube)
     self.assertIsInstance(result, iris.cube.Cube)
     self.assertEqual(len(result.data), len(self.coeff_names))
Exemplo n.º 4
0
    def test_historic_forecast_unit_conversion(self):
        """Ensure the expected optimised coefficients are generated,
        even if the input historic forecast cube has different units."""
        self.historic_temperature_forecast_cube.convert_units("Fahrenheit")
        desired_units = "Kelvin"

        plugin = Plugin(self.distribution,
                        self.current_cycle,
                        desired_units=desired_units)
        result = plugin.process(self.historic_temperature_forecast_cube,
                                self.temperature_truth_cube)

        self.assertEMOSCoefficientsAlmostEqual(
            result.data, self.expected_mean_predictor_gaussian)
Exemplo n.º 5
0
    def test_coefficient_values_for_gaussian_distribution(self):
        """Ensure that the values for the optimised_coefficients match the
        expected values, and the coefficient names also match
        expected values for a Gaussian distribution. In this case,
        a linear least-squares regression is used to construct the initial
        guess."""
        plugin = Plugin(self.distribution, self.current_cycle)
        result = plugin.process(self.historic_temperature_forecast_cube,
                                self.temperature_truth_cube)

        self.assertEMOSCoefficientsAlmostEqual(
            result.data, self.expected_mean_predictor_gaussian)
        self.assertArrayEqual(
            result.coord("coefficient_name").points, self.coeff_names)
Exemplo n.º 6
0
    def test_coefficients_gaussian_distribution_default_initial_guess(self):
        """Ensure that the values for the optimised_coefficients match the
        expected values, and the coefficient names also match
        expected values for a Gaussian distribution, where the default
        values for the initial guess are used, rather than using a linear
        least-squares regression to construct an initial guess."""
        expected = [-0.0002, 0.7977, 0.0004, 0.9973]
        plugin = Plugin(self.distribution, self.current_cycle)
        plugin.ESTIMATE_COEFFICIENTS_FROM_LINEAR_MODEL_FLAG = False
        result = plugin.process(self.historic_temperature_forecast_cube,
                                self.temperature_truth_cube)

        self.assertEMOSCoefficientsAlmostEqual(result.data, expected)
        self.assertArrayEqual(
            result.coord("coefficient_name").points, self.coeff_names)
Exemplo n.º 7
0
    def test_coefficients_gaussian_realizations_statsmodels(self):
        """Ensure that the values for the optimised_coefficients match the
        expected values, and the coefficient names also match
        expected values for a Gaussian distribution where the
        realizations are used as the predictor of the mean."""
        predictor_of_mean_flag = "realizations"

        plugin = Plugin(self.distribution,
                        self.current_cycle,
                        predictor_of_mean_flag=predictor_of_mean_flag)
        result = plugin.process(self.historic_temperature_forecast_cube,
                                self.temperature_truth_cube)
        self.assertEMOSCoefficientsAlmostEqual(
            result.data, self.expected_realizations_gaussian_statsmodels)
        self.assertArrayEqual(
            result.coord("coefficient_name").points,
            self.coeff_names_realizations)
Exemplo n.º 8
0
    def test_coefficients_truncated_gaussian_default_initial_guess(self):
        """Ensure that the values for the optimised_coefficients match the
        expected values, and the coefficient names also match
        expected values for a truncated Gaussian distribution, where the
        default values for the initial guess are used, rather than using a
        linear least-squares regression to construct an initial guess.."""
        distribution = "truncated_gaussian"

        plugin = Plugin(distribution, self.current_cycle)
        plugin.ESTIMATE_COEFFICIENTS_FROM_LINEAR_MODEL_FLAG = False
        result = plugin.process(self.historic_wind_speed_forecast_cube,
                                self.wind_speed_truth_cube)

        self.assertEMOSCoefficientsAlmostEqual(
            result.data, self.expected_mean_predictor_truncated_gaussian)
        self.assertArrayEqual(
            result.coord("coefficient_name").points, self.coeff_names)
Exemplo n.º 9
0
    def test_coefficient_values_for_gaussian_distribution_max_iterations(self):
        """Ensure that the values for the optimised_coefficients match the
        expected values, and the coefficient names also match
        expected values for a Gaussian distribution, when the max_iterations
        argument is specified."""
        max_iterations = 800

        plugin = Plugin(self.distribution,
                        self.current_cycle,
                        max_iterations=max_iterations)
        result = plugin.process(self.historic_temperature_forecast_cube,
                                self.temperature_truth_cube)

        self.assertEMOSCoefficientsAlmostEqual(
            result.data, self.expected_mean_predictor_gaussian)
        self.assertArrayEqual(
            result.coord("coefficient_name").points, self.coeff_names)
Exemplo n.º 10
0
    def test_coefficient_values_for_gaussian_distribution_mismatching_inputs(
            self):
        """Test that the values for the optimised coefficients match the
        expected values, and the coefficient names also match
        expected values for a Gaussian distribution for when the historic
        forecasts and truths input having some mismatches in validity time.
        """
        partial_historic_forecasts = (
            self.historic_forecasts[:2] +
            self.historic_forecasts[3:]).merge_cube()
        partial_truth = self.truth[1:].merge_cube()
        plugin = Plugin(self.distribution, self.current_cycle)
        result = plugin.process(partial_historic_forecasts, partial_truth)

        self.assertEMOSCoefficientsAlmostEqual(
            result.data, self.expected_mean_predictor_gaussian)
        self.assertArrayEqual(
            result.coord("coefficient_name").points, self.coeff_names)
Exemplo n.º 11
0
def main(argv=None):
    """Load in arguments for estimating coefficients for Ensemble Model Output
       Statistics (EMOS), otherwise known as Non-homogeneous Gaussian
       Regression (NGR). 2 sources of input data must be provided: historical
       forecasts and historical truth data (to use in calibration). The
       estimated coefficients are written to a netCDF file.
    """
    parser = ArgParser(
        description='Estimate coefficients for Ensemble Model Output '
                    'Statistics (EMOS), otherwise known as Non-homogeneous '
                    'Gaussian Regression (NGR)')
    parser.add_argument('distribution', metavar='DISTRIBUTION',
                        choices=['gaussian', 'truncated gaussian'],
                        help='The distribution that will be used for '
                             'calibration. This will be dependent upon the '
                             'input phenomenon. This has to be supported by '
                             'the minimisation functions in '
                             'ContinuousRankedProbabilityScoreMinimisers.')
    parser.add_argument('cycletime', metavar='CYCLETIME', type=str,
                        help='This denotes the cycle at which forecasts '
                             'will be calibrated using the calculated '
                             'EMOS coefficients. The validity time in the '
                             'output coefficients cube will be calculated '
                             'relative to this cycletime. '
                             'This cycletime is in the format '
                             'YYYYMMDDTHHMMZ.')
    # Filepaths for historic and truth data.
    parser.add_argument('historic_filepath', metavar='HISTORIC_FILEPATH',
                        help='A path to an input NetCDF file containing the '
                             'historic forecast(s) used for calibration.')
    parser.add_argument('truth_filepath', metavar='TRUTH_FILEPATH',
                        help='A path to an input NetCDF file containing the '
                             'historic truth analyses used for calibration.')
    parser.add_argument('output_filepath', metavar='OUTPUT_FILEPATH',
                        help='The output path for the processed NetCDF')
    # Optional arguments.
    parser.add_argument('--units', metavar='UNITS',
                        help='The units that calibration should be undertaken '
                             'in. The historical forecast and truth will be '
                             'converted as required.')
    parser.add_argument('--predictor_of_mean', metavar='PREDICTOR_OF_MEAN',
                        choices=['mean', 'realizations'], default='mean',
                        help='String to specify the predictor used to '
                             'calibrate the forecast mean. Currently the '
                             'ensemble mean ("mean") and the ensemble '
                             'realizations ("realizations") are supported as '
                             'options. Default: "mean".')
    parser.add_argument('--max_iterations', metavar='MAX_ITERATIONS',
                        type=np.int32, default=1000,
                        help='The maximum number of iterations allowed '
                             'until the minimisation has converged to a '
                             'stable solution. If the maximum number '
                             'of iterations is reached, but the '
                             'minimisation has not yet converged to a '
                             'stable solution, then the available solution '
                             'is used anyway, and a warning is raised.'
                             'This may be modified for testing purposes '
                             'but otherwise kept fixed. If the '
                             'predictor_of_mean is "realizations", '
                             'then the number of iterations may require '
                             'increasing, as there will be more coefficients '
                             'to solve for.')
    args = parser.parse_args(args=argv)

    historic_forecast = load_cube(args.historic_filepath)
    truth = load_cube(args.truth_filepath)

    # Estimate coefficients using Ensemble Model Output Statistics (EMOS).
    estcoeffs = EstimateCoefficientsForEnsembleCalibration(
        args.distribution, args.cycletime, desired_units=args.units,
        predictor_of_mean_flag=args.predictor_of_mean,
        max_iterations=args.max_iterations)
    coefficients = (
        estcoeffs.process(historic_forecast, truth))

    save_netcdf(coefficients, args.output_filepath)