# Copyright (c) 2019-2020, 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 warnings
from typing import Optional, Dict, Literal
from grid2op.Exceptions import AmbiguousAction
from grid2op.Action.baseAction import BaseAction
[docs]class PlayableAction(BaseAction):
"""
From this class inherit all actions that the player will be allowed to do. This includes for example
:class:`TopologyAndDispatchAction` or :class:`TopologyAction`
"""
authorized_keys = {
"set_line_status",
"change_line_status",
"set_bus",
"change_bus",
"redispatch",
"set_storage",
"curtail",
"raise_alarm",
"raise_alert"
}
attr_list_vect = [
"_set_line_status",
"_switch_line_status",
"_set_topo_vect",
"_change_bus_vect",
"_redispatch",
"_storage_power",
"_curtail",
"_raise_alarm",
"_raise_alert"
]
attr_list_set = set(attr_list_vect)
shunt_added = True # no shunt here
[docs] def __init__(self, _names_chronics_to_backend: Optional[Dict[Literal["loads", "prods", "lines"], Dict[str, str]]]=None):
super().__init__(_names_chronics_to_backend)
self.authorized_keys_to_digest = {
"set_line_status": self._digest_set_status,
"change_line_status": self._digest_change_status,
"set_bus": self._digest_setbus,
"change_bus": self._digest_change_bus,
"redispatch": self._digest_redispatching,
"set_storage": self._digest_storage,
"curtail": self._digest_curtailment,
"raise_alarm": self._digest_alarm,
"raise_alert": self._digest_alert,
}
[docs] def __call__(self):
"""
.. warning:: /!\\\\ Internal, do not use unless you know what you are doing /!\\\\
Compare to the ancestor :func:`BaseAction.__call__` this type of BaseAction doesn't allow internal actions
The returned tuple is same, but with empty dictionaries for internal actions
Returns
-------
dict_injection: ``dict``
This dictionary is always empty
set_line_status: :class:`numpy.ndarray`, dtype:int
This array is :attr:`BaseAction._set_line_status`
switch_line_status: :class:`numpy.ndarray`, dtype:bool
This array is :attr:`BaseAction._switch_line_status`
set_topo_vect: :class:`numpy.ndarray`, dtype:int
This array is :attr:`BaseAction._set_topo_vect`
change_bus_vect: :class:`numpy.ndarray`, dtype:bool
This array is :attr:`BaseAction._change_bus_vect`
redispatch: :class:`numpy.ndarray`, dtype:float
The array is :attr:`BaseAction._redispatch`
curtail: :class:`numpy.ndarray`, dtype:float
The array is :attr:`BaseAction._curtail`
shunts: ``dict``
Always empty for this class
"""
if self._dict_inj:
raise AmbiguousAction("Injections actions are not playable.")
self._check_for_ambiguity()
return (
{},
self._set_line_status,
self._switch_line_status,
self._set_topo_vect,
self._change_bus_vect,
self._redispatch,
self._storage_power,
{},
)
[docs] def update(self, dict_):
"""
.. warning:: /!\\\\ Internal, do not use unless you know what you are doing /!\\\\
Similar to :class:`BaseAction`, except that the allowed entries are limited to the playable action set
Parameters
----------
dict_: :class:`dict`
See the help of :func:`BaseAction.update` for a detailed explanation.
If an entry is not in the playable action set, this will raise
Returns
-------
self: :class:`PlayableAction`
Return object itself thus allowing multiple calls to "update" to be chained.
"""
self._reset_vect()
warn_msg = (
'The key "{}" used to update an action will be ignored. Valid keys are {}'
)
if dict_ is None:
return self
for kk in dict_.keys():
if kk not in self.authorized_keys:
warn = warn_msg.format(kk, self.authorized_keys)
warnings.warn(warn)
else:
self.authorized_keys_to_digest[kk](dict_)
return self