diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f0bb92f..ad2c45e 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -24,10 +24,15 @@ TODO: in ContingencyAnalysisCpp: add back the `if(!ac_solver_used)` inside the in order to perform the "invertibility" check TODO: in `main.cpp` check the returned policy of pybind11 and also the `py::call_guard()` stuff TODO: a cpp class that is able to compute (DC powerflow) ContingencyAnalysis and TimeSeries using PTDF and LODF +TODO: integration test with pandapower (see `pandapower/contingency/contingency.py` and import `lightsim2grid_installed` and check it's True) [0.9.1] 2024-xx-yy -------------------------- - +- [FIXED] a bug due to wrong type (in a numpy array) for the element name which lead in turn + to a fail assertion (equality between two numpy arrays returning a bool and not an array) +- [IMPROVED] removing a weird `1j * h_` when initializing powerlines and transformers. This was + part of a pandapower "hack" which is not present anymore (see + https://github.com/BDonnot/lightsim2grid/issues/88#issue-2443299039) [0.9.0] 2024-07-29 -------------------------- diff --git a/lightsim2grid/gridmodel/from_pandapower/_aux_add_line.py b/lightsim2grid/gridmodel/from_pandapower/_aux_add_line.py index 98accdf..790b9d0 100644 --- a/lightsim2grid/gridmodel/from_pandapower/_aux_add_line.py +++ b/lightsim2grid/gridmodel/from_pandapower/_aux_add_line.py @@ -7,6 +7,9 @@ # This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. import numpy as np +from packaging import version +_MIN_PP_VERSION = version.parse("2.14.6") +import pandapower as pp from ._pp_bus_to_ls_bus import pp_bus_to_ls @@ -29,21 +32,41 @@ def _aux_add_line(converter, model, pp_net, pp_to_ls=None): "Some pp_net.line[\"parallel\"] != 1 it is not handled by lightsim yet.") #### find the right powerline parameters - 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"], - ) - - ### add them to the grid - model.init_powerlines(line_r, line_x, line_h, - pp_bus_to_ls(pp_net.line["from_bus"].values, pp_to_ls), - pp_bus_to_ls(pp_net.line["to_bus"].values, pp_to_ls) - ) + if version.parse(pp.__version__) >= _MIN_PP_VERSION: + # new pandapower with support for different h at both side + line_r, line_x, line_h_or, line_h_ex = \ + 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["g_us_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.bus.loc[pp_net.line["from_bus"]]["vn_kv"], + pp_net.bus.loc[pp_net.line["to_bus"]]["vn_kv"], + ) + + ### add them to the grid + model.init_powerlines_full(line_r, line_x, line_h_or, line_h_ex, + pp_bus_to_ls(pp_net.line["from_bus"].values, pp_to_ls), + pp_bus_to_ls(pp_net.line["to_bus"].values, pp_to_ls) + ) + + else: + # legacy pandapower, when they did not support lines with different h both side + line_r, line_x, line_h = \ + converter.get_line_param_legacy( + 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["g_us_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.bus.loc[pp_net.line["from_bus"]]["vn_kv"], + pp_net.bus.loc[pp_net.line["to_bus"]]["vn_kv"], + ) + + ### add them to the grid + model.init_powerlines(line_r, line_x, line_h, + pp_bus_to_ls(pp_net.line["from_bus"].values, pp_to_ls), + pp_bus_to_ls(pp_net.line["to_bus"].values, pp_to_ls) + ) for line_id, is_connected in enumerate(pp_net.line["in_service"].values): if not is_connected: # powerline is deactivated diff --git a/lightsim2grid/gridmodel/from_pypowsybl/_from_pypowsybl.py b/lightsim2grid/gridmodel/from_pypowsybl/_from_pypowsybl.py index 7159d1a..cb8181c 100644 --- a/lightsim2grid/gridmodel/from_pypowsybl/_from_pypowsybl.py +++ b/lightsim2grid/gridmodel/from_pypowsybl/_from_pypowsybl.py @@ -138,8 +138,8 @@ def init(net : pypo.network, b2 = df_line["b2"].values * v2*v2/sn_mva + (v2-v1)*tmp_.imag*v2/sn_mva g1 = df_line["g1"].values * v1*v1/sn_mva + (v1-v2)*tmp_.real*v1/sn_mva g2 = df_line["g2"].values * v2*v2/sn_mva + (v2-v1)*tmp_.real*v2/sn_mva - line_h_or = (b1 + 1j * g1) - line_h_ex = (b2 + 1j * g2) + line_h_or = (g1 + 1j * b1) + line_h_ex = (g2 + 1j * b2) lor_bus, lor_disco = _aux_get_bus(bus_df, df_line, conn_key="connected1", bus_key="bus1_id") lex_bus, lex_disco = _aux_get_bus(bus_df, df_line, conn_key="connected2", bus_key="bus2_id") model.init_powerlines_full(line_r, @@ -178,7 +178,7 @@ def init(net : pypo.network, tex_bus, tex_disco = _aux_get_bus(bus_df, df_trafo, conn_key="connected2", bus_key="bus2_id") model.init_trafo(df_trafo["r"].values / trafo_to_pu, df_trafo["x"].values / trafo_to_pu, - 2.*(1j*df_trafo["g"].values + df_trafo["b"].values) * trafo_to_pu, + 2.*(df_trafo["g"].values + 1j * df_trafo["b"].values) * trafo_to_pu, tap_step_pct, tap_pos, shift_, diff --git a/lightsim2grid/lightSimBackend.py b/lightsim2grid/lightSimBackend.py index 5738f60..6a8998e 100644 --- a/lightsim2grid/lightSimBackend.py +++ b/lightsim2grid/lightSimBackend.py @@ -628,20 +628,20 @@ def _load_grid_pypowsybl(self, path=None, filename=None): use_grid2op_default_names = False if use_grid2op_default_names: - self.name_load = np.array([f"load_{el.bus_id}_{id_obj}" for id_obj, el in enumerate(self._grid.get_loads())]) - self.name_gen = np.array([f"gen_{el.bus_id}_{id_obj}" for id_obj, el in enumerate(self._grid.get_generators())]) + self.name_load = np.array([f"load_{el.bus_id}_{id_obj}" for id_obj, el in enumerate(self._grid.get_loads())]).astype(str) + self.name_gen = np.array([f"gen_{el.bus_id}_{id_obj}" for id_obj, el in enumerate(self._grid.get_generators())]).astype(str) self.name_line = np.array([f"{el.bus_or_id}_{el.bus_ex_id}_{id_obj}" for id_obj, el in enumerate(self._grid.get_lines())] + - [f"{el.bus_hv_id}_{el.bus_lv_id}_{id_obj}" for id_obj, el in enumerate(self._grid.get_trafos())]) - self.name_storage = np.array([f"storage_{el.bus_id}_{id_obj}" for id_obj, el in enumerate(self._grid.get_storages())]) - self.name_shunt = np.array([f"shunt_{el.bus_id}_{id_obj}" for id_obj, el in enumerate(self._grid.get_shunts())]) + [f"{el.bus_hv_id}_{el.bus_lv_id}_{id_obj}" for id_obj, el in enumerate(self._grid.get_trafos())]).astype(str) + self.name_storage = np.array([f"storage_{el.bus_id}_{id_obj}" for id_obj, el in enumerate(self._grid.get_storages())]).astype(str) + self.name_shunt = np.array([f"shunt_{el.bus_id}_{id_obj}" for id_obj, el in enumerate(self._grid.get_shunts())]).astype(str) else: - self.name_load = np.array(load_sub.index) - self.name_gen = np.array(gen_sub.index) - self.name_line = np.concatenate((lor_sub.index, tor_sub.index)) - self.name_storage = np.array(batt_sub.index) - self.name_shunt = np.array(sh_sub.index) - self.name_sub = np.array(buses_sub_id.index) - + self.name_load = np.array(load_sub.index.astype(str)) + self.name_gen = np.array(gen_sub.index.astype(str)) + self.name_line = np.concatenate((lor_sub.index.astype(str), tor_sub.index.astype(str))) + self.name_storage = np.array(batt_sub.index.astype(str)) + self.name_shunt = np.array(sh_sub.index.astype(str)) + self.name_sub = np.array(buses_sub_id.index.astype(str)) + if "reconnect_disco_gen" in loader_kwargs and loader_kwargs["reconnect_disco_gen"]: for el in self._grid.get_generators(): if not el.connected: diff --git a/lightsim2grid/tests/test_DataConverter.py b/lightsim2grid/tests/test_DataConverter.py index 0e6c5de..f477a72 100644 --- a/lightsim2grid/tests/test_DataConverter.py +++ b/lightsim2grid/tests/test_DataConverter.py @@ -8,13 +8,12 @@ import unittest import numpy as np -import pdb -from numpy.lib.arraysetops import isin import pandapower.networks as pn from lightsim2grid_cpp import PandaPowerConverter -class MakeTests(unittest.TestCase): +class TestDataConverterLegacy(unittest.TestCase): + """test the converter used for legacy pandapower version (before 2.14.something)""" def setUp(self): self.converter = PandaPowerConverter() self.tol = 1e-8 @@ -27,17 +26,17 @@ 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( + line_r, line_x, line_h = self.converter.get_line_param_legacy( 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.line["c_nf_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]) + res_h = 1j * 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]) # new in pandapower 2.7.0 : order changed, sn_mva changed! order_270 = [0, 1, 3, 4, 5, 6, 7, 8, 9, 10, 2] # for el_r, el_x in zip(line_r, line_x): print(np.where((np.abs(res_r*100.-el_r) <= 1e-6) & (np.abs(res_x*100.-el_x) <= 1e-6))[0]) @@ -49,11 +48,11 @@ 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( + line_r, line_x, line_h = self.converter.get_line_param_legacy( 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.line["c_nf_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"] ) @@ -69,12 +68,12 @@ def test_case30_data(self): 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]) + res_h = 1j * 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]) # new in pandapower 2.7.0 : order changed, sn_mva changed! # for el_r, el_x, el_h in zip(line_r, line_x, line_h): # print(np.where((np.abs(res_r*100.-el_r) <= 1e-6) & @@ -90,11 +89,11 @@ 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( + line_r, line_x, line_h = self.converter.get_line_param_legacy( 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.line["c_nf_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"] ) @@ -156,41 +155,41 @@ def test_case118_data(self): 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]) + res_h = 1j * 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]) # new in pandapower 2.7.0 : order changed, sn_mva changed! # for el_r, el_x in zip(line_r, line_x): print(np.where((np.abs(res_r*100.-el_r) <= 1e-6) & (np.abs(res_x*100.-el_x) <= 1e-6))[0]) # for el in [el for el in np.arange(line_r.shape[0]) if not np.isin(el, order_270)]: print(el) @@ -243,13 +242,13 @@ def test_case118_data(self): 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]) + trafo_h_res = 1j * 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]) # new in pandapower 2.7.0 : order changed, sn_mva changed! # for el_r, el_x in zip(trafo_r, trafo_x): print(np.where((np.abs(trafo_r_res*100.-el_r) <= 1e-6) & (np.abs(trafo_x_res*100.-el_x) <= 1e-6))[0]) # for el in [el for el in np.arange(line_r.shape[0]) if not np.isin(el, order_270)]: print(el) diff --git a/lightsim2grid/tests/test_SameResPP.py b/lightsim2grid/tests/test_SameResPP.py index 4ecad36..dd995c2 100644 --- a/lightsim2grid/tests/test_SameResPP.py +++ b/lightsim2grid/tests/test_SameResPP.py @@ -385,15 +385,15 @@ def _aux_test(self, pn_net): 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) - + b_t *= 1j # to fix https://github.com/BDonnot/lightsim2grid/issues/88 + # 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}") - + raise AssertionError(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 diff --git a/lightsim2grid/tests/test_issue_30.py b/lightsim2grid/tests/test_issue_30.py index 9c6d178..7ad3116 100644 --- a/lightsim2grid/tests/test_issue_30.py +++ b/lightsim2grid/tests/test_issue_30.py @@ -59,3 +59,8 @@ def test_diverge(self): # and it used to make this diverge sim_o, sim_r, sim_d, sim_i = obs.simulate(self.env.action_space()) assert not sim_d, "should have converged" + + +if __name__ == "__main__": + unittest.main() + \ No newline at end of file diff --git a/lightsim2grid/tests/test_issue_56.py b/lightsim2grid/tests/test_issue_56.py index 412615b..d3589b7 100644 --- a/lightsim2grid/tests/test_issue_56.py +++ b/lightsim2grid/tests/test_issue_56.py @@ -90,5 +90,4 @@ def setUp(self) -> None: if __name__ == "__main__": unittest.main() - \ No newline at end of file diff --git a/src/DataConverter.cpp b/src/DataConverter.cpp index 49c5f52..d0f3970 100644 --- a/src/DataConverter.cpp +++ b/src/DataConverter.cpp @@ -7,6 +7,8 @@ // This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. #include "DataConverter.h" +#include + void PandaPowerConverter::_check_init(){ if(sn_mva_ <= 0.){ @@ -114,7 +116,8 @@ std::tuple res = @@ -126,12 +129,12 @@ std::tuple - PandaPowerConverter::get_line_param(const RealVect & branch_r, - const RealVect & branch_x, - const RealVect & branch_c, - const RealVect & branch_g, // TODO - const RealVect & branch_from_kv, - const RealVect & branch_to_kv) + PandaPowerConverter::get_line_param_legacy(const RealVect & branch_r, + const RealVect & branch_x, + const RealVect & branch_g, + const RealVect & branch_c, + const RealVect & branch_from_kv, + const RealVect & branch_to_kv) { //TODO does not use c at the moment! _check_init(); @@ -141,11 +144,50 @@ std::tuple(); + // b = 2 * net.f_hz * math.pi * line["c_nf_per_km"].values * 1e-9 * baseR * length_km * parallel + // g = line["g_us_per_km"].values * 1e-6 * baseR * length_km * parallel + // g + 1j . b + CplxVect powerlines_h = CplxVect::Constant(nb_line, 0.); + powerlines_h.array() += (1e-6 * branch_g.array().cast() + + my_i * 2.0 * f_hz_ * M_PI * 1e-9 * branch_c.array().cast()); powerlines_h.array() *= branch_from_pu.array().cast(); std::tuple res = std::tuple (std::move(powerlines_r), std::move(powerlines_x), std::move(powerlines_h)); return res; } + +std::tuple + PandaPowerConverter::get_line_param(const RealVect & branch_r, + const RealVect & branch_x, + const RealVect & branch_g, + const RealVect & branch_c, + const RealVect & branch_from_kv, + const RealVect & branch_to_kv) +{ + _check_init(); + const int nb_line = static_cast(branch_r.size()); + RealVect branch_from_pu = branch_from_kv.array() * branch_from_kv.array() / sn_mva_; + + RealVect powerlines_r = branch_r.array() / branch_from_pu.array(); + RealVect powerlines_x = branch_x.array() / branch_from_pu.array(); + + // TODO + // b = 2 * net.f_hz * math.pi * line["c_nf_per_km"].values * 1e-9 * baseR * length_km * parallel + // g = line["g_us_per_km"].values * 1e-6 * baseR * length_km * parallel + // g + 1j . b + CplxVect powerlines_h_or = CplxVect::Constant(nb_line, 0.); + powerlines_h_or.array() += (1e-6 * branch_g.array().cast() + + my_i * 2.0 * f_hz_ * M_PI * 1e-9 * branch_c.array().cast()); + powerlines_h_or.array() *= branch_from_pu.array().cast() * 0.5; + CplxVect powerlines_h_ex = powerlines_h_or; + // END TODO + + std::tuple res = std::tuple( + std::move(powerlines_r), std::move(powerlines_x), std::move(powerlines_h_or), std::move(powerlines_h_ex)); + return res; +} + diff --git a/src/DataConverter.h b/src/DataConverter.h index d8106da..d339497 100644 --- a/src/DataConverter.h +++ b/src/DataConverter.h @@ -50,15 +50,28 @@ class PandaPowerConverter : public BaseConstants const RealVect & trafo_pfe_kw, const RealVect & trafo_i0_pct); /** - pair unit properly the powerlines + pair unit properly the powerlines (for legacy (<= 2.14.somthing) pandapower) **/ std::tuple + get_line_param_legacy(const RealVect & branch_r, + const RealVect & branch_x, + const RealVect & branch_g, + const RealVect & branch_c, + const RealVect & branch_from_kv, + const RealVect & branch_to_kv); + /** + pair unit properly the powerlines (for most recent pandapower) + **/ + std::tuple get_line_param(const RealVect & branch_r, const RealVect & branch_x, + const RealVect & branch_g, const RealVect & branch_c, - const RealVect & branch_g, //TODO g is not supported atm! const RealVect & branch_from_kv, const RealVect & branch_to_kv); diff --git a/src/element_container/LineContainer.cpp b/src/element_container/LineContainer.cpp index fdcf6bc..5dd6758 100644 --- a/src/element_container/LineContainer.cpp +++ b/src/element_container/LineContainer.cpp @@ -143,8 +143,8 @@ void LineContainer::_update_model_coeffs() // for AC // see https://matpower.org/docs/MATPOWER-manual.pdf eq. 3.2 const cplx_type ys = 1. / (powerlines_r_(i) + my_i * powerlines_x_(i)); - const cplx_type h_or = my_i * powerlines_h_or_(i); - const cplx_type h_ex = my_i * powerlines_h_ex_(i); + const cplx_type h_or = powerlines_h_or_(i); + const cplx_type h_ex = powerlines_h_ex_(i); yac_ff_(i) = (ys + h_or); yac_tt_(i) = (ys + h_ex); yac_tf_(i) = -ys; @@ -287,8 +287,8 @@ void LineContainer::fillBp_Bpp(std::vector > & Bp, yft_bp = -ys_bp_r; ytf_bp = -ys_bp_r; const real_type ys_bpp_r = std::imag(ys_bpp); - yff_bpp = ys_bpp_r + std::imag(my_i * powerlines_h_or_(line_id)); - ytt_bpp = ys_bpp_r + std::imag(my_i * powerlines_h_ex_(line_id)); + yff_bpp = ys_bpp_r + std::imag(powerlines_h_or_(line_id)); + ytt_bpp = ys_bpp_r + std::imag(powerlines_h_ex_(line_id)); yft_bpp = -ys_bpp_r; ytf_bpp = -ys_bpp_r; diff --git a/src/element_container/TrafoContainer.cpp b/src/element_container/TrafoContainer.cpp index 488c2e7..acb850d 100644 --- a/src/element_container/TrafoContainer.cpp +++ b/src/element_container/TrafoContainer.cpp @@ -133,7 +133,7 @@ void TrafoContainer::_update_model_coeffs() // for AC // see https://matpower.org/docs/MATPOWER-manual.pdf eq. 3.2 const cplx_type ys = 1. / (r_(i) + my_i * x_(i)); - const cplx_type h = my_i * h_(i) * 0.5; + const cplx_type h = h_(i) * 0.5; double tau = ratio_(i); if(!is_tap_hv_side_[i]) tau = my_one_ / tau; real_type theta_shift = shift_(i); @@ -452,8 +452,8 @@ void TrafoContainer::fillBp_Bpp(std::vector > & Bp, ytf_bp = -std::imag(ys_bp * emitheta_shift_bp); yft_bp = -std::imag(ys_bp * eitheta_shift_bp); const real_type ys_bpp_r = std::imag(ys_bpp); - yff_bpp = (ys_bpp_r + std::imag(0.5 * my_i * h_(tr_id))) / (tau_bpp * tau_bpp); - ytt_bpp = ys_bpp_r + std::imag(0.5 * my_i * h_(tr_id)); + yff_bpp = (ys_bpp_r + std::imag(0.5 * h_(tr_id))) / (tau_bpp * tau_bpp); + ytt_bpp = ys_bpp_r + std::imag(0.5 * h_(tr_id)); ytf_bpp = -ys_bpp_r / tau_bpp; yft_bpp = -ys_bpp_r / tau_bpp; diff --git a/src/main.cpp b/src/main.cpp index ac31441..78c0dda 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -659,6 +659,7 @@ PYBIND11_MODULE(lightsim2grid_cpp, m) .def(py::init<>()) .def("set_f_hz", &PandaPowerConverter::set_f_hz) .def("set_sn_mva", &PandaPowerConverter::set_sn_mva) + .def("get_line_param_legacy", &PandaPowerConverter::get_line_param_legacy) .def("get_line_param", &PandaPowerConverter::get_line_param) .def("get_trafo_param", &PandaPowerConverter::get_trafo_param);