Skip to content

Commit afc8a17

Browse files
committed
merge
Signed-off-by: Thijs Baaijen <[email protected]>
2 parents 9dabf71 + 8124ef2 commit afc8a17

File tree

3 files changed

+60
-58
lines changed

3 files changed

+60
-58
lines changed

src/power_grid_model_ds/_core/model/grids/_text_sources.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"""Create a grid from text a text file"""
66

77
import logging
8+
import re
89
from typing import TYPE_CHECKING
910

1011
from power_grid_model_ds._core.model.enums.nodes import NodeType
@@ -34,9 +35,12 @@ class TextSource:
3435
def __init__(self, grid_class: type["Grid"]):
3536
self.grid = grid_class.empty()
3637

37-
def load_from_txt(self, txt_lines: list[str]) -> "Grid":
38+
def load_from_txt(self, *args: str) -> "Grid":
3839
"""Load a grid from text"""
39-
txt_nodes, txt_branches = self.read_txt(txt_lines)
40+
41+
text_lines = [line for arg in args for line in arg.strip().split("\n")]
42+
43+
txt_nodes, txt_branches = self.read_txt(text_lines)
4044
self.add_nodes(txt_nodes)
4145
self.add_branches(txt_branches)
4246
self.grid.set_feeder_ids()
@@ -50,11 +54,14 @@ def read_txt(txt_lines: list[str]) -> tuple[set, dict]:
5054
for text_line in txt_lines:
5155
if not text_line.strip() or text_line.startswith("#"):
5256
continue # skip empty lines and comments
53-
try:
54-
from_node_str, to_node_str, *comments = text_line.strip().split(" ")
55-
except ValueError as err:
56-
raise ValueError(f"Text line '{text_line}' is invalid. Skipping...") from err
57-
comments = comments[0].split(",") if comments else []
57+
58+
pattern = re.compile(r"^\s*(\S+)\s+(\S+)(?:\s+(\S+))?\s*$")
59+
match = pattern.match(text_line)
60+
if not match:
61+
raise ValueError(f"Text line '{text_line}' is invalid. Skipping...")
62+
63+
from_node_str, to_node_str, comment = match.groups()
64+
comments = comment.split(",") if comment else []
5865

5966
txt_nodes |= {from_node_str, to_node_str}
6067
txt_branches[(from_node_str, to_node_str)] = comments

src/power_grid_model_ds/_core/model/grids/base.py

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,6 @@ def get_nearest_substation_node(self, node_id: int):
331331

332332
def get_downstream_nodes(self, node_id: int, inclusive: bool = False):
333333
"""Get the downstream nodes from a node.
334-
Assuming each node has a single feeding substation and the grid is radial
335334
336335
Example:
337336
given this graph: [1] - [2] - [3] - [4], with 1 being a substation node
@@ -350,14 +349,15 @@ def get_downstream_nodes(self, node_id: int, inclusive: bool = False):
350349
Returns:
351350
list[int]: The downstream nodes.
352351
"""
353-
substation_nodes = self.node.filter(node_type=NodeType.SUBSTATION_NODE.value)
352+
substation_node_id = self.get_nearest_substation_node(node_id).id.item()
354353

355-
if node_id in substation_nodes.id:
354+
if node_id == substation_node_id:
356355
raise NotImplementedError("get_downstream_nodes is not implemented for substation nodes!")
357356

358-
return self.graphs.active_graph.get_downstream_nodes(
359-
node_id=node_id, start_node_ids=list(substation_nodes.id), inclusive=inclusive
360-
)
357+
path_to_substation, _ = self.graphs.active_graph.get_shortest_path(node_id, substation_node_id)
358+
upstream_node = path_to_substation[1]
359+
360+
return self.graphs.active_graph.get_connected(node_id, nodes_to_ignore=[upstream_node], inclusive=inclusive)
361361

362362
def cache(self, cache_dir: Path, cache_name: str, compress: bool = True):
363363
"""Cache Grid to a folder
@@ -409,15 +409,19 @@ def _from_pickle(cls, pickle_path: Path):
409409
return grid
410410

411411
@classmethod
412-
def from_txt(cls, txt_lines: list[str]):
412+
def from_txt(cls, *args: str):
413413
"""Build a grid from a list of strings
414414
415415
See the documentation for the expected format of the txt_lines
416416
417-
Example:
418-
>>> Grid.from_txt(["1 2", "2 3", "3 4 transformer", "4 5", "S1 6"])
417+
Args:
418+
*args (str): The lines of the grid
419+
420+
Examples:
421+
>>> Grid.from_txt("1 2", "2 3", "3 4 transformer", "4 5", "S1 6")
422+
alternative: Grid.from_txt("1 2\n2 3\n3 4 transformer\n4 5\nS1 6")
419423
"""
420-
return TextSource(grid_class=cls).load_from_txt(txt_lines)
424+
return TextSource(grid_class=cls).load_from_txt(*args)
421425

422426
@classmethod
423427
# pylint: disable=arguments-differ
@@ -429,7 +433,7 @@ def from_txt_file(cls, txt_file_path: Path):
429433
"""
430434
with open(txt_file_path, "r", encoding="utf-8") as f:
431435
txt_lines = f.readlines()
432-
return TextSource(grid_class=cls).load_from_txt(txt_lines)
436+
return TextSource(grid_class=cls).load_from_txt(*txt_lines)
433437

