-
Notifications
You must be signed in to change notification settings - Fork 0
Depth-first search algorithm for BusBarSection IDs #293
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Depth-first search algorithm for BusBarSection IDs #293
Conversation
Test OK on several voltage levels |
public final class BusbarSectionFinderTraverser { | ||
|
||
/** | ||
* Private constructor to prevent instantiation of this utility class. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
comment to remove
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed
import java.util.*; | ||
|
||
/** | ||
* @author Slimane Amar <slimane.amar at rte-france.com> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ghazwa
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
* @param terminal the starting terminal | ||
* @return the busbar ID or null if none found | ||
*/ | ||
public static String findBusbarSectionId(Terminal terminal) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move the records and this method just under the constructor
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
* @param terminal the starting terminal | ||
* @return the best busbar result according to selection criteria, or null if none found | ||
*/ | ||
public static BusbarSectionResult findBestBusbar(Terminal terminal) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should return the busbar section id
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, you can remove findBusbarSectionId() and name this method findBusbarSectionId()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
renamed to getBusbarSectionResult
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer not remove findBusbarSectionId, renamed to getBusbarSectionResult
} | ||
|
||
private final boolean onlyConnectedBbs; | ||
// Fallback: select first busbar |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to remove
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
public String getFirstTraversedBbsId() { | ||
return firstTraversedBbsId; | ||
/** | ||
* Internal record to track the path during graph traversal. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
comments on records to remove ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed
/** | ||
* Record containing the result of a busbar search with selection metadata. | ||
*/ | ||
public record BusbarSectionResult(String busbarSectionId, int depth, int switchesBeforeLast, SwitchInfo lastSwitch) { } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the difference between depth and switchesBeforeLast ? It is redondant nope ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Depth is not the same as switchesBeforeLast (busbarSectionId)
but switchesBeforeLast to be removed
* Selects the best busbar from a list of candidates using a priority-based approach: | ||
* Priority 1: Busbar with closed last switch (minimum depth, then minimum switches before last) | ||
* Priority 2: Busbar with open last switch (minimum depth, then minimum switches before last) | ||
* Priority 3: Busbar without switch (direct connection, minimum depth) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this one is the least prioritized strategy ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be the first one ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
/** | ||
* Record containing information about a switch in the topology. | ||
*/ | ||
public record SwitchInfo(String id, SwitchKind kind, boolean isOpen, int node1, int node2) { } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
int node1, int node2 are not used
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had used them for debugging purposes, but now we don't need them anymore
removed
return !visited.contains(node); | ||
} | ||
|
||
private static Optional<BusbarSectionResult> tryCreateBusbarResult(VoltageLevel.NodeBreakerView view, NodePath currentNodePath) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
private static Optional<BusbarSectionResult> tryCreateBusbarResult(VoltageLevel.NodeBreakerView view, NodePath currentNodePath) { | |
private static Optional<BusbarSectionResult> getDirectlyConnectedBusbarSection(VoltageLevel.NodeBreakerView view, NodePath currentNodePath) { |
Something like this ? Here, we check if a busbar section is directly connected to the node and return it if yes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
something better than getDirectlyConnectedBusbarSection() to be found
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
findBusbarSectionAtNode maybe ?
return results; | ||
} | ||
|
||
private static boolean hasNotBeenVisited(int node, Set<Integer> visited) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
private static boolean hasNotBeenVisited(int node, Set<Integer> visited) { | |
private static boolean hasBeenVisited(int node, Set<Integer> visited) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
} | ||
|
||
private static boolean hasNotBeenVisited(int node, Set<Integer> visited) { | ||
return !visited.contains(node); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return !visited.contains(node); | |
return visited.contains(node); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
private static List<BusbarSectionResult> searchAllBusbars(VoltageLevel.NodeBreakerView view, int startNode) { | ||
List<BusbarSectionResult> results = new ArrayList<>(); | ||
Set<Integer> visited = new HashSet<>(); | ||
Queue<NodePath> queue = new LinkedList<>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Queue<NodePath> queue = new LinkedList<>(); | |
Queue<NodePath> pathsToVisit = new LinkedList<>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nodePathsToVisit maybe ?
return Optional.empty(); | ||
} | ||
|
||
private static void exploreAdjacentNodes(VoltageLevel.NodeBreakerView view, NodePath currentNodePath, Set<Integer> visited, Queue<NodePath> queue) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
private static void exploreAdjacentNodes(VoltageLevel.NodeBreakerView view, NodePath currentNodePath, Set<Integer> visited, Queue<NodePath> queue) { | |
private static void exploreAdjacentNodes(VoltageLevel.NodeBreakerView view, NodePath currentNodePath, Set<Integer> visited, Queue<NodePath> pathsToVisit) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
} | ||
|
||
private final boolean onlyConnectedBbs; | ||
private record NodePath(int node, List<SwitchInfo> pathSwitches, SwitchInfo lastSwitch) { } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
private record NodePath(int node, List<SwitchInfo> pathSwitches, SwitchInfo lastSwitch) { } | |
private record Path(int startNode, List<SwitchInfo> traversedSwitches, SwitchInfo lastSwitch) { } |
Something like this ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The term 'Path' is a generic name
it is ok for startNode and traversedSwitches
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tests:
This does not return the right busbar section in some cases for voltage levels with 'pontages' ...
private record NodePath(int node, List<SwitchInfo> pathSwitches, SwitchInfo lastSwitch) { } | ||
|
||
private String firstTraversedBbsId; | ||
public record SwitchInfo(String id, SwitchKind kind, boolean isOpen) { } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SwitchKind kind is not used
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I used it at the beginning for logging and debugging purposes
to be removed
if (!withOpenSwitch.isEmpty()) { | ||
return withOpenSwitch.stream().min(Comparator.comparingInt(BusbarSectionResult::depth)).orElse(null); | ||
} | ||
return results.getFirst(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can be empty
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This case is handled by returning result.busbarSectionId() when result is not null, and otherwise the Id of the first busbar section in the terminal’s voltage level
} | ||
|
||
private static void exploreAdjacentNodes(VoltageLevel.NodeBreakerView view, NodePath currentNodePath, Set<Integer> visited, Queue<NodePath> queue) { | ||
view.getSwitchStream().forEach(sw -> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not efficient. Maybe we can do something better
#293 (review) Iteration 7: Processing node 2 (depth: 3) Iteration 8: Processing node 0 (depth: 3) => return ALBERP6_1.1 |
|
Overview:
Nodes represent connection points
Switches (breakers, disconnectors) connect nodes
=> The goal is to use these informations to find which busbar a terminal is connected to
Algorithm Steps:
The search starts from a terminal's node and explores the network topology
The algorithm uses a queue to explore nodes level by level (by depth)
Finds the shortest path to each busbar
The algorithm applies a priority-based selection to choose the best busbar:
Priority 1: Direct connection (no switch in the path)
Priority 2: Connection through CLOSED switches
Priority 3: Connection through OPEN switches
=> The algorithm respects the real state of switches, prioritizing closed (active) connections over open (inactive) ones
if we consider the example of voltage level MUR P6 and we will search for the busbar section linked to MUR Y632
=== Starting busbar section search ===
Starting node: 7
--- Starting traversal ---
Iteration 1: Processing node 7 (depth: 0)
-> Marked as visited
-> Not a busbar, exploring adjacent nodes...
-> Adding node 14 to queue via switch MUR P6_MUR 6TR632 DJ_OC (BREAKER, CLOSED)
Iteration 2: Processing node 14 (depth: 1)
-> Marked as visited
-> Not a busbar, exploring adjacent nodes...
-> Adding node 0 to queue via switch MUR P6_MUR 6TR632 SA.1_OC (DISCONNECTOR, CLOSED)
-> Adding node 1 to queue via switch MUR P6_MUR 6TR632 SA.2_OC (DISCONNECTOR, OPEN)
Iteration 3: Processing node 0 (depth: 2)
-> Marked as visited
-> BUSBAR FOUND: MUR P6_1 (depth: 2, last switch: MUR P6_MUR 6TR632 SA.1_OC - CLOSED)
Iteration 4: Processing node 1 (depth: 2)
-> Marked as visited
-> BUSBAR FOUND: MUR P6_2 (depth: 2, last switch: MUR P6_MUR 6TR632 SA.2_OC - OPEN)
--- BFS traversal completed ---
Total nodes visited: 4
Total busbars found: 4
Found 4 busbar section(s), selecting best one...
--- Selecting best busbar from 4 candidates ---
Found 2 busbar(s) through closed switch
Selected: MUR P6_1 (depth: 2, last switch: MUR P6_MUR 6TR632 SA.1_OC - CLOSED)
Final result: MUR P6_1
=== Search completed ===