예제 #1
0
def disconnect_node(node, target_obj_result, graph, debug):
    """ Disconnects `node` from `target_obj`

    Parameters
    ----------
    node: LVLoadAreaCentreDing0, i.e.
        Origin node - Ding0 graph object (e.g. LVLoadAreaCentreDing0)
    target_obj_result: LVLoadAreaCentreDing0, i.e.
        Origin node - Ding0 graph object (e.g. LVLoadAreaCentreDing0)
    graph: :networkx:`NetworkX Graph Obj< >`
        NetworkX graph object with nodes and newly created branches
    debug: bool
        If True, information is printed during process

    """

    # backup kind and type of branch
    branch_kind = graph.adj[node][target_obj_result]['branch'].kind
    branch_type = graph.adj[node][target_obj_result]['branch'].type
    branch_ring = graph.adj[node][target_obj_result]['branch'].ring

    graph.remove_edge(node, target_obj_result)

    if isinstance(target_obj_result, MVCableDistributorDing0):

        neighbor_nodes = list(graph.neighbors(target_obj_result))

        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])
            graph.add_edge(neighbor_nodes[0],
                           neighbor_nodes[1],
                           branch=BranchDing0(length=branch_length,
                                              kind=branch_kind,
                                              type=branch_type,
                                              ring=branch_ring))

    if debug:
        logger.debug('disconnect edge {0}-{1}'.format(node, target_obj_result))
예제 #2
0
파일: grids.py 프로젝트: mhdella/ding0
    def set_voltage_level(self, mode='distance'):
        """ Sets voltage level of MV grid according to load density of MV Grid District or max.
        distance between station and Load Area.

        Parameters
        ----------
        mode: :obj:`str`
            method to determine voltage level
            
            * 'load_density': Decision on voltage level is determined by load density
              of the considered region. Urban areas (load density of
              >= 1 MW/km2 according to [#]_) usually got a voltage of
              10 kV whereas rural areas mostly use 20 kV.

            * 'distance' (default): Decision on voltage level is determined by the max.
              distance between Grid District's HV-MV station and Load
              Areas (LA's centre is used). According to [#]_ a value of
              1kV/kV can be assumed. The `voltage_per_km_threshold`
              defines the distance threshold for distinction.
              (default in config = (20km+10km)/2 = 15km)

        References
        ----------
        .. [#] Falk Schaller et al., "Modellierung realitätsnaher zukünftiger Referenznetze im Verteilnetzsektor zur
            Überprüfung der Elektroenergiequalität", Internationaler ETG-Kongress Würzburg, 2011
        .. [#] Klaus Heuck et al., "Elektrische Energieversorgung", Vieweg+Teubner, Wiesbaden, 2007

        """

        if mode == 'load_density':

            # get power factor for loads
            cos_phi_load = cfg_ding0.get('assumptions', 'cos_phi_load')

            # get load density
            load_density_threshold = float(
                cfg_ding0.get('assumptions', 'load_density_threshold'))

            # 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

            # calculate load density
            kw2mw = 1e-3
            sqm2sqkm = 1e6
            load_density = (
                (self.grid_district.peak_load * kw2mw / cos_phi_load) /
                (transform(projection, self.grid_district.geo_data).area /
                 sqm2sqkm))  # unit MVA/km^2

            # identify voltage level
            if load_density < load_density_threshold:
                self.v_level = 20
            elif load_density >= load_density_threshold:
                self.v_level = 10
            else:
                raise ValueError('load_density is invalid!')

        elif mode == 'distance':

            # get threshold for 20/10kV disambiguation
            voltage_per_km_threshold = float(
                cfg_ding0.get('assumptions', 'voltage_per_km_threshold'))

            # initial distance
            dist_max = 0
            import time
            start = time.time()
            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
                    if dist_node > dist_max:
                        dist_max = dist_node

            # max. occurring distance to a Load Area exceeds threshold => grid operates at 20kV
            if dist_max >= voltage_per_km_threshold:
                self.v_level = 20
            # not: grid operates at 10kV
            else:
                self.v_level = 10

        else:
            raise ValueError('parameter \'mode\' is invalid!')
