Completed
Push — dev ( 794b9b...9be114 )
by Patrik
20s queued 16s
created

solph.components._converter.Transformer.__init__()   A

Complexity

Conditions 1

Size

Total Lines 20
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 16
dl 0
loc 20
rs 9.6
c 0
b 0
f 0
cc 1
nop 6
1
# -*- coding: utf-8 -*-
2
3
"""
4
solph version of oemof.network.Converter including
5
sets, variables, constraints and parts of the objective function
6
for ConverterBlock objects.
7
8
SPDX-FileCopyrightText: Uwe Krien <[email protected]>
9
SPDX-FileCopyrightText: Simon Hilpert
10
SPDX-FileCopyrightText: Cord Kaldemeyer
11
SPDX-FileCopyrightText: Patrik Schönfeldt
12
SPDX-FileCopyrightText: Stephan Günther
13
SPDX-FileCopyrightText: Birgit Schachler
14
SPDX-FileCopyrightText: jnnr
15
SPDX-FileCopyrightText: jmloenneberga
16
SPDX-FileCopyrightText: David Fuhrländer
17
SPDX-FileCopyrightText: Johannes Röder
18
SPDX-FileCopyrightText: Johannes Kochems
19
20
SPDX-License-Identifier: MIT
21
22
"""
23
24
from oemof.network import Node
25
from pyomo.core import BuildAction
26
from pyomo.core import Constraint
27
from pyomo.core.base.block import ScalarBlock
28
29
from oemof.solph._helpers import warn_if_missing_attribute
30
from oemof.solph._plumbing import sequence
31
32
33
class Converter(Node):
34
    """A linear ConverterBlock object with n inputs and n outputs.
35
36
    Node object that relates any number of inflow and outflows with
37
    conversion factors. Inputs and outputs must be given as dictinaries.
38
39
    Parameters
40
    ----------
41
    inputs : dict
42
        Dictionary with inflows. Keys must be the starting node(s) of the
43
        inflow(s).
44
    outputs : dict
45
        Dictionary with outflows. Keys must be the ending node(s) of the
46
        outflow(s).
47
    conversion_factors : dict
48
        Dictionary containing conversion factors for conversion of each flow.
49
        Keys must be the connected nodes (typically Buses).
50
        The dictionary values can either be a scalar or an iterable with
51
        individual conversion factors for each time step.
52
        Default: 1. If no conversion_factor is given for an in- or outflow, the
53
        conversion_factor is set to 1.
54
55
    Examples
56
    --------
57
    Defining an linear converter:
58
59
    >>> from oemof import solph
60
    >>> bgas = solph.buses.Bus(label='natural_gas')
61
    >>> bcoal = solph.buses.Bus(label='hard_coal')
62
    >>> bel = solph.buses.Bus(label='electricity')
63
    >>> bheat = solph.buses.Bus(label='heat')
64
65
    >>> trsf = solph.components.Converter(
66
    ...    label='pp_gas_1',
67
    ...    inputs={bgas: solph.flows.Flow(), bcoal: solph.flows.Flow()},
68
    ...    outputs={bel: solph.flows.Flow(), bheat: solph.flows.Flow()},
69
    ...    conversion_factors={bel: 0.3, bheat: 0.5,
70
    ...                        bgas: 0.8, bcoal: 0.2})
71
    >>> print(sorted([x[1][5] for x in trsf.conversion_factors.items()]))
72
    [0.2, 0.3, 0.5, 0.8]
73
74
    >>> type(trsf)
75
    <class 'oemof.solph.components._converter.Converter'>
76
77
    >>> sorted([str(i) for i in trsf.inputs])
78
    ['hard_coal', 'natural_gas']
79
80
    >>> trsf_new = solph.components.Converter(
81
    ...    label='pp_gas_2',
82
    ...    inputs={bgas: solph.flows.Flow()},
83
    ...    outputs={bel: solph.flows.Flow(), bheat: solph.flows.Flow()},
84
    ...    conversion_factors={bel: 0.3, bheat: 0.5})
85
    >>> trsf_new.conversion_factors[bgas][3]
86
    1
87
88
    Notes
89
    -----
90
    The following sets, variables, constraints and objective parts are created
91
     * :py:class:`~oemof.solph.components._converter.ConverterBlock`
92
    """
93
94
    def __init__(
95
        self,
96
        label=None,
97
        inputs=None,
98
        outputs=None,
99
        conversion_factors=None,
100
        custom_properties=None,
101
    ):
102
        if inputs is None:
103
            inputs = {}
104
        if outputs is None:
105
            outputs = {}
106
        if custom_properties is None:
107
            custom_properties = {}
108
109
        super().__init__(
110
            label=label,
111
            inputs=inputs,
112
            outputs=outputs,
113
            custom_properties=custom_properties,
114
        )
115
        if not inputs:
116
            warn_if_missing_attribute(self, "inputs")
117
        if not outputs:
118
            warn_if_missing_attribute(self, "outputs")
