Open DEUCE1957 opened 8 months ago
Hello,
Sorry for the late reply. This sounds like a super interesting idea.
I am not sure the current implementation you propose is correct though. To check this we need to run an algorithm to check if the graph is connected or not. For example you could have one part of the graph with substation 0, 1 and 4 and all the substations connected together (for example by disconnecting lines 4->5, 4->3, 1->3 and 1-> 2). And if my understanding of your algorithm is correct, this would be ok (but maybe I read it too fast, sorry if I'm wrong here)
One solution would be to convert the action to a networkx graph and then run a connected_components()
and check that the results has length 1 (which takes lots of time, probably around the same time as an obs.simulate I would guess). Of course it would be possible to implement the "check connected component" which would make it faster but requires some coding time which I don't have :-( (i'm open for a PR though :-) )
I'll try to work on it ASAP but it will probably not be in grid2op 1.10.2 release unfortunately
Have tested this algorithm again just now, you are correct that it misidentifies the case you suggest as having no disconnected substations:
For the next attempts I will proceed with 4 scenarios:
env.parameters.MAX_LINE_STATUS_CHANGED = env.n_line
init_obs = env.reset()
valid_obs = init_obs + env.action_space({"set_line_status":[('0_1_0', -1)]})
game_over_obs = init_obs + env.action_space({"set_line_status":[('6_7_18', -1)]})
disjoint_obs = init_obs + env.action_space({"set_line_status":[('4_5_17', -1), ('3_4_6', -1), ('1_3_3', -1), ('1_2_2', -1)]})
Returns (False, False, True, False). Incorrectly identified 'disjoint_obs' as False since it does not capture multi-substation islands.
Using the elements graph we can write:
import networkx as nx
def is_sub_disconnected_nx(obs):
G = obs.get_elements_graph()
return len([network for network in nx.connected_components(G.to_undirected())]) > 1
is_sub_disconnected_nx(init_obs), is_sub_disconnected_nx(valid_obs), is_sub_disconnected_nx(game_over_obs), is_sub_disconnected_nx(disjoint_obs)
This returns (False, True, True, True). So it incorrectly identifies 'valid_obs' as False. This is because the elements graph includes all elements, in this case the disconnected line ends from line 0->1 are seen as islands.
In the specific case of power line status changes, we know if the bus_connectivity_matrix shrinks after the powerline status change that is must be because a single bus become isolated. Like before, however, this does not capture the disjoint islanding scenario. The bus connectivity matrices for our 4 scenarios look as follows, if you look carefully at the disjoint case you can see that buses 0, 1, and 4 form an island (they are not connected to any other buses).
Assuming the disjoint cases are rare, we can first compare the matrix sizes (which should be v. fast) and only then check if there are islands:
from scipy.sparse.csgraph import connected_components
from grid2op.Action import PowerlineSetAction
def get_sub_disconnected(obs:BaseObservation, powerline_act:PowerlineSetAction):
"""
Determines if one or more substations will become disconnected (typical game
over condition) as a result of a change in Powerline status. This will not
work for actions that modify the substation topology.
Args:
obs (BaseObservation): Observation before action is taken
powerline_act (PowerlineSetAction): Desired setting of powerline status
Returns:
_type_: _description_
"""
# Assume: Only available action is powerline status change
new_obs = obs + powerline_act
new_obs._is_done = False # Ensures BCM is available
BCM = new_obs.bus_connectivity_matrix()
# Check if single substation was disconnected
if BCM.size < obs.bus_connectivity_matrix().size:
return True
# Check if multiple islands exist
n_islands = connected_components(BCM, directed=False, return_labels=False)
if n_islands > 1:
return True
return False
Returns (False, False, True, True) which is correct. This takes about 593ns / iteration for non-disjoint cases. It takes 293 microseconds for disjoint cases (which are hopefully rare).
Ideally we would have a obs.sub_connectivity_matrix(), won't necessarily speedup the above case but would allow us to generalize to none-PowerlineSetAction cases. It might also be possible to speed up the connected_components check by exiting as soon as more than 1 island is detected (since we are not actually interested in the no. of islands in this case).
Wahou, awesome work thank you :-)
Let me know if something can be done on my end.
Best
Benjamin
Not sure, we are currently using it to check whether a maintenance action is safe. Since it only works for power line status changes (won't generalize to substation topology changes) its utility is somewhat limited. Can make a pull request but not sure where it would belong? / Xavier
Expand Observation + Action to check for game-ending topology changes
I am playing around with the various options to simulate the environment. In the latest version there appear to be 4 ways:
I wanted to do a quick topology check to see that my agent does not isolate any substations For this I wanted to use Observati
Ex. This is what i do:
However, the Observation + Action method does not return a sensible bus_connectivity_matrix() nor get_energy_graph(): Since there are no power flows, the energy graph is naturally empty. The bus connectivity graph is also empty, however, which I would not expect. This appears to be because "(obs + action)._done" is always True.
While the comments on add_act() method state that this functionality is not intended to check for game over conditions, it does seem to me like it would be perfect for checking if a substation (and hence any connected elements) becomes disconnected.
An equality check between the elements matrix before and after the action can see if anything has been disconnected, but this will also return False for legal bus changes (such as the change from bus 1 to 2 on Substation 12 below):
Describe alternatives you've considered
Interestingly if I set "_done = False" on the sim_obs I can get access the bus_connectivity matrix. Not sure if this is intended behaviour, but seems to me it would be nicer to be able to access it since it is not actually a game over.
Additional context
Working on extending this into a topology-check PR.