diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 61ee6f2ad..f7552ac16 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -2,16 +2,10 @@ ## Summary - - ## Upgrading - - ## New Features - - ## Bug Fixes - +* Fixed component graph validation to allow dangling components. Previously, a strict tree (connected acyclic undirected graph) structure was required, but now components can exist without being connected to anything. diff --git a/src/frequenz/sdk/microgrid/component_graph.py b/src/frequenz/sdk/microgrid/component_graph.py index 1b0be619e..67937e6af 100644 --- a/src/frequenz/sdk/microgrid/component_graph.py +++ b/src/frequenz/sdk/microgrid/component_graph.py @@ -889,7 +889,7 @@ def _validate_graph(self) -> None: InvalidGraphError: If: - There are no components. - There are no connections. - - The graph is not a tree. + - The graph is not a tree (but tree with dangling components is allowed). - Any node lacks its associated component data. """ if self._graph.number_of_nodes() == 0: @@ -899,7 +899,7 @@ def _validate_graph(self) -> None: raise InvalidGraphError("No connections in component graph!") if not nx.is_directed_acyclic_graph(self._graph): - raise InvalidGraphError("Component graph is not a tree!") + raise InvalidGraphError("Component graph has a cycle!") # This check doesn't seem to have much sense, it only search for nodes without # data associated with them. We leave it here for now, but we should consider @@ -918,15 +918,12 @@ def _validate_graph(self) -> None: if sum(1 for _ in self.connections()) <= 0: raise InvalidGraphError("Graph must have a least one connection!") - # should be true as a consequence of the tree property: - # there should be no unconnected components - unconnected = filter( - lambda c: self._graph.degree(c.component_id) == 0, self.components() - ) - if sum(1 for _ in unconnected) != 0: - raise InvalidGraphError( - "Every component must have at least one connection!" - ) + for component in self.components(): + if self._graph.degree(component.component_id) == 0: + _logger.warning( + "Component %s has no connections and will be ignored", + component, + ) def _validate_graph_root(self) -> None: """Check that there is exactly one node without predecessors, of valid type.