예제 #3
0
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
예제 #4
0
def mv_connect_generators(mv_grid_district, graph, debug=False):
    """Connect MV generators to MV grid

    Parameters
    ----------
    mv_grid_district: MVGridDistrictDing0
        MVGridDistrictDing0 object for which the connection process has to be
        done
    graph: :networkx:`NetworkX Graph Obj< >`
        NetworkX graph object with nodes
    debug: bool, defaults to False
        If True, information is printed during process.

    Returns
    -------
    :networkx:`NetworkX Graph Obj< >`
        NetworkX graph object with nodes and newly created branches
    """

    generator_buffer_radius = cfg_ding0.get('mv_connect',
                                            'generator_buffer_radius')
    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

    # 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

    for generator in sorted(mv_grid_district.mv_grid.generators(),
                            key=lambda x: repr(x)):

        # ===== voltage level 4: generator has to be connected to MV station =====
        if generator.v_level == 4:
            mv_station = mv_grid_district.mv_grid.station()

            branch_length = calc_geo_dist_vincenty(generator, mv_station)

            # TODO: set branch type to something reasonable (to be calculated)
            branch_kind = mv_grid_district.mv_grid.default_branch_kind
            branch_type = mv_grid_district.mv_grid.default_branch_type

            branch = BranchDing0(length=branch_length,
                                 kind=branch_kind,
                                 type=branch_type,
                                 ring=None)
            graph.add_edge(generator, mv_station, branch=branch)

            if debug:
                logger.debug('Generator {0} was connected to {1}'.format(
                    generator, mv_station))

        # ===== voltage level 5: generator has to be connected to MV grid (next-neighbor) =====
        elif generator.v_level == 5:
            generator_shp = transform(proj1, generator.geo_data)

            # get branches within a the predefined radius `generator_buffer_radius`
            branches = calc_geo_branches_in_buffer(
                generator, mv_grid_district.mv_grid, generator_buffer_radius,
                generator_buffer_radius_inc, proj1)

            # calc distance between generator and grid's lines -> find nearest line
            conn_objects_min_stack = find_nearest_conn_objects(
                generator_shp,
                branches,
                proj1,
                conn_dist_weight=1,
                debug=debug,
                branches_only=False)

            # connect!
            # go through the stack (from nearest to most far connection target object)
            generator_connected = False
            for dist_min_obj in conn_objects_min_stack:
                # Note 1: conn_dist_ring_mod=0 to avoid re-routing of existent lines
                # Note 2: In connect_node(), the default cable/line type of grid is used. This is reasonable since
                #         the max. allowed power of the smallest possible cable/line type (3.64 MVA for overhead
                #         line of type 48-AL1/8-ST1A) exceeds the max. allowed power of a generator (4.5 MVA (dena))
                #         (if connected separately!)
                target_obj_result = connect_node(generator,
                                                 generator_shp,
                                                 mv_grid_district.mv_grid,
                                                 dist_min_obj,
                                                 proj2,
                                                 graph,
                                                 conn_dist_ring_mod=0,
                                                 debug=debug)

                if target_obj_result is not None:
                    if debug:
                        logger.debug(
                            'Generator {0} was connected to {1}'.format(
                                generator, target_obj_result))
                    generator_connected = True
                    break

            if not generator_connected and debug:
                logger.debug(
                    'Generator {0} could not be connected, try to '
                    'increase the parameter `generator_buffer_radius` in '
                    'config file `config_calc.cfg` to gain more possible '
                    'connection points.'.format(generator))

    return graph
