From b3d3c8460342455d8433e0ba6a6ec6f16876ada8 Mon Sep 17 00:00:00 2001 From: "benjamin.ayliffe" Date: Tue, 17 Oct 2023 14:05:03 +0100 Subject: [PATCH 1/2] Loosen dz scaling application to use the longest forecast period scaling coefficient available if no forecast period has a greater length than the forecast itself. --- improver/calibration/dz_rescaling.py | 18 ++++++++---------- .../dz_rescaling/test_apply_dz_rescaling.py | 13 ++++++------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/improver/calibration/dz_rescaling.py b/improver/calibration/dz_rescaling.py index 74abf125c1..cd9f8f004a 100644 --- a/improver/calibration/dz_rescaling.py +++ b/improver/calibration/dz_rescaling.py @@ -373,7 +373,9 @@ def _create_forecast_period_constraint( """Create a forecast period constraint to identify the most appropriate forecast period from the scaled_dz to extract. The most appropriate scaled dz is selected by choosing the nearest forecast period that is greater than or - equal to the forecast period of the forecast. + equal to the forecast period of the forecast. If no forecast periods in the + scaled fz cube are greater than the forecast period of the forecast, the + longest available forecast period is used. Args: forecast: Forecast to be adjusted using dz rescaling. @@ -391,16 +393,12 @@ def _create_forecast_period_constraint( - forecast.coord("forecast_period").points ) - if not any(fp_diff >= 0): - (fp_hour,) = forecast.coord("forecast_period").points / SECONDS_IN_HOUR - msg = ( - "There is no scaled version of the difference in altitude for " - f"a forecast period greater than or equal to {fp_hour}" - ) - raise ValueError(msg) + if any(fp_diff >= 0): + fp_index = np.argmax(fp_diff >= 0) + chosen_fp = scaled_dz.coord("forecast_period").points[fp_index] + else: + chosen_fp = scaled_dz.coord("forecast_period").points[-1] - fp_index = np.argmax(fp_diff >= 0) - chosen_fp = scaled_dz.coord("forecast_period").points[fp_index] return iris.Constraint(forecast_period=chosen_fp) @staticmethod diff --git a/improver_tests/calibration/dz_rescaling/test_apply_dz_rescaling.py b/improver_tests/calibration/dz_rescaling/test_apply_dz_rescaling.py index e326ed5d29..a1331efbf3 100644 --- a/improver_tests/calibration/dz_rescaling/test_apply_dz_rescaling.py +++ b/improver_tests/calibration/dz_rescaling/test_apply_dz_rescaling.py @@ -158,7 +158,7 @@ def _create_scaling_factor_cube( @pytest.mark.parametrize("wmo_id", [True, False]) -@pytest.mark.parametrize("forecast_period", [6, 18]) +@pytest.mark.parametrize("forecast_period", [6, 18, 30]) @pytest.mark.parametrize("frt_hour", [3, 12]) @pytest.mark.parametrize("scaling_factor", [0.99, 1.01]) @pytest.mark.parametrize("forecast_period_offset", [0, -1, -5]) @@ -179,7 +179,7 @@ def test_apply_dz_rescaling( cube to ensure the plugin always snaps to the next largest forecast_time when the precise point is not available. frt_hour_offset (hours) alters the forecast reference time hour within the forecast - whilst the forececast reference time hour of the scaling factor remains the same. + whilst the forecast reference time hour of the scaling factor remains the same. This checks that the a mismatch in the forecast reference time hour can still result in a match, if a leniency is specified. """ @@ -199,8 +199,10 @@ def test_apply_dz_rescaling( forecast_period + forecast_period_offset, forecast, ) + # Use min(fp, 24) here to ensure that the scaling cube contains + # the scaling factor for the last forecast_period if nowhere else. scaling_factor = _create_scaling_factor_cube( - frt_hour, forecast_period, scaling_factor + frt_hour, min(forecast_period, 24), scaling_factor ) kwargs = {} @@ -275,10 +277,7 @@ def test_mismatching_sites(): @pytest.mark.parametrize( "forecast_period,frt_hour,exception", - [ - (25, 3, "forecast period greater than or equal to 25"), - (7, 1, "forecast reference time hour equal to 1"), - ], + [(7, 1, "forecast reference time hour equal to 1")], ) def test_no_appropriate_scaled_dz(forecast_period, frt_hour, exception): """Test an exception is raised if no appropriate scaled version of the difference From 8b629f72aa851f8327f739affc8bf569c62ded3e Mon Sep 17 00:00:00 2001 From: "benjamin.ayliffe" Date: Thu, 19 Oct 2023 13:12:26 +0100 Subject: [PATCH 2/2] Adjustments to doc-strings as per review. --- improver/calibration/dz_rescaling.py | 2 +- improver/cli/apply_dz_rescaling.py | 6 ++++-- .../calibration/dz_rescaling/test_apply_dz_rescaling.py | 7 +++++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/improver/calibration/dz_rescaling.py b/improver/calibration/dz_rescaling.py index cd9f8f004a..d7730e7f63 100644 --- a/improver/calibration/dz_rescaling.py +++ b/improver/calibration/dz_rescaling.py @@ -374,7 +374,7 @@ def _create_forecast_period_constraint( forecast period from the scaled_dz to extract. The most appropriate scaled dz is selected by choosing the nearest forecast period that is greater than or equal to the forecast period of the forecast. If no forecast periods in the - scaled fz cube are greater than the forecast period of the forecast, the + scaled dz cube are greater than the forecast period of the forecast, the longest available forecast period is used. Args: diff --git a/improver/cli/apply_dz_rescaling.py b/improver/cli/apply_dz_rescaling.py index 41542a6c39..3bd6b51bda 100755 --- a/improver/cli/apply_dz_rescaling.py +++ b/improver/cli/apply_dz_rescaling.py @@ -56,8 +56,10 @@ def process( forecast period and forecast reference_time_hour pair within the rescaling cube are chosen using the forecast reference time hour from the forecast and the nearest forecast period that is greater than or - equal to the forecast period of the forecast. This cube is generated - using the estimate_dz_rescaling CLI. + equal to the forecast period of the forecast. However, if the forecast + period of the forecast exceeds all forecast periods within the rescaling + cube, the scaling factor from the maximum forecast period is used. + This cube is generated using the estimate_dz_rescaling CLI. site_id_coord (str): The name of the site ID coordinate. This defaults to 'wmo_id'. diff --git a/improver_tests/calibration/dz_rescaling/test_apply_dz_rescaling.py b/improver_tests/calibration/dz_rescaling/test_apply_dz_rescaling.py index a1331efbf3..3002e4c464 100644 --- a/improver_tests/calibration/dz_rescaling/test_apply_dz_rescaling.py +++ b/improver_tests/calibration/dz_rescaling/test_apply_dz_rescaling.py @@ -177,7 +177,9 @@ def test_apply_dz_rescaling( contains the scaling_factor value. forecast_period_offset (hours) adjusts the forecast period coord on the forecast cube to ensure the plugin always snaps to the next largest forecast_time when the - precise point is not available. + precise point is not available except when the forecast period of the forecast + exceeds all forecast periods within the scaling factor cube. In this case, the + last forecast period within the scaling factor cube will be used. frt_hour_offset (hours) alters the forecast reference time hour within the forecast whilst the forecast reference time hour of the scaling factor remains the same. This checks that the a mismatch in the forecast reference time hour can still @@ -200,7 +202,8 @@ def test_apply_dz_rescaling( forecast, ) # Use min(fp, 24) here to ensure that the scaling cube contains - # the scaling factor for the last forecast_period if nowhere else. + # the scaling factor for the last forecast_period if the specified + # forecast period is beyond the T+24 limit of the scaling cube. scaling_factor = _create_scaling_factor_cube( frt_hour, min(forecast_period, 24), scaling_factor )