Passed
Pull Request — dev (#1193)
by
unknown
01:46
created

facade_example.main()   B

Complexity

Conditions 3

Size

Total Lines 196
Code Lines 92

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 92
dl 0
loc 196
rs 7.2618
c 0
b 0
f 0
cc 3
nop 0

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:
34
    `facade_example.py </../examples/facade/facade_example.py>`
35
36
.. dropdown:: Click to display code
37
38
    .. literalinclude:: /../examples/facade/facade_example.py
39
        :language: python
40
        :lines: 61-
41
42
Data
43
----
44
Download data: :download:
45
    `facade_example_data.csv </../examples/facade/facade_example_data.csv>`
46
47
Installation requirements
48
-------------------------
49
This example requires oemof.solph (v0.5.x), install by:
50
51
.. code:: bash
52
53
    pip install oemof.solph[examples]
54
55
License
56
-------
57
`MIT license <https://github.com/oemof/oemof-solph/blob/dev/LICENSE>`_
58
"""
59
###########################################################################
60
# imports
61
###########################################################################
62
63
import logging
64
import os
65
66
import matplotlib.pyplot as plt
67
import pandas as pd
68
from oemof.network.network import HierachicalLabel
69
70
from facade import DSO
71
from oemof.tools import logger
72
73
from oemof.solph import EnergySystem
74
from oemof.solph import Model
75
from oemof.solph import Results
76
from oemof.solph import buses
77
from oemof.solph import components
78
from oemof.solph import create_time_index
79
from oemof.solph import flows
80
from oemof.solph import helpers
81
82
STORAGE_LABEL = "battery_storage"
83
84
85
def get_data_from_file_path(file_path: str) -> pd.DataFrame:
86
    my_dir = os.path.dirname(os.path.abspath(__file__))
87
    data = pd.read_csv(my_dir + "/" + file_path)
88
    return data
89
90
91 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...
92
    figure, axes = plt.subplots(figsize=(10, 5))
93
    element["sequences"].plot(ax=axes, kind="line", drawstyle="steps-post")
94
    plt.legend(
95
        loc="upper center",
96
        prop={"size": 8},
97
        bbox_to_anchor=(0.5, 1.25),
98
        ncol=2,
99
    )
100
    figure.subplots_adjust(top=0.8)
101
    plt.show()
102
103
104
def main():
105
    # For models that need a long time to optimise, saving and loading the
106
    # EnergySystem might be advised. By default, we do not do this here. Feel
107
    # free to experiment with this once you understood the rest of the code.
108
109
    # *************************************************************************
110
    # ********** PART 1 - Define and optimise the energy system ***************
111
    # *************************************************************************
112
113
    # Read data file
114
    file_name = "facade_example_data.csv"
115
    data = get_data_from_file_path(file_name)
116
117
    solver = "cbc"  # 'glpk', 'gurobi',....
118
    debug = False  # Set number_of_timesteps to 3 to get a readable lp-file.
119
    number_of_time_steps = len(data)
120
    solver_verbose = False  # show/hide solver output
121
122
    # initiate the logger (see the API docs for more information)
123
    logger.define_logging(
124
        logfile="oemof_example.log",
125
        screen_level=logging.INFO,
126
        file_level=logging.INFO,
127
    )
128
129
    logging.info("Initialize the energy system")
130
    date_time_index = create_time_index(2012, number=number_of_time_steps)
131
132
    # create the energysystem and assign the time index
133
    energysystem = EnergySystem(
134
        timeindex=date_time_index, infer_last_interval=False
135
    )
136
    ##########################################################################
137
    # Create oemof objects
138
    ##########################################################################
139
140
    logging.info("Create oemof objects")
141
142
    # The bus objects were assigned to variables which makes it easier to
143
    # connect components to these buses (see below).
144
145
    # create natural gas bus
146
    bus_gas = buses.Bus(label=HierachicalLabel("natural_gas"))
147
148
    # create electricity bus
149
    bus_electricity = buses.Bus(label=HierachicalLabel("electricity"))
150
151
    # adding the buses to the energy system
152
    energysystem.add(bus_gas, bus_electricity)
153
154
    # create excess component for the electricity bus to allow overproduction
155
    energysystem.add(
156
        components.Sink(
157
            label=HierachicalLabel("excess_bus_electricity"),
158
            inputs={bus_electricity: flows.Flow()},
159
        )
160
    )
161
162
    energysystem.add(
163
        DSO(
164
            label="My_DSO",
165
            el_bus=bus_electricity,
166
            energy_price=0.1,
167
            feedin_tariff=0.04,
168
        )
169
    )
170
    # energysystem.add(
171
    #     components.Source(
172
    #         label="DSO_simple",
173
    #         outputs={
174
    #             bus_electricity: flows.Flow(variable_costs=0.1)
175
    #         },
176
    #     )
177
    # )
178
179
    # create fixed source object representing wind power plants
180
    energysystem.add(
181
        components.Source(
182
            label=HierachicalLabel("wind"),
183
            outputs={
184
                bus_electricity: flows.Flow(
185
                    fix=data["wind"], nominal_capacity=1000000
186
                )
187
            },
188
        )
189
    )
190
191
    # create fixed source object representing pv power plants
192
    energysystem.add(
193
        components.Source(
194
            label=HierachicalLabel("pv"),
195
            outputs={
196
                bus_electricity: flows.Flow(
197
                    fix=data["pv"], nominal_capacity=582000
198
                )
199
            },
200
        )
201
    )
202
203
    # create simple sink object representing the electrical demand
204
    # nominal_value is set to 1 because demand_el is not a normalised series
205
    energysystem.add(
206
        components.Sink(
207
            label=HierachicalLabel("demand"),
208
            inputs={
209
                bus_electricity: flows.Flow(
210
                    fix=data["demand_el"], nominal_capacity=1
211
                )
212
            },
213
        )
214
    )
215
216
    # create storage object representing a battery
217
    nominal_capacity = 10077997
218
    nominal_value = nominal_capacity / 6
219
220
    battery_storage = components.GenericStorage(
221
        nominal_capacity=nominal_capacity,
222
        label=HierachicalLabel(STORAGE_LABEL),
223
        inputs={bus_electricity: flows.Flow(nominal_capacity=nominal_value)},
224
        outputs={
225
            bus_electricity: flows.Flow(
226
                nominal_capacity=nominal_value, variable_costs=0.001
227
            )
228
        },
229
        loss_rate=0.00,
230
        initial_storage_level=None,
231
        inflow_conversion_factor=1,
232
        outflow_conversion_factor=0.8,
233
    )
234
235
    energysystem.add(battery_storage)
236
237
    ##########################################################################
238
    # Optimise the energy system and plot the results
239
    ##########################################################################
240
241
    logging.info("Optimise the energy system")
242
243
    # initialise the operational model
244
    energysystem_model = Model(energysystem)
245
246
    # This is for debugging only. It is not(!) necessary to solve the problem
247
    # and should be set to False to save time and disc space in normal use. For
248
    # debugging the timesteps should be set to 3, to increase the readability
249
    # of the lp-file.
250
    if debug:
251
        file_path = os.path.join(
252
            helpers.extend_basic_path("lp_files"), "basic_example.lp"
253
        )
254
        logging.info(f"Store lp-file in {file_path}.")
255
        io_option = {"symbolic_solver_labels": True}
256
        energysystem_model.write(file_path, io_options=io_option)
257
258
    # if tee_switch is true solver messages will be displayed
259
    logging.info("Solve the optimization problem")
260
    energysystem_model.solve(
261
        solver=solver, solve_kwargs={"tee": solver_verbose}
262
    )
263
    results = Results(energysystem_model)
264
265
    # ToDO Implement a filter methode for the Result object to exclude
266
    #  subcomponents of a facade/sub-network
267
    # The following lines are meant to show how the result should look like
268
    # in case the subcomponents should be exclude. There should not be a
269
    # postprocessing it is better to filter the nodes directly
270
271
    # Filter columns that are internal only
272
    keep_columns = [
273
        c
274
        for c in results.flow.columns
275
        if getattr(c[1].label, "parent", None)
276
        != getattr(c[0].label, "parent", None)
277
        or (
278
            getattr(c[0].label, "parent", True) is True
279
            and getattr(c[1].label, "parent", True) is True
280
        )
281
    ]
282
    flow_results_filtered = results.flow[keep_columns].copy()
283
284
    # Replace subcomponent with facade object
285
    for level in [0, 1]:
286
        flow_results_filtered.rename(
287
            columns={
288
                c[level]: getattr(c[level].label, "parent", c[level])
289
                for c in flow_results_filtered.columns
290
            },
291
            level=level,
292
            inplace=True,
293
        )
294
295
    print("**** All results ****")
296
    print(results.flow.sum())
297
298
    print("**** Filtered results ****")
299
    print(flow_results_filtered.sum())
300
301
302
if __name__ == "__main__":
303
    main()
304