예제 #5
0
def mv_connect_stations(mv_grid_district, graph, debug=False):
    """ Connect LV stations to MV grid

    Parameters
    ----------
    mv_grid_district: MVGridDistrictDing0
        MVGridDistrictDing0 object for which the connection process has to be done
    graph: :networkx:`NetworkX Graph Obj< >`
        NetworkX graph object with nodes
    debug: bool, defaults to False
        If True, information is printed during process

    Returns
    -------
    :networkx:`NetworkX Graph Obj< >`
        NetworkX graph object with nodes and newly created branches
    """

    # 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

    # 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

    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')

    for lv_load_area in mv_grid_district.lv_load_areas():

        # exclude aggregated Load Areas and choose only load areas that were connected to grid before
        if not lv_load_area.is_aggregated and \
           lv_load_area.lv_load_area_centre not in mv_grid_district.mv_grid.graph_isolated_nodes():

            lv_load_area_centre = lv_load_area.lv_load_area_centre

            # there's only one station: Replace Load Area centre by station in graph
            if lv_load_area.lv_grid_districts_count() == 1:
                # get station
                lv_station = list(
                    lv_load_area.lv_grid_districts())[0].lv_grid.station()

                # get branches that are connected to Load Area centre
                branches = mv_grid_district.mv_grid.graph_branches_from_node(
                    lv_load_area_centre)

                # connect LV station, delete Load Area centre
                for node, branch in branches:
                    # backup kind and type of branch
                    branch_kind = branch['branch'].kind
                    branch_type = branch['branch'].type
                    branch_ring = branch['branch'].ring

                    # respect circuit breaker if existent
                    circ_breaker = branch['branch'].circuit_breaker
                    if circ_breaker is not None:
                        branch[
                            'branch'].circuit_breaker.geo_data = calc_geo_centre_point(
                                lv_station, node)

                    # 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 = BranchDing0(length=branch_length,
                                         circuit_breaker=circ_breaker,
                                         kind=branch_kind,
                                         type=branch_type,
                                         ring=branch_ring)
                    if circ_breaker is not None:
                        circ_breaker.branch = branch
                    graph.add_edge(lv_station, node, branch=branch)

                # delete Load Area centre from graph
                graph.remove_node(lv_load_area_centre)

            # there're more than one station: Do normal connection process (as in satellites)
            else:
                # connect LV stations of all grid districts
                # =========================================
                for lv_grid_district in lv_load_area.lv_grid_districts():
                    # get branches that are partly or fully located in load area
                    branches = calc_geo_branches_in_polygon(
                        mv_grid_district.mv_grid,
                        lv_load_area.geo_area,
                        mode='intersects',
                        proj=proj1)

                    # filter branches that belong to satellites (load area groups) if Load Area is not a satellite
                    # itself
                    if not lv_load_area.is_satellite:
                        branches_valid = []
                        for branch in branches:
                            node1 = branch['adj_nodes'][0]
                            node2 = branch['adj_nodes'][1]
                            lv_load_area_group = get_lv_load_area_group_from_node_pair(
                                node1, node2)

                            # delete branch as possible conn. target if it belongs to a group (=satellite) or
                            # if it belongs to a ring different from the ring of the current LVLA
                            if (lv_load_area_group is None) and\
                               (branch['branch'].ring is lv_load_area.ring):
                                branches_valid.append(branch)
                        branches = branches_valid

                    # find possible connection objects
                    lv_station = lv_grid_district.lv_grid.station()
                    lv_station_shp = transform(proj1, lv_station.geo_data)
                    conn_objects_min_stack = find_nearest_conn_objects(
                        lv_station_shp,
                        branches,
                        proj1,
                        conn_dist_weight,
                        debug,
                        branches_only=False)

                    # connect!
                    connect_node(lv_station, lv_station_shp,
                                 mv_grid_district.mv_grid,
                                 conn_objects_min_stack[0], proj2, graph,
                                 conn_dist_ring_mod, debug)

                # Replace Load Area centre by cable distributor
                # ================================================
                # create cable distributor and add it to grid
                cable_dist = MVCableDistributorDing0(
                    geo_data=lv_load_area_centre.geo_data,
                    grid=mv_grid_district.mv_grid)
                mv_grid_district.mv_grid.add_cable_distributor(cable_dist)

                # get branches that are connected to Load Area centre
                branches = mv_grid_district.mv_grid.graph_branches_from_node(
                    lv_load_area_centre)

                # connect LV station, delete Load Area centre
                for node, branch in branches:
                    # backup kind and type of branch
                    branch_kind = branch['branch'].kind
                    branch_type = branch['branch'].type
                    branch_ring = branch['branch'].ring

                    # respect circuit breaker if existent
                    circ_breaker = branch['branch'].circuit_breaker
                    if circ_breaker is not None:
                        branch[
                            'branch'].circuit_breaker.geo_data = calc_geo_centre_point(
                                cable_dist, node)

                    # 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 = BranchDing0(length=branch_length,
                                         circuit_breaker=circ_breaker,
                                         kind=branch_kind,
                                         type=branch_type,
                                         ring=branch_ring)
                    if circ_breaker is not None:
                        circ_breaker.branch = branch
                    graph.add_edge(cable_dist, node, branch=branch)

                # delete Load Area centre from graph
                graph.remove_node(lv_load_area_centre)

            # Replace all overhead lines by cables
            # ====================================
            # if grid's default type is overhead line
            if mv_grid_district.mv_grid.default_branch_kind == 'line':
                # get all branches in load area
                branches = calc_geo_branches_in_polygon(
                    mv_grid_district.mv_grid,
                    lv_load_area.geo_area,
                    mode='contains',
                    proj=proj1)
                # set type
                for branch in branches:
                    branch[
                        'branch'].kind = mv_grid_district.mv_grid.default_branch_kind_settle
                    branch[
                        'branch'].type = mv_grid_district.mv_grid.default_branch_type_settle

    return graph
