Passed
Push — dev ( eccf95...cfc126 )
by Uwe
06:21 queued 04:51
created

solph.components.experimental._piecewise_linear_transformer   A

Complexity

Total Complexity 10

Size/Duplication

Total Lines 202
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 10
eloc 89
dl 0
loc 202
rs 10
c 0
b 0
f 0

4 Methods

Rating   Name   Duplication   Size   Complexity  
A PiecewiseLinearTransformerBlock.__init__() 0 2 1
A PiecewiseLinearTransformer.__init__() 0 18 4
A PiecewiseLinearTransformer.constraint_group() 0 2 1
B PiecewiseLinearTransformerBlock._create() 0 98 4
1
# -*- coding: utf-8 -*-
2
3
"""
4
In-development transfomer with piecewise linar efficiencies.
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
from oemof.network import network as on
20
from pyomo.core.base.block import ScalarBlock
21
from pyomo.environ import BuildAction
22
from pyomo.environ import Constraint
23
from pyomo.environ import Piecewise
24
from pyomo.environ import Set
25
from pyomo.environ import Var
26
27
28
class PiecewiseLinearTransformer(on.Transformer):
29
    """Component to model a transformer with one input and one output
30
    and an arbitrary piecewise linear conversion function.
31
32
    Parameters
33
    ----------
34
    in_breakpoints : list
35
        List containing the domain breakpoints, i.e. the breakpoints for the
36
        incoming flow.
37
38
    conversion_function : func
39
        The function describing the relation between incoming flow and outgoing
40
        flow which is to be approximated.
41
42
    pw_repn : string
43
        Choice of piecewise representation that is passed to
44
        pyomo.environ.Piecewise
45
46
    Examples
47
    --------
48
    >>> import oemof.solph as solph
49
50
    >>> b_gas = solph.buses.Bus(label='biogas')
51
    >>> b_el = solph.buses.Bus(label='electricity')
52
53
    >>> pwltf = solph.components.experimental.PiecewiseLinearTransformer(
54
    ...    label='pwltf',
55
    ...    inputs={b_gas: solph.flows.Flow(
56
    ...    nominal_value=100,
57
    ...    variable_costs=1)},
58
    ...    outputs={b_el: solph.flows.Flow()},
59
    ...    in_breakpoints=[0,25,50,75,100],
60
    ...    conversion_function=lambda x: x**2,
61
    ...    pw_repn='CC')
62
63
    >>> type(pwltf)
64
    <class 'oemof.solph.components.experimental._piecewise_linear_transformer.\
65
PiecewiseLinearTransformer'>
66
    """
67
68
    def __init__(self, *args, **kwargs):
69
        super().__init__(*args, **kwargs)
70
71
        self.in_breakpoints = list(kwargs.get("in_breakpoints"))
72
        self.conversion_function = kwargs.get("conversion_function")
73
        self.pw_repn = kwargs.get("pw_repn")
74
75
        if len(self.inputs) > 1 or len(self.outputs) > 1:
76
            raise ValueError(
77
                "Component `PiecewiseLinearTransformer` cannot have "
78
                + "more than 1 input and 1 output!"
79
            )
80
81
        nominal_value = [a.nominal_value for a in self.inputs.values()][0]
82
        if max(self.in_breakpoints) < nominal_value:
83
            raise ValueError(
84
                "Largest in_breakpoint must be larger or equal "
85
                + "nominal value"
86
            )
87
88
    def constraint_group(self):
89
        return PiecewiseLinearTransformerBlock
90
91
92
class PiecewiseLinearTransformerBlock(ScalarBlock):
93
    r"""Block for the relation of nodes with type
94
    :class:`~oemof.solph.components.experimental._piecewise_linear_transformer.PiecewiseLinearTransformer`
95
96
    **The following constraints are created:**
97
98
    """
99
    CONSTRAINT_GROUP = True
100
101
    def __init__(self, *args, **kwargs):
102
        super().__init__(*args, **kwargs)
103
104
    def _create(self, group=None):
105
        """Creates the relation for the class:`PiecewiseLinearTransformer`.
106
107
        Parameters
108
        ----------
