Source code for oemof.solph.constraints.storage_level

"""A constraint to allow flows from to a storage based on the storage level.

SPDX-FileCopyrightText: Patrik Schönfeldt
SPDX-FileCopyrightText: Johannes Kochems
SPDX-FileCopyrightText: Deutsches Zentrum für Luft- und Raumfahrt (DLR)

SPDX-License-Identifier: MIT
"""

from pyomo import environ as po


[docs]def storage_level_constraint( model, name, storage_component, multiplexer_bus, input_levels, output_levels, ): r""" Add constraints to implement storage content based access. As a GenericStorage just allows exactly one input and one output, an additional bus, the multiplexer_bus, is used for the connections. Note that all Flow objects connected to the multiplexer_bus have to have a nominal_value. Parameters ---------- model : oemof.solph.Model Model to which the constraint is added. name : string Name of the multiplexer. storage_component : oemof.solph.components.GenericStorage Storage component whose content should mandate the possible inputs and outputs. multiplexer_bus : oemof.solph.Bus Bus which connects the input and output levels to the storage. input_levels : dictionary with oemof.solph.Bus as keys and float as values Dictionary of buses which act as inputs and corresponding levels output_levels : dictionary with oemof.solph.Bus as keys and float as values Dictionary of buses which act as outputs and corresponding level Verbose description can be found in https://arxiv.org/abs/2211.14080 """ def _outputs(): OUTPUTS = po.Set(initialize=output_levels.keys()) setattr(model, f"{name}_OUTPUTS", OUTPUTS) active_output = po.Var( OUTPUTS, model.TIMESTEPS, domain=po.Binary, bounds=(0, 1) ) setattr(model, f"{name}_active_output", active_output) constraint_name = f"{name}_output_active_constraint" def _output_active_rule(m): r""" .. math:: y_n \le E(t) / E_n """ for t in m.TIMESTEPS: for o in output_levels: getattr(m, constraint_name).add( (o, t), m.GenericStorageBlock.storage_content[ storage_component, t + 1 ] / storage_component.nominal_storage_capacity >= active_output[o, t] * output_levels[o], ) setattr( model, constraint_name, po.Constraint( OUTPUTS, model.TIMESTEPS, noruleinit=True, ), ) setattr( model, constraint_name + "build", po.BuildAction(rule=_output_active_rule), ) # Define constraints on the output flows def _constraint_output_rule(m, o, p, t): return ( m.flow[multiplexer_bus, o, p, t] / m.flows[multiplexer_bus, o].nominal_value <= active_output[o, t] ) setattr( model, f"{name}_output_constraint", po.Constraint( OUTPUTS, model.TIMEINDEX, rule=_constraint_output_rule, ), ) _outputs() def _inputs(): INPUTS = po.Set(initialize=input_levels.keys()) setattr(model, f"{name}_INPUTS", INPUTS) inactive_input = po.Var( INPUTS, model.TIMESTEPS, domain=po.Binary, bounds=(0, 1) ) setattr(model, f"{name}_active_input", inactive_input) constraint_name = f"{name}_input_active_constraint" def _input_active_rule(m): r""" .. math:: \hat{y}_n \ge (E(t) - E_n) / E_{max} """ for t in m.TIMESTEPS: for i in input_levels: getattr(m, constraint_name).add( (i, t), ( m.GenericStorageBlock.storage_content[ storage_component, t ] / storage_component.nominal_storage_capacity - input_levels[i] ) <= inactive_input[i, t], ) setattr( model, constraint_name, po.Constraint( INPUTS, model.TIMESTEPS, noruleinit=True, ), ) setattr( model, constraint_name + "build", po.BuildAction(rule=_input_active_rule), ) # Define constraints on the input flows def _constraint_input_rule(m, i, p, t): return ( m.flow[i, multiplexer_bus, p, t] / m.flows[i, multiplexer_bus].nominal_value <= 1 - inactive_input[i, t] ) setattr( model, f"{name}_input_constraint", po.Constraint( INPUTS, model.TIMEINDEX, rule=_constraint_input_rule, ), ) _inputs()