예제 #6
0
def connect_node(node, node_shp, mv_grid, target_obj, proj, graph,
                 conn_dist_ring_mod, debug):
    """ Connects `node` to `target_obj`.

    Parameters
    ----------
    node: LVLoadAreaCentreDing0, i.e.
        Origin node - Ding0 graph object (e.g. LVLoadAreaCentreDing0)
    node_shp: :shapely:`Shapely Point object<points>`
        Shapely Point object of origin node
    target_obj: type
        object that node shall be connected to
    proj: :pyproj:`pyproj Proj object< >`
        equidistant CRS to conformal CRS (e.g. ETRS -> WGS84)
    graph: :networkx:`NetworkX Graph Obj< >`
        NetworkX graph object with nodes and newly created branches
    conn_dist_ring_mod: float
        Max. distance when nodes are included into route instead of creating a 
        new line.
    debug: bool
        If True, information is printed during process.

    Returns
    -------
    :obj:`LVLoadAreaCentreDing0`
        object that node was connected to.
        
        (instance of :obj:`LVLoadAreaCentreDing0` or :obj:`MVCableDistributorDing0`.
        
        If node is included into line instead of creating a new line (see arg
        `conn_dist_ring_mod`), `target_obj_result` is None.
                           
    See Also
    --------
    ding0.grid.mv_grid.mv_connect : for details on the `conn_dist_ring_mod` parameter.
    """

    target_obj_result = None

    # MV line is nearest connection point
    if isinstance(target_obj['shp'], LineString):

        adj_node1 = target_obj['obj']['adj_nodes'][0]
        adj_node2 = target_obj['obj']['adj_nodes'][1]

        # find nearest point on MV line
        conn_point_shp = target_obj['shp'].interpolate(
            target_obj['shp'].project(node_shp))
        conn_point_shp = transform(proj, conn_point_shp)

        # target MV line does currently not connect a load area of type aggregated
        if not target_obj['obj']['branch'].connects_aggregated:

            # Node is close to line
            # -> insert node into route (change existing route)
            if (target_obj['dist'] < conn_dist_ring_mod):
                # backup kind and type of branch
                branch_type = graph.adj[adj_node1][adj_node2]['branch'].type
                branch_kind = graph.adj[adj_node1][adj_node2]['branch'].kind
                branch_ring = graph.adj[adj_node1][adj_node2]['branch'].ring

                # check if there's a circuit breaker on current branch,
                # if yes set new position between first node (adj_node1) and newly inserted node
                circ_breaker = graph.adj[adj_node1][adj_node2][
                    'branch'].circuit_breaker
                if circ_breaker is not None:
                    circ_breaker.geo_data = calc_geo_centre_point(
                        adj_node1, node)

                # split old ring main route into 2 segments (delete old branch and create 2 new ones
                # along node)
                graph.remove_edge(adj_node1, adj_node2)

                branch_length = calc_geo_dist_vincenty(adj_node1, node)
                branch = BranchDing0(length=branch_length,
                                     circuit_breaker=circ_breaker,
                                     kind=branch_kind,
                                     type=branch_type,
                                     ring=branch_ring)
                if circ_breaker is not None:
                    circ_breaker.branch = branch
                graph.add_edge(adj_node1, node, branch=branch)

                branch_length = calc_geo_dist_vincenty(adj_node2, node)
                graph.add_edge(adj_node2,
                               node,
                               branch=BranchDing0(length=branch_length,
                                                  kind=branch_kind,
                                                  type=branch_type,
                                                  ring=branch_ring))

                target_obj_result = 're-routed'

                if debug:
                    logger.debug('Ring main route modified to include '
                                 'node {}'.format(node))

            # Node is too far away from route
            # => keep main route and create new line from node to (cable distributor on) route.
            else:

                # create cable distributor and add it to grid
                cable_dist = MVCableDistributorDing0(geo_data=conn_point_shp,
                                                     grid=mv_grid)
                mv_grid.add_cable_distributor(cable_dist)

                # check if there's a circuit breaker on current branch,
                # if yes set new position between first node (adj_node1) and newly created cable distributor
                circ_breaker = graph.adj[adj_node1][adj_node2][
                    'branch'].circuit_breaker
                if circ_breaker is not None:
                    circ_breaker.geo_data = calc_geo_centre_point(
                        adj_node1, cable_dist)

                # split old branch into 2 segments (delete old branch and create 2 new ones along cable_dist)
                # ===========================================================================================

                # backup kind and type of branch
                branch_kind = graph.adj[adj_node1][adj_node2]['branch'].kind
                branch_type = graph.adj[adj_node1][adj_node2]['branch'].type
                branch_ring = graph.adj[adj_node1][adj_node2]['branch'].ring

                graph.remove_edge(adj_node1, adj_node2)

                branch_length = calc_geo_dist_vincenty(adj_node1, cable_dist)
                branch = BranchDing0(length=branch_length,
                                     circuit_breaker=circ_breaker,
                                     kind=branch_kind,
                                     type=branch_type,
                                     ring=branch_ring)
                if circ_breaker is not None:
                    circ_breaker.branch = branch
                graph.add_edge(adj_node1, cable_dist, branch=branch)

                branch_length = calc_geo_dist_vincenty(adj_node2, cable_dist)
                graph.add_edge(adj_node2,
                               cable_dist,
                               branch=BranchDing0(length=branch_length,
                                                  kind=branch_kind,
                                                  type=branch_type,
                                                  ring=branch_ring))

                # add new branch for satellite (station to cable distributor)
                # ===========================================================

                # get default branch kind and type from grid to use it for new branch
                branch_kind = mv_grid.default_branch_kind
                branch_type = mv_grid.default_branch_type

                branch_length = calc_geo_dist_vincenty(node, cable_dist)
                graph.add_edge(node,
                               cable_dist,
                               branch=BranchDing0(length=branch_length,
                                                  kind=branch_kind,
                                                  type=branch_type,
                                                  ring=branch_ring))
                target_obj_result = cable_dist

                # debug info
                if debug:
                    logger.debug('Nearest connection point for object {0} '
                                 'is branch {1} (distance={2} m)'.format(
                                     node, target_obj['obj']['adj_nodes'],
                                     target_obj['dist']))

    # node ist nearest connection point
    else:

        # what kind of node is to be connected? (which type is node of?)
        #   LVLoadAreaCentreDing0: Connect to LVLoadAreaCentreDing0 only
        #   LVStationDing0: Connect to LVLoadAreaCentreDing0, LVStationDing0 or MVCableDistributorDing0
        #   GeneratorDing0: Connect to LVLoadAreaCentreDing0, LVStationDing0, MVCableDistributorDing0 or GeneratorDing0
        if isinstance(node, LVLoadAreaCentreDing0):
            valid_conn_objects = LVLoadAreaCentreDing0
        elif isinstance(node, LVStationDing0):
            valid_conn_objects = (LVLoadAreaCentreDing0, LVStationDing0,
                                  MVCableDistributorDing0)
        elif isinstance(node, GeneratorDing0):
            valid_conn_objects = (LVLoadAreaCentreDing0, LVStationDing0,
                                  MVCableDistributorDing0, GeneratorDing0)
        else:
            raise ValueError(
                'Oops, the node you are trying to connect is not a valid connection object'
            )

        # if target is Load Area centre or LV station, check if it belongs to a load area of type aggregated
        # (=> connection not allowed)
        if isinstance(target_obj['obj'],
                      (LVLoadAreaCentreDing0, LVStationDing0)):
            target_is_aggregated = target_obj['obj'].lv_load_area.is_aggregated
        else:
            target_is_aggregated = False

        # target node is not a load area of type aggregated
        if isinstance(target_obj['obj'],
                      valid_conn_objects) and not target_is_aggregated:

            # get default branch kind and type from grid to use it for new branch
            branch_kind = mv_grid.default_branch_kind
            branch_type = mv_grid.default_branch_type

            # get branch ring obj
            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'])
            graph.add_edge(node,
                           target_obj['obj'],
                           branch=BranchDing0(length=branch_length,
                                              kind=branch_kind,
                                              type=branch_type,
                                              ring=branch_ring))
            target_obj_result = target_obj['obj']

            # debug info
            if debug:
                logger.debug(
                    'Nearest connection point for object {0} is station {1} '
                    '(distance={2} m)'.format(node, target_obj['obj'],
                                              target_obj['dist']))

    return target_obj_result
