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