Skip to content

Commit eb59367

Browse files
committed
improved hw support
1 parent 7d230f7 commit eb59367

6 files changed

Lines changed: 286 additions & 45 deletions

File tree

docs/reference/api-full.md

Lines changed: 54 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Quick links:
1212
- [CLI Reference](cli.md)
1313
- [DSL Reference](dsl.md)
1414

15-
Generated from source code on: August 12, 2025 at 16:01 UTC
15+
Generated from source code on: August 12, 2025 at 18:57 UTC
1616

1717
Modules auto-discovered: 67
1818

@@ -114,22 +114,46 @@ Example (YAML-like):
114114
- `get(self, name: 'str') -> 'Optional[Component]'` - Retrieves a Component by its name from the library.
115115
- `merge(self, other: 'ComponentsLibrary', override: 'bool' = True) -> 'ComponentsLibrary'` - Merges another ComponentsLibrary into this one. By default (override=True),
116116

117-
### resolve_hw_component(attrs: 'Dict[str, Any]', library: 'ComponentsLibrary') -> 'Tuple[Optional[Component], float]'
117+
### resolve_link_end_components(attrs: 'Dict[str, Any]', library: 'ComponentsLibrary') -> 'tuple[tuple[Optional[Component], float, bool], tuple[Optional[Component], float, bool], bool]'
118118

119-
Resolve node hardware component and multiplier from node attributes.
119+
Resolve per-end hardware components for a link.
120120

121-
Looks up ``attrs['hw_component']`` in the provided ``library``. If present,
122-
also reads an optional ``attrs['hw_count']`` multiplier. The multiplier
123-
defaults to 1 if not provided.
121+
Input format inside ``link.attrs``:
122+
123+
Structured mapping under ``hardware`` key only:
124+
``{"hardware": {"source": {"component": NAME, "count": N},
125+
"target": {"component": NAME, "count": N}}}``
126+
127+
Args:
128+
attrs: Link attributes mapping.
129+
library: Components library for lookups.
130+
131+
Exclusive usage:
132+
133+
- Optional ``exclusive: true`` per end indicates unsharable usage.
134+
135+
For exclusive ends, validation and BOM counting should round-up counts
136+
to integers.
137+
138+
Returns:
139+
((src_comp, src_count, src_exclusive), (dst_comp, dst_count, dst_exclusive), per_end_specified)
140+
where components may be ``None`` if name is absent/unknown. ``per_end_specified``
141+
is True when a structured per-end mapping is present.
142+
143+
### resolve_node_hardware(attrs: 'Dict[str, Any]', library: 'ComponentsLibrary') -> 'Tuple[Optional[Component], float]'
144+
145+
Resolve node hardware from ``attrs['hardware']``.
146+
147+
Expects the mapping: ``{"hardware": {"component": NAME, "count": N}}``.
148+
``count`` defaults to 1 if missing or invalid. If ``component`` is missing
149+
or unknown, returns ``(None, 1.0)``.
124150

125151
Args:
126-
attrs: Node attribute mapping.
152+
attrs: Node attributes mapping.
127153
library: Component library used for lookups.
128154

129155
Returns:
130-
A tuple ``(component, hw_count)`` where ``component`` is ``None`` if
131-
``hw_component`` is absent or unknown, and ``hw_count`` is a positive
132-
float multiplier (defaults to 1.0).
156+
Tuple of (component or None, positive multiplier).
133157

134158
### totals_with_multiplier(comp: 'Component', hw_count: 'float') -> 'Tuple[float, float, float]'
135159

@@ -191,6 +215,9 @@ Provides hierarchical exploration of a Network, computing statistics in two mode
191215
**Methods:**
192216

