Skip to content

Commit 1130c6a

Browse files
authored
Ensure root component is as close as possible to the grid component (#1216)
The `find_first_descendant_component` accepts a `root_category` and finds a component matching that category to begin traversing from. This PR ensures that the matching root component that's found is as close as possible to the GRID component.
2 parents de4e062 + da0372b commit 1130c6a

File tree

5 files changed

+15
-48
lines changed

5 files changed

+15
-48
lines changed

RELEASE_NOTES.md

+2
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,5 @@
5555
| EV Chargers | Maximum power (aka max consumption power) |
5656

5757
- PV Pool instances can now be created in sites without any PV. This allows for writing generic code that works for all locations, that depends on the PV power formula, for example.
58+
59+
- The `find_first_descendant_component` method in the component graph was allowing non-root components to be used as the root component during traversal. This was leading to confusing behaviour when the root component couldn't be identified deterministically. For example, if the root category was specified as a meter, it could start traversing from a different meter each time. It is no-longer possible to specify a root category anymore and it always traverses from the `GRID` component.

src/frequenz/sdk/microgrid/component_graph.py

+13-21
Original file line numberDiff line numberDiff line change
@@ -311,30 +311,24 @@ def dfs(
311311
def find_first_descendant_component(
312312
self,
313313
*,
314-
root_category: ComponentCategory,
315314
descendant_categories: Iterable[ComponentCategory],
316315
) -> Component:
317316
"""Find the first descendant component given root and descendant categories.
318317
319-
This method searches for the root component within the provided root
320-
category. If multiple components share the same root category, the
321-
first found one is considered as the root component.
322-
323-
Subsequently, it looks for the first descendant component from the root
318+
This method looks for the first descendant component from the GRID
324319
component, considering only the immediate descendants.
325320
326321
The priority of the component to search for is determined by the order
327322
of the descendant categories, with the first category having the
328323
highest priority.
329324
330325
Args:
331-
root_category: The class of the root component to search for.
332326
descendant_categories: The descendant classes to search for the first
333327
descendant component in.
334328
335329
Returns:
336330
The first descendant component found in the component graph,
337-
considering the specified `root` and `descendants` categories.
331+
considering the specified `descendants` categories.
338332
"""
339333

340334

@@ -824,42 +818,40 @@ def dfs(
824818
def find_first_descendant_component(
825819
self,
826820
*,
827-
root_category: ComponentCategory,
828821
descendant_categories: Iterable[ComponentCategory],
829822
) -> Component:
830823
"""Find the first descendant component given root and descendant categories.
831824
832-
This method searches for the root component within the provided root
833-
category. If multiple components share the same root category, the
834-
first found one is considered as the root component.
835-
836-
Subsequently, it looks for the first descendant component from the root
825+
This method looks for the first descendant component from the GRID
837826
component, considering only the immediate descendants.
838827
839828
The priority of the component to search for is determined by the order
840829
of the descendant categories, with the first category having the
841830
highest priority.
842831
843832
Args:
844-
root_category: The class of the root component to search for.
845833
descendant_categories: The descendant classes to search for the first
846834
descendant component in.
847835
848836
Returns:
849837
The first descendant component found in the component graph,
850-
considering the specified `root` and `descendants` categories.
838+
considering the specified `descendants` categories.
851839
852840
Raises:
853-
ValueError: When the root component is not found in the component
854-
graph or when no component is found in the given categories.
841+
InvalidGraphError: When no GRID component is found in the graph.
842+
ValueError: When no component is found in the given categories.
855843
"""
856844
root_component = next(
857-
(comp for comp in self.components(component_categories={root_category})),
845+
(
846+
comp
847+
for comp in self.components(
848+
component_categories={ComponentCategory.GRID}
849+
)
850+
),
858851
None,
859852
)
860-
861853
if root_component is None:
862-
raise ValueError(f"Root component not found for {root_category.name}")
854+
raise InvalidGraphError("No GRID component found in the component graph!")
863855

864856
# Sort by component ID to ensure consistent results.
865857
successors = sorted(

src/frequenz/sdk/timeseries/_grid_frequency.py

-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ def __init__(
5353
if not source:
5454
component_graph = connection_manager.get().component_graph
5555
source = component_graph.find_first_descendant_component(
56-
root_category=ComponentCategory.GRID,
5756
descendant_categories=(
5857
ComponentCategory.METER,
5958
ComponentCategory.INVERTER,

src/frequenz/sdk/timeseries/_voltage_streamer.py

-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,6 @@ def __init__(
8181
if not source_component:
8282
component_graph = connection_manager.get().component_graph
8383
source_component = component_graph.find_first_descendant_component(
84-
root_category=ComponentCategory.GRID,
8584
descendant_categories=[
8685
ComponentCategory.METER,
8786
ComponentCategory.INVERTER,

tests/microgrid/test_graph.py

-25
Original file line numberDiff line numberDiff line change
@@ -590,45 +590,21 @@ def test_find_first_descendant_component(self) -> None:
590590

591591
# Find the first descendant component of the grid endpoint.
592592
result = graph.find_first_descendant_component(
593-
root_category=ComponentCategory.GRID,
594593
descendant_categories=(ComponentCategory.METER,),
595594
)
596595
assert result == Component(2, ComponentCategory.METER)
597596

598-
# Find the first descendant component of the first meter found.
599-
result = graph.find_first_descendant_component(
600-
root_category=ComponentCategory.METER,
601-
descendant_categories=(ComponentCategory.INVERTER,),
602-
)
603-
assert result == Component(4, ComponentCategory.INVERTER, InverterType.BATTERY)
604-
605597
# Find the first descendant component of the grid,
606598
# considering meter or inverter categories.
607599
result = graph.find_first_descendant_component(
608-
root_category=ComponentCategory.GRID,
609600
descendant_categories=(ComponentCategory.METER, ComponentCategory.INVERTER),
610601
)
611602
assert result == Component(2, ComponentCategory.METER)
612603

613-
# Find the first descendant component of the first meter with nested meters.
614-
result = graph.find_first_descendant_component(
615-
root_category=ComponentCategory.METER,
616-
descendant_categories=(ComponentCategory.METER,),
617-
)
618-
assert result == Component(3, ComponentCategory.METER)
619-
620-
# Verify behavior when root component is not found.
621-
with pytest.raises(ValueError):
622-
graph.find_first_descendant_component(
623-
root_category=ComponentCategory.CHP,
624-
descendant_categories=(ComponentCategory.INVERTER,),
625-
)
626-
627604
# Verify behavior when component is not found in immediate descendant
628605
# categories for the first meter.
629606
with pytest.raises(ValueError):
630607
graph.find_first_descendant_component(
631-
root_category=ComponentCategory.METER,
632608
descendant_categories=(
633609
ComponentCategory.EV_CHARGER,
634610
ComponentCategory.BATTERY,
@@ -639,7 +615,6 @@ def test_find_first_descendant_component(self) -> None:
639615
# categories from the grid component as root.
640616
with pytest.raises(ValueError):
641617
graph.find_first_descendant_component(
642-
root_category=ComponentCategory.GRID,
643618
descendant_categories=(ComponentCategory.INVERTER,),
644619
)
645620

0 commit comments

Comments
 (0)