Passed
Pull Request — dev (#836)
by Uwe
03:17 queued 01:53
created

solph.flows._non_convex_flow   A

Complexity

Total Complexity 34

Size/Duplication

Total Lines 684
Duplicated Lines 10.96 %

Importance

Changes 0
Metric Value
wmc 34
eloc 285
dl 75
loc 684
rs 9.68
c 0
b 0
f 0

4 Methods

Rating   Name   Duplication   Size   Complexity  
F NonConvexFlowBlock._create() 75 302 16
A NonConvexFlow.__init__() 0 34 3
A NonConvexFlowBlock.__init__() 0 2 1
F NonConvexFlowBlock._objective_expression() 0 61 14

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
# -*- coding: utf-8 -*-
2
3
"""Creating sets, variables, constraints and parts of the objective function
4
for nonconvex FlowBlock 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
SPDX-FileCopyrightText: Johannes Kochems (jokochems)
14
15
SPDX-License-Identifier: MIT
16
17
"""
18
19
from pyomo.core import Binary
20
from pyomo.core import BuildAction
21
from pyomo.core import Constraint
22
from pyomo.core import Expression
23
from pyomo.core import Set
24
from pyomo.core import Var
25
from pyomo.core.base.block import ScalarBlock
26
27
from oemof.solph._options import NonConvex
28
29
from ._flow import Flow
30
31
32
class NonConvexFlow(Flow):
33
    r"""
34
    Flow with a binary variable that states whether it is active or not.
35
36
    Parameters
37
    ----------
38
    startup_costs : numeric (iterable or scalar)
39
        Costs associated with a start of the flow (representing a unit).
40
    shutdown_costs : numeric (iterable or scalar)
41
        Costs associated with the shutdown of the flow (representing a unit).
42
    activity_costs : numeric (iterable or scalar)
43
        Costs associated with the active operation of the flow, independently
44
        from the actual output.
45
    minimum_uptime : numeric (1 or positive integer)
46
        Minimum time that a flow must be greater then its minimum flow after
47
        startup. Be aware that minimum up and downtimes can contradict each
48
        other and may lead to infeasible problems.
49
    minimum_downtime : numeric (1 or positive integer)
50
        Minimum time a flow is forced to zero after shutting down.
51
        Be aware that minimum up and downtimes can contradict each
52
        other and may to infeasible problems.
53
    maximum_startups : numeric (0 or positive integer)
54
        Maximum number of start-ups.
55
    maximum_shutdowns : numeric (0 or positive integer)
56
        Maximum number of shutdowns.
57
    initial_status : numeric (0 or 1)
58
        Integer value indicating the status of the flow in the first time step
59
        (0 = off, 1 = on). For minimum up and downtimes, the initial status
60
        is set for the respective values in the edge regions e.g. if a
61
        minimum uptime of four timesteps is defined, the initial status is
62
        fixed for the four first and last timesteps of the optimization period.
63
        If both, up and downtimes are defined, the initial status is set for
64
        the maximum of both e.g. for six timesteps if a minimum downtime of
65
        six timesteps is defined in addition to a four timestep minimum uptime.
66
    positive_gradient : :obj:`dict`, default: `{'ub': None, 'costs': 0}`
67
        A dictionary containing the following two keys:
68
69
         * `'ub'`: numeric (iterable, scalar or None), the normed *upper
70
           bound* on the positive difference (`flow[t-1] < flow[t]`) of
71
           two consecutive flow values.
72
         * `'costs``: numeric (scalar or None), the gradient cost per
73
           unit.
74
75
    negative_gradient : :obj:`dict`, default: `{'ub': None, 'costs': 0}`
76
        A dictionary containing the following two keys:
77
78
          * `'ub'`: numeric (iterable, scalar or None), the normed *upper
79
            bound* on the negative difference (`flow[t-1] > flow[t]`) of
80
            two consecutive flow values.
81
          * `'costs``: numeric (scalar or None), the gradient cost per
82
            unit.
83
    """
84
85
    def __init__(
86
        self,
87
        startup_costs=None,
88
        shutdown_costs=None,
89
        activity_costs=None,
90
        inactivity_costs=None,
91
        minimum_uptime=None,
92
        minimum_downtime=None,
93
        maximum_startups=None,
94
        maximum_shutdowns=None,
95
        initial_status=0,
96
        positive_gradient=None,
97
        negative_gradient=None,
98
        **kwargs,
99
    ):
100
        default_gradient = {"ub": None, "costs": 0}
101
        if positive_gradient is None:
102
            positive_gradient = default_gradient
103
        if negative_gradient is None:
104
            negative_gradient = default_gradient
105
        nonconvex = NonConvex(
106
            startup_costs=startup_costs,
107
            shutdown_costs=shutdown_costs,
108
            activity_costs=activity_costs,
109
            inactivity_costs=inactivity_costs,
110
            minimum_uptime=minimum_uptime,
111
            minimum_downtime=minimum_downtime,
112
            maximum_startups=maximum_startups,
113
            maximum_shutdowns=maximum_shutdowns,
114
            initial_status=initial_status,
115
            positive_gradient=positive_gradient,
116
            negative_gradient=negative_gradient,
117
        )
118
        super().__init__(nonconvex=nonconvex, **kwargs)
119
120
121
class NonConvexFlowBlock(ScalarBlock):
122
    r"""
123
    **The following sets are created:** (-> see basic sets at
124
    :class:`.Model` )
125
126
    A set of flows with the attribute `nonconvex` of type
127
        :class:`.options.NonConvex`.
128
    MIN_FLOWS
129
        A subset of set NONCONVEX_FLOWS with the attribute `min`
130
        being not None in the first timestep.
131
    ACTIVITYCOSTFLOWS
132
        A subset of set NONCONVEX_FLOWS with the attribute
133
        `activity_costs` being not None.
134
    INACTIVITYCOSTFLOWS
135
        A subset of set NONCONVEX_FLOWS with the attribute
136
        `inactivity_costs` being not None.
137
    STARTUPFLOWS
138
        A subset of set NONCONVEX_FLOWS with the attribute
139
        `maximum_startups` or `startup_costs`
140
        being not None.
141
    MAXSTARTUPFLOWS
142
        A subset of set STARTUPFLOWS with the attribute
143
        `maximum_startups` being not None.
144
    SHUTDOWNFLOWS
145
        A subset of set NONCONVEX_FLOWS with the attribute
146
        `maximum_shutdowns` or `shutdown_costs`
147
        being not None.
148
    MAXSHUTDOWNFLOWS
149
        A subset of set SHUTDOWNFLOWS with the attribute
150
        `maximum_shutdowns` being not None.
151
    MINUPTIMEFLOWS
152
        A subset of set NONCONVEX_FLOWS with the attribute
153
        `minimum_uptime` being not None.
154
    MINDOWNTIMEFLOWS
155
        A subset of set NONCONVEX_FLOWS with the attribute
156
        `minimum_downtime` being not None.
157
    POSITIVE_GRADIENT_FLOWS
158
        A subset of set NONCONVEX_FLOWS with the attribute
159
        `positive_gradient` being not None.
160
    NEGATIVE_GRADIENT_FLOWS
161
        A subset of set NONCONVEX_FLOWS with the attribute
162
        `negative_gradient` being not None.
163
164
    **The following variables are created:**
165
166
    Status variable (binary) `om.NonConvexFlowBlock.status`:
167
        Variable indicating if flow is >= 0 indexed by FLOWS
168
169
    Startup variable (binary) `om.NonConvexFlowBlock.startup`:
170
        Variable indicating startup of flow (component) indexed by
171
        STARTUPFLOWS
172
173
    Shutdown variable (binary) `om.NonConvexFlowBlock.shutdown`:
174
        Variable indicating shutdown of flow (component) indexed by
175
        SHUTDOWNFLOWS
176
177
    Positive gradient (continuous) `om.NonConvexFlowBlock.positive_gradient`:
178
        Variable indicating the positive gradient, i.e. the load increase
179
        between two consecutive timesteps, indexed by
180
        POSITIVE_GRADIENT_FLOWS
181
182
    Negative gradient (continuous) `om.NonConvexFlowBlock.negative_gradient`:
183
        Variable indicating the negative gradient, i.e. the load decrease
184
        between two consecutive timesteps, indexed by
185
        NEGATIVE_GRADIENT_FLOWS
186
187
188
    **The following constraints are created:**
189
190
    Minimum flow constraint `om.NonConvexFlowBlock.min[i,o,t]`
191
        .. math::
192
            flow(i, o, t) \geq min(i, o, t) \cdot nominal\_value \
193
                \cdot status(i, o, t), \\
194
            \forall t \in \textrm{TIMESTEPS}, \\
195
            \forall (i, o) \in \textrm{NONCONVEX\_FLOWS}.
196
197
    Maximum flow constraint `om.NonConvexFlowBlock.max[i,o,t]`
198
        .. math::
199
            flow(i, o, t) \leq max(i, o, t) \cdot nominal\_value \
200
                \cdot status(i, o, t), \\
201
            \forall t \in \textrm{TIMESTEPS}, \\
202
            \forall (i, o) \in \textrm{NONCONVEX\_FLOWS}.
203
204
    Startup constraint `om.NonConvexFlowBlock.startup_constr[i,o,t]`
205
        .. math::
206
            startup(i, o, t) \geq \
207
                status(i,o,t) - status(i, o, t-1) \\
208
            \forall t \in \textrm{TIMESTEPS}, \\
209
            \forall (i,o) \in \textrm{STARTUPFLOWS}.
210
211
    Maximum startups constraint
212
      `om.NonConvexFlowBlock.max_startup_constr[i,o,t]`
213
        .. math::
214
            \sum_{t \in \textrm{TIMESTEPS}} startup(i, o, t) \leq \
215
                N_{start}(i,o)
216
            \forall (i,o) \in \textrm{MAXSTARTUPFLOWS}.
217
218
    Shutdown constraint `om.NonConvexFlowBlock.shutdown_constr[i,o,t]`
219
        .. math::
220
            shutdown(i, o, t) \geq \
221
                status(i, o, t-1) - status(i, o, t) \\
222
            \forall t \in \textrm{TIMESTEPS}, \\
223
            \forall (i, o) \in \textrm{SHUTDOWNFLOWS}.
224
225
    Maximum shutdowns constraint
226
      `om.NonConvexFlowBlock.max_startup_constr[i,o,t]`
227
        .. math::
228
            \sum_{t \in \textrm{TIMESTEPS}} startup(i, o, t) \leq \
229
                N_{shutdown}(i,o)
230
            \forall (i,o) \in \textrm{MAXSHUTDOWNFLOWS}.
231
232
    Minimum uptime constraint `om.NonConvexFlowBlock.uptime_constr[i,o,t]`
233
        .. math::
234
            (status(i, o, t)-status(i, o, t-1)) \cdot minimum\_uptime(i, o) \\
235
            \leq \sum_{n=0}^{minimum\_uptime-1} status(i,o,t+n) \\
236
            \forall t \in \textrm{TIMESTEPS} | \\
237
            t \neq \{0..minimum\_uptime\} \cup \
238
            \{t\_max-minimum\_uptime..t\_max\} , \\
239
            \forall (i,o) \in \textrm{MINUPTIMEFLOWS}.
240
            \\ \\
241
            status(i, o, t) = initial\_status(i, o) \\
242
            \forall t \in \textrm{TIMESTEPS} | \\
243
            t = \{0..minimum\_uptime\} \cup \
244
            \{t\_max-minimum\_uptime..t\_max\} , \\
245
            \forall (i,o) \in \textrm{MINUPTIMEFLOWS}.
246
247
    Minimum downtime constraint `om.NonConvexFlowBlock.downtime_constr[i,o,t]`
248
        .. math::
249
            (status(i, o, t-1)-status(i, o, t)) \
250
            \cdot minimum\_downtime(i, o) \\
251
            \leq minimum\_downtime(i, o) \
252
            - \sum_{n=0}^{minimum\_downtime-1} status(i,o,t+n) \\
253
            \forall t \in \textrm{TIMESTEPS} | \\
254
            t \neq \{0..minimum\_downtime\} \cup \
255
            \{t\_max-minimum\_downtime..t\_max\} , \\
256
            \forall (i,o) \in \textrm{MINDOWNTIMEFLOWS}.
257
            \\ \\
258
            status(i, o, t) = initial\_status(i, o) \\
259
            \forall t \in \textrm{TIMESTEPS} | \\
260
            t = \{0..minimum\_downtime\} \cup \
261
            \{t\_max-minimum\_downtime..t\_max\} , \\
262
            \forall (i,o) \in \textrm{MINDOWNTIMEFLOWS}.
263
264
    Positive gradient constraint
265
      `om.NonConvexFlowBlock.positive_gradient_constr[i, o]`:
266
        .. math:: flow(i, o, t) \cdot status(i, o, t)
267
        - flow(i, o, t-1) \cdot status(i, o, t-1)  \geq \
268
          positive\_gradient(i, o, t), \\
269
          \forall (i, o) \in \textrm{POSITIVE\_GRADIENT\_FLOWS}, \\
270
          \forall t \in \textrm{TIMESTEPS}.
271
272
    Negative gradient constraint
273
      `om.NonConvexFlowBlock.negative_gradient_constr[i, o]`:
274
        .. math::
275
          flow(i, o, t-1) \cdot status(i, o, t-1)
276
          - flow(i, o, t) \cdot status(i, o, t) \geq \
277
          negative\_gradient(i, o, t), \\
278
          \forall (i, o) \in \textrm{NEGATIVE\_GRADIENT\_FLOWS}, \\
279
          \forall t \in \textrm{TIMESTEPS}.
280
281
282
    **The following parts of the objective function are created:**
283
284
    If `nonconvex.startup_costs` is set by the user:
285
        .. math::
286
            \sum_{i, o \in STARTUPFLOWS} \sum_t  startup(i, o, t) \
287
            \cdot startup\_costs(i, o)
288
289
    If `nonconvex.shutdown_costs` is set by the user:
290
        .. math::
291
            \sum_{i, o \in SHUTDOWNFLOWS} \sum_t shutdown(i, o, t) \
292
            \cdot shutdown\_costs(i, o)
293
294
    If `nonconvex.activity_costs` is set by the user:
295
        .. math::
296
            \sum_{i, o \in ACTIVITYCOSTFLOWS} \sum_t status(i, o, t) \
297
            \cdot activity\_costs(i, o)
298
299
    If `nonconvex.inactivity_costs` is set by the user:
300
        .. math::
301
            \sum_{i, o \in INACTIVITYCOSTFLOWS} \sum_t (1 - status(i, o, t)) \
302
            \cdot inactivity\_costs(i, o)
303
304
    If `nonconvex.positive_gradient["costs"]` is set by the user:
305
        .. math::
306
            \sum_{i, o \in POSITIVE_GRADIENT_FLOWS} \sum_t
307
            positive_gradient(i, o, t) \cdot positive\_gradient\_costs(i, o)
308
309
    If `nonconvex.negative_gradient["costs"]` is set by the user:
310
        .. math::
311
            \sum_{i, o \in NEGATIVE_GRADIENT_FLOWS} \sum_t
312
            negative_gradient(i, o, t) \cdot negative\_gradient\_costs(i, o)
313
314
    """
315
316
    def __init__(self, *args, **kwargs):
317
        super().__init__(*args, **kwargs)
318
319
    def _create(self, group=None):
320
        """Creates set, variables, constraints for all flow object with
321
        an attribute flow of type class:`.NonConvexFlowBlock`.
322
323
        Parameters
324
        ----------
325
        group : list
326
            List of oemof.solph.NonConvexFlowBlock objects for which
327
            the constraints are build.
328
        """
329
        if group is None:
330
            return None
331
332
        m = self.parent_block()
333
        # ########################## SETS #####################################
334
        self.NONCONVEX_FLOWS = Set(initialize=[(g[0], g[1]) for g in group])
335
336
        self.MIN_FLOWS = Set(
337
            initialize=[(g[0], g[1]) for g in group if g[2].min[0] is not None]
338
        )
339
        self.STARTUPFLOWS = Set(
340
            initialize=[
341
                (g[0], g[1])
342
                for g in group
343
                if g[2].nonconvex.startup_costs[0] is not None
344
                or g[2].nonconvex.maximum_startups is not None
345
            ]
346
        )
347
        self.MAXSTARTUPFLOWS = Set(
348
            initialize=[
349
                (g[0], g[1])
350
                for g in group
351
                if g[2].nonconvex.maximum_startups is not None
352
            ]
353
        )
354
        self.SHUTDOWNFLOWS = Set(
355
            initialize=[
356
                (g[0], g[1])
357
                for g in group
358
                if g[2].nonconvex.shutdown_costs[0] is not None
359
                or g[2].nonconvex.maximum_shutdowns is not None
360
            ]
361
        )
362
        self.MAXSHUTDOWNFLOWS = Set(
363
            initialize=[
364
                (g[0], g[1])
365
                for g in group
366
                if g[2].nonconvex.maximum_shutdowns is not None
367
            ]
368
        )
369
        self.MINUPTIMEFLOWS = Set(
370
            initialize=[
371
                (g[0], g[1])
372
                for g in group
373
                if g[2].nonconvex.minimum_uptime is not None
374
            ]
375
        )
376
377
        self.MINDOWNTIMEFLOWS = Set(
378
            initialize=[
379
                (g[0], g[1])
380
                for g in group
381
                if g[2].nonconvex.minimum_downtime is not None
382
            ]
383
        )
384
385
        self.ACTIVITYCOSTFLOWS = Set(
386
            initialize=[
387
                (g[0], g[1])
388
                for g in group
389
                if g[2].nonconvex.activity_costs[0] is not None
390
            ]
391
        )
392
393
        self.INACTIVITYCOSTFLOWS = Set(
394
            initialize=[
395
                (g[0], g[1])
396
                for g in group
397
                if g[2].nonconvex.inactivity_costs[0] is not None
398
            ]
399
        )
400
401
        self.NEGATIVE_GRADIENT_FLOWS = Set(
402
            initialize=[
403
                (g[0], g[1])
404
                for g in group
405
                if g[2].nonconvex.negative_gradient["ub"][0] is not None
406
            ]
407
        )
408
409
        self.POSITIVE_GRADIENT_FLOWS = Set(
410
            initialize=[
411
                (g[0], g[1])
412
                for g in group
413
                if g[2].nonconvex.positive_gradient["ub"][0] is not None
414
            ]
415
        )
416
417
        # ################### VARIABLES AND CONSTRAINTS #######################
418
        self.status = Var(self.NONCONVEX_FLOWS, m.TIMESTEPS, within=Binary)
419
420
        if self.STARTUPFLOWS:
421
            self.startup = Var(self.STARTUPFLOWS, m.TIMESTEPS, within=Binary)
422
423
        if self.SHUTDOWNFLOWS:
424
            self.shutdown = Var(self.SHUTDOWNFLOWS, m.TIMESTEPS, within=Binary)
425
426
        if self.POSITIVE_GRADIENT_FLOWS:
427
            self.positive_gradient = Var(
428
                self.POSITIVE_GRADIENT_FLOWS, m.TIMESTEPS
429
            )
430
431
        if self.NEGATIVE_GRADIENT_FLOWS:
432
            self.negative_gradient = Var(
433
                self.NEGATIVE_GRADIENT_FLOWS, m.TIMESTEPS
434
            )
435
436
        def _minimum_flow_rule(block, i, o, t):
437
            """Rule definition for MILP minimum flow constraints."""
438
            expr = (
439
                self.status[i, o, t]
440
                * m.flows[i, o].min[t]
0 ignored issues
show
introduced by
The variable m does not seem to be defined for all execution paths.
Loading history...
441
                * m.flows[i, o].nominal_value
442
                <= m.flow[i, o, t]
443
            )
444
            return expr
445
446
        self.min = Constraint(
447
            self.MIN_FLOWS, m.TIMESTEPS, rule=_minimum_flow_rule
448
        )
449
450
        def _maximum_flow_rule(block, i, o, t):
451
            """Rule definition for MILP maximum flow constraints."""
452
            expr = (
453
                self.status[i, o, t]
454
                * m.flows[i, o].max[t]
0 ignored issues
show
introduced by
The variable m does not seem to be defined for all execution paths.
Loading history...
455
                * m.flows[i, o].nominal_value
456
                >= m.flow[i, o, t]
457
            )
458
            return expr
459
460
        self.max = Constraint(
461
            self.MIN_FLOWS, m.TIMESTEPS, rule=_maximum_flow_rule
462
        )
463
464 View Code Duplication
        def _startup_rule(block, i, o, t):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
465
            """Rule definition for startup constraint of nonconvex flows."""
466
            if t > m.TIMESTEPS[1]:
0 ignored issues
show
introduced by
The variable m does not seem to be defined for all execution paths.
Loading history...
467
                expr = (
468
                    self.startup[i, o, t]
469
                    >= self.status[i, o, t] - self.status[i, o, t - 1]
470
                )
471
            else:
472
                expr = (
473
                    self.startup[i, o, t]
474
                    >= self.status[i, o, t]
475
                    - m.flows[i, o].nonconvex.initial_status
476
                )
477
            return expr
478
479
        self.startup_constr = Constraint(
480
            self.STARTUPFLOWS, m.TIMESTEPS, rule=_startup_rule
481
        )
482
483
        def _max_startup_rule(block, i, o):
484
            """Rule definition for maximum number of start-ups."""
485
            lhs = sum(self.startup[i, o, t] for t in m.TIMESTEPS)
0 ignored issues
show
introduced by
The variable m does not seem to be defined for all execution paths.
Loading history...
486
            return lhs <= m.flows[i, o].nonconvex.maximum_startups
487
488
        self.max_startup_constr = Constraint(
489
            self.MAXSTARTUPFLOWS, rule=_max_startup_rule
490
        )
491
492 View Code Duplication
        def _shutdown_rule(block, i, o, t):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
493
            """Rule definition for shutdown constraints of nonconvex flows."""
494
            if t > m.TIMESTEPS[1]:
0 ignored issues
show
introduced by
The variable m does not seem to be defined for all execution paths.
Loading history...
495
                expr = (
496
                    self.shutdown[i, o, t]
497
                    >= self.status[i, o, t - 1] - self.status[i, o, t]
498
                )
499
            else:
500
                expr = (
501
                    self.shutdown[i, o, t]
502
                    >= m.flows[i, o].nonconvex.initial_status
503
                    - self.status[i, o, t]
504
                )
505
            return expr
506
507
        self.shutdown_constr = Constraint(
508
            self.SHUTDOWNFLOWS, m.TIMESTEPS, rule=_shutdown_rule
509
        )
510
511
        def _max_shutdown_rule(block, i, o):
512
            """Rule definition for maximum number of start-ups."""
513
            lhs = sum(self.shutdown[i, o, t] for t in m.TIMESTEPS)
0 ignored issues
show
introduced by
The variable m does not seem to be defined for all execution paths.
Loading history...
514
            return lhs <= m.flows[i, o].nonconvex.maximum_shutdowns
515
516
        self.max_shutdown_constr = Constraint(
517
            self.MAXSHUTDOWNFLOWS, rule=_max_shutdown_rule
518
        )
519
520 View Code Duplication
        def _min_uptime_rule(block, i, o, t):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
521
            """
522
            Rule definition for min-uptime constraints of nonconvex flows.
523
            """
524
            if (
525
                m.flows[i, o].nonconvex.max_up_down
0 ignored issues
show
introduced by
The variable m does not seem to be defined for all execution paths.
Loading history...
526
                <= t
527
                <= m.TIMESTEPS[-1] - m.flows[i, o].nonconvex.max_up_down
528
            ):
529
                expr = 0
530
                expr += (
531
                    self.status[i, o, t] - self.status[i, o, t - 1]
532
                ) * m.flows[i, o].nonconvex.minimum_uptime
533
                expr += -sum(
534
                    self.status[i, o, t + u]
535
                    for u in range(0, m.flows[i, o].nonconvex.minimum_uptime)
536
                )
537
                return expr <= 0
538
            else:
539
                expr = 0
540
                expr += self.status[i, o, t]
541
                expr += -m.flows[i, o].nonconvex.initial_status
542
                return expr == 0
543
544
        self.min_uptime_constr = Constraint(
545
            self.MINUPTIMEFLOWS, m.TIMESTEPS, rule=_min_uptime_rule
546
        )
547
548 View Code Duplication
        def _min_downtime_rule(block, i, o, t):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
549
            """
550
            Rule definition for min-downtime constraints of nonconvex flows.
551
            """
552
            if (
553
                m.flows[i, o].nonconvex.max_up_down
0 ignored issues
show
introduced by
The variable m does not seem to be defined for all execution paths.
Loading history...
554
                <= t
555
                <= m.TIMESTEPS[-1] - m.flows[i, o].nonconvex.max_up_down
556
            ):
557
                expr = 0
558
                expr += (
559
                    self.status[i, o, t - 1] - self.status[i, o, t]
560
                ) * m.flows[i, o].nonconvex.minimum_downtime
561
                expr += -m.flows[i, o].nonconvex.minimum_downtime
562
                expr += sum(
563
                    self.status[i, o, t + d]
564
                    for d in range(0, m.flows[i, o].nonconvex.minimum_downtime)
565
                )
566
                return expr <= 0
567
            else:
568
                expr = 0
569
                expr += self.status[i, o, t]
570
                expr += -m.flows[i, o].nonconvex.initial_status
571
                return expr == 0
572
573
        self.min_downtime_constr = Constraint(
574
            self.MINDOWNTIMEFLOWS, m.TIMESTEPS, rule=_min_downtime_rule
575
        )
576
577
        def _positive_gradient_flow_rule(block):
578
            """Rule definition for positive gradient constraint."""
579
            for i, o in self.POSITIVE_GRADIENT_FLOWS:
580
                for t in m.TIMESTEPS:
0 ignored issues
show
introduced by
The variable m does not seem to be defined for all execution paths.
Loading history...
581
                    if t > 0:
582
                        lhs = (
583
                            m.flow[i, o, t] * self.status[i, o, t]
584
                            - m.flow[i, o, t - 1] * self.status[i, o, t - 1]
585
                        )
586
                        rhs = self.positive_gradient[i, o, t]
587
                        self.positive_gradient_constr.add(
588
                            (i, o, t), lhs <= rhs
589
                        )
590
                    else:
591
                        pass  # return(Constraint.Skip)
592
593
        self.positive_gradient_constr = Constraint(
594
            self.POSITIVE_GRADIENT_FLOWS, m.TIMESTEPS, noruleinit=True
595
        )
596
        self.positive_gradient_build = BuildAction(
597
            rule=_positive_gradient_flow_rule
598
        )
599
600
        def _negative_gradient_flow_rule(block):
601
            """Rule definition for negative gradient constraint."""
602
            for i, o in self.NEGATIVE_GRADIENT_FLOWS:
603
                for t in m.TIMESTEPS:
0 ignored issues
show
introduced by
The variable m does not seem to be defined for all execution paths.
Loading history...
604
                    if t > 0:
605
                        lhs = (
606
                            m.flow[i, o, t - 1] * self.status[i, o, t - 1]
607
                            - m.flow[i, o, t] * self.status[i, o, t]
608
                        )
609
                        rhs = self.negative_gradient[i, o, t]
610
                        self.negative_gradient_constr.add(
611
                            (i, o, t), lhs <= rhs
612
                        )
613
                    else:
614
                        pass  # return(Constraint.Skip)
615
616
        self.negative_gradient_constr = Constraint(
617
            self.NEGATIVE_GRADIENT_FLOWS, m.TIMESTEPS, noruleinit=True
618
        )
619
        self.negative_gradient_build = BuildAction(
620
            rule=_negative_gradient_flow_rule
621
        )
622
623
    def _objective_expression(self):
624
        r"""Objective expression for nonconvex flows."""
625
        if not hasattr(self, "NONCONVEX_FLOWS"):
626
            return 0
627
628
        m = self.parent_block()
629
630
        startup_costs = 0
631
        shutdown_costs = 0
632
        activity_costs = 0
633
        inactivity_costs = 0
634
        gradient_costs = 0
635
636
        if self.STARTUPFLOWS:
637
            for i, o in self.STARTUPFLOWS:
638
                if m.flows[i, o].nonconvex.startup_costs[0] is not None:
639
                    startup_costs += sum(
640
                        self.startup[i, o, t]
641
                        * m.flows[i, o].nonconvex.startup_costs[t]
642
                        for t in m.TIMESTEPS
643
                    )
644
            self.startup_costs = Expression(expr=startup_costs)
645
646
        if self.SHUTDOWNFLOWS:
647
            for i, o in self.SHUTDOWNFLOWS:
648
                if m.flows[i, o].nonconvex.shutdown_costs[0] is not None:
649
                    shutdown_costs += sum(
650
                        self.shutdown[i, o, t]
651
                        * m.flows[i, o].nonconvex.shutdown_costs[t]
652
                        for t in m.TIMESTEPS
653
                    )
654
            self.shutdown_costs = Expression(expr=shutdown_costs)
655
656
        if self.ACTIVITYCOSTFLOWS:
657
            for i, o in self.ACTIVITYCOSTFLOWS:
658
                if m.flows[i, o].nonconvex.activity_costs[0] is not None:
659
                    activity_costs += sum(
660
                        self.status[i, o, t]
661
                        * m.flows[i, o].nonconvex.activity_costs[t]
662
                        for t in m.TIMESTEPS
663
                    )
664
665
            self.activity_costs = Expression(expr=activity_costs)
666
667
        if self.INACTIVITYCOSTFLOWS:
668
            for i, o in self.INACTIVITYCOSTFLOWS:
669
                if m.flows[i, o].nonconvex.inactivity_costs[0] is not None:
670
                    inactivity_costs += sum(
671
                        (1 - self.status[i, o, t])
672
                        * m.flows[i, o].nonconvex.inactivity_costs[t]
673
                        for t in m.TIMESTEPS
674
                    )
675
676
            self.inactivity_costs = Expression(expr=inactivity_costs)
677
678
        return (
679
            startup_costs
680
            + shutdown_costs
681
            + activity_costs
682
            + inactivity_costs
683
            + gradient_costs
684
        )
685