434438
def set_feeder_ids(self):
435439
"""Sets feeder and substation id properties in the grids arrays"""

tests/unit/model/grids/test_grid_base.py

Lines changed: 31 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ def test_grid_as_str(basic_grid):
277277

278278
class TestFromTxt:
279279
def test_from_txt_lines(self):
280-
txt_lines = [
280+
grid = Grid.from_txt(
281281
"S1 2",
282282
"S1 3 open",
283283
"2 7",
@@ -286,64 +286,55 @@ def test_from_txt_lines(self):
286286
"5 7",
287287
"7 8",
288288
"8 9",
289-
]
290-
grid = Grid.from_txt(txt_lines)
289+
)
291290
assert 8 == grid.node.size
292291
assert 1 == grid.branches.filter(to_status=0).size
293292
assert 1 == grid.transformer.size
294293
np.testing.assert_array_equal([14, 10, 11, 12, 13, 15, 16, 17], grid.branches.id)
295294

295+
def test_from_txt_string(self):
296+
txt_string = "S1 2\nS1 3 open\n2 7\n3 5\n3 6 transformer\n5 7\n7 8\n8 9"
297+
assert Grid.from_txt(txt_string)
298+
299+
def test_from_txt_string_with_spaces(self):
300+
txt_string = "S1 2 \nS1 3 open\n2 7\n3 5\n 3 6 transformer\n5 7\n7 8\n8 9"
301+
assert Grid.from_txt(txt_string)
302+
303+
def test_from_docstring(self):
304+
assert Grid.from_txt("""
305+
S1 2
306+
S1 3 open
307+
2 7
308+
3 5
309+
3 6 transformer
310+
5 7
311+
7 8
312+
8 9
313+
""")
314+
296315
def test_from_txt_with_branch_ids(self):
297-
txt_lines = [
298-
"S1 2 91",
299-
"S1 3 92,open",
300-
"2 7 93",
301-
"3 5 94",
302-
"3 6 transformer,95",
303-
"5 7 96",
304-
"7 8 97",
305-
"8 9 98",
306-
]
307-
308-
grid = Grid.from_txt(txt_lines)
316+
grid = Grid.from_txt(
317+
"S1 2 91", "S1 3 92,open", "2 7 93", "3 5 94", "3 6 transformer,95", "5 7 96", "7 8 97", "8 9 98"
318+
)
309319
assert 8 == grid.node.size
310320
assert 1 == grid.branches.filter(to_status=0).size
311321
assert 1 == grid.transformer.size
312322
np.testing.assert_array_equal([95, 91, 92, 93, 94, 96, 97, 98], grid.branches.id)
313323

314324
def test_from_txt_with_conflicting_ids(self):
315-
txt_lines = [
316-
"S1 2",
317-
"1 3",
318-
]
325+
with pytest.raises(ValueError):
326+
Grid.from_txt("S1 2", "1 3")
319327

328+
def test_from_txt_with_invalid_line(self):
320329
with pytest.raises(ValueError):
321-
Grid.from_txt(txt_lines)
330+
Grid.from_txt("S1 2 arg3 arg4")
322331

323332
def test_from_txt_with_unordered_node_ids(self):
324-
txt_lines = [
325-
"S1 2",
326-
"S1 10",
327-
"10 11",
328-
"2 5",
329-
"5 6",
330-
"3 4",
331-
"3 7",
332-
]
333-
grid = Grid.from_txt(txt_lines)
333+
grid = Grid.from_txt("S1 2", "S1 10", "10 11", "2 5", "5 6", "3 4", "3 7")
334334
assert 9 == grid.node.size
335335

336336
def test_from_txt_with_unordered_branch_ids(self):
337-
txt_lines = [
338-
"5 6 16",
339-
"3 4 17",
340-
"3 7 18",
341-
"S1 2 12",
342-
"S1 10 13",
343-
"10 11 14",
344-
"2 5 15",
345-
]
346-
grid = Grid.from_txt(txt_lines)
337+
grid = Grid.from_txt("5 6 16", "3 4 17", "3 7 18", "S1 2 12", "S1 10 13", "10 11 14", "2 5 15")
347338
assert 9 == grid.node.size
348339

349340
def test_from_txt_file(self, tmp_path):

0 commit comments

Comments
 (0)