From 5f50f9feac8bbf2def86a4425b2b10187decc154 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Thu, 3 Oct 2024 17:33:17 +0200 Subject: [PATCH 1/4] adding possibility to load a grid where everything is not 'fully connected' Signed-off-by: DONNOT Benjamin --- CHANGELOG.rst | 8 +++++++- docs/conf.py | 2 +- lightsim2grid/__init__.py | 2 +- lightsim2grid/lightSimBackend.py | 19 +++++++++++++++++-- setup.py | 2 +- 5 files changed, 27 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 38869fc..08cb081 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -26,7 +26,13 @@ TODO: in `main.cpp` check the returned policy of pybind11 and also the `py::call 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 +[0.9.2] 2024-xx-yy +-------------------------- +- [ADDED] support loading a grid when everything is NOT on the same bus + (`topo_vect` used to be wrong in this case). This is especially usefull + for grid loaded with `pypowsybl` + +[0.9.1] 2024-09-30 -------------------------- - [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) diff --git a/docs/conf.py b/docs/conf.py index cabfe19..c739668 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -22,7 +22,7 @@ author = 'Benjamin DONNOT' # The full version, including alpha/beta/rc tags -release = "0.9.1" +release = "0.9.2.dev0" version = '0.9' # -- General configuration --------------------------------------------------- diff --git a/lightsim2grid/__init__.py b/lightsim2grid/__init__.py index 8922c7c..0e2f352 100644 --- a/lightsim2grid/__init__.py +++ b/lightsim2grid/__init__.py @@ -6,7 +6,7 @@ # SPDX-License-Identifier: MPL-2.0 # This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. -__version__ = "0.9.1" +__version__ = "0.9.2.dev0" __all__ = ["newtonpf", "SolverType", "ErrorType", "solver", "compilation_options"] diff --git a/lightsim2grid/lightSimBackend.py b/lightsim2grid/lightSimBackend.py index 35e91e7..d6f29d2 100644 --- a/lightsim2grid/lightSimBackend.py +++ b/lightsim2grid/lightSimBackend.py @@ -991,7 +991,6 @@ def _aux_finish_setup_after_reading(self): self.nb_obj_per_bus = np.zeros(2 * self.__nb_bus_before, dtype=dt_int).reshape(-1) self.topo_vect = np.ones(cls.dim_topo, dtype=dt_int).reshape(-1) - if cls.shunts_data_available: self.shunt_topo_vect = np.ones(cls.n_shunt, dtype=dt_int) # shunts @@ -1031,12 +1030,28 @@ def _aux_finish_setup_after_reading(self): self.storage_theta = np.full(cls.n_storage, dtype=dt_float, fill_value=np.NaN).reshape(-1) self._count_object_per_bus() + self.topo_vect[cls.load_pos_topo_vect] = cls.global_bus_to_local(np.array([el.bus_id for el in self._grid.get_loads()]), + cls.load_to_subid) + self.topo_vect[cls.gen_pos_topo_vect] = cls.global_bus_to_local(np.array([el.bus_id for el in self._grid.get_generators()]), + cls.gen_to_subid) + self.topo_vect[cls.storage_pos_topo_vect] = cls.global_bus_to_local(np.array([el.bus_id for el in self._grid.get_storages()]), + cls.storage_to_subid) + lor_glob_bus = np.concatenate((np.array([el.bus_or_id for el in self._grid.get_lines()]), + np.array([el.bus_hv_id for el in self._grid.get_trafos()]))) + self.topo_vect[cls.line_or_pos_topo_vect] = cls.global_bus_to_local(lor_glob_bus, + cls.line_or_to_subid) + lex_glob_bus = np.concatenate((np.array([el.bus_ex_id for el in self._grid.get_lines()]), + np.array([el.bus_lv_id for el in self._grid.get_trafos()]))) + self.topo_vect[cls.line_ex_pos_topo_vect] = cls.global_bus_to_local(lex_glob_bus, + cls.line_ex_to_subid) + self._grid.tell_solver_need_reset() self.__me_at_init = self._grid.copy() self.__init_topo_vect = np.ones(cls.dim_topo, dtype=dt_int) self.__init_topo_vect[:] = self.topo_vect if cls.shunts_data_available: - self.sh_bus[:] = 1 + self.sh_bus[:] = cls.global_bus_to_local(np.array([el.bus_id for el in self._grid.get_shunts()]), + cls.shunt_to_subid) def assert_grid_correct_after_powerflow(self) -> None: """ diff --git a/setup.py b/setup.py index d80be10..615ad60 100644 --- a/setup.py +++ b/setup.py @@ -14,7 +14,7 @@ from pybind11.setup_helpers import Pybind11Extension, build_ext -__version__ = "0.9.1" +__version__ = "0.9.2.dev0" KLU_SOLVER_AVAILABLE = False # Try to link against SuiteSparse (if available) From 7d332dfa9f82374b75412033aaaf9db7b57e2adf Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Fri, 18 Oct 2024 10:37:58 +0200 Subject: [PATCH 2/4] ready for version 0.9.2 Signed-off-by: DONNOT Benjamin --- .circleci/config.yml | 6 +- CHANGELOG.rst | 22 +- DISCLAIMER.md | 2 +- README.md | 4 +- benchmarks/benchmark_grid_size.py | 138 ++++++--- docs/benchmarks.rst | 2 +- docs/benchmarks_grid_sizes.rst | 269 ++++++++++++++++++ docs/conf.py | 2 +- docs/disclaimer.rst | 3 +- docs/index.rst | 3 +- docs/install_from_source.rst | 2 +- docs/quickstart.rst | 4 +- docs/solvers.rst | 4 +- docs/use_with_grid2op.rst | 2 +- lightsim2grid/__init__.py | 2 +- lightsim2grid/lightSimBackend.py | 2 +- lightsim2grid/tests/test_SameResPP.py | 2 +- .../tests/test_integration_pandapower.py | 2 +- .../tests/test_n1contingencyrewards.py | 2 +- setup.py | 4 +- src/linear_solvers/CKTSOSolver.cpp | 2 +- src/linear_solvers/CKTSOSolver.h | 4 +- utils/Dockerfile | 2 +- utils/Dockerfile_light | 2 +- utils/Dockerfile_test | 2 +- 25 files changed, 415 insertions(+), 74 deletions(-) create mode 100644 docs/benchmarks_grid_sizes.rst diff --git a/.circleci/config.yml b/.circleci/config.yml index 321b182..5ce651d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -366,7 +366,7 @@ jobs: command: | source venv_test/bin/activate pip install --upgrade pip setuptools wheel - git clone https://github.com/rte-france/grid2op.git _grid2op + git clone https://github.com/Grid2Op/grid2op.git _grid2op pip install -e _grid2op - run: name: "Show package versions" @@ -583,7 +583,7 @@ jobs: command: | source venv_test/bin/activate pip install --upgrade pip setuptools wheel - git clone https://github.com/rte-france/grid2op.git _grid2op + git clone https://github.com/Grid2Op/grid2op.git _grid2op pip install -e _grid2op - run: name: "Show package versions" @@ -619,7 +619,7 @@ jobs: command: | .\venv_test\Scripts\activate pip install gymnasium "numpy<2" pybind11 scipy - git clone https://github.com/rte-france/grid2op.git _grid2op + git clone https://github.com/Grid2Op/grid2op.git _grid2op pip install -e _grid2op gymnasium - run: name: "Install lightsim2grid" diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 08cb081..22c7eaa 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -26,11 +26,15 @@ TODO: in `main.cpp` check the returned policy of pybind11 and also the `py::call 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.2] 2024-xx-yy +[0.9.2] 2024-10-18 -------------------------- - [ADDED] support loading a grid when everything is NOT on the same bus (`topo_vect` used to be wrong in this case). This is especially usefull for grid loaded with `pypowsybl` +- [ADDED] a file benchmarking the timings for running powerflow on different + grid sizes. +- [UPDATED] urls to match the new repo location +- [UPDATED] urls to match new grid2op location [0.9.1] 2024-09-30 -------------------------- @@ -52,7 +56,7 @@ TODO: integration test with pandapower (see `pandapower/contingency/contingency. one substation in lightsim2grid. - [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) + https://github.com/Grid2Op/lightsim2grid/issues/88#issue-2443299039) [0.9.0] 2024-07-29 -------------------------- @@ -231,7 +235,7 @@ gridmodel.get_Bf() gridmodel.get_Bf_solver() - [IMPROVED] computation speed: grid is not read another time in some cases. For example, if load and generators do not change, then Sbus is not recomputed. Likewise, if the topology does not change, then the Ybus - is not recomputed either see https://github.com/BDonnot/lightsim2grid/issues/72 + is not recomputed either see https://github.com/Grid2Op/lightsim2grid/issues/72 [0.7.5.post1] 2024-03-14 ------------------------- @@ -244,12 +248,12 @@ gridmodel.get_Bf() gridmodel.get_Bf_solver() multiple powerflows used the same solver - [FIXED] a bug in AC and DC powerflow when shunts had active values - [ADDED] possibility to initialize a powergrid based on pypowsybl - see https://github.com/BDonnot/lightsim2grid/issues/53 + see https://github.com/Grid2Op/lightsim2grid/issues/53 - [ADDED] some more algorithm to perform powerflow: Fast Decoupled Powerflow (in BX and XB variant) - see https://github.com/BDonnot/lightsim2grid/issues/63 + see https://github.com/Grid2Op/lightsim2grid/issues/63 - [ADDED] build lightsim2grid for python 3.12 - [ADDED] support for non distributed slack but multiple slack buses - see https://github.com/BDonnot/lightsim2grid/issues/50 (ONLY FOR AC powerflow) + see https://github.com/Grid2Op/lightsim2grid/issues/50 (ONLY FOR AC powerflow) - [IMPROVED] now shipping `src` and `eigen` directory in the source of lightsim2grid to allow their installation if wheels are not provided. - [IMPROVED] in the underlying cpp GridModel powerlines can now have 2 @@ -266,7 +270,7 @@ gridmodel.get_Bf() gridmodel.get_Bf_solver() - [IMPROVED] initialize the underlying "PandaPowerBackend" without numba - [IMPROVED] grid2op import to be more compliant with renaming of uppercased file names - [IMPROVED] decoupling of the PandapowerBackend class and the class "internally" used by LightSimBackend - when loading the grid. This caused some issue, *eg* https://github.com/rte-france/Grid2Op/issues/508 + when loading the grid. This caused some issue, *eg* https://github.com/Grid2Op/grid2op/issues/508 [0.7.2] 2023-06-06 -------------------- @@ -384,7 +388,7 @@ gridmodel.get_Bf() gridmodel.get_Bf_solver() - [FIXED] a bug for static generator (wrong signed convention were used in some part of the c++ code). This has no impact at all for provided grid2op environments. - [FIXED] An issue where the backend could get "stuck" in a wrong state because of the way the Vinit was computed (see - `Issue 30 `_) + `Issue 30 `_) - [ADDED] experimental support for the `NICSLU` linear solver (requires a proper license and library, see https://github.com/chenxm1986/nicslu for more information. Support does not include multi threaded at the moment). - [IMPROVED] minor performance improvements for the solvers based on Newton Raphson (faster filling of the Jacobian @@ -412,7 +416,7 @@ gridmodel.get_Bf() gridmodel.get_Bf_solver() [0.5.1] 2021-04-09 ------------------- - [FIXED] yet another compilation issue with clang (see - `Issue 22 `_) + `Issue 22 `_) - [ADDED] circleci to check compilation for gcc - [ADDED] circleci to check compilation for clang - [ADDED] circleci to check compilation for msvc diff --git a/DISCLAIMER.md b/DISCLAIMER.md index 8f807d0..4342a52 100644 --- a/DISCLAIMER.md +++ b/DISCLAIMER.md @@ -18,7 +18,7 @@ This disclaimer only serves as a complement to the [`LICENSE`](LICENSE.md) file replacement of this file. The simulator implemented in this package is made for speed mainly to serve as a grid2op backend (see -https://github.com/rte-france/grid2op for more information). We recall here that grid2op is a research testbed platform +https://github.com/Grid2Op/grid2op for more information). We recall here that grid2op is a research testbed platform aiming at emulating sequential decisions making in powergrids targeting mainly the "reinforcement learning" community (though open to anyone). diff --git a/README.md b/README.md index 333f39c..b8a9d7f 100644 --- a/README.md +++ b/README.md @@ -213,7 +213,7 @@ If you use this package in one of your work, please cite: year = {2020}, publisher = {GitHub}, journal = {GitHub repository}, - howpublished = {\url{https://GitHub.com/bdonnot/lightsim2grid}}, + howpublished = {\url{https://GitHub.com/Grid2Op/lightsim2grid}}, } ``` @@ -269,7 +269,7 @@ using the Newton-Raphson algorithm, with a single slack bus, without enforcing q **NB** to run these tests you need to install grid2op from source otherwise all the test of the LightSim2gridBackend will fail. In order to do so you can do: ``` -git clone https://github.com/rte-france/Grid2Op.git +git clone https://github.com/Grid2Op/grid2op.git cd Grid2Op pip3 install -U -e . cd .. diff --git a/benchmarks/benchmark_grid_size.py b/benchmarks/benchmark_grid_size.py index 550c633..1d5578e 100644 --- a/benchmarks/benchmark_grid_size.py +++ b/benchmarks/benchmark_grid_size.py @@ -184,6 +184,53 @@ def get_loads_gens(load_p_init, load_q_init, gen_p_init, sgen_p_init, prng): return load_p, load_q, gen_p, sgen_p +def run_grid2op_env(env_lightsim, case, reset_solver, + solver_preproc_solver_time, + g2op_speeds, + g2op_step_time, + ls_solver_time, + ls_gridmodel_time, + g2op_sizes + ): + _ = env_lightsim.reset() + done = False + nb_step = 0 + changed_sgen = case.sgen["in_service"].values + while not done: + # hack for static gen... + changed_sgen = copy.deepcopy(case.sgen["in_service"].values) + this_sgen = sgen_p[nb_step, :].astype(np.float32) + # this_sgen = sgen_p_init[changed_sgen].astype(np.float32) + env_lightsim.backend._grid.update_sgens_p(changed_sgen, this_sgen) + obs, reward, done, info = env_lightsim.step(env_lightsim.action_space()) + if reset_solver: + env_lightsim.backend._grid.tell_solver_need_reset() + nb_step += 1 + + # NB lightsim2grid does not handle "static gen" because I cannot set "p" in gen in grid2op + # so results will vary between TimeSeries and grid2op ! + # env_lightsim.backend._grid.tell_solver_need_reset() + # env_lightsim.backend._grid.dc_pf(env_lightsim.backend.V, 1, 1e-7) + # env_lightsim.backend._grid.get_bus_status() + if nb_step != nb_ts: + warnings.warn(f"only able to make {nb_step} (out of {nb_ts}) for {case_name} in grid2op. Results will not be availabe for grid2op step") + solver_preproc_solver_time.append(None) + g2op_speeds.append(None) + g2op_step_time.append(None) + ls_solver_time.append(None) + ls_gridmodel_time.append(None) + else: + total_time = env_lightsim.backend._timer_preproc + env_lightsim.backend._timer_solver # + env_lightsim.backend._timer_postproc + # total_time = env_lightsim._time_step + solver_preproc_solver_time.append(total_time) + g2op_speeds.append(1.0 * nb_step / total_time) + g2op_step_time.append(1.0 * env_lightsim._time_step / nb_step) + ls_solver_time.append(env_lightsim.backend.comp_time) + ls_gridmodel_time.append(env_lightsim.backend.timer_gridmodel_xx_pf) + g2op_sizes.append(env_lightsim.n_sub) + return nb_step + + if __name__ == "__main__": prng = np.random.default_rng(42) case_names_displayed = [get_env_name_displayed(el) for el in case_names] @@ -194,6 +241,13 @@ def get_loads_gens(load_p_init, load_q_init, gen_p_init, sgen_p_init, prng): ls_solver_time = [] ls_gridmodel_time = [] + solver_preproc_solver_time_reset = [] + g2op_speeds_reset = [] + g2op_sizes_reset = [] + g2op_step_time_reset = [] + ls_solver_time_reset = [] + ls_gridmodel_time_reset = [] + ts_times = [] ts_speeds = [] ts_sizes = [] @@ -269,40 +323,25 @@ def get_loads_gens(load_p_init, load_q_init, gen_p_init, sgen_p_init, prng): gen_p_g2op, sgen_p) # Perform the computation using grid2op - _ = env_lightsim.reset() - done = False - nb_step = 0 - changed_sgen = case.sgen["in_service"].values - while not done: - # hack for static gen... - changed_sgen = copy.deepcopy(case.sgen["in_service"].values) - this_sgen = sgen_p[nb_step, :].astype(np.float32) - # this_sgen = sgen_p_init[changed_sgen].astype(np.float32) - env_lightsim.backend._grid.update_sgens_p(changed_sgen, this_sgen) - obs, reward, done, info = env_lightsim.step(env_lightsim.action_space()) - nb_step += 1 - # NB lightsim2grid does not handle "static gen" because I cannot set "p" in gen in grid2op - # so results will vary between TimeSeries and grid2op ! - # env_lightsim.backend._grid.tell_solver_need_reset() - # env_lightsim.backend._grid.dc_pf(env_lightsim.backend.V, 1, 1e-7) - # env_lightsim.backend._grid.get_bus_status() - if nb_step != nb_ts: - warnings.warn(f"only able to make {nb_step} (out of {nb_ts}) for {case_name} in grid2op. Results will not be availabe for grid2op step") - solver_preproc_solver_time.append(None) - g2op_speeds.append(None) - g2op_step_time.append(None) - ls_solver_time.append(None) - ls_gridmodel_time.append(None) - g2op_sizes.append(env_lightsim.n_sub) - else: - total_time = env_lightsim.backend._timer_preproc + env_lightsim.backend._timer_solver # + env_lightsim.backend._timer_postproc - # total_time = env_lightsim._time_step - solver_preproc_solver_time.append(total_time) - g2op_speeds.append(1.0 * nb_step / total_time) - g2op_step_time.append(1.0 * env_lightsim._time_step / nb_step) - ls_solver_time.append(env_lightsim.backend.comp_time) - ls_gridmodel_time.append(env_lightsim.backend.timer_gridmodel_xx_pf) - g2op_sizes.append(env_lightsim.n_sub) + reset_solver = True # non default + nb_step_reset = run_grid2op_env(env_lightsim, case, reset_solver, + solver_preproc_solver_time_reset, + g2op_speeds_reset, + g2op_step_time_reset, + ls_solver_time_reset, + ls_gridmodel_time_reset, + g2op_sizes_reset + ) + + reset_solver = False # default + nb_step = run_grid2op_env(env_lightsim, case, reset_solver, + solver_preproc_solver_time, + g2op_speeds, + g2op_step_time, + ls_solver_time, + ls_gridmodel_time, + g2op_sizes + ) # Perform the computation using TimeSerie env_lightsim.reset() @@ -362,8 +401,36 @@ def get_loads_gens(load_p_init, load_q_init, gen_p_init, sgen_p_init, prng): print_configuration() print(f"Solver used for linear algebra: {linear_solver_used_str}") print() + + print("Results using grid2op.steps (288 consecutive steps, only measuring 'dc pf [init] + ac pf') (no recycling allowed, non default)") + tab_g2op = [] + for i, nm_ in enumerate(case_names_displayed): + tab_g2op.append((nm_, + ts_sizes[i], + 1000. * g2op_step_time_reset[i] if g2op_step_time_reset[i] else None, + 1000. / g2op_speeds_reset[i] if g2op_speeds_reset[i] else None, + g2op_speeds_reset[i], + 1000. * ls_gridmodel_time_reset[i] / nb_step_reset if ls_gridmodel_time_reset[i] else None, + 1000. * ls_solver_time_reset[i] / nb_step_reset if ls_solver_time_reset[i] else None, + )) + if TABULATE_AVAIL: + res_use_with_grid2op_2 = tabulate(tab_g2op, + headers=["grid", + "size (nb bus)", + "avg step duration (ms)", + "time [DC + AC] (ms / pf)", + "speed (pf / s)", + "time in 'gridmodel' (ms / pf)", + "time in 'pf algo' (ms / pf)", + ], + tablefmt="rst") + print(res_use_with_grid2op_2) + else: + print(tab_g2op) + print() + - print("Results using grid2op.steps (288 consecutive steps, only measuring 'dc pf [init] + ac pf')") + print("Results using grid2op.steps (288 consecutive steps, only measuring 'dc pf [init] + ac pf') (recyling allowed, default)") tab_g2op = [] for i, nm_ in enumerate(case_names_displayed): tab_g2op.append((nm_, @@ -390,7 +457,6 @@ def get_loads_gens(load_p_init, load_q_init, gen_p_init, sgen_p_init, prng): print(tab_g2op) print() - print("Results for TimeSeries (288 consecutive steps)") tab_ts = [] for i, nm_ in enumerate(case_names_displayed): diff --git a/docs/benchmarks.rst b/docs/benchmarks.rst index e8d30df..b1a35d3 100644 --- a/docs/benchmarks.rst +++ b/docs/benchmarks.rst @@ -365,7 +365,7 @@ The results can be found in: .. note:: Any contribution here is more than welcomed. You can write a github discussion here - https://github.com/BDonnot/lightsim2grid/discussions/new?category=show-and-tell + https://github.com/Grid2Op/lightsim2grid/discussions/new?category=show-and-tell and describe rapidly your setup and we'll make sure to include your benchmark in future release. Thanks ! diff --git a/docs/benchmarks_grid_sizes.rst b/docs/benchmarks_grid_sizes.rst new file mode 100644 index 0000000..dd890aa --- /dev/null +++ b/docs/benchmarks_grid_sizes.rst @@ -0,0 +1,269 @@ + +Benchmarks (grid size) +====================== + +In this paragraph we will expose some brief benchmarks about the use of lightsim2grid in the grid2op settings. +The code to run these benchmarks are given with this package int the [benchmark](./benchmarks) folder. + +TODO DOC in progress + +If you are interested in other type of benchmark, let us know ! + +TL;DR +------- + +In summary, lightsim2grid (when using KLU linear solver) perfomances are: + +================ =============== =================== ===================== ===================== ================================ +grid name size (nb bus) time (recycling) time (no recycling) time (`TimeSerie`) time (`ContingencyAnalysis`) +================ =============== =================== ===================== ===================== ================================ +case14 14 0.0303807 0.0688201 0.00972245 0.0344761 +case118 118 0.167014 0.383771 0.0651537 0.0940448 +case_illinois200 200 0.366178 0.747475 0.152984 0.251852 +case300 300 0.592916 1.21181 0.379875 0.467905 +case1354pegase 1354 3.13735 5.17859 1.58152 2.01299 +case1888rte 1888 4.78187 7.58089 2.08743 2.72081 +case2848rte 2848 7.49326 12.0294 3.22694 4.19178 +case2869pegase 2869 7.06508 12.0486 3.64617 4.62894 +case3120sp 3120 8.54887 13.4784 3.04654 4.64494 +case6495rte 6495 26.4778 37.8204 10.9002 12.5037 +case6515rte 6515 29.8737 42.66 11.38 12.9684 +case9241pegase 9241 36.0544 55.4857 16.6537 20.0572 +================ =============== =================== ===================== ===================== ================================ + +All timings reported above are in milliseconds (ms) for one powerflow (in all cases lots of powerflow are carried out, up to a thousands +and the timings here are averaged accross all the powerflows performed) + +For detailed explanation about each column as well as the hardware used, please refer to the section below, but in summary: + +- benchmark were run on python 3.12 with an old laptop (i7-6820HQ CPU @ 2.70GHz from 2015) see section :ref:`bench_grid_size_hardware` + for more information +- `time (recycling)` indicates the average time it took to run 1 powerflow (with consecutive run of 288 powerflows) + while allowing lighsim2grid to re use some basic previous computation from one powerflow to another. This is the most consommations + usecase in grid2op for example (default behaviour). See :ref:`bench_grid_size_glop` for more information +- `time (no recycling)` indicates the same average time as aboved but lightsim2grid is forced to restart the + computation from scratch each time, as if it was a completely different grid on a completely different computers. + See :ref:`bench_grid_size_glop` for more information. +- `time (TimeSerie)` reports the time it takes to run one powerflow using the lightsim2grid `TimeSerie` module, were + everything is in c++ and some care has been taken to improve the performance (reuse of as many things as possible, + carefull memory allocation, etc.). See :ref:`bench_grid_size_ts` for more information. +- `time (ContingencyAnalysis)` reports the time it takes to run one powerflow using the lightsim2grid `ContingencyAnalysis` module, were + everything is in c++ and some care has been taken to improve the performance (reuse of as many things as possible, + carefull memory allocation, etc.). See :ref:`bench_grid_size_ca` for more information. **NB** on this settings, + as opposed to the others, the grid production / generations stay the same, but the grid topology changes by the + connection and disconnection of powerlines. + +.. _bench_grid_size_hardware: + +Using a grid2op environment +---------------------------- +In this section we perform some benchmark of a `do nothing` agent to test the raw performance of lightsim2grid +on different grid sizes varying from the ieee case 14 grid (14 buses) up to the pegase 9241 grid (case9241 from pandapower +counting 9241 buses). + +All of them has been run on a computer with a the following characteristics: + +- date: 2024-10-18 09:35 CEST +- system: Linux 5.15.0-56-generic +- OS: ubuntu 20.04 +- processor: Intel(R) Core(TM) i7-6820HQ CPU @ 2.70GHz +- python version: 3.12.4.final.0 (64 bit) +- numpy version: 1.26.4 +- pandas version: 2.2.3 +- pandapower version: 2.14.10 +- grid2op version: 1.10.5 +- lightsim2grid version: 0.9.2 +- lightsim2grid extra information: + + - klu_solver_available: True + - nicslu_solver_available: True + - cktso_solver_available: True + - compiled_march_native: True + - compiled_o3_optim: True + +Solver used for linear algebra: NR single (KLU) + + +To run the benchmark `cd` in the [benchmark](./benchmarks) folder and type: + +.. code-block:: bash + + python benchmark_grid_size.py + +(results may vary depending on the hard drive, the ram etc. and are presented here for illustration only) + +(we remind that these simulations correspond to simulation on one core of the CPU. Of course it is possible to +make use of all the available cores, which would increase the number of steps that can be performed) + +.. _bench_grid_size_glop: + +Computation time using grid2op +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This benchmark in doing repeat calls to `env.step(do_nothing)` (usually 288) for a given environment build +on a grid coming from data available in pandapower. + +Then we compare different measurments: + +- `avg step duration (ms)` is the average time it takes to perform the `grid2op.step`. It is given in milliseconds (ms). + It takes into account the time to read the data, to feed the data to the underlying c++ model, to run the powerflow + and to read back the data from the c++ model. +- `time [DC + AC] (ms / pf)` is the time it takes to perform the entire powerflow, which consists in first + providing an initial guess (DC approximation) and then to compute the powerflow. As compared to the + above timings, it only take into account the time to run the powerflow. This "time to run the powerflow" + can be at this stage decomposed in: + + - converting the provided data into valid matrix / vector to run a DC powerflow + - computing a DC powerflow (used to initialize the AC powerflow) + - converting again the provided data into valid matrix / vector to run an AC powerflow + - computint the AC Powerflow + - post processing the internal data (which includes *eg* the flows on the lines in amps, the reactive value + produced / absorbed by each generator etc.) + +- `time in 'gridmodel' (ms / pf)` gives the time it takes to only perform the AC powerflow: + + - converting the provided data into valid matrix / vector to run an AC powerflow + - computing the AC Powerflow + - post processing the internal data (which includes *eg* the flows on the lines in amps, the reactive value + produced / absorbed by each generator etc.) + +- `time in 'pf algo' (ms / pf)` gives the time spent in the algorithm that computes the AC powerflow only + +The results are given in two tables: + +- the first one corresponds to the default settings were lightsim2grid is allowed to "recycle" previous + results, which is the default in grid2op and lightsim2grid. This corresponds to a generic grid2op usecase. +- the second one is the same run for the same environment, but this time lightsim2grid recreate everything from + scratch each time, the "recycling" is deactivated. + +The main impact on "recycling" is that, when activated (default), lightsim2grid can skip some of its internal +computation, especially in the steps: + +- "converting the provided data into valid matrix / vector to run a DC powerflow" +- "converting again the provided data into valid matrix / vector to run an AC powerflow" +- also the computation of the DC and AC powerflows can be a little bit faster (depending on the linear solver used) + +The "no recycling" strategy is closer to a situation were you would simulate different powerflows on +different cores or even on different computers and cannot share the internal state of the solvers (for example). +It can also represent a situation were you would run powerflows for vastly different grids one after +the other. + + +Results using grid2op.steps (288 consecutive steps, only measuring 'dc pf [init] + ac pf') (recyling allowed, default) + +================ =============== ======================== ========================== ================ =============================== ============================= +grid size (nb bus) avg step duration (ms) time [DC + AC] (ms / pf) speed (pf / s) time in 'gridmodel' (ms / pf) time in 'pf algo' (ms / pf) +================ =============== ======================== ========================== ================ =============================== ============================= +case14 14 0.758799 0.0597669 16731.7 0.0303807 0.0250171 +case118 118 0.913219 0.211025 4738.78 0.167014 0.149728 +case_illinois200 200 1.18555 0.424583 2355.25 0.366178 0.340139 +case300 300 1.44624 0.661998 1510.58 0.592916 0.557392 +case1354pegase 1354 5.26387 3.37046 296.695 3.13735 2.9635 +case1888rte 1888 6.32057 5.04453 198.234 4.78187 4.58628 +case2848rte 2848 9.52315 7.88586 126.809 7.49326 7.19927 +case2869pegase 2869 10.428 7.51632 133.044 7.06508 6.70432 +case3120sp 3120 10.6149 9.01426 110.935 8.54887 8.24586 +case6495rte 6495 30.5814 27.5533 36.2933 26.4778 25.6759 +case6515rte 6515 34.0398 30.9591 32.3007 29.8737 29.0781 +case9241pegase 9241 46.1182 37.7921 26.4606 36.0544 34.7085 +================ =============== ======================== ========================== ================ =============================== ============================= + +Results using grid2op.steps (288 consecutive steps, only measuring 'dc pf [init] + ac pf') (**no recycling allowed**, non default) + +================ =============== ======================== ========================== ================ =============================== ============================= +grid size (nb bus) avg step duration (ms) time [DC + AC] (ms / pf) speed (pf / s) time in 'gridmodel' (ms / pf) time in 'pf algo' (ms / pf) +================ =============== ======================== ========================== ================ =============================== ============================= +case14 14 0.777772 0.119986 8334.27 0.0688201 0.0567457 +case118 118 1.26015 0.531649 1880.94 0.383771 0.343062 +case_illinois200 200 1.77514 0.961583 1039.95 0.747475 0.688786 +case300 300 2.39949 1.52385 656.232 1.21181 1.12254 +case1354pegase 1354 8.08618 6.32786 158.031 5.17859 4.75853 +case1888rte 1888 10.3294 9.00365 111.066 7.58089 7.0991 +case2848rte 2848 16.0491 14.2892 69.9832 12.0294 11.2664 +case2869pegase 2869 17.6752 14.6977 68.0376 12.0486 11.0712 +case3120sp 3120 17.6044 15.9006 62.8906 13.4784 12.7485 +case6495rte 6495 46.697 43.6531 22.9079 37.8204 35.8113 +case6515rte 6515 51.8558 48.7368 20.5184 42.66 40.588 +case9241pegase 9241 74.1648 65.6422 15.2341 55.4857 51.7239 +================ =============== ======================== ========================== ================ =============================== ============================= + + +.. _bench_grid_size_ts: + +Computation time using the lightsim2grid `TimeSerie` module +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +As opposed to the experiment above, the `TimeSerie` lightsim2grid module allows to perform sequential computation +of varying productions and loads with the exact same grid topology. + +This does not rely on grid2op and is coded in "pure c++" still using one single CPU core. It should be faster than +the timings reported on the above sequence because: + +- the loop is made in c++ instead of python +- the code has been optimize to run faster and "recycle" as many things as possible: the + matrices representing the grid is computed only once, it is factorized only once, + conversion from the internal solver representation to MW, MVAr and A is done in + a vectorized way etc. + +This rapidity has a cost, it is much less flexible. With the grid2op framework an "agent" +can do a lot of different actions (even though "do nothing" was used for the benchmark). Here +on the other hand, only a "*do nothing*" action can be performed (and without emulation of +any kind of protections). + +The column `time (ms / pf)` can be compared with the column `time [DC + AC] (ms / pf)` of the +table in the previous benchmark. + +================ =============== ================ ================ +grid size (nb bus) time (ms / pf) speed (pf / s) +================ =============== ================ ================ +case14 14 0.00972245 102855 +case118 118 0.0651537 15348.3 +case_illinois200 200 0.152984 6536.64 +case300 300 0.379875 2632.45 +case1354pegase 1354 1.58152 632.305 +case1888rte 1888 2.08743 479.059 +case2848rte 2848 3.22694 309.891 +case2869pegase 2869 3.64617 274.26 +case3120sp 3120 3.04654 328.241 +case6495rte 6495 10.9002 91.7417 +case6515rte 6515 11.38 87.8737 +case9241pegase 9241 16.6537 60.0467 +================ =============== ================ ================ + +.. _bench_grid_size_ca: + +Computation time using the lightsim2grid `ContingencyAnalysis` module +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +As opposed to the benchmarks reported in the previous two sections, this benchmark +is focused on the `ContingencyAnalysis` lightsim2grid module. + +A "contingency analysis" is often carried out in power system. The objective is +to assess whether or not the current grid state is safe if one (or more) +powerline would be disconnected. It uses the same +productions / consommations for each computation. Each time it disconnects +one or more powerlines, run the powerflow and then stores the results. + +For this benchmark we focus on disconnecting only one powerline (though +lightsim2grid offers the possibility to disconnect as many as you want) with +a limit on 1000 contingency simulated (even for grid were there would be +more than 1000 powerlines / trafos to disconnect we limit the computation to +only 1000). + +================ =============== =================== =================== +grid size (nb bus) time (ms / cont.) speed (cont. / s) +================ =============== =================== =================== +case14 14 0.0344761 29005.6 +case118 118 0.0940448 10633.2 +case_illinois200 200 0.251852 3970.58 +case300 300 0.467905 2137.18 +case1354pegase 1354 2.01299 496.774 +case1888rte 1888 2.72081 367.537 +case2848rte 2848 4.19178 238.562 +case2869pegase 2869 4.62894 216.032 +case3120sp 3120 4.64494 215.288 +case6495rte 6495 12.5037 79.9763 +case6515rte 6515 12.9684 77.1104 +case9241pegase 9241 20.0572 49.8575 +================ =============== =================== =================== + diff --git a/docs/conf.py b/docs/conf.py index c739668..4812d14 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -22,7 +22,7 @@ author = 'Benjamin DONNOT' # The full version, including alpha/beta/rc tags -release = "0.9.2.dev0" +release = "0.9.2" version = '0.9' # -- General configuration --------------------------------------------------- diff --git a/docs/disclaimer.rst b/docs/disclaimer.rst index f8091f3..ebece10 100644 --- a/docs/disclaimer.rst +++ b/docs/disclaimer.rst @@ -3,8 +3,9 @@ Disclaimer Disclaimer ---------- + This disclaimer only serves as a complement to the -`LICENSE `_ file provided with it. It can not serve as a +`LICENSE `_ file provided with it. It can not serve as a replacement of this file. The simulator implemented in this package is made for speed mainly to serve as a grid2op backend (see diff --git a/docs/index.rst b/docs/index.rst index 66f4dcd..aae3a5e 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -6,7 +6,7 @@ Welcome to LightSim2Grid's documentation! ========================================= LightSim2Grid is a simulator that can be used to accelerate the computation (leading to approximately the -same results) when using the `Grid2Op `_ platform. +same results) when using the `Grid2Op `_ platform. It is a port in c++ of some part of the excellent `PandaPower `_ package to make the computation faster. @@ -29,6 +29,7 @@ As from version 0.5.3: disclaimer use_with_grid2op benchmarks + benchmarks_grid_sizes use_solver rewards physical_law_checker diff --git a/docs/install_from_source.rst b/docs/install_from_source.rst index c44a00a..d242a7c 100644 --- a/docs/install_from_source.rst +++ b/docs/install_from_source.rst @@ -40,7 +40,7 @@ First, you can download it with git with: .. code-block:: - git clone https://github.com/BDonnot/lightsim2grid.git + git clone https://github.com/Grid2Op/lightsim2grid.git cd lightsim2grid # it is recommended to do a python virtual environment python -m virtualenv venv # optional diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 7ab2224..e2549aa 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -102,7 +102,7 @@ which was, at time of writing: install it by typing `brew install llvm` in a terminal. We do not cover in this installation guide how to install such compiler. But if you have any issue, -feel free to send us a `github issue `_ and we will +feel free to send us a `github issue `_ and we will do our best to answer. Install python and git @@ -148,7 +148,7 @@ First, you can download it with git with: .. code-block:: bash - git clone https://github.com/BDonnot/lightsim2grid.git + git clone https://github.com/Grid2Op/lightsim2grid.git cd lightsim2grid # it is recommended to do a python virtual environment python -m virtualenv venv # optional diff --git a/docs/solvers.rst b/docs/solvers.rst index 7f6ef75..8ff8998 100644 --- a/docs/solvers.rst +++ b/docs/solvers.rst @@ -103,8 +103,8 @@ Usage -------------------------- In this section we briefly explain how to switch from one solver to another. An example of code using this feature is given in the -`"benchmark_solvers.py" `_ -script available in the `"benchmarks" `_ +`"benchmark_solvers.py" `_ +script available in the `"benchmarks" `_ directory of the lightsim2grid repository. To change the solver used by the backend, the preferred solution is to set it once you create it: diff --git a/docs/use_with_grid2op.rst b/docs/use_with_grid2op.rst index 66f45cf..d22309d 100644 --- a/docs/use_with_grid2op.rst +++ b/docs/use_with_grid2op.rst @@ -12,7 +12,7 @@ can use it transparently. **NB** By default, the fastest resolution method (among KLU and SparseLU linear solver is used). For now it is not easy to change it. If this is of any interest for you, please let us know with a -`feature request `_ +`feature request `_ Regular environments diff --git a/lightsim2grid/__init__.py b/lightsim2grid/__init__.py index 0e2f352..e8001c0 100644 --- a/lightsim2grid/__init__.py +++ b/lightsim2grid/__init__.py @@ -6,7 +6,7 @@ # SPDX-License-Identifier: MPL-2.0 # This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. -__version__ = "0.9.2.dev0" +__version__ = "0.9.2" __all__ = ["newtonpf", "SolverType", "ErrorType", "solver", "compilation_options"] diff --git a/lightsim2grid/lightSimBackend.py b/lightsim2grid/lightSimBackend.py index d6f29d2..0317c1c 100644 --- a/lightsim2grid/lightSimBackend.py +++ b/lightsim2grid/lightSimBackend.py @@ -1162,7 +1162,7 @@ def apply_action(self, backendAction: Union["grid2op.Action._backendAction._Back self._grid.update_loads_q(backendAction.load_q.changed, backendAction.load_q.values) except RuntimeError as exc_: - # see https://github.com/BDonnot/lightsim2grid/issues/66 (even though it's not a "bug" and has not been replicated) + # see https://github.com/Grid2Op/lightsim2grid/issues/66 (even though it's not a "bug" and has not been replicated) raise BackendError(f"{exc_}") from exc_ if self.__has_storage: diff --git a/lightsim2grid/tests/test_SameResPP.py b/lightsim2grid/tests/test_SameResPP.py index dd995c2..e865960 100644 --- a/lightsim2grid/tests/test_SameResPP.py +++ b/lightsim2grid/tests/test_SameResPP.py @@ -385,7 +385,7 @@ 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 + b_t *= 1j # to fix https://github.com/Grid2Op/lightsim2grid/issues/88 # check where there are mismatch if any val_r_pp = r_t diff --git a/lightsim2grid/tests/test_integration_pandapower.py b/lightsim2grid/tests/test_integration_pandapower.py index d2c1a9c..88b9a95 100644 --- a/lightsim2grid/tests/test_integration_pandapower.py +++ b/lightsim2grid/tests/test_integration_pandapower.py @@ -82,7 +82,7 @@ def setUp(self) -> None: return super().setUp() def _aux_test_case(self, case=None, nminus1_cases=None): - """test inspired from the test provided in the github issue https://github.com/BDonnot/lightsim2grid/issues/88#issuecomment-2265150641 + """test inspired from the test provided in the github issue https://github.com/Grid2Op/lightsim2grid/issues/88#issuecomment-2265150641 linked https://github.com/pawellytaev/pandapower/blob/da4b5ae6acd42e75dd37ef20d4dcbd823fef48d3/pandapower/test/contingency/test_contingency.py#L156""" if case is None: case = pp.networks.case118() diff --git a/lightsim2grid/tests/test_n1contingencyrewards.py b/lightsim2grid/tests/test_n1contingencyrewards.py index 8485291..a072c1a 100644 --- a/lightsim2grid/tests/test_n1contingencyrewards.py +++ b/lightsim2grid/tests/test_n1contingencyrewards.py @@ -63,7 +63,7 @@ def l_ids(self): def setUp(self) -> None: import sys if sys.platform == 'win32': - self.skipTest("Not working, see issue https://github.com/BDonnot/lightsim2grid/issues/85") + self.skipTest("Not working, see issue https://github.com/Grid2Op/lightsim2grid/issues/85") reward = N1ContingencyReward(dc=self.is_dc(), threshold_margin=self.threshold_margin(), l_ids=self.l_ids()) diff --git a/setup.py b/setup.py index 615ad60..601a779 100644 --- a/setup.py +++ b/setup.py @@ -14,7 +14,7 @@ from pybind11.setup_helpers import Pybind11Extension, build_ext -__version__ = "0.9.2.dev0" +__version__ = "0.9.2" KLU_SOLVER_AVAILABLE = False # Try to link against SuiteSparse (if available) @@ -377,7 +377,7 @@ version=__version__, author='Benjamin Donnot', author_email='benjamin.donnot@rte-france.com', - url='https://github.com/BDonnot/lightsim2grid/', + url='https://github.com/grid2op/lightsim2grid/', description='LightSim2Grid implements a c++ backend targeting the Grid2Op platform.', long_description=long_description, long_description_content_type='text/markdown', diff --git a/src/linear_solvers/CKTSOSolver.cpp b/src/linear_solvers/CKTSOSolver.cpp index 11d8756..34525a0 100644 --- a/src/linear_solvers/CKTSOSolver.cpp +++ b/src/linear_solvers/CKTSOSolver.cpp @@ -20,7 +20,7 @@ ErrorType CKTSOLinearSolver::reset(){ if(ai_ != nullptr) delete [] ai_; if(ap_ != nullptr) delete [] ap_; - // should not be deleted, see https://github.com/BDonnot/lightsim2grid/issues/52#issuecomment-1333565959 + // should not be deleted, see https://github.com/Grid2Op/lightsim2grid/issues/52#issuecomment-1333565959 // if(iparm_!= nullptr) delete iparm_; // if(oparm_!= nullptr) delete oparm_; diff --git a/src/linear_solvers/CKTSOSolver.h b/src/linear_solvers/CKTSOSolver.h index 5e94829..c620458 100644 --- a/src/linear_solvers/CKTSOSolver.h +++ b/src/linear_solvers/CKTSOSolver.h @@ -22,7 +22,7 @@ /** class to handle the solver using newton-raphson method, using CKTSO algorithm and sparse matrices. -CKTSO, according to https://github.com/BDonnot/lightsim2grid/issues/52 +CKTSO, according to https://github.com/Grid2Op/lightsim2grid/issues/52 is the successor of NICSLU. As long as the admittance matrix of the system does not change, you can reuse the same solver. @@ -53,7 +53,7 @@ class CKTSOLinearSolver if(ai_!= nullptr) delete [] ai_; if(ap_!= nullptr) delete [] ap_; - // should not be deleted, see https://github.com/BDonnot/lightsim2grid/issues/52#issuecomment-1333565959 + // should not be deleted, see https://github.com/Grid2Op/lightsim2grid/issues/52#issuecomment-1333565959 // if(iparm_!= nullptr) delete iparm_; // if(oparm_!= nullptr) delete oparm_; diff --git a/utils/Dockerfile b/utils/Dockerfile index d111284..7e51c09 100644 --- a/utils/Dockerfile +++ b/utils/Dockerfile @@ -29,7 +29,7 @@ RUN apt-get update && \ RUN pip3 install -U grid2op[optional] pybind11 # install lightsim -RUN git clone --recurse-submodules https://github.com/BDonnot/lightsim2grid.git +RUN git clone --recurse-submodules https://github.com/Grid2Op/lightsim2grid.git WORKDIR /lightsim2grid RUN git pull RUN git remote update diff --git a/utils/Dockerfile_light b/utils/Dockerfile_light index b963a58..6b6adea 100644 --- a/utils/Dockerfile_light +++ b/utils/Dockerfile_light @@ -29,7 +29,7 @@ RUN apt-get update && \ RUN pip3 install -U grid2op pybind11 # install lightsim -RUN git clone --recurse-submodules https://github.com/BDonnot/lightsim2grid.git +RUN git clone --recurse-submodules https://github.com/Grid2Op/lightsim2grid.git WORKDIR /lightsim2grid RUN git pull RUN git remote update diff --git a/utils/Dockerfile_test b/utils/Dockerfile_test index d464972..d08aada 100644 --- a/utils/Dockerfile_test +++ b/utils/Dockerfile_test @@ -36,7 +36,7 @@ RUN pip3 install -e .[optional] WORKDIR / # install lightsim -RUN git clone --recurse-submodules https://github.com/BDonnot/lightsim2grid.git +RUN git clone --recurse-submodules https://github.com/Grid2Op/lightsim2grid.git WORKDIR /lightsim2grid RUN git pull RUN git remote update From 26d4903149bf4ad42c3d8e550cada76ed67509fe Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Fri, 18 Oct 2024 15:56:36 +0200 Subject: [PATCH 3/4] fixing a bug in LightSimBackend for grid2op compat version Signed-off-by: DONNOT Benjamin --- lightsim2grid/lightSimBackend.py | 128 ++++++++++++++++++++----------- 1 file changed, 82 insertions(+), 46 deletions(-) diff --git a/lightsim2grid/lightSimBackend.py b/lightsim2grid/lightSimBackend.py index 0317c1c..9bde6e9 100644 --- a/lightsim2grid/lightSimBackend.py +++ b/lightsim2grid/lightSimBackend.py @@ -57,7 +57,11 @@ class LightSimBackend(Backend): shunts_data_available = True glop_version = Backend.glop_version if hasattr(Backend, "glop_version") else grid2op.__version__ - + if not hasattr(Backend, "n_busbar_per_sub"): + # for legacy grid2op + n_busbar_per_sub = DEFAULT_N_BUSBAR_PER_SUB + + def __init__(self, detailed_infos_for_cascading_failures: bool=False, can_be_copied: bool=True, @@ -673,8 +677,10 @@ def _load_grid_pypowsybl(self, path=None, filename=None): self.gen_to_subid = np.array(this_gen_sub, dtype=dt_int) self.line_or_to_subid = np.concatenate((this_lor_sub, this_tor_sub)).astype(dt_int) self.line_ex_to_subid = np.concatenate((this_lex_sub, this_tex_sub)).astype(dt_int) - self.storage_to_subid = np.array(this_batt_sub, dtype=dt_int) - self.shunt_to_subid = np.array(this_sh_sub, dtype=dt_int) + if self.__has_storage: + self.storage_to_subid = np.array(this_batt_sub, dtype=dt_int) + if self.n_shunt is not None: + self.shunt_to_subid = np.array(this_sh_sub, dtype=dt_int) else: # consider effectively that each "voltage_levels" in powsybl grid # is a substation (in the underlying gridmodel) @@ -686,9 +692,11 @@ def _load_grid_pypowsybl(self, path=None, filename=None): self.gen_to_subid = np.array(gen_sub.values.ravel(), dtype=dt_int) self.line_or_to_subid = np.concatenate((lor_sub.values.ravel(), tor_sub.values.ravel())).astype(dt_int) self.line_ex_to_subid = np.concatenate((lex_sub.values.ravel(), tex_sub.values.ravel())).astype(dt_int) - self.storage_to_subid = np.array(batt_sub.values.ravel(), dtype=dt_int) + if self.__has_storage: + self.storage_to_subid = np.array(batt_sub.values.ravel(), dtype=dt_int) self.shunt_to_subid = np.array(sh_sub.values.ravel(), dtype=dt_int) - self.n_sub = grid_tmp.get_voltage_levels().shape[0] + if self.n_shunt is not None: + self.n_sub = grid_tmp.get_voltage_levels().shape[0] # the names use_grid2op_default_names = True @@ -1030,29 +1038,40 @@ def _aux_finish_setup_after_reading(self): self.storage_theta = np.full(cls.n_storage, dtype=dt_float, fill_value=np.NaN).reshape(-1) self._count_object_per_bus() - self.topo_vect[cls.load_pos_topo_vect] = cls.global_bus_to_local(np.array([el.bus_id for el in self._grid.get_loads()]), - cls.load_to_subid) - self.topo_vect[cls.gen_pos_topo_vect] = cls.global_bus_to_local(np.array([el.bus_id for el in self._grid.get_generators()]), - cls.gen_to_subid) - self.topo_vect[cls.storage_pos_topo_vect] = cls.global_bus_to_local(np.array([el.bus_id for el in self._grid.get_storages()]), - cls.storage_to_subid) - lor_glob_bus = np.concatenate((np.array([el.bus_or_id for el in self._grid.get_lines()]), - np.array([el.bus_hv_id for el in self._grid.get_trafos()]))) - self.topo_vect[cls.line_or_pos_topo_vect] = cls.global_bus_to_local(lor_glob_bus, - cls.line_or_to_subid) - lex_glob_bus = np.concatenate((np.array([el.bus_ex_id for el in self._grid.get_lines()]), - np.array([el.bus_lv_id for el in self._grid.get_trafos()]))) - self.topo_vect[cls.line_ex_pos_topo_vect] = cls.global_bus_to_local(lex_glob_bus, - cls.line_ex_to_subid) - self._grid.tell_solver_need_reset() - self.__me_at_init = self._grid.copy() - self.__init_topo_vect = np.ones(cls.dim_topo, dtype=dt_int) - self.__init_topo_vect[:] = self.topo_vect - if cls.shunts_data_available: - self.sh_bus[:] = cls.global_bus_to_local(np.array([el.bus_id for el in self._grid.get_shunts()]), - cls.shunt_to_subid) + # set the initial topology vector + n_sub_cls_orig = cls.n_sub + n_sub_ls_orig = LightSimBackend.n_sub + try: + cls.n_sub = self.n_sub + LightSimBackend.n_sub = self.n_sub + self.topo_vect[cls.load_pos_topo_vect] = cls.global_bus_to_local(np.array([el.bus_id for el in self._grid.get_loads()]), + cls.load_to_subid) + self.topo_vect[cls.gen_pos_topo_vect] = cls.global_bus_to_local(np.array([el.bus_id for el in self._grid.get_generators()]), + cls.gen_to_subid) + if self.__has_storage: + self.topo_vect[cls.storage_pos_topo_vect] = cls.global_bus_to_local(np.array([el.bus_id for el in self._grid.get_storages()]), + cls.storage_to_subid) + lor_glob_bus = np.concatenate((np.array([el.bus_or_id for el in self._grid.get_lines()]), + np.array([el.bus_hv_id for el in self._grid.get_trafos()]))) + self.topo_vect[cls.line_or_pos_topo_vect] = cls.global_bus_to_local(lor_glob_bus, + cls.line_or_to_subid) + lex_glob_bus = np.concatenate((np.array([el.bus_ex_id for el in self._grid.get_lines()]), + np.array([el.bus_lv_id for el in self._grid.get_trafos()]))) + self.topo_vect[cls.line_ex_pos_topo_vect] = cls.global_bus_to_local(lex_glob_bus, + cls.line_ex_to_subid) + self._grid.tell_solver_need_reset() + self.__me_at_init = self._grid.copy() + self.__init_topo_vect = np.ones(cls.dim_topo, dtype=dt_int) + self.__init_topo_vect[:] = self.topo_vect + if cls.shunts_data_available: + self.sh_bus[:] = cls.global_bus_to_local(np.array([el.bus_id for el in self._grid.get_shunts()]), + cls.shunt_to_subid) + finally: + cls.n_sub = n_sub_cls_orig + LightSimBackend.n_sub = n_sub_ls_orig + def assert_grid_correct_after_powerflow(self) -> None: """ This method is called by the environment. It ensure that the backend remains consistent even after a powerflow @@ -1069,10 +1088,11 @@ def assert_grid_correct_after_powerflow(self) -> None: self._backend_action_class = _BackendAction.init_grid(type(self)) self._init_action_to_set = self._backend_action_class() try: - # feature added in grid2op 1.4 or 1.5 _init_action_to_set = self.get_action_to_set() - except TypeError as exc_: - _init_action_to_set = self._get_action_to_set_deprecated() + except TypeError: + # I am in legacy grid2op version... + _init_action_to_set = _dont_use_get_action_to_set_legacy(self) + self._init_action_to_set += _init_action_to_set if self.prod_pu_to_kv is not None: assert np.isfinite(self.prod_pu_to_kv).all() @@ -1084,22 +1104,6 @@ def assert_grid_correct_after_powerflow(self) -> None: assert np.isfinite(self.lines_ex_pu_to_kv).all() if self.__has_storage and self.n_storage > 0 and self.storage_pu_to_kv is not None: assert np.isfinite(self.storage_pu_to_kv).all() - - def _get_action_to_set_deprecated(self): - warnings.warn("DEPRECATION: grid2op <=1.4 is not well supported with lightsim2grid. Lots of bugs have been" - "fixed since then. Please upgrade to grid2op >= 1.5", - DeprecationWarning) - line_status = self.get_line_status() - line_status = 2 * line_status - 1 - line_status = line_status.astype(dt_int) - topo_vect = self.get_topo_vect() - prod_p, _, prod_v = self.generators_info() - load_p, load_q, _ = self.loads_info() - complete_action_class = CompleteAction.init_grid(self) - set_me = complete_action_class() - set_me.update({"set_line_status": line_status, - "set_bus": topo_vect}) - return set_me def _count_object_per_bus(self): # should be called only when self.topo_vect and self.shunt_topo_vect are set @@ -1582,7 +1586,7 @@ def _compute_shunt_bus_with_compat(self, shunt_bus): # backward compat when this was not defined: n_busbar_per_sub = DEFAULT_N_BUSBAR_PER_SUB for i in range(n_busbar_per_sub): - res[(i * cls.n_sub <= shunt_bus) & (shunt_bus < (i+1) * cls.n_sub)] = i + 1 + res[(i * self.n_sub <= shunt_bus) & (shunt_bus < (i+1) * self.n_sub)] = i + 1 res[shunt_bus == -1] = -1 self.sh_bus[:] = res @@ -1625,3 +1629,35 @@ def reset(self, if type(self).shunts_data_available: self.sh_bus[:] = 1 # TODO self._compute_shunt_bus_with_compat(self._grid.get_all_shunt_buses()) self.topo_vect[:] = self.__init_topo_vect # TODO# + + +def _dont_use_global_bus_to_local_legacy(cls, global_bus: np.ndarray, to_sub_id: np.ndarray) -> np.ndarray: + res = (1 * global_bus).astype(dt_int) # make a copy + assert cls.n_busbar_per_sub >= 1, f"cls.n_busbar_per_sub should be >=1, found {cls.n_busbar_per_sub}" + assert cls.n_sub >= 1, f"cls.n_sub should be >=1, found {cls.n_sub}" + for i in range(cls.n_busbar_per_sub): + res[(i * cls.n_sub <= global_bus) & (global_bus < (i+1) * cls.n_sub)] = i + 1 + res[global_bus == -1] = -1 + return res + + +def _dont_use_get_action_to_set_legacy(self, *arg, **kwargs): + warnings.warn("DEPRECATION: grid2op <=1.4 is not well supported with lightsim2grid. Lots of bugs have been" + "fixed since then. Please upgrade to grid2op >= 1.10", + DeprecationWarning) + line_status = self.get_line_status() + line_status = 2 * line_status - 1 + line_status = line_status.astype(dt_int) + topo_vect = self.get_topo_vect() + prod_p, _, prod_v = self.generators_info() + load_p, load_q, _ = self.loads_info() + complete_action_class = CompleteAction.init_grid(self) + set_me = complete_action_class() + set_me.update({"set_line_status": line_status, + "set_bus": topo_vect}) + return set_me + + +if not hasattr(Backend, "global_bus_to_local"): + # for legacy grid2op + setattr(LightSimBackend, "global_bus_to_local", classmethod(_dont_use_global_bus_to_local_legacy)) From d22a07681c1e6ea065d628f073fd5e8836dc27b7 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Fri, 18 Oct 2024 16:10:59 +0200 Subject: [PATCH 4/4] fixing a bug in LightSimBackend for grid2op compat version Signed-off-by: DONNOT Benjamin --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5ce651d..d2d4d5a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -162,7 +162,7 @@ jobs: command: | source venv_test/bin/activate pip uninstall gym -y - pip install gymnasium + pip install "gymnasium<1" # issue with OrderedDict otherwise pip install "grid2op~=1.9.0" python -m unittest lightsim2grid/tests/test_compat_legacy_grid2op.py - run: @@ -210,7 +210,7 @@ jobs: command: | source venv_test/bin/activate pip uninstall gym -y - pip install gymnasium + pip install "gymnasium<1" # issue with OrderedDict otherwise pip install grid2op==1.9.0 python -m unittest lightsim2grid/tests/test_compat_legacy_grid2op.py - run: