Wind and Solar¶
Wind and Solar power are semantically different, but for LTM those are identical. For the LTM API project they are distinct types and will be treated as such, but shares a common base.
flowchart TD
inext(External timeseries input)
inth5(Internal HDF5 file)
resh5(Internal HDF5 results file)
outext(External timeseries output)
inext --> model --api--> inth5 --> LTM --> resh5
resh5 --api--> outext
Two main methods of providing data.
One-dimensional timeseries spanning the whole historical period, using the historical timestamp as key.
Two-dimensional matrix with scenarios per historical year, using the data period timestamp as key.
Two input modes, 1D or 2D, slightly different data capture and conversion based on the type of input data. For 1D input data, scenarios/historical years uses a sliding window, while 2D-input data uses all available data defined. For 2D data, the time series timestamp is “stuck” to the first scenario. IE, all data is defined for 2024->2027. While for 1D data, there is a continuous series, IE, 1931->1981.
Example JSON with wind and solar:
{
"model":
{
"wind": [
{
"#comment": "Gruppe 18 1D",
"name": "NORDSJO_1D",
"capacity": {
"external_reference": {
"type": "hdf5",
"filename": "wind_series.h5",
"path": "/wind/geitgalgjartind_1d"
}
}
},
{
"#comment": "Gruppe 18 2D",
"name": "NORDSJO_2D",
"capacity": {
"external_reference": {
"type": "hdf5",
"filename": "wind_series.h5",
"path": "/wind/geitgalgjartind_2d"
}
}
}
],
"solar": [
{
"#comment": "Gruppe 18 1D",
"name": "DANMARK_1D",
"capacity": {
"external_reference": {
"type": "hdf5",
"filename": "wind_series.h5",
"path": "/solar/sahara_1d"
}
}
},
{
"#comment": "Gruppe 18 2D",
"name": "DANMARK_2D",
"capacity": {
"external_reference": {
"type": "hdf5",
"filename": "wind_series.h5",
"path": "/solar/sahara_2d"
}
}
}
]
}
}
Example of a 2D matrix with data:¶
By using a 2D matrix as input for wind and solar series ensure complete control over the data.
Example matrix:
Timestamp |
Scenario 1 |
Scenario 2 |
… |
Scenario 50 |
|---|---|---|---|---|
2024.01.01T00:00 |
0.0 |
1.0 |
… |
3.0 |
2024.01.01T01:00 |
4.0 |
5.0 |
… |
7.0 |
2024.01.01T02:00 |
8.0 |
9.0 |
… |
9.5 |
… |
… |
… |
… |
… |
2026.12.27T23:00 |
10.0 |
12.0 |
… |
16.0 |
For 3 years of data and 50 scenarios/historical years, the dimensions are 50 x 26208 (3 years * 52 weeks * 168 hours in week)
This period is derived from calc_water_value_flag key in global_settings.
"calc_water_value_flag": {
"timestamps": [
"2024-01-01T00:00:00Z",
"2026-12-28T00:00:00Z"
],
"scenarios": [
[
1,
0
]
]
}
Example of a 1D vector with data:¶
By using a 1D continous series, some data will be repeated.
Timestamp |
Value |
|---|---|
1931.01.01T00:00 |
0.0 |
1931.01.01T01:00 |
4.0 |
1931.01.01T02:00 |
8.0 |
… |
… |
1980.12.31T23:00 |
10.0 |
For 50 scenarios/historical years of hourly resolution, the dimension is 1x436800 (50 years * 52 weeks * 168 hours in week)
This corresponds to historical_period in global_settings.
"historical_period": {
"timestamps": [
"1931-01-01T00:00:00Z",
"1981-01-01T00:00:00Z"
],
"scenarios": [
[
1,
0
]
]
}
Synthetic solar and wind series:¶
This script creates 1D and 2D series for LTM-API consumption.
import os
import numpy as np
from create_csv_timeseries import create_csv_timeseries
from create_hdf5_timeseries import create_hdf5_timeseries, unix_epoch
from create_sqlite_timeseries import create_sqlite_timeseries
def create_1d_wind_solar_series(farms):
for farm in farms:
timestamps = np.arange(
np.datetime64("1931-01-01"),
np.datetime64("1981-01-01"),
np.timedelta64(1, "h"),
)
num_values = len(timestamps)
# x = np.linspace(0, 2 * np.pi, num_values)
# y = np.sin(x) + np.random.random(num_values) * 0.2
y = np.linspace(0, num_values, num_values)
y.shape = (1, num_values)
farmname = str(farm).replace("/", "_")
create_sqlite_timeseries(sqlname, f"/{farm}", timestamps, y)
create_csv_timeseries(f"{csvname}-{farmname}.csv", timestamps, y)
create_hdf5_timeseries(windfile, f"/{farm}", timestamps, y)
pass
def create_2d_wind_solar_series(farms, watervalue_years, scenarios):
for farm in farms:
start_date = np.datetime64("2024-01-01")
end_date = start_date + np.timedelta64(watervalue_years * 52, "W")
timestamps = np.arange(
start_date,
end_date,
np.timedelta64(1, "h"),
)
num_values = len(timestamps)
# x = np.linspace(0, 2 * np.pi, num_values * scenarios)
# y = np.sin(x) * 2 + np.random.random(num_values * scenarios) * 0.2 + 1
x = np.linspace(0, num_values * scenarios, num_values * scenarios)
y = x
y.shape = (scenarios, num_values)
farmname = str(farm).replace("/", "_")
create_sqlite_timeseries(sqlname, f"/{farm}", timestamps, y)
create_csv_timeseries(f"{csvname}-{farmname}.csv", timestamps, y)
create_hdf5_timeseries(windfile, f"/{farm}", timestamps, y)
pass
if __name__ == "__main__":
windfile = "wind_series.h5"
csvname = "wind_series"
sqlname = "wind_series.sqlite"
if os.path.isfile(windfile):
os.remove(windfile)
farms_1d = (
"wind/geitgalgjartind_1d",
"wind/holmenkollen_1d",
"wind/storfjellet_1d",
"solar/sahara_1d",
"solar/lindesnes_1d",
)
create_1d_wind_solar_series(farms_1d)
farms_2d = (
"wind/geitgalgjartind_2d",
"wind/holmenkollen_2d",
"wind/storfjellet_2d",
"solar/sahara_2d",
"solar/lindesnes_2d",
)
watervalue_years = 3
scenarios = 50
create_2d_wind_solar_series(farms_2d, watervalue_years, scenarios)
pass