Passed
Pull Request — dev (#852)
by Uwe
07:13 queued 05:59
created

constraint_tests.TestsConstraint.test_flow_idle()   A

Complexity

Conditions 1

Size

Total Lines 27
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 19
dl 0
loc 27
rs 9.45
c 0
b 0
f 0
cc 1
nop 1
1
# -*- coding: utf-8 -
2
3
"""Test the created constraints against approved constraints.
4
5
This file is part of project oemof (github.com/oemof/oemof). It's copyrighted
6
by the contributors recorded in the version control history of the file,
7
available from its original location oemof/tests/constraint_tests.py
8
9
SPDX-License-Identifier: MIT
10
"""
11
12
import logging
13
import re
14
from difflib import unified_diff
15
from os import path as ospath
16
17
import pandas as pd
18
import pytest
19
from nose.tools import assert_raises
20
from nose.tools import eq_
21
from oemof.network.network import Node
22
23
from oemof import solph
24
25
logging.disable(logging.INFO)
26
27
28
class TestsConstraint:
29
    @classmethod
30
    def setup_class(cls):
31
        cls.objective_pattern = re.compile(
32
            r"^objective.*(?=s\.t\.)", re.DOTALL | re.MULTILINE
33
        )
34
35
        cls.date_time_index = pd.date_range("1/1/2012", periods=3, freq="H")
36
37
        cls.tmppath = solph.helpers.extend_basic_path("tmp")
38
        logging.info(cls.tmppath)
39
40
    def setup(self):
41
        self.energysystem = solph.EnergySystem(
42
            groupings=solph.GROUPINGS, timeindex=self.date_time_index
43
        )
44
        Node.registry = self.energysystem
45
46
    def get_om(self):
47
        return solph.Model(
48
            self.energysystem, timeindex=self.energysystem.timeindex
49
        )
50
51
    def compare_lp_files(self, filename, ignored=None, my_om=None):
52
        r"""Compare lp-files to check constraints generated within solph.
53
54
        An lp-file is being generated automatically when the tests are
55
        executed. Make sure that you create an empty file first and
56
        transfer the content from the one that has been created automatically
57
        into this one afterwards. Please ensure that the content is being
58
        checked carefully. Otherwise, errors are included within the code base.
59
        """
60
        if my_om is None:
61
            om = self.get_om()
62
        else:
63
            om = my_om
64
        tmp_filename = filename.replace(".lp", "") + "_tmp.lp"
65
        new_filename = ospath.join(self.tmppath, tmp_filename)
66
        om.write(new_filename, io_options={"symbolic_solver_labels": True})
67
        logging.info("Comparing with file: {0}".format(filename))
68
        with open(ospath.join(self.tmppath, tmp_filename)) as generated_file:
69
            with open(
70
                ospath.join(
71
                    ospath.dirname(ospath.realpath(__file__)),
72
                    "lp_files",
73
                    filename,
74
                )
75
            ) as expected_file:
76
77
                def chop_trailing_whitespace(lines):
78
                    return [re.sub(r"\s*$", "", ln) for ln in lines]
79
80
                def remove(pattern, lines):
81
                    if not pattern:
82
                        return lines
83
                    return re.subn(pattern, "", "\n".join(lines))[0].split(
84
                        "\n"
85
                    )
86
87
                expected = remove(
88
                    ignored,
89
                    chop_trailing_whitespace(expected_file.readlines()),
90
                )
91
                generated = remove(
92
                    ignored,
93
                    chop_trailing_whitespace(generated_file.readlines()),
94
                )
95
96
                def normalize_to_positive_results(lines):
97
                    negative_result_indices = [
98
                        n
99
                        for n, line in enumerate(lines)
100
                        if re.match("^= -", line)
101
                    ]
102
                    equation_start_indices = [
103
                        [
104
                            n
105
                            for n in reversed(range(0, nri))
106
                            if re.match(".*:$", lines[n])
107
                        ][0]
108
                        + 1
109
                        for nri in negative_result_indices
110
                    ]
111
                    for (start, end) in zip(
112
                        equation_start_indices, negative_result_indices
113
                    ):
114
                        for n in range(start, end):
115
                            lines[n] = (
116
                                "-"
117
                                if lines[n] and lines[n][0] == "+"
118
                                else "+"
119
                                if lines[n]
120
                                else lines[n]
121
                            ) + lines[n][1:]
122
                        lines[end] = "= " + lines[end][3:]
123
                    return lines
124
125
                expected = normalize_to_positive_results(expected)
126
                generated = normalize_to_positive_results(generated)
127
128
                eq_(
129
                    generated,
130
                    expected,
131
                    "Failed matching expected with generated lp file:\n"
132
                    + "\n".join(
133
                        unified_diff(
134
                            expected,
135
                            generated,
136
                            fromfile=ospath.relpath(expected_file.name),
137
                            tofile=ospath.basename(generated_file.name),
138
                            lineterm="",
139
                        )
140
                    ),
141
                )
142
143
    def test_linear_transformer(self):
144
        """Constraint test of a Transformer without Investment."""
145
        bgas = solph.buses.Bus(label="gas")
146
147
        bel = solph.buses.Bus(label="electricity")
148
149
        solph.components.Transformer(
150
            label="powerplantGas",
151
            inputs={bgas: solph.flows.Flow()},
152
            outputs={
153
                bel: solph.flows.Flow(nominal_value=10e10, variable_costs=50)
154
            },
155
            conversion_factors={bel: 0.58},
156
        )
157
158
        self.compare_lp_files("linear_transformer.lp")
159
160 View Code Duplication
    def test_linear_transformer_invest(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
161
        """Constraint test of a Transformer with Investment."""
162
163
        bgas = solph.buses.Bus(label="gas")
164
165
        bel = solph.buses.Bus(label="electricity")
166
167
        solph.components.Transformer(
168
            label="powerplant_gas",
169
            inputs={bgas: solph.flows.Flow()},
170
            outputs={
171
                bel: solph.flows.Flow(
172
                    variable_costs=50,
173
                    investment=solph.Investment(maximum=1000, ep_costs=20),
174
                )
175
            },
176
            conversion_factors={bel: 0.58},
177
        )
178
179
        self.compare_lp_files("linear_transformer_invest.lp")
180
181
    def test_max_source_min_sink(self):
182
        """ """
183
        bel = solph.buses.Bus(label="electricityBus")
184
185
        solph.components.Source(
186
            label="wind",
187
            outputs={
188
                bel: solph.flows.Flow(nominal_value=54, max=(0.85, 0.95, 0.61))
189
            },
190
        )
191
192
        solph.components.Sink(
193
            label="minDemand",
194
            inputs={
195
                bel: solph.flows.Flow(
196
                    nominal_value=54, min=(0.84, 0.94, 0.59), variable_costs=14
197
                )
198
            },
199
        )
200
201
        self.compare_lp_files("max_source_min_sink.lp")
202
203
    def test_fixed_source_variable_sink(self):
204
        """Constraint test with a fixed source and a variable sink."""
205
206
        bel = solph.buses.Bus(label="electricityBus")
207
208
        solph.components.Source(
209
            label="wind",
210
            outputs={
211
                bel: solph.flows.Flow(
212
                    fix=[0.43, 0.72, 0.29], nominal_value=10e5
213
                )
214
            },
215
        )
216
217
        solph.components.Sink(
218
            label="excess", inputs={bel: solph.flows.Flow(variable_costs=40)}
219
        )
220
221
        self.compare_lp_files("fixed_source_variable_sink.lp")
222
223
    def test_nominal_value_to_zero(self):
224
        """If the nominal value is set to zero nothing should happen."""
225
        bel = solph.buses.Bus(label="electricityBus")
226
227
        solph.components.Source(
228
            label="s1", outputs={bel: solph.flows.Flow(nominal_value=0)}
229
        )
230
        self.compare_lp_files("nominal_value_to_zero.lp")
231
232
    def test_fixed_source_invest_sink(self):
233
        """
234
        Wrong constraints for fixed source + invest sink w.
235
        `full_load_time_max`.
236
        """
237
238
        bel = solph.buses.Bus(label="electricityBus")
239
240
        solph.components.Source(
241
            label="wind",
242
            outputs={
243
                bel: solph.flows.Flow(fix=[12, 16, 14], nominal_value=1000000)
244
            },
245
        )
246
247
        solph.components.Sink(
248
            label="excess",
249
            inputs={
250
                bel: solph.flows.InvestmentFlow(
251
                    full_load_time_max=2.3,
252
                    variable_costs=25,
253
                    max=0.8,
254
                    investment=solph.Investment(
255
                        ep_costs=500, maximum=10e5, existing=50
256
                    ),
257
                )
258
            },
259
        )
260
261
        self.compare_lp_files("fixed_source_invest_sink.lp")
262
263
    def test_invest_source_fixed_sink(self):
264
        """Constraint test with a fixed sink and a dispatch invest source."""
265
266
        bel = solph.buses.Bus(label="electricityBus")
267
268
        solph.components.Source(
269
            label="pv",
270
            outputs={
271
                bel: solph.flows.Flow(
272
                    max=[45, 83, 65],
273
                    variable_costs=13,
274
                    investment=solph.Investment(ep_costs=123),
275
                )
276
            },
277
        )
278
279
        solph.components.Sink(
280
            label="excess",
281
            inputs={
282
                bel: solph.flows.Flow(fix=[0.5, 0.8, 0.3], nominal_value=10e4)
283
            },
284
        )
285
286
        self.compare_lp_files("invest_source_fixed_sink.lp")
287
288
    def test_storage(self):
289
        """ """
290
        bel = solph.buses.Bus(label="electricityBus")
291
292
        solph.components.GenericStorage(
293
            label="storage_no_invest",
294
            inputs={
295
                bel: solph.flows.Flow(nominal_value=16667, variable_costs=56)
296
            },
297
            outputs={
298
                bel: solph.flows.Flow(nominal_value=16667, variable_costs=24)
299
            },
300
            nominal_storage_capacity=10e4,
301
            loss_rate=0.13,
302
            inflow_conversion_factor=0.97,
303
            outflow_conversion_factor=0.86,
304
            initial_storage_level=0.4,
305
        )
306
307
        self.compare_lp_files("storage.lp")
308
309
    def test_storage_invest_1(self):
310
        """All invest variables are coupled. The invest variables of the Flows
311
        will be created during the initialisation of the storage e.g. battery
312
        """
313
        bel = solph.buses.Bus(label="electricityBus")
314
315
        solph.components.GenericStorage(
316
            label="storage1",
317
            inputs={bel: solph.flows.Flow(variable_costs=56)},
318
            outputs={bel: solph.flows.Flow(variable_costs=24)},
319
            nominal_storage_capacity=None,
320
            loss_rate=0.13,
321
            max_storage_level=0.9,
322
            min_storage_level=0.1,
323
            invest_relation_input_capacity=1 / 6,
324
            invest_relation_output_capacity=1 / 6,
325
            inflow_conversion_factor=0.97,
326
            outflow_conversion_factor=0.86,
327
            investment=solph.Investment(ep_costs=145, maximum=234),
328
        )
329
330
        self.compare_lp_files("storage_invest_1.lp")
331
332
    def test_storage_invest_2(self):
333
        """All can be free extended to their own cost."""
334
        bel = solph.buses.Bus(label="electricityBus")
335
336
        solph.components.GenericStorage(
337
            label="storage2",
338
            inputs={
339
                bel: solph.flows.Flow(investment=solph.Investment(ep_costs=99))
340
            },
341
            outputs={
342
                bel: solph.flows.Flow(investment=solph.Investment(ep_costs=9))
343
            },
344
            investment=solph.Investment(ep_costs=145),
345
            initial_storage_level=0.5,
346
        )
347
        self.compare_lp_files("storage_invest_2.lp")
348
349
    def test_storage_invest_3(self):
350
        """The storage capacity is fixed, but the Flows can be extended.
351
        e.g. PHES with a fixed basin but the pump and the turbine can be
352
        adapted
353
        """
354
        bel = solph.buses.Bus(label="electricityBus")
355
356
        solph.components.GenericStorage(
357
            label="storage3",
358
            inputs={
359
                bel: solph.flows.Flow(investment=solph.Investment(ep_costs=99))
360
            },
361
            outputs={
362
                bel: solph.flows.Flow(investment=solph.Investment(ep_costs=9))
363
            },
364
            nominal_storage_capacity=5000,
365
        )
366
        self.compare_lp_files("storage_invest_3.lp")
367
368
    def test_storage_invest_4(self):
369
        """Only the storage capacity can be extended."""
370
        bel = solph.buses.Bus(label="electricityBus")
371
372
        solph.components.GenericStorage(
373
            label="storage4",
374
            inputs={bel: solph.flows.Flow(nominal_value=80)},
375
            outputs={bel: solph.flows.Flow(nominal_value=100)},
376
            investment=solph.Investment(ep_costs=145, maximum=500),
377
        )
378
        self.compare_lp_files("storage_invest_4.lp")
379
380
    def test_storage_invest_5(self):
381
        """The storage capacity is fixed, but the Flows can be extended.
382
        e.g. PHES with a fixed basin but the pump and the turbine can be
383
        adapted. The installed capacity of the pump is 10 % bigger than the
384
        the capacity of the turbine due to 'invest_relation_input_output=1.1'.
385
        """
386
        bel = solph.buses.Bus(label="electricityBus")
387
388
        solph.components.GenericStorage(
389
            label="storage5",
390
            inputs={
391
                bel: solph.flows.Flow(
392
                    investment=solph.Investment(ep_costs=99, existing=110)
393
                )
394
            },
395
            outputs={
396
                bel: solph.flows.Flow(
397
                    investment=solph.Investment(existing=100)
398
                )
399
            },
400
            invest_relation_input_output=1.1,
401
            nominal_storage_capacity=10000,
402
        )
403
        self.compare_lp_files("storage_invest_5.lp")
404
405
    def test_storage_invest_6(self):
406
        """Like test_storage_invest_5 but there can also be an investment in
407
        the basin.
408
        """
409
        bel = solph.buses.Bus(label="electricityBus")
410
411
        solph.components.GenericStorage(
412
            label="storage6",
413
            inputs={
414
                bel: solph.flows.InvestmentFlow(
415
                    investment=solph.Investment(ep_costs=99, existing=110)
416
                )
417
            },
418
            outputs={
419
                bel: solph.flows.InvestmentFlow(
420
                    investment=solph.Investment(existing=100)
421
                )
422
            },
423
            invest_relation_input_output=1.1,
424
            investment=solph.Investment(ep_costs=145, existing=10000),
425
        )
426
        self.compare_lp_files("storage_invest_6.lp")
427
428
    def test_storage_minimum_invest(self):
429
        """All invest variables are coupled. The invest variables of the Flows
430
        will be created during the initialisation of the storage e.g. battery
431
        """
432
        bel = solph.buses.Bus(label="electricityBus")
433
434
        solph.components.GenericStorage(
435
            label="storage1",
436
            inputs={bel: solph.flows.Flow()},
437
            outputs={bel: solph.flows.Flow()},
438
            investment=solph.Investment(
439
                ep_costs=145, minimum=100, maximum=200
440
            ),
441
        )
442
443
        self.compare_lp_files("storage_invest_minimum.lp")
444
445
    def test_storage_unbalanced(self):
446
        """Testing a unbalanced storage (e.g. battery)."""
447
        bel = solph.buses.Bus(label="electricityBus")
448
449
        solph.components.GenericStorage(
450
            label="storage1",
451
            inputs={bel: solph.flows.Flow()},
452
            outputs={bel: solph.flows.Flow()},
453
            nominal_storage_capacity=1111,
454
            initial_storage_level=None,
455
            balanced=False,
456
            invest_relation_input_capacity=1,
457
            invest_relation_output_capacity=1,
458
        )
459
        self.compare_lp_files("storage_unbalanced.lp")
460
461
    def test_storage_invest_unbalanced(self):
462
        """Testing a unbalanced storage (e.g. battery)."""
463
        bel = solph.buses.Bus(label="electricityBus")
464
465
        solph.components.GenericStorage(
466
            label="storage1",
467
            inputs={bel: solph.flows.Flow()},
468
            outputs={bel: solph.flows.Flow()},
469
            nominal_storage_capacity=None,
470
            initial_storage_level=0.5,
471
            balanced=False,
472
            invest_relation_input_capacity=1,
473
            invest_relation_output_capacity=1,
474
            investment=solph.Investment(ep_costs=145),
475
        )
476
        self.compare_lp_files("storage_invest_unbalanced.lp")
477
478
    def test_storage_fixed_losses(self):
479
        """ """
480
        bel = solph.buses.Bus(label="electricityBus")
481
482
        solph.components.GenericStorage(
483
            label="storage_no_invest",
484
            inputs={
485
                bel: solph.flows.Flow(nominal_value=16667, variable_costs=56)
486
            },
487
            outputs={
488
                bel: solph.flows.Flow(nominal_value=16667, variable_costs=24)
489
            },
490
            nominal_storage_capacity=1e5,
491
            loss_rate=0.13,
492
            fixed_losses_relative=0.01,
493
            fixed_losses_absolute=3,
494
            inflow_conversion_factor=0.97,
495
            outflow_conversion_factor=0.86,
496
            initial_storage_level=0.4,
497
        )
498
499
        self.compare_lp_files("storage_fixed_losses.lp")
500
501
    def test_storage_invest_1_fixed_losses(self):
502
        """All invest variables are coupled. The invest variables of the Flows
503
        will be created during the initialisation of the storage e.g. battery
504
        """
505
        bel = solph.buses.Bus(label="electricityBus")
506
507
        solph.components.GenericStorage(
508
            label="storage1",
509
            inputs={bel: solph.flows.Flow(variable_costs=56)},
510
            outputs={bel: solph.flows.Flow(variable_costs=24)},
511
            nominal_storage_capacity=None,
512
            loss_rate=0.13,
513
            fixed_losses_relative=0.01,
514
            fixed_losses_absolute=3,
515
            max_storage_level=0.9,
516
            min_storage_level=0.1,
517
            invest_relation_input_capacity=1 / 6,
518
            invest_relation_output_capacity=1 / 6,
519
            inflow_conversion_factor=0.97,
520
            outflow_conversion_factor=0.86,
521
            investment=solph.Investment(ep_costs=145, maximum=234),
522
        )
523
524
        self.compare_lp_files("storage_invest_1_fixed_losses.lp")
525
526
    def test_transformer(self):
527
        """Constraint test of a LinearN1Transformer without Investment."""
528
        bgas = solph.buses.Bus(label="gasBus")
529
        bbms = solph.buses.Bus(label="biomassBus")
530
        bel = solph.buses.Bus(label="electricityBus")
531
        bth = solph.buses.Bus(label="thermalBus")
532
533
        solph.components.Transformer(
534
            label="powerplantGasCoal",
535
            inputs={bbms: solph.flows.Flow(), bgas: solph.flows.Flow()},
536
            outputs={
537
                bel: solph.flows.Flow(variable_costs=50),
538
                bth: solph.flows.Flow(nominal_value=5e10, variable_costs=20),
539
            },
540
            conversion_factors={bgas: 0.4, bbms: 0.1, bel: 0.3, bth: 0.5},
541
        )
542
543
        self.compare_lp_files("transformer.lp")
544
545 View Code Duplication
    def test_transformer_invest(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
546
        """Constraint test of a LinearN1Transformer with Investment."""
547
548
        bgas = solph.buses.Bus(label="gasBus")
549
        bcoal = solph.buses.Bus(label="coalBus")
550
        bel = solph.buses.Bus(label="electricityBus")
551
        bth = solph.buses.Bus(label="thermalBus")
552
553
        solph.components.Transformer(
554
            label="powerplant_gas_coal",
555
            inputs={bgas: solph.flows.Flow(), bcoal: solph.flows.Flow()},
556
            outputs={
557
                bel: solph.flows.Flow(
558
                    variable_costs=50,
559
                    investment=solph.Investment(maximum=1000, ep_costs=20),
560
                ),
561
                bth: solph.flows.Flow(variable_costs=20),
562
            },
563
            conversion_factors={bgas: 0.58, bcoal: 0.2, bel: 0.3, bth: 0.5},
564
        )
565
566
        self.compare_lp_files("transformer_invest.lp")
567
568 View Code Duplication
    def test_transformer_invest_with_existing(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
569
        """Constraint test of a LinearN1Transformer with Investment."""
570
571
        bgas = solph.buses.Bus(label="gasBus")
572
        bcoal = solph.buses.Bus(label="coalBus")
573
        bel = solph.buses.Bus(label="electricityBus")
574
        bth = solph.buses.Bus(label="thermalBus")
575
576
        solph.components.Transformer(
577
            label="powerplant_gas_coal",
578
            inputs={bgas: solph.flows.Flow(), bcoal: solph.flows.Flow()},
579
            outputs={
580
                bel: solph.flows.Flow(
581
                    variable_costs=50,
582
                    investment=solph.Investment(
583
                        maximum=1000, ep_costs=20, existing=200
584
                    ),
585
                ),
586
                bth: solph.flows.Flow(variable_costs=20),
587
            },
588
            conversion_factors={bgas: 0.58, bcoal: 0.2, bel: 0.3, bth: 0.5},
589
        )
590
591
        self.compare_lp_files("transformer_invest_with_existing.lp")
592
593
    def test_linear_transformer_chp(self):
594
        """
595
        Constraint test of a Transformer without Investment (two outputs).
596
        """
597
        bgas = solph.buses.Bus(label="gasBus")
598
        bheat = solph.buses.Bus(label="heatBus")
599
        bel = solph.buses.Bus(label="electricityBus")
600
601
        solph.components.Transformer(
602
            label="CHPpowerplantGas",
603
            inputs={
604
                bgas: solph.flows.Flow(nominal_value=10e10, variable_costs=50)
605
            },
606
            outputs={bel: solph.flows.Flow(), bheat: solph.flows.Flow()},
607
            conversion_factors={bel: 0.4, bheat: 0.5},
608
        )
609
610
        self.compare_lp_files("linear_transformer_chp.lp")
611
612 View Code Duplication
    def test_linear_transformer_chp_invest(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
613
        """Constraint test of a Transformer with Investment (two outputs)."""
614
615
        bgas = solph.buses.Bus(label="gasBus")
616
        bheat = solph.buses.Bus(label="heatBus")
617
        bel = solph.buses.Bus(label="electricityBus")
618
619
        solph.components.Transformer(
620
            label="chp_powerplant_gas",
621
            inputs={
622
                bgas: solph.flows.Flow(
623
                    variable_costs=50,
624
                    investment=solph.Investment(maximum=1000, ep_costs=20),
625
                )
626
            },
627
            outputs={bel: solph.flows.Flow(), bheat: solph.flows.Flow()},
628
            conversion_factors={bel: 0.4, bheat: 0.5},
629
        )
630
631
        self.compare_lp_files("linear_transformer_chp_invest.lp")
632
633
    def test_variable_chp(self):
634
        """ """
635
        bel = solph.buses.Bus(label="electricityBus")
636
        bth = solph.buses.Bus(label="heatBus")
637
        bgas = solph.buses.Bus(label="commodityBus")
638
639
        solph.components.ExtractionTurbineCHP(
640
            label="variable_chp_gas1",
641
            inputs={bgas: solph.flows.Flow(nominal_value=100)},
642
            outputs={bel: solph.flows.Flow(), bth: solph.flows.Flow()},
643
            conversion_factors={bel: 0.3, bth: 0.5},
644
            conversion_factor_full_condensation={bel: 0.5},
645
        )
646
647
        solph.components.ExtractionTurbineCHP(
648
            label="variable_chp_gas2",
649
            inputs={bgas: solph.flows.Flow(nominal_value=100)},
650
            outputs={bel: solph.flows.Flow(), bth: solph.flows.Flow()},
651
            conversion_factors={bel: 0.3, bth: 0.5},
652
            conversion_factor_full_condensation={bel: 0.5},
653
        )
654
655
        self.compare_lp_files("variable_chp.lp")
656
657
    def test_generic_invest_limit(self):
658
        """ """
659
        bus = solph.buses.Bus(label="bus_1")
660
661
        solph.components.Source(
662
            label="source_0",
663
            outputs={
664
                bus: solph.flows.Flow(
665
                    investment=solph.Investment(ep_costs=50, space=4)
666
                )
667
            },
668
        )
669
670
        solph.components.Source(
671
            label="source_1",
672
            outputs={
673
                bus: solph.flows.Flow(
674
                    investment=solph.Investment(ep_costs=100, space=1)
675
                )
676
            },
677
        )
678
679
        solph.components.Source(
680
            label="source_2",
681
            outputs={
682
                bus: solph.flows.Flow(investment=solph.Investment(ep_costs=75))
683
            },
684
        )
685
686
        om = self.get_om()
687
688
        om = solph.constraints.additional_investment_flow_limit(
689
            om, "space", limit=20
690
        )
691
692
        self.compare_lp_files("generic_invest_limit.lp", my_om=om)
693
694
    def test_emission_constraints(self):
695
        """ """
696
        bel = solph.buses.Bus(label="electricityBus")
697
698
        solph.components.Source(
699
            label="source1",
700
            outputs={
701
                bel: solph.flows.Flow(
702
                    nominal_value=100, emission_factor=[0.5, -1.0, 2.0]
703
                )
704
            },
705
        )
706
        solph.components.Source(
707
            label="source2",
708
            outputs={
709
                bel: solph.flows.Flow(nominal_value=100, emission_factor=3.5)
710
            },
711
        )
712
713
        # Should be ignored because the emission attribute is not defined.
714
        solph.components.Source(
715
            label="source3", outputs={bel: solph.flows.Flow(nominal_value=100)}
716
        )
717
718
        om = self.get_om()
719
720
        solph.constraints.emission_limit(om, limit=777)
721
722
        self.compare_lp_files("emission_limit.lp", my_om=om)
723
724
    def test_flow_count_limit(self):
725
        """ """
726
        bel = solph.buses.Bus(label="electricityBus")
727
728
        solph.components.Source(
729
            label="source1",
730
            outputs={
731
                bel: solph.flows.Flow(
732
                    nonconvex=solph.NonConvex(),
733
                    nominal_value=100,
734
                    emission_factor=[0.5, -1.0, 2.0],
735
                )
736
            },
737
        )
738
        solph.components.Source(
739
            label="source2",
740
            outputs={
741
                bel: solph.flows.Flow(
742
                    nonconvex=solph.NonConvex(),
743
                    nominal_value=100,
744
                    emission_factor=3.5,
745
                )
746
            },
747
        )
748
749
        # Should be ignored because emission_factor is not defined.
750
        solph.components.Source(
751
            label="source3",
752
            outputs={
753
                bel: solph.flows.Flow(
754
                    nonconvex=solph.NonConvex(), nominal_value=100
755
                )
756
            },
757
        )
758
759
        # Should be ignored because it is not NonConvex.
760
        solph.components.Source(
761
            label="source4",
762
            outputs={
763
                bel: solph.flows.Flow(
764
                    emission_factor=1.5, min=0.3, nominal_value=100
765
                )
766
            },
767
        )
768
769
        om = self.get_om()
770
771
        # one of the two flows has to be active
772
        solph.constraints.limit_active_flow_count_by_keyword(
773
            om, "emission_factor", lower_limit=1, upper_limit=2
774
        )
775
776
        self.compare_lp_files("flow_count_limit.lp", my_om=om)
777
778
    def test_shared_limit(self):
779
        """ """
780
        b1 = solph.buses.Bus(label="bus")
781
782
        storage1 = solph.components.GenericStorage(
783
            label="storage1",
784
            nominal_storage_capacity=5,
785
            inputs={b1: solph.flows.Flow()},
786
            outputs={b1: solph.flows.Flow()},
787
        )
788
        storage2 = solph.components.GenericStorage(
789
            label="storage2",
790
            nominal_storage_capacity=5,
791
            inputs={b1: solph.flows.Flow()},
792
            outputs={b1: solph.flows.Flow()},
793
        )
794
795
        model = self.get_om()
796
797
        components = [storage1, storage2]
798
799
        solph.constraints.shared_limit(
800
            model,
801
            model.GenericStorageBlock.storage_content,
802
            "limit_storage",
803
            components,
804
            [0.5, 1.25],
805
            upper_limit=7,
806
        )
807
808
        self.compare_lp_files("shared_limit.lp", my_om=model)
809
810
    def test_flow_without_emission_for_emission_constraint(self):
811
        """ """
812
813
        def define_emission_limit():
814
            bel = solph.buses.Bus(label="electricityBus")
815
            solph.components.Source(
816
                label="source1",
817
                outputs={
818
                    bel: solph.flows.Flow(
819
                        nominal_value=100, emission_factor=0.8
820
                    )
821
                },
822
            )
823
            solph.components.Source(
824
                label="source2",
825
                outputs={bel: solph.flows.Flow(nominal_value=100)},
826
            )
827
            om = self.get_om()
828
            solph.constraints.emission_limit(om, om.flows, limit=777)
829
830
        assert_raises(AttributeError, define_emission_limit)
831
832
    def test_flow_without_emission_for_emission_constraint_no_error(self):
833
        """ """
834
        bel = solph.buses.Bus(label="electricityBus")
835
        solph.components.Source(
836
            label="source1",
837
            outputs={
838
                bel: solph.flows.Flow(nominal_value=100, emission_factor=0.8)
839
            },
840
        )
841
        solph.components.Source(
842
            label="source2", outputs={bel: solph.flows.Flow(nominal_value=100)}
843
        )
844
        om = self.get_om()
845
        solph.constraints.emission_limit(om, limit=777)
846
847
    def test_equate_variables_constraint(self):
848
        """Testing the equate_variables function in the constraint module."""
849
        bus1 = solph.buses.Bus(label="Bus1")
850
        storage = solph.components.GenericStorage(
851
            label="storage_constraint",
852
            invest_relation_input_capacity=0.2,
853
            invest_relation_output_capacity=0.2,
854
            inputs={bus1: solph.flows.Flow()},
855
            outputs={bus1: solph.flows.Flow()},
856
            investment=solph.Investment(ep_costs=145),
857
        )
858
        sink = solph.components.Sink(
859
            label="Sink",
860
            inputs={
861
                bus1: solph.flows.Flow(
862
                    investment=solph.Investment(ep_costs=500)
863
                )
864
            },
865
        )
866
        source = solph.components.Source(
867
            label="Source",
868
            outputs={
869
                bus1: solph.flows.Flow(
870
                    investment=solph.Investment(ep_costs=123)
871
                )
872
            },
873
        )
874
        om = self.get_om()
875
        solph.constraints.equate_variables(
876
            om,
877
            om.InvestmentFlowBlock.invest[source, bus1],
878
            om.InvestmentFlowBlock.invest[bus1, sink],
879
            2,
880
        )
881
        solph.constraints.equate_variables(
882
            om,
883
            om.InvestmentFlowBlock.invest[source, bus1],
884
            om.GenericInvestmentStorageBlock.invest[storage],
885
        )
886
887
        self.compare_lp_files("connect_investment.lp", my_om=om)
888
889
    def test_flow_idle(self):
890
        bus1 = solph.buses.Bus(label="Bus1")
891
        sink = solph.components.Sink(
892
            label="Sink",
893
            inputs={
894
                bus1: solph.flows.Flow(
895
                    nominal_value=1,
896
                    min=0.2,
897
                    nonconvex=solph.NonConvex(),
898
                )
899
            },
900
        )
901
        source = solph.components.Source(
902
            label="Source",
903
            outputs={
904
                bus1: solph.flows.Flow(
905
                    nominal_value=1,
906
                    min=0.2,
907
                    nonconvex=solph.NonConvex(),
908
                )
909
            },
910
        )
911
912
        om = self.get_om()
913
        solph.constraints.set_idle_time(om, (source, bus1), (bus1, sink), n=1)
914
915
        self.compare_lp_files("set_idle_time.lp", my_om=om)
916
917
918
    def test_gradient(self):
919
        """Testing gradient constraints and costs."""
920
        bel = solph.buses.Bus(label="electricityBus")
921
922
        solph.components.Source(
923
            label="powerplant",
924
            outputs={
925
                bel: solph.flows.Flow(
926
                    nominal_value=999,
927
                    variable_costs=23,
928
                    positive_gradient={"ub": 0.03},
929
                    negative_gradient={"ub": 0.05},
930
                )
931
            },
932
        )
933
934
        self.compare_lp_files("source_with_gradient.lp")
935
936
    def test_nonconvex_gradient(self):
937
        """Testing gradient constraints and costs."""
938
        bel = solph.buses.Bus(label="electricityBus")
939
940
        solph.components.Source(
941
            label="powerplant",
942
            outputs={
943
                bel: solph.flows.Flow(
944
                    nominal_value=999,
945
                    variable_costs=23,
946
                    nonconvex=solph.NonConvex(
947
                        positive_gradient={"ub": 0.03},
948
                        negative_gradient={"ub": 0.05},
949
                    ),
950
                )
951
            },
952
        )
953
954
        self.compare_lp_files("source_with_nonconvex_gradient.lp")
955
956
    def test_nonconvex_positive_gradient_error(self):
957
        """Testing nonconvex positive gradient error."""
958
        msg = (
959
            "You specified a positive gradient in your nonconvex "
960
            "option. This cannot be combined with a positive or a "
961
            "negative gradient for a standard flow!"
962
        )
963
964
        with pytest.raises(ValueError, match=msg):
965
            solph.flows.Flow(
966
                nonconvex=solph.NonConvex(
967
                    positive_gradient={"ub": 0.03},
968
                ),
969
                positive_gradient={"ub": 0.03},
970
            )
971
972
    def test_nonconvex_negative_gradient_error(self):
973
        """Testing nonconvex positive gradient error."""
974
        msg = (
975
            "You specified a negative gradient in your nonconvex "
976
            "option. This cannot be combined with a positive or a "
977
            "negative gradient for a standard flow!"
978
        )
979
980
        with pytest.raises(ValueError, match=msg):
981
            solph.flows.Flow(
982
                nonconvex=solph.NonConvex(
983
                    negative_gradient={"ub": 0.03, "costs": 7},
984
                ),
985
                negative_gradient={"ub": 0.03},
986
            )
987
988
    def test_investment_limit(self):
989
        """Testing the investment_limit function in the constraint module."""
990
        bus1 = solph.buses.Bus(label="Bus1")
991
        solph.components.GenericStorage(
992
            label="storage_invest_limit",
993
            invest_relation_input_capacity=0.2,
994
            invest_relation_output_capacity=0.2,
995
            inputs={bus1: solph.flows.Flow()},
996
            outputs={bus1: solph.flows.Flow()},
997
            investment=solph.Investment(ep_costs=145),
998
        )
999
        solph.components.Source(
1000
            label="Source",
1001
            outputs={
1002
                bus1: solph.flows.Flow(
1003
                    investment=solph.Investment(ep_costs=123)
1004
                )
1005
            },
1006
        )
1007
        om = self.get_om()
1008
        solph.constraints.investment_limit(om, limit=900)
1009
1010
        self.compare_lp_files("investment_limit.lp", my_om=om)
1011
1012
    def test_min_max_runtime(self):
1013
        """Testing min and max runtimes for nonconvex flows."""
1014
        bus_t = solph.buses.Bus(label="Bus_T")
1015
        solph.components.Source(
1016
            label="cheap_plant_min_down_constraints",
1017
            outputs={
1018
                bus_t: solph.flows.NonConvexFlow(
1019
                    nominal_value=10,
1020
                    min=0.5,
1021
                    max=1.0,
1022
                    variable_costs=10,
1023
                    minimum_downtime=4,
1024
                    minimum_uptime=2,
1025
                    initial_status=2,
1026
                    startup_costs=5,
1027
                    shutdown_costs=7,
1028
                )
1029
            },
1030
        )
1031
        self.compare_lp_files("min_max_runtime.lp")
1032
1033
    def test_activity_costs(self):
1034
        """Testing activity_costs attribute for nonconvex flows."""
1035
        bus_t = solph.buses.Bus(label="Bus_C")
1036
        solph.components.Source(
1037
            label="cheap_plant_activity_costs",
1038
            outputs={
1039
                bus_t: solph.flows.NonConvexFlow(
1040
                    nominal_value=10,
1041
                    min=0.5,
1042
                    max=1.0,
1043
                    variable_costs=10,
1044
                    activity_costs=2,
1045
                )
1046
            },
1047
        )
1048
        self.compare_lp_files("activity_costs.lp")
1049
1050
    def test_inactivity_costs(self):
1051
        """Testing inactivity_costs attribute for nonconvex flows."""
1052
        bus_t = solph.buses.Bus(label="Bus_C")
1053
        solph.components.Source(
1054
            label="cheap_plant_inactivity_costs",
1055
            outputs={
1056
                bus_t: solph.flows.NonConvexFlow(
1057
                    nominal_value=10,
1058
                    min=0.5,
1059
                    max=1.0,
1060
                    variable_costs=10,
1061
                    inactivity_costs=2,
1062
                )
1063
            },
1064
        )
1065
        self.compare_lp_files("inactivity_costs.lp")
1066
1067 View Code Duplication
    def test_piecewise_linear_transformer_cc(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1068
        """Testing PiecewiseLinearTransformer using CC formulation."""
1069
        bgas = solph.buses.Bus(label="gasBus")
1070
        bel = solph.buses.Bus(label="electricityBus")
1071
        solph.components.experimental.PiecewiseLinearTransformer(
1072
            label="pwltf",
1073
            inputs={
1074
                bgas: solph.flows.Flow(nominal_value=100, variable_costs=1)
1075
            },
1076
            outputs={bel: solph.flows.Flow()},
1077
            in_breakpoints=[0, 25, 50, 75, 100],
1078
            conversion_function=lambda x: x**2,
1079
            pw_repn="CC",
1080
        )
1081
        self.compare_lp_files("piecewise_linear_transformer_cc.lp")
1082
1083 View Code Duplication
    def test_piecewise_linear_transformer_dcc(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1084
        """Testing PiecewiseLinearTransformer using DCC formulation."""
1085
        bgas = solph.buses.Bus(label="gasBus")
1086
        bel = solph.buses.Bus(label="electricityBus")
1087
        solph.components.experimental.PiecewiseLinearTransformer(
1088
            label="pwltf",
1089
            inputs={
1090
                bgas: solph.flows.Flow(nominal_value=100, variable_costs=1)
1091
            },
1092
            outputs={bel: solph.flows.Flow()},
1093
            in_breakpoints=[0, 25, 50, 75, 100],
1094
            conversion_function=lambda x: x**2,
1095
            pw_repn="DCC",
1096
        )
1097
        self.compare_lp_files("piecewise_linear_transformer_dcc.lp")
1098
1099
    def test_maximum_startups(self):
1100
        """Testing maximum_startups attribute for nonconvex flows."""
1101
        bus_t = solph.buses.Bus(label="Bus_C")
1102
        solph.components.Source(
1103
            label="cheap_plant_maximum_startups",
1104
            outputs={
1105
                bus_t: solph.flows.Flow(
1106
                    nominal_value=10,
1107
                    min=0.5,
1108
                    max=1.0,
1109
                    variable_costs=10,
1110
                    nonconvex=solph.NonConvex(maximum_startups=2),
1111
                )
1112
            },
1113
        )
1114
        self.compare_lp_files("maximum_startups.lp")
1115
1116
    def test_maximum_shutdowns(self):
1117
        """Testing maximum_shutdowns attribute for nonconvex flows."""
1118
        bus_t = solph.buses.Bus(label="Bus_C")
1119
        solph.components.Source(
1120
            label="cheap_plant_maximum_shutdowns",
1121
            outputs={
1122
                bus_t: solph.flows.Flow(
1123
                    nominal_value=10,
1124
                    min=0.5,
1125
                    max=1.0,
1126
                    variable_costs=10,
1127
                    nonconvex=solph.NonConvex(maximum_shutdowns=2),
1128
                )
1129
            },
1130
        )
1131
        self.compare_lp_files("maximum_shutdowns.lp")
1132
1133
    def test_offsettransformer(self):
1134
        """Constraint test of a OffsetTransformer."""
1135
        bgas = solph.buses.Bus(label="gasBus")
1136
        bth = solph.buses.Bus(label="thermalBus")
1137
1138
        solph.components.OffsetTransformer(
1139
            label="gasboiler",
1140
            inputs={
1141
                bgas: solph.flows.Flow(
1142
                    nonconvex=solph.NonConvex(),
1143
                    nominal_value=100,
1144
                    min=0.32,
1145
                )
1146
            },
1147
            outputs={bth: solph.flows.Flow()},
1148
            coefficients=[-17, 0.9],
1149
        )
1150
1151
        self.compare_lp_files("offsettransformer.lp")
1152
1153 View Code Duplication
    def test_dsm_module_DIW(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1154
        """Constraint test of SinkDSM with approach=DLR"""
1155
1156
        b_elec = solph.buses.Bus(label="bus_elec")
1157
        solph.components.experimental.SinkDSM(
1158
            label="demand_dsm",
1159
            inputs={b_elec: solph.flows.Flow()},
1160
            demand=[1] * 3,
1161
            capacity_up=[0.5] * 3,
1162
            capacity_down=[0.5] * 3,
1163
            approach="DIW",
1164
            max_demand=1,
1165
            max_capacity_up=1,
1166
            max_capacity_down=1,
1167
            delay_time=1,
1168
            cost_dsm_down_shift=2,
1169
            shed_eligibility=False,
1170
        )
1171
        self.compare_lp_files("dsm_module_DIW.lp")
1172
1173
    def test_dsm_module_DLR(self):
1174
        """Constraint test of SinkDSM with approach=DLR"""
1175
1176
        b_elec = solph.buses.Bus(label="bus_elec")
1177
        solph.components.experimental.SinkDSM(
1178
            label="demand_dsm",
1179
            inputs={b_elec: solph.flows.Flow()},
1180
            demand=[1] * 3,
1181
            capacity_up=[0.5] * 3,
1182
            capacity_down=[0.5] * 3,
1183
            approach="DLR",
1184
            max_demand=1,
1185
            max_capacity_up=1,
1186
            max_capacity_down=1,
1187
            delay_time=2,
1188
            shift_time=1,
1189
            cost_dsm_down_shift=2,
1190
            shed_eligibility=False,
1191
        )
1192
        self.compare_lp_files("dsm_module_DLR.lp")
1193
1194 View Code Duplication
    def test_dsm_module_oemof(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1195
        """Constraint test of SinkDSM with approach=oemof"""
1196
1197
        b_elec = solph.buses.Bus(label="bus_elec")
1198
        solph.components.experimental.SinkDSM(
1199
            label="demand_dsm",
1200
            inputs={b_elec: solph.flows.Flow()},
1201
            demand=[1] * 3,
1202
            capacity_up=[0.5, 0.4, 0.5],
1203
            capacity_down=[0.5, 0.4, 0.5],
1204
            approach="oemof",
1205
            max_demand=1,
1206
            max_capacity_up=1,
1207
            max_capacity_down=1,
1208
            shift_interval=2,
1209
            cost_dsm_down_shift=2,
1210
            shed_eligibility=False,
1211
        )
1212
        self.compare_lp_files("dsm_module_oemof.lp")
1213
1214 View Code Duplication
    def test_dsm_module_DIW_invest(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1215
        """Constraint test of SinkDSM with approach=DLR and investments"""
1216
1217
        b_elec = solph.buses.Bus(label="bus_elec")
1218
        solph.components.experimental.SinkDSM(
1219
            label="demand_dsm",
1220
            inputs={b_elec: solph.flows.Flow()},
1221
            demand=[1] * 3,
1222
            capacity_up=[0.5] * 3,
1223
            capacity_down=[0.5] * 3,
1224
            approach="DIW",
1225
            flex_share_up=1,
1226
            flex_share_down=1,
1227
            delay_time=1,
1228
            cost_dsm_down_shift=2,
1229
            shed_eligibility=False,
1230
            investment=solph.Investment(
1231
                ep_cost=100, existing=50, minimum=33, maximum=100
1232
            ),
1233
        )
1234
        self.compare_lp_files("dsm_module_DIW_invest.lp")
1235
1236 View Code Duplication
    def test_dsm_module_DLR_invest(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1237
        """Constraint test of SinkDSM with approach=DLR and investments"""
1238
1239
        b_elec = solph.buses.Bus(label="bus_elec")
1240
        solph.components.experimental.SinkDSM(
1241
            label="demand_dsm",
1242
            inputs={b_elec: solph.flows.Flow()},
1243
            demand=[1] * 3,
1244
            capacity_up=[0.5] * 3,
1245
            capacity_down=[0.5] * 3,
1246
            approach="DLR",
1247
            flex_share_up=1,
1248
            flex_share_down=1,
1249
            delay_time=2,
1250
            shift_time=1,
1251
            cost_dsm_down_shift=2,
1252
            shed_eligibility=False,
1253
            investment=solph.Investment(
1254
                ep_cost=100, existing=50, minimum=33, maximum=100
1255
            ),
1256
        )
1257
        self.compare_lp_files("dsm_module_DLR_invest.lp")
1258
1259 View Code Duplication
    def test_dsm_module_oemof_invest(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1260
        """Constraint test of SinkDSM with approach=oemof and investments"""
1261
1262
        b_elec = solph.buses.Bus(label="bus_elec")
1263
        solph.components.experimental.SinkDSM(
1264
            label="demand_dsm",
1265
            inputs={b_elec: solph.flows.Flow()},
1266
            demand=[1] * 3,
1267
            capacity_up=[0.5, 0.4, 0.5],
1268
            capacity_down=[0.5, 0.4, 0.5],
1269
            approach="oemof",
1270
            flex_share_up=1,
1271
            flex_share_down=1,
1272
            shift_interval=2,
1273
            cost_dsm_down_shift=2,
1274
            shed_eligibility=False,
1275
            investment=solph.Investment(
1276
                ep_cost=100, existing=50, minimum=33, maximum=100
1277
            ),
1278
        )
1279
        self.compare_lp_files("dsm_module_oemof_invest.lp")
1280
1281 View Code Duplication
    def test_nonconvex_investment_storage_without_offset(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1282
        """All invest variables are coupled. The invest variables of the Flows
1283
        will be created during the initialisation of the storage e.g. battery
1284
        """
1285
        bel = solph.buses.Bus(label="electricityBus")
1286
1287
        solph.components.GenericStorage(
1288
            label="storage_non_convex",
1289
            inputs={bel: solph.flows.Flow(variable_costs=56)},
1290
            outputs={bel: solph.flows.Flow(variable_costs=24)},
1291
            nominal_storage_capacity=None,
1292
            loss_rate=0.13,
1293
            max_storage_level=0.9,
1294
            min_storage_level=0.1,
1295
            invest_relation_input_capacity=1 / 6,
1296
            invest_relation_output_capacity=1 / 6,
1297
            inflow_conversion_factor=0.97,
1298
            outflow_conversion_factor=0.86,
1299
            investment=solph.Investment(
1300
                ep_costs=141, maximum=244, minimum=12, nonconvex=True
1301
            ),
1302
        )
1303
1304
        self.compare_lp_files("storage_invest_without_offset.lp")
1305
1306 View Code Duplication
    def test_nonconvex_investment_storage_with_offset(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1307
        """All invest variables are coupled. The invest variables of the Flows
1308
        will be created during the initialisation of the storage e.g. battery
1309
        """
1310
        bel = solph.buses.Bus(label="electricityBus")
1311
1312
        solph.components.GenericStorage(
1313
            label="storagenon_convex",
1314
            inputs={bel: solph.flows.Flow(variable_costs=56)},
1315
            outputs={bel: solph.flows.Flow(variable_costs=24)},
1316
            nominal_storage_capacity=None,
1317
            loss_rate=0.13,
1318
            max_storage_level=0.9,
1319
            min_storage_level=0.1,
1320
            invest_relation_input_capacity=1 / 6,
1321
            invest_relation_output_capacity=1 / 6,
1322
            inflow_conversion_factor=0.97,
1323
            outflow_conversion_factor=0.86,
1324
            investment=solph.Investment(
1325
                ep_costs=145,
1326
                minimum=19,
1327
                offset=5,
1328
                nonconvex=True,
1329
                maximum=1454,
1330
            ),
1331
        )
1332
1333
        self.compare_lp_files("storage_invest_with_offset.lp")
1334
1335
    def test_nonconvex_invest_storage_all_nonconvex(self):
1336
        """All invest variables are free and nonconvex."""
1337
        b1 = solph.buses.Bus(label="bus1")
1338
1339
        solph.components.GenericStorage(
1340
            label="storage_all_nonconvex",
1341
            inputs={
1342
                b1: solph.flows.Flow(
1343
                    investment=solph.Investment(
1344
                        nonconvex=True,
1345
                        minimum=5,
1346
                        offset=10,
1347
                        maximum=30,
1348
                        ep_costs=10,
1349
                    )
1350
                )
1351
            },
1352
            outputs={
1353
                b1: solph.flows.Flow(
1354
                    investment=solph.Investment(
1355
                        nonconvex=True,
1356
                        minimum=8,
1357
                        offset=15,
1358
                        ep_costs=10,
1359
                        maximum=20,
1360
                    )
1361
                )
1362
            },
1363
            investment=solph.Investment(
1364
                nonconvex=True, ep_costs=20, offset=30, minimum=20, maximum=100
1365
            ),
1366
        )
1367
1368
        self.compare_lp_files("storage_invest_all_nonconvex.lp")
1369
1370
    def test_nonconvex_invest_sink_without_offset(self):
1371
        """Non convex invest flow without offset, with minimum."""
1372
        bel = solph.buses.Bus(label="electricityBus")
1373
1374
        solph.components.Sink(
1375
            label="sink_nonconvex_invest",
1376
            inputs={
1377
                bel: solph.flows.Flow(
1378
                    full_load_time_max=2.3,
1379
                    variable_costs=25,
1380
                    max=0.8,
1381
                    investment=solph.Investment(
1382
                        ep_costs=500, minimum=15, nonconvex=True, maximum=172
1383
                    ),
1384
                )
1385
            },
1386
        )
1387
        self.compare_lp_files("flow_invest_without_offset.lp")
1388
1389
    def test_nonconvex_invest_source_with_offset(self):
1390
        """Non convex invest flow with offset, with minimum."""
1391
        bel = solph.buses.Bus(label="electricityBus")
1392
1393
        solph.components.Source(
1394
            label="source_nonconvex_invest",
1395
            inputs={
1396
                bel: solph.flows.Flow(
1397
                    full_load_time_max=2.3,
1398
                    variable_costs=25,
1399
                    max=0.8,
1400
                    investment=solph.Investment(
1401
                        ep_costs=500,
1402
                        minimum=15,
1403
                        maximum=20,
1404
                        offset=34,
1405
                        nonconvex=True,
1406
                    ),
1407
                )
1408
            },
1409
        )
1410
        self.compare_lp_files("flow_invest_with_offset.lp")
1411
1412
    def test_nonconvex_invest_source_with_offset_no_minimum(self):
1413
        """Non convex invest flow with offset, without minimum."""
1414
        bel = solph.buses.Bus(label="electricityBus")
1415
1416
        solph.components.Source(
1417
            label="source_nonconvex_invest",
1418
            inputs={
1419
                bel: solph.flows.Flow(
1420
                    full_load_time_max=2.3,
1421
                    variable_costs=25,
1422
                    max=0.8,
1423
                    investment=solph.Investment(
1424
                        ep_costs=500, maximum=1234, offset=34, nonconvex=True
1425
                    ),
1426
                )
1427
            },
1428
        )
1429
        self.compare_lp_files("flow_invest_with_offset_no_minimum.lp")
1430
1431
    def test_nonequidistant_storage(self):
1432
        """Constraint test of an energysystem with nonequidistant timeindex"""
1433
        idxh = pd.date_range("1/1/2017", periods=3, freq="H")
1434
        idx2h = pd.date_range("1/1/2017 03:00:00", periods=2, freq="2H")
1435
        idx30m = pd.date_range("1/1/2017 07:00:00", periods=4, freq="30min")
1436
        timeindex = idxh.append([idx2h, idx30m])
1437
        es = solph.EnergySystem(timeindex=timeindex, infer_last_interval=False)
1438
        b_gas = solph.Bus(label="gas")
1439
        b_th = solph.Bus(label="heat")
1440
        boiler = solph.components.Transformer(
1441
            label="boiler",
1442
            inputs={b_gas: solph.Flow(variable_costs=100)},
1443
            outputs={b_th: solph.Flow(nominal_value=200)},
1444
        )
1445
        storage = solph.components.GenericStorage(
1446
            label="storage",
1447
            inputs={b_th: solph.Flow(nominal_value=100, variable_costs=56)},
1448
            outputs={b_th: solph.Flow(nominal_value=100, variable_costs=24)},
1449
            nominal_storage_capacity=300,
1450
            loss_rate=0.1,
1451
            initial_storage_level=1,
1452
        )
1453
        es.add(b_gas, b_th, boiler, storage)
1454
        om = solph.Model(es)
1455
        self.compare_lp_files("nonequidistant_timeindex.lp", my_om=om)
1456