Module 2: Core Language Concepts
Namespaces, packages, import/visibility, specialisation, multiplicity, and the full feature model — the six core language mechanisms you will use in every SysML v2 model. Each is a SysML L2 realisation of a KerML L1 concept.
KerML → SysML: How Layer 1 becomes Layer 2
Module 1 introduced KerML as the semantic foundation (L1) and SysML v2 as the language layer (L2) built on top. What does that actually mean in practice? Every SysML v2 keyword is a constrained, named application of a KerML concept. The table below shows exactly which KerML construct underlies each SysML feature covered in this module.
| SysML v2 concept (L2) | Underlying KerML construct (L1) | What SysML adds |
|---|---|---|
package | Namespace (non-Type specialisation) | Convention that it holds definitions only; no instance semantics |
part def / part | Class (a Classifier) | Keyword part implies composite ownership and default multiplicity [1..1] |
attribute def / attribute | DataType (a Classifier) | Keyword attribute implies referential, value-typed semantics |
action def / action | Behavior (a Classifier) | Keyword action implies occurrence-over-time semantics |
port def / port | PortDefinition (specialises StructureDefinition) / directed composite Feature typed by a PortDefinition | Keyword port implies boundary interaction point; conjugation (~) reverses directions |
specializes | Subclassification (a Relationship) | Same semantics; SysML just restricts it to definition-level usage |
subsets | Subsetting (a Relationship) | Same semantics; SysML restricts it to usage-level features |
redefines | Redefinition (a Relationship) | Same semantics; SysML restricts it to usage-level features |
import | Import (a Namespace membership) | SysML adds public/private visibility and recursive (::**) variants |
Multiplicity [m..n] | Multiplicity Feature on a typed element | SysML imposes default [1..1] on part/attribute/port; KerML default is [0..*] |
You do not need to write KerML — you write SysML v2. But knowing the KerML origin of each keyword explains why the rules are what they are. When something surprises you (e.g. why default multiplicity differs, why every Type is a Namespace), the answer is always found one layer down in KerML.
With that map in hand, the rest of this module covers each SysML v2 concept in depth, noting where KerML behaviour is inherited vs where SysML adds its own constraints.
Namespaces — the backbone of every model
KerML origin: Namespace (Root layer, §8.3.2.4.5)
A Namespace is an Element that contains other Elements, called its members. This is the most fundamental structural concept in KerML. Every pair of curly braces { } in the textual notation delineates a namespace boundary. SysML v2 inherits this concept unchanged — which is why part def, action def, and even individual feature bodies all act as namespaces.
In SysML v2, every Type is also a Namespace. Every part def, action def, port def, and even every feature body acts as a namespace. In SysML v1, only Packages and Models were namespaces — in v2, the concept is pervasive.
Name resolution and qualified names
Elements can carry two identifiers: a declared name (regular identifier or unrestricted name in single quotes like 'Ordinary Cargo Bay') and a short name in angle brackets (like <CB>). Qualified names use :: as separator:
1 Vehicles::Powertrain::Engine
When a simple unqualified name is unambiguous within the current scope chain, use it directly. When collisions exist or the element is out of scope, use a qualified name.
KerML textual notation for bare namespaces
1 namespace Outer { 2 namespace Inner { 3 // Elements here: Outer::Inner::elementName 4 } 5 }
Assuming only Packages are namespaces — because every Type is a Namespace, imports inside a definition are legal and useful. This technique avoids polluting the outer package with names relevant only to one definition:
1 part def Reindeer { 2 private import Datatypes::Color; // scoped to Reindeer only 3 attribute noseColor : Color; 4 }
Packages — grouping without semantics
KerML origin: Namespace that does not specialise Type
A Package is a Namespace, but unlike part def or action def, it does not specialise Type in KerML. This single fact explains everything: Types classify instances in the modelled universe; Packages have no instance semantics at all — they exist purely to organise the model's elements. You can nest packages freely without any implications for the type hierarchy.
1 package VehicleModel { 2 import ScalarValues::*; 3 import ISQ::*; 4 5 part def Vehicle { 6 attribute mass : ISQ::MassValue; 7 part engine : Engine; 8 } 9 10 part def Engine; 11 12 package Powertrain { 13 part def Transmission; 14 part def Drivetrain; 15 } 16 }
Library packages
Reusable model libraries use the library package form. Standard libraries use standard library package:
1 library package CommonParts { 2 part def Fastener; 3 part def Seal; 4 }
A package must reside in a single file — there is no mechanism to re-open or extend a package across multiple source files (unlike C# partial classes). Plan your package granularity accordingly.
Import & Visibility
KerML origin: Import (a Membership Relationship); VisibilityKind on every Membership
In KerML, Import is a Membership Relationship that introduces elements from one namespace into another. SysML v2 inherits this unchanged and adds two constraints: a visibility annotation (public or private) that controls re-export, and a recursive form (::**) that descends into nested namespaces. Every element in a namespace carries its own VisibilityKind as well — the two are orthogonal.
SysML v2's import system therefore governs three orthogonal dimensions: which elements are imported, how they are imported, and whether they are re-exported.
Visibility kinds
Every Membership carries a VisibilityKind:
| Keyword | Scope |
|---|---|
public (default) | Visible outside the namespace |
private | Visible only inside the owning namespace |
Default visibility is public — everything you declare is visible unless you say otherwise. Best practice: declare private for implementation details.
Two import mechanisms
1 // MembershipImport — single named element 2 import Sensors::Accelerometer; 3 4 // NamespaceImport — all public members at once 5 import Sensors::*; 6 7 // Private import — names do NOT re-export 8 private import InternalUtils::*; 9 10 // Recursive import — all nested namespaces too 11 import VehicleModel::**; 12 13 // Alias — create an alternative name 14 alias Accel for Sensors::Accelerometer;
Public vs private import — controlling re-export
The import relationship's own visibility controls whether imported names are re-exported when this namespace is subsequently imported by others:
1 package HighLevel { 2 public import Sensors::*; // names ARE re-exported 3 private import InternalUtils::*; // names stay here 4 }
Because both member and import visibility default to public, a chain of wildcard imports can propagate names far beyond their intended scope. Always write private import unless you explicitly intend re-export.
Specialisation Chains
KerML origin: Subclassification, Subsetting, Redefinition (Relationship subtypes)
SysML v2 unifies UML generalisation, subsetting, and redefinition under a single concept: Specialisation. These three relationships all exist at the KerML layer — SysML simply exposes them through the keywords specializes, subsets, and redefines, and restricts their use: specializes applies between definitions, while subsets and redefines apply between usages. Because KerML treats both classifiers and features as Types, specialisation applies uniformly across the whole model.
Four specialisation relationships
| Relationship | Applies to | Keyword | Shorthand | Semantic effect |
|---|---|---|---|---|
| Subclassification | Definitions ↔ Definitions | specializes | :> | Subtype inherits all features |
| Subsetting | Usages ↔ Usages | subsets | :> | Values form a subset |
| Redefinition | Usages ↔ Usages | redefines | :>> | Replaces inherited feature |
| Feature Typing | Usage → Definition | typed by | : | Specifies the type |
The :> operator means subclassification between definitions, and subsetting between usages. The definition/usage distinction determines the meaning.
Subsetting example
1 part def ReindeerTeam { 2 abstract part reindeer : Reindeer[9]; // 9 reindeer total 3 part rudolph : Reindeer subsets reindeer; // rudolph is one of the 9 4 part dasher : Reindeer subsets reindeer; // dasher is another 5 }
Redefinition example
1 part def Vehicle { 2 part eng : Engine; 3 attribute vendor : ScalarValues::String; 4 } 5 6 part def DieselVehicle specializes Vehicle { 7 part eng : DieselEngine redefines eng; // narrows type 8 attribute vendor : ScalarValues::String = "BoschMotorsport" 9 redefines vendor; // binds value 10 }
Conjugation — mirror-image ports
Conjugation creates a mirror image of a type where all feature directions are reversed: in ↔ out, inout stays. The ~ prefix denotes the conjugated form — primarily used for port compatibility:
1 port def FuelPort { 2 out item fuelSupply : Fuel; 3 in item fuelReturn : Fuel; 4 } 5 6 part def FuelTank { port tankPort : FuelPort; } // sends fuel 7 part def Engine { port engFuelPort: ~FuelPort; } // receives fuel
Confusing subsets with redefines. Using subsets when you intend redefines creates an additional feature alongside the inherited one — the model ends up with both the original and the new feature. Redefines replaces; subsets adds alongside.
Multiplicity
KerML origin: Multiplicity (a Feature — typed element carries a Multiplicity Feature as an owned child)
In KerML, Multiplicity is itself a Feature — it is not a UML-style annotation but a first-class model element owned by the typed element. This matters in two ways: multiplicity participates in the same Feature mechanisms as everything else (it can be inherited, redefined, constrained), and it allows parametric multiplicity — multiplicity expressed as a model-level expression rather than a literal integer. SysML v2 inherits this directly and additionally imposes domain-specific defaults on top of KerML's open [0..*].
Syntax and common forms
1 part def Vehicle { 2 part engine : Engine; // default [1..1] 3 part wheels : Wheel[4]; // exactly 4 4 part passengers: Person[0..5]; // zero to 5 5 attribute tags : String[*]; // zero or more (= [0..*]) 6 part sensors : Sensor[1..*]; // one or more 7 ref part trailer: Trailer[0..1];// optional reference 8 }
Default multiplicity — critical subtlety
| Context | Default multiplicity |
|---|---|
KerML feature (unspecified) | [0..*] — no constraint |
SysML part usage | [1..1] — exactly one |
SysML attribute usage | [1..1] — exactly one |
SysML port usage | [1..1] — exactly one |
Ordered, unique, and collection types
| Keywords | Collection type |
|---|---|
| (default) | Set — unordered, unique |
ordered | Ordered set — ordered, unique |
nonunique | Bag — unordered, allows duplicates |
ordered nonunique | Sequence — ordered, allows duplicates |
1 part def DataCollector { 2 attribute readings : Real[*] ordered nonunique; // sequence 3 attribute deviceIds : String[1..*]; // set of IDs 4 }
In KerML, the collection type (ordered, nonunique) is encoded as properties on the Multiplicity Feature itself. SysML inherits these flags as keyword modifiers. The default (no keywords) is a set: unordered and unique — consistent with mathematical set theory.
Multiplicity on a feature constrains values per featuring instance (e.g., each Vehicle has 4 Wheels). Multiplicity on a classifier constrains the total population across the universe. These are very different — feature-level meaning is overwhelmingly more common in practice.
The Feature Model
KerML origin: Feature (a Type — Core layer)
A Feature is a Type that classifies relations between things. Because Features are Types, they can own sub-features, be specialised, carry multiplicity, and participate in every Type relationship. This unification — absent in UML — is the single most consequential architectural decision in KerML. In SysML v2, every usage keyword (part, attribute, action, port) produces a Feature under the hood; the keyword just adds domain-specific constraints on top of the raw KerML Feature.
Feature direction: in, out, inout
Direction declares which way data or control flows through a feature relative to the element that owns it. It is only meaningful on features that participate in an interaction boundary — items inside a port, parameters of an action, or inputs and outputs of a function.
in— the value is supplied to this element from outside. Think of it as an input socket.out— the value is produced by this element and sent outward. Think of it as an output socket.inout— the value flows both ways; the feature can both receive and produce values (e.g. a read/write data bus).
Think of a power socket on a wall. From the building's perspective, the wall supplies power — that is out. From the appliance's perspective, it receives power — that is in. The same physical connection looks like out from one side and in from the other. This is exactly what conjugation (~) captures: it creates a mirror-image port type where every direction is reversed.
1 port def PowerPort { 2 out item voltage : ElectricalPower; // this port supplies power outward 3 in item load : LoadSignal; // this port receives a load signal 4 } 5 6 // ~PowerPort is the conjugate — reverses all directions: 7 // in item voltage : ElectricalPower (now receives power) 8 // out item load : LoadSignal (now sends load signal) 9 10 action def MotorControl { 11 in rpm : Real; // input: desired RPM from controller 12 in voltage : Real; // input: supply voltage 13 out torque : Real; // output: resulting torque produced 14 }
Directed features are automatically referential — no ref keyword needed. Ports themselves are never directed: do not write in port p. Direction belongs to the features inside the port definition, not to the port itself.
Composite vs reference: part vs ref part
This distinction controls ownership and lifetime. It answers: does this element exist because its parent exists, or does it exist independently of it?
part— composite: the parent exclusively owns the child. The child is created with the parent and destroyed with it. A car physically contains its engine — you cannot remove the engine and have it still meaningfully be "the car's engine".ref part— reference: the child exists independently; the parent merely holds a reference to it. The samePersoncan be the driver of multiple cars simultaneously. Destroying the car does not destroy the driver.attribute— a child of the part, likepart, but typed by aDataTyperather than aClass. BecauseDataTypeinstances have no identity, attributes cannot be referenced or shared independently. They disappear with the parent for the same reason any child does.
Composite is like the engine welded into a car: it lives and dies with the car. Reference is like the driver sitting in the car: they step out, they still exist independently.
1 part def Hospital { 2 part ward : Ward[1..*]; // isComposite=true: wards owned by hospital 3 ref part doctor: Doctor[0..*]; // isComposite=false: doctor exists independently 4 attribute name : String; // DataType child: gone when Hospital is deleted 5 } 6 7 // Deleting the Hospital also deletes its Wards (composite ownership) 8 // and its name value (DataType, no separate object). Doctors are unaffected.
Four kinds of value assignment
SysML v2 is precise about how a value is assigned, distinguishing permanent constraints from starting points, and inherited defaults from fixed values. The question each form answers:
| Syntax | Name | Question it answers | Can it be overridden? |
|---|---|---|---|
= expr | Bound value | What is this value always? | No — permanent invariant, cannot change |
:= expr | Initial value | What does this value start at? | Yes — can change during the instance lifetime |
default = expr | Default bound | What is the value unless a specialisation changes it? | Yes — a redefining subtype can override it |
default := expr | Default initial | What does it start at unless a specialisation changes it? | Yes — a redefining subtype can override it |
A car dashboard. A bound value is like the number of wheels being permanently 4 — a structural fact that never varies. An initial value is like the fuel level starting at full when the car leaves the factory — it changes during use. A default bound is like a standard tyre pressure that all models share unless a sports variant overrides it. A default initial is like the factory seat position setting — it can be changed per driver.
1 part def Engine { 2 attribute cylinders : Integer = 4; // bound: always 4, never changes 3 attribute rpm : Real := 0; // initial: starts at 0, changes at runtime 4 attribute idleRPM : Real default = 800; // default bound: 800 unless subtype overrides 5 attribute warmupTime : Real default := 30; // default initial: starts at 30s, overridable 6 } 7 8 part def RacingEngine specializes Engine { 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 at any point in the instance lifetime. Use := when you mean starts at this value but will change during operation.
Feature chaining — dot-notation paths
Feature chaining lets you navigate a path through nested features using dot notation. It builds a derived feature whose values are the result of following the entire chain — each step produces a set of values that becomes the input domain for the next step.
You use feature chaining in three main situations:
- Defining a derived feature — using dot notation (e.g.,
department.employee) to declare that one feature's values are reached by traversing a path through other features. Note: there is nochainskeyword in SysML v2; feature chaining is expressed purely through dot notation. - Writing flow statements — addressing exactly which nested item endpoint a flow connects to, because a bare port name is not specific enough.
- Accessing nested attributes in constraints and requirements, where you need to reach inside a sub-part.
"My cousins" is not a direct relationship — you have to follow: my parents, then their siblings, then their children. Feature chaining names exactly this kind of multi-hop path as a single reachable feature.
1 // ── Derived feature using dot-notation chaining ───────────── 2 part def Company { 3 part department : Department[1..*]; 4 // allEmployees navigates: Company -> Department -> Employee 5 feature allEmployees : Employee[*] = department.employee; 6 } 7 part def Department { 8 part employee : Employee[1..*]; 9 } 10 11 // ── Dot notation in flow statements ───────────────────────── 12 // A flow cannot just say "from tank to engine" — it must name 13 // the specific item endpoint nested inside each port. 14 part def DriveSystem { 15 part tank : FuelTank { port fuelOut : FuelPort; } 16 part engine : Engine { port fuelIn : ~FuelPort; } 17 18 flow of Fuel from tank.fuelOut.fuelSupply // tank -> port fuelOut -> item fuelSupply 19 to engine.fuelIn.fuelSupply; // engine -> port fuelIn -> item fuelSupply 20 } 21 22 // ── Accessing nested attributes in a constraint ────────────── 23 requirement def PowerBudget { 24 subject sys : ElectricalSystem; 25 require constraint { 26 sys.battery.voltage >= 12[V] // sys -> battery (part) -> voltage (attribute) 27 } 28 }
Dot notation in SysML v2 has precise KerML semantics: each segment of a.b.c narrows the set of values produced by the previous segment. The resulting chained feature is a real Feature in the model with its own multiplicity derived from the chain — it is not merely syntactic sugar.
Complete Example — Multi-package System
The following example, adapted from the official Sensmetry SysML v2 cheatsheet, demonstrates most of the concepts from this module in one cohesive model:
1 package VehicleSystem { 2 private import ScalarValues::*; 3 private import ISQ::*; 4 private import SI::*; 5 6 package Definitions { 7 part def Vehicle { 8 attribute mass : ISQ::MassValue; 9 part engine : Engine; 10 part fuelTank: FuelTank; 11 port fuelPort: FuelPort; 12 } 13 14 part def Engine { 15 attribute power : ISQ::PowerValue; 16 port engineFuelPort : ~FuelPort; 17 } 18 19 port def FuelPort { 20 out item fuelSupply : Fuel; 21 in item fuelReturn : Fuel; 22 } 23 24 part def SportsCar specializes Vehicle { 25 part engine : TurboEngine redefines engine; 26 } 27 28 part def TurboEngine specializes Engine { 29 attribute boostPressure : ISQ::PressureValue; 30 } 31 } 32 33 package Instances { 34 private import Definitions::*; 35 36 part myCar : SportsCar { 37 attribute mass : ISQ::MassValue = 1400[kg] redefines mass; 38 } 39 } 40 41 package Requirements { 42 private import Definitions::*; 43 private import Instances::*; 44 45 requirement def MassLimit { 46 subject vehicle : Vehicle; 47 attribute massLimit : ISQ::MassValue; 48 require constraint { vehicle.mass <= massLimit } 49 } 50 51 requirement vehicleMassReq : MassLimit { 52 subject vehicle = myCar; 53 attribute massLimit : ISQ::MassValue = 1800[kg] redefines massLimit; 54 } 55 56 assert satisfy vehicleMassReq by myCar; 57 } 58 }
This model demonstrates: packages and nesting, private wildcard imports, subclassification (specializes), redefinition (redefines), port definitions with directed features, conjugation (~), feature chaining, constraints, requirements, and the definition/usage pattern.
Module Summary
| SysML v2 concept | KerML origin | Key rule |
|---|---|---|
package | Namespace (does not specialise Type) | No instance semantics; organises the model only |
import | Import (a Membership Relationship) | Default is public; use private import unless you intend re-export |
Visibility (public/private) | VisibilityKind on every Membership | Applies to both members and imports independently |
specializes | Subclassification Relationship | Between definitions only; subtype inherits all features |
subsets | Subsetting Relationship | Between usages; adds a named subset alongside the inherited feature |
redefines | Redefinition Relationship | Between usages; replaces the inherited feature entirely |
Conjugation (~T) | KerML Conjugation Relationship | Reverses all directions on Features within the type |
Multiplicity [m..n] | Multiplicity Feature (owned child) | KerML default: [0..*]; SysML part/attribute/port: [1..1] |
ordered / nonunique | Properties on Multiplicity Feature | Encode set/bag/sequence/ordered-set collection type |
Feature direction (in/out/inout) | Direction property on Feature | Only meaningful at interaction boundaries (ports, actions) |
| Feature chaining (dot notation) | FeatureChaining Relationship | Multi-hop dot-notation path (e.g., a.b.c) builds a derived Feature with multiplicity from the chain |