예제 #7
0
def lv_connect_generators(lv_grid_district, graph, debug=False):
    """ Connect LV generators to LV grid
    
    Args
    ----
    lv_grid_district: LVGridDistrictDing0
        LVGridDistrictDing0 object for which the connection process has to be done
    graph: :networkx:`NetworkX Graph Obj< >`
        NetworkX graph object with nodes
    debug: bool, defaults to False
        If True, information is printed during process

    Returns
    -------
    :networkx:`NetworkX Graph Obj< >`
        NetworkX graph object with nodes and newly created branches
    """

    cable_lf = cfg_ding0.get('assumptions', 'load_factor_lv_cable_fc_normal')
    cos_phi_gen = cfg_ding0.get('assumptions', 'cos_phi_gen')
    v_nom = cfg_ding0.get('assumptions',
                          'lv_nominal_voltage') / 1e3  # v_nom in kV
    seed = int(cfg_ding0.get('random', 'seed'))
    random.seed(a=seed)

    # generate random list (without replacement => unique elements)
    # of loads (residential) to connect genos (P <= 30kW) to.
    lv_loads_res = sorted(lv_grid_district.lv_grid.loads_sector(sector='res'),
                          key=lambda _: repr(_))
    if len(lv_loads_res) > 0:
        lv_loads_res_rnd = (random.sample(lv_loads_res, len(lv_loads_res)))
    else:
        lv_loads_res_rnd = None

    # generate random list (without replacement => unique elements)
    # of loads (retail, industrial, agricultural) to connect genos
    # (30kW <= P <= 100kW) to.
    lv_loads_ria = sorted(lv_grid_district.lv_grid.loads_sector(sector='ria'),
                          key=lambda _: repr(_))
    if len(lv_loads_ria) > 0:
        lv_loads_ria_rnd = (random.sample(lv_loads_ria, len(lv_loads_ria)))
    else:
        lv_loads_ria_rnd = None

    for generator in sorted(lv_grid_district.lv_grid.generators(),
                            key=lambda x: repr(x)):

        # generator is of v_level 6 -> connect to LV station
        if generator.v_level == 6:
            lv_station = lv_grid_district.lv_grid.station()

            branch_length = calc_geo_dist_vincenty(generator, lv_station)
            branch_type = cable_type(
                generator.capacity / (cable_lf * cos_phi_gen), v_nom,
                lv_grid_district.lv_grid.network.static_data['LV_cables'])

            branch = BranchDing0(length=branch_length,
                                 kind='cable',
                                 type=branch_type)

            graph.add_edge(generator, lv_station, branch=branch)

        # generator is of v_level 7 -> assign geno to load
        elif generator.v_level == 7:

            # connect genos with P <= 30kW to residential loads, if available
            if (generator.capacity <= 30) and (lv_loads_res_rnd is not None):
                if len(lv_loads_res_rnd) > 0:
                    lv_load = lv_loads_res_rnd.pop()
                # if random load list is empty, create new one
                else:
                    lv_loads_res_rnd = (random.sample(lv_loads_res,
                                                      len(lv_loads_res)))
                    lv_load = lv_loads_res_rnd.pop()

                # get cable distributor of building
                lv_conn_target = list(graph.neighbors(lv_load))[0]

            # connect genos with 30kW <= P <= 100kW to residential loads
            # to retail, industrial, agricultural loads, if available
            elif (generator.capacity > 30) and (lv_loads_ria_rnd is not None):
                if len(lv_loads_ria_rnd) > 0:
                    lv_load = lv_loads_ria_rnd.pop()
                # if random load list is empty, create new one
                else:
                    lv_loads_ria_rnd = (random.sample(lv_loads_ria,
                                                      len(lv_loads_ria)))
                    lv_load = lv_loads_ria_rnd.pop()

                # get cable distributor of building
                lv_conn_target = list(graph.neighbors(lv_load))[0]

            # fallback: connect to station
            else:
                lv_conn_target = lv_grid_district.lv_grid.station()

                logger.warning('No valid conn. target found for {}.'
                               'Connected to {}.'.format(
                                   repr(generator), repr(lv_conn_target)))

            # determine appropriate type of cable
            branch_type = cable_type(
                generator.capacity / (cable_lf * cos_phi_gen), v_nom,
                lv_grid_district.lv_grid.network.static_data['LV_cables'])

            # connect to cable dist. of building
            branch = BranchDing0(length=1, kind='cable', type=branch_type)

            graph.add_edge(generator, lv_conn_target, branch=branch)

    return graph