Repository: openego/ding0 (language: python)

Issue:
Python 3.8 is not supported
Requirements:

## Critical

- [x] Tests fixed (smaller updates, some reference files recreated due to format changes introduced by io-packages, e.g. nx)
- [x] Update of pandas' DatetimeIndex
- [x] Migrated from geopy's vincenty() to distance()
- [x] Packages updated
- [x] Further minor adjustments
- [x] Test for isomorphism fails #341
- [x] Set package versions

## Warnings

- [x] Adjustments due to pyproj update #343
- [x] pandas: `FutureWarning: pandas.util.testing is deprecated. Use the functions in the public API at pandas.testing instead.
    from pandas.util.testing import assert_frame_equal`
- [x] `FutureWarning: Indexing with multiple keys (implicitly converted to a tuple of keys) will be deprecated, use a list instead.`
- [ ] pypsa: `FutureWarning: Index.__or__ operating as a set operation is deprecated, in the future this will be a logical operation matching Series.__or__.  Use index.union(other) instead`
- [ ] pypsa: `DeprecationWarning: `np.complex` is a deprecated alias for the builtin `complex`. To silence this warning, use `complex` by itself. Doing this will not modify any behavior and is safe. If you specifically wanted the numpy scalar type, use `np.complex128` here.`
- [ ] pypsa: `DeprecationWarning: The default dtype for empty Series will be 'object' instead of 'float64' in a future version. Specify a dtype explicitly to silence this warning.`
- [ ] imp: `DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses`
- [ ] sqlalchemy: `SAWarning: Ignoring declarative-like tuple value of attribute 'segment': possibly a copy-and-paste error with a comma accidentally placed at the end of the line?`
- [ ] pytest: `PytestUnknownMarkWarning: Unknown pytest.mark.dependency - is this a typo?  You can register custom marks to avoid this warning`

## Final steps

- [x] Tests passing
- [ ] ~travis file includes new python versions~ Dropped in favor of #338
- [x] SPDX classifier update
- [x] Test whether installation from scratch works properly
- [x] Bump version no
- [x] Update whatsnew

Finally: make new release!

Fix/#343 update pyproj crs transformation



Discussion / hints:

Fix patch:
diff --git a/dev_requirements.txt b/dev_requirements.txt
index 5c6e7ba8..c5a08e72 100644
--- a/dev_requirements.txt
+++ b/dev_requirements.txt
@@ -1,4 +1,5 @@
 pytest
 pytest-dependency
+pytest-parallel
 sphinx
 sphinx_rtd_theme
\ No newline at end of file
diff --git a/ding0/core/network/grids.py b/ding0/core/network/grids.py
index 4d9daf76..ff969462 100644
--- a/ding0/core/network/grids.py
+++ b/ding0/core/network/grids.py
@@ -22,18 +22,17 @@
 from ding0.grid.mv_grid import mv_routing, mv_connect
 from ding0.grid.lv_grid import build_grid, lv_connect
 from ding0.tools import config as cfg_ding0, pypsa_io, tools
-from ding0.tools.geo import calc_geo_dist_vincenty
+from ding0.tools.geo import calc_geo_dist
 from ding0.grid.mv_grid.tools import set_circuit_breakers
 from ding0.flexopt.reinforce_grid import *
 from ding0.core.structure.regions import LVLoadAreaCentreDing0
 from ding0.tools.pypsa_io import initialize_component_dataframes, fill_mvgd_component_dataframes
 
 import os
+import logging
 import networkx as nx
 from datetime import datetime
-import pyproj
-from functools import partial
-import logging
+from pyproj import Transformer
 
 if not 'READTHEDOCS' in os.environ:
     from shapely.ops import transform