193217
- `explore_network(network: 'Network', components_library: 'Optional[ComponentsLibrary]' = None) -> 'NetworkExplorer'` - Build a NetworkExplorer, constructing a tree plus 'all' and 'active' stats.
218+
- `get_bom(self, include_disabled: 'bool' = True) -> 'Dict[str, float]'` - Return aggregated hardware BOM for the whole network.
219+
- `get_bom_by_path(self, path: 'str', include_disabled: 'bool' = True) -> 'Dict[str, float]'` - Return the hardware BOM for a specific hierarchy path.
220+
- `get_bom_map(self, include_disabled: 'bool' = True, include_root: 'bool' = True, root_label: 'str' = '') -> 'Dict[str, Dict[str, float]]'` - Return a mapping from hierarchy path to BOM for each subtree.
194221
- `print_tree(self, node: 'Optional[TreeNode]' = None, indent: 'int' = 0, max_depth: 'Optional[int]' = None, skip_leaves: 'bool' = False, detailed: 'bool' = False, include_disabled: 'bool' = True, max_external_lines: 'Optional[int]' = None) -> 'None'` - Print the hierarchy from 'node' down (default: root).
195222

196223
### TreeNode
@@ -214,8 +241,8 @@ Attributes:
214241
- `children` (Dict[str, TreeNode]) = {}
215242
- `subtree_nodes` (Set[str]) = set()
216243
- `active_subtree_nodes` (Set[str]) = set()
217-
- `stats` (TreeStats) = TreeStats(node_count=0, internal_link_count=0, internal_link_capacity=0.0, external_link_count=0, external_link_capacity=0.0, external_link_details={}, total_capex=0.0, total_power=0.0)
218-
- `active_stats` (TreeStats) = TreeStats(node_count=0, internal_link_count=0, internal_link_capacity=0.0, external_link_count=0, external_link_capacity=0.0, external_link_details={}, total_capex=0.0, total_power=0.0)
244+
- `stats` (TreeStats) = TreeStats(node_count=0, internal_link_count=0, internal_link_capacity=0.0, external_link_count=0, external_link_capacity=0.0, external_link_details={}, total_capex=0.0, total_power=0.0, bom={}, active_bom={})
245+
- `active_stats` (TreeStats) = TreeStats(node_count=0, internal_link_count=0, internal_link_capacity=0.0, external_link_count=0, external_link_capacity=0.0, external_link_details={}, total_capex=0.0, total_power=0.0, bom={}, active_bom={})
219246
- `raw_nodes` (List[Node]) = []
220247

221248
**Methods:**
@@ -247,6 +274,8 @@ Attributes:
247274
- `external_link_details` (Dict[str, ExternalLinkBreakdown]) = {}
248275
- `total_capex` (float) = 0.0
249276
- `total_power` (float) = 0.0
277+
- `bom` (Dict[str, float]) = {}
278+
- `active_bom` (Dict[str, float]) = {}
250279

251280
---
252281

@@ -2679,7 +2708,7 @@ availability target).
26792708

26802709
Optionally collects node and/or link hardware entries to provide an inventory
26812710
view of hardware usage. Each entry includes hardware capacity, allocated
2682-
capacity, typical and maximum power, component name, and component count.
2711+
capacity, typical and maximum power, and a normalized hardware mapping.
26832712

26842713
This step does not compute BAC itself; it expects callers to pass the delivered
26852714
bandwidth value explicitly or to point to a prior step result.
@@ -2706,16 +2735,20 @@ Results stored in `scenario.results`:
27062735
- watts_per_gbit: Normalized power (float, inf if denominator <= 0)
27072736
- node_hw_entries: Optional list of node-level hardware dicts with keys:
27082737

2709-
node, hw_component, hw_count, hw_capacity, allocated_capacity,
2738+
node, hardware {component, count}, hw_capacity, allocated_capacity,
27102739
power_watts, power_watts_max
27112740

27122741
- link_hw_entries: Optional list of link-level hardware dicts with keys:
27132742

27142743
link_id, source, target, capacity,
2715-
src_hw_component, src_hw_count,
2716-
dst_hw_component, dst_hw_count,
2744+
hardware {source {component, count, exclusive}, target {component, count, exclusive}},
27172745
hw_capacity, power_watts, power_watts_max
27182746

