Tariffs
Annular supports simulating the effect of market interventions through various policies and tariffs. We aim to support a wide range of potential tariffs, and provide an easy interface for accessing their values in the internals of any bidding strategy.
This page will explain the:
design philosophy
expected data format and schema
provided runtime interface
Design philosophy
Tariffs can be defined for many different scenarios, and are commonly separated according to both some categorical values and some temporal aspect. Each tariff should be recorded in a separate file to avoid having to specify unnecessary values for all possible index combinations. This means that each file only specifies the required indexing levels relevant for that particular tariff. As an added benefit, this makes the system very modular, as replacing a single tariff means exactly replacing a single file, without being bound by indexing levels of those other tariffs.
In use, we see the main distinction between ‘categorial’ and ‘temporal’ index levels, with the categorical index assumed to be fixed during the runtime of a simulation. This leaves the temporal index to be selected at runtime through a simple interface.
We acknowledge that some of these categories may depend on the maximum used capacity, with jumps in tariff value between them. In such cases, we assume that the final category in which you end up is known to you beforehand with a high degree of certainty.
Data format and schema
All tariffs should be defined in tidy CSV files. A single CSV file
<tariff_name>.csv should contain the full specification of a single tariff.
The name of the file will be used as the identifier for that tariff when loaded.
This name is not case-sensitive. The collection of tariff files should be
contained in a single folder. This should include any other tariff-related
variables such as weighting factors.
Each CSV file should be formatted as follows:
grid level |
consumer type |
… |
year |
… |
hour |
value |
|---|---|---|---|---|---|---|
… |
… |
… |
… |
… |
… |
… |
In order, the file should contain the following columns, preferably in this order:
Categorical index levels (grid level, consumer type, …)
Temporal index levels if relevant (year, …, hour)
Value
The names of these columns are case-insensitive. They will all be cast to lowercase upon being loaded.
Each file should only contain those columns that are necessary to specify the
tariff. E.g., if there is no monthly variance in a tariff, a month column
should not be specified.
Runtime interface
A folder of tariffs can be loaded using the TariffManager.from_folder()
method. This takes an argument of type Path pointing to the folder containing
the collection of CSV files defining the tariffs. Additionally, a dictionary of
categorical index keys must be passed in with the preselect argument to select
values that remain fixed for the runtime of the simulation. Once the
TariffManager has been instantiated, tariff values can be retrieved by name
using the various fetching methods.
Fetch types
We distinguish between the following ‘tariff fetching scenarios’:
A tariff is not temporally indexed (temporally least granular)
A tariff is indexed by each hour (temporally most granular)
A tariff is temporally indexed, but not by the hour (intermediate temporal granularity)
There is a separate fetching function for each of these scenarios with different corresponding behavior. While the first scenario only needs the tariff name, the fetching functions for temporally indexed tariffs both require a timeseries as input. Based on the timestamps in this timeseries, the corresponding tariff values are then returned:
Tariff values are needed for given timestamps: use
fetch_timeseries(name, timestamps), withtimestampsas its index.With this call, the timestamps are parsed using the provided temporal index columns, to fetch the appropriate value for each timestamp.
For example, given the following monthly and hourly indexed tariff:
month
hour
value
…
…
…
1
0
10
1
1
11
…
…
…
2
0
20
2
1
21
…
…
…
The following result is retrieved when fetching with hourly timestamps:
2025-01-30 00:00, 2025-01-30 01:00, 2025-01-30 02:00, ..., 2025-02-02 23:00:timestamp
value
2025-01-30 00:00
10
2025-01-30 01:00
11
…
…
2025-01-31 00:00
10
2025-01-31 01:00
11
…
…
2025-02-01 00:00
20
2025-02-01 01:00
21
…
…
2025-02-02 00:00
20
2025-02-02 01:00
21
…
…
Only certain tariff values are needed, based on given timestamps: use
fetch_indexed(name, timestamps)In these cases there is no need for a value per timestamp, but only those values that are relevant for the given window. An example might be values for a monthly capacity tariff. This fetch function selects all rows that match the given timestamps, and returns them as-is with their original index columns.
For example, given the following monthly indexed tariff:
month
value
1
5
2
6
…
…
The following result is retrieved when fetching with hourly timestamps:
2025-01-30 00:00, 2025-01-30 01:00, 2025-01-30 02:00, ..., 2025-02-02 23:00:month
value
1
5
2
6
Temporal index level parsing
Since all remaining index levels are temporal in nature, the timestamps
argument of the fetch_timeseries and fetch_indexed methods is used to
select the relevant values. The TariffManager will parse the given
timestamps to match with the temporal index columns. The available parsing
options are listed below with the associated valid values. Note that the column
names are not case-sensitive.
"year": integers, e.g. 2025, 2026, etc."month": integers 1 … 12, with optional leading zeros (e.g. 01)."weekday/weekend"or"weekend/weekday": litteral strings"weekday"or"weekend"(where Dutch national holidays are included in the ‘weekend’ category). Note that the entry values"weekday"and"weekend"must be lowercase."hour": integers 0 … 23, with optional leading zeros (e.g. 00).