@@ -207,7 +206,7 @@ def rings_nodes(self, include_root_node=False, include_satellites=False):
             Circuit breakers must be closed to find rings, this is done automatically.
         """
         for circ_breaker in self.circuit_breakers():
-            if circ_breaker.status is 'open':
+            if circ_breaker.status == 'open':
                 circ_breaker.close()
                 logger.info('Circuit breakers were closed in order to find MV '
                             'rings')
@@ -469,10 +468,7 @@ def set_voltage_level(self, mode='distance'):
 
             # transform MVGD's area to epsg 3035
             # to achieve correct area calculation
-            projection = partial(
-                pyproj.transform,
-                pyproj.Proj(init='epsg:4326'),  # source coordinate system
-                pyproj.Proj(init='epsg:3035'))  # destination coordinate system
+            projection = Transformer.from_crs("epsg:4326", "epsg:3035", always_xy=True).transform
 
             # calculate load density
             kw2mw = 1e-3
@@ -501,7 +497,7 @@ def set_voltage_level(self, mode='distance'):
             for node in self.graph_nodes_sorted():
                 if isinstance(node, LVLoadAreaCentreDing0):
                     # calc distance from MV-LV station to LA centre
-                    dist_node = calc_geo_dist_vincenty(self.station(), node) / 1e3
+                    dist_node = calc_geo_dist(self.station(), node) / 1e3
                     if dist_node > dist_max:
                         dist_max = dist_node
 
diff --git a/ding0/core/powerflow/__init__.py b/ding0/core/powerflow/__init__.py
index 2a3e8432..02436c8f 100644
--- a/ding0/core/powerflow/__init__.py
+++ b/ding0/core/powerflow/__init__.py
@@ -14,7 +14,7 @@
 
 
 from datetime import datetime
-from pandas import DatetimeIndex
+from pandas import DatetimeIndex, date_range
 
 
 class PFConfigDing0:
@@ -74,9 +74,9 @@ def __init__(self, **kwargs):
                 # create pandas DatetimeIndex object for given values
                 self._timeranges = []
                 for _ in enumerate(self._scenarios):
-                    self._timeranges.append(DatetimeIndex(freq=self._resolution,
-                                                          periods=timesteps_count,
-                                                          start=timestep_start))
+                    self._timeranges.append(date_range(freq=self._resolution,
+                                                       periods=timesteps_count,
+                                                       start=timestep_start))
 
         elif len(self._scenarios) != len(self._timeranges):
             raise ValueError('PF config: Count of scenarios has to equal count of timeranges.')
diff --git a/ding0/grid/lv_grid/lv_connect.py b/ding0/grid/lv_grid/lv_connect.py
index ceaac0e6..351ac83f 100644
--- a/ding0/grid/lv_grid/lv_connect.py
+++ b/ding0/grid/lv_grid/lv_connect.py
@@ -16,7 +16,7 @@
 from ding0.core.network import BranchDing0
 
 from ding0.tools import config as cfg_ding0
-from ding0.tools.geo import calc_geo_dist_vincenty
+from ding0.tools.geo import calc_geo_dist
 from ding0.grid.tools import cable_type
 import logging
 import random
@@ -78,7 +78,7 @@ def lv_connect_generators(lv_grid_district, graph, debug=False):
         if generator.v_level == 6:
             lv_station = lv_grid_district.lv_grid.station()
 
-            branch_length = calc_geo_dist_vincenty(generator, lv_station)
+            branch_length = calc_geo_dist(generator, lv_station)
             branch_type = cable_type(
                 generator.capacity / (cable_lf * cos_phi_gen),
                 v_nom,
diff --git a/ding0/grid/mv_grid/mv_connect.py b/ding0/grid/mv_grid/mv_connect.py
index 93e385bc..a5e60f78 100644
--- a/ding0/grid/mv_grid/mv_connect.py
+++ b/ding0/grid/mv_grid/mv_connect.py
@@ -14,10 +14,9 @@
 
 
 import os
-import pyproj
-from functools import partial
 import time
 import logging
+from pyproj import Transformer
 
 from ding0.core.network.stations import *
 from ding0.core.network import BranchDing0, GeneratorDing0
@@ -25,7 +24,7 @@
 from ding0.core.structure.groups import LoadAreaGroupDing0
 from ding0.core.structure.regions import LVLoadAreaCentreDing0
 from ding0.tools import config as cfg_ding0
-from ding0.tools.geo import calc_geo_branches_in_buffer,calc_geo_dist_vincenty,\
+from ding0.tools.geo import calc_geo_branches_in_buffer,calc_geo_dist,\
                             calc_geo_centre_point, calc_geo_branches_in_polygon
 
 if not 'READTHEDOCS' in os.environ:
@@ -407,7 +406,7 @@ def connect_node(node, node_shp, mv_grid, target_obj, proj, graph, conn_dist_rin
                 # along node)
                 graph.remove_edge(adj_node1, adj_node2)
 
-                branch_length = calc_geo_dist_vincenty(adj_node1, node)
+                branch_length = calc_geo_dist(adj_node1, node)
                 branch = BranchDing0(length=branch_length,
                                      circuit_breaker=circ_breaker,
                                      kind=branch_kind,
@@ -418,7 +417,7 @@ def connect_node(node, node_shp, mv_grid, target_obj, proj, graph, conn_dist_rin
                     circ_breaker.branch = branch
                 graph.add_edge(adj_node1, node, branch=branch)
 
-                branch_length = calc_geo_dist_vincenty(adj_node2, node)
+                branch_length = calc_geo_dist(adj_node2, node)
                 graph.add_edge(adj_node2, node, branch=BranchDing0(length=branch_length,
                                                                    kind=branch_kind,
                                                                    grid=mv_grid,
@@ -456,7 +455,7 @@ def connect_node(node, node_shp, mv_grid, target_obj, proj, graph, conn_dist_rin
 
                 graph.remove_edge(adj_node1, adj_node2)
 
-                branch_length = calc_geo_dist_vincenty(adj_node1, cable_dist)
+                branch_length = calc_geo_dist(adj_node1, cable_dist)
                 branch = BranchDing0(length=branch_length,
                                      circuit_breaker=circ_breaker,
                                      kind=branch_kind,
@@ -467,7 +466,7 @@ def connect_node(node, node_shp, mv_grid, target_obj, proj, graph, conn_dist_rin
                     circ_breaker.branch = branch
                 graph.add_edge(adj_node1, cable_dist, branch=branch)
 
-                branch_length = calc_geo_dist_vincenty(adj_node2, cable_dist)
+                branch_length = calc_geo_dist(adj_node2, cable_dist)
                 graph.add_edge(adj_node2, cable_dist, branch=BranchDing0(length=branch_length,
                                                                          kind=branch_kind,
                                                                          grid=mv_grid,
@@ -481,7 +480,7 @@ def connect_node(node, node_shp, mv_grid, target_obj, proj, graph, conn_dist_rin
                 branch_kind = mv_grid.default_branch_kind
                 branch_type = mv_grid.default_branch_type
 
-                branch_length = calc_geo_dist_vincenty(node, cable_dist)
+                branch_length = calc_geo_dist(node, cable_dist)
                 graph.add_edge(node, cable_dist, branch=BranchDing0(length=branch_length,
                                                                     kind=branch_kind,
                                                                     grid=mv_grid,
@@ -529,7 +528,7 @@ def connect_node(node, node_shp, mv_grid, target_obj, proj, graph, conn_dist_rin
             branch_ring = mv_grid.get_ring_from_node(target_obj['obj'])
 
             # add new branch for satellite (station to station)
-            branch_length = calc_geo_dist_vincenty(node, target_obj['obj'])
+            branch_length = calc_geo_dist(node, target_obj['obj'])
             graph.add_edge(node, target_obj['obj'], branch=BranchDing0(length=branch_length,
                                                                        kind=branch_kind,
                                                                        grid=mv_grid,
@@ -576,7 +575,7 @@ def disconnect_node(node, target_obj_result, graph, debug):
         if len(neighbor_nodes) == 2:
             node.grid.remove_cable_distributor(target_obj_result)
 
-            branch_length = calc_geo_dist_vincenty(neighbor_nodes[0], neighbor_nodes[1])
+            branch_length = calc_geo_dist(neighbor_nodes[0], neighbor_nodes[1])
             graph.add_edge(neighbor_nodes[0], neighbor_nodes[1], branch=BranchDing0(length=branch_length,
                                                                                     kind=branch_kind,
                                                                                     grid=node.grid,
@@ -663,16 +662,9 @@ def mv_connect_satellites(mv_grid, graph, mode='normal', debug=False):
     start = time.time()
 
     # WGS84 (conformal) to ETRS (equidistant) projection
-    proj1 = partial(
-            pyproj.transform,
-            pyproj.Proj(init='epsg:4326'),  # source coordinate system
-            pyproj.Proj(init='epsg:3035'))  # destination coordinate system
-
+    proj1 = Transformer.from_crs("epsg:4326", "epsg:3035", always_xy=True).transform
     # ETRS (equidistant) to WGS84 (conformal) projection
-    proj2 = partial(
-            pyproj.transform,
-            pyproj.Proj(init='epsg:3035'),  # source coordinate system
-            pyproj.Proj(init='epsg:4326'))  # destination coordinate system
+    proj2 = Transformer.from_crs("epsg:3035", "epsg:4326", always_xy=True).transform
 
     # check all nodes
     if mode == 'normal':
@@ -748,16 +740,9 @@ def mv_connect_stations(mv_grid_district, graph, debug=False):
     """
 
     # WGS84 (conformal) to ETRS (equidistant) projection
-    proj1 = partial(
-            pyproj.transform,
-            pyproj.Proj(init='epsg:4326'),  # source coordinate system
-            pyproj.Proj(init='epsg:3035'))  # destination coordinate system
-
+    proj1 = Transformer.from_crs("epsg:4326", "epsg:3035", always_xy=True).transform
     # ETRS (equidistant) to WGS84 (conformal) projection
-    proj2 = partial(
-            pyproj.transform,
-            pyproj.Proj(init='epsg:3035'),  # source coordinate system
-            pyproj.Proj(init='epsg:4326'))  # destination coordinate system
+    proj2 = Transformer.from_crs("epsg:3035", "epsg:4326", always_xy=True).transform
 
     conn_dist_weight = cfg_ding0.get('mv_connect', 'load_area_sat_conn_dist_weight')
     conn_dist_ring_mod = cfg_ding0.get('mv_connect', 'load_area_stat_conn_dist_ring_mod')
@@ -793,7 +778,7 @@ def mv_connect_stations(mv_grid_district, graph, debug=False):
                     # delete old branch to Load Area centre and create a new one to LV station
                     graph.remove_edge(lv_load_area_centre, node)
 
