def replace_branch_switches(net, reserved_aux_node_names=None): """ Instead of directly connect branch elements (trafo and line) to nodes which connection is done by switches, this function creates an auxiliary node in between of the branch on the one hand and the switch and node on the other hand. """ # --- determine indices idx_t_sw = net.switch.index[net.switch.et == "t"] idx_l_sw = net.switch.index[net.switch.et == "l"] n_branch_switches = len(idx_t_sw)+len(idx_l_sw) idx_bus = net.switch.bus[idx_t_sw | idx_l_sw] # --- create auxiliary nodes names, reserved_aux_node_names = append_str_by_underline_count( net.bus.name[idx_bus], reserved_strings=reserved_aux_node_names) if "subnet" in net.switch.columns: # if replace_branch_switches() is called by pp2csv_data(), "subnet" is available subnets = net.switch.subnet.loc[idx_t_sw | idx_l_sw].values else: # if replace_branch_switches() is called out of pp2csv_data(), this else statement is given subnets = net.bus.zone[idx_bus].values geodata = net.bus_geodata.loc[idx_bus, ["x", "y"]].values if net["bus_geodata"].shape[0] else \ np.empty((len(idx_bus), 2)) aux_buses = pp.create_buses( net, n_branch_switches, net.bus.vn_kv[idx_bus].values, name=names.values, type="auxiliary", geodata=geodata, zone=subnets) for col in ["min_vm_pu", "max_vm_pu", "substation", "voltLvl"]: if col in net.bus.columns: net.bus[col].loc[aux_buses] = net.bus[col][idx_bus].values if "subnet" in net.bus.columns: net.bus.subnet.loc[aux_buses] = subnets assert len(idx_bus) == len(aux_buses) # --- replace branch bus by new auxiliary node for idx_b_sw, branch, bus_types in zip([idx_t_sw, idx_l_sw], ["trafo", "line"], [["hv_bus", "lv_bus"], ["from_bus", "to_bus"]]): idx_elm = net.switch.element[idx_b_sw] is_first_bus_type = net[branch][bus_types[0]].loc[idx_elm].values == idx_bus.loc[ idx_b_sw].values # is_first_bus_type == hv_bus resp. from_bus pos_in_aux_buses = idx_in_2nd_array(np.array(idx_b_sw[is_first_bus_type]), np.array(idx_t_sw | idx_l_sw)) net[branch][bus_types[0]].loc[idx_elm[is_first_bus_type]] = aux_buses[pos_in_aux_buses] # ~is_first_bus_type == lv_bus resp. to_bus pos_in_aux_buses = idx_in_2nd_array(np.array(idx_b_sw[~is_first_bus_type]), np.array(idx_t_sw | idx_l_sw)) net[branch][bus_types[1]].loc[idx_elm[~is_first_bus_type]] = aux_buses[pos_in_aux_buses] # --- replace switch element by new auxiliary nodes net.switch.element.loc[idx_t_sw | idx_l_sw] = aux_buses net.switch.et.loc[idx_t_sw | idx_l_sw] = "b" return reserved_aux_node_names
def _sort_switch_nodes_and_prepare_element_and_et(csv_data): """ 1) Swaps nodeA and nodeB data in switch table, if nodeA has auxiliary node names. As a result, no auxiliary node names are in nodeA column. 2) Prepares "et" and "nodeB" (-> "element") columns for conversion to pp format. """ # --- get indices/booleans idx_aux_node = csv_data["Node"].type == "auxiliary" aux_node_names = csv_data["Node"].id[idx_aux_node] nodeA_is_aux_node = csv_data["Switch"].nodeA.isin(aux_node_names) nodeB_is_aux_node = csv_data["Switch"].nodeB.isin(aux_node_names) both_are_aux_nodes = nodeA_is_aux_node & nodeB_is_aux_node if sum(both_are_aux_nodes): raise ValueError( "In switch table, nodeA and nodeB are auxiliary node names in the" + "indices: " + str(list(both_are_aux_nodes))) # --- swap nodeA data if there are auxiliary node names nodeA_data = deepcopy(csv_data["Switch"].nodeA[nodeA_is_aux_node]) csv_data["Switch"].nodeA.loc[nodeA_is_aux_node] = csv_data["Switch"].nodeB[ nodeA_is_aux_node] csv_data["Switch"].nodeB.loc[nodeA_is_aux_node] = nodeA_data # --- prepare 'element' and 'et' columns csv_data["Switch"]["et"] = "b" csv_data["Switch"]["nodeB"] = csv_data["Node"].index[idx_in_2nd_array( csv_data["Switch"]["nodeB"].values, csv_data["Node"]["id"].values)]
def _replace_buses_connected_to_busbars(net, buses): """ Replaces buses, which are no busbars, connected to a transformer and connected at least one busbar via bus-bus switch, by busbars to set transformer setpoints to the busbars. """ no_busbar = ~net.bus.loc[buses].type.str.contains("busbar") non_busbars = buses[no_busbar.values] bb_sw = net.switch.loc[net.switch.et == "b"] new_buses = pd.Series() for X, Y in zip(["element", "bus"], ["bus", "element"]): X_in_nonb = bb_sw[X].isin(non_busbars) Y_is_busbar = net.bus.loc[bb_sw[Y]].type.str.contains("busbar").values idx_sw_to_set = bb_sw.index[X_in_nonb & Y_is_busbar] if len(idx_sw_to_set): idx_sw_in_nonb = idx_in_2nd_array( bb_sw[X].loc[idx_sw_to_set].values, non_busbars.values) trafos = non_busbars.index[idx_sw_in_nonb] new_buses = pd.concat([ new_buses, pd.Series(bb_sw[Y].loc[idx_sw_to_set].values, index=trafos) ]) new_buses = pd.concat([ buses.loc[~pd.Series(buses.index).isin(new_buses.index).values], new_buses ]) return new_buses
def _add_phys_type_and_vm_va_setpoints_to_element_tables(csv_data): """ Creates 'phys_type', 'vm_pu' and 'va_degree' column in 'ExternalNet', 'PowerPlant', 'RES' tables as well as 'vm_from_pu' and 'vm_to_pu' in 'Line' table for dclines and autoTapSetp for trafos. """ # --- "ExternalNet", "PowerPlant", "RES" for gen_table in ["ExternalNet", "PowerPlant", "RES"]: csv_data[gen_table]["phys_type"] = gen_table idx_node = idx_in_2nd_array(csv_data[gen_table].node.values, csv_data["Node"].id.values) csv_data[gen_table]["vm_pu"] = csv_data["Node"].vmSetp[idx_node].values csv_data[gen_table]["va_degree"] = csv_data["Node"].vaSetp[ idx_node].values # --- Line (for dclines) for bus_type, param in zip(["nodeA", "nodeB"], ['vm_from_pu', 'vm_to_pu']): idx_node = idx_in_2nd_array(csv_data["Line"][bus_type].values, csv_data["Node"].id.values) csv_data["Line"][param] = csv_data["Node"].vmSetp[idx_node].values
def _ensure_single_switch_at_aux_node_and_copy_vm_setp(csv_data, new_type_name="node"): """ This function set the Node type from 'auxiliary' to new_type_name for all nodes which are connected to multiple switches, because 'auxiliary' Nodes will be removed in create_branch_switches() while nodes with multiple switches are necessary for bus-bus switches. Furthermore, this function copies the vmSetp information from connected busbars to the nodes which got a new type name. """ sw_nodes = pd.concat([csv_data["Switch"].nodeA, csv_data["Switch"].nodeB], ignore_index=True) dupl_sw_node = sw_nodes[sw_nodes.duplicated()] dupl_node_ids = csv_data["Node"].id.isin(dupl_sw_node) aux_node_ids = csv_data["Node"].type == "auxiliary" idx_nodes_dupl_sw = csv_data["Node"].index[dupl_node_ids & aux_node_ids] # rename node type csv_data["Node"].type.loc[idx_nodes_dupl_sw] = new_type_name for X, Y in zip(["nodeA", "nodeB"], ["nodeB", "nodeA"]): # get indices to copy the setpoint node_names_dupl_sw = csv_data["Node"].id.loc[idx_nodes_dupl_sw] X_in_dupl = csv_data["Switch"][X].isin(node_names_dupl_sw) idx_Y = idx_in_2nd_array(csv_data["Switch"][Y].values, csv_data["Node"]["id"].values) Y_is_busbar = csv_data["Node"].loc[idx_Y].type.str.contains( "busbar").values idx_in_sw_to_set = csv_data["Switch"].index[X_in_dupl & Y_is_busbar] idx_X = idx_in_2nd_array( csv_data["Switch"][X].loc[idx_in_sw_to_set].values, csv_data["Node"]["id"].values) idx_Y = idx_Y[X_in_dupl & Y_is_busbar] # only use the first setpoint for nodes which are connected to multiple busbars idx_X_pd = pd.Series(idx_X) idx_node_dupl = idx_X_pd.duplicated() # set setpoint csv_data["Node"].vmSetp.loc[idx_X[idx_node_dupl]] = csv_data[ "Node"].vmSetp.loc[idx_Y[idx_node_dupl]].values
def create_branch_switches(net): """ Changes bus-bus switches with auxiliary buses into bus-branch switches and drops all auxiliary buses. """ # initialize DataFrame to store the indices of auxiliary buses ("aux_buses"), the switch indices # the auxiliary buses are connected to ("idx_switch"), the bus indices which are connected to # auxiliary buses via the switches("connected_buses"), the element type the auxiliary buses are # connected to ("et") and the element the auxiliary buses are connected to ("element") aux_bus_df = pd.DataFrame([], columns=["idx_switch", "aux_buses", "connected_buses", "et", "element"]) # determine the bus indices of all auxiliary buses all_aux_buses = net.bus.index[net.bus.type == "auxiliary"] # determine the switch indices which are connected to auxiliary buses aux_bus_df["idx_switch"] = net.switch.index[net.switch.element.isin(all_aux_buses)] # determine the auxiliary bus indices of the switches aux_bus_df["aux_buses"] = net.switch.element.loc[aux_bus_df["idx_switch"]].values # determine the indices of the buses which are connected to auxiliary buses via switches aux_bus_df["connected_buses"] = net.switch.bus.loc[aux_bus_df["idx_switch"]].values # determine the element types and element indices which are connected to auxiliary buses for branch, bus_types in zip(["trafo", "line"], [["hv_bus", "lv_bus"], ["from_bus", "to_bus"]]): for bus_type in bus_types: current_branch_bus_type_buses = net[branch][bus_type].astype(int) aux_buses_are_cbbtb = aux_bus_df["aux_buses"].isin(current_branch_bus_type_buses) current_branch_bus_types_aux_buses = aux_bus_df["aux_buses"][aux_buses_are_cbbtb].values aux_bus_df["element"].loc[aux_buses_are_cbbtb] = current_branch_bus_type_buses.index[ idx_in_2nd_array(current_branch_bus_types_aux_buses, current_branch_bus_type_buses.values)] # requirement: only one # switch per aux bus aux_bus_df["et"].loc[aux_buses_are_cbbtb] = branch[0] # replace auxiliary buses in line and trafo tables net[branch][bus_type].loc[aux_bus_df["element"].loc[aux_buses_are_cbbtb]] = aux_bus_df[ "connected_buses"].loc[aux_buses_are_cbbtb].values if pd.isnull(aux_bus_df).any().any(): logger.error("Auxiliary bus replacement fails.") # replace auxiliary buses in switch table by branch elements for col in ["et", "element"]: net.switch[col].loc[aux_bus_df["idx_switch"]] = aux_bus_df[col].values # drop all auxiliary buses net.bus.drop(aux_bus_df["aux_buses"], inplace=True) idx_in_res_bus = aux_bus_df["aux_buses"][aux_bus_df["aux_buses"].isin(net.res_bus.index)] net.res_bus.drop(idx_in_res_bus, inplace=True) idx_in_bus_geodata = aux_bus_df["aux_buses"][aux_bus_df["aux_buses"].isin( net.bus_geodata.index)] net.bus_geodata.drop(idx_in_bus_geodata, inplace=True)
def _extend_coordinates_to_node_shape(csv_data): """ Extends the Coordinates table to the shape of Nodes to enable copying simply to bus_geodata. """ bus_geodata = pd.DataFrame([], index=csv_data["Node"].index, columns=["x", "y"]) with_coord = ~csv_data["Node"]["coordID"].isnull() idx_in_coordID = idx_in_2nd_array( csv_data["Node"]["coordID"].loc[with_coord].values, csv_data["Coordinates"]["id"].values) bus_geodata.loc[with_coord, ["x", "y"]] = csv_data["Coordinates"].loc[ idx_in_coordID, ["x", "y"]].values csv_data["Coordinates"] = bus_geodata
def _set_vm_setpoint_to_trafos(net, csv_data): """ Adds 'autoTapSetp' to trafo and trafo3w tables. """ for elm in ["trafo", "trafo3w"]: if "autoTap" in net[elm] and "autoTapSide" in net[elm]: autotap_trafos = net[elm].autoTap.fillna(False).astype(bool) assert all(autotap_trafos == net[elm].autoTapSide.fillna(False).astype(bool)) if sum(autotap_trafos): bus_type = net[elm].autoTapSide.loc[autotap_trafos].str.lower() + "_bus" bus_type_col_idx = column_indices(net[elm], bus_type) buses = net[elm].values[autotap_trafos.index[autotap_trafos], bus_type_col_idx] bus_names = net.bus.name.loc[buses] idx_node = idx_in_2nd_array(bus_names.values, csv_data["Node*bus"].name.values) net[elm].loc[autotap_trafos, "autoTapSetp"] = csv_data["Node*bus"].vmSetp[ idx_node].values