11from pathlib import Path
22from typing import Dict , List , Any
33
4- from pydantic import BaseModel , Field , RootModel , HttpUrl
4+ from pydantic import BaseModel , Field , RootModel , HttpUrl , field_validator
55
66PROJECT_ROOT = Path (__file__ ).parents [2 ]
77
@@ -70,9 +70,15 @@ class RunConfig(BaseModel):
7070 None ,
7171 description = "The label for the run that will be used in experiment results such as reports and figures." ,
7272 )
73- steps : str | None = Field (
74- None ,
75- description = "Forecast steps to be used from interpolator, e.g. '0/126/6'." ,
73+ steps : str = Field (
74+ ...,
75+ description = (
76+ "Forecast lead times in hours, formatted as 'start/end/step'. "
77+ "The range includes the start lead time and continues with the given step "
78+ "until reaching or exceeding the end lead time. "
79+ "Example: '0/120/6' for lead times every 6 hours up to 120 h, "
80+ "or '0/33/6' up to 30 h."
81+ ),
7682 )
7783 extra_dependencies : List [str ] = Field (
7884 default_factory = list ,
@@ -86,6 +92,27 @@ class RunConfig(BaseModel):
8692
8793 config : Dict [str , Any ] | str
8894
95+ @field_validator ("steps" )
96+ def validate_steps (cls , v : str ) -> str :
97+ if "/" not in v :
98+ raise ValueError (
99+ f"Steps must follow the format 'start/stop/step', got '{ v } '"
100+ )
101+ parts = v .split ("/" )
102+ if len (parts ) != 3 :
103+ raise ValueError ("Steps must be formatted as 'start/end/step'." )
104+ try :
105+ start , end , step = map (int , parts )
106+ except ValueError :
107+ raise ValueError ("Start, end, and step must be integers." )
108+ if start > end :
109+ raise ValueError (
110+ f"Start ({ start } ) must be less than or equal to end ({ end } )."
111+ )
112+ if step <= 0 :
113+ raise ValueError (f"Step ({ step } ) must be a positive integer." )
114+ return v
115+
89116
90117class ForecasterConfig (RunConfig ):
91118 """Single training run stored in MLflow."""
@@ -240,9 +267,6 @@ class ConfigModel(BaseModel):
240267 description = "Description of the experiment, e.g. 'Hindcast of the 2023 season.'" ,
241268 )
242269 dates : Dates | ExplicitDates
243- lead_time : str = Field (
244- ..., description = "Forecast length, e.g. '120h'" , pattern = r"^\d+[hmd]$"
245- )
246270 runs : List [ForecasterItem | InterpolatorItem ] = Field (
247271 ...,
248272 description = "Dictionary of runs to execute, with run IDs as keys and configurations as values." ,
0 commit comments