• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

oemof / oemof-solph / 17377487432

01 Sep 2025 12:22PM UTC coverage: 81.765% (-5.8%) from 87.55%
17377487432

Pull #1174

github

web-flow
Merge 553ade8ee into 49e9272c9
Pull Request #1174: Feature/custom attributes for investments

925 of 1228 branches covered (75.33%)

Branch coverage included in aggregate %.

2 of 17 new or added lines in 2 files covered. (11.76%)

179 existing lines in 7 files now uncovered.

2707 of 3214 relevant lines covered (84.23%)

0.84 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.03
/src/oemof/solph/components/_link.py
1
# -*- coding: utf-8 -*-
2

3
"""
4
Link to connect two Busses.
5

6
SPDX-FileCopyrightText: Uwe Krien <krien@uni-bremen.de>
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
SPDX-FileCopyrightText: Johannes Kochems
15

16
SPDX-License-Identifier: MIT
17

18
"""
19
from warnings import warn
1✔
20

21
from oemof.network import Node
1✔
22
from oemof.tools import debugging
1✔
23
from pyomo.core import Set
1✔
24
from pyomo.core.base.block import ScalarBlock
1✔
25
from pyomo.environ import BuildAction
1✔
26
from pyomo.environ import Constraint
1✔
27

28
from oemof.solph._helpers import warn_if_missing_attribute
1✔
29
from oemof.solph._plumbing import sequence
1✔
30

31

32
class Link(Node):
1✔
33
    """A Link object with 2 inputs and 2 outputs.
34

35
    Parameters
36
    ----------
37
    inputs : dict
38
        Dictionary with inflows. Keys must be the starting node(s) of the
39
        inflow(s).
40
    outputs : dict
41
        Dictionary with outflows. Keys must be the ending node(s) of the
42
        outflow(s).
43
    conversion_factors : dict
44
        Dictionary containing conversion factors for conversion of each flow.
45
        Keys are the connected tuples (input, output) bus objects.
46
        The dictionary values can either be a scalar or an iterable with length
47
        of time horizon for simulation.
48

49
    Notes
50
    -----
51
    The sets, variables, constraints and objective parts are created
52
     * :py:class:`~oemof.solph.components._link.LinkBlock`
53

54
    Examples
55
    --------
56

57
    >>> from oemof import solph
58
    >>> bel0 = solph.buses.Bus(label="el0")
59
    >>> bel1 = solph.buses.Bus(label="el1")
60

61
    >>> link = solph.components.Link(
62
    ...    label="transshipment_link",
63
    ...    inputs={bel0: solph.flows.Flow(nominal_capacity=4),
64
    ...            bel1: solph.flows.Flow(nominal_capacity=2)},
65
    ...    outputs={bel0: solph.flows.Flow(),
66
    ...             bel1: solph.flows.Flow()},
67
    ...    conversion_factors={(bel0, bel1): 0.8, (bel1, bel0): 0.9})
68
    >>> print(sorted([x[1][5] for x in link.conversion_factors.items()]))
69
    [0.8, 0.9]
70

71
    >>> type(link)
72
    <class 'oemof.solph.components._link.Link'>
73

74
    >>> sorted([str(i) for i in link.inputs])
75
    ['el0', 'el1']
76

77
    >>> link.conversion_factors[(bel0, bel1)][3]
78
    0.8
79
    """
80

81
    def __init__(
1✔
82
        self,
83
        label=None,
84
        inputs=None,
85
        outputs=None,
86
        conversion_factors=None,
87
        custom_attributes=None,
88
    ):
89
        # compatibility with omeof.network w/o explicit named arguments
90
        if inputs is None:
1✔
91
            inputs = {}
1✔
92
        if outputs is None:
1✔
93
            outputs = {}
1✔
94
        if custom_attributes is None:
1!
95
            custom_attributes = {}
1✔
96
        super().__init__(
1✔
97
            label,
98
            inputs=inputs,
99
            outputs=outputs,
100
            custom_properties=custom_attributes,
101
        )
