Skip to content

Commit 7fc81a4

Browse files
committed
extended components lib
1 parent a5c9435 commit 7fc81a4

3 files changed

Lines changed: 3733 additions & 429 deletions

File tree

ngraph/components.py

Lines changed: 130 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -9,83 +9,153 @@
99
@dataclass(slots=True)
1010
class Component:
1111
"""
12-
A generic component with a name, cost, power, and optional children.
13-
14-
This can represent items such as chassis, line cards, optic modules, etc.
12+
A generic component that can represent chassis, line cards, optics, etc.
13+
Components can have nested children, each with their own cost, power, etc.
1514
1615
Attributes:
1716
name (str): Name of the component (e.g., "SpineChassis" or "400G-LR4").
18-
component_type (str): An optional string label ("chassis", "linecard", "optic", etc.).
19-
cost (float): Base capex cost for the component.
20-
power_watts (float): Power consumption of the component in watts.
17+
component_type (str): A string label (e.g., "chassis", "linecard", "optic").
18+
description (str): A human-readable description of this component.
19+
cost (float): Cost (capex) of a single instance of this component.
20+
power_watts (float): Typical/nominal power usage (watts) for one instance.
21+
power_watts_max (float): Maximum/peak power usage (watts) for one instance.
22+
capacity (float): A generic capacity measure (e.g., platform capacity).
2123
ports (int): Number of ports if relevant for this component.
22-
children (Dict[str, Component]): Child components keyed by their names.
24+
count (int): How many identical copies of this component are present.
25+
attrs (Dict[str, Any]): Arbitrary key-value attributes for extra metadata.
26+
children (Dict[str, Component]): Nested child components (e.g., line cards
27+
inside a chassis), keyed by child name.
2328
"""
2429

2530
name: str
2631
component_type: str = "generic"
32+
description: str = ""
2733
cost: float = 0.0
28-
power_watts: float = 0.0
34+
35+
power_watts: float = 0.0 # Typical power usage
36+
power_watts_max: float = 0.0 # Peak power usage
37+
38+
capacity: float = 0.0
2939
ports: int = 0
40+
count: int = 1
41+
42+
attrs: Dict[str, Any] = field(default_factory=dict)
3043
children: Dict[str, Component] = field(default_factory=dict)
3144

3245
def total_cost(self) -> float:
3346
"""
34-
Calculates the total cost including all nested children.
47+
Computes the total (recursive) cost of this component, including children,
48+
multiplied by this component's count.
3549
3650
Returns:
37-
float: Recursive sum of costs for this component and its children.
51+
float: The total cost.
3852
"""
39-
total = self.cost
53+
single_instance_cost = self.cost
4054
for child in self.children.values():
41-
total += child.total_cost()
42-
return total
55+
single_instance_cost += child.total_cost()
56+
return single_instance_cost * self.count
4357

4458
def total_power(self) -> float:
4559
"""
46-
Calculates the total power consumption including all nested children.
60+
Computes the total *typical* (recursive) power usage of this component,
61+
including children, multiplied by this component's count.
62+
63+
Returns:
64+
float: The total typical power in watts.
65+
"""
66+
single_instance_power = self.power_watts
67+
for child in self.children.values():
68+
single_instance_power += child.total_power()
69+
return single_instance_power * self.count
70+
71+
def total_power_max(self) -> float:
72+
"""
73+
Computes the total *peak* (recursive) power usage of this component,
74+
including children, multiplied by this component's count.
75+
76+
Returns:
77+
float: The total maximum (peak) power in watts.
78+
"""
79+
single_instance_power_max = self.power_watts_max
80+
for child in self.children.values():
81+
single_instance_power_max += child.total_power_max()
82+
return single_instance_power_max * self.count
83+
84+
def total_capacity(self) -> float:
85+
"""
86+
Computes the total (recursive) capacity of this component,
87+
including children, multiplied by this component's count.
4788
4889
Returns:
49-
float: Recursive sum of power for this component and its children.
90+
float: The total capacity (dimensionless or user-defined units).
5091
"""
51-
total = self.power_watts
92+
single_instance_capacity = self.capacity
5293
for child in self.children.values():
53-
total += child.total_power()
54-
return total
94+
single_instance_capacity += child.total_capacity()
95+
return single_instance_capacity * self.count
96+
97+
def as_dict(self, include_children: bool = True) -> Dict[str, Any]:
98+
"""
99+
Returns a dictionary containing all properties of this component.
100+
101+
Args:
102+
include_children (bool): If True, recursively includes children.
103+
104+
Returns:
105+
Dict[str, Any]: Dictionary representation of this component.
106+
"""
107+
data = {
108+
"name": self.name,
109+
"component_type": self.component_type,
110+
"description": self.description,
111+
"cost": self.cost,
112+
"power_watts": self.power_watts,
113+
"power_watts_max": self.power_watts_max,
114+
"capacity": self.capacity,
115+
"ports": self.ports,
116+
"count": self.count,
117+
"attrs": dict(self.attrs), # shallow copy
118+
}
119+
if include_children:
120+
data["children"] = {
121+
child_name: child.as_dict(True)
122+
for child_name, child in self.children.items()
123+
}
124+
return data
55125

