1- import logging
21import os
32import sys
43
5- sys .path .append (os .path .abspath (os .path .dirname (__file__ )))
6- sys .path .append (
7- os .path .abspath (os .path .join (os .path .dirname (__file__ ), ".." ))
8- )
4+ sys .path .append (os .path .abspath (os .path .dirname (__file__ )))
5+ sys .path .append (os .path .abspath (os .path .join (os .path .dirname (__file__ ), ".." )))
6+
7+ import collections
8+ import itertools
9+ import os
10+ import re
911
1012import matplotlib .pyplot as plt
1113import numpy as np
1214import pandas as pd
1315import pypsa
14- import os , re , collections
15- import itertools
16-
1716from _helpers import configure_logging , mock_snakemake
1817
1918groups = {
2019 "gas" : ["gas CHP" , "OCGT" , "CCGT" , "gas" ],
2120 "heat vent" : ["heat vent" ],
2221 "water tanks" : ["water tank" , "water pit" ],
23- "heat pump" : ["heat pump" ],
24- "resistive heater" : ["resistive heater" ],
22+ "heat pump" : ["heat pump" ],
23+ "resistive heater" : ["resistive heater" ],
2524 "biomass" : ["biomass" ],
2625 "lignite" : ["lignite" ],
2726 "coal" : ["coal" ],
3130 "offwind" : ["offwind" ],
3231}
3332
33+
3434def aggregate_by_keywords (opex_comp_agg , groups ):
3535 """
3636 Aggregate rows in opex_comp_agg according to keyword groups.
37-
37+
3838 Parameters
3939 ----------
4040 opex_comp_agg : pd.DataFrame
4141 DataFrame with row index as technology names.
4242 groups : dict
43- Keys = new aggregated name,
43+ Keys = new aggregated name,
4444 Values = list of substrings to match in the index.
45-
45+
4646 Returns
4747 -------
4848 pd.DataFrame
@@ -57,7 +57,6 @@ def aggregate_by_keywords(opex_comp_agg, groups):
5757 return df_out
5858
5959
60-
6160if __name__ == "__main__" :
6261 if "snakemake" not in globals ():
6362 import os
@@ -72,21 +71,21 @@ def aggregate_by_keywords(opex_comp_agg, groups):
7271 configure_logging (snakemake )
7372 config = snakemake .config
7473 planning_horizons = snakemake .params .planning_horizons
75- scenarios = ["AriadneDemand" , "LowDemand" ]
74+ scenarios = ["AriadneDemand" , "LowDemand" ]
7675 tech_colors = snakemake .params .plotting ["tech_colors" ]
7776
7877 # Nested dict: networks[year][scenario][decision] = Network
79- networks = collections .defaultdict (
80- lambda : collections .defaultdict (dict )
81- )
78+ networks = collections .defaultdict (lambda : collections .defaultdict (dict ))
8279
8380 for fn in snakemake .input .regret_networks :
8481 parts = fn .split (os .sep )
8582
8683 # scenario is the folder name 2 levels up
8784 scenario = parts [- 3 ]
8885 if scenario not in scenarios :
89- raise ValueError (f"Unexpected scenario '{ scenario } ' in { fn } . Allowed: { scenarios } " )
86+ raise ValueError (
87+ f"Unexpected scenario '{ scenario } ' in { fn } . Allowed: { scenarios } "
88+ )
9089
9190 # extract year (4 digits before .nc)
9291 m = re .search (r"_(\d{4})\.nc$" , fn )
@@ -121,17 +120,21 @@ def aggregate_by_keywords(opex_comp_agg, groups):
121120
122121 for i , year in enumerate (years ):
123122 for scenario , decision in itertools .product (scenarios , decisions ):
124-
125- n = networks [year ][scenario ][decision ]
126- lmps = n .buses_t .marginal_price .loc [:,
127- (n .buses .carrier == "AC" ) & (n .buses .index .str .startswith ("DE" ))]
128- lmps_sorted = pd .DataFrame (lmps .values .flatten (), columns = ["lmp" ]).sort_values (by = "lmp" , ascending = False )
129- lmps_sorted ["percentage" ] = np .arange (len (lmps_sorted )) / len (lmps_sorted ) * 100
123+ n = networks [year ][scenario ][decision ]
124+ lmps = n .buses_t .marginal_price .loc [
125+ :, (n .buses .carrier == "AC" ) & (n .buses .index .str .startswith ("DE" ))
126+ ]
127+ lmps_sorted = pd .DataFrame (
128+ lmps .values .flatten (), columns = ["lmp" ]
129+ ).sort_values (by = "lmp" , ascending = False )
130+ lmps_sorted ["percentage" ] = (
131+ np .arange (len (lmps_sorted )) / len (lmps_sorted ) * 100
132+ )
130133
131134 ax [i ].plot (
132- lmps_sorted ["percentage" ],
133- lmps_sorted ["lmp" ],
134- label = f"{ scenario } _{ decision } (avg: { lmps_sorted ['lmp' ].mean ():.2f} )"
135+ lmps_sorted ["percentage" ],
136+ lmps_sorted ["lmp" ],
137+ label = f"{ scenario } _{ decision } (avg: { lmps_sorted ['lmp' ].mean ():.2f} )" ,
135138 )
136139
137140 ax [i ].legend ()
@@ -143,17 +146,15 @@ def aggregate_by_keywords(opex_comp_agg, groups):
143146 plt .savefig (snakemake .output .elec_price_comp_de , bbox_inches = "tight" )
144147 plt .close ()
145148
146-
147149 # Print CO2 prices
148-
150+
149151 # for i, year in enumerate(years):
150152 # for scenario, decision in itertools.product(scenarios, decisions):
151153
152- # n = networks[year][scenario][decision]
154+ # n = networks[year][scenario][decision]
153155
154156 # print(f"CO2 price for {year}, {scenario}, {decision}: {n.global_constraints.loc["CO2Limit", "mu"] + n.global_constraints.loc["co2_limit-DE", "mu"]}")
155157
156-
157158 # Plot OPEX
158159
159160 kwargs = {
@@ -166,7 +167,9 @@ def aggregate_by_keywords(opex_comp_agg, groups):
166167 axes = axes .flatten ()
167168
168169 for i , year in enumerate (years ):
169- opex_comp = pd .DataFrame (columns = ["_" .join (tup ) for tup in itertools .product (scenarios , decisions )])
170+ opex_comp = pd .DataFrame (
171+ columns = ["_" .join (tup ) for tup in itertools .product (scenarios , decisions )]
172+ )
170173
171174 # Collect OPEX for all scenario-decision combinations
172175 for scenario , decision in itertools .product (scenarios , decisions ):
@@ -175,7 +178,8 @@ def aggregate_by_keywords(opex_comp_agg, groups):
175178 opex = (
176179 n .statistics .opex (** kwargs )
177180 .filter (like = "DE" )
178- .groupby ("carrier" ).sum ()
181+ .groupby ("carrier" )
182+ .sum ()
179183 .multiply (1e-9 ) # to billion €
180184 )
181185 opex_comp [f"{ scenario } _{ decision } " ] = opex
@@ -185,43 +189,59 @@ def aggregate_by_keywords(opex_comp_agg, groups):
185189 small_rows = opex_comp_agg .abs ().max (axis = 1 ) < 0.1
186190 other_row = opex_comp_agg [small_rows ].sum (axis = 0 )
187191 opex_comp_agg = opex_comp_agg .loc [~ small_rows ]
188- opex_comp_agg .loc [' Other' ] = other_row
192+ opex_comp_agg .loc [" Other" ] = other_row
189193
190194 # Prepare labels with line breaks
191- labels = [col .replace ('_' , ' \n ' ) for col in opex_comp_agg .columns ]
195+ labels = [col .replace ("_" , " \n " ) for col in opex_comp_agg .columns ]
192196
193197 # Plot stacked bar
194198 ax = axes [i ]
195199 bottom = np .zeros (len (opex_comp_agg .columns ))
196200
197201 for tech in opex_comp_agg .index :
198202 values = opex_comp_agg .loc [tech ].values
199- ax .bar (labels , values , bottom = bottom , color = tech_colors .get (tech , '#333333' ), label = tech )
203+ ax .bar (
204+ labels ,
205+ values ,
206+ bottom = bottom ,
207+ color = tech_colors .get (tech , "#333333" ),
208+ label = tech ,
209+ )
200210
201211 # Add numbers in the middle, except for 'Other'
202- if tech != ' Other' :
212+ if tech != " Other" :
203213 for j , val in enumerate (values ):
204214 if val > 0 : # only if positive
205215 ax .text (
206- j ,
207- bottom [j ] + val / 2 , # middle of the segment
208- f'{ val :.2f} ' ,
209- ha = 'center' , va = 'center' , fontsize = 8 , color = 'white'
216+ j ,
217+ bottom [j ] + val / 2 , # middle of the segment
218+ f"{ val :.2f} " ,
219+ ha = "center" ,
220+ va = "center" ,
221+ fontsize = 8 ,
222+ color = "white" ,
210223 )
211224
212225 bottom += values
213226
214227 # Add total sum labels on top of bars
215228 totals = opex_comp_agg .sum (axis = 0 )
216229 for j , total in enumerate (totals ):
217- ax .text (j , total + total * 0.02 , f'{ total :.2f} ' , ha = 'center' , va = 'bottom' , fontsize = 10 )
230+ ax .text (
231+ j ,
232+ total + total * 0.02 ,
233+ f"{ total :.2f} " ,
234+ ha = "center" ,
235+ va = "bottom" ,
236+ fontsize = 10 ,
237+ )
218238
219239 # Adjust y-limit
220- ax .set_ylim (0 , max (totals )* 1.08 )
221- ax .set_ylabel (' OPEX [billion €]' )
222- ax .set_title (f' Stacked OPEX composition by technology, { year } ' )
240+ ax .set_ylim (0 , max (totals ) * 1.08 )
241+ ax .set_ylabel (" OPEX [billion €]" )
242+ ax .set_title (f" Stacked OPEX composition by technology, { year } " )
223243
224244 # Legend outside
225- axes [- 1 ].legend (loc = ' upper left' , bbox_to_anchor = (1 ,1 ))
226- plt .savefig (snakemake .output [- 1 ] + f "/opex_comp_de.png" , bbox_inches = "tight" )
245+ axes [- 1 ].legend (loc = " upper left" , bbox_to_anchor = (1 , 1 ))
246+ plt .savefig (snakemake .output [- 1 ] + "/opex_comp_de.png" , bbox_inches = "tight" )
227247 plt .close ()
0 commit comments