Passed
Pull Request — dev (#1193)
by Uwe
01:38
created

basic_example   A

Complexity

Total Complexity 6

Size/Duplication

Total Lines 321
Duplicated Lines 3.43 %

Importance

Changes 0
Metric Value
wmc 6
eloc 132
dl 11
loc 321
rs 10
c 0
b 0
f 0

3 Functions

Rating   Name   Duplication   Size   Complexity  
A get_data_from_file_path() 0 4 1
A plot_figures_for() 11 11 1
B main() 0 212 4

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
# -*- coding: utf-8 -*-
2
3
"""
4
General description
5
-------------------
6
7
A basic example to show how to model a simple energy system with oemof.solph.
8
9
The following energy system is modeled:
10
11
.. code-block:: text
12
13
                     input/output  bgas     bel
14
                         |          |        |
15
                         |          |        |
16
     wind(FixedSource)   |------------------>|
17
                         |          |        |
18
     pv(FixedSource)     |------------------>|
19
                         |          |        |
20
     rgas(Commodity)     |--------->|        |
21
                         |          |        |
22
     demand(Sink)        |<------------------|
23
                         |          |        |
24
                         |          |        |
25
     pp_gas(Converter)   |<---------|        |
26
                         |------------------>|
27
                         |          |        |
28
     storage(Storage)    |<------------------|
29
                         |------------------>|
30
31
Code
32
----
33
Download source code: :download:`basic_example.py </../examples/basic_example/basic_example.py>`
34
35
.. dropdown:: Click to display code
36
37
    .. literalinclude:: /../examples/basic_example/basic_example.py
38
        :language: python
39
        :lines: 61-
40
41
Data
42
----
43
Download data: :download:`basic_example.csv </../examples/basic_example/basic_example.csv>`
44
45
Installation requirements
46
-------------------------
47
This example requires oemof.solph (v0.5.x), install by:
48
49
.. code:: bash
50
51
    pip install oemof.solph[examples]
52
53
License
54
-------
55
`MIT license <https://github.com/oemof/oemof-solph/blob/dev/LICENSE>`_
56
"""
57
###########################################################################
58
# imports
59
###########################################################################
60
61
import logging
62
import os
63
import pprint as pp
64
from datetime import datetime
65
66
import matplotlib.pyplot as plt
67
import pandas as pd
68
from oemof.tools import logger
69
70
from oemof.solph import EnergySystem
71
from oemof.solph import Model
72
from oemof.solph import buses
73
from oemof.solph import components
74
from oemof.solph import create_time_index
75
from oemof.solph import flows
76
from oemof.solph import helpers
77
from oemof.solph import processing
78
from oemof.solph import views
79
80
from facade import DSO
81
82
83
STORAGE_LABEL = "battery_storage"
84
85
86
def get_data_from_file_path(file_path: str) -> pd.DataFrame:
87
    my_dir = os.path.dirname(os.path.abspath(__file__))
88
    data = pd.read_csv(my_dir + "/" + file_path)
89
    return data
90
91
92 View Code Duplication
def plot_figures_for(element: dict) -> None:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
93
    figure, axes = plt.subplots(figsize=(10, 5))
94
    element["sequences"].plot(ax=axes, kind="line", drawstyle="steps-post")
95
    plt.legend(
96
        loc="upper center",
97
        prop={"size": 8},
98
        bbox_to_anchor=(0.5, 1.25),
99
        ncol=2,
100
    )
101
    figure.subplots_adjust(top=0.8)
102
    plt.show()
103
104
105
def main(dump_and_restore=False):
106
    # For models that need a long time to optimise, saving and loading the
107
    # EnergySystem might be advised. By default, we do not do this here. Feel
108
    # free to experiment with this once you understood the rest of the code.
109
    dump_results = restore_results = dump_and_restore
110
111
    # *************************************************************************
112
    # ********** PART 1 - Define and optimise the energy system ***************
113
    # *************************************************************************
114
115
    # Read data file
116
    file_name = "basic_example.csv"
117
    data = get_data_from_file_path(file_name)
118
119
    solver = "cbc"  # 'glpk', 'gurobi',....
120
    debug = False  # Set number_of_timesteps to 3 to get a readable lp-file.
121
    number_of_time_steps = len(data)
122
    solver_verbose = False  # show/hide solver output
123
124
    # initiate the logger (see the API docs for more information)
125
    logger.define_logging(
126
        logfile="oemof_example.log",
127
        screen_level=logging.INFO,
128
        file_level=logging.INFO,
129
    )
130
131
    logging.info("Initialize the energy system")
132
    date_time_index = create_time_index(2012, number=number_of_time_steps)
133
134
    # create the energysystem and assign the time index
135
    energysystem = EnergySystem(
136
        timeindex=date_time_index, infer_last_interval=False
137
    )
138
139
    ##########################################################################
140
    # Create oemof objects
141
    ##########################################################################
142
143
    logging.info("Create oemof objects")
144
145
    # The bus objects were assigned to variables which makes it easier to
146
    # connect components to these buses (see below).
147
148
    # create natural gas bus
149
    bus_gas = buses.Bus(label="natural_gas")
150
151
    # create electricity bus
152
    bus_electricity = buses.Bus(label="electricity")
153
154
    # adding the buses to the energy system
155
    energysystem.add(bus_gas, bus_electricity)
156
157
    # create excess component for the electricity bus to allow overproduction
158
    energysystem.add(
159
        components.Sink(
160
            label="excess_bus_electricity",
161
            inputs={bus_electricity: flows.Flow()},
162
        )
163
    )
164
165
    energysystem.add(
166
        DSO(
167
            label="DSO",
168
            el_bus=bus_electricity,
169
            energy_price=0.1,
170
            feedin_tariff=0.04,
171
        )
172
    )
173
174
    # create fixed source object representing wind power plants
175
    energysystem.add(
176
        components.Source(
177
            label="wind",
178
            outputs={
179
                bus_electricity: flows.Flow(
180
                    fix=data["wind"], nominal_value=1000000
181
                )
182
            },
183
        )
184
    )
185
186
    # create fixed source object representing pv power plants
187
    energysystem.add(
188
        components.Source(
189
            label="pv",
190
            outputs={
191
                bus_electricity: flows.Flow(
192
                    fix=data["pv"], nominal_value=582000
193
                )
194
            },
195
        )
196
    )
197
198
    # create simple sink object representing the electrical demand
199
    # nominal_value is set to 1 because demand_el is not a normalised series
200
    energysystem.add(
201
        components.Sink(
202
            label="demand",
203
            inputs={
204
                bus_electricity: flows.Flow(
205
                    fix=data["demand_el"], nominal_value=1
206
                )
207
            },
208
        )
209
    )
210
211
    # create storage object representing a battery
212
    nominal_capacity = 10077997
213
    nominal_value = nominal_capacity / 6
214
215
    battery_storage = components.GenericStorage(
216
        nominal_storage_capacity=nominal_capacity,
217
        label=STORAGE_LABEL,
218
        inputs={bus_electricity: flows.Flow(nominal_value=nominal_value)},
219
        outputs={
220
            bus_electricity: flows.Flow(
221
                nominal_value=nominal_value, variable_costs=0.001
222
            )
223
        },
224
        loss_rate=0.00,
225
        initial_storage_level=None,
226
        inflow_conversion_factor=1,
227
        outflow_conversion_factor=0.8,
228
    )
229
230
    energysystem.add(battery_storage)
231
232
    ##########################################################################
233
    # Optimise the energy system and plot the results
234
    ##########################################################################
235
236
    logging.info("Optimise the energy system")
237
238
    # initialise the operational model
239
    energysystem_model = Model(energysystem)
240
241
    # This is for debugging only. It is not(!) necessary to solve the problem
242
    # and should be set to False to save time and disc space in normal use. For
243
    # debugging the timesteps should be set to 3, to increase the readability
244
    # of the lp-file.
245
    if debug:
246
        file_path = os.path.join(
247
            helpers.extend_basic_path("lp_files"), "basic_example.lp"
248
        )
249
        logging.info(f"Store lp-file in {file_path}.")
250
        io_option = {"symbolic_solver_labels": True}
251
        energysystem_model.write(file_path, io_options=io_option)
252
253
    # if tee_switch is true solver messages will be displayed
254
    logging.info("Solve the optimization problem")
255
    energysystem_model.solve(
256
        solver=solver, solve_kwargs={"tee": solver_verbose}
257
    )
258
259
    logging.info("Store the energy system with the results.")
260
261
    # The processing module of the outputlib can be used to extract the results
262
    # from the model transfer them into a homogeneous structured dictionary.
263
264
    # add results to the energy system to make it possible to store them.
265
    energysystem.results["main"] = processing.results(energysystem_model)
266
    energysystem.results["meta"] = processing.meta_results(energysystem_model)
267
268
    # The default path is the '.oemof' folder in your $HOME directory.
269
    # The default filename is 'es_dump.oemof'.
270
    # You can omit the attributes (as None is the default value) for testing
271
    # cases. You should use unique names/folders for valuable results to avoid
272
    # overwriting.
273
    if dump_results:
274
        energysystem.dump(dpath=None, filename=None)
275
276
    # *************************************************************************
277
    # ********** PART 2 - Processing the results ******************************
278
    # *************************************************************************
279
280
    # Saved data can be restored in a second script. So you can work on the
281
    # data analysis without re-running the optimisation every time. If you do
282
    # so, make sure that you really load the results you want. For example,
283
    # if dumping fails, you might exidentially load outdated results.
284
    if restore_results:
285
        logging.info("**** The script can be divided into two parts here.")
286
        logging.info("Restore the energy system and the results.")
287
288
        energysystem = EnergySystem()
289
        energysystem.restore(dpath=None, filename=None)
290
291
    # define an alias for shorter calls below (optional)
292
    results = energysystem.results["main"]
293
    storage = energysystem.groups[STORAGE_LABEL]
294
295
    # print a time slice of the state of charge
296
    start_time = datetime(2012, 2, 25, 8, 0, 0)
297
    end_time = datetime(2012, 2, 25, 17, 0, 0)
298
299
    print("\n********* State of Charge (slice) *********")
300
    print(f"{results[(storage, None)]['sequences'][start_time : end_time]}\n")
301
302
    # get all variables of a specific component/bus
303
    custom_storage = views.node(results, STORAGE_LABEL)
304
    electricity_bus = views.node(results, "electricity")
305
306
    # plot the time series (sequences) of a specific component/bus
307
    plot_figures_for(custom_storage)
308
    plot_figures_for(electricity_bus)
309
310
    # print the solver results
311
    print("********* Meta results *********")
312
    pp.pprint(f"{energysystem.results['meta']}\n")
313
314
    # print the sums of the flows around the electricity bus
315
    print("********* Main results *********")
316
    print(electricity_bus["sequences"].sum(axis=0))
317
318
319
if __name__ == "__main__":
320
    main()
321