Skip to content

Commit

Permalink
Merge pull request #5 from BDonnot/handy_methods_obs
Browse files Browse the repository at this point in the history
adding more useful methods in observation, making some more tests and…
  • Loading branch information
BDonnot committed Nov 26, 2019
2 parents 5baf29a + 80b2313 commit 081642f
Show file tree
Hide file tree
Showing 8 changed files with 293 additions and 24 deletions.
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
author = 'Benjamin Donnot'

# The full version, including alpha/beta/rc tags
release = '0.3.1'
release = '0.3.3'
version = '0.3'


Expand Down
6 changes: 3 additions & 3 deletions grid2op/Action.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
# TODO time delay somewhere (eg action is implemented after xxx timestep, and not at the time where it's proposed)

# TODO have the "reverse" action, that does the opposite of an action. Will be hard but who know ? :eyes:

# TODO add serialization of ActionSpace to json or yaml

class Action(object):
"""
Expand Down Expand Up @@ -1399,7 +1399,7 @@ def effect_on(self, _sentinel=None, load_id=None, gen_id=None, line_id=None, sub
Parameters
----------
_sentinel: ``None``
Used to prevent positional _parameters. Internal, do not use.
Used to prevent positional parameters. Internal, do not use.
load_id: ``int``
ID of the load we want to inspect
Expand All @@ -1411,7 +1411,7 @@ def effect_on(self, _sentinel=None, load_id=None, gen_id=None, line_id=None, sub
ID of the powerline we want to inspect
substation_id: ``int``
ID of the powerline we want to inspect
ID of the substation we want to inspect
Returns
-------
Expand Down
185 changes: 185 additions & 0 deletions grid2op/Observation.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@

# TODO finish documentation

# TODO add serialization of ObservationSpace to json or yaml

# TODO catch warnings in simulate


class ObsCH(object):
"""
Expand Down Expand Up @@ -429,6 +433,147 @@ def __init__(self, parameters,
# value to assess if two observations are equal
self._tol_equal = 5e-1

def state_of(self, _sentinel=None, load_id=None, gen_id=None, line_id=None, substation_id=None):
"""
Return the state of this action on a give unique load, generator unit, powerline of substation.
Only one of load, gen, line or substation should be filled.
The querry of these objects can only be done by id here (ie by giving the integer of the object in the backed).
The :class:`HelperAction` has some utilities to access them by name too.
Parameters
----------
_sentinel: ``None``
Used to prevent positional _parameters. Internal, do not use.
load_id: ``int``
ID of the load we want to inspect
gen_id: ``int``
ID of the generator we want to inspect
line_id: ``int``
ID of the powerline we want to inspect
substation_id: ``int``
ID of the powerline we want to inspect
Returns
-------
res: :class:`dict`
A dictionnary with keys and value depending on which object needs to be inspected:
- if a load is inspected, then the keys are:
- "p" the active value consumed by the load
- "q" the reactive value consumed by the load
- "v" the voltage magnitude of the bus to which the load is connected
- "bus" on which bus the load is connected in the substation
- "sub_id" the id of the substation to which the load is connected
- if a generator is inspected, then the keys are:
- "p" the active value produced by the generator
- "q" the reactive value consumed by the generator
- "v" the voltage magnitude of the bus to which the generator is connected
- "bus" on which bus the generator is connected in the substation
- "sub_id" the id of the substation to which the generator is connected
- if a powerline is inspected then the keys are "origin" and "extremity" each being dictionnary with keys:
- "p" the active flow on line end (extremity or origin)
- "q" the reactive flow on line end (extremity or origin)
- "v" the voltage magnitude of the bus to which the line end (extremity or origin) is connected
- "bus" on which bus the line end (extremity or origin) is connected in the substation
- "sub_id" the id of the substation to which the generator is connected
- "a" the current flow on the line end (extremity or origin)
- if a substation is inspected, it returns the topology to this substation in a dictionary with keys:
- "topo_vect": the representation of which object is connected where
- "nb_bus": number of active buses in this substations
Raises
------
Grid2OpException
If _sentinel is modified, or if None of the arguments are set or alternatively if 2 or more of the
_parameters are being set.
"""
if _sentinel is not None:
raise Grid2OpException("action.effect_on should only be called with named argument.")

if load_id is None and gen_id is None and line_id is None and substation_id is None:
raise Grid2OpException("You ask the state of an object in a observation without specifying the object id. "
"Please provide \"load_id\", \"gen_id\", \"line_id\" or \"substation_id\"")

if load_id is not None:
if gen_id is not None or line_id is not None or substation_id is not None:
raise Grid2OpException("You can only the inspect the effect of an action on one single element")
if load_id >= len(self.load_p):
raise Grid2OpException("There are no load of id \"load_id={}\" in this grid.".format(load_id))

res = {"p": self.load_p[load_id],
"q": self.load_q[load_id],
"v": self.load_v[load_id],
"bus": self.topo_vect[self._load_pos_topo_vect[load_id]],
"sub_id": self._load_to_subid[load_id]
}
elif gen_id is not None:
if line_id is not None or substation_id is not None:
raise Grid2OpException("You can only the inspect the effect of an action on one single element")
if gen_id >= len(self.prod_p):
raise Grid2OpException("There are no generator of id \"gen_id={}\" in this grid.".format(gen_id))

res = {"p": self.prod_p[gen_id],
"q": self.prod_q[gen_id],
"v": self.prod_v[gen_id],
"bus": self.topo_vect[self._gen_pos_topo_vect[gen_id]],
"sub_id": self._gen_to_subid[gen_id]
}
elif line_id is not None:
if substation_id is not None:
raise Grid2OpException("You can only the inspect the effect of an action on one single element")
if line_id >= len(self.p_or):
raise Grid2OpException("There are no powerline of id \"line_id={}\" in this grid.".format(line_id))
res = {}

# origin information
res["origin"] = {
"p": self.p_or[line_id],
"q": self.q_or[line_id],
"v": self.v_or[line_id],
"a": self.a_or[line_id],
"bus": self.topo_vect[self._lines_or_pos_topo_vect[line_id]],
"sub_id": self._lines_or_to_subid[line_id]
}
# extremity information
res["extremity"] = {
"p": self.p_ex[line_id],
"q": self.q_ex[line_id],
"v": self.v_ex[line_id],
"a": self.a_ex[line_id],
"bus": self.topo_vect[self._lines_ex_pos_topo_vect[line_id]],
"sub_id": self._lines_ex_to_subid[line_id]
}
else:
if substation_id >= len(self.subs_info):
raise Grid2OpException("There are no substation of id \"substation_id={}\" in this grid.".format(substation_id))

beg_ = int(np.sum(self.subs_info[:substation_id]))
end_ = int(beg_ + self.subs_info[substation_id])
topo_sub = self.topo_vect[beg_:end_]
if np.any(topo_sub > 0):
nb_bus = np.max(topo_sub[topo_sub > 0]) - np.min(topo_sub[topo_sub > 0]) + 1
else:
nb_bus = 0
res = {
"topo_vect": topo_sub,
"nb_bus": nb_bus
}

return res

def reset(self):
"""
Reset the :class:`Observation` to a blank state, where everything is set to either ``None`` or to its default
Expand Down Expand Up @@ -1269,4 +1414,44 @@ def from_vect(self, obs):
"""
res = copy.deepcopy(self.empty_obs)
res.from_vect(obs)
return res

def get_obj_connect_to(self, _sentinel=None, substation_id=None):
"""
Get all the object connected to a given substation:
Parameters
----------
_sentinel: ``None``
Used to prevent positional parameters. Internal, do not use.
substation_id: ``int``
ID of the substation we want to inspect
Returns
-------
res: ``dict`` with keys:
- "loads_id": a vector giving the id of the loads connected to this substation, empty if none
- "generators_id": a vector giving the id of the generators connected to this substation, empty if none
- "lines_or_id": a vector giving the id of the origin end of the powerlines connected to this substation,
empty if none
- "lines_ex_id": a vector giving the id of the extermity end of the powerlines connected to this
substation, empty if none.
- "nb_elements" : number of elements connected to this substation
"""

if substation_id is None:
raise Grid2OpException("You ask the composition of a substation without specifying its id."
"Please provide \"substation_id\"")
if substation_id >= len(self.subs_info):
raise Grid2OpException("There are no substation of id \"substation_id={}\" in this grid.".format(substation_id))

res = {}
res["loads_id"] = np.where(self.load_to_subid == substation_id)[0]
res["generators_id"] = np.where(self.gen_to_subid == substation_id)[0]
res["lines_or_id"] = np.where(self.lines_or_to_subid == substation_id)[0]
res["lines_ex_id"] = np.where(self.lines_ex_to_subid == substation_id)[0]
res["nb_elements"] = self.subs_info[substation_id]
return res
2 changes: 1 addition & 1 deletion grid2op/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import os
import pkg_resources

__version__ = '0.3.1'
__version__ = '0.3.3'

__all__ = ['Action', "BackendPandaPower", "Agent", "Backend", "ChronicsHandler", "Environment", "Exceptions",
"Observation", "Parameters", "GameRules", "Reward", "Runner", "main"]
Expand Down
4 changes: 2 additions & 2 deletions grid2op/tests/test_Agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,13 +130,13 @@ def test_1_powerlineswitch(self):
agent = PowerLineSwitch(self.env.helper_action_player)
i, cum_reward = self._aux_test_agent(agent)
assert i == 31, "The powerflow diverged before step 30 for powerline switch agent"
assert np.abs(cum_reward - 619.994755) <= self.tol_one, "The reward has not been properly computed"
assert np.abs(cum_reward - 619.9950) <= self.tol_one, "The reward has not been properly computed"

def test_2_busswitch(self):
agent = TopologyGreedy(self.env.helper_action_player)
i, cum_reward = self._aux_test_agent(agent)
assert i == 31, "The powerflow diverged before step 30 for greedy agent"
assert np.abs(cum_reward - 619.996155) <= self.tol_one, "The reward has not been properly computed"
assert np.abs(cum_reward - 619.999011) <= self.tol_one, "The reward has not been properly computed"


if __name__ == "__main__":
Expand Down
79 changes: 79 additions & 0 deletions grid2op/tests/test_Observation.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,85 @@ def test_6_simulate_dont_affect_env(self):
obs_after = self.env.helper_observation(self.env)
assert obs_orig == obs_after

def test_inspect_load(self):
obs = self.env.helper_observation(self.env)
dict_ = obs.state_of(load_id=0)
assert "p" in dict_
assert dict_["p"] == 18.8
assert "q" in dict_
assert dict_["q"] == 13.4
assert "v" in dict_
assert dict_["v"] == 141.075
assert "bus" in dict_
assert dict_["bus"] == 1
assert "sub_id" in dict_
assert dict_["sub_id"] == 1

def test_inspect_gen(self):
obs = self.env.helper_observation(self.env)
dict_ = obs.state_of(gen_id=0)
assert "p" in dict_
assert dict_["p"] == 0.0
assert "q" in dict_
assert dict_["q"] == 47.48313177017934
assert "v" in dict_
assert dict_["v"] == 141.075
assert "bus" in dict_
assert dict_["bus"] == 1
assert "sub_id" in dict_
assert dict_["sub_id"] == 1

def test_inspect_line(self):
obs = self.env.helper_observation(self.env)
dict_both = obs.state_of(line_id=0)
assert "origin" in dict_both
dict_ = dict_both["origin"]

assert "p" in dict_
assert dict_["p"] == 109.77536682689008
assert "q" in dict_
assert dict_["q"] == -8.7165023030358
assert "v" in dict_
assert dict_["v"] == 143.1
assert "bus" in dict_
assert dict_["bus"] == 1
assert "sub_id" in dict_
assert dict_["sub_id"] == 0

assert "extremity" in dict_both
dict_ = dict_both["extremity"]
assert "p" in dict_
assert dict_["p"] == -107.69115512018216
assert "q" in dict_
assert dict_["q"] == 9.230658220781127
assert "v" in dict_
assert dict_["v"] == 141.075
assert "bus" in dict_
assert dict_["bus"] == 1
assert "sub_id" in dict_
assert dict_["sub_id"] == 1

def test_inspect_topo(self):
obs = self.env.helper_observation(self.env)
dict_ = obs.state_of(substation_id=1)
assert "topo_vect" in dict_
assert np.all(dict_["topo_vect"] == [1, 1, 1, 1, 1, 1])
assert "nb_bus" in dict_
assert dict_["nb_bus"] == 1

def test_get_obj_connect_to(self):
dict_ = self.env.helper_observation.get_obj_connect_to(substation_id=1)
assert 'loads_id' in dict_
assert np.all(dict_['loads_id'] == 0)
assert 'generators_id' in dict_
assert np.all(dict_['generators_id'] == 0)
assert 'lines_or_id' in dict_
assert np.all(dict_['lines_or_id'] == [7, 8, 9])
assert 'lines_ex_id' in dict_
assert np.all(dict_['lines_ex_id'] == 0)
assert 'nb_elements' in dict_
assert dict_['nb_elements'] == 6


if __name__ == "__main__":
unittest.main()

0 comments on commit 081642f

Please sign in to comment.