109
        group : list
110
            List of
111
            oemof.solph.components.experimental.PiecewiseLinearTransformer
112
            objects for which the relation of inputs and outputs is created
113
            e.g. group = [pwltf1, pwltf2, pwltf3, ...].
114
115
        """
116
        if group is None:
117
            return None
118
119
        m = self.parent_block()
120
121
        self.PWLINEARTRANSFORMERS = Set(initialize=[n for n in group])
122
123
        pw_repns = [n.pw_repn for n in group]
124
        if all(x == pw_repns[0] for x in pw_repns):
125
            self.pw_repn = pw_repns[0]
126
        else:
127
            print(
128
                "Cannot different piecewise representations ",
129
                [n.pw_repn for n in group],
130
            )
131
132
        self.breakpoints = {}
133
134
        def build_breakpoints(block, n):
135
            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...
136
                self.breakpoints[(n, t)] = n.in_breakpoints
137
138
        self.breakpoint_build = BuildAction(
139
            self.PWLINEARTRANSFORMERS, rule=build_breakpoints
140
        )
141
142
        def _conversion_function(block, n, t, x):
143
            expr = n.conversion_function(x)
144
            return expr
145
146
        # bounds are min/max of breakpoints
147
        lower_bound_in = {n: min(n.in_breakpoints) for n in group}
148
        upper_bound_in = {n: max(n.in_breakpoints) for n in group}
149
        lower_bound_out = {
150
            n: n.conversion_function(bound)
151
            for (n, bound) in lower_bound_in.items()
152
        }
153
        upper_bound_out = {
154
            n: n.conversion_function(bound)
155
            for (n, bound) in upper_bound_in.items()
156
        }
157
158
        def get_inflow_bounds(model, n, t):
159
            return lower_bound_in[n], upper_bound_in[n]
0 ignored issues
show
introduced by
The variable upper_bound_in does not seem to be defined for all execution paths.
Loading history...
introduced by
The variable lower_bound_in does not seem to be defined for all execution paths.
Loading history...
160
161
        def get_outflow_bounds(model, n, t):
162
            return lower_bound_out[n], upper_bound_out[n]
0 ignored issues
show
introduced by
The variable upper_bound_out does not seem to be defined for all execution paths.
Loading history...
introduced by
The variable lower_bound_out does not seem to be defined for all execution paths.
Loading history...
163
164
        self.inflow = Var(
165
            self.PWLINEARTRANSFORMERS, m.TIMESTEPS, bounds=get_inflow_bounds
166
        )
167
        self.outflow = Var(
168
            self.PWLINEARTRANSFORMERS, m.TIMESTEPS, bounds=get_outflow_bounds
169
        )
170
171
        def _in_equation(block, n, t):
172
            """Link binary input and output flow to component outflow."""
173
            expr = 0
174
            expr += -m.flow[list(n.inputs.keys())[0], n, t]
0 ignored issues
show
introduced by
The variable m does not seem to be defined for all execution paths.
Loading history...
175
            expr += self.inflow[n, t]
176
            return expr == 0
177
178
        self.equate_in = Constraint(
179
            self.PWLINEARTRANSFORMERS, m.TIMESTEPS, rule=_in_equation
180
        )
181
182
        def _out_equation(block, n, t):
183
            """Link binary input and output flow to component outflow."""
184
            expr = 0
185
            expr += -m.flow[n, list(n.outputs.keys())[0], t]
0 ignored issues
show
introduced by
The variable m does not seem to be defined for all execution paths.
Loading history...
186
            expr += self.outflow[n, t]
187
            return expr == 0
188
189
        self.equate_out = Constraint(
190
            self.PWLINEARTRANSFORMERS, m.TIMESTEPS, rule=_out_equation
191
        )
192
193
        self.piecewise = Piecewise(
194
            self.PWLINEARTRANSFORMERS,
195
            m.TIMESTEPS,
196
            self.outflow,
197
            self.inflow,
198
            pw_repn=self.pw_repn,
199
            pw_constr_type="EQ",
200
            pw_pts=self.breakpoints,
201
            f_rule=_conversion_function,
202
        )
203