|
1
|
|
|
import pandas as pd |
|
2
|
|
|
from helpers import LCOH |
|
3
|
|
|
from helpers import epc |
|
4
|
|
|
|
|
5
|
|
|
import oemof.solph as solph |
|
6
|
|
|
|
|
7
|
|
|
|
|
8
|
|
|
data = pd.read_csv("input_data.csv", sep=";", index_col=0, parse_dates=True) |
|
9
|
|
|
|
|
10
|
|
|
district_heating_system = solph.EnergySystem( |
|
11
|
|
|
timeindex=data.index, infer_last_interval=False |
|
12
|
|
|
) |
|
13
|
|
|
|
|
14
|
|
|
heat_bus = solph.Bus(label="heat network") |
|
15
|
|
|
gas_bus = solph.Bus(label="gas network") |
|
16
|
|
|
waste_heat_bus = solph.Bus(label="waste heat network") |
|
17
|
|
|
electricity_bus = solph.Bus(label="electricity network") |
|
18
|
|
|
|
|
19
|
|
|
district_heating_system.add(heat_bus, gas_bus, waste_heat_bus, electricity_bus) |
|
20
|
|
|
|
|
21
|
|
|
gas_source = solph.components.Source( |
|
22
|
|
|
label="gas source", |
|
23
|
|
|
outputs={gas_bus: solph.flows.Flow(variable_costs=data["gas price"])}, |
|
24
|
|
|
) |
|
25
|
|
|
|
|
26
|
|
|
electricity_source = solph.components.Source( |
|
27
|
|
|
label="electricity source", |
|
28
|
|
|
outputs={ |
|
29
|
|
|
electricity_bus: solph.flows.Flow(variable_costs=data["el_spot_price"]) |
|
30
|
|
|
}, |
|
31
|
|
|
) |
|
32
|
|
|
|
|
33
|
|
|
heat_sink = solph.components.Sink( |
|
34
|
|
|
label="heat sink", |
|
35
|
|
|
inputs={ |
|
36
|
|
|
heat_bus: solph.flows.Flow( |
|
37
|
|
|
nominal_value=data["heat demand"].max(), |
|
38
|
|
|
fix=data["heat demand"] / data["heat demand"].max(), |
|
39
|
|
|
) |
|
40
|
|
|
}, |
|
41
|
|
|
) |
|
42
|
|
|
|
|
43
|
|
|
district_heating_system.add(gas_source, electricity_source, heat_sink) |
|
44
|
|
|
|
|
45
|
|
|
# %%[sec_1_start] |
|
46
|
|
|
waste_heat_source = solph.components.Source( |
|
47
|
|
|
label="waste heat source", |
|
48
|
|
|
outputs={waste_heat_bus: solph.flows.Flow(nominal_value=1, fix=2.5)}, |
|
49
|
|
|
) |
|
50
|
|
|
|
|
51
|
|
|
district_heating_system.add(waste_heat_source) |
|
52
|
|
|
# %%[sec_1_end] |
|
53
|
|
|
|
|
54
|
|
|
spec_inv_gas_boiler = 60000 |
|
55
|
|
|
var_cost_gas_boiler = 1.10 |
|
56
|
|
|
|
|
57
|
|
|
gas_boiler = solph.components.Converter( |
|
58
|
|
|
label="gas boiler", |
|
59
|
|
|
inputs={gas_bus: solph.flows.Flow()}, |
|
60
|
|
|
outputs={ |
|
61
|
|
|
heat_bus: solph.flows.Flow( |
|
62
|
|
|
nominal_value=solph.Investment( |
|
63
|
|
|
ep_costs=epc(spec_inv_gas_boiler), maximum=50 |
|
64
|
|
|
), |
|
65
|
|
|
variable_costs=var_cost_gas_boiler, |
|
66
|
|
|
) |
|
67
|
|
|
}, |
|
68
|
|
|
conversion_factors={gas_bus: 0.95}, |
|
69
|
|
|
) |
|
70
|
|
|
|
|
71
|
|
|
district_heating_system.add(gas_boiler) |
|
72
|
|
|
|
|
73
|
|
|
spec_inv_storage = 1060 |
|
74
|
|
|
var_cost_storage = 0.1 |
|
75
|
|
|
|
|
76
|
|
|
heat_storage = solph.components.GenericStorage( |
|
77
|
|
|
label="heat storage", |
|
78
|
|
|
nominal_capacity=solph.Investment(ep_costs=epc(spec_inv_storage)), |
|
79
|
|
|
inputs={heat_bus: solph.flows.Flow(variable_costs=var_cost_storage)}, |
|
80
|
|
|
outputs={heat_bus: solph.flows.Flow(variable_costs=var_cost_storage)}, |
|
81
|
|
|
invest_relation_input_capacity=1 / 24, |
|
82
|
|
|
invest_relation_output_capacity=1 / 24, |
|
83
|
|
|
balanced=True, |
|
84
|
|
|
loss_rate=0.001, |
|
85
|
|
|
) |
|
86
|
|
|
|
|
87
|
|
|
district_heating_system.add(heat_storage) |
|
88
|
|
|
|
|
89
|
|
|
cop = 3.5 |
|
90
|
|
|
spec_inv_heat_pump = 500000 |
|
91
|
|
|
var_cost_heat_pump = 1.2 |
|
92
|
|
|
|
|
93
|
|
|
|
|
94
|
|
|
heat_pump = solph.components.Converter( |
|
95
|
|
|
label="heat pump", |
|
96
|
|
|
inputs={ |
|
97
|
|
|
electricity_bus: solph.flows.Flow(), |
|
98
|
|
|
waste_heat_bus: solph.flows.Flow(), |
|
99
|
|
|
}, |
|
100
|
|
|
outputs={ |
|
101
|
|
|
heat_bus: solph.flows.Flow( |
|
102
|
|
|
nominal_value=solph.Investment( |
|
103
|
|
|
ep_costs=epc(spec_inv_heat_pump), maximum=999 |
|
104
|
|
|
), |
|
105
|
|
|
variable_costs=1.2, |
|
106
|
|
|
min=0.5, |
|
107
|
|
|
nonconvex=solph.NonConvex(), |
|
108
|
|
|
) |
|
109
|
|
|
}, |
|
110
|
|
|
conversion_factors={ |
|
111
|
|
|
electricity_bus: 1 / cop, |
|
112
|
|
|
waste_heat_bus: (cop - 1) / cop, |
|
113
|
|
|
}, |
|
114
|
|
|
) |
|
115
|
|
|
district_heating_system.add(heat_pump) |
|
116
|
|
|
|
|
117
|
|
|
# solve model |
|
118
|
|
|
model = solph.Model(district_heating_system) |
|
119
|
|
|
model.solve( |
|
120
|
|
|
solver="cbc", |
|
121
|
|
|
solve_kwargs={"tee": True}, |
|
122
|
|
|
cmdline_options={"ratio": 0.01}, |
|
123
|
|
|
allow_nonoptimal=True, |
|
124
|
|
|
) |
|
125
|
|
|
|
|
126
|
|
|
# results |
|
127
|
|
|
results = solph.processing.results(model) |
|
128
|
|
|
|
|
129
|
|
|
data_gas_bus = solph.views.node(results, "gas network")["sequences"] |
|
130
|
|
|
data_heat_bus = solph.views.node(results, "heat network")["sequences"] |
|
131
|
|
|
data_el_bus = solph.views.node(results, "electricity network")["sequences"] |
|
132
|
|
|
data_caps = solph.views.node(results, "heat network")["scalars"] |
|
133
|
|
|
|
|
134
|
|
|
cap_gas_boiler = data_caps[("gas boiler", "heat network"), "invest"] |
|
135
|
|
|
cap_heat_pump = data_caps[("heat pump", "heat network"), "invest"] |
|
136
|
|
|
cap_storage = solph.views.node(results, "heat storage")["scalars"][ |
|
137
|
|
|
(("heat storage", "None"), "invest") |
|
138
|
|
|
] |
|
139
|
|
|
cap_storage_out = data_caps[("heat storage", "heat network"), "invest"] |
|
140
|
|
|
|
|
141
|
|
|
print(f"capacity gas boiler: {cap_gas_boiler:.1f} MW") |
|
142
|
|
|
print(f"capacity heat pump: {cap_heat_pump:.1f} MW") |
|
143
|
|
|
print(f"capacity heat storage: {cap_storage:.1f} MWh") |
|
144
|
|
|
print(f"capacity heat storage (out): {cap_storage_out:.1f} MW") |
|
145
|
|
|
|
|
146
|
|
|
invest_cost = ( |
|
147
|
|
|
spec_inv_gas_boiler * cap_gas_boiler |
|
148
|
|
|
+ spec_inv_storage * cap_storage |
|
149
|
|
|
+ spec_inv_heat_pump * cap_heat_pump |
|
150
|
|
|
) |
|
151
|
|
|
operation_cost = ( |
|
152
|
|
|
var_cost_gas_boiler |
|
153
|
|
|
* data_heat_bus[(("gas boiler", "heat network"), "flow")].sum() |
|
154
|
|
|
+ ( |
|
155
|
|
|
data["gas price"] |
|
156
|
|
|
* data_gas_bus[(("gas network", "gas boiler"), "flow")] |
|
157
|
|
|
).sum() |
|
158
|
|
|
+ var_cost_heat_pump |
|
159
|
|
|
* data_heat_bus[(("heat pump", "heat network"), "flow")].sum() |
|
160
|
|
|
+ ( |
|
161
|
|
|
data["el_spot_price"] |
|
162
|
|
|
* data_el_bus[(("electricity network", "heat pump"), "flow")] |
|
163
|
|
|
).sum() |
|
164
|
|
|
+ var_cost_storage |
|
165
|
|
|
* data_heat_bus[(("heat storage", "heat network"), "flow")].sum() |
|
166
|
|
|
+ var_cost_storage |
|
167
|
|
|
* data_heat_bus[(("heat network", "heat storage"), "flow")].sum() |
|
168
|
|
|
) |
|
169
|
|
|
heat_produced = data_heat_bus[(("heat network", "heat sink"), "flow")].sum() |
|
170
|
|
|
|
|
171
|
|
|
lcoh = LCOH(invest_cost, operation_cost, heat_produced) |
|
172
|
|
|
print(f"LCOH: {lcoh:.2f} €/MWh") |
|
173
|
|
|
|
|
174
|
|
|
import matplotlib.pyplot as plt |
|
175
|
|
|
|
|
176
|
|
|
# plt.style.use('dark_background') |
|
177
|
|
|
|
|
178
|
|
|
unit_colors = { |
|
179
|
|
|
"gas boiler": "#EC6707", |
|
180
|
|
|
"heat pump": "#B54036", |
|
181
|
|
|
"heat storage (discharge)": "#BFBFBF", |
|
182
|
|
|
"heat storage (charge)": "#696969", |
|
183
|
|
|
} |
|
184
|
|
|
|
|
185
|
|
|
fig, ax = plt.subplots(figsize=[10, 6]) |
|
186
|
|
|
|
|
187
|
|
|
bottom = 0 |
|
188
|
|
|
for unit in ["heat pump", "gas boiler", "heat storage"]: |
|
189
|
|
|
unit_label = f"{unit} (discharge)" if "storage" in unit else unit |
|
190
|
|
|
ax.bar( |
|
191
|
|
|
data_heat_bus.index, |
|
192
|
|
|
data_heat_bus[((unit, "heat network"), "flow")], |
|
193
|
|
|
label=unit_label, |
|
194
|
|
|
color=unit_colors[unit_label], |
|
195
|
|
|
bottom=bottom, |
|
196
|
|
|
) |
|
197
|
|
|
bottom += data_heat_bus[((unit, "heat network"), "flow")] |
|
198
|
|
|
|
|
199
|
|
|
unit_label = "heat storage (charge)" |
|
200
|
|
|
ax.bar( |
|
201
|
|
|
data_heat_bus.index, |
|
202
|
|
|
-1 * data_heat_bus[(("heat network", "heat storage"), "flow")], |
|
203
|
|
|
label=unit_label, |
|
204
|
|
|
color=unit_colors[unit_label], |
|
205
|
|
|
) |
|
206
|
|
|
|
|
207
|
|
|
ax.legend(loc="upper center", ncol=2) |
|
208
|
|
|
ax.grid(axis="y") |
|
209
|
|
|
ax.set_ylabel("Hourly heat production in MWh") |
|
210
|
|
|
|
|
211
|
|
|
# plt.tight_layout() |
|
212
|
|
|
# plt.savefig('intro_tut_dhs_4_hourly_heat_production.svg') |
|
213
|
|
|
|
|
214
|
|
|
|
|
215
|
|
|
fig, ax = plt.subplots(figsize=[10, 6]) |
|
216
|
|
|
|
|
217
|
|
|
data_heat_storage = solph.views.node(results, "heat storage")["sequences"] |
|
218
|
|
|
|
|
219
|
|
|
ax.plot( |
|
220
|
|
|
data_heat_storage[(("heat storage", "None"), "storage_content")], |
|
221
|
|
|
color="#00395B", |
|
222
|
|
|
) |
|
223
|
|
|
|
|
224
|
|
|
ax.grid(axis="y") |
|
225
|
|
|
ax.set_ylabel("Hourly heat storage content in MWh") |
|
226
|
|
|
|
|
227
|
|
|
# plt.tight_layout() |
|
228
|
|
|
# plt.savefig('intro_tut_dhs_4_hourly_storage_content.svg') |
|
229
|
|
|
|
|
230
|
|
|
plt.show() |
|
231
|
|
|
|