119
120
        if conversion_factors is None:
121
            conversion_factors = {}
122
123
        self.conversion_factors = {
124
            k: sequence(v) for k, v in conversion_factors.items()
125
        }
126
127
        missing_conversion_factor_keys = (
128
            set(self.outputs) | set(self.inputs)
129
        ) - set(self.conversion_factors)
130
131
        for cf in missing_conversion_factor_keys:
132
            self.conversion_factors[cf] = sequence(1)
133
134
    def constraint_group(self):
135
        return ConverterBlock
136
137
138
class ConverterBlock(ScalarBlock):
139
    r"""Block for the linear relation of nodes with type
140
    :class:`~oemof.solph.components._converter.ConverterBlock`
141
142
    **The following sets are created:** (-> see basic sets at
143
    :class:`.Model` )
144
145
    CONVERTERS
146
        A set with all
147
        :class:`~oemof.solph.components._converter.Converter` objects.
148
149
    **The following constraints are created:**
150
151
    Linear relation :attr:`om.ConverterBlock.relation[i,o,t]`
152
        .. math::
153
            P_{i}(t) \cdot \eta_{o}(t) =
154
            P_{o}(t) \cdot \eta_{i}(t), \\
155
            \forall t \in \textrm{TIMESTEPS}, \\
156
            \forall n \in \textrm{CONVERTERS}, \\
157
            \forall i \in \textrm{INPUTS}, \\
158
            \forall o \in \textrm{OUTPUTS}
159
160
    While INPUTS is the set of Bus objects connected with the input of the
161
    Transformer and OUPUTS the set of Bus objects connected with the output of
162
    the Transformer. The constraint above will be created for all combinations
163
    of INPUTS and OUTPUTS for all TIMESTEPS. A Transformer with two inflows and
164
    two outflows for one day with an hourly resolution will lead to 96
165
    constraints.
166
167
    The index :math: n is the index for the Transformer node itself. Therefore,
168
    a `flow[i, n, t]` is a flow from the Bus i to the Transformer n at
169
    time index p, t.
170
171
    ======================  ============================  ====================
172
    symbol                  attribute                     explanation
173
    ======================  ============================  ====================
174
    :math:`P_{i,n}(p, t)`   `flow[i, n, t]`               Converter, inflow
175
176
    :math:`P_{n,o}(p, t)`   `flow[n, o, t]`               Converter, outflow
177
178
    :math:`\eta_{i}(t)`     `conversion_factor[i, n, t]`  Inflow, efficiency
179
180
    :math:`\eta_{o}(t)`     `conversion_factor[n, o, t]`  Outflow, efficiency
181
182
    ======================  ============================  ====================
183
184
    """
185
186
    def __init__(self, *args, **kwargs):
187
        super().__init__(*args, **kwargs)
188
189
    def _create(self, group=None):
190
        """Creates the linear constraint for the class:`ConverterBlock`
191
        block.
192
193
        Parameters
194
        ----------
195
196
        group : list
197
            List of oemof.solph.components.Converters objects for which
198
            the linear relation of inputs and outputs is created
199
            e.g. group = [trsf1, trsf2, trsf3, ...]. Note that the relation
200
            is created for all existing relations of all inputs and all outputs
201
            of the converter. The components inside the list need to hold
202
            an attribute `conversion_factors` of type dict containing the
203
            conversion factors for all inputs to outputs.
204
        """
205
        if group is None:
206
            return None
207
208
        m = self.parent_block()
209
210
        in_flows = {n: [i for i in n.inputs.keys()] for n in group}
211
        out_flows = {n: [o for o in n.outputs.keys()] for n in group}
212
213
        self.relation = Constraint(
214
            [
215
                (n, i, o, t)
216
                for t in m.TIMESTEPS
217
                for n in group
218
                for o in out_flows[n]
219
                for i in in_flows[n]
220
            ],
221
            noruleinit=True,
222
        )
223
224
        def _input_output_relation(block):
225
            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...
226
                for n in group:
227
                    for o in out_flows[n]:
0 ignored issues
show
introduced by
The variable out_flows does not seem to be defined for all execution paths.
Loading history...
228
                        for i in in_flows[n]:
0 ignored issues
show
introduced by
The variable in_flows does not seem to be defined for all execution paths.
Loading history...
229
                            try:
230
                                lhs = (
231
                                    m.flow[i, n, t]
232
                                    * n.conversion_factors[o][t]
233
                                )
234
                                rhs = (
235
                                    m.flow[n, o, t]
236
                                    * n.conversion_factors[i][t]
237
                                )
238
                            except ValueError:
239
                                raise ValueError(
240
                                    "Error in constraint creation",
241
                                    "source: {0}, target: {1}".format(
242
                                        n.label, o.label
243
                                    ),
244
                                )
245
                            block.relation.add((n, i, o, t), (lhs == rhs))
246
247
        self.relation_build = BuildAction(rule=_input_output_relation)
248