diff --git a/.gitignore b/.gitignore index c97dd3095..b7b8900c8 100644 --- a/.gitignore +++ b/.gitignore @@ -391,6 +391,7 @@ _profiling/profile.json actspace_converter.py grid2op/data_test/input_data_local/ test_sim2real_battery.py +grid2op/tests/list_test_debug # profiling files **.prof diff --git a/CHANGELOG.rst b/CHANGELOG.rst index cb35c7031..3a744fc01 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -37,7 +37,10 @@ Change Log - [FIXED] issue https://github.com/rte-france/Grid2Op/issues/446 - [FIXED] issue https://github.com/rte-france/Grid2Op/issues/523 by having a "_BackendAction" folder instead of a file - [FIXED] issue https://github.com/rte-france/Grid2Op/issues/522 and adding back certain notebooks to the CI -- [FIXED] and issue when disconnecting loads / generators on msot recent pandas version +- [FIXED] an issue when disconnecting loads / generators on msot recent pandas version +- [FIXED] issue https://github.com/rte-france/Grid2Op/issues/527 : now do nothing action are detected in + `act.as_serializable_dict()` AND weird do nothing action can be made through the action space + (`env.action_space({"change_bus": {}})` is not ambiguous, though might not be super efficient...) [1.9.4] - 2023-09-04 --------------------- diff --git a/grid2op/Action/baseAction.py b/grid2op/Action/baseAction.py index 8c58c9145..02e3770f1 100644 --- a/grid2op/Action/baseAction.py +++ b/grid2op/Action/baseAction.py @@ -612,10 +612,16 @@ def as_serializable_dict(self) -> dict: res["raise_alert"] = [ int(id_) for id_, val in enumerate(self._raise_alert) if val ] + if not res["raise_alert"]: + del res["raise_alert"] + if self._modif_alarm: res["raise_alarm"] = [ int(id_) for id_, val in enumerate(self._raise_alarm) if val ] + if not res["raise_alarm"]: + del res["raise_alarm"] + if self._modif_change_bus: res["change_bus"] = {} self._aux_serialize_add_key_change("load_change_bus", "loads_id", res["change_bus"]) @@ -623,11 +629,15 @@ def as_serializable_dict(self) -> dict: self._aux_serialize_add_key_change("line_or_change_bus", "lines_or_id", res["change_bus"]) self._aux_serialize_add_key_change("line_ex_change_bus", "lines_ex_id", res["change_bus"]) self._aux_serialize_add_key_change("storage_change_bus", "storages_id", res["change_bus"]) + if not res["change_bus"]: + del res["change_bus"] if self._modif_change_status: res["change_line_status"] = [ int(id_) for id_, val in enumerate(self._switch_line_status) if val ] + if not res["change_line_status"]: + del res["change_line_status"] # int elements if self._modif_set_bus: @@ -637,6 +647,8 @@ def as_serializable_dict(self) -> dict: self._aux_serialize_add_key_set("line_or_set_bus", "lines_or_id", res["set_bus"]) self._aux_serialize_add_key_set("line_ex_set_bus", "lines_ex_id", res["set_bus"]) self._aux_serialize_add_key_set("storage_set_bus", "storages_id", res["set_bus"]) + if not res["set_bus"]: + del res["set_bus"] if self._modif_set_status: res["set_line_status"] = [ @@ -644,6 +656,8 @@ def as_serializable_dict(self) -> dict: for id_, val in enumerate(self._set_line_status) if val != 0 ] + if not res["set_line_status"]: + del res["set_line_status"] # float elements if self._modif_redispatch: @@ -652,18 +666,26 @@ def as_serializable_dict(self) -> dict: for id_, val in enumerate(self._redispatch) if val != 0.0 ] + if not res["redispatch"]: + del res["redispatch"] + if self._modif_storage: res["set_storage"] = [ (int(id_), float(val)) for id_, val in enumerate(self._storage_power) if val != 0.0 ] + if not res["set_storage"]: + del res["set_storage"] + if self._modif_curtailment: res["curtail"] = [ (int(id_), float(val)) for id_, val in enumerate(self._curtail) if val != -1 ] + if not res["curtail"]: + del res["curtail"] # more advanced options if self._modif_inj: @@ -1787,7 +1809,7 @@ def _digest_setbus(self, dict_): if dict_["set_bus"] is None: # no real action has been made return - + if isinstance(dict_["set_bus"], dict): ddict_ = dict_["set_bus"] handled = False @@ -1809,6 +1831,9 @@ def _digest_setbus(self, dict_): if "substations_id" in ddict_: self.sub_set_bus = ddict_["substations_id"] handled = True + if ddict_ == {}: + handled = True + # weird way to do nothing but hey, how am I to judge ? if not handled: msg = 'Invalid way to set the topology. When dict_["set_bus"] is a dictionary it should have' msg += ( @@ -1849,6 +1874,9 @@ def _digest_change_bus(self, dict_): if "substations_id" in ddict_: self.sub_change_bus = ddict_["substations_id"] handled = True + if ddict_ == {}: + handled = True + # weird way to do nothing but hey, how am I to judge ? if not handled: msg = 'Invalid way to change the topology. When dict_["set_bus"] is a dictionary it should have' msg += ( diff --git a/grid2op/tests/test_issue_511.py b/grid2op/tests/test_issue_511.py index 8499042a6..0303c32ac 100644 --- a/grid2op/tests/test_issue_511.py +++ b/grid2op/tests/test_issue_511.py @@ -21,6 +21,9 @@ def setUp(self) -> None: test=True ) return super().setUp() + + def tearDown(self): + self.env.close() def test_issue_set_bus(self): act = { diff --git a/grid2op/tests/test_issue_527.py b/grid2op/tests/test_issue_527.py new file mode 100644 index 000000000..3b7a6e381 --- /dev/null +++ b/grid2op/tests/test_issue_527.py @@ -0,0 +1,44 @@ + +# Copyright (c) 2023, RTE (https://www.rte-france.com) +# See AUTHORS.txt +# 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 warnings +import unittest +import pdb + + +class Issue527Tester(unittest.TestCase): + def setUp(self) -> None: + with warnings.catch_warnings(): + warnings.filterwarnings("ignore") + self.env = grid2op.make( + "l2rpn_case14_sandbox", + test=True + ) + self.env.seed(0) + return super().setUp() + + def tearDown(self): + self.env.close() + + def test_action_space_sampling(self) -> None: + obs = self.env.reset() + for ind in range(1000): + act = self.env.action_space.sample() + act_dict = act.as_serializable_dict() + self.env.action_space(act_dict) + + def test_do_nothing_act_weird(self) -> None: + obs = self.env.reset() + self.env.action_space({"change_bus": {}}) + self.env.action_space({"set_bus": {}}) + + +if __name__ == '__main__': + unittest.main()