Passed
Pull Request — dev (#1193)
by Patrik
06:44
created

nested_subnetwork_example.main()   B

Complexity

Conditions 3

Size

Total Lines 213
Code Lines 106

Duplication

Lines 213
Ratio 100 %

Importance

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