Skip to content

Conversation

@wwadman
Copy link

@wwadman wwadman commented Jan 13, 2026

Fixes issue: #118

Changes proposed in this PR include:

Adding function Grid.merge(self, other_grid) that merges another grid into this grid.

  1. I went for the hardcoded approach as explained in [FEATURE] Merge grids #118 (comment)
  2. Ideally we should make this more general, for example by adding an indicator ID_COLUMN whether a column contains ids or not.

Could you please pay extra attention to the points below when reviewing the PR:

Search for Question for reviewer in the diff

Checks

I added some tests that in my view cover most of the workings.

  • If changes are related to CI/CD, are they verified via a manual run on this branch?

wwadman added 11 commits January 8, 2026 14:37
Signed-off-by: wander.wadman <wander.wadman@alliander.com>
Signed-off-by: wander.wadman <wander.wadman@alliander.com>
Signed-off-by: wander.wadman <wander.wadman@alliander.com>
Signed-off-by: wander.wadman <wander.wadman@alliander.com>
Signed-off-by: wander.wadman <wander.wadman@alliander.com>
Signed-off-by: wander.wadman <wander.wadman@alliander.com>
Signed-off-by: wander.wadman <wander.wadman@alliander.com>
Signed-off-by: wander.wadman <wander.wadman@alliander.com>
Signed-off-by: wander.wadman <wander.wadman@alliander.com>
Signed-off-by: wander.wadman <wander.wadman@alliander.com>
@wwadman wwadman marked this pull request as ready for review January 13, 2026 14:51
@wwadman wwadman marked this pull request as draft January 13, 2026 14:51
Signed-off-by: wander.wadman <wander.wadman@alliander.com>
I, wander.wadman <wander.wadman@alliander.com>, hereby add my Signed-off-by to this commit: e0fe4f2

Signed-off-by: wander.wadman <wander.wadman@alliander.com>
@vincentkoppen vincentkoppen changed the title Feat/merge grids feat: support merge of two grids Jan 14, 2026
Signed-off-by: wander.wadman <wander.wadman@alliander.com>
Signed-off-by: wander.wadman <wander.wadman@alliander.com>
Signed-off-by: wander.wadman <wander.wadman@alliander.com>

# Conflicts:
#	src/power_grid_model_ds/_core/model/grids/_helpers.py
#	src/power_grid_model_ds/_core/model/grids/base.py
#	tests/unit/model/grids/test_modify.py
@wwadman wwadman marked this pull request as ready for review January 15, 2026 13:43
Thijss and others added 2 commits January 21, 2026 14:13
Signed-off-by: Thijs Baaijen <13253091+Thijss@users.noreply.github.com>
Signed-off-by: wander.wadman <wander.wadman@alliander.com>
@wwadman wwadman marked this pull request as draft January 21, 2026 14:09
Signed-off-by: wander.wadman <wander.wadman@alliander.com>
Signed-off-by: wander.wadman <wander.wadman@alliander.com>
@Thijss Thijss marked this pull request as ready for review January 21, 2026 14:47
Signed-off-by: wander.wadman <wander.wadman@alliander.com>
Signed-off-by: wander.wadman <wander.wadman@alliander.com>
wwadman and others added 3 commits January 21, 2026 15:55
Signed-off-by: wander.wadman <wander.wadman@alliander.com>
Signed-off-by: wander.wadman <wander.wadman@alliander.com>
Signed-off-by: jaapschoutenalliander <jaap.schouten@alliander.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.

Thanks for the good work

Signed-off-by: jaapschoutenalliander <jaap.schouten@alliander.com>
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.

Some small remarks, but looks good overall!
Thanks for the PR

Comment on lines +387 to +394
"""Merge another grid into this grid. When ids overlap, ids of other_grid are offset to avoid conflicts.
Args:
other_grid (G): The grid to merge into this grid.
mode (str): The merge mode:
- "recalculate_ids": Recalculate ids of other_grid to avoid conflicts.
- "keep_ids": Keep ids of other_grid. Raises an error if grids contain overlapping indices.
"""
Copy link
Member

@vincentkoppen vincentkoppen Jan 22, 2026

Choose a reason for hiding this comment

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

Update docstring to explicitely mention the current hardcoded support.

Suggested change
"""Merge another grid into this grid. When ids overlap, ids of other_grid are offset to avoid conflicts.
Args:
other_grid (G): The grid to merge into this grid.
mode (str): The merge mode:
- "recalculate_ids": Recalculate ids of other_grid to avoid conflicts.
- "keep_ids": Keep ids of other_grid. Raises an error if grids contain overlapping indices.
"""
"""Merge another grid into this grid.
Args:
other_grid (G): The grid to merge into this grid.
mode (str): The merge mode:
- "recalculate_ids": ids in the arays of other_grid are offset to avoid conflicts.
IMPORTANT: we currently only update any `id` column and all id references in the default PGM-DS grid.
- "keep_ids": Keep ids of other_grid. Raises an error if grids contain overlapping indices.
"""

)
return save_grid_to_pickle(self, cache_dir=cache_dir, cache_name=cache_name, compress=compress)

def merge(self, other_grid: G, mode: str) -> None:
Copy link
Member

