Skip to content

Commit f02c7cb

Browse files
committed
Add type-hints for the component graph bindings
Signed-off-by: Sahas Subramanian <[email protected]>
1 parent e032b6b commit f02c7cb

File tree

1 file changed

+261
-0
lines changed

1 file changed

+261
-0
lines changed
Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
# License: MIT
2+
# Copyright © 2025 Frequenz Energy-as-a-Service GmbH
3+
4+
# Type hints for the component graph module.
5+
6+
"""A graph representation of the electrical components in a microgrid."""
7+
8+
from collections import abc
9+
from typing import Generic, Protocol, TypeVar
10+
11+
class InvalidGraphError(Exception):
12+
"""Exception type that will be thrown if graph data is not valid."""
13+
14+
class FormulaGenerationError(Exception):
15+
"""Encountered during formula generation from the component graph."""
16+
17+
class ComponentGraphConfig:
18+
"""Configuration for the component graph."""
19+
20+
def __init__(
21+
self,
22+
allow_component_validation_failures: bool = False,
23+
allow_unconnected_components: bool = False,
24+
allow_unspecified_inverters: bool = False,
25+
disable_fallback_components: bool = False,
26+
) -> None:
27+
"""Initialize this instance.
28+
29+
Args:
30+
allow_component_validation_failures: Whether to allow validation errors on
31+
components. When this is `True`, the graph will be built even if there
32+
are validation errors on the components.
33+
allow_unconnected_components: Whether to allow unconnected components in the
34+
graph, that are not reachable from the root.
35+
allow_unspecified_inverters: Whether to allow untyped inverters in the
36+
graph. When this is `True`, inverters that have
37+
`InverterType::Unspecified` will be assumed to be Battery inverters.
38+
disable_fallback_components: Whether to disable fallback components in
39+
generated formulas. When this is `True`, the formulas will not include
40+
fallback components.
41+
"""
42+
43+
class ComponentIdProtocol(Protocol):
44+
def __int__(self) -> int:
45+
"""Get the integer representation of the Component ID."""
46+
47+
class ComponentProtocol(Protocol):
48+
@property
49+
def id(self) -> ComponentIdProtocol:
50+
"""The Component ID"""
51+
52+
class ConnectionProtocol(Protocol):
53+
@property
54+
def source(self) -> ComponentIdProtocol:
55+
"""The ID of the component at the start of the connection."""
56+
57+
@property
58+
def destination(self) -> ComponentIdProtocol:
59+
"""The ID of the component at the end of the connection."""
60+
61+
ComponentT = TypeVar("ComponentT", bound=ComponentProtocol)
62+
ConnectionT = TypeVar("ConnectionT", bound=ConnectionProtocol)
63+
ComponentIdT = TypeVar("ComponentIdT", bound=ComponentIdProtocol)
64+
65+
class ComponentGraph(Generic[ComponentT, ConnectionT, ComponentIdT]):
66+
"""A graph representation of the electrical components in a microgrid."""
67+
68+
def __init__(
69+
self,
70+
components: abc.Iterable[ComponentT],
71+
connections: abc.Iterable[ConnectionT],
72+
config: ComponentGraphConfig = ComponentGraphConfig(),
73+
) -> None:
74+
"""Initialize this instance.
75+
76+
Args:
77+
components: The list of components to build the graph with.
78+
connections: The list of connections between the components.
79+
config: The configuration for the component graph.
80+
"""
81+
82+
def component(self, component_id: ComponentIdT) -> ComponentT:
83+
"""Fetch the component with the specified `component_id`.
84+
85+
Args:
86+
component_id: The id of the component to look for.
87+
88+
Returns:
89+
The component with the given id.
90+
91+
Raises:
92+
ValueError: if no component exists with the given ID.
93+
"""
94+
95+
def components(
96+
self,
97+
matching_ids: abc.Iterable[ComponentIdT] | None = None,
98+
matching_types: abc.Iterable[type[ComponentT]] | None = None,
99+
) -> set[ComponentT]:
100+
"""Fetch all components in this graph.
101+
102+
Returns:
103+
A set of all components in this graph.
104+
"""
105+
106+
def connections(self) -> abc.Set[ConnectionT]:
107+
"""Fetch all connections in this graph.
108+
109+
Returns:
110+
A set of all connections in this graph.
111+
"""
112+
113+
def predecessors(self, component_id: ComponentIdT) -> abc.Set[ComponentT]:
114+
"""Fetch all predecessors of the specified component ID.
115+
116+
Args:
117+
component_id: ID of the component whose predecessors should be fetched.
118+
119+
Returns:
120+
A set of components that are predecessors of the given component ID.
121+
122+
Raises:
123+
ValueError: if no component exists with the given ID.
124+
"""
125+
126+
def successors(self, component_id: ComponentIdT) -> abc.Set[ComponentT]:
127+
"""Fetch all successors of the specified component ID.
128+
129+
Args:
130+
component_id: ID of the component whose successors should be fetched.
131+
132+
Returns:
133+
A set of components that are successors of the given component ID.
134+
135+
Raises:
136+
ValueError: if no component exists with the given ID.
137+
"""
138+
139+
def is_pv_meter(self, component_id: ComponentIdT) -> bool:
140+
"""Check if the specified component is a PV meter.
141+
142+
Args:
143+
component_id: ID of the component to check.
144+
145+
Returns:
146+
Whether the specified component is a PV meter.
147+
148+
Raises:
149+
ValueError: if no component exists with the given ID.
150+
"""
151+
152+
def is_battery_meter(self, component_id: ComponentIdT) -> bool:
153+
"""Check if the specified component is a battery meter.
154+
155+
Args:
156+
component_id: ID of the component to check.
157+
158+
Returns:
159+
Whether the specified component is a battery meter.
160+
161+
Raises:
162+
ValueError: if no component exists with the given ID.
163+
"""
164+
165+
def is_ev_charger_meter(self, component_id: ComponentIdT) -> bool:
166+
"""Check if the specified component is an EV charger meter.
167+
168+
Args:
169+
component_id: ID of the component to check.
170+
171+
Returns:
172+
Whether the specified component is an EV charger meter.
173+
174+
Raises:
175+
ValueError: if no component exists with the given ID.
176+
"""
177+
178+
def is_chp_meter(self, component_id: ComponentIdT) -> bool:
179+
"""Check if the specified component is a CHP meter.
180+
181+
Args:
182+
component_id: ID of the component to check.
183+
184+
Returns:
185+
Whether the specified component is a CHP meter.
186+
187+
Raises:
188+
ValueError: if no component exists with the given ID.
189+
"""
190+
191+
def consumer_formula(self) -> str:
192+
"""Generate the consumer formula for this component graph.
193+
194+
Returns:
195+
The consumer formula as a string.
196+
"""
197+
198+
def producer_formula(self) -> str:
199+
"""Generate the producer formula for this component graph.
200+
201+
Returns:
202+
The producer formula as a string.
203+
"""
204+
205+
def grid_formula(self) -> str:
206+
"""Generate the grid formula for this component graph.
207+
208+
Returns:
209+
The grid formula as a string.
210+
"""
211+
212+
def pv_formula(self, pv_inverter_ids: abc.Set[ComponentIdT] | None) -> str:
213+
"""Generate the PV formula for this component graph.
214+
215+
Returns:
216+
The PV formula as a string.
217+
"""
218+
219+
def battery_formula(self, battery_ids: abc.Set[ComponentIdT] | None) -> str:
220+
"""Generate the battery formula for this component graph.
221+
222+
Returns:
223+
The battery formula as a string.
224+
"""
225+
226+
def chp_formula(self, chp_ids: abc.Set[ComponentIdT] | None) -> str:
227+
"""Generate the CHP formula for this component graph.
228+
229+
Returns:
230+
The CHP formula as a string.
231+
"""
232+
233+
def ev_charger_formula(self, ev_charger_ids: abc.Set[ComponentIdT] | None) -> str:
234+
"""Generate the EV charger formula for this component graph.
235+
236+
Returns:
237+
The EV charger formula as a string.
238+
"""
239+
240+
def grid_coalesce_formula(self) -> str:
241+
"""Generate the grid coalesce formula for this component graph.
242+
243+
Returns:
244+
The grid coalesced formula as a string.
245+
"""
246+
247+
def battery_coalesce_formula(
248+
self, battery_ids: abc.Set[ComponentIdT] | None
249+
) -> str:
250+
"""Generate the battery coalesce formula for this component graph.
251+
252+
Returns:
253+
The battery coalesced formula as a string.
254+
"""
255+
256+
def pv_coalesce_formula(self, pv_inverter_ids: abc.Set[ComponentIdT] | None) -> str:
257+
"""Generate the PV coalesce formula for this component graph.
258+
259+
Returns:
260+
The PV coalesced formula as a string.
261+
"""

0 commit comments

Comments
 (0)