def __init__(self): self.solver = KLUSolver() self.converter = PandaPowerConverter() self.ppci = None self.V = None self.Ybus = None self.pv = None self.pq = None # TODO self.ppci = None self.Ybus = None self.Yf = None self.Yt = None self.bus = None self.gen = None self.branch = None self.ref = None self.pv = None self.pq = None self.ref_gens = None self.baseMVA = None raise RuntimeError("This module is deprecated.")
def setUp(self): self.converter = PandaPowerConverter() self.tol = 1e-8
class MakeTests(unittest.TestCase): def setUp(self): self.converter = PandaPowerConverter() self.tol = 1e-8 def assert_equal(self, tmp, ref): assert np.max(np.abs(tmp - ref)) <= self.tol assert np.sum(np.abs(tmp - ref)) <= tmp.shape[0] * self.tol def test_case6_data(self): net = pn.case6ww() self.converter.set_sn_mva( net.sn_mva) # TODO raise an error if not set ! self.converter.set_f_hz(net.f_hz) line_r, line_x, line_h = self.converter.get_line_param( net.line["r_ohm_per_km"].values * net.line["length_km"].values, net.line["x_ohm_per_km"].values * net.line["length_km"].values, net.line["c_nf_per_km"].values * net.line["length_km"].values, net.line["g_us_per_km"].values * net.line["length_km"].values, net.bus.loc[net.line["from_bus"]]["vn_kv"], net.bus.loc[net.line["to_bus"]]["vn_kv"]) res_r = np.array([ 0.001, 0.0005, 0.001, 0.0008, 0.0005, 0.0005, 0.001, 0.0007, 0.0012, 0.0002, 0.002 ]) res_x = np.array([ 0.002, 0.002, 0.003, 0.003, 0.0025, 0.001, 0.003, 0.002, 0.0026, 0.001, 0.004 ]) res_h = np.array([ 4. + 0.j, 4. + 0.j, 6. + 0.j, 6. + 0.j, 6. + 0.j, 2. + 0.j, 4. + 0.j, 5. + 0.j, 5. + 0.j, 2. + 0.j, 8. + 0.j ]) self.assert_equal(line_r, res_r) self.assert_equal(line_x, res_x) self.assert_equal(line_h, res_h) def test_case30_data(self): net = pn.case30() self.converter.set_sn_mva( net.sn_mva) # TODO raise an error if not set ! self.converter.set_f_hz(net.f_hz) line_r, line_x, line_h = self.converter.get_line_param( net.line["r_ohm_per_km"].values * net.line["length_km"].values, net.line["x_ohm_per_km"].values * net.line["length_km"].values, net.line["c_nf_per_km"].values * net.line["length_km"].values, net.line["g_us_per_km"].values * net.line["length_km"].values, net.bus.loc[net.line["from_bus"]]["vn_kv"], net.bus.loc[net.line["to_bus"]]["vn_kv"]) res_r = np.array([ 0.0002, 0.0005, 0., 0., 0., 0., 0., 0., 0.0012, 0.0007, 0.0009, 0.0022, 0.0006, 0.0008, 0.0011, 0.0006, 0.0003, 0.0009, 0.0003, 0.0003, 0.0007, 0.0001, 0.001, 0.0001, 0.0012, 0.0013, 0.0019, 0.0025, 0.0011, 0., 0.0022, 0.0032, 0.0024, 0.0006, 0.0005, 0.0002, 0.0006, 0.0001, 0.0005, 0.0003, 0.0001 ]) res_x = np.array([ 0.0006, 0.0019, 0.0021, 0.0056, 0.0021, 0.0011, 0.0026, 0.0014, 0.0026, 0.0013, 0.002, 0.002, 0.0017, 0.0019, 0.0022, 0.0013, 0.0007, 0.0021, 0.0008, 0.0007, 0.0015, 0.0002, 0.002, 0.0004, 0.0018, 0.0027, 0.0033, 0.0038, 0.0021, 0.004, 0.0042, 0.006, 0.0045, 0.002, 0.002, 0.0006, 0.0018, 0.0004, 0.0012, 0.0008, 0.0004 ]) res_h = np.array([ 3. + 0.j, 2. + 0.j, 0. + 0.j, 0. + 0.j, 0. + 0.j, 0. + 0.j, 0. + 0.j, 0. + 0.j, 0. + 0.j, 0. + 0.j, 0. + 0.j, 0. + 0.j, 2. + 0.j, 0. + 0.j, 0. + 0.j, 0. + 0.j, 0. + 0.j, 0. + 0.j, 0. + 0.j, 0. + 0.j, 0. + 0.j, 0. + 0.j, 0. + 0.j, 0. + 0.j, 0. + 0.j, 0. + 0.j, 0. + 0.j, 0. + 0.j, 0. + 0.j, 0. + 0.j, 0. + 0.j, 0. + 0.j, 0. + 0.j, 2. + 0.j, 2. + 0.j, 1. + 0.j, 2. + 0.j, 0. + 0.j, 1. + 0.j, 1. + 0.j, 0. + 0.j ]) self.assert_equal(line_r, res_r) self.assert_equal(line_x, res_x) self.assert_equal(line_h, res_h) def test_case118_data(self): net = pn.case118() self.converter.set_sn_mva( net.sn_mva) # TODO raise an error if not set ! self.converter.set_f_hz(net.f_hz) line_r, line_x, line_h = self.converter.get_line_param( net.line["r_ohm_per_km"].values * net.line["length_km"].values, net.line["x_ohm_per_km"].values * net.line["length_km"].values, net.line["c_nf_per_km"].values * net.line["length_km"].values, net.line["g_us_per_km"].values * net.line["length_km"].values, net.bus.loc[net.line["from_bus"]]["vn_kv"], net.bus.loc[net.line["to_bus"]]["vn_kv"]) res_r = np.array([ 3.030e-04, 1.290e-04, 5.950e-05, 8.820e-05, 4.880e-04, 4.460e-04, 8.660e-05, 4.010e-04, 4.280e-04, 4.050e-04, 1.230e-04, 4.440e-04, 3.090e-04, 1.870e-04, 6.010e-04, 3.760e-05, 5.460e-05, 1.700e-04, 2.940e-04, 1.560e-04, 2.980e-04, 1.120e-04, 6.250e-04, 4.300e-04, 4.840e-04, 3.020e-04, 3.500e-04, 2.000e-04, 2.390e-04, 1.390e-04, 5.180e-04, 2.380e-04, 2.540e-04, 9.900e-05, 3.930e-04, 8.620e-05, 3.870e-04, 2.580e-04, 4.810e-04, 2.230e-04, 1.320e-04, 3.560e-04, 1.620e-04, 2.690e-04, 1.830e-04, 2.380e-04, 2.225e-04, 4.540e-04, 6.480e-04, 1.780e-04, 1.710e-04, 1.730e-04, 3.970e-04, 1.800e-04, 2.770e-04, 1.230e-04, 2.460e-04, 2.150e-04, 1.600e-04, 4.510e-04, 4.660e-04, 5.350e-04, 6.050e-04, 9.940e-05, 1.400e-04, 5.300e-04, 2.610e-04, 5.300e-04, 7.440e-04, 1.050e-04, 3.906e-04, 2.780e-04, 2.200e-04, 2.470e-04, 9.130e-05, 6.150e-04, 1.350e-04, 1.640e-04, 2.300e-05, 5.950e-04, 3.290e-04, 1.450e-04, 1.640e-04, 2.120e-04, 1.320e-04, 1.760e-05, 4.540e-04, 1.230e-04, 1.119e-04, 2.520e-04, 1.200e-04, 1.830e-04, 2.090e-04, 3.420e-04, 1.350e-04, 1.560e-04, 2.410e-04, 3.180e-04, 1.913e-04, 2.370e-04, 4.310e-05, 7.990e-05, 4.740e-04, 1.080e-04, 3.170e-04, 2.980e-04, 2.290e-04, 1.190e-04, 3.800e-04, 7.520e-04, 2.240e-05, 1.100e-04, 4.150e-04, 8.710e-05, 2.560e-05, 3.210e-04, 5.930e-04, 4.640e-05, 4.590e-05, 1.840e-04, 1.450e-04, 5.550e-04, 4.100e-04, 6.080e-04, 4.130e-04, 2.240e-04, 4.000e-04, 3.800e-04, 6.010e-04, 2.440e-05, 1.910e-04, 7.150e-04, 7.150e-04, 6.840e-04, 1.790e-04, 2.670e-04, 4.860e-04, 2.030e-04, 4.050e-04, 2.630e-04, 2.580e-05, 7.300e-04, 8.690e-04, 1.690e-04, 2.750e-05, 4.880e-05, 3.430e-04, 4.740e-04, 3.430e-04, 2.550e-04, 5.030e-04, 2.090e-04, 8.250e-04, 8.030e-04, 4.739e-04, 3.170e-04, 3.280e-04, 2.640e-05, 1.230e-04, 8.240e-05, 1.720e-05, 9.010e-05, 2.030e-04, 2.690e-05, 1.800e-04, 1.800e-04, 4.820e-04, 2.580e-04, 2.240e-04, 8.440e-04, 9.850e-04, 3.000e-04, 2.210e-05 ]) res_x = np.array([ 9.990e-04, 4.240e-04, 1.960e-04, 3.550e-04, 1.960e-03, 1.800e-03, 4.540e-04, 1.323e-03, 1.410e-03, 1.220e-03, 4.060e-04, 1.480e-03, 1.010e-03, 6.160e-04, 1.999e-03, 1.240e-04, 2.440e-04, 4.850e-04, 1.050e-03, 7.040e-04, 8.530e-04, 3.665e-04, 1.320e-03, 1.480e-03, 1.600e-03, 6.410e-04, 1.230e-03, 1.020e-03, 1.730e-03, 7.120e-04, 1.880e-03, 9.970e-04, 8.360e-04, 5.050e-04, 1.581e-03, 3.400e-04, 1.272e-03, 8.480e-04, 1.580e-03, 7.320e-04, 4.340e-04, 1.820e-03, 5.300e-04, 8.690e-04, 9.340e-04, 1.080e-03, 7.310e-04, 2.060e-03, 2.950e-03, 5.800e-04, 5.470e-04, 8.850e-04, 1.790e-03, 8.130e-04, 1.262e-03, 5.590e-04, 1.120e-03, 7.070e-04, 5.250e-04, 2.040e-03, 1.584e-03, 1.625e-03, 2.290e-03, 3.780e-04, 5.470e-04, 1.830e-03, 7.030e-04, 1.830e-03, 2.444e-03, 2.880e-04, 1.813e-03, 7.620e-04, 7.550e-04, 6.400e-04, 3.010e-04, 2.030e-03, 6.120e-04, 7.410e-04, 1.040e-04, 1.950e-03, 1.400e-03, 4.810e-04, 5.440e-04, 8.340e-04, 4.370e-04, 7.980e-05, 1.801e-03, 5.050e-04, 4.930e-04, 1.170e-03, 3.940e-04, 8.490e-04, 9.700e-04, 1.590e-03, 4.920e-04, 8.000e-04, 1.080e-03, 1.630e-03, 8.550e-04, 9.430e-04, 5.040e-04, 8.600e-04, 1.563e-03, 3.310e-04, 1.153e-03, 9.850e-04, 7.550e-04, 5.400e-04, 1.244e-03, 2.470e-03, 1.020e-04, 4.970e-04, 1.420e-03, 2.680e-04, 9.400e-05, 1.060e-03, 1.680e-03, 5.400e-04, 2.080e-04, 6.050e-04, 4.870e-04, 1.830e-03, 1.350e-03, 2.454e-03, 1.681e-03, 9.010e-04, 1.356e-03, 1.270e-03, 1.890e-03, 3.050e-04, 6.250e-04, 3.230e-03, 3.230e-03, 1.860e-03, 5.050e-04, 7.520e-04, 1.370e-03, 5.880e-04, 1.635e-03, 1.220e-03, 3.220e-04, 2.890e-03, 2.910e-03, 7.070e-04, 9.550e-05, 1.510e-04, 9.660e-04, 1.340e-03, 9.660e-04, 7.190e-04, 2.293e-03, 6.880e-04, 2.510e-03, 2.390e-03, 2.158e-03, 1.450e-03, 1.500e-03, 1.350e-04, 5.610e-04, 3.760e-04, 2.000e-04, 9.860e-04, 6.820e-04, 3.020e-04, 9.190e-04, 9.190e-04, 2.180e-03, 1.170e-03, 1.015e-03, 2.778e-03, 3.240e-03, 1.270e-03, 4.115e-03 ]) res_h = np.array([ 2.54 + 0.j, 1.082 + 0.j, 0.502 + 0.j, 0.878 + 0.j, 4.88 + 0.j, 4.444 + 0.j, 1.178 + 0.j, 3.368 + 0.j, 3.6 + 0.j, 12.4 + 0.j, 1.034 + 0.j, 3.68 + 0.j, 10.38 + 0.j, 1.572 + 0.j, 4.978 + 0.j, 1.264 + 0.j, 0.648 + 0.j, 4.72 + 0.j, 2.28 + 0.j, 1.87 + 0.j, 8.174 + 0.j, 3.796 + 0.j, 2.58 + 0.j, 3.48 + 0.j, 4.06 + 0.j, 1.234 + 0.j, 2.76 + 0.j, 2.76 + 0.j, 4.7 + 0.j, 1.934 + 0.j, 5.28 + 0.j, 10.6 + 0.j, 2.14 + 0.j, 5.48 + 0.j, 4.14 + 0.j, 0.874 + 0.j, 3.268 + 0.j, 2.18 + 0.j, 4.06 + 0.j, 1.876 + 0.j, 1.11 + 0.j, 4.94 + 0.j, 5.44 + 0.j, 2.3 + 0.j, 2.54 + 0.j, 2.86 + 0.j, 1.876 + 0.j, 5.46 + 0.j, 4.72 + 0.j, 6.04 + 0.j, 1.474 + 0.j, 2.4 + 0.j, 4.76 + 0.j, 2.16 + 0.j, 3.28 + 0.j, 1.464 + 0.j, 2.94 + 0.j, 1.816 + 0.j, 5.36 + 0.j, 5.41 + 0.j, 4.07 + 0.j, 4.08 + 0.j, 6.2 + 0.j, 0.986 + 0.j, 1.434 + 0.j, 4.72 + 0.j, 1.844 + 0.j, 4.72 + 0.j, 6.268 + 0.j, 0.76 + 0.j, 4.61 + 0.j, 2.02 + 0.j, 2. + 0.j, 6.2 + 0.j, 0.768 + 0.j, 5.18 + 0.j, 1.628 + 0.j, 1.972 + 0.j, 0.276 + 0.j, 5.02 + 0.j, 3.58 + 0.j, 1.198 + 0.j, 1.356 + 0.j, 2.14 + 0.j, 4.44 + 0.j, 0.21 + 0.j, 4.66 + 0.j, 1.298 + 0.j, 1.142 + 0.j, 2.98 + 0.j, 1.01 + 0.j, 2.16 + 0.j, 2.46 + 0.j, 4.04 + 0.j, 4.98 + 0.j, 8.64 + 0.j, 2.84 + 0.j, 17.64 + 0.j, 2.16 + 0.j, 2.38 + 0.j, 51.4 + 0.j, 90.8 + 0.j, 3.99 + 0.j, 0.83 + 0.j, 11.73 + 0.j, 2.51 + 0.j, 1.926 + 0.j, 1.426 + 0.j, 3.194 + 0.j, 6.32 + 0.j, 0.268 + 0.j, 1.318 + 0.j, 3.66 + 0.j, 0.568 + 0.j, 0.984 + 0.j, 2.7 + 0.j, 4.2 + 0.j, 42.2 + 0.j, 0.55 + 0.j, 1.552 + 0.j, 1.222 + 0.j, 4.66 + 0.j, 3.44 + 0.j, 6.068 + 0.j, 4.226 + 0.j, 2.24 + 0.j, 3.32 + 0.j, 3.16 + 0.j, 4.72 + 0.j, 116.2 + 0.j, 1.604 + 0.j, 8.6 + 0.j, 8.6 + 0.j, 4.44 + 0.j, 1.258 + 0.j, 1.874 + 0.j, 3.42 + 0.j, 1.396 + 0.j, 4.058 + 0.j, 3.1 + 0.j, 123. + 0.j, 7.38 + 0.j, 7.3 + 0.j, 2.02 + 0.j, 0.732 + 0.j, 0.374 + 0.j, 2.42 + 0.j, 3.32 + 0.j, 2.42 + 0.j, 1.788 + 0.j, 5.98 + 0.j, 1.748 + 0.j, 5.69 + 0.j, 5.36 + 0.j, 5.646 + 0.j, 3.76 + 0.j, 3.88 + 0.j, 1.456 + 0.j, 1.468 + 0.j, 0.98 + 0.j, 21.6 + 0.j, 104.6 + 0.j, 1.738 + 0.j, 38. + 0.j, 2.48 + 0.j, 2.48 + 0.j, 5.78 + 0.j, 3.1 + 0.j, 2.682 + 0.j, 7.092 + 0.j, 8.28 + 0.j, 12.2 + 0.j, 10.198 + 0.j ]) self.assert_equal(line_r, res_r) self.assert_equal(line_x, res_x) self.assert_equal(line_h, res_h) pp_net = net # fix the missing values 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. is_tap_hv_side = pp_net.trafo["tap_side"].values == "hv" is_tap_hv_side[~np.isfinite(is_tap_hv_side)] = 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 = self.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, ) trafo_r_res = np.array([ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 2.81494977e-04, 3.39887086e-06, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 1.37295648e-05, 0.00000000e+00, 1.73571860e-05 ]) trafo_x_res = np.array([ 2.67000000e-04, 3.82000000e-04, 3.70000000e-04, 2.06930358e-03, 4.04933224e-05, 3.88000000e-04, 3.75000000e-04, 3.86000000e-04, 2.68000000e-04, 3.70000000e-04, 1.59594718e-04, 3.70000000e-04, 2.01181945e-04 ]) trafo_h_res = np.array([ 0. - 0.j, 0. - 0.j, 0. - 0.j, 4.4602909 - 0.00140652j, 16.40272367 - 0.00022869j, 0. - 0.j, 0. - 0.j, 0. - 0.j, 0. - 0.j, 0. - 0.j, 63.96323106 - 0.01411497j, 0. - 0.j, 81.1310369 - 0.02879733j ]) self.assert_equal(trafo_r, trafo_r_res) self.assert_equal(trafo_x, trafo_x_res) self.assert_equal(trafo_b, trafo_h_res)
class KLU4Pandapower(): def __init__(self): self.solver = KLUSolver() self.converter = PandaPowerConverter() self.ppci = None self.V = None self.Ybus = None self.pv = None self.pq = None # TODO self.ppci = None self.Ybus = None self.Yf = None self.Yt = None self.bus = None self.gen = None self.branch = None self.ref = None self.pv = None self.pq = None self.ref_gens = None self.baseMVA = None raise RuntimeError("This module is deprecated.") def runpp(self, net, max_iteration=10, need_reset=True, **kwargs): net_orig = copy.deepcopy(net) pp.runpp(net_orig) V_orig = net_orig._ppc["internal"]["V"] # ---------- pp.run.runpp() ----------------- t0_start = time() t0_options = time() passed_parameters = _passed_runpp_parameters(locals()) _init_runpp_options(net, algorithm="nr", calculate_voltage_angles="auto", init="auto", max_iteration=max_iteration, tolerance_mva=1e-8, trafo_model="t", trafo_loading="current", enforce_q_lims=False, check_connectivity=False, voltage_depend_loads=True, consider_line_temperature=False, passed_parameters=passed_parameters, numba=True, **kwargs) _check_bus_index_and_print_warning_if_high(net) _check_gen_index_and_print_warning_if_high(net) et_options = time() - t0_options # ---------- pp.powerflow._powerflow() ----------------- """ Gets called by runpp or rundcpp with different arguments. """ # get infos from options t0_early_init = time() init_results = net["_options"]["init_results"] ac = net["_options"]["ac"] algorithm = net["_options"]["algorithm"] net["converged"] = False net["OPF_converged"] = False _add_auxiliary_elements(net) if not ac or init_results: verify_results(net) else: reset_results(net, all_empty=False) # TODO remove this when zip loads are integrated for all PF algorithms if algorithm not in ['nr', 'bfsw']: net["_options"]["voltage_depend_loads"] = False _add_auxiliary_elements(net) # convert pandapower net to ppc ppc, self.ppci = _pd2ppc(net) # pdb.set_trace() # store variables net["_ppc"] = ppc if not "VERBOSE" in kwargs: kwargs["VERBOSE"] = 0 # ----- run the powerflow ----- options = net["_options"] et_early_init = time() - t0_early_init # ---------- pp.powerflow._run_pf_algorithm() ---------------- # ---------- pp.pf.run_newton_raphson_pf.run_newton_raphson_pf() ---------------- t0 = time() t0_init = t0 et_init_dc = 0. if need_reset: if isinstance(options["init_va_degree"], str) and options["init_va_degree"] == "dc": self.ppci = _run_dc_pf(self.ppci) et_init_dc = time() - t0 if options["enforce_q_lims"]: raise NotImplementedError("enforce_q_lims not yet implemented") t0_init = time() # ---------- pp.pf.run_newton_raphson_pf._run_ac_pf_without_qlims_enforced ---------- # ppci, success, iterations = _run_ac_pf_without_qlims_enforced(ppci, options) makeYbus, pfsoln = _get_numba_functions(self.ppci, options) self.baseMVA, self.bus, self.gen, self.branch, self.ref, self.pv, self.pq, _, _, V0, self.ref_gens = _get_pf_variables_from_ppci( self.ppci) self.ppci, self.Ybus, self.Yf, self.Yt = _get_Y_bus( self.ppci, options, makeYbus, self.baseMVA, self.bus, self.branch) # TODO i have a problem here for the order of the bus / id of bus tmp_bus_ind = np.argsort(net.bus.index) model = DataModel() # model.set_sn_mva(net.sn_mva) # model.set_f_hz(net.f_hz) # TODO set that elsewhere self.converter.set_sn_mva(net.sn_mva) self.converter.set_f_hz(net.f_hz) # init_but should be called first among all the rest model.init_bus(net.bus.iloc[tmp_bus_ind]["vn_kv"].values, net.line.shape[0], net.trafo.shape[0]) # init the shunts line_r, line_x, line_h = self.converter.get_line_param( net.line["r_ohm_per_km"].values * net.line["length_km"].values, net.line["x_ohm_per_km"].values * net.line["length_km"].values, net.line["c_nf_per_km"].values * net.line["length_km"].values, net.line["g_us_per_km"].values * net.line["length_km"].values, net.bus.loc[net.line["from_bus"]]["vn_kv"], net.bus.loc[net.line["to_bus"]]["vn_kv"]) model.init_powerlines(line_r, line_x, line_h, net.line["from_bus"].values, net.line["to_bus"].values) # init the shunts model.init_shunt(net.shunt["p_mw"].values, net.shunt["q_mvar"].values, net.shunt["bus"].values) # init trafo if net.trafo.shape[0]: trafo_r, trafo_x, trafo_b = self.converter.get_trafo_param( net.trafo["vn_hv_kv"].values, net.trafo["vn_lv_kv"].values, net.trafo["vk_percent"].values, net.trafo["vkr_percent"].values, net.trafo["sn_mva"].values, net.trafo["pfe_kw"].values, net.trafo["i0_percent"].values, net.bus.loc[net.trafo["lv_bus"]]["vn_kv"]) # trafo_branch = ppc["branch"][net.line.shape[0]:, :] tap_step_pct = net.trafo["tap_step_percent"].values tap_step_pct[~np.isfinite(tap_step_pct)] = 0. tap_pos = net.trafo["tap_pos"].values tap_pos[~np.isfinite(tap_pos)] = 0. is_tap_hv_side = net.trafo["tap_side"].values == "hv" is_tap_hv_side[~np.isfinite(tap_pos)] = True model.init_trafo(trafo_r, trafo_x, trafo_b, tap_step_pct, tap_pos, is_tap_hv_side, net.trafo["hv_bus"].values, net.trafo["lv_bus"].values) model.init_loads(net.load["p_mw"].values, net.load["q_mvar"].values, net.load["bus"].values) model.init_generators(net.gen["p_mw"].values, net.gen["vm_pu"].values, net.gen["bus"].values) # TODO better way here! model.add_slackbus(net.ext_grid["bus"].values) # model.init_Ybus() # Ybus = model.get_Ybus() # be careful, the order is not the same between this and pandapower, you need to change it # Ybus_proper_oder = Ybus[np.array([net.bus.index]).T, np.array([net.bus.index])] # self.Ybus_proper_oder = self.Ybus else: pass # TODO update self.ppci with new values of generation - load such that Sbus is properly udpated # compute complex bus power injections [generation - load] Sbus = _get_Sbus(self.ppci, False) # Sbus_me = model.get_Sbus() # pdb.set_trace() # Sbus_me_r = np.real(Sbus_me) # Va0 = np.full(net.bus.shape[0], fill_value=net["_options"]["init_vm_pu"], dtype=np.complex_) # Va0[net.ext_grid["bus"].values] = net.ext_grid["vm_pu"].values * np.exp(1j * net.ext_grid["va_degree"].values / 360. * 2 * np.pi) #dctheta = model.dc_pf(Sbus_me_r, Va0) # self.dctheta = V0[tmp_bus_ind] # self.dcYbus = self.ppci["internal"]['Bbus'][np.array([tmp_bus_ind]).T, np.array([tmp_bus_ind])] # tmpdc = np.abs(dcYbus - self.dcYbus) # pv_me = model.get_pv() # pq_me = model.get_pq() # pdb.set_trace() # run the newton power flow # ------------------- pp.pypower.newtonpf --------------------- max_it = options["max_iteration"] tol = options['tolerance_mva'] self.Ybus = sparse.csc_matrix(self.Ybus) et_init = time() - t0_init t0__ = time() if need_reset: # reset the solver self.solver.reset() self.V = 1.0 * copy.deepcopy(V0) else: # reuse previous voltages pass self.solver.solve(self.Ybus, self.V, Sbus, self.pv, self.pq, max_it, tol) et__ = time() - t0__ t0_ = time() Va = self.solver.get_Va() Vm = self.solver.get_Vm() self.V = Vm * np.exp(1j * Va) J = self.solver.get_J() success = self.solver.converged() iterations = self.solver.get_nb_iter() # timer_Fx_, timer_solve_, timer_initialize_, timer_check_, timer_dSbus_, timer_fillJ_, timer_total_nr_ timers = self.solver.get_timers() et_ = time() - t0_ # ---------------------- pp.pypower.newtonpf --------------------- self.ppci = _store_internal( self.ppci, { "J": J, "Vm_it": None, "Va_it": None, "bus": self.bus, "gen": self.gen, "branch": self.branch, "baseMVA": self.baseMVA, "V": self.V, "pv": self.pv, "pq": self.pq, "ref": self.ref, "Sbus": Sbus, "ref_gens": self.ref_gens, "Ybus": self.Ybus, "Yf": self.Yf, "Yt": self.Yt, "timers": timers, "time_get_res": et_, "time_solve": et__, "time_init": et_init, "time_init_dc": et_init_dc, "time_early_init": et_early_init, "time_options": et_options }) t0_ppci_to_pfsoln = time() # update data matrices with solution store in ppci # ---------- pp.pf.run_newton_raphson_pf._run_ac_pf_without_qlims_enforced ---------- self.bus, self.gen, self.branch = ppci_to_pfsoln(self.ppci, options) te_ppci_to_pfsoln = time() - t0_ppci_to_pfsoln # these are the values from pypower / matlab t0_store_res = time() et = t0_store_res - t0 result = _store_results_from_pf_in_ppci(self.ppci, self.bus, self.gen, self.branch, success, iterations, et) t0_to_net = time() et_store_res = t0_to_net - t0_store_res # ---------- pp.pf.run_newton_raphson_pf.run_newton_raphson_pf() ---------------- # ---------- pp.powerflow._run_pf_algorithm() ---------------- # read the results (=ppci with results) to net _ppci_to_net(result, net) et_to_net = time() - t0_to_net # ---------- pp.powerflow._powerflow() ---------------- # ---------- pp.run.runpp() ----------------- # added et_start = time() - t0_start self.ppci = _store_internal( self.ppci, { "time_store_res": et_store_res, "time_to_net": et_to_net, "time_all": et_start, "time_ppci_to_pfsoln": te_ppci_to_pfsoln }) has_conv = model.compute_newton(V0[tmp_bus_ind], max_it, tol) # check the results results_solver = np.max(np.abs(V_orig - self.V)) Ybus = model.get_Ybus() Ybus_proper_oder = Ybus self.Ybus_proper_oder = self.Ybus[np.array([tmp_bus_ind]).T, np.array([tmp_bus_ind])] tmp = np.abs(Ybus_proper_oder - self.Ybus_proper_oder) # > 1e-7 por, qor, vor, aor = model.get_lineor_res() pex, qex, vex, aex = model.get_lineex_res() load_p, load_q, load_v = model.get_loads_res() np.max(np.abs(por - net_orig.res_line["p_from_mw"])) np.max(np.abs(qor - net_orig.res_line["q_from_mvar"])) a_or_pp = np.sqrt(net.res_line["p_from_mw"].values**2 + net.res_line["q_from_mvar"].values**2) a_or_pp /= np.sqrt(3) * net.bus.loc[net.line["from_bus"].values][ "vn_kv"].values * net.res_line["vm_from_pu"].values np.max(np.abs(a_or_pp - aor)) np.max(np.abs(a_or_pp - net.res_line["i_from_ka"])) np.max(np.abs(a_or_pp - net.res_line["i_from_ka"])) Va_me2 = model.get_Va() Vm_me2 = model.get_Vm() res_vm = np.abs(Vm_me2 - Vm[tmp_bus_ind]) res_va = np.abs(Va_me2 - Va[tmp_bus_ind]) # check that if i start the solver on the data Sbus_me = model.get_Sbus() pv_me = model.get_pv() pq_me = model.get_pq() np.all(sorted(pv_me) == sorted(net.gen["bus"])) np.all(sorted(pq_me) == sorted(tmp_bus_ind[self.pq])) plv, qlv, vlv, alv = model.get_trafolv_res() phv, qhv, vhv, ahv = model.get_trafohv_res() res_trafo = np.abs(plv - net_orig.res_trafo["p_lv_mw"].values) res_trafo_q = np.abs(qlv - net_orig.res_trafo["q_lv_mvar"].values) # self.solver.reset() # self.solver.solve(Ybus, V0, Sbus, pv_me, pq_me, max_it, tol) # Va2 = self.solver.get_Va() # Vm2 = self.solver.get_Vm() pdb.set_trace()
def init(pp_net): """ Convert a pandapower network as input into a GridModel. This does not throw any error at the moment when the conversion is not possible. Cases for which conversion is not possible include, but are not limited to: - the pandapower grid has 3 winding transformers - the pandapower grid has xwards - the pandapower grid any parrallel "elements" (at least one of the column "parrallel" is not 1) - some `g_us_per_km` for some lines are not zero - some `p_mw` for some shunts are not zero - some `tap_step_degre` are non zero for some trafo - no "ext_grid" is reported on the initial grid if you really need any of the above, please submit a github issue and we will work on their support. This conversion has been extensively studied for the case118() of pandapower.networks and should work really well for this grid. Actually, this grid is used for testing the GridModel class. Parameters ---------- pp_net: :class:`pandapower.grid` The initial pandapower network you want to convert Returns ------- model: :class:`GridModel` The initialize gridmodel """ # initialize and use converters converter = PandaPowerConverter() converter.set_sn_mva(pp_net.sn_mva) # TODO raise an error if not set ! converter.set_f_hz(pp_net.f_hz) line_r, line_x, line_h = \ converter.get_line_param( pp_net.line["r_ohm_per_km"].values * pp_net.line["length_km"].values, pp_net.line["x_ohm_per_km"].values * pp_net.line["length_km"].values, pp_net.line["c_nf_per_km"].values * pp_net.line["length_km"].values, pp_net.line["g_us_per_km"].values * pp_net.line["length_km"].values, pp_net.bus.loc[pp_net.line["from_bus"]]["vn_kv"], pp_net.bus.loc[pp_net.line["to_bus"]]["vn_kv"] ) trafo_r, trafo_x, trafo_b = \ converter.get_trafo_param(pp_net.trafo["vn_hv_kv"].values, pp_net.trafo["vn_lv_kv"].values, 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, pp_net.bus.loc[pp_net.trafo["lv_bus"]]["vn_kv"] ) # set up the data model accordingly model = GridModel() tmp_bus_ind = np.argsort(pp_net.bus.index) model.init_bus(pp_net.bus.iloc[tmp_bus_ind]["vn_kv"].values, pp_net.line.shape[0], pp_net.trafo.shape[0]) model.init_powerlines(line_r, line_x, line_h, pp_net.line["from_bus"].values, pp_net.line["to_bus"].values ) # init the shunts model.init_shunt(pp_net.shunt["p_mw"].values, pp_net.shunt["q_mvar"].values, pp_net.shunt["bus"].values ) tap_step_pct = pp_net.trafo["tap_step_percent"].values tap_step_pct[~np.isfinite(tap_step_pct)] = 0. tap_pos = pp_net.trafo["tap_pos"].values tap_pos[~np.isfinite(tap_pos)] = 0. is_tap_hv_side = pp_net.trafo["tap_side"].values == "hv" is_tap_hv_side[~np.isfinite(tap_pos)] = True model.init_trafo(trafo_r, trafo_x, trafo_b, tap_step_pct, tap_pos, is_tap_hv_side, pp_net.trafo["hv_bus"].values, pp_net.trafo["lv_bus"].values) model.init_loads(pp_net.load["p_mw"].values, pp_net.load["q_mvar"].values, pp_net.load["bus"].values ) model.init_generators(pp_net.gen["p_mw"].values, pp_net.gen["vm_pu"].values, pp_net.gen["min_q_mvar"].values, pp_net.gen["max_q_mvar"].values, pp_net.gen["bus"].values ) # TODO handle that better maybe, and warn only one slack bus is implemented if np.any(pp_net.gen["slack"].values): slack_gen_id = np.where(pp_net.gen["slack"].values)[0] else: # there is no slack bus in the generator of the pp grid # first i try to see if a generator is connected to a slack bus slack_bus_id = pp_net.ext_grid["bus"].values[0] if np.any(pp_net.gen["bus"].values == slack_bus_id): slack_gen_id = np.where(pp_net.gen["bus"].values == slack_bus_id)[0] else: # no gen is connected to a slack bus, so i create one. gen_p = np.concatenate((pp_net.gen["p_mw"].values, [np.sum(pp_net.load["p_mw"]) - np.sum(pp_net.gen["p_mw"])])) gen_v = np.concatenate((pp_net.gen["vm_pu"].values, [pp_net.ext_grid["vm_pu"].values[0]])) gen_bus = np.concatenate((pp_net.gen["bus"].values, [slack_bus_id])) gen_min_q = np.concatenate((pp_net.gen["min_q_mvar"].values, [-999999.])) gen_max_q = np.concatenate((pp_net.gen["max_q_mvar"].values, [+99999.])) model.init_generators(gen_p, gen_v, gen_min_q, gen_max_q, gen_bus) slack_gen_id = pp_net.gen["bus"].shape[0] model.add_gen_slackbus(slack_gen_id) return model
def init(pp_net): """ Convert a pandapower network as input into a GridModel. This does not throw any error at the moment when the conversion is not possible. Cases for which conversion is not possible include, but are not limited to: - the pandapower grid has 3 winding transformers - the pandapower grid has xwards - the pandapower grid any parrallel "elements" (at least one of the column "parrallel" is not 1) - some `g_us_per_km` for some lines are not zero - some `p_mw` for some shunts are not zero - some `tap_step_degre` are non zero for some trafo - no "ext_grid" is reported on the initial grid if you really need any of the above, please submit a github issue and we will work on their support. This conversion has been extensively studied for the case118() of pandapower.networks and should work really well for this grid. Actually, this grid is used for testing the GridModel class. Parameters ---------- pp_net: :class:`pandapower.grid` The initial pandapower network you want to convert Returns ------- model: :class:`GridModel` The initialize gridmodel """ # check for things not supported and raise if needed _aux_check_legit(pp_net) # initialize and use converters converter = PandaPowerConverter() converter.set_sn_mva(pp_net.sn_mva) # TODO raise an error if not set ! converter.set_f_hz(pp_net.f_hz) # set up the data model accordingly model = GridModel() if "_options" in pp_net: if "init_vm_pu" in pp_net["_options"]: model.set_init_vm_pu(pp_net["_options"]["init_vm_pu"]) model.set_sn_mva(pp_net.sn_mva) tmp_bus_ind = np.argsort(pp_net.bus.index) model.init_bus(pp_net.bus.iloc[tmp_bus_ind]["vn_kv"].values, pp_net.line.shape[0], pp_net.trafo.shape[0]) # init the powerlines _aux_add_line(converter, model, pp_net) # init the shunts _aux_add_shunt(model, pp_net) # handle the trafos _aux_add_trafo(converter, model, pp_net) # handle loads _aux_add_load(model, pp_net) # handle static generators (PQ generator) _aux_add_sgen(model, pp_net) # handle generators _aux_add_gen(model, pp_net) # handle storage units _aux_add_storage(model, pp_net) # deal with slack bus _aux_add_slack(model, pp_net) return model
me_lv = tpex[id_trafo_bug] me_hv = tpor[id_trafo_bug] pdb.set_trace() print("V - Check trafo proper conversion to r,x, b") test_ok = True 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 values # trafo_df = pp_net["trafo"] # vn_trafo_hv, vn_trafo_lv, shift = _calc_tap_from_dataframe(pp_net, trafo_df) converter = PandaPowerConverter() converter.set_sn_mva(pp_net.sn_mva) # TODO raise an error if not set ! converter.set_f_hz(pp_net.f_hz) # fix the missing values tap_neutral = 1.0 * pp_net.trafo["tap_neutral"].values if np.any(~np.isfinite(tap_neutral)): warnings.warn( "There were some Nan in the pp_net.trafo[\"tap_neutral\"], they have been replaced by 0" ) 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" )
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}"