Entity: ForecastConfig¶
Definition¶
ForecastConfig is the optional frozen Pydantic model that activates forecast mode in a pipeline run. Its presence as ExperimentConfig.forecast (non-None) switches every stage from the hindcast weekly-grid path to the single-init-date forecast path. It holds three YAML-declared fields — the raw observations zarr path, the materialised climatology zarr path, and the residual_mode for conformal calibration — plus the runtime-injected init_date (set to None in YAML, then populated by build_forecast_features).
ForecastConfig is a subordinate config, not a root aggregate. It has no independent lifecycle outside ExperimentConfig.
Kind¶
Pydantic BaseModel, frozen=True. Nested inside ExperimentConfig as an optional field (forecast: ForecastConfig | None = None).
Source of truth¶
market_insights_models/src/commodity_hindcast/config.py:579
Key attributes¶
| Field | Type | Default | Meaning | YAML example |
|---|---|---|---|---|
raw_obs_filepath |
ResolvablePath |
required | Path to the raw observation zarr (weather data for the current season up to init_date). Resolved against data_root |
s3://{env}-treefera-greenprint-data/weather/processed/areal_aggregation/conus_adm2.zarr |
materialised_climo_filepath |
ResolvablePath |
required | Path to the pre-materialised climatology zarr (all DOYs pre-computed for fast forecast feature build). Resolved against data_root. See the centralised-climo feedback note in project memory |
s3://{env}-treefera-greenprint-data/weather/processed/climatology/conus_adm2_baseline_1980_2025_w31_materialised.zarr |
residual_mode |
ResidualMode |
required (no default) | Which past residuals to calibrate conformal intervals against at forecast time. Must correspond to a sidecar present in run_dir/conformal/{residual_mode}.parquet. Made mandatory in PR #372 |
hindcast_oos_per_init_date |
init_date |
date \| None |
None |
The target calendar date for this forecast. None in YAML / hindcast mode. Injected at runtime by build_forecast_features via model_copy(update={"init_date": <date>}) from the CLI --init-date argument |
— (runtime only) |
residual_mode values¶
ResidualMode is defined in models/meta_models/types.py:16:
ResidualMode = Literal[
"hindcast_oos_per_init_date",
"hindcast_oos_per_year",
"hindcast_oos_fully_pooled",
"in_sample_pooled",
]
All four production YAMLs with a forecast: block use "hindcast_oos_per_init_date".
"in_sample_pooled" is the only mode that works after make fit-production alone (no prior hindcast CV). The other three modes require a hindcast run to produce walk-forward OOS residuals.
residual_mode is mandatory — PR #372¶
Before PR #372, residual_mode defaulted to postprocess.conformalise[0], conflating "which sidecars to fit during hindcast" with "which sidecar to apply at forecast time". This caused opaque crashes (KeyError: 'fold_year') when the run_dir lacked the needed sidecar.
PR #372 made residual_mode a required field with no default. The validate_residual_mode gate at the top of stages/run_forecast.run() now rejects three failure modes before any feature I/O:
- Empty
run_dir(no hindcast or fit-production) → action: runmake hindcastorcli run fit-production. - OOS mode + no CV folds in
run_dir→ action: run hindcast, or switch to"in_sample_pooled". "in_sample_pooled"+ no production fold → action: runcli run fit-productionfirst.
ResidualMode was also extracted to a leaf module (models/meta_models/types.py) to break a circular import between config.py and conformalise.py.
Lifecycle¶
- Present in YAML only for forecast-capable commodity configs (corn, soybeans USA, soybeans BRA, wheat USA). Omitted entirely for hindcast-only runs.
- At YAML load time,
raw_obs_filepath,materialised_climo_filepath, andresidual_modeare validated;init_datedefaults toNone. ExperimentConfig._resolve_data_pathsresolvesraw_obs_filepathandmaterialised_climo_filepathagainstdata_root.- At forecast runtime,
build_forecast_featurescallsconfig.model_copy(update={"forecast": config.forecast.model_copy(update={"init_date": init_date})})to inject the CLI-supplied--init-date. ExperimentConfig.init_dates_for(season_year)returns[self.forecast.init_date]whenforecast.init_dateis set, overriding the hindcast weekly grid.validate_residual_mode(stages/run_forecast.py) gates the forecast stage before any S3 reads.
Relationships¶
- Owned by
ExperimentConfig.forecast(0:1 —Nonein hindcast mode). - Presence switches pipeline mode:
forecast is None→ hindcast;forecast is not None→ forecast. - References
PostprocessConfig.conformaliseindirectly:residual_modemust match a mode that was listed inconformaliseso the sidecar parquet exists inrun_dir/conformal/. - Drives
ForecastSliceartefact layout: per-(season_year, init_date)subtree underrun_dir/forecast/. - Driven by CLI flags
--init-dateand--season-yearincli.run_forecast_cmd.
Concepts and pipelines that touch this entity¶
- Pipeline: forecast —
ForecastConfigis the activation switch;validate_residual_modeis the entry gate. - PostprocessConfig —
conformalisedeclares which sidecars to write;residual_modedeclares which to read. - Concept: conformal calibration —
residual_modeselects the pooling strategy. - Concept: materialised climo —
materialised_climo_filepathmust point to the pre-computed climo zarr produced bymaterialise_for_forecast().
PRs and commits¶
- PR #361 (PR-361.md) —
ForecastConfiggained aresidual_modeplaceholder field (then optional, defaulting topostprocess.conformalise[0]). - PR #372 (PR-372.md) —
residual_modemade mandatory (no default).ResidualModeextracted tomodels/meta_models/types.py.validate_residual_modegate added at forecast entry.cli run forecast --config <yaml>removed; all forecasts now require--run-dir.
Open questions¶
- No pydantic validator checks that
residual_modeis present inPostprocessConfig.conformalise. Thevalidate_residual_moderuntime gate catches the mismatch but only when the forecast stage is invoked, not at config-load time. init_datebeingNoneat YAML load time meansExperimentConfigis technically in an incomplete state untilbuild_forecast_featuresinjects it. A future refactor could separate "forecast config at load time" from "forecast invocation params" to make the injection explicit.