Skip to content

Commit a48c699

Browse files
committed
EtienneH code review remarks
1 parent 6b5a7f1 commit a48c699

File tree

2 files changed

+36
-91
lines changed

2 files changed

+36
-91
lines changed

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

Lines changed: 23 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -11,26 +11,27 @@
1111
import java.util.*;
1212

1313
/**
14-
* @author Slimane Amar <slimane.amar at rte-france.com>
14+
* @author Ghazwa Rehili <ghazwa.rehili at rte-france.com>
1515
*/
1616
// TODO : to remove when this class is available in network-store
1717
public final class BusbarSectionFinderTraverser {
1818

19-
/**
20-
* Private constructor to prevent instantiation of this utility class.
21-
*/
2219
private BusbarSectionFinderTraverser() {
2320
throw new UnsupportedOperationException();
2421
}
2522

26-
/**
27-
* Finds the best busbar section connected to the given terminal.
28-
* Uses a breadth-first search algorithm to explore all possible paths.
29-
*
30-
* @param terminal the starting terminal
31-
* @return the best busbar result according to selection criteria, or null if none found
32-
*/
33-
public static BusbarSectionResult findBestBusbar(Terminal terminal) {
23+
private record NodePath(int node, List<SwitchInfo> pathSwitches, SwitchInfo lastSwitch) { }
24+
25+
public record SwitchInfo(String id, SwitchKind kind, boolean isOpen) { }
26+
27+
public record BusbarSectionResult(String busbarSectionId, int depth, SwitchInfo lastSwitch) { }
28+
29+
public static String findBusbarSectionId(Terminal terminal) {
30+
BusbarSectionResult result = getBusbarSectionResult(terminal);
31+
return result != null ? result.busbarSectionId() : null;
32+
}
33+
34+
public static BusbarSectionResult getBusbarSectionResult(Terminal terminal) {
3435
VoltageLevel.NodeBreakerView view = terminal.getVoltageLevel().getNodeBreakerView();
3536
int startNode = terminal.getNodeBreakerView().getNode();
3637
List<BusbarSectionResult> allResults = searchAllBusbars(view, startNode);
@@ -40,46 +41,22 @@ public static BusbarSectionResult findBestBusbar(Terminal terminal) {
4041
return selectBestBusbar(allResults);
4142
}
4243

43-
/**
44-
* Selects the best busbar from a list of candidates using a priority-based approach:
45-
* Priority 1: Busbar with closed last switch (minimum depth, then minimum switches before last)
46-
* Priority 2: Busbar with open last switch (minimum depth, then minimum switches before last)
47-
* Priority 3: Busbar without switch (direct connection, minimum depth)
48-
*
49-
* @param results list of all found busbar results
50-
* @return the best busbar according to selection criteria
51-
*/
5244
private static BusbarSectionResult selectBestBusbar(List<BusbarSectionResult> results) {
53-
// Priority 1: Search for busbar with closed last switch
45+
List<BusbarSectionResult> withoutSwitch = results.stream().filter(r -> r.lastSwitch() == null).toList();
46+
if (!withoutSwitch.isEmpty()) {
47+
return withoutSwitch.stream().min(Comparator.comparingInt(BusbarSectionResult::depth)).orElse(null);
48+
}
5449
List<BusbarSectionResult> withClosedSwitch = results.stream().filter(r -> r.lastSwitch() != null && !r.lastSwitch().isOpen()).toList();
5550
if (!withClosedSwitch.isEmpty()) {
56-
return withClosedSwitch.stream().min(Comparator.comparingInt(BusbarSectionResult::depth).thenComparingInt(BusbarSectionResult::switchesBeforeLast)).orElse(null);
51+
return withClosedSwitch.stream().min(Comparator.comparingInt(BusbarSectionResult::depth)).orElse(null);
5752
}
58-
59-
// Priority 2: Search for busbar with open last switch
6053
List<BusbarSectionResult> withOpenSwitch = results.stream().filter(r -> r.lastSwitch() != null && r.lastSwitch().isOpen()).toList();
6154
if (!withOpenSwitch.isEmpty()) {
62-
return withOpenSwitch.stream().min(Comparator.comparingInt(BusbarSectionResult::depth).thenComparingInt(BusbarSectionResult::switchesBeforeLast)).orElse(null);
55+
return withOpenSwitch.stream().min(Comparator.comparingInt(BusbarSectionResult::depth)).orElse(null);
6356
}
64-
65-
// Priority 3: Busbars without switch direct connection
66-
List<BusbarSectionResult> withoutSwitch = results.stream().filter(r -> r.lastSwitch() == null).toList();
67-
if (!withoutSwitch.isEmpty()) {
68-
return withoutSwitch.stream().min(Comparator.comparingInt(BusbarSectionResult::depth)).orElse(null);
69-
}
70-
71-
// Fallback: select first busbar
7257
return results.getFirst();
7358
}
7459

75-
/**
76-
* Searches all accessible busbars from a starting node using breadth-first search.
77-
* Explores the node-breaker topology through switches.
78-
*
79-
* @param view the node-breaker view of the voltage level
80-
* @param startNode the starting node index
81-
* @return list of all busbar results found
82-
*/
8360
private static List<BusbarSectionResult> searchAllBusbars(VoltageLevel.NodeBreakerView view, int startNode) {
8461
List<BusbarSectionResult> results = new ArrayList<>();
8562
Set<Integer> visited = new HashSet<>();
@@ -111,13 +88,11 @@ private static Optional<BusbarSectionResult> tryCreateBusbarResult(VoltageLevel.
11188
return Optional.empty();
11289
}
11390
Terminal term = nodeTerminal.get();
114-
// Check if current node is a busbar section
11591
if (term.getConnectable().getType() == IdentifiableType.BUSBAR_SECTION) {
11692
String busbarSectionId = term.getConnectable().getId();
11793
int depth = currentNodePath.pathSwitches().size();
11894
SwitchInfo lastSwitch = currentNodePath.lastSwitch();
119-
int switchesBeforeLast = lastSwitch != null ? (depth - 1) : 0;
120-
return Optional.of(new BusbarSectionResult(busbarSectionId, depth, switchesBeforeLast, lastSwitch));
95+
return Optional.of(new BusbarSectionResult(busbarSectionId, depth, lastSwitch));
12196
}
12297
return Optional.empty();
12398
}
@@ -128,7 +103,7 @@ private static void exploreAdjacentNodes(VoltageLevel.NodeBreakerView view, Node
128103
int node2 = view.getNode2(sw.getId());
129104
Optional<Integer> nextNode = getNextNodeIfAdjacent(currentNodePath.node(), node1, node2);
130105
if (nextNode.isPresent() && !visited.contains(nextNode.get())) {
131-
NodePath newPath = createNodePath(currentNodePath, sw, node1, node2, nextNode.get());
106+
NodePath newPath = createNodePath(currentNodePath, sw, nextNode.get());
132107
queue.offer(newPath);
133108
}
134109
});
@@ -144,36 +119,10 @@ private static Optional<Integer> getNextNodeIfAdjacent(int currentNode, int node
144119
return Optional.empty();
145120
}
146121

147-
private static NodePath createNodePath(NodePath currentNodePath, Switch sw, int node1, int node2, int nextNode) {
122+
private static NodePath createNodePath(NodePath currentNodePath, Switch sw, int nextNode) {
148123
List<SwitchInfo> newPathSwitches = new ArrayList<>(currentNodePath.pathSwitches());
149-
SwitchInfo switchInfo = new SwitchInfo(sw.getId(), sw.getKind(), sw.isOpen(), node1, node2);
124+
SwitchInfo switchInfo = new SwitchInfo(sw.getId(), sw.getKind(), sw.isOpen());
150125
newPathSwitches.add(switchInfo);
151126
return new NodePath(nextNode, newPathSwitches, switchInfo);
152127
}
153-
154-
/**
155-
* Internal record to track the path during graph traversal.
156-
*/
157-
private record NodePath(int node, List<SwitchInfo> pathSwitches, SwitchInfo lastSwitch) { }
158-
159-
/**
160-
* Record containing information about a switch in the topology.
161-
*/
162-
public record SwitchInfo(String id, SwitchKind kind, boolean isOpen, int node1, int node2) { }
163-
164-
/**
165-
* Record containing the result of a busbar search with selection metadata.
166-
*/
167-
public record BusbarSectionResult(String busbarSectionId, int depth, int switchesBeforeLast, SwitchInfo lastSwitch) { }
168-
169-
/**
170-
* Convenience method to get only the busbar ID.
171-
*
172-
* @param terminal the starting terminal
173-
* @return the busbar ID or null if none found
174-
*/
175-
public static String findBusbarSectionId(Terminal terminal) {
176-
BusbarSectionResult result = findBestBusbar(terminal);
177-
return result != null ? result.busbarSectionId() : null;
178-
}
179128
}

src/test/java/org/gridsuite/network/map/mapper/BusbarSectionFinderTraverserTest.java

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -333,8 +333,8 @@ void testForkTopologyFindsBus2() {
333333
// Both must find BUS2 because SECT_BUS2 is closed
334334
Line line7 = network.getLine("LINE7_FORK");
335335
Line line8 = network.getLine("LINE8_FORK");
336-
BusbarSectionFinderTraverser.BusbarSectionResult result7 = BusbarSectionFinderTraverser.findBestBusbar(line7.getTerminal2());
337-
BusbarSectionFinderTraverser.BusbarSectionResult result8 = BusbarSectionFinderTraverser.findBestBusbar(line8.getTerminal1());
336+
BusbarSectionFinderTraverser.BusbarSectionResult result7 = BusbarSectionFinderTraverser.getBusbarSectionResult(line7.getTerminal2());
337+
BusbarSectionFinderTraverser.BusbarSectionResult result8 = BusbarSectionFinderTraverser.getBusbarSectionResult(line8.getTerminal1());
338338
// Both lines must find the same busbar
339339
assertNotNull(result7);
340340
assertEquals("BUS2_NGEN7", result7.busbarSectionId());
@@ -350,7 +350,7 @@ void testForkTopologyFindsBus2() {
350350
@Test
351351
void testForkPreferencesClosedOverOpen() {
352352
Line line7 = network.getLine("LINE7_FORK");
353-
BusbarSectionFinderTraverser.BusbarSectionResult result = BusbarSectionFinderTraverser.findBestBusbar(line7.getTerminal2());
353+
BusbarSectionFinderTraverser.BusbarSectionResult result = BusbarSectionFinderTraverser.getBusbarSectionResult(line7.getTerminal2());
354354
// Must prefer BUS2 with closed switch rather than BUS1 with open switch
355355
assertEquals("BUS2_NGEN7", result.busbarSectionId());
356356
assertFalse(result.lastSwitch().isOpen());
@@ -362,7 +362,7 @@ void testForkFallbackToBus1WhenBus2Open() {
362362
// Open SECT_BUS2 to disconnect BUS2
363363
vlgen7.getNodeBreakerView().getSwitch("SECT_BUS2").setOpen(true);
364364
Line line7 = network.getLine("LINE7_FORK");
365-
BusbarSectionFinderTraverser.BusbarSectionResult result = BusbarSectionFinderTraverser.findBestBusbar(line7.getTerminal2());
365+
BusbarSectionFinderTraverser.BusbarSectionResult result = BusbarSectionFinderTraverser.getBusbarSectionResult(line7.getTerminal2());
366366
// Must find BUS1 (with open switch)
367367
assertNotNull(result);
368368
assertEquals("BUS1_NGEN7", result.busbarSectionId());
@@ -378,7 +378,7 @@ void testForkFallbackToBus1WhenBus2Open() {
378378
@Test
379379
void testBypassTopologyActivePath() {
380380
Line line9 = network.getLine("LINE9_INDEPENDENT");
381-
BusbarSectionFinderTraverser.BusbarSectionResult result = BusbarSectionFinderTraverser.findBestBusbar(line9.getTerminal1());
381+
BusbarSectionFinderTraverser.BusbarSectionResult result = BusbarSectionFinderTraverser.getBusbarSectionResult(line9.getTerminal1());
382382
// Must find BUS4 via the bypass path
383383
assertNotNull(result);
384384
assertEquals("BUS4_NGEN7", result.busbarSectionId());
@@ -396,7 +396,7 @@ void testBypassSwitchesToMainPathWhenBreakerCloses() {
396396
vlgen7.getNodeBreakerView().getSwitch("BRKR10").setOpen(false);
397397
vlgen7.getNodeBreakerView().getSwitch("DISC7").setOpen(true);
398398
Line line9 = network.getLine("LINE9_INDEPENDENT");
399-
BusbarSectionFinderTraverser.BusbarSectionResult result = BusbarSectionFinderTraverser.findBestBusbar(line9.getTerminal1());
399+
BusbarSectionFinderTraverser.BusbarSectionResult result = BusbarSectionFinderTraverser.getBusbarSectionResult(line9.getTerminal1());
400400
// Must still find BUS4
401401
assertNotNull(result);
402402
assertEquals("BUS4_NGEN7", result.busbarSectionId());
@@ -410,7 +410,7 @@ void testBypassFallbackToBus3() {
410410
// Completely isolate BUS4
411411
vlgen7.getNodeBreakerView().getSwitch("SECT_BUS4").setOpen(true);
412412
Line line9 = network.getLine("LINE9_INDEPENDENT");
413-
BusbarSectionFinderTraverser.BusbarSectionResult result = BusbarSectionFinderTraverser.findBestBusbar(line9.getTerminal1());
413+
BusbarSectionFinderTraverser.BusbarSectionResult result = BusbarSectionFinderTraverser.getBusbarSectionResult(line9.getTerminal1());
414414
// Should still find BUS4 because bypass is still connected to it
415415
assertNotNull(result);
416416
assertEquals("BUS4_NGEN7", result.busbarSectionId());
@@ -424,12 +424,11 @@ void testBypassFallbackToBus3() {
424424
@Test
425425
void testPrioritizesShortestClosedPath() {
426426
Line line7 = network.getLine("LINE7_FORK");
427-
BusbarSectionFinderTraverser.BusbarSectionResult result = BusbarSectionFinderTraverser.findBestBusbar(line7.getTerminal2());
427+
BusbarSectionFinderTraverser.BusbarSectionResult result = BusbarSectionFinderTraverser.getBusbarSectionResult(line7.getTerminal2());
428428
// BUS2 is the closest with closed switch
429429
assertNotNull(result);
430430
assertEquals("BUS2_NGEN7", result.busbarSectionId());
431431
assertEquals(4, result.depth());
432-
assertEquals(3, result.switchesBeforeLast());
433432
}
434433

435434
@Test
@@ -442,7 +441,7 @@ void testSelectionPriorityOrder() {
442441
vlgen7.getNodeBreakerView().getSwitch("SECT_BUS4").setOpen(true);
443442
// But keep fork connections
444443
Line line7 = network.getLine("LINE7_FORK");
445-
BusbarSectionFinderTraverser.BusbarSectionResult result = BusbarSectionFinderTraverser.findBestBusbar(line7.getTerminal2());
444+
BusbarSectionFinderTraverser.BusbarSectionResult result = BusbarSectionFinderTraverser.getBusbarSectionResult(line7.getTerminal2());
446445
// Must find a busbar with open switch priority 2
447446
assertNotNull(result);
448447
assertNotNull(result.lastSwitch());
@@ -456,7 +455,7 @@ void testReturnsResultEvenWithNoClosedPaths() {
456455
vlgen7.getNodeBreakerView().getSwitch("SECT_BUS1").setOpen(true);
457456
vlgen7.getNodeBreakerView().getSwitch("SECT_BUS2").setOpen(true);
458457
Line line7 = network.getLine("LINE7_FORK");
459-
BusbarSectionFinderTraverser.BusbarSectionResult result = BusbarSectionFinderTraverser.findBestBusbar(line7.getTerminal2());
458+
BusbarSectionFinderTraverser.BusbarSectionResult result = BusbarSectionFinderTraverser.getBusbarSectionResult(line7.getTerminal2());
460459
// Must return a result (busbar accessible via open switch)
461460
assertNotNull(result);
462461
assertTrue(result.depth() > 0);
@@ -478,17 +477,16 @@ void testForkLinesShareSameBusbar() {
478477
void testSwitchesBeforeLastCount() {
479478
Line line7 = network.getLine("LINE7_FORK");
480479
Terminal terminal = line7.getTerminal2();
481-
BusbarSectionFinderTraverser.BusbarSectionResult result = BusbarSectionFinderTraverser.findBestBusbar(terminal);
480+
BusbarSectionFinderTraverser.BusbarSectionResult result = BusbarSectionFinderTraverser.getBusbarSectionResult(terminal);
482481
assertNotNull(result);
483482
assertEquals(4, result.depth());
484-
assertEquals(3, result.switchesBeforeLast());
485483
}
486484

487485
@Test
488486
void testHandlesMixedSwitchTypes() {
489487
Line line7 = network.getLine("LINE7_FORK");
490488
Terminal terminal = line7.getTerminal2();
491-
BusbarSectionFinderTraverser.BusbarSectionResult result = BusbarSectionFinderTraverser.findBestBusbar(terminal);
489+
BusbarSectionFinderTraverser.BusbarSectionResult result = BusbarSectionFinderTraverser.getBusbarSectionResult(terminal);
492490
assertNotNull(result);
493491
// Path contains both breakers and disconnectors
494492
assertTrue(result.depth() > 0);
@@ -497,9 +495,7 @@ void testHandlesMixedSwitchTypes() {
497495
@Test
498496
void testSwitchesBeforeLastCountAccuracy() {
499497
Line line7 = network.getLine("LINE7_FORK");
500-
BusbarSectionFinderTraverser.BusbarSectionResult result = BusbarSectionFinderTraverser.findBestBusbar(line7.getTerminal2());
498+
BusbarSectionFinderTraverser.BusbarSectionResult result = BusbarSectionFinderTraverser.getBusbarSectionResult(line7.getTerminal2());
501499
assertNotNull(result);
502-
// With depth=4, we must have 3 switches before the last one
503-
assertEquals(result.depth() - 1, result.switchesBeforeLast());
504500
}
505501
}

0 commit comments

Comments
 (0)