Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ Work "in progress"

General grid2op improvments:

- add voltage angle attributes to the complete observation vector (issue #711)
Comment thread
Gaurav890 marked this conversation as resolved.
Outdated
- ill formed docstring in the BaseAction module
- remove pandapower dependency (have a way to install grid2op without pandapower)
- better logging
Expand Down
27 changes: 26 additions & 1 deletion grid2op/Observation/baseObservation.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
NoForecastAvailable,
BaseObservationError,
)
from grid2op.Space import GridObjects, ElTypeInfo
from grid2op.Space import GridObjects, ElTypeInfo, GRID2OP_CURRENT_VERSION_STR

# TODO have a method that could do "forecast" by giving the _injection by the agent,
# TODO if he wants to make custom forecasts
Expand Down Expand Up @@ -1375,6 +1375,24 @@ def _aux_process_grid2op_compat_1_11_0(cls):
except ValueError as exc_: # noqa: F841
# this attribute was not there in the first place
pass

@classmethod
def _aux_process_grid2op_compat_1_12_6(cls):
"""Keep the observation vector compatible with versions before 1.12.6."""
theta_attrs = [
"theta_or",
"theta_ex",
"load_theta",
"gen_theta",
"storage_theta",
]
cls.attr_list_vect = copy.deepcopy(cls.attr_list_vect)
cls.attr_list_json = copy.deepcopy(cls.attr_list_json)
for el in theta_attrs:
if el in cls.attr_list_vect:
cls.attr_list_vect.remove(el)
if el not in cls.attr_list_json:
cls.attr_list_json.append(el)

@classmethod
def process_grid2op_compat(cls) -> None:
Expand Down Expand Up @@ -1408,6 +1426,13 @@ def process_grid2op_compat(cls) -> None:
if glop_ver < cls.MIN_VERSION_DETACH:
# detachment has been added in grid2op 1.11
cls._aux_process_grid2op_compat_1_11_0()

if (
glop_ver < version.parse("1.12.6")
and cls.glop_version != GRID2OP_CURRENT_VERSION_STR
):
# voltage angles have been added to the observation vector in 1.12.6
cls._aux_process_grid2op_compat_1_12_6()

cls.attr_list_set = copy.deepcopy(cls.attr_list_set)
cls.attr_list_set = set(cls.attr_list_vect)
Expand Down
24 changes: 19 additions & 5 deletions grid2op/Observation/completeObservation.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,19 @@ class CompleteObservation(BaseObservation):
and 0 means "not used"
#. :attr:`BaseObservation.attack_under_alert` For each attackable line `i` it says if an alert has been sent (+1) or not (-1)
for each attackable line currently under attack.
#. :attr:`BaseObservation.theta_or` voltage angle at the origin of each powerline
[:attr:`grid2op.Space.GridObjects.n_line` elements].
#. :attr:`BaseObservation.theta_ex` voltage angle at the extremity of each powerline
[:attr:`grid2op.Space.GridObjects.n_line` elements].
#. :attr:`BaseObservation.load_theta` voltage angle at each load
[:attr:`grid2op.Space.GridObjects.n_load` elements].
#. :attr:`BaseObservation.gen_theta` voltage angle at each generator
[:attr:`grid2op.Space.GridObjects.n_gen` elements].
#. :attr:`BaseObservation.storage_theta` voltage angle at each storage unit
[:attr:`grid2op.Space.GridObjects.n_storage` elements].

These attributes are set to zero when the backend does not support voltage
angles; see :attr:`BaseObservation.support_theta`.

