def test_generic_invest_limit(self): """ """ bus = solph.Bus(label='bus_1') solph.Source( label='source_0', outputs={ bus: solph.Flow(investment=solph.Investment(ep_costs=50, space=4)) }) solph.Source( label='source_1', outputs={ bus: solph.Flow(investment=solph.Investment(ep_costs=100, space=1)) }) solph.Source(label='source_2', outputs={ bus: solph.Flow(investment=solph.Investment(ep_costs=75)) }) om = self.get_om() om = solph.constraints.additional_investment_flow_limit(om, "space", limit=20) self.compare_lp_files('generic_invest_limit.lp', my_om=om)
def source_dispatchable_fix(model, dict_asset, **kwargs): r""" Defines a dispatchable source with a fixed capacity. See :py:func:`~.source` for more information, including parameters. Notes ----- Tested with: - test_source_dispatchable_fix_normalized_timeseries() - test_source_dispatchable_fix_timeseries_not_normalized_timeseries() Returns ------- Indirectly updated `model` and dict of asset in `kwargs` with the source object. """ if TIMESERIES_NORMALIZED in dict_asset: outputs = { kwargs[OEMOF_BUSSES][dict_asset[OUTFLOW_DIRECTION]]: solph.Flow( label=dict_asset[LABEL], max=dict_asset[TIMESERIES_NORMALIZED], existing=dict_asset[INSTALLED_CAP][VALUE], variable_costs=dict_asset[DISPATCH_PRICE][VALUE], # add emission_factor for emission contraint emission_factor=dict_asset[EMISSION_FACTOR][VALUE], ) } source_dispatchable = solph.Source( label=dict_asset[LABEL], outputs=outputs, ) else: if TIMESERIES in dict_asset: logging.info( f"Asset {dict_asset[LABEL]} is introduced as a dispatchable source with an availability schedule." ) logging.debug( f"The availability schedule is solely introduced because the key {TIMESERIES_NORMALIZED} was not in the asset´s dictionary. \n" f"It should only be applied to DSO sources. " f"If the asset should not have this behaviour, please create an issue.", ) outputs = { kwargs[OEMOF_BUSSES][dict_asset[OUTFLOW_DIRECTION]]: solph.Flow( label=dict_asset[LABEL], existing=dict_asset[INSTALLED_CAP][VALUE], variable_costs=dict_asset[DISPATCH_PRICE][VALUE], ) } source_dispatchable = solph.Source( label=dict_asset[LABEL], outputs=outputs, ) model.add(source_dispatchable) kwargs[OEMOF_SOURCE].update({dict_asset[LABEL]: source_dispatchable}) logging.debug( f"Added: Dispatchable source {dict_asset[LABEL]} (fixed capacity) to bus {dict_asset[OUTFLOW_DIRECTION]}." )
def define_emission_limit(): bel = solph.Bus(label='electricityBus') solph.Source(label='source1', outputs={bel: solph.Flow( nominal_value=100, emission=0.8)}) solph.Source(label='source2', outputs={bel: solph.Flow( nominal_value=100)}) om = self.get_om() solph.constraints.emission_limit(om, om.flows, limit=777)
def test_flow_without_emission_for_emission_constraint_no_error(self): """ """ bel = solph.Bus(label='electricityBus') solph.Source(label='source1', outputs={bel: solph.Flow( nominal_value=100, emission=0.8)}) solph.Source(label='source2', outputs={bel: solph.Flow( nominal_value=100)}) om = self.get_om() solph.constraints.emission_limit(om, limit=777)
def must_run_source(param, data, busses): r""" Get must run source 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 ---- Must run source uses the following parameters: - 'active' is a binary parameter wether is used or not - 'type' defines wether it is used constant or time dependent - 'op_cost_var' are the variable operational costs in €/MWh - 'Q_N' is the constant nominal value in MWh - 'Q_MR' is the time series of nominal value in MWh Topology -------- Input: none Output: High temperature heat network (wnw) """ if param['MR']['active']: if param['MR']['type'] == 'constant': must_run_source = solph.Source( label='Mustrun', outputs={ busses['wnw']: solph.Flow(variable_costs=param['MR']['op_cost_var'], nominal_value=float(param['MR']['Q_N']), actual_value=1) }) return must_run_source elif param['MR']['type'] == 'time series': must_run_source = solph.Source( label='Mustrun', outputs={ busses['wnw']: solph.Flow(variable_costs=param['MR']['op_cost_var'], nominal_value=1, actual_value=data['Q_MR']) }) return must_run_source
def create_fuel_bus_with_source(nodes, fuel, region, input_data): """ Parameters ---------- nodes fuel region input_data Returns ------- """ fuel = fuel.replace("_", " ") cs_data = input_data["commodity sources"].loc[region, fuel] bus_label = Label("bus", "commodity", fuel, region) if bus_label not in nodes: nodes[bus_label] = solph.Bus(label=bus_label) cs_label = Label("source", "commodity", fuel, region) co2_price = float(input_data["general"]["co2 price"]) variable_costs = cs_data["emission"] / 1000 * co2_price + cs_data["costs"] if cs_data.get("annual limit", float("inf")) != float("inf"): if cs_label not in nodes: nodes[cs_label] = solph.Source( label=cs_label, outputs={ nodes[bus_label]: solph.Flow( variable_costs=variable_costs, emission=cs_data["emission"], nominal_value=cs_data["annual limit"], summed_max=1, ) }, ) else: if cs_label not in nodes: nodes[cs_label] = solph.Source( label=cs_label, outputs={ nodes[bus_label]: solph.Flow( variable_costs=variable_costs, emission=cs_data["emission"], ) }, )
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 source_non_dispatchable_fix(model, dict_asset, **kwargs): r""" Defines a non dispatchable source with a fixed capacity. See :py:func:`~.source` for more information, including parameters. Notes ----- Tested with: - test_source_non_dispatchable_fix() Returns ------- Indirectly updated `model` and dict of asset in `kwargs` with the source object. """ outputs = { kwargs[OEMOF_BUSSES][dict_asset[OUTFLOW_DIRECTION]]: solph.Flow( label=dict_asset[LABEL], fix=dict_asset[TIMESERIES], nominal_value=dict_asset[INSTALLED_CAP][VALUE], variable_costs=dict_asset[DISPATCH_PRICE][VALUE], emission_factor=dict_asset[EMISSION_FACTOR][VALUE], ) } source_non_dispatchable = solph.Source(label=dict_asset[LABEL], outputs=outputs) model.add(source_non_dispatchable) kwargs[OEMOF_SOURCE].update({dict_asset[LABEL]: source_non_dispatchable}) logging.debug( f"Added: Non-dispatchable source {dict_asset[LABEL]} (fixed capacity) to bus {dict_asset[OUTFLOW_DIRECTION]}.", )
def gas_source(param, busses): r""" Get gas source 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 ---- Gas source uses the following parameters: - 'gas_price' is the constant price of gas in €/MWh - 'co2_price' is the constant price of caused emissions in €/t_co2 - 'ef_gas' is the constant emission factor of gas in t_co2/MWh Topology -------- Input: none Output: Gas network (gnw) """ gas_source = solph.Source( label='Gasquelle', outputs={ busses['gnw']: solph.Flow(variable_costs=( param['param']['gas_price'] + (param['param']['co2_price'] * param['param']['ef_gas']))) }) return gas_source
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 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 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 add_sources(it, labels, gd, nodes, busd): for i, cs in it.iterrows(): labels['l_3'] = 'source' if cs['active']: labels['l_2'] = cs['label_2'] outflow_args = {} if cs['cost_series']: print('error: noch nicht angepasst!') else: outflow_args['variable_costs'] = cs['variable costs'] nodes.append( solph.Source( label=oh.Label(labels['l_1'], labels['l_2'], labels['l_3'], labels['l_4']), outputs={ busd[(labels['l_1'], cs['label_2'], 'bus', labels['l_4'])]: solph.Flow(**outflow_args) })) return nodes, busd
def test_nominal_value_to_zero(self): """If the nominal value is set to zero nothing should happen. """ bel = solph.Bus(label='electricityBus') solph.Source(label='s1', outputs={bel: solph.Flow(nominal_value=0)}) self.compare_lp_files('nominal_value_to_zero.lp')
def create_oemof_model(self, busses, _): from_grid = solph.Source(label=self.name, outputs={ busses[self.bus_out]: solph.Flow(nominal_value=self.power_max, variable_costs=self.current_ac) }) return from_grid
def test_special(): date_time_index = pd.date_range('1/1/2012', periods=5, freq='H') energysystem = solph.EnergySystem(timeindex=date_time_index) bel = solph.Bus(label='electricityBus') flow1 = solph.Flow(nominal_value=100, my_factor=0.8) flow2 = solph.Flow(nominal_value=50) src1 = solph.Source(label='source1', outputs={bel: flow1}) src2 = solph.Source(label='source2', outputs={bel: flow2}) energysystem.add(bel, src1, src2) model = solph.Model(energysystem) flow_with_keyword = { (src1, bel): flow1, } solph.constraints.generic_integral_limit(model, "my_factor", flow_with_keyword, limit=777)
def test_flow_count_limit(self): """ """ bel = solph.Bus(label='electricityBus') solph.Source(label='source1', outputs={ bel: solph.Flow(nonconvex=solph.NonConvex(), nominal_value=100, emission_factor=[0.5, -1.0, 2.0]) }) solph.Source(label='source2', outputs={ bel: solph.Flow(nonconvex=solph.NonConvex(), nominal_value=100, emission_factor=3.5) }) # Should be ignored because emission_factor is not defined. solph.Source(label='source3', outputs={ bel: solph.Flow(nonconvex=solph.NonConvex(), nominal_value=100) }) # Should be ignored because it is not NonConvex. solph.Source(label='source4', outputs={ bel: solph.Flow(emission_factor=1.5, min=0.3, nominal_value=100) }) om = self.get_om() # one of the two flows has to be active solph.constraints.limit_active_flow_count_by_keyword(om, "emission_factor", lower_limit=1, upper_limit=2) self.compare_lp_files('flow_count_limit.lp', my_om=om)
def test_that_the_solph_source_warnings_actually_get_raised(): """ Source doesn't warn about potentially erroneous usage. """ look_out = network.Bus() msg = "`Source` 'solph_sink' constructed without `outputs`." with warnings.catch_warnings(record=True) as w: solph.Source(label="solph_sink", inputs={look_out: "A typo!"}) ok_(len(w) == 1) eq_(msg, str(w[-1].message))
def test_emission_constraints(self): """ """ bel = solph.Bus(label='electricityBus') solph.Source(label='source1', outputs={bel: solph.Flow( nominal_value=100, emission=0.5)}) solph.Source(label='source2', outputs={bel: solph.Flow( nominal_value=100, emission=0.8)}) # Should be ignored because the emission attribute is not defined. solph.Source(label='source3', outputs={bel: solph.Flow( nominal_value=100)}) om = self.get_om() solph.constraints.emission_limit(om, limit=777) self.compare_lp_files('emission_limit.lp', my_om=om)
def create_oemof_model(self, busses, _): energy_source_from_csv = solph.Source( label=self.name, outputs={ busses[self.bus_out]: solph.Flow( actual_value=self.data.iloc[self.sim_params.i_interval], nominal_value=self.nominal_value, fixed=True) }) return energy_source_from_csv
def test_gradient(self): """ """ bel = solph.Bus(label='electricityBus') solph.Source(label='powerplant', outputs={bel: solph.Flow( nominal_value=999, variable_costs=23, positive_gradient={'ub': 0.03, 'costs': 7}, negative_gradient={'ub': 0.05, 'costs': 8})}) self.compare_lp_files('source_with_gradient.lp')
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 source_non_dispatchable_optimize(model, dict_asset, **kwargs): r""" Defines a non dispatchable source with a capacity to be optimized. See :py:func:`~.source` for more information, including parameters. Notes ----- Tested with: - test_source_non_dispatchable_optimize() Returns ------- Indirectly updated `model` and dict of asset in `kwargs` with the source object. """ if MAXIMUM_ADD_CAP_NORMALIZED in dict_asset: maximum = dict_asset[MAXIMUM_ADD_CAP_NORMALIZED][VALUE] else: maximum = dict_asset[MAXIMUM_ADD_CAP][VALUE] if INSTALLED_CAP_NORMALIZED in dict_asset: existing = dict_asset[INSTALLED_CAP_NORMALIZED][VALUE] else: existing = dict_asset[INSTALLED_CAP][VALUE] outputs = { kwargs[OEMOF_BUSSES][dict_asset[OUTFLOW_DIRECTION]]: solph.Flow( label=dict_asset[LABEL], fix=dict_asset[TIMESERIES_NORMALIZED], investment=solph.Investment( ep_costs=dict_asset[SIMULATION_ANNUITY][VALUE] / dict_asset[TIMESERIES_PEAK][VALUE], maximum=maximum, existing=existing, ), # variable_costs are devided by time series peak as normalized time series are used as actual_value variable_costs=dict_asset[DISPATCH_PRICE][VALUE] / dict_asset[TIMESERIES_PEAK][VALUE], # add emission_factor for emission contraint emission_factor=dict_asset[EMISSION_FACTOR][VALUE], ) } source_non_dispatchable = solph.Source(label=dict_asset[LABEL], outputs=outputs) model.add(source_non_dispatchable) kwargs[OEMOF_SOURCE].update({dict_asset[LABEL]: source_non_dispatchable}) logging.debug( f"Added: Non-dispatchable source {dict_asset[LABEL]} (capacity to be optimized) to bus {dict_asset[OUTFLOW_DIRECTION]}." )
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 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_maximum_shutdowns(self): """Testing maximum_shutdowns attribute for nonconvex flows.""" bus_t = solph.Bus(label='Bus_C') solph.Source(label='cheap_plant_maximum_shutdowns', outputs={ bus_t: solph.Flow( nominal_value=10, min=0.5, max=1.0, variable_costs=10, nonconvex=solph.NonConvex(maximum_shutdowns=2)) }) self.compare_lp_files('maximum_shutdowns.lp')
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