diff --git a/pandapower/pd2ppc.py b/pandapower/pd2ppc.py index cba942b48..275dfa4a7 100644 --- a/pandapower/pd2ppc.py +++ b/pandapower/pd2ppc.py @@ -3,24 +3,17 @@ # Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. -import copy import numpy as np -import math import pandapower.auxiliary as aux -import warnings -from pandapower.build_branch import _initialize_branch_lookup,\ -_build_branch_ppc, _switch_branches, _branches_with_oos_buses,\ -_calc_tap_from_dataframe,_calc_nominal_ratio_from_dataframe,\ -_transformer_correction_factor +from pandapower.build_branch import _switch_branches, _branches_with_oos_buses, _build_branch_ppc from pandapower.build_bus import _build_bus_ppc, _calc_pq_elements_and_add_on_ppc, \ _calc_shunts_and_add_on_ppc, _add_gen_impedances_ppc, _add_motor_impedances_ppc from pandapower.build_gen import _build_gen_ppc, _check_voltage_setpoints_at_same_bus, \ _check_voltage_angles_at_same_bus, _check_for_reference_bus from pandapower.opf.make_objective import _make_objective from pandapower.pypower.idx_area import PRICE_REF_BUS -from pandapower.pypower.idx_brch import F_BUS, T_BUS, BR_STATUS, branch_cols, \ - TAP, SHIFT, BR_R, BR_X, BR_B -from pandapower.pypower.idx_bus import NONE, BUS_I, BUS_TYPE, BASE_KV, GS, BS +from pandapower.pypower.idx_brch import F_BUS, T_BUS, BR_STATUS +from pandapower.pypower.idx_bus import NONE, BUS_I, BUS_TYPE from pandapower.pypower.idx_gen import GEN_BUS, GEN_STATUS from pandapower.pypower.run_userfcn import run_userfcn @@ -78,6 +71,7 @@ def _pd2ppc(net, sequence=None): # generate ppc['bus'] and the bus lookup _build_bus_ppc(net, ppc) if sequence == 0: + from pandapower.pd2ppc_zero import _add_ext_grid_sc_impedance_zero, _build_branch_ppc_zero # Adds external grid impedance for 3ph and sc calculations in ppc0 _add_ext_grid_sc_impedance_zero(net, ppc) # Calculates ppc0 branch impedances from branch elements @@ -311,346 +305,4 @@ def _init_lookup(net, lookup_name, pandapower_index, ppc_index): # update lookup lookup[pandapower_index] = ppc_index - aux._write_lookup_to_net(net, lookup_name, lookup) - - -def _build_branch_ppc_zero(net, ppc): - """ - Takes an empty ppc0 and puts zero sequence branch impedances. The branch - datatype will be np.complex 128 afterwards. - - .. note:: The order of branches in the ppc is: - 1. Lines - 2. Transformers - - **INPUT**: - **net** -The pandapower format network - - **ppc** - The PYPOWER format network to fill in values - - """ - length = _initialize_branch_lookup(net) - lookup = net._pd2ppc_lookups["branch"] - mode = net._options["mode"] - ppc["branch"] = np.zeros(shape=(length, branch_cols), dtype=np.complex128) - if mode == "sc": - from pandapower.shortcircuit.idx_brch import branch_cols_sc - branch_sc = np.empty(shape=(length, branch_cols_sc), dtype=float) - branch_sc.fill(np.nan) - ppc["branch"] = np.hstack((ppc["branch"], branch_sc)) - ppc["branch"][:, :13] = np.array([0, 0, 0, 0, 0, 250, 250, 250, 1, 0, \ - 1, -360, 360]) - # Adds zero sequence impedances of lines in ppc0 - _add_line_sc_impedance_zero(net, ppc) - # Adds zero sequence impedances of transformers in ppc0 - _add_trafo_sc_impedance_zero(net, ppc) - if "trafo3w" in lookup: - raise NotImplementedError("Three winding transformers are not \ - implemented for unbalanced calculations") - - -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([]) - 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 - 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 --- No longer needed since we are using Pi model - # ============================================================================= - # 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 - zb = z_temp / z1 - zc = z_temp / z3 # ZAB Transfer impedance - YAB = 1 / zc.astype(complex) - YAN = 1 / za.astype(complex) - YBN = 1 / zb.astype(complex) - 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]) - - y = (YAB + YBN).astype(complex) * int(ppc["baseMVA"]) # pi 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]) - - # gs_all = np.hstack([gs_all, y0_k.real*in_service])#T model - # bs_all = np.hstack([bs_all, y0_k.imag*in_service]) - - y = (YAB_BN + YAN).astype(complex) * int(ppc["baseMVA"]) # pi 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]) - # y = 1/(z0_mag+z0_k).astype(complex)* int(ppc["baseMVA"])#T model - - y = (YAB_AN + YBN).astype(complex) * int(ppc["baseMVA"]) # pi 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]) - # y = 1/(z0_mag+z0_k).astype(complex)* int(ppc["baseMVA"])#T model - y = (YAB_BN + YAN).astype(complex) * int(ppc["baseMVA"]) # pi 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"]) # pi 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 _add_ext_grid_sc_impedance_zero(net, ppc): - mode = net["_options"]["mode"] - - if mode == "sc": - from pandapower.shortcircuit.idx_bus import C_MAX, C_MIN - case = net._options["case"] - else: - case = "max" - bus_lookup = net["_pd2ppc_lookups"]["bus"] - eg = net["ext_grid"][net._is_elements["ext_grid"]] - if len(eg) == 0: - return - eg_buses = eg.bus.values - eg_buses_ppc = bus_lookup[eg_buses] - - if mode == "sc": - c = ppc["bus"][eg_buses_ppc, C_MAX] if case == "max" else \ - ppc["bus"][eg_buses_ppc, C_MIN] - else: - c = 1.1 - if not "s_sc_%s_mva" % case in eg: - raise ValueError("Short Circuit apparent power s_sc_%s_mva for zero sequence needs to be specified for "% case + - "external grid \n Try: net.ext_grid['s_sc_max_mva'] = 1000" ) - else: - s_sc = eg["s_sc_%s_mva" % case].values/ppc['baseMVA'] - if not "rx_%s" % case in eg: - raise ValueError("Positive sequence short circuit R/X rate rx_%s needs to be specified\ - for external grid \n Try: net.ext_grid['rx_max'] = 0.1" % case) - rx = eg["rx_%s" % case].values - z_grid = c / s_sc - if mode == 'pf_3ph': - z_grid = c / (s_sc / 3) - x_grid = z_grid / np.sqrt(rx ** 2 + 1) - r_grid = rx * x_grid - eg["r"] = r_grid - eg["x"] = x_grid - - if not "x0x_%s" % case in eg: - raise ValueError("Zero sequence to positive sequence reactance ratio x0x_%s needs to be specified for "% case + - "external grid \n Try : net.ext_grid[\"x0x_max\"] = 1.0" ) - if not "r0x0_%s" % case in eg: - raise ValueError("Zero sequence short circuit R0/X0 rate r0x0_%s needs to be specified\ - for external grid \n Try : net.ext_grid[\"r0x0_max\"] = 0.1" % case ) - - # ext_grid zero sequence impedance - if case == "max": - x0_grid = net.ext_grid["x0x_%s" % case] * x_grid - r0_grid = net.ext_grid["r0x0_%s" % case] * x0_grid - elif case == "min": - x0_grid = net.ext_grid["x0x_%s" % case] * x_grid - r0_grid = net.ext_grid["r0x0_%s" % case] * x0_grid - y0_grid = 1 / (r0_grid + x0_grid * 1j) - buses, gs, bs = aux._sum_by_group(eg_buses_ppc, np.real(np.array(y0_grid)), np.imag(np.array(y0_grid))) - ppc["bus"][buses, GS] = gs * ppc['baseMVA'] - ppc["bus"][buses, BS] = bs * ppc['baseMVA'] - -def _add_line_sc_impedance_zero(net, ppc): - branch_lookup = net["_pd2ppc_lookups"]["branch"] - mode = net["_options"]["mode"] - if not "line" in branch_lookup: - return -# line = net["line"].loc[net._is_elements["line_is_idx"].values.tolist()] - line = net["line"] - bus_lookup = net["_pd2ppc_lookups"]["bus"] - length = line["length_km"].values - parallel = line["parallel"].values - - fb = bus_lookup[line["from_bus"].values] - tb = bus_lookup[line["to_bus"].values] - baseR = np.square(ppc["bus"][fb, BASE_KV]) / net.sn_mva - if mode == 'pf_3ph': - baseR = np.square(ppc["bus"][fb, BASE_KV]) / (3 * net.sn_mva) - f, t = branch_lookup["line"] - # line zero sequence impedance - ppc["branch"][f:t, F_BUS] = fb - ppc["branch"][f:t, T_BUS] = tb - # Just putting pos seq resistance if zero seq resistance is zero - if not "r0_ohm_per_km" in line: - raise ValueError("Zero sequence resistance R0 needs to be specified\ - for line \n Try : net.line[\"r0_ohm_per_km\"] = net.line[\"r_ohm_per_km\"].values * 4" ) - ppc["branch"][f:t, BR_R] = line["r0_ohm_per_km"].values * length / baseR / parallel if \ - line["r0_ohm_per_km"].values.all() != 0 else \ - line["r_ohm_per_km"].values * 4 * length / baseR / parallel - # Just putting pos seq inducatance if zero seq inductance is zero - if not "x0_ohm_per_km" in line: - raise ValueError("Zero sequence reactance X0 needs to be specified\ - for line \n Try : net.line[\"x0_ohm_per_km\"] = net.line[\"x_ohm_per_km\"].values * 4" ) - ppc["branch"][f:t, BR_X] = \ - line["x0_ohm_per_km"].values * length / baseR / parallel if \ - line["x0_ohm_per_km"].values.all() != 0 else \ - line["x_ohm_per_km"].values * 4 * length / baseR / parallel - # Just putting pos seq capacitance if zero seq capacitance is zero - if not "c0_nf_per_km" in line: - raise ValueError("Zero sequence capacitance C0 needs to be specified\ - for line \n Try : net.line[\"c0_nf_per_km\"] = net.line[\"c_nf_per_km\"].values * 0.25" ) - ppc["branch"][f:t, BR_B] = \ - (2 * net["f_hz"] * math.pi * line["c0_nf_per_km"].values * 1e-9 * baseR \ - * length * parallel) if \ - line["c0_nf_per_km"].values.all() != 0 else \ - (2 * net["f_hz"] * math.pi * (line["c_nf_per_km"].values * 0.25) * 1e-9 \ - * baseR * length * parallel) - ppc["branch"][f:t, BR_STATUS] = line["in_service"].astype(int) \ No newline at end of file + aux._write_lookup_to_net(net, lookup_name, lookup) \ No newline at end of file diff --git a/pandapower/pd2ppc_zero.py b/pandapower/pd2ppc_zero.py index a850efbf5..0609629b9 100644 --- a/pandapower/pd2ppc_zero.py +++ b/pandapower/pd2ppc_zero.py @@ -5,20 +5,18 @@ import math import numpy as np -import copy -import pandas as pd -from packaging import version import pandapower.auxiliary as aux -from pandapower.pd2ppc import _init_ppc from pandapower.build_bus import _build_bus_ppc from pandapower.build_gen import _build_gen_ppc -from pandapower.pd2ppc import _ppc2ppci +#from pandapower.pd2ppc import _ppc2ppci, _init_ppc from pandapower.pypower.idx_brch import BR_B, BR_R, BR_X, F_BUS, T_BUS, branch_cols, BR_STATUS, SHIFT, TAP from pandapower.pypower.idx_bus import BASE_KV, BS, GS from pandapower.build_branch import _calc_tap_from_dataframe, _transformer_correction_factor, _calc_nominal_ratio_from_dataframe -from pandapower.build_branch import _switch_branches, _branches_with_oos_buses, _initialize_branch_lookup +from pandapower.build_branch import _switch_branches, _branches_with_oos_buses, _initialize_branch_lookup, _end_temperature_correction_factor def _pd2ppc_zero(net, sequence=0): + from pandapower.pd2ppc import _ppc2ppci, _init_ppc + """ Builds the ppc data structure for zero impedance system. Includes the impedance values of lines and transformers, but no load or generation data. @@ -77,7 +75,7 @@ def _build_branch_ppc_zero(net, ppc): _add_line_sc_impedance_zero(net, ppc) _add_trafo_sc_impedance_zero(net, ppc) if "trafo3w" in lookup: - raise NotImplemented("Three winding transformers are not implemented for unbalanced calculations") + raise NotImplementedError("Three winding transformers are not implemented for unbalanced calculations") def _add_trafo_sc_impedance_zero(net, ppc, trafo_df=None): @@ -89,9 +87,14 @@ def _add_trafo_sc_impedance_zero(net, ppc, trafo_df=None): bus_lookup = net["_pd2ppc_lookups"]["bus"] mode = net["_options"]["mode"] f, t = branch_lookup["trafo"] - trafo_df["_ppc_idx"] = range(f,t) + 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([]) + 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 @@ -102,14 +105,38 @@ def _add_trafo_sc_impedance_zero(net, ppc, trafo_df=None): 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) + # 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) @@ -119,86 +146,138 @@ def _add_trafo_sc_impedance_zero(net, ppc, trafo_df=None): 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) + 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 + tap_lv = np.square(vn_trafo_lv / vn_lv) * net.sn_mva if mode == 'pf_3ph': - tap_lv = np.square(vn_trafo_lv / vn_lv) * (3*net.sn_mva) # adjust for low voltage side voltage converter - + # ============================================================================= + # 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) + 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) + kt = _transformer_correction_factor(vk_percent, vkr_percent, \ + sn_mva, cmax) z0_k *= kt - y0_k = 1 / z0_k - # zero sequence transformer magnetising impedance + 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) + 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 + zb = z_temp / z1 + zc = z_temp / z3 # ZAB Transfer impedance + YAB = 1 / zc.astype(complex) + YAN = 1 / za.astype(complex) + YBN = 1 / zb.astype(complex) + 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]) - gs_all = np.hstack([gs_all, y0_k.real*in_service* int(ppc["baseMVA"])]) - bs_all = np.hstack([bs_all, y0_k.imag*in_service* int(ppc["baseMVA"])]) + if mode == "sc": + 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]) - gs_all = np.hstack([gs_all, y0_k.real*in_service* int(ppc["baseMVA"])]) - bs_all = np.hstack([bs_all, y0_k.imag*in_service* int(ppc["baseMVA"])]) + if mode == "sc": + 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]) - y = 1/(z0_mag+z0_k).astype(complex)* int(ppc["baseMVA"]) - gs_all = np.hstack([gs_all, y.real*in_service]) - bs_all = np.hstack([bs_all, y.imag*in_service]) + if mode == "sc": + 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 + 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 - ya = 1/za - yb = 1/zb + # 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, ya.real * in_service * int(ppc["baseMVA"])]) - bs_all = np.hstack([bs_all, ya.imag * in_service * int(ppc["baseMVA"])]) - - + 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, yb.real * in_service * int(ppc["baseMVA"])]) - bs_all = np.hstack([bs_all, yb.imag * in_service * int(ppc["baseMVA"])]) + 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]) - y = 1 / (z0_mag+z0_k).astype(complex)* int(ppc["baseMVA"]) - gs_all = np.hstack([gs_all, y.real*in_service]) - bs_all = np.hstack([bs_all, y.imag*in_service]) + if mode == "sc": + y = 1/(z0_mag+z0_k).astype(complex)* int(ppc["baseMVA"])#pi model + else: + y = (YAB_BN + YAN).astype(complex) * int(ppc["baseMVA"]) # pi 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"]) # pi 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) + 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) + 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 @@ -275,6 +354,10 @@ def _add_line_sc_impedance_zero(net, ppc): ppc["branch"][f:t, F_BUS] = fb ppc["branch"][f:t, T_BUS] = tb ppc["branch"][f:t, BR_R] = line["r0_ohm_per_km"].values * length / baseR / parallel + if mode == "sc": + # temperature correction + if net["_options"]["case"] == "min": + ppc["branch"][f:t, BR_R] *= _end_temperature_correction_factor(net, short_circuit=True) ppc["branch"][f:t, BR_X] = line["x0_ohm_per_km"].values * length / baseR / parallel ppc["branch"][f:t, BR_B] = (2 * net["f_hz"] * math.pi * line["c0_nf_per_km"].values * 1e-9 * baseR * length * parallel) ppc["branch"][f:t, BR_STATUS] = line["in_service"].astype(int) \ No newline at end of file diff --git a/pandapower/shortcircuit/calc_sc.py b/pandapower/shortcircuit/calc_sc.py index e0995a088..3225c85d7 100644 --- a/pandapower/shortcircuit/calc_sc.py +++ b/pandapower/shortcircuit/calc_sc.py @@ -125,8 +125,6 @@ def calc_sc(net, fault="3ph", case='max', lv_tol_percent=10, topology="auto", ip if fault == "2ph": _calc_sc(net) if fault == "1ph": - if case == "min": - raise NotImplementedError("Minimum 1ph short-circuits are not yet implemented") _calc_sc_1ph(net) diff --git a/pandapower/std_types.py b/pandapower/std_types.py index d1d013e2b..934eca644 100644 --- a/pandapower/std_types.py +++ b/pandapower/std_types.py @@ -334,6 +334,7 @@ def add_zero_impedance_parameters(net): parameter_from_std_type(net, "c0_nf_per_km") parameter_from_std_type(net, "r0_ohm_per_km") parameter_from_std_type(net, "x0_ohm_per_km") + parameter_from_std_type(net, "endtemp_degree") def add_temperature_coefficient(net, fill=None): diff --git a/pandapower/test/shortcircuit/test_1ph.py b/pandapower/test/shortcircuit/test_1ph.py index 5d3865dd8..782b4786e 100644 --- a/pandapower/test/shortcircuit/test_1ph.py +++ b/pandapower/test/shortcircuit/test_1ph.py @@ -6,7 +6,6 @@ import pandapower as pp import pandapower.shortcircuit as sc import pandapower.test -import copy import numpy as np import os import pytest @@ -24,10 +23,13 @@ def add_network(net, vector_group): b4 = pp.create_bus(net, 20, zone=vector_group) pp.create_bus(net, 20) - pp.create_ext_grid(net, b1, s_sc_max_mva=100, s_sc_min_mva=80, rx_min=0.20, rx_max=0.35) + pp.create_ext_grid(net, b1, s_sc_max_mva=100, s_sc_min_mva=100, rx_min=0.35, rx_max=0.35) net.ext_grid["r0x0_max"] = 0.4 net.ext_grid["x0x_max"] = 1.0 - + + net.ext_grid["r0x0_min"] = 0.4 + net.ext_grid["x0x_min"] = 1.0 + pp.create_std_type(net, {"r_ohm_per_km": 0.122, "x_ohm_per_km": 0.112, "c_nf_per_km": 304, "max_i_ka": 0.421, "endtemp_degree": 70.0, "r0_ohm_per_km": 0.244, "x0_ohm_per_km": 0.336, "c0_nf_per_km": 2000}, "unsymmetric_line_type") @@ -62,6 +64,7 @@ def test_1ph_shortcircuit(): ,"Dyn": [0.52209347337, 3.5054043285, 2.1086590382, 1.2980120038 ] ,"Dd": [0.52209347337, 0.74400073149, 0.74563682772, 0.81607276962] } + net = pp.create_empty_network() for vc in results.keys(): add_network(net, vc) @@ -73,6 +76,30 @@ def test_1ph_shortcircuit(): for vc, result in results.items(): check_results(net, vc, result) +def test_1ph_shortcircuit_min(): + results = { + "Yy": [0.52209346201, 0.66632662571, 0.66756160176, 0.72517293174] + ,"Yyn": [0.52209346201, 2.4135757259, 1.545054139, 0.99373917957] + ,"Yd": [0.52209346201, 0.66632662571, 0.66756160176, 0.72517293174] + ,"YNy": [0.62316686505, 0.66632662571, 0.66756160176, 0.72517293174] + ,"YNyn":[0.620287259, 2.9155736491, 1.7561556936, 1.0807305212] + ,"YNd": [0.75434229157, 0.66632662571, 0.66756160176, 0.72517293174] + ,"Dy": [0.52209346201, 0.66632662571, 0.66756160176, 0.72517293174] + ,"Dyn": [0.52209346201, 3.4393798093, 1.9535982949, 1.1558364456] + ,"Dd": [0.52209346201, 0.66632662571, 0.66756160176, 0.72517293174] + } + + net = pp.create_empty_network() + for vc in results.keys(): + add_network(net, vc) + try: + sc.calc_sc(net, fault="1ph", case="min") + except: + raise UserWarning("Did not converge after adding transformer with vector group %s"%vc) + + for vc, result in results.items(): + check_results(net, vc, result) + def test_iec60909_example_4(): file = os.path.join(pp.pp_dir, "test", "test_files", "IEC60909-4_example.json") net = pp.from_json(file) @@ -97,4 +124,4 @@ def test_1ph_with_switches(): check_results(net, vc, [0.52209347338, 2.0620266652, 2.3255761263, 2.3066467489]) if __name__ == "__main__": - pytest.main([__file__]) + pytest.main([__file__]) \ No newline at end of file diff --git a/pandapower/test/test_files/sc_test.pfd b/pandapower/test/test_files/sc_test.pfd index ddf4606e7..c227f6817 100644 Binary files a/pandapower/test/test_files/sc_test.pfd and b/pandapower/test/test_files/sc_test.pfd differ