Multi-Sheet Nodes
This guide demonstrates how to create networks where nodes appear on multiple sheets. This pattern is common for:
- Overview diagrams showing the entire network at a glance
- Detail sheets focusing on specific feeders or areas
- Voltage level views separating HV, MV, and LV sections
Full Example
View the complete code: 10_multi_sheet_nodes.py
Concept
A single electrical node (one GUID) can have multiple visual representations across different sheets. This allows the same network element to appear in:
- A high-level overview
- A detailed feeder diagram
- Both simultaneously
The electrical model remains unified: changes to the node affect all its presentations.
Example: Feeder Network
Create Sheets for Different Views
from pyptp import NetworkMV, configure_logging
from pyptp.elements.color_utils import CL_BLUE, CL_GRAY, CL_GREEN
from pyptp.elements.mv.sheet import SheetMV
configure_logging(level="INFO")
network = NetworkMV()
# Overview sheet shows entire network
overview_sheet = SheetMV(SheetMV.General(name="Overview", color=CL_GRAY))
overview_sheet.register(network)
overview_guid = overview_sheet.general.guid
# Detail sheets for individual feeders
feeder1_sheet = SheetMV(SheetMV.General(name="Feeder 1 Detail", color=CL_BLUE))
feeder1_sheet.register(network)
feeder1_guid = feeder1_sheet.general.guid
feeder2_sheet = SheetMV(SheetMV.General(name="Feeder 2 Detail", color=CL_GREEN))
feeder2_sheet.register(network)
feeder2_guid = feeder2_sheet.general.guidCreate Shared Busbar
The main busbar appears on all three sheets:
from pyptp.elements.mv.node import NodeMV
from pyptp.elements.mv.presentations import NodePresentation
busbar = NodeMV(
NodeMV.General(name="Main_Busbar", unom=10.0),
presentations=[
NodePresentation(sheet=overview_guid, x=200, y=200),
NodePresentation(sheet=feeder1_guid, x=100, y=150),
NodePresentation(sheet=feeder2_guid, x=100, y=150),
],
)
busbar.register(network)Create Sheet-Specific Elements
Some elements only appear on specific sheets:
from pyptp.elements.mv.source import SourceMV
from pyptp.elements.mv.presentations import ElementPresentation
# Source node only on overview
source_node = NodeMV(
NodeMV.General(name="Source", unom=10.0),
presentations=[NodePresentation(sheet=overview_guid, x=200, y=100)],
)
source_node.register(network)
source = SourceMV(
SourceMV.General(node=source_node.general.guid, sk2nom=100.0),
presentations=[ElementPresentation(sheet=overview_guid, x=200, y=50)],
)
source.register(network)Create Feeder Load Nodes
Feeder loads appear on both overview and their respective detail sheets:
# Feeder 1 load node - on overview and feeder1 detail
feeder1_load_node = NodeMV(
NodeMV.General(name="F1_Load", unom=10.0),
presentations=[
NodePresentation(sheet=overview_guid, x=350, y=200),
NodePresentation(sheet=feeder1_guid, x=400, y=150),
],
)
feeder1_load_node.register(network)
# Feeder 2 load node - on overview and feeder2 detail
feeder2_load_node = NodeMV(
NodeMV.General(name="F2_Load", unom=10.0),
presentations=[
NodePresentation(sheet=overview_guid, x=50, y=200),
NodePresentation(sheet=feeder2_guid, x=400, y=150),
],
)
feeder2_load_node.register(network)Create Branches with Multiple Presentations
Branches between multi-sheet nodes need presentations on each common sheet:
from pyptp.elements.mv.link import LinkMV
from pyptp.elements.mv.presentations import BranchPresentation
# Feeder 1 connection - appears on both overview and feeder1 detail
link_f1 = LinkMV(
LinkMV.General(node1=busbar.general.guid, node2=feeder1_load_node.general.guid),
presentations=[
# Overview presentation
BranchPresentation(
sheet=overview_guid,
first_corners=[(200, 200), (275, 200)],
second_corners=[(350, 200)],
),
# Detail presentation
BranchPresentation(
sheet=feeder1_guid,
first_corners=[(100, 150), (250, 150)],
second_corners=[(400, 150)],
),
],
)
link_f1.register(network)Add Loads with Multiple Presentations
from pyptp.elements.mv.load import LoadMV
load1 = LoadMV(
LoadMV.General(node=feeder1_load_node.general.guid, P=30.0, Q=15.0),
presentations=[
ElementPresentation(sheet=overview_guid, x=350, y=250),
ElementPresentation(sheet=feeder1_guid, x=400, y=200),
],
)
load1.register(network)Sheet Visibility Matrix
For the example above:
| Element | Overview | Feeder 1 | Feeder 2 |
|---|---|---|---|
| Main_Busbar | ✓ | ✓ | ✓ |
| Source | ✓ | ||
| F1_Load | ✓ | ✓ | |
| F2_Load | ✓ | ✓ | |
| Link_F1 | ✓ | ✓ | |
| Link_F2 | ✓ | ✓ |
Best Practices
Consistent Positioning
Keep element positions logical across sheets:
# Good: Busbar on left of detail sheets, center of overview
busbar_presentations = [
NodePresentation(sheet=overview_guid, x=200, y=200), # Center
NodePresentation(sheet=detail_guid, x=100, y=150), # Left
]Clear Naming Convention
Use sheet names that indicate their purpose:
sheets = [
("Overview", CL_GRAY),
("Substation_A_Detail", CL_BLUE),
("Feeder_West_Detail", CL_GREEN),
]Branch Presentation per Common Sheet
Always provide a branch presentation for each sheet where both endpoints are visible:
def create_multi_sheet_branch(node1, node2, corner_generator):
"""Create branch with presentations on all common sheets."""
common_sheets = find_common_sheets(node1, node2)
presentations = []
for sheet_guid in common_sheets:
pos1 = get_position_on_sheet(node1, sheet_guid)
pos2 = get_position_on_sheet(node2, sheet_guid)
first, second = corner_generator(pos1, pos2)
presentations.append(
BranchPresentation(sheet=sheet_guid, first_corners=first, second_corners=second)
)
return presentationsComplete Example
"""Nodes can appear on multiple sheets.
This is useful for creating overview diagrams or splitting large networks
across multiple sheets while maintaining connections.
"""
from pyptp import NetworkMV, configure_logging
from pyptp.elements.color_utils import CL_BLUE, CL_GRAY, CL_GREEN
from pyptp.elements.mv.link import LinkMV
from pyptp.elements.mv.load import LoadMV
from pyptp.elements.mv.node import NodeMV
from pyptp.elements.mv.presentations import BranchPresentation, ElementPresentation, NodePresentation
from pyptp.elements.mv.sheet import SheetMV
from pyptp.elements.mv.source import SourceMV
from pyptp.ptp_log import logger
configure_logging(level="INFO")
network = NetworkMV()
# Create sheets for different network sections
overview_sheet = SheetMV(SheetMV.General(name="Overview", color=CL_GRAY))
overview_sheet.register(network)
overview_guid = overview_sheet.general.guid
feeder1_sheet = SheetMV(SheetMV.General(name="Feeder 1 Detail", color=CL_BLUE))
feeder1_sheet.register(network)
feeder1_guid = feeder1_sheet.general.guid
feeder2_sheet = SheetMV(SheetMV.General(name="Feeder 2 Detail", color=CL_GREEN))
feeder2_sheet.register(network)
feeder2_guid = feeder2_sheet.general.guid
# Main busbar appears on all sheets
busbar = NodeMV(
NodeMV.General(name="Main_Busbar", unom=10.0),
presentations=[
NodePresentation(sheet=overview_guid, x=200, y=200),
NodePresentation(sheet=feeder1_guid, x=100, y=150),
NodePresentation(sheet=feeder2_guid, x=100, y=150),
],
)
busbar.register(network)
# Source only on overview
source_node = NodeMV(
NodeMV.General(name="Source", unom=10.0),
presentations=[NodePresentation(sheet=overview_guid, x=200, y=100)],
)
source_node.register(network)
source = SourceMV(
SourceMV.General(node=source_node.general.guid, sk2nom=100.0),
presentations=[ElementPresentation(sheet=overview_guid, x=200, y=50)],
)
source.register(network)
# Feeder nodes on overview and detail sheets
feeder1_load_node = NodeMV(
NodeMV.General(name="F1_Load", unom=10.0),
presentations=[
NodePresentation(sheet=overview_guid, x=350, y=200),
NodePresentation(sheet=feeder1_guid, x=400, y=150),
],
)
feeder1_load_node.register(network)
feeder2_load_node = NodeMV(
NodeMV.General(name="F2_Load", unom=10.0),
presentations=[
NodePresentation(sheet=overview_guid, x=50, y=200),
NodePresentation(sheet=feeder2_guid, x=400, y=150),
],
)
feeder2_load_node.register(network)
# Source connection (overview only)
link_source = LinkMV(
LinkMV.General(node1=source_node.general.guid, node2=busbar.general.guid),
presentations=[
BranchPresentation(
sheet=overview_guid,
first_corners=[(200, 100), (200, 150)],
second_corners=[(200, 200)],
)
],
)
link_source.register(network)
# Feeder 1 connection (overview + detail)
link_f1 = LinkMV(
LinkMV.General(node1=busbar.general.guid, node2=feeder1_load_node.general.guid),
presentations=[
BranchPresentation(
sheet=overview_guid,
first_corners=[(200, 200), (275, 200)],
second_corners=[(350, 200)],
),
BranchPresentation(
sheet=feeder1_guid,
first_corners=[(100, 150), (250, 150)],
second_corners=[(400, 150)],
),
],
)
link_f1.register(network)
# Feeder 2 connection (overview + detail)
link_f2 = LinkMV(
LinkMV.General(node1=busbar.general.guid, node2=feeder2_load_node.general.guid),
presentations=[
BranchPresentation(
sheet=overview_guid,
first_corners=[(200, 200), (125, 200)],
second_corners=[(50, 200)],
),
BranchPresentation(
sheet=feeder2_guid,
first_corners=[(100, 150), (250, 150)],
second_corners=[(400, 150)],
),
],
)
link_f2.register(network)
# Loads with multiple presentations
load1 = LoadMV(
LoadMV.General(node=feeder1_load_node.general.guid, P=30.0, Q=15.0),
presentations=[
ElementPresentation(sheet=overview_guid, x=350, y=250),
ElementPresentation(sheet=feeder1_guid, x=400, y=200),
],
)
load1.register(network)
load2 = LoadMV(
LoadMV.General(node=feeder2_load_node.general.guid, P=40.0, Q=20.0),
presentations=[
ElementPresentation(sheet=overview_guid, x=50, y=250),
ElementPresentation(sheet=feeder2_guid, x=400, y=200),
],
)
load2.register(network)
network.save("multi_sheet.vnf")
logger.info("Multi-sheet network saved")