diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NodeBreakerTopologyModel.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NodeBreakerTopologyModel.java index d707c7f3d6e..93873b380a2 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NodeBreakerTopologyModel.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NodeBreakerTopologyModel.java @@ -1285,8 +1285,8 @@ boolean getDisconnectingSwitches(Terminal terminal, Predicate paths = graph.findAllPaths(node, NodeBreakerTopologyModel::isBusbarSection, SwitchPredicates.IS_OPEN); + // find all paths starting from the current terminal to a terminal that does not contain an open switch + List paths = graph.findAllPaths(node, Objects::nonNull, SwitchPredicates.IS_OPEN); if (paths.isEmpty()) { return false; } diff --git a/iidm/iidm-impl/src/test/java/com/powsybl/iidm/network/impl/tck/ForkConnectDisconnectTest.java b/iidm/iidm-impl/src/test/java/com/powsybl/iidm/network/impl/tck/ForkConnectDisconnectTest.java new file mode 100644 index 00000000000..159491af1e8 --- /dev/null +++ b/iidm/iidm-impl/src/test/java/com/powsybl/iidm/network/impl/tck/ForkConnectDisconnectTest.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2024, 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/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.iidm.network.impl.tck; + +import com.powsybl.iidm.network.tck.AbstractForkConnectDisconnectTest; + +/** + * @author Nicolas Rol {@literal } + */ +class ForkConnectDisconnectTest extends AbstractForkConnectDisconnectTest { +} diff --git a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractForkConnectDisconnectTest.java b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractForkConnectDisconnectTest.java new file mode 100644 index 00000000000..5231e5539d3 --- /dev/null +++ b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractForkConnectDisconnectTest.java @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2024, 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/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.iidm.network.tck; + +import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.extensions.BusbarSectionPositionAdder; +import com.powsybl.iidm.network.util.SwitchPredicates; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertFalse; + +/** + * @author Nicolas Rol {@literal } + */ +public abstract class AbstractForkConnectDisconnectTest { + + public Network createNetwork() { + Network network = Network.create("test", "test"); + // Substations + Substation s1 = network.newSubstation() + .setId("S1") + .setCountry(Country.FR) + .add(); + VoltageLevel vl1 = s1.newVoltageLevel() + .setId("VL1") + .setNominalV(1.0) + .setTopologyKind(TopologyKind.NODE_BREAKER) + .add(); + VoltageLevel vl2 = s1.newVoltageLevel() + .setId("VL2") + .setNominalV(1.0) + .setTopologyKind(TopologyKind.NODE_BREAKER) + .add(); + + // Busbar sections + BusbarSection bbs1 = vl1.getNodeBreakerView() + .newBusbarSection() + .setId("BBS11") + .setNode(0) + .add(); + bbs1.newExtension(BusbarSectionPositionAdder.class) + .withBusbarIndex(1) + .withSectionIndex(1) + .add(); + BusbarSection bbs2A = vl2.getNodeBreakerView() + .newBusbarSection() + .setId("BBS2A") + .setNode(0) + .add(); + bbs2A.newExtension(BusbarSectionPositionAdder.class) + .withBusbarIndex(1) + .withSectionIndex(1) + .add(); + BusbarSection bbs2B = vl2.getNodeBreakerView() + .newBusbarSection() + .setId("BBS2B") + .setNode(1) + .add(); + bbs2B.newExtension(BusbarSectionPositionAdder.class) + .withBusbarIndex(2) + .withSectionIndex(1) + .add(); + + // Lines + network.newLine() + .setId("L1") + .setName("LINE1") + .setR(1.0) + .setX(2.0) + .setG1(3.0) + .setG2(3.5) + .setB1(4.0) + .setB2(4.5) + .setVoltageLevel1("VL1") + .setVoltageLevel2("VL2") + .setNode1(4) + .setNode2(3) + .add(); + + // Add a second line + network.newLine() + .setId("L2") + .setName("LINE2") + .setR(1.0) + .setX(2.0) + .setG1(3.0) + .setG2(3.5) + .setB1(4.0) + .setB2(4.5) + .setVoltageLevel1("VL1") + .setVoltageLevel2("VL2") + .setNode1(5) + .setNode2(5) + .add(); + + // VL1 - Breakers and disconnectors for L1 and L2 + vl1.getNodeBreakerView().newDisconnector() + .setId("D_L1") + .setNode1(4) + .setNode2(6) + .setOpen(false) + .add(); + vl1.getNodeBreakerView().newDisconnector() + .setId("D_L2") + .setNode1(5) + .setNode2(6) + .setOpen(false) + .add(); + vl1.getNodeBreakerView().newBreaker() + .setId("B_L1_L2") + .setNode1(6) + .setNode2(7) + .setOpen(false) + .setFictitious(false) + .add(); + vl1.getNodeBreakerView().newDisconnector() + .setId("D0") + .setNode1(7) + .setNode2(0) + .setOpen(false) + .add(); + + // VL2 - Breakers and disconnectors for L1 and L2 + vl2.getNodeBreakerView().newBreaker() + .setId("B_L1_VL2") + .setNode1(3) + .setNode2(2) + .setOpen(false) + .setFictitious(false) + .add(); + vl2.getNodeBreakerView().newDisconnector() + .setId("D_L1_BBS2A") + .setNode1(2) + .setNode2(0) + .setOpen(false) + .add(); + vl2.getNodeBreakerView().newDisconnector() + .setId("D_L1_BBS2B") + .setNode1(2) + .setNode2(1) + .setOpen(true) + .add(); + vl2.getNodeBreakerView().newBreaker() + .setId("B_L2_VL2") + .setNode1(5) + .setNode2(4) + .setOpen(false) + .setFictitious(false) + .add(); + vl2.getNodeBreakerView().newDisconnector() + .setId("D_L2_BBS2A") + .setNode1(4) + .setNode2(0) + .setOpen(true) + .add(); + vl2.getNodeBreakerView().newDisconnector() + .setId("D_L2_BBS2B") + .setNode1(4) + .setNode2(1) + .setOpen(false) + .add(); + return network; + } + + @Test + public void fullyClosedTest() { + Network network = createNetwork(); + + // Useful elements + VoltageLevel.NodeBreakerView topo = network.getVoltageLevel("VL1").getNodeBreakerView(); + Line line1 = network.getLine("L1"); + Line line2 = network.getLine("L2"); + Switch disconnector = network.getSwitch("D_L1"); + + // L1 and L2 are fully connected + assertTrue(topo.getOptionalTerminal(4).isPresent()); + assertTerminalsStatus(line1.getTerminals(), true); + assertTrue(topo.getOptionalTerminal(5).isPresent()); + assertTerminalsStatus(line2.getTerminals(), true); + + // Disconnect L1 + line1.disconnect(SwitchPredicates.IS_NON_NULL); + + // check that L1 is disconnected while L2 is still connected + assertTrue(topo.getOptionalTerminal(4).isPresent()); + assertTerminalsStatus(line1.getTerminals(), false); + assertTrue(topo.getOptionalTerminal(5).isPresent()); + assertTerminalsStatus(line2.getTerminals(), true); + + // D_L1 should be open + assertTrue(disconnector.isOpen()); + } + + @Test + public void forkDisconnectedTest() { + Network network = createNetwork(); + + // Useful elements + VoltageLevel.NodeBreakerView topo = network.getVoltageLevel("VL1").getNodeBreakerView(); + Line line1 = network.getLine("L1"); + Line line2 = network.getLine("L2"); + Switch disconnectorL1 = network.getSwitch("D_L1"); + Switch disconnectorL2 = network.getSwitch("D_L2"); + Switch breaker = network.getSwitch("B_L1_L2"); + + // In this case, B_L1_L2 is open + breaker.setOpen(true); + + // L1 and L2 are fully connected + assertTrue(topo.getOptionalTerminal(4).isPresent()); + assertTerminalsStatus(line1.getTerminals(), true); + assertTrue(topo.getOptionalTerminal(5).isPresent()); + assertTerminalsStatus(line2.getTerminals(), true); + + // Disconnect L1 + line1.disconnect(SwitchPredicates.IS_NON_NULL); + + // check that L1 and L2 are disconnected + assertTrue(topo.getOptionalTerminal(4).isPresent()); + assertTerminalsStatus(line1.getTerminals(), false); + assertTrue(topo.getOptionalTerminal(5).isPresent()); + assertTerminalsStatus(List.of(line2.getTerminal1()), false); + assertTerminalsStatus(List.of(line2.getTerminal2()), true); + + // D_L1 should be open but not D_L2 + assertTrue(disconnectorL1.isOpen()); + assertFalse(disconnectorL2.isOpen()); + } + + private void assertTerminalsStatus(List terminals, boolean terminalsShouldBeConnected) { + if (terminalsShouldBeConnected) { + terminals.forEach(terminal -> assertNotNull(terminal.getBusView().getBus())); + terminals.forEach(terminal -> assertTrue(terminal.isConnected())); + } else { + terminals.forEach(terminal -> assertNull(terminal.getBusView().getBus())); + terminals.forEach(terminal -> assertFalse(terminal.isConnected())); + } + } +}