/grid2op/operator_attention/attention_budget.py

https://github.com/rte-france/Grid2Op
Python | 168 lines | 85 code | 16 blank | 67 comment | 7 complexity | 98c63d7136ca1d6be397102494ae9e7c MD5 | raw file
  1. # Copyright (c) 2021, RTE (https://www.rte-france.com)
  2. # See AUTHORS.txt
  3. # This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0.
  4. # If a copy of the Mozilla Public License, version 2.0 was not distributed with this file,
  5. # you can obtain one at http://mozilla.org/MPL/2.0/.
  6. # SPDX-License-Identifier: MPL-2.0
  7. # This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems.
  8. import copy
  9. import numpy as np
  10. from grid2op.dtypes import dt_float, dt_int, dt_bool
  11. from grid2op.Exceptions import NotEnoughAttentionBudget
  12. class LinearAttentionBudget:
  13. def __init__(self):
  14. self._max_budget = None
  15. self._budget_per_ts = None
  16. self._alarm_cost = None
  17. self._current_budget = None
  18. self._init_budget = None
  19. self._time_last_alarm_raised = dt_int(-1)
  20. self._time_last_successful_alarm_raised = dt_int(-1)
  21. self._last_alarm_raised = None
  22. self._last_successful_alarm_raised = None
  23. self._all_successful_alarms = []
  24. @property
  25. def time_last_alarm_raised(self):
  26. """
  27. Time step of the last alarm raised (-1 if no alarm has been raised yet)
  28. Returns
  29. -------
  30. """
  31. return self._time_last_alarm_raised
  32. @property
  33. def time_last_successful_alarm_raised(self):
  34. """time of the last successful alarm raised"""
  35. return self._time_last_successful_alarm_raised
  36. @property
  37. def current_budget(self):
  38. """current attention budget"""
  39. return self._current_budget
  40. @property
  41. def last_alarm_raised(self):
  42. """
  43. for each zone, says:
  44. - -1 if no alarm have been raised for this zone for the entire episode
  45. - `k` (with `k>0`) says that the last alarm raised for this zone was at step `k`
  46. .. note::
  47. This counts both successful and non successful alarms
  48. """
  49. return self._last_alarm_raised
  50. @property
  51. def last_successful_alarm_raised(self):
  52. """
  53. for each zone, says:
  54. - -1 if no alarm have been raised for this zone for the entire episode
  55. - `k` (with `k>0`) says that the last alarm raised for this zone was at step `k`
  56. .. note::
  57. This counts only successful alarms
  58. """
  59. return self._last_successful_alarm_raised
  60. def init(self, partial_env, init_budget, max_budget, budget_per_ts, alarm_cost, **kwargs):
  61. self._max_budget = dt_float(max_budget)
  62. self._budget_per_ts = dt_float(budget_per_ts)
  63. self._alarm_cost = dt_float(alarm_cost)
  64. self._init_budget = dt_float(init_budget)
  65. self._last_alarm_raised = np.empty(partial_env.dim_alarms, dtype=dt_int)
  66. self._last_successful_alarm_raised = np.empty(partial_env.dim_alarms, dtype=dt_int)
  67. self.reset()
  68. def reset(self):
  69. """
  70. called each time the scenario is over by the environment
  71. Returns
  72. -------
  73. """
  74. self._current_budget = self._init_budget
  75. self._time_last_alarm_raised = dt_int(-1)
  76. self._time_last_successful_alarm_raised = dt_int(-1)
  77. self._last_alarm_raised[:] = -1
  78. self._last_successful_alarm_raised[:] = -1
  79. self._all_successful_alarms = []
  80. def get_state(self):
  81. """used to retrieve the sate in simulate"""
  82. res = (self._time_last_alarm_raised,
  83. self._last_alarm_raised,
  84. self._current_budget,
  85. self._time_last_successful_alarm_raised,
  86. self._last_successful_alarm_raised,
  87. self._all_successful_alarms)
  88. return res
  89. def set_state(self, state):
  90. """used to update the internal state of the budget, for simulate"""
  91. (_time_last_alarm_raised,
  92. _last_alarm_raised,
  93. _current_budget,
  94. _time_last_successful_alarm_raised,
  95. _last_successful_alarm_raised,
  96. _all_successful_alarms) = state
  97. self._time_last_alarm_raised = _time_last_alarm_raised
  98. self._last_alarm_raised[:] = _last_alarm_raised
  99. self._current_budget = _current_budget
  100. self._time_last_successful_alarm_raised = _time_last_successful_alarm_raised
  101. self._last_successful_alarm_raised[:] = _last_successful_alarm_raised
  102. self._all_successful_alarms = copy.copy(_all_successful_alarms)
  103. def register_action(self, env, action, is_action_illegal, is_action_ambiguous):
  104. """
  105. INTERNAL
  106. Called at each step to update the budget according to the action played
  107. Parameters
  108. ----------
  109. env
  110. action
  111. is_action_illegal
  112. is_action_ambiguous
  113. Returns
  114. -------
  115. """
  116. if action.dim_alarms == 0 or is_action_illegal or is_action_ambiguous:
  117. # this feature is not supported (grid2op <= 1.6.0) or is not activated
  118. # also, if the action is illegal is ambiguous, it is replaced with do nothing, but i don't really
  119. # want to affect the budget on this case
  120. return None
  121. if action.alarm_raised().size:
  122. # an alarm has been raised
  123. self._time_last_alarm_raised = env.nb_time_step
  124. self._last_alarm_raised[action.raise_alarm] = env.nb_time_step
  125. if self._current_budget >= self._alarm_cost:
  126. # i could raise it
  127. self._current_budget -= self._alarm_cost
  128. self._time_last_successful_alarm_raised = env.nb_time_step
  129. self._last_successful_alarm_raised[action.raise_alarm] = env.nb_time_step
  130. self._all_successful_alarms.append((env.nb_time_step, copy.deepcopy(action.raise_alarm)))
  131. else:
  132. # not enough budget
  133. current_budget = self._current_budget
  134. # self._current_budget = 0
  135. return NotEnoughAttentionBudget(f"You need a budget of {self._alarm_cost} to raise an alarm "
  136. f"but you had only {current_budget}. Nothing is done.")
  137. else:
  138. # no alarm has been raised, budget increases
  139. self._current_budget = min(self._max_budget, self._budget_per_ts + self._current_budget)
  140. return None