Advanced Hierarchy Management

Implementation Date: 2025-11-05 Issue: #37 - Advanced hierarchy and sheet management (2.7) Issue: #100 - Hierarchical sheet component references Status: βœ… Complete

Overview

This document covers two aspects of hierarchical schematic management:

  1. Creating Hierarchical Schematics - How to build hierarchical designs with proper component references

  2. Analyzing Hierarchical Schematics - How to analyze existing hierarchical designs


Part 1: Creating Hierarchical Schematics

Component Reference Problem (Issue #100)

When creating hierarchical schematics, components in child sheets must have correct hierarchical instance paths for KiCad to properly assign reference designators. Without proper paths, KiCad shows β€œ?” instead of β€œC1”, β€œR1”, etc.

Solution: set_hierarchy_context()

Use the set_hierarchy_context() method to configure child schematics with proper hierarchical paths.

Quick Start Example

import kicad_sch_api as ksa

# 1. Create parent schematic
main = ksa.create_schematic("MyProject")
parent_uuid = main.uuid

# 2. Add sheet to parent
power_sheet_uuid = main.sheets.add_sheet(
    name="Power Supply",
    filename="power.kicad_sch",
    position=(50, 50),
    size=(100, 100),
    project_name="MyProject"  # MUST match parent project name
)

# 3. Add sheet pins
main.sheets.add_sheet_pin(power_sheet_uuid, "VIN", "input", "left", 5)
main.sheets.add_sheet_pin(power_sheet_uuid, "+3.3V", "output", "right", 5)

# 4. Create child schematic with hierarchy context
power = ksa.create_schematic("MyProject")  # SAME project name
power.set_hierarchy_context(parent_uuid, power_sheet_uuid)  # KEY STEP!

# 5. Add components - they automatically get correct hierarchical paths
vreg = power.components.add(
    'Regulator_Linear:AMS1117-3.3',
    'U1',
    'AMS1117-3.3',
    position=(127, 101.6)
)
# Component instance path: /parent_uuid/power_sheet_uuid (CORRECT!)

# 6. Save both schematics
main.save("main.kicad_sch")
power.save("power.kicad_sch")

Complete Hierarchical Design Pattern

import kicad_sch_api as ksa
from pathlib import Path

# Project configuration
PROJECT_NAME = "STM32_Board"
output_dir = Path("output")
output_dir.mkdir(exist_ok=True)

# ===== STEP 1: Create Main Schematic =====
main = ksa.create_schematic(PROJECT_NAME)
parent_uuid = main.uuid  # Save for child schematics

# ===== STEP 2: Add Hierarchical Sheets =====

# Power supply sheet
power_sheet_uuid = main.sheets.add_sheet(
    name="Power Supply",
    filename="power.kicad_sch",
    position=(50, 50),
    size=(100, 80),
    project_name=PROJECT_NAME
)
main.sheets.add_sheet_pin(power_sheet_uuid, "VIN", "input", "left", 5)
main.sheets.add_sheet_pin(power_sheet_uuid, "+3.3V", "output", "right", 5)
main.sheets.add_sheet_pin(power_sheet_uuid, "GND", "passive", "bottom", 10)

# MCU sheet
mcu_sheet_uuid = main.sheets.add_sheet(
    name="Microcontroller",
    filename="mcu.kicad_sch",
    position=(175, 50),
    size=(100, 80),
    project_name=PROJECT_NAME
)
main.sheets.add_sheet_pin(mcu_sheet_uuid, "+3.3V", "input", "left", 5)
main.sheets.add_sheet_pin(mcu_sheet_uuid, "GND", "passive", "bottom", 10)

# Add labels for connections
main.add_label("+3.3V", position=(155, 55))
main.add_label("GND", position=(100, 140))

# Save main schematic
main.save(str(output_dir / f"{PROJECT_NAME}.kicad_sch"))

# ===== STEP 3: Create Child Schematics with Hierarchy Context =====

# Power supply child schematic
power = ksa.create_schematic(PROJECT_NAME)
power.set_hierarchy_context(parent_uuid, power_sheet_uuid)  # Set context!

vreg = power.components.add(
    'Regulator_Linear:AMS1117-3.3',
    'U1',
    'AMS1117-3.3',
    position=(127, 101.6)
)
vreg.footprint = 'Package_TO_SOT_SMD:SOT-223-3_TabPin2'

c_in = power.components.add('Device:C', 'C1', '10Β΅F', position=(101.6, 101.6))
c_out = power.components.add('Device:C', 'C2', '10Β΅F', position=(152.4, 101.6))

power.add_label("VIN", position=(76.2, 101.6))
power.add_label("+3.3V", position=(177.8, 101.6))
power.add_label("GND", position=(127, 127))

power.save(str(output_dir / "power.kicad_sch"))

# MCU child schematic
mcu_sch = ksa.create_schematic(PROJECT_NAME)
mcu_sch.set_hierarchy_context(parent_uuid, mcu_sheet_uuid)  # Set context!

mcu = mcu_sch.components.add(
    'MCU_ST_STM32G4:STM32G431R_6-8-B_Tx',
    'U2',
    'STM32G431RBT6',
    position=(127, 101.6)
)
mcu.footprint = 'Package_QFP:LQFP-64_10x10mm_P0.5mm'

# Add decoupling caps
for i, x_pos in enumerate([101.6, 152.4], start=3):
    cap = mcu_sch.components.add('Device:C', f'C{i}', '100nF', position=(x_pos, 76.2))
    cap.footprint = 'Capacitor_SMD:C_0603_1608Metric'

mcu_sch.add_label("+3.3V", position=(76.2, 63.5))
mcu_sch.add_label("GND", position=(127, 152.4))

mcu_sch.save(str(output_dir / "mcu.kicad_sch"))

print(f"βœ“ Hierarchical schematic created in {output_dir}/")
print(f"  - {PROJECT_NAME}.kicad_sch (main)")
print(f"  - power.kicad_sch (child)")
print(f"  - mcu.kicad_sch (child)")

Critical Requirements

  1. Same Project Name: All schematics (parent and children) MUST use the same project name

    main = ksa.create_schematic("MyProject")
    child = ksa.create_schematic("MyProject")  # SAME name!
    
  2. Call set_hierarchy_context() BEFORE Adding Components:

    child = ksa.create_schematic("MyProject")
    child.set_hierarchy_context(parent_uuid, sheet_uuid)  # Do this FIRST
    child.components.add(...)  # Then add components
    
  3. Save Parent UUID Early:

    main = ksa.create_schematic("MyProject")
    parent_uuid = main.uuid  # Save this immediately
    
  4. Pass project_name to add_sheet():

    sheet_uuid = main.sheets.add_sheet(
        name="Child",
        filename="child.kicad_sch",
        position=(50, 50),
        size=(100, 80),
        project_name="MyProject"  # Required!
    )
    

What Happens Internally

When you call set_hierarchy_context():

  1. The schematic stores the parent UUID and sheet UUID

  2. The hierarchical path is computed: /{parent_uuid}/{sheet_uuid}

  3. When components are added, this path is automatically set in their instance data

  4. KiCad uses this path to properly annotate components

Without set_hierarchy_context():

  • Component path: /child_uuid ❌ WRONG

  • KiCad shows: β€œC?” instead of β€œC1”

With set_hierarchy_context():

  • Component path: /parent_uuid/sheet_uuid βœ… CORRECT

  • KiCad shows: β€œC1”, β€œC2”, β€œR1” (proper references)

Real-World Example

For hierarchical schematic examples, refer to the test fixtures in tests/reference_kicad_projects/ which demonstrate:

  • Parent/child schematic relationships

  • Sheet pin connections

  • Hierarchical label usage

  • Multi-level hierarchy structures


Part 2: Analyzing Hierarchical Schematics

The HierarchyManager provides comprehensive tools for analyzing existing hierarchical KiCAD schematic designs.

Features Implemented

1. Sheet Reuse Tracking βœ…οƒ

Track sheets used multiple times in a design (same schematic file instantiated in different locations).

tree = sch.hierarchy.build_hierarchy_tree(sch)
reused = sch.hierarchy.find_reused_sheets()

for filename, instances in reused.items():
    print(f"{filename} used {len(instances)} times")

Key Methods:

  • build_hierarchy_tree() - Build complete hierarchy tree

  • find_reused_sheets() - Find sheets instantiated multiple times

2. Cross-Sheet Signal Tracking βœ…οƒ

Trace signals through hierarchical boundaries.

paths = sch.hierarchy.trace_signal_path("VCC")

for path in paths:
    print(f"Signal: {path.signal_name}")
    print(f"Path: {path.start_path} β†’ {path.end_path}")
    print(f"Sheet crossings: {path.sheet_crossings}")

Key Methods:

  • trace_signal_path(signal_name, start_path) - Trace signal through hierarchy

  • Returns SignalPath objects with routing information

3. Sheet Pin Validation βœ…οƒ

Validate sheet pins match hierarchical labels in child schematics.

tree = sch.hierarchy.build_hierarchy_tree(sch, schematic_path)
connections = sch.hierarchy.validate_sheet_pins()

errors = sch.hierarchy.get_validation_errors()
for error in errors:
    print(f"Pin {error['pin_name']}: {error['error']}")

Validation Checks:

  • Sheet pins have matching hierarchical labels

  • Pin types are compatible (input/output/bidirectional)

  • Pin names match exactly

  • No duplicate pins

Key Methods:

  • validate_sheet_pins() - Validate all sheet pin connections

  • get_validation_errors() - Get detailed validation errors

4. Hierarchy Flattening βœ…οƒ

Flatten hierarchical design into single-level representation.

flattened = sch.hierarchy.flatten_hierarchy(prefix_references=True)

print(f"Components: {len(flattened['components'])}")
print(f"Wires: {len(flattened['wires'])}")

# Hierarchy map shows original locations
for ref, path in flattened['hierarchy_map'].items():
    print(f"{ref} was in {path}")

Options:

  • prefix_references=True - Prefix component references with sheet path

  • prefix_references=False - Keep original references

Key Methods:

  • flatten_hierarchy(prefix_references) - Flatten to single level

5. Hierarchy Visualization βœ…οƒ

Generate text-based hierarchy tree visualization.

viz = sch.hierarchy.visualize_hierarchy(include_stats=True)
print(viz)

# Output:
# β”œβ”€β”€ Root (5 components)
# β”‚   β”œβ”€β”€ PowerSupply [power.kicad_sch] (3 components)
# β”‚   β”œβ”€β”€ MCU [mcu.kicad_sch] (12 components)

Key Methods:

  • visualize_hierarchy(include_stats) - Generate tree visualization

  • get_hierarchy_statistics() - Get comprehensive statistics

6. Hierarchy Statistics βœ…οƒ

Get comprehensive statistics about hierarchical design.

stats = sch.hierarchy.get_hierarchy_statistics()

print(f"Total sheets: {stats['total_sheets']}")
print(f"Max depth: {stats['max_hierarchy_depth']}")
print(f"Reused sheets: {stats['reused_sheets_count']}")
print(f"Components: {stats['total_components']}")
print(f"Wires: {stats['total_wires']}")
print(f"Valid connections: {stats['valid_connections']}")

Architecture

Core Classes

HierarchyManager

Main manager class providing all hierarchy operations.

Key Properties:

  • _hierarchy_tree - Root node of hierarchy tree

  • _sheet_instances - Tracks all sheet instances

  • _loaded_schematics - Cache of loaded schematics

  • _pin_connections - Validated sheet pin connections

HierarchyNode

Represents a node in the hierarchy tree.

Key Properties:

  • path - Hierarchical path (e.g., β€œ/root_uuid/child_uuid/”)

  • name - Sheet name

  • schematic - Loaded schematic object

  • parent - Parent node

  • children - List of child nodes

  • is_root - Whether this is the root node

Methods:

  • get_depth() - Get depth in hierarchy (root = 0)

  • get_full_path() - Get full path from root to this node

  • add_child(node) - Add child node

SheetInstance

Represents a single instance of a hierarchical sheet.

Properties:

  • sheet_uuid - UUID of sheet symbol

  • sheet_name - Name of the sheet

  • filename - Referenced schematic filename

  • path - Hierarchical path

  • parent_path - Parent’s path

  • schematic - Loaded schematic object

  • sheet_pins - List of sheet pins

  • position - Position on parent schematic

SheetPinConnection

Represents validated connection between sheet pin and hierarchical label.

Properties:

  • sheet_path - Path to sheet instance

  • sheet_pin_name - Sheet pin name

  • sheet_pin_type - Pin type (input/output/bidirectional)

  • hierarchical_label_name - Matching label name

  • validated - Whether connection is valid

  • validation_errors - List of validation errors

SignalPath

Represents a signal’s path through hierarchy.

Properties:

  • signal_name - Name of signal

  • start_path - Starting hierarchical path

  • end_path - Ending hierarchical path

  • connections - List of connection points

  • sheet_crossings - Number of sheet boundaries crossed

API Reference

Building Hierarchy

# Build hierarchy tree from root schematic
tree = sch.hierarchy.build_hierarchy_tree(sch, schematic_path)

Sheet Reuse

# Find sheets used multiple times
reused = sch.hierarchy.find_reused_sheets()
# Returns: Dict[filename, List[SheetInstance]]

Validation

# Validate sheet pins
connections = sch.hierarchy.validate_sheet_pins()
# Returns: List[SheetPinConnection]

# Get validation errors
errors = sch.hierarchy.get_validation_errors()
# Returns: List[Dict[str, Any]]

Signal Tracing

# Trace signal through hierarchy
paths = sch.hierarchy.trace_signal_path("SIGNAL_NAME", start_path="/")
# Returns: List[SignalPath]

Flattening

# Flatten hierarchy
flattened = sch.hierarchy.flatten_hierarchy(prefix_references=True)
# Returns: Dict with 'components', 'wires', 'labels', 'hierarchy_map'

Statistics

# Get statistics
stats = sch.hierarchy.get_hierarchy_statistics()
# Returns: Dict with comprehensive statistics

Visualization

# Visualize hierarchy
viz = sch.hierarchy.visualize_hierarchy(include_stats=True)
# Returns: String representation of tree

Usage Patterns

Pattern 1: Validate Hierarchical Design

# Load root schematic
sch = ksa.Schematic.load("project.kicad_sch")

# Build hierarchy tree
tree = sch.hierarchy.build_hierarchy_tree(sch, Path("project.kicad_sch"))

# Validate all sheet pins
connections = sch.hierarchy.validate_sheet_pins()

# Check for errors
errors = sch.hierarchy.get_validation_errors()
if errors:
    for error in errors:
        print(f"ERROR: {error['sheet_path']} - {error['pin_name']}: {error['error']}")

Pattern 2: Analyze Reusable Modules

# Build hierarchy
tree = sch.hierarchy.build_hierarchy_tree(sch, sch_path)

# Find reused sheets
reused = sch.hierarchy.find_reused_sheets()

for filename, instances in reused.items():
    print(f"\nModule: {filename}")
    print(f"Used {len(instances)} times:")
    for inst in instances:
        print(f"  - {inst.sheet_name} at {inst.path}")

Pattern 3: Flatten for Analysis

# Build and flatten
tree = sch.hierarchy.build_hierarchy_tree(sch, sch_path)
flattened = sch.hierarchy.flatten_hierarchy(prefix_references=True)

# Analyze flattened design
print(f"Total components: {len(flattened['components'])}")
print(f"Total connections: {len(flattened['wires'])}")

# Map back to original locations
for comp in flattened['components']:
    print(f"{comp['reference']}: {comp['lib_id']} from {comp['hierarchy_path']}")

Pattern 4: Generate Hierarchy Report

# Build hierarchy
tree = sch.hierarchy.build_hierarchy_tree(sch, sch_path)

# Get statistics
stats = sch.hierarchy.get_hierarchy_statistics()

# Generate report
print("=" * 60)
print("HIERARCHY REPORT")
print("=" * 60)
print(f"Total Sheets: {stats['total_sheets']}")
print(f"Max Depth: {stats['max_hierarchy_depth']}")
print(f"Reused Sheets: {stats['reused_sheets_count']}")
print(f"Total Components: {stats['total_components']}")
print(f"Total Wires: {stats['total_wires']}")
print(f"\nHierarchy Tree:")
print(sch.hierarchy.visualize_hierarchy(include_stats=True))

Testing

Test Coverage

19 comprehensive unit tests covering:

  • βœ… Hierarchy tree building (3 tests)

  • βœ… Sheet reuse detection (2 tests)

  • βœ… Sheet pin validation (3 tests)

  • βœ… Hierarchy flattening (2 tests)

  • βœ… Hierarchy statistics (2 tests)

  • βœ… Hierarchy visualization (2 tests)

  • βœ… Signal tracing (2 tests)

  • βœ… Edge cases (3 tests)

All tests passing: pytest tests/unit/test_hierarchy_manager.py -v

Examples

Complete examples available in: examples/hierarchy_example.py

Run examples:

python examples/hierarchy_example.py

Implementation Notes

Design Decisions

  1. Tree-Based Structure: Hierarchy represented as tree of HierarchyNode objects for efficient traversal and depth calculation.

  2. Lazy Loading: Child schematics loaded on-demand during tree building, reducing memory usage for large projects.

  3. Validation Caching: Sheet pin validation results cached in _pin_connections for repeated access without re-validation.

  4. Path-Based Tracking: Hierarchical paths use KiCAD’s UUID-based format (/root_uuid/child_uuid/) for precise tracking.

  5. Flexible Flattening: Flattening creates data representation only (not real schematic), preserving original hierarchy information in hierarchy_map.

Performance Considerations

  • Tree building: O(n) where n is total number of sheets

  • Sheet reuse detection: O(n) lookup in _sheet_instances dictionary

  • Validation: O(m Γ— p) where m is sheets, p is pins per sheet

  • Flattening: O(n Γ— c) where n is sheets, c is components per sheet

  • Signal tracing: O(n Γ— l) where n is sheets, l is labels per sheet

Integration

With ConnectivityAnalyzer

HierarchyManager complements ConnectivityAnalyzer by providing:

  • Sheet structure and navigation

  • Pin validation before connectivity analysis

  • Flattened view for simplified connectivity tracing

With SheetManager

HierarchyManager extends SheetManager with:

  • Multi-level hierarchy tracking

  • Reuse detection

  • Cross-sheet analysis

  • SheetManager: Basic sheet operations

  • HierarchyManager: Advanced hierarchy analysis

Limitations

  1. File System Dependency: Requires actual schematic files to exist for loading child schematics.

  2. Memory Usage: Loading large hierarchies loads all schematics into memory.

  3. Circular References: Does not detect circular sheet references (A includes B includes A).

  4. Read-Only: Hierarchy analysis is read-only; modifications must be made through SheetManager.

Future Enhancements

Potential improvements (not in scope of #37):

  1. Circular Reference Detection: Detect and report circular sheet dependencies

  2. Hierarchy Editing: Modify hierarchy structure programmatically

  3. Diff/Merge: Compare and merge hierarchical designs

  4. Export Formats: Export hierarchy to other formats (JSON, DOT, etc.)

  5. Incremental Loading: Load hierarchy incrementally for very large designs

  6. Cache Management: Persistent caching of hierarchy analysis results

See Also

  • SheetManager: kicad_sch_api/core/managers/sheet.py - Basic sheet operations

  • ConnectivityAnalyzer: kicad_sch_api/core/connectivity.py - Network connectivity

  • Examples: examples/hierarchy_example.py - Usage examples

  • Tests: tests/unit/test_hierarchy_manager.py - Test coverage

Changelog

v0.4.6 (2025-11-05)

  • βœ… Implemented HierarchyManager class

  • βœ… Added 19 comprehensive unit tests

  • βœ… Integrated with Schematic class via sch.hierarchy property

  • βœ… Created usage examples and documentation

  • βœ… All tests passing


Status: Issue #37 - βœ… Complete

For questions or issues, please refer to the GitHub repository.