Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions envs/environment_a.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ dependencies:
- pygam<0.11
- pysteps
- python-stratify
- tensorflow
- tensorflow-hub
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please update the list of exclusions in environment_b to indicate that the temporal interpolation / gap filling CLIs are unsupported by that environment:

# It does not include dependencies for the following IMPROVER CLIs:

# Development
- astroid
- codacy-coverage
Expand Down
2 changes: 2 additions & 0 deletions envs/latest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ dependencies:
- pysteps
- treelite
- tl2cgen
- tensorflow
- tensorflow-hub
# Development
- astroid
- codacy-coverage
Expand Down
97 changes: 97 additions & 0 deletions improver/cli/forecast_period_gap_filler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#!/usr/bin/env python
# (C) Crown Copyright, Met Office. All rights reserved.
#
# This file is part of 'IMPROVER' and is released under the BSD 3-Clause license.
# See LICENSE in the root of the repository for full licensing details.
"""CLI to fill gaps in forecast period sequences using temporal interpolation."""

from improver import cli


@cli.clizefy
@cli.with_output
def process(
*cubes: cli.inputcube,
interval_in_mins: int = None,
interpolation_method: str = "linear",
cluster_sources_attribute: str = None,
interpolation_window_in_hours: int = None,
model_path: str = None,
scaling: str = "minmax",
clipping_bounds: cli.comma_separated_list = (0.0, 1.0),
):
"""Fill gaps in forecast period sequences using temporal interpolation.
Identifies missing forecast periods in a sequence and fills them using
temporal interpolation. Can optionally regenerate periods at forecast
source transitions when cluster_sources configuration is provided.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your comments here and below talk about forecast periods but that's potentially misleading. For the cube shown below, where the data is all valid at the same time I have a set of forecast periods due to differing forecast reference times:

duration_of_sunshine / (seconds)    (forecast_reference_time: 15; latitude: 960; longitude: 1280)
    Dimension coordinates:
        time                             x             -               -
        latitude                         -             x               -
        longitude                        -             -               x
    Auxiliary coordinates:
        forecast_period                  x             -               -
    Scalar coordinates:
        time     2012-07-25 00:00:00
        originating_centre          European Centre for Medium Range Weather Forecasts
    Attributes:
        Conventions                 'CF-1.7'
        GRIB_PARAM                  GRIB1:t032c128n098

It is not appropriate to use this tool to try and fill in a missing forecast initialisation time but that's what would happen in this case. The IMPROVER conventions are to have a single validity time in a cube, which this example adheres to, it simply isn't structured in the way we normally do it.

All of this is to simply say that I think it is important to make clear here that the intention is to fill in missing validity times in a sequence of validity times, providing a more complete forecast evolution through time.

Args:
cubes (iris.cube.CubeList or iris.cube.Cube):
A cube or cubelist containing cubes with potentially missing
forecast periods. Can be a single Cube with forecast_period as
a dimension coordinate (will be sliced automatically), or multiple
individual cubes representing different forecast periods. All cubes
should have forecast_period coordinates and represent the same
diagnostic at different forecast periods.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment above as to why this is potentially misleading.

interval_in_mins (int):
The expected interval between forecast periods in minutes.
Used to identify gaps in the sequence. If not provided, gaps
will not be filled, but periods can still be regenerated if
cluster_sources_attribute is set.
interpolation_method (str):
["linear", "solar", "daynight", "google_film"]
Method of interpolation to use. Default is "linear".
- "linear": Standard linear interpolation
- "solar": Interpolation scaled by solar elevation angle
- "daynight": Linear interpolation with night-time values set to zero
- "google_film": Deep learning model for frame interpolation
cluster_sources_attribute (str):
Name of cube attribute containing cluster sources dictionary.
This dictionary maps realization indices to their forecast sources
and periods. When provided with interpolation_window_in_hours,
enables identification and regeneration of forecast periods at
source transitions. Format: {realization_index: {source_name: [periods]}}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sufficiently au fait with the clustering stuff yet to understand how the forecast period / validity time / forecast reference time coordinates are used across models and whether that's guided you towards all the discussion of forecast periods here.

interpolation_window_in_hours (int):
Time window (in hours) to use as a +/- range around forecast
source transition points. Used with cluster_sources_attribute
to identify which periods should be regenerated. For example,
if set to 3 hours and a transition occurs at T+24, periods
T+21, T+24, and T+27 will be regenerated if they fall within
the sequence.
model_path (str):
Path to the TensorFlow Hub module for the Google FILM model.
Required when interpolation_method is "google_film". Can be a
local path or a TensorFlow Hub URL.
scaling (str):
Scaling method to apply to the data before interpolation when
using "google_film" method. Options are "log10" or "minmax".
Default is "minmax".
clipping_bounds (str):
Comma-separated lower and upper bounds for clipping interpolated
values when using "google_film" method. E.g. "0.0,1.0".
Default is "0.0,1.0".
Returns:
iris.cube.Cube:
A single merged cube with all forecast periods filled. The cube
will have time as a dimension coordinate and will include:
- All original time slices (except those regenerated at transitions)
- Interpolated slices filling identified gaps
- Regenerated slices at source transitions (if configured)
"""
from improver.utilities.temporal_interpolation import ForecastPeriodGapFiller

