solph._options   A
last analyzed

Complexity

Total Complexity 25

Size/Duplication

Total Lines 275
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 25
eloc 108
dl 0
loc 275
rs 10
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A Investment._check_invest_attributes_offset() 0 8 3
A Investment._check_invest_attributes() 0 9 3
A Investment._check_invest_attributes_maximum() 0 12 3
A Investment._check_age_and_lifetime() 0 11 3
A Investment.__init__() 0 39 3
A NonConvex.__init__() 0 38 4
A Investment._check_nonconvex() 0 11 4
A Investment._check_invest_attributes_nonconvex() 0 8 2
1
# -*- coding: utf-8 -*-
2
3
"""Optional classes to be added to a network class.
4
5
SPDX-FileCopyrightText: Uwe Krien <[email protected]>
6
SPDX-FileCopyrightText: Simon Hilpert
7
SPDX-FileCopyrightText: Cord Kaldemeyer
8
SPDX-FileCopyrightText: Stephan Günther
9
SPDX-FileCopyrightText: Patrik Schönfeldt
10
SPDX-FileCopyrightText: jmloenneberga
11
SPDX-FileCopyrightText: Johannes Kochems
12
SPDX-FileCopyrightText: Malte Fritz
13
SPDX-FileCopyrightText: Jonas Freißmann
14
15
SPDX-License-Identifier: MIT
16
17
"""
18
from warnings import warn
19
20
from oemof.tools import debugging
21
22
from oemof.solph._plumbing import sequence
23
24
25
class Investment:
26
    """Defines an Investment object holding all the specifications needed
27
    for investment modeling.
28
29
    Parameters
30
    ----------
31
    maximum : float, :math:`P_{invest,max}(p)` or :math:`E_{invest,max}(p)`
32
        Maximum of the additional invested capacity;
33
        defined per period p for a multi-period model.
34
    minimum : float, :math:`P_{invest,min}(p)` or :math:`E_{invest,min}(p)`
35
        Minimum of the additional invested capacity. If `nonconvex` is `True`,
36
        `minimum` defines the threshold for the invested capacity;
37
        defined per period p for a multi-period model.
38
    ep_costs : float, :math:`c_{invest,var}`
39
        Equivalent periodical costs or investment expenses for the investment
40
41
        * For a standard model: equivalent periodical costs for the investment
42
          per flow capacity, i.e. annuities for investments already calculated.
43
        * For a multi-period model: Investment expenses for the respective
44
          period (in nominal terms). Annuities are calculated within the
45
          objective term, also considering age and lifetime.
46
    existing : float, :math:`P_{exist}` or :math:`E_{exist}`
47
        Existing / installed capacity. The invested capacity is added on top
48
        of this value. Hence, existing capacities come at no additional costs.
49
        Not applicable if `nonconvex` is set to `True`.
50
    nonconvex : bool
51
        If `True`, a binary variable for the status of the investment is
52
        created. This enables additional fix investment costs (*offset*)
53
        independent of the invested flow capacity. Therefore, use the `offset`
54
        parameter.
55
    offset : float, :math:`c_{invest,fix}`
56
        Additional fixed investment costs. Only applicable if `nonconvex` is
57
        set to `True`.
58
    overall_maximum : float, :math:`P_{overall,max}` or :math:`E_{overall,max}`
59
        Overall maximum capacity investment, i.e. the amount of capacity
60
        that can be totally installed at maximum in any period (taking into
61
        account decommissionings); only applicable for multi-period models
62
    overall_minimum : float :math:`P_{overall,min}` or :math:`E_{overall,min}`
63
        Overall minimum capacity investment that needs to be installed
64
        in the last period of the optimization (taking into account
65
        decommissionings); only applicable for multi-period models
66
    lifetime : int, :math:`l`
67
        Units lifetime, given in years; only applicable for multi-period
68
        models
69
    age : int, :math:`a`
70
        Units start age, given in years at the beginning of the optimization;
71
        only applicable for multi-period models
72
    fixed_costs : float or list of float, :math:`c_{fixed}(p)`
73
        Fixed costs in each period (given in nominal terms);
74
        only applicable for multi-period models
75
76
77
    For the variables, constraints and parts of the objective function, which
78
    are created, see
79
    :py:class:`~oemof.solph.blocks.investment_flow.InvestmentFlow`,
80
    :py:class:`~oemof.solph.components.generic_storage.GenericInvestmentStorageBlock`
81
    :py:class:`~oemof.solph.custom.sink_dsm.SinkDSMOemofInvestmentBlock`,
82
    :py:class:`~oemof.solph.custom.sink_dsm.SinkDSMDLRInvestmentBlock` and
83
    :py:class:`~oemof.solph.custom.sink_dsm.SinkDSMDIWInvestmentBlock`.
84
85
    """  # noqa: E501