-                    branch_length = calc_geo_dist_vincenty(lv_station, node)
+                    branch_length = calc_geo_dist(lv_station, node)
                     branch = BranchDing0(length=branch_length,
                                          circuit_breaker=circ_breaker,
                                          kind=branch_kind,
@@ -876,7 +861,7 @@ def mv_connect_stations(mv_grid_district, graph, debug=False):
                     # delete old branch to Load Area centre and create a new one to LV station
                     graph.remove_edge(lv_load_area_centre, node)
 
-                    branch_length = calc_geo_dist_vincenty(cable_dist, node)
+                    branch_length = calc_geo_dist(cable_dist, node)
                     branch = BranchDing0(length=branch_length,
                                          circuit_breaker=circ_breaker,
                                          kind=branch_kind,
@@ -930,16 +915,9 @@ def mv_connect_generators(mv_grid_district, graph, debug=False):
     generator_buffer_radius_inc = cfg_ding0.get('mv_connect', 'generator_buffer_radius_inc')
 
     # WGS84 (conformal) to ETRS (equidistant) projection
-    proj1 = partial(
-            pyproj.transform,
-            pyproj.Proj(init='epsg:4326'),  # source coordinate system
-            pyproj.Proj(init='epsg:3035'))  # destination coordinate system
-
+    proj1 = Transformer.from_crs("epsg:4326", "epsg:3035", always_xy=True).transform
     # ETRS (equidistant) to WGS84 (conformal) projection
-    proj2 = partial(
-            pyproj.transform,
-            pyproj.Proj(init='epsg:3035'),  # source coordinate system
-            pyproj.Proj(init='epsg:4326'))  # destination coordinate system
+    proj2 = Transformer.from_crs("epsg:3035", "epsg:4326", always_xy=True).transform
 
     for generator in sorted(mv_grid_district.mv_grid.generators(), key=lambda x: repr(x)):
 
@@ -947,7 +925,7 @@ def mv_connect_generators(mv_grid_district, graph, debug=False):
         if generator.v_level == 4:
             mv_station = mv_grid_district.mv_grid.station()
 
-            branch_length = calc_geo_dist_vincenty(generator, mv_station)
+            branch_length = calc_geo_dist(generator, mv_station)
 
             # TODO: set branch type to something reasonable (to be calculated)
             branch_kind = mv_grid_district.mv_grid.default_branch_kind
diff --git a/ding0/grid/mv_grid/mv_routing.py b/ding0/grid/mv_grid/mv_routing.py
index cb3cdd77..88601eb6 100644
--- a/ding0/grid/mv_grid/mv_routing.py
+++ b/ding0/grid/mv_grid/mv_routing.py
@@ -18,7 +18,7 @@
 from ding0.grid.mv_grid.models.models import Graph, Node
 from ding0.grid.mv_grid.util import util, data_input
 from ding0.grid.mv_grid.solvers import savings, local_search
-from ding0.tools.geo import calc_geo_dist_vincenty, calc_geo_dist_matrix_vincenty, calc_geo_centre_point
+from ding0.tools.geo import calc_geo_dist, calc_geo_dist_matrix, calc_geo_centre_point
 from ding0.tools import config as cfg_ding0
 from ding0.core.network.stations import *
 from ding0.core.structure.regions import LVLoadAreaCentreDing0
@@ -93,7 +93,7 @@ def ding0_graph_to_routing_specs(graph):
 
     specs['NODE_COORD_SECTION'] = nodes_pos
     specs['DEMAND'] = nodes_demands
-    specs['MATRIX'] = calc_geo_dist_matrix_vincenty(nodes_pos)
+    specs['MATRIX'] = calc_geo_dist_matrix(nodes_pos)
     specs['IS_AGGREGATED'] = nodes_agg
 
     return specs
@@ -204,7 +204,7 @@ def routing_solution_to_ding0_graph(graph, solution):
                     node1.lv_load_area.ring = ring
 
                 # set branch length
-                b.length = calc_geo_dist_vincenty(node1, node2)
+                b.length = calc_geo_dist(node1, node2)
 
                 # set branch kind and type
                 # 1) default
diff --git a/ding0/tools/geo.py b/ding0/tools/geo.py
index 4096d443..9e1a10c3 100644
--- a/ding0/tools/geo.py
+++ b/ding0/tools/geo.py
@@ -14,9 +14,8 @@
 
 
 import os
-from geopy.distance import vincenty
-import pyproj
-from functools import partial
+from geopy.distance import geodesic
+from pyproj import Transformer
 
 from ding0.tools import config as cfg_ding0
 import logging
@@ -116,7 +115,7 @@ def calc_geo_branches_in_buffer(node, mv_grid, radius, radius_inc, proj):
     return branches
 
 
-def calc_geo_dist_vincenty(node_source, node_target):
+def calc_geo_dist(node_source, node_target):
     """ Calculates the geodesic distance between `node_source` and `node_target`
     incorporating the detour factor specified in :file:`ding0/ding0/config/config_calc.cfg`.
 
@@ -135,8 +134,8 @@ def calc_geo_dist_vincenty(node_source, node_target):
 
     branch_detour_factor = cfg_ding0.get('assumptions', 'branch_detour_factor')
 
-    # notice: vincenty takes (lat,lon)
-    branch_length = branch_detour_factor * vincenty((node_source.geo_data.y, node_source.geo_data.x),
+    # notice: geodesic takes (lat,lon)
+    branch_length = branch_detour_factor * geodesic((node_source.geo_data.y, node_source.geo_data.x),
                                                     (node_target.geo_data.y, node_target.geo_data.x)).m
 
     # ========= BUG: LINE LENGTH=0 WHEN CONNECTING GENERATORS ===========
@@ -152,14 +151,13 @@ def calc_geo_dist_vincenty(node_source, node_target):
     return branch_length
 
 
-def calc_geo_dist_matrix_vincenty(nodes_pos):
+def calc_geo_dist_matrix(nodes_pos):
     """ Calculates the geodesic distance between all nodes in `nodes_pos` incorporating the detour factor in config_calc.cfg.
         
-    For every two points/coord it uses geopy's vincenty function (formula devised by Thaddeus Vincenty, 
-    with an accurate ellipsoidal model of the earth). As default ellipsoidal model of the earth WGS-84 is used.
+    For every two points/coord it uses geopy's geodesic function. As default ellipsoidal model of the earth WGS-84 is used.
     For more options see
     
-    https://geopy.readthedocs.org/en/1.10.0/index.html?highlight=vincenty#geopy.distance.vincenty
+    https://geopy.readthedocs.io/en/stable/index.html?highlight=geodesic#geopy.distance.geodesic
 
     Parameters
     ----------
@@ -196,8 +194,8 @@ def calc_geo_dist_matrix_vincenty(nodes_pos):
 
         for j in nodes_pos:
             pos_dest = tuple(nodes_pos[j])
-            # notice: vincenty takes (lat,lon), thus the (x,y)/(lon,lat) tuple is reversed
-            distance = branch_detour_factor * vincenty(tuple(reversed(pos_origin)), tuple(reversed(pos_dest))).km
+            # notice: geodesic takes (lat,lon), thus the (x,y)/(lon,lat) tuple is reversed
+            distance = branch_detour_factor * geodesic(tuple(reversed(pos_origin)), tuple(reversed(pos_dest))).km
             matrix[i][j] = distance
 
     return matrix
@@ -220,20 +218,14 @@ def calc_geo_centre_point(node_source, node_target):
         Distance in m.
     """
 
-    proj_source = partial(
-            pyproj.transform,
-            pyproj.Proj(init='epsg:4326'),  # source coordinate system
-            pyproj.Proj(init='epsg:3035'))  # destination coordinate system
-
+    # WGS84 (conformal) to ETRS (equidistant) projection
+    proj_source = Transformer.from_crs("epsg:4326", "epsg:3035", always_xy=True).transform
     # ETRS (equidistant) to WGS84 (conformal) projection
-    proj_target = partial(
-            pyproj.transform,
-            pyproj.Proj(init='epsg:3035'),  # source coordinate system
-            pyproj.Proj(init='epsg:4326'))  # destination coordinate system
+    proj_target = Transformer.from_crs("epsg:3035", "epsg:4326", always_xy=True).transform
 
     branch_shp = transform(proj_source, LineString([node_source.geo_data, node_target.geo_data]))
 
-    distance = vincenty((node_source.geo_data.y, node_source.geo_data.x),
+    distance = geodesic((node_source.geo_data.y, node_source.geo_data.x),
                         (node_target.geo_data.y, node_target.geo_data.x)).m
 
     centre_point_shp = transform(proj_target, branch_shp.interpolate(distance/2))
diff --git a/ding0/tools/pypsa_io.py b/ding0/tools/pypsa_io.py
index caa79b0b..aeb226c5 100644
--- a/ding0/tools/pypsa_io.py
+++ b/ding0/tools/pypsa_io.py
@@ -31,7 +31,7 @@
 
 from geoalchemy2.shape import from_shape
 from math import tan, acos, pi, sqrt
-from pandas import Series, DataFrame, DatetimeIndex
+from pandas import Series, DataFrame, date_range
 from pypsa.io import import_series_from_dataframe
 from pypsa import Network
 from shapely.geometry import Point
@@ -1317,9 +1317,9 @@ def run_powerflow_onthefly(components, components_data, grid,
         data_integrity(components, components_data)
 
     # define investigated time range
-    timerange = DatetimeIndex(freq=resolution,
-                              periods=timesteps,
-                              start=start_time)
+    timerange = date_range(freq=resolution,
+                           periods=timesteps,
+                           start=start_time)
 
     # TODO: Instead of hard coding PF config, values from class PFConfigDing0 
     #  can be used here.
diff --git a/ding0/tools/results.py b/ding0/tools/results.py
index f9c98eed..c929048a 100644
--- a/ding0/tools/results.py
+++ b/ding0/tools/results.py
@@ -32,8 +32,7 @@
 from ding0.core.network.loads import LVLoadDing0
 from ding0.core import LVLoadAreaCentreDing0
 
-import pyproj
-from functools import partial
+from pyproj import Transformer
 
 from geoalchemy2.shape import from_shape
 from sqlalchemy.orm import sessionmaker
@@ -294,12 +293,7 @@ def calculate_lvgd_stats(nw):
     """
     ##############################
     #  ETRS (equidistant) to WGS84 (conformal) projection
-    proj = partial(
-        pyproj.transform,
-        # pyproj.Proj(init='epsg:3035'),  # source coordinate system
-        #  pyproj.Proj(init='epsg:4326'))  # destination coordinate system
-        pyproj.Proj(init='epsg:4326'),  # source coordinate system
-        pyproj.Proj(init='epsg:3035'))  # destination coordinate system
+    proj = Transformer.from_crs("epsg:4326", "epsg:3035", always_xy=True).transform
     ##############################
     # close circuit breakers
     nw.control_circuit_breakers(mode='close')
@@ -815,12 +809,7 @@ def calculate_mvgd_stats(nw):
 
         # geographic
         #  ETRS (equidistant) to WGS84 (conformal) projection
-        proj = partial(
-            pyproj.transform,
-            # pyproj.Proj(init='epsg:3035'),  # source coordinate system
-            # pyproj.Proj(init='epsg:4326'))  # destination coordinate system
-            pyproj.Proj(init='epsg:4326'),  # source coordinate system
-            pyproj.Proj(init='epsg:3035'))  # destination coordinate system
+        proj = Transformer.from_crs("epsg:4326", "epsg:3035", always_xy=True).transform
         district_geo = transform(proj, district.geo_data)
         other_nodes_dict[district.mv_grid.id_db].update({'Dist_area': district_geo.area})
 
@@ -991,16 +980,15 @@ def calculate_mvgd_stats(nw):
 
         mvgd_stats = pd.concat([mvgd_stats, LA_data], axis=1)
 
-        LA_data = LA_df.groupby(['grid_id'])['population',
-                                             'residential_peak_load',
-                                             'retail_peak_load',
-                                             'industrial_peak_load',
-                                             'agricultural_peak_load',
-                                             'total_peak_load',
-                                             'lv_generation',
-                                             'lv_gens_lvl_6',
-                                             'lv_gens_lvl_7'
-        ].sum()
+        LA_data = LA_df.groupby(['grid_id'])[['population',
+                                              'residential_peak_load',
+                                              'retail_peak_load',
+                                              'industrial_peak_load',
+                                              'agricultural_peak_load',
+                                              'total_peak_load',
+                                              'lv_generation',
+                                              'lv_gens_lvl_6',
+                                              'lv_gens_lvl_7']].sum()
         LA_data.columns = ['LA Total Population',
                            'LA Total LV Peak Load Residential',
                            'LA Total LV Peak Load Retail',
@@ -1026,9 +1014,9 @@ def calculate_mvgd_stats(nw):
         sat_LA_data.columns = ['Number of Load Areas - Satellite']
         mvgd_stats = pd.concat([mvgd_stats, sat_LA_data], axis=1)
 
-        agg_LA_data = LA_df[LA_df['is_agg']].groupby(['grid_id'])['population',
-                                                                  'lv_generation',
-                                                                  'total_peak_load'].sum()
+        agg_LA_data = LA_df[LA_df['is_agg']].groupby(['grid_id'])[['population',
+                                                                   'lv_generation',
+                                                                   'total_peak_load']].sum()
         agg_LA_data.columns = ['LA Aggregated Population',
                                'LA Aggregated LV Gen. Cap.', 'LA Aggregated LV Peak Load total'
                                ]
@@ -1037,6 +1025,7 @@ def calculate_mvgd_stats(nw):
     ###################################
     mvgd_stats = mvgd_stats.fillna(0)
     mvgd_stats = mvgd_stats[sorted(mvgd_stats.columns.tolist())]
+    mvgd_stats.index.name = 'grid_id'
     return mvgd_stats
 
 
diff --git a/doc/getting_started.rst b/doc/getting_started.rst
index 3a9c47e0..56fc2b41 100644
--- a/doc/getting_started.rst
+++ b/doc/getting_started.rst
@@ -7,7 +7,7 @@ Installation
 ============
 
 .. note::
-    Installation is only tested on (debian like) linux OS.
+    Installation is only tested on (Debian-like) Linux OS.
 
 Ding0 is provided through PyPi package management and, thus, installable from
 sources of pip3.
diff --git a/doc/whatsnew.rst b/doc/whatsnew.rst
index bd205caf..804799b8 100644
--- a/doc/whatsnew.rst
+++ b/doc/whatsnew.rst
@@ -8,6 +8,7 @@ See what's new as per release!
     :local:
     :backlinks: top
 
+.. include::  whatsnew/v0-2-0.rst
 .. include::  whatsnew/v0-1-12.rst
 .. include::  whatsnew/v0-1-10.rst
 .. include::  whatsnew/v0-1-9.rst
diff --git a/doc/whatsnew/v0-1-13.rst b/doc/whatsnew/v0-1-13.rst
deleted file mode 100644
index 75d58dfb..00000000
--- a/doc/whatsnew/v0-1-13.rst
+++ /dev/null
@@ -1,13 +0,0 @@
-Release v0.1.13 month day, year
-+++++++++++++++++++++++++++++++++++
-
-Changes
--------
-
-* Reproducible [stats](https://github.com/openego/ding0/blob/dev/ding0/tools/results.py#L281-L1210) by fixing `#315 <https://github.com/openego/ding0/pull/324>`_, for details see
-  `#324 <https://github.com/openego/ding0/pull/324>`_
-
-* In the CSV export, (in-building) household loads and generators are no more
-  contained as extra nodes but directly connected to the house's grid
-  connection point to reduce the number of nodes.
-  `#322 <https://github.com/openego/ding0/issues/322>`_
diff --git a/doc/whatsnew/v0-2-0.rst b/doc/whatsnew/v0-2-0.rst
new file mode 100644
index 00000000..6df2b344
--- /dev/null
+++ b/doc/whatsnew/v0-2-0.rst
@@ -0,0 +1,18 @@
+Release v0.2.0 (May 20, 2021)
++++++++++++++++++++++++++++++
+
+Changes
+-------
+
+* Added Python 3.8 support `#325 <https://github.com/openego/ding0/issues/325>`_
+* CSV export in PyPSA format `#307 <https://github.com/openego/ding0/issues/307>`_ ,
+  this required further changes and fixing of tests, cf. `#312 <https://github.com/openego/ding0/pull/312>`_
+* Switched from pyproj1 to pyproj2 for CRS transformations `#343 <https://github.com/openego/ding0/issues/343>`_
+* Reproducible [stats](https://github.com/openego/ding0/blob/dev/ding0/tools/results.py#L281-L1210)
+  by fixing `#315 <https://github.com/openego/ding0/issues/315>`_, for details see
+  `#324 <https://github.com/openego/ding0/pull/324>`_
+* In the CSV export, (in-building) household loads and generators are no more
+  contained as extra nodes but directly connected to the house's grid
+  connection point to reduce the number of nodes.
+  `#322 <https://github.com/openego/ding0/issues/322>`_
+* Fix sum capacity of grid generators `#326 <https://github.com/openego/ding0/issues/326>`_
diff --git a/requirements.txt b/requirements.txt
index 1a61a653..b1a49002 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,15 +1,15 @@
-networkx == 2.4
-geopy >= 1.11.0, <= 1.11.0
-pandas >= 0.20.3, <= 0.23.0
-pyproj == 2.2.0
-sqlalchemy >= 1.0.11, <= 1.2.0
-geoalchemy2 >= 0.2.6, <= 0.4.1
-matplotlib  >= 1.5.3, <= 2.1.2
+networkx == 2.5.1
+geopy == 2.1.0
+pandas == 1.2.4
+pyproj == 3.0.1
+sqlalchemy <= 1.3.24
+geoalchemy2 == 0.6.3
+matplotlib >= 1.5.3, <= 3.4.2
 egoio @ git+https://github.com/openego/ego.io.git@ab327e3c8bdb53b5205af84b41b02700f6d3fb20#egg=egoio
-shapely >= 1.5.12, <= 1.7
-pypsa >= 0.11.0, <= 0.11.0
-seaborn == 0.10.1
+shapely == 1.7.1
+pypsa >= 0.11.0, <= 0.17.1
+seaborn == 0.11.1
 unittest2 == 1.1.0
-oedialect == 0.0.6.dev0
-geopandas == 0.7.0
-descartes == 1.1.0
+oedialect == 0.0.10
+geopandas == 0.9.0
+descartes == 1.1.0
\ No newline at end of file
diff --git a/rtd_requirements.txt b/rtd_requirements.txt
index efea3e46..2041e4ca 100644
--- a/rtd_requirements.txt
+++ b/rtd_requirements.txt
@@ -1,19 +1,14 @@
 sphinx_rtd_theme
 networkx >= 2.0
-geopy >=1.11.0, <=1.11.0
-pandas >=0.20.3, <=0.20.3
-pyproj >=1.9.5.1, <=1.9.5.1
-sqlalchemy >= 1.0.11, <= 1.2.0
-geoalchemy2 >= 0.2.6, <= 0.4.1
-matplotlib >=2.0.0, <=2.0.2
+geopy == 2.1.0
+pandas == 1.2.4
+pyproj == 3.0.1
+sqlalchemy <= 1.3.24
+geoalchemy2 == 0.6.3
+matplotlib >= 1.5.3, <= 3.4.2
 egoio >=0.4.7
-pypsa >=0.11.0, <=0.11.0
-seaborn
-unittest2
-oedialect == 0.0.6.dev0
-mock
-
-
-
-
-
+pypsa >= 0.11.0, <= 0.17.1
+seaborn == 0.11.1
+unittest2 == 1.1.0
+oedialect == 0.0.10
+mock
\ No newline at end of file
diff --git a/setup.py b/setup.py
index 0efd991f..8bcd8322 100644
--- a/setup.py
+++ b/setup.py
@@ -26,7 +26,7 @@ def read(fname):
     dev_requirements = f.read().splitlines()
 
 setup(name='ding0',
-      version='v0.1.13dev',
+      version='v0.2.0',
       author='Reiner Lemoine Institut, openego development group',
       author_email='jonathan.amme@rl-institut.de',
       description='DIstribution Network GeneratOr',
@@ -82,6 +82,6 @@ def read(fname):
         "License :: OSI Approved :: GNU Affero General Public License v3",
         "Natural Language :: English",
         "Operating System :: POSIX :: Linux",
-        "Programming Language :: Python :: 3.6",
+        "Programming Language :: Python :: 3.8",
         "Topic :: Scientific/Engineering"],
       )


Test patch:
diff --git a/tests/core/network/test_network.py b/tests/core/network/test_network.py
index 7fcef24c..782e1458 100644
--- a/tests/core/network/test_network.py
+++ b/tests/core/network/test_network.py
@@ -13,10 +13,9 @@
                                  calculate_mvgd_stats,
                                  calculate_mvgd_voltage_current_stats)
 import pandas as pd
-from pandas.util.testing import assert_frame_equal
+from pandas.testing import assert_frame_equal
 import os
 import numpy as np
-from tests.tools.help_functions import compare_data_frames_by_tolerance
 
 
 class TestGridDing0(object):
diff --git a/tests/core/test_core.py b/tests/core/test_core.py
index 16f2777e..14cddf60 100644
--- a/tests/core/test_core.py
+++ b/tests/core/test_core.py
@@ -6,8 +6,6 @@
 import pandas as pd
 import os
 from tests.core.network.test_grids import TestMVGridDing0
-# from pandas.util.testing import assert_frame_equal, assert_series_equal
-# from ding0.tools.results import load_nd_from_pickle
 from ding0.core import NetworkDing0
 import shutil
 
diff --git a/tests/grid/test_grid.py b/tests/grid/test_grid.py
index 7587bb11..d7a2158e 100644
--- a/tests/grid/test_grid.py
+++ b/tests/grid/test_grid.py
@@ -2,8 +2,9 @@
 from ding0.grid.mv_grid import mv_connect
 from networkx.readwrite.graphml import read_graphml
 import networkx as nx
-from networkx.algorithms.isomorphism import categorical_edge_match
+from networkx.algorithms.isomorphism import categorical_edge_match, numerical_edge_match
 import os
+import numbers
 
 
 def test_mv_connect_generators():
@@ -26,7 +27,7 @@ def test_mv_connect_generators():
     for k, v in edges.items():
         edge_attrs = v.__dict__
         edge_attrs.update(**edge_attrs["type"].to_dict())
-        for kk in ["type", "ring", "branch", "circuit_breaker"]:
+        for kk in ["type", "ring", "branch", "circuit_breaker", "grid"]:
             edge_attrs.pop(kk, None)
         edge_attrs = {k1: v1 for k1, v1 in edge_attrs.items() if v1}
         edges[k] = edge_attrs
@@ -35,19 +36,27 @@ def test_mv_connect_generators():
         d.pop("branch", None)
     nx.set_edge_attributes(graph, edges)
 
+    expected_file_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "test_data"))
+
     # Snippet to export new comparison graph:
     # graph_new = nx.Graph()
     # graph_new.add_nodes_from([repr(n) for n in graph.nodes()])
     # for n1, n2, a in graph.edges(data=True):
     #     a['grid'] = repr(a['grid'])
     #     graph_new.add_edge(repr(n1), repr(n2), **a)
