def check_oemof_installation(silent=False): date_time_index = pd.date_range('1/1/2012', periods=5, freq='H') energysystem = solph.EnergySystem(timeindex=date_time_index) bgas = solph.Bus(label="natural_gas") bel = solph.Bus(label="electricity") solph.Sink(label='excess_bel', inputs={bel: solph.Flow()}) solph.Source(label='rgas', outputs={bgas: solph.Flow()}) solph.Sink(label='demand', inputs={bel: solph.Flow( actual_value=[10, 20, 30, 40, 50], fixed=True, nominal_value=1)}) solph.Transformer( label="pp_gas", inputs={bgas: solph.Flow()}, outputs={bel: solph.Flow(nominal_value=10e10, variable_costs=50)}, conversion_factors={bel: 0.58}) om = solph.Model(energysystem) # check solvers solver = dict() for s in ['cbc', 'glpk', 'gurobi', 'cplex']: try: om.solve(solver=s) solver[s] = "working" except Exception: solver[s] = "not working" if not silent: print("*********") print('Solver installed with oemof:') for s, t in solver.items(): print("{0}: {1}".format(s, t)) print("*********") print("oemof successfully installed.")
def test_fixed_source_invest_sink(self): """ Wrong constraints for fixed source + invest sink w. `summed_max`. """ bel = solph.Bus(label='electricityBus') solph.Source(label='wind', outputs={ bel: solph.Flow(actual_value=[12, 16, 14], nominal_value=1000000, fixed=True) }) solph.Sink(label='excess', inputs={ bel: solph.Flow(summed_max=2.3, variable_costs=25, max=0.8, investment=solph.Investment(ep_costs=500, maximum=10e5, existing=50)) }) self.compare_lp_files('fixed_source_invest_sink.lp')
def test_investment_flow_grouping(self): """ Flows of investment sink should be grouped. The constraint tests uncovered a spurious error where the flows of an investment `Sink` where not put into the `InvestmentFlow` group, although the corresponding grouping was present in the energy system. The error occured in the case where the investment `Sink` was not instantiated directly after the `Bus` it is connected to. This test recreates this error scenario and makes sure that the `InvestmentFlow` group is not empty. """ b = solph.Bus(label='Bus') solph.Source(label='Source', outputs={b: solph.Flow( actual_value=[12, 16, 14], nominal_value=1000000, fixed=True)}) solph.Sink(label='Sink', inputs={b: solph.Flow( summed_max=2.3, variable_costs=25, max=0.8, investment=Investment(ep_costs=500, maximum=10e5))}) ok_(self.es.groups.get(IF), ("Expected InvestmentFlow group to be nonempty.\n" + "Got: {}").format(self.es.groups.get(IF)))
def ht_emergency_cooling_sink(param, busses): r""" Get high temp. emergency cooling sink for Generic Model energy system. Parameters ---------- param : dict JSON parameter file of user defined constants. busses : dict of solph.Bus Busses of the energy system. Note ---- High temperature emergency cooling sink uses the following parameters: - 'op_cost_var' are the variable operational costs for emergency cooling in €/MWh Topology -------- Input: High temperature heat network (wnw) Output: none """ if param['HT-EC']['active']: ht_ec_sink = solph.Sink( label='HT-EC', inputs={ busses['wnw']: solph.Flow(variable_costs=param['HT-EC']['op_cost_var']) }) return ht_ec_sink
def electricity_sink(param, data, busses): r""" Get electricity sink for Generic Model energy system. Parameters ---------- param : dict JSON parameter file of user defined constants. data : pandas.DataFrame csv file of user defined time dependent parameters. busses : dict of solph.Bus Busses of the energy system. Note ---- Electricity sink uses the following parameters: - 'el_spot_price' is the time series of spot market price in €/MWh - 'vNNE' is the price for avoided grid usage fees in €/MWh Topology -------- Input: Electricity network (enw) Output: none """ elec_sink = solph.Sink( label='Spotmarkt', inputs={ busses['enw']: solph.Flow(variable_costs=(-data['el_spot_price'] - param['param']['vNNE'])) }) return elec_sink
def test_equate_variables_constraint(self): """Testing the equate_variables function in the constraint module.""" bus1 = solph.Bus(label='Bus1') storage = solph.components.GenericStorage( label='storage_constraint', invest_relation_input_capacity=0.2, invest_relation_output_capacity=0.2, inputs={bus1: solph.Flow()}, outputs={bus1: solph.Flow()}, investment=solph.Investment(ep_costs=145)) sink = solph.Sink( label='Sink', inputs={ bus1: solph.Flow(investment=solph.Investment(ep_costs=500)) }) source = solph.Source( label='Source', outputs={ bus1: solph.Flow(investment=solph.Investment(ep_costs=123)) }) om = self.get_om() solph.constraints.equate_variables( om, om.InvestmentFlow.invest[source, bus1], om.InvestmentFlow.invest[bus1, sink], 2) solph.constraints.equate_variables( om, om.InvestmentFlow.invest[source, bus1], om.GenericInvestmentStorageBlock.invest[storage]) self.compare_lp_files('connect_investment.lp', my_om=om)
def solar_thermal_emergency_cooling(param, busses): r""" Get solar thermal emergency cooling sink for Generic Model energy system. Parameters ---------- param : dict JSON parameter file of user defined constants. data : pandas.DataFrame csv file of user defined time dependent parameters. busses : dict of solph.Bus Busses of the energy system. Note ---- Solar thermal emergency cooling sink uses the following parameters: - 'op_cost_var' are the variable operational costs for emergency cooling in €/MWh Topology -------- Input: Solar thermal node (sol_node) Output: none """ sol_ec_sink = solph.Sink( label='Sol-EC', inputs={ busses['sol_node']: solph.Flow(variable_costs=param['Sol-EC']['op_cost_var']) }) return sol_ec_sink
def add_district_heating_demand(table_collection, nodes): """ Parameters ---------- table_collection nodes Returns ------- """ logging.debug("Add district heating systems to nodes dictionary.") dts = table_collection["heat demand series"] demand_sets = [c for c in dts.columns if "district heating" in str(c)] for demand_set in demand_sets: region = demand_set[0] if dts[demand_set].sum() > 0: bus_label = Label("bus", "heat", "district", region) if bus_label not in nodes: nodes[bus_label] = solph.Bus(label=bus_label) heat_demand_label = Label("demand", "heat", "district", region) nodes[heat_demand_label] = solph.Sink( label=heat_demand_label, inputs={ nodes[bus_label]: solph.Flow( fix=dts[demand_set], nominal_value=1, ) }, )
def add_electricity_demand(input_data, nodes): """ Parameters ---------- input_data nodes Returns ------- """ logging.debug("Add local electricity demand to nodes dictionary.") for idx, series in input_data["electricity demand series"].items(): region = idx[0] demand_name = idx[1] if series.sum() > 0: bus_label = Label("bus", "electricity", "all", region) if bus_label not in nodes: create_electricity_bus(nodes, region) elec_demand_label = Label( "demand", "electricity", demand_name, region ) nodes[elec_demand_label] = solph.Sink( label=elec_demand_label, inputs={ nodes[bus_label]: solph.Flow( fix=series, nominal_value=1, ) }, )
def add_demand(it, labels, gd, series, nodes, busd): for i, de in it.iterrows(): labels['l_3'] = 'demand' if de['active']: labels['l_2'] = de['label_2'] # set static inflow values inflow_args = { 'nominal_value': de['scalingfactor'], 'fixed': de['fixed'], 'actual_value': series[labels['l_2']][labels['l_4']] } # create nodes.append( solph.Sink( label=oh.Label(labels['l_1'], labels['l_2'], labels['l_3'], labels['l_4']), inputs={ busd[(labels['l_1'], labels['l_2'], 'bus', labels['l_4'])]: solph.Flow(**inflow_args) })) return nodes, busd
def create_oemof_model(self, busses, _): sink = solph.Sink( label=self.name, inputs={busses[self.bus_in]: solph.Flow( variable_costs=self.electricity_costs, nominal_value=self.power_max )}) return sink
def test_that_the_sink_warnings_actually_get_raised(): """ Sink doesn't warn about potentially erroneous usage. """ look_out = network.Bus() msg = "`Sink` 'test_sink' constructed without `inputs`." with warnings.catch_warnings(record=True) as w: solph.Sink(label='test_sink', outputs={look_out: "A typo!"}) ok_(len(w) == 1) eq_(msg, str(w[-1].message))
def check_oemof_installation(silent=False): logging.disable(logging.CRITICAL) date_time_index = pd.date_range("1/1/2012", periods=5, freq="H") energysystem = solph.EnergySystem(timeindex=date_time_index) bgas = solph.Bus(label="natural_gas") bel = solph.Bus(label="electricity") solph.Sink(label="excess_bel", inputs={bel: solph.Flow()}) solph.Source(label="rgas", outputs={bgas: solph.Flow()}) solph.Sink( label="demand", inputs={bel: solph.Flow(fix=[10, 20, 30, 40, 50], nominal_value=1)}, ) solph.Transformer( label="pp_gas", inputs={bgas: solph.Flow()}, outputs={bel: solph.Flow(nominal_value=10e10, variable_costs=50)}, conversion_factors={bel: 0.58}, ) om = solph.Model(energysystem) # check solvers solver = dict() for s in ["cbc", "glpk", "gurobi", "cplex"]: try: om.solve(solver=s) solver[s] = "working" except Exception: solver[s] = "not working" if not silent: print() print("*****************************") print("Solver installed with oemof:") print() for s, t in solver.items(): print("{0}: {1}".format(s, t)) print() print("*****************************") print("oemof successfully installed.") print("*****************************")
def create_oemof_model(self, busses, _): energy_demand_from_csv = solph.Sink( label=self.name, inputs={ busses[self.bus_in]: solph.Flow( actual_value=self.data.iloc[self.sim_params.i_interval], nominal_value=self.nominal_value, fixed=True) }) return energy_demand_from_csv
def run_model(params, wind_invest=False, pv_invest=False, storage_invest=False): logging.info('Initialize the energy system') energysystem = solph.EnergySystem(timeindex=date_time_index) Node.registry = energysystem logging.info('Create oemof objects') bgas = solph.Bus(label="natural_gas") bel = solph.Bus(label="electricity") solph.Sink(label='excess_bel', inputs={bel: solph.Flow()}) solph.Source(label='rgas', outputs={bgas: solph.Flow(nominal_value=params['rgas_nom_val'], summed_max=1)}) solph.Source(label='wind', outputs={bel: solph.Flow( actual_value=data['wind'], nominal_value=params['wind_nom_val'], fixed=True)}) solph.Source(label='pv', outputs={bel: solph.Flow( actual_value=data['pv'], nominal_value=params['pv_nom_val'], fixed=True)}) solph.Sink(label='demand', inputs={bel: solph.Flow( actual_value=data['demand_el'], fixed=True, nominal_value=1)}) solph.Transformer( label="pp_gas", inputs={bgas: solph.Flow()}, outputs={bel: solph.Flow(nominal_value=10e10, variable_costs=50)}, conversion_factors={bel: 0.58}) logging.info('Optimise the energy system') om = solph.Model(energysystem) logging.info('Solve the optimization problem') om.solve(solver='cbc') energysystem.results['main'] = processing.convert_keys_to_strings(processing.results(om)) energysystem.results['param'] = processing.convert_keys_to_strings(processing.param_results(energysystem)) return energysystem, om
def test_fixed_source_variable_sink(self): """Constraint test with a fixed source and a variable sink. """ bel = solph.Bus(label='electricityBus') solph.Source(label='wind', outputs={bel: solph.Flow( actual_value=[.43, .72, .29], nominal_value=10e5, fixed=True)}) solph.Sink(label='excess', inputs={bel: solph.Flow(variable_costs=40)}) self.compare_lp_files('fixed_source_variable_sink.lp')
def test_max_source_min_sink(self): """ """ bel = solph.Bus(label='electricityBus') solph.Source(label='wind', outputs={ bel: solph.Flow(nominal_value=54, max=(.85, .95, .61))}) solph.Sink(label='minDemand', inputs={bel: solph.Flow( nominal_value=54, min=(.84, .94, .59), variable_costs=14)}) self.compare_lp_files('max_source_min_sink.lp')
def add_mobility(table_collection, nodes): """ Parameters ---------- table_collection nodes Returns ------- """ mseries = table_collection["mobility demand series"] mtable = table_collection["mobility"] for region in mseries.columns.get_level_values(0).unique(): for fuel in mseries[region].columns: source = mtable.loc[(region, fuel), "source"] source_region = mtable.loc[(region, fuel), "source region"] if mseries[region, fuel].sum() > 0: fuel_transformer = Label("process", "fuel", fuel, region) fuel_demand = Label("demand", "mobility", fuel, region) bus_label = Label("bus", "mobility", fuel, region) if fuel != "electricity": com_bus_label = Label( "bus", "commodity", source, source_region ) else: com_bus_label = Label( "bus", "electricity", "all", source_region ) if bus_label not in nodes: nodes[bus_label] = solph.Bus(label=bus_label) if com_bus_label not in nodes: nodes[com_bus_label] = solph.Bus(label=com_bus_label) cf = mtable.loc[(region, fuel), "efficiency"] nodes[fuel_transformer] = solph.Transformer( label=fuel_transformer, inputs={nodes[com_bus_label]: solph.Flow()}, outputs={nodes[bus_label]: solph.Flow()}, conversion_factors={nodes[bus_label]: cf}, ) fix_value = mseries[region, fuel] nodes[fuel_demand] = solph.Sink( label=fuel_demand, inputs={ nodes[bus_label]: solph.Flow( nominal_value=1, fix=fix_value ) }, ) return nodes
def sink_dispatchable_optimize(model, dict_asset, **kwargs): r""" Define a dispatchable sink. The dispatchable sink is capacity-optimized, without any costs connected to the capacity of the asset. Applications of this asset type are: Feed-in sink, excess sink. See :py:func:`~.sink` for more information, including parameters. Notes ----- Tested with: - test_sink_dispatchable_single_input_bus() - test_sink_dispatchable_multiple_input_busses() Returns ------- Indirectly updated `model` and dict of asset in `kwargs` with the sink object. """ # check if the sink has multiple input busses if isinstance(dict_asset[INFLOW_DIRECTION], list): inputs = {} index = 0 for bus in dict_asset[INFLOW_DIRECTION]: inputs[kwargs[OEMOF_BUSSES][bus]] = solph.Flow( label=dict_asset[LABEL], variable_costs=dict_asset[DISPATCH_PRICE][VALUE][index], investment=solph.Investment(), ) index += 1 else: inputs = { kwargs[OEMOF_BUSSES][dict_asset[INFLOW_DIRECTION]]: solph.Flow( label=dict_asset[LABEL], variable_costs=dict_asset[DISPATCH_PRICE][VALUE], investment=solph.Investment(), ) } # create and add excess electricity sink to micro_grid_system - variable sink_dispatchable = solph.Sink( label=dict_asset[LABEL], inputs=inputs, ) model.add(sink_dispatchable) kwargs[OEMOF_SINK].update({dict_asset[LABEL]: sink_dispatchable}) logging.debug( f"Added: Dispatchable sink {dict_asset[LABEL]} (to be capacity optimized) to bus {dict_asset[INFLOW_DIRECTION]}.", )
def test_invest_source_fixed_sink(self): """Constraint test with a fixed sink and a dispatch invest source. """ bel = solph.Bus(label='electricityBus') solph.Source(label='pv', outputs={bel: solph.Flow( max=[45, 83, 65], variable_costs=13, investment=solph.Investment(ep_costs=123))}) solph.Sink(label='excess', inputs={bel: solph.Flow( actual_value=[.5, .8, .3], nominal_value=10e4, fixed=True)}) self.compare_lp_files('invest_source_fixed_sink.lp')
def add_upstream_import_export_nodes(nodes, bus, costs): logging.info("Add upstream prices from {0}".format(costs["name"])) exp_label = Label("export", "electricity", "all", bus.label.region) nodes[exp_label] = solph.Sink( label=exp_label, inputs={bus: solph.Flow(variable_costs=costs["export"])}, ) imp_label = Label("import", "electricity", "all", bus.label.region) nodes[imp_label] = solph.Source( label=imp_label, outputs={bus: solph.Flow(variable_costs=costs["import"])}, ) return nodes
def add_buses(it, labels, nodes, busd): """ :param it: pd.Dataframe containing tabular information for the creation of buses :param labels: dict of label strings :return: """ for i, b in it.iterrows(): labels['l_3'] = 'bus' if b['active']: labels['l_2'] = b['label_2'] l_bus = oh.Label(labels['l_1'], labels['l_2'], labels['l_3'], labels['l_4']) # check if bus already exists (due to infrastructure) if l_bus in busd: print('bus bereits vorhanden:', l_bus) else: bus = solph.Bus(label=l_bus) nodes.append(bus) busd[l_bus] = bus if b['excess']: labels['l_3'] = 'excess' nodes.append( solph.Sink( label=oh.Label(labels['l_1'], labels['l_2'], labels['l_3'], labels['l_4']), inputs={ busd[l_bus]: solph.Flow(variable_costs=b['excess costs']) })) if b['shortage']: labels['l_3'] = 'shortage' nodes.append( solph.Source( label=oh.Label(labels['l_1'], labels['l_2'], labels['l_3'], labels['l_4']), outputs={ busd[l_bus]: solph.Flow(variable_costs=b['shortage costs']) })) return nodes, busd
def test_optimal_solution(): es = solph.EnergySystem(timeindex=[1]) bel = solph.Bus(label='bus') es.add(bel) es.add( solph.Sink( inputs={ bel: solph.Flow(nominal_value=5, actual_value=[1], fixed=True) })) es.add(solph.Source(outputs={bel: solph.Flow(variable_costs=5)})) m = solph.models.Model(es, timeincrement=1) m.solve('cbc') m.results() outputlib.processing.meta_results(m)
def test_nonconvex_invest_sink_without_offset(self): """ Non convex invest flow without offset, with minimum. """ bel = solph.Bus(label='electricityBus') solph.Sink(label='sink_nonconvex_invest', inputs={ bel: solph.Flow(summed_max=2.3, variable_costs=25, max=0.8, investment=solph.Investment(ep_costs=500, minimum=15, nonconvex=True, maximum=172)) }) self.compare_lp_files('flow_invest_without_offset.lp')
def add_to_oemof_model(self, busses, model): """Creates an oemof Sink component from the information given in the EnergyDemandFromCSV class, to be used in the oemof model. :param busses: virtual buses used in the energy system :type busses: dict :param model: current oemof model :type model: oemof model :return: oemof component """ energy_demand_from_csv = solph.Sink( label=self.name, inputs={busses[self.bus_in]: solph.Flow( fix=self.data.iloc[self.sim_params.i_interval], nominal_value=self.nominal_value)}) model.add(energy_demand_from_csv) return energy_demand_from_csv
def test_infeasible_model(): with tools.assert_raises_regexp(ValueError, ''): with warnings.catch_warnings(record=True) as w: es = solph.EnergySystem(timeindex=[1]) bel = solph.Bus(label='bus') es.add(bel) es.add( solph.Sink(inputs={ bel: solph.Flow(nominal_value=5, actual_value=[1], fixed=True) })) es.add( solph.Source(outputs={ bel: solph.Flow(nominal_value=4, variable_costs=5) })) m = solph.models.Model(es, timeincrement=1) m.solve(solver='cbc') assert "Optimization ended with status" in str(w[0].message) outputlib.processing.meta_results(m)
def add_to_oemof_model(self, busses, model): """Creates an oemof Sink component from the information given in the Sink class, to be used in the oemof model. :param busses: virtual buses used in the energy system :type busses: dict :param model: current oemof model :type model: oemof model :return: oemof component """ sink = solph.Sink(label=self.name, inputs={ busses[self.bus_in]: solph.Flow(variable_costs=self.commodity_costs, nominal_value=self.input_max) }) model.add(sink) return sink
def sink_non_dispatchable(model, dict_asset, **kwargs): r""" Defines a non dispatchable sink. See :py:func:`~.sink` for more information, including parameters. Notes ----- Tested with: - test_sink_non_dispatchable_single_input_bus() - test_sink_non_dispatchable_multiple_input_busses() Returns ------- Indirectly updated `model` and dict of asset in `kwargs` with the sink object. """ # check if the sink has multiple input busses if isinstance(dict_asset[INFLOW_DIRECTION], list): inputs = {} index = 0 for bus in dict_asset[INFLOW_DIRECTION]: inputs[kwargs[OEMOF_BUSSES][bus]] = solph.Flow( fix=dict_asset[TIMESERIES], nominal_value=1) index += 1 else: inputs = { kwargs[OEMOF_BUSSES][dict_asset[INFLOW_DIRECTION]]: solph.Flow(fix=dict_asset[TIMESERIES], nominal_value=1) } # create and add demand sink to micro_grid_system - fixed sink_demand = solph.Sink( label=dict_asset[LABEL], inputs=inputs, ) model.add(sink_demand) kwargs[OEMOF_SINK].update({dict_asset[LABEL]: sink_demand}) logging.debug( f"Added: Non-dispatchable sink {dict_asset[LABEL]} to bus {dict_asset[INFLOW_DIRECTION]}" )
def add_to_oemof_model(self, busses, model): """Creates an oemof Sink component from information given in the H2RefuelCoolingSystem class, to be used in the oemof model :param busses: virtual buses used in the energy system :type busses: dict :param model: current oemof model :type model: oemof model :return: oemof component """ h2_refuel_cooling_system = solph.Sink( label=self.name, inputs={ busses[self.bus_el]: solph.Flow(fix=self.electrical_energy.iloc[ self.sim_params.i_interval], nominal_value=self.nominal_value) }) model.add(h2_refuel_cooling_system) return h2_refuel_cooling_system
def heat_sink(param, data, busses): r""" Get heat sink for Generic Model energy system. Parameters ---------- param : dict JSON parameter file of user defined constants. data : pandas.DataFrame csv file of user defined time dependent parameters. busses : dict of solph.Bus Busses of the energy system. Note ---- Heat sink uses the following parameters: - 'heat_price' is the constant price of the heat sold €/MWh - 'heat_demand' is the time series of the heat to be covered in MWh - 'rel_demand' is a scaling factor for the heat demand Topology -------- Input: High temperature heat network (wnw) Output: none """ heat_sink = solph.Sink( label='Wärmebedarf', inputs={ busses['wnw']: solph.Flow( variable_costs=-param['param']['heat_price'], nominal_value=(max(data['heat_demand'] * param['param']['rel_demand'])), fix=(data['heat_demand'] * param['param']['rel_demand'] / max(data['heat_demand'] * param['param']['rel_demand']))) }) return heat_sink