plugin = ForecastPeriodGapFiller(
interval_in_minutes=interval_in_mins,
interpolation_method=interpolation_method,
cluster_sources_attribute=cluster_sources_attribute,
interpolation_window_in_hours=interpolation_window_in_hours,
model_path=model_path,
scaling=scaling,
clipping_bounds=clipping_bounds,
)

result = plugin.process(*cubes)

return result
29 changes: 27 additions & 2 deletions improver/cli/temporal_interpolate.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ def process(
accumulation: bool = False,
max: bool = False,
min: bool = False,
model_path: str = None,
scaling: str = "minmax",
clipping_bounds: cli.comma_separated_list = None,
):
"""Interpolate data between validity times.

Expand Down Expand Up @@ -47,11 +50,12 @@ def process(
by a comma.
If times are set, interval_in_mins can not be used.
interpolation_method (str):
["linear", "solar", "daynight"]
["linear", "solar", "daynight", "google_film"]
Specifies the interpolation method;
solar interpolates using the solar elevation,
daynight uses linear interpolation but sets night time points to
0.0 linear is linear interpolation.
0.0, linear is linear interpolation, google_film uses a deep
learning model for frame interpolation.
accumulation:
Set True if the diagnostic being temporally interpolated is a
period accumulation. The output will be renormalised to ensure
Expand All @@ -69,6 +73,18 @@ def process(
period minimum. Trends between adjacent input periods will be used
to provide variation across the interpolated periods where these
are consistent with the inputs.
model_path (str):
Path to the TensorFlow model for google_film interpolation.
Required when interpolation_method is "google_film". Can be a
local path or a TensorFlow Hub URL.
scaling (str):
Scaling method to apply to the data before interpolation when
using google_film method. Options are "log10" or "minmax".
Default is "minmax".
clipping_bounds (str):
Comma-separated lower and upper bounds for clipping interpolated
values when using google_film method. E.g. "0.0,1.0".
Default is "0.0,1.0".
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should make clear that the clipping is applied to the scaled values prior to rescaling and so the returned values may not be within these bounds.

Returns:
iris.cube.CubeList:
A list of cubes interpolated to the desired times. The
Expand All @@ -88,12 +104,21 @@ def process(
if times is not None:
times = [cycletime_to_datetime(timestr) for timestr in times]

# Convert clipping_bounds from comma-separated list to tuple
if clipping_bounds is not None:
clipping_bounds = tuple(float(x) for x in clipping_bounds)
else:
clipping_bounds = (0.0, 1.0)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can you not handle clipping bounds in the same way as the other CLI to avoid adding things here that Carwyn will have to later move to a meta plugin?

result = TemporalInterpolation(
interval_in_minutes=interval_in_mins,
times=times,
interpolation_method=interpolation_method,
accumulation=accumulation,
max=max,
min=min,
model_path=model_path,
scaling=scaling,
clipping_bounds=clipping_bounds,
)(start_cube, end_cube)
return MergeCubes()(result)
Loading
Loading