-    # nx.write_graphml(graph_new, os.path.join(expected_file_path, "grid_mv_connect_generators_expected.graphml")
+    # nx.write_graphml(graph_new, os.path.join(expected_file_path, "grid_mv_connect_generators_expected.graphml"))
 
     # Get comparison data (expected) and compare all attributes
-    expected_file_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "test_data"))
     expected_graph = read_graphml(os.path.join(
         expected_file_path,
         "grid_mv_connect_generators_expected.graphml"))
-    attr_names = [_ for _ in edge_attrs.keys() if _ != 'grid']
-    em = categorical_edge_match(attr_names, [0] * len(attr_names))
-    assert nx.is_isomorphic(graph, expected_graph, edge_match=em)
+
+    # distinct categorical and numeric attrs
+    edge_attrs_num = [a for a in edge_attrs if isinstance(list(edges.values())[0].get(a, None), numbers.Number)]
+    edge_attrs_cat = [a for a in edge_attrs if a not in edge_attrs_num]
+
+    em_cat = categorical_edge_match(edge_attrs_cat, [0] * len(edge_attrs_cat))
+    em_num = numerical_edge_match(edge_attrs_num, [0] * len(edge_attrs_num), rtol=1e-3)
+
+    assert nx.is_isomorphic(graph, expected_graph, edge_match=em_cat)
+    assert nx.is_isomorphic(graph, expected_graph, edge_match=em_num)
diff --git a/tests/test_data/grid_mv_connect_generators_expected.graphml b/tests/test_data/grid_mv_connect_generators_expected.graphml
index e53d960e..35b8d96c 100644
--- a/tests/test_data/grid_mv_connect_generators_expected.graphml
+++ b/tests/test_data/grid_mv_connect_generators_expected.graphml
@@ -1,15 +1,15 @@
 <?xml version='1.0' encoding='utf-8'?>
 <graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
