Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Traversal from terminal by depth or breadth first #335

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import com.powsybl.commons.PowsyblException;
import com.powsybl.iidm.network.*;
import com.powsybl.math.graph.TraversalType;
import com.powsybl.math.graph.TraverseResult;
import com.powsybl.network.store.model.Resource;
import com.powsybl.network.store.model.VoltageLevelAttributes;
Expand Down Expand Up @@ -204,14 +205,14 @@ private Bus getOtherBus(String switchId, String busId) {
}
}

boolean traverseFromTerminal(Terminal terminal, Terminal.TopologyTraverser traverser, Set<Terminal> traversedTerminals) {
boolean traverseFromTerminal(Terminal terminal, Terminal.TopologyTraverser traverser, Set<Terminal> traversedTerminals, TraversalType traversalType) {
checkNodeBreakerTopology();
Objects.requireNonNull(traverser);

return traverseFromBus(terminal.getBusBreakerView().getBus(), traverser, traversedTerminals, new HashSet<>());
return traverseFromBus(terminal.getBusBreakerView().getBus(), traverser, traversedTerminals, new HashSet<>(), traversalType);
}

private boolean traverseFromBus(Bus bus, Terminal.TopologyTraverser traverser, Set<Terminal> traversedTerminals, Set<Bus> traversedBuses) {
private boolean traverseFromBus(Bus bus, Terminal.TopologyTraverser traverser, Set<Terminal> traversedTerminals, Set<Bus> traversedBuses, TraversalType traversalType) {
Objects.requireNonNull(bus);
Objects.requireNonNull(traverser);

Expand All @@ -232,7 +233,7 @@ private boolean traverseFromBus(Bus bus, Terminal.TopologyTraverser traverser, S
} else if (result == TraverseResult.CONTINUE) {
Set<Terminal> otherSideTerminals = ((TerminalImpl) terminal).getOtherSideTerminals();
for (Terminal otherSideTerminal : otherSideTerminals) {
if (!((TerminalImpl) otherSideTerminal).traverse(traverser, traversedTerminals)) {
if (!((TerminalImpl) otherSideTerminal).traverse(traverser, traversedTerminals, traversalType)) {
return false;
}
}
Expand All @@ -248,7 +249,7 @@ private boolean traverseFromBus(Bus bus, Terminal.TopologyTraverser traverser, S
return false;
} else if (result == TraverseResult.CONTINUE) {
Bus otherBus = getOtherBus(s.getId(), bus.getId());
if (!traverseFromBus(otherBus, traverser, traversedTerminals, traversedBuses)) {
if (!traverseFromBus(otherBus, traverser, traversedTerminals, traversedBuses, traversalType)) {
return false;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import com.powsybl.commons.PowsyblException;
import com.powsybl.iidm.network.*;
import com.powsybl.math.graph.TraversalType;
import com.powsybl.math.graph.TraverseResult;
import com.powsybl.network.store.model.*;
import org.jgrapht.Graph;
Expand Down Expand Up @@ -115,7 +116,7 @@ public void traverse(int[] nodes, VoltageLevel.NodeBreakerView.TopologyTraverser
Graph<Integer, Edge> graph = NodeBreakerTopology.INSTANCE.buildGraph(index, getVoltageLevelResource(), true, true);
Set<Integer> done = new HashSet<>();
for (int node : nodes) {
if (!traverseFromNode(graph, node, traverser, done)) {
if (!traverseFromNode(graph, node, TraversalType.DEPTH_FIRST, traverser, done)) {
break;
}
}
Expand All @@ -125,44 +126,110 @@ public void traverse(int[] nodes, VoltageLevel.NodeBreakerView.TopologyTraverser
public void traverse(int node, VoltageLevel.NodeBreakerView.TopologyTraverser traverser) {
Objects.requireNonNull(traverser);
checkBusBreakerTopology();
traverseFromNode(node, traverser);
traverseFromNode(node, TraversalType.DEPTH_FIRST, traverser);
}

boolean traverseFromNode(int node, VoltageLevel.NodeBreakerView.TopologyTraverser traverser) {
boolean traverseFromNode(int node, TraversalType traversalType, VoltageLevel.NodeBreakerView.TopologyTraverser traverser) {
Graph<Integer, Edge> graph = NodeBreakerTopology.INSTANCE.buildGraph(index, getVoltageLevelResource(), true, true);
Set<Integer> done = new HashSet<>();
return traverseFromNode(graph, node, traverser, done);
return traverseFromNode(graph, node, traversalType, traverser, done);
}

private boolean traverseFromNode(Graph<Integer, Edge> graph, int node, VoltageLevel.NodeBreakerView.TopologyTraverser traverser,
private boolean traverseFromNode(Graph<Integer, Edge> graph, int node, TraversalType traversalType, VoltageLevel.NodeBreakerView.TopologyTraverser traverser,
Set<Integer> done) {
if (done.contains(node)) {
return true;
}
done.add(node);

for (Edge edge : graph.edgesOf(node)) {
NodeBreakerBiConnectable biConnectable = edge.getBiConnectable();
int nextNode = biConnectable.getNode1() == node ? biConnectable.getNode2() : biConnectable.getNode1();
TraverseResult result;
if (done.contains(nextNode)) {
continue;
}
if (biConnectable instanceof SwitchAttributes) {
result = traverseSwitch(traverser, biConnectable, node, nextNode);
} else if (biConnectable instanceof InternalConnectionAttributes) {
result = traverser.traverse(node, null, nextNode);
} else {
throw new AssertionError();
if (traversalType == TraversalType.DEPTH_FIRST) { // traversal by depth first
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Separate DFS and BFS in two differents methods, otherwise it's a bit difficult to differentiate the recursive DFS with the BFS

if (done.contains(node)) {
return true;
}
if (result == TraverseResult.CONTINUE) {
if (!traverseFromNode(graph, nextNode, traverser, done)) {
done.add(node);

for (Edge edge : graph.edgesOf(node)) {
NodeBreakerBiConnectable biConnectable = edge.getBiConnectable();
int nextNode = biConnectable.getNode1() == node ? biConnectable.getNode2() : biConnectable.getNode1();
TraverseResult result;
if (done.contains(nextNode)) {
continue;
}
if (biConnectable instanceof SwitchAttributes) {
result = traverseSwitch(traverser, biConnectable, node, nextNode);
} else if (biConnectable instanceof InternalConnectionAttributes) {
result = traverser.traverse(node, null, nextNode);
} else {
throw new AssertionError();
}
if (result == TraverseResult.CONTINUE) {
if (!traverseFromNode(graph, nextNode, traversalType, traverser, done)) {
return false;
}
} else if (result == TraverseResult.TERMINATE_TRAVERSER) {
return false;
}
} else if (result == TraverseResult.TERMINATE_TRAVERSER) {
return false;
}
} else { // traversal by breadth first
boolean keepGoing = true;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should be able to get rid of the boolean keepGoing, see proposed changes. Instead of encounteredEdges, we could keep track of visitedNodes similarly to the "done" set in the DFS implementation. See suggestion

Set<Edge> encounteredEdges = new HashSet<>();

LinkedList<Integer> vertexToTraverse = new LinkedList<>();
vertexToTraverse.offer(node);
while (!vertexToTraverse.isEmpty()) {
int firstV = vertexToTraverse.getFirst();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename to currentNode?

vertexToTraverse.poll();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can be one line int firstV = vertexToTraverse.poll(); ?

if (done.contains(firstV)) {
continue;
}
done.add(firstV);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need done here as we have the encounteredEdges Set. we need to choose one or the other


Set<Edge> adjacentEdges = graph.edgesOf(firstV);

for (Edge edge : adjacentEdges) {
if (encounteredEdges.contains(edge)) {
continue;
}
encounteredEdges.add(edge);

NodeBreakerBiConnectable biConnectable = edge.getBiConnectable();
int node1 = biConnectable.getNode1();
int node2 = biConnectable.getNode2();

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can also use int nextNode similarly to the DFS to avoid the following if clause duplicated

TraverseResult traverserResult;
if (!done.contains(node1)) {
if (biConnectable instanceof SwitchAttributes) {
traverserResult = traverseSwitch(traverser, biConnectable, node2, node1);
} else if (biConnectable instanceof InternalConnectionAttributes) {
traverserResult = traverser.traverse(node2, null, node1);
} else {
throw new AssertionError();
}
if (traverserResult == TraverseResult.CONTINUE) {
vertexToTraverse.offer(node1);
} else if (traverserResult == TraverseResult.TERMINATE_TRAVERSER) {
keepGoing = false;
}
} else if (!done.contains(node2)) {
if (biConnectable instanceof SwitchAttributes) {
traverserResult = traverseSwitch(traverser, biConnectable, node1, node2);
} else if (biConnectable instanceof InternalConnectionAttributes) {
traverserResult = traverser.traverse(node1, null, node2);
} else {
throw new AssertionError();
}
if (traverserResult == TraverseResult.CONTINUE) {
vertexToTraverse.offer(node2);
} else if (traverserResult == TraverseResult.TERMINATE_TRAVERSER) {
keepGoing = false;
}
}
if (!keepGoing) {
break;
}
}
if (!keepGoing) {
break;
}
}
return keepGoing;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like this seems to work for me on tests but I haven't tested on a real network only on the tests implemented.

    boolean traverseFromNode(int node, TraversalType traversalType, VoltageLevel.NodeBreakerView.TopologyTraverser traverser) {
        Graph<Integer, Edge> graph = NodeBreakerTopology.INSTANCE.buildGraph(index, getVoltageLevelResource(), true, true);
        if (traversalType == TraversalType.DEPTH_FIRST) {
            Set<Integer> visitedNodes = new HashSet<>();
            return traverseFromNodeDFS(graph, node, traverser, visitedNodes);
        } else {
            return traverseFromNodeBFS(graph, node, traverser);
        }
    }

    private boolean traverseFromNodeBFS(Graph<Integer, Edge> graph, int node, TopologyTraverser traverser) {
        Set<Integer> visitedNodes = new HashSet<>();
        LinkedList<Integer> nodeQueue = new LinkedList<>();
        nodeQueue.offer(node);

        while (!nodeQueue.isEmpty()) {
            int currentNode = nodeQueue.poll();

            for (Edge edge : graph.edgesOf(currentNode)) {
                NodeBreakerBiConnectable biConnectable = edge.getBiConnectable();
                int nextNode = biConnectable.getNode1() == currentNode ? biConnectable.getNode2() : biConnectable.getNode1();
                if (visitedNodes.contains(nextNode)) {
                    continue;
                }
                visitedNodes.add(nextNode);

                TraverseResult traverserResult;
                if (biConnectable instanceof SwitchAttributes) {
                    traverserResult = traverseSwitch(traverser, biConnectable, currentNode, nextNode);
                } else if (biConnectable instanceof InternalConnectionAttributes) {
                    traverserResult = traverser.traverse(currentNode, null, nextNode);
                } else {
                    throw new AssertionError();
                }

                if (traverserResult == TraverseResult.CONTINUE) {
                    nodeQueue.offer(nextNode);
                } else if (traverserResult == TraverseResult.TERMINATE_TRAVERSER) {
                    return false;
                }
            }
        }
        return true;
    }

    private boolean traverseFromNodeDFS(Graph<Integer, Edge> graph, int node,
                                        TopologyTraverser traverser, Set<Integer> visitedNodes) {
        if (visitedNodes.contains(node)) {
            return true;
        }
        visitedNodes.add(node);

        for (Edge edge : graph.edgesOf(node)) {
            NodeBreakerBiConnectable biConnectable = edge.getBiConnectable();
            int nextNode = biConnectable.getNode1() == node ? biConnectable.getNode2() : biConnectable.getNode1();
            TraverseResult result;
            if (visitedNodes.contains(nextNode)) {
                continue;
            }
            if (biConnectable instanceof SwitchAttributes) {
                result = traverseSwitch(traverser, biConnectable, node, nextNode);
            } else if (biConnectable instanceof InternalConnectionAttributes) {
                result = traverser.traverse(node, null, nextNode);
            } else {
                throw new AssertionError();
            }
            if (result == TraverseResult.CONTINUE) {
                if (!traverseFromNodeDFS(graph, nextNode, traverser, visitedNodes)) {
                    return false;
                }
            } else if (result == TraverseResult.TERMINATE_TRAVERSER) {
                return false;
            }
        }
        return true;
    }

}

return true;
}

Expand All @@ -175,12 +242,12 @@ private TraverseResult traverseSwitch(VoltageLevel.NodeBreakerView.TopologyTrave
/**
* This is the method called when we traverse the topology stating from a terminal.
*/
boolean traverseFromTerminal(Terminal terminal, Terminal.TopologyTraverser traverser, Set<Terminal> traversedTerminals) {
boolean traverseFromTerminal(Terminal terminal, Terminal.TopologyTraverser traverser, Set<Terminal> traversedTerminals, TraversalType traversalType) {
checkBusBreakerTopology();
Objects.requireNonNull(traverser);

List<Terminal> nexTerminals = new ArrayList<>();
if (!traverseFromNode(terminal.getNodeBreakerView().getNode(), (node1, sw, node2) -> {
if (!traverseFromNode(terminal.getNodeBreakerView().getNode(), traversalType, (node1, sw, node2) -> {
if (sw != null) {
TraverseResult result = traverser.traverse(sw);
if (result != TraverseResult.CONTINUE) {
Expand All @@ -204,7 +271,7 @@ boolean traverseFromTerminal(Terminal terminal, Terminal.TopologyTraverser trave
}

for (Terminal nextTerminal : nexTerminals) {
if (!((TerminalImpl<?>) nextTerminal).traverse(traverser, traversedTerminals)) {
if (!((TerminalImpl<?>) nextTerminal).traverse(traverser, traversedTerminals, traversalType)) {
return false;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.powsybl.commons.PowsyblException;
import com.powsybl.iidm.network.*;
import com.powsybl.math.graph.TraverseResult;
import com.powsybl.math.graph.TraversalType;
import com.powsybl.network.store.model.*;
import org.jgrapht.Graph;
import org.jgrapht.alg.connectivity.ConnectivityInspector;
Expand Down Expand Up @@ -359,25 +360,30 @@ public String getMessageHeader() {

@Override
public void traverse(Terminal.TopologyTraverser traverser) {
traverse(traverser, TraversalType.DEPTH_FIRST);
}

@Override
public void traverse(Terminal.TopologyTraverser traverser, TraversalType traversalType) {
Set<Terminal> traversedTerminals = new HashSet<>();
if (getAbstractIdentifiable().getOptionalResource().isEmpty()) {
throw new PowsyblException("Associated equipment is removed");
}

// One side
if (!traverse(traverser, traversedTerminals)) {
if (!traverse(traverser, traversedTerminals, traversalType)) {
return;
}

// Other sides
for (Terminal otherSideTerminal : getOtherSideTerminals()) {
if (!((TerminalImpl<?>) otherSideTerminal).traverse(traverser, traversedTerminals)) {
if (!((TerminalImpl<?>) otherSideTerminal).traverse(traverser, traversedTerminals, traversalType)) {
return;
}
}
}

boolean traverse(Terminal.TopologyTraverser traverser, Set<Terminal> traversedTerminals) {
boolean traverse(Terminal.TopologyTraverser traverser, Set<Terminal> traversedTerminals, TraversalType traversalType) {
if (traversedTerminals.contains(this)) {
return true;
}
Expand All @@ -393,9 +399,9 @@ boolean traverse(Terminal.TopologyTraverser traverser, Set<Terminal> traversedTe
TopologyKind topologyKind = getTopologyKind();
switch (topologyKind) {
case NODE_BREAKER:
return ((NodeBreakerViewImpl) voltageLevel.getNodeBreakerView()).traverseFromTerminal(this, traverser, traversedTerminals);
return ((NodeBreakerViewImpl) voltageLevel.getNodeBreakerView()).traverseFromTerminal(this, traverser, traversedTerminals, traversalType);
case BUS_BREAKER:
return ((BusBreakerViewImpl) voltageLevel.getBusBreakerView()).traverseFromTerminal(this, traverser, traversedTerminals);
return ((BusBreakerViewImpl) voltageLevel.getBusBreakerView()).traverseFromTerminal(this, traverser, traversedTerminals, traversalType);
default:
throw new IllegalStateException("Unknown topology kind: " + topologyKind);
}
Expand Down
Loading
Loading