Skip to content

Commit 77796c8

Browse files
authored
Merge pull request #840 from openego/features/#800-clustering-with-focus
Features/#800 clustering with focus
2 parents 661c6d1 + 876a361 commit 77796c8

9 files changed

Lines changed: 623 additions & 305 deletions

File tree

doc/theoretical_background.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ The procedures of the two methods are depicted in the following figure [Esterl20
127127

128128
|
129129
130+
Additionally, there is the option to perform a **clustering with a focus on a defined region**, in which the spatial distribution of clustered buses is intentionally weighted toward the corresponding region. This allows a higher spatial resolution within and around the region of interest, while areas farther away are represented with fewer buses.
131+
130132
In general, the clustering of the **sector-coupled system** is divided into two steps:
131133
First, the electrical and gas grid are clustered independently using one of the methods described above. Afterwards, nodes of the other sectors (hydrogen, heat, e-mobility and DSM nodes) are mapped according to their connection to electricity or gas buses and aggregated to one node per carrier.
132134

etrago/appl.py

Lines changed: 147 additions & 151 deletions
Large diffs are not rendered by default.

etrago/args.json

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"q_allocation": "p_nom"
2424
},
2525
"start_snapshot": 1,
26-
"end_snapshot": 2,
26+
"end_snapshot": 168,
2727
"solver": "gurobi",
2828
"solver_options": {},
2929
"model_formulation": "kirchhoff",
@@ -63,31 +63,36 @@
6363
"extra_functionality": {},
6464
"network_clustering_ehv": {
6565
"active": false,
66-
"busmap": false
66+
"busmap": false,
67+
"cpu_cores": 4
6768
},
68-
"network_clustering": {
69-
"active": true,
70-
"method": "kmedoids-dijkstra",
71-
"n_clusters_AC": 30,
72-
"cluster_foreign_AC": false,
73-
"method_gas": "kmedoids-dijkstra",
74-
"n_clusters_gas": 15,
75-
"n_clusters_h2": 15,
76-
"cluster_foreign_gas": false,
77-
"k_elec_busmap": false,
78-
"k_gas_busmap": false,
79-
"bus_weight_tocsv": null,
80-
"bus_weight_fromcsv": null,
81-
"gas_weight_tocsv": null,
82-
"gas_weight_fromcsv": null,
83-
"line_length_factor": 1,
84-
"remove_stubs": false,
85-
"use_reduced_coordinates": false,
86-
"random_state": 42,
87-
"n_init": 10,
88-
"max_iter": 100,
89-
"tol": 1e-6,
90-
"CPU_cores": 4
69+
"network_clustering": {
70+
"method": {
71+
"focus_region": null,
72+
"per_country": true,
73+
"algorithm": "kmedoids-dijkstra",
74+
"remove_stubs": false,
75+
"use_reduced_coordinates": false,
76+
"line_length_factor": 1,
77+
"random_state": 42,
78+
"n_init": 10,
79+
"max_iter": 100,
80+
"tol": 1e-6,
81+
"cpu_cores": 4
82+
},
83+
"electricity_grid": {
84+
"active": true,
85+
"cluster_within_focus": false,
86+
"n_clusters": 30,
87+
"k_elec_busmap": false
88+
},
89+
"gas_grids": {
90+
"active": true,
91+
"cluster_within_focus": false,
92+
"n_clusters_ch4": 15,
93+
"n_clusters_h2": 15,
94+
"k_ch4_busmap": false,
95+
}
9196
},
9297
"spatial_disaggregation": null,
9398
"snapshot_clustering": {

etrago/cluster/electrical.py

Lines changed: 65 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
from etrago.cluster.spatial import (
4343
busmap_ehv_clustering,
4444
drop_nan_values,
45+
focus_weighting,
4546
group_links,
4647
kmean_clustering,
4748
kmedoids_dijkstra_clustering,
@@ -195,7 +196,7 @@ def find_de_closest(network, bus_ne):
195196
# Do not apply this part if the function is used for creating the market
196197
# model. It adds one bus per country, which is not useful in this case.
197198
if apply_on != "market_model":
198-
if (not etrago.args["network_clustering"]["cluster_foreign_AC"]) & (
199+
if (etrago.args["network_clustering"]["method"]["per_country"]) & (
199200
cluster_met in ["kmeans", "kmedoids-dijkstra"]
200201
):
201202
buses_orig = network.buses.copy()
@@ -515,9 +516,9 @@ def select_elec_network(etrago, apply_on="grid_model"):
515516
apply_on: str
516517
gives information about the objective of the output network. If
517518
"grid_model" is provided, the value assigned in the args for
518-
["network_clustering"]["cluster_foreign_AC""] will define if the
519-
foreign buses will be included in the network. if "market_model" is
520-
provided, foreign buses will be always included.
519+
["network_clustering"]["method"]["per_country""] will
520+
define if the foreign buses will be included in the network.
521+
If "market_model" is provided, foreign buses will be always included.
521522
522523
Returns
523524
-------
@@ -536,10 +537,12 @@ def select_elec_network(etrago, apply_on="grid_model"):
536537
"""Parameter apply_on must be either 'grid_model' or 'market_model'
537538
"""
538539
)
539-
settings = etrago.args["network_clustering"]
540+
settings = etrago.args["network_clustering"]["electricity_grid"]
540541

541542
if apply_on == "grid_model":
542-
include_foreign = settings["cluster_foreign_AC"]
543+
include_foreign = not etrago.args["network_clustering"]["method"][
544+
"per_country"
545+
]
543546
elif apply_on == "market_model":
544547
include_foreign = True
545548
else:
@@ -556,7 +559,7 @@ def select_elec_network(etrago, apply_on="grid_model"):
556559
(elec_network.links.carrier == "AC")
557560
| (elec_network.links.carrier == "DC")
558561
]
559-
n_clusters = settings["n_clusters_AC"]
562+
n_clusters = settings["n_clusters"]
560563
else:
561564
AC_filter = elec_network.buses.carrier.values == "AC"
562565

@@ -572,7 +575,7 @@ def select_elec_network(etrago, apply_on="grid_model"):
572575
elec_network.buses = elec_network.buses[
573576
AC_filter & (elec_network.buses.country.values == "DE")
574577
]
575-
n_clusters = settings["n_clusters_AC"] - num_neighboring_country
578+
n_clusters = settings["n_clusters"] - num_neighboring_country
576579

577580
# Dealing with generators
578581
elec_network.generators = elec_network.generators[
@@ -813,14 +816,17 @@ def preprocessing(etrago, apply_on="grid_model"):
813816
""")
814817
network.buses.country.loc[network.buses.country.isna()] = "DE"
815818

816-
if settings["k_elec_busmap"] is False:
819+
if settings["electricity_grid"]["k_elec_busmap"] is False:
817820
busmap_foreign = unify_foreign_buses(etrago)
818821
else:
819822
busmap_foreign = pd.Series(name="foreign", dtype=str)
820823

821824
network_elec, n_clusters = select_elec_network(etrago, apply_on=apply_on)
822825

823-
if settings["method"] == "kmedoids-dijkstra":
826+
if (
827+
settings["method"]["algorithm"] == "kmedoids-dijkstra"
828+
or settings["method"]["focus_region"] is not None
829+
):
824830
lines_col = network_elec.lines.columns
825831

826832
# The Dijkstra clustering works using the shortest electrical path
@@ -835,19 +841,8 @@ def preprocessing(etrago, apply_on="grid_model"):
835841
network_elec.lines = lines_plus_dc.copy()
836842
network_elec.lines["carrier"] = "AC"
837843

838-
# State whether to create a bus weighting and save it, create or not save
839-
# it, or use a bus weighting from a csv file
840-
if settings["bus_weight_tocsv"] is not None:
841-
weight = weighting_for_scenario(
842-
network=network, save=settings["bus_weight_tocsv"]
843-
)
844-
elif settings["bus_weight_fromcsv"] is not None:
845-
weight = pd.read_csv(
846-
settings["bus_weight_fromcsv"], index_col="Bus", squeeze=True
847-
)
848-
weight.index = weight.index.astype(str)
849-
else:
850-
weight = weighting_for_scenario(network=network, save=False)
844+
# weight buses for clustering
845+
weight = weighting_for_scenario(network=network, save=False)
851846

852847
return network_elec, weight, n_clusters, busmap_foreign
853848

@@ -883,9 +878,9 @@ def postprocessing(
883878
busmap : pandas.Series
884879
Updated mapping between buses and clusters
885880
"""
886-
settings = etrago.args["network_clustering"]
887-
method = settings["method"]
888-
num_clusters = settings["n_clusters_AC"]
881+
settings = etrago.args["network_clustering"]["electricity_grid"]
882+
method = etrago.args["network_clustering"]["method"]["algorithm"]
883+
num_clusters = settings["n_clusters"]
889884

890885
if not settings["k_elec_busmap"]:
891886
busmap.name = "cluster"
@@ -933,7 +928,9 @@ def postprocessing(
933928
)
934929

935930
# merge busmap for foreign buses with the German buses
936-
if not settings["cluster_foreign_AC"] and (apply_on == "grid_model"):
931+
if etrago.args["network_clustering"]["method"]["per_country"] and (
932+
apply_on == "grid_model"
933+
):
937934
for bus in busmap_foreign.index:
938935
busmap[bus] = busmap_foreign[bus]
939936
if bus == busmap_foreign[bus]:
@@ -952,7 +949,9 @@ def postprocessing(
952949
one_port_strategies=strategies_one_ports(),
953950
generator_strategies=strategies_generators(),
954951
aggregate_one_ports=aggregate_one_ports,
955-
line_length_factor=settings["line_length_factor"],
952+
line_length_factor=etrago.args["network_clustering"]["method"][
953+
"line_length_factor"
954+
],
956955
bus_strategies=strategies_buses(),
957956
line_strategies=strategies_lines(),
958957
)
@@ -1139,17 +1138,39 @@ def run_spatial_clustering(self):
11391138
-------
11401139
None
11411140
"""
1142-
if self.args["network_clustering"]["active"]:
1141+
if self.args["network_clustering"]["electricity_grid"]["active"]:
11431142
if self.args["spatial_disaggregation"] is not None:
11441143
self.disaggregated_network = self.network.copy()
11451144
else:
11461145
self.disaggregated_network = self.network.copy(with_time=False)
11471146

11481147
elec_network, weight, n_clusters, busmap_foreign = preprocessing(self)
11491148

1150-
if self.args["network_clustering"]["method"] == "kmeans":
1151-
if not self.args["network_clustering"]["k_elec_busmap"]:
1152-
logger.info("Start k-means Clustering")
1149+
focus_region = self.args["network_clustering"]["method"][
1150+
"focus_region"
1151+
]
1152+
if focus_region:
1153+
weight = focus_weighting(
1154+
self,
1155+
elec_network,
1156+
weight,
1157+
focus_region=focus_region,
1158+
cluster_within=self.args["network_clustering"][
1159+
"electricity_grid"
1160+
]["cluster_within_focus"],
1161+
per_country=self.args["network_clustering"]["method"][
1162+
"per_country"
1163+
],
1164+
cpu_cores=self.args["network_clustering"]["method"][
1165+
"cpu_cores"
1166+
],
1167+
)
1168+
1169+
if self.args["network_clustering"]["method"]["algorithm"] == "kmeans":
1170+
if not self.args["network_clustering"]["electricity_grid"][
1171+
"k_elec_busmap"
1172+
]:
1173+
logger.info("Start k-means Clustering AC")
11531174

11541175
busmap = kmean_clustering(
11551176
self, elec_network, weight, n_clusters
@@ -1159,9 +1180,14 @@ def run_spatial_clustering(self):
11591180
busmap = pd.Series(dtype=str)
11601181
medoid_idx = pd.Series(dtype=str)
11611182

1162-
elif self.args["network_clustering"]["method"] == "kmedoids-dijkstra":
1163-
if not self.args["network_clustering"]["k_elec_busmap"]:
1164-
logger.info("Start k-medoids Dijkstra Clustering")
1183+
elif (
1184+
self.args["network_clustering"]["method"]["algorithm"]
1185+
== "kmedoids-dijkstra"
1186+
):
1187+
if not self.args["network_clustering"]["electricity_grid"][
1188+
"k_elec_busmap"
1189+
]:
1190+
logger.info("Start k-medoids Dijkstra Clustering AC")
11651191

11661192
busmap, medoid_idx = kmedoids_dijkstra_clustering(
11671193
self,
@@ -1193,7 +1219,9 @@ def run_spatial_clustering(self):
11931219

11941220
logger.info(
11951221
"Network clustered to {} buses with ".format(
1196-
self.args["network_clustering"]["n_clusters_AC"]
1222+
self.args["network_clustering"]["electricity_grid"][
1223+
"n_clusters"
1224+
]
11971225
)
1198-
+ self.args["network_clustering"]["method"]
1226+
+ self.args["network_clustering"]["method"]["algorithm"]
11991227
)

0 commit comments

Comments
 (0)