Skip to content

Comments

Add fix_branch_orientations utility function#156

Open
Thijss wants to merge 15 commits intomainfrom
fix-branch-orientation
Open

Add fix_branch_orientations utility function#156
Thijss wants to merge 15 commits intomainfrom
fix-branch-orientation

Conversation

@Thijss
Copy link
Member

@Thijss Thijss commented Feb 14, 2026

Add new utilty function:

def set_branch_orientations(grid: Grid, dry_run: bool = False) -> list[int]:
    """
    Set branch orientations in the grid so that all branches are oriented away from the sources.

    Orientation is determined by the distance of the branch's from_node and to_node to the source node.
    The node that is closer to the source is considered the "from_node".
    Note that this might not reflect the actual power flow direction in the grid.

    Other notes:
    - Sources should not be connected to other sources. If a source is connected to another source,
      a GraphError is raised.
    - Parallel edges (multiple edges between the same two nodes) and cycles are supported.

    Args:
        grid (Grid): The grid to fix branch orientations for.
        dry_run (bool): If True, do not actually modify the grid, just return the branch IDs that would be reverted
            (default: False).

    Returns:
        list[int]: A list of branch IDs that were reverted (or would be reverted in case of ``dry_run=True``).
    """

Todo

  • On second thought, I think we can allow fixing branches on cycles after all.

Piggyback

  • Add BaseGraph.has_parallel_edges()

Performance

I did some testing on our production data

93790 branches to consider
fix_branch_orientations took 8.61 seconds
1226 branches are reversed

Signed-off-by: Thijs Baaijen <13253091+Thijss@users.noreply.github.com>
Signed-off-by: Thijs Baaijen <13253091+Thijss@users.noreply.github.com>
Signed-off-by: Thijs Baaijen <13253091+Thijss@users.noreply.github.com>
…tests

Signed-off-by: Thijs Baaijen <13253091+Thijss@users.noreply.github.com>
… source connection issues

Signed-off-by: Thijs Baaijen <13253091+Thijss@users.noreply.github.com>
Signed-off-by: Thijs Baaijen <13253091+Thijss@users.noreply.github.com>
Signed-off-by: Thijs Baaijen <13253091+Thijss@users.noreply.github.com>
@Thijss Thijss marked this pull request as ready for review February 14, 2026 20:29
@Thijss Thijss changed the title Add fix_branch_orientation utility function Add fix_branch_orientations utility function Feb 14, 2026
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a new utility function fix_branch_orientations() to automatically orient all branches in a grid away from source nodes. The function ensures that in a radial network topology, all branches flow outward from the sources, which is important for power flow calculations and network analysis.

Changes:

  • Added fix_branch_orientations() utility function that reorients branches to flow away from sources
  • Added has_parallel_edges() method to the graph model interface to detect parallel edges
  • Added comprehensive test coverage for both new features

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/power_grid_model_ds/utils.py New file containing the fix_branch_orientations() function and helper functions for branch orientation fixing
src/power_grid_model_ds/_core/model/graphs/models/base.py Added abstract method has_parallel_edges() to the base graph model interface
src/power_grid_model_ds/_core/model/graphs/models/rustworkx.py Implemented has_parallel_edges() by delegating to rustworkx's built-in method
tests/unit/test_utils.py New test file with comprehensive tests for fix_branch_orientations() covering basic functionality, dry-run mode, cycle detection, connected sources, and parallel edges
tests/unit/model/graphs/test_graph_model.py Added tests for has_parallel_edges() method, including both same-direction and reversed parallel edges

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Thijss and others added 5 commits February 14, 2026 21:44
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Thijs Baaijen <13253091+Thijss@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Thijs Baaijen <13253091+Thijss@users.noreply.github.com>
Signed-off-by: Thijs Baaijen <13253091+Thijss@users.noreply.github.com>
Signed-off-by: Thijs Baaijen <13253091+Thijss@users.noreply.github.com>
Signed-off-by: Thijs Baaijen <13253091+Thijss@users.noreply.github.com>
Copy link
Member

@jaapschoutenalliander jaapschoutenalliander left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have some input on the setup but overall it looks good and I think it is a useful util function

if set(nodes_in_order) & set(other_source_nodes):
raise GraphError("Cannot fix branch orientations if source is connected to other sources")

connected_branches = grid.branches.filter(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we consider branches that have a single status to 0 in need to be directed with from_status=1? Such that

[from_node]<from_status> ----> <to_status> [to_node]
[101] 1 -----> 1 [102] 1 <----- 0 [103] 1 <---- 1 [104]

gets corrected to
[101] 1 -----> 1 [102] 1 -----> 0 [103] 1 <---- 1 [104]

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would involve the complete_graph too, so quite some work to implement now. I'd prefer to add this in a second iteration

Copy link
Member

@jaapschoutenalliander jaapschoutenalliander Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure it would involve the complete graph though, in this case the nodes in order are the same.

A filter like this would return the branches to switch I think:

grid.branches.filter(from_status=0, to_status=1, to_node=nodes_in_order).exclude(from_node=nodes_in_order)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, so any branch that matches this filter should be reversed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not entirely sure this works

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it does

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think this could be a nice addition.

Signed-off-by: Thijs Baaijen <13253091+Thijss@users.noreply.github.com>
Signed-off-by: Thijs Baaijen <13253091+Thijss@users.noreply.github.com>
Signed-off-by: Thijs Baaijen <13253091+Thijss@users.noreply.github.com>
@sonarqubecloud
Copy link

Copy link
Member

@vincentkoppen vincentkoppen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good!
Some remarks, but nice functionality to add :)

if set(nodes_in_order) & set(other_source_nodes):
raise GraphError("Cannot fix branch orientations if source is connected to other sources")

connected_branches = grid.branches.filter(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think this could be a nice addition.

Comment on lines +57 to +58
from_index = nodes_in_order.index(from_node)
to_index = nodes_in_order.index(to_node)
Copy link
Member

@vincentkoppen vincentkoppen Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: performance wise the .index is quite inefficient. I think it just loops over the list over and over again.
Instead we could make a dictionary once and use that for the lookup (which is O(1))?

node_rank  = {node: index for index,node in enumerate(nodes_in_order)
...
for branch in connected_branced: 
  ...
  if node_rank[from_node] > node_rank[to_node]]
   ...

from power_grid_model_ds._core.model.grids.base import Grid


def set_branch_orientations(grid: Grid, dry_run: bool = False) -> list[int]:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interface wise I'm not entirely sure on the dry_run yet.
Do we want to support a dry-run, or instead just make the _get_reverted_branches_for_source available publicly as well?
-> If we do we might want to change the interface to:
--- get_reverted_branches(grid) (so the loop moves to this function)
--- set_branch_orientations(grid) (no dry-run argument)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants