Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

The SysML v2 Book

This book teaches you textual SysML v2 by building a model, not by reading the specification.

You will start with an empty file, grow it into a realistic system model, and learn each language feature as you need it. The running example throughout is a Smart Coffee Machine.

How to Read This Book

If you are new to SysML v2, start at Chapter 1 and read in order. Each chapter builds on the one before it, and the coffee machine model grows as you go.

If you already model in SysML v2, jump to the chapter you need. Chapters 3 to 10 cover the core language and Chapter 11 assembles it into one model. Chapters 12 to 14 cover specialization, metadata and views, and libraries; Chapters 15 and 16 cover working in projects and running what you have built. The ten appendices are reference material: syntax, migration from SysML v1, the Systems Modeling API, tooling notes, physics-aware simulation, diagnostic codes, relationship and element kinds, lexical rules, review checklists, and occurrences, time slices, and snapshots.

What You Will Build

By Chapter 11, you will have a complete system model with structure, behavior, requirements, and verification links – all connected into one coherent design.

About this book

The book teaches SysML v2 as the OMG specification defines it, and cites clauses where a claim is load-bearing so you can check it for yourself. Where something is a particular tool’s behaviour rather than the language’s, it says so – usually in a note like this one – so you can skip it and still learn the language.

This book is the language half of a documentation pair. Its companion, the sysml-rs documentation portal, documents the sysml-rs tool itself – installation, projects and dependencies, the CLI, execution, editors, and integrations. Tool-specific notes in these chapters link there rather than duplicating it.

Every code sample is checked against a real parser rather than written from memory.

This is a pre-1.0 draft. Chapters are still being verified against the specification, and some are thinner than others. If you find something wrong, please say so – corrections are the most useful contribution.

The prose is licensed CC-BY-4.0. The code samples are MIT OR Apache-2.0, so you can lift one into your own model without picking up an attribution obligation.

Why Textual SysML v2

SysML v2 is a language for describing systems – what they are made of, how they behave, and what they must do. You write it as plain text, the same way you write code.

Here is a complete SysML v2 model:

package CoffeeMachine {
    part def Grinder;
    part def Brewer;
    part def CoffeeMachine {
        part grinder : Grinder;
        part brewer : Brewer;
    }
}

Read it as: a CoffeeMachine contains a Grinder and a Brewer. It is not a diagram, and it is not a database. It is text in a file, and that file is the model.

Definition and usage: the idea everything else rests on

Look again at those seven lines. Grinder appears twice, and the two appearances mean different things.

part def Grinder introduces a definition – the kind of thing a grinder is. There is one of these no matter how many grinders exist.

part grinder : Grinder introduces a usage – one particular grinder, in the context of this coffee machine, typed by that definition.

If you have written code, the instinct is to read this as class and instance. It is close enough to get started, and Chapter 3 will sharpen it. What matters now is that the split is not a convention someone adopted – it is built into the language. The specification gives definitions and usages their own clause of the textual notation (SysML §8.2.2.6), and nearly every construct you will meet comes in both forms: action def and action, state def and state, requirement def and requirement.

Learn to ask “is this a kind, or a particular one?” and a large part of SysML v2 stops being a vocabulary problem.

Why text

When your model is a text file, you inherit the tooling you already have:

  • Diff and review. A change shows up as a line-by-line diff. A reviewer can comment on the exact line where a requirement changed or a port was renamed.
  • Version control. Branch, merge, and tag model versions the way you tag releases. The model’s history is the repository’s history.
  • Automation. A pipeline can parse the model, check its constraints, and fail the build before anyone sits down to a review meeting.

None of that is available when the model lives in a proprietary binary, and none of it needs a special tool beyond the ones you already run.

What the specification actually says about notation

It would be convenient to claim text is the one true form of a SysML v2 model. That is not what the standard says, and the real answer is more useful.

SysML v2 defines two concrete notations: a textual notation (§8.2.2) and a graphical one (§8.2.3). Both are normative. A tool can demonstrate Textual Notation Conformance, Graphical Notation Conformance, or both – and a tool claiming the graphical variant must support the textual notation at least far enough to render text inside diagrams correctly (SysML §2).

Underneath both sits the abstract syntax: the metamodel of elements and relationships that a model really consists of. Notation is how you write it down. This is why diagrams and text are not rivals. They are two projections of one model, which is also why a diagram can be generated from text but a well-formed model cannot be recovered from a picture alone.

Two conformance levels are mandatory for every conformant tool: Abstract Syntax Conformance and Model Interchange Conformance. Notation support is optional (SysML §2). So a tool is allowed to read and write SysML v2 models without offering either notation – which tells you where the standard thinks the model lives.

Interchange is specified too: projects travel as a .kpar archive, and textual files inside it use the .sysml extension. Those extensions are normative, not conventions.

Where SysML v2 came from

SysML v1 was a profile of UML – a set of stereotypes layered onto a general-purpose software modeling language. SysML v2 is not. It is a metamodel that extends the Kernel Modeling Language (KerML), a domain-independent language for building semantically rich modeling languages (SysML §1).

Two consequences you will feel while reading this book:

  • Names from KerML surface constantly. Occurrence, Feature, Classifier, Specialization are KerML’s, not SysML’s. When a chapter cites a KerML clause, that is why.
  • The standard library is part of the language. Types like Real, String and the ISQ units are defined in model libraries that ship with the specification (Clause 9), not baked into the grammar.

The standard also specifies a formal transformation from SysML v1.7 to v2, so migration is a defined activity rather than a rewrite from scratch. That is Appendix B.

What this book builds

Sixteen chapters that grow one model – a smart coffee machine – from an empty file into a complete specification:

  • Chapters 2–5 build the structure: definitions and usages, packages, parts, ports, attributes.
  • Chapters 6–8 add behaviour: connections and interfaces, actions with control flow, states with guarded transitions.
  • Chapters 9–10 make it checkable: expressions, constraints, requirements and verification cases.
  • Chapter 11 assembles the whole thing into one coherent model.
  • Chapters 12–14 go deeper: specialization and redefinition, metadata and views, libraries.
  • Chapters 15–16 are about working with a model: projects and workspaces, and what it means to run one.

Then ten appendices of reference material – syntax, migration, the API, tooling notes, physics-aware simulation, diagnostic codes, relationship kinds, lexical rules, review checklists, and occurrences, time slices, and snapshots.

How to read it

Start at Chapter 2 and work forward; each chapter builds on the one before, and the model assumes you have the earlier chapters. If you already model in SysML v2, jump to what you need and follow the back-references.

Where a claim about the language is load-bearing, the text cites the clause so you can check it. Where something is a particular tool’s behaviour rather than the language’s, it says so – usually in a note like the ones you will meet in Chapter 8 – so you can skip those and still learn SysML v2.

Blocks marked sysml are real model text, and every one of them is checked against a parser before this book ships. Most are complete enough to drop into a .sysml file as they stand; some are fragments of a larger model, and a few use ... to elide a part you have already seen. Blocks marked text are syntax templates with <angle> placeholders – shapes to read, not code to run.

You do not need the tool to follow along, but if you want it, Appendix D explains how to get it.


Here is that same coffee machine as a diagram – the same model, the other notation:

The coffee machine model as a general view diagram. Pan and zoom to explore.

Build Your First Model

In this chapter, you will build a coffee machine model from an empty file. By the end, you will have a typed, multi-definition model with attributes and imports – and you will understand the core concepts of SysML v2 through building, not through reading definitions.

Create a file called coffee_machine.sysml and follow along. After each step, you can run sysml check coffee_machine.sysml to verify your model.

You do not need the tool to follow the chapter – reading the models is enough. If you do want to run the commands, Appendix D explains how to get the sysml binary; it is a build from source rather than a package install, so it is worth setting up before you start rather than partway through.

The finished version of everything this book builds is already on disk, under examples/coffee-machine/. Chapter 11 maps each file to the chapter it belongs to. Peeking is allowed.

Step 1: An Empty Package

Every SysML v2 model lives inside a package. A package is a namespace – it groups related model elements and gives them a home.

Type this into your file:

package CoffeeMachineDomain {
}

That is a valid model. It does not describe anything yet, but it parses and checks cleanly. A package with nothing inside it is like an empty directory: a place waiting for content.

Run sysml check and confirm there are no errors.

Step 2: Your First Definition

Now add something inside the package. In SysML v2, a definition declares a reusable type – a blueprint for something that can exist in your system.

package CoffeeMachineDomain {
    part def Grinder;
}

part def Grinder says: there is a kind of thing called a Grinder. It is a part definition – a structural component type. You have not placed a grinder anywhere yet. You have only said that grinders exist as a concept in your domain.

The keyword part def is how SysML v2 spells “define a component type.” You will see this pattern everywhere: <keyword> def <Name>.

Step 3: A Usage Inside a Definition

A definition by itself is just a type. To say that a coffee machine contains a grinder, you create a usage of that type inside another definition:

package CoffeeMachineDomain {
    part def Grinder;

    part def CoffeeMachine {
        part grinder : Grinder;
    }
}

part grinder : Grinder says: inside a CoffeeMachine, there is a part called grinder, and it is of type Grinder. This is a typed usage – the most fundamental pattern in SysML v2.

Notice the difference:

  • part def Grinder – defines a type (uppercase, no colon)
  • part grinder : Grinder – uses that type in context (lowercase name, colon, type name)

This definition/usage split is how SysML v2 keeps models reusable. You define a type once, then use it wherever you need it.

Step 4: Adding More Definitions

A coffee machine has more than a grinder. Add a brewer and a water tank:

package CoffeeMachineDomain {
    part def Grinder;
    part def Brewer;
    part def WaterTank;

    part def CoffeeMachine {
        part grinder : Grinder;
        part brewer : Brewer;
        part waterTank : WaterTank;
    }
}

Run sysml check. No errors. The model now says: a CoffeeMachine is composed of three subsystems, each with a named role.

Here is how this model looks in an interactive general view. Each definition appears as a labeled block with its stereotype — «part definition», «item definition», and so on. Pan or zoom to inspect it:

CoffeeMachineDomain definitions: Grinder, Brewer, WaterTank, CoffeeMachine, items, and GrindSetting.

Notice that each definition is independent. Grinder does not know about Brewer. They only meet inside CoffeeMachine, where they each have a named usage. This separation is intentional – it keeps definitions reusable across different contexts.

Step 5: Attributes and Imports

Definitions without properties are just names. To make the model useful, add attributes – measurable or configurable properties of each component.

Attributes need types. SysML v2 provides built-in scalar types like Real, Integer, Boolean, and String in the ScalarValues library. To use them, you need an import:

package CoffeeMachineDomain {
    import ScalarValues::*;

    part def Grinder {
        attribute grindSize : Real;
    }

    part def Brewer {
        attribute waterTemp : Real;
        attribute brewPressure : Real;
    }

    part def WaterTank {
        attribute currentLevel : Real;
        attribute maxCapacity : Real;
    }

    part def CoffeeMachine {
        part grinder : Grinder;
        part brewer : Brewer;
        part waterTank : WaterTank;
        attribute serialNumber : String;
    }
}

Several things happened:

  • import ScalarValues::* brings all scalar types into scope. The ::* means “import everything from this package.” Without this line, Real and String would be unresolved names.
  • Each definition now has attribute members. An attribute is a property with a type: attribute grindSize : Real says the grinder has a numeric grind size setting.
  • CoffeeMachine itself has a serialNumber attribute. Definitions can have both part usages (components) and attribute usages (properties).

Run sysml check again. If you forget the import line, you will see errors like “unresolved name: Real.” That error tells you exactly what is missing – add the import and the errors disappear.

Step 6: Items – Things That Flow

Not everything in a system is a structural component. Some things are consumed, produced, or transferred. In SysML v2, these are items.

A coffee machine deals with water, coffee beans, and brewed coffee. These are not parts of the machine – they flow through it.

package CoffeeMachineDomain {
    import ScalarValues::*;

    // Items: things that flow through the system
    item def Water;
    item def CoffeeBeans;
    item def BrewedCoffee;

    // Parts: structural components
    part def Grinder {
        attribute grindSize : Real;
    }

    part def Brewer {
        attribute waterTemp : Real;
        attribute brewPressure : Real;
    }

    part def WaterTank {
        attribute currentLevel : Real;
        attribute maxCapacity : Real;
    }

    part def CoffeeMachine {
        part grinder : Grinder;
        part brewer : Brewer;
        part waterTank : WaterTank;
        attribute serialNumber : String;
    }
}

item def Water is like part def Grinder, but it signals a different intent. Parts are components you assemble into a structure. Items are things that move, get consumed, or get produced. The distinction matters when you model behavior and flows in later chapters.

The // lines are comments. They are ignored by the checker but help readers navigate the model.

Step 7: An Enumeration

Some properties have a fixed set of valid values. The grind size might be Fine, Medium, or Coarse. SysML v2 models this with an enumeration:

package CoffeeMachineDomain {
    import ScalarValues::*;

    // Items
    item def Water;
    item def CoffeeBeans;
    item def BrewedCoffee;

    // Enumerations
    enum def GrindSetting {
        fine;
        medium;
        coarse;
    }

    // Parts
    part def Grinder {
        attribute grindSize : GrindSetting;
    }

    part def Brewer {
        attribute waterTemp : Real;
        attribute brewPressure : Real;
    }

    part def WaterTank {
        attribute currentLevel : Real;
        attribute maxCapacity : Real;
    }

    part def CoffeeMachine {
        part grinder : Grinder;
        part brewer : Brewer;
        part waterTank : WaterTank;
        attribute serialNumber : String;
    }
}

enum def GrindSetting defines a type with exactly three values. The Grinder attribute now uses GrindSetting instead of Real, which is more precise – a reviewer can see exactly what values are valid.

What You Have Built

With the definitions expanded, you can see every attribute inside each part definition. Notice how CoffeeMachine contains typed usages of the other parts, and each part definition lists its own attributes:

Expanded CoffeeMachineDomain definitions, including typed part usages and attributes.

Step back and look at what you have. In about 40 lines, you have:

  • A package that organizes the model
  • Part definitions for three subsystems (Grinder, Brewer, WaterTank) and the top-level CoffeeMachine
  • Item definitions for things that flow (Water, CoffeeBeans, BrewedCoffee)
  • An enumeration for a constrained value set (GrindSetting)
  • Attributes on each definition, typed with scalar values or enumerations
  • Typed usages that compose the subsystems into the machine
  • An import that brings scalar types into scope

This is already a useful structural model. A reviewer can see what the system is made of, what properties each component has, and how the components relate.

What Comes Next

This model is the foundation for the rest of the book. Every chapter will extend it:

  • Chapter 3 formalizes the definition/usage pattern you used here.
  • Chapter 4 shows how to split this model across multiple files.
  • Chapter 5 adds ports – interaction points where components connect.
  • Chapter 6 wires the components together.
  • Chapter 7 adds behavior: what the machine does when you press brew.
  • Chapter 8 adds operating modes: idle, brewing, cleaning, error.
  • Chapter 9 adds checkable logic: temperature ranges, safety interlocks.
  • Chapter 10 adds requirements and traces them to the design.
  • Chapter 11 assembles everything into one coherent model.

Keep your coffee_machine.sysml file. You will keep building on it.

Common Mistakes

Forgetting the import. If you use Real or String without import ScalarValues::*, the checker will report unresolved names. The fix is always the same: add the import at the top of your package.

Leaving usages untyped. This is valid syntax:

part def CoffeeMachine {
    part grinder;
}

But it tells a reviewer nothing about what kind of grinder this is. Always type your usages: part grinder : Grinder. Typed usages make the model checkable and reusable.

Defining everything at the top level. If you write definitions outside a package, they have no namespace boundary. As your model grows, name collisions become likely. Always wrap definitions in a package.

Definitions and Usages

In Chapter 2, you built a coffee machine model. Along the way, you wrote things like part def Grinder and part grinder : Grinder without much ceremony. This chapter explains why SysML v2 makes that split, what the different definition keywords mean, and how ownership works when you nest elements inside each other.

If you internalize one design rule from this book, make it this: definitions and usages are different jobs. A definition declares a reusable type. A usage places that type in a specific context. Every modeling decision you make in SysML v2 flows from that split.

Why the Split Exists

Consider the Grinder from Chapter 2:

part def Grinder {
    attribute grindSize : GrindSetting;
}

part def CoffeeMachine {
    part grinder : Grinder;
}

Grinder is a definition. It says: grinders exist, and they have a grind size. It does not say where a grinder lives, how many there are, or who owns one.

grinder is a usage. It says: inside a CoffeeMachine, there is one grinder, and it plays the role called grinder.

This separation is what makes the model reusable. Suppose you are modeling a coffee roastery that has a separate grinding station:

part def GrindingStation {
    part grinder : Grinder;
    part spareGrinder : Grinder;
}

The same Grinder definition appears in two completely different contexts – once inside CoffeeMachine, twice inside GrindingStation. The definition did not change. Only the usages differ.

Without this split, you would have to copy the grinder’s properties into every place it appears. Change the grind size attribute, and you would have to find and update every copy. The definition/usage pattern gives you a single source of truth.

The Definition Keywords

SysML v2 has several definition keywords, each signaling a different kind of thing. You have already seen three of them in Chapter 2. Here they are together:

part def – Structural Components

A part definition describes something you can assemble into a structure. Parts have spatial or compositional relationships with their containers. The grinder is inside the machine. The brewer is inside the machine. These are parts.

part def Grinder { ... }
part def Brewer { ... }
part def WaterTank { ... }
part def CoffeeMachine { ... }

Use part def when the thing you are describing is a component that belongs to an assembly. If you can point at it in a bill of materials or a physical breakdown, it is probably a part.

item def – Things That Flow

An item definition describes an identifiable thing the system acts on over time, without requiring that the thing perform actions of its own (SysML §7.10). Items flow through the system, get consumed, get produced, or get exchanged.

item def Water;
item def CoffeeBeans;
item def BrewedCoffee;

Water is not a part of the coffee machine. You do not bolt water onto the frame. But water flows into the tank, gets heated by the brewer, and comes out as brewed coffee. Modeling it as an item tells reviewers: this is something the system processes, not something it is made of.

Parts and items are not rival categories. A part definition is a kind of item definition (SysML §7.11), so every part can be treated as an item when that is the useful view – an engine flows down an assembly line as an item, then goes into a vehicle as a part. Choosing part def picks the primary framing; it does not forbid the other one.

The distinction becomes important when you add behavior in Chapter 7. Actions consume and produce items. Connections in Chapter 6 can carry items between ports. The item keyword sets up these downstream patterns.

attribute def – Properties and Value Types

An attribute definition describes a measurable or configurable property. You used attribute usages throughout Chapter 2 (grindSize, waterTemp, brewPressure), and you also defined one attribute type with enum def GrindSetting.

You can also define custom attribute types explicitly:

attribute def Temperature {
    attribute value : Real;
    attribute unit : String;
}

This defines Temperature as a structured value with a numeric part and a unit. You could then use it:

part def Brewer {
    attribute waterTemp : Temperature;
    attribute brewPressure : Real;
}

Use attribute def when you need a value type that has internal structure. For simple scalar properties, you can type attributes directly with Real, String, Integer, or Boolean from the ScalarValues library.

When to Use Which

KeywordWhat it modelsChapter 2 examples
part defStructural componentGrinder, Brewer, WaterTank, CoffeeMachine
item defThing that flowsWater, CoffeeBeans, BrewedCoffee
attribute defStructured value type(GrindSetting used enum def)
enum defFixed set of valuesGrindSetting

If you are unsure, ask: “Is this thing a component of the assembly?” If yes, part def. “Does it flow through or get consumed?” If yes, item def. “Is it a measurable property?” If yes, attribute def or a scalar type.

You will meet more definition keywords in later chapters – action def for behavior in Chapter 7, state def for operating modes in Chapter 8, requirement def in Chapter 10. They all follow the same <keyword> def <Name> pattern.

How tools render this distinction

The SysML v2 graphical notation gives the def-vs-usage split a visual cue: definitions render with sharp corners, usages render with rounded corners. The same model appears as part def Grinder (a sharp-cornered box) and part grinder : Grinder (a rounded-cornered box) on every conformant diagram. You will see the convention applied throughout the diagram fixtures in this book and in any other SysML v2 tool you use.

Usages: Placing Types in Context

Every definition keyword has a corresponding usage keyword. You drop the def:

part def Grinder { ... }       // definition
part grinder : Grinder;        // usage

item def Water;                // definition
item water : Water;            // usage

attribute def Temperature;     // definition
attribute temp : Temperature;  // usage

A usage always answers three questions:

  1. What kind? The keyword (part, item, attribute).
  2. What name? The usage name (grinder, water, temp).
  3. What type? The definition after the colon (Grinder, Water, Temperature).

The name is how you refer to this particular instance within its context. The type links back to the definition. A reviewer looking at part grinder : Grinder knows immediately what kind of thing it is and where to find its properties.

Untyped Usages

You can write a usage without a type:

part def CoffeeMachine {
    part grinder;
}

This is syntactically valid. But it tells a reviewer nothing about what kind of grinder this is. There is no link to a definition, no properties to check, no way for a tool to validate that the usage makes sense. Treat untyped usages as temporary scaffolding. Always add a type before the model goes into review.

Reuse: One Definition, Many Usages

The real power of the definition/usage split shows up when you reuse definitions across contexts. Consider a company that makes two coffee machine models:

package CoffeeMachineLineup {
    import CoffeeMachineDomain::*;

    part def HomeMachine {
        part grinder : Grinder;
        part brewer : Brewer;
        part waterTank : WaterTank;
        attribute serialNumber : String;
    }

    part def OfficeMachine {
        part grinder : Grinder;
        part brewer : Brewer;
        part waterTank : WaterTank;
        part secondBrewGroup : Brewer;
        attribute serialNumber : String;
    }
}

Both machines use the same Grinder, Brewer, and WaterTank definitions from the CoffeeMachineDomain package you built in Chapter 2. The OfficeMachine happens to have a second brew group, but it reuses the same Brewer definition for both.

If you later add a descaleCount attribute to Brewer, both machines get it automatically. You do not touch HomeMachine or OfficeMachine at all. The definitions are the single source of truth; the usages just point at them.

This pattern also makes the model reviewable. When someone new reads part secondBrewGroup : Brewer, they can look up Brewer once and understand every place it appears. No hunting through duplicated property lists to check if they are all consistent.

Ownership and Membership

When you write an element inside another element’s braces, you are doing more than indenting text. You are declaring an ownership relationship.

part def CoffeeMachine {
    part grinder : Grinder;
    part brewer : Brewer;
}

The grinder usage is owned by the CoffeeMachine definition. This is called membershipgrinder is a member of CoffeeMachine. Ownership has consequences:

  • Scope. The name grinder is visible inside CoffeeMachine. Code outside CoffeeMachine must use a qualified name like CoffeeMachine::grinder to refer to it.
  • Lifecycle. A part or item usage declared like this is composite, and the values of a composite usage cannot exist after the instance that features them ceases to exist (SysML §7.6.3). Destroy a CoffeeMachine, and its grinder goes with it.
  • Containment. Composite parts are inside their owner, structurally. This is what makes CoffeeMachine a composite: it contains its parts rather than merely knowing about them.

Ownership forms a tree. The package owns the definitions. Each definition owns its members. Each member might own further nested elements. This tree gives the model its structure and determines how names resolve.

Nesting Deeper

Ownership can go several levels deep. Suppose the grinder has an internal motor:

part def Motor {
    attribute powerRating : Real;
}

part def Grinder {
    attribute grindSize : GrindSetting;
    part motor : Motor;
}

Now the ownership tree looks like this:

CoffeeMachineDomain (package)
  Grinder (part def)
    grindSize (attribute)
    motor (part)
      powerRating (attribute)
  CoffeeMachine (part def)
    grinder (part) : Grinder
    brewer (part) : Brewer
    ...

Each element has exactly one owner. The motor usage is owned by Grinder. The grinder usage is owned by CoffeeMachine. The Grinder definition is owned by the CoffeeMachineDomain package. There is no ambiguity about where anything lives.

Composite vs. Reference Usages

Every part and item usage you have seen so far is composite – its values belong to the instance that features them and cannot outlive it. But sometimes you need to refer to something without containing it.

Consider a maintenance log that records which grinder was serviced:

part def MaintenanceRecord {
    attribute date : String;
    ref part servicedGrinder : Grinder;
}

The ref keyword says: this usage refers to a Grinder that exists somewhere else. The MaintenanceRecord does not contain a grinder. It just points at one.

Without ref, writing part servicedGrinder : Grinder makes the usage composite – the maintenance record would contain a grinder. That is clearly wrong. A maintenance record is a data entry, not a physical container for machine parts.

The distinction has teeth in the specification, not just in style. Values of a composite usage cannot also be values of another composite usage unless both sit on the same featuring instance (SysML §7.6.3) – a physical grinder is inside exactly one machine. Reference usages carry no such restriction, which is exactly why a record can point at a grinder some machine already contains. It is also why a bill-of-materials rollup counts composite usages and skips references.

Two kinds of usage are referential whether or not you write ref:

  • Attribute usages. A value has no independent existence to contain, so attribute grindSize : GrindSetting is always referential.
  • Directed usages. Anything declared in, out, or inout – including the items inside the port definitions of Chapter 5 – is always referential.

Use ref when the relationship is “knows about” rather than “contains.” We will revisit reference usages when we cover connections in Chapter 6, where they play a larger role.

The Pattern Behind the Pattern

At this point, you have seen the definition/usage split applied to parts, items, and attributes. The pattern is always the same:

<keyword> def <Name> { <members> }     // definition
<keyword> <name> : <Name>;             // composite usage
ref <keyword> <name> : <Name>;         // reference usage

This regularity is intentional. Every new SysML v2 concept you encounter – actions, states, requirements, constraints – follows this same structure. Once you can read part def and part, you can read action def and action, state def and state, requirement def and requirement. The keywords change; the pattern does not.

What Comes Next

You now understand the core modeling pattern of SysML v2: define a type, use it in context. You know the difference between parts (structural), items (flow), and attributes (values). You know that nesting creates ownership, and that ref makes a usage referential so it can point at something without containing it.

In Chapter 4, you will split the coffee machine model across multiple files, using packages and imports to organize definitions so that teams can work on different parts of the model independently.

Common Mistakes

Using part def for everything. Not every concept is a structural component. Water flowing through a brewer is an item, not a part. A temperature reading is an attribute, not a part. Using the right keyword communicates intent and enables correct downstream analysis.

Duplicating definitions instead of reusing them. If HomeMachine and OfficeMachine both need a brewer, define Brewer once and use it twice. Do not create HomeBrewer and OfficeBrewer with identical properties. Specialize only when behavior or structure genuinely differs – Chapter 12 covers how.

Forgetting ref on non-containing usages. If a MaintenanceRecord has part servicedGrinder : Grinder without ref, the model says the record physically contains a grinder. This is a modeling error that tools may not catch as a syntax problem – it is a semantic mistake. When the relationship is “refers to” rather than “contains,” always use ref.

Packages and Imports

The coffee machine model from Chapter 2 fits in a single file. That will not last. As soon as a second engineer needs to work on the model, or a second subsystem needs its own definitions, you need a way to split things up without breaking name resolution.

SysML v2 gives you two tools for this: packages for grouping and imports for connecting.

When One File Is Not Enough

Here is the model from Chapter 2, all in one file:

package CoffeeMachineDomain {
    import ScalarValues::*;

    item def Water;
    item def CoffeeBeans;
    item def BrewedCoffee;

    enum def GrindSetting {
        fine;
        medium;
        coarse;
    }

    part def Grinder {
        attribute grindSize : GrindSetting;
    }

    part def Brewer {
        attribute waterTemp : Real;
        attribute brewPressure : Real;
    }

    part def WaterTank {
        attribute currentLevel : Real;
        attribute maxCapacity : Real;
    }

    part def CoffeeMachine {
        part grinder : Grinder;
        part brewer : Brewer;
        part waterTank : WaterTank;
        attribute serialNumber : String;
    }
}

This is 30 lines. But over the next several chapters, this model will grow to include ports, connections, actions, states, constraints, and requirements. One file will become hard to navigate and harder to review.

The fix is not to scatter definitions randomly across files. It is to decide what belongs together and draw clear boundaries.

Packages Are Namespace Boundaries

A package is a container for related model elements. Every name inside a package is scoped to that package. Two different packages can each define a Status enumeration without collision, because the full name includes the package: Brewing::Status versus Maintenance::Status.

You already used a package in Chapter 2. The new idea is using multiple packages to separate concerns.

Here is the coffee machine model split into two packages:

package CoffeeTypes {
    import ScalarValues::*;

    item def Water;
    item def CoffeeBeans;
    item def BrewedCoffee;

    enum def GrindSetting {
        fine;
        medium;
        coarse;
    }
}

package CoffeeParts {
    import ScalarValues::*;
    import CoffeeTypes::*;

    part def Grinder {
        attribute grindSize : GrindSetting;
    }

    part def Brewer {
        attribute waterTemp : Real;
        attribute brewPressure : Real;
    }

    part def WaterTank {
        attribute currentLevel : Real;
        attribute maxCapacity : Real;
    }

    part def CoffeeMachine {
        part grinder : Grinder;
        part brewer : Brewer;
        part waterTank : WaterTank;
        attribute serialNumber : String;
    }
}

CoffeeTypes holds items and enumerations – domain vocabulary that does not depend on the machine structure. CoffeeParts holds the structural definitions and imports the types it needs.

The split follows a principle: separate what things are from how they are assembled.

Wildcard vs. Explicit Imports

The import CoffeeTypes::* line is a wildcard import. It brings every public member of CoffeeTypes into scope: Water, CoffeeBeans, BrewedCoffee, and GrindSetting all become directly usable names.

Wildcard imports are convenient when you are building a model and the package boundaries are still shifting. But they hide which names actually get used. If someone adds a Pressure attribute definition to CoffeeTypes later, it silently enters scope in CoffeeParts – possibly colliding with a local name.

An explicit import names exactly what you need:

package CoffeeParts {
    import ScalarValues::Real;
    import ScalarValues::String;
    import CoffeeTypes::GrindSetting;

    part def Grinder {
        attribute grindSize : GrindSetting;
    }

    // ...
}

Now a reviewer can see at a glance: this package uses Real, String, and GrindSetting. Nothing else. If GrindSetting gets renamed or removed, this import line will fail and point you to the problem.

Use wildcard imports while exploring. Switch to explicit imports when the model stabilizes. Standard library packages like ScalarValues are a reasonable exception – wildcard-importing ScalarValues::* is common practice because those types rarely change and are used everywhere.

Qualified Names Without Importing

You do not always need an import. You can refer to any element by its fully qualified name:

package CoffeeParts {
    part def Grinder {
        attribute grindSize : CoffeeTypes::GrindSetting;
    }
}

This is verbose but unambiguous. It works well for one-off references where adding an import would be noise. If you use the same qualified name three or more times, an import is cleaner.

Visibility: Public and Private Members

By default, every member of a package is public – visible to any package that imports it (KerML §7.2.5.2). You can make a member private so it is only visible inside its own package:

package CoffeeTypes {
    import ScalarValues::*;

    // Public: other packages can use these
    item def Water;
    item def CoffeeBeans;
    item def BrewedCoffee;

    enum def GrindSetting {
        fine;
        medium;
        coarse;
    }

    // Private: internal helper, not part of the public API
    private item def UsedGrounds;
}

UsedGrounds exists inside CoffeeTypes but will not appear in CoffeeParts even with a wildcard import. This is useful when a package has internal helpers that other packages should not depend on.

Think of visibility the same way you think about public and private functions in code. Expose what other packages need. Keep implementation details hidden.

There is a third keyword, protected. Outside a type it behaves exactly like private; inside a type it matters for what specializations inherit. Reach for it in Chapter 12 territory, not here.

Imports Have Visibility Too

The visibility keywords do not only apply to members. They apply to imports, and this is the part that surprises people.

[public | protected | private] import <Package>::<name-or-*>;
package Structure {
    // Anyone importing Structure also gets these
    public import Definitions::*;

    // Structure can use these; importers of Structure cannot
    private import Internal::*;
}

An import’s visibility decides whether the names it brings in are visible to your importers, or only to you. If the import is public, the imported memberships become public in the importing namespace – you have re-exported them. If it is private, they stay yours (SysML §7.5.3, KerML §7.2.5.4).

Read it as: public import forwards, private import consumes.

And the default is the one worth memorising, because it is the opposite of what most people guess: a bare import is private. So this does not do what it looks like:

package Structure {
    import Definitions::*;      // private -- NOT re-exported
}

A package that imports Structure gets nothing from Definitions. If you meant to pass those names through, you have to say public import.

This is exactly why the coffee machine model’s package layout writes public import in each of its concern packages: Structure, Behavior, and QualityModel each re-export what they pull in, so a consumer imports one package and gets a coherent set of names without knowing how the model is split internally.

Importing a whole subtree

import <Package>::**;

Suffixing an import with ::** makes it recursive: it takes the namespace’s members and then keeps going into nested namespaces (KerML §7.2.5.4). One ** replaces a column of imports when you genuinely want everything under a root.

package CoffeeMachineAnalysis {
    import CoffeeMachineDomain::**;
}

Recursion stops at what was actually imported – it follows owned members of imported namespaces, and does not drag in memberships that arrived through implied specializations. Use it when you want a whole subtree and be aware that, like any wildcard, it makes the origin of a name harder to see at a glance.

Two forms the tool does not accept yet. The specification lets an import carry a body – public import D::* { doc /* why */ } – and lets a namespace import be filtered by metadata, as in import D::**[@Safety]. Both are in the grammar; sysml-rs parses neither, and for the body form it reports the error against the visibility keyword rather than the body, which is misleading. The semicolon forms above are what works today.

Nested Packages

Packages can contain other packages. This creates a hierarchy:

package CoffeeMachineDomain {
    package Types {
        import ScalarValues::*;

        item def Water;
        item def CoffeeBeans;
        item def BrewedCoffee;

        enum def GrindSetting {
            fine;
            medium;
            coarse;
        }
    }

    package Parts {
        import ScalarValues::*;
        import CoffeeMachineDomain::Types::*;

        part def Grinder {
            attribute grindSize : GrindSetting;
        }

        part def Brewer {
            attribute waterTemp : Real;
            attribute brewPressure : Real;
        }

        part def WaterTank {
            attribute currentLevel : Real;
            attribute maxCapacity : Real;
        }

        part def CoffeeMachine {
            part grinder : Grinder;
            part brewer : Brewer;
            part waterTank : WaterTank;
            attribute serialNumber : String;
        }
    }
}

From outside the model, you would refer to CoffeeMachineDomain::Parts::CoffeeMachine. From inside Parts, you can refer to its own members directly.

To reach a sibling package, it helps to know how a name gets resolved. Resolution starts in the local namespace and then searches outwards through each containing namespace, up to the root and finally the global namespace (KerML §7.2.5.1). So from inside Parts, the name Types resolves – it is a member of the enclosing CoffeeMachineDomain – and Types::GrindSetting works with no import at all. What the import buys you is the unqualified name: without import CoffeeMachineDomain::Types::*, the bare name GrindSetting does not resolve inside Parts, because it is a member of the sibling package, not of the parent.

Nesting is useful for large models with several subsystem domains. For most models, one level of packages is enough. Do not nest deeper than two levels unless the model genuinely has that much structure.

When you want to see the nesting at a glance rather than read it from source, build a view def :> BrowserView over the package — BrowserView is one of the eight standard view definitions (SysML §9.2.20) covered in Chapter 13, and it presents the containment hierarchy as a tree.

Splitting Across Files

In SysML v2, a file is just a container for text. The language does not mandate a relationship between file names and package names. But a predictable convention saves time.

Here is a practical layout for the coffee machine model, with one package per file:

coffee-machine/
    types.sysml          -- CoffeeTypes package
    parts.sysml          -- CoffeeParts package
    architecture.sysml   -- top-level assembly (later chapters)

types.sysml:

package CoffeeTypes {
    import ScalarValues::*;

    item def Water;
    item def CoffeeBeans;
    item def BrewedCoffee;

    enum def GrindSetting {
        fine;
        medium;
        coarse;
    }
}

parts.sysml:

package CoffeeParts {
    import ScalarValues::*;
    import CoffeeTypes::GrindSetting;

    part def Grinder {
        attribute grindSize : GrindSetting;
    }

    part def Brewer {
        attribute waterTemp : Real;
        attribute brewPressure : Real;
    }

    part def WaterTank {
        attribute currentLevel : Real;
        attribute maxCapacity : Real;
    }

    part def CoffeeMachine {
        part grinder : Grinder;
        part brewer : Brewer;
        part waterTank : WaterTank;
        attribute serialNumber : String;
    }
}

Nothing in the language ties an element to the file it was declared in. Names resolve by namespace, not by file position, so the order the files are loaded in does not matter.

Loading a whole directory is the tool’s job, not the language’s. sysml check <file> takes a single file. To load every .sysml file in a directory and resolve names across them, reach for sysml inspect --workspace <dir> instead.

The convention is simple: one package per file, file name matches the package purpose. This makes it easy to find where a definition lives without searching. When you are ready to give your directory a formal identity with versioned dependencies, see Chapter 15.

What Changes, What Stays the Same

The model content has not changed. CoffeeMachine still contains a Grinder, a Brewer, and a WaterTank. The grinder still has a GrindSetting attribute. What changed is the organization: types in one package, parts in another, each in its own file.

This reorganization pays off starting in Chapter 5, when you add ports to the part definitions. You will import from CoffeeParts and CoffeeTypes rather than working inside one large package where everything is tangled together.

The browser view shows how the model’s packages and their contents form a tree:

Package tree: expand nodes to see how definitions are organized across packages.

Common Mistakes

Circular imports. If package A imports from B and B imports from A, you have a circular dependency. The specification explicitly permits circularity in imports (KerML §8.2.3.5.1), so this is legal and a conformant tool must cope with it – but it makes your model hard to understand and refactor. If two packages need each other, they probably belong in the same package, or you need to extract the shared elements into a third package.

Over-splitting. Creating a separate package for every single definition adds import noise without improving clarity. Group things that change together. If Grinder, Brewer, and WaterTank are always used as a set, keeping them in one package is better than scattering them across three.

Expecting a sibling package’s members to be in scope. A nested package does see the names its parent holds – resolution searches outwards – so Types, and anything else declared directly in CoffeeMachineDomain, resolves from inside Parts with no import. What you do not get is the sibling’s members: GrindSetting needs either the qualifier Types::GrindSetting or an import. Nesting gives you a naming hierarchy; it does not flatten one package’s members into another.

Structure

Your coffee machine model has definitions, usages, attributes, and an enumeration. It describes what things are and what properties they carry. But it says nothing about how those parts interact with the outside world – where water enters the brewer, where ground beans come out of the grinder, or how many heating elements exist. This chapter fills those gaps.

You will add ports to define interaction boundaries, multiplicity to say how many of something exist, and port definitions to type exactly what flows through each boundary. By the end, your coffee machine will have a fully specified structural interface.

Composite Structure Revisited

Before adding new concepts, look at what composite structure already gives you. In Chapter 2, you built this:

part def CoffeeMachine {
    part grinder : Grinder;
    part brewer : Brewer;
    part waterTank : WaterTank;
    attribute serialNumber : String;
}

This says a CoffeeMachine is composed of three parts. The keyword part inside a definition creates an ownership relationship: the grinder, brewer, and water tank exist because the coffee machine exists. If you destroy the machine, the parts go with it. This is composite structure – the whole owns the parts.

Each part usage is typed. part grinder : Grinder does not just say “there is something called grinder.” It says the grinder conforms to the Grinder definition, which means it carries every attribute and port that Grinder declares. Type a usage, and all the definition’s features come along for free.

But right now, these three parts sit side by side inside CoffeeMachine with no way to interact. The grinder has no output chute. The brewer has no water inlet. To model interaction, you need ports.

Ports: Where Things Cross a Boundary

A port is a point on a part where interaction happens. Think of a physical connector, a pipe fitting, or an electrical terminal. In SysML v2, a port declares that something can flow into or out of a component at a specific point.

Here is the simplest possible port:

part def Brewer {
    attribute waterTemp : Real;
    attribute brewPressure : Real;

    port waterInlet;
}

This adds an untyped port called waterInlet to the Brewer. It compiles, but it does not say anything useful. What flows through it? In what direction? To make ports meaningful, you need two things: a port definition that types the port, and a direction that says which way things flow.

Port Definitions

A port definition describes the kind of interaction point. It declares what items flow through it and in which direction.

port def WaterPort {
    in item water : Water;
}

This says: a WaterPort is an interaction point where Water flows in. The direction keyword in is part of the item usage inside the port definition. Now apply it:

part def Brewer {
    attribute waterTemp : Real;
    attribute brewPressure : Real;

    port waterInlet : WaterPort;
}

port waterInlet : WaterPort says: the brewer has an interaction point called waterInlet, and it conforms to the WaterPort definition. Because WaterPort declares in item water : Water, a reviewer (or a tool) can verify that anything connected to waterInlet must supply Water, flowing inward.

The pattern mirrors what you already know: port def defines a reusable type, port creates a usage of that type. Same definition/usage split as part def and part.

Direction: in, out, inout

Port definitions use direction keywords to declare which way items flow through a port. There are three:

  • in – the item flows into the owning part
  • out – the item flows out of the owning part
  • inout – the item flows in both directions

Direction is relative to the part that owns the port. When you write in item water : Water inside a port on the Brewer, you are saying water flows into the brewer through this port. From the perspective of whatever is connected on the other side, that same water is flowing out.

Here is what all three look like in port definitions:

port def WaterPort {
    in item water : Water;
}

port def GroundCoffeePort {
    out item grounds : CoffeeBeans;
}

port def StatusPort {
    inout attribute level : Real;
}

WaterPort receives water. GroundCoffeePort emits ground coffee. StatusPort can both send and receive a level reading – useful for a sensor that reports values but can also be calibrated.

A single port definition can carry multiple items:

port def BrewInputPort {
    in item water : Water;
    in item grounds : CoffeeBeans;
}

This models a port where two distinct items enter. That is less common for physical ports (you would typically use separate connectors), but useful for logical interfaces where a single interaction point carries multiple signals.

Why Direction Matters

Direction is not decoration. It enables three things:

  1. Connection validation. When you wire two ports together in Chapter 6, tools can check that an out port connects to an in port of a compatible type. Connecting two in ports is a modeling error.

  2. Flow analysis. Direction tells you which way items move through the system. In Chapter 7, action flows will use port directions to determine data dependencies.

  3. Interface contracts. A port definition is a contract: “this component expects to receive Water here.” Any component with a matching port can plug in. Direction is part of that contract.

If you omit direction, the item has no declared flow direction. That weakens every check that depends on knowing which way things move. Always declare direction on items inside port definitions.

Building Port Definitions for the Coffee Machine

Now apply ports to your existing model. Think about each component and ask: what crosses its boundary?

Grinder: Coffee beans go in; ground coffee comes out.

port def BeanInputPort {
    in item beans : CoffeeBeans;
}

port def GroundCoffeePort {
    out item grounds : CoffeeBeans;
}

Both ports carry CoffeeBeans, but at different stages. The beans that enter are whole; the grounds that exit have been processed. For now, both are typed as CoffeeBeans. In a more detailed model, you might define separate item types for whole beans and ground beans. Keep it simple until the model demands more precision.

Brewer: Water and ground coffee go in; brewed coffee comes out.

port def WaterPort {
    in item water : Water;
}

port def BrewOutputPort {
    out item coffee : BrewedCoffee;
}

WaterTank: Water goes out (to the brewer).

port def WaterSupplyPort {
    out item water : Water;
}

Notice that WaterPort and WaterSupplyPort both involve Water, but in opposite directions. WaterPort receives water (in); WaterSupplyPort provides water (out). When you connect them in Chapter 6, the directions will match: one outputs what the other inputs.

Adding Ports to Existing Parts

With port definitions in hand, update the part definitions from Chapter 2. You are not redefining these parts – you are extending them with ports:

package CoffeeMachineDomain {
    import ScalarValues::*;

    // Items
    item def Water;
    item def CoffeeBeans;
    item def BrewedCoffee;

    // Enumerations
    enum def GrindSetting {
        fine;
        medium;
        coarse;
    }

    // Port definitions
    port def BeanInputPort {
        in item beans : CoffeeBeans;
    }

    port def GroundCoffeePort {
        out item grounds : CoffeeBeans;
    }

    port def WaterPort {
        in item water : Water;
    }

    port def WaterSupplyPort {
        out item water : Water;
    }

    port def BrewOutputPort {
        out item coffee : BrewedCoffee;
    }

    // Parts with ports
    part def Grinder {
        attribute grindSize : GrindSetting;

        port beanInput : BeanInputPort;
        port groundOutput : GroundCoffeePort;
    }

    part def Brewer {
        attribute waterTemp : Real;
        attribute brewPressure : Real;

        port waterInlet : WaterPort;
        port groundInput : BeanInputPort;
        port brewOutput : BrewOutputPort;
    }

    part def WaterTank {
        attribute currentLevel : Real;
        attribute maxCapacity : Real;

        port waterOut : WaterSupplyPort;
    }

    part def CoffeeMachine {
        part grinder : Grinder;
        part brewer : Brewer;
        part waterTank : WaterTank;
        attribute serialNumber : String;
    }
}

Look at what the model says now. The Grinder accepts beans through beanInput and emits grounds through groundOutput. The Brewer receives water and grounds, then outputs brewed coffee. The WaterTank supplies water. Every boundary is named, typed, and directional.

Notice that Brewer.groundInput reuses BeanInputPort. That is the same port definition used by Grinder.beanInput. Reuse is the point of port definitions: define the interface once, use it on any part that needs it. The Brewer accepts beans (ground) through the same type of input port as the Grinder, because structurally the interface is the same – coffee material flowing inward.

This general view shows the structural definitions with their ports and typing relationships:

Ports and interfaces — expand definitions to see port directions and types.

sysml-rs extends the spec here. Container nodes in this book’s diagram fixtures (a part def with attributes inside, a port with a typed item flowing through it) can be expanded or collapsed. A collapsed node shows only its name and outline; an expanded node shows its members. The expand/collapse contract is a sysml-rs presentation convention — the spec graphical notation does not prescribe it. Reach for collapsed nodes when you want a high-level architectural read; expand them when you want the full attribute and port inventory.

Enumerations: A Brief Recap

Chapter 2 introduced enum def GrindSetting with three values. Enumerations are attribute definitions with a fixed set of members. A few additional patterns are worth knowing.

Enumeration values can carry data. The data is declared once, on an attribute definition, and each value fills it in:

attribute def BrewRatio {
    attribute ratio : Real;
}

enum def BrewStrength :> BrewRatio {
    enum light  { :>> ratio = 15.0; }
    enum medium { :>> ratio = 12.0; }
    enum strong { :>> ratio = 10.0; }
}

BrewStrength specializes BrewRatio, so it inherits ratio; each value then redefines ratio with its own number. Any attribute typed by BrewStrength can be asked for its ratio, because there is one ratio feature rather than three.

Resist the temptation to declare attribute ratio : Real inside the enumeration definition itself. Setting aside annotations – a doc or a comment is always welcome – an enumeration definition’s body holds only its enumeration values (SysML §7.8.2). The definition is a set of alternatives, not a place to hang features. Declaring a fresh ratio inside each value instead would parse, but it would give you three unrelated attributes and nothing common to query.

You can reference enum members using qualified names:

BrewStrength::strong
GrindSetting::fine

This is the same :: scoping syntax you use with packages and imports. An enum definition is a namespace; its members are named elements inside that namespace.

For most modeling work, simple enumerations like GrindSetting are sufficient. Use attributed enumerations when the domain genuinely needs data attached to each variant.

Multiplicity

So far, every usage in the model means “exactly one.” The coffee machine has one grinder, one brewer, one water tank. But real systems are not always that simple. A machine might have multiple heating elements, zero or more optional accessories, or a sensor array with a specific count.

Multiplicity declares how many instances of a usage can exist. You write it in square brackets, most commonly right after the type – though the specification also allows it directly after the name, or after a :> or :>> clause, and only one multiplicity per usage (SysML §7.6.3):

part def Brewer {
    attribute waterTemp : Real;
    attribute brewPressure : Real;
    part heatingElement : HeatingElement [2];

    port waterInlet : WaterPort;
    port groundInput : BeanInputPort;
    port brewOutput : BrewOutputPort;
}

part heatingElement : HeatingElement [2] says: every Brewer contains exactly two heating elements. Not one, not three – two.

Multiplicity Notation

The general form is [lower..upper]. Common patterns:

NotationMeaning
[1]Exactly one (common explicit singleton intent)
[2]Exactly two
[0..1]Optional – zero or one
[1..*]One or more
[0..*]Any number, including zero
[2..4]Between two and four

The * means “unbounded.” [0..*] is the most permissive multiplicity: any number of instances.

When You Omit Multiplicity

If you write part grinder : Grinder with no brackets, you almost always get [1..1] anyway. The specification declares an implicit [1..1] when all three of these hold (SysML §7.6.3):

  • the usage is an attribute usage, an item usage (including a part usage, but not a connection usage), or a port usage;
  • it is owned by a definition or another usage – not directly by a package;
  • and it declares no subsetting or redefinition of its own.

Every usage in the coffee machine model meets all three, so the model already means “exactly one” everywhere. When those conditions do not hold – a usage sitting directly in a package, or one that subsets an inherited collection – the usage takes the bounds of whatever it subsets or redefines, falling back to the most general [0..*] if nothing tighter is inherited.

Writing [1] therefore changes nothing about the meaning. It makes the intent visible:

part def CoffeeMachine {
    part grinder : Grinder [1];
    part brewer : Brewer [1];
    part waterTank : WaterTank [1];
}

This is more verbose but leaves nothing to a reader’s memory of the default rule. Use explicit multiplicity when the count matters for analysis, and leave it off when “one” is obvious from context.

Multiplicity on Attributes and Ports

Multiplicity is not limited to parts. Attributes and ports support it too:

part def SensorArray {
    attribute readings : Real [0..*];
    port outlets : PowerOutlet [6];
}

readings carries zero or more values. outlets provides exactly six connection points. You will not need multi-valued attributes or ports for the coffee machine, but the syntax is the same wherever multiplicity appears.

Attribute Definitions and Default Values

Chapter 2 used Real, String, and GrindSetting as attribute types. Two additional patterns are worth knowing before you see the full model.

Attribute definitions create reusable value types, the same way part def creates reusable component types:

attribute def TemperatureRange {
    attribute min : Real;
    attribute max : Real;
}

Then use it on a part: attribute operatingTemp : TemperatureRange. This bundles related properties into a single named attribute instead of scattering loose fields.

Values attach a number to an attribute, and there are three ways to do it that mean three different things:

part def WaterTank {
    attribute currentLevel : Real := 0.0;
    attribute maxCapacity : Real default = 1500.0;
    attribute nominalPressure : Real = 1.0;

    port waterOut : WaterSupplyPort;
}
  • := is an initial value. currentLevel starts at zero and changes as the tank fills.
  • default = is an overridable default. maxCapacity is 1500.0 unless a particular usage says otherwise. Writing default 1500.0, with the = elided, means the same thing.
  • A bare = is a binding. It asserts that the attribute is that value, for every instance, at all times.

The bare = is the one that catches people out. attribute currentLevel : Real = 0.0 does not say “starts empty” – it asserts the level is always zero (SysML §7.6.3). Reach for = when the value is a genuine constant of the type, := when it is a starting point, and default = when a usage should be free to override it. For computed values derived from other properties, see Chapter 9.

The Complete Model So Far

Here is the full coffee machine model after this chapter. Everything from Chapter 2 is preserved; ports, port definitions, and explicit multiplicity are the additions:

package CoffeeMachineDomain {
    import ScalarValues::*;

    // Items: things that flow through the system
    item def Water;
    item def CoffeeBeans;
    item def BrewedCoffee;

    // Enumerations
    enum def GrindSetting {
        fine;
        medium;
        coarse;
    }

    // Port definitions: typed interaction boundaries
    port def BeanInputPort {
        in item beans : CoffeeBeans;
    }

    port def GroundCoffeePort {
        out item grounds : CoffeeBeans;
    }

    port def WaterPort {
        in item water : Water;
    }

    port def WaterSupplyPort {
        out item water : Water;
    }

    port def BrewOutputPort {
        out item coffee : BrewedCoffee;
    }

    // Structural definitions with ports
    part def Grinder {
        attribute grindSize : GrindSetting;

        port beanInput : BeanInputPort;
        port groundOutput : GroundCoffeePort;
    }

    part def Brewer {
        attribute waterTemp : Real;
        attribute brewPressure : Real;

        port waterInlet : WaterPort;
        port groundInput : BeanInputPort;
        port brewOutput : BrewOutputPort;
    }

    part def WaterTank {
        attribute currentLevel : Real := 0.0;
        attribute maxCapacity : Real default = 1500.0;

        port waterOut : WaterSupplyPort;
    }

    part def CoffeeMachine {
        part grinder : Grinder [1];
        part brewer : Brewer [1];
        part waterTank : WaterTank [1];
        attribute serialNumber : String;
    }
}

Count the ports. The grinder has two (beans in, grounds out). The brewer has three (water in, grounds in, coffee out). The water tank has one (water out). Together, these ports define every boundary where material crosses from one subsystem to another.

The CoffeeMachine itself does not have ports yet. That is intentional – the machine’s external interface (where the user puts beans in, where the cup sits) is a design decision for later. Right now, the internal boundaries are what matter for wiring subsystems together.

What Comes Next

The parts have boundaries, but nothing connects them. The grinder’s groundOutput and the brewer’s groundInput are compatible – they both involve CoffeeBeans flowing in matching directions – but the model does not say they are wired together.

Chapter 6 adds connections and interfaces to wire these ports together, completing the structural picture. The ports you defined here are the endpoints; Chapter 6 provides the wires.

Common Mistakes

Forgetting direction on port items. This compiles:

port def WaterPort {
    item water : Water;
}

But without in, out, or inout, tools cannot validate connections or analyze flow direction. Always specify direction.

Defining ports without port definitions. You can write port waterInlet; with no type, but that tells tools nothing about what flows through it. An untyped port is like an untyped attribute – technically valid, practically useless.

Mismatched directions at connection time. If you define WaterPort as in and WaterSupplyPort as in, you cannot connect them – both sides are trying to receive. One side must output what the other inputs. Think of directions as pipe fittings: a supply end connects to a receiving end.

Over-specifying multiplicity. Writing [1] on every single usage adds noise without information when the count is obvious. Use explicit multiplicity when it conveys design intent: [2] for dual heating elements, [0..1] for optional accessories, [1..*] for extensible arrays. Leave it off when “one” is self-evident.

Connections and Interfaces

Your coffee machine has parts and ports. The WaterTank has an outlet, the Brewer has inlets, the Grinder has an output. But right now, nothing is wired together. The ports exist in isolation – interaction points with nothing to interact with.

This chapter connects the machine. You will start with the simplest possible connection, add port-level wiring, define interface contracts, and finally model what actually flows through those wires. By the end, the coffee machine will be a connected system, not just a bag of parts.

The Simplest Connection

A connection in SysML v2 says: these two parts interact. The simplest form names the two ends directly:

part def CoffeeMachine {
    part waterTank : WaterTank;
    part brewer : Brewer;

    connect waterTank to brewer;
}

connect waterTank to brewer creates a connector between the two part usages. It says the water tank and brewer are linked somehow, but it does not say how. No ports are involved. No item type is specified. It is a statement of topology: these two parts interact.

This is useful early in modeling when you want to capture the fact that two subsystems communicate before you have worked out the details. A reviewer can see the interaction structure without getting buried in port definitions.

But there is a problem. The connection says nothing about where on each part the interaction happens, or what moves between them. For a coffee machine, that matters. Water enters the brewer through a specific inlet, not through the grinder’s bean hopper. To express that, you need port-level connections.

Connecting Through Ports

In Chapter 5, you added ports to the coffee machine’s parts. The WaterTank has a waterOut port that sends Water. The Brewer has a waterInlet that receives Water and a groundInput port that receives ground CoffeeBeans. The Grinder has a groundOutput that sends ground CoffeeBeans.

Now connect them at the port level:

part def CoffeeMachine {
    part waterTank : WaterTank;
    part brewer : Brewer;
    part grinder : Grinder;

    connect waterTank.waterOut to brewer.waterInlet;
    connect grinder.groundOutput to brewer.groundInput;
}

This is more precise. connect waterTank.waterOut to brewer.waterInlet says: the water tank’s waterOut port is connected to the brewer’s waterInlet port. The dot notation navigates from the part usage to its port.

Two things changed from the simple form:

  1. Each end is a port, not a part. The connection targets the specific interaction point on each component.
  2. Direction matters implicitly. waterOut is typed by WaterSupplyPort, which declares out item water; waterInlet is typed by WaterPort, which declares in item water. The connection goes from a source of Water to a sink of Water. SysML v2 does not force you to declare the direction on the connector itself – the port definitions already carry that information.

This is the form you will use most often. Port-level connections make the interaction topology explicit and checkable. A tool can verify that the types on each end are compatible: an outlet sending Water connects to an inlet receiving Water.

Naming Your Connections

Every connection so far has been anonymous. You can give a connector a name if you want to refer to it later – in constraints, requirements, or documentation:

part def CoffeeMachine {
    part waterTank : WaterTank;
    part brewer : Brewer;
    part grinder : Grinder;

    connection waterLine connect waterTank.waterOut to brewer.waterInlet;
    connection beanFeed connect grinder.groundOutput to brewer.groundInput;
}

waterLine and beanFeed are now named connectors. The names do not change behavior, but they make the model more readable and give you handles to attach flows, constraints, or requirements to specific connections.

Interface Definitions

A connection says two ports are linked. An interface definition says what a valid connection between two kinds of ports looks like. It is a contract.

Suppose you want to formalize what it means to connect a water source to a water consumer. Define an interface:

interface def WaterSupply {
    end supplierPort : WaterSupplyPort;
    end consumerPort : WaterPort;
}

interface def WaterSupply declares a reusable connection contract with two ends. Each end is typed by a port definition. Any connection that matches this pattern – a WaterSupplyPort on one side, a WaterPort on the other – conforms to the WaterSupply interface.

You use the interface when creating the connection:

part def CoffeeMachine {
    part waterTank : WaterTank;
    part brewer : Brewer;

    interface waterLine : WaterSupply {
        end supplierPort ::> waterTank.waterOut;
        end consumerPort ::> brewer.waterInlet;
    }
}

This says: the connection between waterTank.waterOut and brewer.waterInlet is an instance of the WaterSupply interface. The ::> binds each interface end to a specific port usage.

When do you need interface definitions? Not always. For simple models, direct connect statements are fine. Interface definitions earn their keep when:

  • Multiple connections follow the same pattern and you want to enforce consistency.
  • You need to attach shared constraints or metadata to a class of connections.
  • You are modeling a system where the interface contract matters independently of who implements it – like a standardized connector.

Item Flows

Connections and interfaces describe the topology: what is linked to what. Item flows describe the content: what actually moves through a connection.

You already defined item types in Chapter 2 – Water, CoffeeBeans, BrewedCoffee. An item flow says that a specific item type travels along a specific connection:

part def CoffeeMachine {
    part waterTank : WaterTank;
    part brewer : Brewer;
    part grinder : Grinder;

    connection waterLine connect waterTank.waterOut to brewer.waterInlet;
    connection beanFeed connect grinder.groundOutput to brewer.groundInput;

    flow of Water from waterTank.waterOut to brewer.waterInlet;
    flow of CoffeeBeans from grinder.groundOutput to brewer.groundInput;
}

flow of Water from waterTank.waterOut to brewer.waterInlet says: Water moves from the tank’s outlet to the brewer’s inlet. The of Water clause types the flow. The from ... to ... clause identifies the source and destination.

Item flows serve a different purpose than connections. A connection says two ports are linked. A flow says something specific travels through that link. You can have a connection without a flow (the link exists, but you have not specified what moves), and in principle you can have multiple flows on the same connection (water and dissolved minerals, say).

In practice, most connections carry one primary flow, and you will often write both the connection and the flow together. The flow is what makes reviewers confident the model is complete: not just “these parts are linked” but “Water moves from here to there.”

Named Flows

Like connections, flows can be named:

flow hotWater of Water from waterTank.waterOut to brewer.waterInlet;
flow groundBeans of CoffeeBeans from grinder.groundOutput to brewer.groundInput;

Named flows are useful when you need to reference them in constraints or requirements. For example, a requirement might say “the hotWater flow shall not exceed 95 degrees Celsius.” Without a name, you have nothing to point at.

Flow Definitions

Every flow so far has been a usage written directly where it happens. Flows have definitions too, the same way parts and actions do – and if you find yourself writing the same of <Item> from <port> to <port> shape repeatedly, that is the signal to define it once.

flow def <Name> {
    end <name> : <SourcePortType>;
    end <name> : <TargetPortType>;
}
flow def WaterFlow {
    end supply : WaterSupplyPort;
    end intake : WaterPort;
}

part def CoffeeMachine {
    part waterTank : WaterTank;
    part brewer : Brewer;

    flow supplyLine : WaterFlow from waterTank.waterOut to brewer.waterInlet;
}

A flow definition is a reusable kind of flow. Its end members say what the two sides must be – here, that a WaterFlow always runs from something with a WaterSupplyPort to something with a WaterPort. The usage then names the particular flow and says which ports it connects.

Read it as: WaterFlow is the kind of movement; supplyLine is this movement, in this machine.

This is the def/usage split from Chapter 3 applied to flows, and it buys the same things it always does. The definition is one place to document what the flow means, one place to attach a constraint on its rate or temperature, and one thing a requirement can point at for every machine rather than for one instance.

The coffee machine’s own model defines WaterFlow, CoffeeFlow and SteamFlow this way, and then declares usages against them.

Flows that also order

part def CoffeeMachine {
    succession flow waterThenCoffee of Water
        from waterTank.waterOut to brewer.waterInlet;
}

A succession flow does two jobs at once: it carries something, and it constrains time. The plain flow says water moves from the tank to the brewer. succession flow adds that the source end completes before the target end begins – the ordering meaning you met as first ... then in Chapter 7, attached to a flow rather than to two actions.

Reach for it when the sequence matters as much as the transfer: the tank finishes delivering before the brewer starts drawing, rather than both being live at once.

Binding Connectors

There is one more kind of connector you should know about: the binding connector. A regular connection says two separate things interact. A binding says two things are the same thing, seen from different contexts.

Suppose you expose a port on the CoffeeMachine itself, and you want to bind it to an internal part’s port:

part def Brewer {
    port waterInlet : WaterPort;
    port groundInput : BeanInputPort;
    port brewOutput : BrewOutputPort;
}

part def CoffeeMachine {
    port dispenserOut : BrewOutputPort;

    part brewer : Brewer;

    bind dispenserOut = brewer.brewOutput;
}

bind dispenserOut = brewer.brewOutput says: the machine’s dispenserOut port is the brewer’s brewOutput port. They are not separate endpoints with something flowing between them. They are the same port, exposed at two levels of the hierarchy. What appears on brewer.brewOutput appears on dispenserOut, by identity.

Bindings are how you thread internal ports up to an external interface. If the CoffeeMachine is used inside a larger system – a cafe counter, say – that outer system connects to dispenserOut without knowing about the brewer inside.

Allocation

Every relationship so far has been structure-to-structure: this port talks to that port, this item moves along that link. Allocation is the one that crosses between models. It says a behavioral element is the responsibility of a structural one – this step of the brew cycle is the grinder’s job.

You need it because the two decompositions are deliberately independent (Chapter 7 makes that point about actions). BrewCycle breaks into grind, heat, and extract; CoffeeMachine breaks into grinder, waterTank, and brewer. Nothing in either decomposition says which part does which step. Allocation is where you write that down:

part def CoffeeMachine {
    part grinder : Grinder;
    part brewer : Brewer;
    part waterTank : WaterTank;

    perform action brewCycle : BrewCycle;

    // Commit each step of the cycle to the part responsible for it
    allocate brewCycle.grind to grinder;
    allocate brewCycle.heat to waterTank;
    allocate brewCycle.extract to brewer;
}

allocate brewCycle.grind to grinder says: whatever the grind step requires, the grinder is what has to deliver it. That is a claim a reviewer can check. If grind needs 200 W and the grinder is specified at 150 W, the allocation is where the mismatch surfaces – not in a meeting six months later. Allocations are also queryable in both directions: “what does the brewer have to do?” and “who is responsible for heating?” both have answers in the model.

When you need to attach anything to the allocation itself – a rationale, a margin, a review status – name it and give it a definition, exactly as with connections:

allocation def FunctionToComponent;

part def CoffeeMachine {
    part grinder : Grinder;
    perform action brewCycle : BrewCycle;

    allocation grindAlloc : FunctionToComponent allocate brewCycle.grind to grinder;
}

Reach for the bare allocate when the mapping is the whole story, and the named form when the mapping itself needs properties. Allocation is not limited to behavior-to-structure: the same construct maps requirements to components, logical designs to physical ones, or software functions to processors. The pattern is always “this element is realized by that one”.

The Connected Coffee Machine

Here is the coffee machine with all of its connections and flows in one place. This builds on the ports from Chapter 5 and the definitions from Chapter 2:

package CoffeeMachineConnections {
    import CoffeeMachineDomain::*;

    part def CoffeeMachine {
        // Internal parts
        part waterTank : WaterTank;
        part brewer : Brewer;
        part grinder : Grinder;

        // External port
        port dispenserOut : BrewOutputPort;

        // Port-level connections
        connection waterLine connect waterTank.waterOut to brewer.waterInlet;
        connection beanFeed connect grinder.groundOutput to brewer.groundInput;

        // Typed flows -- what moves through each connection
        flow hotWater of Water from waterTank.waterOut to brewer.waterInlet;
        flow groundBeans of CoffeeBeans from grinder.groundOutput to brewer.groundInput;

        // Binding -- expose brewer output as machine output
        bind dispenserOut = brewer.brewOutput;
    }
}

Read this model from top to bottom. The parts tell you what the machine is made of. The connections tell you how the parts are wired. The flows tell you what moves through those wires. The binding tells you where the output appears to the outside world.

A reviewer looking at this model can answer: What are the subsystems? How do they connect? What flows between them? Where does the product come out? That is a complete structural interaction picture, and it took about 15 lines.

The Progression

This chapter followed a deliberate progression that mirrors how you should model connections in practice:

  1. Simple connect. connect tank to brewer – capture the topology before the details.
  2. Port-level connect. connect waterTank.waterOut to brewer.waterInlet – specify exactly where interaction happens.
  3. Named connection. connection waterLine connect ... – give handles for later reference.
  4. Interface definition. interface def WaterSupply – formalize reusable connection contracts.
  5. Item flow. flow of Water from ... to ... – specify what moves.
  6. Binding. bind dispenserOut = brewer.brewOutput – expose internal ports to the outside.
  7. Allocation. allocate brewCycle.grind to grinder – commit behavior to the structure responsible for it.

You do not need all seven levels for every connection. Many models get by with port-level connections and typed flows. Interface definitions and bindings appear when the model needs to formalize contracts or manage hierarchy. Start simple and add precision where the design demands it.

Visualizing Connections

This interconnection view shows how the parts connect through their ports, with item flows indicating what travels through each connection:

Interconnection view: parts connected through ports with typed item flows.

This is the Interconnection view — one of the eight standard view definitions covered in Chapter 13. If you are coming from SysML v1, you will recognize the same content under the older labels: a part-level Interconnection is what v1 called an Internal Block Diagram (IBD), and a definition-level Interconnection is what v1 called a Block Definition Diagram (BDD).

sysml-rs extends the spec here. When the connection sits between two ports whose types are quantities from the standard library’s ISQ family — voltage, current, force, velocity, and so on — the simulation runtime treats the connection as a bond in a bond graph, with conservation laws applied automatically. The bond-graph synthesis is a sysml-rs simulation behaviour, not a spec construct; see Appendix E (the physics-aware simulation reference) once you reach the simulation chapters.

Common Mistakes

Connecting definitions instead of usages. This is wrong:

connect WaterTank to Brewer;  // Wrong: these are definitions, not usages

Connections link usages – the specific parts inside a context. You cannot connect WaterTank as a type; you connect waterTank, the specific instance inside CoffeeMachine.

Mismatched port types on a connection. If waterTank.waterOut sends Water but brewer.groundInput expects CoffeeBeans, connecting them is a type error. The port types on each end of a connection should be compatible. This is one of the main benefits of typed ports – the model catches wiring mistakes.

Flows without connections. A flow says what moves, but a connection says what is linked. If you write a flow between two ports that have no connection, you are asserting transfer through a link that does not exist. Always establish the connection first, then add the flow.

Using bind when you mean connect. A binding says two things are identical. A connection says two separate things interact. If the water tank and brewer are separate parts with different ports, you want connect. If you are exposing an internal port at a higher level in the hierarchy, you want bind.

What Comes Next

The coffee machine now has structure, ports, connections, and flows. It is fully wired but entirely static. In Chapter 7, you will add behavior – the brew cycle that moves water through the tank, grinds beans, and produces coffee. The connections you built here become the pathways that behavior uses.

Actions

Up to this point, the coffee machine model describes what the system is – parts, attributes, ports, connections. But a machine that only has structure does nothing. This chapter adds behavior: what the system does when someone presses the brew button.

In SysML v2, behavior is modeled with actions. An action definition describes a reusable unit of behavior, the same way a part definition describes a reusable structural component. Actions compose into workflows through sequencing, parallelism, branching, and signaling.

Defining an Action

An action definition declares a kind of behavior. It follows the same def pattern you have seen with parts and items:

action def GrindBeans;

That says: there is a kind of behavior called GrindBeans. It does not say when or where it happens. It is a type, not an occurrence.

To say that something performs this behavior, you create an action usage inside another action:

action def GrindBeans;
action def HeatWater;

action def BrewCycle {
    action grind : GrindBeans;
    action heat : HeatWater;
}

action grind : GrindBeans says: inside a BrewCycle, there is a step called grind, and it is of type GrindBeans. This is the same definition/usage pattern from Chapter 2, applied to behavior instead of structure.

Without anything else, the two sub-actions have no defined order. SysML v2 does not assume sequential execution. You have to say it.

Sequential Composition

The first ... then statement declares that one action must complete before another begins:

action def BrewCycle {
    action grind : GrindBeans;
    action heat : HeatWater;

    first grind then heat;
}

first grind then heat creates a succession – an ordering constraint that says grind finishes before heat starts. This is a directed relationship between two action usages, not a property of either action by itself.

You can chain successions to build longer sequences:

action def BrewCycle {
    action grind : GrindBeans;
    action heat : HeatWater;
    action extract : ExtractCoffee;
    action dispense : DispenseCoffee;

    first grind then heat;
    first heat then extract;
    first extract then dispense;
}

That reads: grind, then heat, then extract, then dispense. Each first ... then is a separate succession statement. Together they form a chain.

Every action has two implicit endpoints: start and done. You can anchor your sequence to them:

action def BrewCycle {
    action grind : GrindBeans;
    action heat : HeatWater;
    action extract : ExtractCoffee;
    action dispense : DispenseCoffee;

    first start then grind;
    first grind then heat;
    first heat then extract;
    first extract then dispense;
    first dispense then done;
}

first start then grind says that grind is the first thing that happens when a BrewCycle begins. first dispense then done says the cycle finishes after dispensing completes. These endpoints make the boundary of the action explicit – a reviewer can see exactly where behavior begins and ends.

You can also chain successions on a single line using the then keyword after an action usage:

action def BrewCycle {
    action grind : GrindBeans;
    then action heat : HeatWater;
    then action extract : ExtractCoffee;
    then action dispense : DispenseCoffee;
}

The then before an action usage is shorthand: it creates a succession from the previously declared action to the current one. This form is more compact but means the same thing as writing separate first ... then statements.

Naming a succession

Everything so far has written the ordering inline, inside the action body. There is a second form that names it:

succession [<name>] [first] <source> then <target>;
action def BrewCycle {
    action grind : GrindBeans;
    action heat : HeatWater;
    action extract : Extract;

    succession grindDone first grind then heat;
    succession heat then extract;
}

Read the first one as: this ordering constraint is called grindDone, and it says grind comes before heat.

Both lines declare the same kind of relationship the bare first ... then does. What the keyword buys you is a name, and a named succession is a model element like any other – you can document it, refer to it from a requirement, or allocate it. That is why larger models tend to use this form for the orderings that matter and the bare form for the rest.

Note that first is optional here. succession heat then extract; and succession first heat then extract; mean the same thing – the specification says so directly: “If a succession declaration includes only the related features part, then the keyword first can be omitted” (KerML §7.4.6.4). The coffee machine’s own orchestration model uses the shorter spelling throughout.

One more thing that clause tells you, and it is worth knowing because it explains what a succession actually is: if you give a succession no explicit type, it is implicitly typed by the library association HappensBefore, which lives in the standard library’s Occurrences package. An ordering constraint is not a special syntactic device bolted onto actions – it is a connector, typed from the standard library, whose two ends happen to be things ordered in time.

Action Parameters

Actions often need inputs and produce outputs. You declare these with in and out parameters:

action def GrindBeans {
    in item beans : CoffeeBeans;
    out item grounds : GroundCoffee;
}

in item beans : CoffeeBeans says that GrindBeans receives coffee beans as input. out item grounds : GroundCoffee says it produces ground coffee as output. The item keyword here means the parameter carries an item – something that flows through the system.

Parameters make the interface of an action explicit. When you compose actions, the outputs of one step can connect to the inputs of the next:

action def GrindBeans {
    in item beans : CoffeeBeans;
    out item grounds : GroundCoffee;
}

action def ExtractCoffee {
    in item grounds : GroundCoffee;
    in item water : HotWater;
    out item coffee : BrewedCoffee;
}

A reviewer can see at a glance what each step needs and what it produces, without reading the internals.

Parallel Composition with Fork and Join

Not everything in a workflow happens one step at a time. In the coffee machine, heating water and grinding beans can happen simultaneously – the grinder and the heater are independent subsystems.

SysML v2 models parallelism with fork and join nodes. A fork splits one flow into multiple concurrent paths. A join waits for all concurrent paths to complete before continuing.

action def BrewCycle {
    action grind : GrindBeans;
    action heat : HeatWater;
    action extract : ExtractCoffee;
    action dispense : DispenseCoffee;

    first start then fork forkNode;
        first forkNode then grind;
        first forkNode then heat;
    first grind then join joinNode;
    first heat then joinNode;
    first joinNode then extract;
    first extract then dispense;
    first dispense then done;
}
Action flow: fork, parallel grind/heat, join, extract, dispense. Pan and zoom to explore.

Here is what happens at runtime:

  1. The cycle starts.
  2. forkNode splits execution into two concurrent paths.
  3. grind and heat run in parallel.
  4. joinNode waits until both grind and heat finish.
  5. extract runs after the join.
  6. dispense runs after extraction.
  7. The cycle ends.

The fork keyword declares a fork node – a control node with no behavior of its own. Its job is to start multiple paths at the same time. The join keyword declares a join node that synchronizes those paths back together. Think of a fork as a split and a join as a rendezvous.

Fork and join nodes are not actions with duration. They are instantaneous control points. The real work happens in the action usages (grind, heat, extract, dispense). The control nodes just direct traffic.

Decisions and Merges

Sometimes a workflow needs to choose between paths based on a condition. SysML v2 models this with decide and merge nodes.

A decision node picks one outgoing path. A merge node brings multiple alternative paths back together. Where fork/join models “do all of these,” decide/merge models “do one of these.”

Suppose the coffee machine can make either espresso or drip coffee, depending on a setting:

action def BrewCycle {
    in attribute espressoMode : Boolean;

    action grind : GrindBeans;
    action heat : HeatWater;
    action espressoExtract : ExtractEspresso;
    action dripExtract : ExtractDrip;
    action dispense : DispenseCoffee;

    first start then grind;
    first grind then heat;
    first heat then decide decideMethod;
        if espressoMode then espressoExtract;
        else dripExtract;
    first espressoExtract then merge mergeMethod;
    first dripExtract then mergeMethod;
    first mergeMethod then dispense;
    first dispense then done;
}

After heating, the decideMethod node evaluates the condition. If espressoMode is true, execution follows the path to espressoExtract. Otherwise, it follows the path to dripExtract. Only one path runs.

The mergeMethod node reunites the two alternatives. Whichever extraction method was chosen, execution continues to dispense after the merge. The merge does not wait for both paths – only one path is active, and the merge simply accepts whichever one arrives.

This is the key difference between fork/join and decide/merge:

  • Fork/join: all paths execute concurrently, join waits for all.
  • Decide/merge: one path executes based on a condition, merge accepts whichever arrives.

The guard condition (if espressoMode) is attached to the outgoing succession from the decision node. The else keyword marks the default path taken when no guard is satisfied.

Combining Control Flow

Real workflows combine sequencing, parallelism, and decisions. Here is a more complete brew cycle that uses all three:

package CoffeeMachineActions {
    import ScalarValues::*;
    import CoffeeMachineDomain::*;

    action def GrindBeans {
        in item beans : CoffeeBeans;
    }

    action def HeatWater {
        in item water : Water;
    }

    action def ExtractEspresso;
    action def ExtractDrip;

    action def DispenseCoffee {
        out item coffee : BrewedCoffee;
    }

    action def BrewCycle {
        in attribute espressoMode : Boolean;
        in item beans : CoffeeBeans;
        in item water : Water;
        out item coffee : BrewedCoffee;

        action grind : GrindBeans {
            in item beans = BrewCycle::beans;
        }

        action heat : HeatWater {
            in item water = BrewCycle::water;
        }

        action espressoExtract : ExtractEspresso;
        action dripExtract : ExtractDrip;

        action dispense : DispenseCoffee {
            out item coffee = BrewCycle::coffee;
        }

        // Grind and heat in parallel
        first start then fork forkPrep;
            first forkPrep then grind;
            first forkPrep then heat;
        first grind then join joinPrep;
        first heat then joinPrep;

        // Choose extraction method
        first joinPrep then decide decideMethod;
            if espressoMode then espressoExtract;
            else dripExtract;
        first espressoExtract then merge mergeMethod;
        first dripExtract then mergeMethod;

        // Dispense and finish
        first mergeMethod then dispense;
        first dispense then done;
    }
}
General view: all action definitions expanded. Click to zoom and explore.

Read the control flow section from top to bottom and you get the full story: the cycle starts by forking into parallel preparation (grind and heat). After both finish, a decision selects the extraction method. After extraction, the results merge and the coffee is dispensed.

The action parameters (in item beans, out item coffee) make the data flow explicit alongside the control flow. The binding in item beans = BrewCycle::beans connects the sub-action’s input to the enclosing action’s parameter. This is how data threads through a composed workflow.

Send and Accept

Actions can communicate with each other through signals. The send action transmits a signal, and the accept action waits for one to arrive. This is how you model event-driven behavior – one part of the system notifying another that something happened.

action def NotifyReady {
    send BrewComplete() to display;
}

send BrewComplete() to display says: transmit a BrewComplete signal to the display port (or part). The signal is an item that carries information about an event.

On the receiving side:

action def WaitForBrew {
    accept brewDone : BrewComplete;
}

accept brewDone : BrewComplete says: pause and wait until a BrewComplete signal arrives. When it does, execution continues and the signal is available as brewDone.

You can use accept inside an action body to model a step that blocks until an event occurs:

action def ServeCustomer {
    accept orderReceived : BrewRequest;
    then action brew : BrewCycle;
    then action notify send BrewComplete() to customerDisplay;
}

This reads: wait for a brew request, then perform the brew cycle, then notify the customer display. The accept at the beginning means the action does not start its real work until the triggering event arrives.

Routing a message through a specific port

When a part has more than one port, a plain send ... to <part> leaves the channel unspecified. The via keyword routes a signal along a particular port so the model says which connection carries it:

action def RouteSignalThroughPort {
    send LowWaterSignal() via communicationPort;
}

send LowWaterSignal() via communicationPort says: route the LowWaterSignal through the communicationPort, not whichever other port the part happens to have. This matters when a part exposes several ports and the routing decision is part of the model, not left to the runtime.

On the receiving side you can select an incoming channel with via as well:

action def RouteAcceptThroughPort {
    accept alert : FaultSignal via communicationPort;
}

accept alert : FaultSignal via communicationPort says: wait for a FaultSignal arriving on the communicationPort. The via keyword selects which incoming channel the message comes from, so an accept with a port filter only fires for messages on that port.

Send and accept are the bridge between structure and behavior. The structural model defines ports and connections (Chapter 6). The behavioral model uses send and accept to say what signals travel through those connections and when.

Connecting Actions to Structure

An action definition describes behavior in the abstract. To say which part of the system performs it, you use perform inside a part definition:

part def CoffeeMachine {
    part grinder : Grinder;
    part brewer : Brewer;
    part waterTank : WaterTank;

    perform action brewCycle : BrewCycle;
}

perform action brewCycle : BrewCycle says: a CoffeeMachine performs a BrewCycle. This connects the structural model (what the machine is made of) with the behavioral model (what the machine does). You can also allocate individual sub-actions to specific parts, saying that the grinder performs the grinding and the brewer performs the extraction – that is what allocate is for, covered in Chapter 6.

This separation is deliberate. The action decomposition (BrewCycle contains grind, heat, extract, dispense) is independent of the structural decomposition (CoffeeMachine contains grinder, brewer, waterTank). You can change the action decomposition without touching the structure, and vice versa. The perform relationship is the bridge between the two.

Actions That Modify State

So far every action the coffee machine has performed only reads its inputs and produces outputs – the model describes data flowing through the workflow, but nothing changes a value mid-flight. Real behavior is messier: an action increments a brew counter, flips a mode flag, or aborts a cycle when a sensor trips. SysML v2 has constructs for all three – assignments, bidirectional parameters, loops, and early termination – and they all live inside action bodies, the same bodies you have been writing.

Assignment

An assignment mutates a value as the workflow runs. It binds an expression to an attribute or a parameter reference with the := operator:

assign <target> := <expression>;

Here is a brew cycle that records how many brews it has run:

action def BrewCycle {
    inout attribute brewCycles : Integer;

    assign brewCycles := brewCycles + 1;
}

assign brewCycles := brewCycles + 1 says: take the current value of brewCycles, add one to it, and write the result back. This is an assignment – it runs when the action body executes, not when the model is parsed. It is different from declaring an attribute with an initial value (which is set once when the part is created), and different from a calc (which is a pure function of its inputs). An assignment is a side effect on a binding the caller owns.

That is why the target has to be something the action can reach back out to. Writing to a plain in parameter would be pointless – the caller would never see the change. The next section makes the mechanics explicit.

inout parameters

Parameters come in three directions. in carries a value into the action; out carries a value out of it. inout does both: the caller supplies an initial value, the action can read it and write it back:

action def ToggleMode {
    inout attribute espressoMode : Boolean;
}

inout attribute espressoMode : Boolean says: the caller hands the action the current value of espressoMode, and any change the action makes is visible to the caller when the action completes. Inside the action body, espressoMode reads like an in and writes like an out.

Use inout whenever the natural model of the behavior is “update this thing in place” rather than “take an old value, return a new one”. A mode toggle, a running counter, a shared status flag – all of these want inout, because the same binding carries the value in and the updated value out.

While loops

Not every workflow is a fixed sequence of steps. Sometimes you need to keep doing something as long as a condition holds. A while loop runs its body repeatedly until a guard becomes false:

while <condition> { ... }

Stir the water until it reaches the target temperature:

action def HeatAndStir {
    in  attribute targetTemp : Real;
    inout attribute currentTemp : Real;

    while currentTemp < targetTemp {
        action stir : Stir;
        assign currentTemp := currentTemp + 1.0;
    }
}

while currentTemp < targetTemp { ... } says: as long as currentTemp is below targetTemp, keep running the actions inside the braces. The condition is evaluated at the start of each iteration; when it becomes false, the loop exits and execution continues after the closing brace.

Reach for while when the number of iterations is not known up front – a polling loop, a feedback controller, a “keep trying until the sensor is happy” pattern. If you already know how many times to run, the next loop is clearer.

For loops

When you have a collection and want to do something once per element, a for loop iterates over it:

for <item> in <collection> { ... }

Process each brew request in a queue:

action def ProcessBrewQueue {
    in  attribute brewRequests : BrewRequest[0..*];
    out attribute successCount : Integer;

    assign successCount := 0;

    for request in brewRequests {
        action brew : BrewCycle {
            in item beans = request.beans;
        }
        assign successCount := successCount + 1;
    }
}

for request in brewRequests { ... } says: bind request to each element of brewRequests in turn and run the body once per element. Inside the body, request refers to the current element. The loop runs over the whole sequence; when it is done, execution continues after the loop.

for is the loop to reach for when the iteration is driven by data – one pass per item in a collection – rather than by a runtime condition. It keeps the “does this run the right number of times?” question off the reviewer’s plate: the answer is “once per element”.

A tool caveat. sysml-rs currently reports AX009 -- for loop has no collection reference on every for loop, including this one. The construct is specification syntax and the example is correct; the tool is not yet resolving the collection. If you see AX009, you have not mistyped anything.

Terminate

A workflow should be able to abort early when something goes wrong. The terminate action stops the enclosing action immediately and returns control to the caller:

terminate;

Abort a brew if the temperature is unsafe:

action def BrewCycle {
    in attribute temperature : Real;

    if temperature > 105.0 {
        send FaultSignal() to panelPort;
        terminate;
    }

    then action extract : ExtractCoffee;
}

terminate; says: stop this action right now. No further actions in the current scope run – the extract step below the guard never executes. The fault signal has already been sent; the caller is handed back control with the action in an aborted state.

Use terminate for hard cutoffs: a safety interlock that must not let the rest of the cycle proceed, a validation failure that makes the rest of the work meaningless. It is the action equivalent of an early return – reach for it when nothing later in the body should run.

The Story So Far

The coffee machine now has behavior. Here is what the model captures after this chapter:

  • Action definitions for each step: GrindBeans, HeatWater, ExtractEspresso, ExtractDrip, DispenseCoffee.
  • A composed workflow (BrewCycle) that sequences, parallelizes, and branches those steps.
  • Parameters that thread data (beans, water, coffee) through the workflow.
  • Control flow with fork/join for parallelism and decide/merge for branching.
  • Send and accept for event-driven communication between parts.
  • Message routing with via <port> to direct signals through a specific channel.
  • Assignment (:=) and inout parameters for state a workflow mutates in place.
  • Loopswhile for condition-driven repetition, for for collection iteration.
  • Terminate for an early, clean abort of an action body.
  • Perform to connect actions to the parts that execute them.

The next chapter, Chapter 8, builds on this foundation. Where actions describe what the machine does during a single brew, states describe which mode the machine is in over time: idle, brewing, cleaning, error. Actions will appear again as entry, do, and exit behaviors attached to states.

Common Mistakes

Forgetting to sequence sub-actions. If you write two action usages without any first ... then between them, SysML v2 does not assume they run in order. They have no defined ordering at all. Always add explicit successions.

Confusing fork/join with decide/merge. A fork starts all outgoing paths concurrently. A decision picks exactly one. If you use a fork where you meant a decision, your model says the system does both things at the same time – which may not be what you intended. Likewise, a join waits for all incoming paths. A merge just accepts whichever single path arrives. Mixing them up changes the semantics of your workflow.

Writing one massive action body. It is tempting to put all behavior into a single action definition with dozens of sub-actions and complex control flow. Break large actions into smaller named definitions. Each definition should represent a meaningful step that can be reviewed and discussed independently. Compose them in a top-level action the way you compose parts in a top-level structure.

States

Your coffee machine has structure – parts, ports, connections – and behavior – the BrewCycle action that grinds, heats, extracts, and dispenses. But an action describes what happens. It does not describe when the machine is allowed to do it.

A real coffee machine has operating modes. It sits idle until someone presses a button. It brews, then returns to idle. It enters a cleaning cycle periodically. It shuts down into an error mode when something goes wrong. These modes govern which actions are permitted and which transitions between modes are allowed.

SysML v2 models operating modes with states and transitions. This chapter adds a MachineStates state definition to the coffee machine, covering state definitions and usages, transitions with guards and triggers, entry and exit actions, and nested states.

Defining States

A state definition declares a reusable operating mode, the same way a part definition declares a reusable component type. The pattern should look familiar:

state def Idle;
state def Brewing;

These are definitions – blueprints for modes that can be used inside a larger state machine. By themselves, they do not say which system uses them or how they connect.

To build a state machine, you create a composite state definition that contains state usages and transitions between them:

state def MachineStates {
    state idle : Idle;
    state brewing : Brewing;

    transition first idle then brewing;
}

The pattern mirrors what you saw with parts. state def Idle defines a type. state idle : Idle creates a usage of that type inside MachineStates. The transition says: from idle, proceed to brewing.

The keyword first in transition first idle then brewing identifies the source state. The keyword then identifies the target. Read it as: “transition, starting from idle, then go to brewing.”

Building the Coffee Machine States

The coffee machine needs four operating modes: idle (waiting for input), brewing (making coffee), cleaning (maintenance cycle), and error (fault detected). Start with the definitions and a containing state machine:

package CoffeeMachineStates {
    state def Idle;
    state def Brewing;
    state def Cleaning;
    state def Error;

    state def MachineStates {
        state idle : Idle;
        state brewing : Brewing;
        state cleaning : Cleaning;
        state error : Error;

        transition first idle then brewing;
        transition first brewing then idle;
        transition first idle then cleaning;
        transition first cleaning then idle;
    }
}

Here is what this state machine looks like in an interactive state-transition view. Each state is a rounded block, and the arrows show which transitions are possible:

MachineStates: Idle, Brewing, Cleaning, and Error, with their transitions.

This is a valid state machine, but it is too permissive. Nothing prevents the machine from transitioning to brewing when there is no water, or from leaving error without intervention. Every transition fires unconditionally. You need guards.

Guards

A guard is a boolean condition on a transition. The transition only fires when the guard evaluates to true. In SysML v2, you write a guard with the if keyword after the source state:

transition first <source> if <condition> then <target>;

Add guards to the coffee machine transitions. Assume the machine has boolean attributes that represent sensor readings:

package CoffeeMachineStates {
    import ScalarValues::*;

    attribute def StartBrewSignal;
    attribute def StartCleanSignal;

    state def Idle;
    state def Brewing;
    state def Cleaning;
    state def Error;

    state def MachineStates {
        attribute waterAvailable : Boolean;
        attribute cupDetected : Boolean;
        attribute brewComplete : Boolean;
        attribute cleanComplete : Boolean;
        attribute faultDetected : Boolean;

        state idle : Idle;
        state brewing : Brewing;
        state cleaning : Cleaning;
        state error : Error;

        transition first idle
            if waterAvailable and cupDetected
            then brewing;

        transition first brewing
            if brewComplete
            then idle;

        transition first idle
            if cleanComplete == false
            then cleaning;

        transition first cleaning
            if cleanComplete
            then idle;

        transition first idle
            if faultDetected
            then error;

        transition first brewing
            if faultDetected
            then error;
    }
}

Now the idle-to-brewing transition only fires when water is available and a cup is detected. The brewing-to-idle transition only fires when the brew cycle has completed. And any state can reach error when a fault is detected.

Guards make transition intent explicit. A reviewer reading this model can see exactly what conditions must hold for each mode change. This is especially valuable for safety-critical transitions – you do not want the machine to start brewing with no cup in place.

Triggers

Guards check conditions, but they do not say what event initiates the transition. A trigger specifies the event that causes a transition to fire. In SysML v2, triggers use the accept keyword:

transition first <source> accept <event> then <target>;

The event is typically an attribute definition that represents a signal. You define the signal type, then reference it in the transition:

attribute def StartBrewSignal;

state def MachineStates {
    state idle : Idle;
    state brewing : Brewing;

    transition first idle
        accept StartBrewSignal
        if waterAvailable and cupDetected
        then brewing;
}

Read this as: “When the machine is in idle and receives a StartBrewSignal, if water is available and a cup is detected, transition to brewing.”

Triggers and guards work together. The trigger says what event to respond to. The guard says whether the response is allowed. You can have a trigger without a guard (always respond to the event), a guard without a trigger (transition when the condition becomes true), or both.

Time-driven triggers: accept after

Some transitions fire on the passage of time rather than on an external signal. The spec gives you accept after(<duration>) for that case, with duration literals using SI time-units:

transition first heating accept after(30 [s]) then ready;
transition first cooling accept after(500 [ms]) then off;
transition first warming accept after(t_dead) then idle;   // any Real attribute typed as a duration

The unit literal can be s, ms, us, ns, min, h — anything in the Time quantity kind from the ISQ standard library. The duration argument is a real expression: a literal like 30 [s], a model attribute like t_dead, or a calc result.

accept after is one member of a small family of trigger constructors from the spec stdlib (Triggers.kerml). The other two move the triggering event off the passage of time and onto a moment or a condition.

Condition triggers: when

A condition trigger fires when a Boolean condition becomes true, rather than when an external signal arrives. The spec constructor is TriggerWhen; in the textual form you write it on the transition as a when clause:

transition first <source> accept when <condition> then <target>;

Stand down the brewer once the water is hot enough:

transition first brewing accept when waterTemp >= 92.0 then ready;

accept when waterTemp >= 92.0 says: while the machine is in brewing, watch waterTemp; the instant the condition changes from false to true, the transition fires. Note the difference from a guard. A guard (if) is evaluated at the moment a trigger event occurs and decides whether the response is allowed. A when trigger is the event – it watches the condition continuously and fires the transition the moment it becomes true, with no external signal involved.

sysml-rs extends the spec here. The when construct is spec language. What the runtime adds is the precision: when the condition mentions a continuously-varying state variable, the runtime watches that variable and fires the transition the instant the guard becomes true, with sub-tick precision via zero-crossing detection. The spec says the condition must become true; sysml-rs’s runtime says exactly when. See Chapter 16.

Absolute-time triggers: at

after is relative – d time units elapse in the source state. at is absolute – the transition fires at a specific moment on the clock. The spec constructor is TriggerAt:

transition first <source> accept at <time-expression> then <target>;

Start the clean cycle on a schedule:

attribute cleanTime : Time;

transition first idle accept at cleanTime then cleaning;

accept at cleanTime says: when the machine is in idle, fire the transition at the clock instant named by cleanTime. cleanTime is a point on the timeline, not a duration – so at is what you reach for when the trigger is a deadline or a schedule, where after is what you reach for when the trigger is “wait, then go”.

Transitions on port messages

Every trigger so far listens for a signal by name. But the signal can also be a message arriving on a specific port. The via keyword (the same one you saw on action send/accept in Chapter 7) routes the trigger to a particular channel:

transition first <source> accept <name> : <Signal> via <port> then <target>;

Suppose the brewer exposes a dispensePort and emits CupReady on it when a cup is in place. The machine dispenses only when that specific port reports readiness:

port def DispensePort {
    out item ready : CupReady;
}

part def Brewer {
    port dispensePort : DispensePort;
}

transition first brewing
    accept cup : CupReady via brewer.dispensePort
    then dispensing;

accept cup : CupReady via brewer.dispensePort says: wait for a CupReady message arriving on brewer.dispensePort, and bind it to cup when it arrives. The via <port> clause makes the trigger precise about which connection carries the message, instead of listening for any CupReady in the machine’s namespace.

Port-message triggers pay off in component architectures where several parts emit the same signal type on different ports; via is how you say which one this transition is waiting on.

Entry actions as full expressions

The body of an entry or exit action does not have to be a plain reference to an action declared elsewhere. It can be an inline action with a body of its own – an assignment, a nested step, or a sequence of these – so you can drive state-local computation from the state itself rather than through a separate action def.

state Brewing {
    entry action setPower  { assign brewer.heaterPower := 1500.0; }
    exit  action clearPower { assign brewer.heaterPower := 0.0; }
}

When the entry/exit logic is reused across states, lift it into an action def. When it is a single in-place setting or a one-line calc, write it inline.

Entry and Exit Actions

When the machine enters a state, it often needs to do something – turn on a heater, start a timer, display a status message. When it leaves a state, it may need to clean up – turn off a pump, reset a counter. SysML v2 models these with entry and exit actions inside a state.

action def LogMessage;
action def StartHeating;
action def StopHeating;

state def Brewing {
    entry action : StartHeating;
    exit action : StopHeating;
}

The entry action runs every time the machine enters the Brewing state. The exit action runs every time it leaves. These are guarantees: no matter which transition enters Brewing, the heater starts. No matter which transition leaves, the heater stops.

There is also a do action, which runs continuously while the state is active:

action def MonitorTemperature;

state def Brewing {
    entry action : StartHeating;
    do action : MonitorTemperature;
    exit action : StopHeating;
}

The do action is the ongoing behavior of the state. Think of entry as setup, do as the main work, and exit as teardown.

The Full Coffee Machine State Model

Putting it all together – states, transitions with guards and triggers, and entry/exit actions:

package CoffeeMachineStates {
    import ScalarValues::*;

    // Signal definitions for triggers
    attribute def StartBrewSignal;
    attribute def StartCleanSignal;
    attribute def FaultSignal;
    attribute def ResetSignal;

    // Action definitions for entry/exit
    action def LogStatus;
    action def StartHeating;
    action def StopHeating;
    action def RunCleanCycle;
    action def StopCleanCycle;
    action def DisableOutputs;

    // State definitions with entry/exit behavior
    state def Idle {
        entry action : LogStatus;
    }

    state def Brewing {
        entry action : StartHeating;
        exit action : StopHeating;
    }

    state def Cleaning {
        entry action : RunCleanCycle;
        exit action : StopCleanCycle;
    }

    state def Error {
        entry action : DisableOutputs;
    }

    // The composite state machine
    state def MachineStates {
        attribute waterAvailable : Boolean;
        attribute cupDetected : Boolean;
        attribute brewComplete : Boolean;
        attribute cleanComplete : Boolean;

        state idle : Idle;
        state brewing : Brewing;
        state cleaning : Cleaning;
        state error : Error;

        // Start in idle
        entry; then idle;

        // Idle -> Brewing: user requests brew, preconditions met
        transition first idle
            accept StartBrewSignal
            if waterAvailable and cupDetected
            then brewing;

        // Brewing -> Idle: brew cycle finished
        transition first brewing
            if brewComplete
            then idle;

        // Idle -> Cleaning: user requests cleaning
        transition first idle
            accept StartCleanSignal
            then cleaning;

        // Cleaning -> Idle: clean cycle finished
        transition first cleaning
            if cleanComplete
            then idle;

        // Any operational state -> Error: fault detected
        transition first idle
            accept FaultSignal
            then error;

        transition first brewing
            accept FaultSignal
            then error;

        transition first cleaning
            accept FaultSignal
            then error;

        // Error -> Idle: operator resets
        transition first error
            accept ResetSignal
            then idle;
    }
}

Several things to notice:

  • entry; then idle; at the top of MachineStates declares the initial state. When the state machine starts, it enters idle first.
  • Each state definition carries its own entry/exit behavior. Brewing always starts the heater on entry and stops it on exit, regardless of which transition enters or leaves.
  • The FaultSignal transitions are repeated for each source state. In SysML v2, each transition has exactly one source and one target. If three states can reach error, you write three transitions.
  • The ResetSignal transition from error back to idle is the only way out of the error state. This is a deliberate design choice – the operator must explicitly acknowledge and reset the fault.

Nested States

States can contain substates. When the machine is brewing, it might go through sub-modes: heating, extracting, and dispensing. You model this by putting state usages inside a state definition:

state def Brewing {
    entry action : StartHeating;

    state heating;
    state extracting;
    state dispensing;

    entry; then heating;

    transition first heating
        then extracting;

    transition first extracting
        then dispensing;

    exit action : StopHeating;
}

From the outside, the machine is in Brewing. From the inside, it moves through heating, extracting, and dispensing as substates. When the final substate completes, the Brewing state itself completes, and the outer state machine transitions to whatever follows.

Nested states are useful when a single operating mode has internal phases, but the rest of the system only needs to know about the outer mode. Keep nesting shallow – one level of substates covers most real systems. Deep nesting makes models hard to review.

Parallel States

Recall the closing remark from the full model above: “The FaultSignal transitions are repeated for each source state. If three states can reach error, you write three transitions.” That repetition is not just tedious – it is fragile. Add a fourth operational state, and you have to remember to give it a FaultSignal transition too. Miss one, and your machine can sit in that state forever ignoring a fault.

The reason this hurts is that the model treats a fault as something each operational mode has to anticipate individually. But a fault is not a property of brewing or cleaning – it is a property of the machine as a whole, one that should be able to interrupt whichever mode is active. SysML v2 has a construct for exactly this: a state that contains parallel regions – independent sub-machines that run concurrently inside one parent state.

Defining orthogonal regions

A region is written as a state usage nested inside a composite state, just like the nested substates you have already seen. Two things are different: you include more than one, each with its own initial transition, and you mark the parent parallel. That keyword is not decoration, and the specification is explicit about what it switches: with isParallel true the owned states are all performed in parallel, and with it false only one owned state may be performed (SysML §8.3.18.5):

state def <Parent> parallel {
    state <regionA> {
        /* one sub-machine: states + transitions + its own entry; then ... */
    }
    state <regionB> {
        /* a second sub-machine, running concurrently with regionA */
    }
}

The parent state def contains two named sub-machines. Entering the parent enters both; neither waits on the other. Read it as: “while the machine is in <Parent>, region A and region B are each doing their own thing, in parallel.”

One constraint comes with parallel, and the specification names it. Under validateStateDefinitionParallelSubactions (SysML §8.3.18.5): if a state definition is parallel, its owned actions – which includes its owned states – must have no incoming and no outgoing transitions. Every transition a region declares stays inside that region. This is what makes the regions orthogonal – you cannot transition from a substate of one region to a substate of another, which is exactly the independence the next section relies on.

Splitting the fault out of the operational modes

Apply this to the coffee machine. Give MachineStates two regions: an operational region that runs idle, brewing, and cleaning, and a supervision region that just listens for a fault and can move the whole machine to error:

state def MachineStates parallel {
    attribute waterAvailable : Boolean;
    attribute cupDetected : Boolean;

    // Region 1: the operational lifecycle
    state operational {
        state idle : Idle;
        state brewing : Brewing;
        state cleaning : Cleaning;

        entry; then idle;

        transition first idle
            accept StartBrewSignal
            if waterAvailable and cupDetected
            then brewing;
        transition first brewing if brewComplete then idle;
        transition first idle accept StartCleanSignal then cleaning;
        transition first cleaning if cleanComplete then idle;
    }

    // Region 2: fault supervision, running concurrently
    state supervision {
        state armed;
        state error : Error;

        entry; then armed;

        transition first armed accept FaultSignal then error;
        transition first error accept ResetSignal then armed;
    }
}

Now the fault lives in one place. The supervision region has a single FaultSignal transition, regardless of how many operational modes exist in the other region. Add a fifth or sixth operational state to the operational region and you do not touch the fault handling at all.

The trade-off is that the two regions are independent. A region does not know which substate of its sibling is active. If the fault recovery needed to behave differently depending on whether the machine was brewing or cleaning, a single supervision region would not be enough – you would be back to per-mode transitions (or you would model that knowledge a different way). Reach for parallel regions when a concern truly cuts across all the sibling substates, not when it branches on them.

Completion

SysML v2 has no final marker on a state. What it has instead is a pair of boundary states that every state inherits. States::StateAction, the standard library’s base type for every state definition, declares ref state start and ref state done – the starting and ending snapshots of the state’s performance. You mark completion by transitioning to done:

state def OneShot {
    state warmup;
    state ready;
    entry; then warmup;
    transition first warmup accept ReadySignal then ready;
    transition first ready then done;
}

The last transition says: once ready has been reached, the enclosing OneShot state is finished. That is the same shape the standard library uses for its own state machines – Actions.sysml writes transition aTransition first start accept apayload: Anything via receiver then done;. start and done are ordinary inherited features, so you reference them by name like any other substate; there is no keyword to remember.

In the MachineStates model above, nothing transitions to done, so the machine never completes on its own – which is what you want for a machine that runs until it is switched off.

Resuming an interrupted region

When a region is interrupted – by a fault, by its parent being exited and re-entered, or by a transition that points at the composite from the outside – it restarts from its initial substate. Sometimes that is wrong. A brew interrupted by a clean cycle should, on returning to brewing, pick up where it left off, not restart grinding.

SysML v2 has no history pseudostate. If you are coming from SysML v1 or UML, this is one of the constructs that did not carry over: there is no shallow or deep history marker in the language, and no library element that stands in for one. Resuming is something you model explicitly.

The straightforward way is to keep the last active substate in an attribute and branch on it when the region is re-entered:

state def BrewPhases {
    attribute resumeAt : String;

    state dispatch;
    state grinding   { entry action markGrinding   { assign resumeAt := "grinding";   } }
    state extracting { entry action markExtracting { assign resumeAt := "extracting"; } }

    entry; then dispatch;

    // Re-entry branches on the recorded phase instead of always restarting
    transition first dispatch if resumeAt == "extracting" then extracting;
    transition first dispatch if resumeAt != "extracting" then grinding;

    transition first grinding accept PhaseDone then extracting;
}

Each phase records that it was entered; a small dispatch state reads that record and routes to the right phase using ordinary guarded transitions. It is more verbose than a history marker, but the state being remembered is now a visible part of the model – which is what a reviewer needs anyway. Deep history has no shorthand either: nest the same pattern, one attribute per level.

sysml-rs extends the spec here. Parallel regions are spec – parallel is a real modifier on a state definition or usage. What the runtime adds on top is the scheduling: when an event arrives, the runtime advances each region to a stable point before delivering the next external event, so every region sees a consistent snapshot of the others. That run-to-completion guarantee is the sysml-rs execution rule; the spec says the regions are performed concurrently, not how a tool must drain their internal queues.

One caveat if you are following along with the tool: sysml-rs does not yet parse the parallel keyword, so the model above will not check. The keyword is specification syntax and the example is correct – the gap is on the tool’s side.

Completion Transitions

Every transition so far has an explicit trigger – a signal, a duration, a condition, a port message. But a state that runs a do behavior has one more way to leave: it can finish. A completion transition fires when the source state’s do behavior completes, with no trigger written at all:

transition first <source> then <target>;

The absence of a trigger is the trigger. The Brewing state from the nested-states section runs MonitorTemperature as its do action; when that action ends, the state is done, and a completion transition can carry the machine onward. Like every transition, it connects state usages, so it lives in the containing state machine:

state def Brewing {
    entry action : StartHeating;
    do action : MonitorTemperature;
    exit action : StopHeating;
}

state def Dispensing;

state def BrewSequence {
    state brewing : Brewing;
    state dispensing : Dispensing;

    transition first brewing then dispensing;   // fires when brewing's `do` completes
}

Read it as: “when the brewing state is done, go to dispensing.” No accept, no when, no after – the completion of the do behavior is the implicit event.

Completion is stricter in a parallel composite: the parent is not finished while any region is still running. Since there is no final marker in the language, a region signals that it is finished the way any state does – by transitioning to its inherited done state, as in the OneShot example above.

Exhibiting State Machines on Parts

So far the state machine has lived in its own state def MachineStates, separate from the parts it governs. SysML v2 also lets a part carry its own state machine inline, with the exhibit keyword.

part def <Part> {
    exhibit state <name> { <states and transitions> };
}

Give the brewer its own small lifecycle, owned by the part rather than declared outside it:

part def Brewer {
    exhibit state brewingMode {
        state heating;
        state ready;
        entry; then heating;
        transition first heating accept when waterTemp >= 92.0 then ready;
    }
}

exhibit state brewingMode { ... } says: every Brewer instance runs its own brewingMode state machine. The states and transitions live inside the part definition, so a reviewer reading Brewer sees the part’s structure and its lifecycle in one place.

Exhibiting is the structural counterpart to the parallel regions from earlier. Parallel regions live inside a composite state and share one machine. An exhibit lives on a part and is owned by that part – the part’s internal modes are its own API, and other parts reach them by sending signals to that part. Reach for exhibit when a component’s modes are nobody else’s business; reach for a shared state def when the modes are a property of the system the parts belong to.

States and Actions Together

States and actions serve different roles. Actions describe what happens – the sequence of steps in a brew cycle. States describe when things are allowed – the machine must be in Idle before it can start brewing.

In a complete model, you connect them. The do action inside a state can reference an action definition:

action def BrewCycle {
    action grind : GrindBeans;
    action heat : HeatWater;
    action extract : ExtractCoffee;
    action dispense : DispenseCoffee;

    first grind then heat then extract then dispense;
}

state def Brewing {
    entry action : StartHeating;
    do action : BrewCycle;
    exit action : StopHeating;
}

When the machine enters Brewing, it runs StartHeating, then executes BrewCycle as its ongoing behavior, and runs StopHeating when it leaves. The state governs the lifecycle; the action governs the procedure.

sysml-rs extends the spec here. A state machine can also gate continuous behavior, not just discrete actions. When a model carries an ODE — see Chapter 9’s :> GetDerivative pattern and the worked hybrid example in Chapter 16 — entering one state can switch which derivative is integrated. The runtime monitors transition guards that mention continuous state variables and fires those transitions the instant the guard becomes true, with sub-tick precision. This is what lets a thermostat trip cleanly at 100°C and a bouncing ball bounce at exactly y=0. The state-machine half is spec; the zero-crossing-as-transition mechanism is sysml-rs’s runtime contribution.

Common Mistakes

Forgetting the initial state. A composite state definition without entry; then <state>; has no defined starting point. Reviewers and tools will not know which substate activates first. Always declare the initial transition.

Omitting guards on safety-critical transitions. An unguarded transition from idle to brewing means the machine can start brewing at any time – no water check, no cup check. If a transition has safety implications, give it a guard. The guard makes the precondition visible in the model, not hidden in implementation code.

Using states where actions belong. If a “state” has no transitions into or out of it and just runs a fixed sequence of steps, it is probably an action, not a state. States model modes that the system can be in. Actions model procedures that the system performs. The test is: can the system stay in this mode indefinitely, waiting for an event? If yes, it is a state. If it always runs to completion, it is an action.

What You Have Built

With the state definitions expanded, you can see the entry and exit actions inside each state, plus all the transitions with their guards and triggers:

Expanded MachineStates, including guarded and triggered transitions.

The coffee machine now has a complete lifecycle model. MachineStates defines four operating modes with guarded, triggered transitions between them. Entry and exit actions ensure consistent setup and teardown. The initial state is explicit, the error state requires a deliberate reset, and the guard on idle to brewing enforces preconditions that a reviewer can inspect.

In Chapter 9, you will add constraints – checkable logic that makes properties like temperature ranges and safety interlocks part of the model itself.

Expressions and Constraints

So far, the coffee machine model describes what the system is and what it does. It has structure (parts, ports, connections), behavior (actions with control flow), and modes (states with guarded transitions). What it cannot do yet is answer a quantitative question: Is the brew temperature correct? Is it safe to dispense?

This chapter adds checkable logic. You will write expressions that compute values, define calculations that can be reused, and create constraints that the model must satisfy. By the end, the coffee machine will have a temperature target calculation, a safety interlock constraint, and the language vocabulary you need to read every expression in the book.

Expression Basics

An expression in SysML v2 is any combination of values and operators that produces a result. You have already seen simple expressions in attribute defaults and transition guards. Now we look at the full operator set.

The everyday operators come in four families:

Arithmetic: +, -, *, /

Relational: <, >, <=, >=

Equality: ==, !=

Logical: and, or, not, xor, implies, and the eager pair & and |

These work the way you expect from engineering math. Arithmetic operators produce numeric results. Relational and equality operators produce Boolean results. Logical operators combine booleans.

Here is an expression that checks whether a temperature is within brewing range:

waterTemp >= 90.0 and waterTemp <= 96.0

And one that computes a total cycle time:

grindSeconds + heatSeconds + extractSeconds

Both are just expressions – sequences of values and operators. They become useful when you put them inside calculations and constraints.

and or &? They are not the same

There are two spellings of logical conjunction, and the difference is the one you know from programming languages even though it is spelled differently here.

and is conditional. In the grammar its right-hand side is a reference to an expression rather than the expression itself, which is what lets it be left unevaluated when the left side already decides the answer. & is eager: both sides are plain operands and both are evaluated. or and | divide the same way.

That matters when the right-hand side is expensive or unsafe:

constraint def SafeToBrew {
    doc /* The second test is only reached when a tank is present. */
    waterTank != null and waterTank.level > minimumLevel
}

With and, the level is never read when there is no tank. With & it would be.

Prefer and and or – they are the safer default and what the rest of this book uses. Reach for & or | only when you actually want both sides evaluated regardless.

A tool wrinkle. & and | are accepted inside a model file, but the standalone expression evaluator rejects them: sysml eval 'true & false' reports that it cannot parse the expression, while sysml eval 'true and false' answers false. If you are testing an expression at the command line, use the word forms.

xor is exclusive or, and implies is logical implication – a implies b is false only when a is true and b is false. Both are ordinary infix operators, and both evaluate: sysml eval 'true xor false' gives true, and sysml eval 'true implies false' gives false.

Operator Precedence and Parentheses

SysML v2 follows standard mathematical precedence: multiplication and division bind tighter than addition and subtraction, relational operators bind tighter than logical ones. But relying on precedence in complex expressions is a recipe for review comments.

Compare these two:

not brewCommand or cupDetected and lidClosed
(not brewCommand) or (cupDetected and lidClosed)

They mean the same thing, but only the second one is obvious at a glance. Parentheses cost nothing to write and save time in every review. Use them whenever an expression has more than one logical operator.

Operators Beyond the Basics

The four families above cover most everyday model logic. The language has more, and you need them once your model gets serious.

Power and modulo. ** (or ^) raises to a power, % is integer remainder.

attribute storedEnergy = boilerVolume * thermalCapacity * tempDelta ** 2;
attribute selfTestSlot = cycleNumber % 10;   // every tenth cycle is a self-test

More logical operators. Beyond and, or, not, the spec gives you xor (exclusive or) and implies (logical implication). They are useful when a boolean condition reads more naturally as one of these forms:

attribute brewOk     = brewRequested implies cupPresent;   // identical to: not brewRequested or cupPresent
attribute exactlyOne = manualOverride xor scheduledRun;    // exactly one is active at a time

Null coalescing. ?? returns its left operand if that operand has a value, otherwise its right operand. Use it for defaulting:

configuredTargetTemp ?? 93.0

If configuredTargetTemp is set, you get that value; otherwise you get 93.0. This is far cleaner than if configuredTargetTemp != null ? configuredTargetTemp else 93.0.

Type tests and coercion. istype, hastype, and as ask and answer questions about an element’s classification:

attribute isExactly = sensor istype Thermistor;   // is `sensor` exactly Thermistor?
attribute isAKind   = sensor hastype Sensor;      // is `sensor` Sensor or any subtype?
attribute coerced   = sensor as Thermistor;       // treat sensor as a Thermistor

istype is exact; hastype includes subtypes; as is a coercion that gives you access to the more specific feature set.

Sequence operators. When an attribute has multiplicity greater than one, you have a sequence, and you operate on it with -> operators. The per-element rule goes in a body expression – braces holding an in parameter, a ;, and the expression over that parameter:

attribute highPower  = brewers->select {in b; b.power > 1000.0};       // filter
attribute allTargets = brewers->collect {in b; b.targetTemp};          // map
attribute healthy    = brewers->reject {in b; b.fault};                // filter (negated)
attribute allBelow   = brewers->forAll {in b; b.targetTemp <= 100.0};  // every element satisfies
attribute anyFault   = brewers->exists {in b; b.fault};                // some element satisfies

These compose. A common pattern is “is every brewer’s temperature in the safe range?”:

brewers->forAll {in b; b.waterTemp >= 90.0 and b.waterTemp <= 96.0}

You will reach for ->forAll and ->select constantly once a model has any non-trivial structure. They turn what would be a repeated constraint into one rule that scales with the part count.

Indexing and ranges. #(n) selects the n’th element of a sequence; .. builds a range:

attribute firstBrewer  = brewers#(1);   // first brewer
attribute cycleNumbers = 1..5;          // sequence 1, 2, 3, 4, 5

Metadata access. @ reads a metadata annotation; @@ reads all annotations of a kind. You will see these in Chapter 13, where metadata drives view rendering.

Qualified enumeration references. When two enumerations share a literal name, qualify the reference with :::

mode == BrewMode::Espresso       // not just `Espresso`

Most of these operators show up rarely, but when you need ?? or ->forAll, no other operator does the job. Reach for them by name; do not work around them.

String Literals and Structured Values

So far every value in this chapter has been a number or a Boolean. SysML v2 treats strings as ordinary value types too, and it lets you build your own structured value types out of them.

String literals are written with double quotes:

attribute label : String := "Brewing";
attribute statusText : String := if waterLow ? "Low Water" else "System OK";

The quotes are not part of the value. You can include escaped characters like \" (an embedded quote) or \n (a newline) inside the literal. Strings are useful for human-readable labels and diagnostics; the next chapter uses them inside doc comments on requirements.

The second example uses the conditional expression from the next section – if <cond> ? <then> else <else> – with two string results.

Structured attribute definitions group a small, fixed set of named, typed fields into one value. There is no anonymous tuple type in the language – you name the record with an attribute def and then use it like any other type:

attribute def SensorReading {
    attribute name  : String;
    attribute value : Real;
    attribute unit  : String;
}

attribute reading : SensorReading;

SensorReading is the type of a three-field record – a label, a measurement, and a unit. An attribute def is the right shape when a few values belong together (a sensor reading with its units, a coordinate’s x/y/z) but you do not need a full part def to give them an identity: an attribute value has no occurrence, no ports, and no lifecycle. Compare it to a sequence, which holds many values of one type: a structured attribute holds few values of different types, named.

The Conditional Expression

SysML v2 has a conditional expression with the syntax if <condition> ? <then-value> else <else-value>. This is not an if-statement – it is an expression that evaluates to one of two values.

if ambient < 18.0 ? 94.0 else 93.0

This reads: if the ambient temperature is below 18 degrees, the result is 94.0; otherwise 93.0. You will use this pattern in calculations where the output depends on a condition.

The ? separating the condition from the then-value is required. It prevents ambiguity in cases where the condition itself contains operators.

Calculations: Reusable Computations

A calc def defines a reusable computation. It takes typed inputs, declares a return, and contains an expression that computes the result.

calc def TargetBrewTemp {
    in ambient : Real;
    return result : Real;

    result = if ambient < 18.0 ? 94.0 else 93.0
}

This says: given an ambient temperature, compute a target brew temperature. If the environment is cold (below 18 degrees), aim higher to compensate for heat loss.

A calc def is like a function in a programming language, but it lives inside your model. It has a name, typed parameters, and a body that computes a return value. The in keyword marks input parameters. The return keyword marks the output.

You can use a calculation anywhere you need its result. Inside a part, you create a usage of the calculation:

part def Brewer {
    attribute waterTemp : Real;
    attribute ambientTemp : Real;

    calc targetTemp : TargetBrewTemp {
        in ambient = ambientTemp;
        return result : Real;
    }
}

The usage targetTemp binds the calculation’s input to a concrete attribute. Whenever ambientTemp has a value, targetTemp can be evaluated. This is how calculations connect abstract formulas to specific parts of the model.

A calculation’s body is not restricted to its own input parameters. The body is a normal SysML expression and can refer to any element reachable through the owner chain at the call site. So targetTemp could read brewer.boiler.power directly, without that attribute being passed in as an in parameter – the name resolves at evaluation time, against the calc usage’s surrounding scope.

Why this matters. Authors often over-parameterise calc defs because they assume each call is a sealed black box. It is not: calc bodies resolve names through the owner chain when they evaluate, so a calc that reads brewer.boiler.power continues to work after the boiler’s power is set, overridden, or computed by another calc. You can compose calcs into a small DAG of named equations without threading every value through in parameters.

Computed Attributes

For one-shot values that do not need a name or reuse, SysML v2 lets you put the expression directly on an attribute:

part def Brewer {
    attribute ambientTemp : Real;
    attribute targetTemp : Real = if ambientTemp < 18.0 ? 94.0 else 93.0;
}

targetTemp is now an attribute whose value is computed from ambientTemp. There is no separate calc def and no usage to bind. Reading targetTemp evaluates the right-hand side; if ambientTemp is set, you get a number.

Use a computed attribute when:

  • the formula appears in exactly one place,
  • it has no parameters (it reads other attributes by name),
  • you do not need to reuse it,
  • you do not need a named contract to refer to.

Use a calc def instead when any of those things are false. A calc def is a named, parametric, reusable equation. A computed attribute is a one-shot derived value.

Computed attributes evaluate lazily on read, and they compose. You can write:

part def Brewer {
    attribute ambientTemp : Real;
    attribute targetTemp : Real = if ambientTemp < 18.0 ? 94.0 else 93.0;
    attribute boilerLimit : Real = 100.0;
    attribute headroom : Real = boilerLimit - targetTemp;
    attribute safe : Boolean = headroom > 0.0;
}

Reading safe evaluates headroom, which in turn reads targetTemp, which reads ambientTemp. The evaluation order is dictated by what the reader needs; you do not write it explicitly.

Why this matters. The chapter used to suggest you wrap every conditional default in a calc def. That is overkill for one-shot fields. Computed attributes give you a small DAG of derived values without in/return ceremony, and the lazy-on-read evaluation means you do not pay for them until something asks.

Equations and Overrides

Once you start treating calc defs as named equations, two things follow.

First, the body of a calc def is the equation. Every usage of that calc def is an instance of the same equation, evaluated against whatever scope the usage is in. Two parts that use TargetBrewTemp are not running two different formulas – they are running the same formula with different bindings.

Second, you can replace the bindings without touching the equation. The simplest case is at the usage site:

part highAltitudeBrewer : Brewer {
    calc targetTemp : TargetBrewTemp {
        in ambient = ambientTemp - 5.0;   // override: simulate cooler effective ambient
        return result : Real;
    }
}

The equation is unchanged; only the input binding moved. The same idea extends to overriding attribute values along a feature chain (boiler.power = 1500.0) or substituting a whole calc def via subclassing and :>>-redefinition. Trade studies, sensitivity sweeps, and “what if the heater were 1.5 kW” exploration all work this way.

Why this matters. Most useful analyses in a system model are not “evaluate the model once”. They are “evaluate it many times with different parameters and compare”. Once you separate equations (the calc bodies) from bindings (the values flowing in), you can vary the bindings without forking the model. Authors who do not see this distinction tend to copy entire packages just to change one number.

Calculations Over Sequences

Once a part has multiplicity, calcs over sequences become the natural way to ask system-level questions. The sequence operators from earlier (->select, ->collect, ->forAll) are first-class inside calc bodies:

calc def WorstHeadroom {
    in stations : Brewer[*];
    return : Real = stations->collect {in s; s.boilerLimit - s.targetTemp}->reduce min;
}

This calc takes a sequence of brewers and returns the smallest headroom across them all. The ->collect builds a sequence of headroom values; ->reduce min folds that sequence to a single number by applying min pairwise. (min and max in the standard library are two-argument functions, so a sequence is reduced with them rather than aggregated by them.) The whole thing is one expression.

->forAll and ->exists are useful in constraint bodies, not just calcs:

constraint def AllStationsSafe {
    in stations : Brewer[*];
    stations->forAll {in s; s.waterTemp >= 90.0 and s.waterTemp <= 96.0}
}

This says “every brewer is in the safe range”. One named constraint scales with however many brewers exist.

Why this matters. Without sequence operators in calc and constraint bodies, an author writes parallel attribute trees (“worstHeadroom1, worstHeadroom2, …”) or tries to extract aggregations into the structural model. The collection operators put the aggregation where it belongs – in the equation – and let the system grow without rewriting the calc.

Calculations as ODE Right-Hand Sides

When a calc def captures the time-derivative of a state variable, you can flag that intent by subtyping GetDerivative from the standard library’s StateSpaceRepresentation package:

import StateSpaceRepresentation::*;

calc def HeaterRhs :> GetDerivative {
    in t : Real;
    in temp : Real;
    in heaterPower : Real;
    in lossCoeff : Real;
    in thermalMass : Real;
    return dT_dt : Real = (heaterPower - lossCoeff * temp) / thermalMass;
}

HeaterRhs is still an ordinary calc def. The supertype :> GetDerivative carries the contract: this calc returns the time-derivative of one named state variable. There is no special metadata, no magic string, no tool-specific annotation – just a stdlib supertype.

A second pattern, GetOutput, captures algebraic outputs as a function of state and inputs (for example, “the sensor reading is the boiler’s temp plus a fixed offset”). Together, GetDerivative and GetOutput describe a state-space block in the spec’s standard way; they are usually grouped under an action def :> ContinuousStateSpaceDynamics. All three come from StateSpaceRepresentation.

We cover ODE authoring properly in Chapter 16, which shows the coffee machine evolving over time with a heater turning on and off. For now, the only thing to take away is that the spec gives you a language shape for derivatives and algebraic outputs – you do not need to leave SysML to describe them.

Why this matters. Older sysml-rs material taught ODE authoring through @ToolVariable { derivative = "..." } metadata. That pattern is removed. The spec-standard pattern is calc def Foo :> GetDerivative { ... }, and the runtime picks up the contract from the supertype. As a SysML author, the change is: write a calc def, type its body the way you would type any equation, give it the right supertype, and the rest is the runtime’s problem.

Constraints: Reusable Rules

A constraint def defines a reusable boolean rule. Its body is an expression that must evaluate to true for the model to be considered valid.

constraint def BrewTempInRange {
    in temp : Real;
    temp >= 90.0 and temp <= 96.0
}

This constraint takes a temperature and checks that it falls within the safe brewing range. It does not enforce anything on its own – it just defines the rule. Think of it as a named predicate: a question that can be answered true or false.

Constraints and calculations look similar. The difference is intent:

  • A calc def computes a value. Its body is an assignment: result = <expression>.
  • A constraint def checks a condition. Its body is a boolean expression with no assignment.

Both are reusable. Both take typed parameters. But a calculation answers “what is the value?” while a constraint answers “is this acceptable?”

How Constraints Find Their Values

Constraint bodies often reference attributes by short name – waterTemp, cupPresent – and you might wonder how the language decides which waterTemp. Two scoping rules cover the cases you will hit:

  1. In a part context. A constraint asserted inside a part resolves names by walking the owner chain outward. waterTemp first looks for an attribute named waterTemp on the part the assertion lives in, then on its owner, and so on. The first match wins.

  2. In a requirement context. A constraint inside a requirement def resolves names against the requirement’s declared subject, actor, and parameter list. The satisfy site (covered in Chapter 10) supplies the concrete bindings.

Mixing the two scoping models is the single biggest source of “constraint always passes” or “constraint cannot find variable” failures in real models. If you assert a constraint in a part and the body references specification.inputSupply.voltage, every name on that path must resolve through the owner chain – there is no implicit “specification” sitting at the top level of the package. If the model intended specification to be a parameter, it belongs as an in parameter on the constraint or as a parameter on the surrounding requirement.

A short rule of thumb: constraints inside parts read attributes by name from the enclosing part. Constraints inside requirements read attributes by name from the requirement’s parameters and subject. If you find yourself reaching across packages to name a value, you probably want a parameter.

Asserting Constraints

A constraint definition by itself is just a template. To apply it, you use assert constraint inside a part or other context:

part def Brewer {
    attribute waterTemp : Real;
    attribute ambientTemp : Real;

    calc targetTemp : TargetBrewTemp {
        in ambient = ambientTemp;
        return result : Real;
    }

    assert constraint tempCheck : BrewTempInRange {
        in temp = waterTemp;
    }
}

The assert constraint line does two things. First, it creates a usage of BrewTempInRange inside Brewer, binding the constraint’s temp parameter to the brewer’s waterTemp attribute. Second, the assert keyword declares that this constraint must hold – it is not just a check you could run, it is a stated requirement on this part.

An asserted constraint is a commitment. It says: in any valid instance of Brewer, waterTemp must be between 90.0 and 96.0. When the constraint is evaluated – either by a static check or as the model runs – it produces a verdict that is either true (the assertion holds) or false (it does not), with the operand values that contributed. The same machinery powers require constraint inside a requirement; we pick that up in Chapter 10.

You can also assert a constraint inline, without referencing a named definition:

assert constraint {
    waterTemp >= 90.0 and waterTemp <= 96.0
}

This works for one-off checks, but named constraints are better for anything you might reuse or need to trace to a requirement.

Asserting That Something Must Not Hold

The opposite of assert constraint is assert not constraint. It says: this combination must never hold.

part def Brewer {
    attribute dispenseRequested : Boolean;
    attribute cupPresent : Boolean;

    assert not constraint {
        dispenseRequested and not cupPresent
    }
}

This reads cleanly as “it must never be the case that a dispense was requested with no cup present”. You could write the same rule as a positive assertion – assert constraint { not dispenseRequested or cupPresent } – but the negated form usually reads better when the unsafe combination is what you care about.

assert not is a separate language form, not just a textual transformation. Use it whenever the rule you want to express is “this must never be true.”

Building the Coffee Machine’s Logic

The coffee machine from previous chapters has structure, connections, actions, and states. Now add the quantitative logic that makes it checkable.

The Temperature Calculation

The brewer needs to know what temperature to target. That depends on ambient conditions – a cold room means more heat loss, so the target should be higher. This is exactly what calc def is for:

package CoffeeMachineAnalysis {
    import ScalarValues::*;
    import CoffeeMachineDomain::*;

    calc def TargetBrewTemp {
        in ambient : Real;
        return target : Real;

        target = if ambient < 18.0 ? 94.0 else 93.0
    }
}

This is a simple two-branch formula. In a real system, it might be a polynomial curve fit or a lookup table. The point is that it lives in one place with a clear name, typed inputs, and a typed output.

The Safety Interlock

A coffee machine should not dispense hot water unless a cup is present. This is a safety interlock – a boolean rule that must be satisfied before the brew action proceeds:

package CoffeeMachineAnalysis {
    import ScalarValues::*;
    import CoffeeMachineDomain::*;

    calc def TargetBrewTemp {
        in ambient : Real;
        return target : Real;

        target = if ambient < 18.0 ? 94.0 else 93.0
    }

    constraint def SafetyInterlock {
        in cupDetected : Boolean;
        in brewCommand : Boolean;

        not brewCommand or cupDetected
    }
}

Read the constraint body carefully: not brewCommand or cupDetected. This is the logical implication “if brewCommand, then cupDetected.” It evaluates to true in three cases: no brew command was given, a cup is detected, or both. It is false only when a brew is commanded with no cup present – exactly the unsafe condition.

You could write the same rule using implies:

constraint def SafetyInterlock {
    in cupDetected : Boolean;
    in brewCommand : Boolean;

    brewCommand implies cupDetected
}

Both forms mean the same thing. implies reads more naturally when the rule is “A requires B”; not A or B reads more naturally when you want the user to see both branches.

Applying Both to the Machine

Now bring the calculation and constraint into the coffee machine structure:

package CoffeeMachineSystem {
    import ScalarValues::*;
    import CoffeeMachineDomain::*;
    import CoffeeMachineAnalysis::*;

    part coffeeMachine : CoffeeMachine {

        attribute ambientTemp : Real;
        attribute cupPresent : Boolean;
        attribute brewRequested : Boolean;

        part brewer : Brewer {

            calc targetTemp : TargetBrewTemp {
                in ambient = coffeeMachine.ambientTemp;
                return target : Real;
            }

            assert constraint tempSafe : BrewTempInRange {
                in temp = waterTemp;
            }
        }

        assert constraint interlock : SafetyInterlock {
            in cupDetected = cupPresent;
            in brewCommand = brewRequested;
        }
    }
}

Two things are asserted here. Inside brewer, the tempSafe constraint checks that the actual water temperature stays within range. At the machine level, the interlock constraint checks that no brew can happen without a cup. Both are named, both are traceable, and both can be verified independently.

The targetTemp calculation inside brewer binds to the machine’s ambient temperature. It does not enforce anything – it computes the target. The tempSafe constraint checks that the actual temperature (measured by a sensor, represented by waterTemp) stays in range. These are complementary: the calculation says what the temperature should be, and the constraint says what range is acceptable.

Calculation definitions and constraint usages in the coffee machine model.

Calculations with Multiple Inputs

A calculation can take as many inputs as needed. Here is one that estimates the total brew cycle time:

calc def BrewCycleTime {
    in grindSeconds : Real;
    in heatSeconds : Real;
    in extractSeconds : Real;
    return total : Real;

    total = grindSeconds + heatSeconds + extractSeconds
}

And a more interesting one that adjusts extraction time based on grind size:

calc def AdjustedExtractTime {
    in baseSeconds : Real;
    in grindFactor : Real;
    return adjusted : Real;

    adjusted = baseSeconds * grindFactor
}

You can compose calculations by using one calculation’s output as another’s input. This lets you build up complex formulas from simple, testable pieces.

Constraints with Multiple Conditions

A single constraint can check several conditions at once using logical operators:

constraint def ReadyToBrew {
    in waterLevel : Real;
    in minWaterLevel : Real;
    in temp : Real;
    in cupPresent : Boolean;

    cupPresent and waterLevel >= minWaterLevel
        and temp >= 90.0 and temp <= 96.0
}

This combines three checks: cup is present, water level is sufficient, and temperature is in range. It is a single named rule that answers a clear engineering question: is the machine ready to brew?

When a compound constraint like this gets long, consider splitting it. You already have BrewTempInRange and SafetyInterlock as separate constraints. A composite constraint can reference them conceptually while keeping the logic explicit.

When to Use What

Expressions, calculations, and constraints serve different purposes. Here is when to reach for each:

Use a bare expression when you need a one-time value – an attribute default, a transition guard, or a simple formula that appears in exactly one place.

Use a computed attribute when the value is derived from other attributes in the same part, and you do not need parameters or a name to refer to it elsewhere.

Use calc def when a computation is reusable, has clear inputs and outputs, or is complex enough to deserve a name. If you find yourself writing the same arithmetic in two places, extract it into a calculation.

Use constraint def when a boolean rule represents an engineering requirement – a range check, a safety condition, or an interlock. If someone might ask “where is that rule defined?” it should be a named constraint.

Use assert constraint to commit to a rule in a specific context. An unasserted constraint is just a definition sitting on a shelf. Assertion is what makes it binding.

Use assert not constraint when the rule you want is “this combination must never happen.” The negated form often reads better than its positive equivalent.

Reading Constraints as Math

Every expression you write in SysML v2 has a clean mathematical reading. Reviewers and stakeholders often see the math form before they see the source. Knowing the operator-to-notation correspondence makes constraints easier to read in both directions:

SourceMath notation
>=, <=, !=, ,
and, or, not, , ¬
implies
^ or **superscript: xⁿ
/fraction: a / b
abs(x)`
sqrt(x)√x
brewer.boiler.powersubscripted identifier

So temp >= 90.0 and temp <= 96.0 reads as 90.0 ≤ temp ≤ 96.0, and not brewCommand or cupDetected reads as ¬brewCommand ∨ cupDetected. When a constraint is hard to read in source form, try writing it the way it would render mathematically – often the cleaner phrasing is the one that translates directly. Prefer abs(x) over an ad-hoc conditional, prefer implies over not A or B when the rule is “A requires B”, and prefer pow(x, 2) over x ** 2 when the constraint is going to be reviewed verbally.

Common Mistakes

Writing constraints that do not evaluate to boolean. A constraint body must be an expression that produces true or false. If you accidentally write an arithmetic expression (like a + b) instead of a comparison (like a + b <= limit), the model will not check.

Forgetting to assert. Defining a constraint def does nothing until you assert it somewhere. If you define SafetyInterlock but never write assert constraint ... : SafetyInterlock, the rule exists in the model but is never checked. This is the most common oversight when adding constraints.

Duplicating logic instead of naming it. If the expression waterTemp >= 90.0 and waterTemp <= 96.0 appears in three places, a change to the range requires three edits. Define it once as a constraint def and reference it wherever needed.

Using calculations where constraints belong. If you write a calc def that returns a boolean, ask yourself whether it should be a constraint def instead. Calculations compute values; constraints check conditions. Using the right one makes the model’s intent clearer to reviewers.

Referencing an attribute that has no assigned value. When a constraint walks a chain like specification.inputSupply.busbarCurrentRating, every step must resolve to a bound value. If specification is declared but never given a binding (no part instance, no :>> redefinition, no satisfy site supplying it), the chain cannot evaluate. The fix is almost always to make the missing binding explicit – either declare the constraint inside a part that owns those features, or add the missing values as in parameters and let the satisfy site supply them. The error you see at evaluation time will name the unbound variable; treat it as a model-completeness signal, not a bug in the constraint.

What Comes Next

The coffee machine now has checkable logic: a temperature calculation that adapts to ambient conditions, a safety interlock that prevents dispensing without a cup, and the language vocabulary – expanded operators, equations, sequence calculations, computed attributes, and asserted constraints both positive and negated – to write the rest of the system.

In Chapter 10, you will turn these engineering rules into formal requirements and trace them from stakeholder intent through design to verification evidence. The constraints you defined here will become the “satisfy” targets that link requirements to the model.

In Chapter 16, you will let the same model run – ODE state variables advancing in time, calc defs serving as right-hand sides, asserted constraints firing per-tick as live monitors. The :> GetDerivative pattern from earlier in this chapter is the doorway into that.

Requirements and Verification

Your coffee machine has structure, connections, behavior, states, and constraints. You can describe what the system is made of, how it behaves, and what rules it must obey. But none of that answers a fundamental engineering question: does the design actually meet what was asked for?

That is what requirements and verification are for. A requirement captures something the system must do or be. A satisfaction link says which part of the design addresses it. A verification case says how you plan to prove it. Together, these three elements form a traceability loop – the mechanism that connects stakeholder intent to design decisions to objective evidence.

SysML v2 models requirements as first-class elements, not as external documents referenced by ID. They live in the same model as the parts, actions, and constraints they trace to. This means tools can check whether every requirement has a design link and a verification plan, rather than relying on spreadsheets and manual audits.

Defining a Requirement

A requirement starts with requirement def. Like part definitions and action definitions, a requirement definition is a reusable type. You can give it a short identifier (useful for document references) and a doc comment that carries the requirement text.

The short identifier goes in angle brackets and single quotes – <'REQ-TEMP-001'>. The quotes are what let it contain hyphens, which a plain identifier cannot; Appendix H covers that form and the rest of the naming rules.

package CoffeeMachineRequirements {
    requirement def <'REQ-TEMP-001'> BrewTempReq {
        doc /* The brewer extraction temperature shall remain
               between 90 and 96 degrees Celsius during brewing. */
    }
}

BrewTempReq is the model name you use in SysML code. REQ-TEMP-001 is the short ID – a human-readable tag that maps to whatever requirement numbering scheme your project uses. The doc comment holds the requirement text itself. This text is not checked by the model, but it is visible to reviewers and tools.

You can define as many requirements as your project needs. Here is a second one for the safety interlock from Chapter 9:

package CoffeeMachineRequirements {
    requirement def <'REQ-TEMP-001'> BrewTempReq {
        doc /* The brewer extraction temperature shall remain
               between 90 and 96 degrees Celsius during brewing. */
    }

    requirement def <'REQ-SAFE-001'> SafetyInterlockReq {
        doc /* The system shall not initiate a brew cycle
               unless a cup is detected on the drip tray. */
    }
}

Each requirement stands alone as a definition. It does not yet say anything about which part of the design addresses it or how you would test it. Those connections come next.

Requirement Text vs. Formal Constraints

The doc comment on a requirement is prose. It communicates intent to human readers, but the model checker cannot evaluate it. For requirements where precision matters, you can add a formal constraint inside the requirement body.

requirement def <'REQ-TEMP-001'> BrewTempReq {
    doc /* The brewer extraction temperature shall remain
           between 90 and 96 degrees Celsius during brewing. */

    attribute temp : Real;
    require constraint { temp >= 90.0 and temp <= 96.0; }
}

The require constraint block adds a machine-checkable condition alongside the prose. The temp attribute is a parameter of the requirement – it represents whatever value needs to satisfy the condition. When this requirement is linked to a design element, the attribute can be bound to an actual property.

A formal constraint also turns the requirement into something the runtime can produce evidence for. During constraint evaluation, verification, or simulation-coupled verification, the constraint runs against the bound values and produces a Pass / Fail / Inconclusive / Error verdict (covered in How Verification Verdicts Work, below).

Not every requirement needs a formal constraint. Some requirements are inherently qualitative (“the user manual shall be written in English”) and do not benefit from formalization. The general rule: if the requirement text contains a number, a range, or a logical condition, consider adding a require constraint to make it checkable.

Subjects, Actors, and the Binding Surface

A requirement can declare a subject – the element it constrains. The subject says “this requirement is about that thing.”

requirement def <'REQ-TEMP-001'> BrewTempReq {
    doc /* The brewer extraction temperature shall remain
           between 90 and 96 degrees Celsius during brewing. */

    subject brewer : Brewer;
}

The subject keyword declares that BrewTempReq applies to a Brewer. This is a typed parameter: any element that satisfies this requirement must be (or contain) a Brewer. Subjects make requirements more precise. Without a subject, a requirement floats – you know it exists, but not what it applies to. With a subject, the requirement is anchored to a specific kind of element.

A requirement can also declare one or more actors – the elements that interact with the subject. Actors are typed in the same way as subjects:

requirement def <'REQ-SAFE-001'> SafetyInterlockReq {
    doc /* The system shall not initiate a brew cycle
           unless a cup is detected on the drip tray. */

    subject machine : CoffeeMachine;
    actor user : Operator;
}

The actor declaration says “this requirement is about how the Operator interacts with the CoffeeMachine.” Actors are useful when a requirement is fundamentally about an interaction, not just a property of a single part.

Together, the subject, the actors, and any attribute parameters form the requirement’s binding surface – the set of names that a satisfy site must supply. The binding surface is also the scope inside which a require constraint resolves names (recall Chapter 9, “How Constraints Find Their Values”). When the runtime evaluates the constraint, it walks this surface, not the surrounding package, to find values.

Why this matters. Authors often write require constraint bodies that reference attributes the requirement does not declare. The constraint then either picks up the wrong attribute by accident or fails to evaluate because no value is bound. The fix is to declare every name the constraint uses as part of the binding surface – subject, actor, or attribute – so the satisfy site has a clean place to supply them.

Satisfying a Requirement

A requirement on its own is just an assertion about what should be true. To close the loop, you need to say which part of the design addresses it. That is what satisfy does.

part def CoffeeMachine {
    part brewer : Brewer;

    satisfy requirement : BrewTempReq;
}

The satisfy statement inside CoffeeMachine says: this design element claims to address BrewTempReq. The exact strength of that claim depends on the requirement.

Requirements with no formal constraint are pure traceability links. The model does not check whether CoffeeMachine actually meets the qualitative goal – the link just makes the relationship explicit and queryable. A tool or a reviewer can ask “which requirements does CoffeeMachine satisfy?” and get a definitive answer.

Requirements that carry a require constraint are stronger. The satisfy site supplies the bindings for the requirement’s parameters (subject, actors, attributes), and at evaluation time – whether driven by a static check, a verification case, or a simulation – the constraint is evaluated against those bindings and produces a verdict. This is not a manual claim; the model knows whether the constraint holds for the values the satisfy site supplied. We unpack the verdict shape in How Verification Verdicts Work, below.

You can satisfy multiple requirements from the same design element:

part def CoffeeMachine {
    part brewer : Brewer;
    part grinder : Grinder;
    part waterTank : WaterTank;

    satisfy requirement : BrewTempReq;
    satisfy requirement : SafetyInterlockReq;
}

You can also place satisfaction links at a more specific level. If the temperature requirement is really about the brewer, not the whole machine, put the satisfaction there:

part def Brewer {
    attribute waterTemp : Real;
    attribute brewPressure : Real;

    satisfy requirement : BrewTempReq;
}

Where you place the satisfy link is a modeling decision. Put it at the level of the design element that is actually responsible for meeting the requirement. If the brewer controls the temperature, the brewer should carry the satisfaction link.

Verification Cases

Satisfaction says “this design addresses this requirement.” But that is a claim, not evidence. Verification cases capture how you plan to produce evidence.

A verification case is defined with verification def. Inside it, an objective block declares which requirement it verifies:

verification def BrewTempTest {
    doc /* Measure extraction temperature across 10 consecutive
           brew cycles and confirm all readings fall within range. */

    objective {
        verify requirement : BrewTempReq;
    }
}

BrewTempTest is a verification case that targets BrewTempReq. The doc comment describes the test procedure in prose. The verify statement inside the objective block creates the formal link.

A verification case can verify more than one requirement:

verification def SafetyInterlockTest {
    doc /* Attempt to start a brew cycle with no cup present.
           Confirm the system rejects the command. Then place a cup,
           retry, and confirm the cycle starts. */

    objective {
        verify requirement : SafetyInterlockReq;
    }
}

You can also define actions inside a verification case to describe the test procedure as modeled steps:

verification def SafetyInterlockTest {
    doc /* Verify that the brew interlock prevents operation
           without a cup. */

    action removeCup { doc /* Remove any cup from the tray. */ }
    action attemptBrew { doc /* Press the brew button. */ }
    action confirmRejection { doc /* Verify the machine refuses to brew. */ }
    action placeCup { doc /* Place a cup on the tray. */ }
    action retryBrew { doc /* Press the brew button again. */ }
    action confirmSuccess { doc /* Verify the brew cycle starts. */ }

    first removeCup then attemptBrew then confirmRejection
        then placeCup then retryBrew then confirmSuccess;

    objective {
        verify requirement : SafetyInterlockReq;
    }
}

This level of detail is optional. For many projects the prose description in doc is sufficient. But when your verification plan will eventually drive automated test execution or when reviewers need to see the exact sequence, modeling the steps as actions makes the procedure unambiguous. The essential thing is the verify link – it connects the requirement to a planned piece of evidence.

How Verification Verdicts Work

sysml-rs extends the spec here. The OMG spec defines verify as a relationship that produces an outcome, but does not normatively define the outcome’s shape. sysml-rs settles on a four-valued verdict with a structured payload, described below. Other SysML v2 implementations may produce different verdict shapes; the spec-level traceability still works either way.

When a requirement that carries a require constraint is evaluated – by a verify case, by assert constraint running against the satisfy site, or by a simulation step – the result is a verdict. A verdict is one of four values:

  • Pass — the constraint evaluated to true against the bound values. The requirement is met for this evaluation.
  • Fail — the constraint evaluated to false. The requirement is violated.
  • Inconclusive — the constraint did not have enough information to decide (for example, an attribute on the binding surface had no assigned value, or the evaluation depended on a not-yet-simulated time point).
  • Error — the evaluation itself broke (a name did not resolve, a type mismatch, division by zero). The model has a defect that must be fixed before the verdict means anything.

A verdict carries more than just the kind. The structure typically looks like this:

Verdict {
    kind:      Pass | Fail | Inconclusive | Error
    actual:    <value the constraint evaluated to>
    expected:  <value the constraint required>
    margin:    <how close to the boundary, when applicable>
    message:   <human-readable explanation>
    evidence:  <reference to the source: a tick number, a test step, etc.>
}

For BrewTempReq with the constraint temp >= 90.0 and temp <= 96.0, three sample verdicts might look like:

Pass:          actual=92.4, expected=[90.0, 96.0], margin=2.4
Fail:          actual=98.1, expected=[90.0, 96.0], margin=-2.1
Inconclusive:  actual=null, message="`temp` had no assigned value"
Error:         message="undefined variable: `specification.temperature`"

The Inconclusive case is the one that surprises authors. A passing verdict is good news; a failing verdict tells you something concrete to fix; an Error is a model bug; but Inconclusive says the model is incomplete – the constraint is well-formed and the design site is willing to be checked, there just is not yet a value to check against. In a static review, that means the satisfy site needs to bind a missing parameter. In a simulation, it usually means the constraint is being checked before the relevant state variable has been initialised.

Treat each kind as actionable:

  • Pass → record evidence; you have met the requirement.
  • Fail → fix the design or relax the requirement (with stakeholder agreement).
  • Inconclusive → bind the missing values; complete the model.
  • Error → fix the model; the constraint is unevaluable as-written.

Verifying Against a Simulation

sysml-rs extends the spec here. Coupling a verify to a running simulation – so the verdict is computed from time-series state, not from static attribute values – is one of sysml-rs’s intended use cases. The OMG spec is more abstract about how verification produces its outcome.

For requirements that constrain behaviour over time (a temperature stays in range, a cycle completes within a deadline), the natural evidence is a simulation run. Here is what that looks like end-to-end for the coffee machine.

In Chapter 9 you saw the :> GetDerivative pattern for ODE right-hand sides. The brewer’s heater, modelled as a state with an ODE, looks like:

import StateSpaceRepresentation::*;

calc def HeaterRhs :> GetDerivative {
    in t : Real;
    in temp : Real;
    in heaterPower : Real;
    in lossCoeff : Real;
    in thermalMass : Real;
    return dT_dt : Real = (heaterPower - lossCoeff * temp) / thermalMass;
}

The brewer holds the state variable waterTemp and uses HeaterRhs to advance it. Now we tie verification to that:

package CoffeeMachineVerification {
    import CoffeeMachineDomain::*;
    import CoffeeMachineRequirements::*;

    verification def BrewTempSimulationTest {
        doc /* Simulate one brew cycle. The brewer's waterTemp must
               stay within [90.0, 96.0] across the entire extraction
               window. */

        subject brewer : Brewer;

        objective {
            verify requirement BrewTempReq {
                in temp = brewer.waterTemp;
            }
        }
    }
}

The verify requirement block binds the requirement’s temp parameter to the simulated brewer.waterTemp. When the verification runs against a simulation:

  1. The simulation advances waterTemp over time using HeaterRhs.
  2. At each tick, the require constraint is evaluated against the current waterTemp value.
  3. If waterTemp ever leaves [90.0, 96.0], the verdict is Fail and the evidence carries the offending tick.
  4. If waterTemp stays in range across the whole simulated window, the verdict is Pass.
  5. If the simulation never starts heating (a stuck binding, a missing input), the verdict is Inconclusive for that window.

The shape that makes this work is the same shape as the rest of the chapter: a requirement with a typed binding surface, a require constraint body that resolves names against that surface, and a verify link that supplies the bindings – only this time the bindings come from running state, not from a static attribute.

Why this matters. A numerical requirement (a temperature, a duration, a tolerance) is rarely satisfiable by inspection. Coupling verify to a simulation gives you primary evidence: the constraint either held over the simulated trajectory or it did not, and you can replay the failing tick. The chapter’s earlier verification cases describe what you would do; this is what you can run.

The Full Traceability Loop

With requirements, satisfaction, and verification in place, you have a complete traceability loop. Here it is for the coffee machine, all in one package:

package CoffeeMachineRequirements {
    import ScalarValues::*;
    import CoffeeMachineDomain::*;

    // --- Requirements ---

    requirement def <'REQ-TEMP-001'> BrewTempReq {
        doc /* The brewer extraction temperature shall remain
               between 90 and 96 degrees Celsius during brewing. */

        subject brewer : Brewer;

        attribute temp : Real;
        require constraint { temp >= 90.0 and temp <= 96.0; }
    }

    requirement def <'REQ-SAFE-001'> SafetyInterlockReq {
        doc /* The system shall not initiate a brew cycle
               unless a cup is detected on the drip tray. */

        subject machine : CoffeeMachine;
    }

    requirement def <'REQ-BREW-001'> BrewTimeReq {
        doc /* A brew cycle shall complete within 60 seconds
               from initiation to coffee dispensed. */

        subject machine : CoffeeMachine;

        attribute duration : Real;
        require constraint { duration <= 60.0; }
    }

    // --- Satisfaction (in a usage context) ---

    part coffeeMachine : CoffeeMachine {
        satisfy requirement : BrewTempReq;
        satisfy requirement : SafetyInterlockReq;
        satisfy requirement : BrewTimeReq;
    }

    // The form above is the most common, but the spec also accepts:
    //
    //     satisfy requirement brewTempReq : BrewTempReq by brewer;
    //
    // which gives the satisfaction usage its own name (`brewTempReq`)
    // and lets you spell out which design element does the satisfying
    // (`by brewer`) when it is not the enclosing element.

    // --- Verification ---

    verification def BrewTempTest {
        doc /* Measure extraction temperature across 10 consecutive
               brew cycles. All readings must fall between 90 and
               96 degrees Celsius. */

        objective {
            verify requirement : BrewTempReq;
        }
    }

    verification def SafetyInterlockTest {
        doc /* Attempt brew with no cup: confirm rejection.
               Place cup, retry: confirm cycle starts. */

        objective {
            verify requirement : SafetyInterlockReq;
        }
    }

    verification def BrewTimeTest {
        doc /* Time 5 consecutive brew cycles from button press
               to coffee dispensed. All must complete within
               60 seconds. */

        objective {
            verify requirement : BrewTimeReq;
        }
    }
}

Read this from the bottom up: BrewTempTest verifies BrewTempReq, which is satisfied by coffeeMachine. That is one complete loop. Every requirement has a design link (who addresses it) and an evidence link (how you check it). A reviewer can trace from any requirement to both its design rationale and its verification plan.

This is where SysML v2 earns its keep as an engineering tool. The model is not just a picture of the system – it is a queryable database of commitments. You can ask:

  • Which requirements have no satisfaction link? (Unaddressed requirements.)
  • Which requirements have no verification case? (Unverified claims.)
  • Which verification cases are not linked to any requirement? (Orphan tests.)

These are the questions that project reviews are built around. With model-based requirements, the answers come from the model itself rather than from manually maintained traceability matrices.

Requirement Decomposition

Large requirements are hard to verify directly. A common pattern is to decompose a top-level requirement into smaller, testable pieces. You can express this using nested requirements or by specializing a parent requirement.

requirement def <'REQ-PERF-001'> PerformanceReq {
    doc /* The coffee machine shall deliver acceptable
           performance across all operating conditions. */

    requirement <'REQ-PERF-001a'> startupTimeReq {
        doc /* The machine shall reach operating temperature
               within 90 seconds of power-on. */
    }

    requirement <'REQ-PERF-001b'> brewTimeReq {
        doc /* Each brew cycle shall complete within
               60 seconds of initiation. */
    }
}

The nested requirements startupTimeReq and brewTimeReq are children of PerformanceReq. Each child can have its own satisfaction and verification links. The parent captures the high-level intent; the children are the testable leaves.

Keep decomposition shallow. One or two levels is usually enough. Deep requirement hierarchies create more traceability overhead than they solve.

Use Cases and Concerns

SysML v2 includes two related concepts that provide context for requirements.

A use case describes a goal-oriented interaction with the system. It is defined with use case def:

use case def BrewEspresso {
    doc /* The user selects espresso, the machine grinds beans,
           heats water, extracts coffee, and dispenses into cup. */

    subject machine : CoffeeMachine;
    objective {
        doc /* Deliver a single espresso shot to the user. */
    }
}

Use cases sit at a higher level than actions. Where an action definition describes a sequence of steps, a use case describes a stakeholder goal. Use cases are useful for capturing “why does this behavior exist?” and for organizing requirements around user-facing scenarios.

A concern captures a stakeholder interest or worry:

concern def SafetyConcern {
    doc /* The machine must not create burn hazards or
           dispense onto the counter without a cup. */
}

Concerns are lightweight. They give requirements a “why” – a named reason for their existence. You do not need concerns for every requirement, but they help when a reviewer asks “why does this requirement exist?” and the answer involves stakeholder context that is not obvious from the requirement text alone.

Both use cases and concerns are optional. Many projects work fine with just requirements, satisfaction, and verification. Add use cases when you need to organize requirements around user goals. Add concerns when stakeholder rationale needs to be explicit.

Analysis Cases

A verification case asks a yes/no question – does the design meet the requirement? An analysis case asks a different question – what is the answer? – and returns a typed result. Use analysis def:

analysis def ThermalLossAnalysis {
    doc /* Estimate the heat lost from the brewer during a 30-second
           idle period at an ambient temperature of 20 degrees C. */

    subject brewer : Brewer;
    return lossRate : Real;
}

analysis def defines a case whose purpose is to compute something, not to check something. The body is shaped like the calc def from Chapter 9: it declares inputs (here, via the subject), does work, and returns typed outputs. The difference from a bare calc is that an analysis case carries the same envelope as a verification case – a subject, a doc-driven purpose, optional actor – so it sits inside the same traceability structure as the rest of the model.

Reach for analysis def when the engineering question is estimation (worst-case heat loss, peak load, mean time to brew) rather than compliance. The analysis case can even feed a verification case: it produces the number the verification case then checks against a requirement.

The General case Envelope

Verification, analysis, and use cases are siblings. All three are specializations of a single case def:

case def ThermalLoadProfile {
    subject brewer : Brewer;
    return idleLoad : Real;
    return heatingLoad : Real;
}

case def is the common envelope. Every case definition can carry a subject, an optional actor, an objective, and a body. verification def adds a verify requirement linkage; analysis def adds return values; use case def adds stakeholder actions. The grammar equips them with the same structure; the intent is what you choose when you pick the keyword.

For most modeling, you spell out the specific kind (verification def, analysis def, use case def) and never write the bare case def. But knowing they share an envelope pays off when you build tooling that treats all three uniformly, or when you specialize a case family of your own.

Rolling Up Verdicts Across Cases

The How Verification Verdicts Work section above defined a single verdict’s four values – Pass, Fail, Inconclusive, Error. A requirement is rarely checked once. It is checked against several verification cases, or, in a simulation-coupled verification, at every tick of a run. The individual verdicts are rolled up into one overall judgement for the requirement:

  • If every verdict is Pass, the requirement is Pass.
  • If any verdict is Fail, the requirement is Fail. A single failing observation is enough – the requirement was violated.
  • If there are no Fail verdicts but at least one Inconclusive, the requirement is Inconclusive. You cannot claim Pass when part of the evidence was undecidable.
  • If all verdicts are Error, the requirement is Error – the model itself is broken and no verification result is trustworthy until it is fixed.

This rollup is what makes verification metric-able. A project dashboard can report a pass rate per requirement (how many of its checks were Pass), flag requirements with outstanding Inconclusive verdicts, and surface the requirements carrying the most fails. The point of the four-valued verdict – and especially the Inconclusive case – is that absence of a fail is not the same as a pass. Rollup preserves that distinction at the requirement level.

Putting It Together: The Coffee Machine Grows

The coffee machine now has three layers of traceability. Here is how the pieces connect:

Structure (from Chapter 2 through Chapter 6): CoffeeMachine contains Grinder, Brewer, and WaterTank, connected through ports and interfaces.

Behavior (from Chapter 7 and Chapter 8): BrewCycle defines the action sequence. MachineStates defines the operating modes with guarded transitions.

Constraints (from Chapter 9): BrewTempSafe checks the temperature range. SafetyInterlock prevents brewing without a cup.

Requirements and verification (this chapter): BrewTempReq, SafetyInterlockReq, and BrewTimeReq capture the engineering intent. Satisfaction links connect them to the design. Verification cases connect them to evidence.

The constraints from Chapter 9 and the requirements from this chapter serve different purposes. A constraint is a rule the model enforces – it evaluates to true or false against actual values. A requirement is a commitment the project tracks – it may reference a constraint, but its primary purpose is traceability.

Consider the temperature range. In Chapter 9, you defined:

constraint def BrewTempSafe {
    in temp : Real;
    temp >= 90.0 and temp <= 96.0;
}

In this chapter, you defined:

requirement def <'REQ-TEMP-001'> BrewTempReq {
    doc /* The brewer extraction temperature shall remain
           between 90 and 96 degrees Celsius during brewing. */
    attribute temp : Real;
    require constraint { temp >= 90.0 and temp <= 96.0; }
}

These are not redundant. The constraint lives in the design and can be asserted against actual values during analysis or simulation. The requirement lives in the traceability structure and connects to satisfaction links, verification cases, and project reviews. You can have a constraint without a requirement (an internal design rule that nobody asked for) and a requirement without a constraint (a qualitative goal that cannot be formalized). When both exist for the same concern, the constraint makes the requirement checkable and the requirement makes the constraint traceable.

Visualizing Requirements and Traceability

{{#tab name="Requirements" }}
Requirements diagram: requirement blocks with satisfy and verify relationships.
{{#endtab }} {{#tab name="Traceability Matrix" }}
Traceability grid: requirements mapped to design elements and verification cases.
{{#endtab }} {{#endtabs }}

Common Mistakes

Satisfaction without verification. A satisfy link is a claim, not evidence. If you satisfy a requirement but never define a verification case for it, you have an unverified claim. Some tools will flag this, but even without tooling, make it a habit: every satisfy should have a corresponding verify somewhere.

Requirements with no subject. A requirement without a subject can still be satisfied, but it is harder for reviewers to understand what it applies to. When you know which element a requirement targets, say so with subject.

Overly deep decomposition. Splitting one requirement into ten sub-requirements creates a maintenance burden. Each sub-requirement needs its own satisfaction and verification links. Prefer fewer, testable leaf requirements over deep hierarchies.

Duplicating constraint logic in requirements. If you already have a constraint def BrewTempSafe from Chapter 9, you do not need to rewrite that logic inside the requirement. You can reference the constraint or simply note in the requirement text that the constraint captures the formal condition. Do not maintain the same expression in two places.

What You Have Built

Your coffee machine model now includes:

  • Three requirements with short IDs, prose text, subjects, and formal constraints
  • Satisfaction links from the design to each requirement
  • Verification cases with objectives that trace back to each requirement
  • A complete traceability loop: requirement to design to evidence

This is the layer that makes the model auditable. In Chapter 11, you will assemble all of these pieces – structure, behavior, constraints, requirements, and verification – into one coherent system model.

Your Complete System Model

Over the past nine chapters, you built a coffee machine model piece by piece: structure, ports, connections, actions, states, constraints, requirements, and verification. Each chapter added one concern. This chapter assembles everything into a single coherent system model.

This chapter does not teach new syntax. Instead, it shows how the pieces fit together – and what a complete, reviewable system model looks like.

The Model So Far

Here is what each chapter contributed:

  • Chapter 2 defined the core types: Grinder, Brewer, WaterTank, CoffeeMachine, plus item definitions for Water, CoffeeBeans, and BrewedCoffee.
  • Chapter 5 added typed ports to each component: beanInput and groundOutput on the grinder; waterInlet, groundInput, and brewOutput on the brewer; waterOut on the water tank.
  • Chapter 6 wired the components together with connections and typed item flows.
  • Chapter 7 defined BrewCycle as a sequence of actions with parallel branches.
  • Chapter 8 defined MachineStates with guarded transitions between operating modes.
  • Chapter 9 added BrewTempTarget (calculation) and SafetyInterlock (constraint).
  • Chapter 10 added requirements with satisfy and verify links.

Now we bring all of these into one model.

Package Organization

A complete model needs clear package organization. Here is a practical layout:

package CoffeeMachineDomain {
    import ScalarValues::*;

    // Domain vocabulary
    item def Water;
    item def CoffeeBeans;
    item def BrewedCoffee;

    enum def GrindSetting {
        fine;
        medium;
        coarse;
    }
}
package CoffeeMachineStructure {
    import ScalarValues::*;
    import CoffeeMachineDomain::*;

    port def BeanInputPort {
        in item beans : CoffeeBeans;
    }

    port def GroundCoffeePort {
        out item grounds : CoffeeBeans;
    }

    port def WaterPort {
        in item water : Water;
    }

    port def WaterSupplyPort {
        out item water : Water;
    }

    port def BrewOutputPort {
        out item coffee : BrewedCoffee;
    }

    part def Grinder {
        attribute grindSize : GrindSetting;
        port beanInput : BeanInputPort;
        port groundOutput : GroundCoffeePort;
    }

    part def Brewer {
        attribute waterTemp : Real;
        attribute brewPressure : Real;
        port waterInlet : WaterPort;
        port groundInput : BeanInputPort;
        port brewOutput : BrewOutputPort;
    }

    part def WaterTank {
        attribute currentLevel : Real;
        attribute maxCapacity : Real;
        port waterOut : WaterSupplyPort;
    }

    part def CoffeeMachine {
        part grinder : Grinder;
        part brewer : Brewer;
        part waterTank : WaterTank;
        attribute serialNumber : String;

        connect waterTank.waterOut to brewer.waterInlet;
        connect grinder.groundOutput to brewer.groundInput;
    }
}
package CoffeeMachineBehavior {
    import CoffeeMachineStructure::*;

    action def GrindBeans {
        in item beans : CoffeeBeans;
    }

    action def HeatWater;

    action def ExtractCoffee {
        in item water : Water;
        in item grounds : CoffeeBeans;
        out item coffee : BrewedCoffee;
    }

    action def DispenseCoffee {
        in item coffee : BrewedCoffee;
    }

    action def BrewCycle {
        action grind : GrindBeans;
        action heat : HeatWater;
        action extract : ExtractCoffee;
        action dispense : DispenseCoffee;

        first grind then heat;

        // After heating, extract and dispense in sequence
        first heat then extract;
        first extract then dispense;
    }

    state def Idle;
    state def Brewing;
    state def Cleaning;
    state def Error;

    state def MachineStates {
        state idle : Idle;
        state brewing : Brewing;
        state cleaning : Cleaning;
        state error : Error;

        transition idleToBrewing
            first idle
            then brewing;

        transition brewingToIdle
            first brewing
            then idle;

        transition idleToCleaning
            first idle
            then cleaning;

        transition cleaningToIdle
            first cleaning
            then idle;

        transition anyToError
            first idle
            then error;
    }
}
package CoffeeMachineAnalysis {
    import ScalarValues::*;

    calc def BrewTempTarget {
        in ambient : Real;
        return result : Real;
        result = if ambient < 18.0 ? 94.0 else 93.0;
    }

    constraint def BrewTempSafe {
        in temp : Real;
        temp >= 90.0 and temp <= 96.0;
    }

    constraint def SafetyInterlock {
        in cupDetected : Boolean;
        in brewCommand : Boolean;
        not brewCommand or cupDetected;
    }
}
package CoffeeMachineRequirements {
    import ScalarValues::*;
    import CoffeeMachineStructure::*;
    import CoffeeMachineAnalysis::*;

    requirement def <'REQ-TEMP-001'> BrewTempReq {
        doc /* The extraction temperature shall remain between 90 and 96 degrees Celsius
               during the brew cycle. */
        subject coffeeMachine : CoffeeMachine;
    }

    requirement def <'REQ-SAFE-001'> SafetyInterlockReq {
        doc /* The system shall not begin a brew cycle unless a cup is detected
               on the drip tray. */
        subject coffeeMachine : CoffeeMachine;
    }

    requirement def <'REQ-TIME-001'> BrewTimeReq {
        doc /* A standard brew cycle shall complete within 60 seconds. */
        subject coffeeMachine : CoffeeMachine;
    }
}
package CoffeeMachineVerification {
    import CoffeeMachineRequirements::*;

    verification def BrewTempTest {
        doc /* Measure extraction temperature during a standard brew cycle. */
        objective {
            verify requirement : BrewTempReq;
        }
    }

    verification def SafetyInterlockTest {
        doc /* Attempt to start a brew cycle without a cup. Confirm the system
               refuses. */
        objective {
            verify requirement : SafetyInterlockReq;
        }
    }

    verification def BrewTimeTest {
        doc /* Time a standard brew cycle from start signal to dispensing
               complete. */
        objective {
            verify requirement : BrewTimeReq;
        }
    }
}

How the Packages Connect

The packages form a clear dependency chain:

CoffeeMachineDomain          (items, enums -- no imports)
    ↓
CoffeeMachineStructure       (parts, ports, connections -- imports Domain)
    ↓
CoffeeMachineBehavior        (actions, states -- imports Structure)
    ↓
CoffeeMachineAnalysis        (calcs, constraints -- imports ScalarValues)
    ↓
CoffeeMachineRequirements    (requirements -- imports Structure + Analysis)
    ↓
CoffeeMachineVerification    (verification cases -- imports Requirements)

Each package depends only on packages above it. No circular imports. A reviewer can read the model top-down: vocabulary, then structure, then behavior, then analysis, then requirements, then verification.

Cross-Concern Traceability

The real power of a complete model is traceability across concerns. From any requirement, you can follow links to:

  1. The design element that satisfies it (via subject on the requirement)
  2. The constraint that formalizes its condition (in the Analysis package)
  3. The verification case that will produce evidence (via verify)
  4. The state or action that exercises the relevant behavior

For example, BrewTempReq traces to:

  • CoffeeMachine (the subject that must satisfy it)
  • BrewTempSafe constraint (the formal condition: 90–96 C)
  • BrewTempTest verification case (how you prove compliance)
  • BrewCycle action (the behavior under test)

This is the traceability loop that SysML v2 is designed for. Without it, requirements live in spreadsheets, design lives in diagrams, and verification lives in test plans – all disconnected. With it, everything is linked in one reviewable model.

What Makes This Model Review-Ready

A reviewer looking at this model can answer key questions quickly:

  • What is the system made of? Read CoffeeMachineStructure. Every component, port, and connection is visible.
  • What does it do? Read CoffeeMachineBehavior. The brew cycle sequence and operating modes are explicit.
  • What must it satisfy? Read CoffeeMachineRequirements. Every requirement has a subject and formal text.
  • How will we verify it? Read CoffeeMachineVerification. Every requirement has a planned verification case.
  • What constraints apply? Read CoffeeMachineAnalysis. Safety interlocks and temperature bounds are checkable.

No concern is hidden. No link is implicit. That is the goal.

The Model on Disk

Everything in this book is snippets of one model, and that model exists as files. If you cloned this repository, it is under examples/coffee-machine/ – fifteen .sysml files plus a sysml.toml, checkable with sysml check.

Reading a whole model is a different experience from reading it a construct at a time, and it is worth doing once. Here is which file goes with which chapter:

FileChapter
definitions.sysml3 – definitions and usages
package-structure.sysml4 – packages, visibility, public import
ports-and-interfaces.sysml5, 6 – ports and interface definitions
connections.sysml6 – connections and connectors
flows.sysml6 – flows and messages
actions.sysml7 – actions and control behaviour
brew-cycle-flow.sysml7 – the brew cycle alone, for diagram generation
states.sysml8 – states and transitions
calculations.sysml9 – calculations and constraints
requirements.sysml10 – requirements and verification
typing-and-specialization.sysml12 – typing, specialization, redefinition
metadata.sysml13 – metadata and documentation
views.sysml13 – viewpoints, views, renderings
orchestration.sysml16 – multi-subsystem simulation
demo-analysis.sysml10, 16 – an analysis case

Two other example projects sit beside it: examples/beverage-workspace/, the multi-project workspace Chapter 15 walks through, and examples/views-library/, eleven small models that each isolate one piece of the view chain.

A caveat so you are not surprised: views.sysml reports three diagnostics about satisfying a viewpoint. The model is correct – a viewpoint definition is a kind of requirement definition, so satisfying one is what the specification intends – and the resolver in the current tool does not accept it. The file is left as the specification says it should be written.

Exploring the Model

The complete coffee machine model can be explored from four different perspectives. Each tab shows the same underlying model through a different diagram view:

{{#tab name="Structure" }}
General view (a structural overview, BDD-equivalent): hierarchy with parts, ports, and attributes.
{{#endtab }} {{#tab name="Actions" }}
ActionFlow view: the BrewCycle with fork/join parallelism and decide/merge branching.
{{#endtab }} {{#tab name="States" }}
StateTransition view: machine modes with transitions, guards, and entry/exit actions.
{{#endtab }} {{#tab name="Requirements" }}
General view with a Requirement preset filter: traceability from requirements to design elements.
{{#endtab }} {{#endtabs }}

Extending the Model

This model is a foundation, not a finished product. Real projects extend it in several directions:

  • More requirements. Add power consumption, noise level, maintenance interval requirements.
  • More states. Add Steaming, Descaling, Standby modes with guarded transitions.
  • More actions. Add SteamMilk, RunDescale actions with their own control flow.
  • Variants. Use specialization (Chapter 12) to create EspressoMachine and DripMachine variants.
  • Governance. Use metadata and views (Chapter 13) to add review status and stakeholder-specific model slices.
  • Physical units. Use library quantities (Chapter 14) to replace Real with TemperatureValue and PressureValue.

The structure is ready for all of these. Each extension adds to the model without rewriting what exists.

Specialization and Redefinition

By now you have a complete coffee machine model: definitions, usages, ports, connections, actions, states, constraints, and requirements. Everything so far has been about building one machine. But what happens when you need a second kind of machine – one that shares most of the original design but changes a few things?

You could copy CoffeeMachine, rename it, and edit the copy. That works once. It becomes a maintenance problem the moment you fix a bug in the original and forget to propagate it to the copy. SysML v2 gives you better tools: specialization and redefinition. This chapter covers four related mechanisms for refining types without duplicating them.

Specialization

Specialization says: this new type is a kind of an existing type. It inherits everything from the general type and can add more.

The operator is :>.

package CoffeeMachineVariants {
    import CoffeeMachineDomain::*;

    part def EspressoMachine :> CoffeeMachine {
        attribute pumpPressure : Real;
    }
}

EspressoMachine :> CoffeeMachine means every EspressoMachine is a CoffeeMachine. It inherits all of CoffeeMachine’s features – the grinder, the brewer, the waterTank, the serialNumber. Then it adds pumpPressure, which only espresso machines have.

You have actually been using specialization implicitly since Chapter 2. When you write part def Grinder, it implicitly subclassifies the base library definition Parts::Part. When you write attribute waterTemp : Real, the attribute usage implicitly subsets the base library usage Attributes::attributeValues. Definitions get an implicit subclassification, usages an implicit subsetting, and writing your own explicit specialization suppresses the implicit one (SysML §7.6.8). The :> operator just makes the relationship explicit between your own types.

When to specialize

Specialize when the new type genuinely is a kind of the base type. Every instance of the specialized type should be substitutable wherever the general type is expected.

Good uses:

  • EspressoMachine :> CoffeeMachine – an espresso machine is a coffee machine.
  • PressurizedBrewer :> Brewer – a pressurized brewer is a brewer.
  • LargeWaterTank :> WaterTank – it is still a water tank, just bigger.

Bad uses:

  • Grinder :> CoffeeMachine – a grinder is not a coffee machine.
  • BrewCycle :> CoffeeMachine – an action is not a structural type.

If the “is a kind of” sentence does not sound right in plain English, specialization is probably the wrong tool.

Multiple specialization

A definition can specialize more than one general type:

part def HeatedGrinder :> Grinder, HeatedComponent {
    attribute grindTemp : Real;
}

HeatedGrinder inherits from both Grinder and HeatedComponent. This is useful when a concept belongs to two categories simultaneously. Use it sparingly – deep multiple-inheritance hierarchies become difficult to reason about.

Redefinition

Specialization adds new features. Redefinition replaces an inherited feature with a more specific version. The operator is :>>.

Suppose CoffeeMachine has a general target temperature attribute:

part def CoffeeMachine {
    attribute targetTemp : Real;
    // ... other features
}

An espresso machine needs that temperature locked to a specific value:

part def EspressoMachine :> CoffeeMachine {
    attribute brewTemp :>> targetTemp = 94.0;
}

The line attribute brewTemp :>> targetTemp = 94.0 does three things at once:

  1. It says brewTemp redefines the inherited targetTemp.
  2. It gives the feature a new name in this context: brewTemp instead of targetTemp.
  3. It binds the value 94.0.

That third one is a binding, not a suggestion: a bare = asserts the value for every instance of the type. If you want a value a particular usage can still override, write default = 94.0 instead (SysML §7.6.3, and Chapter 5).

Inside an EspressoMachine, there is no separate targetTemp alongside brewTemp. The inherited feature is brewTemp. If you access targetTemp on an EspressoMachine instance, you get the same feature that brewTemp names.

Redefinition with tighter typing

You can also redefine a feature to narrow its type:

part def CoffeeMachine {
    part brewer : Brewer;
}

part def EspressoMachine :> CoffeeMachine {
    part espressoBrewer :>> brewer : PressurizedBrewer;
}

The inherited brewer is redefined to be typed by PressurizedBrewer (which itself specializes Brewer). The redefined type must be compatible with the original – you cannot redefine a Brewer as a WaterTank. Redefinition is a kind of subsetting, and a subsetting feature’s type has to specialize the type of what it subsets (KerML §8.3.3.3.10).

When to redefine

Redefine when an inherited feature needs to be more specific in the specialized context. Common cases:

  • Binding a value: :>> targetTemp = 94.0
  • Narrowing a type: :>> brewer : PressurizedBrewer
  • Tightening multiplicity: :>> sensors [2] on a base that allows [1..*]

Do not redefine for cosmetic renaming alone. If the inherited name is fine and the type does not change, there is nothing to redefine.

Subsetting

Subsetting narrows a collection. Where redefinition replaces a feature, subsetting says: this feature is a subset of that inherited collection.

The operator is :> applied to a usage (not a definition). Context determines which relationship you get: on a definition, :> is subclassification; on a usage, it is subsetting (SysML §7.6.2, §7.6.3). The long spellings make it explicit – specializes on definitions, subsets on usages.

part def CoffeeMachine {
    part sensors : Sensor[1..*];
}

part def EspressoMachine :> CoffeeMachine {
    part tempSensors :> sensors : TemperatureSensor[1..2];
    part pressureSensors :> sensors : PressureSensor[1];
}

tempSensors :> sensors says the temperature sensors are a subset of the total sensors collection. pressureSensors :> sensors is another subset. Both are parts of the same inherited sensors group, but they carve it into named subsets with more specific types.

The key difference from redefinition: subsetting does not replace the inherited feature. The sensors collection still exists. tempSensors and pressureSensors are portions of it.

When to subset vs. redefine

  • Redefine when the specialized type has exactly one version of the inherited feature, and you want to replace it entirely.
  • Subset when the inherited feature is a collection and you want to name specific portions of it without replacing the whole.

If the base type has part brewer : Brewer (a single part), redefine it. If the base type has part sensors : Sensor[1..*] (a collection), subset it.

Conjugation

Conjugation makes a type inherit another type’s features with their input and output directions reversed (SysML §7.12.3). The operator is ~ (tilde) placed before a type name.

You saw ports in Chapter 5. A port definition declares features with directions:

port def WaterSupplyPort {
    out item water : Water;
}

When a water tank sends water out, it uses WaterSupplyPort directly. But the brewer that receives that water needs the same port shape with the direction flipped – out becomes in. Chapter 5 handled that by defining a second port type by hand: WaterPort, with in item water. Conjugation gets you the receiving side for free instead:

part def WaterTank {
    port waterOut : WaterSupplyPort;
}

part def Brewer {
    port waterInlet : ~WaterSupplyPort;
}

~WaterSupplyPort is WaterSupplyPort with all directions reversed. The out item water becomes in item water. The port shapes are structurally compatible – they carry the same items in opposite directions – which is exactly what you need for a connection.

When to conjugate

Conjugate when two components interact through complementary ports and the only difference is direction. This is the standard pattern for producer/consumer, sender/receiver, and request/response pairs.

Do not conjugate when the two sides of an interaction carry different items or have different structure. In that case, define two separate port types.

Putting It Together: The EspressoMachine

Here is a fuller example that uses specialization, redefinition, and conjugation together:

package CoffeeMachineVariants {
    import ScalarValues::*;
    import CoffeeMachineDomain::*;

    part def PressurizedBrewer :> Brewer {
        attribute boilerPressure : Real;
    }

    part def EspressoMachine :> CoffeeMachine {
        // Redefine the brewer to a pressurized variant, and pin the
        // water temperature it inherits from Brewer
        part espressoBrewer :>> brewer : PressurizedBrewer {
            attribute :>> waterTemp = 94.0;
        }

        // Add espresso-specific features
        attribute shotVolume : Real;
    }
}

Read this from top to bottom:

  1. PressurizedBrewer :> Brewer – a new brewer type that adds boilerPressure.
  2. EspressoMachine :> CoffeeMachine – inherits grinder, brewer, water tank, serial number.
  3. espressoBrewer :>> brewer – the inherited brewer part is redefined to use PressurizedBrewer.
  4. :>> waterTemp = 94.0 – the brewer’s water temperature is bound to 94.0. Note where this line sits: inside espressoBrewer, because waterTemp is a feature of Brewer, not of CoffeeMachine. A redefinition can only target a feature the enclosing type actually inherits, so attribute brewTemp :>> waterTemp written directly in EspressoMachine would fail to resolve.
  5. shotVolume – a new attribute that only espresso machines have.

That fourth line also shows a shortcut worth knowing: a redefining usage with no declared name takes the name of the feature it redefines (SysML §7.6.5), so this one is still called waterTemp.

An EspressoMachine instance has everything a CoffeeMachine has, plus the refinements. Any constraint, requirement, or verification case that references CoffeeMachine applies to EspressoMachine too, because specialization preserves that relationship.

Type hierarchy showing specialization and redefinition relationships.

Decision Guide

When you need to refine a type, choose the simplest mechanism that fits:

SituationMechanismOperatorExample
New type is a kind of existing typeSpecialization:>EspressoMachine :> CoffeeMachine
Inherited feature needs a new default or narrower typeRedefinition:>>brewTemp :>> targetTemp = 94.0
Inherited collection needs named subgroupsSubsetting:> on usagetempSensors :> sensors
Port directions need to be flippedConjugation~port waterInlet : ~WaterSupplyPort
Just adding new features to a typeSpecialization alone:>Add pumpPressure in subtype
Replacing a part with a more specific variantRedefinition:>>espressoBrewer :>> brewer
Documenting a family of named alternativesVariationvariation / variantsee below

Start at the top of this table. If specialization alone solves your problem, stop there. Reach for redefinition only when you need to change something inherited, not just add to it. Use subsetting only for genuine collections. Use conjugation only for direction inversion.

Variation

Sometimes a design has several named alternatives that share a common base by deliberate choice, rather than one refining the next. A coffee machine might offer two brew profiles – standard and intense – where neither is a refinement of the other; both are options the customer picks between.

variation <keyword> def <ChoiceName> :> <BaseName> {
    variant <keyword> <name> : <AlternativeName>;
    variant <keyword> <name> : <AlternativeName>;
}
part def BrewProfile {
    attribute targetTemp : Real;
}

part def StandardBrew :> BrewProfile;
part def IntenseBrew :> BrewProfile;

variation part def BrewProfileChoice :> BrewProfile {
    variant part standard : StandardBrew;
    variant part intense : IntenseBrew;
}

part def CoffeeMachine {
    part brewProfile : BrewProfileChoice;
}

BrewProfileChoice is a variation – a variation point, marked by the variation keyword before the kind keyword. Its members are its variants, each marked variant. A brewProfile typed by BrewProfileChoice resolves to exactly one of them per configuration.

The direction of those two keywords is what people get backwards. variation goes on the choice, not on the alternatives, and variant goes on the members inside it. Five rules follow, and the specification states all of them (SysML §7.6.7, with the formal constraints at §8.3.6.2):

  • Variant usages may only be declared inside a variation.
  • Every usage in a variation’s body is a variant, so a variation cannot carry ordinary features of its own. That is why targetTemp lives on BrewProfile and reaches BrewProfileChoice by inheritance.
  • A variation is always abstract. You never write abstract on it, and you never instantiate the choice – only a variant.
  • The kind of a variant must match the kind of its variation: variant part inside a variation part def.
  • A variation may not specialize another variation. You can build a variation on an ordinary definition, as BrewProfileChoice builds on BrewProfile, but you cannot chain choices together – a choice of choices is not a thing (validateDefinitionVariationSpecialization).

Usages can be variation points too, with the same shape – variation part transmission : Transmission { variant ...; } turns a single usage into a choice rather than a whole definition.

The point of variation is not refinement – it is alternation. Where EspressoMachine :> CoffeeMachine says every EspressoMachine is a CoffeeMachine, a variation says a given configuration picks one of several peers.

Use variation when the model genuinely enumerates alternatives the system can be configured into. When the relationship is “this is a more specific kind of that,” plain specialization is clearer; variation is the right tool only for the “choose one of these” pattern.

Elaboration: The Implicit Pass

Everything in this chapter – specialization, redefinition, subsetting, conjugation, variation – is something you write. But the model is also understood to gain relationships you did not write down. Weaving those in is called elaboration: the model is read as if a small set of implicit relationships were filled in for you.

Suppose a library declares:

part def Grinder {
    attribute rpm : Real;
}

and your model specializes it:

part def HeatedGrinder :> Grinder {
    attribute heatLevel : Real;
}

You wrote one :>, but HeatedGrinder is now understood to have both rpm (inherited from Grinder) and heatLevel. You did not redeclare rpm; elaboration supplies it. The same is true when specialization chains: declare BurrGrinder :> HeatedGrinder on top of HeatedGrinder :> Grinder, and the generalization from BurrGrinder all the way up to Grinder is implicit, so a constraint that references Grinder also sees every specialization of it as a valid operand.

The same idea fills in two more structures:

  • Library-defined features. Standard-library supertypes (such as Part) carry features the spec says every specialization gains; you don’t write them, but elaboration treats them as present.
  • Subsetting portions. When tempSensors :> sensors and pressureSensors :> sensors both subset the same collection, elaboration recognizes them as portions of the one inherited sensors collection, so they participate together in any query over sensors.

The point of elaboration is that you model in the direct relationships and the spec supplies the transitive ones. You don’t have to flatten the chain, redeclare inherited features, or enumerate library extensions – they are understood to exist because the spec says they do.

sysml-rs extends the spec here. The relationships elaboration adds – inherited features, transitive generalizations, library-provided extensions, subsetting-portion structure – are all spec. What sysml-rs chooses is when to realize them: it materializes them at elaboration time (eagerly, before the model is queried) rather than deriving them lazily on each request. The visible effect is the same; only the timing is the tool’s choice.

Common Mistakes

Specializing when composition is enough. If you just need a coffee machine with different parts inside, you do not need a new definition. Create a usage and configure it:

// Unnecessary specialization:
part def LargeCoffeeMachine :> CoffeeMachine {
    part bigTank :>> waterTank : LargeWaterTank;
}

// Often sufficient: just a configured usage
part largeMachine : CoffeeMachine {
    part bigTank :>> waterTank : LargeWaterTank;
}

The second form creates a specific usage of CoffeeMachine with a redefined water tank. No new definition needed. Specialize only when other parts of your model need to reference the new type by name – in constraints, requirements, or further specializations.

Redefining without a semantic reason. Renaming an inherited feature just because you prefer a different name adds noise to the model. Redefinition is a semantic statement: “this feature means something more specific here.” If the inherited name and type are fine, leave them alone.

Defining mirrored port types by hand. If you see two port definitions that are identical except that in and out are swapped, replace one with a conjugation. Two definitions means two places to update when the port structure changes. Conjugation keeps them automatically synchronized.

What You Have Built

The coffee machine now has a family, not just a shape. EspressoMachine specializes CoffeeMachine, redefines the brewer to a pressurized one and pins the temperature it inherits, and adds what only espresso machines have.

Four relationships did that work, and they are worth keeping straight:

  • Specialization (:> on a definition) – a more specific kind.
  • Redefinition (:>>) – an inherited feature, made more specific here.
  • Subsetting (:> on a usage) – this feature’s values are among those.
  • Conjugation (~) – the same interface with its directions reversed.

Variation is the odd one out, and deliberately so: it is alternation rather than refinement. EspressoMachine :> CoffeeMachine says every espresso machine is a coffee machine. A variation says a configuration picks one of several peers.

In Chapter 13 the model starts carrying information about itself – maturity, ownership, safety classification – and you will use that to generate views for particular readers.

Metadata and Views

Your coffee machine model is built. It has structure, behavior, states, constraints, requirements, and verification cases. But models do not exist in isolation. People need to review them, approve them, and understand them – and different people care about different things.

This chapter covers two features that serve that human side of modeling: metadata for attaching governance information to model elements, and views for presenting the right slice of the model to the right stakeholder.

Comments vs. doc vs. Metadata

You have already used // comments throughout the model. Comments are notes in the source text. A tool ignores them during analysis. They are for the person reading the .sysml file, not for the model itself.

SysML v2 provides two more layers for human-oriented information, and the distinction matters.

doc is a model-level documentation string. Unlike a comment, doc is part of the model. A tool can extract it, display it in a hover tooltip, or include it in a generated report. You place it inside the element it describes:

part def Brewer {
    doc /* Heats water and forces it through ground coffee
           under controlled pressure and temperature. */

    attribute waterTemp : Real;
    attribute brewPressure : Real;
}

Metadata is structured, queryable data attached to model elements. Review status, maturity level, safety classification, responsible team – these are not free text. They have known fields and known values, and you need to filter or report on them. Metadata is the right tool for that job.

The rule of thumb: if it must be queried, filtered, or enforced by tooling, make it metadata. If it explains intent to a human reader, use doc. If it is a local note to yourself, use a comment.

Defining Metadata

A metadata definition looks like any other definition in SysML v2. You declare it with metadata def, and it can have attributes:

package CoffeeMachineGovernance {
    import ScalarValues::*;

    metadata def ReviewStatus {
        attribute status : String;
        attribute reviewer : String;
    }

    metadata def SafetyClass {
        attribute level : String;
    }
}

ReviewStatus is now a reusable type. It says: anything that carries a review status has a status string and a reviewer string. You define it once, in one package, and every team uses the same structure.

This is important. If one team invents ReviewInfo with a state field and another invents ApprovalStatus with a currentStatus field, you end up with incompatible schemas that no tool can query uniformly. Centralize your metadata definitions early.

Annotating Elements with Metadata

To attach metadata to a model element, you use the @ annotation syntax. The annotation goes immediately before the element it describes:

package CoffeeMachineStructure {
    import ScalarValues::*;
    import CoffeeMachineGovernance::*;

    @ReviewStatus {
        :>> status = "approved";
        :>> reviewer = "Systems Team";
    }
    part def CoffeeMachine {
        doc /* Top-level system definition for the Smart Coffee Machine. */

        part grinder : Grinder;
        part brewer : Brewer;
        part waterTank : WaterTank;
        attribute serialNumber : String;
    }
}

The @ReviewStatus { ... } block annotates the CoffeeMachine definition. The :>> syntax redefines the metadata attributes with specific values. After this annotation, any tool that queries the model can determine that CoffeeMachine has been approved by the Systems Team.

You can annotate any element – definitions, usages, requirements, actions, states. The annotation always appears directly before the element:

@SafetyClass { :>> level = "SIL-2"; }
requirement def SafetyInterlockReq {
    doc /* The machine shall disable the heater if water
           temperature exceeds 100 degrees Celsius. */
}

Now the safety interlock requirement carries its safety integrity level as structured data, not as a note buried in a comment.

Multiple Annotations

An element can carry more than one annotation. Stack them:

@ReviewStatus {
    :>> status = "in-review";
    :>> reviewer = "Safety Board";
}
@SafetyClass { :>> level = "SIL-2"; }
part def Brewer {
    doc /* Heating subsystem. Under safety review. */

    attribute waterTemp : Real;
    attribute brewPressure : Real;
}

The Brewer now carries both governance metadata and safety classification. A review dashboard could show all elements where status is "in-review", or all elements classified as SIL-2.

A Maturity Tracking Example

Metadata is especially useful for tracking model maturity across a project lifecycle. Here is a pattern that covers the typical progression:

package CoffeeMachineGovernance {
    import ScalarValues::*;

    metadata def ReviewStatus {
        attribute status : String;
        attribute reviewer : String;
    }

    metadata def SafetyClass {
        attribute level : String;
    }

    metadata def MaturityLevel {
        attribute level : String;
    }
}

Then, as the model evolves, annotations track where each element stands:

@MaturityLevel { :>> level = "preliminary"; }
action def BrewCycle { /* ... */ }

@MaturityLevel { :>> level = "baselined"; }
part def WaterTank { /* ... */ }

@MaturityLevel { :>> level = "verified"; }
requirement def BrewTempReq { /* ... */ }

A project manager does not need to understand the brew cycle control flow to know that the action definition is still preliminary. The metadata tells them directly.

From Metadata to Views

Metadata tells tools about the status of individual elements. But stakeholders do not review individual elements. They review slices of the model that are relevant to their concerns. A safety engineer cares about hazards, interlocks, and safety-classified requirements. An architect cares about structural decomposition and interfaces. Showing either of them the entire model is counterproductive.

SysML v2 formalizes this with two concepts: viewpoints and views.

Viewpoints: Defining Stakeholder Concerns

A viewpoint declares what a particular stakeholder cares about. It does not select specific model elements – it describes the kind of information that matters for a particular review purpose.

package CoffeeMachineViews {
    viewpoint def SafetyViewpoint {
        doc /* Concerns: hazard mitigations, safety interlocks,
               and safety-classified requirements. */
    }

    viewpoint def ArchitectureViewpoint {
        doc /* Concerns: structural decomposition, subsystem
               interfaces, and key part definitions. */
    }
}

Think of a viewpoint as a lens. SafetyViewpoint says: when you look through this lens, you want to see safety-related elements. ArchitectureViewpoint says: through this lens, you want to see structure.

A viewpoint can also specify stakeholder to name who uses it, and purpose to explain why:

viewpoint def VerificationViewpoint {
    doc /* Requirement-to-test traceability for verification closure. */
}

Views: Selecting Model Content

A view usage is the concrete selection. It is typed by a view def, can satisfy one or more viewpoints, and uses expose to pull specific elements into the view.

Before getting into stakeholder-specific views, it helps to see the simplest possible case: one view definition, one usage, one expose. This is the running anchor we will refine through the rest of the chapter.

package CoffeeMachineViews {
    import CoffeeMachineStructure::*;

    view def CoffeeOverview;

    view overview : CoffeeOverview {
        expose CoffeeMachine;
    }
}

That’s a complete, valid view: a definition that names the kind of view, and a usage that picks up the definition and exposes the top-level part. Anything that walks the model can ask “what does the overview view contain?” and get back the CoffeeMachine element and – by transitive containment – everything inside it.

Notice which half carries the expose. The grammar is strict about this division of labour: a view definition body admits filter and render clauses (plus ordinary definition members), while expose is legal only in a view usage body. The definition is the reusable recipe – what kinds of elements, presented how. The usage is the concrete selection – these elements, from this model. Keep that split in mind through the rest of the chapter; it is the single easiest thing to get backwards. (sysml-rs’s parser currently accepts an expose inside a view def without a diagnostic – a known parser gap, not a licence. The construct is not in the grammar, and conformant tools reject it.)

Now, the more interesting case. A view usage can satisfy one or more viewpoints, and can expose more than one element. satisfy reaches a viewpoint because a ViewpointDefinition is a RequirementDefinition in the abstract syntax – satisfying a viewpoint and satisfying a requirement are the same relationship:

package CoffeeMachineViews {
    import CoffeeMachineStructure::*;
    import CoffeeMachineRequirements::*;

    viewpoint def SafetyViewpoint {
        doc /* Concerns: hazard mitigations and safety interlocks. */
    }

    viewpoint def ArchitectureViewpoint {
        doc /* Concerns: structural decomposition and interfaces. */
    }

    view def SafetyReviewView;
    view def ArchitectureReviewView;

    view safetyReview : SafetyReviewView {
        satisfy SafetyViewpoint;
        expose CoffeeMachineRequirements::SafetyInterlockReq;
        expose CoffeeMachineRequirements::BrewTempReq;
    }

    view architectureReview : ArchitectureReviewView {
        satisfy ArchitectureViewpoint;
        expose CoffeeMachineStructure::CoffeeMachine;
        expose CoffeeMachineStructure::Brewer;
        expose CoffeeMachineStructure::Grinder;
        expose CoffeeMachineStructure::WaterTank;
    }
}

expose is the key operation. It does not copy the element into the view. It references it. The safetyReview view usage satisfies SafetyViewpoint and exposes two requirements. The architectureReview view usage satisfies ArchitectureViewpoint and exposes four structural definitions. A tool rendering the safety review shows only the safety requirements – the grinder and its grind settings are nowhere in sight.

This is the critical difference between a view and a package. A package owns its members. A view exposes references to members that live elsewhere. The underlying model is unchanged. Views are projections, not copies.

Two flavours of expose: member and namespace

expose comes in two forms, and the choice is more than syntactic sugar.

view interfaceReview : ArchitectureReviewView {
    expose CoffeeMachineStructure::Brewer;          // member expose
    expose CoffeeMachineStructure::*;               // namespace expose
}

The first form is a member expose: it pulls one named element into the view. The second is a namespace expose: it pulls every member of the named namespace into the view. The trailing ::* is the cue.

Member expose is precise; namespace expose is sweeping. You reach for namespace expose when the natural unit of review is a whole package – the safety package, the requirements package, the API surface – and you do not want the view to drift behind the package as new elements are added. Once CoffeeMachineStructure::* is exposed, anything new you drop into that package is automatically visible in the view.

Both forms are subject to the same qualified-name resolution. If you misspell the qname, the view exposes nothing for that line. There is no error – the line is parsed, the qname does not resolve, and tools simply have nothing to show. That silent failure mode is one of the more common authoring mistakes; we return to it in Common Mistakes below.

Anonymous view usages

A view usage does not have to be named. When you want a one-off slice of the model – to share a screenshot in a review, to drop a quick visualization into a diagram, to test what a particular expose would surface – you can write the usage inline without giving it a name:

view scratch { expose CoffeeMachine; }

That is a complete view usage. There is no view def, no satisfaction, no rendering – just a name and a body. Tools treat it like any other view usage: they resolve the expose, project the element, and render the result. Anonymous views are the modeling equivalent of a scratch buffer. Reach for them when you want a slice now and have not yet decided whether the slice deserves a definition.

Combining Metadata and Views

Metadata and views work well together. You can use metadata annotations to mark which elements belong in which review, and then build views that surface exactly those elements:

package CoffeeMachineReview {
    import ScalarValues::*;
    import CoffeeMachineGovernance::*;
    import CoffeeMachineStructure::*;
    import CoffeeMachineRequirements::*;

    viewpoint def SafetyViewpoint {
        doc /* All elements classified SIL-2 or above,
               plus their associated requirements. */
    }

    view def SafetyReviewView;

    // The safety review exposes only safety-relevant elements
    view safetyReview : SafetyReviewView {
        satisfy SafetyViewpoint;
        expose CoffeeMachineRequirements::SafetyInterlockReq;
        expose CoffeeMachineRequirements::BrewTempReq;
        expose CoffeeMachineStructure::Brewer;
    }

    viewpoint def ArchitectureViewpoint {
        doc /* System decomposition for architecture board review. */
    }

    view def ArchitectureReviewView;

    // The architecture review exposes structural elements
    view architectureReview : ArchitectureReviewView {
        satisfy ArchitectureViewpoint;
        expose CoffeeMachineStructure::CoffeeMachine;
        expose CoffeeMachineStructure::Grinder;
        expose CoffeeMachineStructure::Brewer;
        expose CoffeeMachineStructure::WaterTank;
    }
}

Notice that Brewer appears in both views. The safety engineer sees it because it contains the heater that is safety-classified. The architect sees it because it is a major subsystem. Same element, different reasons, different context. The model does not duplicate anything.

Filtering a View with filter

expose says “pull this element (or namespace) in”. Sometimes you want the opposite shape: pull a whole namespace in, then filter out the parts that do not belong in this particular view. That is what filter is for.

Note where each half lives: expose sits in the view usage, while filter typically sits in the view definition, where it becomes part of the reusable recipe and screens whatever any usage of that definition exposes. (A usage body may carry its own filter too, for one-off narrowing.)

A filter clause is a Boolean expression evaluated once per element that the view exposes. If the expression is true, the element stays in the view; if it is false, the element is dropped.

The spec’s own filter idiom is a classification test: the @ operator asks whether the current element is classified by a metaclass or a metadata definition. That is the portable form, and it is what the standard library’s own filtered packages use:

view def PartsOnlyPortable {
    filter @SysML::PartUsage;                                  // metaclass test
}

view partsOnly : PartsOnlyPortable {
    expose CoffeeMachineStructure::*;
}

view def MandatorySafety {
    filter @SafetyClassification and SafetyClassification::level == "SIL-2";
}

view mandatorySafety : MandatorySafety {
    expose CoffeeMachineStructure::*;
}

The second form is the pattern for filtering on a metadata field: classify with @, then reach into the annotation’s attribute through a ::-qualified reference.

The simplest filters are literal:

view def AllOfIt    { filter true;  }    // accept everything
view def NoneOfIt   { filter false; }    // accept nothing

filter true and filter false are useful as scaffolding – a starting point you can sharpen.

sysml-rs extends the spec here. In addition to the spec’s classification tests, sysml-rs’s filter evaluator binds four convenience names for the element under test: self (the element), kind (its element kind, as a string), name (its declared name), and id (its identifier). These are not spec language – another tool will not resolve them. They are convenient for exploratory slices in this toolchain; use @ classification tests for filters that have to travel.

With those bindings in hand, the next step up is a predicate over kind or name:

view def PartsOnly {
    filter kind == "PartUsage";
}

view def JustTheBrewer {
    filter name == "Brewer";
}

view structureParts : PartsOnly {
    expose CoffeeMachineStructure::*;
}

view brewerOnly : JustTheBrewer {
    expose CoffeeMachineStructure::*;
}

structureParts exposes everything in CoffeeMachineStructure; its definition keeps only the elements whose kind is "PartUsage". brewerOnly exposes the same package; its definition keeps only the element named "Brewer". The kind == ... predicate is the most common shape because it lets you say “the structural slice”, “the requirements slice”, “the actions slice” without listing each element by hand.

You can stack filters. Multiple filter clauses on the same view compose as logical AND: an element passes only if every filter accepts it.

view def SafetyParts {
    filter kind == "PartUsage";          // first filter
    filter name != "Grinder";            // second filter
}

view safetyParts : SafetyParts {
    expose CoffeeMachineStructure::*;
}

safetyParts is the parts of the structure package that are not the grinder. Both filters must return true for an element to survive. Stacking is how you build up a complex viewCondition from small, readable predicates.

sysml-rs extends the spec here. The spec defines ElementFilterMembership – a Boolean expression bound to a view – but does not mandate what happens when that expression cannot be evaluated. sysml-rs’s evaluator is conservative: a filter expression that the runtime cannot reach (an unsupported operator, a reference that does not resolve, a type the evaluator does not yet handle) falls through to true. The element passes. The reasoning: a half-implemented filter that drops elements silently is more dangerous than one that admits too much. The default is over-inclusion, which the reader can see, rather than under-inclusion, which the reader cannot. Spec compliance: do not rely on this fallthrough as a contract – write filter expressions you are confident the evaluator supports, and treat any “this view shows more than I expected” surprise as a possible safe-default trip rather than a real model issue.

The expressions inside filter are real Boolean expressions, written in the same language as constraints (chapter 9). You can compose them with and, or, not; you can index members of self; you can compare id to a qualified-name literal. In practice, most filters stay close to the kind == "..." and name == "..." shape – those two predicates cover most stakeholder slices.

Renderings

A view says what to show. A rendering says how to show it. They are separate concepts because the same slice of the model can sensibly appear as a table, a tree, a diagram, or a matrix depending on who is reading it.

The spec gives you a small kit:

  • rendering def Foo; declares a rendering by name.
  • A view definition picks up a rendering with a render clause in its body. Two forms: name an existing rendering usage (render asTreeDiagram; – the standard library’s Views package ships asTextualNotation, asTreeDiagram, and asInterconnectionDiagram ready-made), or declare one inline from a rendering definition (render rendering standard : StandardRendering;). The bare-reference form is spec, but sysml-rs currently parses only the inline form; the runnable examples in this book use the inline form throughout.
  • A view usage can override its definition’s rendering by writing its own render clause in the usage body.

Here is the running coffee-machine example with a rendering attached:

package CoffeeMachineViews {
    import CoffeeMachineStructure::*;
    import CoffeeMachineRequirements::*;

    rendering def StandardRendering;

    view def SafetyReviewView {
        render rendering standard : StandardRendering;
    }

    view safetyReview : SafetyReviewView {
        satisfy SafetyViewpoint;
        expose CoffeeMachineRequirements::SafetyInterlockReq;
        expose CoffeeMachineRequirements::BrewTempReq;
    }
}

The render rendering standard : StandardRendering; line is a ViewRenderingMembership: it declares a rendering usage inside the view and types it by the rendering definition. The view uses the rendering by owning a usage of it.

A rendering definition can carry attributes – things like layout direction, paper size, page break preferences – but most authors start with an empty rendering def and let the tool’s defaults take over. The point of the rendering is the binding: the view declares its intent, and the tool that consumes the view picks up the matching rendering and presents the slice accordingly.

What Kind of Diagram Does a View Become?

You have written view def SafetyReviewView and view def CoffeeOverview. When a tool renders one of these, what shape does it choose? A flat list? A tree? A diagram with boxes and lines? The answer is determined by the view definition’s supertype.

The standard library package StandardViewDefinitions defines exactly eight standard view definitions, each corresponding to a recognizable diagram shape. When you write view def Foo :> InterconnectionView, you are saying “this view should be presented as an interconnection diagram”. When you write view def Foo :> ActionFlowView, you are saying “this view should be presented as an action-flow diagram”. The supertype is the contract.

The eight standard view definitions are:

SupertypeWhat it shows
GeneralViewAny exposed elements, as a general graph of nodes and edges
InterconnectionViewFeatures as (nested) nodes and the connections between them as edges
ActionFlowViewActions, their parameters, and the flows between them
StateTransitionViewStates and the transitions between them
SequenceViewTime-ordered event occurrences and messages on lifelines
GeometryViewSpatial items visualized in two or three dimensions
GridViewElements and relationships in a rectangular grid (tables, matrices)
BrowserViewThe hierarchical membership structure (containment tree)

Pick the supertype that matches the question the view is answering. “Show me how the parts connect” → InterconnectionView. “Show me the order of actions in the brew cycle” → ActionFlowView. “Show me the state machine” → StateTransitionView. The supertype is the difference between a view that lands as a useful diagram and one that lands as a generic table.

Two diagram kinds you might expect are deliberately not on the list. There is no UseCaseView and no RequirementView in the standard library. For those, the library’s own documentation on GeneralView gives the recipe: specialize GeneralView and narrow it with a filter. A requirement view, for example, is a GeneralView filtered to requirement elements:

view def RequirementOverview :> GeneralView {
    filter @SysML::RequirementDefinition or @SysML::RequirementUsage
        or @SysML::SatisfyRequirementUsage;
}

The same pattern gives you a package view, a definition-and-usage view, or any other slice-by-kind: GeneralView plus the metaclass filter naming the kinds you want.

view def CoffeeStructure :> InterconnectionView;
view def BrewSequence :> ActionFlowView;

view def HeaterStateMachine :> StateTransitionView {
    filter kind == "StateUsage" or kind == "TransitionUsage";
}

view structure : CoffeeStructure { expose CoffeeMachine; }
view brewOrder : BrewSequence { expose BrewCycle; }
view heaterStates : HeaterStateMachine { expose Brewer; }

Each of those views asks for model elements wrapped in a different presentation contract. The tool consuming them picks the appropriate diagram engine based on the supertype. You must write the :> explicitly. A view def Foo with no supertype is treated as the most general kind of view – GeneralView is the root of the family – and renders as a generic projection; the renderer does not infer a kind from the view’s name.

The Stakeholder → Viewpoint → View → Rendering Chain

Everything in this chapter so far has been one element at a time. Pulling them together: SysML v2’s view model is a single chain that runs from a person’s concern at one end to a rendered artifact at the other.

Stakeholder      a person or role with a question
     │
     │ has
     ▼
Viewpoint        the question, captured as a requirement
     │           (`viewpoint def` is a kind of `requirement def`)
     │ satisfied by
     ▼
View Usage       the concrete view — exposes the content and declares
     │           which viewpoints it satisfies
     │           (typed by a View Definition: the reusable recipe —
     │            supertype + filters + rendering;
     │            `view def` is a kind of `part def`)
     │ rendered through
     ▼
Rendering Usage  the visual style applied
     │
     │ produces
     ▼
View artifact    what a human sees

Here is the full chain in 25 lines:

package SafetyReview {
    import CoffeeMachineStructure::*;

    // Stakeholder — a role
    part def SafetyEngineer;

    // Viewpoint — the concern, expressed as a requirement
    viewpoint def SafetyConcernViewpoint {
        stakeholder reviewer : SafetyEngineer;
        doc /* Reviewers must see all SIL-2-classified parts. */
    }

    // Rendering — the style
    rendering def DarkInterconnection;

    // View Definition — the recipe
    view def SafetySIL2View :> InterconnectionView {
        render rendering dark : DarkInterconnection;
        filter @SafetyClassification and SafetyClassification::level == "SIL-2";
    }

    // View Usage — the concrete instance
    view sil2Diagram : SafetySIL2View {
        satisfy SafetyConcernViewpoint;
        expose CoffeeMachine;
    }
}

Reading top to bottom: a SafetyEngineer is the stakeholder; the SafetyConcernViewpoint captures their concern; the DarkInterconnection is a reusable rendering style; the SafetySIL2View is the recipe that filters to SIL-2 parts only and applies the dark rendering; the sil2Diagram is the concrete view a tool actually renders – it exposes the content and declares that it satisfies the viewpoint.

(Strictly, satisfy is an ordinary usage member, so the grammar admits it inside a view def body as well – the standard library’s own abstract View definition carries one. But the satisfaction claim reads most naturally on the concrete view, and that is where this book puts it.)

You do not have to use every link in the chain. Most authors start at the View end (view def + view usage) and add viewpoints + stakeholders only when traceability becomes a requirement. But when you do need traceability, every link is queryable: “which views satisfy this viewpoint”, “which viewpoints concern this stakeholder”, “what is the rendering for this view”. The chain is the trace.

How the Editor Surfaces Views

Two panels matter when you are working with views in an editor.

The Views panel lists every declared view def and view usage in the open file (or workspace). Clicking one renders the view exactly as the author specified — supertype-driven shape, exposes resolved, filters applied, rendering bound. The Views panel is the place to see views; if a view is not listed there, the model has not declared it.

The Outline panel shows the containment tree of the open file — packages, parts, ports, attributes, requirements. The Outline is not a view in the spec sense. It is a code outline, like the symbol pane in any IDE. It exists so you can navigate the model file structurally; it does not respect viewpoints, exposes, or filters. Clicking a node in the Outline reveals it in the source; it does not produce a diagram.

The two panels answer different questions:

  • “What views does this model publish?” → Views panel.
  • “What is in this file structurally?” → Outline panel.

If you find yourself wanting “show me this file as an interconnection diagram” with no view def in the model, the spec-faithful answer is to write a one-line scratch view and let the Views panel render it:

view scratch : InterconnectionView { expose CoffeeMachine; }

This is the same vocabulary the rest of the chapter uses — no special command, no editor mode, no separate API. Once the scratch view is in the file, it appears in the Views panel like any other view; rename it when you want to keep it. The editor may offer a “create view from selection” affordance that inserts this snippet for you, but the artifact it produces is a real view usage you can edit, name, and commit.

When to Add Metadata and Views

You do not need metadata or views from day one. They become valuable when the model is shared beyond the original author:

  • Add doc early. As soon as someone else will read your definitions, document intent. It costs almost nothing and saves review time.
  • Add metadata when governance starts. When the project has formal reviews, maturity gates, or safety classifications, metadata ensures that status is tracked in the model, not in a spreadsheet that drifts out of sync.
  • Add views when stakeholders diverge. When different reviewers need different slices, views prevent the “here is the entire model, find what you need” problem.

If you are working alone on a small model, comments and doc may be all you need. If you are on a team with safety reviews, architecture boards, and verification milestones, metadata and views are essential governance infrastructure.

Visualizing Views and Metadata

{{#tab name="Views" }}
Browser view: view definitions and their contents in the model tree.
{{#endtab }} {{#tab name="Metadata" }}
General view: metadata annotations attached to model elements.
{{#endtab }} {{#endtabs }}

Common Mistakes

Storing review status only in comments. A comment like // approved by safety board 2024-03-15 is invisible to tooling. If you need to query which elements are approved, that information must be metadata. Use @ReviewStatus { :>> status = "approved"; } instead.

Creating one giant view for all stakeholders. A view that exposes everything is just the model again. The value of a view is in what it leaves out. Keep views small and purpose-driven. If a stakeholder does not need to see the grinder to make their decision, do not expose it.

Duplicating model content in views. Views reference elements with expose. They do not redefine them. If you find yourself copying attribute lists or requirement text into a view, you are doing it wrong. Expose the original element and let the tool present it.

Proliferating metadata definitions. If the safety team defines SafetyLevel and the quality team defines SafetyRating with the same fields, you have a schema drift problem. Agree on shared metadata definitions early and put them in a governance package that everyone imports.

Writing a filter predicate the evaluator cannot reach. A filter expression that the runtime cannot evaluate falls through to true and admits every element (sysml-rs’s safe-default behaviour). If a view “shows more than you expected”, the most common cause is a filter expression with an unsupported operator, an unresolved reference, or a binding the evaluator does not yet handle. Stick to predicates over kind, name, id, and self’s direct attributes – those compose cleanly. If you see a stranger surface in a view, suspect a tripped safe-default before suspecting the model.

Misspelling an expose qname. expose CoffeeMachineRequirements::SaftyInterlockReq; (note the typo) does not produce an error. The qname does not resolve, the line exposes nothing, and the view comes up smaller than you expected. There is no “expose a name that does not exist” diagnostic – the line parses fine, it just has no effect. When a view comes up missing an element, check the qnames first.

Treating render as a styling hook. render rendering standard : StandardRendering; selects a RenderingDefinition. It does not set fonts, colours, or themes. If you want a view to look a particular way, the visual style is a property of the tool that consumes the view, not of the view itself. Use render to declare presentation intent (“table”, “diagram”, “matrix”) and let the tool’s visual style apply.

Expecting the Outline panel to behave like a SysML view. The Outline lists the structural contents of a file; it is not a view in the spec sense. It does not respect viewpoints, exposes, or filters, and clicking a node does not produce a diagram. If you want a diagram, the model must declare a view def and a view usage. The Views panel is where declared views live; the Outline is where you navigate the source.

Forgetting the supertype on view def. A bare view def Foo; is legal but renders as a generic projection – the most general view, GeneralView. For an interconnection, action-flow, or state-transition diagram, you must specialize from the matching standard definition: view def Foo :> InterconnectionView { ... }. There is no name-based fallback — a view called FooInterconnection without an explicit :> InterconnectionView still renders as a general view.

Putting expose inside a view def body. The grammar admits expose only in a view usage body; a view definition body takes filter and render. This one is easy to get wrong because sysml-rs currently parses an expose inside a view def without a diagnostic – the file looks fine, but the construct has no spec meaning and conformant tools reject it. “It parses” is not evidence here. Keep the recipe (supertype, filters, rendering) in the definition and the selection (expose) in the usage.

What You Have Built

The model can now describe itself and present itself.

Metadata attaches structured facts about elements – maturity, owner, safety classification – distinct from doc, which carries prose for a human, and from comments, which are not in the model at all.

Views turn that into something a reader can use. The chain runs stakeholder → viewpoint → view → rendering: someone has a concern, a viewpoint states it, a view selects the elements that answer it, and a rendering says what form the answer takes. expose (in the usage) chooses content, filter (in the definition) narrows it, and the definition’s supertype decides what kind of diagram you get.

The thing most worth remembering is that a view is part of the model. It is not a diagram someone drew and saved – it is a declaration of what a particular reader needs, which is why it can be reviewed, versioned, and regenerated when the model changes.

Chapter 14 turns outward to what you did not write: the standard library, and how much of the language is defined in it.

Libraries

You have been using libraries since Chapter 2. Every time you wrote import ScalarValues::*, you reached into a model library that ships with the specification. This chapter makes that implicit knowledge explicit: what those libraries are, how they are layered, how to find things in them, and how to write one of your own.

The single most useful idea in this chapter is that the library is not a convenience bundle shipped alongside the language. A large part of what the language means is defined by library models, written in the same language you write. That is a deliberate design decision, and it is the reason this chapter is worth your time.

Why the Library Is Part of the Language

KerML is built in three syntactic layers, each adding more specific constructs on the one below (KerML §6.1):

  • The Root Layer holds the most general constructs for structuring models: elements, relationships, annotations, packaging.
  • The Core Layer holds the most general constructs whose semantics are based on classification.
  • The Kernel Layer provides commonly needed modeling capabilities, such as associations and behavior.

Only the Core Layer is grounded directly, by interpreting it in mathematical logic. The Kernel Layer is different: its meaning comes from relating its abstract syntax constructs to model elements in the Kernel Semantic Library, which is written in KerML itself (KerML §6.1). A model you write reuses elements of that library in order to have any semantics at all, and the library models are what state the conditions under which a real system conforms to your model.

The specification is explicit about the payoff, and it is aimed squarely at you: because the library models are expressed in the same language as user models, engineers and tool builders can inspect them to understand what a model actually specifies (KerML §6.1).

So when this chapter tells you to go and read a library file, that is not a debugging tip. It is the intended way to find out what your own model means.

The Library Is Written in the Language

Here is the actual declaration of Real, from ScalarValues.kerml in the shipped standard library (the doc comments are elided):

standard library package ScalarValues {
    private import Base::DataValue;

    abstract datatype ScalarValue specializes DataValue;
    datatype Boolean specializes ScalarValue;
    datatype String specializes ScalarValue;
    abstract datatype NumericalValue specializes ScalarValue;

    abstract datatype Number specializes NumericalValue;
    datatype Complex specializes Number;
    datatype Real specializes Complex;
    datatype Rational specializes Real;
    datatype Integer specializes Rational;
    datatype Natural specializes Integer;
    datatype Positive specializes Natural;
}

That is the whole file. Eleven datatype declarations and one private import.

A model library is a collection of models meant to be reused across many user models, marked with the library keyword so tooling can recognize its contents as library elements (KerML §7.4.14). The libraries that ship with the specification go one step further and add standard, a marker reserved for the kernel and systems libraries and other recognized standard libraries (KerML §7.4.14).

This explains something you may have wondered about. Real is not a reserved word. It is not in the grammar. It is an ordinary datatype declared in an ordinary package, and you import it for exactly the same reason you import CoffeeBeans from your own package – because it lives somewhere else. Misspell it and you get an unresolved name, not a syntax error. Shadow it with a local declaration and the local one wins.

The same is true all the way down. part def Grinder works because Parts.sysml in the Systems Model Library declares abstract part def Part :> Item;, and your definition picks that up implicitly (SysML §9.1; see Chapter 12). Arithmetic works because RealFunctions.kerml declares functions named '+' and '*'.

The clearest case is ordering. When you write a succession in an action body – grind, then brew – and give it no explicit type, what you have created is a connector typed by a library association. Occurrences.kerml declares it:

assoc all HappensBefore specializes HappensLink, Without { <doc and end features> }

An ordering constraint, which certainly feels like it ought to be a grammar rule, turns out to be an association in a library file you can open and read. Nothing here is built in that could have been written down instead.

The Three Layers

The libraries are stacked in three layers, from general to specific.

LayerWhere it is specifiedWhat lives there
Kernel Model LibraryKerML Clause 9The Semantic Library (§9.2), the Data Type Library (§9.3), the Function Library (§9.4)
Systems Model LibrarySysML §9.2Parts, Items, Ports, Attributes, Actions, States, Requirements, Views, and 13 more
Domain LibrariesSysML §9.3 – §9.8Metadata, Analysis, Cause and Effect, Requirement Derivation, Geometry, Quantities and Units

The Kernel Model Library is the foundation. The Semantic Library gives every element its meaning – Base, Occurrences, Performances, Transfers, Links, Objects and the rest (KerML §9.2). The Data Type Library holds the values: ScalarValues, Collections, VectorValues (KerML §9.3). The Function Library holds everything an expression can call (KerML §9.4). You name the Data Type Library constantly and the other two almost never.

The Systems Model Library is SysML’s own layer, and it is what turns a KerML classifier into a part, an action, or a requirement. You rarely import from it by hand, because every definition and usage you write already specializes something in it implicitly. part def Grinder picks up Parts::Part whether you ask for it or not.

Domain Libraries are the ones you import deliberately. They model concepts from domains of particular importance in systems engineering, and they are normative – available in every conformant model, not an optional add-on (SysML §9.1). Six of them ship: the Metadata Domain Library (§9.3), Analysis (§9.4), Cause and Effect (§9.5), Requirement Derivation (§9.6), Geometry (§9.7), and Quantities and Units (§9.8).

Loading is a tooling concern. In sysml-rs the kernel and systems layers load in every project with no configuration, while the domain libraries are opt-in through the [stdlib] section of sysml.toml. That split is the toolchain’s, not the language’s – the spec calls all six domain libraries normative. Chapter 15 covers the manifest.

What a Wildcard Import Actually Brings In

You have written import ScalarValues::* in every chapter. Now you can say precisely what it does, because you have seen the file. It brings in eleven names:

ScalarValue, Boolean, String, NumericalValue, Number, Complex, Real, Rational, Integer, Natural, Positive.

It does not bring in DataValue, even though ScalarValues imports it, because that import is written private import Base::DataValue. Which brings us to the rule that matters most when you start writing libraries of your own.

<visibility> import <QualifiedName>::*;
library package CoffeeIndustryTypes {
    private import ScalarValues::*;
    public import ISQ::CelsiusTemperatureValue;

    enum def RoastLevel {
        light;
        medium;
        dark;
    }

    item def CoffeeBeans {
        attribute roast : RoastLevel;
        attribute origin : String;
        attribute recommendedBrewTemp : CelsiusTemperatureValue;
    }
}

An import has its own visibility, independent of the visibility of the things it imports. A public import re-exports: anyone who imports CoffeeIndustryTypes also gets CelsiusTemperatureValue. A private import does not: String is usable inside CoffeeIndustryTypes but does not leak out of it.

The trap is the default. An import written with no visibility keyword at all is private (KerML §7.2.5.4). So this consumer does not compile the way you might expect:

package CoffeeMachineModel {
    import CoffeeIndustryTypes::*;
    import ScalarValues::*;

    part def Grinder {
        attribute currentBeans : CoffeeBeans;
        attribute targetTemp : CelsiusTemperatureValue;
    }
}

CoffeeBeans and RoastLevel arrive because they are owned public members. CelsiusTemperatureValue arrives only because the library re-exported it with public import. Real and String do not arrive through CoffeeIndustryTypes at all – hence the second import line. If you are writing a library and you want consumers to get a type you depend on, you must say public import. The standard library is meticulous about this: ISQ.sysml writes private import ScalarValues::Real; for its internal need and public import ISQBase::*; for the twelve ISO 80000 parts it exists to aggregate.

The other thing to know about wildcards is what happens on a collision. If an imported name clashes with an owned member, or with a visible name from another imported namespace, the conflicting membership is hidden – it is simply excluded from the importing namespace (KerML §7.2.5.4). There is no error. Two wildcard imports that both export Temperature will quietly cancel each other out, and the failure surfaces later as an unresolved name at the point of use. This is the concrete cost of a wildcard import, and it is why the advice from Chapter 4 hardens as a model grows: wildcard while exploring, explicit once it matters.

A false positive to expect. sysml-rs currently reports IM006import references unknown standard library namespace – for any single-member import from a standard library package, including import ScalarValues::Real;. It fires only in workspace mode (sysml inspect --workspace); the same file checked on its own is clean, and wildcard forms like import ISQ::* are never flagged. The check is reading the final segment of a membership import as though it named a package. The language is fine here and so is your model – import ScalarValues::Real is exactly the form Chapter 4 recommends. Do not restructure your imports to silence it.

There is a third form worth knowing, because the library documentation uses it:

import <QualifiedName>::**;

The ::** suffix makes the import recursive – it imports the namespace, its visible members, and then recurses into each imported member that is itself a namespace (KerML §7.2.5.4). Reach for it rarely. import ISQ::** is a very large hammer, as the next section makes clear.

Quantities and Units

attribute waterTemp : Real says almost nothing. Celsius, Fahrenheit, kelvin? The Quantities and Units Domain Library (SysML §9.8) exists to close that gap, and it is the most practical thing in this chapter.

Two packages do the work. ISQ gives you the quantity types – the International System of Quantities from ISO/IEC 80000. SI gives you the units. A dimensioned literal pairs a number with a unit:

<number> [ <unit> ]
package CoffeeMachineWithUnits {
    import ISQ::CelsiusTemperatureValue;
    import ISQ::PressureValue;
    import ISQ::VolumeValue;
    import ISQ::DurationValue;
    import SI::*;

    part def Brewer {
        attribute waterTemp : CelsiusTemperatureValue;
        attribute brewPressure : PressureValue = 9.0 [SI::Pa];
        attribute brewTime : DurationValue = 25.0 [SI::s];
    }

    part def WaterTank {
        attribute currentLevel : VolumeValue;
        attribute maxCapacity : VolumeValue = 1.5 [SI::L];
    }
}

The square brackets are not array syntax. [ is a genuine operator, declared in the library as calc def '[' { in num: Number[1]; in mRef: ScalarMeasurementReference[1]; return quantity : ScalarQuantityValue[1]; } in QuantityCalculations. It takes a number and a measurement reference and constructs a quantity value. The specification uses the same form in an accept action: accept after 30 [SI::s] (SysML §8.4.13.6).

SI::Pa, SI::s, SI::L are the unit short names. SI.sysml declares each unit with both, as in attribute <Pa> pascal : PressureUnit = N/m^2; – so SI::Pa and SI::pascal name the same thing, and the unit’s definition in terms of the base units is right there in the model.

Watch the temperature names

This is worth a paragraph because it will bite you. There is no ISQ::TemperatureValue declared anywhere. The name resolves, but only as an alias: ISQThermodynamics.sysml contains alias TemperatureValue for ThermodynamicTemperatureValue;. Thermodynamic temperature is measured in kelvin. So attribute waterTemp : ISQ::TemperatureValue = 93.0 [SI::K] describes water 180 degrees below freezing.

There are three temperature quantities, and the distinction between them is real physics rather than library clutter:

package TempOptions {
    import ISQ::ThermodynamicTemperatureValue;
    import ISQ::CelsiusTemperatureValue;
    import ISQ::TemperatureDifferenceValue;
    import SI::*;

    part def Brewer {
        attribute targetTemp : ThermodynamicTemperatureValue = 366.15 [SI::K];
        attribute elementRise : TemperatureDifferenceValue = 6.0 [SI::'°C'];
        attribute brewTemp : CelsiusTemperatureValue = 93.0 [SI::'°C_abs'];
    }
}

ThermodynamicTemperatureValue (ISQBase.sysml) is absolute temperature in kelvin. CelsiusTemperatureValue (ISQThermodynamics.sysml) is the Celsius reading. TemperatureDifferenceValue is declared in ISQ.sysml itself, and its doc comment explains why it has to exist separately: a difference of 6 °C is not the same kind of quantity as a temperature of 6 °C.

The unit names repay a close look, because they are how the library encodes that difference. SI::'°C' is typed TemperatureDifferenceUnit, and it converts to kelvin with a factor of 1. SI::'°C_abs' is not a unit at all: it is typed IntervalScale, which sits under MeasurementScale rather than MeasurementUnit, and it carries an explicit zero shift against the kelvin scale.

The reason for that split is visible in the library. UnitConversion declares exactly three features – referenceUnit, conversionFactor, isExact. There is no offset anywhere in it, so unit conversion is purely multiplicative. An absolute Celsius reading needs a zero shift, so it cannot be expressed as a MeasurementUnit, and the library models it as a scale instead. Celsius is an interval scale, not a ratio scale, and the type hierarchy says so.

The 273.15 is not missing, either – it is inside the '°C_abs' declaration, as a placement of one coordinate frame relative to another:

private attribute zeroDegreeCelsiusInKelvin: ThermodynamicTemperatureValue = 273.15 [K];
attribute zeroDegreeCelsiusToKelvinShift : CoordinateFramePlacement :>> transformation {
    :>> source = K; :>> origin = zeroDegreeCelsiusInKelvin;
}

That is worth noticing, because it reframes what looks like a gap. The library has not forgotten about Celsius. It has decided that a scale with a shifted origin is a coordinate frame placement, not a unit conversion – which is precisely why Celsius cannot be another derived unit in the manner of km or kPa.

Quote both names – ° is not an identifier character, so SI::'°C' needs the single quotes.

Note that SI ships no instance typed CelsiusTemperatureUnit, the type that CelsiusTemperatureValue redefines its mRef to. The interval scale is what you use in practice, and sysml-rs accepts it. SI.sysml is candid about gaps of this kind in its own header: “This is a representative but not yet complete list of measurement units.” When you need a unit the library does not define, declare it in your own library package rather than waiting for one to appear.

The scale of it

Some sense of proportion, since it bears on how you import. ISQ is almost entirely an aggregator: twelve public imports, one for base quantities and eleven for the numbered ISO/IEC 80000 parts, plus the three temperature-difference declarations it owns. Those twelve packages declare 945 attribute defs between them, of which 524 are distinct *Value quantity types, plus 250 aliases. SI adds 255 units. SI also does public import ISQ::*, so import SI::* pulls every quantity type in behind the units.

That is fine – name resolution does not care about volume – but it is a lot of surface for a collision to hide in. If your model needs four quantity types, import four quantity types.

What sysml-rs actually checks. The runtime carries dimensions through expressions and reports mismatches as UQ001. Given attribute confused : DurationValue = brewPressure * brewTime; it reports: attribute ‘confused’ declares dimension [T] but its default value has dimension [L^-1·M·T^-1]. Two limits are worth knowing. A bare literal is not compared against its attribute’s type, so attribute brewPressure : PressureValue = 9.0 [SI::s]; passes silently; the check fires on expressions and on references to other attributes. And mixing dimensions under + yields an unconstrained result rather than an error, so brewPressure + brewTime is accepted. Dimensional checking here catches real mistakes but is not yet a proof of dimensional soundness.

Finding Things in the Library

The question you will actually have is “does something for this already exist, and what is it called?” You do not need to guess. The library is source on disk, in libraries/standard/, split into library.kernel/, library.systems/, and library.domain/.

Two habits answer almost everything.

Grep the library. Functions are declared two different ways, so search for both. KerML uses function with no def; SysML uses calc def:

grep -rnE "(function|calc def) '?sqrt'?" libraries/standard/
library.domain/Quantities and Units/QuantityCalculations.sysml:46:	calc def sqrt{ in x: ScalarQuantityValue[1]; return : ScalarQuantityValue[1]; }
library.kernel/RealFunctions.kerml:39:	function sqrt{ in x: Real[1]; return : Real[1]; }

Two answers, and the signatures tell you which one applies to which argument type. The '? in that pattern matters: many library names are quoted, and a plain grep "function sqrt" would have missed nothing here but will give you false negatives elsewhere.

For a quantity type, search the units directory and search for aliases separately, or you will conclude a name does not exist when it does:

grep -rn 'TemperatureValue' 'libraries/standard/library.domain/Quantities and Units/'

Evaluate the name. sysml eval runs a single expression with no model file, which confirms in about a second that a function exists and behaves the way you think:

sysml eval 'sqrt(16.0)'      # 4
sysml eval 'max(3.0, 7.5)'   # 7.5
sysml eval 'round(3.5)'      # 4
sysml eval 'deg(3.14159)'    # 179.9998479605043
sysml eval 'size(1..3)'      # 3

Two caveats on eval. It tells you what this toolchain evaluates, which is a subset of what the library declares. StringFunctions declares Length, but sysml eval 'Length("espresso")' reports unknown function: Length – the name is real, the evaluator has not implemented it. And eval has no model in scope, so anything needing a type or a feature reference has to go in a file instead. Read the library for what the language declares; use eval for what you can run today.

One habit not to rely on: a clean parse. sysml inspect --diagnostics does not report unresolved import names, so a file full of misspelled library types comes back with zero diagnostics. Confirm names against the library, not against a green parse.

Reading a kernel library file

Open a file under library.systems/ and it looks like the SysML you have been writing all book. Open one under library.kernel/ and the first line may not. The kernel libraries are written in KerML, one layer below SysML, and they use declaration keywords SysML models never need. Here is enough to stop being surprised by them.

KerMLWhat it declaresReal use
structA structure. SysML’s part def and item def sit on this.abstract struct Object specializes Occurrence
assocAn association – a relationship as a classifier.assoc all HappensBefore specializes HappensLink, Without
assoc structAn association that is also a structure, so the link itself has features.abstract assoc struct LinkObject specializes Link, Object
functionA function. This is the KerML spelling; SysML writes calc def.function sqrt{ in x: Real[1]; return : Real[1]; }
behaviorA behavior. SysML’s action def sits on this.abstract behavior Performance specializes Occurrence
invAn invariant – a constraint that must hold.inv { flattenedSize == size(elements) }
boolA boolean expression, not a boolean type.bool guard[*] subsets enclosedPerformances;

bool is the one most likely to mislead, so it is worth being explicit: the grammar production is BooleanExpression, and bool introduces an expression that yields a boolean. It is not a shorthand for the Boolean datatype.

You will also meet disjoint, which asserts that two types can have no common instances:

abstract behavior Performance specializes Occurrence disjoint from Object { <body> }

That says a performance is never an object – something happening is not something existing. It reads oddly the first time and then becomes one of the more useful things in the semantic library.

Three further keywords exist for writing a relationship as a standalone, separately named declaration rather than inline: subclassifier X specializes Y, disjoining <name> disjoint X from Y, and inverting <name> inverse X of Y. They are rare – across all 36 kernel library files, subclassifier is used once and the other two never. If you meet them, they are the same relationships in a different shape.

That is the whole surprise. The kernel libraries are not a different language; they are the layer SysML’s own keywords are defined on top of, which is exactly what KerML §6.1 says they are.

The Function Library

The libraries are not only a type vocabulary. They also declare every function an expression can call:

package TankMath {
    import ScalarValues::*;

    part def WaterTank {
        attribute maxCapacity : Real default = 1500.0;
        attribute currentLevel : Real := 0.0;

        attribute headroom : Real = max(0.0, maxCapacity - currentLevel);
        attribute fillFraction : Real = currentLevel / maxCapacity;
    }
}

max is not built into the language. It is declared in RealFunctions, and it is in scope because the Function Library loads with every project. Here is the useful subset, with the package each name actually comes from:

GroupPackageNames
Real arithmeticRealFunctionsabs, min, max, sqrt, floor, round, sum, product, re, im, arg
Cross-numericNumericalFunctionsisZero, isUnit, abs, min, max, sum, product, sum0, product1
ConversionsRealFunctions, IntegerFunctions, RationalFunctionsToInteger, ToRational, ToReal, ToNatural, ToString
TrigonometricTrigFunctionssin, cos, tan, cot, arcsin, arccos, arctan, deg, rad
StringsStringFunctionsLength, Substring, ToString
SequencesSequenceFunctionssize, isEmpty, notEmpty, includes, excludes, union, intersection, including, excluding, subsequence, head, tail, last
CollectionsCollectionFunctionssize, isEmpty, notEmpty, contains, containsAll, head, tail, last
Iteration and filteringControlFunctionsselect, selectOne, collect, reject, reduce, forAll, exists, allTrue, anyTrue, minimize, maximize
OccurrencesOccurrenceFunctionsisDuring, create, destroy, addNew, addNewAt
QuantitiesQuantityCalculationsConvertQuantity, ToDimensionOneValue, plus dimension-aware abs, min, max, sqrt, sum, product

Two things to keep straight.

First, arithmetic, ordering and equality are written as operators, not as function calls. Exponentiation is ** or ^; ordering is < > <= >=; inequality is !=. There is no pow, no lessThan, no notEquals – reach for the operator. Appendix A has the full operator table.

But – and this is the interesting part – those operators are library functions. RealFunctions declares function '**' and function '<'. BaseFunctions declares function '==', function 'istype', function 'as' and function 'meta'. ControlFunctions declares function 'if', function 'and', function 'or', function 'implies'. The names are quoted because they are not identifiers, and that is the only reason a grep for function istype comes back empty while function 'istype' finds it. So the operators are not a separate mechanism bolted onto the grammar; they are library functions with special syntax, which is why QuantityCalculations can declare its own '+' and give addition a dimension-aware meaning for quantity values.

Second, that table is a subset, not an inventory. Each number type has its own package (IntegerFunctions, NaturalFunctions, RationalFunctions, ComplexFunctions, VectorFunctions), and there are more for booleans, data values and scalars. Look for a library name before writing your own calc def.

Writing Your Own Library Package

Everything above applies to libraries you write. The declaration takes one extra keyword:

library package <Name> { ... }
library package CoffeeIndustryTypes {
    private import ScalarValues::*;
    public import ISQ::CelsiusTemperatureValue;

    enum def RoastLevel {
        light;
        medium;
        dark;
    }

    item def CoffeeBeans {
        attribute roast : RoastLevel;
        attribute origin : String;
        attribute recommendedBrewTemp : CelsiusTemperatureValue;
    }
}

library marks the package as reusable vocabulary rather than part of one particular design, which lets tooling treat everything inside it as a library element (KerML §7.4.14). Define domain vocabulary once here, import it everywhere else, and the definitions cannot drift apart.

Three things to get right when you write one:

  • Re-export what your consumers need. public import for anything that appears in a signature they will use; private import for your internal dependencies. Getting this backwards produces a library whose types nobody can name.
  • Keep the export surface small. Your consumers will wildcard-import you. Every public name is a potential silent collision.
  • Do not write standard. standard library package is reserved for the kernel and systems libraries and other recognized standard libraries (KerML §7.4.14). It is not a quality marker for your own work.

Libraries the Runtime Recognizes

A handful of library packages are structural entry points: specialize from them and a simulation runtime knows what your model means without further configuration. They span two layers, which is worth keeping straight when you go looking for them on disk.

From the Analysis Domain Library (SysML §9.4):

PackageWhat it provides
StateSpaceRepresentationThe ODE pattern: calc def GetDerivative and calc def GetOutput to subtype, action def ContinuousStateSpaceDynamics to specialize (see Chapter 16)
TradeStudiesTradeStudy, EvaluationFunction, MinimizeObjective, MaximizeObjective
SampledFunctionsSampledFunction, SamplePair, and the Domain / Range / Sample / Interpolate calculations
AnalysisToolingExactly two declarations, both metadata: metadata def ToolExecution and metadata def ToolVariable, for tying a model to an external analysis tool

From the Kernel Model Library (KerML §9.2):

PackageWhat it provides
OccurrencesOccurrence, Life, and the snapshots / startShot / endShot structure – anything with temporal extent
ClocksClock, BasicClock, and the universalClock feature
SpatialFramesSpatialFrame, CartesianSpatialFrame, and the defaultFrame feature
PerformancesThe Performance and Evaluation hierarchy behind actions and expressions

Typing for dynamics. In sysml-rs, when the simulation runtime sees connections between ISQ-typed ports it classifies each port’s role in a bond graph – effort, flow, displacement, momentum – and synthesizes the conservation laws from the dimensions. Nobody writes the balance equations by hand; the runtime reads them off the types. The rule of thumb is to type any attribute that participates in dynamics with an ISQ quantity rather than Real. Appendix E has the details.

What You Have Built

You now know what the standard library is, and why it is not optional. It is roughly ninety files of KerML and SysML source, layered as kernel, systems, and domain, and a large share of the language’s meaning is defined there rather than in the grammar – which is why the specification expects you to read it. You can say exactly what import ScalarValues::* brings into scope and why import without a visibility keyword does not re-export. You can attach real dimensions to the coffee machine’s attributes with ISQ and SI, you know which of the three temperature types you actually want, and you know how far your tool’s dimensional checking goes. You can open a kernel file without being stopped by assoc or inv. And when you need a function, you have two ways to find out whether it already exists rather than writing a calc def the library already has.

The remaining question is where the library comes from – which libraries a project loads, how versions get pinned, and how you ship your own library package so somebody else can depend on it. That is Chapter 15.

Projects and Workspaces

Two Questions That Sound Alike

In Chapter 4, you organized model elements into packages and made names visible across package boundaries with import. In Chapter 14, you saw that a large share of the language’s meaning lives in standard libraries your model draws on. Both chapters kept quiet about a practical question: when you write import BeverageTypes::*, how does a tool know where BeverageTypes lives – which file, which directory, which version?

The honest answer is that these are two different questions, and only one of them belongs to SysML v2.

  • “What does this name mean here?” is a language question. KerML defines name resolution precisely: resolution starts in the local namespace and searches outward through containing namespaces (KerML §7.2.5.1), and import makes another namespace’s visible members available under shorter names (KerML §7.2.5.4). Every conformant tool must answer this question the same way.
  • “Which files and projects should be loaded so those names exist at all?” is a tooling question. The specification defines resolution over the elements a tool has loaded; it does not say how the tool decides what to load. Manifests, dependency declarations, version pins, lock files, and workspace layouts are each tool’s own answer, and they differ between tools.

Keep the two apart and this chapter is short. An import is part of your model and travels with it. A dependency declaration is part of your project setup and does not.

import Is Language; a Dependency Is Not

Here is the boundary in one example. The model side is SysML v2 and means the same thing in every conformant tool:

package BeverageTypes {
    item def Water;
    item def CoffeeBeans;
}

package CoffeeParts {
    private import BeverageTypes::*;

    part def WaterTank {
        item water : Water;
    }
}

The import says: inside CoffeeParts, the visible members of BeverageTypes may be used by their unqualified names. It says nothing about disk layout. BeverageTypes could be in the same file, another file, another project, or an archive fetched from a server – the import is equally correct in all four cases, and equally meaningless if no loaded element provides the name.

The project side, by contrast, lives in a tool manifest. In sysml-rs it looks like this:

[dependencies]
beverage-types = { path = "../beverage-types" }

That line is not SysML v2. It is an instruction to one particular toolchain about where to find another project so that its packages become loadable. Another SysML v2 tool will express the same intent with a different file, or a repository configuration, or an IDE project – and your model’s import lines do not change when it does.

import BeverageTypes::*beverage-types = { path = ... }
Lives inyour .sysml model sourcea tool manifest
Defined byKerML §7.2.5.4the tool
Answerswhat a name means in a namespacewhere a project’s sources come from
Portable across toolsyesno

The practical rule: if removing a line changes what your model means, it is language. If removing it changes what your tool can find, it is tooling.

Projects, Commits, and Interchange

The language stops at name resolution, but OMG does standardize two things above the level of a single file, and both are worth knowing at concept level.

Projects and commits. The SysML v2 specification suite includes a standard API for model repositories, organized around a small set of concepts: a project is a top-level container for a model, a branch is a named line of development within it, and a commit is an immutable snapshot of the model at a point in time. These are interchange notions – they let two tools talk about “the same model at the same version” without sharing an implementation. Appendix C covers the API surface itself.

Interchange artifacts. As Chapter 1 noted, model interchange is normative: a project travels as a .kpar archive, and the textual files inside it use the .sysml extension. A .kpar bundles the project’s source files with a .project.json manifest that records the project’s identity and the libraries and projects it uses. These are artifacts tools produce and consume so that models can cross tool boundaries; in day-to-day modelling you author .sysml files and let your toolchain generate the interchange form when you publish.

That is the whole standardized story: names resolve by KerML’s rules, projects and commits identify model versions, and .kpar carries a project between tools. Everything else – how dependencies are declared, how versions are pinned and cached, how several projects are grouped into a workspace – is toolchain territory.

Working with Projects in sysml-rs

sysml-rs tooling – documented on the portal. Project and workspace mechanics in sysml-rs are product documentation, not language, and their canonical home is the sysml-rs documentation portal. Start there for the current behaviour; the pages below are kept up to date with the tool.

What Travels and What Does Not

Every construct in the previous fourteen chapters is SysML v2 and travels with your model. The way this toolchain names, versions, and locates projects does not travel – another SysML v2 tool answers the same needs differently. What does carry across tools is the concept layer: KerML’s name-resolution rules, the project/commit vocabulary, and the .kpar interchange form. Learn the concepts here; look up the mechanics on the portal for whichever tool you are using.

Chapter 16 is the last one, and it asks what it means for a model like this to run – what a run is, how a state machine advances, and where a continuous ODE fits alongside discrete behaviour.

Running Your Model

The previous chapters taught you how to write a SysML v2 model: parts, actions, states, expressions, constraints, requirements, verification cases, metadata, and views. Everything so far has been structural – it describes what your coffee machine is. Even the state machine in Chapter 8 was a description, not an execution. Even the constraints in Chapter 9 were inert.

This chapter is about the other half. A SysML v2 model can also run. The same attribute waterTemp : Real that was a static description in Chapter 5 becomes a value that changes over time. The same state heating that was a static node in Chapter 8 becomes a state the machine actually enters and leaves. The same constraint that was a static obligation in Chapter 9 becomes a verdict that flips when a value moves.

This is not a separate language. There is no “simulation SysML” to learn. The constructs you already know describe the dynamic behaviour. What this chapter teaches is how to read your own model once it starts running, and the handful of authoring patterns that make a model run cleanly.

A note on scope. The concepts here are language: the run-as-occurrence framing, the state-space pattern, binding override, and the verdict stream are things any SysML v2 author should understand. The mechanics – which solver picks up your ODE, what the tool’s run surface looks like, how a trace is encoded, which commands to type – are sysml-rs’s runtime, and their canonical documentation is the sysml-rs portal: the runtime and CLI workflows. Blockquotes in this chapter mark the boundary.

16.A What It Means for a SysML v2 Model to Run

Start with the distinction that everything else depends on:

the model     a file (or workspace) of declarations. Has a tree shape,
              cross-references, types. Has no time and no current value.

a run of it   one trajectory through that model. Has a clock, a current
              value for every observable, a history, and a current state
              for every state machine.

The model is not the run. The model is what you wrote. A run is one execution of it. One model has many possible runs.

The language already has vocabulary for this, in KerML rather than SysML. A definition classifies; the thing classified is an occurrence, and clause 9.2.4.2.13 of the KerML specification puts it plainly: “An Occurrence is Anything that happens over time and space (the four physical dimensions). Occurrences can be portions of another Occurrence within time and space, including slices in time, leading to snapshots that take zero time.”

That sentence is the whole abstraction. A run is an occurrence. A tick is a snapshot of it. Asking “what is waterTemp?” of the model is a category error; you ask it of a snapshot.

Authors coming from static tools blur the two. They edit the model when they want to change one run; they talk about the answer when there are many. Hold the distinction and three later patterns become obvious: overriding a binding (§16.C) changes the run, not the model; verification (§16.D) checks a run; a Monte Carlo sweep is N runs over one model.

A run you can read

Here is a discrete run, end to end. The model is a three-state machine whose states write to shared attributes on entry – nothing in it is written for simulation; entry; then idle;, accept startGrind, and entry action are the Chapter 8 constructs:

state def GrinderController {
    doc /* Controls the burr grinder.
    * Writes: grindWeight, grinderRunning
    */

    entry; then idle;

    state idle {
        entry action { grinderRunning = 0; }
    }

    state grinding {
        entry action { grinderRunning = 1; grindWeight = 0; }
    }

    state dosed {
        entry action { grinderRunning = 0; grindWeight = 18.5; }
    }

    transition idle_to_grinding first idle accept startGrind then grinding;
    transition grinding_to_dosed first grinding accept doseReached then dosed;
    transition dosed_to_idle first dosed accept clearDose then idle;
}

Drive a run of it with the event sequence startGrind, doseReached, clearDose and read what comes back as a run rather than as a diagram. The run starts in idle – that is the entry; then idle; succession resolved. At each point, the set of available transitions is the set enabled from the state the run is currently in – not from the state definition, from the run. startGrind moves it to grinding, whose entry action sets grinderRunning to 1 and grindWeight to 0; doseReached moves it to dosed, where grindWeight becomes 18.5; clearDose returns it to idle. That pair of numbers – grindWeight was 0 at one tick and 18.5 at the next – exists nowhere in the model. It belongs to this run. The run is a reading of the Chapter 8 declarations.

Whatever tool you use, a run offers roughly four operations, and everything later in this chapter is built on them: step it forward one tick; run it until a horizon, a guard, or you stop it; reset it, returning time to t=0 and every observable to its declared initial value; and fork it, so two runs share a past and diverge from here.

sysml-rs extends the spec here. The specification defines the elements of a model and their semantics. It does not prescribe a run loop or a session abstraction, and it defines no command-line interface. Step, run, reset and fork are sysml-rs’s session API; another conformant tool will do these things under other names, or not run models at all. How to start, drive, and inspect a run in sysml-rs is documented on the portal: the runtime and CLI workflows.

16.B ODE-Bearing Models – The Language Story

The biggest reason a model has to run rather than just be analyzed is that it contains differential equations. Boiler water does not jump from 22 degrees to 92; it warms along a thermal curve. Conductor current does not appear instantaneously; it rises along an inductor curve.

SysML v2 has a standard-library answer, and it needs no metadata and no tool-specific annotation. The shape is:

part def <Thing> {
    attribute  <parameters>              // constants of the run
    out attribute <stateVar> default <x0>   // integrated over time

    action def <Dynamics> :> ContinuousStateSpaceDynamics {
        calc def <D> :> GetDerivative { return d<stateVar>dt = <rhs>; }
        calc def <O> :> GetOutput     { return <name>      = <expr>; }
        event occurrence <e> : ZeroCrossingEventDef;
    }
}

Here is that shape as a real, running file – a vessel heated at a constant rate past boiling:

private import StateSpaceRepresentation::*;

part def HeatedVessel {
    // Parameters
    attribute heaterRate : Real default 5.0;
    attribute boilingPoint : Real default 100.0;

    // ODE state variable
    out attribute temperature : Real default 20.0;

    action def HeatingDynamics :> ContinuousStateSpaceDynamics {
        // Derivative: dT/dt = heaterRate
        calc def TemperatureDerivative :> GetDerivative {
            return dTdt = heaterRate;
        }

        // Algebraic output: overshoot above boiling point
        calc def OvershootOutput :> GetOutput {
            return overshoot = temperature - boilingPoint;
        }

        event occurrence boilingReached : ZeroCrossingEventDef;
    }
}

Four things in that file carry the meaning, and each has a name:

  • An out attribute on the part is a state variable: a value the run integrates rather than one you assign. Its default is the initial condition.
  • A calc def specializing GetDerivative is a state equation. Its body is the right-hand side of dx/dt = f(x, u).
  • A calc def specializing GetOutput is an output equation: an algebraic function of the state, computed each tick, never integrated. y = g(x, u).
  • An action def specializing ContinuousStateSpaceDynamics is the envelope that holds them and marks the part’s behaviour as continuous.

All four come from StateSpaceRepresentation, a standard library package, and the specification devotes clause 9.4.4 to it. Its overview is worth quoting for one warning it contains: state-space representation describes “a set of state variables whose evolution by a state equation (note that this is a different conception of ‘state’ than used in the behavioral state modeling constructs described in 7.18)”. A state variable and a state machine state are different things. temperature is the former; heating is the latter. This chapter uses both, and they meet in the hybrid pattern below.

ZeroCrossingEventDef is also from that package. Clause 9.4.4.2 explains why it exists: solvers, “especially variable-step ones, need to identify such points for precise integration”, and an implementation “may notify zero-crossings with these event occurrences”. Declaring one says this run has a boundary the integrator should locate, not fire this callback.

Tool note: name your return variable after the state. sysml-rs matches a derivative to the state variable it integrates by name, so write return dwaterTempdt for waterTemp, and pick state names where none is a substring of another. The matching rule and its edge cases are documented on the portal under the runtime.

The coffee-machine boiler

Cast in the running example, with the constraints from Chapter 9 alongside:

package BoilerHybrid {
    private import ScalarValues::*;
    private import StateSpaceRepresentation::*;

    part def Boiler {
        attribute heaterPower : Real default 1500.0;
        attribute ambientTemp : Real default 20.0;
        attribute thermalMass : Real default 1500.0;
        attribute heatLossCoeff : Real default 4.5;
        attribute readyTemp : Real default 92.0;

        out attribute waterTemp : Real default 22.0;

        action def BoilerFieldDynamics :> ContinuousStateSpaceDynamics {
            calc def WaterTempDerivative :> GetDerivative {
                return dwaterTempdt =
                    (heaterPower - heatLossCoeff * (waterTemp - ambientTemp))
                    / thermalMass;
            }
            calc def HeadroomOutput :> GetOutput {
                return headroom = readyTemp - waterTemp;
            }
            event occurrence readyReached : ZeroCrossingEventDef;
        }
    }

    constraint def HeaterRated {
        heaterPower > 0.0 and heaterPower <= 2000.0
    }
    constraint def PositiveThermalMass {
        thermalMass > 0.0
    }
    constraint def TempInRange {
        waterTemp >= 0.0 and waterTemp <= 105.0
    }
}

You can read the derivative’s value at any state by hand, which is the cheapest way to sanity-check a right-hand side before running anything. Cold, at 22 degrees, (1500.0 - 4.5 * (22.0 - 20.0)) / 1500.0 is 0.994 – near full rate. At temperature, 92 degrees, (1500.0 - 4.5 * (92.0 - 20.0)) / 1500.0 is 0.784 – losses biting. The derivative falls as the water warms, which is what a first-order thermal lag should do. That check costs nothing and catches sign errors and misplaced parentheses, and any expression evaluator will do it – yours, or the one described in the portal’s CLI workflows.

When the ODE depends on a state machine

Real systems are hybrid: an ODE describes the continuous evolution, and a state machine selects which regime applies. A boiler is the textbook case. The state variable waterTemp is one variable; whether the heater is driving it is a state-machine question.

You compose the two by writing them next to each other and letting the guards read the state variable:

state def BoilerMode {
    in attribute waterTemp : Real;
    in attribute readyTemp : Real;
    in attribute standbyTemp : Real;

    state idle;
    state heating;

    entry; then heating;

    transition heating_to_idle
        first heating
        accept when waterTemp >= readyTemp
        then idle;

    transition idle_to_heating
        first idle
        accept when waterTemp <= standbyTemp
        then heating;
}

Two constructs are doing the work. The in attribute declarations are how the state machine names the continuous values it depends on; they are bound to the part’s state variables of the same name when the model is assembled. And accept when <expression> is a change trigger: the transition fires when the expression becomes true, rather than on a received event. A change trigger over a state variable is exactly the zero crossing that ZeroCrossingEventDef announces.

The same pattern scales. A full-size hybrid model – a reciprocating pump, say – gives its cycle state machine in attributes for each continuous value it needs (stroke, velocity, exposure), and every cycle transition is a located crossing of one of them:

transition compress_to_discharge
    first compress
    accept when velocity < 0.0
    then discharge;

Its dynamics envelope supplies one :> GetDerivative calc per state variable and a handful of :> GetOutput calcs for the shared algebraic chain. The distribution of labour is the general one: derivatives say how the state moves, outputs say what it means, guards say when the regime changes.

sysml-rs extends the spec here. Given a calc def :> GetDerivative inside an action def :> ContinuousStateSpaceDynamics, sysml-rs auto-detects the ODE and picks an integrator; when a change trigger mentions a state variable it locates the crossing with sub-tick precision rather than at the next discrete step. Neither is authored. Solver selection and how continuous runs are driven are documented on the portal under the runtime.

A note on what’s gone. Older sysml-rs material and example models declared ODEs with @ToolVariable { derivative = "..." } and @ToolExecution { toolName = "builtin:ode-rk45" }. All of that is removed. Those two metadata definitions are real – they are the whole content of the standard library’s AnalysisTooling package, and they mean “dispatch this action to an external tool” – but they are not how you write an ODE. If you find a model that still uses them for one, rewrite it with the state-space pattern above: AnalysisTooling makes a model tool-specific, StateSpaceRepresentation does not.

16.C One Model, Many Runs: Overriding a Binding

You have a boiler model. Someone asks: what if the water came in at 110 degrees, could that happen? You would like to answer without editing the file, because editing means committing to a value, re-checking everything, and telling everyone who depended on the old one.

What you want is to override a binding for one run:

same model + binding B1  ->  run 1  ->  answer 1
same model + binding B2  ->  run 2  ->  answer 2

The language framing is already familiar. Every calc def is an equation. Every in binding is an input substitution. Every :>> redefinition is a substitution you wrote into the source. What is new at run time is only that the substitution does not require editing the source.

With the boiler model from §16.B, a run at declared values satisfies all three constraints: HeaterRated, PositiveThermalMass, and TempInRange all pass. Re-evaluate the same file with the single binding waterTemp = 110.0 overridden, and TempInRangewaterTemp >= 0.0 and waterTemp <= 105.0 – fails while the other two still pass. Two answers, one model, nothing edited. That is the whole idea, and it scales in three directions you may already use:

  • What-if analysis overrides one binding and re-evaluates interactively.
  • Trade studies sweep a binding across a range and re-evaluate at each point.
  • Monte Carlo samples a binding from a distribution and re-evaluates across N draws.

All three are one override repeated. Think of every calc as an equation, every binding as an input, and every override as a substitution, and it does not matter whether the override comes from a UI knob, an API call, or a sweep script.

sysml-rs extends the spec here. The override mechanism – a flag on the constraint checker, an interactive equations workbench that lists the model’s equations and exposes their bindings as editable fields, and the sweep APIs – is tooling, and all of its entry points go through the same path, so they agree. See the portal’s runtime and CLI workflows pages.

16.D Sensing What Happened – Verdicts and Traces

The run finished. The boiler heated, the mode flipped, a constraint changed verdict. The question that matters is rarely “what was the value at tick 35” but “why”.

Verdicts as live observations

Chapter 9 introduced assert constraint, which produces a verdict at evaluation time. Chapter 10 aggregated verdicts into verification outcomes. Both treated a verdict as a discrete artifact: you evaluated, you got an answer.

In a run it is not discrete. A constraint over a state variable is re-decided as the run advances, so one constraint becomes a stream of verdicts, each carrying the operand values that were live when it fired. The TempInRange failure in §16.C is one sample from that stream: the sample where waterTemp was 110.0.

The verdict is four-valued, not two. The specification’s VerdictKind (clause 9.2.17.2.2) enumerates exactly pass, fail, inconclusive and error, and inconclusive is not failure: it means the constraint could not be decided. That happens whenever an operand has no value, which is common mid-authoring:

constraint def ReadyTempSane {
    readyTemp <= 100.0
}
constraint def PumpPressureOk {
    pumpPressure >= 9.0        // pumpPressure is not declared anywhere
}

Evaluate these and the honest reading is “one passed, none failed, one inconclusive”: ReadyTempSane passes, and PumpPressureOk cannot be decided because pumpPressure names nothing. A tool that scored the second as a failure would send you hunting for a physics bug that is really a missing declaration; one that scored it as a pass would be lying. When you consume verdicts programmatically, insist on the four-valued kind, not a boolean.

sysml-rs extends the spec here. That an asserted constraint yields a four-valued verdict is spec (Chapter 10). How often it is evaluated is not. sysml-rs re-evaluates every asserted constraint on every tick of a run, because constraints are cheap and a dense verdict stream makes a better trace; another tool might evaluate once at the end, or only when asked. The verdict wire format and the verification commands are on the portal under the runtime and CLI workflows.

From a verdict to a verification case

A verification case rolls the stream up into one verdict for a requirement. The idiom is the one from Chapter 10, and it reads the run’s values as free names:

part def Boiler {
    out attribute waterTemp : Real default 22.0;
}

requirement def ReachesBrewTemp {
    require constraint atTemperature {
        waterTemp >= 92.0
    }
}

verification def BrewTempCase {
    subject boiler;
    objective brewTempObjective {
        verify requirement brewTempCheck : ReachesBrewTemp;
    }
}

Run against the declared initial condition, the boiler has not heated yet, so BrewTempCase fails. Override the binding to waterTemp = 95.0, as in §16.C, and the same case passes. Note that waterTemp in atTemperature is a free name resolved from the run, not a feature of the requirement. That is what makes the same requirement reusable across runs – and it is why an un-run model reports the constraint as inconclusive (waterTemp has no value yet) rather than issuing a verdict. Inconclusive, again, is the right answer there.

Causal trace

Every value change in a run has a cause. A constraint flipped because a state variable crossed a threshold; the variable crossed because the integrator stepped a derivative; the derivative was non-zero because a transition switched the active regime; the transition fired because a change trigger became true. Those are causal links, and a runtime can record them and walk them backwards.

A useful cause is often a *non-*event. Guard a brew transition on a value another subsystem writes:

transition standby_to_brewing
    first standby
    accept startBrew
    if machineReady > 0
    then brewing;

Drive the brew controller with startBrew on its own and it does not move: machineReady is still 0, because nothing has run the boiler. “The brew did not start because machineReady was 0” is the answer you want, and it is only available because the guard names a shared attribute rather than testing an anonymous expression.

Which is the whole language-level lesson: the trace is only as good as the identifiers in your model. A trace that says “heating_to_idle fired because waterTemp reached readyTemp” is readable; “t_3 changed attr_17” is not, and no tooling can repair it. The cost of skipping a name is visible immediately. Write your constraints in the anonymous nested form, constraint { boilerTemp < 100 }, and the verdicts come back with nothing in the name column:

[PASS] : machineReady > 0
[FAIL] : waterPressure >= 8 and waterPressure <= 10

Ten constraints, ten blank labels, three failures you now have to locate by reading expressions. Name your transitions, name your constraints, name the return variable of every calc. That is not decoration.

Breakpoints

A breakpoint stops a run at an event so you can look at it. The events worth stopping on are the ones the language already names:

  • entering or leaving a state
  • a transition firing
  • an action being invoked
  • a constraint flipping verdict
  • a state variable crossing a threshold

You do not author breakpoints inside the model; they are decorations on a run, set from outside, and another tool reading the same model will not see them. So the authoring work that makes breakpoints useful is the work that makes traces useful: clear state names, named transitions, named constraints.

sysml-rs extends the spec here. The causal recorder, its query shape, and the breakpoint variants are runtime features, not specification. They are documented on the portal under the runtime.

How These Patterns Compose

The four sections stand alone but interlock:

  • A model with an ODE (§16.B) needs a run (§16.A) to evolve at all.
  • A run can be re-asked with a different binding (§16.C), which is how you get from one trajectory to a trade study.
  • A run emits a verdict stream (§16.D), and a causal trace walks back from any verdict to its cause.
  • A verification case (Chapter 10) rolls the stream into one answer about a requirement.

The boiler exercises all four. WaterTempDerivative evolves waterTemp; BoilerMode selects whether the heater is driving it; overriding heaterPower answers “what if the element were bigger”; TempInRange says whether the water stayed safe; BrewTempCase says whether the requirement held. You did not learn a new language for any of it – you wrote out attribute, state, calc def ... :> GetDerivative, accept when, and constraint def. The same declarations that were a static description in earlier chapters are a running system here. The runtime only reads them.

Where the Mechanics Live

sysml-rs tooling – documented on the portal. Everything behind the blockquotes in this chapter – sessions and the run loop, solver auto-selection, binding overrides and sweeps, the verdict wire format, causal-trace queries, breakpoints, and the command surface that drives them – is product documentation, and its canonical home is the sysml-rs portal: the runtime and CLI workflows.

If you are writing a model you intend to run, you should not need to reach for those. Occurrence and snapshot, state variable and state, derivative and output, binding and override, verdict stream and trace – that vocabulary is enough.

Appendix A: Syntax Quick Reference

This appendix is a compact index of SysML v2 syntax forms. For each construct family you will find the canonical declaration pattern, a one-line description of purpose, and a pointer to the chapter that explains the semantics in full. Use it as a quick lookup when you know what you want to write but cannot remember the exact keyword order.

Each code block is a minimal but valid syntax template. Angle-bracket placeholders like <Name> are things you supply. Items in [...] are optional. Items separated by | are alternatives.


Operators and Relationship Punctuation

Every relationship in SysML v2 is written as a punctuation token after a name. This table covers all the tokens you will encounter in declarations.

TokenLong formMeaning
:defined byType a feature: names the classifier the feature conforms to
:>specializes / subsetsSpecialize a type or subset a feature
:>>redefinesRedefine (override) an inherited feature
::>referencesReference-subsetting: a non-owning reference to another feature
=>crossesCross-subsetting: coordinates the two ends of a binary connection definition
~(none)Conjugated port typing: port p : ~P types a port by the conjugate of P (flips in/out)
=(feature value)Bind a feature value (default or fixed)
:=(initial value)Set an initial value that may change at runtime
default =(default value)Set a default value that specializations may override
default :=(default initial)Set a default initial value

The long forms are the SysML spellings. KerML spells : as typed by and has a general conjugates keyword on type declarations; neither is a SysML keyword. Appendix G lists the full symbol/keyword pairs with the metamodel class each one creates.

Examples

// Typing: the feature waterTemp is of type Real
attribute waterTemp : Real;

// Subsetting: grinder subsets parts (narrows the inherited feature)
part grinder : Grinder :> parts;

// Redefining: espressoBrewTemp narrows and renames waterTemp
attribute espressoBrewTemp :>> waterTemp;

// Reference-subsetting: a non-owning reference to the primary sensor
ref primarySensor ::> sensors;

// Conjugation: ~WaterPort flips the direction of all port features
port def WaterPort;    // conjugate is ~WaterPort, generated automatically

// Fixed value
attribute maxTemp : Real = 96.0;

// Initial value (may change)
attribute currentTemp : Real := 20.0;

// Default value (overridable)
attribute targetTemp : Real default = 93.0;

Multiplicity

Multiplicity constrains how many instances of a feature may exist. Write it in brackets after the feature name (or after its type).

SyntaxMeaning
[1]Exactly one (default for most features)
[0..1]Zero or one (optional)
[1..*]One or more
[*]Zero or more (unbounded)
[2]Exactly two
[1..4]Between one and four
// Two sensors, exactly
part sensors : CupSensor [2];

// Optional steam wand
part steamWand : SteamWand [0..1];

// Any number of logged events
item events : LogEvent [*];

Feature Modifiers

These keywords add properties to a feature declaration. Most are prefixes that appear before the feature keyword; ordered and nonunique are the exception – they follow the multiplicity.

ModifierMeaning
abstractFeature or definition has no direct instances; must be specialized
inInput direction (for action parameters)
outOutput direction (for action parameters)
inoutBidirectional (for action parameters)
refNon-owning reference usage (does not own the referenced element)
derivedValue computed from other features; not directly settable
constantValue stays the same for the entire existence of the featuring instance (KerML files spell it const)
variationMarks a definition or usage as a variation point whose variant members are the allowed choices
orderedSequence order of feature values is significant (written after the multiplicity)
nonuniqueDuplicate values are permitted (written after the multiplicity)
varValue may vary over time (KerML files only)
endMarks a connector end feature
// Abstract part: every concrete machine must specialize this
abstract part def Machine;

// Input parameter
in attribute targetPressure : Real;

// Derived attribute computed from sub-parts
derived attribute totalMass : Real;

// Ordered list of brew steps (`ordered` follows the multiplicity)
ref action brewSteps : BrewStep [*] ordered;

Visibility

Visibility controls whether a member is accessible outside its owning namespace.

KeywordMeaning
publicAccessible everywhere (default for package members)
privateAccessible only within the owning namespace
protectedInherited by specializations of the owning definition or usage, but not visible outside; on other namespaces (e.g. package members) it is equivalent to private
package CoffeeMachineDomain {
    public part def CoffeeMachine { ... }    // visible outside
    private part def InternalBoard { ... }   // hidden from importers
}

Documentation and Comments

// Line note -- discarded by the parser

//* Block note, also discarded.
    It runs to the closing marker, so always close it. */

/* A bare block comment IS model data: it becomes a Comment element
   whose body is this text */

doc /* Documentation attached to the owning element as model data */

comment about CoffeeMachine
    /* Standalone annotating comment -- can target any element */

// and //* are hidden terminals (hidden(WS, ML_NOTE, SL_NOTE)) – the parser throws them away, so nothing downstream can see them. /* ... */ is not hidden: alone it becomes a Comment element, and after doc it becomes documentation on the owning element.

Mind the closing marker: ML_NOTE is defined as '//*' -> '*/', so a //* note runs until the next */ anywhere later in the file. Leave one unclosed and it silently swallows everything up to the next block comment. Appendix H covers doc versus //* and when to reach for each.


Group 1: Packages and Namespaces

Packages organize model elements into named scopes.

// Plain package
package <Name> {
    <members>
}

// Library package (marks reusable standard content)
library package <Name> {
    <members>
}

// Standard library package (normative standard libraries only)
standard library package <Name> {
    <members>
}

Grammar: SysML.xtext lines 183-208

See: Chapter 4: Packages and Imports


Group 2: Imports and Aliases

Imports bring names from other namespaces into scope.

// Import a single named member
import <Package>::<Name>;

// Import all public members of a package
import <Package>::*;

// Import all members recursively (including nested packages)
import <Package>::*::**;

// Private import (imported names stay private to this namespace)
private import <Package>::*;

// Alias: give a new local name to an existing element
alias <LocalName> for <Package>::<OriginalName>;

Grammar: SysML.xtext lines 234-298

See: Chapter 4: Packages and Imports


Group 3: Definitions and Usages

Every major element kind has a paired definition (reusable type) and usage (instance within a context). The pattern is uniform across all element families.

// General pattern
<keyword> def <Name> [specialization] {
    <members>
}

<keyword> <name> [: <Type>] [specialization] [= <value>];
<keyword> <name> [: <Type>] [specialization] {
    <members>
}

The definition declares a reusable classifier. The usage declares an occurrence of that classifier within an owning definition.

Grammar: SysML.xtext lines 303-330; KerML.xtext lines 460-558

See: Chapter 3: Definitions and Usages


Group 4: Typing and Specialization

These four relationships control how types relate to each other.

// Typing (feature conforms to a classifier)
part grinder : Grinder;

// Specialization (subtype extends a supertype)
part def EspressoMachine :> CoffeeMachine { ... }

// Subsetting (a feature narrows an inherited feature)
part grinder : Grinder :> components;

// Redefinition (override an inherited feature with a new name or type)
part espressoGrinder :>> grinder;

// Conjugation (type a port by the conjugate of a port definition)
port waterReturn : ~WaterPort;

Grammar: KerML.xtext lines 332-626

See: Chapter 12: Specialization and Redefinition


Group 5: Structural Constructs

Attribute

Holds a data value (scalar, string, quantity, etc.).

// Definition
attribute def <Name> [: <SuperType>] { <members> }

// Usage
attribute <name> : <Type> [multiplicity] [= <value>];
attribute def Temperature :> Real;
attribute waterTemp : Temperature = 93.0;

Grammar: SysML.xtext lines 739-749

Enumeration

A type whose values are a fixed set of named literals.

enum def <Name> {
    <literal>;
    <literal>;
}
enum def DrinkSize {
    small;
    medium;
    large;
}

Grammar: SysML.xtext lines 764-787

Item

A thing that can flow through the system (material, energy, signal, data).

// Definition
item def <Name> [: <SuperType>] { <members> }

// Usage
item <name> : <Type> [multiplicity];
item def Water;
item def BrewedCoffee;
item waterSupply : Water [1];

Grammar: SysML.xtext lines 910-916

See: Chapter 5: Structure

Part

A physical or logical component that composes a system.

// Definition
part def <Name> [:> <SuperType>] { <members> }

// Usage
part <name> : <Type> [multiplicity];
part def CoffeeMachine {
    part grinder : Grinder [1];
    part brewer  : Brewer  [1];
}
part myCoffeeMachine : CoffeeMachine;

Grammar: SysML.xtext lines 932-938

See: Chapter 5: Structure

Port

An interaction point through which a part communicates with its environment.

// Definition
port def <Name> { <port features> }

// Usage
port <name> : <PortType>;
port <name> : ~<PortType>;    // conjugated: reverses in/out
port def WaterPort {
    out item waterOut : Water;
}
part def WaterTank {
    port waterOut : WaterPort;
}
part def Brewer {
    port waterInlet : ~WaterPort;    // conjugated: receives water
}

Grammar: SysML.xtext lines 952-985

See: Chapter 5: Structure

Connection

A link between two or more parts or port ends.

// Definition
connection def <Name> [:> <SuperType>] { <members> }

// Usage: named connection
connection <name> connect <source> to <target>;

// Usage: anonymous inline connection
connect <source> to <target>;

// Binding connector (equates two features)
bind <feature1> = <feature2>;
connection def WaterLine :> BinaryConnection;

connect waterTank.waterOut to brewer.waterInlet;

bind brewer.targetTemp = controlPanel.setTemp;

Grammar: SysML.xtext lines 1015-1080

See: Chapter 6: Connections and Interfaces

Interface

A typed connection between ports, specifying a protocol.

// Definition
interface def <Name> {
    end <portName> : <PortType>;
    end <portName> : <PortType>;
}

// Usage
interface <name> : <InterfaceType> connect <port1> to <port2>;
interface <name> connect <port1> to <port2>;        // untyped
interface def WaterInterface {
    end supply : WaterPort;
    end demand : ~WaterPort;
}
interface waterLine : WaterInterface connect
    waterTank.waterOut to brewer.waterInlet;

Grammar: SysML.xtext lines 1102-1181

See: Chapter 6: Connections and Interfaces

Allocation

A directed mapping from one element to another (logical-to-physical, function-to-component).

// Definition
allocation def <Name> [:> <SuperType>];

// Usage
allocate <source> to <target>;
allocation <name> allocate <source> to <target>;
allocate brewCycle to brewer;
allocation heatingAlloc allocate heatAction to brewer.heatingElement;

Grammar: SysML.xtext lines 1195-1216

See: Chapter 6: Connections and Interfaces


Group 6: Behavioral Constructs

Action

A unit of behavior with inputs, outputs, and internal control flow.

// Definition
action def <Name> {
    in <param> : <Type>;
    out <result> : <Type>;
    <action body>
}

// Usage
action <name> : <ActionType>;

// Perform (reference an action that already exists elsewhere)
perform action <name> : <ActionType>;

// Succession (ordering between two steps)
first <actionName>
then <nextAction>;
action def BrewCycle {
    in beanType : BeanType;
    out coffee : BrewedCoffee;
    action grind : GrindBeans;
    action heat  : HeatWater;
    action extract : Extract;
    first grind then heat;
    first heat then extract;
}
action brewCycle : BrewCycle;

Grammar: SysML.xtext lines 1352-1518

See: Chapter 7: Actions

Action Nodes (Control Flow)

Built-in action node types for structured control flow inside an action body.

// Conditional
if <condition> {
    <true branch>
} else {
    <false branch>
}

// While loop
while <condition> {
    <body>
}

// Loop-until variant (`loop` takes no condition of its own)
loop {
    <body>
} until <condition>;

// For loop
for <var> : <Type> in <collection> {
    <body>
}

// Send a message (`to` names the receiver, `via` the sending port; both optional)
send <payloadExpr> to <receiver>;
send <payloadExpr> via <senderPort>;
send <payloadExpr> via <senderPort> to <receiver>;

// Accept a message or trigger
accept <payload> : <Type>;
accept <payload> : <Type> via <port>;
accept at <time>;
accept after <duration>;
accept when <change-condition>;

// Assignment
assign <target> := <expr>;

// Control nodes (name optional)
fork <name>;              // split into concurrent branches
join <name>;              // wait for all branches
merge <name>;             // accept whichever branch arrived

// A decision node is followed by its branches, one succession per line
decide <name>;
    if <guardExpr> then <targetStep>;
    else <targetStep>;

// Abort the enclosing action
terminate;

Grammar: SysML.xtext lines 1437-1734

See: Chapter 7: Actions

State

A named state in a state machine; may contain entry, do, and exit sub-actions.

// Definition
state def <Name> {
    entry;                              // or: entry <action>;
    do;                                 // or: do <action>;
    exit;                               // or: exit <action>;
    transition <Name> first <state>
        accept <trigger>
        if <guard>
        do <effect>
        then <target>;
}

// Usage
state <name> : <StateType>;

// Exhibit (reference a state that exists elsewhere)
exhibit state <name> : <StateType>;
exhibit state <name> { <states and transitions> }
exhibit <stateUsageRef>;

// Parallel states
state <name> parallel {
    <region1>;
    <region2>;
}
state def MachineStates {
    state idle;
    state brewing;
    state cleaning;

    transition T1 first idle
        accept StartBrewEvent
        if cupDetected
        then brewing;

    transition T2 first brewing
        accept BrewCompleteEvent
        then idle;
}
state machineStates : MachineStates;

Grammar: SysML.xtext lines 1735-1923

See: Chapter 8: States

Transition

A directed arc between two states, with optional trigger, guard, and effect.

transition [<Name>] first <source>
    [accept <trigger> [via <port>]]
    [if <guard-expression>]
    [do <effect-action>]
    then <target>;
transition T1 first idle
    accept StartBrewEvent
    if cupSensor.cupDetected
    then brewing;

Grammar: SysML.xtext lines 1849-1877

See: Chapter 8: States

Succession

Ordering relationship between two occurrences (temporal “happens before”).

// Inline within an action or state body
first <occurrence1>
then <occurrence2>;

// Explicit succession
succession <name> first <occurrence1> then <occurrence2>;

// Guarded target -- follows a decision node, one line per branch
if <guardExpr> then <targetStep>;

// Default target -- taken when no guard matched (note: no `then`)
else <targetStep>;

Grammar: SysML.xtext lines 1028-1033

See: Chapter 7: Actions

Flow

A directed transfer of items between feature ends.

// Definition
flow def <Name> [:> <SuperType>];

// Usage: anonymous inline flow
flow of <ItemType> from <source> to <target>;

// Named flow usage
flow <name> of <ItemType> from <source> to <target>;

// Succession flow (ordered flow across a time boundary)
succession flow of <ItemType> from <source> to <target>;
flow def WaterFlow;
flow of Water from waterTank.waterOut to brewer.waterInlet;

Grammar: SysML.xtext lines 1230-1285

See: Chapter 6: Connections and Interfaces

Calculation

A function-like behavior that returns a value. Used for expressions and analyses.

// Definition
calc def <Name> {
    in <param> : <Type>;
    return <result> : <ReturnType>;
    <result expression>
}

// Usage
calc <name> : <CalcType>;
calc def BrewTime {
    in volume : Real;
    in pressure : Real;
    return t : Real = volume / pressure * 60.0;
}

Grammar: SysML.xtext lines 1937-1974

See: Chapter 9: Expressions and Constraints


Group 7: Analytical Constructs

Constraint

A Boolean predicate that must hold for the model to be valid.

// Definition
constraint def <Name> {
    <boolean expression>
}

// Usage
constraint <name> : <ConstraintType>;

// Inline constraint
constraint { <boolean expression> }

// Assert a constraint holds
assert constraint <name>;

// Assert a constraint does not hold
assert not constraint <name>;
constraint def BrewTempInRange {
    brewer.waterTemp >= 90.0 and brewer.waterTemp <= 96.0
}
assert constraint BrewTempInRange;

Grammar: SysML.xtext lines 1988-2012

See: Chapter 9: Expressions and Constraints

Concern

A stakeholder interest modeled as a requirement-like predicate.

// Definition
concern def <Name> { <requirement body> }

// Usage
concern <name> : <ConcernType>;

Grammar: SysML.xtext lines 2150-2160

See: Chapter 10: Requirements and Verification


Group 8: Requirements

Requirements capture what a system must do or be.

Requirement

// Definition
requirement def <Name> {
    subject <subjectName> : <SubjectType>;
    assume constraint { <assumption expression> }
    require constraint { <requirement expression> }
    frame <concern>;
    stakeholder <name>;
}

// Usage
requirement <name> : <RequirementType>;
requirement def BrewTimeRequirement {
    subject machine : CoffeeMachine;
    require constraint { machine.brewTime <= 60.0 }
}
requirement brewTime : BrewTimeRequirement;

Grammar: SysML.xtext lines 2026-2110

See: Chapter 10: Requirements and Verification

Satisfy

Declares that a subject satisfies a requirement.

// Reference an existing requirement usage
satisfy <requirementUsageRef> by <subject>;

// Declare the satisfied requirement here, typed by a requirement definition
satisfy requirement [<name>] : <RequirementDef> by <subject>;

assert satisfy <requirementUsageRef> by <subject>;
assert not satisfy <requirementUsageRef> by <subject>;
satisfy requirement : BrewTimeReq by myCoffeeMachine;

Grammar: SysML.xtext lines 2112-2120

See: Chapter 10: Requirements and Verification


Group 9: Cases

Cases are structured analyses with a subject, objective, and result.

Analysis Case

Captures a system analysis (e.g., performance, safety).

analysis def <Name> {
    subject <name> : <SubjectType>;
    objective { <requirement body> }
    <action body items>
    return <result> : <ResultType>;
}
analysis <name> : <AnalysisType>;

Grammar: SysML.xtext lines 2227-2233

See: Chapter 10: Requirements and Verification

Verification Case

Documents how a requirement is verified.

verification def <Name> {
    subject <name> : <SubjectType>;
    objective { verify requirement <ReqName>; }
}
verification <name> : <VerificationType>;

Grammar: SysML.xtext lines 2249-2255

See: Chapter 10: Requirements and Verification

Use Case

Documents system usage by actors.

use case def <Name> {
    subject <name> : <SubjectType>;
    actor <actorName> : <ActorType>;
    objective { <requirement body> }
}
use case <name> : <UseCaseType>;

// Include another use case inline
include use case <name>;

Grammar: SysML.xtext lines 2287-2301

See: Chapter 10: Requirements and Verification


Group 10: Views and Viewpoints

Views select and frame content for a specific audience.

Viewpoint

Defines the concerns and stakeholders a view must address.

viewpoint def <Name> {
    stakeholder <name> : <StakeholderType>;
    frame <concern>;
    require constraint { <criteria> }
}
viewpoint <name> : <ViewpointType>;

Grammar: SysML.xtext lines 2399-2405

See: Chapter 13: Metadata and Views

View

Projects selected model content through a rendering.

view def <Name> :> <SupertypeView> {
    filter <BooleanExpression>;
    render rendering <name> : <RenderingDef>;
}

view <name> : <ViewType> {
    expose <element>;                  // member expose
    expose <Namespace>::*;             // namespace expose
    filter <BooleanExpression>;
    render <renderingUsageRef>;
}

// Anonymous view usage — no def required
view <name> { expose <element>; }
view def StructuralView :> InterconnectionView {
    filter @SysML::PartUsage;
    render rendering asTree : Views::GraphicalRendering;
}
view structuralView : StructuralView {
    expose CoffeeMachineDomain::*;
}

view scratch { expose CoffeeMachine; }   // anonymous

expose is legal only in a view usage body; a view definition body admits filter and render (ViewDefinitionBodyItem has no expose alternative). sysml-rs currently fails to flag an expose inside a view def – do not read that as acceptance.

The eight standard view definitions (package StandardViewDefinitions) are: GeneralView, InterconnectionView, ActionFlowView, StateTransitionView, SequenceView, GeometryView, GridView, BrowserView. There is no standard UseCaseView or RequirementView; a requirement view is written as a GeneralView specialization with a requirement-metaclass filter.

filter expressions are Boolean and are evaluated once per exposed element. The spec’s own idiom is a classification test against a metaclass or metadata definition – filter @SysML::PartUsage;, filter @Safety and Safety::isMandatory;. The self, kind, name, and id bindings used elsewhere in this book are a sysml-rs evaluator extension, not spec language. Multiple filter clauses on the same view compose as logical AND.

Grammar: SysML.xtext lines 2315-2383

See: Chapter 13: Metadata and Views

Rendering

Defines how a view’s contents should be displayed.

rendering def <Name> { <rendering body> }
rendering <name> : <RenderingType>;

Grammar: SysML.xtext lines 2417-2427


Group 11: Metadata

Metadata annotates any model element with typed, queryable data.

// Definition (a metaclass)
metadata def <Name> {
    attribute <field> : <Type>;
}

// Usage: attach metadata to an element
@<MetadataType> { <field> = <value>; }

// Using the metadata keyword explicitly
metadata <name> : <MetadataType> about <element>;

// Prefix form: a user-defined keyword before the declaration it annotates
#<MetadataType>
<annotated element>
metadata def ReviewStatus {
    attribute reviewer : String;
    attribute approved : Boolean;
}

part def CoffeeMachine {
    // Without an `about` clause, a metadata usage annotates its
    // containing element -- so this attaches to CoffeeMachine
    @ReviewStatus { reviewer = "Alice"; approved = true; }
}

Grammar: SysML.xtext lines 115-177

See: Chapter 13: Metadata and Views


Group 12: Expressions

Expressions compute values. They appear in constraints, feature values, calculations, and guard conditions.

Arithmetic and Logic Operators

CategoryOperators
Arithmetic+ - * / % ** ^
Comparison< > <= >=
Equality== != === !==
Logical (eager – both operands evaluated)& | xor
Logical (short-circuit)and or implies
Null coalescing??
Unary+ - ~ not
Ternary conditionalif <cond> ? <then> else <else>
Range<low>..<high>

Type Testing and Casting

// Test whether a value is an instance of a type
<expr> hastype <Type>
<expr> istype <Type>
<expr> @ <Type>

// Cast to a type
<expr> as <Type>

// Metaclass test / cast
<expr> @@ <MetaType>
<expr> meta <MetaType>

// Extent: all instances of a type
all <Type>

Collection and Feature Navigation

// Dot notation: navigate to an owned feature
brewer.waterTemp

// Feature chaining: navigate through a chain
machine.brewer.waterTemp

// Sequence expression (parenthesized, comma-separated)
(a, b, c)

// Range (also usable in expressions)
1..10

// Indexing (1-based)
<sequence>#(n)

// Sequence operators (arrow form; the per-element rule is a body expression)
<sequence>->select {in <x>; <predicate over x>}
<sequence>->collect {in <x>; <expr over x>}
<sequence>->reject {in <x>; <predicate over x>}
<sequence>->forAll {in <x>; <predicate over x>}
<sequence>->exists {in <x>; <predicate over x>}

// Aggregators (argument-list form -- the sequence is the argument)
<sequence>->size()
<sequence>->isEmpty()
<sequence>->notEmpty()
<sequence>->sum()
<sequence>->product()

// min and max are two-argument functions, so a sequence is folded with ->reduce
<sequence>->reduce min
<sequence>->reduce max

Body Expression (Inline Function)

// Body expression -- the inline function passed to ->select, ->collect, ->forAll, ...
{in <param>; <expression>}
{in <param> : <Type>; <expression>}

Grammar: KerMLExpressions.xtext (full file, 579 lines)

See: Chapter 9: Expressions and Constraints


Occurrence Modifiers

Occurrences (parts, items, actions, states) can carry temporal modifiers.

KeywordMeaning
individualRepresents exactly one real-world individual (not a reusable type)
snapshotA portion that captures the state at an instant in time
timesliceA portion that spans a time interval
individual part myEspressoMachine : EspressoMachine;
snapshot part machineAtStartup : CoffeeMachine;
timeslice part machineInOperation : CoffeeMachine;

Grammar: SysML.xtext lines 801-860


The Definition/Usage Pattern (Summary)

Every major construct family in SysML v2 follows this paired pattern:

Definition keywordUsage keywordWhat it models
attribute defattributeScalar data value
enum defenumEnumerated type
item defitemFlowing thing
part defpartPhysical/logical component
port defportInteraction point
connection defconnectionLink between ends
interface definterfaceTyped port-to-port link
allocation defallocationLogical-to-physical mapping
flow defflowItem transfer
action defactionUnit of behavior
state defstateState machine state
calc defcalcReturning computation
constraint defconstraintBoolean predicate
requirement defrequirementSystem obligation
concern defconcernStakeholder interest
case defcaseGeneric structured analysis
analysis defanalysisSystem analysis case
verification defverificationVerification case
use case defuse caseActor-system interaction
viewpoint defviewpointStakeholder concern framing
view defviewProjected model content
rendering defrenderingDisplay format
metadata defmetadata or @Model annotation

connect <a> to <b> is not the connection usage keyword – it is the clause that introduces the ends, and stands alone only as the short form of an unnamed connection. The same pattern holds for allocate (usage keyword allocation) and bind (usage keyword binding). See Appendix G for the metamodel class behind each pair.


Cross-References


Source Anchors

  • Grammar (primary): references/sysmlv2/SysML-v2-Pilot-Implementation/org.omg.sysml.xtext/src/org/omg/sysml/xtext/SysML.xtext (2,433 lines)
  • Grammar (KerML base): references/sysmlv2/SysML-v2-Pilot-Implementation/org.omg.kerml.xtext/src/org/omg/kerml/xtext/KerML.xtext
  • Grammar (expressions): references/sysmlv2/SysML-v2-Pilot-Implementation/org.omg.kerml.expressions.xtext/src/org/omg/kerml/expressions/xtext/KerMLExpressions.xtext
  • Metamodel vocabulary: references/sysmlv2/SysML-vocab.ttl (182 SysML types)
  • KerML vocabulary: references/sysmlv2/Kerml-Vocab.ttl (84 KerML types)
  • Corpus examples: sysml-spec-tests/corpus/advent/ (56 files)
  • Coffee Machine examples: examples/coffee-machine/ (12 files)

Appendix B: Migration from SysML v1

If you have existing SysML v1 models, this appendix maps the v1 concepts to their v2 equivalents and provides a practical migration approach.

Concept Mapping

SysML v2 replaces SysML v1’s diagram-centric approach with a textual, element-centric model. Many v1 concepts have direct v2 equivalents, but the framing is different.

Structural Concepts

SysML v1SysML v2Notes
Blockpart defBlocks become part definitions. The keyword changed, but the intent is the same.
Block propertypart usageA property typed by a Block becomes a typed part usage.
Value Typeattribute defValue types become attribute definitions.
Value Propertyattribute usageProperties typed by a value type become attribute usages.
Flow Portport def / portFlow ports become port definitions with directional items.
Full Portport def / portFull ports become port definitions. The in/out direction model is more explicit.
Item Flowflow on connectionItem flows are now part of connection modeling.
Internal Block DiagramComposite structure in part defNo separate diagram type – structure is expressed inline.
Block Definition Diagrampart def declarationsDefinitions are written directly in text.

Behavioral Concepts

SysML v1SysML v2Notes
Activityaction defActivities become action definitions.
Activity PartitionOwnership or allocationPartitions become structural ownership or explicit allocations.
State Machinestate def with state usagesState machines are modeled as state definitions with transitions.
Statestate def / stateStates become state definitions (reusable) or state usages (in context).
Transitiontransition ... from ... to ...Transitions name source, target, guard, and trigger explicitly.
Signalitem defSignals become item definitions that flow through ports.

Requirements and Traceability

SysML v1SysML v2Notes
Requirementrequirement defRequirements become first-class model elements with doc text.
Satisfy relationshipsatisfyDirect equivalent. Applied inside the satisfying element.
Verify relationshipverify inside verification defVerification cases explicitly link to requirements.
Derive relationshipSpecialization (:>)Derived requirements specialize their parent.
Copy relationshipNo direct equivalentUse specialization or imports instead.
Requirement DiagramPackage with requirementsNo separate diagram – requirements live in packages.

Organizational Concepts

SysML v1SysML v2Notes
PackagepackageDirect equivalent.
ModelTop-level packageNo separate Model element. Packages serve the same role.
Profilemetadata defProfiles become metadata definitions applied with @.
Stereotypemetadata defStereotypes become metadata definitions.
Tagged Valueattribute in metadata defTagged values become attributes on metadata definitions.
Viewview / viewpoint defViews and viewpoints are now explicit model elements.

Semantic Caveats

The mapping tables above are one-to-one enough to be useful, but three of the mappings hide a change in meaning. These are the ones teams get wrong.

A v1 Block splits into two concepts

In v1 a Block played two roles with no syntactic distinction. On a BDD it stood for a classifier – a reusable type. On an IBD it appeared as a property – an occurrence of another Block owned by a parent. Tools inferred the role from context.

v2 makes the distinction mandatory and syntactic. Every v1 Block becomes one of:

  • part def if it was used as a type – something instantiated more than once, referenced from elsewhere, or specialized.
  • a part usage (no def) if it was used as a slot – one occurrence inside one owning definition.

So a v1 Block: CoffeeMachine with part properties brewer : Brewer and grinder : Grinder becomes two kinds of element:

part def Brewer { }
part def Grinder { }

part def CoffeeMachine {
    part brewer : Brewer;
    part grinder : Grinder;
}

Brewer and Grinder are definitions; brewer and grinder are usages of them. The test when you are unsure: can this element be typed by, specialized by, or referenced from somewhere else? If yes, it needs def.

FlowPort direction becomes conjugation

A v1 FlowPort carried direction as a flag – in, out, or inout – and you matched an out port to an in port when connecting.

v2 uses conjugation instead. You write one port def from one side’s perspective and take its conjugate with ~, which reverses every flow direction inside it. Two ports are connector-compatible when one is the conjugate of the other:

port def WaterOutlet {
    out item water : Water;
}

part def WaterTank {
    port supply : WaterOutlet;       // supplies water
}

part def Brewer {
    port inlet : ~WaterOutlet;       // receives water (conjugate)
}

out item water becomes in item water in ~WaterOutlet. You did not write a second port def; the language derived it.

For migration: for every pair of v1 FlowPorts you used to connect, write one port def and apply ~ at the other end. Writing symmetric WaterInlet/WaterOutlet pairs is the v1 pattern translated literally, not the v2 idiom.

Stereotypes become first-class metadata

In v1, extending the language meant a UML Profile: a Profile package holding Stereotype elements that extended base metaclasses, applied through ProfileApplication, with tagged values as owned Property elements. It worked, but profiles coupled models together, tagged values were stringly-typed in practice, and cross-tool compatibility was uneven.

v2 replaces the whole mechanism with metadata def – an ordinary element declared in a package alongside part def, carrying typed attributes, applied with @. No profile file, no ProfileApplication, no metaclass extension:

metadata def Safety {
    attribute level : String;
    attribute standard : String;
}
@Safety { :>> level = "SIL-2"; :>> standard = "IEC-61508"; }
part def Brewer {
    attribute waterTemp : Real;
}

Three consequences. The annotations live in your package structure, so they import, specialize, and query like anything else. The attributes are strongly typed – including quantity types from ISQ – where v1 tagged values defaulted to String even when a number was meant. And :>> is just the redefinition syntax you already use everywhere else; there is no separate tagged-value syntax.

One thing metadata does not do, which profiles attempted: it does not extend the metamodel. Stacking @ annotations adds independent annotations to an element; it does not create new kinds of element or new structural relationships. If you wrote profiles to introduce constructs that behaved like SysML built-ins, those have to be re-expressed with standard v2 mechanisms.

Diagram Types to Constructs

v1 divided modeling across nine diagram types, each a separate artifact with its own notation. v2 has no diagram types – the model is one textual artifact – but the concerns each diagram addressed still exist as language constructs. This table maps the concern; the view-preset table further down maps how you would render it.

v1 diagram typeAbbr.v2 constructs covering the same concern
Block Definition DiagramBDDpart def, attribute, port def, interface def, item def, connection def at package scope
Internal Block DiagramIBDpart usages, connect, flow, port usages inside a part def body
Requirement DiagramREQrequirement def, requirement usages, satisfy, verify, nested decomposition; refinement is expressed by specialization (:>), not a refine keyword
Activity DiagramACTaction def, action usages, first/then successions, flow, fork, join, decide, merge, send, accept
State Machine DiagramSTMstate def, state usages, transition, accept, if guard, do effect, entry/do/exit sub-actions, exhibit state
Parametric DiagramPARconstraint def, constraint usages, assume constraint, require constraint
Sequence DiagramSDmessage usages plus successions between action performers. Partial: message is real surface syntax but produces a FlowUsage – there is no Message metaclass and no interaction def form (see Appendix G)
Use Case DiagramUCuse case def, use case usages, actor, subject, include use case
Package DiagramPKGpackage, import, alias; public / private visibility

What replaces the BDD/IBD boundary

The BDD/IBD split was the most operationally significant boundary in v1: the BDD defined types, the IBD defined the internal architecture of one Block. In v2 both live in one part def body:

part def CoffeeMachine {
    // BDD-style: typed part usages naming sub-components
    part grinder : Grinder;
    part brewer : Brewer;
    part waterTank : WaterTank;

    // IBD-style: connectivity between those parts' ports
    connect waterTank.waterOut to brewer.waterInlet;
    flow of Water from waterTank.waterOut to brewer.waterInlet;
}

No file boundary, no diagram boundary, and no synchronization problem between the two.

What replaces the REQ diagram

A v1 Requirements Diagram showed Requirement-stereotyped Classes, containment decomposition, DeriveReqt arrows, and satisfy/verify dependencies. These become body members and relationships on requirement def:

requirement def BrewSafetyReqs {
    doc /* The brewer shall not operate unsafely. */
    subject machine : CoffeeMachine;

    // nested decomposition (v1 containment)
    requirement temperatureReq : WaterTemperatureReq;
    requirement cupReq : CupPresenceReq;
}

// v1 satisfy dependency -> v2 satisfy
part myCoffeeMachine : CoffeeMachine {
    satisfy requirement : BrewSafetyReqs;
}

Note the : in satisfy requirement : BrewSafetyReqs. The name slot after requirement declares a requirement usage; the type annotation is what binds it to BrewSafetyReqs. Writing satisfy requirement BrewSafetyReqs; parses, but it declares an untyped usage that happens to share the name and links to nothing.

What replaces the PAR diagram

A v1 Parametric Diagram showed ConstraintBlock instances with parameters wired together by binding connectors. In v2 the constraint usage and its bindings are written inline:

constraint def TemperatureInRange {
    in attribute temp : Real;
    in attribute minTemp : Real;
    in attribute maxTemp : Real;
    constraint { temp >= minTemp and temp <= maxTemp }
}

part def Brewer {
    attribute waterTemp : Real;
    constraint brewTempOk : TemperatureInRange {
        :>> temp = waterTemp;
        :>> minTemp = 90.0;
        :>> maxTemp = 96.0;
    }
}

The v1 binding connectors become :>> redefinitions on the constraint usage. Same structure, inline text.

Migration Strategy

Migration works best in phases. Do not try to convert everything at once.

Phase 1: Structure First

Start with your block definitions and their properties. Convert blocks to part def, value types to attribute def, and block properties to typed usages. This gives you a structural baseline in v2 that you can check immediately.

// v1: Block "Grinder" with value property "grindSize: Real"
// v2:
part def Grinder {
    attribute grindSize : Real;
}

Phase 2: Requirements and Traceability

Migrate your requirements next. Convert each v1 requirement into a requirement def with a short ID and doc text. Add satisfy links to the structural elements from Phase 1.

requirement def <'REQ-001'> BrewTempReq {
    doc /* Extraction temperature shall remain between 90 and 96 C. */
}

part def CoffeeMachine {
    satisfy requirement : BrewTempReq;
}

This gives you traceable requirements early, before you tackle behavior.

Phase 3: Behavior

Convert activities to action def and state machines to state def. The biggest change is that v2 uses textual control flow (first...then, fork, join, decide, merge) instead of diagram-based arrows. Guards and triggers on transitions are now written inline.

Phase 4: Governance and Views

Add metadata definitions to replace profiles and stereotypes. Create viewpoints and views to replace v1’s diagram-based stakeholder communication.

The v1 diagram zoo collapses to a small set of v2 view definitions, often combined with a sysml-rs-supplied preset (in parentheses):

v1 Diagramv2 View Definition
Block Definition DiagramGeneralView with a definition/usage filter (BDD preset)
Internal Block DiagramInterconnectionView (IBD preset)
Parametric DiagramInterconnectionView + binding overlay (Parametric preset)
Activity DiagramActionFlowView
State Machine DiagramStateTransitionView
Sequence DiagramSequenceView
Use Case DiagramGeneralView with a use-case filter (no standard UseCaseView exists)
Requirement DiagramGeneralView with a requirement-metaclass filter
Package DiagramBrowserView (or Package preset)

The eight standard view definitions (GeneralView, InterconnectionView, ActionFlowView, StateTransitionView, SequenceView, GeometryView, GridView, BrowserView) are spec, defined in the standard library package StandardViewDefinitions; the parenthesised preset names are sysml-rs’s preset registry. There is no standard use-case or requirement view definition – both are written as GeneralView specializations with a filter, which is the recipe the library’s own GeneralView documentation prescribes. Either form is enough to replace the v1 diagram. See Chapter 13.

Key Mindset Shifts

From diagrams to text. In v1, the diagram was the primary artifact. In v2, the text is the source of truth and diagrams are generated views. This changes your workflow: you edit text files, review diffs, and run checks – not drag boxes on a canvas.

From implicit to explicit. v1 often relied on diagram layout to convey meaning. v2 makes everything explicit in text: ownership, direction, typing, sequencing. This feels verbose at first but pays off in reviewability and automation.

From copy to reuse. v1 models often duplicated similar blocks across diagrams. v2’s definition/usage pattern makes reuse natural: define once, use everywhere. Resist the urge to copy-paste definitions.

From monolithic to modular. v1 models often lived in one large project file. v2 models naturally split across files and packages, with imports making dependencies visible. Plan your package structure early.

Common Migration Pitfalls

Direct syntax translation without cleanup. Migration is an opportunity to improve model quality. Do not just translate v1 ambiguity into v2 ambiguity – tighten definitions, add types, and make traceability explicit.

Postponing verification links. In v1, verification was often managed outside the model. In v2, verification cases are model elements. Add them during migration, not after.

Mixing conventions. If part of your team uses v1 habits (untyped usages, missing imports) while another part uses v2 patterns, the model becomes inconsistent. Set v2 conventions early and enforce them in review.

Appendix C. The Systems Modeling API

Alongside the textual notation this book teaches, the SysML v2 specification suite defines a second interchange surface: the Systems Modeling API and Services specification, a standard REST/HTTP API for programmatic access to model data. Where the textual notation is how people write and exchange models, the API is how tools do – a requirements-management system pulling coverage data, a CI job comparing two versions of a model, a dashboard tracking element counts across a programme, all without any of them parsing anyone’s source files.

This appendix explains the API as the OMG specification defines it: the concepts, why they are shaped the way they are, and what any conformant implementation gives you. It is a reference companion to the modelling chapters – you do not need any of it to write a SysML v2 model. Come here when you are ready to connect a model to your broader engineering toolchain.


Why a standard API exists

The textual notation answers “what does this model say?”; the API answers “how do tools agree on which model, at which version, says it?”. Interchange needs an identity story as much as it needs a syntax. The API specification supplies that story with four concepts:

ConceptDescription
ProjectA top-level container. Every model lives inside a project.
BranchA named line of development within a project (similar to git).
CommitAn immutable snapshot of the model at a point in time.
ElementAny SysML element: parts, requirements, actions, connections, …

Two tools that share nothing but this vocabulary can still talk about “the same model at the same version”. A commit is immutable, so any query pinned to a project and a commit is reproducible: the same request returns the same answer next month, regardless of what the model has become since. That is the property every downstream artifact – a coverage report, an exported diagram, an audit trail – should be built on.

On top of these concepts the specification defines resource-oriented services: listing and creating projects, walking a project’s branches and commits, retrieving and storing elements at a commit, navigating ownership and relationships, and a query language (PrimitiveConstraint / CompoundConstraint objects) for filtering elements server-side. The element payloads are JSON, governed by a published JSON Schema, so the shape of a PartUsage on the wire is as standardised as its textual syntax.

Where the OMG definition lives

The normative materials ship in the spec distribution:

  • references/sysmlv2/OpenAPI.json – the REST endpoints, request/response schemas, and error codes.
  • references/sysmlv2/SysmlAPISchema.json – the element-payload JSON Schema.
  • references/sysmlv2/Systems-Modeling-API.xmi – the underlying service architecture model.
  • references/sysmlv2/SysML-v2-API-Cookbook/ – Jupyter notebooks of common query patterns.

sysml-rs extends the spec here. sysml-rs implements the project/commit/model read side of the OMG API as a subset, and extends it with a native REST surface, WebSocket streams, and an MCP transport that share one in-process service – so a model loaded over REST is queryable from an MCP-aware assistant, and vice versa. The route tables, authentication and CORS defaults, session and streaming endpoints, and MCP setup are product documentation and live on the sysml-rs portal: Integrations. If your tooling needs strict OMG conformance for branch operations or the compound query objects, treat sysml-rs as a partial implementation and round-trip through a conformant store for those operations.


Working with a model API

These practices are about the interchange concepts, not any particular implementation; they hold against any conformant store.

Always pin a commit. Reports and exports should record the commit ID that produced them, so results are reproducible and auditable. Without a pinned commit, the same query can return different answers across runs – and nobody can tell whether the model changed or the report did.

Read first, write later. Build trust in your automation by producing useful reports before attempting programmatic model modifications. Read-only automation cannot corrupt model data; teams that jump straight to programmatic writes often create elements that conflict with manual modelling.

Query server-side. Downloading every element and filtering locally works for small models but does not scale. The query surface exists so the store does the filtering.

Keep automation narrow. One well-maintained coverage report is worth more than ten fragile scripts.

A good first target is requirement coverage: query for requirements lacking a verification case, and pair it with a requirement-to-verification traceability matrix (Chapter 10). It is read-only, it produces immediate review value, and it exercises the identity story end to end.

Further reading

  • Chapter 15 – how the project/commit interchange notions relate to workspaces and manifests when you are authoring.
  • Chapter 10 – the verification semantics behind coverage queries.
  • The OMG materials listed above are the normative reference for endpoints, payloads, and the query language.

Appendix D: Tooling Notes (sysml-rs)

The language chapters teach SysML v2 – the OMG specification, KerML, the standard library, and the modelling patterns any conformant tool should support. Real systems work, however, runs on a particular tool, and the particular tool used throughout this book is sysml-rs: an open-source SysML v2 toolchain (parser, analyzer, simulation runtime, language server, REST/MCP service, and editor extensions).

Product documentation for sysml-rs lives on its own portal, where it is maintained against the tool rather than against this book: rickymillar.github.io/sysml-rs. This appendix is the signpost between the two: for each tooling surface the chapters mention, a couple of sentences on what sysml-rs offers and a link to the page that documents it properly. Nothing here is required to write a SysML v2 model.

Spec vs. tooling. Everything in this appendix is sysml-rs-specific. Other SysML v2 tools may behave differently – they may pick different solvers, render diagrams differently, surface different diagnostics, or choose different defaults. Where a topic has a spec-faithful counterpart elsewhere in the book, this appendix cross-references it.

§Getting the tool

There is no published package yet; you build from source. The build has two genuine prerequisites beyond a Rust toolchain: fetching the OMG specification sources (the code generator derives element kinds and validation from them, so a bare cargo build fails without this step) and generating the tree-sitter parser (generated rather than committed; mind the ABI version the README pins, because a parser generated at the wrong ABI compiles and then crashes at parse time). The authoritative, maintained instructions are the Quick start in the sysml-rs README – follow that, not a summary.

§Running a model from the command line

The sysml binary checks files, evaluates expressions, simulates state machines, runs actions, executes verification cases, queries models (stats, find, trace, unverified), and exports PlantUML, canonical JSON, or a declared view’s ViewModel. Worked walkthroughs with real output live in CLI workflows on the portal; sysml help <command> is authoritative for any subcommand.

One caution worth carrying with you: the state-machine simulator can misreport the initial state and transition targets for some models, so cross-check a run against the transitions you wrote before you trust it. This and the other confirmed defects are tracked in Known limitations.

§Solver auto-selection

When the runtime detects the state-space pattern of Chapter 16 §16.B it wires an integrator automatically – a fixed-step Runge-Kutta, an adaptive one, or a stiff-system method – and locates threshold crossings in transition guards with sub-tick precision. You author the model, not the numerics. The runtime’s execution model, and the conscious simplifications it makes (it is a modelling runtime, not a calibrated physics engine), are documented in The runtime; the spec-side story of what the runtime is interpreting is Appendix E.

§Equations workbench

The equations workbench is the interactive surface for the binding-override pattern in Chapter 16 §16.C: it lists the model’s calculations, typesets them as math, exposes bindings as editable fields, and re-evaluates on change – through the same what-if path a sweep script uses, so both see the same answers. See The runtime for the execution substrate it sits on.

§Causal trace and breakpoints

A running session records every value change with its cause, and the trace is queryable backwards from any change (Chapter 16 §16.D). Breakpoints pause a session on model events – entering a state, a transition firing, a constraint flipping to Fail, a threshold crossing, or an arbitrary condition – without touching the model itself. Both are session-level runtime features: The runtime.

Two authoring habits pay off here regardless of tool: name your transitions, constraints, and actions descriptively (a trace through Idle->Heating and BrewTempReq reads far better than one through t_3 and c_17), and keep calculation bodies free of side effects so causal edges stay unambiguous.

§Editor support

The language server provides diagnostics with stable codes (Appendix F), hover with signatures and inherited members, quick-fixes (auto-import, keyword suggestions, ISQ-type replacement), live PASS/FAIL lenses on constraints, and full support for the sysml.toml manifest (Chapter 15). Editor setup and the extension surfaces are documented in Editors.

§Views and diagrams

Two conventions in sysml-rs’s diagrams are spec, not house style: definitions render with sharp corners and usages with rounded corners, as the SysML v2 graphical notation mandates, and every view definition derives from one of the eight standard view definitions, which determine what kind of diagram a view becomes (Chapter 13). Everything else – the card aesthetic, the colour palette, dark-mode behaviour, and the live overlays a running session paints onto a diagram – is sysml-rs rendering, documented in Views and diagrams along with the ViewModel contract diagrams travel over.

§The service, the API, and MCP

One in-process service backs every transport – CLI, language server, REST/WebSocket API, and MCP – so a model loaded through one is visible to the others. The interchange concepts are Appendix C; the route tables, security defaults, and MCP setup are Integrations.

§Worked examples

The models used throughout this book, and a larger catalogue of maintained runnable fixtures (hybrid dynamics, multi-file workspaces, deliberately-broken diagnostic demos), are catalogued in Examples. Every model there is executed by the regression suite, so they work at the commit you checked out.

What’s not in this appendix

The neighbouring appendices carry the topics this one does not:

  • Appendix C (The Systems Modeling API) – the OMG interchange API: projects, commits, elements, and why commit-pinning matters.
  • Appendix E (Physics-aware Simulation Reference) – the spec-side story of continuous behaviour: the state-space pattern, occurrences, spatial frames, performances.
  • Appendix F (Diagnostic Codes) – the diagnostic-code families and how to read live diagnostics.

Appendix E. Physics-aware Simulation Reference

This appendix is the reference for modelling continuous and hybrid behaviour in SysML v2: which language constructs carry physical meaning, what the standard library defines for them, and where the spec ends and a particular runtime begins. The solver machinery that executes these models – integrators, differential-algebraic assembly, numerical mechanics – is product territory and lives in the sysml-rs portal’s runtime documentation; this appendix keeps the language story.

Scope tags. Each section is tagged [SPEC] or [EXTENSION] so you can tell the spec story from the sysml-rs story at a glance. The headline rule: the modelling pattern (specialise GetDerivative, type ports with ISQ::* quantities, write conservation as assert constraint) is spec language. The runtime mechanism that turns the pattern into a numerically integrated trajectory is a sysml-rs extension, and other SysML v2 implementations may execute the same model differently or not at all.


E.1 The state-space representation pattern – [SPEC]

The State-Space Representation (SSR) pattern is how SysML v2 models continuous dynamics. There are three modelling moves; all three are pure spec:

calc def HeaterRhs :> GetDerivative {
    in t : Real; in temp : Real;
    in heaterPower : Real; in ambientTemp : Real;
    attribute heatLossCoeff : Real := 0.2;
    attribute thermalMass : Real := 4180.0;
    return dT_dt = (heaterPower - heatLossCoeff * (temp - ambientTemp))
                   / thermalMass;
}

calc def BrewerReady :> GetOutput {
    in temp : Real;
    return ready : Boolean = temp >= 90.0 and temp <= 96.0;
}

action def BrewerDynamics :> ContinuousStateSpaceDynamics {
    in brewer : Brewer;
    action stateEqs : HeaterRhs {
        in t = simulationTime;
        in temp = brewer.waterTemp;
        in heaterPower = brewer.heaterPower;
        in ambientTemp = 20.0;
    }
}

GetDerivative, GetOutput, and ContinuousStateSpaceDynamics are defined in the standard library’s StateSpaceRepresentation domain package, so that is the package to import when you specialise them. (AnalysisTooling is a different domain package: it defines the ToolExecution and ToolVariable metadata, including the toolName attribute a model can use to request a particular analysis tool.) The author specialises the patterns; a runtime detects the specialisation and wires the integration. A spec-faithful SSR model can be picked up by any conformant runtime.

A note on what’s gone. Earlier sysml-rs material used non-spec metadata fields (@ToolVariable { derivative = "..." }, @ToolExecution { signal = "..." }) to declare ODE structure. Those fields have been removed. @ToolExecution { toolName = ... } remains, because toolName is the spec-defined attribute from AnalysisTooling; sysml-rs reads it as solver-selection metadata. Old models carrying the removed fields should be rewritten using the SSR pattern above.

E.2 Hybrid models: states with continuous behaviour – [SPEC] pattern, [EXTENSION] execution

A hybrid model is a state machine whose states each select different continuous dynamics – boilers, bouncing balls, valves, and clutches all evolve continuously and jump discretely at thresholds. The modelling recipe is spec language end to end:

  1. Write a state machine with named states (Heating, Idle, Falling, Rising).
  2. Write one calc def :> GetDerivative per state.
  3. Write transitions whose triggers mention the continuously-varying values (accept when temp >= 100.0).

Worked snippet (the bouncing ball, condensed from the workspace’s examples/bouncing-ball/):

state def BallMode {
    state Falling;
    state Rising;
    transition first Falling accept when y <= 0.0 and v < 0.0 then Rising;
    transition first Rising accept when v <= 0.0 then Falling;
}

calc def FallingRhs :> GetDerivative {
    in t : Real; in y : Real; in v : Real;
    return dy_dt = v;
    return dv_dt = -9.81;
}

calc def RisingRhs :> GetDerivative {
    in t : Real; in y : Real; in v : Real;
    return dy_dt = v;
    return dv_dt = -9.81;   // Rising and Falling share gravity; reset of v is in the transition.
}

The transition’s effect (not shown) reverses v with a coefficient of restitution. What executes this – integrating the active state’s derivative, watching every trigger, switching dynamics when a transition fires – is the runtime’s job, not the model’s. How sysml-rs’s runtime does it, and the simplifications it makes, are documented on the runtime page.

E.3 Change triggers and threshold crossings – [SPEC] concept, [EXTENSION] precision

accept when temp >= 100.0 is spec syntax: a change trigger that fires when its condition becomes true. When the condition mentions a continuously-varying value, a runtime has to decide when it became true – and the honest answer usually falls between two integration steps. sysml-rs locates the crossing within the step and fires the transition there, rather than a step late; that is why a thermostat trips cleanly at 100 °C and a bouncing ball bounces at y = 0 instead of slightly below the floor.

Authoring habits that make threshold behaviour clean under any runtime:

  • Express thresholds as direct comparisons on a state variable (temp >= 100.0), not as derived expressions.
  • Avoid pairs of transitions that would both fire on the same crossing – pick one canonical guard.
  • For signals that chatter near a threshold, model an explicit hysteresis margin (temp >= 100.0 to enter, temp <= 95.0 to leave).

E.4 Occurrences and Life – [SPEC]

The spec’s Occurrence model (Occurrences.kerml in the Kernel Semantic Library) gives every happening a temporal identity. A Life is the complete temporal extent of an occurrence; an occurrence’s snapshots are its instantaneous time slices; and the library defines temporal relations between occurrences – HappensBefore (one occurrence completes before another begins) and HappensDuring (one occurrence’s extent lies within another’s).

This is the lifecycle layer everything dynamic hangs off. A simulation trajectory is an occurrence unfolding; a verdict stream is a time-indexed reading of snapshots; “did the pump start before the valve opened?” is a HappensBefore question. Chapter 16 §16.A develops the run-as-occurrence framing; Appendix J covers the occurrence vocabulary in depth.

E.5 Spatial frames – [SPEC]

SpatialFrames.kerml (Kernel Semantic Library) supplies the spec’s spatial surface. A SpatialFrame is a three-dimensional body that provides a spatial extent for positions to be measured against, and the library defines functions such as PositionOf (the position of a point relative to a frame at a given time) and CurrentPositionOf (the same, against a clock’s current time).

A model that imports SpatialFrames::* can declare frames, place occurrences within them, and ask “where is X, in frame F, at time t?” as a model-level query. Spatial frames matter most for multi-body mechanical systems and any model that must switch between body-fixed and inertial coordinates.

E.6 Performances and Evaluation – [SPEC]

Performances.kerml (Kernel Semantic Library) defines a Performance as an execution of a behaviour – itself a kind of occurrence, so everything in E.4 applies to it. An Evaluation is the performance of a function: the act of computing a result, as an occurrence with its own temporal extent, with BooleanEvaluation as the predicate case. StatePerformances.kerml adds StatePerformance and StateTransitionPerformance – a state being occupied, a transition being taken – plus helpers such as allSubstatePerformances for walking a nested state machine’s performance record.

This is the layer verification ultimately reads: when a verification case runs against a simulation (Chapter 10), the constraint checks it performs over the trajectory are, in the library’s terms, boolean evaluations over performances.

E.7 What sysml-rs’s runtime adds – [EXTENSION]

sysml-rs extends the spec here. When a model types its ports and attributes with ISQ::* quantities (Chapter 14), sysml-rs classifies each port’s physical role – effort, flow, displacement, momentum, and friends – from the type’s dimensions, and synthesizes the corresponding conservation obligations at connection junctions. Nobody writes “current in equals current out”; the runtime adds that obligation when it sees the topology, and a violated balance surfaces as an ordinary Fail verdict. On top of that, the runtime selects a numerical integrator automatically (fixed-step and adaptive Runge-Kutta methods, and a stiff-system method), honouring @ToolExecution { toolName = ... } when a model requests one explicitly.

None of this is spec, and sysml-rs’s physics is deliberately simplified – it executes idealised models for insight, and is labelled as such; it is not a calibrated physics engine. The mechanism, its limits, and the confirmed defects are documented on the portal: The runtime and Known limitations.


Tooling cross-references. The physics diagnostic family (PH codes, including the “did you mean ISQ type?” quick-fix) is described in Appendix F. Runnable physics examples – bouncing-ball, damped-oscillator, dc-motor, the espresso fixtures, and the rest – are catalogued on the portal’s Examples page.

Appendix F. Diagnostic Codes

The chapters cite diagnostic codes inline – E200 for an unresolved name, S001 for a duplicate member, PH006 for a Real that could be an ISQ type. This appendix explains where those codes come from, what the families mean, and how to look a code up. What it deliberately does not contain is an exhaustive hand-maintained code table: earlier editions carried one, it drifted from the tool (codes appeared, wordings changed, at least one severity was listed wrongly), and a reference that silently rots is worse than none. The live tool output is authoritative; a reference generated from the tool’s own source is planned for the sysml-rs portal.

Tag: [TOOLING]. The codes are sysml-rs’s diagnostic vocabulary – other SysML v2 implementations will emit a different set or use different identifiers. The checks behind the codes (e.g. “every requirement should have a verification case”) are spec-faithful where applicable; the labels are sysml-rs.

Where diagnostics come from

sysml-rs emits diagnostics from two layers:

  • The core analyzer checks structure and semantics as it elaborates a model: ownership integrity, name resolution, well-formedness of memberships and specializations, property validation, import health, and physics typing.
  • The runtime’s health checks look at what a model would do when executed: state machines with unreachable states, actions with no steps, flows with mismatched endpoints, verification cases that would vacuously pass, constraints that cannot be evaluated.

Every diagnostic carries a severity – error (the model does not parse, validate, or execute), warning (structurally valid but semantically suspect), or info (a non-blocking observation, often intentional) – and info-level codes deserve a review, not a reflexive fix.

The code families

Codes are grouped by prefix into families. These are the families the chapters reference; the set is open-ended and grows with the tool:

PrefixFamilyCross-reference
EStructural integrity and name resolution (E200 is the unresolved-name code behind the auto-import quick-fix)Chapter 4
SSemantic well-formedness: duplicate names, misplaced memberships, illegal typingsChapter 3, Chapter 12
VProperty validation
IMImports: circular chains, duplicates, unused importsChapter 4
SMState machines: missing initial states, unreachable states, dead endsChapter 8
AXActions: empty bodies, unreachable steps, malformed control nodesChapter 7
FLFlows and ports: missing endpoints, type mismatches, direction conflictsChapter 6
VCVerification cases: missing subjects, vacuous verificationChapter 10
CNConstraints: uncompilable or non-boolean expressions, violationsChapter 9
RQRequirements: unsatisfied, unverified, unresolvable referencesChapter 10
PHPhysics: incompatible domains, conservation imbalance, ISQ-type suggestions (PH006 carries the “did you mean ISQ type?” quick-fix)Chapter 14, Appendix E

Looking a code up

When a diagnostic fires, read it where it lives:

  • On the command line, sysml inspect --diagnostics <file> reports every diagnostic for a file or workspace, with its code, severity, and span. The surrounding CLI workflow – checking, querying, and inspecting models – is documented on the sysml-rs portal: CLI workflows.
  • In the editor, the same diagnostics appear as squiggles with the code attached, and codes with quick-fixes (auto-import for E200, the ISQ replacement for PH006, and others) offer them in place. See Editors for setup.

The message text is written to stand alone – it names the element, says what is wrong, and usually says what to do – so the code is a stable handle for searching and suppressing, not something you should need a table to decode.

When the tool is wrong

A diagnostic can itself be the defect: some codes are known to fire on well-formed models. Confirmed cases – with reproductions and workarounds where they exist – are tracked on the portal’s Known limitations page. If a diagnostic contradicts what you can verify against the specification, check that page before rewriting a correct model.

Appendix G: Relationship & Element Kinds Reference

Appendix A answers “how do I write this?”. This appendix answers a different question: “what is this, and does it actually exist?”

Every construct you write in SysML v2 produces one or more elements in an underlying metamodel – a fixed set of classes with fixed names and fixed inheritance. When you write part engine : Engine; you create a PartUsage whose typing is a FeatureTyping; when you write :> you create a Subclassification or a Subsetting depending on context. Tool APIs, model interchange files, diagnostic messages, and the specification itself all speak in these class names, so sooner or later you will meet one and need to know what it is.

The tables below are that dictionary. Read them in this direction:

  • Metamodel class is the authoritative name, as declared in the OMG vocabulary files. Where the class lives in KerML rather than SysML, that is noted – SysML inherits the whole KerML metamodel, so a great many “SysML” relationships are really KerML’s.
  • Specializes is the direct parent from rdfs:subClassOf. This is the fastest way to understand an unfamiliar kind: Redefinition specializes Subsetting specializes Specialization, which tells you almost everything about how redefinition behaves.
  • Textual form is what you type. Some kinds have a keyword, some have a punctuation token, some have both, and a few have no textual notation at all – they exist only as structure the parser creates for you. That last group is called out explicitly, because knowing you cannot write something directly saves you looking for a syntax that does not exist.
  • Clause is where the specification defines the notation. Clause numbers prefixed KerML are from the KerML specification; the rest are SysML.

Nothing here is extrapolated. Every row was checked against the vocabulary files, the specification text, and the reference grammar, and the handful of names that turn out not to exist are listed in their own section rather than quietly dropped.

A keyword does not imply a metaclass

Before the tables, one principle, because it is the single most common way to get lost in a reference like this: the notation and the metamodel are two separate vocabularies, and they do not line up one-to-one. A keyword you can type is not a promise that a class of the same name exists, and a class that exists is not a promise that there is a keyword for it. All three combinations occur:

Has a keywordNo keyword
Has a metaclassReturnParameterMembership, written returnResultExpressionMembership, written as a bare trailing expression
No metaclassmessage, which produces a FlowUsagenothing to name, so nothing to look up

So when a search of the vocabulary for some name comes up empty, that is not yet an answer. It rules out one of two possibilities, and the keyword may still be perfectly normative notation for a class with a different name. Each of the three cells above is worked through below – message under Messages, the two memberships in the membership table – and the same reasoning is what separates the genuinely non-existent names in Names that do not exist from the merely differently-named ones.

The specialization family

These are the relationships you write as a token inside a declaration. They all descend from Specialization, and each has both a symbol and an equivalent keyword. The pairs are fixed by the specification’s lexical structure, not by convention.

Metamodel classSpecializesTextual formClause
SpecializationRelationship:> or specializesKerML 7.3.2.3
SubclassificationSpecialization:> or specializes (on a definition)KerML 7.3.3.3
FeatureTypingSpecialization: or defined byKerML 7.3.4.3
SubsettingSpecialization:> or subsetsKerML 7.3.4.4
RedefinitionSubsetting:>> or redefinesKerML 7.3.4.5
ReferenceSubsettingSubsetting::> or references7.13.2
CrossSubsettingSubsetting=> or crosses7.13.2, 8.4.9.1

The six symbol/keyword pairs are defined as lexical terminals in clause 8.2.2.1.2, which is worth reading once because it is the only place they are all listed together:

DEFINED_BY  = ':'   | 'defined' 'by'
SPECIALIZES = ':>'  | 'specializes'
SUBSETS     = ':>'  | 'subsets'
REFERENCES  = '::>' | 'references'
CROSSES     = '=>'  | 'crosses'
REDEFINES   = ':>>' | 'redefines'

Two of these deserve a note. First, :> is genuinely ambiguous in the source – it creates a Subclassification on a definition and a Subsetting on a feature – so the metamodel class you get depends on what you attached it to, not on the token. Second, in SysML the long form of : is defined by, not typed by; typed by is the KerML spelling of the same terminal and typed is not a SysML keyword at all.

CrossSubsetting is the newest member of the family and is not covered elsewhere in this book. It lets a binary connection definition declare that each end must be one of the values of a feature reached through the other end, which keeps the two sides of the connection coordinated:

package CrossExample {
    part def Device {
        part connectingHub : Hub[1];
    }
    part def Hub {
        part connectedDevice : Device[*];
    }

    connection def DeviceToHub {
        end part device : Device crosses hub.connectedDevice;
        end part hub : Hub crosses device.connectingHub;
    }
}

Relationships with their own declaration syntax

These are written as standalone statements rather than as a token on some other declaration.

Metamodel classSpecializesTextual formClause
DependencyRelationshipdependency [<name> from] <clients> to <suppliers>;7.3.2
AnnotationRelationshipnone directly; created by doc, comment, and bare /* */ (// and //* notes are discarded at parse time and create no element)7.4.1
ImportRelationshipimport7.5.3
MembershipImportImportimport <ns>::<member>;7.5.3
NamespaceImportImportimport <ns>::*;7.5.3
ExposeImportexpose (inside a view)7.26.2
MembershipExposeExpose, MembershipImportexpose <ns>::<member>;7.26.2
NamespaceExposeExpose, NamespaceImportexpose <ns>::*;7.26.2
ConjugationRelationship~ (see note)KerML 7.3.2.4
PortConjugationConjugationcreated implicitly with every port def7.12.3
ConjugatedPortTypingFeatureTypingport <name> : ~<PortDef>;7.12.3
DisjoiningRelationshipdisjoint from (KerML only)KerML 7.3.2.5
UnioningRelationshipunions (KerML only)KerML 7.3.2.7
IntersectingRelationshipintersects (KerML only)KerML 7.3.2.7
DifferencingRelationshipdifferences (KerML only)KerML 7.3.2.7
FeatureChainingRelationship. in SysML; chains in KerML7.6.6, KerML 7.3.4.6
FeatureInvertingRelationshipinverse of (KerML only)KerML 7.3.4.7
TypeFeaturingRelationshipfeatured by (KerML only)KerML 7.3.4.8

Conjugation is a general KerML relationship that flips the direction of every feature of a type, and KerML writes it as ~ or conjugates on any type declaration. SysML exposes only the port-specific slice of it: every port def silently acquires a ConjugatedPortDefinition via a PortConjugation, and you reach it by writing ~ in front of the port definition’s name when typing a port. There is no general conjugates keyword in SysML, and conjugates is not in the SysML keyword list.

The last four rows – chains, inverse of, featured by, and the set operators – are the clearest example of a real kind with no SysML notation. All four classes are in the SysML vocabulary, because SysML inherits KerML’s metamodel wholesale, but none of the keywords appears in the SysML reserved-keyword list in clause 8.2.2.1.2, and the SysML specification text uses phrases like “featured by” only as prose describing semantics. In SysML you get FeatureChaining by writing a dotted path, and you do not get FeatureInverting or TypeFeaturing at all.

Tool note. sysml-rs uses one unified grammar for .sysml and .kerml files, so it will happily accept chains, featured by, and inverse of in a .sysml file and build real syntax nodes for them. Other tools are entitled to reject them. Do not rely on this.

Membership kinds

A membership is the relationship between a namespace and something inside it. Almost all of them are created structurally – you write a nested declaration and the parser decides which membership class it produced – but a surprising number do have a keyword, and those are the ones worth memorising.

Metamodel classSpecializesTextual formClause
MembershipRelationshipnone; created by nesting. Visibility via public / private / protectedKerML 7.2.5.2
OwningMembershipMembershipnone; created by nestingKerML 7.2.5.2
FeatureMembershipOwningMembershipnone; created by nesting a feature in a bodyKerML 7.3.2.6
EndFeatureMembershipFeatureMembershipend7.13.2
ParameterMembershipFeatureMembershipin / out / inout7.6.3
ReturnParameterMembershipParameterMembershipreturn7.19.2
ResultExpressionMembershipFeatureMembershipnone; the trailing bare expression of a body7.19.2
ElementFilterMembershipOwningMembershipfilter <booleanExpr>;7.5.4
SubjectMembershipParameterMembershipsubject7.22.2
ActorMembershipParameterMembershipactor7.21.2
StakeholderMembershipParameterMembershipstakeholder7.21.2
ObjectiveMembershipFeatureMembershipobjective7.22.2
RequirementConstraintMembershipFeatureMembershipassume or require7.21.2
FramedConcernMembershipRequirementConstraintMembershipframe7.21.2
RequirementVerificationMembershipRequirementConstraintMembershipverify7.24.2
StateSubactionMembershipFeatureMembershipentry / do / exit7.18.2
TransitionFeatureMembershipFeatureMembershipaccept (trigger) / if (guard) / do (effect)7.18.3
VariantMembershipOwningMembershipvariant; also each enum literal7.6.7
ViewRenderingMembershipFeatureMembershiprender7.26.2

ReturnParameterMembership and ResultExpressionMembership are the two most commonly doubted names on this list, and both are real KerML metaclasses, each with its own abstract-syntax subclause – KerML 8.3.4.7.8 and 8.3.4.7.7 respectively. They are the mirror image of message: where message is a keyword with no class behind it, these are classes, and only one of them has a keyword. The normative notation in KerML 8.2.5.7.1 shows the difference in two lines:

ReturnFeatureMember   : ReturnParameterMembership   = MemberPrefix 'return' ownedRelatedElement += FeatureElement
ResultExpressionMember : ResultExpressionMembership = MemberPrefix          ownedRelatedElement += OwnedExpression

One has 'return' between the prefix and the element; the other has nothing at all. A calc def body can carry both at once – a named, typed return parameter, and a trailing expression that supplies its value:

package ResultExample {
    calc def RectangleArea {
        in width : ScalarValues::Real;
        in height : ScalarValues::Real;
        return area : ScalarValues::Real;
        width * height
    }
}

The return area : ScalarValues::Real; line is the ReturnParameterMembership. The bare width * height on the last line is the ResultExpressionMembership: there is no keyword to write, and the only thing that marks it is its position as the final expression in the body. If you go looking for a result keyword, you will not find one.

Relationship-shaped usages

SysML’s connectors, flows and behavioural references are relationships and usages at the same time. That double life is visible in the Specializes column, where you will often see two parents.

Metamodel classSpecializesTextual formClause
ConnectorFeature, Relationshipconnect <a> to <b>7.13.2
BindingConnectorConnectorbind <a> = <b>;7.13.3
BindingConnectorAsUsageBindingConnector, ConnectorAsUsagebinding <name> bind <a> = <b>;7.13.3
SuccessionConnectorfirst <a> then <b>; / then <b>;7.13.5
SuccessionAsUsageConnectorAsUsage, Successionsuccession <name> first <a> then <b>;7.13.5
FlowConnector, Stepflow <src> to <tgt>;7.16.2
FlowUsageActionUsage, ConnectorAsUsage, Flowflow or message (see below)7.16.2
SuccessionFlowFlow, Successionsuccession flow <src> to <tgt>;7.16.2
SuccessionFlowUsageFlowUsage, SuccessionFlowsuccession flow7.16.2
AllocationUsageConnectionUsageallocate <a> to <b>;7.15.2
TransitionUsageActionUsagetransition [<name>] first <src> ... then <tgt>;7.18.3
AssignmentActionUsageActionUsageassign <target> := <expr>;7.17.9
PerformActionUsageActionUsage, EventOccurrenceUsageperform action <name> or perform <ref>7.17.6
ExhibitStateUsagePerformActionUsage, StateUsageexhibit state <name> or exhibit <ref>7.18.4
IncludeUseCaseUsagePerformActionUsage, UseCaseUsageinclude use case <name> or include <ref>7.25.3
EventOccurrenceUsageOccurrenceUsageevent occurrence <name> or event <ref>7.9.5
AssertConstraintUsageConstraintUsage, Invariantassert constraint { ... } or assert <ref>7.20.3
SatisfyRequirementUsageAssertConstraintUsage, RequirementUsagesatisfy requirement <ref> by <part>;7.21.4

Notice the pattern in the last several rows: each has a long form that declares something new, and a short form that just names an existing element. That short form is not a special case per construct – it is always the same mechanism. perform a;, exhibit s;, include u;, assume c;, require c;, verify r;, assert c;, event e;, and a bare entry a; all attach the named element by a ReferenceSubsetting, which is why ::> and references appear so far from the connection chapter that introduces them. The specification says so explicitly in clauses 7.9.5, 7.17.6, 7.18.2, 7.18.4, 7.20.3, 7.21.2 and 7.24.2.

Messages

message is the clean worked example of the principle above, and it is worth spelling out because two apparently contradictory statements about it are both true:

  1. message is real, normative SysML v2 syntax. It is in the reserved-keyword list in clause 8.2.2.1.2, it has a normative grammar production in clause 8.2.2.16, and the specification uses it in its own examples.
  2. There is no Message class in the metamodel. Searching SysML-vocab.ttl and Kerml-Vocab.ttl for one returns nothing, and that is the correct result, not a gap in the search.

The reconciliation is in the production’s return type. Clause 8.2.2.16 (Flows Textual Notation) declares:

Message : FlowUsage =
    OccurrenceUsagePrefix 'message'
    MessageDeclaration DefinitionBody
    { isAbstract = true }

Message : FlowUsage – the name on the left is the grammar rule, and the class it constructs is FlowUsage. So message is concrete syntax for an existing metaclass, not the name of a new one. Anyone who says the keyword is real is right; anyone who says the metaclass does not exist is also right; and Message is a production name that never becomes a class.

Two further things exist under the same name, which is what makes it such reliable bait:

  • Flows::Message, an abstract flow def in the shipped Systems library, described there as “the base type of all FlowUsages”. Flows::Flow specializes it directly, and Flows::SuccessionFlow specializes Flows::Flow in turn. This is a library type, not a metaclass.
  • Transfers::MessageTransfer, a KerML library interaction, which FlowTransfer is declared disjoint from. Also a library element.

So the way to think about message is as one of the three kind keywords for a flow usage, alongside flow and succession flow (clause 7.16.2). A message differs from a streaming flow in what it identifies at its ends: a flow names an out feature of the source and an in feature of the target, whereas a message names the source and target events between which a transfer may happen. The { isAbstract = true } in the production above is why a message is always abstract whether or not you write abstract.

package MessageExample {
    attribute def ControlSignal;

    part def Vehicle {
        part controller {
            event occurrence sendControl;
        }
        part engine {
            event occurrence receiveControl;
        }

        message throttleCmd of ControlSignal
            from controller.sendControl
            to engine.receiveControl;
    }
}

The declaration parts are all optional, and this is the general shape:

message [<name>] [of [<payloadName> :] <PayloadType>] [from <sourceEvent> to <targetEvent>];
message <sourceEvent> to <targetEvent>;

The name, the of payload clause, and the from/to event pair are each independently omissible. The payload clause can also carry a multiplicity, and the : before the payload type is dropped along with the payload name when you omit it, which is why of ControlSignal above needs no colon. The second line is a separate grammar alternative: with no name and no payload you may drop from and write the two events with only to between them. If you omit the payload specification entirely, the message places no constraint on what may be transferred; and if you omit the source and target events instead, you may supply a feature value for the message in their place (clause 7.16.2).

Here is how the forms fare in practice. Every one of them produces a message_usage node in the tree, so the keyword is genuinely recognised in all four cases:

FormGrammar alternativesysml-rs
message <name> from <src> to <tgt>;firstaccepted, no diagnostics
message <src> to <tgt>;secondaccepted, no diagnostics
message of <Type> from <src> to <tgt>;firstaccepted; one info on payload typing
message;first, everything omittedparses, but rejected with “missing source endpoint” / “missing target endpoint”

The first two are the forms worth remembering, and both are exactly what the disputed claim said they were.

Tool note. The last row is the one divergence. A bare message; is grammatically legal – every part of the first alternative is optional – and sysml-rs does build a message_usage node for it, but then raises two errors demanding endpoints. It is a degenerate declaration with nothing to say, so this costs you nothing in practice; it is listed only so the errors are not mistaken for a problem with your model.

For contrast, the streaming-flow sibling. Note what changes: a flow def declares its ends and can constrain the payload by redefining it, and the usage names an out feature on one side and an in feature on the other, rather than a pair of events.

package FlowPair {
    item def Fuel;
    part def FuelTank { out fuelOut : Fuel; }
    part def Engine { in fuelIn : Fuel; }

    flow def FuelFlow {
        ref item :>> payload : Fuel;
        end tank : FuelTank;
        end eng : Engine;
    }

    part def Vehicle {
        part tank : FuelTank;
        part eng : Engine;
        flow supply : FuelFlow from tank.fuelOut to eng.fuelIn;
    }
}

FlowDefinition, FlowUsage and SuccessionFlowUsage are all real metaclasses, unlike Message – which is why the flow rows in the table above carry class names and the message row does not.

Names that do not exist

Each of the following is a plausible-sounding name that is not a metamodel class. They are listed rather than omitted because they circulate in tutorials, in tool output, and in earlier drafts of this book, and a confirmed negative is more useful than silence. Every one was checked against Kerml-Vocab.ttl, SysML-vocab.ttl, KerML-shapes.ttl, SysML-shapes.ttl, both specification texts, and the reference grammar.

Name you may seeVerdictWhat actually exists
MessageNot a metamodel class – but a real grammar rule nameThe keyword message, normative in clause 8.2.2.16, producing a FlowUsage; plus Flows::Message in the standard library
TraceDoes not exist anywhere in the languageNothing. trace appears only as jazz_am:trace, an OSLC interchange property in the API shapes files – not a SysML relationship and not a keyword
AssignmentNot a metamodel classAssignmentActionUsage
AllocationNot a metamodel classAllocationDefinition and AllocationUsage
TransitionNot a metamodel classTransitionUsage
PerformNot a metamodel classPerformActionUsage
ExhibitNot a metamodel classExhibitStateUsage
IncludeNot a metamodel classIncludeUseCaseUsage
FrameNot a metamodel classFramedConcernMembership
RequireConstraintUsageNot a metamodel classRequirementConstraintMembership, whose kind is require or assume
AssumeConstraintUsageNot a metamodel classas above
CompositionNot a metamodel classFeature::isComposite, a Boolean property. A usage is composite unless you write ref (clause 7.6.3)
PortionNot a metamodel classPortionKind, an enumeration on OccurrenceUsage, written snapshot or timeslice (clause 7.9.3)
OwningFeatureMembershipNot a metamodel classFeatureMembership, which already specializes OwningMembership
MessageTransferNot a metamodel classTransfers::MessageTransfer, a KerML library interaction. FlowTransfer is declared disjoint from it

ReturnParameterMembership and ResultExpressionMembership have also been challenged, and they are the opposite case to Message: both do exist as metaclasses, each with its own dedicated abstract-syntax subclause in the KerML specification (ResultExpressionMembership at KerML 8.3.4.7.7, ReturnParameterMembership at KerML 8.3.4.7.8). See the membership table above, and the worked example below it.

Definition and usage pairs

Most SysML element kinds come as a matched pair: a def that declares a reusable type, and a usage that places one in a context. The definition keyword is always the usage keyword plus def.

DefinitionUsage keywordMetamodel classCovered?
attribute defattributeAttributeDefinitionch05
enum defenumEnumerationDefinition (specializes AttributeDefinition)ch05
item defitemItemDefinitionch05
part defpartPartDefinitionch05
port defportPortDefinitionch05
occurrence defoccurrenceOccurrenceDefinitionreference only
connection defconnectionConnectionDefinitionch06
interface definterfaceInterfaceDefinitionch06
allocation defallocationAllocationDefinitionch06
flow defflowFlowDefinitionch06
action defactionActionDefinitionch07
state defstateStateDefinitionch08
calc defcalcCalculationDefinitionch09
constraint defconstraintConstraintDefinitionch09
requirement defrequirementRequirementDefinitionch10
concern defconcernConcernDefinitionch10
case defcaseCaseDefinitionch10
analysis defanalysisAnalysisCaseDefinitionch10
verification defverificationVerificationCaseDefinitionch10
use case defuse caseUseCaseDefinitionch10
view defviewViewDefinitionch13
viewpoint defviewpointViewpointDefinitionch13
rendering defrenderingRenderingDefinitionch13
metadata defmetadata, or the symbol @MetadataDefinitionch13

That is the complete set. The vocabulary declares exactly one more *Definition class, ConjugatedPortDefinition, and it has no keyword of its own because you never declare one – every port def generates its conjugate automatically, and you reach it as ~<PortDef>.

Two things that look like they belong in this table but do not:

  • There is no package def. Package specializes Namespace, not Definition, so there is no definition/usage split to have. You write package <Name> { ... } and that is the whole story.
  • connect is not the connection usage keyword. The usage keyword is connection; connect introduces the ends. Both of these are valid, and the second is the short form you get when the declaration part is empty:
connection <name> : <ConnectionDef> connect <a> to <b>;
connect <a> to <b>;

The same shape applies to allocations (allocation <name> : <Def> allocate <a> to <b>;, or just allocate <a> to <b>;) and to bindings (binding <name> bind <a> = <b>;, or just bind <a> = <b>;).

Metadata is worth one extra note, because it has two notations that are easy to confuse and only one of them is a @:

@<MetadataDef> [{ <body> }]          // a metadata usage, as a member
#<MetadataDef> <declaration>         // a user-defined keyword, as a prefix

The @ form is a metadata usage attached to the element that owns it, and @ is simply the symbolic equivalent of metadata ... defined by ... (clause 7.27.2). The # form is different in kind: it is a user-defined keyword, placed immediately before the reserved keyword of a declaration, and if the named metadata definition specializes SemanticMetadata it also implies a specialization (clause 7.27.4). So #situation occurrence def Failure; declares an OccurrenceDefinition that implicitly subclassifies whatever situation’s base type names. With semantic metadata you may even drop the language keyword entirely and write #situation def Failure;.

Verifying the examples

Every sysml block in this appendix parses with no errors and no error nodes in the tree. The forms were checked with:

sysml --quiet inspect --no-stdlib --diagnostics --json <file>
sysml --quiet inspect --no-stdlib --cst <file>

The second command matters as much as the first. A construct that is not really in the grammar can still parse without complaint, by being absorbed as an ordinary identifier – so the check is not “did it parse?” but “did a node for this construct appear in the tree?”. For the three examples above, the tree contains message_usage, crosses_clause, return_feature and result_expression nodes, which is what makes them evidence rather than guesswork.

The message example does draw one info-level advisory, that its payload type is not matched by a feature type on its endpoints. That is expected: a message names events at its ends rather than typed ports, so there is nothing there to carry the payload type. The example is structurally the same as the specification’s own in clause 7.16.2.

Tool note. Three spec-legal forms are not yet accepted by sysml-rs, so if you meet them in someone else’s model do not assume the model is wrong.

  • frame <ref>; inside a requirement body is rejected with “Unexpected keyword frame, though the long form frame concern <name> : <Concern>; works. Clause 7.21.2 permits both.
  • verify <ref>; is accepted inside an objective { ... } block but not directly in a verification definition body, where verify requirement <ref>; is required instead. Clause 7.24.2 permits both.
  • #<MetadataDef> as a declaration prefix produces “Syntax error near #.... The # token is recognised, but it cannot yet be attached to the declaration that follows it. The @ metadata usage form is unaffected and parses cleanly.

Source anchors

  • Metamodel vocabulary: references/sysmlv2/SysML-vocab.ttl, references/sysmlv2/Kerml-Vocab.ttlrdfs:subClassOf gives the Specializes column, rdfs:comment the descriptions
  • API interchange shapes: references/sysmlv2/SysML-shapes.ttl, references/sysmlv2/KerML-shapes.ttl – note these carry OSLC properties such as jazz_am:trace that are not language constructs
  • Specification text: references/sysmlv2/derived/SysML-spec-r2025-04.txt, references/sysmlv2/derived/KerML-spec-r2025-04.txt – source of every clause number above
  • Reference grammar: references/sysmlv2/SysML-v2-Pilot-Implementation/org.omg.sysml.xtext/src/org/omg/sysml/xtext/SysML.xtext, plus …/org.omg.kerml.xtext/…/KerML.xtext and …/KerMLExpressions.xtext
  • Shipped standard library, for the spec authors’ own usage: libraries/standard/library.kernel/ and libraries/standard/library.systems/Flows.sysml and Transfers.kerml are where Message and MessageTransfer live

Appendix H: Lexical Reference

The chapters teach constructs. This appendix covers the smaller rules underneath them – how names, literals, and comments are actually written – and in particular the two forms the chapters use without stopping to explain: root-qualified names and quoted names.

Names

An ordinary name is an identifier: CoffeeMachine, grindSize, waterTank. The book’s convention, and a common one, is UpperCamelCase for definitions and lowerCamelCase for usages and attributes. Nothing in the language enforces that, but a reader scanning a model relies on it.

Qualified and root-qualified names

<Package>::<Member>
$::<Package>::<Member>
part grinder : CoffeeMachineDomain::Definitions::Grinder;

A qualified name walks down from a visible namespace. Reach for one when a bare name would be ambiguous across packages.

Prefixing $:: makes it a root-qualified name: lookup starts at the model root rather than the enclosing scope. It is the escape hatch for the case where a local name shadows the one you meant.

Quoted names, for names that are not identifiers

<'<short-name>'> '<long name>'
requirement def <'REQ-TEMP-01'> 'Extraction Temperature' {
    doc /* Water temperature shall remain within operating range. */
}

Read it as: this requirement’s short name is REQ-TEMP-01 and its name is Extraction Temperature.

A name in single quotes is an unrestricted name, and it may contain characters an identifier cannot – spaces, hyphens, punctuation. Angle brackets declare a short name, which is why requirement identifiers like REQ-TEMP-01 are written <'REQ-TEMP-01'>: the hyphens would otherwise be read as subtraction. To put a quote inside such a name, escape it: 'REQ\'TEMP-02'.

This is the form Chapter 10 and Chapter 11 use for requirement identifiers.

Use quoted names where they earn their keep – requirements and cases that carry an external identifier or a human-readable title – and plain identifiers everywhere else.

Literals

attribute grindSize   : Real    = 0.4;
attribute enabled     : Boolean = true;
attribute label       : String  = "Main Grinder";
  • Booleans are true and false.
  • Strings are double-quoted.
  • Reals may lead with a dot – .5 is valid – and may use scientific notation: 1e3, 9.8e-2.
  • null is the null literal. () is an equivalent spelling of the same empty value, which is worth recognising when you meet it in library sources.
  • * is the infinity literal. It is what makes [0..*] mean “any number of” in a multiplicity, and it is the same token as multiplication – position tells them apart.

Comments, and why doc is different

part def Brewer {
    doc /* Heats and pressurizes water for extraction. */

    //* A note to whoever is editing this file next.
    attribute waterTemp : Real = 93.0;
}

doc creates a documentation element in the model. It survives into the model itself, so tools can show it on hover, carry it into a generated document, and export it through the API.

//* notes do not. Per the specification’s own grammar they are hidden tokens, discarded during parsing in the same way whitespace is. They never become part of the model.

So the choice is not stylistic. If the sentence explains what an element is, it belongs in doc, where a reviewer who never opens the source file will still see it. If it is a message to the next person editing the file, //* is right.

The most common mistake here is writing model meaning into a note and then wondering why it does not appear anywhere in the tooling.

Marking the language of documentation

part def Brewer {
    doc locale "en-GB" /* Heats and pressurises water for extraction. */
}

A doc may carry a locale – a string naming the natural language the documentation is written in. It is optional, and worth setting on a model whose documentation is translated or whose readership is mixed, because it lets a tool pick the right text rather than guessing. Comments take the same modifier.

Embedding another language

The specification also provides a way to carry a fragment of some other language inside a model element:

[rep <name>] language "<language-name>" /* <the fragment> */

This is a textual representation (SysML §8.2.2.4.3). The intent is that a constraint expressed in OCL, a formula in MathML, or a snippet of source can travel with the element it belongs to, tagged with what language it is in, so a tool that understands that language can pick it up and one that does not can leave it alone.

Not available in sysml-rs. The parser does not accept this form. Worse, a rep line without the string is absorbed as a pair of ordinary names rather than reported as an error, so it fails quietly. The construct is in the specification and in the grammar; it is simply not implemented here, and this book does not use it.

Appendix I: Modeling Review Checklists

Model review is a team activity, not a solo exercise. You bring a model to a review gate, a group of peers asks hard questions about completeness and correctness, and the model either passes or goes back for revision. The problem is that “completeness” is vague. Without a written standard, reviewers focus on whatever they happen to notice, and different reviewers notice different things.

This appendix gives you four copy-ready checklists, one for each major review type: Architecture, Requirements, Behavior, and Verification. Each checklist is a set of yes/no questions phrased so that “yes” means the model is correct in that dimension. A “no” or “unsure” answer identifies a specific gap that needs attention before the review can pass.

How to Use These Checklists

Copy the relevant checklist into your review document, pull request description, or team wiki page before the review meeting. Work through each item independently: read the question, inspect the model, and mark it. Do not answer from memory – open the .sysml files and check.

The checklist items are sequenced so that earlier items catch structural problems that would make later items meaningless. If item 1 fails, fix it before assessing item 3.

Each item includes a chapter reference in parentheses. If a question is unclear, that chapter explains the underlying construct in full.

Not every item is a language rule. Items marked Convention: or Style: describe review heuristics – a model is perfectly legal either way. They earn their place because they catch review pain later, but a team can consciously adapt or waive them.

Note: These checklists target model completeness and structural consistency. They are not a substitute for domain correctness review. A model can pass every checklist item and still describe a system that does not work in the real world. Use domain experts alongside these checklists.

The Coffee Machine example developed throughout this book is a useful validation target. After reading this appendix, you can apply each checklist to the files in examples/coffee-machine/ and observe which items pass immediately and which require model elaboration.


Checklist 1: Architecture Review

Use this checklist during a structural architecture review, typically at the end of the definition phase, before behavioral modeling begins. It verifies that the structural model has enough completeness and internal consistency to serve as a scaffold for behaviors, requirements, and connections.

  • Convention: does every part def have at least one part usage that instantiates it within the model? The language does not require this – an uninstantiated definition is perfectly legal, and library or catalogue definitions legitimately have no usage in the package that declares them. The review question is whether each unused definition is intentional (a library entry, work in progress) or dead weight that nothing verifies, connects, or simulates. (Chapter 3, Chapter 5)

  • Convention: is every connection across a port def governed by a named interface def that specifies the connection contract? There is nothing to check on the conjugate side – every port def automatically carries a conjugated form (~PortDef); the language creates it for you. The heuristic is about the contract: when connections across a port have no interface def, reviewers and tools have no stated agreement to check the two ends against. (Chapter 5, Chapter 6)

  • Style: are load-bearing connect usages typed by a named connection def? Anonymous connections are fully normative – they parse, carry flows, and are queryable like any other usage. Naming the definition is a reuse and traceability convention: a named connection def can be instantiated again elsewhere and gives allocations and satisfaction links a stable target. (Chapter 6)

  • Does every part usage that can have more than one instance declare an explicit multiplicity? Omitting multiplicity implies exactly one instance. If a coffee machine can have between one and four heating elements, that constraint belongs in the model, not in a comment or document. (Chapter 5, Chapter 3)

  • Convention: are all import statements explicit rather than wildcard in production packages? Wildcard imports (import SomePkg::*;) are convenient during drafting but create invisible name dependencies. In a reviewed package, each imported name should be traceable to a specific definition. (Chapter 4)

  • Does every behavioral action that is assigned to a structural part have a corresponding allocation linking the action to that part? Without explicit allocation, the model declares behavior in the abstract but does not commit it to any physical or logical element. Reviewers and analysis tools need this link to check feasibility. (Chapter 6, Chapter 5)

  • Convention: does the package structure separate domain definitions from contextual usages, with no definitions buried inside usage packages? Definitions mixed into context packages cannot be reused elsewhere and are harder to find during review. The canonical pattern is a definitions package imported by one or more context packages. (Chapter 4, Chapter 5)


Checklist 2: Requirements Review

Use this checklist during a requirements review, typically after the requirements package has been baselined and before the architecture team begins assigning satisfaction links. It verifies that every requirement is well-formed, bounded, and connected to the rest of the model.

  • Does every requirement usage have a subject member declaring what the requirement constrains? A requirement without a subject is a floating assertion. The subject tells every reader – and every automated tool – exactly which element of the model must satisfy the requirement. (Chapter 10)

  • Convention: does every leaf requirement usage (one with no child requirements) have at least one require constraint or explicit constraint expression? A requirement with only a doc annotation is legal, but it is an English sentence rather than a formal constraint. This convention asks leaf requirements to carry a verifiable condition that a constraint solver or test can evaluate. (Chapter 9, Chapter 10)

  • Is every leaf requirement either satisfied by a satisfy usage, or explicitly deferred with a documented rationale? An orphan requirement – one that exists in the model without a satisfaction link and without a deferral note – is an invisible gap. Reviewers cannot tell whether it was overlooked or intentionally left open. (Chapter 10)

  • Does every satisfy usage name a specific requirement usage rather than a requirement definition? Satisfying a definition claims to satisfy all possible instances of it, which is almost never the intent. The satisfaction link must point to the specific requirement usage in its context. (Chapter 10)

  • Convention: is the requirement hierarchy no deeper than four levels? A hierarchy deeper than four levels usually indicates scope creep: requirements at the bottom level are design decisions, not stakeholder needs. Inspect any branch deeper than four levels and ask whether the lowest items belong in a design document instead. (Chapter 10, Chapter 15)

  • Convention: does every requirement have a doc annotation with rationale text explaining why the requirement exists? A requirement without rationale cannot be prioritized, traded off, or defended to a stakeholder. Rationale also guides verification: it tells you what evidence would actually satisfy the requirement. (Chapter 13, Chapter 10)

  • Does every stakeholder concern raised during elicitation appear as a named concern usage or requirement in the model? A concern that was raised during review but never captured in the model is a hidden risk. Even concerns that do not yet have a formal requirement should appear as concern usages so that the model tracks them explicitly. (Chapter 10)


Checklist 3: Behavior Review

Use this checklist during a behavior review, typically after action flows and state machines have been drafted. It verifies that behavioral elements are internally consistent, properly connected, and grounded in the structural model.

  • Does every state def have at least one entry transition and at least one exit condition? A state with no entry transition is unreachable. A state with no exit condition is a dead end. Either situation represents a logical error in the behavioral model. Check every state node, including nested parallel states. (Chapter 8, Chapter 7)

  • Does every transition usage have an explicit guard, trigger, or both – with the only exception being the initial pseudo-transition? An unguarded, untriggered transition (other than the initial transition from the initial state) fires unconditionally on entry to its source state, which almost always indicates a modeling error. Verify the intent before approving. (Chapter 8)

  • Are all action usages within a sequence model connected by succession links, with no disconnected action nodes? An action node with no incoming succession and no outgoing succession is an island: it never executes in the sequence and will not appear in simulation or analysis. Every non-initial action needs at least one incoming succession link. (Chapter 7)

  • Do all control-flow fork nodes have a corresponding join node that synchronizes the same branches? A fork without a join creates parallel branches that never converge. This may be intentional in some models, but it should be explicit: if there is no join, add a comment explaining why the branches are designed to diverge permanently. (Chapter 7)

  • Does every accept action and send action reference a port or flow definition that is declared in the structural model? Behavioral elements that send or receive across ports must reference the same port type used in the structural layer. A mismatch – for example, an action that sends a WaterFlow on a port typed for ElectricalPower – is a semantic error that the structural review should have caught but the behavior review must confirm. (Chapter 6, Chapter 7)

  • Style: does every exhibit usage in an action definition reference a named state def rather than an anonymous inline state? Inline anonymous states are legal, but they cannot be referenced from other actions, tested in isolation, or carry documentation. Prefer a named state definition for any state that has more than trivial logic. (Chapter 8, Chapter 3)

  • Do action parameter types match the item types declared on the ports or flows they read from or write to? A perform action that reads a CoffeeGrounds item but whose input parameter is typed as Water will pass a parser but fail during simulation or analysis. Parameter types must match the flow types at every boundary. (Chapter 7, Chapter 6)


Checklist 4: Verification Review

Use this checklist during a verification review, typically after the verification cases package has been drafted and before formal test planning begins. It verifies that every requirement has a path to objective evidence and that no verification case exists without a purpose.

  • Does every leaf requirement usage have at least one verify usage inside a verification case that references it? A requirement with no verification case cannot be closed. Requirements without objective evidence are assumptions. This item should be checked against the same list of leaf requirements identified in Checklist 2 item 3. (Chapter 10)

  • Convention: is every verification case usage annotated with a verification method metadata tag – one of: analysis, test, inspection, or demonstration? Without a method tag, the verification case is unscheduled: you do not know who performs it, what evidence it produces, or when it runs. Method classification is also required for V&V plan traceability. (Chapter 13, Chapter 10)

  • Does every verification case usage contain at least one verify usage that references a named requirement? A verification case that contains no verify usages is a placeholder with no executable content. It may pass a parser but it contributes nothing to requirement closure. These orphan cases must either be populated or removed. (Chapter 10)

  • Style: does every assert constraint usage inside a verification case reference a named constraint def rather than an inline anonymous constraint? An anonymous inline constraint inside a verification case cannot be reused, named, or referenced from a test script or analysis tool. Named constraint definitions allow the same constraint to appear in both the requirement and the verification case, making the link explicit. (Chapter 9, Chapter 10)

  • Convention: is every verification case annotated with a responsible stakeholder or team via a metadata usage? An unassigned verification case will not be executed. Ownership metadata can be as simple as a #Responsible { team = "Systems Test"; } annotation; the convention asks that it be present so that test planning tools and project managers can schedule the work. (Chapter 13)

  • Does the coverage matrix show every requirement at the leaf level mapped to at least one verification case? Individual checklist items verify individual connections. This item asks you to look at the whole picture: generate or inspect the requirement-to-verification-case coverage matrix and confirm no leaf requirement appears as uncovered. The Systems Modeling API supports this as a graph query. (Chapter 10, Appendix C)

  • Are verdict expressions in verification cases written using named calculation def results rather than literal boolean constants? A verification case whose verdict is assert true; is always passing and provides no information. Verdicts must evaluate observable conditions: parameter values, measured outputs, or constraint evaluations drawn from a named calculation. (Chapter 9, Chapter 10)

  • Has a view def <Milestone>ReviewView (carrying the filter <viewCondition>;) plus a view usage (carrying expose <package>::*;) been authored to scope the milestone review? A milestone review pulls a particular slice of the model — the requirements affected by this gate, the parts touched by this design phase, the verification cases due this iteration. Authoring that slice as a named view (rather than relying on each reviewer to remember which packages to inspect) makes the scope explicit, reviewable, and re-runnable for the next milestone. The filter clause lives in the view definition and narrows the slice to the relevant element kinds; the expose ... ::* lives in the view usage (a view def body does not admit expose) and keeps the view fresh as the underlying packages grow. (Chapter 13: Metadata and Views)


Appendix J: Occurrences, Time Slices, and Snapshots

The chapters model a coffee machine: what it is made of, what it does, what it must satisfy. What they do not dwell on is that a machine, a brew, and a fault are all things that happen over time, and that SysML v2 has a specific family of constructs for saying so.

You can build a great deal without them. Reach for them when time itself becomes part of what you are modelling – phases of an operation, the state of something at an instant, or one particular real-world unit as opposed to the kind.

Occurrences

occurrence def <Name>;
occurrence <name> : <OccurrenceDef>;
occurrence def BrewCycle;

An occurrence is something that happens or exists over an interval of time. occurrence is its kind keyword, and it works like every other kind keyword you have met – occurrence def for the definition, occurrence for a usage (SysML §7.9.2).

You have been using occurrences all along without naming them. Actions and states are occurrence usages of more specialized kinds: that is what lets a state have a duration and an action have a start and an end. This appendix is about the layer underneath them.

An occurrence usage may only be typed by an occurrence definition of some kind, or by a KerML class.

Time slices and snapshots

timeslice <name>;          // equivalent to `timeslice occurrence <name>`
snapshot  <name>;          // equivalent to `snapshot occurrence <name>`
timeslice <kind> <name>;   // e.g. `timeslice part activeSection`
occurrence def BrewCycle {
    timeslice grinding;
    timeslice heating;
    timeslice extracting;

    snapshot extractionStart;
}

Read it as: a brew cycle has three phases and one instant worth naming.

A time slice is a portion of an occurrence over an interval – the grinding phase of a brew. A snapshot is a portion at a single instant – the moment extraction begins. Both are written by putting the keyword immediately before the kind keyword of the declaration, and both may stand in place of the kind keyword, in which case they mean timeslice occurrence and snapshot occurrence (SysML §7.9.3).

One rule to remember: a time slice or snapshot must be declared inside the body of an occurrence definition or usage. It is a portion of something, so there has to be a something.

The distinction is worth keeping straight because it is the difference between a duration and an instant. “While heating” is a time slice. “At the moment the pump started” is a snapshot. A constraint on a temperature rise belongs to the former; a constraint on a temperature reading belongs to the latter.

Individuals

individual def <Name> :> <OccurrenceDef>;
individual <kind> def <Name> :> <Def>;
part def Grinder;
individual part def Grinder_SN0417 :> Grinder;

occurrence def BrewCycle;
individual def BrewCycle_20260821_0730 :> BrewCycle;

An individual definition describes exactly one thing in the world, rather than a kind of thing. Grinder is a kind; Grinder_SN0417 is the grinder on the bench with that serial number. BrewCycle is a kind of operation; the second declaration is the brew that ran at half past seven.

Like timeslice and snapshot, the keyword goes immediately before the kind keyword, or in place of it (SysML §7.9.4).

A usage counts as an individual usage when its definition is an individual definition – you do not have to mark the usage. And a usage may not have more than one individual definition, which follows from what an individual is: two “exactly one thing” claims about the same usage would contradict each other.

This is the construct to reach for when the model has to talk about real units – a serial-numbered part, a specific test run, an incident that actually happened – rather than the design.

Event occurrences

occurrence def BrewCycle {
    event occurrence pumpOn;
}

An event occurrence marks a point of interest within an occurrence (SysML §7.9.5). It is what the from and to ends of a message name, which is why Appendix G mentions them under flows: a message runs between two events rather than between two features.

Where this shows up elsewhere

  • Successions and the HappensBefore association order occurrences in time. Chapter 7 covers successions.
  • States and actions are occurrence usages, which is why they have durations and boundaries. Chapter 8, Chapter 7.
  • The standard library’s Occurrences package declares the machinery – including HappensBefore, and the startShot / endShot / snapshots structure that gives every occurrence its boundaries. Chapter 14.

Language Reference

This section is generated from the sysml-rs language pack: a machine-readable index of every SysML v2 / KerML language concept, one “card” per concept. The pack is an index over the normative sources, not an authority: every card points at the governing specification clause and paraphrases it — where a card and the specification disagree, the specification wins. Use these pages to find which clause governs a question, then read and cite that clause.

Implementation-support marks (parse / resolve / elaborate / execute) are machine-derived from test evidence in the sysml-rs repository, never hand-written: ✓ means a gate test passed for that axis at the current spec drop, ✗ means a reviewed known limitation, and unknown means no evidence either way — it never means “no”.

Generated from spec drop 2025-04 (327 cards, pack tree 44751e816e61e04c8d799cf5c1adacb6a987bb52073b87794f486eb35e71f8b5).

Categories

Raw JSON for tools and agents

The pack itself ships with this book as static JSON under language-pack/: manifest.json (spec-drop identity and pinned source hashes), cards/<id>.json (one card per concept), and indexes/ (keywords.json term index, aliases.json alias → card id, dependencies.json one-hop expansion map, cards.jsonl the whole corpus as JSONL). On the published site these resolve as <book-url>/language-pack/manifest.json, <book-url>/language-pack/cards/<id>.json, and so on. The intended lookup pattern: find candidate cards via indexes/keywords.json or indexes/aliases.json, read cards/<id>.json, expand one hop via indexes/dependencies.json, then cite the card’s normative_clauses.

Regenerating

These pages and the raw JSON are generated artifacts — edit the generator, not the pages. From a sibling sysml-rs checkout:

# regenerate the pack (see tools/spec-index/README.md for source fetching)
cargo run -p spec-index -- language-pack

# re-render this section + refresh the shipped JSON (from the book repo)
./tools/render-language-pack.sh

Licensing and attribution

The pack is citation-only by design: no OMG specification prose is reproduced in any card, example, or page — summaries are original paraphrases, and normative content is referenced by document + clause locator. The grammar information (rule names, structure, keyword literals) is derived from the Xtext grammars of the SysML-v2 Pilot Implementation, which is licensed LGPL-3.0-or-later; this notice covers that derivation. Metamodel facets are derived from the OMG-published TTL vocabularies at pinned revisions. These generated pages and the shipped JSON carry the terms above, distinct from the CC-BY-4.0 license of this book’s prose chapters.

Behavior

Generated from the sysml-rs language pack — see the Language Reference index for provenance, licensing, and the raw JSON. 51 cards.

Behavior

KerMLkerml.behavior.behavior

A behavior is a classifier of things that happen over time — a specification of how a system acts. It is the KerML base of SysML action definitions and its steps order the occurrences it coordinates.

package P { behavior B; }

Normative clauses: KerML §7.4.7

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Function

KerMLkerml.behavior.function

A function is a behavior that produces a result by evaluating an expression over its parameters. It is the KerML base of SysML calculation definitions; a return parameter carries its result.

package P { function F; }

Normative clauses: KerML §7.4.8

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Interaction

KerMLkerml.behavior.interaction

An interaction is both a behavior and an association: a behavior that coordinates the things it connects, describing how they interact over time. It is the KerML base of item flows between participants.

package P { interaction I; }

Normative clauses: KerML §7.4.10

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Invariant

KerMLkerml.behavior.invariant

An invariant asserts that a boolean expression must always hold, written inv { expr } inside a constraint. It lowers to a distinct Invariant carrying the asserted boolean condition.

constraint def C { inv { 1 > 0 } }

Normative clauses: KerML §8.3.4.7.5

Support (sysml-rs): parse ✓ · resolve unknown · elaborate unknown · execute unknown

Predicate

KerMLkerml.behavior.predicate

A predicate is a function whose result is a boolean — a named condition that evaluates to true or false. It is the KerML base of SysML constraint definitions.

package P { predicate Pr { true } }

Normative clauses: KerML §8.2.5.7.3

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Calc Result Is Expression Value

KerMLkerml.validation.calc-result-is-expression-value

A calculation returns, in its result parameter, the value of evaluating its result expression.

Normative clauses: KerML §7.4.8.3

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Clock Timeflow Constraint

KerMLkerml.validation.clock-timeflow-constraint

The currentTime of a Clock snapshot equals the TimeOf that snapshot relative to the clock: snapshots->forAll{TimeOf(s, thisClock) == s.currentTime}.

Normative clauses: KerML §9.2.12.2 (nearest heading)

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Durationof End Minus Start

KerMLkerml.validation.durationof-end-minus-start

DurationOf = TimeOf(endShot) − TimeOf(startShot).

Normative clauses: KerML §9.2.12.2 (nearest heading)

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Happens Just Before No Intervening

KerMLkerml.validation.happens-just-before-no-intervening

HappensJustBefore: no occurrence can exist in the gap between earlier and later.

Normative clauses: KerML §9.2.4.2 (nearest heading)

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Invocation Binds Arguments To Input Params

KerMLkerml.validation.invocation-binds-arguments-to-input-params

Input parameters bind to their corresponding argument values.

Normative clauses: KerML §7.4.9.1

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Local Clock Defaults To Universal Clock

KerMLkerml.validation.local-clock-defaults-to-universal-clock

An occurrence’s localClock defaults to Clocks::universalClock; suboccurrences inherit it.

Normative clauses: KerML §9.2.4.2 (nearest heading)

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Occurrence Has Lifetime Extent

KerMLkerml.validation.occurrence-has-lifetime-extent

An occurrence has an extent in time (lifetime) from start to end.

Normative clauses: KerML §9.2.4

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Timeof Continuity Constraint

KerMLkerml.validation.timeof-continuity-constraint

If A HappensJustBefore B, TimeOf(A.endShot) == TimeOf(B).

Normative clauses: KerML §9.2.12.2 (nearest heading)

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Timeof Ordering Constraint

KerMLkerml.validation.timeof-ordering-constraint

If A HappensBefore B, TimeOf(A.endShot) ≤ TimeOf(B).

Normative clauses: KerML §9.2.12.2 (nearest heading)

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Accept Action

SysMLsysml.behavior.accept-node

An accept action waits for an incoming event or message and binds its payload, so the action’s flow continues only once a matching occurrence is received. It is written with the accept keyword inside an action body.

package P { action def A { accept sig; } }

Normative clauses: SysML §7.17.8

Support (sysml-rs): parse ✓ · resolve unknown · elaborate unknown · execute unknown

Action Definition

SysMLsysml.behavior.action-definition

An action definition defines a reusable kind of behavior that transforms inputs into outputs over time. Action usages instantiate it as steps within a larger behavior.

package P { action def Move; }

Normative clauses: SysML §8.3.17.3

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Action Usage

SysMLsysml.behavior.action-usage

An action usage is an occurrence of behavior that transforms inputs into outputs. It is typed by an action definition and can hold parameters and nested actions.

package P { action def Move; action m : Move; }

Normative clauses: SysML §8.3.17.4

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Assignment Action

SysMLsysml.behavior.assignment-node

An assignment action sets a feature to the value of an expression, mutating model state as the action flow proceeds. It is written with the assign keyword and the := operator.

package P { action def A { attribute x; assign x := 1; } }

Normative clauses: SysML §7.17.9

Support (sysml-rs): parse ✓ · resolve unknown · elaborate unknown · execute unknown

Calculation Definition

SysMLsysml.behavior.calculation-definition

A calculation definition defines a reusable computation that maps inputs to a returned result. It is a behavior whose body is an expression; usages invoke it to compute values.

package P { calc def Sum { in a; in b; return a + b; } }

Normative clauses: SysML §8.3.19.2

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Calculation Usage

SysMLsysml.behavior.calculation-usage

A calculation usage is a typed occurrence of a calculation within a model. It is typed by a calculation definition and binds its inputs to produce a result.

package P { calc def Sum { in a; in b; return a + b; } calc s : Sum; }

Normative clauses: SysML §8.3.19.3

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Decision Node

SysMLsysml.behavior.decision-node

A decision node is a control node that routes flow down exactly one outgoing branch, chosen by guards on its successions. It is written with the decide keyword inside an action body.

package P { action def A { decide d; } }

Normative clauses: SysML §7.17.3

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Exhibit State Usage

SysMLsysml.behavior.exhibit-state

An exhibit state usage lets a part or other structure display a state machine defined elsewhere as its own behavior, without redefining it. It is written with the exhibit state keywords and references the state whose behavior is exhibited.

package P { state def SD; part def M { exhibit state st : SD; } }

Normative clauses: SysML §8.3.18.2

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

For Loop Action

SysMLsysml.behavior.for-loop-node

A for loop action iterates a body action over the elements of a sequence, binding a loop variable to each element in turn. It is written with the for keyword, a variable, and an in sequence.

package P { action def A { attribute nums; for i in nums { action b; } } }

Normative clauses: SysML §7.17.12

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Fork Node

SysMLsysml.behavior.fork-node

A fork node is a control node that splits action flow into concurrent branches, activating every outgoing succession at once. It is written with the fork keyword inside an action body.

package P { action def A { fork f; } }

Normative clauses: SysML §7.17.3

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Join Node

SysMLsysml.behavior.join-node

A join node is a control node that synchronizes concurrent branches, resuming flow only once every incoming succession has completed. It is written with the join keyword inside an action body.

package P { action def A { join j; } }

Normative clauses: SysML §7.17.3

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Merge Node

SysMLsysml.behavior.merge-node

A merge node is a control node that combines alternative branches back into a single flow, continuing when any one incoming succession arrives. It is written with the merge keyword inside an action body.

package P { action def A { merge m; } }

Normative clauses: SysML §7.17.3

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Message

SysMLsysml.behavior.message

A message (message ... from ... to ...) models a transfer between a source and target end without specifying how the payload is obtained or delivered. The spec models a message as a flow usage (the grammar rule Message returns FlowUsage), so it lowers to a FlowUsage — there is no distinct Message metaclass.

package P { part a; part b; message m from a to b; }

Normative clauses: SysML §7.16.1

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Message Event

SysMLsysml.behavior.message-event

A message event is one end of a message (message m from a to b; — each of a and b). The grammar rule MessageEvent returns EventOccurrenceUsage: each end lowers to an unnamed EventOccurrenceUsage owned by the message through a ParameterMembership, referencing the end feature via an owned ReferenceSubsetting — there is no distinct MessageEvent metaclass.

package P { part def C { part a; part b; message m from a to b; } }

Normative clauses: SysML §8.2.2.16

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Perform Action

SysMLsysml.behavior.perform-action

A perform action invokes an action defined elsewhere as a step in the current behavior, reusing that action’s definition. It is written with the perform keyword inside an action body.

package P { action def A { perform action pa; } }

Normative clauses: SysML §7.17.6

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Send Action

SysMLsysml.behavior.send-node

A send action dispatches a payload to a target so it can be accepted elsewhere. It is written with the send keyword and a to target inside an action body.

package P { action def A { action tgt; send sig to tgt; } }

Normative clauses: SysML §7.17.7

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

State Action Usage

SysMLsysml.behavior.state-action-usage

A state subaction (an entry, do, or exit action of a state) is modeled as an ActionUsage owned by the state through a StateSubactionMembership that carries the subaction kind (the grammar rule StateActionUsage returns ActionUsage). It lowers to an ActionUsage owned via a StateSubactionMembership — there is no distinct StateActionUsage metaclass.

package P { state def S { entry action e; do action d; exit action x; } }

Normative clauses: SysML §8.3.18.4

Support (sysml-rs): parse ✓ · resolve unknown · elaborate unknown · execute unknown

State Definition

SysMLsysml.behavior.state-definition

A state definition defines a reusable kind of state machine — a set of states and transitions the modelled system moves through. State usages instantiate it.

package P { state def SM { state s1; state s2; } }

Normative clauses: SysML §8.3.18.5

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

State Usage

SysMLsysml.behavior.state-usage

A state usage is an occurrence of a state in a state machine, optionally with entry, do, and exit behaviors. It is a stage the modelled system can occupy.

package P { state def SM { state s1; state s2; } }

Normative clauses: SysML §8.3.18.6

Support (sysml-rs): parse ✓ · resolve unknown · elaborate unknown · execute unknown

Succession

SysMLsysml.behavior.succession

A succession orders behavior, requiring one action or step to complete before its successor begins. It connects a source occurrence to a target occurrence.

package P { action def A { action a1; action a2; first a1 then a2; } }

Normative clauses: SysML §8.3.13.6

Support (sysml-rs): parse ✓ · resolve unknown · elaborate unknown · execute unknown

Succession Flow Usage

SysMLsysml.behavior.succession-flow-usage

A succession flow, written succession flow … from … to …, is a flow that also imposes a temporal succession between its ends. It lowers to a distinct SuccessionFlowUsage.

part def P { in item a; out item b; succession flow sf from a to b; }

Normative clauses: SysML §8.3.16.4

Support (sysml-rs): parse ✓ · resolve unknown · elaborate unknown · execute unknown

Terminate Node

SysMLsysml.behavior.terminate-node

A terminate node (terminate) ends an occurrence during an action’s performance. It lowers to a distinct TerminateActionUsage — an ActionUsage specializing the Systems Library TerminateAction — whose terminatedOccurrence defaults to the performing occurrence.

package P { action def A { terminate; } }

Normative clauses: SysML §8.3.17.16

Support (sysml-rs): parse ✓ · resolve unknown · elaborate unknown · execute unknown

Transition Usage

SysMLsysml.behavior.transition-usage

A transition usage specifies a change from a source state to a target state, optionally guarded by a condition and carrying trigger and effect behaviors. It is written with the transition keyword and a then target.

package P { state def TrafficLight { state red; state green; transition first red then green; } }

Normative clauses: SysML §8.3.18.9

Support (sysml-rs): parse ✓ · resolve unknown · elaborate unknown · execute unknown

While Loop Action

SysMLsysml.behavior.while-loop-node

A while loop action repeats a body action while a boolean condition holds, re-evaluating the condition before each iteration. It is written with the while keyword and a condition inside an action body.

package P { action def A { while true { action w; } } }

Normative clauses: SysML §7.17.12

Support (sysml-rs): parse ✓ · resolve unknown · elaborate unknown · execute unknown

Calc Always Has Result Parameter

SysMLsysml.validation.calc-always-has-result-parameter

A calculation always has a result parameter (inherited if not owned); evaluation always yields a result.

Normative clauses: SysML §7.19.2

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Decision Node Exactly One Outgoing

SysMLsysml.validation.decision-node-exactly-one-outgoing

A decision routes to exactly one outgoing branch per performance.

Normative clauses: SysML §7.17.3

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

For Loop Iterates Over Sequence

SysMLsysml.validation.for-loop-iterates-over-sequence

A ForLoopAction assigns each successive value from seq to its var loop variable and performs body for each; internally implemented via a nested WhileLoopAction.

Normative clauses: SysML §8.4.13.10

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Fork Node Concurrent Fanout

SysMLsysml.validation.fork-node-concurrent-fanout

A fork orders itself before ALL outgoing targets (every branch activates).

Normative clauses: SysML §7.17.3

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

If Action Evaluates Test Then Branch

SysMLsysml.validation.if-action-evaluates-test-then-branch

An IfThenAction evaluates its ifTest; if true, performs thenClause; an IfThenElseAction additionally performs elseClause when ifTest is false.

Normative clauses: SysML §8.4.13.9

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Interpolate Returns Null Out Of Bounds

SysMLsysml.validation.interpolate-returns-null-out-of-bounds

Interpolate returns null (no extrapolation) for an out-of-bounds input.

Normative clauses: SysML §9.4.3.2 (nearest heading)

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Join Node Synchronize All Incoming

SysMLsysml.validation.join-node-synchronize-all-incoming

A join is ordered after ALL incoming sources complete.

Normative clauses: SysML §7.17.3

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Merge Node Any One Incoming

SysMLsysml.validation.merge-node-any-one-incoming

A merge fires once per exactly-one incoming control.

Normative clauses: SysML §7.17.3

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Mref Dimension Must Match Attribute

SysMLsysml.validation.mref-dimension-must-match-attribute

A supplied mRef must have the same quantity dimension as the attribute being bound/assigned/compared.

Normative clauses: SysML §9.8.9.1

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Quantity Arithmetic Dimension Rules

SysMLsysml.validation.quantity-arithmetic-dimension-rules

+/− require equal dimension; × multiplies dimensions; relational ops require same quantity type.

Normative clauses: SysML §9.8.9.1

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Sampled Function Must Be Monotonic

SysMLsysml.validation.sampled-function-must-be-monotonic

A SampledFunction’s domain values must be strictly increasing or decreasing.

Normative clauses: SysML §9.4.3.2 (nearest heading)

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Send Action Initiates Message Transfer

SysMLsysml.validation.send-action-initiates-message-transfer

A send initiates a MessageTransfer carrying the payload from the sender.

Normative clauses: SysML §8.4.13.5

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

While Loop Iterates While Test

SysMLsysml.validation.while-loop-iterates-while-test

A WhileLoopAction performs its body while whileTest evaluates to true and untilTest evaluates to false; terminates when whileTest is false or untilTest is true.

Normative clauses: SysML §8.4.13.10

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Cases

Generated from the sysml-rs language pack — see the Language Reference index for provenance, licensing, and the raw JSON. 15 cards.

Analysis Case

SysMLsysml.cases.analysis-case

An analysis case computes results from inputs to support a decision, typed by an analysis case definition. Its subject and bound features feed the analysis.

package P { analysis def AC; analysis ac : AC; }

Normative clauses: SysML §8.3.23.3

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Related: Analysis Case (Library Definition)

Analysis Case Definition

SysMLsysml.cases.analysis-case-definition

An analysis case definition is a case that computes an analytical result about its subject — a study, trade, or evaluation. It is written with the analysis def keywords.

package P { analysis def AnaD; }

Normative clauses: SysML §8.3.23.2

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Case Definition

SysMLsysml.cases.case-definition

A case definition is a process that produces a result while relating a subject to an objective — the common base of analysis, verification, and use cases. It is written with the case def keywords.

package P { case def CaseD; }

Normative clauses: SysML §8.3.22.2

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Case Usage

SysMLsysml.cases.case-usage

A case usage is a specific performed case, typed by a case definition, that carries out its process for a subject. It is written with the case keyword and a type.

package P { case def CaseD; case c : CaseD; }

Normative clauses: SysML §8.3.22.3

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Include Use Case

SysMLsysml.cases.include-use-case

An included use case invokes another use case as a step of the current one, reusing its behavior. It is written with the include use case keywords and lowers to a distinct include-use-case usage.

package P { use case def UC { include use case inc; } }

Normative clauses: SysML §8.3.25.2

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Objective

SysMLsysml.cases.objective

An objective states the requirement a case aims to satisfy — the goal that defines when the case has succeeded. It is written with the objective keyword inside a case and lowers to an objective membership carrying the requirement usage.

package P { case def CaseD { objective obj; } }

Normative clauses: SysML §8.3.22.4

Support (sysml-rs): parse ✓ · resolve unknown · elaborate unknown · execute unknown

Requirement Verification

SysMLsysml.cases.requirement-verification

A requirement verification names the requirement a verification case is checking, tying the case’s verdict to that requirement. It is written with the verify keyword and lowers to a requirement-verification membership carrying the requirement usage.

package P { requirement def R; verification def VerD { verify requirement r : R; } }

Normative clauses: SysML §8.3.24.2

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Use Case Definition

SysMLsysml.cases.use-case-definition

A use case definition defines a reusable goal-oriented interaction between a subject system and its actors. Use case usages instantiate it against a specific subject.

package P { use case def Withdraw; }

Normative clauses: SysML §8.3.25.3

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Use Case Usage

SysMLsysml.cases.use-case-usage

A use case usage is a typed occurrence of a use case within a model, typed by a use case definition. It applies the interaction to a concrete subject and actors.

package P { use case def Withdraw; use case uc : Withdraw; }

Normative clauses: SysML §8.3.25.4

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Verification Case

SysMLsysml.cases.verification-case

A verification case checks that a system satisfies its requirements, producing a verdict from its objective and result. It verifies the requirements it references.

package P { verification def VC; verification vc : VC; }

Normative clauses: SysML §8.3.24.4

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Related: Verification Case (Library Definition)

Verification Case Definition

SysMLsysml.cases.verification-case-definition

A verification case definition is a case that checks whether a subject meets its requirements, producing a verdict. It is written with the verification def keywords.

package P { verification def VerD; }

Normative clauses: SysML §8.3.24.3

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Analysis Case Objective Bound To Result

SysMLsysml.validation.analysis-case-objective-bound-to-result

An analysis case’s objective subject is bound to the analysis result (not the case subject).

Normative clauses: SysML §7.23.2

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Case Has Subject And Objective

SysMLsysml.validation.case-has-subject-and-objective

A case has ≤1 subject (first input) and ≤1 objective.

Normative clauses: SysML §8.3.22.2

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Verdict Criteria Modeled Explicitly

SysMLsysml.validation.verdict-criteria-modeled-explicitly

The pass/fail criteria must be modeled explicitly in the case body; no implicit derivation.

Normative clauses: SysML §8.4.20.1

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Verdict Semantics

SysMLsysml.validation.verdict-semantics

Pass=subject determined to satisfy; Fail=determined not to; Inconclusive=determination could not be made; Error=error during verification.

Normative clauses: SysML §7.24.1

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Connections

Generated from the sysml-rs language pack — see the Language Reference index for provenance, licensing, and the raw JSON. 1 cards.

Port Usage Referential

SysMLsysml.validation.port-usage-referential

A port’s non-port nested/owned usages must be referential (non-composite).

Normative clauses: SysML §8.3

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Expressions

Generated from the sysml-rs language pack — see the Language Reference index for provenance, licensing, and the raw JSON. 37 cards.

Additive Operator

Expressionskerml.expression.additive-operator

The additive operator groups expression operands with precedence level 13 of 16 (higher binds tighter); it is left-associative. Symbols: + -.

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

And Operator

Expressionskerml.expression.and-operator

The and operator groups expression operands with precedence level 6 of 16 (higher binds tighter); it is left-associative. Symbols: &.

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Cast Operator

Expressionskerml.expression.cast-operator

The cast operator groups expression operands with precedence level 10 of 16 (higher binds tighter); it is left-associative. Symbols: as.

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

ClassificationTest Operator

Expressionskerml.expression.classification-test-operator

The classificationtest operator groups expression operands with precedence level 8 of 16 (higher binds tighter); it is left-associative. Symbols: hastype istype @.

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

ConditionalAnd Operator

Expressionskerml.expression.conditional-and-operator

The conditionaland operator groups expression operands with precedence level 6 of 16 (higher binds tighter); it is left-associative. Symbols: and.

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Conditional Operator

Expressionskerml.expression.conditional-operator

The conditional operator groups expression operands with precedence level 1 of 16 (higher binds tighter); it is left-associative. Symbols: if.

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

ConditionalOr Operator

Expressionskerml.expression.conditional-or-operator

The conditionalor operator groups expression operands with precedence level 4 of 16 (higher binds tighter); it is left-associative. Symbols: or.

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Constructor Expression

Expressionskerml.expression.constructor

A constructor expression (new T(args)) instantiates a type, binding some or all of its features to the argument results. The new keyword is the discriminator: new Widget(size = 1) lowers to a distinct ConstructorExpression (a subclass of InstantiationExpression), whereas the un-prefixed Widget(size = 1) lowers to a plain InvocationExpression.

package P { part def Widget { attribute size; } part a { attribute x = new Widget(size = 1); } }

Normative clauses: KerML §8.3.4.8.3

Support (sysml-rs): parse ✓ · resolve unknown · elaborate unknown · execute unknown

Equality Operator

Expressionskerml.expression.equality-operator

The equality operator groups expression operands with precedence level 7 of 16 (higher binds tighter); it is left-associative. Symbols: == != === !==.

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Exponentiation Operator

Expressionskerml.expression.exponentiation-operator

The exponentiation operator groups expression operands with precedence level 15 of 16 (higher binds tighter); it is right-associative. Symbols: ** ^.

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Feature Reference Expression

Expressionskerml.expression.feature-reference

A feature reference expression names a feature to yield its value — the way one feature’s value is read into another expression. It is written as the referenced feature’s name.

package P { attribute y = 1; attribute x = y; }

Normative clauses: KerML §8.3.4.8.5

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Feature Value

KerMLkerml.expression.feature-value

A feature value binds a feature to the result of an expression, either as an initial value or a fixed binding. The value’s type must be compatible with the feature.

package P { attribute a = 5; }

Normative clauses: KerML §8.3.4.10.2

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Related: Calculation (Library Definition)

Implies Operator

Expressionskerml.expression.implies-operator

The implies operator groups expression operands with precedence level 3 of 16 (higher binds tighter); it is left-associative. Symbols: implies.

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Invocation Expression

Expressionskerml.expression.invocation

An invocation expression applies a function or calculation to argument expressions, binding each argument to a parameter and producing a result. Arguments may be positional.

package P { calc def Add { in a; in b; return a + b; } part def Q { attribute r = Add(1, 2); } }

Normative clauses: KerML §8.3.4.8.8

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Boolean Literal

Expressionskerml.expression.literal-boolean

A boolean literal is one of the two truth constants, true or false.

package P { attribute x = true; }

Normative clauses: KerML §8.3.4.8.9

Support (sysml-rs): parse ✓ · resolve unknown · elaborate unknown · execute unknown

Infinity Literal

Expressionskerml.expression.literal-infinity

The infinity literal, written *, denotes an unbounded value — most often the upper bound of an unbounded multiplicity.

package P { attribute x = *; }

Normative clauses: KerML §8.3.4.8.11

Support (sysml-rs): parse ✓ · resolve unknown · elaborate unknown · execute unknown

Integer Literal

Expressionskerml.expression.literal-integer

An integer literal is a whole-number constant expression, such as 42. It is the simplest literal value a feature can be given.

package P { attribute x = 5; }

Normative clauses: KerML §8.3.4.8.12

Support (sysml-rs): parse ✓ · resolve unknown · elaborate unknown · execute unknown

Real Literal

Expressionskerml.expression.literal-real

A real literal is a rational-number constant expression written with a decimal point, such as 3.14. Its grammar rule is LiteralReal; it lowers to the engine’s LiteralRational kind.

package P { attribute x = 3.14; }

Normative clauses: KerML §8.3.4.8.13

Support (sysml-rs): parse ✓ · resolve unknown · elaborate unknown · execute unknown

String Literal

Expressionskerml.expression.literal-string

A string literal is a text constant expression enclosed in double quotes, such as “hello”.

package P { attribute x = "hi"; }

Normative clauses: KerML §8.3.4.8.14

Support (sysml-rs): parse ✓ · resolve unknown · elaborate unknown · execute unknown

MetaCast Operator

Expressionskerml.expression.meta-cast-operator

The metacast operator groups expression operands with precedence level 10 of 16 (higher binds tighter); it is left-associative. Symbols: meta.

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

MetaClassificationTest Operator

Expressionskerml.expression.meta-classification-test-operator

The metaclassificationtest operator groups expression operands with precedence level 9 of 16 (higher binds tighter); it is left-associative. Symbols: @@.

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Metadata Access Expression

Expressionskerml.expression.metadata-access

A metadata access expression, written element.metadata, yields the metadata annotations attached to an element as a value. It lowers to a distinct MetadataAccessExpression.

metadata def M; part def P { attribute a = P.metadata; }

Normative clauses: KerML §8.3.4.8.15

Support (sysml-rs): parse ✓ · resolve unknown · elaborate unknown · execute unknown

Multiplicative Operator

Expressionskerml.expression.multiplicative-operator

The multiplicative operator groups expression operands with precedence level 14 of 16 (higher binds tighter); it is left-associative. Symbols: * / %.

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

NullCoalescing Operator

Expressionskerml.expression.null-coalescing-operator

The nullcoalescing operator groups expression operands with precedence level 2 of 16 (higher binds tighter); it is left-associative. Symbols: ??.

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Null Expression

Expressionskerml.expression.null-expression

A null expression, written null, denotes the absence of a value — an empty result with no elements.

package P { attribute x = null; }

Normative clauses: KerML §8.3.4.8.16

Support (sysml-rs): parse ✓ · resolve unknown · elaborate unknown · execute unknown

Or Operator

Expressionskerml.expression.or-operator

The or operator groups expression operands with precedence level 4 of 16 (higher binds tighter); it is left-associative. Symbols: |.

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Relational Operator

Expressionskerml.expression.relational-operator

The relational operator groups expression operands with precedence level 11 of 16 (higher binds tighter); it is left-associative. Symbols: < > <= >=.

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Unary Operator

Expressionskerml.expression.unary-operator

The unary operator groups expression operands with precedence level 16 of 16 (higher binds tighter); it is a prefix unary operator. Symbols: + - ~ not.

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Xor Operator

Expressionskerml.expression.xor-operator

The xor operator groups expression operands with precedence level 5 of 16 (higher binds tighter); it is left-associative. Symbols: xor.

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Constraint Result Boolean

KerMLkerml.validation.constraint-result-boolean

A constraint/predicate produces exactly one Boolean result; a non-Boolean result is not a valid verdict.

Normative clauses: KerML §7.4.8

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Core Operator Semantics

KerMLkerml.validation.core-operator-semantics

Constraint (Boolean) expressions evaluate comparison/logical/arithmetic operators per KerML expression semantics.

Normative clauses: KerML §7.4.8

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Feature Ref Resolves To Bound Value

KerMLkerml.validation.feature-ref-resolves-to-bound-value

A feature reference in a constraint evaluates to the value bound to that feature.

Normative clauses: KerML §8.3.4.8.5

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Unbound Feature Yields Inconclusive Not False

KerMLkerml.validation.unbound-feature-yields-inconclusive-not-false

An unresolved feature reference evaluates to the empty list; the result is not false. The behavior of an ordering comparison over an empty operand is spec-silent.

Normative clauses: KerML §8.3.4.8.5

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Assert Constraint Must Be True

SysMLsysml.validation.assert-constraint-must-be-true

A non-negated assert constraint asserts its result is true at all times; a false result is a logical inconsistency the tool flags.

Normative clauses: SysML §7.20

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Constraint Satisfied Iff True

SysMLsysml.validation.constraint-satisfied-iff-true

A constraint usage is satisfied iff its expression evaluates to true, and violated otherwise.

Normative clauses: SysML §7.20

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Constraint Usage Discovered

SysMLsysml.validation.constraint-usage-discovered

A ConstraintUsage and an AssertConstraintUsage are both surfaced as evaluable constraints.

Normative clauses: SysML §7.20

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Negated Assert Must Be False

SysMLsysml.validation.negated-assert-must-be-false

A negated assert not constraint asserts its result is false; a true result is the inconsistency.

Normative clauses: SysML §7.20

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Implementation Notes

Generated from the sysml-rs language pack — see the Language Reference index for provenance, licensing, and the raw JSON. 10 cards.

constructor expression does not lower to a distinct kind

Toolingtooling.implementation.constructor-expression-generic-lowering

A constructor expression (Type(args)) parses cleanly but lowers to a generic InvocationExpression rather than a distinct ConstructorExpression, so it is not distinguished from an ordinary invocation. An implementation limitation, not a language restriction; the concept exists in the grammar.

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Invocation Expression

if / terminate action nodes do not lower to distinct node kinds

Toolingtooling.implementation.control-node-if-terminate-generic-lowering

The if control node and the terminate action node parse cleanly, but the lowering does not materialize their distinct IfActionUsage / TerminateActionUsage kinds — an if conditional succession lowers to a generic TransitionUsage and terminate does not surface a terminate node. An implementation limitation, not a language restriction; the grammar declares both node kinds.

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Transition Usage, Action Usage

enum members do not lower to a distinct EnumerationUsage

Toolingtooling.implementation.enumeration-usage-not-distinct

Members declared with enum inside an enum def parse cleanly, but the lowering produces no model element for them at all — not merely a generic usage in place of a distinct EnumerationUsage, but zero elements — so enumerated-value membership is not modelled in any form. This is an implementation limitation, not a language restriction — the concept exists in the grammar.

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Enumeration Definition

message / message-event do not lower to distinct kinds

Toolingtooling.implementation.message-generic-lowering

A message (and its message event) parses cleanly but lowers to a generic FlowUsage / EventOccurrenceUsage rather than a distinct Message / MessageEvent, so the message’s payload-transfer semantics are not modelled as such. An implementation limitation, not a language restriction; the concepts exist in the grammar.

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Flow Connection, Event Occurrence Usage

individual / portion occurrence prefixes do not lower to distinct kinds

Toolingtooling.implementation.occurrence-prefix-generic-lowering

The occurrence prefixes individual (individual def/individual), portion (portion), snapshot and timeslice parse but do not lower to their distinct occurrence element kinds — individual def in particular is not recognised as an OccurrenceDefinition marked individual, and the usage prefixes drop to a generic ReferenceUsage/OccurrenceUsage without their individual/portion role. An implementation limitation, not a language restriction; the concepts exist in the grammar.

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Occurrence Definition, Occurrence Usage

prefixed individual|variation <kind> def and ref <keyword-usage> forms misparse

Toolingtooling.implementation.prefixed-def-and-ref-keyword-usage-misparse

Two LR(1) lookahead splits: (a) individual part def / individual occurrence def / variation part def misparse into a standard_usage plus a feature_declaration — the usage-vs-definition choice must be made on the prefix+keyword lookahead but the forms diverge one token later at def; (b) ref enum / ref port (any ref <keyword-usage>) splits into an empty standard_usage plus the keyword usage. Implementation limitations, not language restrictions — both forms are normative (SysML.xtext OccurrenceDefinitionPrefix:800-806 / IndividualDefinition:813-817 / DefinitionPrefix variation, and RefPrefix on usages). The bare forms (individual def X;, standalone enum e;, port p;) all parse and lower.

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Occurrence Definition, Part Definition, Enumeration Definition, Port Usage

membership-wrapped role usages under-resolve their type refs nondeterministically

Toolingtooling.implementation.role-usage-resolution-nondeterminism

Membership-wrapped role usages once stamped their typing/reference target as a string prop on the membership (no lowered intermediate usage), so the resolver’s pass-1 loop hit a _ => {} no-op: the target was never resolved and a missing one never counted as unresolved — a silent drop (subject s : Missing gave no diagnostic), with nondeterminism from a runtime fallback scanning a HashMap in hash order. FIXED. Subject/objective: pass 1 routes them through resolve_feature_typing (resolves in the membership’s owning scope, stamps a type Ref, fail-hards a dangling target). assume/require constraint and framed concern reference forms: the parser mints the membership’s owned ConstraintUsage and hangs the grammar’s relationship on it — a ReferenceSubsetting for the bare-name form (SysML.xtext:448), a FeatureTyping for the : Def form — and referencedConstraint derives from that (SysML-vocab.ttl:2576), the resolver fail-harding both through the standard path. actor/stakeholder were always unaffected (they lower a real FeatureTyping).

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Subject, Actor, Stakeholder, Framed Concern, Objective

state effect / state action / trigger subactions do not lower to distinct kinds

Toolingtooling.implementation.state-subaction-generic-lowering

A state effect behavior, a state action usage and a transition trigger action parse cleanly but lower to a generic ActionUsage / AcceptActionUsage rather than their distinct EffectBehaviorUsage / StateActionUsage / TriggerAction kinds — the state-subaction and trigger roles are not modelled as distinct usages. An implementation limitation, not a language restriction; the concepts exist in the grammar.

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Exhibit State Usage, Accept Action

TextualRepresentation lowers to a generic ReferenceUsage

Toolingtooling.implementation.textual-representation-generic-lowering

A KerML TextualRepresentation annotating element (a rep with a language string and a body) parses cleanly, but the tree-sitter lowering produces a generic ReferenceUsage rather than a distinct TextualRepresentation element, so the representation’s language and body are not modelled as such. This is an implementation limitation, not a language restriction — the concept exists in the grammar.

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Reference Usage

type-relationship operators do not materialize a distinct relationship kind

Toolingtooling.implementation.type-relationship-fragment-generic-lowering

The type-relationship operators unioning (unions), intersecting (intersects), differencing (differences), disjoining (disjoint from), feature-inversion (inverse of), type-featuring (featured by) and conjugation (conjugate/~ on a type) parse cleanly, but the lowering does not produce their distinct Unioning/Intersecting/Differencing/Disjoining/ FeatureInverting/TypeFeaturing/Conjugation relationship elements — the participating types are recorded without the specialization relationship being modelled as such. An implementation limitation, not a language restriction; the concepts exist in the grammar. (The ~ port-conjugation form on ports IS modelled — see the conjugated-port-definition card.)

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Specialization

Standard Library

Generated from the sysml-rs language pack — see the Language Reference index for provenance, licensing, and the raw JSON. 33 cards.

Boolean Evaluation

KerMLkerml.library.boolean-evaluation

The Kernel Semantic Library predicate that is the most general class of Boolean-valued evaluations (a specialization of Evaluation). It is the base that the Systems Library ConstraintCheck specializes.

Normative clauses: KerML §9.2.6

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Constraint Check

FlowTransfer

KerMLkerml.library.flow-transfer

The concrete Transfer that names the source output feature and the target input feature the payload flows between — what an ordinary structural flow connection lowers to. Carries move/push semantics.

Normative clauses: KerML §9.2.7

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Transfer, MessageTransfer

HappensBefore

KerMLkerml.library.happens-before

The Kernel association asserting that one occurrence completely finishes before another begins, with no overlap in time — the semantic backbone of succession (then) ordering.

Normative clauses: KerML §9.2.4

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Occurrence, HappensDuring

HappensDuring

KerMLkerml.library.happens-during

The Kernel association asserting that one occurrence’s whole time interval falls inside another’s — used for containment timing, e.g. a substate being active during its enclosing state.

Normative clauses: KerML §9.2.4

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Occurrence, HappensBefore

head (Sequence Function)

KerMLkerml.library.head

The Kernel sequence function returning the first element of an ordered collection (equivalent to indexing position 1), or nothing when it is empty.

Normative clauses: KerML §9.4.14

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: tail (Sequence Function), last (Sequence Function)

includes (Sequence Function)

KerMLkerml.library.includes

The Kernel sequence predicate that is true when every element of one collection is also present in another — a membership/containment test.

Normative clauses: KerML §9.4.14

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: size (Sequence Function)

isEmpty (Sequence Function)

KerMLkerml.library.is-empty

The Kernel sequence predicate that is true exactly when a collection contains no elements. Complementary to notEmpty.

Normative clauses: KerML §9.4.14

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: notEmpty (Sequence Function), size (Sequence Function)

last (Sequence Function)

KerMLkerml.library.last

The Kernel sequence function returning the final element of an ordered collection, or nothing when it is empty.

Normative clauses: KerML §9.4.14

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: head (Sequence Function)

MessageTransfer

KerMLkerml.library.message-transfer

The Transfer variant that carries a payload with no named source-output or target-input feature — the transfer underlying send/accept action semantics. It is disjoint from FlowTransfer.

Normative clauses: KerML §9.2.7

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Transfer, FlowTransfer

notEmpty (Sequence Function)

KerMLkerml.library.not-empty

The Kernel sequence predicate that is true when a collection holds at least one element — the negation of isEmpty.

Normative clauses: KerML §9.4.14

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: isEmpty (Sequence Function)

Occurrence

KerMLkerml.library.occurrence

The Kernel root classifier for anything with identity that exists or happens across time and space. Actions, states, and individual parts ultimately specialize it; it anchors the timing (start/end snapshots) and transfer features every temporal element uses.

Normative clauses: KerML §9.2.4

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: HappensBefore, HappensDuring, Transfer

max (Scalar Function)

KerMLkerml.library.scalar-max

The Kernel scalar function returning whichever of its two ordered scalar operands is the greater. Invoked as max(a, b) in expressions.

Normative clauses: KerML §9.4.4

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: min (Scalar Function)

min (Scalar Function)

KerMLkerml.library.scalar-min

The Kernel scalar function returning whichever of its two ordered scalar operands is the lesser. Invoked as min(a, b) in expressions.

Normative clauses: KerML §9.4.4

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: max (Scalar Function)

size (Sequence Function)

KerMLkerml.library.size

The Kernel sequence function counting how many elements a collection holds, returning a Natural. col->size() is the idiomatic length query.

Normative clauses: KerML §9.4.14

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: includes (Sequence Function), isEmpty (Sequence Function)

State Performance

KerMLkerml.library.state-performance

The Kernel Semantic Library performance of being in a state (a specialization of DecisionPerformance) with entry/do/exit substeps. On entry, the transfer/event that triggered the entry is recorded on the state’s performance via its incomingTransitionTrigger feature.

Normative clauses: KerML §9.2.11

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: State Action (Library Definition)

tail (Sequence Function)

KerMLkerml.library.tail

The Kernel sequence function returning every element of an ordered collection except the first — the complement of head, used for recursive traversal.

Normative clauses: KerML §9.4.14

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: head (Sequence Function)

Transfer

KerMLkerml.library.transfer

The Kernel interaction that moves a payload from a source occurrence to a target occurrence. It is the abstract base every flow and message transfer specializes; a transfer may be instantaneous or take time.

Normative clauses: KerML §9.2.7

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: FlowTransfer, MessageTransfer, Occurrence

Analysis Case (Library Definition)

SysMLsysml.library.analysis-case

The abstract library base of all analysis cases (a specialization of Case). It carries out an evaluation over its subject, producing a result rather than a pass/fail verdict.

Normative clauses: SysML §7.23

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Case (Library Definition), Analysis Case

Calculation (Library Definition)

SysMLsysml.library.calculation

The abstract library base of all calculations (a specialization of Action and the Kernel Evaluation). A CalculationUsage evaluates an expression over its parameters and returns a result; when an argument is omitted, the declared parameter’s default value (a KerML FeatureValue) is used.

Normative clauses: SysML §7.19

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Feature Value

Case (Library Definition)

SysMLsysml.library.case

The abstract library base of all cases (a specialization of Calculation). A Case has a subject under investigation, actor parts, an objective expressed as a RequirementCheck, and a result that should satisfy that objective.

Normative clauses: SysML §7.22

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Requirement Check, Analysis Case (Library Definition), Verification Case (Library Definition)

Constraint Check

SysMLsysml.library.constraint-check

The abstract library base of all constraint definitions (a specialization of the Kernel BooleanEvaluation predicate). A ConstraintCheck evaluates to a Boolean; asserted and negated constraint checks partition it into the true/false evaluation subsets.

Normative clauses: SysML §7.20

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Boolean Evaluation, Requirement Check, Constraint Definition

DurationValue (ISQ Base Quantity)

SysMLsysml.library.duration-value

The ISQ base quantity kind for duration/time (dimension T). A DurationValue is a scalar quantity measured in a DurationUnit such as second.

Normative clauses: SysML §9.8.4

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: second (SI Unit)

kilogram (SI Unit)

SysMLsysml.library.kilogram

The SI base unit of mass (short name kg), typing MassValue quantities. Defined by applying the kilo prefix to the gram.

Normative clauses: SysML §9.8.6

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: MassValue (ISQ Base Quantity)

LengthValue (ISQ Base Quantity)

SysMLsysml.library.length-value

The ISQ base quantity kind for length (dimension L). A LengthValue is a scalar quantity measured in a LengthUnit such as metre.

Normative clauses: SysML §9.8.4

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: metre (SI Unit)

MassValue (ISQ Base Quantity)

SysMLsysml.library.mass-value

The ISQ base quantity kind for mass (dimension M). A MassValue is a scalar quantity measured in a MassUnit such as kilogram.

Normative clauses: SysML §9.8.4

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: kilogram (SI Unit)

metre (SI Unit)

SysMLsysml.library.metre

The SI base unit of length (short name m), typing LengthValue quantities. Imported from the SI library and composed into derived units such as newton.

Normative clauses: SysML §9.8.6

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: LengthValue (ISQ Base Quantity)

Pass If

SysMLsysml.library.pass-if

The library calculation mapping a Boolean to a VerdictKind: it returns VerdictKind::pass when its isPassing argument is true, otherwise VerdictKind::fail.

Normative clauses: SysML §7.24

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Verdict Kind

Requirement Check

SysMLsysml.library.requirement-check

The abstract library base of all requirement definitions (a specialization of RequirementConstraintCheck). It checks whether its subject satisfies the required constraints, given that all assumptions hold: its result is allTrue(assumptions) implies allTrue(constraints).

Normative clauses: SysML §7.21

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Case (Library Definition), Constraint Check, Requirement Definition

second (SI Unit)

SysMLsysml.library.second

The SI base unit of time/duration (short name s), typing DurationValue quantities. The reference unit for rates and time-based dynamics.

Normative clauses: SysML §9.8.6

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: DurationValue (ISQ Base Quantity)

State Action (Library Definition)

SysMLsysml.library.state-action

The abstract library base of all state usages (a specialization of Action and the Kernel StatePerformance). Its mutually-exclusive substates are sequenced by stateSequencing successions: with N exclusive substates there are exactly N-1 such successions.

Normative clauses: SysML §7.18

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: State Performance

Verdict Kind

SysMLsysml.library.verdict-kind

The library enumeration of the possible results of a verification case: pass, fail, inconclusive, and error. A VerificationCase returns a VerdictKind as its result.

Normative clauses: SysML §7.24

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Verification Case (Library Definition), Pass If

Verification Case (Library Definition)

SysMLsysml.library.verification-case

The abstract library base of all verification cases (a specialization of Case). It returns a VerdictKind verdict and records the RequirementChecks of the requirements being verified in its objective.

Normative clauses: SysML §7.24

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Verification Case, Case (Library Definition), Verdict Kind, Verification Method Kind

Verification Method Kind

SysMLsysml.library.verification-method-kind

The library enumeration of the standard methods by which verification can be carried out: inspect, analyze, demo, and test. Used via the VerificationMethod metadata annotating a verification case or action.

Normative clauses: SysML §7.24

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Verification Case (Library Definition)

Metadata

Generated from the sysml-rs language pack — see the Language Reference index for provenance, licensing, and the raw JSON. 5 cards.

Comment

KerMLkerml.metadata.comment

A comment carries free-form explanatory text about model elements. Its body is a note; an optional about list names the elements it annotates, and an optional locale tags the natural language.

package P { comment About /* explains the package */ part def Widget; }

Normative clauses: KerML §8.3.2.3.4

Support (sysml-rs): parse ✓ · resolve unknown · elaborate unknown · execute unknown

Documentation

KerMLkerml.metadata.documentation

Documentation is comment text owned by the element it documents, giving that element a human-readable description. Its body is required; an optional locale tags the natural language.

package P { part def Widget { doc /* the primary component */ } }

Normative clauses: KerML §8.3.2.3.5

Support (sysml-rs): parse ✓ · resolve unknown · elaborate unknown · execute unknown

Textual Representation

KerMLkerml.metadata.textual-representation

A textual representation (rep <name> language "L" /* body */, or the unnamed language "L" /* body */) is an AnnotatingElement whose comment body represents its owning element in a named language. It lowers to a distinct TextualRepresentation carrying the language string and the body text; the grammar admits it inside constraint/requirement bodies.

package P { constraint c { rep r language "html" /* <b>hi</b> */ } }

Normative clauses: KerML §8.3.2.3.6

Support (sysml-rs): parse ✓ · resolve unknown · elaborate unknown · execute unknown

Metadata Definition

SysMLsysml.metadata.metadata-definition

A metadata definition defines a kind of annotation that carries structured data about model elements — semantic tags a tool or process can read. Usages of it attach that data to the elements they annotate.

package P { metadata def Priority; }

Normative clauses: SysML §8.3.27.2

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Metadata Usage

SysMLsysml.metadata.metadata-usage

A metadata usage attaches an instance of a metadata definition to one or more model elements. The prefix form @Name annotates the element it precedes with metadata of that kind.

package P { metadata def Priority; part def Widget { @Priority; } }

Normative clauses: SysML §8.3.27.3

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Requirements

Generated from the sysml-rs language pack — see the Language Reference index for provenance, licensing, and the raw JSON. 18 cards.

Actor

SysMLsysml.requirements.actor

An actor is an external party — a person, system, or role — that interacts with the subject of a requirement or case. It is written with the actor keyword and lowers to an actor membership carrying the actor part.

package P { part def Driver; requirement def R { actor a : Driver; } }

Normative clauses: SysML §8.3.21.2

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Assert Constraint

SysMLsysml.requirements.assert-constraint

An asserted constraint states a boolean condition that is claimed to hold for the enclosing element — it is taken as true rather than required or assumed. It is written with the assert keyword and lowers to a distinct assert-constraint usage.

package P { requirement def R { assert constraint ac { true } } }

Normative clauses: SysML §8.3.20.2

Support (sysml-rs): parse ✓ · resolve unknown · elaborate unknown · execute unknown

Concern Definition

SysMLsysml.requirements.concern-definition

A concern definition defines a matter of interest to stakeholders that a system must address — a specialized requirement definition. Viewpoints frame concerns; views address them.

package P { concern def Safety { subject s; } }

Normative clauses: SysML §8.3.21.3

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Concern Usage

SysMLsysml.requirements.concern-usage

A concern usage is a typed occurrence of a concern, typed by a concern definition. It raises the concern where it applies, for a viewpoint to frame.

package P { concern def Safety { subject s; } concern cn : Safety; }

Normative clauses: SysML §8.3.21.4

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Constraint Definition

SysMLsysml.requirements.constraint-definition

A constraint definition defines a reusable boolean condition over parameters. Constraint usages assert it must hold, backing requirements and checks.

package P { constraint def Positive { in x; x > 0 } }

Normative clauses: SysML §8.3.20.3

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Related: Constraint Check

Constraint Usage

SysMLsysml.requirements.constraint-usage

A constraint usage asserts that a boolean condition holds for a model. It is typed by a constraint definition and evaluates to true or false.

package P { constraint def Positive { in x; x > 0 } constraint c : Positive; }

Normative clauses: SysML §8.3.20.4

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Framed Concern

SysMLsysml.requirements.framed-concern

A framed concern brings a concern into a requirement as something the requirement addresses — it frames the concern within the requirement’s scope. It is written with the frame keyword and lowers to a framed-concern membership carrying the concern usage.

package P { concern def Cn; requirement def R { frame concern fc : Cn; } }

Normative clauses: SysML §8.3.21.5

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Requirement Constraint (assume / require)

SysMLsysml.requirements.requirement-constraint

A requirement constraint is a boolean condition that a requirement either assumes as a precondition or requires as an obligation. It is written with the assume or require keyword and lowers to a requirement-constraint membership recording which role it plays.

package P { requirement def R { assume constraint ac { true } require constraint rc { true } } }

Normative clauses: SysML §8.3.21.7

Support (sysml-rs): parse ✓ · resolve unknown · elaborate unknown · execute unknown

Requirement Definition

SysMLsysml.requirements.requirement-definition

A requirement definition states a condition a system must meet, over a subject and optional constraints. A requirement has at most one subject.

package P { requirement def R { subject s; } }

Normative clauses: SysML §8.3.21.8

Support (sysml-rs): parse ✓ · resolve unknown · elaborate unknown · execute unknown

Related: Requirement Check

Requirement Usage

SysMLsysml.requirements.requirement-usage

A requirement usage is a typed occurrence of a requirement within a model, typed by a requirement definition. It states a condition an element must satisfy, nested where it applies.

package P { requirement def Rq { subject s; } requirement rq : Rq; }

Normative clauses: SysML §8.3.21.9

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Requirement Satisfaction

SysMLsysml.requirements.satisfaction

A satisfy relationship asserts that a subject element meets a requirement. It links the satisfying element to the requirement it fulfils.

package P { requirement def R { subject s; } part def Sys; part sys : Sys; satisfy R by sys; }

Normative clauses: SysML §8.3.21.10

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Stakeholder

SysMLsysml.requirements.stakeholder

A stakeholder is a party with an interest in a requirement’s satisfaction or a concern being addressed. It is written with the stakeholder keyword and lowers to a stakeholder membership carrying the stakeholder part.

package P { part def Owner; requirement def R { stakeholder sh : Owner; } }

Normative clauses: SysML §8.3.21.12

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Subject

SysMLsysml.requirements.subject

A subject binds a requirement, case, or concern to the thing it is about — the system or element under consideration. It is written with the subject keyword and lowers to a subject membership carrying the subject parameter.

package P { part def Car; requirement def R { subject s : Car; } }

Normative clauses: SysML §8.3.21.11

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Negated Satisfy Requires Not Satisfied

SysMLsysml.validation.negated-satisfy-requires-not-satisfied

A negated satisfy usage asserts the requirement evaluates to false; verdict inverts.

Normative clauses: SysML §7.21.4

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Requirement Check Result Is Boolean

SysMLsysml.validation.requirement-check-result-is-boolean

Every RequirementCheck result is Boolean.

Normative clauses: SysML §8.4.17.1

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Requirement Is Constraint Satisfied Iff True

SysMLsysml.validation.requirement-is-constraint-satisfied-iff-true

A requirement is a kind of constraint, satisfied iff it evaluates to true.

Normative clauses: SysML §7.21.1

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Requirement Result Is Assumption Implies Required

SysMLsysml.validation.requirement-result-is-assumption-implies-required

Effective result = allTrue(assumptions) implies allTrue(constraints); required constraints checked only when assumptions hold.

Normative clauses: SysML §7.21.2

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Requirement Subject Must Be First Parameter

SysMLsysml.validation.requirement-subject-must-be-first-parameter

The subject parameter must be the requirement’s first input.

Normative clauses: SysML §8.3.21

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

State Machines

Generated from the sysml-rs language pack — see the Language Reference index for provenance, licensing, and the raw JSON. 8 cards.

At Most One Transition Fires Per Trigger

KerMLkerml.validation.at-most-one-transition-fires-per-trigger

At most one outgoing transition fires per trigger (via accepted[0..1] + isDispatch).

Normative clauses: KerML §9.2.11.1

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

At Most One Each Subaction Kind

SysMLsysml.validation.at-most-one-each-subaction-kind

≤1 entry, ≤1 do, ≤1 exit per state.

Normative clauses: SysML §8.3.18.5

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Entry Do Exit Ordering

SysMLsysml.validation.entry-do-exit-ordering

Entry completes → do starts and runs while active → on exit, do interrupted, exit runs to completion.

Normative clauses: SysML §7.18.1

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Initial State Via Entry Succession

SysMLsysml.validation.initial-state-via-entry-succession

The initial substate is the target of a succession from the entry action.

Normative clauses: SysML §7.18.2

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

No Transition On Unmatched Event

SysMLsysml.validation.no-transition-on-unmatched-event

An event matching no outgoing transition of the current state leaves the machine in place (no spurious firing).

Normative clauses: SysML §7.18.3

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Transition Firing Order Exit Effect Entry

SysMLsysml.validation.transition-firing-order-exit-effect-entry

Firing sequence: interrupt source do → source exit → transition effect → target entry → target do.

Normative clauses: SysML §7.18.3

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Transition Guard Boolean

SysMLsysml.validation.transition-guard-boolean

A transition guard is a Boolean[1..1] expression that must be true for the transition to occur.

Normative clauses: SysML §7.18.3

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Transition Selection

SysMLsysml.validation.transition-selection

An accepted event-triggered transition fires and moves the machine to its named target state.

Normative clauses: SysML §7.18.3

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Structure

Generated from the sysml-rs language pack — see the Language Reference index for provenance, licensing, and the raw JSON. 65 cards.

Association

KerMLkerml.structure.association

An association is a classifier of links between things, relating two or more end features. It is the KerML base of SysML connection definitions; its ends name the related types.

package P { class C; assoc A { end a : C; end b : C; } }

Normative clauses: KerML §7.4.5

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Association Structure

KerMLkerml.structure.association-structure

An association structure is both an association and a structure: a link that itself carries structural features, so the link between things is a structured object in its own right.

package P { class C; assoc struct AS { end a : C; end b : C; } }

Normative clauses: KerML §7.4.5.3

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Binding Connector

KerMLkerml.structure.binding-connector

A binding connector asserts that its two ends are the same thing — a binding that forces the connected features to have identical values. It is written with the equals form.

package P { class C; feature x : C; binding b = x; }

Normative clauses: KerML §8.2.5.5.2

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Class

KerMLkerml.structure.class

A class is a classifier of occurrences — things that exist over time and space with intrinsic identity. It is the KerML base from which SysML parts and other occurrence-typed constructs derive.

package P { class C; }

Normative clauses: KerML §7.4.3

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Classifier

KerMLkerml.structure.classifier

A classifier is a type that classifies individual things by their intrinsic identity, as opposed to a feature that classifies things by their role. Classes, structures, and behaviors are all classifiers.

package P { classifier C; }

Normative clauses: KerML §7.3.3

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Classifier Conjugation

KerMLkerml.structure.classifier-conjugation

A classifier conjugation (classifier C ~ D; or ... conjugates D;) declares a classifier as the conjugate of another, inheriting its features with directions reversed. The grammar rule ClassifierConjugation returns Conjugation — the declaring classifier is the conjugatedType, the target the originalType; there is no distinct ClassifierConjugation metaclass.

package P { classifier D; classifier C ~ D; }

Normative clauses: KerML §8.2.4.2.1

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Conjugation

KerMLkerml.structure.conjugation

A conjugation relationship (conjugates or the symbolic ~) makes a type inherit another’s features with input/output directions reversed. Lowers to a distinct Conjugation relationship (conjugatedType = the declaring type, originalType = the target). Both spellings are admitted; in lambda-parameter position only the conjugates keyword applies (the ~ there is the unary operator).

package P { class A; class X conjugates A; }

Normative clauses: KerML §8.3.3.1.2

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Connector

KerMLkerml.structure.connector

A connector is a feature that links two or more other features, typed by an association. It is the KerML base of SysML connection usages; its ends name the connected features.

package P { class C; feature x : C; feature y : C; connector c from x to y; }

Normative clauses: KerML §7.4.6

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Data Type

KerMLkerml.structure.data-type

A datatype is a classifier of data values — things distinguished only by their value, with no intrinsic identity, so two equal values are the same datum. It is the KerML base of SysML attribute value types.

package P { datatype D; }

Normative clauses: KerML §7.4.2

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Differencing

KerMLkerml.structure.differencing

A differencing relationship (differences) makes a type the difference of the listed types — the instances of the first that are not in the rest. Lowers to a distinct Differencing relationship owned by the declaring type.

package P { class A; class B; class D differences A, B; }

Normative clauses: KerML §8.3.3.1.3

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Disjoining

KerMLkerml.structure.disjoining

A disjoining relationship (disjoint from) asserts two types share no instances. Lowers to a distinct Disjoining relationship owned by the declaring type.

package P { class A; class B; class J disjoint from A, B; }

Normative clauses: KerML §8.3.3.1.4

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

EndFeatureMembership

KerMLkerml.structure.end-feature-membership

The EndFeatureMembership metamodel relationship (KerML abstract syntax). It has no dedicated concrete notation — it is how the model graph is wired — so it is documented as a metamodel concept with its inheritance and property constraints, not a grammar card.

Normative clauses: KerML §8.3.3.3.3

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Feature Conjugation

KerMLkerml.structure.feature-conjugation

A feature conjugation (feature b ~ a; or ... conjugates a;) declares a feature as the conjugate of another feature. The grammar rule FeatureConjugation returns Conjugation — the declaring feature is the conjugatedType, the target feature the originalType; there is no distinct FeatureConjugation metaclass.

package P { part def B { attribute a; attribute b ~ a; } }

Normative clauses: KerML §8.2.4.3.1

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Feature Inverting

KerMLkerml.structure.feature-inverting

A feature-inverting relationship (inverse of) asserts two features are inverses — each instance’s value under one is the reverse of the other. Lowers to a distinct FeatureInverting relationship owned by the declaring feature.

package P { feature f; feature g inverse of f; }

Normative clauses: KerML §8.3.3.3.6

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

FeatureMembership

KerMLkerml.structure.feature-membership

The FeatureMembership metamodel relationship (KerML abstract syntax). It has no dedicated concrete notation — it is how the model graph is wired — so it is documented as a metamodel concept with its inheritance and property constraints, not a grammar card.

Normative clauses: KerML §8.3.3.1.6

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Feature Typing

KerMLkerml.structure.feature-typing

A feature typing relates a feature to a type that classifies it — the relationship behind x : T. The same abstract-syntax relationship is realized by two divergent concrete syntaxes: KerML’s 'typing' typedFeature (':'|'typed' 'by') FeatureType versus SysML’s OwnedFeatureTyping | ConjugatedPortTyping dispatcher.

package P { part def A; part a : A; }

Normative clauses: KerML §8.3.3.3.7

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Related: Feature Typing

Filter Package

KerMLkerml.structure.filter-package

A filter package restricts which members are imported by a filter condition, written filter @Metadata inside the package. The condition lowers to a distinct ElementFilterMembership.

metadata def Safety; package P { filter @Safety; }

Normative clauses: KerML §8.3.4.13.2

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Import

KerMLkerml.structure.import

An import brings the members of another namespace into scope so their names resolve without full qualification. Visibility controls whether the imported names are re-exported.

package Lib { part def Widget; }
package App { import Lib::*; part w : Widget; }

Normative clauses: KerML §8.3.2.4.2

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Intersecting

KerMLkerml.structure.intersecting

An intersecting relationship (intersects) makes a type the intersection of the listed types — its instances are those common to all intersected types. Lowers to a distinct Intersecting relationship owned by the declaring type.

package P { class A; class B; class I2 intersects A, B; }

Normative clauses: KerML §8.3.3.1.7

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Library Package

KerMLkerml.structure.library-package

A library package is a package marked as a reusable model library with the library keyword. Its members are standard/shared definitions imported by other models; it lowers to a distinct LibraryPackage element.

library package Lib { part def P; }

Normative clauses: KerML §8.3.4.13.3

Support (sysml-rs): parse ✓ · resolve unknown · elaborate unknown · execute unknown

Membership

KerMLkerml.structure.membership

The Membership metamodel relationship (KerML abstract syntax). It has no dedicated concrete notation — it is how the model graph is wired — so it is documented as a metamodel concept with its inheritance and property constraints, not a grammar card.

Normative clauses: KerML §8.3.2.4.3

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Membership Import

KerMLkerml.structure.membership-import

A membership import brings one named member of a namespace into scope, written import Path::member. It lowers to a distinct MembershipImport, in contrast to the wildcard namespace import.

package Q { part def X; } package R { import Q::X; }

Normative clauses: KerML §8.3.2.4.4

Support (sysml-rs): parse ✓ · resolve unknown · elaborate unknown · execute unknown

Metaclass

KerMLkerml.structure.metaclass

A metaclass is a class whose instances are themselves model elements — the type of a metadata annotation. Metadata features are typed by metaclasses to attach structured data to the elements they annotate.

package P { metaclass M; }

Normative clauses: KerML §8.4.4.13.1

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Multiplicity

KerMLkerml.structure.multiplicity

A multiplicity bounds how many instances a feature may have, written as a range in square brackets. Bounds are a lower and an optional upper limit.

package P { part def A { attribute a[0..1]; } }

Normative clauses: KerML §8.3.3.1.9

Support (sysml-rs): parse ✓ · resolve unknown · elaborate unknown · execute unknown

Namespace

KerMLkerml.structure.namespace

A namespace owns named members and defines the scope in which those names are visible and resolvable. Packages are the usual concrete namespaces a modeller writes; membership establishes the owned names.

package Ns { part def A; part def B; }

Normative clauses: KerML §8.3.2.4.5

Support (sysml-rs): parse ✓ · resolve unknown · elaborate unknown · execute unknown

Namespace Import

KerMLkerml.structure.namespace-import

A namespace import brings every member of a namespace into scope, written import Path::*. It lowers to a distinct NamespaceImport and is the wildcard companion of the single-member membership import.

package Q { part def X; } package R { import Q::*; }

Normative clauses: KerML §8.3.2.4.6

Support (sysml-rs): parse ✓ · resolve unknown · elaborate unknown · execute unknown

OwningMembership

KerMLkerml.structure.owning-membership

The OwningMembership metamodel relationship (KerML abstract syntax). It has no dedicated concrete notation — it is how the model graph is wired — so it is documented as a metamodel concept with its inheritance and property constraints, not a grammar card.

Normative clauses: KerML §8.3.2.4.8

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Package

KerMLkerml.structure.package

A package is the concrete namespace a modeller writes to group related members and control their visibility. It owns named members and defines the scope in which those names resolve; membership within a package must be name-unique.

package P { class C; package Inner { class D; } }

Normative clauses: KerML §7.4.14

Support (sysml-rs): parse ✓ · resolve unknown · elaborate unknown · execute unknown

Redefinition

KerMLkerml.structure.redefinition

A redefinition relates a feature to an inherited feature it replaces, so the redefining feature stands in for the original in its context — the redefines (or :>>) relationship. It is a specialized subsetting that also hides the redefined feature.

package P { type A; feature f : A; feature h : A redefines f; }

Normative clauses: KerML §8.3.3.3.8

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Specialization

KerMLkerml.structure.specialization

A specialization relates a more specific type to a more general one, so the specific type inherits the general type’s features. In usage syntax it is written with the subclassification operator.

package P { part def A; part def B :> A; }

Normative clauses: KerML §8.3.3.1.8

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Structure

KerMLkerml.structure.structure

A structure is a class whose instances are structural — composed of interconnected parts rather than behavioral occurrences. It is the KerML base from which SysML part definitions derive.

package P { struct S; }

Normative clauses: KerML §7.4.4

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Subsetting

KerMLkerml.structure.subsetting

A subsetting relates a feature to a more general feature whose values include all of the subsetting feature’s values — the subsets (or :>) relationship between features. It is the feature-level analogue of subclassification.

package P { type A; feature f : A; feature g : A subsets f; }

Normative clauses: KerML §8.3.3.3.10

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Type

KerMLkerml.structure.type

A type is the most general KerML classifier of things: it classifies a set of instances and may specialize other types, inheriting their features. Classes, datatypes, associations, and behaviors are all specialized kinds of type.

package P { type T; }

Normative clauses: KerML §7.3.2

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Type Featuring

KerMLkerml.structure.type-featuring

A type-featuring relationship (featured by) makes a feature a feature of another type. Lowers to a distinct TypeFeaturing relationship owned by the declaring feature.

package P { class A; feature g featured by A; }

Normative clauses: KerML §8.3.3.3.11

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Unioning

KerMLkerml.structure.unioning

A unioning relationship (unions) makes a type the union of the listed types — its instances are those of any unioned type. Lowers to a distinct Unioning relationship owned by the declaring type.

package P { class A; class B; class U unions A, B; }

Normative clauses: KerML §8.3.3.1.11

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Allocation Definition

SysMLsysml.structure.allocation-definition

An allocation definition, written allocation def, defines a reusable kind of allocation relating a source element to a target element. It lowers to a distinct AllocationDefinition.

allocation def AD { }

Normative clauses: SysML §7.15.2

Support (sysml-rs): parse ✓ · resolve unknown · elaborate unknown · execute unknown

Allocation Usage

SysMLsysml.structure.allocation-usage

An allocation usage records that one element is allocated to another — a mapping decision (for example a function to a component) pending refinement into a firmer relationship. It has two or more ends and is typed by an allocation definition.

package P { part def Sys { part a; part b; allocation al allocate a to b; } }

Normative clauses: SysML §8.3.15.3

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Attribute Definition

SysMLsysml.structure.attribute-definition

An attribute definition defines a kind of value-typed feature that carries data rather than identity. Usages typed by it hold quantities or literals.

package P { attribute def Temperature; attribute t : Temperature; }

Normative clauses: SysML §8.3.7.2

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Attribute Usage

SysMLsysml.structure.attribute-usage

An attribute usage is a value-typed feature carrying data — a quantity or literal — rather than identity. It is typed by an attribute definition or a value type.

package P { attribute def Temperature; attribute t : Temperature; }

Normative clauses: SysML §8.3.7.3

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Binding Connector (as Usage)

SysMLsysml.structure.binding-connector-as-usage

A binding connector as usage, written bind a = b, asserts that two features have the same value at all times. It lowers to a distinct BindingConnectorAsUsage — the SysML usage form of a KerML binding connector.

part def P { attribute a; attribute b; bind a = b; }

Normative clauses: SysML §8.3.13.2

Support (sysml-rs): parse ✓ · resolve unknown · elaborate unknown · execute unknown

Conjugated Port Definition

SysMLsysml.structure.conjugated-port-definition

A conjugated port definition is the input/output-reversed counterpart of a port definition, referenced with the ~ prefix (port p : ~P). It lowers to a distinct ConjugatedPortDefinition whose directions are flipped from the base port.

port def Pt { out item a; } part def Q { port p : ~Pt; }

Normative clauses: SysML §7.12.3

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Connection Definition

SysMLsysml.structure.connection-definition

A connection definition defines a reusable kind of connection between parts, with two or more ends. Connection usages instantiate it to wire specific parts together.

package P { connection def Link; }

Normative clauses: SysML §8.3.13.3

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Connection Usage

SysMLsysml.structure.connection-usage

A connection usage wires two or more parts together through connector ends. It is owned by a containing type (a part or action), not directly by a package, and instantiates a connection definition or the base association.

package P { part def Sys { part a; part b; connection c connect a to b; } }

Normative clauses: SysML §8.3.13.4

Support (sysml-rs): parse ✓ · resolve unknown · elaborate unknown · execute unknown

Dependency

SysMLsysml.structure.dependency

A dependency declares that one or more client elements depend on one or more supplier elements — a coarse traceability link. It is a cross-grammar concept declared identically in KerML and SysML.

package P { part def A; part def B; dependency A to B; }

Normative clauses: SysML §7.3.2

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Enumerated Value

SysMLsysml.structure.enumerated-value

An enumerated value is the member form inside an enumeration definition. The spec models it as an EnumerationUsage (the grammar rule EnumeratedValue returns EnumerationUsage), so both the bare-name value form and the enum <name> form lower to an EnumerationUsage via a VariantMembership — there is no distinct EnumeratedValue metaclass.

package P { enum def Priority { high; low; } }

Normative clauses: SysML §7.8.2

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Enumeration Definition

SysMLsysml.structure.enumeration-definition

An enumeration definition defines a closed set of named literal values. Each enumerated value is a singleton usage of the definition, and a typed usage may take only one of them.

package P { enum def Color { enum red; enum green; } }

Normative clauses: SysML §8.3.8.2

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Enumeration Usage

SysMLsysml.structure.enumeration-usage

An enumeration usage is an attribute usage whose type is an enumeration definition. Inside an enum def body, enum <name>; declares an enumerated value (owned through a VariantMembership); standalone, enum <name> : <EnumDef>; declares an ordinary usage typed by the enumeration (plain owned member — the VariantMembership wrapper is enum-def-body-only). Both lower to a distinct EnumerationUsage.

package P { enum def Color { enum red; enum green; } }

Normative clauses: SysML §8.3.8.3

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Event Occurrence Usage

SysMLsysml.structure.event-occurrence-usage

An event occurrence usage marks a point event within an enclosing occurrence’s life — the happening of an occurrence, rather than the occurrence itself. It is written with the event keyword and either references an existing occurrence or declares one inline.

package P { occurrence def O { event occurrence e; } }

Normative clauses: SysML §8.3.9.2

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Feature Typing

SysMLsysml.structure.feature-typing

A feature typing relates a feature to a type that classifies it — the relationship behind x : T. The same abstract-syntax relationship is realized by two divergent concrete syntaxes: KerML’s 'typing' typedFeature (':'|'typed' 'by') FeatureType versus SysML’s OwnedFeatureTyping | ConjugatedPortTyping dispatcher.

package P { part def A; part a : A; }

Normative clauses: KerML §8.3.3.3.7

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Related: Feature Typing

Flow Connection

SysMLsysml.structure.flow-connection

A flow connection transfers items from a source port or feature to a compatible target across a connection. Endpoints must be reachable and their item types compatible.

package P { part def A { out port o; } part def B { in port i; } part def Sys { part a : A; part b : B; flow a.o to b.i; } }

Normative clauses: SysML §8.3.16.3

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Flow Definition

SysMLsysml.structure.flow-definition

A flow definition, written flow def, defines a reusable kind of transfer of items between features. It lowers to a distinct FlowDefinition that flow usages can be typed by.

flow def FD { }

Normative clauses: SysML §8.3.16.2

Support (sysml-rs): parse ✓ · resolve unknown · elaborate unknown · execute unknown

Individual Definition

SysMLsysml.structure.individual-definition

An individual definition (individual def X;) defines a class constrained to represent at most one individual over its lifetime. The grammar rule IndividualDefinition returns OccurrenceDefinition with isIndividual = true — there is no distinct IndividualDefinition metaclass. It lowers to an OccurrenceDefinition carrying the flag (the spec’s owned empty-multiplicity member is consciously carried by the flag alone).

package P { individual def X; abstract individual def Y :> X; }

Normative clauses: SysML §7.9.4

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Individual Usage

SysMLsysml.structure.individual-usage

An individual usage (the individual prefix) names a single identified occurrence. The spec models it as an OccurrenceUsage with isIndividual = true; it lowers to an OccurrenceUsage carrying that flag — there is no distinct IndividualUsage metaclass.

package P { occurrence def D; individual d : D; }

Normative clauses: SysML §7.9.4

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Interface Definition

SysMLsysml.structure.interface-definition

An interface definition defines a reusable kind of connection between ports, coupling their directed features. Interface usages instantiate it between specific ports.

package P { interface def Ifc; }

Normative clauses: SysML §8.3.14.2

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Interface Usage

SysMLsysml.structure.interface-usage

An interface usage connects two ports through interface ends, coupling their directed features. Like a connection it must be owned by a containing type, not directly by a package, and instantiates an interface definition.

package P { port def Sig; part def Sys { part x { port p1 : Sig; } part y { port p2 : Sig; } interface i connect x.p1 to y.p2; } }

Normative clauses: SysML §8.3.14.3

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Item Definition

SysMLsysml.structure.item-definition

An item definition defines a kind of thing that can be created, stored, or moved through a system — items carry identity but need not be behavioral parts. Usages typed by it occupy structure or flow along connections.

package P { item def Widget; }

Normative clauses: SysML §8.3.10.2

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Item Usage

SysMLsysml.structure.item-usage

An item usage is a typed occurrence of an item within a structure or flow. It is typed by an item definition and may be nested in a part or carried by a flow connection.

package P { item def Widget; item w : Widget; }

Normative clauses: SysML §8.3.10.3

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Occurrence Definition

SysMLsysml.structure.occurrence-definition

An occurrence definition defines a kind of thing that exists over time and space — the common base of parts (structure) and actions (behavior).

package P { occurrence def Event; }

Normative clauses: SysML §8.3.9.3

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Occurrence Usage

SysMLsysml.structure.occurrence-usage

An occurrence usage is a typed occurrence within a model’s space-time. It is typed by an occurrence definition and may be portioned in time or space.

package P { occurrence def Event; occurrence e : Event; }

Normative clauses: SysML §8.3.9.4

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Part Definition

SysMLsysml.structure.part-definition

A part definition defines a reusable kind of system component with structure and behavior. Part usages instantiate it within a containing structure.

package P { part def Engine; }

Normative clauses: SysML §8.3.11.2

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Part Usage

SysMLsysml.structure.part-usage

A part usage is a typed occurrence of a part within a containing structure. It is typed by a part definition and may nest further usages or redefine inherited ones.

package P { part def Engine; part e : Engine; }

Normative clauses: SysML §8.3.11.3

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Port Definition

SysMLsysml.structure.port-definition

A port definition defines a reusable kind of interaction point through which items and information pass. Port usages instantiate it on parts, optionally conjugated to reverse direction.

package P { port def Fuel; }

Normative clauses: SysML §8.3.12.5

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Port Usage

SysMLsysml.structure.port-usage

A port usage is a typed interaction point on a part through which items and information pass. It is typed by a port definition and may be conjugated to reverse direction.

package P { port def Fuel; part def E { port p : Fuel; } }

Normative clauses: SysML §8.3.12.6

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Portion Usage

SysMLsysml.structure.portion-usage

A portion usage (snapshot or timeslice) denotes a temporal portion of an occurrence. The spec models it as an OccurrenceUsage carrying a PortionKind; it lowers to an OccurrenceUsage with that portion kind — there is no distinct PortionUsage metaclass.

package P { occurrence def D; occurrence o : D { snapshot s; } }

Normative clauses: SysML §8.3.9.5

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Reference Usage

SysMLsysml.structure.reference-usage

A reference usage is a non-composite feature that refers to something owned elsewhere, rather than composing it. The ref keyword marks a feature as referential.

package P { part def W { ref r; } }

Normative clauses: SysML §8.3.6.3

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Validation Rules

Generated from the sysml-rs language pack — see the Language Reference index for provenance, licensing, and the raw JSON. 76 cards.

At Most One Return Parameter

KerMLkerml.validation.at-most-one-return-parameter

A function can have at most one return parameter

Normative clauses: KerML §8.3.4.7.4

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Function

Behavior Not Specialize Structure

KerMLkerml.validation.behavior-not-specialize-structure

A behavior cannot specialize a structure

Normative clauses: KerML §8.3.4.6.2

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Behavior

Binary Connector Two Ends

KerMLkerml.validation.binary-connector-two-ends

A binary connector must have exactly two end features

Normative clauses: KerML §8.3.4.5.3

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Binding Connector Two Ends

KerMLkerml.validation.binding-connector-two-ends

A binding connector must have exactly two end features

Normative clauses: KerML §8.3.4.5.2

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Class Not Specialize Datatype

KerMLkerml.validation.class-not-specialize-datatype

A class cannot specialize a data type or association

Normative clauses: KerML §8.3.4.2.2

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Class

Connector Owned By Type

KerMLkerml.validation.connector-owned-by-type

A connection must be owned by a type (part, action, etc.), not a package

Normative clauses: KerML §8.3.4.5.3

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Connection Usage, Flow Connection, Interface Usage

Datatype Not Specialize Class

KerMLkerml.validation.datatype-not-specialize-class

A data type cannot specialize a class or association

Normative clauses: KerML §8.3.4.1.2

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Data Type

Model Element Must Be Owned

KerMLkerml.validation.model-element-must-be-owned

Every non-root model element is owned by (a member of) a namespace; a bare top-level usage/def that is not a package is an orphan.

Normative clauses: KerML §8.2

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

No Inherited Name Conflict

KerMLkerml.validation.no-inherited-name-conflict

Member name ‘{name}’ conflicts with inherited member

Normative clauses: KerML §8.3.2.4.5

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Namespace

No Name Alias Conflict

KerMLkerml.validation.no-name-alias-conflict

Member name ‘{name}’ duplicates an alias

Normative clauses: KerML §8.3.2.4.5

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Namespace

Parameter Membership Owning Type

KerMLkerml.validation.parameter-membership-owning-type

Parameter membership is only allowed in behaviors and steps

Normative clauses: KerML §8.3.4.6.4

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Result Expression In Function Or Expression

KerMLkerml.validation.result-expression-in-function-or-expression

Result expression membership is only allowed in functions and expressions

Normative clauses: KerML §8.3.4.7.7

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Return Parameter Membership Owning Type

KerMLkerml.validation.return-parameter-membership-owning-type

Return parameter membership is only allowed in functions and expressions

Normative clauses: KerML §8.3.4.7.8

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Structure Not Specialize Behavior

KerMLkerml.validation.structure-not-specialize-behavior

A structure cannot specialize a behavior

Normative clauses: KerML §8.3.4.3.2

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Structure

Succession Two Ends

KerMLkerml.validation.succession-two-ends

A succession must have exactly two end features

Normative clauses: KerML §8.3.4.5.4

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Unique Owned Member Names

KerMLkerml.validation.unique-owned-member-names

Duplicate owned member name ‘{name}’

Normative clauses: KerML §8.3.2.4.5

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Namespace

Accept Action Has Payload

SysMLsysml.validation.accept-action-has-payload

An accept action must have a payload parameter

Normative clauses: SysML §8.3.17.2

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Accept Action

Action Typed By Behavior

SysMLsysml.validation.action-typed-by-behavior

An action must be typed by action definitions

Normative clauses: SysML §8.3.17.4

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Action Usage

Actor Membership In Req Or Case

SysMLsysml.validation.actor-membership-in-req-or-case

Only requirements and cases can have actors

Normative clauses: SysML §8.3.21.2

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Actor

Allocation Has Ends

SysMLsysml.validation.allocation-has-ends

An allocation must have at least two end features

Normative clauses: SysML §8.3.15.3

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Allocation Usage

Allocation Typed By Allocation Defs

SysMLsysml.validation.allocation-typed-by-allocation-defs

An allocation must be typed by allocation definitions

Normative clauses: SysML §8.3.15.3

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Allocation Usage

Analysis Case At Most One Subject

SysMLsysml.validation.analysis-case-at-most-one-subject

An analysis case can have at most one subject

Normative clauses: SysML §8.3.23.2

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Analysis Case, Analysis Case Definition

Assignment Action Has Target

SysMLsysml.validation.assignment-action-has-target

An assignment action must have a target feature

Normative clauses: SysML §8.3.17.5

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Assignment Action

At Most One Objective

SysMLsysml.validation.at-most-one-objective

A case definition can have at most one objective

Normative clauses: SysML §8.3.22.2

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Case Definition, Case Usage

At Most One State Subaction

SysMLsysml.validation.at-most-one-state-subaction

A state definition may have at most one entry, one do, and one exit action

Normative clauses: SysML §8.3.18.5

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: State Definition, State Usage

At Most One Subject

SysMLsysml.validation.at-most-one-subject

A requirement definition can have at most one subject

Normative clauses: SysML §8.3.21.8

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Case Definition, Case Usage, Requirement Definition, Requirement Usage

At Most One View Rendering

SysMLsysml.validation.at-most-one-view-rendering

A view definition may have at most one view rendering

Normative clauses: SysML §8.3.26.7

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: View Definition, View Usage

Attribute Def Not Specialize Item Def

SysMLsysml.validation.attribute-def-not-specialize-item-def

An attribute definition cannot specialize an item definition

Normative clauses: SysML §8.3.7.2

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Attribute Definition

Attribute Must Not Be Composite

SysMLsysml.validation.attribute-must-not-be-composite

An AttributeUsage must not be composite

Normative clauses: SysML §8.3.7.3

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Attribute Definition, Attribute Usage

Attribute Typed By Datatypes

SysMLsysml.validation.attribute-typed-by-datatypes

An attribute must be typed by attribute definitions

Normative clauses: SysML §8.3.7.3

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Attribute Usage

Calculation Typed By One Calc Def

SysMLsysml.validation.calculation-typed-by-one-calc-def

A calculation must be typed by one calculation definition

Normative clauses: SysML §8.3.19.3

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Calculation Usage

Case Typed By One Case Def

SysMLsysml.validation.case-typed-by-one-case-def

A case must be typed by one case definition

Normative clauses: SysML §8.3.22.3

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Case Usage

Concern At Most One Subject

SysMLsysml.validation.concern-at-most-one-subject

A concern can have at most one subject

Normative clauses: SysML §8.3.21.3

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Concern Definition, Concern Usage

Connection Has Ends

SysMLsysml.validation.connection-has-ends

A connection must have at least two end features

Normative clauses: SysML §8.3.13.4

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Connection Usage

Connection Typed By Association

SysMLsysml.validation.connection-typed-by-association

A connection must be typed by connection definitions

Normative clauses: SysML §8.3.13.4

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Connection Usage

Constraint Typed By Predicate

SysMLsysml.validation.constraint-typed-by-predicate

A constraint must be typed by one constraint definition

Normative clauses: SysML §8.3.20.4

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Constraint Usage

Decision Node One Incoming

SysMLsysml.validation.decision-node-one-incoming

A decision node must have at most one incoming succession

Normative clauses: SysML §8.3.17.7

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Decision Node

Enumeration Typed By One Enum Def

SysMLsysml.validation.enumeration-typed-by-one-enum-def

An enumeration must be typed by one enumeration definition

Normative clauses: SysML §8.3.8.3

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Enumeration Usage

Exhibit State One Type

SysMLsysml.validation.exhibit-state-one-type

An exhibit state must be typed by exactly one state definition

Normative clauses: SysML §8.3.18.2

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Exhibit State Usage

Flow Typed By Interaction

SysMLsysml.validation.flow-typed-by-interaction

A flow connection must be typed by flow connection definitions

Normative clauses: SysML §8.3.16.3

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Flow Connection

Fork Node One Incoming

SysMLsysml.validation.fork-node-one-incoming

A fork node must have at most one incoming succession

Normative clauses: SysML §8.3.17.8

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Fork Node

Interface Has Ends

SysMLsysml.validation.interface-has-ends

An interface must have at least two end features

Normative clauses: SysML §8.3.14.3

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Interface Usage

Interface Typed By Interface Defs

SysMLsysml.validation.interface-typed-by-interface-defs

An interface must be typed by interface definitions

Normative clauses: SysML §8.3.14.3

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Interface Usage

Item Def Not Specialize Attribute Def

SysMLsysml.validation.item-def-not-specialize-attribute-def

An item definition cannot specialize an attribute definition

Normative clauses: SysML §8.3.10.2

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Item Definition

Item Typed By Item Defs

SysMLsysml.validation.item-typed-by-item-defs

An item must be typed by item definitions

Normative clauses: SysML §8.3.10.3

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Item Usage

Join Node One Outgoing

SysMLsysml.validation.join-node-one-outgoing

A join node must have at most one outgoing succession

Normative clauses: SysML §8.3.17.11

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Join Node

Merge Node One Outgoing

SysMLsysml.validation.merge-node-one-outgoing

A merge node must have at most one outgoing succession

Normative clauses: SysML §8.3.17.13

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Merge Node

Objective Membership In Case

SysMLsysml.validation.objective-membership-in-case

Only cases can have objectives

Normative clauses: SysML §8.3.22.4

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Objective

Occurrence Typed By Occurrence Defs

SysMLsysml.validation.occurrence-typed-by-occurrence-defs

An occurrence must be typed by occurrence definitions

Normative clauses: SysML §8.3.9.4

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Occurrence Usage

Parallel State No Transitions

SysMLsysml.validation.parallel-state-no-transitions

A parallel state cannot have transitions

Normative clauses: SysML §8.3.18.5

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: State Definition, State Usage

Part Typed By Part Defs

SysMLsysml.validation.part-typed-by-part-defs

A part must be typed by at least one part definition

Normative clauses: SysML §8.3.11.3

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Part Usage

Perform Action One Type

SysMLsysml.validation.perform-action-one-type

A perform action must be typed by exactly one action definition

Normative clauses: SysML §8.3.17.14

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Perform Action

Port Definition Owned Usages Referential

SysMLsysml.validation.port-definition-owned-usages-referential

The non-port owned usages of a port definition must be referential (non-composite)

Normative clauses: SysML §8.3.12.5

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Port Definition

Port Typed By Port Defs

SysMLsysml.validation.port-typed-by-port-defs

A port must be typed by port definitions

Normative clauses: SysML §8.3.12.6

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Port Usage

Port Usage Nested Usages Referential

SysMLsysml.validation.port-usage-nested-usages-referential

The non-port nested usages of a port usage must be referential (non-composite)

Normative clauses: SysML §8.3.12.6

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Port Usage

Requirement Constraint In Requirement

SysMLsysml.validation.requirement-constraint-in-requirement

Only requirements can have assumed or required constraints

Normative clauses: SysML §8.3.21.7

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Requirement Constraint (assume / require)

Requirement Constraints Composite

SysMLsysml.validation.requirement-constraints-composite

Assumed constraints must be composite

Normative clauses: SysML §8.3.21.8

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Requirement Definition, Requirement Usage

Requirement Typed By One Req Def

SysMLsysml.validation.requirement-typed-by-one-req-def

A requirement must be typed by one requirement definition

Normative clauses: SysML §8.3.21.9

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Requirement Usage

Satisfy Req One Type

SysMLsysml.validation.satisfy-req-one-type

A satisfy requirement must be typed by one requirement definition

Normative clauses: SysML §8.3.21.10

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Requirement Satisfaction

Send Action Has Payload

SysMLsysml.validation.send-action-has-payload

A send action must have a payload parameter

Normative clauses: SysML §8.3.17.15

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Send Action

Stakeholder Membership In Requirement

SysMLsysml.validation.stakeholder-membership-in-requirement

Only requirements can have stakeholders

Normative clauses: SysML §8.3.21.12

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Stakeholder

State Subaction Owned By State

SysMLsysml.validation.state-subaction-owned-by-state

Entry, do, and exit actions can only appear in state definitions or usages

Normative clauses: SysML §8.3.18.4

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

State Typed By State Defs

SysMLsysml.validation.state-typed-by-state-defs

A state must be typed by state definitions

Normative clauses: SysML §8.3.18.6

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: State Usage

Subject Is First Parameter

SysMLsysml.validation.subject-is-first-parameter

The subject must be the first parameter (input) of a requirement definition

Normative clauses: SysML §8.3.21.8

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Case Definition, Case Usage, Requirement Definition, Requirement Usage

Subject Membership In Req Or Case

SysMLsysml.validation.subject-membership-in-req-or-case

Only requirements and cases can have subjects

Normative clauses: SysML §8.3.21.11

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Subject

Transition Feature In Transition

SysMLsysml.validation.transition-feature-in-transition

Transition feature membership is only allowed in transitions

Normative clauses: SysML §8.3.18.8

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Transition Has Source

SysMLsysml.validation.transition-has-source

A transition must have a source

Normative clauses: SysML §8.3.18.9

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Transition Usage

Transition Owned By State Or Action

SysMLsysml.validation.transition-owned-by-state-or-action

A transition must be owned by a state or an action

Normative clauses: SysML §8.3.18.9

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Transition Usage

Usage Typed By Definitions

SysMLsysml.validation.usage-typed-by-definitions

A usage must be typed by definitions

Normative clauses: SysML §8.3.6.4

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Use Case At Most One Subject

SysMLsysml.validation.use-case-at-most-one-subject

A use case can have at most one subject

Normative clauses: SysML §8.3.25.3

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Use Case Definition, Use Case Usage

Variant Membership In Variation

SysMLsysml.validation.variant-membership-in-variation

A variant must be an owned member of a variation

Normative clauses: SysML §8.3.6.5

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Variation Members Are Variants

SysMLsysml.validation.variation-members-are-variants

An owned usage of a variation must be a variant

Normative clauses: SysML §8.3.6.2

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Variation Must Be Abstract

SysMLsysml.validation.variation-must-be-abstract

A variation definition must be abstract

Normative clauses: SysML §8.3.6.2

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Variation No Chain

SysMLsysml.validation.variation-no-chain

A variation must not specialize another variation

Normative clauses: SysML §8.3.6.2

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Verification Case At Most One Subject

SysMLsysml.validation.verification-case-at-most-one-subject

A verification case can have at most one subject

Normative clauses: SysML §8.3.24.3

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Related: Verification Case, Verification Case Definition

View Rendering In View

SysMLsysml.validation.view-rendering-in-view

Only views can have view renderings

Normative clauses: SysML §8.3.26.10

Support (sysml-rs): parse unknown · resolve unknown · elaborate unknown · execute unknown

Views

Generated from the sysml-rs language pack — see the Language Reference index for provenance, licensing, and the raw JSON. 8 cards.

Membership Expose

SysMLsysml.views.membership-expose

A membership expose imports a single named member of a namespace into a view. It is written with expose and a qualified name naming that one member.

package P { part def Car { part e; } view v { expose Car::e; } }

Normative clauses: SysML §8.3.26.3

Support (sysml-rs): parse ✓ · resolve unknown · elaborate unknown · execute unknown

Namespace Expose

SysMLsysml.views.namespace-expose

A namespace expose imports every member of a namespace into a view, so the whole namespace is available to be rendered. It is written with expose and a namespace path ending in ::*.

package P { part def Car { part e; } view v { expose Car::*; } }

Normative clauses: SysML §8.3.26.4

Support (sysml-rs): parse ✓ · resolve unknown · elaborate unknown · execute unknown

Rendering Definition

SysMLsysml.views.rendering-definition

A rendering definition defines how a view is presented — the notation or format used to draw it. Rendering usages bind a rendering to a view.

package P { rendering def AsTree; }

Normative clauses: SysML §8.3.26.5

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Rendering Usage

SysMLsysml.views.rendering-usage

A rendering usage is a typed occurrence of a rendering, typed by a rendering definition. It selects the presentation applied to the view that owns it.

package P { rendering def Rn; rendering rn : Rn; }

Normative clauses: SysML §8.3.26.6

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

View Definition

SysMLsysml.views.view-definition

A view definition defines a reusable kind of view — a rendered projection of the model for a stakeholder, governed by a viewpoint. View usages instantiate it.

package P { view def Overview; }

Normative clauses: SysML §8.3.26.7

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

View Usage

SysMLsysml.views.view-usage

A view usage renders a selection of the model according to a viewpoint, exposing chosen elements to a stakeholder. It is typed by a view definition.

package P { view def V; view v : V; }

Normative clauses: SysML §8.3.26.11

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Viewpoint Definition

SysMLsysml.views.viewpoint-definition

A viewpoint definition defines the concerns a view must address for a set of stakeholders — the specification a conforming view satisfies. Viewpoint usages bind it to concrete views.

package P { viewpoint def Stakeholder; }

Normative clauses: SysML §8.3.26.8

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown

Viewpoint Usage

SysMLsysml.views.viewpoint-usage

A viewpoint usage is a typed occurrence of a viewpoint, typed by a viewpoint definition. It applies the viewpoint’s concerns to the view that owns it.

package P { viewpoint def Vp; viewpoint vp : Vp; }

Normative clauses: SysML §8.3.26.9

Support (sysml-rs): parse ✓ · resolve ✓ · elaborate unknown · execute unknown