Passed
Push — dev ( 8c4313...155e10 )
by Uwe
01:40 queued 11s
created

solph.blocks.flow   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 270
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 20
eloc 113
dl 0
loc 270
rs 10
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
A Flow.__init__() 0 2 1
F Flow._create() 0 152 15
A Flow._objective_expression() 0 18 4
1
# -*- coding: utf-8 -*-
2
3
"""Creating sets, variables, constraints and parts of the objective function
4
for Flow objects.
5
6
SPDX-FileCopyrightText: Uwe Krien <[email protected]>
7
SPDX-FileCopyrightText: Simon Hilpert
8
SPDX-FileCopyrightText: Cord Kaldemeyer
9
SPDX-FileCopyrightText: Patrik Schönfeldt
10
SPDX-FileCopyrightText: Birgit Schachler
11
SPDX-FileCopyrightText: jnnr
12
SPDX-FileCopyrightText: jmloenneberga
13
14
SPDX-License-Identifier: MIT
15
16
"""
17
18
from pyomo.core import BuildAction
19
from pyomo.core import Constraint
20
from pyomo.core import NonNegativeIntegers
21
from pyomo.core import Set
22
from pyomo.core import Var
23
from pyomo.core.base.block import SimpleBlock
24
25
26
class Flow(SimpleBlock):
27
    r""" Flow block with definitions for standard flows.
28
29
    **The following variables are created**:
30
31
    negative_gradient :
32
        Difference of a flow in consecutive timesteps if flow is reduced
33
        indexed by NEGATIVE_GRADIENT_FLOWS, TIMESTEPS.
34
35
    positive_gradient :
36
        Difference of a flow in consecutive timesteps if flow is increased
37
        indexed by NEGATIVE_GRADIENT_FLOWS, TIMESTEPS.
38
39
    **The following sets are created:** (-> see basic sets at :class:`.Model` )
40
41
    SUMMED_MAX_FLOWS
42
        A set of flows with the attribute :attr:`summed_max` being not None.
43
    SUMMED_MIN_FLOWS
44
        A set of flows with the attribute :attr:`summed_min` being not None.
45
    NEGATIVE_GRADIENT_FLOWS
46
        A set of flows with the attribute :attr:`negative_gradient` being not
47
        None.
48
    POSITIVE_GRADIENT_FLOWS
49
        A set of flows with the attribute :attr:`positive_gradient` being not
50
        None
51
    INTEGER_FLOWS
52
        A set of flows where the attribute :attr:`integer` is True (forces flow
53
        to only take integer values)
54
55
    **The following constraints are build:**
56
57
    Flow max sum :attr:`om.Flow.summed_max[i, o]`
58
      .. math::
59
        \sum_t flow(i, o, t) \cdot \tau
60
            \leq summed\_max(i, o) \cdot nominal\_value(i, o), \\
61
        \forall (i, o) \in \textrm{SUMMED\_MAX\_FLOWS}.
62
63
    Flow min sum :attr:`om.Flow.summed_min[i, o]`
64
      .. math::
65
        \sum_t flow(i, o, t) \cdot \tau
66
            \geq summed\_min(i, o) \cdot nominal\_value(i, o), \\
67
        \forall (i, o) \in \textrm{SUMMED\_MIN\_FLOWS}.
68
69
    Negative gradient constraint
70
      :attr:`om.Flow.negative_gradient_constr[i, o]`:
71
        .. math::
72
          flow(i, o, t-1) - flow(i, o, t) \geq \
73
          negative\_gradient(i, o, t), \\
74
          \forall (i, o) \in \textrm{NEGATIVE\_GRADIENT\_FLOWS}, \\
75
          \forall t \in \textrm{TIMESTEPS}.
76
77
    Positive gradient constraint
78
      :attr:`om.Flow.positive_gradient_constr[i, o]`:
79
        .. math:: flow(i, o, t) - flow(i, o, t-1) \geq \
80
          positive\__gradient(i, o, t), \\
81
          \forall (i, o) \in \textrm{POSITIVE\_GRADIENT\_FLOWS}, \\
82
          \forall t \in \textrm{TIMESTEPS}.
83
84
    **The following parts of the objective function are created:**
85
86
    If :attr:`variable_costs` are set by the user:
87
      .. math::
88
          \sum_{(i,o)} \sum_t flow(i, o, t) \cdot variable\_costs(i, o, t)
89
90
    The expression can be accessed by :attr:`om.Flow.variable_costs` and
91
    their value after optimization by :meth:`om.Flow.variable_costs()` .
92
93
    """