-  <key attr.name="length" attr.type="long" for="edge" id="d9" />
-  <key attr.name="C_per_km" attr.type="double" for="edge" id="d8" />
-  <key attr.name="L_per_km" attr.type="double" for="edge" id="d7" />
-  <key attr.name="R_per_km" attr.type="double" for="edge" id="d6" />
-  <key attr.name="I_max_th" attr.type="long" for="edge" id="d5" />
-  <key attr.name="U_n" attr.type="long" for="edge" id="d4" />
-  <key attr.name="name" attr.type="string" for="edge" id="d3" />
-  <key attr.name="kind" attr.type="string" for="edge" id="d2" />
-  <key attr.name="length" attr.type="double" for="edge" id="d1" />
-  <key attr.name="grid" attr.type="string" for="edge" id="d0" />
+  <key id="d9" for="edge" attr.name="length" attr.type="int" />
+  <key id="d8" for="edge" attr.name="C_per_km" attr.type="double" />
+  <key id="d7" for="edge" attr.name="L_per_km" attr.type="double" />
+  <key id="d6" for="edge" attr.name="R_per_km" attr.type="double" />
+  <key id="d5" for="edge" attr.name="I_max_th" attr.type="int" />
+  <key id="d4" for="edge" attr.name="U_n" attr.type="int" />
+  <key id="d3" for="edge" attr.name="name" attr.type="string" />
+  <key id="d2" for="edge" attr.name="kind" attr.type="string" />
+  <key id="d1" for="edge" attr.name="length" attr.type="double" />
+  <key id="d0" for="edge" attr.name="grid" attr.type="string" />
   <graph edgedefault="undirected">
     <node id="MVStation_mvgd_0" />
     <node id="Generator_mvgd_0_biogas_from_grid_0" />
