Skip to content

Commit 4454425

Browse files
authored
Merge pull request #352 from openego/dev
Release v0.2.0
2 parents f4f1c61 + 07f86fc commit 4454425

File tree

69 files changed

+9560
-902
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+9560
-902
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ var/
2222
*.egg-info/
2323
.installed.cfg
2424
*.egg
25+
doc/_html/
26+
.idea/
2527

2628
# PyInstaller
2729
# Usually these files are written by a python script from a template
@@ -56,6 +58,9 @@ doc/_build/
5658
# PyBuilder
5759
target/
5860

61+
# Data
62+
.pkl
63+
5964

6065

6166

.readthedocs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,6 @@ formats: all
1818

1919
# Optionally set the version of Python and requirements required to build your docs
2020
python:
21-
version: 3.6
21+
version: 3.8
2222
install:
2323
- requirements: rtd_requirements.txt

.travis.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1+
dist: bionic
12
language: python
23
python:
3-
- "3.4"
4-
- "3.5"
5-
- "3.6"
4+
- "3.6.10"
65
# - "3.7"
76
# command to install dependencies
87
install:
9-
- pip install -r requirements.txt
8+
- pip install -r requirements.txt -r dev_requirements.txt
9+
- pip freeze
1010
# command to run tests
1111
script:
1212
- pytest -vv

README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,16 @@
77
DING0
88
=====
99
DIstribution Network GeneratOr -- A tool to generate synthetic medium and low
10-
voltage power distribution grids based on open (or at least accessible) data.
10+
voltage power distribution grids based on open (or at least publicly available)
11+
data.
1112

1213
Installation and usage
1314
----------------------
1415

1516
Ding0 is provided through the package management tool *pip*. Install it by
1617

1718
```
18-
pip3 install ding0
19+
pip install ding0
1920
```
2021