Choose a reason for hiding this comment

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

Type hinting wise, we now allow any Grid. However I think that here we want it to be the same as the current grid.
So I think this should be Self?

return grid_class(**empty_fields)


def merge_grids(grid: G, other_grid: G, mode: str) -> None:
Copy link
Member

Choose a reason for hiding this comment

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

Also use the literal here.

case SymGenArray() | SymLoadArray() | SourceArray():
columns = ["node"]
case _:
raise NotImplementedError(f"The array of type {type(array)} is not implemented for appending")
Copy link
Member

Choose a reason for hiding this comment

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

We could maybe add an call for action?
To signal that we are willing to think about a more general solution if this is needed by anyone?

Something along the line of:

Suggested change
raise NotImplementedError(f"The array of type {type(array)} is not implemented for appending")
raise NotImplementedError(f"The array of type {type(array)} is not implemented for appending. Let us know if more general support is needed.")

Comment on lines +11 to +20
def test_merge_two_grids(self):
grid1 = Grid.from_txt("S1 2", "S1 3 link", "3 4 transformer")
grid2 = Grid.from_txt("S11 12", "S11 13 link", "13 14 transformer")
grid1_size = grid1.node.size
grid2_size = grid2.node.size

grid1.merge(grid2, mode="keep_ids")
merged_grid_size = grid1.node.size

assert merged_grid_size == grid1_size + grid2_size, "Merged grid size should be the sum of both grids' sizes"
Copy link
Member

@vincentkoppen vincentkoppen Jan 22, 2026

Choose a reason for hiding this comment

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

Also test that we didn't alter the ids. I'm also in facor of more direct values if the example is small enough, so we could do something like below.

So something like below?

Suggested change
def test_merge_two_grids(self):
grid1 = Grid.from_txt("S1 2", "S1 3 link", "3 4 transformer")
grid2 = Grid.from_txt("S11 12", "S11 13 link", "13 14 transformer")
grid1_size = grid1.node.size
grid2_size = grid2.node.size
grid1.merge(grid2, mode="keep_ids")
merged_grid_size = grid1.node.size
assert merged_grid_size == grid1_size + grid2_size, "Merged grid size should be the sum of both grids' sizes"
def test_merge_two_grids(self):
grid1 = Grid.from_txt("S1 2", "S1 3 link", "3 4 transformer")
grid2 = Grid.from_txt("S11 12", "S11 13 link", "13 14 transformer")
grid1.merge(grid2, mode="keep_ids")
assert grid1.node.id.tolist() == [1,2,3,4,11,12,13], "Merged node ids should be equal to those of grid1 and grid2 combined"

)

# assert node in grid.source is updated by checking if the node column contains values that are all node ids:
assert set(grid1.source.node).issubset(grid1.node.id), "All source nodes should be valid node ids!"
Copy link
Member

@vincentkoppen vincentkoppen Jan 22, 2026

Choose a reason for hiding this comment

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

nit:
Same here, I would prefer hardcoded values since the example is small enough. This removes the complexity from the test.

We now the grid_counter should be 501 (or maybe 502?)
So than it becomes like (have not updated all checks)

        grid1 = Grid.from_txt("S1 2", "S1 3 link", "3 4 transformer")
        grid2 = Grid.from_txt("S1 2", "S1 13 link", "13 14 transformer")
        source = SourceArray(id=[501], node=[1], status=[1], u_ref=[0.0])
        grid1.append(source)
        grid2.append(source)

        grid1.merge(grid2, mode="recalculate_ids")
        assert grid1.check_ids() is None, "Asset ids are not unique after merging!"

        expected_offset = 501
        assert grid1.node.id = [1, 2, 3, 4, expected_offset +1, expected_offset + 2, expected_offset + 13,expected_offset  + 14]
        assert grid1.link.from_node = [.....]

Comment on lines +40 to +46
def test_merge_two_grids_with_overlapping_line(self):
# Now both grids have 14 as highest node id, so both will have branch ids 15, 16 and 17:
grid1 = Grid.from_txt("S1 2", "S1 3 link", "3 14 transformer")
grid2 = Grid.from_txt("S1 2", "S1 13 link", "13 14 transformer")

grid1.merge(grid2, mode="recalculate_ids")
assert grid1.check_ids() is None, "Asset ids are not unique after merging!"
Copy link
Member

Choose a reason for hiding this comment

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

Don't see what this tests?
Missing asserts on the ids?

from power_grid_model_ds._core.model.arrays import SourceArray


class TestMergeGrids:
Copy link
Member

Choose a reason for hiding this comment

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

Add a test that we give a runtime error if we have an ExtendedGrid with a new array type and use recalculate_ids.

from power_grid_model_ds._core.model.arrays import SourceArray


class TestMergeGrids:
Copy link
Member

@vincentkoppen vincentkoppen Jan 22, 2026

Choose a reason for hiding this comment

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

Test not implemented error for invalid call to the merge function. (so mode="invalid" in the call)

…grid

Signed-off-by: wander.wadman <wander.wadman@alliander.com>
Signed-off-by: wander.wadman <wander.wadman@alliander.com>
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.

4 participants