/
_electrical_line.py
197 lines (157 loc) · 6.77 KB
/
_electrical_line.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# -*- coding: utf-8 -*-
"""
In-development electrical line components.
SPDX-FileCopyrightText: Uwe Krien <krien@uni-bremen.de>
SPDX-FileCopyrightText: Simon Hilpert
SPDX-FileCopyrightText: Cord Kaldemeyer
SPDX-FileCopyrightText: Patrik Schönfeldt
SPDX-FileCopyrightText: Johannes Röder
SPDX-FileCopyrightText: jakob-wo
SPDX-FileCopyrightText: gplssm
SPDX-FileCopyrightText: jnnr
SPDX-FileCopyrightText: Johannes Kochems
SPDX-License-Identifier: MIT
"""
import logging
from pyomo.core.base.block import ScalarBlock
from pyomo.environ import BuildAction
from pyomo.environ import Constraint
from pyomo.environ import Set
from pyomo.environ import Var
from oemof.solph._plumbing import sequence as solph_sequence
from oemof.solph.buses.experimental._electrical_bus import ElectricalBus
from oemof.solph.flows._flow import Flow
class ElectricalLine(Flow):
r"""An ElectricalLine to be used in linear optimal power flow calculations.
based on angle formulation. Check out the Notes below before using this
component!
Parameters
----------
reactance : float or array of floats
Reactance of the line to be modelled
Note: This component is experimental. Use it with care.
Notes
-----
* To use this object the connected buses need to be of the type
:py:class:`~oemof.solph.experimental.ElectricalBus`.
* It does not work together with flows that have set the attr.`nonconvex`,
i.e. unit commitment constraints are not possible
* Input and output of this component are set equal, therefore just use
either only the input or the output to parameterize.
* Default attribute `min` of in/outflows is overwritten by -1 if not set
differently by the user
The following sets, variables, constraints and objective parts are created
* :py:class:`~oemof.solph.experimental.electrical_line.ElectricalLineBlock`
""" # noqa: E501
def __init__(self, **kwargs):
super().__init__(
nominal_value=kwargs.get("nominal_value"),
variable_costs=kwargs.get("variable_costs", 0),
min=kwargs.get("min"),
max=kwargs.get("max"),
fix=kwargs.get("fix"),
positive_gradient_limit=kwargs.get("positive_gradient_limit"),
negative_gradient_limit=kwargs.get("negative_gradient_limit"),
full_load_time_max=kwargs.get("full_load_time_max"),
full_load_time_min=kwargs.get("full_load_time_min"),
integer=kwargs.get("integer", False),
bidirectional=kwargs.get("bidirectiona", False),
investment=kwargs.get("investment"),
nonconvex=kwargs.get("nonconvex"),
custom_attributes=kwargs.get("costom_attributes"),
)
self.reactance = solph_sequence(kwargs.get("reactance", 0.00001))
self.input = kwargs.get("input")
self.output = kwargs.get("output")
# set input / output flow values to -1 by default if not set by user
if self.nonconvex is not None:
raise ValueError(
(
"Attribute `nonconvex` must be None for "
+ "component `ElectricalLine` from {} to {}!"
).format(self.input, self.output)
)
if self.min is None:
self.min = -1
# to be used in grouping for all bidi flows
self.bidirectional = True
def constraint_group(self):
return ElectricalLineBlock
class ElectricalLineBlock(ScalarBlock):
r"""Block for the linear relation of nodes with type
class:`.ElectricalLine`
Note: This component is experimental. Use it with care.
**The following constraints are created:**
Linear relation :attr:`om.ElectricalLine.electrical_flow[n,t]`
.. math::
flow(n, o, p, t) = 1 / reactance(n, t) \cdot
voltage\_angle(i(n), t) - voltage\_angle(o(n), t), \\
\forall p, t \in \textrm{TIMEINDEX}, \\
\forall n \in \textrm{ELECTRICAL\_LINES}.
TODO: Add equate constraint of flows
**The following variable are created:**
TODO: Add voltage angle variable
TODO: Add fix slack bus voltage angle to zero constraint / bound
TODO: Add tests
"""
CONSTRAINT_GROUP = True
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def _create(self, group=None):
"""Creates the linear constraint for the class:`ElectricalLine`
block.
Parameters
----------
group : list
List of oemof.solph.ElectricalLine (eline) objects for which
the linear relation of inputs and outputs is created
e.g. group = [eline1, eline2, ...]. The components inside the
list need to hold a attribute `reactance` of type Sequence
containing the reactance of the line.
"""
if group is None:
return None
m = self.parent_block()
# create voltage angle variables
self.ELECTRICAL_BUSES = Set(
initialize=[n for n in m.es.nodes if isinstance(n, ElectricalBus)]
)
def _voltage_angle_bounds(block, b, t):
return b.v_min, b.v_max
self.voltage_angle = Var(
self.ELECTRICAL_BUSES, m.TIMESTEPS, bounds=_voltage_angle_bounds
)
if True not in [b.slack for b in self.ELECTRICAL_BUSES]:
# TODO: Make this robust to select the same slack bus for
# the same problems
bus = [b for b in self.ELECTRICAL_BUSES][0]
logging.info(
"No slack bus set,setting bus {0} as slack bus".format(
bus.label
)
)
bus.slack = True
def _voltage_angle_relation(block):
for p, t in m.TIMEINDEX:
for n in group:
if n.input.slack is True:
self.voltage_angle[n.output, t].value = 0
self.voltage_angle[n.output, t].fix()
try:
lhs = m.flow[n.input, n.output, p, t]
rhs = (
1
/ n.reactance[t]
* (
self.voltage_angle[n.input, t]
- self.voltage_angle[n.output, t]
)
)
except ValueError:
raise ValueError(
"Error in constraint creation",
"of node {}".format(n.label),
)
block.electrical_flow.add((n, p, t), (lhs == rhs))
self.electrical_flow = Constraint(group, m.TIMEINDEX, noruleinit=True)
self.electrical_flow_build = BuildAction(rule=_voltage_angle_relation)