102
        if not inputs:
1✔
103
            warn_if_missing_attribute(self, "inputs")
1✔
104
        if not outputs:
1✔
105
            warn_if_missing_attribute(self, "outputs")
1✔
106
        if conversion_factors is None:
1✔
107
            warn_if_missing_attribute(self, "conversion_factors")
1✔
108
            conversion_factors = {}
1✔
109
        self.conversion_factors = {
1✔
110
            k: sequence(v) for k, v in conversion_factors.items()
111
        }
112
        msg = (
1✔
113
            "Component `Link` should have exactly "
114
            + "2 inputs, 2 outputs, and 2 "
115
            + "conversion factors connecting these. You are initializing "
116
            + "a `Link`without obeying this specification. "
117
            + "If this is intended and you know what you are doing you can "
118
            + "disable the SuspiciousUsageWarning globally."
119
        )
120

121
        if (
1✔
122
            len(self.inputs) != 2
123
            or len(self.outputs) != 2
124
            or len(self.conversion_factors) != 2
125
        ):
126
            warn(msg, debugging.SuspiciousUsageWarning)
1✔
127

128
    def constraint_group(self):
1✔
129
        return LinkBlock
1✔
130

131

132
class LinkBlock(ScalarBlock):
1✔
133
    r"""Block for the relation of nodes with type
134
    :class:`~oemof.solph.components.Link`
135

136
    **The following constraints are created:**
137

138
    .. _Link-equations:
139

140
    .. math::
141
        &
142
        (1) \qquad P_{\mathrm{in},n}(p, t) = c_n(t)
143
        \times P_{\mathrm{out},n}(p, t)
144
            \quad \forall t \in T, \forall n in {1,2} \\
145
        &
146

147
    """
148

149
    CONSTRAINT_GROUP = True
1✔
150

151
    def __init__(self, *args, **kwargs):
1✔
152
        super().__init__(*args, **kwargs)
1✔
153

154
    def _create(self, group=None):
1✔
155
        """Creates the relation for the class:`Link`.
156

157
        Parameters
158
        ----------
159
        group : list
160
            List of oemof.solph.components.Link objects for which
161
            the relation of inputs and outputs is createdBuildAction
162
            e.g. group = [link1, link2, link3, ...]. The components inside
163
            the list need to hold an attribute `conversion_factors` of type
164
            dict containing the conversion factors for all inputs to outputs.
165
        """
166
        if group is None:
1!
167
            return None
×
168

169
        m = self.parent_block()
1✔
170

171
        all_conversions = {}
1✔
172
        for n in group:
1✔
173
            all_conversions[n] = {
1✔
174
                k: v for k, v in n.conversion_factors.items()
175
            }
176

177
        self.LINKS = Set(initialize=[g for g in group])
1✔
178

179
        def _input_output_relation(block):
1✔
180
            for t in m.TIMESTEPS:
1!
181
                for n, conversion in all_conversions.items():
1!
182
                    for cidx, c in conversion.items():
1!
183
                        try:
1✔
184
                            expr = (
1✔
185
                                m.flow[n, cidx[1], t]
186
                                == c[t] * m.flow[cidx[0], n, t]
187
                            )
188
                        except KeyError:
1✔
189
                            raise KeyError(
1✔
190
                                "Error in constraint creation "
191
                                f"from: {cidx[0]}, to: {cidx[1]}, via: {n}. "
192
                                "Check if all connected buses match "
193
                                "the conversion factors.",
194
                            )
UNCOV
195
                        block.relation.add((n, cidx[0], cidx[1], t), expr)
×
196

197
        self.relation = Constraint(
1✔
198
            [
199
                (n, cidx[0], cidx[1], t)
200
                for t in m.TIMESTEPS
201
                for n, conversion in all_conversions.items()
202
                for cidx, c in conversion.items()
203
            ],
204
            noruleinit=True,
205
        )
206
        self.relation_build = BuildAction(rule=_input_output_relation)
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc