Passed
Pull Request — dev (#850)
by Uwe
01:39
created

variable_chp.shape_legend()   A

Complexity

Conditions 3

Size

Total Lines 32
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 27
dl 0
loc 32
rs 9.232
c 0
b 0
f 0
cc 3
nop 3
1
# -*- coding: utf-8 -*-
2
3
"""
4
General description
5
-------------------
6
This example is not a real use case of an energy system but an example to show
7
how a combined heat and power plant (chp) with an extraction turbine works in
8
contrast to a chp (eg. block device) with a fixed heat fraction. Both chp
9
plants distribute power and heat to a separate heat and power Bus with a heat
10
and power demand. The i/o balance plot shows that the fixed chp plant produces
11
heat and power excess and therefore needs more natural gas. The bar plot just
12
shows the difference in the usage of natural gas.
13
14
Installation requirements
15
-------------------------
16
17
This example requires oemof.solph (v0.5.x), install by:
18
19
    pip install oemof.solph[examples]
20
21
Optional to see the i/o balance plot:
22
23
    pip install git+https://github.com/oemof/oemof_visio.git
24
25
License
26
-------
27
`MIT license <https://github.com/oemof/oemof-solph/blob/dev/LICENSE>`_
28
29
"""
30
31
###############################################################################
32
# imports
33
###############################################################################
34
35
import logging
36
import os
37
import warnings
38
39
import matplotlib.pyplot as plt
40
import pandas as pd
41
from oemof.tools import logger
42
43
from oemof import solph
44
45
# import oemof plots
46
try:
47
    from oemof_visio import plot as oeplot
48
except ImportError:
49
    oeplot = None
50
51
52
def shape_legend(node, reverse=True, **kwargs):
53
    handels = kwargs["handles"]
54
    labels = kwargs["labels"]
55
    axes = kwargs["ax"]
56
    parameter = {}
57
58
    new_labels = []
59
    for label in labels:
60
        label = label.replace("(", "")
61
        label = label.replace("), flow)", "")
62
        label = label.replace(node, "")
63
        label = label.replace(",", "")
64
        label = label.replace(" ", "")
65
        new_labels.append(label)
66
    labels = new_labels
67
68
    parameter["bbox_to_anchor"] = kwargs.get("bbox_to_anchor", (1, 0.5))
69
    parameter["loc"] = kwargs.get("loc", "center left")
70
    parameter["ncol"] = kwargs.get("ncol", 1)
71
    plotshare = kwargs.get("plotshare", 0.9)
72
73
    if reverse:
74
        handels.reverse()
75
        labels.reverse()
76
77
    box = axes.get_position()
78
    axes.set_position([box.x0, box.y0, box.width * plotshare, box.height])
79
80
    parameter["handles"] = handels
81
    parameter["labels"] = labels
82
    axes.legend(**parameter)
83
    return axes
84
85
86
def write_lp_file():
87
    lp_filename = os.path.join(
88
        solph.helpers.extend_basic_path("lp_files"), "variable_chp.lp"
89
    )
90
    logging.info("Store lp-file in {0}.".format(lp_filename))
91
    om.write(lp_filename, io_options={"symbolic_solver_labels": True})
92
93
94
# Read data file
95
filename = os.path.join(os.getcwd(), "variable_chp.csv")
96
try:
97
    data = pd.read_csv(filename)
98
except FileNotFoundError:
99
    msg = "Data file not found: {0}. Only one value used!"
100
    warnings.warn(msg.format(filename), UserWarning)
101
    data = pd.DataFrame(
102
        {"pv": [0.3], "wind": [0.6], "demand_el": [500], "demand_th": [344]}
103
    )
104
105
logger.define_logging()
106
logging.info("Initialize the energy system")
107
108
# create time index for 192 hours in May.
109
date_time_index = solph.create_time_index(2012, number=len(data))
110
111
energysystem = solph.EnergySystem(
112
    timeindex=date_time_index, infer_last_interval=False
113
)
114
115
##########################################################################
116
# Create oemof.solph objects
117
##########################################################################
118
logging.info("Create oemof.solph objects")
119
120
# container for instantiated nodes
121
noded = dict()
122
123
# create natural gas bus
124
noded["bgas"] = solph.Bus(label="natural_gas")
125
126
# create commodity object for gas resource
127
noded["rgas"] = solph.components.Source(
128
    label="rgas", outputs={noded["bgas"]: solph.Flow(variable_costs=50)}
129
)
130
131
# create two electricity buses and two heat buses
132
noded["bel"] = solph.Bus(label="electricity")
133
noded["bel2"] = solph.Bus(label="electricity_2")
134
noded["bth"] = solph.Bus(label="heat")
135
noded["bth2"] = solph.Bus(label="heat_2")
136
137
# create excess components for the elec/heat bus to allow overproduction
138
noded["excess_bth_2"] = solph.components.Sink(
139
    label="excess_bth_2", inputs={noded["bth2"]: solph.Flow()}
140
)
141
noded["excess_therm"] = solph.components.Sink(
142
    label="excess_therm", inputs={noded["bth"]: solph.Flow()}
143
)
144
noded["excess_bel_2"] = solph.components.Sink(
145
    label="excess_bel_2", inputs={noded["bel2"]: solph.Flow()}
146
)
147
noded["excess_elec"] = solph.components.Sink(
148
    label="excess_elec", inputs={noded["bel"]: solph.Flow()}
149
)
150
151
# create simple sink object for electrical demand for each electrical bus
152
noded["demand_elec"] = solph.components.Sink(
153
    label="demand_elec",
154
    inputs={noded["bel"]: solph.Flow(fix=data["demand_el"], nominal_value=1)},
155
)
156
noded["demand_el_2"] = solph.components.Sink(
157
    label="demand_el_2",
158
    inputs={noded["bel2"]: solph.Flow(fix=data["demand_el"], nominal_value=1)},
159
)
160
161
# create simple sink object for heat demand for each thermal bus
162
noded["demand_therm"] = solph.components.Sink(
163
    label="demand_therm",
164
    inputs={
165
        noded["bth"]: solph.Flow(fix=data["demand_th"], nominal_value=741000)
166
    },
167
)
168
noded["demand_therm_2"] = solph.components.Sink(
169
    label="demand_th_2",
170
    inputs={
171
        noded["bth2"]: solph.Flow(fix=data["demand_th"], nominal_value=741000)
172
    },
173
)
174
175
# This is just a dummy transformer with a nominal input of zero
176
noded["fixed_chp_gas"] = solph.components.Transformer(
177
    label="fixed_chp_gas",
178
    inputs={noded["bgas"]: solph.Flow(nominal_value=0)},
179
    outputs={noded["bel"]: solph.Flow(), noded["bth"]: solph.Flow()},
180
    conversion_factors={noded["bel"]: 0.3, noded["bth"]: 0.5},
181
)
182
183
# create a fixed transformer to distribute to the heat_2 and elec_2 buses
184
noded["fixed_chp_gas_2"] = solph.components.Transformer(
185
    label="fixed_chp_gas_2",
186
    inputs={noded["bgas"]: solph.Flow(nominal_value=10e10)},
187
    outputs={noded["bel2"]: solph.Flow(), noded["bth2"]: solph.Flow()},
188
    conversion_factors={noded["bel2"]: 0.3, noded["bth2"]: 0.5},
189
)
190
191
# create a fixed transformer to distribute to the heat and elec buses
192
noded["variable_chp_gas"] = solph.components.ExtractionTurbineCHP(
193
    label="variable_chp_gas",
194
    inputs={noded["bgas"]: solph.Flow(nominal_value=10e10)},
195
    outputs={noded["bel"]: solph.Flow(), noded["bth"]: solph.Flow()},
196
    conversion_factors={noded["bel"]: 0.3, noded["bth"]: 0.5},
197
    conversion_factor_full_condensation={noded["bel"]: 0.5},
198
)
199
200
##########################################################################
201
# Optimise the energy system
202
##########################################################################
203
204
logging.info("Optimise the energy system")
205
206
energysystem.add(*noded.values())
207
208
om = solph.Model(energysystem)
209
210
# If uncomment the following line you can store the lp file but you should use
211
# less timesteps (3) to make it better readable and smaller.
212
# write_lp_file()
213
214
logging.info("Solve the optimization problem")
215
om.solve(solver="cbc", solve_kwargs={"tee": False})
216
217
results = solph.processing.results(om)
218
219
##########################################################################
220
# Plot the results
221
##########################################################################
222
223
myresults = solph.views.node(results, "natural_gas")
224
myresults = myresults["sequences"].sum(axis=0)
225
myresults = myresults.drop(myresults.index[0]).reset_index(drop=True)
226
myresults.rename({0: "fixed", 1: "variable", 2: "total"}, inplace=True)
227
myresults.plot(kind="bar", rot=0, title="Usage of natural gas")
228
plt.show()
229
230
# Create a plot with 6 tiles that shows the difference between the
231
# Transformer and the ExtractionTurbineCHP used for chp plants.
232
smooth_plot = True
233
234
if oeplot:
235
    logging.info("Plot the results")
236
237
    cdict = {
238
        (("variable_chp_gas", "electricity"), "flow"): "#42c77a",
239
        (("fixed_chp_gas_2", "electricity_2"), "flow"): "#20b4b6",
240
        (("fixed_chp_gas", "electricity"), "flow"): "#20b4b6",
241
        (("fixed_chp_gas", "heat"), "flow"): "#20b4b6",
242
        (("variable_chp_gas", "heat"), "flow"): "#42c77a",
243
        (("heat", "demand_therm"), "flow"): "#5b5bae",
244
        (("heat_2", "demand_th_2"), "flow"): "#5b5bae",
245
        (("electricity", "demand_elec"), "flow"): "#5b5bae",
246
        (("electricity_2", "demand_el_2"), "flow"): "#5b5bae",
247
        (("heat", "excess_therm"), "flow"): "#f22222",
248
        (("heat_2", "excess_bth_2"), "flow"): "#f22222",
249
        (("electricity", "excess_elec"), "flow"): "#f22222",
250
        (("electricity_2", "excess_bel_2"), "flow"): "#f22222",
251
        (("fixed_chp_gas_2", "heat_2"), "flow"): "#20b4b6",
252
    }
253
254
    fig = plt.figure(figsize=(18, 9))
255
    plt.rc("legend", **{"fontsize": 13})
256
    plt.rcParams.update({"font.size": 13})
257
    fig.subplots_adjust(
258
        left=0.07, bottom=0.12, right=0.86, top=0.93, wspace=0.03, hspace=0.2
259
    )
260
261
    # subplot of electricity bus (fixed chp) [1]
262
    electricity_2 = solph.views.node(results, "electricity_2")
263
    x_length = len(electricity_2["sequences"].index)
264
    myplot = oeplot.io_plot(
265
        bus_label="electricity_2",
266
        df=electricity_2["sequences"],
267
        cdict=cdict,
268
        smooth=smooth_plot,
269
        line_kwa={"linewidth": 4},
270
        ax=fig.add_subplot(3, 2, 1),
271
        inorder=[(("fixed_chp_gas_2", "electricity_2"), "flow")],
272
        outorder=[
273
            (("electricity_2", "demand_el_2"), "flow"),
274
            (("electricity_2", "excess_bel_2"), "flow"),
275
        ],
276
    )
277
    myplot["ax"].set_ylabel("Power in MW")
278
    myplot["ax"].set_xlabel("")
279
    myplot["ax"].get_xaxis().set_visible(False)
280
    myplot["ax"].set_xlim(0, x_length)
281
    myplot["ax"].set_title("Electricity output (fixed chp)")
282
    myplot["ax"].legend_.remove()
283
284
    # subplot of electricity bus (variable chp) [2]
285
    electricity = solph.views.node(results, "electricity")
286
    myplot = oeplot.io_plot(
287
        bus_label="electricity",
288
        df=electricity["sequences"],
289
        cdict=cdict,
290
        smooth=smooth_plot,
291
        line_kwa={"linewidth": 4},
292
        ax=fig.add_subplot(3, 2, 2),
293
        inorder=[
294
            (("fixed_chp_gas", "electricity"), "flow"),
295
            (("variable_chp_gas", "electricity"), "flow"),
296
        ],
297
        outorder=[
298
            (("electricity", "demand_elec"), "flow"),
299
            (("electricity", "excess_elec"), "flow"),
300
        ],
301
    )
302
    myplot["ax"].get_yaxis().set_visible(False)
303
    myplot["ax"].set_xlabel("")
304
    myplot["ax"].get_xaxis().set_visible(False)
305
    myplot["ax"].set_title("Electricity output (variable chp)")
306
    myplot["ax"].set_xlim(0, x_length)
307
    shape_legend("electricity", plotshare=1, **myplot)
308
309
    # subplot of heat bus (fixed chp) [3]
310
    heat_2 = solph.views.node(results, "heat_2")
311
    myplot = oeplot.io_plot(
312
        bus_label="heat_2",
313
        df=heat_2["sequences"],
314
        cdict=cdict,
315
        smooth=smooth_plot,
316
        line_kwa={"linewidth": 4},
317
        ax=fig.add_subplot(3, 2, 3),
318
        inorder=[(("fixed_chp_gas_2", "heat_2"), "flow")],
319
        outorder=[
320
            (("heat_2", "demand_th_2"), "flow"),
321
            (("heat_2", "excess_bth_2"), "flow"),
322
        ],
323
    )
324
    myplot["ax"].set_ylabel("Power in MW")
325
    myplot["ax"].set_ylim([0, 600000])
326
    myplot["ax"].get_xaxis().set_visible(False)
327
    myplot["ax"].set_title("Heat output (fixed chp)")
328
    myplot["ax"].set_xlim(0, x_length)
329
    myplot["ax"].legend_.remove()
330
331
    # subplot of heat bus (variable chp) [4]
332
    heat = solph.views.node(results, "heat")
333
    myplot = oeplot.io_plot(
334
        bus_label="heat",
335
        df=heat["sequences"],
336
        cdict=cdict,
337
        smooth=smooth_plot,
338
        line_kwa={"linewidth": 4},
339
        ax=fig.add_subplot(3, 2, 4),
340
        inorder=[
341
            (("fixed_chp_gas", "heat"), "flow"),
342
            (("variable_chp_gas", "heat"), "flow"),
343
        ],
344
        outorder=[
345
            (("heat", "demand_therm"), "flow"),
346
            (("heat", "excess_therm"), "flow"),
347
        ],
348
    )
349
    myplot["ax"].set_ylim([0, 600000])
350
    myplot["ax"].get_yaxis().set_visible(False)
351
    myplot["ax"].get_xaxis().set_visible(False)
352
    myplot["ax"].set_title("Heat output (variable chp)")
353
    myplot["ax"].set_xlim(0, x_length)
354
    shape_legend("heat", plotshare=1, **myplot)
355
356
    if smooth_plot:
357
        style = None
358
    else:
359
        style = "steps-mid"
360
361
    # subplot of efficiency (fixed chp) [5]
362
    fix_chp_gas2 = solph.views.node(results, "fixed_chp_gas_2")
363
    ngas = fix_chp_gas2["sequences"][
364
        (("natural_gas", "fixed_chp_gas_2"), "flow")
365
    ]
366
    elec = fix_chp_gas2["sequences"][
367
        (("fixed_chp_gas_2", "electricity_2"), "flow")
368
    ]
369
    heat = fix_chp_gas2["sequences"][(("fixed_chp_gas_2", "heat_2"), "flow")]
370
    e_ef = elec.div(ngas)
371
    h_ef = heat.div(ngas)
372
    df = pd.DataFrame(pd.concat([h_ef, e_ef], axis=1))
373
    my_ax = df.reset_index(drop=True).plot(
374
        drawstyle=style, ax=fig.add_subplot(3, 2, 5), linewidth=2
375
    )
376
    my_ax.set_ylabel("efficiency")
377
    my_ax.set_ylim([0, 0.55])
378
    my_ax.set_xlabel("May 2012")
379
    my_ax = oeplot.set_datetime_ticks(
380
        my_ax,
381
        df.index,
382
        tick_distance=24,
383
        date_format="%d",
384
        offset=12,
385
        tight=True,
386
    )
387
    my_ax.set_title("Efficiency (fixed chp)")
388
    my_ax.legend_.remove()
389
390
    # subplot of efficiency (variable chp) [6]
391
    var_chp_gas = solph.views.node(results, "variable_chp_gas")
392
    ngas = var_chp_gas["sequences"][
393
        (("natural_gas", "variable_chp_gas"), "flow")
394
    ]
395
    elec = var_chp_gas["sequences"][
396
        (("variable_chp_gas", "electricity"), "flow")
397
    ]
398
    heat = var_chp_gas["sequences"][(("variable_chp_gas", "heat"), "flow")]
399
    e_ef = elec.div(ngas)
400
    h_ef = heat.div(ngas)
401
    e_ef.name = "electricity           "
402
    h_ef.name = "heat"
403
    df = pd.DataFrame(pd.concat([h_ef, e_ef], axis=1))
404
    my_ax = df.reset_index(drop=True).plot(
405
        drawstyle=style, ax=fig.add_subplot(3, 2, 6), linewidth=2
406
    )
407
    my_ax.set_ylim([0, 0.55])
408
    my_ax = oeplot.set_datetime_ticks(
409
        my_ax,
410
        df.index,
411
        tick_distance=24,
412
        date_format="%d",
413
        offset=12,
414
        tight=True,
415
    )
416
    my_ax.get_yaxis().set_visible(False)
417
    my_ax.set_xlabel("May 2012")
418
419
    my_ax.set_title("Efficiency (variable chp)")
420
    my_box = my_ax.get_position()
421
    my_ax.set_position([my_box.x0, my_box.y0, my_box.width * 1, my_box.height])
422
    my_ax.legend(loc="center left", bbox_to_anchor=(1, 0.5), ncol=1)
423
424
    plt.show()
425
426
else:
427
    logging.warning("You have to install 'oemof-visio' to see the i/o-plot")
428
    logging.warning(
429
        "Use: pip install git+https://github.com/oemof/oemof_visio.git"
430
    )
431