docs: def routing_solution_to_ding0_graph(graph, solution):
      """ Insert `solution` from routing into `graph`
  
      Args
      ----
      graph: :networkx:`NetworkX Graph Obj< >`
          NetworkX graph object with nodes
      solution: BaseSolution
          Instance of `BaseSolution` or child class (e.g. `LocalSearchSolution`) (=solution from routing)
  
      Returns
      -------
      :networkx:`NetworkX Graph Obj< >` 
          NetworkX graph object with nodes and edges
      """
      # TODO: Bisherige Herangehensweise (diese Funktion): Branches werden nach Routing erstellt um die Funktionsfähigkeit
      # TODO: des Routing-Tools auch für die TestCases zu erhalten. Es wird ggf. notwendig, diese direkt im Routing vorzunehmen.
  
      # build node dict (name: obj) from graph nodes to map node names on node objects
      node_list = {str(n): n for n in graph.nodes()}
  
      # add edges from solution to graph
      try:
          depot = solution._nodes[solution._problem._depot.name()]
          depot_node = node_list[depot.name()]
          for r in solution.routes():
              circ_breaker_pos = None
  
              # if route has only one node and is not aggregated, it wouldn't be possible to add two lines from and to
              # this node (undirected graph of NetworkX). So, as workaround, an additional MV cable distributor is added
              # at nodes' position (resulting route: HV/MV_subst --- node --- cable_dist --- HV/MV_subst.
              if len(r._nodes) == 1:
                  if not solution._problem._is_aggregated[r._nodes[0]._name]:
                      # create new cable dist
                      cable_dist = MVCableDistributorDing0(geo_data=node_list[r._nodes[0]._name].geo_data,
                                                           grid=depot_node.grid)
                      depot_node.grid.add_cable_distributor(cable_dist)
  
                      # create new node (as dummy) an allocate to route r
                      r.allocate([Node(name=repr(cable_dist), demand=0)])
  
                      # add it to node list and allocated-list manually
                      node_list[str(cable_dist)] = cable_dist
                      solution._problem._is_aggregated[str(cable_dist)] = False
  
                      # set circ breaker pos manually
                      circ_breaker_pos = 1
  
              # build edge list
              n1 = r._nodes[0:len(r._nodes)-1]
              n2 = r._nodes[1:len(r._nodes)]
              edges = list(zip(n1, n2))
              edges.append((depot, r._nodes[0]))
              edges.append((r._nodes[-1], depot))
  
              # create MV Branch object for every edge in `edges`
              mv_branches = [BranchDing0() for _ in edges]
              edges_with_branches = list(zip(edges, mv_branches))
  
              # recalculate circuit breaker positions for final solution, create it and set associated branch.
              # if circ. breaker position is not set manually (routes with more than one load area, see above)
              if not circ_breaker_pos:
                  circ_breaker_pos = r.calc_circuit_breaker_position()
  
              node1 = node_list[edges[circ_breaker_pos - 1][0].name()]
              node2 = node_list[edges[circ_breaker_pos - 1][1].name()]
  
              # ALTERNATIVE TO METHOD ABOVE: DO NOT CREATE 2 BRANCHES (NO RING) -> LA IS CONNECTED AS SATELLITE
              # IF THIS IS COMMENTED-IN, THE IF-BLOCK IN LINE 87 HAS TO BE COMMENTED-OUT
              # See issue #114
              # ===============================
              # do not add circuit breaker for routes which are aggregated load areas or
              # routes that contain only one load area
              # if not (node1 == depot_node and solution._problem._is_aggregated[edges[circ_breaker_pos - 1][1].name()] or
              #         node2 == depot_node and solution._problem._is_aggregated[edges[circ_breaker_pos - 1][0].name()] or
              #         len(r._nodes) == 1):
              # ===============================
  
              # do not add circuit breaker for routes which are aggregated load areas
              if not (node1 == depot_node and solution._problem._is_aggregated[edges[circ_breaker_pos - 1][1].name()] or
                      node2 == depot_node and solution._problem._is_aggregated[edges[circ_breaker_pos - 1][0].name()]):
                  branch = mv_branches[circ_breaker_pos - 1]
                  circ_breaker = CircuitBreakerDing0(grid=depot_node.grid, branch=branch,
                                                     geo_data=calc_geo_centre_point(node1, node2))
                  branch.circuit_breaker = circ_breaker
  
              # create new ring object for route
              ring = RingDing0(grid=depot_node.grid)
  
              # translate solution's node names to graph node objects using dict created before
              # note: branch object is assigned to edge using an attribute ('branch' is used here), it can be accessed
              # using the method `graph_edges()` of class `GridDing0`
              edges_graph = []
              for ((n1, n2), b) in edges_with_branches:
                  # get node objects
                  node1 = node_list[n1.name()]
                  node2 = node_list[n2.name()]
  
                  # set branch's ring attribute
                  b.ring = ring
                  # set LVLA's ring attribute
                  if isinstance(node1, LVLoadAreaCentreDing0):
                      node1.lv_load_area.ring = ring
  
                  # set branch length
                  b.length = calc_geo_dist_vincenty(node1, node2)
  
                  # set branch kind and type
                  # 1) default
                  b.kind = depot_node.grid.default_branch_kind
                  b.type = depot_node.grid.default_branch_type
                  # 2) aggregated load area types
                  if node1 == depot_node and solution._problem._is_aggregated[n2.name()]:
                      b.connects_aggregated = True
                      b.kind = depot_node.grid.default_branch_kind_aggregated
                      b.type = depot_node.grid.default_branch_type_aggregated
                  elif node2 == depot_node and solution._problem._is_aggregated[n1.name()]:
                      b.connects_aggregated = True
                      b.kind = depot_node.grid.default_branch_kind_aggregated
                      b.type = depot_node.grid.default_branch_type_aggregated
  
                  # append to branch list
                  edges_graph.append((node1, node2, dict(branch=b)))
  
              # add branches to graph
              graph.add_edges_from(edges_graph)
  
      except:
          logger.exception(
              'unexpected error while converting routing solution to DING0 graph (NetworkX).')
  
      return graph