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

facade_example.plot_figures_for()   A

Complexity

Conditions 1

Size

Total Lines 11
Code Lines 10

Duplication

Lines 11
Ratio 100 %

Importance

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