From 5e15d64127e7b45fd7d0f3840b81080992fb5f9c Mon Sep 17 00:00:00 2001 From: Nicolas Rol Date: Wed, 4 Dec 2024 17:10:30 +0100 Subject: [PATCH] fix fork disconnection Signed-off-by: Nicolas Rol --- .../impl/NodeBreakerTopologyModel.java | 6 +- .../impl/ForkConnectDisconnectTest.java | 16 ++ .../AbstractForkConnectDisconnectTest.java | 193 ++++++++++++++++++ 3 files changed, 214 insertions(+), 1 deletion(-) create mode 100644 iidm/iidm-impl/src/test/java/com/powsybl/iidm/network/impl/ForkConnectDisconnectTest.java create mode 100644 iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractForkConnectDisconnectTest.java 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..a6c09915fc1 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 @@ -1167,6 +1167,10 @@ private static boolean isBusbarSection(Terminal t) { return t != null && t.getConnectable().getType() == IdentifiableType.BUSBAR_SECTION; } + private static boolean isTerminal(Terminal t) { + return t != null; + } + /** * Check if a switch is open and cannot be operated (according to the given predicate) * @param sw the switch to test @@ -1286,7 +1290,7 @@ boolean getDisconnectingSwitches(Terminal terminal, Predicate paths = graph.findAllPaths(node, NodeBreakerTopologyModel::isBusbarSection, SwitchPredicates.IS_OPEN); + List paths = graph.findAllPaths(node, NodeBreakerTopologyModel::isTerminal, SwitchPredicates.IS_OPEN); if (paths.isEmpty()) { return false; } diff --git a/iidm/iidm-impl/src/test/java/com/powsybl/iidm/network/impl/ForkConnectDisconnectTest.java b/iidm/iidm-impl/src/test/java/com/powsybl/iidm/network/impl/ForkConnectDisconnectTest.java new file mode 100644 index 00000000000..2fa1ed9df69 --- /dev/null +++ b/iidm/iidm-impl/src/test/java/com/powsybl/iidm/network/impl/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; + +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..0f43aa4734a --- /dev/null +++ b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractForkConnectDisconnectTest.java @@ -0,0 +1,193 @@ +/* + * 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 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.BUS_BREAKER) + .add(); + + // Busbar sections + BusbarSection bbs11 = vl1.getNodeBreakerView() + .newBusbarSection() + .setId("BBS11") + .setNode(0) + .add(); + bbs11.newExtension(BusbarSectionPositionAdder.class) + .withBusbarIndex(1) + .withSectionIndex(1) + .add(); + vl2.getBusBreakerView() + .newBus() + .setId("bus2A") + .add(); + vl2.getBusBreakerView() + .newBus() + .setId("bus2B") + .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) + .setBus2("bus2A") + .setConnectableBus2("bus2A") + .add(); + 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) + .setBus2("bus2B") + .setConnectableBus2("bus2B") + .add(); + + // 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(); + 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()); + line1.getTerminals().forEach(terminal -> assertNotNull(terminal.getBusView().getBus())); + line1.getTerminals().forEach(terminal -> assertTrue(terminal.isConnected())); + assertTrue(topo.getOptionalTerminal(5).isPresent()); + line2.getTerminals().forEach(terminal -> assertNotNull(terminal.getBusView().getBus())); + line2.getTerminals().forEach(terminal -> assertTrue(terminal.isConnected())); + + // Disconnect L1 + line1.disconnect(SwitchPredicates.IS_OPEN.negate()); + + // check that L1 is disconnected while L2 is still connected + assertTrue(topo.getOptionalTerminal(4).isPresent()); + line1.getTerminals().forEach(terminal -> assertNull(terminal.getBusView().getBus())); + line1.getTerminals().forEach(terminal -> assertFalse(terminal.isConnected())); + assertTrue(topo.getOptionalTerminal(5).isPresent()); + line2.getTerminals().forEach(terminal -> assertNotNull(terminal.getBusView().getBus())); + line2.getTerminals().forEach(terminal -> assertTrue(terminal.isConnected())); + + // 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()); + line1.getTerminals().forEach(terminal -> assertNotNull(terminal.getBusView().getBus())); + line1.getTerminals().forEach(terminal -> assertTrue(terminal.isConnected())); + assertTrue(topo.getOptionalTerminal(5).isPresent()); + line2.getTerminals().forEach(terminal -> assertNotNull(terminal.getBusView().getBus())); + line2.getTerminals().forEach(terminal -> assertTrue(terminal.isConnected())); + + // Disconnect L1 + line1.disconnect(SwitchPredicates.IS_OPEN.negate()); + + // check that L1 and L2 are disconnected + assertTrue(topo.getOptionalTerminal(4).isPresent()); + line1.getTerminals().forEach(terminal -> assertNull(terminal.getBusView().getBus())); + line1.getTerminals().forEach(terminal -> assertFalse(terminal.isConnected())); + assertTrue(topo.getOptionalTerminal(5).isPresent()); + line2.getTerminals().forEach(terminal -> assertNull(terminal.getBusView().getBus())); + line2.getTerminals().forEach(terminal -> assertFalse(terminal.isConnected())); + // TODO: Warning - L2 is half connected! + + // D_L1 should be open but not D_L2 + assertTrue(disconnectorL1.isOpen()); + assertFalse(disconnectorL2.isOpen()); + } +}