diff --git a/config.py b/config.py new file mode 100644 index 0000000..02857a6 --- /dev/null +++ b/config.py @@ -0,0 +1,138 @@ +# -*- coding: utf-8 -*- + +"""Reegis geometry tools. + +Copyright (c) 2016-2018 Uwe Krien + +SPDX-License-Identifier: GPL-3.0-or-later +""" +__copyright__ = "Uwe Krien " +__license__ = "GPLv3" + + +# Python libraries +import os +import logging +import configparser as cp +import sys + + +FILE = None +cfg = cp.RawConfigParser() +cfg.optionxform = str +_loaded = False + +# Path of the package that imports this package. +importer = os.path.dirname(sys.modules['__main__'].__file__) + + +def get_ini_filenames(additional_paths=None): + paths = list() + files = list() + + paths.append(os.path.join(os.path.dirname(__file__))) + if additional_paths is not None: + paths.extend(additional_paths) + paths.append(os.path.join(os.path.expanduser("~"), 'oemof/q100_ini')) + + for p in paths: + for f in os.listdir(p): + if f[-4:] == '.ini': + files.append(os.path.join(p, f)) + return files + + +def main(): + pass + + +def init(files=None, paths=None): + """Read config file(s). + + Parameters + ---------- + files : str or list or None + Absolute path to config file (incl. filename) + paths : list + List of paths where it is searched for .ini files. + """ + if files is None: + files = get_ini_filenames(paths) + + cfg.read(files) + global _loaded + _loaded = True + + +def get(section, key): + """Returns the value of a given key in a given section. + """ + if not _loaded: + init(FILE) + try: + return cfg.getint(section, key) + except ValueError: + try: + return cfg.getfloat(section, key) + except ValueError: + try: + return cfg.getboolean(section, key) + except ValueError: + try: + value = cfg.get(section, key) + if value == 'None': + value = None + return value + except ValueError: + logging.error( + "section {0} with key {1} not found in {2}".format( + section, key, FILE)) + return cfg.get(section, key) + + +def get_list(section, parameter, sep=',', string=False): + """Returns the values (separated by sep) of a given key in a given + section as a list. + """ + try: + my_list = get(section, parameter).split(sep) + my_list = [x.strip() for x in my_list] + + except AttributeError: + if string is True: + my_list = list((cfg.get(section, parameter),)) + else: + my_list = list((get(section, parameter),)) + return my_list + + +def get_dict(section): + """Returns the values of a section as dictionary + """ + if not _loaded: + init(FILE) + dc = {} + for key, value in cfg.items(section): + dc[key] = get(section, key) + return dc + + +def get_dict_list(section, string=False): + """Returns the values of a section as dictionary + """ + if not _loaded: + init(FILE) + dc = {} + for key, value in cfg.items(section): + dc[key] = get_list(section, key, string=string) + return dc + + +def tmp_set(section, key, value): + if not _loaded: + init(FILE) + return cfg.set(section, key, value) + + +if __name__ == "__main__": + print(get('paths', 'package_data')) diff --git a/postprocessing.py b/postprocessing.py new file mode 100644 index 0000000..a46f284 --- /dev/null +++ b/postprocessing.py @@ -0,0 +1,158 @@ +""" +oemof application for research project quarree100. + +Some parts of the code are adapted from +https://github.com/oemof/oemof-examples +-> excel_reader + +SPDX-License-Identifier: GPL-3.0-or-later +""" + +import oemof.outputlib as outputlib +import pandas as pd +import os +import config as cfg +from matplotlib import pyplot as plt + + +def plot_buses(res=None, es=None): + + l_buses = [] + + for n in es.nodes: + type_name =\ + str(type(n)).replace("", "") + if type_name == "network.Bus": + l_buses.append(n.label) + + for n in l_buses: + bus_sequences = outputlib.views.node(res, n)["sequences"] + bus_sequences.plot(kind='line', drawstyle="steps-mid", subplots=False, + sharey=True) + plt.show() + + +def plot_trans_invest(res=None, es=None): + + l_transformer = [] + + for n in es.nodes: + type_name =\ + str(type(n)).replace("", "") + if type_name == "network.Transformer": + l_transformer.append(n.label) + + p_trans_install = [] + + for q in l_transformer: + p_install = outputlib.views.node(res, q)["scalars"][0] + p_trans_install.append(p_install) + + # plot the installed Transformer Capacities + y = p_trans_install + x = l_transformer + width = 1/2 + plt.bar(x, y, width, color="blue") + plt.ylabel('Installierte Leistung [kW]') + plt.show() + + +def plot_storages_soc(res=None, es=None): + + l_storages = [] + + for n in es.nodes: + type_name =\ + str(type(n)).replace("", "") + if type_name == "components.GenericStorage": + l_storages.append(n.label) + + for n in l_storages: + soc_sequences = outputlib.views.node(res, n)["sequences"] + soc_sequences = soc_sequences.drop(soc_sequences.columns[[0, 2]], 1) + soc_sequences.plot(kind='line', drawstyle="steps-mid", subplots=False, + sharey=True) + plt.show() + + +def plot_storages_invest(res=None, es=None): + + l_storages = [] + + for n in es.nodes: + type_name =\ + str(type(n)).replace("", "") + if type_name == "components.GenericStorage": + l_storages.append(n.label) + + c_storage_install = [] + + for n in l_storages: + c_storage = outputlib.views.node(res, n)["scalars"][0] + c_storage_install.append(c_storage) + + # plot the installed Storage Capacities + plt.bar(l_storages, c_storage_install, width=0.5, color="blue") + plt.ylabel('Kapazität [kWh]') + plt.show() + + +def export_excel(res=None, es=None): + + l_buses = [] + + for n in es.nodes: + type_name =\ + str(type(n)).replace("", "") + if type_name == "network.Bus": + l_buses.append(n.label) + + l_transformer = [] + + for n in es.nodes: + type_name =\ + str(type(n)).replace("", "") + if type_name == "network.Transformer": + l_transformer.append(n.label) + + l_storages = [] + + for s in es.nodes: + type_name =\ + str(type(s)).replace("", "") + if type_name == "components.GenericStorage": + l_storages.append(s.label) + + df_series = pd.DataFrame() + + for n in l_buses: + bus_sequences = outputlib.views.node(res, n)["sequences"] + df_series = pd.concat([df_series, bus_sequences], axis=1) + + for n in l_storages: + soc_sequences = outputlib.views.node(res, n)["sequences"] + df_series = pd.concat([df_series, soc_sequences], axis=1) + + c_storage_install = [] + + for n in l_storages: + c_storage = outputlib.views.node(res, n)["scalars"][0] + c_storage_install.append(c_storage) + + p_trans_install = [] + + for q in l_transformer: + p_install = outputlib.views.node(res, q)["scalars"][0] + p_trans_install.append(p_install) + + df_invest_ges = pd.DataFrame( + [p_trans_install+c_storage_install], + columns=l_transformer+l_storages) + + # the result_gesamt df is exported in excel + path_to_results = os.path.join(os.path.expanduser("~"), + cfg.get('paths', 'results')) + filename = 'results.xlsx' + with pd.ExcelWriter(os.path.join(path_to_results, filename)) as xls: + df_series.to_excel(xls, sheet_name='Timeseries') + df_invest_ges.to_excel(xls, sheet_name='Invest') diff --git a/run_model_example.py b/run_model_example.py new file mode 100644 index 0000000..66efbd2 --- /dev/null +++ b/run_model_example.py @@ -0,0 +1,43 @@ +""" +oemof application for research project quarree100 + +Copyright (c) 2018 Quarree100 AB-3 Project-Team + +SPDX-License-Identifier: GPL-3.0-or-later +""" + +import setup_solve_model +import postprocessing +import config as cfg +import os + +# getting path to data from ini file +path_to_data = os.path.join(os.path.expanduser("~"), + cfg.get('paths', 'data')) + +filename = os.path.join( + os.path.expanduser("~"), path_to_data, 'Parameter.xlsx') + +# reading data from excel file with data read function +node_data = setup_solve_model.nodes_from_excel(filename) + +# setting up energy system +e_sys = setup_solve_model.setup_es(excel_nodes=node_data) + +# optimising the energy system +results = setup_solve_model.solve_es(energysystem=e_sys, excel_nodes=node_data) + +# plot the buses +postprocessing.plot_buses(res=results, es=e_sys) + +# plot the investments in transformer +postprocessing.plot_trans_invest(res=results, es=e_sys) + +# plot the storage SoC(t) +postprocessing.plot_storages_soc(res=results, es=e_sys) + +# plot the installed storage capacities +postprocessing.plot_storages_invest(res=results, es=e_sys) + +# expoprt the results to excel +postprocessing.export_excel(res=results, es=e_sys) diff --git a/setup_solve_model.py b/setup_solve_model.py new file mode 100644 index 0000000..d3deb3e --- /dev/null +++ b/setup_solve_model.py @@ -0,0 +1,287 @@ +""" +oemof application for research project quarree100. + +Adaption of the oemof_examples repository: +https://github.com/oemof/oemof-examples +-> excel_reader + +5.1.2018 - uwe.krien@rl-institut.de +7.5.2018 - jonathan.amme@rl-institut.de + +SPDX-License-Identifier: GPL-3.0-or-later +""" + +from oemof.tools import logger +from oemof.tools import economics +import oemof.solph as solph +import oemof.outputlib as outputlib +import logging +import pandas as pd + + +def nodes_from_excel(filename): + + xls = pd.ExcelFile(filename) + + nodes_data = {'buses': xls.parse('Buses'), + 'commodity_sources': xls.parse('Sources'), + 'sources_series': xls.parse('Sources_series'), + 'demand': xls.parse('Demand'), + 'transformers_siso': xls.parse('Transformer_siso'), + 'transformers_sido': xls.parse('Transformer_sido'), + 'storages': xls.parse('Storages'), + 'timeseries': xls.parse('Timeseries'), + 'general': xls.parse('General') + } + + # set datetime index + nodes_data['timeseries'].set_index('timestamp', inplace=True) + nodes_data['timeseries'].index = pd.to_datetime( + nodes_data['timeseries'].index) + + print('Data from Excel file {} imported.' + .format(filename)) + + return nodes_data + + +def create_nodes(nd=None): + """Create nodes (oemof objects) from node dict + + Parameters + ---------- + nd : :obj:`dict` + Nodes data + + Returns + ------- + nodes : `obj`:dict of :class:`nodes ` + """ + + if not nd: + raise ValueError('No nodes data provided.') + + nodes = [] + + # Create Bus objects from buses table + busd = {} + + for i, b in nd['buses'].iterrows(): + if b['active']: + bus = solph.Bus(label=b['label']) + nodes.append(bus) + + busd[b['label']] = bus + if b['excess']: + nodes.append( + solph.Sink(label=b['label'] + '_excess', + inputs={busd[b['label']]: solph.Flow( + variable_costs=b['excess costs'])}) + ) + if b['shortage']: + nodes.append( + solph.Source(label=b['label'] + '_shortage', + outputs={busd[b['label']]: solph.Flow( + variable_costs=b['shortage costs'])}) + ) + + # Create Source objects from table 'Sources' + for i, cs in nd['commodity_sources'].iterrows(): + if cs['active']: + nodes.append( + solph.Source(label=cs['label'], + outputs={busd[cs['to']]: solph.Flow( + variable_costs=cs['variable costs'], + emission=cs['emissions'])}) + ) + + # Create Source objects with fixed time series from 'renewables' table + for i, ss in nd['sources_series'].iterrows(): + if ss['active']: + # set static outflow values + outflow_args = {'nominal_value': ss['scalingfactor'], + 'fixed': True} + # get time series for node and parameter + for col in nd['timeseries'].columns.values: + if col.split('.')[0] == ss['label']: + outflow_args[col.split('.')[1]] = nd['timeseries'][col] + + # create + nodes.append( + solph.Source(label=ss['label'], + outputs={ + busd[ss['to']]: solph.Flow(**outflow_args)}) + ) + + # Create Sink objects with fixed time series from 'demand' table + for i, de in nd['demand'].iterrows(): + if de['active']: + # set static inflow values + inflow_args = {'nominal_value': de['scalingfactor'], + 'fixed': de['fixed']} + # get time series for node and parameter + for col in nd['timeseries'].columns.values: + if col.split('.')[0] == de['label']: + inflow_args[col.split('.')[1]] = nd['timeseries'][col] + + # create + nodes.append( + solph.Sink(label=de['label'], + inputs={ + busd[de['from']]: solph.Flow(**inflow_args)}) + ) + + # Create Transformer objects from 'transformers' table + for i, t in nd['transformers_siso'].iterrows(): + if t['active']: + + # calculation epc + epc_t = economics.annuity( + capex=t['capex'], n=t['n'], + wacc=nd['general']['interest rate'][0]) * \ + nd['general']['timesteps'][0] / 8760 + + # create + nodes.append( + solph.Transformer( + label=t['label'], + inputs={busd[t['from']]: solph.Flow()}, + outputs={busd[t['to']]: solph.Flow( + variable_costs=t['variable costs'], + emissions=['emissions'], + investment=solph.Investment( + ep_costs=epc_t))}, + conversion_factors={busd[t['to']]: t['efficiency']}) + ) + + for i, tdo in nd['transformers_sido'].iterrows(): + if tdo['active']: + # calculation epc + epc_tdo = economics.annuity( + capex=tdo['capex'], n=tdo['n'], + wacc=nd['general']['interest rate'][0]) *\ + nd['general']['timesteps'][0] / 8760 + + # create + nodes.append( + solph.Transformer( + label=tdo['label'], + inputs={busd[tdo['from']]: solph.Flow()}, + outputs={busd[tdo['to_1']]: solph.Flow( + investment=solph.Investment(ep_costs=epc_tdo)), + busd[tdo['to_2']]: solph.Flow() + }, + conversion_factors={ + busd[tdo['to_1']]: tdo['efficiency_1'], + busd[tdo['to_2']]: tdo['efficiency_2'] + }) + ) + + for i, s in nd['storages'].iterrows(): + if s['active']: + # calculate epc + epc_s = economics.annuity( + capex=s['capex'], n=s['n'], + wacc=nd['general']['interest rate'][0]) * \ + nd['general']['timesteps'][0] / 8760 + + # create Storages + nodes.append( + solph.components.GenericStorage( + label=s['label'], + inputs={busd[s['bus']]: solph.Flow()}, + outputs={busd[s['bus']]: solph.Flow()}, + capacity_loss=s['capacity_loss'], + invest_relation_input_capacity=s[ + 'invest_relation_input_capacity'], + invest_relation_output_capacity=s[ + 'invest_relation_output_capacity'], + inflow_conversion_factor=s['inflow_conversion_factor'], + outflow_conversion_factor=s['outflow_conversion_factor'], + investment=solph.Investment(ep_costs=epc_s))) + + return nodes + + +def setup_es(excel_nodes=None): + # Initialise the Energy System + logger.define_logging() + logging.info('Initialize the energy system') + + number_timesteps = excel_nodes['general']['timesteps'][0] + + date_time_index = pd.date_range('1/1/2016', + periods=number_timesteps, + freq='H') + energysystem = solph.EnergySystem(timeindex=date_time_index) + + logging.info('Create oemof objects') + + # create nodes from Excel sheet data with create_nodes function + my_nodes = create_nodes(nd=excel_nodes) + + # add nodes and flows to energy system + energysystem.add(*my_nodes) + + print('Energysystem has been created') + + print("*********************************************************") + print("The following objects have been created from excel sheet:") + for n in energysystem.nodes: + oobj =\ + str(type(n)).replace("", "") + print(oobj + ':', n.label) + print("*********************************************************") + + return energysystem + + +def solve_es(energysystem=None, excel_nodes=None): + # Optimise the energy system + logging.info('Optimise the energy system') + + # initialise the operational model + om = solph.Model(energysystem) + + # Global CONSTRAINTS: CO2 Limit + solph.constraints.emission_limit( + om, flows=None, limit=excel_nodes['general']['emission limit'][0]) + + logging.info('Solve the optimization problem') + # if tee_switch is true solver messages will be displayed + om.solve(solver='cbc', solve_kwargs={'tee': True}) + + logging.info('Store the energy system with the results.') + + # processing results + result = outputlib.processing.results(om) + + return result + + +def create_comp_lists(es=None): + + l_buses = [] + l_storages = [] + l_transformer = [] + + for n in es.nodes: + + type_name =\ + str(type(n)).replace("", "") + + if type_name == "network.Bus": + l_buses.append(n.label) + + if type_name == "network.Transformer": + l_transformer.append(n.label) + + if type_name == "components.GenericStorage": + l_storages.append(n.label) + + comp_dict = {'buses': l_buses, + 'transformer': l_transformer, + 'storages': l_storages + } + + return comp_dict