@@ -47,8 +47,8 @@
     <node id="MVCableDist_mvgd_0_4" />
     <node id="MVCableDist_mvgd_0_5" />
     <edge source="MVStation_mvgd_0" target="MVCableDist_mvgd_0_1">
-      <data key="d0">'mv_grid_0'</data>
-      <data key="d1">13778.969533166168</data>
+      <data key="d0">mv_grid_0</data>
+      <data key="d1">13778.978047867522</data>
       <data key="d2">line</data>
       <data key="d3">48-AL1/8-ST1A</data>
       <data key="d4">20</data>
@@ -58,8 +58,8 @@
       <data key="d8">0.0098</data>
     </edge>
     <edge source="MVStation_mvgd_0" target="MVCableDist_mvgd_0_2">
-      <data key="d0">'mv_grid_0'</data>
-      <data key="d1">38338.98762038968</data>
+      <data key="d0">mv_grid_0</data>
+      <data key="d1">38339.02005888106</data>
       <data key="d2">line</data>
       <data key="d3">48-AL1/8-ST1A</data>
       <data key="d4">20</data>
@@ -69,8 +69,8 @@
       <data key="d8">0.0098</data>
     </edge>
     <edge source="MVStation_mvgd_0" target="MVCableDist_mvgd_0_3">
-      <data key="d0">'mv_grid_0'</data>
-      <data key="d1">44574.621997364935</data>
+      <data key="d0">mv_grid_0</data>
+      <data key="d1">44574.65321549841</data>
       <data key="d2">line</data>
       <data key="d3">48-AL1/8-ST1A</data>
       <data key="d4">20</data>
@@ -80,8 +80,8 @@
       <data key="d8">0.0098</data>
     </edge>
     <edge source="MVStation_mvgd_0" target="LVStation_mvgd_0_lvgd_1000">
-      <data key="d0">'mv_grid_0'</data>
-      <data key="d1">5513.892738953686</data>
+      <data key="d0">mv_grid_0</data>
+      <data key="d1">5513.896144897531</data>
       <data key="d2">line</data>
       <data key="d3">48-AL1/8-ST1A</data>
       <data key="d4">20</data>
@@ -91,8 +91,8 @@
       <data key="d8">0.0098</data>
     </edge>
     <edge source="MVStation_mvgd_0" target="LVStation_mvgd_0_lvgd_1001">
-      <data key="d0">'mv_grid_0'</data>
-      <data key="d1">16077.998189026202</data>
+      <data key="d0">mv_grid_0</data>
+      <data key="d1">16078.003272121707</data>
       <data key="d2">line</data>
       <data key="d3">48-AL1/8-ST1A</data>
       <data key="d4">20</data>
@@ -102,8 +102,8 @@
       <data key="d8">0.0098</data>
     </edge>
     <edge source="MVStation_mvgd_0" target="LVStation_mvgd_0_lvgd_1013">
-      <data key="d0">'mv_grid_0'</data>
-      <data key="d1">21538.98886916947</data>
+      <data key="d0">mv_grid_0</data>
+      <data key="d1">21538.994758563163</data>
       <data key="d2">line</data>
       <data key="d3">48-AL1/8-ST1A</data>
       <data key="d4">20</data>
