Module 3: Structure Modelling
How to describe what a system is made of — and how every structural keyword maps to a precise KerML construct underneath.
KerML → SysML: Structure Modelling Concepts
Every structural keyword in this module is a SysML L2 application of a KerML L1 construct. The table below shows the mapping for every concept covered here.
| SysML v2 concept (L2) | Underlying KerML construct (L1) | What SysML adds |
|---|---|---|
part def | Class (a Classifier) | Keyword implies composite containment as default; instances have identity and persist over time |
part (usage) | Composite Feature typed by a Class | isComposite = true; default multiplicity [1..1] |
ref part | Non-composite Feature typed by a Class | isComposite = false; the referenced object exists independently |
attribute def | DataType (a Classifier) | Instances have no identity — pure values. Can carry invariants and unit constraints |
attribute (usage) | Feature typed by a DataType | Value-typed child; same lifetime as parent |
enum def | DataType with EnumerationLiteral members | Closed set of named values; each literal has no identity |
item def | Class (same as part def at KerML level) | Convention only: signals a flowing participant, not a persistent structure |
port def | Directed Feature on a Type boundary | Marks interaction point; ~T conjugation reverses all directions |
connect | Connector (a Feature) | Links Feature instances; SysML provides named and anonymous forms |
interface def | Association with end Features | Adds typed flow declarations to a named connection template |
specializes (on part def) | Subclassification Relationship | Subtype inherits all features of supertype |
subsets (on part) | Subsetting Relationship | Values of sub-feature are drawn from the pool of parent feature |
redefines | Redefinition Relationship | Replaces inherited feature, narrowing type, multiplicity, or value |
The key distinction between part def and item def is convention, not KerML semantics. Both map to Class. SysML uses item def to signal that something flows (a signal, fluid, data packet) rather than persisting as structure. Tooling may use this convention to validate flow connections.
Parts & Decomposition
KerML origin: part def → Class; part → composite Feature; ref part → non-composite Feature
In SysML v2, structural modelling answers: what is this system made of, and how do those pieces relate? The primary vehicle is the part.
Think of a car. It has an engine, four wheels, a chassis, a fuel tank. The engine itself has cylinders, a crankshaft, a fuel injector. SysML v2 lets you describe exactly this kind of nested "made of" structure down to whatever level of detail you need.
Definitions vs usages
part def— declares a reusable type. KerML: aClasswhose instances persist over time and have identity.part— a usage of that type in context. KerML: a compositeFeaturetyped by thatClass,isComposite = true.
1 part def Engine { 2 attribute displacement : Real; 3 attribute cylinders : Integer; 4 } 5 6 part def Car { 7 part engine : Engine; // one composite Engine child 8 part wheel : Wheel[4]; // four composite Wheel children 9 part chassis : Chassis; 10 }
Composite vs reference: part vs ref part
Both are Features typed by a Class. The difference is the KerML isComposite flag:
part—isComposite = true. Parent exclusively owns child. Created and destroyed with parent.ref part—isComposite = false. Child exists independently; parent holds a reference. The samePersoncan drive multiple cars; destroying the car does not destroy the driver.attribute— a child typed byDataType(Section 2). No identity, no separate existence — just data inside the instance.
Composite is like the engine welded into the car: it lives and dies with the car. Reference is like the driver: they step out, they still exist.
1 part def Fleet { 2 part car : Car[1..*]; // isComposite=true: owned by fleet 3 ref part driver : Person; // isComposite=false: exists independently 4 attribute name : String; // DataType child: gone when Fleet is deleted 5 } 6 7 // Deleting the Fleet also deletes its Cars (composite ownership) 8 // and its name value (DataType, no separate object). Drivers are unaffected.
| Keyword | KerML property | Lifetime | Use when… |
|---|---|---|---|
part | isComposite = true | Same as parent | Child is physically part of parent |
ref part | isComposite = false | Independent of parent | Child is shared or exists on its own |
attribute | Typed by DataType | Same as parent | Storing a number, string, or quantity |
Every part usage defaults to multiplicity [1..1] — exactly one. Forgetting to write [0..1] for optional parts or [1..*] for collections is the most common structural modelling mistake.
Attributes & Value Types
KerML origin: attribute def → DataType; attribute → Feature typed by DataType; enum def → DataType with EnumerationLiterals
Attributes store data values that characterise a part. In KerML, they are Features typed by DataType — a Classifier whose instances carry no identity. A value like 3.7 or "Generic" is not an object; it is a quantity or label belonging to the owning instance.
Scalar value types — the primitive DataTypes
SysML v2 provides four primitive scalar types in the ScalarValues standard library:
| Type | What it represents | Typical uses | Example literal |
|---|---|---|---|
Real | 64-bit IEEE 754 float. Full range of physical quantities when no unit system is attached. | Dimensionless ratios, raw sensor readings, coefficients | 3.14, -0.5, 1.0e6 |
Integer | Whole number, exact arithmetic. | Counts, indices, protocol versions, CAN IDs | 4, -1, 255 |
String | Sequence of Unicode characters. | Labels, identifiers, descriptions, vendor names | "GPS", "BoschMotorsport" |
Boolean | Exactly true or false. | Flags, enable/disable switches, validity indicators | true, false |
1 private import ScalarValues::*; 2 3 part def Sensor { 4 attribute sampleRate : Real; // dimensionless; prefer ISQ::FrequencyValue in practice 5 attribute label : String; // human-readable name 6 attribute channelCount : Integer; // must be a whole number 7 attribute enabled : Boolean; // is this sensor active? 8 }
Use Real only for dimensionless quantities. For physical measurements that carry a unit, use ISQ types — see below. This lets tools verify unit compatibility at model validation time.
ISQ quantity types — physics-aware value types
The ISQ (International System of Quantities) library defines typed values for every physical quantity. Each ISQ type is a DataType carrying dimensional metadata: the model knows that MassValue is not the same as LengthValue, and that 3.7[V] cannot be assigned to a MassValue attribute. Values are written as number[unit]:
1 private import ISQ::*; // quantity types: MassValue, LengthValue, etc. 2 private import SI::*; // SI units: kg, m, s, A, K, and derived units
| ISQ type | Physical quantity | SI unit | Example assignment |
|---|---|---|---|
MassValue | Mass | kg | = 1.2[kg] |
LengthValue | Length / distance | m | = 0.35[m] |
TimeValue | Duration | s | = 2.5[s] |
ElectricPotentialValue | Voltage | V | = 3.7[V] |
ElectricCurrentValue | Current | A | = 15[A] |
EnergyValue | Energy / work | J (or W·h) | = 44.4[W*h] |
PowerValue | Power | W | = 250[W] |
FrequencyValue | Frequency | Hz | = 400[Hz] |
PressureValue | Pressure | Pa | = 101325[Pa] |
TemperatureValue | Thermodynamic temperature | K (or °C) | = 298[K] |
SpeedValue | Speed / velocity | m/s | = 340[m/s] |
AccelerationValue | Acceleration | m/s² | = 9.81[m/s^2] |
ForceValue | Force | N | = 500[N] |
VolumeValue | Volume | m³ (or L) | = 0.05[L] |
1 package DroneModel { 2 private import ISQ::*; 3 private import SI::*; 4 5 part def Battery { 6 attribute nominalVoltage : ISQ::ElectricPotentialValue = 22.2[V]; 7 attribute capacity : ISQ::EnergyValue; 8 attribute peakCurrent : ISQ::ElectricCurrentValue; 9 attribute mass : ISQ::MassValue; 10 } 11 12 part def Motor { 13 attribute kv : Real; // dimensionless: RPM/V 14 attribute maxCurrent : ISQ::ElectricCurrentValue; 15 attribute stallTorque : ISQ::TorqueValue; 16 } 17 }
Do not mix ISQ types and raw Real for the same physical quantity. If Battery.voltage is ISQ::ElectricPotentialValue, any constraint or binding must also use a compatible-unit value. Assigning a bare Real to an ISQ attribute is a type error.
Defining your own value types with attribute def
attribute def creates a new named DataType — a reusable structured value type. Use it when a concept has multiple fields or appears across many parts:
1 attribute def GeoCoordinate { 2 private import ISQ::*; 3 attribute latitude : Real; // degrees, -90 to +90 4 attribute longitude : Real; // degrees, -180 to +180 5 attribute altitude : ISQ::LengthValue; 6 } 7 8 attribute def BatteryHealth { 9 attribute stateOfCharge : Real; // 0.0 to 1.0 10 attribute cycleCount : Integer; 11 attribute temperature : ISQ::TemperatureValue; 12 attribute internalResistance : ISQ::ElectricalResistanceValue; 13 } 14 15 part def UAV { 16 attribute homePosition : GeoCoordinate; 17 attribute batteryState : BatteryHealth; 18 }
attribute def is a DataType in KerML — not a Class. Instances have no identity: two GeoCoordinate values with the same fields are considered equal. There is no concept of "this specific coordinate object." If you need identity (so two objects with the same values can still be distinguished), use part def instead.
Enumerations with enum def
When an attribute can only take one of a fixed set of named values, use an enumeration. In KerML, each literal is an EnumerationLiteral; the enumeration itself is a DataType:
1 enum def FlightMode { 2 enum MANUAL; 3 enum STABILISED; 4 enum AUTO; 5 enum RETURN_TO_HOME; 6 enum EMERGENCY; 7 } 8 9 enum def HealthStatus { 10 enum OK; 11 enum DEGRADED; 12 enum FAILED; 13 } 14 15 part def FlightController { 16 attribute currentMode : FlightMode := FlightMode::STABILISED; 17 attribute systemHealth : HealthStatus := HealthStatus::OK; 18 }
Use enum def when the set of valid values is closed and known at model time (flight modes, operational states). Use Real or Integer for measurements with no fixed vocabulary. Avoid String for enumerated concepts — it has no closed-world semantics and cannot be validated by tooling.
Four kinds of value assignment
| Syntax | Name | Question it answers | Can it be overridden? |
|---|---|---|---|
= expr | Bound value | What is this value always? | No — permanent invariant |
:= expr | Initial value | What does this value start at? | Yes — can change during instance lifetime |
default = expr | Default bound | What is the value unless a specialisation changes it? | Yes — subtype can redefine |
default := expr | Default initial | What does it start at unless specialisation changes it? | Yes — subtype can redefine |
A car dashboard. A bound value is like the number of wheels being permanently 4 — a structural invariant. An initial value is like the fuel level starting at full — it changes during use. A default bound is like a standard tyre pressure all models share unless a sports variant overrides it. A default initial is like the factory seat position — each driver can change it.
1 part def Motor { 2 attribute cylinders : Integer = 4; // bound: always 4, never changes 3 attribute currentRPM : Real := 0; // initial: starts at 0, changes at runtime 4 attribute idleRPM : Real default = 800; // default bound: 800 unless subtype overrides 5 attribute vendor : String default := "Generic"; // default initial: overridable start 6 } 7 8 part def RacingMotor specializes Motor { 9 attribute idleRPM : Real = 1200 redefines idleRPM; // overrides the default bound 10 }
= is a permanent invariant, not a starting point. Writing attribute rpm : Real = 0 means RPM can never deviate from zero during the entire instance lifetime. Use := when you mean starts at this value but will change during operation.
Items — Things That Flow
KerML origin: item def → Class (same as part def at KerML level; distinction is SysML convention)
An item is something that flows between parts — signals, data packets, fluids, physical materials, energy. Like part def, item def maps to a KerML Class: instances have identity and can carry attributes. The distinction from part def is purely convention — items are flow participants, not persistent structural components.
Parts are the pipes and pumps; items are what flows through them. A fuel line (part) carries fuel (item). A network cable (part) carries data packets (item). An axle (part) transfers torque (item).
Defining items
1 item def ElectricalPower { 2 private import ISQ::*; 3 attribute voltage : ISQ::ElectricPotentialValue; 4 attribute current : ISQ::ElectricCurrentValue; 5 } 6 7 item def DataPacket { 8 attribute payload : String; 9 attribute checksum : Integer; 10 attribute priority : Integer default = 0; 11 } 12 13 item def Fuel { 14 private import ISQ::*; 15 attribute volumeFlow : ISQ::VolumeFlowRateValue; 16 }
Items in ports and flows
1 port def PowerPort { 2 out item power : ElectricalPower; 3 } 4 5 part def DriveSystem { 6 part battery : Battery { port powerOut : PowerPort; } 7 part motor : Motor { port powerIn : ~PowerPort; } 8 9 flow of ElectricalPower 10 from battery.powerOut.power 11 to motor.powerIn.power; 12 }
| Concept | Keyword | KerML origin | Purpose |
|---|---|---|---|
| Item definition | item def | Class | Template describing what a flowing thing is |
| Item usage | item | Composite Feature typed by Class | A specific flowing participant in context |
| Port feature | out item / in item | Directed Feature | Declares what a port sends or receives and in which direction |
| Flow statement | flow of X from … to … | ItemFlow | Explicitly connects two item endpoints |
Ports & Connections
KerML origin: port def → directed Feature on Type boundary; connect → Connector; interface def → Association with end Features
Ports are defined interaction points of a part. In KerML, a port is a directed Feature owned by a Type, sitting on its boundary. Connections link port Features together via KerML Connector instances.
Port definitions
1 port def ElectricalPort { 2 out item power : ElectricalPower; // sends power 3 in item control : ControlSignal; // receives control 4 } 5 6 // Conjugate ~ElectricalPort reverses all directions: 7 // in item power, out item control
Ports on parts
1 part def PowerSupply { port output : ElectricalPort; } // supplier side 2 part def Controller { port input : ~ElectricalPort; } // consumer side (conjugated)
Connecting ports
The connect statement creates a KerML Connector between two Feature instances:
1 part def System { 2 part psu : PowerSupply; 3 part controller : Controller; 4 5 connect psu.output to controller.input; // anonymous Connector 6 7 connection powerLink : ElectricalInterface // named, typed Connector 8 connect psu.output to controller.input; 9 }
Interface definitions
interface def maps to a KerML Association — a Classifier whose instances are connections, with named end Features identifying each participant:
1 interface def PowerInterface { 2 end port supplier : ElectricalPort; 3 end port consumer : ~ElectricalPort; 4 flow of ElectricalPower from supplier.power to consumer.power; 5 } 6 7 part def System { 8 part psu : PowerSupply; part controller : Controller; 9 interface : PowerInterface 10 connect supplier ::> psu.output 11 to consumer ::> controller.input; 12 }
Use bare connect for simple point-to-point wiring. Use interface def when you want to document the protocol, include flow declarations, or reuse the pattern across multiple subsystems.
Decomposition Patterns
KerML origin: Direct → composite Features; Subsetting → Subsetting Relationship; Specialisation → Subclassification; Redefinition → Redefinition
Four standard patterns for breaking a system into parts, each mapping to a different set of KerML relationships.
Pattern 1 — Direct decomposition
A definition directly lists its sub-parts as composite Features:
1 part def AircraftSystem { 2 part airframe : Airframe; 3 part propulsion : PropulsionSystem; 4 part avionics : Avionics; 5 part fuelSystem : FuelSystem; 6 }
Pattern 2 — Abstract pool with subsetting
Declare an abstract pool feature, then introduce named individuals using subsets (KerML Subsetting Relationship). The abstract keyword forces the pool to be fully covered:
1 part def PropulsionSystem { 2 abstract part engine : Engine[1..4]; // pool - must be fully named 3 part leftEngine : TurboFan subsets engine; // KerML Subsetting Relationship 4 part rightEngine : TurboFan subsets engine; 5 }
Pattern 3 — Specialisation with redefinition
Specialise using specializes (KerML Subclassification), then narrow inherited features using redefines (KerML Redefinition):
1 part def Vehicle { 2 part powertrain : Powertrain; 3 attribute range : ISQ::LengthValue; 4 } 5 6 part def ElectricVehicle specializes Vehicle { 7 part powertrain : ElectricPowertrain redefines powertrain; // narrows type 8 attribute range : ISQ::LengthValue = 500[km] redefines range; // fixes value 9 } 10 11 part def HybridVehicle specializes Vehicle { 12 part powertrain : HybridPowertrain redefines powertrain; 13 }
Pattern 4 — Multiplicity-driven decomposition
1 part def SolarArray { 2 part panel : SolarPanel[1..*]; // at least one panel 3 part string : PanelString[2..8]; // 2 to 8 strings 4 part inverter : Inverter; // exactly one inverter 5 }
| Pattern | KerML mechanism | When to use | Key syntax |
|---|---|---|---|
| Direct | Composite Features | Simple flat structure | part x : T; |
| Abstract pool + subsets | Subsetting Relationship | Named individuals from a pool | abstract part pool : T[n];part x : T subsets pool; |
| Specialise + redefine | Subclassification + Redefinition | Generic-to-specific refinement | part def Sub specializes Super { part x : NarrowT redefines x;} |
| Multiplicity-driven | KerML Multiplicity Feature | Variable-count components | part x : T[1..*]; |
Complete Worked Example
A UAV system putting all module concepts together. The devUAV usage at the end applies the :>> shorthand for concise overrides — both shorthand and explicit redefines are valid.
1 package UAVSystem { 2 private import ISQ::*; private import SI::*; private import ScalarValues::*; 3 4 enum def FlightMode { enum MANUAL; enum STABILISED; enum AUTO; enum RTH; } 5 6 // Items (KerML Class, convention: flowing) 7 item def ElectricalPower { 8 attribute voltage : ISQ::ElectricPotentialValue; 9 attribute current : ISQ::ElectricCurrentValue; } 10 item def ControlCommand { 11 attribute channel : Integer; attribute pwmValue : Integer; } 12 13 // Ports (KerML directed Features) 14 port def PowerPort { out item pwr : ElectricalPower; } 15 port def MotorPort { in item cmd : ControlCommand; in item pwr : ElectricalPower; } 16 17 // Parts (KerML Class) 18 part def Battery { 19 attribute capacity : ISQ::EnergyValue; 20 attribute nominalVoltage : ISQ::ElectricPotentialValue = 22.2[V]; 21 attribute health : Real default := 1.0; 22 port powerOut : PowerPort; } 23 24 part def FlightController { 25 attribute loopRate : ISQ::FrequencyValue = 400[Hz]; 26 attribute mode : FlightMode := FlightMode::STABILISED; 27 port powerIn : ~PowerPort; 28 port motorCmd : MotorPort[4]; } 29 30 part def Motor { 31 attribute kv : Real; 32 attribute maxCurrent : ISQ::ElectricCurrentValue; 33 port ctrl : MotorPort; } 34 35 part def MotorAssembly { part motor : Motor; part prop : Propeller; } 36 part def Propeller { 37 attribute diameter : ISQ::LengthValue; attribute pitch : ISQ::LengthValue; } 38 39 // UAV: abstract pool + direct decomposition 40 part def UAV { 41 attribute mass : ISQ::MassValue; 42 attribute maxPayload : ISQ::MassValue; 43 part battery : Battery; part fc : FlightController; 44 abstract part motorAsm : MotorAssembly[4]; 45 part frontLeft : MotorAssembly subsets motorAsm; // explicit subsets 46 part frontRight : MotorAssembly subsets motorAsm; 47 part rearLeft : MotorAssembly subsets motorAsm; 48 part rearRight : MotorAssembly subsets motorAsm; 49 connect battery.powerOut to fc.powerIn; 50 connect fc.motorCmd[1] to frontLeft.motor.ctrl; 51 connect fc.motorCmd[2] to frontRight.motor.ctrl; 52 connect fc.motorCmd[3] to rearLeft.motor.ctrl; 53 connect fc.motorCmd[4] to rearRight.motor.ctrl; } 54 55 // Usage-level override (shorthand :>> used here) 56 part devUAV : UAV { 57 :>> mass = 1.2[kg]; :>> maxPayload = 0.5[kg]; 58 part :>> battery { :>> capacity = 44.4[W*h]; } 59 part :>> fc { :>> loopRate = 500[Hz]; } } 60 }
Demonstrates: enum def, ISQ attributes, item def (Class, flow convention), port def (directed Features), composite decomposition, abstract pool with explicit subsets, connect (Connector), and usage-level override via shorthand.
Module Summary
| SysML v2 concept | KerML origin | Key rule |
|---|---|---|
part def | Class | Instances have identity and persist over time |
part | Composite Feature (isComposite=true) | Created and destroyed with parent; default [1..1] |
ref part | Non-composite Feature (isComposite=false) | Child exists independently; parent holds reference |
attribute def | DataType | No identity — two equal values are indistinguishable |
attribute | Feature typed by DataType | Same lifetime as parent; use ISQ types for physical quantities |
enum def | DataType with EnumerationLiterals | For closed sets of named values |
item def | Class (flow convention) | Same as part def at KerML level; signals a flowing participant |
port def | Directed Feature on Type boundary | ~T conjugation reverses all directions |
connect | Connector | Links Feature instances; use interface def for typed connections |
subsets | Subsetting Relationship | Adds named individual from a pool; original pool remains |
specializes | Subclassification | Subtype inherits all features; use with redefines to narrow |
redefines | Redefinition | Replaces inherited feature; can narrow type, multiplicity, or value |