diff --git a/graph/src/main/java/eu/itesla_project/graph/UndirectedGraphImpl.java b/graph/src/main/java/eu/itesla_project/graph/UndirectedGraphImpl.java index 0cee227a..244911e1 100644 --- a/graph/src/main/java/eu/itesla_project/graph/UndirectedGraphImpl.java +++ b/graph/src/main/java/eu/itesla_project/graph/UndirectedGraphImpl.java @@ -369,21 +369,12 @@ private boolean findAllPaths(int e, int v1or2, Function pathComplete return false; } Vertex obj1or2 = vertices.get(v1or2); + path.add(e); if (pathComplete.apply(obj1or2.getObject())) { - path.add(e); paths.add(path); return true; } else { - if (i < adjacentEdges.size () - 1) { - TIntArrayList path2 = new TIntArrayList(path); - BitSet encountered2 = new BitSet(vertices.size()); - encountered2.or(encountered); - path2.add(e); - findAllPaths(v1or2, pathComplete, pathCanceled, path2, encountered2, paths); - } else { - path.add(e); - findAllPaths(v1or2, pathComplete, pathCanceled, path, encountered, paths); - } + findAllPaths(v1or2, pathComplete, pathCanceled, path, encountered, paths); return false; } } @@ -398,17 +389,27 @@ private void findAllPaths(int v, Function pathComplete, Function edge = edges.get(e); if (pathCanceled != null && pathCanceled.apply(edge.getObject())) { - return; + continue; } int v1 = edge.getV1(); int v2 = edge.getV2(); + TIntArrayList path2; + BitSet encountered2; + if (i < adjacentEdges.size () - 1) { + path2 = new TIntArrayList(path); + encountered2 = new BitSet(vertices.size()); + encountered2.or(encountered); + } else { + path2 = path; + encountered2 = encountered; + } if (v == v2) { - if (findAllPaths(e, v1, pathComplete, pathCanceled, adjacentEdges, i, path, encountered, paths)) { - return; + if (findAllPaths(e, v1, pathComplete, pathCanceled, adjacentEdges, i, path2, encountered2, paths)) { + continue; } } else if (v == v1) { - if (findAllPaths(e, v2, pathComplete, pathCanceled, adjacentEdges, i, path, encountered, paths)) { - return; + if (findAllPaths(e, v2, pathComplete, pathCanceled, adjacentEdges, i, path2, encountered2, paths)) { + continue; } } else { throw new AssertionError(); diff --git a/iidm-network-api/src/main/java/eu/itesla_project/iidm/network/Network.java b/iidm-network-api/src/main/java/eu/itesla_project/iidm/network/Network.java index 652a7c60..3e92e6de 100644 --- a/iidm-network-api/src/main/java/eu/itesla_project/iidm/network/Network.java +++ b/iidm-network-api/src/main/java/eu/itesla_project/iidm/network/Network.java @@ -313,6 +313,13 @@ public static interface BusView { */ StaticVarCompensator getStaticVarCompensator(String id); + /** + * Get a switch from its id. + * @param id id of the switch + * @return the switch + */ + Switch getSwitch(String id); + /** * Get a equipment. * diff --git a/iidm-network-impl/src/main/java/eu/itesla_project/iidm/network/impl/NetworkImpl.java b/iidm-network-impl/src/main/java/eu/itesla_project/iidm/network/impl/NetworkImpl.java index 60dd5d7b..483b28cf 100644 --- a/iidm-network-impl/src/main/java/eu/itesla_project/iidm/network/impl/NetworkImpl.java +++ b/iidm-network-impl/src/main/java/eu/itesla_project/iidm/network/impl/NetworkImpl.java @@ -364,6 +364,11 @@ public StaticVarCompensatorImpl getStaticVarCompensator(String id) { return objectStore.get(id, StaticVarCompensatorImpl.class); } + @Override + public Switch getSwitch(String id) { + return objectStore.get(id, SwitchImpl.class); + } + @Override public Identifiable getIdentifiable(String id) { return objectStore.get(id, Identifiable.class); diff --git a/iidm-network-impl/src/main/java/eu/itesla_project/iidm/network/impl/NodeBreakerVoltageLevel.java b/iidm-network-impl/src/main/java/eu/itesla_project/iidm/network/impl/NodeBreakerVoltageLevel.java index 38f3cbc0..b8739fd1 100644 --- a/iidm-network-impl/src/main/java/eu/itesla_project/iidm/network/impl/NodeBreakerVoltageLevel.java +++ b/iidm-network-impl/src/main/java/eu/itesla_project/iidm/network/impl/NodeBreakerVoltageLevel.java @@ -887,18 +887,29 @@ public boolean disconnect(TerminalExt terminal) { // find all paths starting from the current terminal to a busbar section that does not contain an open disconnector // (because otherwise there is nothing we can do to connected the terminal using only breakers) List paths = graph.findAllPaths(node, NodeBreakerVoltageLevel::isBusbarSection, NodeBreakerVoltageLevel::isOpenedDisconnector); - for (TIntArrayList path : paths) { - for (int i = 0; i < path.size(); i++) { - int e = path.get(i); - SwitchImpl sw = graph.getEdgeObject(e); - if (sw.getKind() == SwitchKind.BREAKER && !sw.isOpen()) { - sw.setOpen(true); - // open just one breaker is enough to disconnect the terminal, so we can stop - return true; + if (paths.isEmpty()) { + return false; + } else { + for (TIntArrayList path : paths) { + boolean pathOpen = false; + for (int i = 0; i < path.size(); i++) { + int e = path.get(i); + SwitchImpl sw = graph.getEdgeObject(e); + if (sw.getKind() == SwitchKind.BREAKER) { + if (!sw.isOpen()) { + sw.setOpen(true); + } + // just one open breaker is enough to disconnect the terminal, so we can stop + pathOpen = true; + break; + } + } + if (!pathOpen) { + return false; } } + return true; } - return false; } boolean isConnected(TerminalExt terminal) { diff --git a/iidm-network-impl/src/test/java/eu/itesla_project/iidm/network/impl/NodeBreakerDisconnectionDoublePathBugTest.java b/iidm-network-impl/src/test/java/eu/itesla_project/iidm/network/impl/NodeBreakerDisconnectionDoublePathBugTest.java new file mode 100644 index 00000000..aa8c07f6 --- /dev/null +++ b/iidm-network-impl/src/test/java/eu/itesla_project/iidm/network/impl/NodeBreakerDisconnectionDoublePathBugTest.java @@ -0,0 +1,83 @@ +/** + * Copyright (c) 2016, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package eu.itesla_project.iidm.network.impl; + +import eu.itesla_project.iidm.network.*; +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * @author Geoffroy Jamgotchian + */ +public class NodeBreakerDisconnectionDoublePathBugTest { + + private Network createNetwork() { + Network network = NetworkFactory.create("test", "test"); + Substation s = network.newSubstation() + .setId("S") + .setCountry(Country.FR) + .add(); + VoltageLevel vl = s.newVoltageLevel() + .setId("VL") + .setNominalV(400f) + .setTopologyKind(TopologyKind.NODE_BREAKER) + .add(); + vl.getNodeBreakerView().setNodeCount(10); + vl.getNodeBreakerView().newBusbarSection() + .setId("BBS1") + .setNode(0) + .add(); + vl.getNodeBreakerView().newBusbarSection() + .setId("BBS2") + .setNode(1) + .add(); + vl.newLoad() + .setId("L") + .setNode(2) + .setP0(1) + .setQ0(1) + .add(); + vl.getNodeBreakerView().newBreaker() + .setId("BR0") + .setNode1(2) + .setNode2(3) + .setOpen(false) + .add(); + vl.getNodeBreakerView().newBreaker() + .setId("BR1") + .setNode1(3) + .setNode2(0) + .setOpen(true) + .add(); + vl.getNodeBreakerView().newBreaker() + .setId("BR2") + .setNode1(3) + .setNode2(1) + .setOpen(false) + .add(); + return network; + } + + @Test + public void testOnePathAlreadyOpen() { + Network network = createNetwork(); + Load l = network.getLoad("L"); + assertTrue(l.getTerminal().isConnected()); + l.getTerminal().disconnect(); + assertFalse(l.getTerminal().isConnected()); + } + + @Test + public void testBothPathClosed() { + Network network = createNetwork(); + Load l = network.getLoad("L"); + network.getSwitch("BR1").setOpen(false); + assertTrue(l.getTerminal().isConnected()); + l.getTerminal().disconnect(); + assertFalse(l.getTerminal().isConnected()); + } +}