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

facade_example.main()   B

Complexity

Conditions 4

Size

Total Lines 220
Code Lines 97

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 97
dl 0
loc 220
rs 7.0981
c 0
b 0
f 0
cc 4
nop 1

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 = "facade_example_data.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="My_DSO",
168
            el_bus=bus_electricity,
169
            energy_price=0.1,
170
            feedin_tariff=0.04,
171
        )
172
    )
173
    # energysystem.add(
174
    #     components.Source(
175
    #         label="DSO_simple",
176
    #         outputs={
177
    #             bus_electricity: flows.Flow(variable_costs=0.1)
178
    #         },
179
    #     )
180
    # )
181
182
    # create fixed source object representing wind power plants
183
    energysystem.add(
184
        components.Source(
185
            label="wind",
186
            outputs={
187
                bus_electricity: flows.Flow(
188
                    fix=data["wind"], nominal_value=1000000
189
                )
190
            },
191
        )
192
    )
193
194
    # create fixed source object representing pv power plants
195
    energysystem.add(
196
        components.Source(
197
            label="pv",
198
            outputs={
199
                bus_electricity: flows.Flow(
200
                    fix=data["pv"], nominal_value=582000
201
                )
202
            },
203
        )
204
    )
205
206
    # create simple sink object representing the electrical demand
207
    # nominal_value is set to 1 because demand_el is not a normalised series
208
    energysystem.add(
209
        components.Sink(
210
            label="demand",
211
            inputs={
212
                bus_electricity: flows.Flow(
213
                    fix=data["demand_el"], nominal_value=1
214
                )
215
            },
216
        )
217
    )
218
219
    # create storage object representing a battery
220
    nominal_capacity = 10077997
221
    nominal_value = nominal_capacity / 6
222
223
    battery_storage = components.GenericStorage(
224
        nominal_storage_capacity=nominal_capacity,
225
        label=STORAGE_LABEL,
226
        inputs={bus_electricity: flows.Flow(nominal_value=nominal_value)},
227
        outputs={
228
            bus_electricity: flows.Flow(
229
                nominal_value=nominal_value, variable_costs=0.001
230
            )
231
        },
232
        loss_rate=0.00,
233
        initial_storage_level=None,
234
        inflow_conversion_factor=1,
235
        outflow_conversion_factor=0.8,
236
    )
237
238
    energysystem.add(battery_storage)
239
240
    ##########################################################################
241
    # Optimise the energy system and plot the results
242
    ##########################################################################
243
244
    logging.info("Optimise the energy system")
245
246
    # initialise the operational model
247
    energysystem_model = Model(energysystem)
248
249
    # This is for debugging only. It is not(!) necessary to solve the problem
250
    # and should be set to False to save time and disc space in normal use. For
251
    # debugging the timesteps should be set to 3, to increase the readability
252
    # of the lp-file.
253
    if debug:
254
        file_path = os.path.join(
255
            helpers.extend_basic_path("lp_files"), "basic_example.lp"
256
        )
257
        logging.info(f"Store lp-file in {file_path}.")
258
        io_option = {"symbolic_solver_labels": True}
259
        energysystem_model.write(file_path, io_options=io_option)
260
261
    # if tee_switch is true solver messages will be displayed
262
    logging.info("Solve the optimization problem")
263
    energysystem_model.solve(
264
        solver=solver, solve_kwargs={"tee": solver_verbose}
265
    )
266
267
    logging.info("Store the energy system with the results.")
268
269
    # The processing module of the outputlib can be used to extract the results
270
    # from the model transfer them into a homogeneous structured dictionary.
271
272
    # add results to the energy system to make it possible to store them.
273
    energysystem.results["main"] = processing.results(energysystem_model)
274
    energysystem.results["meta"] = processing.meta_results(energysystem_model)
275
276
    # The default path is the '.oemof' folder in your $HOME directory.
277
    # The default filename is 'es_dump.oemof'.
278
    # You can omit the attributes (as None is the default value) for testing
279
    # cases. You should use unique names/folders for valuable results to avoid
280
    # overwriting.
281
    if dump_results:
282
        energysystem.dump(dpath=None, filename=None)
283
284
    # *************************************************************************
285
    # ********** PART 2 - Processing the results ******************************
286
    # *************************************************************************
287
288
    # Saved data can be restored in a second script. So you can work on the
289
    # data analysis without re-running the optimisation every time. If you do
290
    # so, make sure that you really load the results you want. For example,
291
    # if dumping fails, you might exidentially load outdated results.
292
    if restore_results:
293
        logging.info("**** The script can be divided into two parts here.")
294
        logging.info("Restore the energy system and the results.")
295
296
        energysystem = EnergySystem()
297
        energysystem.restore(dpath=None, filename=None)
298
299
    # define an alias for shorter calls below (optional)
300
    results = energysystem.results["main"]
301
    storage = energysystem.groups[STORAGE_LABEL]
302
303
    # print a time slice of the state of charge
304
    start_time = datetime(2012, 2, 25, 8, 0, 0)
305
    end_time = datetime(2012, 2, 25, 17, 0, 0)
306
307
    print("\n********* State of Charge (slice) *********")
308
    print(f"{results[(storage, None)]['sequences'][start_time : end_time]}\n")
309
310
    # get all variables of a specific component/bus
311
    custom_storage = views.node(results, STORAGE_LABEL)
312
    electricity_bus = views.node(results, "electricity")
313
314
    # plot the time series (sequences) of a specific component/bus
315
    plot_figures_for(custom_storage)
316
    plot_figures_for(electricity_bus)
317
318
    # print the solver results
319
    print("********* Meta results *********")
320
    pp.pprint(f"{energysystem.results['meta']}\n")
321
322
    # print the sums of the flows around the electricity bus
323
    print("********* Main results *********")
324
    print(electricity_bus["sequences"].sum(axis=0))
325
326
327
if __name__ == "__main__":
328
    main()
329