Unit Forecasts#
Each unit in ASSUME has an associated forecaster that provides it with static input data such as availability, fuel prices, or market price predictions. Forecast data is typically provided as time series, either loaded from a CSV file or calculated internally from simulation data.
Forecasters can generally be safed via config parameter save_forecasts to the specified file given by: forecast_save_file (default: SCENARIO_PATH/saved_forecasts.csv)
Forecaster Types#
Each unit type has its own forecaster class. All forecasters inherit from
UnitForecaster, which provides the base lifecycle interface and
common attributes (price, residual_load, availability).
Unit Type |
Forecaster Class |
Additional attributes |
|---|---|---|
Power Plants |
|
|
Storage Units |
|
(base only) |
Demand Units |
|
|
Exchange Units |
|
|
DSM Units |
|
|
Steelplant Units |
|
DSM attrs + |
Steam Generation Units |
|
DSM attrs + |
Building Units |
|
DSM attrs + |
Hydrogen Units |
|
DSM attrs + |
Custom Units (CSV import) |
|
(arbitrary keyword attributes) |
Forecast Lifecycle#
Every forecaster exposes three lifecycle methods:
preprocess— prepares intermediate information needed by the other two steps.initialize— computes all forecast timeseries. Called once after all units are created.update— revises forecasts during runtime (e.g. during bid calculation in bidding strategies).
These methods are called automatically by the simulation framework at the appropriate points.
Algorithm Resolution and Registries#
Each lifecycle method decides which function to run through two layers of indirection:
``forecast_algorithms`` (
dict[str, str]) — maps a key to an algorithm ID. Keys follow the pattern{prefix}_{forecast_metric}where prefix ispreprocess,update, or empty forinitialize.``forecast_registries`` (
dict[str, dict]) — maps each algorithm ID to the actual Python callable. It contains three sub-dicts:"init"— callables for theinitializestep"preprocess"— callables for thepreprocessstep"update"— callables for theupdatestep
The default registries are provided by
get_forecast_registries()and contain all built-in algorithms listed below.
The resolution flow is:
forecast_algorithms["price"] → "price_naive_forecast" (algorithm ID)
↓
forecast_registries["init"]["price_naive_forecast"] → calculate_naive_price (callable)
This design lets you swap algorithms via configuration without touching code.
Configuration#
Via config.yaml#
Specify which algorithms to use in the forecast_algorithms section of your study case:
example_study_case:
other_stuff:
...
forecast_algorithms:
# initialize algorithms have no prefix
price: price_naive_forecast
residual_load: residual_load_naive_forecast
# preprocess and update algorithms take the corresponding prefix
preprocess_price: price_default
update_price: price_default
update_congestion_signal: congestion_signal_default
Via unit CSV files#
You can also override algorithms per unit using columns in the unit CSV files.
The column name follows the pattern forecast_{prefix}_{forecast_metric} and the cell value
is the algorithm_id. (Prefix is empty for initialize algorithms, and preprocess or update for the other two steps.)
Note
CSV file specifications overwrite config.yaml specifications for that unit (only when the CSV cell is not empty/None).
If neither CSV nor config specifies a certain algorithm, a default is chosen automatically.
Available Algorithms#
Initialize algorithms#
These are used by the initialize method. Their key in forecast_algorithms has no prefix
(e.g. price, residual_load).
Price forecast algorithms:
Type |
algorithm_id |
Description |
|---|---|---|
Naive (default) |
|
Merit-order dispatch against demand (excl. storages and DSM units) for all timesteps. Automatically uses elastic or inelastic clearing depending on demand unit types. Columns in |
Keep |
|
Keeps the forecast series provided at instantiation unchanged. |
Test |
|
Returns a constant price of 50 for the EOM market (for testing only). |
Residual load forecast algorithms:
Type |
algorithm_id |
Description |
|---|---|---|
Naive (default) |
|
Total demand (incl. exchange volumes) minus renewable generation (wind/solar) for all timesteps. |
Keep |
|
Keeps the forecast series provided at instantiation unchanged. |
Test |
|
Returns an empty dict (for testing only). |
Congestion signal forecast algorithms (DSM units only):
Type |
algorithm_id |
Description |
|---|---|---|
Naive (default) |
|
Per-node congestion severity: for each node, takes the max ratio of net load to line capacity across all connected transmission lines. Returns empty dict if grid data is unavailable. |
Keep |
|
Keeps the forecast series provided at instantiation unchanged. |
Test |
|
Returns a constant zero signal (for testing only). |
Renewable utilisation forecast algorithms (DSM units only):
Type |
algorithm_id |
Description |
|---|---|---|
Naive (default) |
|
Per-node renewable generation (availability × max_power) plus an all-nodes aggregate. Returns empty dict if grid data is unavailable. |
Keep |
|
Keeps the forecast series provided at instantiation unchanged. |
Test |
|
Returns a constant zero signal (for testing only). |
Preprocess algorithms#
These are used by the preprocess method. Their key uses the preprocess_ prefix
(e.g. preprocess_price).
algorithm_id |
Applies to |
Description |
|---|---|---|
|
price |
No-op (returns None). This is the default. |
|
residual_load |
No-op (returns None). This is the default. |
|
residual_load |
Extracts unit-specific residual load columns from |
|
congestion_signal |
No-op (returns None). This is the default. |
|
renewable_utilisation |
No-op (returns None). This is the default. |
Update algorithms#
These are used by the update method. Their key uses the update_ prefix
(e.g. update_price).
algorithm_id |
Applies to |
Description |
|---|---|---|
|
price |
No-op (keeps current forecast unchanged). This is the default. |
|
residual_load |
No-op (keeps current forecast unchanged). This is the default. |
|
residual_load |
Swaps the current residual load forecast with a named series from |
|
congestion_signal |
No-op (keeps current forecast unchanged). This is the default. |
|
renewable_utilisation |
No-op (keeps current forecast unchanged). This is the default. |
Other Ways to Provide Forecasts#
Besides configuring algorithms, there are two additional ways to supply forecast data:
Via forecast_df.csv — create a CSV file with columns matching the expected keys (e.g.
price_EOM,residual_load_EOM,{node}_congestion_signal). Default forecast algorithms will not overwrite columns found in this file. To disable the use of / overwriting withforecasts_dfset the flaguse_forecasts_dfto false in the config.Direct instantiation — when creating a simulation programmatically (without scenario loaders), you can pass forecast series directly to the forecaster constructor. In this case, set the corresponding algorithm to
{forecast_metric}_keep_givento prevent the initialize step from overwriting your data.
Note
When using direct instantiation you are responsible for driving the forecast lifecycle.
If you do not call initialize() (or
init_forecasts()), DSM units will not have their internal
optimisation model built and you must call unit.setup_model() explicitly before
running optimisation or dispatching the unit.
Adding Custom Algorithms#
To add a custom forecast algorithm:
Write your function following the signature of existing algorithms in
forecast_algorithms.Register it by adding an entry to the appropriate registry dict (
forecast_algorithms,forecast_preprocess_algorithms, orforecast_update_algorithms).Reference your new
algorithm_idin the config.yaml or unit CSV files.
For custom units, there are two options to handle forecasts:
Create a new forecaster class inheriting from
UnitForecasterwith the necessary fields and methods.Use
CustomUnitForecaster, which accepts arbitrary keyword attributes. This is used automatically when importing custom units via CSV files.