|
1
|
|
|
# -*- coding: utf-8 -*- |
|
2
|
|
|
|
|
3
|
|
|
"""Creating sets, variables, constraints and parts of the objective function |
|
4
|
|
|
for Flow objects with nonconvex but without investment options. |
|
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 |
|
14
|
|
|
|
|
15
|
|
|
SPDX-License-Identifier: MIT |
|
16
|
|
|
|
|
17
|
|
|
""" |
|
18
|
|
|
|
|
19
|
|
|
from pyomo.core import Binary |
|
20
|
|
|
from pyomo.core import Constraint |
|
21
|
|
|
from pyomo.core import Expression |
|
22
|
|
|
from pyomo.core import NonNegativeReals |
|
23
|
|
|
from pyomo.core import Set |
|
24
|
|
|
from pyomo.core import Var |
|
25
|
|
|
from pyomo.core.base.block import ScalarBlock |
|
26
|
|
|
|
|
27
|
|
|
from . import _shared |
|
28
|
|
|
|
|
29
|
|
|
|
|
30
|
|
|
class NonConvexFlowBlock(ScalarBlock): |
|
31
|
|
|
r""" |
|
32
|
|
|
.. automethod:: _create_constraints |
|
33
|
|
|
.. automethod:: _create_variables |
|
34
|
|
|
.. automethod:: _create_sets |
|
35
|
|
|
|
|
36
|
|
|
.. automethod:: _objective_expression |
|
37
|
|
|
|
|
38
|
|
|
Parameters are defined in :class:`Flow`. |
|
39
|
|
|
""" |
|
40
|
|
|
|
|
41
|
|
|
def __init__(self, *args, **kwargs): |
|
42
|
|
|
super().__init__(*args, **kwargs) |
|
43
|
|
|
|
|
44
|
|
|
def _create(self, group=None): |
|
45
|
|
|
"""Creates set, variables, constraints for all flow object with |
|
46
|
|
|
an attribute flow of type class:`.NonConvexFlowBlock`. |
|
47
|
|
|
|
|
48
|
|
|
Parameters |
|
49
|
|
|
---------- |
|
50
|
|
|
group : list |
|
51
|
|
|
List of oemof.solph.NonConvexFlowBlock objects for which |
|
52
|
|
|
the constraints are build. |
|
53
|
|
|
""" |
|
54
|
|
|
if group is None: |
|
55
|
|
|
return None |
|
56
|
|
|
|
|
57
|
|
|
self._create_sets(group) |
|
58
|
|
|
self._create_variables() |
|
59
|
|
|
self._create_constraints() |
|
60
|
|
|
|
|
61
|
|
|
def _create_sets(self, group): |
|
62
|
|
|
r""" |
|
63
|
|
|
**The following sets are created:** (-> see basic sets at |
|
64
|
|
|
:class:`.Model` ) |
|
65
|
|
|
|
|
66
|
|
|
FIXED_CAPACITY_NONCONVEX_FLOWS |
|
67
|
|
|
A set of flows with the attribute `nonconvex` of type |
|
68
|
|
|
:class:`.options.NonConvex`. |
|
69
|
|
|
|
|
70
|
|
|
Also creates everything listed in `_sets_for_non_convex_flows`. |
|
71
|
|
|
""" |
|
72
|
|
|
self.FIXED_CAPACITY_NONCONVEX_FLOWS = Set( |
|
73
|
|
|
initialize=[(g[0], g[1]) for g in group] |
|
74
|
|
|
) |
|
75
|
|
|
|
|
76
|
|
|
_shared.sets_for_non_convex_flows(self, group) |
|
77
|
|
|
|
|
78
|
|
|
def _create_variables(self): |
|
79
|
|
|
r""" |
|
80
|
|
|
:math:`Y_{status}` (binary) `om.NonConvexFlowBlock.status`: |
|
81
|
|
|
Variable indicating if flow is >= 0 |
|
82
|
|
|
|
|
83
|
|
|
:math:`P_{max,status}` Status_nominal (continuous) |
|
84
|
|
|
Variable indicating if flow is >= 0 |
|
85
|
|
|
|
|
86
|
|
|
Also creates :py:func:`_variables_for_non_convex_flows`. |
|
87
|
|
|
""" |
|
88
|
|
|
m = self.parent_block() |
|
89
|
|
|
self.status = Var( |
|
90
|
|
|
self.FIXED_CAPACITY_NONCONVEX_FLOWS, m.TIMESTEPS, within=Binary |
|
91
|
|
|
) |
|
92
|
|
|
for o, i in self.FIXED_CAPACITY_NONCONVEX_FLOWS: |
|
93
|
|
|
if m.flows[o, i].nonconvex.initial_status is not None: |
|
94
|
|
|
for t in range( |
|
95
|
|
|
0, m.flows[o, i].nonconvex.first_flexible_timestep |
|
96
|
|
|
): |
|
97
|
|
|
self.status[o, i, t] = m.flows[ |
|
98
|
|
|
o, i |
|
99
|
|
|
].nonconvex.initial_status |
|
100
|
|
|
self.status[o, i, t].fix() |
|
101
|
|
|
|
|
102
|
|
|
# `status_nominal` is a parameter which represents the |
|
103
|
|
|
# multiplication of a binary variable (`status`) |
|
104
|
|
|
# and a continuous variable (`invest` or `nominal_capacity`) |
|
105
|
|
|
self.status_nominal = Var( |
|
106
|
|
|
self.FIXED_CAPACITY_NONCONVEX_FLOWS, |
|
107
|
|
|
m.TIMESTEPS, |
|
108
|
|
|
within=NonNegativeReals, |
|
109
|
|
|
) |
|
110
|
|
|
|
|
111
|
|
|
_shared.variables_for_non_convex_flows(self) |
|
112
|
|
|
|
|
113
|
|
|
def _create_constraints(self): |
|
114
|
|
|
""" |
|
115
|
|
|
The following constraints are created: |
|
116
|
|
|
|
|
117
|
|
|
.. automethod:: _status_nominal_constraint |
|
118
|
|
|
|
|
119
|
|
|
Also creates: :py:func:`_minimum_flow_constraint`, |
|
120
|
|
|
:py:func:`_maximum_flow_constraint`, and |
|
121
|
|
|
:py:func:`_shared_constraints_for_non_convex_flows`. |
|
122
|
|
|
|
|
123
|
|
|
""" |
|
124
|
|
|
|
|
125
|
|
|
self.status_nominal_constraint = self._status_nominal_constraint() |
|
126
|
|
|
self.min = _shared.minimum_flow_constraint(self) |
|
127
|
|
|
self.max = _shared.maximum_flow_constraint(self) |
|
128
|
|
|
|
|
129
|
|
|
_shared.shared_constraints_for_non_convex_flows(self) |
|
130
|
|
|
|
|
131
|
|
|
def _objective_expression(self): |
|
132
|
|
|
r""" |
|
133
|
|
|
The following terms are to the cost function: |
|
134
|
|
|
|
|
135
|
|
|
* :py:func:`_startup_costs` |
|
136
|
|
|
* :py:func:`_shutdown_costs` |
|
137
|
|
|
* :py:func:`_activity_costs` |
|
138
|
|
|
* :py:func:`_inactivity_costs` |
|
139
|
|
|
""" |
|
140
|
|
|
if not hasattr(self, "FIXED_CAPACITY_NONCONVEX_FLOWS"): |
|
141
|
|
|
return 0 |
|
142
|
|
|
|
|
143
|
|
|
startup_costs = _shared.startup_costs(self) |
|
144
|
|
|
shutdown_costs = _shared.shutdown_costs(self) |
|
145
|
|
|
activity_costs = _shared.activity_costs(self) |
|
146
|
|
|
inactivity_costs = _shared.inactivity_costs(self) |
|
147
|
|
|
|
|
148
|
|
|
self.activity_costs = Expression(expr=activity_costs) |
|
149
|
|
|
self.inactivity_costs = Expression(expr=inactivity_costs) |
|
150
|
|
|
self.startup_costs = Expression(expr=startup_costs) |
|
151
|
|
|
self.shutdown_costs = Expression(expr=shutdown_costs) |
|
152
|
|
|
|
|
153
|
|
|
self.costs = Expression( |
|
154
|
|
|
expr=( |
|
155
|
|
|
startup_costs |
|
156
|
|
|
+ shutdown_costs |
|
157
|
|
|
+ activity_costs |
|
158
|
|
|
+ inactivity_costs |
|
159
|
|
|
) |
|
160
|
|
|
) |
|
161
|
|
|
|
|
162
|
|
|
return self.costs |
|
163
|
|
|
|
|
164
|
|
|
def _status_nominal_constraint(self): |
|
165
|
|
|
r""" |
|
166
|
|
|
.. math:: |
|
167
|
|
|
P_{max,status}(t) = Y_{status}(t) \cdot P_{nom}, \\ |
|
168
|
|
|
\forall t \in \textrm{TIMESTEPS}. |
|
169
|
|
|
""" |
|
170
|
|
|
m = self.parent_block() |
|
171
|
|
|
|
|
172
|
|
|
def _status_nominal_rule(_, i, o, t): |
|
173
|
|
|
"""Rule definition for status_nominal""" |
|
174
|
|
|
expr = ( |
|
175
|
|
|
self.status_nominal[i, o, t] |
|
176
|
|
|
== self.status[i, o, t] * m.flows[i, o].nominal_capacity |
|
177
|
|
|
) |
|
178
|
|
|
return expr |
|
179
|
|
|
|
|
180
|
|
|
return Constraint( |
|
181
|
|
|
self.FIXED_CAPACITY_NONCONVEX_FLOWS, |
|
182
|
|
|
m.TIMESTEPS, |
|
183
|
|
|
rule=_status_nominal_rule, |
|
184
|
|
|
) |
|
185
|
|
|
|