86
87
    def __init__(
88
        self,
89
        maximum=float("+inf"),
90
        minimum=0,
91
        ep_costs=0,
92
        existing=0,
93
        nonconvex=False,
94
        offset=0,
95
        overall_maximum=None,
96
        overall_minimum=None,
97
        lifetime=None,
98
        age=0,
99
        fixed_costs=None,
100
        custom_attributes=None,
101
    ):
102
        if custom_attributes is None:
103
            custom_attributes = {}
104
        self.maximum = sequence(maximum)
105
        self.minimum = sequence(minimum)
106
        self.ep_costs = sequence(ep_costs)
107
        self.existing = existing
108
        self.nonconvex = nonconvex
109
        self.offset = sequence(offset)
110
        self.overall_maximum = overall_maximum
111
        self.overall_minimum = overall_minimum
112
        self.lifetime = lifetime
113
        self.age = age
114
        self.fixed_costs = sequence(fixed_costs)
115
116
        for attribute in custom_attributes.keys():
117
            value = custom_attributes.get(attribute)
118
            setattr(self, attribute, value)
119
120
        self._check_invest_attributes()
121
        self._check_invest_attributes_maximum()
122
        self._check_invest_attributes_offset()
123
        self._check_age_and_lifetime()
124
        self._check_invest_attributes_nonconvex()
125
        self._check_nonconvex()
126
127
    def _check_invest_attributes(self):
128
        """Throw an error if existing is other than 0 and nonconvex is True"""
129
        if (self.existing != 0) and (self.nonconvex is True):
130
            e1 = (
131
                "Values for 'offset' and 'existing' are given in"
132
                " investement attributes. \n These two options cannot be "
133
                "considered at the same time."
134
            )
135
            raise AttributeError(e1)
136
137
    def _check_invest_attributes_maximum(self):
138
        """Throw an error if maximum is infinite and nonconvex is True"""
139
        if (self.maximum[0] == float("+inf")) and (self.nonconvex is True):
140
            e2 = (
141
                "Please provide a maximum investment value in case of"
142
                " nonconvex investment (nonconvex=True), which is in the"
143
                " expected magnitude."
144
                " \nVery high maximum values (> 10e8) as maximum investment"
145
                " limit might lead to numeric issues, so that no investment"
146
                " is done, although it is the optimal solution!"
147
            )
148
            raise AttributeError(e2)
149
150
    def _check_invest_attributes_offset(self):
151
        """Throw an error if offset is given without nonconvex=True"""
152
        if (self.offset[0] != 0) and (self.nonconvex is False):
153
            e3 = (
154
                "If `nonconvex` is `False`, the `offset` parameter will be"
155
                " ignored."
156
            )
157
            raise AttributeError(e3)
158
159
    def _check_age_and_lifetime(self):
160
        """Throw an error if age is chosen greater or equal to lifetime;
161
        only applicable for multi-period models
162
        """
163
        if self.lifetime is not None:
164
            if self.age >= self.lifetime:
165
                e4 = (
166
                    "A unit's age must be smaller than its "
167
                    "expected lifetime."
168
                )
169
                raise AttributeError(e4)
170
171
    def _check_invest_attributes_nonconvex(self):
172
        """Throw an error if nonconvex is not of type boolean."""
173
        if not isinstance(self.nonconvex, bool):
174
            e5 = (
175
                "The `nonconvex` parameter of the `Investment` class has to be"
176
                + f" of type boolean, not {type(self.nonconvex)}."
177
            )
178
            raise AttributeError(e5)
179
180
    def _check_nonconvex(self):
181
        """Checking for unnecessary setting of nonconvex"""
182
        if self.nonconvex:
183
            if (self.minimum.min() == 0) and (self.offset.min() == 0):
184
                msg = (
185
                    "It is not necessary to set the investment to `nonconvex` "
186
                    "if `minimum` and `offset` are 0.\n"
187
                    "This can lead to the `invest_status` variable becoming "
188
                    "1, even if the `nominal_capacity` is optimized to 0."
189
                )
190
                warn(msg, debugging.SuspiciousUsageWarning)
191
192
193
class NonConvex:
194
    """Defines a NonConvex object holding all the specifications for NonConvex
195
    Flows, i.e. Flows with binary variables associated to them.
196
197
    Parameters
198
    ----------
199
    startup_costs : numeric (iterable or scalar)
200
        Costs associated with a start of the flow (representing a unit).
201
    shutdown_costs : numeric (iterable or scalar)
202
        Costs associated with the shutdown of the flow (representing a unit).
203
    activity_costs : numeric (iterable or scalar)
204
        Costs associated with the active operation of the flow, independently
205
        from the actual output.
206
    inactivity_costs : numeric (iterable or scalar)
207
        Costs associated with not operating the flow.
208
    minimum_uptime : numeric or list of numeric (1 or positive integer)
209
        Minimum number of time steps that a flow must be greater then its
210
        minimum flow after startup. Be aware that minimum up and downtimes
211
        can contradict each other and may lead to infeasible problems.
212
    minimum_downtime : numeric or list of numeric (1 or positive integer)
213
        Minimum number of time steps a flow is forced to zero after
214
        shutting down. Be aware that minimum up and downtimes can
215
        contradict each other and may to infeasible problems.
216
    maximum_startups : numeric (0 or positive integer)
217
        Maximum number of start-ups in the optimization timeframe.
218
    maximum_shutdowns : numeric (0 or positive integer)
219
        Maximum number of shutdowns in the optimization timeframe.
220
    initial_status : numeric (0 or 1)
221
        Integer value indicating the status of the flow in the first time step
222
        (0 = off, 1 = on). For minimum up and downtimes, the initial status
223
        is set for the respective values in the beginning e.g. if a
224
        minimum uptime of four timesteps is defined and the initial status is
225
        set to one, the initial status is fixed for the four first timesteps
226
        of the optimization period. Otherwise if the initial status is set to
227
        zero and the first timesteps are fixed for the number of minimum
228
        downtime steps.
229
    negative_gradient_limit : numeric (iterable, scalar or None)
230
        the normed *upper bound* on the positive difference
231
        (`flow[t-1] < flow[t]`) of two consecutive flow values.
232
    positive_gradient_limit : numeric (iterable, scalar or None)
233
            the normed *upper bound* on the negative difference
234
            (`flow[t-1] > flow[t]`) of two consecutive flow values.
235
    """
236
237
    def __init__(
238
        self,
239
        initial_status=0,
240
        minimum_uptime=0,
241
        minimum_downtime=0,
242
        maximum_startups=None,
243
        maximum_shutdowns=None,
244
        startup_costs=None,
245
        shutdown_costs=None,
246
        activity_costs=None,
247
        inactivity_costs=None,
248
        negative_gradient_limit=None,
249
        positive_gradient_limit=None,
250
        custom_attributes=None,
251
    ):
252
        if custom_attributes is None:
253
            custom_attributes = {}
254
255
        self.initial_status = initial_status
256
        self.minimum_uptime = sequence(minimum_uptime)
257
        self.minimum_downtime = sequence(minimum_downtime)
258
        self.maximum_startups = maximum_startups
259
        self.maximum_shutdowns = maximum_shutdowns
260
261
        self.startup_costs = sequence(startup_costs)
262
        self.shutdown_costs = sequence(shutdown_costs)
263
        self.activity_costs = sequence(activity_costs)
264
        self.inactivity_costs = sequence(inactivity_costs)
265
        self.negative_gradient_limit = sequence(negative_gradient_limit)
266
        self.positive_gradient_limit = sequence(positive_gradient_limit)
267
268
        for attribute, value in custom_attributes.items():
269
            setattr(self, attribute, value)
270
271
        if initial_status == 0:
272
            self.first_flexible_timestep = self.minimum_downtime[0]
273
        else:
274
            self.first_flexible_timestep = self.minimum_uptime[0]
275