56126

57127
@dataclass(slots=True)
58128
class ComponentsLibrary:
59129
"""
60130
Holds a collection of named Components. Each entry is a top-level "template"
61-
that can be referenced for cost/power lookups, possibly with nested children.
131+
that can be referenced for cost/power/capacity lookups, possibly with nested children.
62132
63-
Example:
133+
Example (YAML-like):
64134
components:
65135
BigSwitch:
66136
component_type: chassis
67137
cost: 20000
68-
power_watts: 1000
138+
power_watts: 1750
139+
capacity: 25600
69140
children:
70-
LC-48x10G:
141+
PIM16Q-16x200G:
71142
component_type: linecard
72-
cost: 5000
73-
power_watts: 300
74-
ports: 48
75-
400G-LR4:
143+
cost: 1000
144+
power_watts: 10
145+
ports: 16
146+
count: 8
147+
200G-FR4:
76148
component_type: optic
77149
cost: 2000
78-
power_watts: 15
79-
80-
Attributes:
81-
components (Dict[str, Component]): A dictionary of component name -> Component.
150+
power_watts: 6
151+
power_watts_max: 6.5
82152
"""
83153

84154
components: Dict[str, Component] = field(default_factory=dict)
85155

86156
def get(self, name: str) -> Optional[Component]:
87157
"""
88-
Retrieves a Component by its name.
158+
Retrieves a Component by its name from the library.
89159
90160
Args:
91161
name (str): Name of the component.
@@ -104,7 +174,7 @@ def merge(
104174
105175
Args:
106176
other (ComponentsLibrary): Another library to merge into this one.
107-
override (bool): Whether components in `other` override existing ones.
177+
override (bool): If True, components in `other` override existing ones.
108178
109179
Returns:
110180
ComponentsLibrary: This instance, updated in place.
@@ -154,19 +224,45 @@ def _build_component(cls, name: str, definition_data: Dict[str, Any]) -> Compone
154224
comp_type = definition_data.get("component_type", "generic")
155225
cost = float(definition_data.get("cost", 0.0))
156226
power = float(definition_data.get("power_watts", 0.0))
227+
power_max = float(definition_data.get("power_watts_max", 0.0))
228+
capacity = float(definition_data.get("capacity", 0.0))
157229
ports = int(definition_data.get("ports", 0))
230+
count = int(definition_data.get("count", 1))
158231

159-
children_map: Dict[str, Component] = {}
160232
child_definitions = definition_data.get("children", {})
233+
children_map: Dict[str, Component] = {}
161234
for child_name, child_data in child_definitions.items():
162235
children_map[child_name] = cls._build_component(child_name, child_data)
163236

237+
recognized_keys = {
238+
"component_type",
239+
"cost",
240+
"power_watts",
241+
"power_watts_max",
242+
"capacity",
243+
"ports",
244+
"children",
245+
"attrs",
246+
"count",
247+
"description",
248+
}
249+
attrs: Dict[str, Any] = dict(definition_data.get("attrs", {}))
250+
leftover_keys = {
251+
k: v for k, v in definition_data.items() if k not in recognized_keys
252+
}
253+
attrs.update(leftover_keys)
254+
164255
return Component(
165256
name=name,
166257
component_type=comp_type,
258+
description=definition_data.get("description", ""),
167259
cost=cost,
168260
power_watts=power,
261+
power_watts_max=power_max,
262+
capacity=capacity,
169263
ports=ports,
264+
count=count,
265+
attrs=attrs,
170266
children=children_map,
171267
)
172268

@@ -175,7 +271,7 @@ def from_yaml(cls, yaml_str: str) -> ComponentsLibrary:
175271
"""
176272
Constructs a ComponentsLibrary from a YAML string. If the YAML contains
177273
a top-level 'components' key, that key is used; otherwise the entire
178-
top-level of the YAML is treated as component definitions.
274+
top-level is treated as component definitions.
179275
180276
Args:
181277
yaml_str (str): A YAML-formatted string of component definitions.

0 commit comments

Comments
 (0)