94
95
    def __init__(self, *args, **kwargs):
96
        super().__init__(*args, **kwargs)
97
98
    def _create(self, group=None):
99
        r"""Creates sets, variables and constraints for all standard flows.
100
101
        Parameters
102
        ----------
103
        group : list
104
            List containing tuples containing flow (f) objects and the
105
            associated source (s) and target (t)
106
            of flow e.g. groups=[(s1, t1, f1), (s2, t2, f2),..]
107
        """
108
        if group is None:
109
            return None
110
111
        m = self.parent_block()
112
113
        # ########################## SETS #################################
114
        # set for all flows with an global limit on the flow over time
115
        self.SUMMED_MAX_FLOWS = Set(
116
            initialize=[
117
                (g[0], g[1])
118
                for g in group
119
                if g[2].summed_max is not None
120
                and g[2].nominal_value is not None
121
            ]
122
        )
123
124
        self.SUMMED_MIN_FLOWS = Set(
125
            initialize=[
126
                (g[0], g[1])
127
                for g in group
128
                if g[2].summed_min is not None
129
                and g[2].nominal_value is not None
130
            ]
131
        )
132
133
        self.NEGATIVE_GRADIENT_FLOWS = Set(
134
            initialize=[
135
                (g[0], g[1])
136
                for g in group
137
                if g[2].negative_gradient["ub"][0] is not None
138
            ]
139
        )
140
141
        self.POSITIVE_GRADIENT_FLOWS = Set(
142
            initialize=[
143
                (g[0], g[1])
144
                for g in group
145
                if g[2].positive_gradient["ub"][0] is not None
146
            ]
147
        )
148
149
        self.INTEGER_FLOWS = Set(
150
            initialize=[(g[0], g[1]) for g in group if g[2].integer]
151
        )
152
        # ######################### Variables  ################################
153
154
        self.positive_gradient = Var(self.POSITIVE_GRADIENT_FLOWS, m.TIMESTEPS)
155
156
        self.negative_gradient = Var(self.NEGATIVE_GRADIENT_FLOWS, m.TIMESTEPS)
157
158
        self.integer_flow = Var(
159
            self.INTEGER_FLOWS, m.TIMESTEPS, within=NonNegativeIntegers
160
        )
161
        # set upper bound of gradient variable
162
        for i, o, f in group:
163
            if m.flows[i, o].positive_gradient["ub"][0] is not None:
164
                for t in m.TIMESTEPS:
165
                    self.positive_gradient[i, o, t].setub(
166
                        f.positive_gradient["ub"][t] * f.nominal_value
167
                    )
168
            if m.flows[i, o].negative_gradient["ub"][0] is not None:
169
                for t in m.TIMESTEPS:
170
                    self.negative_gradient[i, o, t].setub(
171
                        f.negative_gradient["ub"][t] * f.nominal_value
172
                    )
173
174
        # ######################### CONSTRAINTS ###############################
175
176
        def _flow_summed_max_rule(model):
177
            """Rule definition for build action of max. sum flow constraint."""
178
            for inp, out in self.SUMMED_MAX_FLOWS:
179
                lhs = sum(
180
                    m.flow[inp, out, ts] * m.timeincrement[ts]
0 ignored issues
show
introduced by
The variable m does not seem to be defined for all execution paths.
Loading history...
181
                    for ts in m.TIMESTEPS
182
                )
183
                rhs = (
184
                    m.flows[inp, out].summed_max
185
                    * m.flows[inp, out].nominal_value
186
                )
187
                self.summed_max.add((inp, out), lhs <= rhs)
