def _add_trafo_sc_impedance_zero(net, ppc, trafo_df=None): if trafo_df is None: trafo_df = net["trafo"] branch_lookup = net["_pd2ppc_lookups"]["branch"] if not "trafo" in branch_lookup: return bus_lookup = net["_pd2ppc_lookups"]["bus"] mode = net["_options"]["mode"] f, t = branch_lookup["trafo"] trafo_df["_ppc_idx"] = range(f, t) bus_lookup = net["_pd2ppc_lookups"]["bus"] buses_all, gs_all, bs_all = np.array([], dtype=int), np.array([]), np.array([]) for vector_group, trafos in trafo_df.groupby("vector_group"): ppc_idx = trafos["_ppc_idx"].values.astype(int) ppc["branch"][ppc_idx, BR_STATUS] = 0 if vector_group in ["Yy", "Yd", "Dy", "Dd"]: continue vk_percent = trafos["vk_percent"].values.astype(float) vkr_percent = trafos["vkr_percent"].values.astype(float) sn_mva = trafos["sn_mva"].values.astype(float) vk0_percent = trafos["vk0_percent"].values.astype(float) vkr0_percent = trafos["vkr0_percent"].values.astype(float) lv_buses = trafos["lv_bus"].values.astype(int) hv_buses = trafos["hv_bus"].values.astype(int) lv_buses_ppc = bus_lookup[lv_buses] hv_buses_ppc = bus_lookup[hv_buses] mag0_percent = trafos.mag0_percent.values.astype(float) mag0_rx = trafos["mag0_rx"].values.astype(float) si0_hv_partial = trafos.si0_hv_partial.values.astype(float) parallel = trafos.parallel.values.astype(float) in_service = trafos["in_service"].astype(int) ppc["branch"][ppc_idx, F_BUS] = hv_buses_ppc ppc["branch"][ppc_idx, T_BUS] = lv_buses_ppc vn_trafo_hv, vn_trafo_lv, shift = _calc_tap_from_dataframe(net, trafos) vn_lv = ppc["bus"][lv_buses_ppc, BASE_KV] ratio = _calc_nominal_ratio_from_dataframe(ppc, trafos, vn_trafo_hv, vn_trafo_lv, bus_lookup) ppc["branch"][ppc_idx, TAP] = ratio ppc["branch"][ppc_idx, SHIFT] = shift # zero seq. transformer impedance tap_lv = np.square( vn_trafo_lv / vn_lv ) * net.sn_mva # adjust for low voltage side voltage converter z_sc = vk0_percent / 100. / sn_mva * tap_lv r_sc = vkr0_percent / 100. / sn_mva * tap_lv z_sc = z_sc.astype(float) r_sc = r_sc.astype(float) x_sc = np.sign(z_sc) * np.sqrt(z_sc**2 - r_sc**2) z0_k = (r_sc + x_sc * 1j) / parallel if mode == "sc": from pandapower.shortcircuit.idx_bus import C_MAX cmax = net._ppc["bus"][lv_buses_ppc, C_MAX] kt = _transformer_correction_factor(vk_percent, vkr_percent, sn_mva, cmax) z0_k *= kt y0_k = 1 / z0_k # zero sequence transformer magnetising impedance z_m = vk0_percent * mag0_percent / 100. / sn_mva * tap_lv x_m = z_m / np.sqrt(mag0_rx**2 + 1) r_m = x_m * mag0_rx r0_trafo_mag = r_m / parallel x0_trafo_mag = x_m / parallel z0_mag = r0_trafo_mag + x0_trafo_mag * 1j if vector_group == "Dyn": buses_all = np.hstack([buses_all, lv_buses_ppc]) gs_all = np.hstack([gs_all, y0_k.real * in_service]) bs_all = np.hstack([bs_all, y0_k.imag * in_service]) elif vector_group == "YNd": buses_all = np.hstack([buses_all, hv_buses_ppc]) gs_all = np.hstack([gs_all, y0_k.real * in_service]) bs_all = np.hstack([bs_all, y0_k.imag * in_service]) elif vector_group == "Yyn": buses_all = np.hstack([buses_all, lv_buses_ppc]) y = 1 / (z0_mag + z0_k).astype(complex) gs_all = np.hstack([gs_all, y.real * in_service]) bs_all = np.hstack([bs_all, y.imag * in_service]) elif vector_group == "YNyn": ppc["branch"][ppc_idx, BR_STATUS] = in_service # convert the t model to pi model z1 = si0_hv_partial * z0_k z2 = (1 - si0_hv_partial) * z0_k z3 = z0_mag z_temp = z1 * z2 + z2 * z3 + z1 * z3 za = z_temp / z2 zb = z_temp / z1 zc = z_temp / z3 ppc["branch"][ppc_idx, BR_R] = zc.real ppc["branch"][ppc_idx, BR_X] = zc.imag y = 2 / za ppc["branch"][ppc_idx, BR_B] = y.imag - y.real * 1j # add a shunt element parallel to zb if the leakage impedance distribution is unequal #TODO: this only necessary if si0_hv_partial!=0.5 --> test zs = (za * zb) / (za - zb) ys = 1 / zs.astype(complex) buses_all = np.hstack([buses_all, lv_buses_ppc]) gs_all = np.hstack([gs_all, ys.real * in_service]) bs_all = np.hstack([bs_all, ys.imag * in_service]) elif vector_group == "YNy": buses_all = np.hstack([buses_all, hv_buses_ppc]) y = 1 / (z0_mag + z0_k).astype(complex) gs_all = np.hstack([gs_all, y.real * in_service]) bs_all = np.hstack([bs_all, y.imag * in_service]) elif vector_group[-1].isdigit(): raise ValueError( "Unknown transformer vector group %s - please specify vector group without phase shift number. Phase shift can be specified in net.trafo.shift_degree" % vector_group) else: raise ValueError( "Transformer vector group %s is unknown / not implemented" % vector_group) buses, gs, bs = aux._sum_by_group(buses_all, gs_all, bs_all) ppc["bus"][buses, GS] += gs ppc["bus"][buses, BS] += bs del net.trafo["_ppc_idx"]
if False: # case_name = "test_case6495rte.json" bus_lv = 6195 trafo_bug = pp_net.trafo.loc[pp_net.trafo["lv_bus"] == bus_lv] shunt_bug = pp_net.shunt.loc[pp_net.shunt["bus"] == bus_lv] bus_hv = int(trafo_bug["hv_bus"]) me_val_hv_hv = Y_me[bus_hv, bus_hv] me_val_lv_lv = Y_me[bus_lv, bus_lv] pp_val_hv_hv = Y_pp_right_order[bus_hv, bus_hv] pp_val_lv_lv = Y_pp_right_order[bus_lv, bus_lv] np.abs(me_val_lv_lv / pp_val_lv_lv) np.abs(me_val_hv_hv / pp_val_hv_hv) # TODO add the first two lines (the function _calc_tap_from_dataframe) to the converter ! from pandapower.build_branch import _calc_tap_from_dataframe vn_trafo_hv, vn_trafo_lv, shift = _calc_tap_from_dataframe( pp_net, pp_net.trafo) tap_angles = np.deg2rad(trafo_bug["tap_step_degree"].values) if ~np.isfinite(tap_angles): tap_angles = 0. vn = trafo_bug["vn_lv_kv"].values u1 = vn tap_steps = (trafo_bug["tap_pos"].values - trafo_bug["tap_neutral"].values ) * trafo_bug["tap_step_percent"].values / 100. du = u1 * tap_steps np.sqrt((u1 + du * np.cos(tap_angles))**2 + (du * np.sin(tap_angles))**2) pdb.set_trace() if False: if case_name == 'test_case2848rte.json':
def _add_trafo_sc_impedance_zero(net, ppc, trafo_df=None): if trafo_df is None: trafo_df = net["trafo"] branch_lookup = net["_pd2ppc_lookups"]["branch"] if not "trafo" in branch_lookup: return bus_lookup = net["_pd2ppc_lookups"]["bus"] mode = net["_options"]["mode"] trafo_model = net["_options"]["trafo_model"] f, t = branch_lookup["trafo"] trafo_df["_ppc_idx"] = range(f, t) bus_lookup = net["_pd2ppc_lookups"]["bus"] buses_all, gs_all, bs_all = np.array([], dtype=int), np.array([]), \ np.array([]) if not "vector_group" in trafo_df: raise ValueError( "Vector Group of transformer needs to be specified for zero \ sequence modelling \n Try : net.trafo[\"vector_group\"] = 'Dyn'" ) for vector_group, trafos in trafo_df.groupby("vector_group"): ppc_idx = trafos["_ppc_idx"].values.astype(int) ppc["branch"][ppc_idx, BR_STATUS] = 0 if vector_group in ["Yy", "Yd", "Dy", "Dd"]: continue vk_percent = trafos["vk_percent"].values.astype(float) vkr_percent = trafos["vkr_percent"].values.astype(float) sn_mva = trafos["sn_mva"].values.astype(float) # Just put pos seq parameter if zero seq parameter is zero if not "vk0_percent" in trafos: raise ValueError( "Short circuit voltage of transformer Vk0 needs to be specified for zero \ sequence modelling \n Try : net.trafo[\"vk0_percent\"] = net.trafo[\"vk_percent\"]" ) vk0_percent = trafos["vk0_percent"].values.astype(float) if \ trafos["vk0_percent"].values.astype(float).all() != 0. else \ trafos["vk_percent"].values.astype(float) # Just put pos seq parameter if zero seq parameter is zero if not "vkr0_percent" in trafos: raise ValueError( "Real part of short circuit voltage Vk0(Real) needs to be specified for transformer \ modelling \n Try : net.trafo[\"vkr0_percent\"] = net.trafo[\"vkr_percent\"]" ) vkr0_percent = trafos["vkr0_percent"].values.astype(float) if \ trafos["vkr0_percent"].values.astype(float).all() != 0. else \ trafos["vkr_percent"].values.astype(float) lv_buses = trafos["lv_bus"].values.astype(int) hv_buses = trafos["hv_bus"].values.astype(int) lv_buses_ppc = bus_lookup[lv_buses] hv_buses_ppc = bus_lookup[hv_buses] if not "mag0_percent" in trafos: # For Shell Type transformers vk0 = vk * 1 # and mag0_percent = 10 ... 100 Zm0/ Zsc0 # --pg 50 DigSilent Power Factory Transformer manual raise ValueError( "Magnetizing impedance to vk0 ratio needs to be specified for transformer \ modelling \n Try : net.trafo[\"mag0_percent\"] = 100" ) mag0_ratio = trafos.mag0_percent.values.astype(float) if not "mag0_rx" in trafos: raise ValueError( "Magnetizing impedance R/X ratio needs to be specified for transformer \ modelling \n Try : net.trafo[\"mag0_rx\"] = 0 ") mag0_rx = trafos["mag0_rx"].values.astype(float) if not "si0_hv_partial" in trafos: raise ValueError( "Zero sequence short circuit impedance partition towards HV side needs to be specified for transformer \ modelling \n Try : net.trafo[\"si0_hv_partial\"] = 0.9 " ) si0_hv_partial = trafos.si0_hv_partial.values.astype(float) parallel = trafos.parallel.values.astype(float) in_service = trafos["in_service"].astype(int) ppc["branch"][ppc_idx, F_BUS] = hv_buses_ppc ppc["branch"][ppc_idx, T_BUS] = lv_buses_ppc vn_trafo_hv, vn_trafo_lv, shift = _calc_tap_from_dataframe(net, trafos) vn_lv = ppc["bus"][lv_buses_ppc, BASE_KV] ratio = _calc_nominal_ratio_from_dataframe(ppc, trafos, vn_trafo_hv, \ vn_trafo_lv, bus_lookup) ppc["branch"][ppc_idx, TAP] = ratio ppc["branch"][ppc_idx, SHIFT] = shift # zero seq. transformer impedance tap_lv = np.square(vn_trafo_lv / vn_lv) * net.sn_mva if mode == 'pf_3ph': # ============================================================================= # Changing base from transformer base to Network base to get Zpu(Net) # Zbase = (kV).squared/S_mva # Zpu(Net)={Zpu(trafo) * Zb(trafo)} / {Zb(Net)} # Note: # Network base voltage is Line-Neutral voltage in each phase # Line-Neutral voltage= Line-Line Voltage(vn_lv) divided by sq.root(3) # ============================================================================= tap_lv = np.square(vn_trafo_lv / vn_lv) * (3 * net.sn_mva) z_sc = vk0_percent / 100. / sn_mva * tap_lv r_sc = vkr0_percent / 100. / sn_mva * tap_lv z_sc = z_sc.astype(float) r_sc = r_sc.astype(float) x_sc = np.sign(z_sc) * np.sqrt(z_sc**2 - r_sc**2) z0_k = (r_sc + x_sc * 1j) / parallel y0_k = 1 / z0_k #adding admittance for "pi" model if mode == "sc": # or trafo_model == "pi": from pandapower.shortcircuit.idx_bus import C_MAX cmax = net._ppc["bus"][lv_buses_ppc, C_MAX] kt = _transformer_correction_factor(vk_percent, vkr_percent, \ sn_mva, cmax) z0_k *= kt y0_k = 1 / z0_k # ============================================================================= # Transformer magnetising impedance for zero sequence # ============================================================================= z_m = z_sc * mag0_ratio x_m = z_m / np.sqrt(mag0_rx**2 + 1) r_m = x_m * mag0_rx r0_trafo_mag = r_m / parallel x0_trafo_mag = x_m / parallel z0_mag = r0_trafo_mag + x0_trafo_mag * 1j # ============================================================================= # Star - Delta conversion ( T model to Pi Model) # ----------- |__zc=ZAB__|----------------- # _| _| # za=ZAN|_| |_| zb=ZBN # | | # ============================================================================= z1 = si0_hv_partial * z0_k z2 = (1 - si0_hv_partial) * z0_k z3 = z0_mag z_temp = z1 * z2 + z2 * z3 + z1 * z3 za = z_temp / z2 # za = z_temp / (z2+z3) zb = z_temp / z1 # zb = z_temp / (z1+z3) zc = z_temp / z3 # ZAB Transfer impedance # zc = z_temp / (z1+z2) # ZAB Transfer impedance YAB = 1 / zc.astype(complex) YAN = 1 / za.astype(complex) YBN = 1 / zb.astype(complex) # YAB_AN = (zc + za) /(zc * za).astype(complex) # Series conn YAB and YAN # YAB_BN = (zc + zb) / (zc * zb).astype(complex) # Series conn YAB and YBN YAB_AN = 1 / (zc + za).astype(complex) # Series conn YAB and YAN YAB_BN = 1 / (zc + zb).astype(complex) # Series conn YAB and YBN if vector_group == "Dyn": buses_all = np.hstack([buses_all, lv_buses_ppc]) if trafo_model == "pi": y = y0_k # pi model else: y = (YAB + YBN).astype(complex) * int( ppc["baseMVA"]) # T model gs_all = np.hstack([gs_all, y.real * in_service]) bs_all = np.hstack([bs_all, y.imag * in_service]) elif vector_group == "YNd": buses_all = np.hstack([buses_all, hv_buses_ppc]) if trafo_model == "pi": y = y0_k # pi model else: y = (YAB_BN + YAN).astype(complex) * int( ppc["baseMVA"]) #T model gs_all = np.hstack([gs_all, y.real * in_service]) bs_all = np.hstack([bs_all, y.imag * in_service]) elif vector_group == "Yyn": buses_all = np.hstack([buses_all, lv_buses_ppc]) if trafo_model == "pi": y = 1 / (z0_mag + z0_k).astype(complex) * int( ppc["baseMVA"]) #pi model else: # y = (YAB_AN + YBN).astype(complex) * int(ppc["baseMVA"]) #T model print(YAB, YAB_AN, YAB_BN, YBN) y = (YAB + YAB_BN + YBN).astype(complex) * int( ppc["baseMVA"]) # T model gs_all = np.hstack([gs_all, y.real * in_service]) bs_all = np.hstack([bs_all, y.imag * in_service]) elif vector_group == "YNyn": ppc["branch"][ppc_idx, BR_STATUS] = in_service # zc = ZAB ppc["branch"][ppc_idx, BR_R] = zc.real ppc["branch"][ppc_idx, BR_X] = zc.imag buses_all = np.hstack([buses_all, hv_buses_ppc]) gs_all = np.hstack([gs_all, YAN.real * in_service \ * int(ppc["baseMVA"])]) bs_all = np.hstack([bs_all, YAN.imag * in_service \ * int(ppc["baseMVA"])]) buses_all = np.hstack([buses_all, lv_buses_ppc]) gs_all = np.hstack([gs_all, YBN.real * in_service \ * int(ppc["baseMVA"])]) bs_all = np.hstack([bs_all, YBN.imag * in_service \ * int(ppc["baseMVA"])]) elif vector_group == "YNy": buses_all = np.hstack([buses_all, hv_buses_ppc]) if trafo_model == "pi": y = 1 / (z0_mag + z0_k).astype(complex) * int( ppc["baseMVA"]) #pi model else: y = (YAB_BN + YAN).astype(complex) * int( ppc["baseMVA"]) #T model gs_all = np.hstack([gs_all, y.real * in_service]) bs_all = np.hstack([bs_all, y.imag * in_service]) elif vector_group == "Yzn": buses_all = np.hstack([buses_all, lv_buses_ppc]) # y = 1/(z0_mag+z0_k).astype(complex)* int(ppc["baseMVA"])#T model # y= (za+zb+zc)/((za+zc)*zb).astype(complex)* int(ppc["baseMVA"])#pi model y = (YAB_AN + YBN).astype(complex) * int(ppc["baseMVA"]) #T model gs_all = np.hstack([gs_all, (1.1547) * y.real * in_service \ * int(ppc["baseMVA"])]) bs_all = np.hstack([bs_all, (1.1547) * y.imag * in_service \ * int(ppc["baseMVA"])]) elif vector_group[-1].isdigit(): raise ValueError("Unknown transformer vector group %s -\ please specify vector group without \ phase shift number. Phase shift can be \ specified in net.trafo.shift_degree" % vector_group) else: raise ValueError("Transformer vector group %s is unknown\ / not implemented for three phase load flow" % vector_group) buses, gs, bs = aux._sum_by_group(buses_all, gs_all, bs_all) ppc["bus"][buses, GS] += gs ppc["bus"][buses, BS] += bs del net.trafo["_ppc_idx"]
def _aux_test(self, pn_net): with tempfile.TemporaryDirectory() as path: case_name = os.path.join(path, "this_case.json") pp.to_json(pn_net, case_name) real_init_file = pp.from_json(case_name) backend = LightSimBackend() with warnings.catch_warnings(): warnings.filterwarnings("ignore") backend.load_grid(case_name) nb_sub = backend.n_sub pp_net = backend.init_pp_backend._grid # first i deactivate all slack bus in pp that are connected but not handled in ls pp_net.ext_grid["in_service"].loc[:] = False pp_net.ext_grid["in_service"].iloc[0] = True conv = backend.runpf() conv_pp = backend.init_pp_backend.runpf() assert conv_pp, "Error: pandapower do not converge, impossible to perform the necessary checks" assert conv, "Error: lightsim do not converge" por_pp, qor_pp, vor_pp, aor_pp = copy.deepcopy( backend.init_pp_backend.lines_or_info()) pex_pp, qex_pp, vex_pp, aex_pp = copy.deepcopy( backend.init_pp_backend.lines_ex_info()) # I- Check for divergence and equality of flows" por_ls, qor_ls, vor_ls, aor_ls = backend.lines_or_info() max_mis = np.max(np.abs(por_ls - por_pp)) assert max_mis <= self.tol, f"Error: por do not match, maximum absolute error is {max_mis:.5f} MW" max_mis = np.max(np.abs(qor_ls - qor_pp)) assert max_mis <= self.tol, f"Error: qor do not match, maximum absolute error is {max_mis:.5f} MVAr" max_mis = np.max(np.abs(vor_ls - vor_pp)) assert max_mis <= self.tol, f"Error: vor do not match, maximum absolute error is {max_mis:.5f} kV" max_mis = np.max(np.abs(aor_ls - aor_pp)) assert max_mis <= self.tol, f"Error: aor do not match, maximum absolute error is {max_mis:.5f} A" # "II - Check for possible solver issues" with warnings.catch_warnings(): warnings.filterwarnings("ignore") pp.runpp(backend.init_pp_backend._grid, v_debug=True) v_tmp = backend.init_pp_backend._grid.res_bus[ "vm_pu"].values[:nb_sub] + 0j v_tmp *= np.exp( 1j * np.pi / 180. * backend.init_pp_backend._grid.res_bus["va_degree"].values[:nb_sub]) v_tmp = np.concatenate((v_tmp, v_tmp)) backend._grid.ac_pf(v_tmp, 1000, 1e-5) Y_pp = backend.init_pp_backend._grid._ppc["internal"]["Ybus"] Sbus = backend.init_pp_backend._grid._ppc["internal"]["Sbus"] pv_ = backend.init_pp_backend._grid._ppc["internal"]["pv"] pq_ = backend.init_pp_backend._grid._ppc["internal"]["pq"] max_iter = 10 tol_this = 1e-8 All_Vms = backend.init_pp_backend._grid._ppc["internal"]["Vm_it"] AllVas = backend.init_pp_backend._grid._ppc["internal"]["Va_it"] for index_V in range(All_Vms.shape[1] - 1, -1, -1): nb_iter = All_Vms.shape[1] - 1 # i check from easiest to hardest, so from the last iteartion of pandapower to the first iteration of pandapower # take the same V as pandapower V_init = All_Vms[:, index_V] * (np.cos(AllVas[:, index_V]) + 1j * np.sin(AllVas[:, index_V])) # V_init *= np.exp(1j * AllVas[:, 0]) V_init_ref = copy.deepcopy(V_init) solver = ClassSolver() solver.solve(scipy.sparse.csc_matrix(Y_pp), V_init, Sbus, pv_, pq_, max_iter, tol_this) time_for_nr = solver.get_timers()[3] if TIMER_INFO: print( f"\t Info: Time to perform {nb_iter - index_V} NR iterations for a grid with {nb_sub} " f"buses: {1000. * time_for_nr:.2f}ms") error_va = np.abs( solver.get_Va() - np.angle(backend.init_pp_backend._grid._ppc["internal"]["V"])) assert np.max(error_va) <= self.tol, f"Error: VA do not match for iteration {index_V}, maximum absolute " \ f"error is {np.max(error_va):.5f} rad" error_vm = np.abs( np.abs(solver.get_Vm() - np.abs( backend.init_pp_backend._grid._ppc["internal"]["V"]))) assert np.max(error_vm) <= self.tol, f"\t Error: VM do not match for iteration {index_V}, maximum absolute " \ f"error is {np.max(error_vm):.5f} pu" solver.reset() if TIMER_INFO: print("") # 'III - Check the data conversion' pp_vect_converter = backend.init_pp_backend._grid._pd2ppc_lookups[ "bus"][:nb_sub] pp_net = backend.init_pp_backend._grid # 1) Checking Sbus conversion Sbus_pp = backend.init_pp_backend._grid._ppc["internal"]["Sbus"] Sbus_pp_right_order = Sbus_pp[pp_vect_converter] Sbus_me = backend._grid.get_Sbus() error_p = np.abs(np.real(Sbus_me) - np.real(Sbus_pp_right_order)) assert np.max(error_p) <= self.tol, f"\t Error: P do not match for Sbus, maximum absolute error is " \ f"{np.max(error_p):.5f} MW, \t Error: significative difference for bus " \ f"index (lightsim): {np.where(error_p > self.tol)[0]}" error_q = np.abs(np.imag(Sbus_me) - np.imag(Sbus_pp_right_order)) assert np.max(error_q) <= self.tol, f"\t Error: Q do not match for Sbus, maximum absolute error is " \ f"{np.max(error_q):.5f} MVAr, \t Error: significative difference for bus " \ f"index (lightsim): {np.where(error_q > self.tol)[0]}" # 2) Checking Ybus conversion" Y_me = backend._grid.get_Ybus() Y_pp = backend.init_pp_backend._grid._ppc["internal"]["Ybus"] Y_pp_right_order = Y_pp[pp_vect_converter.reshape(nb_sub, 1), pp_vect_converter.reshape(1, nb_sub)] error_p = np.abs(np.real(Y_me) - np.real(Y_pp_right_order)) assert np.max(error_p) <= self.tol, f"Error: P do not match for Ybus, maximum absolute error " \ f"is {np.max(error_p):.5f}" error_q = np.abs(np.imag(Y_me) - np.imag(Y_pp_right_order)) assert np.max(error_q) <= self.tol, f"\t Error: Q do not match for Ybus, maximum absolute error is " \ f"{np.max(error_q):.5f}" # "IV - Check for the initialization (dc powerflow)" # 1) check that the results are same for dc lightsim and dc pandapower Vinit = np.ones(backend.nb_bus_total, dtype=np.complex_) * pp_net["_options"]["init_vm_pu"] backend._grid.deactivate_result_computation() Vdc = backend._grid.dc_pf(Vinit, max_iter, tol_this) backend._grid.reactivate_result_computation() Ydc_me = backend._grid.get_Ybus() Sdc_me = backend._grid.get_Sbus() assert np.max(np.abs(V_init_ref[pp_vect_converter] - Vdc[:nb_sub])) <= 100.*self.tol,\ f"\t Error for the DC approximation: resulting voltages are different " \ f"{np.max(np.abs(V_init_ref[pp_vect_converter] - Vdc[:nb_sub])):.5f}pu" if np.max(np.abs(V_init_ref[pp_vect_converter] - Vdc[:nb_sub])) >= self.tol: warnings.warn( "\t Warning: maximum difference after DC approximation is " "{np.max(np.abs(V_init_ref[pp_vect_converter] - Vdc[:nb_sub])):.5f} which is higher than " "the tolerance (this is just a warning because we noticed this could happen even if the " "results match perfectly. Probably some conversion issue with complex number and " "radian / degree.") # "2) check that the Sbus vector is same for PP and lightisim in DC" from pandapower.pd2ppc import _pd2ppc from pandapower.pf.run_newton_raphson_pf import _get_pf_variables_from_ppci from pandapower.pypower.idx_brch import F_BUS, T_BUS, BR_X, TAP, SHIFT, BR_STATUS from pandapower.pypower.idx_bus import VA, GS from pandapower.pypower.makeBdc import makeBdc from pandapower.pypower.makeSbus import makeSbus pp_net._pd2ppc_lookups = { "bus": np.array([], dtype=int), "ext_grid": np.array([], dtype=int), "gen": np.array([], dtype=int), "branch": np.array([], dtype=int) } # convert pandapower net to ppc ppc, ppci = _pd2ppc(pp_net) baseMVA, bus, gen, branch, ref, pv, pq, on, gbus, _, refgen = _get_pf_variables_from_ppci( ppci) Va0 = bus[:, VA] * (np.pi / 180.) B, Bf, Pbusinj, Pfinj = makeBdc(bus, branch) Pbus = makeSbus(baseMVA, bus, gen) - Pbusinj - bus[:, GS] / baseMVA Pbus_pp_ro = Pbus[pp_vect_converter] error_p = np.abs(np.real(Sdc_me) - np.real(Pbus_pp_ro)) test_ok = True #### pandapower DC algo (yet another one) Va = copy.deepcopy(Va0) pvpq = np.r_[pv, pq] pvpq_matrix = B[pvpq.T, :].tocsc()[:, pvpq] ref_matrix = np.transpose(Pbus[pvpq] - B[pvpq.T, :].tocsc()[:, ref] * Va0[ref]) Va[pvpq] = np.real(scipy.sparse.linalg.spsolve(pvpq_matrix, ref_matrix)) #### assert np.max(error_p) <= self.tol, f"\t Error: P do not match for Sbus (dc), maximum absolute error is " \ f"{np.max(error_p):.5f} MW, \nError: significative difference for bus " \ f"index (lightsim): {np.where(error_p > self.tol)[0]}" error_q = np.abs(np.imag(Sdc_me) - np.imag(Pbus_pp_ro)) assert np.max(error_q) <= self.tol, f"\t Error: Q do not match for Sbus (dc), maximum absolute error is " \ f"{np.max(error_q):.5f} MVAr, \n\t Error: significative difference for " \ f"bus index (lightsim): {np.where(error_q > self.tol)[0]}" # "3) check that the Ybus matrix is same for PP and lightisim in DC" with warnings.catch_warnings(): warnings.filterwarnings("ignore") pp.rundcpp(pp_net) Ydc_pp = backend.init_pp_backend._grid._ppc["internal"]["Bbus"] Ydc_pp_right_order = Ydc_pp[pp_vect_converter.reshape(nb_sub, 1), pp_vect_converter.reshape(1, nb_sub)] error_p = np.abs(np.real(Ydc_me) - np.real(Ydc_pp_right_order)) assert np.max(error_p) <= self.tol, f"Error: P do not match for Ybus (dc mode), maximum absolute error " \ f"is {np.max(error_p):.5f}" error_q = np.abs(np.imag(Ydc_me) - np.imag(Ydc_pp_right_order)) assert np.max(error_q) <= self.tol, f"\t Error: Q do not match for Ybus (dc mdoe), maximum absolute error " \ f"is {np.max(error_q):.5f}" # "3) check that lightsim ac pf init with pp dc pf give same results (than pp)" Vinit = np.ones(backend.nb_bus_total, dtype=np.complex_) * pp_net["_options"]["init_vm_pu"] Vinit[:nb_sub] = V_init_ref[pp_vect_converter] conv = backend._grid.ac_pf(Vinit, max_iter, tol_this) assert conv.shape[ 0] > 0, "\t Error: the lightsim diverge when initialized with pandapower Vinit_dc" lpor, lqor, lvor, laor = backend._grid.get_lineor_res() tpor, tqor, tvor, taor = backend._grid.get_trafohv_res() tpex, tqex, tvex, taex = backend._grid.get_trafolv_res() nb_trafo = tpor.shape[0] nb_powerline = lpor.shape[0] p_or_me2 = np.concatenate((lpor, tpor)) q_or_me2 = np.concatenate((lqor, tqor)) v_or_me2 = np.concatenate((lvor, tvor)) a_or_me2 = 1000. * np.concatenate((laor, taor)) test_ok = True # pdb.set_trace() max_mis = np.max(np.abs(p_or_me2 - por_pp)) assert np.max( error_q ) <= self.tol, f"\t Error: por do not match, maximum absolute error is {max_mis:.5f} MW" max_mis = np.max(np.abs(q_or_me2 - qor_pp)) assert np.max( error_q ) <= self.tol, f"\t Error: qor do not match, maximum absolute error is {max_mis:.5f} MVAr" max_mis = np.max(np.abs(v_or_me2 - vor_pp)) assert np.max( error_q ) <= self.tol, f"\t Error: vor do not match, maximum absolute error is {max_mis:.5f} kV" max_mis = np.max(np.abs(a_or_me2 - aor_pp)) assert np.max( error_q ) <= self.tol, f"\t Error: aor do not match, maximum absolute error is {max_mis:.5f} A" # "V - Check trafo proper conversion to r,x, b" from lightsim2grid_cpp import GridModel, PandaPowerConverter, SolverType from pandapower.build_branch import _calc_branch_values_from_trafo_df, get_trafo_values from pandapower.build_branch import _calc_nominal_ratio_from_dataframe, _calc_r_x_y_from_dataframe from pandapower.build_branch import _calc_tap_from_dataframe, BASE_KV, _calc_r_x_from_dataframe # my trafo parameters converter = PandaPowerConverter() converter.set_sn_mva(pp_net.sn_mva) converter.set_f_hz(pp_net.f_hz) tap_neutral = 1.0 * pp_net.trafo["tap_neutral"].values tap_neutral[~np.isfinite(tap_neutral)] = 0. if np.any(tap_neutral != 0.): raise RuntimeError( "lightsim converter supposes that tap_neutral is 0 for the transformers" ) tap_step_pct = 1.0 * pp_net.trafo["tap_step_percent"].values tap_step_pct[~np.isfinite(tap_step_pct)] = 0. tap_pos = 1.0 * pp_net.trafo["tap_pos"].values tap_pos[~np.isfinite(tap_pos)] = 0. shift_ = 1.0 * pp_net.trafo["shift_degree"].values shift_[~np.isfinite(shift_)] = 0. is_tap_hv_side = pp_net.trafo["tap_side"].values == "hv" is_tap_hv_side[~np.isfinite(is_tap_hv_side)] = True if np.any(pp_net.trafo["tap_phase_shifter"].values): raise RuntimeError( "ideal phase shifter are not modeled. Please remove all trafo with " "pp_net.trafo[\"tap_phase_shifter\"] set to True.") tap_angles_ = 1.0 * pp_net.trafo["tap_step_degree"].values tap_angles_[~np.isfinite(tap_angles_)] = 0. tap_angles_ = np.deg2rad(tap_angles_) trafo_r, trafo_x, trafo_b = \ converter.get_trafo_param(tap_step_pct, tap_pos, tap_angles_, # in radian ! is_tap_hv_side, pp_net.bus.loc[pp_net.trafo["hv_bus"]]["vn_kv"], pp_net.bus.loc[pp_net.trafo["lv_bus"]]["vn_kv"], pp_net.trafo["vk_percent"].values, pp_net.trafo["vkr_percent"].values, pp_net.trafo["sn_mva"].values, pp_net.trafo["pfe_kw"].values, pp_net.trafo["i0_percent"].values, ) # pandapower trafo parameters ppc = copy.deepcopy(pp_net._ppc) bus_lookup = pp_net["_pd2ppc_lookups"]["bus"] trafo_df = pp_net["trafo"] lv_bus = get_trafo_values(trafo_df, "lv_bus") vn_lv = ppc["bus"][bus_lookup[lv_bus], BASE_KV] vn_trafo_hv, vn_trafo_lv, shift_pp = _calc_tap_from_dataframe( pp_net, trafo_df) ratio = _calc_nominal_ratio_from_dataframe(ppc, trafo_df, vn_trafo_hv, vn_trafo_lv, bus_lookup) r_t, x_t, b_t = _calc_r_x_y_from_dataframe(pp_net, trafo_df, vn_trafo_lv, vn_lv, pp_net.sn_mva) # check where there are mismatch if any val_r_pp = r_t val_r_me = trafo_r all_equals_r = np.abs(val_r_pp - val_r_me) <= self.tol if not np.all(all_equals_r): test_ok = False print( f"\t Error: some trafo resistance are not equal, max error: {np.max(np.abs(val_r_pp - val_r_me)):.5f}" ) val_x_pp = x_t val_x_me = trafo_x all_equals_x = np.abs(val_x_pp - val_x_me) <= self.tol assert np.all(all_equals_x), f"\t Error: some trafo x are not equal, max error: " \ f"{np.max(np.abs(val_x_pp - val_x_me)):.5f}" val_ib_pp = np.imag(b_t) val_ib_me = np.imag(trafo_b) all_equals_imag_b = np.abs(val_ib_pp - val_ib_me) <= self.tol assert np.all(all_equals_imag_b), f"\t Error: some trafo (imag) b are not equal, max error: " \ f"{np.max(np.abs(val_ib_pp - val_ib_me)):.5f}" val_reb_pp = np.real(b_t) val_reb_me = np.real(trafo_b) all_equals_real_b = np.abs(val_reb_pp - val_reb_me) <= self.tol assert np.all(all_equals_real_b), f"\t Error: some trafo (real) b are not equal, max error: " \ f"{np.max(np.abs(val_reb_pp - val_reb_me)):.5f}"