diff --git a/improver/lightning.py b/improver/lightning.py index 558f9e134b..35cd056ff9 100644 --- a/improver/lightning.py +++ b/improver/lightning.py @@ -74,7 +74,8 @@ def _get_inputs(cubes: CubeList) -> Tuple[Cube, Cube]: """ Separates CAPE and precipitation rate cubes and checks that the following match: forecast_reference_time, spatial coords, time-bound interval and - that CAPE time is exactly one hour older than precipitation rate time. + that CAPE time is at the lower bound of precipitation rate time. + The precipitation rate data must represent a period of 1 or 3 hours. """ cape = cubes.extract( iris.Constraint( @@ -100,14 +101,14 @@ def _get_inputs(cubes: CubeList) -> Tuple[Cube, Cube]: raise ValueError(f"No cube named precipitation_rate_max found in {cubes}") (cape_time,) = list(cape.coord("time").cells()) (precip_time,) = list(precip.coord("time").cells()) - if cape_time.point + timedelta(hours=1) != precip_time.point: + if cape_time.point != precip_time.bound[0]: raise ValueError( - f"CAPE cube time ({cape_time.point}) should be valid one hour earlier " - f"than precipitation_rate_max cube time ({precip_time.point})." + f"CAPE cube time ({cape_time.point}) should be valid at the " + f"precipitation_rate_max cube lower bound ({precip_time.bound[0]})." ) - if np.diff(precip_time.bound) != timedelta(hours=1): + if np.diff(precip_time.bound) not in [timedelta(hours=1), timedelta(hours=3)]: raise ValueError( - f"Precipitation_rate_max cube time window must be one hour, " + f"Precipitation_rate_max cube time window must be one or three hours, " f"not {np.diff(precip_time.bound)}." ) if cape.coord("forecast_reference_time") != precip.coord( diff --git a/improver_tests/lightning/test_LightningFromCapePrecip.py b/improver_tests/lightning/test_LightningFromCapePrecip.py index 887f08fd5d..2d14e3854e 100644 --- a/improver_tests/lightning/test_LightningFromCapePrecip.py +++ b/improver_tests/lightning/test_LightningFromCapePrecip.py @@ -129,6 +129,20 @@ def test_basic(cape_cube, precip_cube, expected_cube): assert np.allclose(result.data, expected_cube.data) +def test_3h_cubes(cape_cube, precip_cube, expected_cube): + """Run the plugin again with 3h cubes""" + cape_cube.coord("time").points = cape_cube.coord("time").points - 2 * 3600 + bounds = precip_cube.coord("time").bounds + precip_cube.coord("time").bounds = (bounds[0][0] - 2 * 3600, bounds[0][1]) + precip_cube.rename("precipitation_rate_max-PT03H") + expected_cube.coord("time").bounds = (bounds[0][0] - 2 * 3600, bounds[0][1]) + result = LightningFromCapePrecip()(CubeList([cape_cube, precip_cube])) + assert result.xml().splitlines(keepends=True) == expected_cube.xml().splitlines( + keepends=True + ) + assert np.allclose(result.data, expected_cube.data) + + def test_with_model_attribute(cape_cube, precip_cube, expected_cube): """Run the plugin with model_id_attr and check the result cube matches the expected_cube""" expected_cube.attributes["mosg__model_configuration"] = "gl_ens" @@ -142,21 +156,18 @@ def test_with_model_attribute(cape_cube, precip_cube, expected_cube): def break_time_point(cape_cube, precip_cube): - """Modifies precip_cube time points to be incremented by 1 second and + """Modifies cape_cube time points to be incremented by 1 second and returns the error message this will trigger""" - precip_cube.coord("time").points = precip_cube.coord("time").points + 1 - return ( - r"CAPE cube time .* should be valid one hour earlier than " - r"precipitation_rate_max cube time .*" - ) + cape_cube.coord("time").points = cape_cube.coord("time").points + 1 + return r"CAPE cube time .* should be valid at the precipitation_rate_max cube lower bound .*" def break_time_bound(cape_cube, precip_cube): - """Modifies lower bound on precip_cube time coord to be incremented by 1 second and + """Modifies upper bound on precip_cube time coord to be incremented by 1 second and returns the error message this will trigger""" bounds = precip_cube.coord("time").bounds - precip_cube.coord("time").bounds = (bounds[0][0] + 1, bounds[0][1]) - return r"Precipitation_rate_max cube time window must be one hour, not .*" + precip_cube.coord("time").bounds = (bounds[0][0], bounds[0][1] + 1) + return r"Precipitation_rate_max cube time window must be one or three hours, not .*" def break_reference_time(cape_cube, precip_cube):