diff --git a/edisgo/flex_opt/reinforce_grid.py b/edisgo/flex_opt/reinforce_grid.py index e56eb58b4..210399585 100644 --- a/edisgo/flex_opt/reinforce_grid.py +++ b/edisgo/flex_opt/reinforce_grid.py @@ -837,6 +837,7 @@ def enhanced_reinforce_grid( activate_cost_results_disturbing_mode: bool = False, separate_lv_grids: bool = True, separation_threshold: int | float = 2, + use_standard_line_type: bool = True, **kwargs, ) -> EDisGo: """ @@ -877,7 +878,12 @@ def enhanced_reinforce_grid( separation_threshold : int or float Overloading threshold for LV grid separation. If the overloading is higher than the threshold times the total nominal apparent power of the MV/LV transformer(s) - the grid is separated. + the grid is separated. Default: 2. + use_standard_line_type : bool + Only used when `separate_lv_grids` is set to True. If use_standard_line_type is + True, standard line type is used to connect bus, where feeder is split, to + the station. If False, the same line type and number of parallel lines as + the original line is used. Default: True. kwargs : dict Keyword arguments can be all parameters of function :func:`edisgo.flex_opt.reinforce_grid.reinforce_grid`, except @@ -891,6 +897,7 @@ def enhanced_reinforce_grid( """ kwargs.pop("skip_mv_reinforcement", False) + # ToDo kwargs timesteps_pfa is currently ignored, should that be changed? num_lv_grids_standard_lines = 0 num_lv_grids_aggregated = 0 @@ -900,7 +907,11 @@ def enhanced_reinforce_grid( "Separating lv grids. Set the parameter 'separate_lv_grids' to False if " "this is not desired." ) - run_separate_lv_grids(edisgo_object, threshold=separation_threshold) + run_separate_lv_grids( + edisgo_object, + threshold=separation_threshold, + use_standard_line_type=use_standard_line_type, + ) logger.info("Run initial grid reinforcement for single LV grids.") for lv_grid in list(edisgo_object.topology.mv_grid.lv_grids): @@ -1077,7 +1088,11 @@ def enhanced_reinforce_grid( return edisgo_object -def run_separate_lv_grids(edisgo_obj: EDisGo, threshold: int | float = 2) -> None: +def run_separate_lv_grids( + edisgo_obj: EDisGo, + threshold: int | float = 2, + use_standard_line_type: bool = True, +) -> None: """ Separate all highly overloaded LV grids within the MV grid. @@ -1095,6 +1110,10 @@ def run_separate_lv_grids(edisgo_obj: EDisGo, threshold: int | float = 2) -> Non Overloading threshold. If the overloading is higher than the threshold times the total nominal apparent power of the MV/LV transformer(s), the grid is separated. + use_standard_line_type : bool + If use_standard_line_type is True, standard line type is used to connect bus + where feeder is split to the station. If False, the same line type and number + of parallel lines as the original line is used. Default: True. Returns ------- @@ -1169,7 +1188,7 @@ def run_separate_lv_grids(edisgo_obj: EDisGo, threshold: int | float = 2) -> Non if worst_case > threshold * transformers_s_nom: logger.info(f"Trying to separate {lv_grid}...") transformers_changes, lines_changes = separate_lv_grid( - edisgo_obj, lv_grid + edisgo_obj, lv_grid, use_standard_line_type ) if len(lines_changes) > 0: _add_lines_changes_to_equipment_changes( diff --git a/edisgo/flex_opt/reinforce_measures.py b/edisgo/flex_opt/reinforce_measures.py index b93e0f274..4be4f44df 100644 --- a/edisgo/flex_opt/reinforce_measures.py +++ b/edisgo/flex_opt/reinforce_measures.py @@ -342,65 +342,92 @@ def reinforce_mv_lv_station_voltage_issues(edisgo_obj, critical_stations): return transformers_changes -def reinforce_lines_voltage_issues(edisgo_obj, grid, crit_nodes): +def get_standard_line(edisgo_obj, grid=None, nominal_voltage=None): """ - Reinforce lines in MV and LV topology due to voltage issues. + Get standard line type for given voltage level from config. Parameters - ---------- + ----------- edisgo_obj : :class:`~.EDisGo` grid : :class:`~.network.grids.MVGrid` or :class:`~.network.grids.LVGrid` - crit_nodes : :pandas:`pandas.DataFrame` - Dataframe with maximum deviations from allowed lower or upper voltage limits - in p.u. for all buses in specified grid. For more information on dataframe see - :attr:`~.flex_opt.check_tech_constraints.voltage_issues`. + nominal_voltage : float + Nominal voltage of grid level to obtain standard line type for. Can be + 0.4, 10 or 20 kV. Returns - ------- - dict - Dictionary with name of lines as keys and the corresponding number of - lines added as values. + --------- + str + Name of standard line, e.g. "NAYY 4x1x150". - Notes - ----- - Reinforce measures: + """ + if grid is not None: + if isinstance(grid, LVGrid): + nominal_voltage = 0.4 + elif isinstance(grid, MVGrid): + nominal_voltage = grid.buses_df.v_nom.values[0] + else: + raise ValueError("Inserted grid is invalid.") + if nominal_voltage == 0.4: + standard_line_type = edisgo_obj.config["grid_expansion_standard_equipment"][ + "lv_line" + ] + else: + standard_line_type = edisgo_obj.config["grid_expansion_standard_equipment"][ + f"mv_line_{int(nominal_voltage)}kv" + ] + return standard_line_type - 1. Disconnect line at 2/3 of the length between station and critical node - farthest away from the station and install new standard line - 2. Install parallel standard line - In LV grids only lines outside buildings are reinforced; loads and - generators in buildings cannot be directly connected to the MV/LV station. +def split_feeder_at_given_length( + edisgo_obj, grid, feeder_name, crit_nodes_in_feeder, disconnect_length=2 / 3 +): + """ + Splits given feeder at specified length. - In MV grids lines can only be disconnected at LV stations because they + This is a standard grid expansion measure in case of voltage issues. There, the + feeder is usually disconnected at 2/3 of the feeder length. + + The feeder is split at 2/3 of the length between the station and the critical node + farthest away from the station. A new standard line is installed, or if the line is + already connected to the grid's station exchanged by standard line or a + parallel standard line installed. + In LV grids, feeder can only be split outside of buildings, i.e. loads and + generators in buildings cannot be directly connected to the MV/LV station. + In MV grids feeder can only be split at LV stations because they have switch disconnectors needed to operate the lines as half rings (loads in MV would be suitable as well because they have a switch bay (Schaltfeld) - but loads in dingo are only connected to MV busbar). If there is no - suitable LV station the generator is directly connected to the MV busbar. - There is no need for a switch disconnector in that case because generators - don't need to be n-1 safe. + but this is currently not implemented). - """ + Parameters + ----------- + edisgo_obj : :class:`~.EDisGo` + grid : :class:`~.network.grids.MVGrid` or :class:`~.network.grids.LVGrid` + feeder_name : str + The feeder name corresponds to the name of the neighboring + node of the respective grid's station. + crit_nodes_in_feeder : list(str) + List with names of buses that have voltage issues or should be considered + when finding the point in the feeder where to split it. This is needed + in order to find the critical node farthest away from the station. + disconnect_length : float + Relative length at which the feeder should be split. Default: 2/3. - # load standard line data - if isinstance(grid, LVGrid): - standard_line = edisgo_obj.config["grid_expansion_standard_equipment"][ - "lv_line" - ] - elif isinstance(grid, MVGrid): - standard_line = edisgo_obj.config["grid_expansion_standard_equipment"][ - f"mv_line_{int(grid.nominal_voltage)}kv" - ] - else: - raise ValueError("Inserted grid is invalid.") + Returns + ------- + dict{str: float} + Dictionary with name of lines at which feeder was split as keys and the + corresponding number of lines added as values. + + """ + standard_line = get_standard_line(edisgo_obj, grid=grid) + lines_changes = {} # find path to each node in order to find node with voltage issues farthest - # away from station in each feeder - station_node = grid.transformers_df.bus1.iloc[0] + # away from station graph = grid.graph + station_node = grid.station.index[0] paths = {} - nodes_feeder = {} - for node in crit_nodes.index: + for node in crit_nodes_in_feeder: path = nx.shortest_path(graph, station_node, node) paths[node] = path # raise exception if voltage issue occurs at station's secondary side @@ -411,107 +438,236 @@ def reinforce_lines_voltage_issues(edisgo_obj, grid, crit_nodes): "Voltage issues at busbar in LV network {} should have " "been solved in previous steps.".format(grid) ) - nodes_feeder.setdefault(path[1], []).append(node) - lines_changes = {} - for repr_node in nodes_feeder.keys(): - # find node farthest away - get_weight = lambda u, v, data: data["length"] # noqa: E731 - path_length = 0 - for n in nodes_feeder[repr_node]: - path_length_dict_tmp = dijkstra_shortest_path_length( - graph, station_node, get_weight, target=n - ) - if path_length_dict_tmp[n] > path_length: - node = n - path_length = path_length_dict_tmp[n] - path_length_dict = path_length_dict_tmp - path = paths[node] - - # find first node in path that exceeds 2/3 of the line length - # from station to critical node farthest away from the station - node_2_3 = next( - j for j in path if path_length_dict[j] >= path_length_dict[node] * 2 / 3 + # find node farthest away + get_weight = lambda u, v, data: data["length"] # noqa: E731 + path_length = 0 + for n in crit_nodes_in_feeder: + path_length_dict_tmp = dijkstra_shortest_path_length( + graph, grid.station.index[0], get_weight, target=n ) + if path_length_dict_tmp[n] > path_length: + node = n + path_length = path_length_dict_tmp[n] + path_length_dict = path_length_dict_tmp + path = paths[node] + + # find first node in path that exceeds given length of the line length + # from station to critical node farthest away from the station where feeder should + # be separated + disconnect_node = next( + j + for j in path + if path_length_dict[j] >= path_length_dict[node] * disconnect_length + ) - # if LVGrid: check if node_2_3 is outside of a house - # and if not find next BranchTee outside the house - if isinstance(grid, LVGrid): - while ( - ~np.isnan(grid.buses_df.loc[node_2_3].in_building) - and grid.buses_df.loc[node_2_3].in_building - ): - node_2_3 = path[path.index(node_2_3) - 1] - # break if node is station - if node_2_3 is path[0]: - logger.error("Could not reinforce voltage issue.") - break - - # if MVGrid: check if node_2_3 is LV station and if not find - # next LV station - else: - while node_2_3 not in edisgo_obj.topology.transformers_df.bus0.values: - try: - # try to find LVStation behind node_2_3 - node_2_3 = path[path.index(node_2_3) + 1] - except IndexError: - # if no LVStation between node_2_3 and node with - # voltage problem, connect node directly to - # MVStation - node_2_3 = node - break - - # if node_2_3 is a representative (meaning it is already - # directly connected to the station), line cannot be - # disconnected and must therefore be reinforced - if node_2_3 in nodes_feeder.keys(): - crit_line_name = graph.get_edge_data(station_node, node_2_3)["branch_name"] - crit_line = grid.lines_df.loc[crit_line_name] - - # if critical line is already a standard line install one - # more parallel line - if crit_line.type_info == standard_line: - edisgo_obj.topology.update_number_of_parallel_lines( - pd.Series( - index=[crit_line_name], - data=[ - edisgo_obj.topology._lines_df.at[ - crit_line_name, "num_parallel" - ] - + 1 - ], - ) - ) - lines_changes[crit_line_name] = 1 + # if LVGrid: check if disconnect_node is outside of a house + # and if not find next BranchTee outside the house + if isinstance(grid, LVGrid): + while ( + ~np.isnan(grid.buses_df.loc[disconnect_node].in_building) + and grid.buses_df.loc[disconnect_node].in_building + ): + disconnect_node = path[path.index(disconnect_node) - 1] + # break if node is station + if disconnect_node is path[0]: + logger.error("Could not reinforce voltage issue.") + break - # if critical line is not yet a standard line replace old - # line by a standard line - else: - # number of parallel standard lines could be calculated - # following [2] p.103; for now number of parallel - # standard lines is iterated - edisgo_obj.topology.change_line_type([crit_line_name], standard_line) - lines_changes[crit_line_name] = 1 + # if MVGrid: check if disconnect_node is LV station and if not find + # next LV station + else: + while disconnect_node not in edisgo_obj.topology.transformers_df.bus0.values: + try: + # try to find LVStation behind disconnect_node + disconnect_node = path[path.index(disconnect_node) + 1] + except IndexError: + # if no LVStation between disconnect_node and node with + # voltage problem, connect node directly to + # MVStation + disconnect_node = node + break - # if node_2_3 is not a representative, disconnect line + # if disconnect_node is a representative (meaning it is already + # directly connected to the station), line cannot be + # disconnected and must therefore be reinforced + if disconnect_node == feeder_name: + crit_line_name = graph.get_edge_data(station_node, disconnect_node)[ + "branch_name" + ] + crit_line = grid.lines_df.loc[crit_line_name] + + # if critical line is already a standard line install one + # more parallel line + if crit_line.type_info == standard_line: + edisgo_obj.topology.update_number_of_parallel_lines( + pd.Series( + index=[crit_line_name], + data=[ + edisgo_obj.topology._lines_df.at[crit_line_name, "num_parallel"] + + 1 + ], + ) + ) + lines_changes[crit_line_name] = 1 + + # if critical line is not yet a standard line replace old + # line by a standard line else: - # get line between node_2_3 and predecessor node (that is - # closer to the station) - pred_node = path[path.index(node_2_3) - 1] - crit_line_name = graph.get_edge_data(node_2_3, pred_node)["branch_name"] - if grid.lines_df.at[crit_line_name, "bus0"] == pred_node: - edisgo_obj.topology._lines_df.at[crit_line_name, "bus0"] = station_node - elif grid.lines_df.at[crit_line_name, "bus1"] == pred_node: - edisgo_obj.topology._lines_df.at[crit_line_name, "bus1"] = station_node - else: - raise ValueError("Bus not in line buses. Please check.") - # change line length and type - edisgo_obj.topology._lines_df.at[ - crit_line_name, "length" - ] = path_length_dict[node_2_3] + # number of parallel standard lines could be calculated + # following [2] p.103; for now number of parallel + # standard lines is iterated edisgo_obj.topology.change_line_type([crit_line_name], standard_line) lines_changes[crit_line_name] = 1 - # TODO: Include switch disconnector + logger.debug( + f"When solving voltage issues in grid {grid.id} in feeder " + f"{feeder_name}, disconnection at 2/3 was tried but bus is already " + f"connected to the station, wherefore line {crit_line_name} was " + f"reinforced." + ) + + # if disconnect_node is not a representative, disconnect line + else: + # get line between disconnect_node and predecessor node (that is + # closer to the station) + pred_node = path[path.index(disconnect_node) - 1] + crit_line_name = graph.get_edge_data(disconnect_node, pred_node)["branch_name"] + if grid.lines_df.at[crit_line_name, "bus0"] == pred_node: + edisgo_obj.topology._lines_df.at[crit_line_name, "bus0"] = station_node + elif grid.lines_df.at[crit_line_name, "bus1"] == pred_node: + edisgo_obj.topology._lines_df.at[crit_line_name, "bus1"] = station_node + else: + raise ValueError("Bus not in line buses. Please check.") + # change line length and type + edisgo_obj.topology._lines_df.at[crit_line_name, "length"] = path_length_dict[ + disconnect_node + ] + edisgo_obj.topology.change_line_type([crit_line_name], standard_line) + lines_changes[crit_line_name] = 1 + # TODO: Include switch disconnector + logger.debug( + f"Feeder {feeder_name} in grid {grid.id} was split at " + f"line {crit_line_name}." + ) + return lines_changes + + +def reinforce_lines_voltage_issues(edisgo_obj, grid, crit_nodes): + """ + Reinforce lines in MV and LV topology due to voltage issues. + + Parameters + ---------- + edisgo_obj : :class:`~.EDisGo` + grid : :class:`~.network.grids.MVGrid` or :class:`~.network.grids.LVGrid` + crit_nodes : :pandas:`pandas.DataFrame` + Dataframe with maximum deviations from allowed lower or upper voltage limits + in p.u. for all buses in specified grid. For more information on dataframe see + :attr:`~.flex_opt.check_tech_constraints.voltage_issues`. + + Returns + ------- + dict + Dictionary with name of lines as keys and the corresponding number of + lines added as values. + + Notes + ----- + Reinforce measures: + + 1. For LV only, exchange all cables in feeder by standard cable if smaller cable is + currently used. + 2. Split feeder at 2/3 of the length between station and critical node + farthest away from the station and install new standard line, or if the line is + already connected to the grid's station exchange by standard line or install + parallel standard line. See function :attr:`split_feeder_at_given_length` for more + information. + + """ + # load standard line data + standard_line = get_standard_line(edisgo_obj, grid=grid) + + # get feeders with voltage issues + grid.assign_grid_feeder() + crit_buses_df = grid.buses_df.loc[crit_nodes.index, :] + crit_feeders = crit_buses_df.grid_feeder.unique() + + # per default, measure to disconnect at two-thirds is set to True and only if cables + # in grid are exchanged by standard lines it is set to False, to recheck voltage + disconnect_2_3 = True + + lines_changes = {} + for repr_node in crit_feeders: + if isinstance(grid, LVGrid): + lines_in_feeder = grid.lines_df[grid.lines_df.grid_feeder == repr_node] + # check if line type is any of the following + small_cables = ["NAYY 4x1x120", "NAYY 4x1x95", "NAYY 4x1x50", "NAYY 4x1x35"] + small_lines_in_feeder = lines_in_feeder[ + lines_in_feeder.type_info.isin(small_cables) + ] + # filter cables connecting houses (their type is kept) + # ToDo Currently new components can be connected to house connection via + # a new cable, wherefore it is checked, whether the house connecting cable + # is an end cable. Needs to be changed once grid connection is changed. + for line in small_lines_in_feeder.index: + lines_bus0 = edisgo_obj.topology.get_connected_lines_from_bus( + small_lines_in_feeder.at[line, "bus0"] + ) + lines_bus1 = edisgo_obj.topology.get_connected_lines_from_bus( + small_lines_in_feeder.at[line, "bus1"] + ) + if len(lines_bus0) == 1 or len(lines_bus1) == 1: + small_lines_in_feeder.drop(index=line, inplace=True) + # if there are small lines, exchange them + if len(small_lines_in_feeder) > 0: + edisgo_obj.topology.change_line_type( + small_lines_in_feeder.index, standard_line + ) + # check if s_nom before is larger than when using standard cable + # and if so, install parallel cable + lines_lower_snom = small_lines_in_feeder[ + small_lines_in_feeder.s_nom + > grid.lines_df.loc[small_lines_in_feeder.index, "s_nom"] + ] + if len(lines_lower_snom) > 0: + number_parallel_lines = np.ceil( + lines_lower_snom.s_nom + / grid.lines_df.loc[lines_lower_snom.index, "s_nom"] + ) + # update number of parallel lines + edisgo_obj.topology.update_number_of_parallel_lines( + number_parallel_lines + ) + # add to lines changes + update_dict = { + _: grid.lines_df.at[_, "num_parallel"] + for _ in small_lines_in_feeder.index + } + lines_changes.update(update_dict) + logger.debug( + f"When solving voltage issues in LV grid {grid.id} in feeder " + f"{repr_node}, {len(small_lines_in_feeder)} were exchanged by " + f"standard lines." + ) + # if any cable was changed, set disconnect_2_3 to False + disconnect_2_3 = False + + if disconnect_2_3 is True: + lines_changes_tmp = split_feeder_at_given_length( + edisgo_obj, + grid, + feeder_name=repr_node, + crit_nodes_in_feeder=crit_buses_df[ + crit_buses_df.grid_feeder == repr_node + ].index, + disconnect_length=2 / 3, + ) + logger.debug( + f"When solving voltage issues in grid {grid.id} in feeder " + f"{repr_node}, disconnection at 2/3 was conducted " + f"(line {list(lines_changes_tmp.keys())[0]})." + ) + lines_changes.update(lines_changes_tmp) if not lines_changes: logger.debug( @@ -693,14 +849,9 @@ def _replace_by_parallel_standard_lines(lines): nominal_voltage = edisgo_obj.topology.buses_df.loc[ edisgo_obj.topology.lines_df.loc[relevant_lines.index[0], "bus0"], "v_nom" ] - if nominal_voltage == 0.4: - standard_line_type = edisgo_obj.config["grid_expansion_standard_equipment"][ - "lv_line" - ] - else: - standard_line_type = edisgo_obj.config["grid_expansion_standard_equipment"][ - f"mv_line_{int(nominal_voltage)}kv" - ] + standard_line_type = get_standard_line( + edisgo_obj, nominal_voltage=nominal_voltage + ) # handling of standard lines lines_standard = relevant_lines.loc[ @@ -735,7 +886,9 @@ def _replace_by_parallel_standard_lines(lines): def separate_lv_grid( - edisgo_obj: EDisGo, grid: LVGrid + edisgo_obj: EDisGo, + grid: LVGrid, + use_standard_line_type: bool = True, ) -> tuple[dict[Any, Any], dict[str, int]]: """ Separate LV grid by adding a new substation and connect half of each feeder. @@ -765,6 +918,10 @@ def separate_lv_grid( ---------- edisgo_obj : :class:`~.EDisGo` grid : :class:`~.network.grids.LVGrid` + use_standard_line_type : bool + If True, standard line type is used to connect bus, where feeder is split, to + the station. If False, the same line type and number of parallel lines as + the original line is used. Default: True. Returns ------- @@ -856,13 +1013,12 @@ def add_standard_transformer( ) try: + standard_transformer_name = edisgo_obj.config[ + "grid_expansion_standard_equipment" + ]["mv_lv_transformer"] standard_transformer = edisgo_obj.topology.equipment_data[ "lv_transformers" - ].loc[ - edisgo_obj.config["grid_expansion_standard_equipment"][ - "mv_lv_transformer" - ] - ] + ].loc[standard_transformer_name] except KeyError: raise KeyError("Standard MV/LV transformer is not in the equipment list.") @@ -874,7 +1030,7 @@ def add_standard_transformer( new_transformer_name[grid_id_ind] = lv_grid_id_new new_transformer_df.s_nom = standard_transformer.S_nom - new_transformer_df.type_info = None + new_transformer_df.type_info = standard_transformer_name new_transformer_df.r_pu = standard_transformer.r_pu new_transformer_df.x_pu = standard_transformer.x_pu new_transformer_df.index = ["_".join([str(_) for _ in new_transformer_name])] @@ -1096,9 +1252,7 @@ def add_standard_transformer( logger.info(f"New LV grid {lv_grid_id_new} added to topology.") - lv_standard_line = edisgo_obj.config["grid_expansion_standard_equipment"][ - "lv_line" - ] + lv_standard_line = get_standard_line(edisgo_obj, nominal_voltage=0.4) # changes on relocated lines to the new LV grid # grid_ids @@ -1117,21 +1271,29 @@ def add_standard_transformer( G, station_node, get_weight, target=node_1_2 )[node_1_2] + # predecessor node of node_1_2 + pred_node = path[path.index(node_1_2) - 1] + # the line + line_removed = G.get_edge_data(node_1_2, pred_node)["branch_name"] + if use_standard_line_type is True: + line_type = lv_standard_line + num_parallel = 1 + else: + type_info = edisgo_obj.topology.lines_df.at[line_removed, "type_info"] + line_type = type_info if type_info is not None else lv_standard_line + num_parallel = edisgo_obj.topology.lines_df.at[ + line_removed, "num_parallel" + ] line_added_lv = edisgo_obj.add_component( comp_type="line", bus0=lv_bus_new, bus1=node_1_2, length=dist, - type_info=lv_standard_line, + type_info=line_type, + num_parallel=num_parallel, ) - lines_changes[line_added_lv] = 1 - - # predecessor node of node_1_2 - pred_node = path[path.index(node_1_2) - 1] - - # the line - line_removed = G.get_edge_data(node_1_2, pred_node)["branch_name"] + lines_changes[line_added_lv] = num_parallel edisgo_obj.remove_component( comp_type="line", diff --git a/edisgo/network/topology.py b/edisgo/network/topology.py index 1eb0a9272..b6df2b14f 100755 --- a/edisgo/network/topology.py +++ b/edisgo/network/topology.py @@ -2632,6 +2632,8 @@ def _connect_mv_bus_to_target_object( num_parallel=num_parallel, ) # add line to equipment changes + # ToDo number_parallel_lines should be given to + # _add_line_to_equipment_changes edisgo_object.results._add_line_to_equipment_changes( line=self.lines_df.loc[new_line_name, :], ) diff --git a/tests/flex_opt/test_reinforce_measures.py b/tests/flex_opt/test_reinforce_measures.py index 5ea720064..63e1a0bd4 100644 --- a/tests/flex_opt/test_reinforce_measures.py +++ b/tests/flex_opt/test_reinforce_measures.py @@ -304,15 +304,22 @@ def test_reinforce_lines_voltage_issues(self): # Line_50000003 (which is first line in feeder and not a # standard line) # * check where node_2_3 is not in_building => problem at + # Bus_BranchTee_LVGrid_5_3, leads to reinforcement of line + # Line_50000006 (which is first line in feeder and a standard line) + # * check where node_2_3 is not in_building => problem at # Bus_BranchTee_LVGrid_5_5, leads to reinforcement of line # Line_50000009 (which is first line in feeder and a standard line) crit_nodes = pd.DataFrame( { - "abs_max_voltage_dev": [0.08, 0.05], - "time_index": [self.timesteps[0], self.timesteps[0]], + "abs_max_voltage_dev": [0.08, 0.05, 0.07], + "time_index": [self.timesteps[0], self.timesteps[0], self.timesteps[0]], }, - index=["Bus_BranchTee_LVGrid_5_2", "Bus_BranchTee_LVGrid_5_5"], + index=[ + "Bus_BranchTee_LVGrid_5_2", + "Bus_BranchTee_LVGrid_5_3", + "Bus_BranchTee_LVGrid_5_5", + ], ) grid = self.edisgo.topology.get_lv_grid("LVGrid_5") @@ -321,10 +328,12 @@ def test_reinforce_lines_voltage_issues(self): ) reinforced_lines = lines_changes.keys() - assert len(lines_changes) == 2 + assert len(lines_changes) == 3 assert "Line_50000003" in reinforced_lines - assert "Line_50000009" in reinforced_lines - # check that LV station is one of the buses + assert "Line_50000006" in reinforced_lines + assert "Line_50000008" in reinforced_lines + # check that LV station is one of the buses for first two issues where + # disconnection at 2/3 was done assert ( "BusBar_MVGrid_1_LVGrid_5_LV" in self.edisgo.topology.lines_df.loc[ @@ -334,14 +343,14 @@ def test_reinforce_lines_voltage_issues(self): assert ( "BusBar_MVGrid_1_LVGrid_5_LV" in self.edisgo.topology.lines_df.loc[ - "Line_50000009", ["bus0", "bus1"] + "Line_50000006", ["bus0", "bus1"] ].values ) # check other bus assert ( - "Bus_BranchTee_LVGrid_5_5" + "Bus_BranchTee_LVGrid_5_3" in self.edisgo.topology.lines_df.loc[ - "Line_50000009", ["bus0", "bus1"] + "Line_50000006", ["bus0", "bus1"] ].values ) assert ( @@ -350,26 +359,30 @@ def test_reinforce_lines_voltage_issues(self): "Line_50000003", ["bus0", "bus1"] ].values ) - # check line parameters + # check buses of line that was only exchanged by standard line + assert ( + self.edisgo.topology.lines_df.at["Line_50000008", "bus0"] + == "Bus_BranchTee_LVGrid_5_5" + ) + assert ( + self.edisgo.topology.lines_df.at["Line_50000008", "bus1"] + == "Bus_BranchTee_LVGrid_5_6" + ) + # check line parameters - all lines where exchanged by one standard line std_line = self.edisgo.topology.equipment_data["lv_cables"].loc[ self.edisgo.config["grid_expansion_standard_equipment"]["lv_line"] ] - line = self.edisgo.topology.lines_df.loc["Line_50000003"] - assert line.type_info == std_line.name - assert np.isclose(line.r, std_line.R_per_km * line.length) - assert np.isclose( - line.x, std_line.L_per_km * line.length * 2 * np.pi * 50 / 1e3 - ) - assert np.isclose( - line.s_nom, np.sqrt(3) * grid.nominal_voltage * std_line.I_max_th - ) - assert line.num_parallel == 1 - line = self.edisgo.topology.lines_df.loc["Line_50000009"] - assert line.type_info == std_line.name - assert line.num_parallel == 2 - assert np.isclose(line.r, 0.02781 / 2) - assert np.isclose(line.x, 0.010857344210806 / 2) - assert np.isclose(line.s_nom, 0.190525588832576 * 2) + for line_name in ["Line_50000003", "Line_50000006", "Line_50000008"]: + line = self.edisgo.topology.lines_df.loc[line_name] + assert line.type_info == std_line.name + assert np.isclose(line.r, std_line.R_per_km * line.length) + assert np.isclose( + line.x, std_line.L_per_km * line.length * 2 * np.pi * 50 / 1e3 + ) + assert np.isclose( + line.s_nom, np.sqrt(3) * grid.nominal_voltage * std_line.I_max_th + ) + assert line.num_parallel == 1 def test_reinforce_lines_overloading(self): # * check for needed parallel standard lines (MV and LV) => problems at