@@ -113,8 +113,8 @@
       <data key="d8">0.0098</data>
     </edge>
     <edge source="MVStation_mvgd_0" target="LVStation_mvgd_0_lvgd_1016">
-      <data key="d0">'mv_grid_0'</data>
-      <data key="d1">13778.969533166168</data>
+      <data key="d0">mv_grid_0</data>
+      <data key="d1">13778.978047867522</data>
       <data key="d2">line</data>
       <data key="d3">48-AL1/8-ST1A</data>
       <data key="d4">20</data>
@@ -124,8 +124,8 @@
       <data key="d8">0.0098</data>
     </edge>
     <edge source="MVStation_mvgd_0" target="LVStation_mvgd_0_lvgd_1017">
-      <data key="d0">'mv_grid_0'</data>
-      <data key="d1">38338.98762038968</data>
+      <data key="d0">mv_grid_0</data>
+      <data key="d1">38339.02005888106</data>
       <data key="d2">line</data>
       <data key="d3">48-AL1/8-ST1A</data>
       <data key="d4">20</data>
@@ -135,8 +135,8 @@
       <data key="d8">0.0098</data>
     </edge>
     <edge source="MVStation_mvgd_0" target="LVStation_mvgd_0_lvgd_1018">
-      <data key="d0">'mv_grid_0'</data>
-      <data key="d1">44574.621997364935</data>
+      <data key="d0">mv_grid_0</data>
+      <data key="d1">44574.65321549841</data>
       <data key="d2">line</data>
       <data key="d3">48-AL1/8-ST1A</data>
       <data key="d4">20</data>
@@ -146,8 +146,8 @@
       <data key="d8">0.0098</data>
     </edge>
     <edge source="MVStation_mvgd_0" target="LVStation_mvgd_0_lvgd_1004">
-      <data key="d0">'mv_grid_0'</data>
-      <data key="d1">5516.965603800298</data>
+      <data key="d0">mv_grid_0</data>
+      <data key="d1">5516.969009803307</data>
       <data key="d2">line</data>
       <data key="d3">48-AL1/8-ST1A</data>
       <data key="d4">20</data>
@@ -157,8 +157,8 @@
       <data key="d8">0.0098</data>
     </edge>
     <edge source="MVStation_mvgd_0" target="LVStation_mvgd_0_lvgd_1005">
-      <data key="d0">'mv_grid_0'</data>
-      <data key="d1">16082.214222420695</data>
+      <data key="d0">mv_grid_0</data>
+      <data key="d1">16082.219207948681</data>
       <data key="d2">line</data>
       <data key="d3">48-AL1/8-ST1A</data>
       <data key="d4">20</data>
@@ -168,8 +168,8 @@
       <data key="d8">0.0098</data>
     </edge>
     <edge source="MVStation_mvgd_0" target="LVStation_mvgd_0_lvgd_1007">
-      <data key="d0">'mv_grid_0'</data>
-      <data key="d1">16088.521763265371</data>
+      <data key="d0">mv_grid_0</data>
+      <data key="d1">16088.536598926754</data>
       <data key="d2">line</data>
       <data key="d3">48-AL1/8-ST1A</data>
       <data key="d4">20</data>
@@ -179,8 +179,8 @@
       <data key="d8">0.0098</data>
     </edge>
     <edge source="Generator_mvgd_0_biogas_from_grid_0" target="LVStation_mvgd_0_lvgd_1000">
-      <data key="d0">'mv_grid_0'</data>
-      <data key="d1">5513.892738953839</data>
+      <data key="d0">mv_grid_0</data>
+      <data key="d1">5513.896144897531</data>
       <data key="d2">line</data>
       <data key="d3">48-AL1/8-ST1A</data>
       <data key="d4">20</data>
@@ -190,7 +190,7 @@
       <data key="d8">0.0098</data>
     </edge>
     <edge source="Generator_mvgd_0_biogas_from_grid_0" target="Generator_mvgd_0_biogas_from_grid_1">
-      <data key="d0">'mv_grid_0'</data>
+      <data key="d0">mv_grid_0</data>
       <data key="d9">1</data>
       <data key="d2">line</data>
       <data key="d3">48-AL1/8-ST1A</data>
@@ -201,7 +201,7 @@
       <data key="d8">0.0098</data>
     </edge>
     <edge source="Generator_mvgd_0_biogas_from_grid_0" target="Generator_mvgd_0_wind_onshore_2">
-      <data key="d0">'mv_grid_0'</data>
+      <data key="d0">mv_grid_0</data>
       <data key="d9">1</data>
       <data key="d2">line</data>
       <data key="d3">48-AL1/8-ST1A</data>
@@ -212,7 +212,7 @@
       <data key="d8">0.0098</data>
     </edge>
     <edge source="Generator_mvgd_0_biogas_from_grid_0" target="Generator_mvgd_0_wind_onshore_3">
-      <data key="d0">'mv_grid_0'</data>
+      <data key="d0">mv_grid_0</data>
       <data key="d9">1</data>
       <data key="d2">line</data>
       <data key="d3">48-AL1/8-ST1A</data>
@@ -223,8 +223,8 @@
       <data key="d8">0.0098</data>
     </edge>
     <edge source="LVStation_mvgd_0_lvgd_1000" target="LVStation_mvgd_0_lvgd_1003">
-      <data key="d0">'mv_grid_0'</data>
-      <data key="d1">11693.472599764602</data>
+      <data key="d0">mv_grid_0</data>
+      <data key="d1">11693.483853825253</data>
       <data key="d2">line</data>
       <data key="d3">48-AL1/8-ST1A</data>
       <data key="d4">20</data>
@@ -234,8 +234,8 @@
       <data key="d8">0.0098</data>
     </edge>
     <edge source="LVStation_mvgd_0_lvgd_1001" target="LVStation_mvgd_0_lvgd_1002">
-      <data key="d0">'mv_grid_0'</data>
-      <data key="d1">11673.90868108486</data>
+      <data key="d0">mv_grid_0</data>
+      <data key="d1">11673.919910575649</data>
       <data key="d2">line</data>
       <data key="d3">48-AL1/8-ST1A</data>
       <data key="d4">20</data>
@@ -245,8 +245,8 @@
       <data key="d8">0.0098</data>
     </edge>
     <edge source="LVStation_mvgd_0_lvgd_1002" target="LVStation_mvgd_0_lvgd_1003">
-      <data key="d0">'mv_grid_0'</data>
-      <data key="d1">11699.996757584164</data>
+      <data key="d0">mv_grid_0</data>
+      <data key="d1">11699.99999654535</data>
       <data key="d2">line</data>
       <data key="d3">48-AL1/8-ST1A</data>
       <data key="d4">20</data>
@@ -256,8 +256,8 @@
       <data key="d8">0.0098</data>
     </edge>
     <edge source="LVStation_mvgd_0_lvgd_1004" target="LVStation_mvgd_0_lvgd_1008">
-      <data key="d0">'mv_grid_0'</data>
-      <data key="d1">18508.06998862247</data>
+      <data key="d0">mv_grid_0</data>
+      <data key="d1">18508.076227984075</data>
       <data key="d2">line</data>
       <data key="d3">48-AL1/8-ST1A</data>
       <data key="d4">20</data>
@@ -267,8 +267,8 @@
       <data key="d8">0.0098</data>
     </edge>
     <edge source="LVStation_mvgd_0_lvgd_1005" target="LVStation_mvgd_0_lvgd_1006">
