-
Notifications
You must be signed in to change notification settings - Fork 19
Expand file tree
/
Copy pathedisgo.py
More file actions
executable file
·4029 lines (3535 loc) · 173 KB
/
edisgo.py
File metadata and controls
executable file
·4029 lines (3535 loc) · 173 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
from __future__ import annotations
import copy
import json
import logging
import os
import pickle
import shutil
from numbers import Number
from pathlib import PurePath
import numpy as np
import pandas as pd
from sqlalchemy.engine.base import Engine
from edisgo.flex_opt.charging_strategies import charging_strategy
from edisgo.flex_opt.check_tech_constraints import lines_relative_load
from edisgo.flex_opt.heat_pump_operation import (
operating_strategy as hp_operating_strategy,
)
from edisgo.flex_opt.reinforce_grid import (
catch_convergence_reinforce_grid,
reinforce_grid,
)
from edisgo.io import (
dsm_import,
generators_import,
powermodels_io,
pypsa_io,
timeseries_import,
)
from edisgo.io.db import engine as egon_engine
from edisgo.io.ding0_import import import_ding0_grid
from edisgo.io.electromobility_import import (
distribute_charging_demand,
import_electromobility_from_dir,
import_electromobility_from_oedb,
integrate_charging_parks,
)
from edisgo.io.heat_pump_import import oedb as import_heat_pumps_oedb
from edisgo.io.storage_import import home_batteries_oedb
from edisgo.network import timeseries
from edisgo.network.dsm import DSM
from edisgo.network.electromobility import Electromobility
from edisgo.network.heat import HeatPump
from edisgo.network.overlying_grid import OverlyingGrid
from edisgo.network.results import Results
from edisgo.network.topology import Topology
from edisgo.opf import powermodels_opf
from edisgo.opf.results.opf_result_class import OPFResults
from edisgo.tools import plots, tools
from edisgo.tools.config import Config
from edisgo.tools.geo import find_nearest_bus
from edisgo.tools.spatial_complexity_reduction import spatial_complexity_reduction
from edisgo.tools.tools import (
determine_grid_integration_voltage_level,
get_path_length_to_station,
)
if "READTHEDOCS" not in os.environ:
from shapely.geometry import Point
logger = logging.getLogger(__name__)
class EDisGo:
"""
Provides the top-level API for invocation of data import, power flow
analysis, network reinforcement, flexibility measures, etc..
Parameters
----------
ding0_grid : :obj:`str`
Path to directory containing csv files of network to be loaded.
generator_scenario : None or :obj:`str`, optional
If None, the generator park of the imported grid is kept as is.
Otherwise defines which scenario of future generator park to use
and invokes grid integration of these generators. Possible options are
'nep2035' and 'ego100'. These are scenarios from the research project
`open_eGo <https://openegoproject.wordpress.com/>`_ (see
`final report <https://www.uni-flensburg.de/fileadmin/content/\
abteilungen/industrial/dokumente/downloads/veroeffentlichungen/\
forschungsergebnisse/20190426endbericht-openego-fkz0325881-final.pdf>`_
for more information on the scenarios).
See :attr:`~.EDisGo.import_generators` for further information on how
generators are integrated and what further options there are.
Default: None.
timeindex : None or :pandas:`pandas.DatetimeIndex<DatetimeIndex>`, optional
Defines the time steps feed-in and demand time series of all generators, loads
and storage units need to be set.
The time index is for example used as default for time steps considered in
the power flow analysis and when checking the integrity of the network.
Providing a time index is only optional in case a worst case analysis is set
up using :func:`~set_time_series_worst_case_analysis`.
In all other cases a time index needs to be set manually.
config_path : None or str or dict
Path to the config directory. Options are:
* 'default' (default)
If `config_path` is set to 'default', the provided default config files
are used directly.
* str
If `config_path` is a string, configs will be loaded from the
directory specified by `config_path`. If the directory
does not exist, it is created. If config files don't exist, the
default config files are copied into the directory.
* dict
A dictionary can be used to specify different paths to the
different config files. The dictionary must have the following
keys:
* 'config_db_tables'
* 'config_grid'
* 'config_grid_expansion'
* 'config_timeseries'
Values of the dictionary are paths to the corresponding
config file. In contrast to the other options, the directories
and config files must exist and are not automatically created.
* None
If `config_path` is None, configs are loaded from the edisgo
default config directory ($HOME$/.edisgo). If the directory
does not exist, it is created. If config files don't exist, the
default config files are copied into the directory.
Default: "default".
legacy_ding0_grids : bool
Allow import of old ding0 grids. Default: True.
Attributes
----------
topology : :class:`~.network.topology.Topology`
The topology is a container object holding the topology of the grids including
buses, lines, transformers, switches and components connected to the grid
including generators, loads and storage units.
timeseries : :class:`~.network.timeseries.TimeSeries`
Container for active and reactive power time series of generators, loads and
storage units.
results : :class:`~.network.results.Results`
This is a container holding all calculation results from power flow
analyses and grid reinforcement.
electromobility : :class:`~.network.electromobility.Electromobility`
This class holds data on charging processes (how long cars are parking at a
charging station, how much they need to charge, etc.) necessary to apply
different charging strategies, as well as information on potential charging
sites and integrated charging parks.
heat_pump : :class:`~.network.heat.HeatPump`
This is a container holding heat pump data such as COP, heat demand to be
served and heat storage information.
overlying_grid : :class:`~.network.overlying_grid.OverlyingGrid`
This is a container holding data from the overlying grid such as curtailment
requirements or power plant dispatch.
dsm : :class:`~.network.dsm.DSM`
This is a container holding data on demand side management potential.
"""
def __init__(self, **kwargs):
# load configuration
self._config = Config(**kwargs)
# instantiate topology object and load grid data
self.topology = Topology(config=self.config)
self.import_ding0_grid(
path=kwargs.get("ding0_grid", None),
legacy_ding0_grids=kwargs.get("legacy_ding0_grids", True),
)
self.legacy_grids = kwargs.get("legacy_ding0_grids", True)
# instantiate other data classes
self.results = Results(self)
self.opf_results = OPFResults()
self.timeseries = timeseries.TimeSeries(
timeindex=kwargs.get("timeindex", pd.DatetimeIndex([]))
)
self.electromobility = Electromobility(edisgo_obj=self)
self.heat_pump = HeatPump()
self.dsm = DSM()
self.overlying_grid = OverlyingGrid()
# import new generators
if kwargs.get("generator_scenario", None) is not None:
self.import_generators(
generator_scenario=kwargs.pop("generator_scenario"), **kwargs
)
# add MVGrid id to logging messages of logger "edisgo"
log_grid_id = kwargs.get("log_grid_id", True)
if log_grid_id:
def add_grid_id_filter(record):
record.grid_id = self.topology.id
return True
logger_edisgo = logging.getLogger("edisgo")
for handler in logger_edisgo.handlers:
fmt = handler.formatter._fmt
colon_idx = fmt.index(":")
formatter_str = (
f"{fmt[:colon_idx]} - MVGrid(%(grid_id)s){fmt[colon_idx:]}"
)
formatter = logging.Formatter(formatter_str)
handler.setFormatter(formatter)
handler.addFilter(add_grid_id_filter)
@property
def config(self):
"""
eDisGo configuration data.
Parameters
----------
kwargs : dict
Dictionary with keyword arguments to set up Config object. See parameters
of :class:`~.tools.config.Config` class for more information on possible
input parameters.
Returns
-------
:class:`~.tools.config.Config`
Config object with configuration data from config files.
"""
return self._config
@config.setter
def config(self, kwargs):
self._config = Config(**kwargs)
def import_ding0_grid(self, path, legacy_ding0_grids=True):
"""
Import ding0 topology data from csv files in the format as
`Ding0 <https://github.com/openego/ding0>`_ provides it.
Parameters
-----------
path : str
Path to directory containing csv files of network to be loaded.
legacy_ding0_grids : bool
Allow import of old ding0 grids. Default: True.
"""
if path is not None:
import_ding0_grid(path, self, legacy_ding0_grids)
def set_timeindex(self, timeindex):
"""
Sets :py:attr:`~.network.timeseries.TimeSeries.timeindex` all time-dependent
attributes are indexed by.
The time index is for example used as default for time steps considered in
the power flow analysis and when checking the integrity of the network.
Parameters
-----------
timeindex : :pandas:`pandas.DatetimeIndex<DatetimeIndex>`
Time index to set.
"""
self.timeseries.timeindex = timeindex
def set_time_series_manual(
self,
generators_p=None,
loads_p=None,
storage_units_p=None,
generators_q=None,
loads_q=None,
storage_units_q=None,
):
"""
Sets given component time series.
If time series for a component were already set before, they are overwritten.
Parameters
-----------
generators_p : :pandas:`pandas.DataFrame<DataFrame>`
Active power time series in MW of generators. Index of the data frame is
a datetime index. Columns contain generators names of generators to set
time series for. Default: None.
loads_p : :pandas:`pandas.DataFrame<DataFrame>`
Active power time series in MW of loads. Index of the data frame is
a datetime index. Columns contain load names of loads to set
time series for. Default: None.
storage_units_p : :pandas:`pandas.DataFrame<DataFrame>`
Active power time series in MW of storage units. Index of the data frame is
a datetime index. Columns contain storage unit names of storage units to set
time series for. Default: None.
generators_q : :pandas:`pandas.DataFrame<DataFrame>`
Reactive power time series in MVA of generators. Index of the data frame is
a datetime index. Columns contain generators names of generators to set
time series for. Default: None.
loads_q : :pandas:`pandas.DataFrame<DataFrame>`
Reactive power time series in MVA of loads. Index of the data frame is
a datetime index. Columns contain load names of loads to set
time series for. Default: None.
storage_units_q : :pandas:`pandas.DataFrame<DataFrame>`
Reactive power time series in MVA of storage units. Index of the data frame
is a datetime index. Columns contain storage unit names of storage units to
set time series for. Default: None.
Notes
------
This function raises a warning in case a time index was not previously set.
You can set the time index upon initialisation of the EDisGo object by
providing the input parameter 'timeindex' or using the function
:attr:`~.edisgo.EDisGo.set_timeindex`.
Also make sure that the time steps for which time series are provided include
the set time index.
"""
# check if time index is already set, otherwise raise warning
if self.timeseries.timeindex.empty:
logger.warning(
"When setting time series manually a time index is not automatically "
"set but needs to be set by the user. You can set the time index "
"upon initialisation of the EDisGo object by providing the input "
"parameter 'timeindex' or using the function EDisGo.set_timeindex()."
)
self.timeseries.set_active_power_manual(
self,
ts_generators=generators_p,
ts_loads=loads_p,
ts_storage_units=storage_units_p,
)
self.timeseries.set_reactive_power_manual(
self,
ts_generators=generators_q,
ts_loads=loads_q,
ts_storage_units=storage_units_q,
)
def set_time_series_worst_case_analysis(
self,
cases=None,
generators_names=None,
loads_names=None,
storage_units_names=None,
):
"""
Sets demand and feed-in of all loads, generators and storage units for the
specified worst cases.
See :attr:`~.network.timeseries.TimeSeries.set_worst_case` for more information.
Parameters
-----------
cases : str or list(str)
List with worst-cases to generate time series for. Can be
'feed-in_case', 'load_case' or both. Defaults to None in which case both
'feed-in_case' and 'load_case' are set up.
generators_names : list(str)
Defines for which generators to set worst case time series. If None,
time series are set for all generators. Default: None.
loads_names : list(str)
Defines for which loads to set worst case time series. If None,
time series are set for all loads. Default: None.
storage_units_names : list(str)
Defines for which storage units to set worst case time series. If None,
time series are set for all storage units. Default: None.
"""
if cases is None:
cases = ["load_case", "feed-in_case"]
if isinstance(cases, str):
cases = [cases]
self.timeseries.set_worst_case(
self, cases, generators_names, loads_names, storage_units_names
)
def set_time_series_active_power_predefined(
self,
fluctuating_generators_ts=None,
fluctuating_generators_names=None,
dispatchable_generators_ts=None,
dispatchable_generators_names=None,
conventional_loads_ts=None,
conventional_loads_names=None,
charging_points_ts=None,
charging_points_names=None,
**kwargs,
):
"""
Uses predefined feed-in or demand profiles to set active power time series.
Predefined profiles comprise i.e. standard electric conventional load profiles
for different sectors generated using the oemof
`demandlib <https://github.com/oemof/oemof-demand>`_ or feed-in time series of
fluctuating solar and wind generators provided on the OpenEnergy DataBase.
This function can also be used to provide your own profiles per technology or
load sector.
The active power time series are written to
:attr:`~.network.timeseries.TimeSeries.generators_active_power` or
:attr:`~.network.timeseries.TimeSeries.loads_active_power`.
As data in :class:`~.network.timeseries.TimeSeries` is indexed by
:attr:`~.network.timeseries.TimeSeries.timeindex` it is better to set
:attr:`~.network.timeseries.TimeSeries.timeindex` before calling this function.
You can set the time index upon initialisation of the EDisGo object by
providing the input parameter 'timeindex' or using the function
:attr:`~.edisgo.EDisGo.set_timeindex`.
Also make sure that the time steps of self-provided time series include
the set time index.
Parameters
-----------
fluctuating_generators_ts : str or :pandas:`pandas.DataFrame<DataFrame>` or None
Defines option to set technology-specific or technology- and weather cell
specific active power time series of wind and solar generators.
Possible options are:
* 'oedb'
Technology- and weather cell-specific hourly feed-in time series are
obtained from the
`OpenEnergy DataBase
<https://openenergyplatform.org/database/>`_. See
:func:`edisgo.io.timeseries_import.feedin_oedb` for more information.
This option requires that the parameter `engine` is provided in case
new ding0 grids with geo-referenced LV grids are used. For further
settings, the parameter `timeindex` can also be provided.
* :pandas:`pandas.DataFrame<DataFrame>`
DataFrame with self-provided feed-in time series per technology or
per technology and weather cell ID normalized to a nominal capacity
of 1.
In case time series are provided only by technology, columns of the
DataFrame contain the technology type as string.
In case time series are provided by technology and weather cell ID
columns need to be a :pandas:`pandas.MultiIndex<MultiIndex>` with the
first level containing the technology as string and the second level
the weather cell ID as integer.
Index needs to be a :pandas:`pandas.DatetimeIndex<DatetimeIndex>`.
When importing a ding0 grid and/or using predefined scenarios
of the future generator park,
each generator has an assigned weather cell ID that identifies the
weather data cell from the weather data set used in the research
project `open_eGo <https://openegoproject.wordpress.com/>`_ to
determine feed-in profiles. The weather cell ID can be retrieved
from column `weather_cell_id` in
:attr:`~.network.topology.Topology.generators_df` and could be
overwritten to use own weather cells.
* None
If None, time series are not set.
Default: None.
fluctuating_generators_names : list(str) or None
Defines for which fluctuating generators to apply technology-specific time
series. See parameter `generator_names` in
:attr:`~.network.timeseries.TimeSeries.predefined_fluctuating_generators_by_technology`
for more information. Default: None.
dispatchable_generators_ts : :pandas:`pandas.DataFrame<DataFrame>` or None
Defines which technology-specific time series to use to set active power
time series of dispatchable generators.
See parameter `ts_generators` in
:attr:`~.network.timeseries.TimeSeries.predefined_dispatchable_generators_by_technology`
for more information. If None, no time series of dispatchable generators
are set. Default: None.
dispatchable_generators_names : list(str) or None
Defines for which dispatchable generators to apply technology-specific time
series. See parameter `generator_names` in
:attr:`~.network.timeseries.TimeSeries.predefined_dispatchable_generators_by_technology`
for more information. Default: None.
conventional_loads_ts : str or :pandas:`pandas.DataFrame<DataFrame>` or None
Defines option to set active power time series of conventional loads.
Possible options are:
* 'oedb'
Sets active power demand time series using individual hourly electricity
load time series for one year obtained from the `OpenEnergy DataBase
<https://openenergyplatform.org/database/>`_.
This option requires that the parameters `engine` and `scenario` are
provided. For further settings, the parameter `timeindex` can also be
provided.
* 'demandlib'
Sets active power demand time series using hourly electricity load time
series obtained using standard electric load profiles from
the oemof `demandlib <https://github.com/oemof/oemof-demand>`_.
The demandlib provides sector-specific time series for the sectors
'residential', 'cts', 'industrial', and 'agricultural'.
For further settings, the parameter `timeindex` can also be provided.
* :pandas:`pandas.DataFrame<DataFrame>`
Sets active power demand time series using sector-specific demand
time series provided in this DataFrame.
The load time series per sector need to be normalized to an annual
consumption of 1. Index needs to
be a :pandas:`pandas.DatetimeIndex<DatetimeIndex>`.
Columns need to contain the sector as string.
In the current grid existing load types can be retrieved from column
`sector` in :attr:`~.network.topology.Topology.loads_df` (make sure to
select `type` 'conventional_load').
In ding0 grids the differentiated sectors are 'residential', 'cts',
and 'industrial'.
* None
If None, conventional load time series are not set.
Default: None.
conventional_loads_names : list(str) or None
Defines for which conventional loads to set time series. In case
`conventional_loads_ts` is 'oedb' see parameter `load_names` in
:func:`edisgo.io.timeseries_import.electricity_demand_oedb` for more
information. For other cases see parameter `load_names` in
:attr:`~.network.timeseries.TimeSeries.predefined_conventional_loads_by_sector`
for more information. Default: None.
charging_points_ts : :pandas:`pandas.DataFrame<DataFrame>` or None
Defines which use-case-specific time series to use to set active power
time series of charging points.
See parameter `ts_loads` in
:attr:`~.network.timeseries.TimeSeries.predefined_charging_points_by_use_case`
for more information. If None, no time series of charging points
are set. Default: None.
charging_points_names : list(str) or None
Defines for which charging points to apply use-case-specific time
series. See parameter `load_names` in
:attr:`~.network.timeseries.TimeSeries.predefined_charging_points_by_use_case`
for more information. Default: None.
Other Parameters
------------------
engine : :sqlalchemy:`sqlalchemy.Engine<sqlalchemy.engine.Engine>`
Database engine. This parameter is only required in case
`conventional_loads_ts` or `fluctuating_generators_ts` is 'oedb'.
scenario : str
Scenario for which to retrieve demand data. Possible options are 'eGon2035'
and 'eGon100RE'. This parameter is only required in case
`conventional_loads_ts` is 'oedb'.
timeindex : :pandas:`pandas.DatetimeIndex<DatetimeIndex>` or None
This parameter can optionally be provided in case `conventional_loads_ts`
is 'oedb' or 'demandlib' and in case `fluctuating_generators_ts` is
'oedb'. It is used to specify time steps for which to set active power data.
Leap years can currently not be handled when data is retrieved from the
oedb. In case the given timeindex contains a leap year, the data will
be indexed using a default year and set for the whole year.
If no timeindex is provided, the timeindex set in
:py:attr:`~.network.timeseries.TimeSeries.timeindex` is used.
If :py:attr:`~.network.timeseries.TimeSeries.timeindex` is not set, the data
is indexed using a default year and set for the whole year.
In this case, the EDisGo TimeSeries timeindex is automatically set to
the selected default year so that imported data is linked to a valid
time index.
"""
engine = kwargs["engine"] if "engine" in kwargs else egon_engine()
if self.timeseries.timeindex.empty and kwargs.get("timeindex", None) is None:
if conventional_loads_ts == "oedb":
default_year = tools.get_year_based_on_scenario(kwargs.get("scenario"))
if default_year is None:
default_year = 2011
else:
default_year = 2011
self.timeseries.timeindex = pd.date_range(
f"1/1/{default_year}", periods=8760, freq="H"
)
logger.warning(
"No timeindex was set. TimeSeries.timeindex is automatically "
f"set to the default year {default_year} to match imported "
"time series."
)
elif self.timeseries.timeindex.empty:
logger.warning(
"When setting time series using predefined profiles it is better to "
"set a time index as all data in TimeSeries class is indexed by the"
"time index. You can set the time index upon initialisation of "
"the EDisGo object by providing the input parameter 'timeindex' or by "
"using the function EDisGo.set_timeindex()."
)
if fluctuating_generators_ts is not None:
self.timeseries.predefined_fluctuating_generators_by_technology(
self,
fluctuating_generators_ts,
fluctuating_generators_names,
engine=engine,
timeindex=kwargs.get("timeindex", None),
)
if dispatchable_generators_ts is not None:
self.timeseries.predefined_dispatchable_generators_by_technology(
self, dispatchable_generators_ts, dispatchable_generators_names
)
if conventional_loads_ts is not None:
if (
isinstance(conventional_loads_ts, str)
and conventional_loads_ts == "oedb"
):
loads_ts_df = timeseries_import.electricity_demand_oedb(
edisgo_obj=self,
scenario=kwargs.get("scenario"),
engine=engine,
timeindex=kwargs.get("timeindex", None),
load_names=conventional_loads_names,
)
# concat new time series with existing ones and drop any duplicate
# entries
self.timeseries.loads_active_power = tools.drop_duplicated_columns(
pd.concat([self.timeseries.loads_active_power, loads_ts_df], axis=1)
)
else:
self.timeseries.predefined_conventional_loads_by_sector(
self, conventional_loads_ts, conventional_loads_names
)
if charging_points_ts is not None:
self.timeseries.predefined_charging_points_by_use_case(
self, charging_points_ts, charging_points_names
)
def set_time_series_reactive_power_control(
self,
control="fixed_cosphi",
generators_parametrisation="default",
loads_parametrisation="default",
storage_units_parametrisation="default",
):
"""
Set reactive power time series of components.
Parameters
-----------
control : str
Type of reactive power control to apply. Currently, the only option is
'fixed_coshpi'. See :attr:`~.network.timeseries.TimeSeries.fixed_cosphi`
for further information.
generators_parametrisation : str or :pandas:`pandas.DataFrame<DataFrame>`
See parameter `generators_parametrisation` in
:attr:`~.network.timeseries.TimeSeries.fixed_cosphi` for further
information. Here, per default, the option 'default' is used.
loads_parametrisation : str or :pandas:`pandas.DataFrame<DataFrame>`
See parameter `loads_parametrisation` in
:attr:`~.network.timeseries.TimeSeries.fixed_cosphi` for further
information. Here, per default, the option 'default' is used.
storage_units_parametrisation : str or :pandas:`pandas.DataFrame<DataFrame>`
See parameter `storage_units_parametrisation` in
:attr:`~.network.timeseries.TimeSeries.fixed_cosphi` for further
information. Here, per default, the option 'default' is used.
Notes
------
Be careful to set parametrisation of other component types to None if you only
want to set reactive power of certain components. See example below for further
information.
Examples
--------
To only set reactive power time series of one generator using default
configurations you can do the following:
>>> self.set_time_series_reactive_power_control(
>>> generators_parametrisation=pd.DataFrame(
>>> {
>>> "components": [["Generator_1"]],
>>> "mode": ["default"],
>>> "power_factor": ["default"],
>>> },
>>> index=[1],
>>> ),
>>> loads_parametrisation=None,
>>> storage_units_parametrisation=None
>>> )
In the example above, `loads_parametrisation` and
`storage_units_parametrisation` need to be set to None, otherwise already
existing time series would be overwritten.
To only change configuration of one load and for all other components use
default configurations you can do the following:
>>> self.set_time_series_reactive_power_control(
>>> loads_parametrisation=pd.DataFrame(
>>> {
>>> "components": [["Load_1"],
>>> self.topology.loads_df.index.drop(["Load_1"])],
>>> "mode": ["capacitive", "default"],
>>> "power_factor": [0.98, "default"],
>>> },
>>> index=[1, 2],
>>> )
>>> )
In the example above, `generators_parametrisation` and
`storage_units_parametrisation` do not need to be set as default configurations
are per default used for all generators and storage units anyway.
"""
if control == "fixed_cosphi":
self.timeseries.fixed_cosphi(
self,
generators_parametrisation=generators_parametrisation,
loads_parametrisation=loads_parametrisation,
storage_units_parametrisation=storage_units_parametrisation,
)
else:
raise ValueError("'control' must be 'fixed_cosphi'.")
def to_pypsa(
self, mode=None, timesteps=None, check_edisgo_integrity=False, **kwargs
):
"""
Convert grid to :pypsa:`PyPSA.Network<network>` representation.
You can choose between translation of the MV and all underlying LV grids
(mode=None (default)), the MV network only (mode='mv' or mode='mvlv') or a
single LV network (mode='lv').
Parameters
-----------
mode : str
Determines network levels that are translated to
:pypsa:`PyPSA.Network<network>`.
Possible options are:
* None
MV and underlying LV networks are exported. This is the default.
* 'mv'
Only MV network is exported. MV/LV transformers are not exported in
this mode. Loads, generators and storage units in underlying LV grids
are connected to the respective MV/LV station's primary side. Per
default, they are all connected separately, but you can also choose to
aggregate them. See parameters `aggregate_loads`, `aggregate_generators`
and `aggregate_storages` for more information.
* 'mvlv'
This mode works similar as mode 'mv', with the difference that MV/LV
transformers are as well exported and LV components connected to the
respective MV/LV station's secondary side. Per default, all components
are connected separately, but you can also choose to aggregate them.
See parameters `aggregate_loads`, `aggregate_generators`
and `aggregate_storages` for more information.
* 'lv'
Single LV network topology including the MV/LV transformer is exported.
The LV grid to export is specified through the parameter `lv_grid_id`.
The slack is positioned at the secondary side of the MV/LV station.
timesteps : :pandas:`pandas.DatetimeIndex<DatetimeIndex>` or \
:pandas:`pandas.Timestamp<Timestamp>`
Specifies which time steps to export to pypsa representation to e.g.
later on use in power flow analysis. It defaults to None in which case
all time steps in :attr:`~.network.timeseries.TimeSeries.timeindex`
are used.
Default: None.
check_edisgo_integrity : bool
Check integrity of edisgo object before translating to pypsa. This option is
meant to help the identification of possible sources of errors if the power
flow calculations fail. See :attr:`~.edisgo.EDisGo.check_integrity` for
more information. Default: False.
Other Parameters
-------------------
use_seed : bool
Use a seed for the initial guess for the Newton-Raphson algorithm.
Only available when MV level is included in the power flow analysis.
If True, uses voltage magnitude results of previous power flow
analyses as initial guess in case of PQ buses. PV buses currently do
not occur and are therefore currently not supported.
Default: False.
lv_grid_id : int or str
ID (e.g. 1) or name (string representation, e.g. "LVGrid_1") of LV grid
to export in case mode is 'lv'. Default: None.
aggregate_loads : str
Mode for load aggregation in LV grids in case mode is 'mv' or 'mvlv'.
Can be 'sectoral' aggregating the loads sector-wise, 'all' aggregating all
loads into one or None, not aggregating loads but appending them to the
station one by one. Default: None.
aggregate_generators : str
Mode for generator aggregation in LV grids in case mode is 'mv' or 'mvlv'.
Can be 'type' aggregating generators per generator type, 'curtailable'
aggregating 'solar' and 'wind' generators into one and all other generators
into another one, or None, where no aggregation is undertaken
and generators are added to the station one by one. Default: None.
aggregate_storages : str
Mode for storage unit aggregation in LV grids in case mode is 'mv' or
'mvlv'. Can be 'all' where all storage units in an LV grid are aggregated to
one storage unit or None, in which case no aggregation is conducted and
storage units are added to the station. Default: None.
Returns
-------
:pypsa:`PyPSA.Network<network>`
:pypsa:`PyPSA.Network<network>` representation.
"""
# possibly execute consistency check
if check_edisgo_integrity or logger.level == logging.DEBUG:
self.check_integrity()
return pypsa_io.to_pypsa(self, mode, timesteps, **kwargs)
def to_powermodels(
self,
s_base=1,
flexible_cps=None,
flexible_hps=None,
flexible_loads=None,
flexible_storage_units=None,
opf_version=1,
):
"""
Convert eDisGo representation of the network topology and timeseries to
PowerModels network data format.
Parameters
----------
s_base : int
Base value of apparent power for per unit system.
Default: 1 MVA
flexible_cps : :numpy:`numpy.ndarray<ndarray>` or None
Array containing all charging points that allow for flexible charging.
flexible_hps : :numpy:`numpy.ndarray<ndarray>` or None
Array containing all heat pumps that allow for flexible operation due to an
attached heat storage.
flexible_loads : :numpy:`numpy.ndarray<ndarray>` or None
Array containing all flexible loads that allow for application of demand
side management strategy.
flexible_storage_units : :numpy:`numpy.ndarray<ndarray>` or None
Array containing all flexible storages. Non-flexible storages operate to
optimize self consumption.
Default: None.
opf_version : int
Version of optimization models to choose from. Must be one of [1, 2, 3, 4].
For more information see :func:`edisgo.opf.powermodels_opf.pm_optimize`.
Default: 1.
Returns
-------
dict
Dictionary that contains all network data in PowerModels network data
format.
"""
return powermodels_io.to_powermodels(
self,
s_base=s_base,
flexible_cps=flexible_cps,
flexible_hps=flexible_hps,
flexible_loads=flexible_loads,
flexible_storage_units=flexible_storage_units,
opf_version=opf_version,
)
def pm_optimize(
self,
s_base=1,
flexible_cps=None,
flexible_hps=None,
flexible_loads=None,
flexible_storage_units=None,
opf_version=1,
method="soc",
warm_start=False,
silence_moi=False,
save_heat_storage=True,
save_slack_gen=True,
save_slacks=True,
):
"""
Run OPF in julia subprocess and write results of OPF back to edisgo object.
Results of OPF are time series of operation schedules of flexibilities.
Parameters
----------
s_base : int
Base value of apparent power for per unit system.
Default: 1 MVA.
flexible_cps : :numpy:`numpy.ndarray<ndarray>` or None
Array containing all charging points that allow for flexible charging.
Default: None.
flexible_hps : :numpy:`numpy.ndarray<ndarray>` or None
Array containing all heat pumps that allow for flexible operation due to an
attached heat storage.
Default: None.
flexible_loads : :numpy:`numpy.ndarray<ndarray>` or None
Array containing all flexible loads that allow for application of demand
side management strategy.
Default: None.
flexible_storage_units: :numpy:`numpy.ndarray<ndarray>` or None
Array containing all flexible storages. Non-flexible storages operate to
optimize self consumption.
Default: None.
opf_version : int
Version of optimization models to choose from. Must be one of [1, 2, 3, 4].
For more information see :func:`edisgo.opf.powermodels_opf.pm_optimize`.
Default: 1.
method : str
Optimization method to use. Must be either "soc" (Second Order Cone) or "nc"
(Non Convex). For more information see
:func:`edisgo.opf.powermodels_opf.pm_optimize`.
Default: "soc".
warm_start : bool
If set to True and if method is set to "soc", non-convex IPOPT OPF will be
run additionally and will be warm started with Gurobi SOC solution.
Warm-start will only be run if results for Gurobi's SOC relaxation is exact.
Default: False.
silence_moi : bool
If set to True, MathOptInterface's optimizer attribute "MOI.Silent" is set
to True in julia subprocess. This attribute is for silencing the output of
an optimizer. When set to True, it requires the solver to produce no output,
hence there will be no logging coming from julia subprocess in python
process.
Default: False.
"""
return powermodels_opf.pm_optimize(
self,
s_base=s_base,
flexible_cps=flexible_cps,
flexible_hps=flexible_hps,
flexible_loads=flexible_loads,
flexible_storage_units=flexible_storage_units,
opf_version=opf_version,
method=method,
warm_start=warm_start,
silence_moi=silence_moi,
)
def to_graph(self):
"""
Returns networkx graph representation of the grid.
Returns
-------
:networkx:`networkx.Graph<networkx.Graph>`
Graph representation of the grid as networkx Ordered Graph,
where lines are represented by edges in the graph, and buses and
transformers are represented by nodes.
"""
return self.topology.to_graph()
def import_generators(self, generator_scenario=None, **kwargs):
"""
Gets generator park for specified scenario and integrates generators into grid.
The generator data is retrieved from the
`open energy platform <https://openenergyplatform.org/>`_. Decommissioned
generators are removed from the grid, generators with changed capacity
updated and new generators newly integrated into the grid.
In case you are using new ding0 grids, where the LV is geo-referenced, the
supported data source is scenario data generated in the research project
`eGo^n <https://rego-n.org/>`_. You can choose between two scenarios:
'eGon2035' and 'eGon100RE'. For more information on database tables used and
how generator park is adapted see :func:`~.io.generators_import.oedb`.
In case you are using old ding0 grids, where the LV is not geo-referenced,
the supported data source is scenario data generated in the research project
`open_eGo <https://openegoproject.wordpress.com/>`_. You can choose
between two scenarios: 'nep2035' and 'ego100'. You can get more
information on the scenarios in the
`final report <https://www.uni-flensburg.de/fileadmin/content/\
abteilungen/industrial/dokumente/downloads/veroeffentlichungen/\
forschungsergebnisse/20190426endbericht-openego-fkz0325881-final\
.pdf>`_. For more information on database tables used and
how generator park is adapted see :func:`~.io.generators_import.oedb_legacy`.
After the generator park is adapted there may be grid issues due to the
additional feed-in. These are not solved automatically. If you want to
have a stable grid without grid issues you can invoke the automatic
grid expansion through the function :attr:`~.EDisGo.reinforce`.
Parameters
----------
generator_scenario : str
Scenario for which to retrieve generator data. In case you are using new
ding0 grids, where the LV is geo-referenced, possible options are
'eGon2035' and 'eGon100RE'. In case you are using old ding0 grids, where
the LV is not geo-referenced, possible options are 'nep2035' and 'ego100'.
Other Parameters
----------------
kwargs :
In case you are using new ding0 grids, where the LV is geo-referenced, a
database engine needs to be provided through keyword argument `engine`.
In case you are using old ding0 grids, where the LV is not geo-referenced,
you can check :func:`edisgo.io.generators_import.oedb_legacy` for possible
keyword arguments.