Optimization Input¶
How to access aggregation results for energy system optimization models.
This notebook shows how to access:
- Cluster weights (occurrence counts)
- Cluster assignments (period ordering)
- Typical period data
Import pandas and the relevant time series aggregation class
%load_ext autoreload
%autoreload 2
import pandas as pd
import plotly.express as px
import plotly.io as pio
pio.renderers.default = "notebook_connected"
import warnings
import tsam
from tsam import ClusterConfig, ExtremeConfig
# 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
raw = pd.read_csv("testdata.csv", index_col=0)
Transform the index to a datetime index
raw.index = pd.to_datetime(raw.index)
Plot raw data
fig = px.line(
raw, facet_row="variable", labels={"index": "Time", "value": ""}
).update_yaxes(matches=None)
fig.show()
Aggregate the data¶
Aggregate to typical weeks, including days with minimum temperature and maximum load as extreme periods.
result = tsam.aggregate(
raw,
n_clusters=5,
period_duration=24 * 7, # Weekly periods
cluster=ClusterConfig(method="hierarchical"),
extremes=ExtremeConfig(
method="new_cluster",
min_value=["T"],
max_value=["Load"],
),
)
Create the typical periods
cluster_representatives = result.cluster_representatives
cluster_representatives.head()
| GHI | Load | T | Wind | ||
|---|---|---|---|---|---|
| timestep | |||||
| 0 | 0 | 0.0 | 416.893227 | 0.304081 | 0.312325 |
| 1 | 0.0 | 409.367248 | 0.090194 | 0.000000 | |
| 2 | 0.0 | 403.570841 | -0.123693 | 0.000000 | |
| 3 | 0.0 | 404.420754 | -0.016750 | 0.208217 | |
| 4 | 0.0 | 410.034429 | -0.123693 | 0.312325 |
Show the resulting order of aggregated periods¶
Calculates how the original index is represented by the old index
# Advanced: Access the internal aggregation object for features not exposed in the public API.
# Note: The _aggregation attribute is internal and may change in future versions.
index_matching = result._aggregation.indexMatching()
index_matching.head()
| PeriodNum | TimeStep | |
|---|---|---|
| 2009-12-31 23:30:00 | 3 | 0 |
| 2010-01-01 00:30:00 | 3 | 1 |
| 2010-01-01 01:30:00 | 3 | 2 |
| 2010-01-01 02:30:00 | 3 | 3 |
| 2010-01-01 03:30:00 | 3 | 4 |
Plot the appearance of the 5+2 aggregated periods in the original timeframe
visualization_df = pd.DataFrame(
0, index=index_matching.index, columns=result.period_index
)
for col in visualization_df.columns:
visualization_df.loc[index_matching["PeriodNum"] == col, col] = 1
fig = px.area(
visualization_df,
labels={"index": "Time", "value": "Occurrence"},
color_discrete_sequence=px.colors.sample_colorscale(
"Viridis", len(visualization_df.columns)
),
)
fig.show()
Get input for potential energy system optimization¶
i. cluster_weights - The occurrence count of each typical period for weighting in the objective function.
Note: Period three is only partially evaluated since its appearance at the end of the year exceeds the original time series.
result.cluster_weights
{np.int64(0): np.int64(10),
np.int64(1): np.int64(17),
np.int64(2): np.int64(7),
np.int64(3): np.float64(4.142857142857142),
np.int64(4): np.int64(8),
np.int64(5): np.int64(2),
np.int64(6): np.int64(4)}
weights = pd.Series(result.cluster_weights)
fig = px.bar(
x=weights.index,
y=weights.values,
labels={"x": "Period index", "y": "Number of occurence"},
)
fig.show()
ii. Accessing period data by index
Access aggregated time series values using period and time step indices. This uses internal API methods that may change in future versions.
# Advanced: Access internal dictionary-style data access.
# Note: The _aggregation attribute and its methods are internal and may change in future versions.
agg = result._aggregation
agg.clusterPeriodDict["GHI"][(agg.clusterPeriodIdx[3], agg.stepIdx[12])]
132.49920893072988
Alternatively this is given as data frame
result.cluster_representatives.head()
| GHI | Load | T | Wind | ||
|---|---|---|---|---|---|
| timestep | |||||
| 0 | 0 | 0.0 | 416.893227 | 0.304081 | 0.312325 |
| 1 | 0.0 | 409.367248 | 0.090194 | 0.000000 | |
| 2 | 0.0 | 403.570841 | -0.123693 | 0.000000 | |
| 3 | 0.0 | 404.420754 | -0.016750 | 0.208217 | |
| 4 | 0.0 | 410.034429 | -0.123693 | 0.312325 |
iii. cluster_assignments
The order of the typical periods to represent the original time series, e.g., to model seasonal storage.
result.cluster_assignments
array([3, 2, 6, 6, 5, 2, 6, 6, 3, 0, 0, 0, 4, 0, 0, 4, 1, 4, 4, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 4, 1, 4, 4, 0, 0, 3, 0, 0,
0, 2, 2, 2, 2, 5, 2, 3, 3])