"""

Expand Down Expand Up @@ -208,17 +221,18 @@ class CompleteObservation(BaseObservation):
"gen_p_detached",
"storage_p_detached",
# protections (>= 1.11.0)
"timestep_protection_engaged"
]
attr_list_json = [
"_thermal_limit",
"support_theta",
"timestep_protection_engaged",
# voltage angles (>= 1.12.6)
"theta_or",
"theta_ex",
"load_theta",
"gen_theta",
"storage_theta",
]
attr_list_json = [
"_thermal_limit",
"support_theta",
]
attr_list_set = set(attr_list_vect)

def __init__(self,
Expand Down
38 changes: 35 additions & 3 deletions grid2op/tests/test_Observation.py
Original file line number Diff line number Diff line change
Expand Up @@ -959,7 +959,13 @@ def setUp(self):
# dt_float,
# dt_float,
# timestep_protection_engaged
dt_int
dt_int,
# voltage angles (>= 1.12.6)
dt_float,
dt_float,
dt_float,
dt_float,
dt_float,
],
dtype=object,
)
Expand Down Expand Up @@ -1039,10 +1045,16 @@ def setUp(self):
# 5,
# 0,
# timestep_protection_engaged
20
20,
# voltage angles (>= 1.12.6)
20,
20,
11,
5,
0,
]
)
self.size_obs = 429 + 4 + 4 + 2 + 1 + 10 + 5 + 0 + 5 + 20
self.size_obs = 429 + 4 + 4 + 2 + 1 + 10 + 5 + 0 + 5 + 20 + 56

def tearDown(self):
self.env.close()
Expand Down Expand Up @@ -2133,6 +2145,26 @@ def test_4_to_from_vect(self):
vect2 = obs2.to_vect()
assert np.all(vect == vect2)

def test_theta_attributes_in_vector(self):
obs = self.env.reset()
theta_attrs = (
"theta_or",
"theta_ex",
"load_theta",
"gen_theta",
"storage_theta",
)

for attr_nm in theta_attrs:
assert attr_nm in type(obs).attr_list_vect
assert attr_nm not in type(obs).attr_list_json

obs_from_vect = self.env.observation_space.from_vect(obs.to_vect())
for attr_nm in theta_attrs:
np.testing.assert_array_equal(
getattr(obs_from_vect, attr_nm), getattr(obs, attr_nm)
)

def test_5_simulate_proper_timestep(self):
self.skipTest(
"This is extensively tested elswhere, and the chronics have been changed."
Expand Down
12 changes: 8 additions & 4 deletions grid2op/tests/test_attached_envs.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,8 @@ def test_observation_space(self):
assert issubclass(self.env.observation_space.subtype, CompleteObservation)
# size_th = 467
# size_th = 473 # gen_delta
size_th = 473 + 20 # n_line added for timestep_protection_engaged
# timestep_protection_engaged + theta_or/theta_ex/load/gen/storage
size_th = 473 + 20 + 2 * 20 + 11 + 6
assert self.env.observation_space.n == size_th, (
f"obs space size is {self.env.observation_space.n}," f"should be {size_th}"
)
Expand Down Expand Up @@ -229,7 +230,8 @@ def test_action_space(self):

def test_observation_space(self):
assert issubclass(self.env.observation_space.subtype, CompleteObservation)
size_th = 518 + 20 # n_line added for timestep_protection_engaged
# timestep_protection_engaged + theta_or/theta_ex/load/gen/storage
size_th = 518 + 20 + 2 * 20 + 11 + 6
assert self.env.observation_space.n == size_th, (
f"obs space size is {self.env.observation_space.n}," f"should be {size_th}"
)
Expand Down Expand Up @@ -274,7 +276,8 @@ def test_action_space(self):
def test_observation_space(self):
assert issubclass(self.env.observation_space.subtype, CompleteObservation)
# size_th = 467
size_th = 473 + 20 # n_line added for timestep_protection_engaged
# timestep_protection_engaged + theta_or/theta_ex/load/gen/storage
size_th = 473 + 20 + 2 * 20 + 11 + 6
assert self.env.observation_space.n == size_th, (
f"obs space size is {self.env.observation_space.n}," f"should be {size_th}"
)
Expand Down Expand Up @@ -319,7 +322,8 @@ def test_action_space(self):
def test_observation_space(self):
assert issubclass(self.env.observation_space.subtype, CompleteObservation)
# size_th = 475
size_th = 481 + 20 # n_line added for timestep_protection_engaged
# timestep_protection_engaged + theta_or/theta_ex/load/gen/storage
size_th = 481 + 20 + 2 * 20 + 11 + 6 + 2
assert self.env.observation_space.n == size_th, (
f"obs space size is {self.env.observation_space.n}," f"should be {size_th}"
)
Expand Down