2122
Further details regarding the installation including dependencies are provided
@@ -24,12 +25,13 @@ in the [documentation](https://dingo.readthedocs.io)
2425
A [set of examples](https://dingo.readthedocs.io/en/dev/usage_details.html#examples)
2526
is provided to show how to use Ding0.
2627

27-
An overview on versions of data already generated [can be found in the wiki](https://github.com/openego/ding0/wiki/Ding0-datasets).
28+
An overview on versions of data already generated
29+
[can be found in the wiki](https://github.com/openego/ding0/wiki/Ding0-datasets).
2830

2931
LICENSE
3032
-------
3133

32-
Copyright (C) 2017 openego project group
34+
Copyright (C) 2017-2021 openego project group
3335

3436
This program is free software: you can redistribute it and/or modify it under
3537
the terms of the GNU Affero General Public License as published by the Free

dev_requirements.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pytest
2+
pytest-dependency
3+
pytest-parallel
4+
sphinx
5+
sphinx_rtd_theme

ding0/core/__init__.py

Lines changed: 118 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,12 @@
2222
from ding0.core.network.stations import *
2323
from ding0.core.structure.regions import *
2424
from ding0.core.powerflow import *
25-
from ding0.tools import pypsa_io
25+
from ding0.tools.pypsa_io import initialize_component_dataframes, fill_mvgd_component_dataframes
2626
from ding0.tools.animation import AnimationDing0
2727
from ding0.tools.plots import plot_mv_topology
2828
from ding0.flexopt.reinforce_grid import *
29+
from ding0.tools.logger import get_default_home_dir
30+
from ding0.tools.tools import merge_two_dicts_of_dataframes
2931

3032
import os
3133
import logging
@@ -38,6 +40,7 @@
3840
from sqlalchemy import func
3941
from geoalchemy2.shape import from_shape
4042
import subprocess
43+
import json
4144
import oedialect
4245

4346
if not 'READTHEDOCS' in os.environ:
@@ -266,33 +269,28 @@ def run_ding0(self, session, mv_grid_districts_no=None, debug=False, export_figu
266269
Generators are connected to grids, used approach depends on voltage
267270
level.
268271
269-
* STEP 8: Set IDs for all branches in MV and LV grids
270-
271-
While IDs of imported objects can be derived from dataset's ID, branches
272-
are created in steps 5+6 and need unique IDs (e.g. for PF calculation).
273-
274-
* STEP 9: Relocate switch disconnectors in MV grid
272+
* STEP 8: Relocate switch disconnectors in MV grid
275273
276274
Switch disconnectors are set during routing process (step 6) according
277275
to the load distribution within a ring. After further modifications of
278276
the grid within step 6+7 they have to be relocated (note: switch
279277
disconnectors are called circuit breakers in DING0 for historical reasons).
280278
281-
* STEP 10: Open all switch disconnectors in MV grid
279+
* STEP 9: Open all switch disconnectors in MV grid
282280
283281
Under normal conditions, rings are operated in open state (half-rings).
284282
Furthermore, this is required to allow powerflow for MV grid.
285283
286-
* STEP 11: Do power flow analysis of MV grid
284+
* STEP 10: Do power flow analysis of MV grid
287285
288286
The technically working MV grid created in step 6 was extended by satellite
289287
loads and generators. It is finally tested again using powerflow calculation.
290288
291-
* STEP 12: Reinforce MV grid
289+
* STEP 11: Reinforce MV grid
292290
293291
MV grid is eventually reinforced persuant to results from step 11.
294292
295-
STEP 13: Close all switch disconnectors in MV grid
293+
* STEP 12: Close all switch disconnectors in MV grid
296294
The rings are finally closed to hold a complete graph (if the SDs are open,
297295
the edges adjacent to a SD will not be exported!)
298296
"""
@@ -326,18 +324,15 @@ def run_ding0(self, session, mv_grid_districts_no=None, debug=False, export_figu
326324
if export_figures:
327325
plot_mv_topology(grid, subtitle='Generators connected', filename='2_generators_connected.png')
328326

329-
# STEP 8: Set IDs for all branches in MV and LV grids
330-
self.set_branch_ids()
331-
332-
# STEP 9: Relocate switch disconnectors in MV grid
327+
# STEP 8: Relocate switch disconnectors in MV grid
333328
self.set_circuit_breakers(debug=debug)
334329
if export_figures:
335330
plot_mv_topology(grid, subtitle='Circuit breakers relocated', filename='3_circuit_breakers_relocated.png')
336331

337-
# STEP 10: Open all switch disconnectors in MV grid
332+
# STEP 9: Open all switch disconnectors in MV grid
338333
self.control_circuit_breakers(mode='open')
339334

340-
# STEP 11: Do power flow analysis of MV grid
335+
# STEP 10: Do power flow analysis of MV grid
341336
self.run_powerflow(session, method='onthefly', export_pypsa=False, debug=debug)
342337
if export_figures:
343338
plot_mv_topology(grid, subtitle='PF result (load case)',
@@ -347,10 +342,10 @@ def run_ding0(self, session, mv_grid_districts_no=None, debug=False, export_figu
347342
filename='5_PF_result_feedin.png',
348343
line_color='loading', node_color='voltage', testcase='feedin')
349344

350-
# STEP 12: Reinforce MV grid
345+
# STEP 11: Reinforce MV grid
351346
self.reinforce_grid()
352347

353-
# STEP 13: Close all switch disconnectors in MV grid
348+
# STEP 12: Close all switch disconnectors in MV grid
354349
self.control_circuit_breakers(mode='close')
355350

356351
if export_figures:
@@ -979,7 +974,7 @@ def import_res_generators():
979974
generator = GeneratorFluctuatingDing0(
980975
id_db=id_db,
981976
mv_grid=mv_grid,
982-
capacity=row['electrical_capacity'],
977+
capacity=float(row['electrical_capacity']),
983978
type=row['generation_type'],
984979
subtype=row['generation_subtype'],
985980
v_level=int(row['voltage_level']),
@@ -988,7 +983,7 @@ def import_res_generators():
988983
generator = GeneratorDing0(
989984
id_db=id_db,
990985
mv_grid=mv_grid,
991-
capacity=row['electrical_capacity'],
986+
capacity=float(row['electrical_capacity']),
992987
type=row['generation_type'],
993988
subtype=row['generation_subtype'],
994989
v_level=int(row['voltage_level']))
@@ -1349,7 +1344,7 @@ def validate_grid_districts(self):
13491344
for grid_district in self.mv_grid_districts():
13501345

13511346
# there's only one node (MV station) => grid is empty
1352-
if len(grid_district.mv_grid._graph.nodes()) == 1:
1347+
if len(grid_district.mv_grid.graph.nodes()) == 1:
13531348
invalid_mv_grid_districts.append(grid_district)
13541349
msg_invalidity.append('MV Grid District {} seems to be empty ' \
13551350
'and ' \
@@ -1412,7 +1407,7 @@ def export_mv_grid(self, session, mv_grid_districts):
14121407
lines = []
14131408

14141409
# get nodes from grid's graph and append to corresponding array
1415-
for node in grid_district.mv_grid._graph.nodes():
1410+
for node in grid_district.mv_grid.graph.nodes():
14161411
if isinstance(node, LVLoadAreaCentreDing0):
14171412
lv_load_area_centres.append((node.geo_data.x, node.geo_data.y))
14181413
elif isinstance(node, MVCableDistributorDing0):
@@ -1527,7 +1522,7 @@ def export_mv_grid_new(self, session, mv_grid_districts):
15271522
for grid_district in self.mv_grid_districts():
15281523

15291524
# get nodes from grid's graph and create datasets
1530-
for node in grid_district.mv_grid._graph.nodes():
1525+
for node in grid_district.mv_grid.graph.nodes():
15311526
if hasattr(node, 'voltage_res'):
15321527
node_name = '_'.join(['MV',
15331528
str(grid_district.mv_grid.id_db),
@@ -1610,8 +1605,9 @@ def export_mv_grid_new(self, session, mv_grid_districts):
16101605

16111606
logger.info('=====> MV Grids exported (NEW)')
16121607

1613-
def to_dataframe(self):
1608+
def to_dataframe_old(self):
16141609
"""
1610+
Todo: remove? or replace by part of to_csv()
16151611
Export grid data to dataframes for statistical analysis.
16161612
16171613
The export to dataframe is similar to db tables exported by `export_mv_grid_new`.
@@ -1728,6 +1724,95 @@ def to_dataframe(self):
17281724

17291725
return nodes_df, edges_df
17301726

1727+
def to_csv(self, dir = '', only_export_mv = False):
1728+
'''
1729+
Function to export network to csv. Converts network in dataframes which are adapted to pypsa format.
1730+
Respectively saves files for network, buses, lines, transformers, loads and generators.
1731+
1732+
Parameters
1733+
----------
1734+
dir: :obj:`str`
1735+
Directory to which network is saved.
1736+
only_export_mv: bool
1737+
When True only mv topology is exported with aggregated lv grid districts
1738+
'''
1739+
1740+
buses_df, generators_df, lines_df, loads_df, transformer_df = initialize_component_dataframes()
1741+
if (dir == ''):
1742+
dir = get_default_home_dir() # eventuell ändern
1743+
# open all switch connectors
1744+
self.control_circuit_breakers(mode='open')
1745+
# start filling component dataframes
1746+
for grid_district in self.mv_grid_districts():
1747+
gd_components, network_df, _ = fill_mvgd_component_dataframes(
1748+
grid_district, buses_df, generators_df,
1749+
lines_df, loads_df,transformer_df, only_export_mv)
1750+
# save network and components to csv
1751+
path = os.path.join(dir, str(grid_district.id_db))
1752+
if not os.path.exists(path):
1753+
os.makedirs(path)
1754+
network_df.to_csv(os.path.join(path, 'network.csv'))
1755+
gd_components['HVMV_Transformer'].to_csv(
1756+
os.path.join(path, 'transformers_hvmv.csv'))
1757+
gd_components['Transformer'].to_csv(
1758+
os.path.join(path, 'transformers.csv'))
1759+
gd_components['Bus'].to_csv(
1760+
os.path.join(path, 'buses.csv'))
1761+
gd_components['Line'].to_csv(
1762+
os.path.join(path, 'lines.csv'))
1763+
gd_components['Load'].to_csv(
1764+
os.path.join(path, 'loads.csv'))
1765+
gd_components['Generator'].to_csv(
1766+
os.path.join(path, 'generators.csv'))
1767+
gd_components['Switch'].to_csv(
1768+
os.path.join(path, 'switches.csv'))
1769+
1770+
# Merge metadata of multiple runs
1771+
if 'metadata' not in locals():
1772+
metadata = self.metadata
1773+
1774+
else:
1775+
if isinstance(grid_district, list):
1776+
metadata['mv_grid_districts'].extend(grid_district)
1777+
else:
1778+
metadata['mv_grid_districts'].append(grid_district)
1779+
1780+
# Save metadata to disk
1781+
with open(os.path.join(path, 'Ding0_{}.meta'.format(metadata['run_id'])),
1782+
'w') as f:
1783+
json.dump(metadata, f)
1784+
1785+
1786+
def to_dataframe(self, only_export_mv = False):
1787+
'''
1788+
Function to export network to csv. Converts network in dataframes which are adapted to pypsa format.
1789+
Respectively saves files for network, buses, lines, transformers, loads and generators.
1790+
1791+
Parameters
1792+
----------
1793+
only_export_mv: bool
1794+
When True only mv topology is exported with aggregated lv grid districts
1795+
'''
1796+
buses_df, generators_df, lines_df, loads_df, transformer_df = initialize_component_dataframes()
1797+
components = {}
1798+
networks = pd.DataFrame()
1799+
# open all switch connectors
1800+
self.control_circuit_breakers(mode='open')
1801+
# start filling component dataframes
1802+
for grid_district in self.mv_grid_districts():
1803+
gd_components, network_df, _ = fill_mvgd_component_dataframes(grid_district, buses_df, generators_df,
1804+
lines_df, loads_df, transformer_df, only_export_mv)
1805+
if len(components) == 0:
1806+
components = gd_components
1807+
networks = network_df
1808+
else:
1809+
components = merge_two_dicts_of_dataframes(components, gd_components)
1810+
networks = networks.append(network_df)
1811+
1812+
components['Network'] = network_df
1813+
return components
1814+
1815+
17311816
def mv_routing(self, debug=False, animation=False):
17321817
"""
17331818
Performs routing on all MV grids.
@@ -1825,21 +1910,6 @@ def mv_parametrize_grid(self, debug=False):
18251910

18261911
logger.info('=====> MV Grids parametrized')
18271912

1828-
def set_branch_ids(self):
1829-
"""
1830-
Performs generation and setting of ids
1831-
of branches for all MV and underlying LV grids.
1832-
1833-
See Also
1834-
--------
1835-
ding0.core.network.grids.MVGridDing0.set_branch_ids
1836-
"""
1837-
1838-
for grid_district in self.mv_grid_districts():
1839-
grid_district.mv_grid.set_branch_ids()
1840-
1841-
logger.info('=====> Branch IDs set')
1842-
18431913
def set_circuit_breakers(self, debug=False):
18441914
"""
18451915
Calculates the optimal position of the existing circuit breakers
@@ -1884,7 +1954,7 @@ def control_circuit_breakers(self, mode=None):
18841954
elif mode == 'close':
18851955
logger.info('=====> MV Circuit Breakers closed')
18861956

1887-
def run_powerflow(self, session, method='onthefly', export_pypsa=False, debug=False):
1957+
def run_powerflow(self, session = None, method='onthefly', only_calc_mv = True, export_pypsa=False, debug=False, export_result_dir=None):
18881958
"""
18891959
Performs power flow calculation for all MV grids
18901960
@@ -1915,20 +1985,22 @@ def run_powerflow(self, session, method='onthefly', export_pypsa=False, debug=Fa
19151985
export_pypsa_dir = repr(grid_district.mv_grid)
19161986
else:
19171987
export_pypsa_dir = None
1918-
grid_district.mv_grid.run_powerflow(session, method='db',
1988+
grid_district.mv_grid.run_powerflow(method='db',
19191989
export_pypsa_dir=export_pypsa_dir,
1920-
debug=debug)
1990+
debug=debug,
1991+
export_result_dir=export_result_dir)
19211992

19221993
elif method == 'onthefly':
19231994
for grid_district in self.mv_grid_districts():
19241995
if export_pypsa:
19251996
export_pypsa_dir = repr(grid_district.mv_grid)
19261997
else:
19271998
export_pypsa_dir = None
1928-
grid_district.mv_grid.run_powerflow(session,
1929-
method='onthefly',
1999+
grid_district.mv_grid.run_powerflow(method='onthefly',
2000+
only_calc_mv = only_calc_mv,
19302001
export_pypsa_dir=export_pypsa_dir,
1931-
debug=debug)
2002+
debug=debug,
2003+
export_result_dir=export_result_dir)
19322004

19332005
def reinforce_grid(self):
19342006
"""

0 commit comments

Comments
 (0)