sysmlpy Tutorial¶
A guide to using sysmlpy — a pure Python library for constructing and parsing SysML v2.0 models.
Installation¶
Quick Start¶
from sysmlpy import loads, Model, Package, Part, Attribute, ureg
# Parse SysML text
model = loads("""
package MyModel {
part def Engine;
part engine1: Engine {
attribute mass = 100 [kg];
}
}
""")
# Or build programmatically
p = Part(name="Stage_1", shortname="'3.1'")
a = Attribute(name="mass")
a.set_value(100 * ureg.kilogram)
p._set_child(a)
print(p.dump())
# → part <'3.1'> Stage_1 { attribute mass = 100 [kilogram]; }
Architecture¶
sysmlpy has three layers:
- Public API (
usage.py) — Python classes you use directly:Part,Item,Action,State, etc. - Grammar Layer (
grammar/classes.py) — ~319 internal classes that mirror the ANTLR parse tree. Used for round-trip parsing. - ANTLR Parser (
antlr/,antlr_visitor.py) — Parses SysML v2 text into an internal dict, then into grammar objects.
SysML v2 to Python Mapping¶
Base Classes¶
| Python Class | Role | Key Methods |
|---|---|---|
Searchable |
Mixin — find(), all(), typed property accessors |
find(name, type, recursive), parts, actions, states, etc. |
Usage |
Base for all usage/definition wrappers | dump(), load_from_grammar(), _set_child(), _set_typed_by(), _set_specializes(), _set_subsets(), _set_redefines() |
Model |
Root container | load(s), dump() |
Package |
Namespace container | load_from_grammar(), dump() |
Transition |
State machine transition (standalone) | load_from_grammar(), source, trigger, guard, target, effect |
Structural Elements¶
| Python Class | SysML Keywords | Def/Usage | Grammar Class | load_from_grammar |
|---|---|---|---|---|
Part |
part, part def |
Both | PartUsage, PartDefinition |
Yes |
Item |
item, item def |
Both | ItemUsage, ItemDefinition |
Yes |
Attribute |
attribute, attribute def |
Both | AttributeUsage, AttributeDefinition |
Yes |
Port |
port, port def |
Both | PortUsage, PortDefinition |
Yes |
Connection |
connection, connection def |
Both | ConnectionUsage, ConnectionDefinition |
Yes (via Package) |
Flow |
flow, flow def |
Both | FlowConnectionUsage, FlowConnectionDefinition |
Yes (via Package) |
FlowDef |
flow def |
Def only | FlowDefinition |
No |
Allocation |
allocation, allocation def |
Both | AllocationUsage, AllocationDefinition |
Yes (via Package) |
Individual |
individual, individual def |
Both | IndividualUsageSimple, IndividualDefinition |
Yes (via Package) |
Behavioral Elements¶
| Python Class | SysML Keywords | Def/Usage | Grammar Class | load_from_grammar |
|---|---|---|---|---|
Action |
action, action def |
Both | ActionUsage, ActionDefinition |
Yes (custom) |
State |
state, state def |
Both | StateUsage, StateDefinition |
Yes (custom) |
Transition |
transition, then, entry |
N/A | TransitionUsage, TargetTransitionUsage |
Yes (standalone) |
Requirements¶
| Python Class | SysML Keywords | Def/Usage | Grammar Class | load_from_grammar |
|---|---|---|---|---|
Requirement |
requirement, requirement def |
Both | RequirementUsage, RequirementDefinition |
Yes (via Package) |
UseCase |
use case, use case def |
Both | UseCaseUsage, UseCaseDefinition |
Yes (via Package) |
Cases¶
| Python Class | SysML Keywords | Def/Usage | Grammar Class | load_from_grammar |
|---|---|---|---|---|
Case |
case, case def |
Both | CaseUsage, CaseDefinition |
Yes (via Package) |
AnalysisCase |
analysis, analysis def |
Both | AnalysisCaseUsage, AnalysisCaseDefinition |
Yes (via Package) |
VerificationCase |
verification, verification case def |
Both | VerificationCaseUsage, VerificationCaseDefinition |
Yes (via Package) |
Constraints & Calculations¶
| Python Class | SysML Keywords | Def/Usage | Grammar Class | load_from_grammar |
|---|---|---|---|---|
Constraint |
constraint, constraint def |
Both | ConstraintUsage, ConstraintDefinition |
Yes (via Package) |
Calculation |
calc, calc def |
Both | CalculationUsage, CalculationDefinition |
Yes (via Package) |
Views & Viewpoints¶
| Python Class | SysML Keywords | Def/Usage | Grammar Class | load_from_grammar |
|---|---|---|---|---|
View |
view, view def |
Both | ViewUsage, ViewDefinition |
Yes (via Package) |
Viewpoint |
viewpoint, viewpoint def |
Both | ViewpointUsage, ViewpointDefinition |
Yes (via Package) |
Concern |
concern, concern def |
Both | ConcernUsage, ConcernDefinition |
Yes (via Package) |
Metadata & Rendering¶
| Python Class | SysML Keywords | Def/Usage | Grammar Class | load_from_grammar |
|---|---|---|---|---|
Metadata |
metadata, metadata def |
Both | MetadataUsage, MetadataDefinition |
Yes (via Package) |
Rendering |
rendering, rendering def |
Both | RenderingUsage, RenderingDefinition |
Yes (via Package) |
Enumeration |
enum def |
Def only | EnumerationDefinition |
Yes (via Package) |
Custom (No Grammar Backing)¶
| Python Class | SysML Keywords | Notes |
|---|---|---|
Interface |
interface, interface def |
Custom Python implementation, grammar wrapper only |
Message |
message |
Custom Python implementation |
Reference |
ref |
Custom Python implementation |
DefaultReference |
in/out/inout ref |
Grammar-backed via DefaultReferenceUsage |
Usage Examples¶
Building Parts Programmatically¶
from sysmlpy import Part, Item, Attribute, ureg
# Create a sensor part with children
sensor = Part(name="sensor")
camera = Part(name="camera")
lens = Item(name="lens")
mass = Attribute(name="mass")
mass.set_value(100 * ureg.kilogram)
camera._set_child(mass)
sensor._set_child(camera)
sensor._set_child(lens)
print(sensor.dump())
# part sensor {
# part camera {
# attribute mass = 100 [kilogram];
# }
# item lens;
# }
Actions with Inputs and Outputs¶
from sysmlpy import Action
# Action definition
a = Action(definition=True, name="Focus")
a.add_input("scene", "Scene")
a.add_output("image", "Image")
print(a.dump())
# → action def Focus { in scene : Scene; out image : Image; }
# Action usage
b = Action(name="TakePicture")
b.add_input("scene")
b.add_output("picture")
print(b.dump())
# → action TakePicture { in scene; out picture; }
References¶
from sysmlpy import Reference, Item
# Simple reference
r = Reference(name="driver")
print(r.dump())
# → ref driver;
# Typed reference
person = Item(name="Person")
r2 = Reference(name="driver")
r2.set_type(person)
print(r2.dump())
# → ref driver : Person;
# Reference redefinition
r3 = Reference(name="payload", redefines=True)
r3.set_type(person)
print(r3.dump())
# → ref :>> payload : Person;
Parsing and Round-Trip¶
from sysmlpy import loads
from sysmlpy.formatting import classtree
text = """package 'Action Example' {
action def Focus { in scene : Scene; out image : Image; }
action TakePicture {
in item scene : Scene;
out item picture : Picture;
action focus : Focus { in scene; out image; }
}
}"""
model = loads(text)
tree = classtree(model)
print(tree.dump())
State Machines¶
from sysmlpy import State
# State definition
s = State(definition=True, name="Running")
print(s.dump())
# → state def Running;
# State with transitions (via grammar)
model = loads("""
package States {
state def Engine {
state off;
state on {
entry start;
do run;
exit stop;
}
transition off to on if key_turned;
}
}
""")
Requirements¶
from sysmlpy import Requirement
r = Requirement(definition=True, name="PowerRequirement")
r.set_doc("The system shall provide sufficient power.")
r.add_constraint("Power output >= 1000W")
print(r.dump())
Working with Units¶
from sysmlpy import Attribute, ureg
a = Attribute(name="thrust")
a.set_value(1000 * ureg.newton)
print(a.get_value()) # 1000 newton
a.set_value(a.get_value() + 199 * ureg.newton)
print(a.dump()) # attribute thrust = 1199.0 [newton];
Convenience Functions (v0.12.0+)¶
These methods are available on Model and Package for navigating and analyzing large models.
find_all¶
Recursively find all matching elements across the full tree:
from sysmlpy import loads
model = loads("""
package Vehicle {
part def Engine;
part engine1: Engine {
attribute mass = 100 [kg];
}
part chassis {
part wheel1;
part wheel2;
}
}
""")
# Find all parts by type string
all_parts = model.find_all('part')
print(f"Found {len(all_parts)} parts: {[p.name for p in all_parts]}")
# → Found 3 parts: ['engine1', 'chassis', 'wheel1', 'wheel2']
# Find by class
from sysmlpy import Part
all_parts = model.find_all(type=Part)
# Find by name
engines = model.find_all(name='engine1')
count¶
Count elements by type across the full tree:
# Count specific type
part_count = model.count('part')
print(f"Parts: {part_count}") # → Parts: 3
# Count all types
counts = model.count()
print(counts)
# → {'part': 3, 'attribute': 1}
traverse¶
Walk the element tree with a callback function:
# Print tree structure with indentation
def print_tree(elem, depth):
name = getattr(elem, 'name', '?')
stype = getattr(elem, 'sysml_type', '')
indent = " " * depth
print(f"{indent}{stype}: {name}")
model.traverse(print_tree)
# → package: Vehicle
# → part: engine1
# → attribute: mass
# → part: chassis
# → part: wheel1
# → part: wheel2
to_dict¶
Export the model as a nested dictionary:
d = model.to_dict()
print(list(d.keys()))
# → ['name', 'children']
import json
print(json.dumps(d, indent=2, default=str))
# {
# "name": "Model",
# "children": [
# {
# "name": "Vehicle",
# "sysml_type": "package",
# "children": [...]
# }
# ]
# }
to_graph¶
Export the model to a NetworkX graph for analysis:
# Requires: pip install sysmlpy[graph]
store = model.to_graph()
# Graph statistics
print(store.stats())
# → {'nodes': 7, 'edges': 6, 'density': 0.286, ...}
# Find connected components
components = store.connected_components()
print(f"Connected components: {len(components)}")
# Find cycles (useful for detecting circular type references)
cycles = store.cycles()
print(f"Cycles: {len(cycles)}")
# Node centrality (which elements have the most connections)
centrality = store.centrality()
top = sorted(centrality.items(), key=lambda x: x[1], reverse=True)[:3]
for eid, score in top:
data = store.get(eid)
print(f" {data['name']}: {score:.3f}")
# Export to GraphML for visualization in Gephi or Cytoscape
store.export_graphml("model.graphml")
path_between¶
Find the path between two elements by name:
# Path from parent to child
path = model.path_between('chassis', 'wheel1')
print(path)
# → ['chassis', 'wheel1']
# Path between siblings (goes through common parent)
path = model.path_between('wheel1', 'wheel2')
print(path)
# → ['wheel1', 'chassis', 'wheel2']
# No path returns None
path = model.path_between('engine1', 'nonexistent')
print(path) # → None
Loading Functions¶
| Function | Description |
|---|---|
loads(text) |
Parse SysML v2 text string into a Model |
load(file) |
Parse SysML v2 file into a Model |
load_grammar(text) |
Parse into grammar dict (internal) |
load_antlr(text) |
Explicit ANTLR4 parsing path |
load_grammar_antlr(text) |
Parse into grammar dict via ANTLR4 |
Conformance¶
100% of 123 OMG XPect conformance tests pass (123/123).
Run the full suite: