diff --git a/.gitignore b/.gitignore index 50c39efed..d569d16f0 100644 --- a/.gitignore +++ b/.gitignore @@ -363,6 +363,7 @@ grid2op/data_test/test_issue_367/chronics/2050-01-03_7/generation_quality.json grid2op/data_test/test_issue_367/chronics/2050-01-03_7/maintenance_meta.json test_doc.py test_render.py +requirements_issue_389.txt # profiling files **.prof diff --git a/CHANGELOG.rst b/CHANGELOG.rst index d5ad03d5d..be7511c38 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -37,11 +37,18 @@ Change Log "human" has been removed because it needs some fixes. This should not impact lots of code. - [FIXED] a bug in `PandapowerBackend` when running in dc mode (voltages were not read correctly from the generators) +- [FIXED] issue https://github.com/rte-france/Grid2Op/issues/389 which was caused by 2 independant things + 1) the `PandapowerBackend` did not compute the `theta` correctly on powerline especially if + they are connected to a disconnected bus (in this case I chose to put `theta=0`) + 2) the `obs.as_networkx()` method did not check, when updating nodes attributes if powerlines + were connected or not, which was wrong in some cases - [IMPROVED] documentation of the gym `DiscreteActSpace`: it is now explicit that the "do nothing" action is by default encoded by `0` - [IMPROVED] documentation of `BaseObservation` and its attributes - [IMPROVED] `PandapowerBackend` can now be loaded even if the underlying grid does not converge in `AC` (but it should still converge in `DC`) see https://github.com/rte-france/Grid2Op/issues/391 +- [IMPROVED] `obs.as_networkx()` method: almost all powerlines attributes can now be read from the + resulting graph object. [1.8.1] - 2023-01-11 --------------------- diff --git a/grid2op/Backend/PandaPowerBackend.py b/grid2op/Backend/PandaPowerBackend.py index 5b4e76bda..cfb520799 100644 --- a/grid2op/Backend/PandaPowerBackend.py +++ b/grid2op/Backend/PandaPowerBackend.py @@ -1120,6 +1120,10 @@ def runpf(self, is_dc=False): self.v_ex[~self.line_status] = 0.0 self.v_or[:] *= self.lines_or_pu_to_kv self.v_ex[:] *= self.lines_ex_pu_to_kv + + # see issue https://github.com/rte-france/Grid2Op/issues/389 + self.theta_or[~np.isfinite(self.theta_or)] = 0.0 + self.theta_ex[~np.isfinite(self.theta_ex)] = 0.0 self._nb_bus_before = None self._grid._ppc["gen"][self._iref_slack, 1] = 0.0 diff --git a/grid2op/Observation/baseObservation.py b/grid2op/Observation/baseObservation.py index 3265b0659..a6f64b065 100644 --- a/grid2op/Observation/baseObservation.py +++ b/grid2op/Observation/baseObservation.py @@ -2042,6 +2042,7 @@ def as_networkx(self): assert abs(q_line - q_) <= 1e-5, "error for kirchoff's law for graph for Q" """ + cls = type(self) # TODO save this graph somewhere, in a self._as_networkx attributes for example mat_p, (load_bus, gen_bus, stor_bus, lor_bus, lex_bus) = self.flow_bus_matrix( active_flow=True, as_csr_matrix=True @@ -2053,15 +2054,18 @@ def as_networkx(self): # bus voltage bus_v = np.zeros(mat_p.shape[0]) - bus_v[lor_bus] = self.v_or - bus_v[lex_bus] = self.v_ex + # i need to put lor_bus[self.line_status] otherwise pandapower might not detect a line + # is disconnected and output the "wrong" voltage / theta in the graph + # see issue https://github.com/rte-france/Grid2Op/issues/389 + bus_v[lor_bus[self.line_status]] = self.v_or[self.line_status] + bus_v[lex_bus[self.line_status]] = self.v_ex[self.line_status] bus_theta = np.zeros(mat_p.shape[0]) bus_subid = np.zeros(mat_p.shape[0], dtype=dt_int) - bus_subid[lor_bus] = self.line_or_to_subid - bus_subid[lex_bus] = self.line_ex_to_subid + bus_subid[lor_bus[self.line_status]] = cls.line_or_to_subid[self.line_status] + bus_subid[lex_bus[self.line_status]] = cls.line_ex_to_subid[self.line_status] if self.support_theta: - bus_theta[lor_bus] = self.theta_or - bus_theta[lex_bus] = self.theta_ex + bus_theta[lor_bus[self.line_status]] = self.theta_or[self.line_status] + bus_theta[lex_bus[self.line_status]] = self.theta_ex[self.line_status] # bus active injection bus_p = mat_p.diagonal().copy() @@ -2106,6 +2110,7 @@ def as_networkx(self): self._add_edges_multi(self.p_or, self.p_ex, "p", lor_bus, lex_bus, graph) self._add_edges_multi(self.q_or, self.q_ex, "q", lor_bus, lex_bus, graph) self._add_edges_multi(self.a_or, self.a_ex, "a", lor_bus, lex_bus, graph) + self._add_edges_multi(self.v_or, self.v_ex, "v", lor_bus, lex_bus, graph) if self.support_theta: self._add_edges_multi( self.theta_or, self.theta_ex, "theta", lor_bus, lex_bus, graph @@ -2115,6 +2120,15 @@ def as_networkx(self): self._add_edges_simple( self.time_before_cooldown_line, "cooldown", lor_bus, lex_bus, graph ) + self._add_edges_simple( + self._thermal_limit, "thermal_limit", lor_bus, lex_bus, graph + ) + self._add_edges_simple( + self.time_next_maintenance, "time_next_maintenance", lor_bus, lex_bus, + graph) + self._add_edges_simple( + self.duration_next_maintenance, "duration_next_maintenance", lor_bus, + lex_bus, graph) self._add_edges_simple(self.line_status, "status", lor_bus, lex_bus, graph) self._add_edges_simple( self.thermal_limit, "thermal_limit", lor_bus, lex_bus, graph diff --git a/grid2op/tests/test_issue_389.py b/grid2op/tests/test_issue_389.py new file mode 100644 index 000000000..bd78ba5ec --- /dev/null +++ b/grid2op/tests/test_issue_389.py @@ -0,0 +1,47 @@ +# Copyright (c) 2019-2022, RTE (https://www.rte-france.com) +# See AUTHORS.txt and https://github.com/rte-france/Grid2Op/pull/319 +# This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. +# If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, +# you can obtain one at http://mozilla.org/MPL/2.0/. +# SPDX-License-Identifier: MPL-2.0 +# This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems. + +import grid2op +import unittest +import warnings +import numpy as np +import pdb + + +import grid2op +import matplotlib.pyplot as plt +from grid2op.PlotGrid import PlotMatplot +import numpy as np + + +class Issue380Tester(unittest.TestCase): + def setUp(self) -> None: + with warnings.catch_warnings(): + warnings.filterwarnings("ignore") + # this needs to be tested with pandapower backend + self.env = grid2op.make("rte_case5_example", test=True) + self.env.seed(0) + self.env.set_id(0) + + def test_issue(self): + act = self.env.action_space({"set_bus":{ "substations_id": [(4, (2, 1, 2))]}}) + obs, reward, done, info = self.env.step(act) + act = self.env.action_space({"set_bus":{ "lines_or_id": [(7, -1)]}}) + obs, reward, done, info = self.env.step(act) + assert not done + assert not np.isnan(obs.theta_ex[-1]) + G = obs.as_networkx() + assert not np.isnan(G.nodes[4]["theta"]) + assert G.edges[(0, 4)]["theta_or"] == G.nodes[0]["theta"] + assert G.edges[(0, 4)]["theta_ex"] == G.nodes[4]["theta"] + + assert G.edges[(0, 4)]["v_or"] == G.nodes[0]["v"] + assert G.edges[(0, 4)]["v_ex"] == G.nodes[4]["v"] + +if __name__ == "__main__": + unittest.main() \ No newline at end of file