Source code for oemof.solph.blocks.flow

# -*- coding: utf-8 -*-

"""Creating sets, variables, constraints and parts of the objective function
for Flow objects.

SPDX-FileCopyrightText: Uwe Krien <krien@uni-bremen.de>
SPDX-FileCopyrightText: Simon Hilpert
SPDX-FileCopyrightText: Cord Kaldemeyer
SPDX-FileCopyrightText: Patrik Schönfeldt
SPDX-FileCopyrightText: Birgit Schachler
SPDX-FileCopyrightText: jnnr
SPDX-FileCopyrightText: jmloenneberga

SPDX-License-Identifier: MIT

"""

from pyomo.core import BuildAction
from pyomo.core import Constraint
from pyomo.core import NonNegativeIntegers
from pyomo.core import Set
from pyomo.core import Var
from pyomo.core.base.block import SimpleBlock


[docs]class Flow(SimpleBlock): r""" Flow block with definitions for standard flows. **The following variables are created**: negative_gradient : Difference of a flow in consecutive timesteps if flow is reduced indexed by NEGATIVE_GRADIENT_FLOWS, TIMESTEPS. positive_gradient : Difference of a flow in consecutive timesteps if flow is increased indexed by NEGATIVE_GRADIENT_FLOWS, TIMESTEPS. **The following sets are created:** (-> see basic sets at :class:`.Model` ) SUMMED_MAX_FLOWS A set of flows with the attribute :attr:`summed_max` being not None. SUMMED_MIN_FLOWS A set of flows with the attribute :attr:`summed_min` being not None. NEGATIVE_GRADIENT_FLOWS A set of flows with the attribute :attr:`negative_gradient` being not None. POSITIVE_GRADIENT_FLOWS A set of flows with the attribute :attr:`positive_gradient` being not None INTEGER_FLOWS A set of flows where the attribute :attr:`integer` is True (forces flow to only take integer values) **The following constraints are build:** Flow max sum :attr:`om.Flow.summed_max[i, o]` .. math:: \sum_t flow(i, o, t) \cdot \tau \leq summed\_max(i, o) \cdot nominal\_value(i, o), \\ \forall (i, o) \in \textrm{SUMMED\_MAX\_FLOWS}. Flow min sum :attr:`om.Flow.summed_min[i, o]` .. math:: \sum_t flow(i, o, t) \cdot \tau \geq summed\_min(i, o) \cdot nominal\_value(i, o), \\ \forall (i, o) \in \textrm{SUMMED\_MIN\_FLOWS}. Negative gradient constraint :attr:`om.Flow.negative_gradient_constr[i, o]`: .. math:: flow(i, o, t-1) - flow(i, o, t) \geq \ negative\_gradient(i, o, t), \\ \forall (i, o) \in \textrm{NEGATIVE\_GRADIENT\_FLOWS}, \\ \forall t \in \textrm{TIMESTEPS}. Positive gradient constraint :attr:`om.Flow.positive_gradient_constr[i, o]`: .. math:: flow(i, o, t) - flow(i, o, t-1) \geq \ positive\__gradient(i, o, t), \\ \forall (i, o) \in \textrm{POSITIVE\_GRADIENT\_FLOWS}, \\ \forall t \in \textrm{TIMESTEPS}. **The following parts of the objective function are created:** If :attr:`variable_costs` are set by the user: .. math:: \sum_{(i,o)} \sum_t flow(i, o, t) \cdot variable\_costs(i, o, t) The expression can be accessed by :attr:`om.Flow.variable_costs` and their value after optimization by :meth:`om.Flow.variable_costs()` . """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def _create(self, group=None): r"""Creates sets, variables and constraints for all standard flows. Parameters ---------- group : list List containing tuples containing flow (f) objects and the associated source (s) and target (t) of flow e.g. groups=[(s1, t1, f1), (s2, t2, f2),..] """ if group is None: return None m = self.parent_block() # ########################## SETS ################################# # set for all flows with an global limit on the flow over time self.SUMMED_MAX_FLOWS = Set( initialize=[ (g[0], g[1]) for g in group if g[2].summed_max is not None and g[2].nominal_value is not None ] ) self.SUMMED_MIN_FLOWS = Set( initialize=[ (g[0], g[1]) for g in group if g[2].summed_min is not None and g[2].nominal_value is not None ] ) self.NEGATIVE_GRADIENT_FLOWS = Set( initialize=[ (g[0], g[1]) for g in group if g[2].negative_gradient["ub"][0] is not None ] ) self.POSITIVE_GRADIENT_FLOWS = Set( initialize=[ (g[0], g[1]) for g in group if g[2].positive_gradient["ub"][0] is not None ] ) self.INTEGER_FLOWS = Set( initialize=[(g[0], g[1]) for g in group if g[2].integer] ) # ######################### Variables ################################ self.positive_gradient = Var(self.POSITIVE_GRADIENT_FLOWS, m.TIMESTEPS) self.negative_gradient = Var(self.NEGATIVE_GRADIENT_FLOWS, m.TIMESTEPS) self.integer_flow = Var( self.INTEGER_FLOWS, m.TIMESTEPS, within=NonNegativeIntegers ) # set upper bound of gradient variable for i, o, f in group: if m.flows[i, o].positive_gradient["ub"][0] is not None: for t in m.TIMESTEPS: self.positive_gradient[i, o, t].setub( f.positive_gradient["ub"][t] * f.nominal_value ) if m.flows[i, o].negative_gradient["ub"][0] is not None: for t in m.TIMESTEPS: self.negative_gradient[i, o, t].setub( f.negative_gradient["ub"][t] * f.nominal_value ) # ######################### CONSTRAINTS ############################### def _flow_summed_max_rule(model): """Rule definition for build action of max. sum flow constraint.""" for inp, out in self.SUMMED_MAX_FLOWS: lhs = sum( m.flow[inp, out, ts] * m.timeincrement[ts] for ts in m.TIMESTEPS ) rhs = ( m.flows[inp, out].summed_max * m.flows[inp, out].nominal_value ) self.summed_max.add((inp, out), lhs <= rhs) self.summed_max = Constraint(self.SUMMED_MAX_FLOWS, noruleinit=True) self.summed_max_build = BuildAction(rule=_flow_summed_max_rule) def _flow_summed_min_rule(model): """Rule definition for build action of min. sum flow constraint.""" for inp, out in self.SUMMED_MIN_FLOWS: lhs = sum( m.flow[inp, out, ts] * m.timeincrement[ts] for ts in m.TIMESTEPS ) rhs = ( m.flows[inp, out].summed_min * m.flows[inp, out].nominal_value ) self.summed_min.add((inp, out), lhs >= rhs) self.summed_min = Constraint(self.SUMMED_MIN_FLOWS, noruleinit=True) self.summed_min_build = BuildAction(rule=_flow_summed_min_rule) def _positive_gradient_flow_rule(model): """Rule definition for positive gradient constraint.""" for inp, out in self.POSITIVE_GRADIENT_FLOWS: for ts in m.TIMESTEPS: if ts > 0: lhs = m.flow[inp, out, ts] - m.flow[inp, out, ts - 1] rhs = self.positive_gradient[inp, out, ts] self.positive_gradient_constr.add( (inp, out, ts), lhs <= rhs ) self.positive_gradient_constr = Constraint( self.POSITIVE_GRADIENT_FLOWS, m.TIMESTEPS, noruleinit=True ) self.positive_gradient_build = BuildAction( rule=_positive_gradient_flow_rule ) def _negative_gradient_flow_rule(model): """Rule definition for negative gradient constraint.""" for inp, out in self.NEGATIVE_GRADIENT_FLOWS: for ts in m.TIMESTEPS: if ts > 0: lhs = m.flow[inp, out, ts - 1] - m.flow[inp, out, ts] rhs = self.negative_gradient[inp, out, ts] self.negative_gradient_constr.add( (inp, out, ts), lhs <= rhs ) self.negative_gradient_constr = Constraint( self.NEGATIVE_GRADIENT_FLOWS, m.TIMESTEPS, noruleinit=True ) self.negative_gradient_build = BuildAction( rule=_negative_gradient_flow_rule ) def _integer_flow_rule(block, ii, oi, ti): """Force flow variable to NonNegativeInteger values.""" return self.integer_flow[ii, oi, ti] == m.flow[ii, oi, ti] self.integer_flow_constr = Constraint( self.INTEGER_FLOWS, m.TIMESTEPS, rule=_integer_flow_rule ) def _objective_expression(self): r"""Objective expression for all standard flows with fixed costs and variable costs. """ m = self.parent_block() variable_costs = 0 for i, o in m.FLOWS: if m.flows[i, o].variable_costs[0] is not None: for t in m.TIMESTEPS: variable_costs += ( m.flow[i, o, t] * m.objective_weighting[t] * m.flows[i, o].variable_costs[t] ) return variable_costs