def test_graph_add_node_branch(self, empty_grid): branch1 = BranchDing0() # make sure that call of add_nodes # does nothing len_nodes_before = len(list(empty_grid._graph.nodes())) empty_grid.graph_add_node(branch1) len_nodes_after = len(list(empty_grid._graph.nodes())) assert len_nodes_before == len_nodes_after
def simple_graph_grid(self): grid = GridDing0(id_db=0) station = StationDing0(id_db=0, geo_data=Point(0, 0)) generator = GeneratorDing0(id_db=0, geo_data=Point(0, 1), mv_grid=grid) grid.graph_add_node(station) grid.add_generator(generator) branch = BranchDing0(id_db=0, length=2.0, kind='cable') grid._graph.add_edge(generator, station, branch=branch) return (grid, station, generator, branch)
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))
def build_lv_graph_residential(lvgd, selected_string_df): """Builds nxGraph based on the LV grid model Parameters ---------- lvgd : LVGridDistrictDing0 Low-voltage grid district object selected_string_df: :pandas:`pandas.DataFrame<dataframe>` Table of strings of the selected grid model Note ----- To understand what is happening in this method a few data table columns are explained here * `count house branch`: number of houses connected to a string * `distance house branch`: distance on a string between two house branches * `string length`: total length of a string * `length house branch A|B`: cable from string to connection point of a house A|B in general brings some variation in to the typified model grid and refer to different length of house branches and different cable types respectively different cable widths. """ houses_connected = (selected_string_df['occurence'] * selected_string_df['count house branch']).sum() average_load = lvgd.peak_load_residential / \ houses_connected average_consumption = lvgd.sector_consumption_residential / \ houses_connected hh_branch = 0 # iterate over each type of branch for i, row in selected_string_df.iterrows(): # get overall count of branches to set unique branch_no branch_count_sum = len( list(lvgd.lv_grid._graph.neighbors(lvgd.lv_grid.station()))) # iterate over it's occurences for branch_no in range(1, int(row['occurence']) + 1): hh_branch += 1 # iterate over house branches for house_branch in range(1, row['count house branch'] + 1): if house_branch % 2 == 0: variant = 'B' else: variant = 'A' # cable distributor to divert from main branch lv_cable_dist = LVCableDistributorDing0(grid=lvgd.lv_grid, string_id=i, branch_no=branch_no + branch_count_sum, load_no=house_branch) # add lv_cable_dist to graph lvgd.lv_grid.add_cable_dist(lv_cable_dist) # cable distributor within building (to connect load+geno) lv_cable_dist_building = LVCableDistributorDing0( grid=lvgd.lv_grid, string_id=i, branch_no=branch_no + branch_count_sum, load_no=house_branch, in_building=True) # add lv_cable_dist_building to graph lvgd.lv_grid.add_cable_dist(lv_cable_dist_building) lv_load = LVLoadDing0( grid=lvgd.lv_grid, string_id=i, branch_no=branch_no + branch_count_sum, load_no=house_branch, peak_load=average_load, consumption={'residential': average_consumption}) # add lv_load to graph lvgd.lv_grid.add_load(lv_load) cable_name = row['cable type'] + \ ' 4x1x{}'.format(row['cable width']) cable_type = lvgd.lv_grid.network.static_data['LV_cables'].loc[ cable_name] # connect current lv_cable_dist to station if house_branch == 1: # edge connect first house branch in branch with the station lvgd.lv_grid._graph.add_edge( lvgd.lv_grid.station(), lv_cable_dist, branch=BranchDing0( length=row['distance house branch'], kind='cable', type=cable_type, id_db='branch_{sector}{branch}_{load}'.format( branch=hh_branch, load=house_branch, sector='HH'))) # connect current lv_cable_dist to last one else: lvgd.lv_grid._graph.add_edge( lvgd.lv_grid._cable_distributors[-4], lv_cable_dist, branch=BranchDing0( length=row['distance house branch'], kind='cable', type=lvgd.lv_grid.network.static_data['LV_cables']. loc[cable_name], id_db='branch_{sector}{branch}_{load}'.format( branch=hh_branch, load=house_branch, sector='HH'))) # connect house to cable distributor house_cable_name = row['cable type {}'.format(variant)] + \ ' 4x1x{}'.format( row['cable width {}'.format(variant)]) lvgd.lv_grid._graph.add_edge( lv_cable_dist, lv_cable_dist_building, branch=BranchDing0( length=row['length house branch {}'.format( variant)], kind='cable', type=lvgd.lv_grid.network.static_data['LV_cables']. \ loc[house_cable_name], id_db='branch_{sector}{branch}_{load}'.format( branch=hh_branch, load=house_branch, sector='HH')) ) lvgd.lv_grid._graph.add_edge( lv_cable_dist_building, lv_load, branch=BranchDing0( length=1, kind='cable', type=lvgd.lv_grid.network.static_data['LV_cables']. \ loc[house_cable_name], id_db='branch_{sector}{branch}_{load}'.format( branch=hh_branch, load=house_branch, sector='HH')) )
def lv_graph_attach_branch(): """Attach a single branch including its equipment (cable dist, loads and line segments) to graph of `lv_grid` """ # determine maximum current occuring due to peak load # of this load load_no I_max_load = val['single_peak_load'] / (3**0.5 * v_nom) / cos_phi_load # determine suitable cable for this current suitable_cables_stub = lvgd.lv_grid.network.static_data['LV_cables'][( lvgd.lv_grid.network.static_data['LV_cables']['I_max_th'] * cable_lf) > I_max_load] cable_type_stub = suitable_cables_stub.loc[ suitable_cables_stub['I_max_th'].idxmin(), :] # cable distributor to divert from main branch lv_cable_dist = LVCableDistributorDing0(grid=lvgd.lv_grid, branch_no=branch_no, load_no=load_no) # add lv_cable_dist to graph lvgd.lv_grid.add_cable_dist(lv_cable_dist) # cable distributor within building (to connect load+geno) lv_cable_dist_building = LVCableDistributorDing0(grid=lvgd.lv_grid, branch_no=branch_no, load_no=load_no, in_building=True) # add lv_cable_dist_building to graph lvgd.lv_grid.add_cable_dist(lv_cable_dist_building) # create an instance of Ding0 LV load lv_load = LVLoadDing0(grid=lvgd.lv_grid, branch_no=branch_no, load_no=load_no, peak_load=val['single_peak_load'], consumption=val['consumption']) # add lv_load to graph lvgd.lv_grid.add_load(lv_load) # create branch line segment between either (a) station # and cable distributor or (b) between neighboring cable # distributors if load_no == 1: # case a: cable dist <-> station lvgd.lv_grid._graph.add_edge( lvgd.lv_grid.station(), lv_cable_dist, branch=BranchDing0( length=val['load_distance'], kind='cable', type=cable_type, id_db='branch_{sector}{branch}_{load}'.format( branch=branch_no, load=load_no, sector=sector_short))) else: # case b: cable dist <-> cable dist lvgd.lv_grid._graph.add_edge( lvgd.lv_grid._cable_distributors[-4], lv_cable_dist, branch=BranchDing0( length=val['load_distance'], kind='cable', type=cable_type, id_db='branch_{sector}{branch}_{load}'.format( branch=branch_no, load=load_no, sector=sector_short))) # create branch stub that connects the load to the # lv_cable_dist located in the branch line lvgd.lv_grid._graph.add_edge( lv_cable_dist, lv_cable_dist_building, branch=BranchDing0(length=cfg_ding0.get( 'assumptions', 'lv_ria_branch_connection_distance'), kind='cable', type=cable_type_stub, id_db='stub_{sector}{branch}_{load}'.format( branch=branch_no, load=load_no, sector=sector_short))) lvgd.lv_grid._graph.add_edge( lv_cable_dist_building, lv_load, branch=BranchDing0(length=1, kind='cable', type=cable_type_stub, id_db='stub_{sector}{branch}_{load}'.format( branch=branch_no, load=load_no, sector=sector_short)))
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
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
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
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
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