2747+
- hardware_bom_total: Aggregated hardware BOM for the whole network (Dict[str, float])
2748+
- hardware_bom_by_path: Mapping of hierarchy path -> BOM dict for each subtree
2749+
2750+
(root is stored under the empty string key "")
2751+
27192752
### CostPowerEfficiency
27202753

27212754
Compute $/Gbit and W/Gbit given delivered bandwidth.
@@ -2730,9 +2763,9 @@ Attributes:
27302763
include_disabled: If False, only enabled nodes/links are counted for totals.
27312764
Default ``True`` aggregates regardless of disabled flags.
27322765
collect_node_hw_entries: If True, store per-node hardware entries with
2733-
component, count, capacity, allocated capacity, and power metrics.
2766+
hardware mapping, capacity, allocated capacity, and power metrics.
27342767
collect_link_hw_entries: If True, store per-link hardware entries with
2735-
component, count, capacity, and power metrics.
2768+
per-end hardware mapping, capacity, and power metrics.
27362769

27372770
**Attributes:**
27382771

@@ -3066,6 +3099,9 @@ Ensure adjacency definitions only contain recognized keys.
30663099

30673100
Ensure link_params contain only recognized keys.
30683101

3102+
Link attributes may include "hardware" per-end mapping when set under
3103+
link_params.attrs. This function only validates top-level link_params keys.
3104+
30693105
### check_no_extra_keys(data_dict: 'Dict[str, Any]', allowed: 'set[str]', context: 'str') -> 'None'
30703106

30713107
Raise if ``data_dict`` contains keys outside ``allowed``.

ngraph/components.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,11 @@ def _coerce_positive_float(value: Any, default: float = 1.0) -> float:
365365
def resolve_link_end_components(
366366
attrs: Dict[str, Any],
367367
library: ComponentsLibrary,
368-
) -> tuple[tuple[Optional[Component], float], tuple[Optional[Component], float], bool]:
368+
) -> tuple[
369+
tuple[Optional[Component], float, bool],
370+
tuple[Optional[Component], float, bool],
371+
bool,
372+
]:
369373
"""Resolve per-end hardware components for a link.
370374
371375
Input format inside ``link.attrs``:
@@ -378,17 +382,25 @@ def resolve_link_end_components(
378382
attrs: Link attributes mapping.
379383
library: Components library for lookups.
380384
385+
Exclusive usage:
386+
- Optional ``exclusive: true`` per end indicates unsharable usage.
387+
For exclusive ends, validation and BOM counting should round-up counts
388+
to integers.
389+
381390
Returns:
382-
((src_comp, src_count), (dst_comp, dst_count), per_end_specified)
391+
((src_comp, src_count, src_exclusive), (dst_comp, dst_count, dst_exclusive), per_end_specified)
383392
where components may be ``None`` if name is absent/unknown. ``per_end_specified``
384393
is True when a structured per-end mapping is present.
385394
"""
386395

387-
def _from_mapping(mapping: Dict[str, Any]) -> tuple[Optional[Component], float]:
396+
def _from_mapping(
397+
mapping: Dict[str, Any],
398+
) -> tuple[Optional[Component], float, bool]:
388399
comp_name = mapping.get("component")
389400
count_val = mapping.get("count", 1)
401+
exclusive = bool(mapping.get("exclusive", False))
390402
comp = library.get(str(comp_name)) if comp_name is not None else None
391-
return comp, _coerce_positive_float(count_val, 1.0)
403+
return comp, _coerce_positive_float(count_val, 1.0), exclusive
392404

393405
# 1) Structured under "hardware": {source: {...}, target: {...}}
394406
hw_struct = attrs.get("hardware")
@@ -398,4 +410,4 @@ def _from_mapping(mapping: Dict[str, Any]) -> tuple[Optional[Component], float]:
398410
return _from_mapping(src_map), _from_mapping(dst_map), True
399411

400412
# No legacy or flattened formats supported.
401-
return (None, 1.0), (None, 1.0), False
413+
return (None, 1.0, False), (None, 1.0, False), False

0 commit comments

Comments
 (0)