188
189
        self.summed_max = Constraint(self.SUMMED_MAX_FLOWS, noruleinit=True)
190
        self.summed_max_build = BuildAction(rule=_flow_summed_max_rule)
191
192
        def _flow_summed_min_rule(model):
193
            """Rule definition for build action of min. sum flow constraint."""
194
            for inp, out in self.SUMMED_MIN_FLOWS:
195
                lhs = sum(
196
                    m.flow[inp, out, ts] * m.timeincrement[ts]
0 ignored issues
show
introduced by
The variable m does not seem to be defined for all execution paths.
Loading history...
197
                    for ts in m.TIMESTEPS
198
                )
199
                rhs = (
200
                    m.flows[inp, out].summed_min
201
                    * m.flows[inp, out].nominal_value
202
                )
203
                self.summed_min.add((inp, out), lhs >= rhs)
204
205
        self.summed_min = Constraint(self.SUMMED_MIN_FLOWS, noruleinit=True)
206
        self.summed_min_build = BuildAction(rule=_flow_summed_min_rule)
207
208
        def _positive_gradient_flow_rule(model):
209
            """Rule definition for positive gradient constraint."""
210
            for inp, out in self.POSITIVE_GRADIENT_FLOWS:
211
                for ts in m.TIMESTEPS:
0 ignored issues
show
introduced by
The variable m does not seem to be defined for all execution paths.
Loading history...
212
                    if ts > 0:
213
                        lhs = m.flow[inp, out, ts] - m.flow[inp, out, ts - 1]
214
                        rhs = self.positive_gradient[inp, out, ts]
215
                        self.positive_gradient_constr.add(
216
                            (inp, out, ts), lhs <= rhs
217
                        )
218
219
        self.positive_gradient_constr = Constraint(
220
            self.POSITIVE_GRADIENT_FLOWS, m.TIMESTEPS, noruleinit=True
221
        )
222
        self.positive_gradient_build = BuildAction(
223
            rule=_positive_gradient_flow_rule
224
        )
225
226
        def _negative_gradient_flow_rule(model):
227
            """Rule definition for negative gradient constraint."""
228
            for inp, out in self.NEGATIVE_GRADIENT_FLOWS:
229
                for ts in m.TIMESTEPS:
0 ignored issues
show
introduced by
The variable m does not seem to be defined for all execution paths.
Loading history...
230
                    if ts > 0:
231
                        lhs = m.flow[inp, out, ts - 1] - m.flow[inp, out, ts]
232
                        rhs = self.negative_gradient[inp, out, ts]
233
                        self.negative_gradient_constr.add(
234
                            (inp, out, ts), lhs <= rhs
235
                        )
236
237
        self.negative_gradient_constr = Constraint(
238
            self.NEGATIVE_GRADIENT_FLOWS, m.TIMESTEPS, noruleinit=True
239
        )
240
        self.negative_gradient_build = BuildAction(
241
            rule=_negative_gradient_flow_rule
242
        )
243
244
        def _integer_flow_rule(block, ii, oi, ti):
245
            """Force flow variable to NonNegativeInteger values."""
246
            return self.integer_flow[ii, oi, ti] == m.flow[ii, oi, ti]
0 ignored issues
show
introduced by
The variable m does not seem to be defined for all execution paths.
Loading history...
247
248
        self.integer_flow_constr = Constraint(
249
            self.INTEGER_FLOWS, m.TIMESTEPS, rule=_integer_flow_rule
250
        )
251
252
    def _objective_expression(self):
253
        r"""Objective expression for all standard flows with fixed costs
254
        and variable costs.
255
        """
256
        m = self.parent_block()
257
258
        variable_costs = 0
259
260
        for i, o in m.FLOWS:
261
            if m.flows[i, o].variable_costs[0] is not None:
262
                for t in m.TIMESTEPS:
263
                    variable_costs += (
264
                        m.flow[i, o, t]
265
                        * m.objective_weighting[t]
266
                        * m.flows[i, o].variable_costs[t]
267
                    )
268
269
        return variable_costs
270