Skip to content

Commit bd1c1c5

Browse files
committed
add TU for fork topo
1 parent c112b6a commit bd1c1c5

File tree

3 files changed

+228
-20
lines changed

3 files changed

+228
-20
lines changed

src/main/java/org/gridsuite/network/map/dto/definition/extension/BusbarSectionFinderTraverser.java

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import com.powsybl.iidm.network.SwitchKind;
1212
import com.powsybl.iidm.network.Terminal;
1313
import com.powsybl.math.graph.TraverseResult;
14-
import lombok.Getter;
1514

1615
import java.util.ArrayList;
1716
import java.util.HashSet;
@@ -26,6 +25,11 @@ public class BusbarSectionFinderTraverser implements Terminal.TopologyTraverser
2625
private final List<BusbarCandidate> busbarCandidates = new ArrayList<>();
2726
private final Set<String> visitedTerminals = new HashSet<>();
2827
private static final int MAX_VISITED = 50;
28+
private final boolean allowTraversalThroughOpenDisconnectors;
29+
30+
public BusbarSectionFinderTraverser(boolean allowTraversalThroughOpenDisconnectors) {
31+
this.allowTraversalThroughOpenDisconnectors = allowTraversalThroughOpenDisconnectors;
32+
}
2933

3034
@Override
3135
public TraverseResult traverse(Terminal terminal, boolean connected) {
@@ -56,34 +60,43 @@ public TraverseResult traverse(Switch aSwitch) {
5660
// KEY: Open disconnectors end this path but not the overall traversal
5761
// They block access to this busbar but not to the others
5862
if (aSwitch.isOpen() && aSwitch.getKind() == SwitchKind.DISCONNECTOR) {
59-
return TraverseResult.TERMINATE_PATH; // Ends this path, not the whole traversal
63+
// Use the parameter to control behavior
64+
return allowTraversalThroughOpenDisconnectors ?
65+
TraverseResult.CONTINUE :
66+
TraverseResult.TERMINATE_PATH;
6067
}
6168
return TraverseResult.CONTINUE;
6269
}
6370

6471
public String getBusbarWithClosedDisconnector() {
6572
// Search for a connected busbar (disconnector closed)
6673
for (BusbarCandidate candidate : busbarCandidates) {
67-
if (candidate.isConnected()) {
68-
return candidate.getId();
74+
if (candidate.connected()) {
75+
return candidate.id();
6976
}
7077
}
7178

72-
// If none is connected, return the first one found (fallback)
73-
if (!busbarCandidates.isEmpty()) {
74-
return busbarCandidates.getFirst().getId();
75-
}
76-
return null;
79+
// Return first busbar found or null if none
80+
return !busbarCandidates.isEmpty() ? busbarCandidates.getFirst().id() : null;
7781
}
7882

79-
@Getter
80-
private static class BusbarCandidate {
81-
private final String id;
82-
private final boolean connected;
83+
// Utility method with automatic fallback
84+
public static String findBusbar(Terminal startTerminal) {
85+
// Attempt 1: normal behavior (blocks on open disconnectors)
86+
var traverser1 = new BusbarSectionFinderTraverser(false);
87+
startTerminal.traverse(traverser1);
88+
String result = traverser1.getBusbarWithClosedDisconnector();
8389

84-
public BusbarCandidate(String id, boolean connected) {
85-
this.id = id;
86-
this.connected = connected;
90+
if (result != null) {
91+
return result;
8792
}
93+
94+
// Attempt 2: if null, retry allowing traversal through open disconnectors
95+
var traverser2 = new BusbarSectionFinderTraverser(true);
96+
startTerminal.traverse(traverser2);
97+
return traverser2.getBusbarWithClosedDisconnector();
98+
}
99+
100+
private record BusbarCandidate(String id, boolean connected) {
88101
}
89102
}

src/main/java/org/gridsuite/network/map/dto/utils/ElementUtils.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
package org.gridsuite.network.map.dto.utils;
88

99
import com.powsybl.iidm.network.*;
10-
import com.powsybl.math.graph.TraversalType;
1110
import org.gridsuite.network.map.dto.common.ReactiveCapabilityCurveMapData;
1211
import org.gridsuite.network.map.dto.common.TapChangerData;
1312
import org.gridsuite.network.map.dto.common.TapChangerStepData;
@@ -48,9 +47,7 @@ public static String getBusOrBusbarSection(Terminal terminal) {
4847
}
4948
} else {
5049
// NODE_BREAKER: explore all paths and choose the busbar with the closed disconnector
51-
BusbarSectionFinderTraverser finder = new BusbarSectionFinderTraverser();
52-
terminal.traverse(finder, TraversalType.BREADTH_FIRST);
53-
return finder.getBusbarWithClosedDisconnector();
50+
return BusbarSectionFinderTraverser.findBusbar(terminal);
5451
}
5552
}
5653

src/test/java/org/gridsuite/network/map/NetworkMapControllerTest.java

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1188,6 +1188,204 @@ void setUp() {
11881188
.withDirection(ConnectablePosition.Direction.TOP).add()
11891189
.add();
11901190

1191+
/** VLGEN7 - Fork topology
1192+
*
1193+
* BUS1 ═══════X════════ BUS2 ═══════════════/════ BUS3
1194+
* (node0) (node5) (node7)
1195+
* | | |
1196+
* Disconnector1 Disconnector2 |
1197+
* | | |
1198+
* | | Disconnector3
1199+
* [open = true] [open = false] [open = false]
1200+
* | | |
1201+
* |____________________| ═══════════/════════ BUS4
1202+
* | |
1203+
* | |
1204+
* fork point Disconnector4
1205+
* (node8) [open = false]
1206+
* | |
1207+
* ┌───┴───┐ LINE9
1208+
* | | (→VLGEN9)
1209+
* LINE7 LINE8
1210+
* (→VLGEN4) (→VLGEN8)
1211+
*/
1212+
1213+
VoltageLevel vlgen7 = network.newVoltageLevel()
1214+
.setId("VLGEN7")
1215+
.setName("Fork Distribution Point")
1216+
.setNominalV(24.0)
1217+
.setHighVoltageLimit(30.0)
1218+
.setLowVoltageLimit(20.0)
1219+
.setTopologyKind(TopologyKind.NODE_BREAKER)
1220+
.add();
1221+
1222+
// Create busbarSections
1223+
vlgen7.getNodeBreakerView().newBusbarSection()
1224+
.setId("BUS1_NGEN7")
1225+
.setName("Primary Busbar VLGEN7")
1226+
.setNode(0)
1227+
.add();
1228+
1229+
vlgen7.getNodeBreakerView().newBusbarSection()
1230+
.setId("BUS2_NGEN7")
1231+
.setName("Secondary Busbar VLGEN7")
1232+
.setNode(5)
1233+
.add();
1234+
1235+
vlgen7.getNodeBreakerView().newBusbarSection()
1236+
.setId("BUS3_NGEN7")
1237+
.setName("Third Busbar VLGEN7")
1238+
.setNode(9)
1239+
.add();
1240+
vlgen7.getNodeBreakerView().newBusbarSection()
1241+
.setId("BUS4_NGEN7")
1242+
.setName("Fourth Busbar VLGEN7")
1243+
.setNode(13)
1244+
.add();
1245+
1246+
createSwitch(vlgen7, "SECT_BUS1", SwitchKind.DISCONNECTOR, true, 0, 6); // OPEN
1247+
createSwitch(vlgen7, "SECT_BUS2", SwitchKind.DISCONNECTOR, false, 5, 7); // CLOSED
1248+
createSwitch(vlgen7, "SECT_BUS3", SwitchKind.DISCONNECTOR, false, 9, 10); // CLOSED
1249+
createSwitch(vlgen7, "SECT_BUS4", SwitchKind.DISCONNECTOR, false, 13, 14); // CLOSED
1250+
1251+
createSwitch(vlgen7, "FORK_SW1", SwitchKind.DISCONNECTOR, false, 6, 8); // BUS1 to fork (CLOSED via node 6)
1252+
createSwitch(vlgen7, "FORK_SW2", SwitchKind.DISCONNECTOR, false, 7, 8); // BUS2 to fork (CLOSED via node 7)
1253+
1254+
// LINE7 connection from fork
1255+
createSwitch(vlgen7, "DISC_LINE7", SwitchKind.DISCONNECTOR, false, 8, 1);
1256+
createSwitch(vlgen7, "BRKR_LINE7", SwitchKind.BREAKER, false, 1, 2);
1257+
1258+
// LINE8 connection from fork
1259+
createSwitch(vlgen7, "DISC_LINE8", SwitchKind.DISCONNECTOR, false, 8, 3);
1260+
createSwitch(vlgen7, "BRKR_LINE8", SwitchKind.BREAKER, false, 3, 4);
1261+
1262+
// LINE9 connection from BUS4 (Fixed connection point)
1263+
createSwitch(vlgen7, "DISC_LINE9", SwitchKind.DISCONNECTOR, false, 14, 12); // Connect from node 14
1264+
createSwitch(vlgen7, "BRKR_LINE9", SwitchKind.BREAKER, false, 12, 11);
1265+
1266+
// Assuming VLGEN4 exists, create its switches
1267+
createSwitch(vlgen4, "DISC_VLGEN4", SwitchKind.DISCONNECTOR, false, 0, 10);
1268+
createSwitch(vlgen4, "BRKR_VLGEN4", SwitchKind.BREAKER, false, 10, 15);
1269+
1270+
// LINE7 - Fork Branch 1
1271+
network.newLine()
1272+
.setId("LINE7_FORK")
1273+
.setName("Fork Branch 1 - Primary Line")
1274+
.setVoltageLevel1("VLGEN4")
1275+
.setNode1(15) // Fixed node reference
1276+
.setVoltageLevel2("VLGEN7")
1277+
.setNode2(2)
1278+
.setR(3.0)
1279+
.setX(33.0)
1280+
.setG1(0.0)
1281+
.setB1(386E-6 / 2)
1282+
.setG2(0.0)
1283+
.setB2(386E-6 / 2)
1284+
.add();
1285+
1286+
Line line7 = network.getLine("LINE7_FORK");
1287+
line7.newExtension(ConnectablePositionAdder.class)
1288+
.newFeeder1()
1289+
.withName("LINE7_VLGEN4_Side")
1290+
.withOrder(5)
1291+
.withDirection(ConnectablePosition.Direction.BOTTOM)
1292+
.add()
1293+
.newFeeder2()
1294+
.withName("LINE7_VLGEN7_Fork_Side")
1295+
.withOrder(1)
1296+
.withDirection(ConnectablePosition.Direction.TOP)
1297+
.add()
1298+
.add();
1299+
1300+
// VLGEN8 - Fork destination 2
1301+
VoltageLevel vlgen8 = network.newVoltageLevel()
1302+
.setId("VLGEN8")
1303+
.setName("Fork Destination Point 2")
1304+
.setNominalV(24.0)
1305+
.setHighVoltageLimit(30.0)
1306+
.setLowVoltageLimit(20.0)
1307+
.setTopologyKind(TopologyKind.NODE_BREAKER)
1308+
.add();
1309+
createSwitch(vlgen8, "DISC_VLGEN8", SwitchKind.DISCONNECTOR, false, 0, 1);
1310+
createSwitch(vlgen8, "BRKR_VLGEN8", SwitchKind.BREAKER, false, 1, 2);
1311+
1312+
// LINE8 - Fork Branch 2
1313+
network.newLine()
1314+
.setId("LINE8_FORK")
1315+
.setName("Fork Branch 2 - Secondary Line")
1316+
.setVoltageLevel1("VLGEN7")
1317+
.setNode1(4)
1318+
.setVoltageLevel2("VLGEN8")
1319+
.setNode2(2)
1320+
.setR(2.5)
1321+
.setX(28.0)
1322+
.setG1(0.0)
1323+
.setB1(320E-6 / 2)
1324+
.setG2(0.0)
1325+
.setB2(320E-6 / 2)
1326+
.add();
1327+
Line line8 = network.getLine("LINE8_FORK");
1328+
line8.newExtension(ConnectablePositionAdder.class)
1329+
.newFeeder1()
1330+
.withName("LINE8_VLGEN7_Fork_Side")
1331+
.withOrder(2)
1332+
.withDirection(ConnectablePosition.Direction.BOTTOM)
1333+
.add()
1334+
.newFeeder2()
1335+
.withName("LINE8_VLGEN8_Side")
1336+
.withOrder(1)
1337+
.withDirection(ConnectablePosition.Direction.TOP)
1338+
.add()
1339+
.add();
1340+
1341+
// VLGEN9 - Independent line destination
1342+
VoltageLevel vlgen9 = network.newVoltageLevel()
1343+
.setId("VLGEN9")
1344+
.setName("Independent Line Destination")
1345+
.setNominalV(24.0)
1346+
.setHighVoltageLimit(30.0)
1347+
.setLowVoltageLimit(20.0)
1348+
.setTopologyKind(TopologyKind.NODE_BREAKER)
1349+
.add();
1350+
1351+
vlgen9.getNodeBreakerView().newBusbarSection()
1352+
.setId("BUS_NGEN9")
1353+
.setName("Main Busbar VLGEN9")
1354+
.setNode(0)
1355+
.add();
1356+
1357+
createSwitch(vlgen9, "DISC_VLGEN9", SwitchKind.DISCONNECTOR, false, 0, 1);
1358+
createSwitch(vlgen9, "BRKR_VLGEN9", SwitchKind.BREAKER, false, 1, 2);
1359+
1360+
// LINE9 - Independent line from BUS4
1361+
network.newLine()
1362+
.setId("LINE9_INDEPENDENT")
1363+
.setName("Independent Line from BUS4")
1364+
.setVoltageLevel1("VLGEN7")
1365+
.setNode1(11)
1366+
.setVoltageLevel2("VLGEN9")
1367+
.setNode2(2)
1368+
.setR(2.0)
1369+
.setX(25.0)
1370+
.setG1(0.0)
1371+
.setB1(300E-6 / 2)
1372+
.setG2(0.0)
1373+
.setB2(300E-6 / 2)
1374+
.add();
1375+
Line line9 = network.getLine("LINE9_INDEPENDENT");
1376+
line9.newExtension(ConnectablePositionAdder.class)
1377+
.newFeeder1()
1378+
.withName("LINE9_VLGEN7_Side")
1379+
.withOrder(3)
1380+
.withDirection(ConnectablePosition.Direction.BOTTOM)
1381+
.add()
1382+
.newFeeder2()
1383+
.withName("LINE9_VLGEN9_Side")
1384+
.withOrder(1)
1385+
.withDirection(ConnectablePosition.Direction.BOTTOM)
1386+
.add()
1387+
.add();
1388+
11911389
network.getVariantManager().setWorkingVariant(VariantManagerConstants.INITIAL_VARIANT_ID);
11921390

11931391
// Add new variant 2

0 commit comments

Comments
 (0)