Spaces:
Sleeping
Sleeping
Upload 4 files
Browse filesupdate from branch german_light
- Input_Jahr_2021.xlsx +2 -2
- app.py +216 -87
- model_data.pkl +2 -2
- sourced.py +11 -11
Input_Jahr_2021.xlsx
CHANGED
@@ -1,3 +1,3 @@
|
|
1 |
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:
|
3 |
-
size
|
|
|
1 |
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:03bfa05bc9361595ea2345ee1a423193dab5051900e5acc244100ff787c09624
|
3 |
+
size 1127885
|
app.py
CHANGED
@@ -32,14 +32,15 @@ col4.header("Download Results")
|
|
32 |
# Color dictionary for figures
|
33 |
color_dict = {'Biomasse': 'lightgreen',
|
34 |
'Braunkohle': 'red',
|
35 |
-
'Erdgas': '
|
36 |
'Steinkohle': 'darkgrey',
|
37 |
'Erdöl': 'brown',
|
38 |
'Laufwasser': 'aquamarine',
|
39 |
-
'Kernenergie': '
|
40 |
'PV': 'yellow',
|
41 |
'WindOff': 'darkblue',
|
42 |
-
'WindOn': 'blue'
|
|
|
43 |
|
44 |
# %%
|
45 |
with col1:
|
@@ -86,15 +87,14 @@ def timstep_aggregate(time_steps_aggregate, xr ):
|
|
86 |
t = sets_dict['t']
|
87 |
t_original = sets_dict['t']
|
88 |
i = sets_dict['i']
|
89 |
-
|
90 |
iConv = sets_dict['iConv']
|
91 |
# iPtG = sets_dict['iPtG']
|
92 |
iRes = sets_dict['iRes']
|
93 |
# iHyRes = sets_dict['iHyRes']
|
94 |
|
95 |
# Unpack params_dict into the workspace
|
96 |
-
|
97 |
-
l_co2 = 90
|
98 |
p_co2 = params_dict['p_co2']
|
99 |
|
100 |
eff_i = params_dict['eff_i']
|
@@ -105,7 +105,7 @@ c_inv_i = params_dict['c_inv_i']
|
|
105 |
co2_factor_i = params_dict['co2_factor_i']
|
106 |
c_var_i = params_dict['c_var_i']
|
107 |
K_0_i = params_dict['K_0_i']
|
108 |
-
|
109 |
|
110 |
# Sliders and input boxes for parameters
|
111 |
with col2:
|
@@ -127,13 +127,12 @@ with col3:
|
|
127 |
if i_idx in ['Steinkohle', 'Erdöl','Erdgas']:
|
128 |
c_fuel_i.loc[i_idx] = st.slider(value=int(c_fuel_i.loc[i_idx]), min_value=0, max_value=300, label=i_idx + ' Preis [€/MWh]' , step=10)
|
129 |
|
130 |
-
technologies_invest = st.multiselect(label='Technologien für Investitionen', options=i, default=['
|
|
|
131 |
technologies_no_invest = [x for x in i if x not in technologies_invest]
|
132 |
|
133 |
# Aggregate time series
|
134 |
D_t = timstep_aggregate(dt,params_dict['D_t'])
|
135 |
-
# D_t sorted descending
|
136 |
-
D_t_sorted = D_t.sortby(D_t, ascending = False)
|
137 |
s_t_r_iRes = timstep_aggregate(dt,params_dict['s_t_r_iRes'])
|
138 |
# h_t = timstep_aggregate(dt,params_dict['h_t'])
|
139 |
t = D_t.get_index('t')
|
@@ -164,8 +163,8 @@ C_inv = m.add_variables(name = 'C_inv', lower = 0) # Investment costs
|
|
164 |
|
165 |
K = m.add_variables(coords = [i], name = 'K', lower = 0) # Endogenous capacity
|
166 |
y = m.add_variables(coords = [t,i], name = 'y', lower = 0) # Electricity production --> für Elektrolyseure ausschließen
|
167 |
-
|
168 |
-
|
169 |
w = m.add_variables(coords = [t], name = 'w', lower = 0)
|
170 |
y_curt = m.add_variables(coords = [t,i], name = 'y_curt', lower = 0) # RES curtailment
|
171 |
# y_h2 = m.add_variables(coords = [t,i], name = 'y_h2', lower = 0)
|
@@ -183,8 +182,8 @@ C_op_sum = m.add_constraints((y * c_fuel_i/eff_i).sum() * dt == C_op, name = 'C
|
|
183 |
C_inv_sum = m.add_constraints((K * c_inv_i).sum() == C_inv, name = 'C_inv_sum')
|
184 |
|
185 |
## Load serving
|
186 |
-
|
187 |
-
loadserve_t = m.add_constraints((((y ).sum(dims = 'i') ) * dt == D_t.sel(t = t) * dt), name = 'load')
|
188 |
|
189 |
## Maximum capacity limit
|
190 |
maxcap_i_t = m.add_constraints((y - K <= K_0_i), name = 'max_cap')
|
@@ -196,7 +195,7 @@ maxcap_invest_i = m.add_constraints((K.sel(i = technologies_no_invest) <= 0), na
|
|
196 |
# no_power_prod_iPtG_t = m.add_constraints((y.sel(i = iPtG) <= 0), name = 'prevent_ptg_prod')
|
197 |
|
198 |
## Maximum storage charging and discharging
|
199 |
-
|
200 |
|
201 |
## Maximum electrolyzer capacity
|
202 |
# ptg_prod_iPtG_t = m.add_constraints((y_ch.sel(i = iPtG) - K.sel(i = iPtG) <= K_0_i.sel(i = iPtG)), name = 'max_cha_ptg')
|
@@ -208,13 +207,13 @@ maxcap_invest_i = m.add_constraints((K.sel(i = technologies_no_invest) <= 0), na
|
|
208 |
infeed_iRes_t = m.add_constraints((y.sel(i = iRes) - s_t_r_iRes.sel(i = iRes).sel(t = t) * K.sel(i = iRes) + y_curt.sel(i = iRes) == s_t_r_iRes.sel(i = iRes).sel(t = t) * K_0_i.sel(i = iRes)), name = 'infeed')
|
209 |
|
210 |
## Maximum filling level restriction storage power plant
|
211 |
-
|
212 |
|
213 |
## Filling level restriction hydro reservoir
|
214 |
# filling_iHydro_t = m.add_constraints(l.sel(i = iHyRes) - l.sel(i = iHyRes).roll(t = -1) + y.sel(i = iHyRes) * dt == h_t.sel(t = t) * dt, name = 'filling_level_hydro')
|
215 |
|
216 |
## Filling level restriction other storages
|
217 |
-
|
218 |
|
219 |
## CO2 limit
|
220 |
CO2_limit = m.add_constraints(((y / eff_i) * co2_factor_i * dt).sum() <= l_co2 * 1_000_000 , name = 'CO2_limit')
|
@@ -222,7 +221,8 @@ CO2_limit = m.add_constraints(((y / eff_i) * co2_factor_i * dt).sum() <= l_co2 *
|
|
222 |
## set run-of-river power plants capacity limit to 5 GW
|
223 |
RoR_cap = m.add_constraints(K.sel(i = 'Laufwasser') <= 5000, name = 'RoR_cap')
|
224 |
Biomass_cap = m.add_constraints(K.sel(i = 'Biomasse') <= 9000, name = 'Biomass_cap')
|
225 |
-
|
|
|
226 |
|
227 |
# %%
|
228 |
m.solve(solver_name = 'highs')
|
@@ -268,25 +268,46 @@ with colb1:
|
|
268 |
fig
|
269 |
|
270 |
# %%
|
271 |
-
|
272 |
-
#
|
273 |
-
|
274 |
-
#
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
280 |
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
fig
|
286 |
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
# %%
|
291 |
i_with_capacity = m.solution['K'].where( m.solution['K'] > 0).dropna(dim = 'i').get_index('i')
|
292 |
df_production = m.solution['y'].sel(i = i_with_capacity).to_dataframe().reset_index()
|
@@ -294,44 +315,48 @@ fig = px.area(m.solution['y'].sel(i = i_with_capacity).to_dataframe().reset_inde
|
|
294 |
fig.update_traces(line=dict(width=0))
|
295 |
fig.for_each_trace(lambda trace: trace.update(fillcolor = trace.line.color))
|
296 |
|
297 |
-
with
|
298 |
fig
|
299 |
-
#
|
300 |
-
#Add pie chart of total production per technology type in GWh(divide by 1000)
|
301 |
-
df_production_sum = (df_production.groupby('i')['y'].sum() * dt / 1000 ).round(0).sort_values(ascending=False).reset_index()
|
302 |
|
303 |
-
fig = px.pie(df_production_sum, names="i", values='y', title='Gesamtproduktion [GWh] als Kuchendiagramm',
|
304 |
-
color='i', color_discrete_map=color_dict)
|
305 |
-
|
306 |
-
with colb2:
|
307 |
-
fig
|
308 |
|
309 |
# %%
|
310 |
-
|
311 |
df_price = m.constraints['load'].dual.to_dataframe().reset_index()
|
|
|
|
|
|
|
|
|
|
|
|
|
312 |
|
313 |
-
fig = px.line(
|
314 |
-
with
|
315 |
fig
|
316 |
|
317 |
-
|
318 |
# %%
|
319 |
-
#
|
320 |
-
|
321 |
-
|
322 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
323 |
with colb1:
|
324 |
fig
|
325 |
-
|
326 |
-
# %%
|
327 |
-
|
328 |
-
df_contr_marg = m.constraints['max_cap'].dual.to_dataframe().reset_index()
|
329 |
-
df_contr_marg['dual'] = df_contr_marg['dual'] / dt * (-1)
|
330 |
-
|
331 |
|
332 |
# %%
|
333 |
|
334 |
-
fig = px.line(
|
335 |
with colb2:
|
336 |
fig
|
337 |
|
@@ -346,62 +371,147 @@ fig.for_each_trace(lambda trace: trace.update(fillcolor = trace.line.color))
|
|
346 |
with colb1:
|
347 |
fig
|
348 |
|
|
|
349 |
# %%
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
350 |
df_production_pivot = df_production.pivot(index='t', columns='i', values='y')
|
351 |
# sort columns according to i_with_capacity
|
352 |
df_production_pivot = df_production_pivot[i_with_capacity]
|
|
|
353 |
co2_factor_i_with_capacity = co2_factor_i.sel(i = i_with_capacity)
|
354 |
# colour_dict = {i: color_dict[i] for i in i_with_capacity}
|
355 |
color_dict_with_capacity = {i: color_dict[i] for i in i_with_capacity}
|
356 |
-
|
357 |
# multiply df_production with co2 factor
|
358 |
-
df_production_emissions = df_production_pivot * co2_factor_i_with_capacity
|
359 |
# unpivot df_production_emissions, sorting by datetime
|
360 |
df_production_emissions_unpivot = df_production_emissions.reset_index().melt(id_vars='t', var_name='i', value_name='y')
|
361 |
-
df_production_emissions_unpivot =
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
# sum up cumulated emissions
|
366 |
-
df_production_emissions_sorted['cumsum'] = df_production_emissions_sorted['y'].cumsum()
|
367 |
|
368 |
# generate area plot of df_production_emissions_unpivot over t
|
369 |
fig = px.area(df_production_emissions_unpivot, y='y', x='t', title='Co2-Emissionen [t]', color='i', color_discrete_map=color_dict_with_capacity)
|
370 |
fig.update_traces(line=dict(width=0))
|
371 |
fig.for_each_trace(lambda trace: trace.update(fillcolor = trace.line.color))
|
372 |
-
# fig = px.area(df_production_emissions_unpivot.sel(i = i_with_capacity).to_dataframe().reset_index(), y='y', x='t', title='Stromproduktion Lastgang [MW]', color='i', color_discrete_map=color_dict)
|
373 |
-
# fig.update_traces(line=dict(width=0))
|
374 |
-
# fig.for_each_trace(lambda trace: trace.update(fillcolor = trace.line.color))
|
375 |
-
with colb2:
|
376 |
-
fig
|
377 |
|
|
|
|
|
378 |
|
379 |
# %%
|
380 |
-
#
|
381 |
-
|
382 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
383 |
fig.update_traces(line=dict(width=0))
|
384 |
-
fig.for_each_trace(lambda trace: trace.update(fillcolor
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
385 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
386 |
with colb2:
|
387 |
fig
|
388 |
|
389 |
# %%
|
390 |
-
#
|
391 |
-
#
|
392 |
-
|
393 |
-
# fig
|
|
|
394 |
|
395 |
# with colb2:
|
396 |
# fig
|
397 |
|
398 |
# %%
|
399 |
-
# define vector x with size (1,size D_t_sorted)
|
400 |
-
x = np.arange(1, D_t_sorted.size + 1)
|
401 |
-
fig = px.line(y=D_t_sorted, x=x, title='Lastdauerlinie [€/MWh]', labels={"x": "Stunden im Jahr"})
|
402 |
-
with colb2:
|
403 |
-
fig
|
404 |
-
|
405 |
# # %%
|
406 |
# df_h2_prod = m.solution['y_h2'].sel(i = iPtG).to_dataframe().reset_index()
|
407 |
# fig = px.area(m.solution['y_h2'].sel(i = iPtG).to_dataframe().reset_index(), y='y_h2', x='t', title='Produktion Wasserstoff [MWh_th]', color='i', color_discrete_map=color_dict)
|
@@ -411,6 +521,25 @@ with colb2:
|
|
411 |
# with colb2:
|
412 |
# fig
|
413 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
414 |
# %%
|
415 |
((m.solution['y'] / eff_i) * co2_factor_i * dt).sum()
|
416 |
# %%
|
@@ -453,10 +582,10 @@ with pd.ExcelWriter(output, engine='xlsxwriter') as writer:
|
|
453 |
disaggregate_df(df_total_costs).to_excel(writer, sheet_name='Gesamtkosten', index=False)
|
454 |
disaggregate_df(df_CO2_price).to_excel(writer, sheet_name='CO2 Preis', index=False)
|
455 |
disaggregate_df(df_price).to_excel(writer, sheet_name='Preise', index=False)
|
456 |
-
disaggregate_df(df_contr_marg).to_excel(writer, sheet_name='Deckungsbeiträge', index=False)
|
457 |
disaggregate_df(df_new_capacities).to_excel(writer, sheet_name='Kapazitäten', index=False)
|
458 |
disaggregate_df(df_production).to_excel(writer, sheet_name='Produktion', index=False)
|
459 |
-
|
460 |
disaggregate_df(D_t.to_dataframe().reset_index()).to_excel(writer, sheet_name='Nachfrage', index=False)
|
461 |
disaggregate_df(df_curtailment).to_excel(writer, sheet_name='Abregelung', index=False)
|
462 |
# disaggregate_df(df_h2_prod).to_excel(writer, sheet_name='H2 produktion', index=False)
|
|
|
32 |
# Color dictionary for figures
|
33 |
color_dict = {'Biomasse': 'lightgreen',
|
34 |
'Braunkohle': 'red',
|
35 |
+
'Erdgas': 'orange',
|
36 |
'Steinkohle': 'darkgrey',
|
37 |
'Erdöl': 'brown',
|
38 |
'Laufwasser': 'aquamarine',
|
39 |
+
'Kernenergie': 'cyan',
|
40 |
'PV': 'yellow',
|
41 |
'WindOff': 'darkblue',
|
42 |
+
'WindOn': 'blue',
|
43 |
+
'Batteriespeicher': 'purple'}
|
44 |
|
45 |
# %%
|
46 |
with col1:
|
|
|
87 |
t = sets_dict['t']
|
88 |
t_original = sets_dict['t']
|
89 |
i = sets_dict['i']
|
90 |
+
iSto = sets_dict['iSto']
|
91 |
iConv = sets_dict['iConv']
|
92 |
# iPtG = sets_dict['iPtG']
|
93 |
iRes = sets_dict['iRes']
|
94 |
# iHyRes = sets_dict['iHyRes']
|
95 |
|
96 |
# Unpack params_dict into the workspace
|
97 |
+
l_co2 = params_dict['l_co2']
|
|
|
98 |
p_co2 = params_dict['p_co2']
|
99 |
|
100 |
eff_i = params_dict['eff_i']
|
|
|
105 |
co2_factor_i = params_dict['co2_factor_i']
|
106 |
c_var_i = params_dict['c_var_i']
|
107 |
K_0_i = params_dict['K_0_i']
|
108 |
+
e2p_iSto = params_dict['e2p_iSto']
|
109 |
|
110 |
# Sliders and input boxes for parameters
|
111 |
with col2:
|
|
|
127 |
if i_idx in ['Steinkohle', 'Erdöl','Erdgas']:
|
128 |
c_fuel_i.loc[i_idx] = st.slider(value=int(c_fuel_i.loc[i_idx]), min_value=0, max_value=300, label=i_idx + ' Preis [€/MWh]' , step=10)
|
129 |
|
130 |
+
# technologies_invest = st.multiselect(label='Technologien für Investitionen', options=i, default=['Biomasse','Laufwasser','Kernenergie','Braunkohle','Steinkohle','Erdöl','Erdgas','WindOff','WindOn','PV','Batteriespeicher'])
|
131 |
+
technologies_invest = st.multiselect(label='Technologien für Investitionen', options=i, default=['Kernenergie','Braunkohle','Steinkohle','Erdgas'])
|
132 |
technologies_no_invest = [x for x in i if x not in technologies_invest]
|
133 |
|
134 |
# Aggregate time series
|
135 |
D_t = timstep_aggregate(dt,params_dict['D_t'])
|
|
|
|
|
136 |
s_t_r_iRes = timstep_aggregate(dt,params_dict['s_t_r_iRes'])
|
137 |
# h_t = timstep_aggregate(dt,params_dict['h_t'])
|
138 |
t = D_t.get_index('t')
|
|
|
163 |
|
164 |
K = m.add_variables(coords = [i], name = 'K', lower = 0) # Endogenous capacity
|
165 |
y = m.add_variables(coords = [t,i], name = 'y', lower = 0) # Electricity production --> für Elektrolyseure ausschließen
|
166 |
+
y_ch = m.add_variables(coords = [t,i], name = 'y_ch', lower = 0) # Electricity consumption --> für alles außer Elektrolyseure und Speicher ausschließen
|
167 |
+
l = m.add_variables(coords = [t,i], name = 'l', lower = 0) # Storage filling level
|
168 |
w = m.add_variables(coords = [t], name = 'w', lower = 0)
|
169 |
y_curt = m.add_variables(coords = [t,i], name = 'y_curt', lower = 0) # RES curtailment
|
170 |
# y_h2 = m.add_variables(coords = [t,i], name = 'y_h2', lower = 0)
|
|
|
182 |
C_inv_sum = m.add_constraints((K * c_inv_i).sum() == C_inv, name = 'C_inv_sum')
|
183 |
|
184 |
## Load serving
|
185 |
+
loadserve_t = m.add_constraints((((y ).sum(dims = 'i') - y_ch.sum(dims = 'i')) * dt == D_t.sel(t = t) * dt), name = 'load')
|
186 |
+
# loadserve_t = m.add_constraints((((y ).sum(dims = 'i') ) * dt == D_t.sel(t = t) * dt), name = 'load')
|
187 |
|
188 |
## Maximum capacity limit
|
189 |
maxcap_i_t = m.add_constraints((y - K <= K_0_i), name = 'max_cap')
|
|
|
195 |
# no_power_prod_iPtG_t = m.add_constraints((y.sel(i = iPtG) <= 0), name = 'prevent_ptg_prod')
|
196 |
|
197 |
## Maximum storage charging and discharging
|
198 |
+
maxcha_iSto_t = m.add_constraints((y.sel(i = iSto) - y_ch.sel(i = iSto) - K.sel(i = iSto) <= K_0_i.sel(i = iSto)), name = 'max_cha')
|
199 |
|
200 |
## Maximum electrolyzer capacity
|
201 |
# ptg_prod_iPtG_t = m.add_constraints((y_ch.sel(i = iPtG) - K.sel(i = iPtG) <= K_0_i.sel(i = iPtG)), name = 'max_cha_ptg')
|
|
|
207 |
infeed_iRes_t = m.add_constraints((y.sel(i = iRes) - s_t_r_iRes.sel(i = iRes).sel(t = t) * K.sel(i = iRes) + y_curt.sel(i = iRes) == s_t_r_iRes.sel(i = iRes).sel(t = t) * K_0_i.sel(i = iRes)), name = 'infeed')
|
208 |
|
209 |
## Maximum filling level restriction storage power plant
|
210 |
+
maxcapsto_iSto_t = m.add_constraints((l.sel(i = iSto) - K.sel(i = iSto) * e2p_iSto.sel(i = iSto) <= K_0_i.sel(i = iSto) * e2p_iSto.sel(i = iSto)), name = 'max_sto_filling')
|
211 |
|
212 |
## Filling level restriction hydro reservoir
|
213 |
# filling_iHydro_t = m.add_constraints(l.sel(i = iHyRes) - l.sel(i = iHyRes).roll(t = -1) + y.sel(i = iHyRes) * dt == h_t.sel(t = t) * dt, name = 'filling_level_hydro')
|
214 |
|
215 |
## Filling level restriction other storages
|
216 |
+
filling_iSto_t = m.add_constraints(l.sel(i = iSto) - (l.sel(i = iSto).roll(t = -1) + (y.sel(i = iSto) / eff_i.sel(i = iSto)) * dt - y_ch.sel(i = iSto) * eff_i.sel(i = iSto) * dt) == 0, name = 'filling_level')
|
217 |
|
218 |
## CO2 limit
|
219 |
CO2_limit = m.add_constraints(((y / eff_i) * co2_factor_i * dt).sum() <= l_co2 * 1_000_000 , name = 'CO2_limit')
|
|
|
221 |
## set run-of-river power plants capacity limit to 5 GW
|
222 |
RoR_cap = m.add_constraints(K.sel(i = 'Laufwasser') <= 5000, name = 'RoR_cap')
|
223 |
Biomass_cap = m.add_constraints(K.sel(i = 'Biomasse') <= 9000, name = 'Biomass_cap')
|
224 |
+
# Nuclear_cap = m.add_constraints(K.sel(i = 'Kernenergie') <= 3000, name = 'Kernenergie_cap')
|
225 |
+
# nuclear_production_constraint = m.add_constraints(y.sel(i='Kernenergie') == K.sel(i='Kernenergie'), name='Nuclear_Production_Capacity')
|
226 |
|
227 |
# %%
|
228 |
m.solve(solver_name = 'highs')
|
|
|
268 |
fig
|
269 |
|
270 |
# %%
|
271 |
+
D_t_sorted = D_t.sortby(D_t, ascending = False).to_dataframe().reset_index()
|
272 |
+
# NaN entries to the end
|
273 |
+
D_t_sorted = D_t_sorted.sort_values(by='Nachfrage', ascending=False).reset_index(drop=True)
|
274 |
+
# expand df_price to the size of t
|
275 |
+
D_t_sorted = D_t_sorted.loc[D_t_sorted.index.repeat(dt)].reset_index(drop=True)
|
276 |
+
x_loadcurve = np.arange(1, D_t_sorted['Nachfrage'].size + 1)
|
277 |
+
|
278 |
+
# residual load curve
|
279 |
+
df_production_res = m.solution['y'].sel(i = iRes).to_dataframe().reset_index()
|
280 |
+
# sum up over t
|
281 |
+
df_production_res_sum = df_production_res.groupby('t')['y'].sum().reset_index()
|
282 |
+
# D_t into dateframe
|
283 |
+
D_t_df = D_t.to_dataframe().reset_index()
|
284 |
+
df_residual = D_t_df['Nachfrage'] - df_production_res_sum['y']
|
285 |
+
# sort
|
286 |
+
df_residual = df_residual.sort_values(ascending=False).reset_index(drop=True)
|
287 |
+
df_residual = df_residual.loc[df_residual.index.repeat(dt)].reset_index(drop=True)
|
288 |
+
|
289 |
+
df_combined = pd.DataFrame({
|
290 |
+
'x': np.concatenate([x_loadcurve, x_loadcurve]),
|
291 |
+
'y': np.concatenate([D_t_sorted['Nachfrage'], df_residual]),
|
292 |
+
'label': ['Nachfrage'] * len(x_loadcurve) + ['Residual Load'] * len(x_loadcurve)
|
293 |
+
})
|
294 |
+
|
295 |
+
# Create the integrated plot using Plotly Express
|
296 |
+
fig = px.line(df_combined, x='x', y='y', color='label', title='Lastdauerlinie [€/MW]',
|
297 |
+
labels={"x": "Stunden im Jahr", "y": "Leistung [MW]"})
|
298 |
+
|
299 |
+
# Specific updates for each trace
|
300 |
+
fig.for_each_trace(
|
301 |
+
lambda trace: trace.update(line=dict(color='blue')) if trace.name == 'Nachfrage' else trace.update(line=dict(color='red', dash='dash'))
|
302 |
+
)
|
303 |
|
304 |
+
with colb2:
|
305 |
+
fig.update_layout(
|
306 |
+
legend_title='Legende'
|
307 |
+
)
|
308 |
fig
|
309 |
|
310 |
+
# fig.show()
|
|
|
|
|
311 |
# %%
|
312 |
i_with_capacity = m.solution['K'].where( m.solution['K'] > 0).dropna(dim = 'i').get_index('i')
|
313 |
df_production = m.solution['y'].sel(i = i_with_capacity).to_dataframe().reset_index()
|
|
|
315 |
fig.update_traces(line=dict(width=0))
|
316 |
fig.for_each_trace(lambda trace: trace.update(fillcolor = trace.line.color))
|
317 |
|
318 |
+
with colb1:
|
319 |
fig
|
320 |
+
# fig.show()
|
|
|
|
|
321 |
|
|
|
|
|
|
|
|
|
|
|
322 |
|
323 |
# %%
|
|
|
324 |
df_price = m.constraints['load'].dual.to_dataframe().reset_index()
|
325 |
+
# expand df_price to the size of t
|
326 |
+
df_price = df_price.loc[df_price.index.repeat(dt)].reset_index(drop=True)
|
327 |
+
# sort prices descending
|
328 |
+
df_sorted_price = df_price["dual"].sort_values(ascending=False).reset_index(drop=True)
|
329 |
+
# generate x-axis for price duration curve
|
330 |
+
x_price = np.arange(1, df_sorted_price.size + 1)
|
331 |
|
332 |
+
fig = px.line(y=df_sorted_price, x=x_price, title='Preisdauerlinie [€/MWh]', labels={"x": "Stunden im Jahr"},range_y=[0,350])
|
333 |
+
with colb2:
|
334 |
fig
|
335 |
|
|
|
336 |
# %%
|
337 |
+
# calculate full load hours
|
338 |
+
df_capacity = m.solution['K'].sel(i = i_with_capacity).to_dataframe().reset_index()
|
339 |
+
df_production_sum = (df_production.groupby('i')['y'].sum() * dt).round(0).reset_index()
|
340 |
+
# reorder rows according to i_with_capacity
|
341 |
+
df_production_sum = df_production_sum.set_index('i').loc[i_with_capacity].reset_index()
|
342 |
+
# df_production_sum['i'] = pd.Categorical(df_production_sum['i'], categories=desired_order, ordered=True)
|
343 |
+
|
344 |
+
df_fullload = df_production_sum['y']/df_capacity['K']
|
345 |
+
# to dataframe
|
346 |
+
df_fullload = df_fullload.to_frame()
|
347 |
+
# rename column
|
348 |
+
df_fullload.columns = ['fullload']
|
349 |
+
df_fullload['i'] = df_production_sum['i']
|
350 |
+
# change order of columns
|
351 |
+
df_fullload = df_fullload[['i', 'fullload']]
|
352 |
+
fig = px.bar(df_fullload, y='i', x=df_fullload['fullload'], orientation='h', title='Volllaststunden [h]', color='i', color_discrete_map=color_dict)
|
353 |
with colb1:
|
354 |
fig
|
355 |
+
# fig.show()
|
|
|
|
|
|
|
|
|
|
|
356 |
|
357 |
# %%
|
358 |
|
359 |
+
fig = px.line(df_price, y='dual', x='t', title='Strompreis [€/MWh]', range_y=[0,350])
|
360 |
with colb2:
|
361 |
fig
|
362 |
|
|
|
371 |
with colb1:
|
372 |
fig
|
373 |
|
374 |
+
|
375 |
# %%
|
376 |
+
df_charging = m.solution['y_ch'].sel(i = iSto).to_dataframe().reset_index()
|
377 |
+
fig = px.area(m.solution['y_ch'].sel(i = iSto).to_dataframe().reset_index(), y='y_ch', x='t', title='Speicherbeladung [MWh]', color='i', color_discrete_map=color_dict)
|
378 |
+
fig.update_traces(line=dict(width=0))
|
379 |
+
fig.for_each_trace(lambda trace: trace.update(fillcolor = trace.line.color))
|
380 |
+
|
381 |
+
with colb2:
|
382 |
+
fig
|
383 |
+
|
384 |
+
# %%
|
385 |
+
|
386 |
+
# df_contr_marg = m.constraints['max_cap'].dual.to_dataframe().reset_index()
|
387 |
+
# df_contr_marg['dual'] = df_contr_marg['dual'] / dt * (-1)
|
388 |
+
|
389 |
+
# fig = px.line(df_contr_marg, y='dual', x='t',title='Deckungsbeitrag [€]', color='i', range_y=[0,350], color_discrete_map=color_dict)
|
390 |
+
# with colb2:
|
391 |
+
# fig
|
392 |
+
|
393 |
+
# %%
|
394 |
+
# generate dataframe steplength = 1 same size as t
|
395 |
+
# x = np.arange(1, t.size + 1)
|
396 |
+
x = np.arange(1,t.size)
|
397 |
df_production_pivot = df_production.pivot(index='t', columns='i', values='y')
|
398 |
# sort columns according to i_with_capacity
|
399 |
df_production_pivot = df_production_pivot[i_with_capacity]
|
400 |
+
df_efficiency = eff_i.sel(i = i_with_capacity)
|
401 |
co2_factor_i_with_capacity = co2_factor_i.sel(i = i_with_capacity)
|
402 |
# colour_dict = {i: color_dict[i] for i in i_with_capacity}
|
403 |
color_dict_with_capacity = {i: color_dict[i] for i in i_with_capacity}
|
404 |
+
desired_order = i_with_capacity.tolist()
|
405 |
# multiply df_production with co2 factor
|
406 |
+
df_production_emissions = df_production_pivot/df_efficiency * co2_factor_i_with_capacity*dt
|
407 |
# unpivot df_production_emissions, sorting by datetime
|
408 |
df_production_emissions_unpivot = df_production_emissions.reset_index().melt(id_vars='t', var_name='i', value_name='y')
|
409 |
+
df_production_emissions_unpivot['i'] = pd.Categorical(df_production_emissions_unpivot['i'], categories=desired_order, ordered=True)
|
410 |
+
df_production_emissions_unpivot = df_production_emissions_unpivot.sort_values(by=['t', 'i'])
|
411 |
+
# rearrange rows according to i_with_capacity
|
412 |
+
|
|
|
|
|
413 |
|
414 |
# generate area plot of df_production_emissions_unpivot over t
|
415 |
fig = px.area(df_production_emissions_unpivot, y='y', x='t', title='Co2-Emissionen [t]', color='i', color_discrete_map=color_dict_with_capacity)
|
416 |
fig.update_traces(line=dict(width=0))
|
417 |
fig.for_each_trace(lambda trace: trace.update(fillcolor = trace.line.color))
|
|
|
|
|
|
|
|
|
|
|
418 |
|
419 |
+
with colb1:
|
420 |
+
fig
|
421 |
|
422 |
# %%
|
423 |
+
# Sum up second row of df_production_emissions
|
424 |
+
df_production_emissions_sum = df_production_emissions.copy()
|
425 |
+
df_production_emissions_sum['total'] = df_production_emissions_sum.sum(axis=1)
|
426 |
+
# sort by total generation
|
427 |
+
df_production_emissions_sum = df_production_emissions_sum.sort_values(by='total', ascending=True)
|
428 |
+
# add cumcum column
|
429 |
+
df_production_emissions_sum['cumsum'] = df_production_emissions_sum['total'].cumsum()
|
430 |
+
# make t an ordinary column
|
431 |
+
df_production_emissions_sum = df_production_emissions_sum.reset_index()
|
432 |
+
# add index column
|
433 |
+
df_production_emissions_sum['num'] = np.arange(1, df_production_emissions_sum['total'].size + 1)
|
434 |
+
|
435 |
+
# unpivot df_production_emissions_sum
|
436 |
+
unpivoted_df = df_production_emissions_sum.melt(id_vars=['t', 'num'], var_name='i', value_name='y')
|
437 |
+
# sort by num
|
438 |
+
unpivoted_df_sorted = unpivoted_df.sort_values(by='num', ascending=True)
|
439 |
+
# extract those rows where i is in i_with_capacity
|
440 |
+
unpivoted_df_sorted_cap = unpivoted_df_sorted[unpivoted_df_sorted['i'].isin(i_with_capacity)]
|
441 |
+
# generate stacked area plot of df_unpivoted with i = i_with_capacity
|
442 |
+
fig = px.area(unpivoted_df_sorted_cap, y='y', x='t', title='Kumulierte Co2-Emissionen [t]', color='i', color_discrete_map=color_dict_with_capacity)
|
443 |
+
|
444 |
+
# Update traces
|
445 |
fig.update_traces(line=dict(width=0))
|
446 |
+
fig.for_each_trace(lambda trace: trace.update(fillcolor=trace.line.color))
|
447 |
+
|
448 |
+
# fig.show()
|
449 |
+
with colb2:
|
450 |
+
fig
|
451 |
+
|
452 |
+
|
453 |
+
# %%
|
454 |
+
# plot investment costs
|
455 |
+
# c-inv_i to dataframe
|
456 |
+
if c_inv_i.name is None:
|
457 |
+
c_inv_i.name = 'c_inv_i'
|
458 |
+
c_inv_i_df = c_inv_i.to_dataframe().reset_index()
|
459 |
+
# multiply c_inv_i_df with K
|
460 |
+
df_invest_costs = df_new_capacities['K']* c_inv_i
|
461 |
+
df_invest_costs = df_invest_costs.to_frame()
|
462 |
+
df_invest_costs.columns = ['K']
|
463 |
+
df_invest_costs['i'] = df_new_capacities['i']
|
464 |
+
fig = px.bar(df_invest_costs, y='i', x='K', orientation='h', title='Investitionskosten [Mrd. €]', color='i', color_discrete_map=color_dict)
|
465 |
+
# fig.show()
|
466 |
+
with colb1:
|
467 |
+
fig
|
468 |
+
|
469 |
|
470 |
+
# %%
|
471 |
+
df_production_all = m.solution['y'].sel(i = i).to_dataframe().reset_index()
|
472 |
+
# Deckungsbeitrag = Erlöse - Kosten
|
473 |
+
df_contr_marg = m.constraints['max_cap'].dual.to_dataframe().reset_index()
|
474 |
+
# # contr_margin for i_with_capacity
|
475 |
+
# df_contr_marg = df_contr_marg[df_contr_marg['i'].isin(i_with_capacity)]. reset_index(drop=True)
|
476 |
+
# # multiply
|
477 |
+
df_merged = pd.merge(df_production_all, df_contr_marg, on=['t', 'i'])
|
478 |
+
# Perform the multiplication
|
479 |
+
df_merged['y_new'] = df_merged['y'] * df_merged['dual']
|
480 |
+
df_merged = df_merged[['t', 'i', 'y_new']]
|
481 |
+
df_contr_marg_sum = df_merged.groupby('i')['y_new'].sum().reset_index()
|
482 |
+
|
483 |
+
df_production_res = m.solution['y'].sel(i = iRes).to_dataframe().reset_index()
|
484 |
+
df_price_res = m.constraints['load'].dual.to_dataframe().reset_index()
|
485 |
+
# multiply with df_price_res
|
486 |
+
df_merged_res = pd.merge(df_production_res, df_price_res, on='t')
|
487 |
+
df_merged_res['multiplied_value'] = df_merged_res['y'] * df_merged_res['dual']
|
488 |
+
df_merged_res = df_merged_res[['t', 'i', 'multiplied_value']]
|
489 |
+
df_contr_marg_res = df_merged_res.groupby('i')['multiplied_value'].sum().reset_index()
|
490 |
+
df_contr_marg_res['multiplied_value'] = df_contr_marg_res['multiplied_value'] * -dt
|
491 |
+
|
492 |
+
df_contr_marg_sum = pd.merge(df_contr_marg_sum, df_contr_marg_res, on='i', how='left')
|
493 |
+
df_contr_marg_sum['y_new'] = df_contr_marg_sum['multiplied_value'].combine_first(df_contr_marg_sum['y_new'])
|
494 |
+
df_contr_marg_sum = df_contr_marg_sum.drop(columns=['multiplied_value'])
|
495 |
+
df_contr_marg_sum['y'] = df_contr_marg_sum['y_new']*(-1)
|
496 |
+
# rearrange rows according to i
|
497 |
+
df_contr_marg_sum = df_contr_marg_sum.set_index('i').loc[i].reset_index()
|
498 |
+
# # # barplot
|
499 |
+
fig = px.bar(df_contr_marg_sum, y='i', x='y', orientation='h', title='Deckungsbeitrag [Mrd. €]', color='i', color_discrete_map=color_dict)
|
500 |
+
# fig.show()
|
501 |
with colb2:
|
502 |
fig
|
503 |
|
504 |
# %%
|
505 |
+
# #Add pie chart of total production per technology type in GWh(divide by 1000)
|
506 |
+
# df_production_sum = (df_production.groupby('i')['y'].sum() * dt / 1000 ).round(0).sort_values(ascending=False).reset_index()
|
507 |
+
|
508 |
+
# fig = px.pie(df_production_sum, names="i", values='y', title='Gesamtproduktion [GWh] als Kuchendiagramm',
|
509 |
+
# color='i', color_discrete_map=color_dict)
|
510 |
|
511 |
# with colb2:
|
512 |
# fig
|
513 |
|
514 |
# %%
|
|
|
|
|
|
|
|
|
|
|
|
|
515 |
# # %%
|
516 |
# df_h2_prod = m.solution['y_h2'].sel(i = iPtG).to_dataframe().reset_index()
|
517 |
# fig = px.area(m.solution['y_h2'].sel(i = iPtG).to_dataframe().reset_index(), y='y_h2', x='t', title='Produktion Wasserstoff [MWh_th]', color='i', color_discrete_map=color_dict)
|
|
|
521 |
# with colb2:
|
522 |
# fig
|
523 |
|
524 |
+
|
525 |
+
# %%
|
526 |
+
# #add pie chart which shows new capacities
|
527 |
+
# #round number of new capacities
|
528 |
+
# df_new_capacities_rounded = m.solution['K'].round(0).to_dataframe()
|
529 |
+
# #drop all technologies with K<= 0
|
530 |
+
# df_new_capacities_rounded = df_new_capacities_rounded[df_new_capacities_rounded["K"] > 0].reset_index()
|
531 |
+
|
532 |
+
# total_k_sum = df_new_capacities_rounded["K"].sum()
|
533 |
+
|
534 |
+
# #df_new_capacities_rounded["percentage"] = df_new_capacities_rounded["K"].apply(lambda x: (x/total_k_sum)*100).abs().round(2)
|
535 |
+
|
536 |
+
# fig = px.pie(df_new_capacities_rounded, names='i', values='K', title='Neu installierte Kapazitäten [MW] als Kuchendiagramm',
|
537 |
+
# color='i', color_discrete_map=color_dict)
|
538 |
+
|
539 |
+
# with colb1:
|
540 |
+
# fig
|
541 |
+
|
542 |
+
|
543 |
# %%
|
544 |
((m.solution['y'] / eff_i) * co2_factor_i * dt).sum()
|
545 |
# %%
|
|
|
582 |
disaggregate_df(df_total_costs).to_excel(writer, sheet_name='Gesamtkosten', index=False)
|
583 |
disaggregate_df(df_CO2_price).to_excel(writer, sheet_name='CO2 Preis', index=False)
|
584 |
disaggregate_df(df_price).to_excel(writer, sheet_name='Preise', index=False)
|
585 |
+
# disaggregate_df(df_contr_marg).to_excel(writer, sheet_name='Deckungsbeiträge', index=False)
|
586 |
disaggregate_df(df_new_capacities).to_excel(writer, sheet_name='Kapazitäten', index=False)
|
587 |
disaggregate_df(df_production).to_excel(writer, sheet_name='Produktion', index=False)
|
588 |
+
disaggregate_df(df_charging).to_excel(writer, sheet_name='Ladevorgänge', index=False)
|
589 |
disaggregate_df(D_t.to_dataframe().reset_index()).to_excel(writer, sheet_name='Nachfrage', index=False)
|
590 |
disaggregate_df(df_curtailment).to_excel(writer, sheet_name='Abregelung', index=False)
|
591 |
# disaggregate_df(df_h2_prod).to_excel(writer, sheet_name='H2 produktion', index=False)
|
model_data.pkl
CHANGED
@@ -1,3 +1,3 @@
|
|
1 |
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:
|
3 |
-
size
|
|
|
1 |
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:38a1f162728d5c9435fe52ad066ab037ba90d3c41122d003b7dc754973ea00c1
|
3 |
+
size 1066427
|
sourced.py
CHANGED
@@ -39,8 +39,8 @@ def load_data_from_excel(url_excel, write_to_pickle_flag = True):
|
|
39 |
df_excel = pd.read_excel(url_excel, sheet_name='Technologies')
|
40 |
iRes = pd.Index(df_excel.iloc[0:4, 4], name='iRes')
|
41 |
|
42 |
-
|
43 |
-
|
44 |
|
45 |
# df_excel = pd.read_excel(url_excel, sheet_name='Technologies')
|
46 |
# iPtG = pd.Index(df_excel.iloc[0:1, 8], name='iPtG')
|
@@ -145,13 +145,13 @@ def load_data_from_excel(url_excel, write_to_pickle_flag = True):
|
|
145 |
df_excel = df_excel.set_index('i')
|
146 |
K_0_i = df_excel.iloc[:,0].to_xarray()
|
147 |
|
148 |
-
#
|
149 |
-
|
150 |
-
|
151 |
-
#
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
|
156 |
# # Inflow for hydro reservoir
|
157 |
# df_excel = pd.read_excel(url_excel, sheet_name = 'HydroInflow')
|
@@ -166,7 +166,7 @@ def load_data_from_excel(url_excel, write_to_pickle_flag = True):
|
|
166 |
# Append parameters to the dictionary
|
167 |
sets_dict['t'] = t
|
168 |
sets_dict['i'] = i
|
169 |
-
|
170 |
sets_dict['iConv'] = iConv
|
171 |
# sets_dict['iPtG'] = iPtG
|
172 |
sets_dict['iRes'] = iRes
|
@@ -185,7 +185,7 @@ def load_data_from_excel(url_excel, write_to_pickle_flag = True):
|
|
185 |
params_dict['c_var_i'] = c_var_i
|
186 |
params_dict['s_t_r_iRes'] = s_t_r_iRes
|
187 |
params_dict['K_0_i'] = K_0_i
|
188 |
-
|
189 |
# params_dict['h_t'] = h_t
|
190 |
|
191 |
if write_to_pickle_flag:
|
|
|
39 |
df_excel = pd.read_excel(url_excel, sheet_name='Technologies')
|
40 |
iRes = pd.Index(df_excel.iloc[0:4, 4], name='iRes')
|
41 |
|
42 |
+
df_excel = pd.read_excel(url_excel, sheet_name='Technologies')
|
43 |
+
iSto = pd.Index(df_excel.iloc[0:1, 6], name='iSto')
|
44 |
|
45 |
# df_excel = pd.read_excel(url_excel, sheet_name='Technologies')
|
46 |
# iPtG = pd.Index(df_excel.iloc[0:1, 8], name='iPtG')
|
|
|
145 |
df_excel = df_excel.set_index('i')
|
146 |
K_0_i = df_excel.iloc[:,0].to_xarray()
|
147 |
|
148 |
+
# Energy-to-power ratio storages
|
149 |
+
df_excel = pd.read_excel(url_excel, sheet_name = 'E2P')
|
150 |
+
df_excel = df_excel.rename(columns = {'Speicher':'i', 'Unnamed: 1':'E2P-Ratio'})
|
151 |
+
#df_excel = i.to_frame().reset_index(drop=True).merge(df_excel, how = 'left')
|
152 |
+
df_excel = df_excel.fillna(0)
|
153 |
+
df_excel = df_excel.set_index('i')
|
154 |
+
e2p_iSto = df_excel.iloc[:,0].to_xarray()
|
155 |
|
156 |
# # Inflow for hydro reservoir
|
157 |
# df_excel = pd.read_excel(url_excel, sheet_name = 'HydroInflow')
|
|
|
166 |
# Append parameters to the dictionary
|
167 |
sets_dict['t'] = t
|
168 |
sets_dict['i'] = i
|
169 |
+
sets_dict['iSto'] = iSto
|
170 |
sets_dict['iConv'] = iConv
|
171 |
# sets_dict['iPtG'] = iPtG
|
172 |
sets_dict['iRes'] = iRes
|
|
|
185 |
params_dict['c_var_i'] = c_var_i
|
186 |
params_dict['s_t_r_iRes'] = s_t_r_iRes
|
187 |
params_dict['K_0_i'] = K_0_i
|
188 |
+
params_dict['e2p_iSto'] = e2p_iSto
|
189 |
# params_dict['h_t'] = h_t
|
190 |
|
191 |
if write_to_pickle_flag:
|