Passed
Pull Request — dev (#821)
by Uwe
02:17
created

ElectricalLineBlock.__init__()   A

Complexity

Conditions 1

Size

Total Lines 2
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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