Building Energy System¶
Optimal combination of segments and periods for building energy supply systems.
Author: Leander Kotzur
Import pandas and the relevant time series aggregation class
In [1]:
Copied!
%load_ext autoreload
%autoreload 2
import pandas as pd
import plotly.express as px
import plotly.io as pio
import tsam
from tsam import ClusterConfig, SegmentConfig
from tsam.tuning import find_pareto_front
pio.renderers.default = "notebook_connected"
import warnings
# Added to every example notebook: silence the v3 column-order
# FutureWarning in the rendered docs (tsam v4 returns result columns in
# input order; see migration guide).
warnings.filterwarnings(
"ignore", category=FutureWarning, message=".*sorted alphabetically.*"
)
%load_ext autoreload
%autoreload 2
import pandas as pd
import plotly.express as px
import plotly.io as pio
import tsam
from tsam import ClusterConfig, SegmentConfig
from tsam.tuning import find_pareto_front
pio.renderers.default = "notebook_connected"
import warnings
# Added to every example notebook: silence the v3 column-order
# FutureWarning in the rendered docs (tsam v4 returns result columns in
# input order; see migration guide).
warnings.filterwarnings(
"ignore", category=FutureWarning, message=".*sorted alphabetically.*"
)
Input data¶
Read in time series from testdata.csv with pandas
In [2]:
Copied!
raw = pd.read_csv("testdata.csv", index_col=0)
raw = raw.rename(
columns={
"T": "Temperature [°C]",
"Load": "Load [kW]",
"Wind": "Wind [m/s]",
"GHI": "Solar [W/m²]",
}
)
raw = pd.read_csv("testdata.csv", index_col=0)
raw = raw.rename(
columns={
"T": "Temperature [°C]",
"Load": "Load [kW]",
"Wind": "Wind [m/s]",
"GHI": "Solar [W/m²]",
}
)
In [3]:
Copied!
raw = raw.drop(
columns=[
"Wind [m/s]",
],
)
raw = raw.drop(
columns=[
"Wind [m/s]",
],
)
Use tsam's built-in heatmap plotting for visual comparison of the time series
Plot an example series - in this case the temperature
In [4]:
Copied!
# Original data heatmaps using tsam.unstack_to_periods() with plotly
unstacked = tsam.unstack_to_periods(raw, period_duration=24)
for col in raw.columns:
px.imshow(
unstacked[col].values.T,
labels={"x": "Day", "y": "Hour", "color": col},
title=f"Original {col}",
aspect="auto",
).show()
# Original data heatmaps using tsam.unstack_to_periods() with plotly
unstacked = tsam.unstack_to_periods(raw, period_duration=24)
for col in raw.columns:
px.imshow(
unstacked[col].values.T,
labels={"x": "Day", "y": "Hour", "color": col},
title=f"Original {col}",
aspect="auto",
).show()
Tune a hierarchical aggregation with segments in combination with distribution representation¶
Use the new find_pareto_front() function to explore the Pareto-optimal combinations.
In [5]:
Copied!
pareto_results = find_pareto_front(
raw,
period_duration=24,
max_timesteps=100,
cluster=ClusterConfig(
method="hierarchical",
representation="distribution",
),
n_jobs=-1,
)
pareto_results = find_pareto_front(
raw,
period_duration=24,
max_timesteps=100,
cluster=ClusterConfig(
method="hierarchical",
representation="distribution",
),
n_jobs=-1,
)
Building Pareto front: 0%| | 0/100 [00:00<?, ?it/s]
Building Pareto front: 2%|▏ | 2/100 [00:00<00:21, 4.63it/s]
Building Pareto front: 3%|▎ | 3/100 [00:00<00:20, 4.80it/s]
Building Pareto front: 6%|▌ | 6/100 [00:00<00:11, 8.45it/s]
Building Pareto front: 9%|▉ | 9/100 [00:01<00:08, 10.51it/s]
Building Pareto front: 12%|█▏ | 12/100 [00:01<00:07, 11.76it/s]
Building Pareto front: 15%|█▌ | 15/100 [00:01<00:06, 12.50it/s]
Building Pareto front: 18%|█▊ | 18/100 [00:01<00:06, 12.87it/s]
Building Pareto front: 21%|██ | 21/100 [00:01<00:06, 12.93it/s]
Building Pareto front: 24%|██▍ | 24/100 [00:02<00:05, 12.91it/s]
Building Pareto front: 27%|██▋ | 27/100 [00:02<00:05, 12.78it/s]
Building Pareto front: 30%|███ | 30/100 [00:02<00:05, 12.59it/s]
Building Pareto front: 33%|███▎ | 33/100 [00:02<00:05, 12.43it/s]
Building Pareto front: 36%|███▌ | 36/100 [00:03<00:05, 12.21it/s]
Building Pareto front: 48%|████▊ | 48/100 [00:03<00:02, 22.91it/s]
Building Pareto front: 52%|█████▏ | 52/100 [00:03<00:02, 20.56it/s]
Building Pareto front: 56%|█████▌ | 56/100 [00:03<00:02, 18.71it/s]
Building Pareto front: 70%|███████ | 70/100 [00:04<00:01, 28.82it/s]
Building Pareto front: 75%|███████▌ | 75/100 [00:04<00:00, 25.42it/s]
Building Pareto front: 80%|████████ | 80/100 [00:04<00:00, 22.95it/s]
Building Pareto front: 85%|████████▌ | 85/100 [00:05<00:00, 21.11it/s]
Building Pareto front: 90%|█████████ | 90/100 [00:05<00:00, 17.40it/s]
Building Pareto front: 100%|██████████| 100/100 [00:05<00:00, 18.21it/s]
And determine the pareto optimal aggregation up to 100 total time steps. This may take some time...
In [6]:
Copied!
# Show the last result in the Pareto front
last_result = pareto_results[-1]
print(
f"Final configuration: {last_result.n_clusters} periods, {last_result.n_segments} segments"
)
# Show the last result in the Pareto front
last_result = pareto_results[-1]
print(
f"Final configuration: {last_result.n_clusters} periods, {last_result.n_segments} segments"
)
Final configuration: 20 periods, 5 segments
And show the results for the last aggregation
In [7]:
Copied!
# Reconstruct the data from the last Pareto result
reconstructed = last_result.reconstructed
# Reconstruct the data from the last Pareto result
reconstructed = last_result.reconstructed
In [8]:
Copied!
# Reconstructed data heatmaps from last tuned aggregation
unstacked_recon = tsam.unstack_to_periods(reconstructed, period_duration=24)
for col in reconstructed.columns:
px.imshow(
unstacked_recon[col].values.T,
labels={"x": "Day", "y": "Hour", "color": col},
title=f"Reconstructed {col}",
aspect="auto",
).show()
# Reconstructed data heatmaps from last tuned aggregation
unstacked_recon = tsam.unstack_to_periods(reconstructed, period_duration=24)
for col in reconstructed.columns:
px.imshow(
unstacked_recon[col].values.T,
labels={"x": "Day", "y": "Hour", "color": col},
title=f"Reconstructed {col}",
aspect="auto",
).show()
In [9]:
Copied!
last_result.n_segments
last_result.n_segments
Out[9]:
5
In [10]:
Copied!
last_result.n_clusters
last_result.n_clusters
Out[10]:
20
In [11]:
Copied!
# Example with specific configuration using distribution_minmax representation
result = tsam.aggregate(
raw,
n_clusters=14,
period_duration=24,
cluster=ClusterConfig(
method="hierarchical",
representation="distribution_minmax",
),
segments=SegmentConfig(n_segments=8),
preserve_column_means=False,
)
# Example with specific configuration using distribution_minmax representation
result = tsam.aggregate(
raw,
n_clusters=14,
period_duration=24,
cluster=ClusterConfig(
method="hierarchical",
representation="distribution_minmax",
),
segments=SegmentConfig(n_segments=8),
preserve_column_means=False,
)
In [12]:
Copied!
# Reconstructed data heatmaps with 8 segments and 14 periods
recon = result.reconstructed
unstacked_recon2 = tsam.unstack_to_periods(recon, period_duration=24)
for col in recon.columns:
px.imshow(
unstacked_recon2[col].values.T,
labels={"x": "Day", "y": "Hour", "color": col},
title=f"Reconstructed {col} (8 seg, 14 periods)",
aspect="auto",
).show()
# Reconstructed data heatmaps with 8 segments and 14 periods
recon = result.reconstructed
unstacked_recon2 = tsam.unstack_to_periods(recon, period_duration=24)
for col in recon.columns:
px.imshow(
unstacked_recon2[col].values.T,
labels={"x": "Day", "y": "Hour", "color": col},
title=f"Reconstructed {col} (8 seg, 14 periods)",
aspect="auto",
).show()