-      <data key="d0">'mv_grid_0'</data>
-      <data key="d1">11726.027430271868</data>
+      <data key="d0">mv_grid_0</data>
+      <data key="d1">11726.038724984652</data>
       <data key="d2">line</data>
       <data key="d3">48-AL1/8-ST1A</data>
       <data key="d4">20</data>
@@ -278,8 +278,8 @@
       <data key="d8">0.0098</data>
     </edge>
     <edge source="LVStation_mvgd_0_lvgd_1006" target="LVStation_mvgd_0_lvgd_1007">
-      <data key="d0">'mv_grid_0'</data>
-      <data key="d1">11699.996859580739</data>
+      <data key="d0">mv_grid_0</data>
+      <data key="d1">11699.999996564226</data>
       <data key="d2">line</data>
       <data key="d3">48-AL1/8-ST1A</data>
       <data key="d4">20</data>
@@ -289,8 +289,8 @@
       <data key="d8">0.0098</data>
     </edge>
     <edge source="LVStation_mvgd_0_lvgd_1008" target="MVCableDist_mvgd_0_4">
-      <data key="d0">'mv_grid_0'</data>
-      <data key="d1">2727.1754010604313</data>
+      <data key="d0">mv_grid_0</data>
+      <data key="d1">2727.17127371167</data>
       <data key="d2">line</data>
       <data key="d3">48-AL1/8-ST1A</data>
       <data key="d4">20</data>
@@ -300,8 +300,8 @@
       <data key="d8">0.0098</data>
     </edge>
     <edge source="LVStation_mvgd_0_lvgd_1009" target="LVStation_mvgd_0_lvgd_1010">
-      <data key="d0">'mv_grid_0'</data>
-      <data key="d1">5530.769630927678</data>
+      <data key="d0">mv_grid_0</data>
+      <data key="d1">5530.773027601033</data>
       <data key="d2">line</data>
       <data key="d3">48-AL1/8-ST1A</data>
       <data key="d4">20</data>
@@ -311,8 +311,8 @@
       <data key="d8">0.0098</data>
     </edge>
     <edge source="LVStation_mvgd_0_lvgd_1009" target="MVCableDist_mvgd_0_4">
-      <data key="d0">'mv_grid_0'</data>
-      <data key="d1">2764.71451061956</data>
+      <data key="d0">mv_grid_0</data>
+      <data key="d1">2764.7162362310705</data>
       <data key="d2">line</data>
       <data key="d3">48-AL1/8-ST1A</data>
       <data key="d4">20</data>
@@ -322,8 +322,8 @@
       <data key="d8">0.0098</data>
     </edge>
     <edge source="LVStation_mvgd_0_lvgd_1011" target="LVStation_mvgd_0_lvgd_1015">
-      <data key="d0">'mv_grid_0'</data>
-      <data key="d1">3899.998984507848</data>
+      <data key="d0">mv_grid_0</data>
+      <data key="d1">3899.9999997158393</data>
       <data key="d2">line</data>
       <data key="d3">48-AL1/8-ST1A</data>
       <data key="d4">20</data>
@@ -333,8 +333,8 @@
       <data key="d8">0.0098</data>
     </edge>
     <edge source="LVStation_mvgd_0_lvgd_1012" target="MVCableDist_mvgd_0_5">
-      <data key="d0">'mv_grid_0'</data>
-      <data key="d1">2762.307541243976</data>
+      <data key="d0">mv_grid_0</data>
+      <data key="d1">2762.3092302135738</data>
       <data key="d2">line</data>
       <data key="d3">48-AL1/8-ST1A</data>
       <data key="d4">20</data>
@@ -344,8 +344,8 @@
       <data key="d8">0.0098</data>
     </edge>
     <edge source="LVStation_mvgd_0_lvgd_1013" target="MVCableDist_mvgd_0_5">
-      <data key="d0">'mv_grid_0'</data>
-      <data key="d1">2752.3914325650644</data>
+      <data key="d0">mv_grid_0</data>
+      <data key="d1">2752.3911923495984</data>
       <data key="d2">line</data>
       <data key="d3">48-AL1/8-ST1A</data>
       <data key="d4">20</data>
@@ -355,8 +355,8 @@
       <data key="d8">0.0098</data>
     </edge>
     <edge source="LVStation_mvgd_0_lvgd_1014" target="LVStation_mvgd_0_lvgd_1015">
-      <data key="d0">'mv_grid_0'</data>
-      <data key="d1">5524.641050120628</data>
+      <data key="d0">mv_grid_0</data>
+      <data key="d1">5524.644437298107</data>
       <data key="d2">line</data>
       <data key="d3">48-AL1/8-ST1A</data>
       <data key="d4">20</data>
@@ -366,8 +366,8 @@
       <data key="d8">0.0098</data>
     </edge>
     <edge source="LVStation_mvgd_0_lvgd_1015" target="MVCableDist_mvgd_0_4">
-      <data key="d0">'mv_grid_0'</data>
-      <data key="d1">2794.3971950635623</data>
+      <data key="d0">mv_grid_0</data>
+      <data key="d1">2794.40470952057</data>
       <data key="d2">line</data>
       <data key="d3">48-AL1/8-ST1A</data>
       <data key="d4">20</data>
@@ -377,8 +377,8 @@
       <data key="d8">0.0098</data>
     </edge>
     <edge source="LVStation_mvgd_0_lvgd_1015" target="MVCableDist_mvgd_0_5">
-      <data key="d0">'mv_grid_0'</data>
-      <data key="d1">2773.78084502531</data>
+      <data key="d0">mv_grid_0</data>
+      <data key="d1">2773.784479525642</data>
       <data key="d2">line</data>
       <data key="d3">48-AL1/8-ST1A</data>
       <data key="d4">20</data>
@@ -388,7 +388,7 @@
       <data key="d8">0.0098</data>
     </edge>
     <edge source="LVStation_mvgd_0_lvgd_1016" target="MVCableDist_mvgd_0_1">
-      <data key="d0">'mv_grid_0'</data>
+      <data key="d0">mv_grid_0</data>
       <data key="d9">1</data>
       <data key="d2">line</data>
       <data key="d3">48-AL1/8-ST1A</data>
@@ -399,7 +399,7 @@
       <data key="d8">0.0098</data>
     </edge>
     <edge source="LVStation_mvgd_0_lvgd_1017" target="MVCableDist_mvgd_0_2">
-      <data key="d0">'mv_grid_0'</data>
+      <data key="d0">mv_grid_0</data>
       <data key="d9">1</data>
       <data key="d2">line</data>
       <data key="d3">48-AL1/8-ST1A</data>
@@ -410,7 +410,7 @@
       <data key="d8">0.0098</data>
     </edge>
     <edge source="LVStation_mvgd_0_lvgd_1018" target="MVCableDist_mvgd_0_3">
-      <data key="d0">'mv_grid_0'</data>
+      <data key="d0">mv_grid_0</data>
       <data key="d9">1</data>
       <data key="d2">line</data>
       <data key="d3">48-AL1/8-ST1A</data>
diff --git a/tests/tools/test_results.py b/tests/tools/test_results.py
index 861bb5ca..b709eb65 100644
--- a/tests/tools/test_results.py
+++ b/tests/tools/test_results.py
@@ -2,7 +2,7 @@
 from tests.core.network.test_grids import TestMVGridDing0
 from ding0.tools.results import calculate_mvgd_stats
 import pandas as pd
-from pandas.util.testing import assert_frame_equal
+from pandas.testing import assert_frame_equal
 import os