Skip to content
Merged
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
69 changes: 69 additions & 0 deletions src/evalml/leadtimes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import logging
import re

# Params with no value at lead time 0. Two distinct reasons land here:
# - period-accumulated params (TOT_PREC/tp) verify a [lead - period, lead]
# window, so they have no value at lead times shorter than one step spacing
# (e.g. no 0h precip map).
# - diagnostic params (e.g. CLCT/tcc in AIFS) aren't part of the model's input state,
# so they're simply absent from the initial-state GRIB file written at step 0.
# Cloud cover is prognostic in some models (Varda-single RC); listing it here
# only drops their 0h animation frame.
# Short and canonical names both appear across the workflow (showcases vs maps).
PARAMS_WITHOUT_STEP_ZERO_VALUE = {
"TOT_PREC",
"tp",
"CLCT",
"tcc",
"CLCL",
"lcc",
"CLCM",
"mcc",
"CLCH",
"hcc",
"SSRD",
"ssrd",
}


def resolve_leadtimes(steps_spec, requested="all", param=None):
"""Lead times to compute for a single participant.

A run or baseline produces only the lead times in its own ``steps`` spec
(``start/stop/step``, hours). This returns those of the ``requested``
selection that the participant actually produces — the literal ``"all"``
(every produced lead time) or an explicit list of ints — so a 36h lead is
never requested of an ICON-CH1 baseline (steps ``0/33/6``), nor a >120h
lead of ICON-CH2. Explicitly requested lead times the participant cannot
produce are skipped with a warning. For accumulated ``param``s, lead times
shorter than one step spacing are dropped (no accumulation window).
"""
start, end, step = map(int, steps_spec.split("/"))
supported = set(range(start, end + 1, step))
wanted = supported if requested == "all" else set(requested)

unsupported = sorted(wanted - supported)
if unsupported:
logging.getLogger("snakemake").warning(
"Skipping lead time(s) %sh: not produced by forecast steps '%s'.",
unsupported,
steps_spec,
)

valid = wanted & supported
# Showcase-facing names encode an accumulation window as a numeric suffix
# (e.g. "TOT_PREC1", "TOT_PREC6"); strip it before checking membership so
# those aggregated variants are recognized the same as their base param.
base_param = re.sub(r"\d+$", "", param) if param else param
if (
param in PARAMS_WITHOUT_STEP_ZERO_VALUE
or base_param in PARAMS_WITHOUT_STEP_ZERO_VALUE
):
# For aggregated params (e.g. TOT_PREC6), the accumulation window encoded
# in the suffix is the minimum valid lead time — not the run's step spacing.
# At shorter leads the de-accumulation window doesn't fit (prev_lt < 0),
# so the frame would fall back to showing a raw cumulative total.
agg_match = re.search(r"\d+$", param) if param and base_param != param else None
min_lt = int(agg_match.group()) if agg_match else step
valid = {lt for lt in valid if lt >= min_lt}
return sorted(valid)
Loading