Chapter 2. Tutorial: Modeling the State Structure of a Home with SysML v2

This tutorial introduces the most important state structure modeling concepts of SysML v2:

  1. data types and attributes (or data properties), including predefined data types, such as String and Integer, as well as predefined quantity value types from the International System of Quantities (ISQ), such as ISQ::AreaValue or ISQ::ElectricCurrentValue,
  2. part types and part properties,
  3. connection types and connection properties,
  4. item types and item properties,
  5. port types and port properties,
  6. interface types and interface properties,
  7. functions (called "calculation definitions"),
  8. constraints, and
  9. requirements.

Notice that in SysML v2 lingo, part types (and, likewise, item/port/etc. types) are officially called part (item/port/etc.) "definitions", and part (item/port/etc.) properties are officially called part (item/port/etc.) "usages", but in this book we prefer using the classical terminology of types and properties.

The running example of the tutorial (inspired by an example presented by Michael Jastram) describes a home as a system consisting of

  1. rooms, which are connected by doors, and
  2. devices, which are connected to power outlets of the home's electric grid.

We model a simple type of home consisting of a living room equipped with an electrical heater, a bed room, a kitchen equipped with a dishwasher, and a bath room.

1 Making a Model Sketch

We can make a quick model sketch by not considering the ranges (or the typing) of properties. We follow the method of refinement-based modeling where we start with (incomplete) model sketches and then apply a sequence of refinement steps for obtaining a completed model.

1.1 Modeling Rooms as Parts of a Home

In a first step we just define the attribute size and the type of rooms a home consists of. We can model the rooms of a home as parts (or components) by defining corresponding part properties for the part type Home:

part def Home {
  attribute size;
  part livingRoom;
  part bedRoom;
  part kitchen;
  part bathRoom;
}

This declaration defines a part type Home with the six part properties bedRoom, livingRoom, kitchen, bathRoom, tv and dishwasher, such that for a home h, the expressions h.bedRoom, h.livingRoom, h.kitchen, and h.bathRoom denote the four rooms, while h.tv and h.dishwasher denote two devices, h consists of.

Notice that any system may be a subsystem (or part) of a larger system. For instance, a home may be a part of a city. Therefore, in SysML v2, systems are defined as part types.

1.2 Modeling Devices as Parts of Rooms

We model the devices allocated to specific rooms as (nested) parts:

part def Home {
  attribute size;
  part livingRoom {
    part heater;
  }
  part bedRoom;
  part kitchen {
    part dishwasher;
  }
  part bathRoom;
}

1.3 Modeling Connections between Rooms

We may also want to describe in our model, which rooms are connected to each other. The simplest way of expressing this in SysML v2 is by simply stating which room is connected to which other room in the following way:

part def Home {
  attribute size;
  part livingRoom {
    part heater;
  }
  part bedRoom;
  part kitchen {
    part dishwasher;
  }
  part bathRoom;
  connect livingRoom to bedRoom;    
  connect livingRoom to kitchen;    
  connect livingRoom to bathRoom;    
}

The above connection statements are actually shorthand forms of the full connection property declarations shown below.

The textual model sketch above can be visualized by the following diagram:

???

1.4 Modeling Power Outlets and Device Sockets as Ports

After describing how a home is composed of rooms and devices and how its rooms are connected to each other in a SysML v2 state structure model, we extend this model by adding power outlets of rooms and sockets of electrical devices connected to power outlets. Both the power outlets and the device sockets are modeled in the form of ports, which are special objects that represent connection points and can be interconnected by interfaces. Likewise, we model both a water spigot in the kitchen and the water inlet of the dishwasher as ports.

Ports have directed (output or input) properties and can be connected via an interface for enabling transfers of physical quantities or signals, if their types match in the sense that they have the same directed properties, but with inverse direction.

The resulting model defines port properties as well as interface properties, as shown in the following sketch:

part def Home {
  attribute size;
  part livingRoom {
    part heater {
      port socket { in power;}
    }
    port outlet { out power;}
    interface heater.socket to outlet;
  }
  part bedRoom;
  part kitchen {  
    part dishwasher {
      port socket { in power;}
      port waterInlet { in water;}
    }
    port outlet { out power;}
    port waterSpigot { out water;}
    interface dishwasher.socket to outlet;
    interface dishwasher.waterInlet to waterSpigot;
  }
  part bathRoom;
  connect livingRoom to bedRoom;    
  connect livingRoom to kitchen;    
  connect livingRoom to bathRoom;
}

Notice that the interface connection statements in this model sketch are actually shorthand forms of the full interface property declarations shown in the full model below.

1.5 Adding an Electric Grid as a Component of the Home

We can define a home's electric grid (with 120 V and a rated current of 200 A) in the form of a part property electricGrid that has two attributes voltage and ratedCurrent as well as a many-valued port property outlets. For being able to define a grid with specific voltage and rated current values, we first need to import the predefined quantity units V and A from the SI library:

private import SI::V;
private import SI::A;
part def Home {
  attribute size;
  part electricGrid {
    attribute voltage = 120 [V];
    attribute ratedCurrent = 200 [A];
    port outlets[*] {
      attribute voltage = electricGrid.voltage;
      attribute ratedCurrent;
      out power;
    }
  }
  part livingRoom {
    ...
    port outlet :> electricGrid.outlets {
      attribute redefine ratedCurrent = 10 [A];
    }
    ...
  }
  part bedRoom;
  part kitchen {
    ...
    port outlet :> electricGrid.outlets {
      attribute redefine ratedCurrent = 20 [A];
    }
    ...
  }
  part bathRoom;
  ...
}

Notice that the attributes voltage and ratedCurrent of electricGrid are bound to the quantity values 120 [V] and 200 [A], and the attributes livingRoom.outlet.voltage as well as kitchen.outlet.voltage are bound to electricGrid.voltage.

Also, in this extended model, the port property outlet defined for the livingRoom and the kitchen now specializes the outlets property of electricGrid as expressed by the symbol :>. This makes sure that both livingRoom.outlet and kitchen.outlet are included in electricGrid.outlets.

The multiplicity * shown enclosed in brackets [*] as a suffix of the port property name electricGrid.outlets indicates that the electricGrid can have zero, one or many outlets. The symbol * is a short form of the multiplicity expression 0..* standing for zero-or-more.

For making sure that the maximum current of power outlets cannot be greater than the maximum current of the electric grid, we add a corresponding constraint in the full model below.

2 Adding the Ranges of Attributes and Properties

Since we didn't define a range for the attribute size in the model sketch above, it may have values representing other things than area quantities. In order to prevent this, we have to define the attribute size typed by a suitable data type as its range:

part def Home {
  attribute size : ISQ::AreaValue;
  ...
}

Notice that the data type AreaValue chosen for typing the attribute size comes from the SysML v2 library ISQ, where it is predefined along with other basic quantity value types and quantity units according to the International System of Quantities. It allows area values such as "92.5 [m^2]".

Since we didn't define a range for the part properties in the model above, they may have values representing neither rooms nor devices. In order to prevent this, we define the part types Room and Device, as well as their subtypes Kitchen and Dishwasher, for typing the part properties of Home:

part def Room {
  attribute name : String;
  attribute size : ISQ::AreaValue;
  port outlet { out power :> ISQ::electricPower;}
}
part def Kitchen :> Room {
  port waterSpigot { out water : Water;}
}
part def Device {
  attribute voltage :> ISQ::voltage;
  attribute ratedCurrent : ISQ::ElectricCurrentValue;
  attribute power : Real;
  port socket { in power :> ISQ::electricPower;}
}
part def Dishwasher :> Device {
  port waterInlet { in water : Water;}
}
part def Home {
  attribute size : ISQ::AreaValue;
  part livingRoom : Room {
    part heater: Device;
  }
  part bedRoom : Room;
  part kitchen : Kitchen {
    part dishwasher: Dishwasher;
  }
  part bathRoom : Room;
  ...
}

Notice that for some quantity types, such as for voltage and electric power, the ISQ library currently only defines quantity properties such as ISQ::voltage and ISQ::electricPower, but no corresponding quantity value types (e.g., there is no VoltageValue element in the ISQ library).

3 Defining an Attribute with a Computed Value

We can have the value of the Home::size attribute being computed by adding up the sizes of all four rooms and binding the resulting value to the attribute:

part def Room {...}
part def Home {
  attribute size : ISQ::AreaValue = livingRoom.size + 
      bedRoom.size + kitchen.size + bathRoom.size;
  part bedRoom : Room;
  ...
}

We could model the computation of the Home::size attribute in a generic way by adding a property rooms[1..*] that represents the collection of all the rooms of the home:

part def Room {...}
part def Home {
  attribute size : ISQ::AreaValue = RealFunctions::sum( rooms.size);
  part rooms[1..*] : Room;
  part livingRoom :> rooms;
  part bedRoom :> rooms;
  ...
}

Notice that for making sure that the bed room, living room, etc. are included in the collection rooms, the corresponding part properties are declared by subsetting rooms. The expression rooms.size represents the set of all room size values.

4 Modeling Involved Objects That Are Not Parts

In SysML v2, objects are called items. Therefore, objects that are not parts in the sense of SysML v2 can be modelled as plain items.

In addition to describing the rooms of a home as its parts, we can also describe other involved objects that are not parts of the home, such as the people that are currently present in the home or the city, in which the home is located, by classifying them as items like so:

part def Room {...}
item def Person {
  attribute name : String;
}
item def City {
  attribute name : String;
}
part def Home {
  item peoplePresentInHome[*]: Person; 
  item city: City; 
  part bedRoom : Room;
  ...
}

Notice the multiplicity * shown enclosed in brackets [*] as a suffix of the item property name peoplePresentInHome. It indicates that there can be zero, one or many people present in the home. The symbol * is a short form of the multiplicity expression 0..* standing for zero-or-more.

5 Distinguishing between "Composite" and "Referential" Properties

In KerML and SysML v2, properties (or features) are either composite or referential. By default, attributes are referential, while part and item properties are composite. When a part has a value for a composite property, this value represents an occurrence, the life of which ends when the life of the part ends. In other words, the value is destroyed together with the part. Such a life cycle dependency does not hold for referential properties.

For the model fragment in the previous section, this means that the item property city should be declared to be referential because it does not make sense to state that destroying a home implies destroying the city it is located in. On the other hand, the item property peoplePresentInHome may be left as composite (by default), if we want to make the assumption that the people present in a home when it is destroyed are destroyed together with the home resulting in the following corrected model fragment:

part def Room {...}
item def Person {...}
item def City {...}
part def Home {
  item peoplePresentInHome[*]: Person; 
  ref item city: City; 
  part bedRoom : Room;
  ...
}

6 Adding the Ranges and Multiplicities of Connection Properties

The shorthand syntax for defining connections used in the model sketch above leaves them kind of abstract by not describing their concrete type. We may want to state that rooms are connected via doors. Notice that a door, which is mounted between two rooms of a home, is not just an object, but also a connection between the two rooms (such a connection is called a binary link and its connection type is called an association in UML and KerML/SysML2).

So, we first need to define the binary connection type Door with the two attributes width and height before we can define the (types of) connections between rooms in the form of connection properties typed by Door. Since a room has at least one door to another room, but may also have more than one door connecting it to other rooms, the multiplicity that constrains its number of doors is 1..*. Consequently, the cross multiplicity for both ends of the connection type Door is 1..*, as expressed in the definition of Door in the following model fragment:

part def Room {...}
connection def Door {
  end [1..*] part room1 : Room;
  end [1..*] part room2 : Room;
  attribute width : ISQ::LengthValue;
  attribute height : ISQ::LengthValue;
}
part def Home {
  ...
  connection livingRoom2bedRoom[1] : Door
connect livingRoom to bedRoom; connection livingRoom2kitchen[1] : Door connect livingRoom to kitchen; connection livingRoom2bathRoom[1] : Door
connect livingRoom to bathRoom; }

Notice that the multiplicity 1 defined for the three connection properties livingRoom2bedRoom, livingRoom2kitchen and livingRoom2bathRoom implies that these properties hold exactly one value representing the door that connects the two rooms concerned. This implies that there is exactly one door between the two rooms connected. We could also allow having a second door between two rooms by changing these multiplicities from 1 to 1..2.

7 Adding Port Types and Interface Types

We finally complete the typing of all properties by adding the port and interface types that provide the ranges for port and interface properties.

We need to define two pairs of matching port types: PowerOutletPort pairs with DeviceSocketPort and WaterSpigotPort pairs with DeviceWaterInletPort.

port def PowerOutletPort {
  out attribute power :> ISQ::electricPower;
  attribute voltage :> ISQ::voltage;
  attribute ratedCurrent : ISQ::ElectricCurrentValue;
}
part def Room {
  attribute name : String;
  attribute size : ISQ::AreaValue;
  port outlet : PowerOutletPort;
}
item def Water;
port def WaterSpigotPort {
  out item water : Water;
}
part def Kitchen :> Room {
  port waterSpigot : WaterSpigotPort;
}
part def ElectricGrid {
  attribute nominalVoltage :> ISQ::voltage;
  attribute ratedCurrent : ISQ::ElectricCurrentValue;
  port outlets[1..*] : PowerOutletPort {
    attribute redefines voltage default = nominalVoltage;
  }
}
port def DeviceSocketPort {
  in attribute power :> ISQ::electricPower;
  attribute voltage :> ISQ::voltage;
  attribute ratedCurrent : ISQ::ElectricCurrentValue;
}
part def Device {
  attribute name : String;
  attribute voltage :> ISQ::voltage;
  attribute ratedCurrent : ISQ::ElectricCurrentValue;
  port socket : DeviceSocketPort {
    attribute redefines voltage = Device::voltage;
    attribute redefines ratedCurrent = Device::ratedCurrent;
  }
}
port def DeviceWaterInletPort {
  in item water : Water;
}
part def Dishwasher :> Device {
  port waterInlet : DeviceWaterInletPort;
}

Concerning the two interface types needed for connecting our matching port types, we have to answer the following multiplicity questions:

  1. To how many sockets (or water inlets) is a power outlet (or water spigot) connected?
  2. To how many power outlets (or water spigots) is a socket (or water inlet) connected?

The answer to both questions is: exactly one and, consequently, we define both interface types with the cross multiplicity of 1 at both ends:

interface def Socket2OutletInterface {
  end [1] port socket : DeviceSocketPort;
  end [1] port outlet : PowerOutletPort;
}
interface def WaterInlet2SpigotInterface {
  end [1] port waterInlet : DeviceWaterInletPort;
  end [1] port waterSpigot : WaterSpigotPort;
}

Finally, we use the two interface types for typing the three interface properties heater2PowerOutlet, dishwasher2PowerOutlet and dishwasher2WaterSpigot, each with a multiplicity of exactly one, since both the living room and the kitchen have only one device and, consequently, only one interface connection for each of the three interface types being the values of these three interface properties:

part def Home {
  ...
  part livingRoom : Room {
    part heater: Device;
    interface heater2PowerOutlet[1] : Socket2OutletInterface 
      connect heater.socket to outlet;
    }
  part bedRoom : Room;
  part kitchen : Kitchen {
    part dishwasher: Dishwasher;
    interface dishwasher2PowerOutlet[1] : Socket2OutletInterface 
      connect dishwasher.socket to outlet;
    interface dishwasher2WaterSpigot[1] : WaterInlet2SpigotInterface 
      connect dishwasher.waterInlet to waterSpigot;
  }
  part bathRoom : Room;
  ...
}

8 Asserting a Constraint

For making sure that the rated current of power outlets must not be greater than the rated current of the electric grid, we can add a corresponding constraint in the model element concerned, which is the port property ElectricGrid::outlets:

part def ElectricGrid {
  attribute voltage :> ISQ::voltage;
  attribute ratedCurrent : ISQ::ElectricCurrentValue;
  port outlets[1..*] : PowerOutletPort {
    redefines voltage default = nominalVoltage;
    assert constraint { (that as PowerOutletPort).ratedCurrent <= ratedCurrent }
  }
}

Notice that in the constraint assertion we use the predefined context variable that for referencing the values of the outlets property. Since that has to be casted to the type of the values it references (here, the port type PowerOutletPort), we need to use the cast expression (that as PowerOutletPort) for being able to access the type's properties (such as the attribute ratedCurrent).

9 Defining a Function for Computing Heat Loss

A HeatLoss function is declared as a calculation definition with calc def according to Newton's law of cooling with 4 input parameters:

calc def HeatLoss {
  //  Applying Newton's law of cooling: Q = h * A * ΔT  
  in A  : Real;   // exterior wall area [m²]
  in ambientTemp : Real;  // outside temperature [°C]
  in targetTemp : Real;  // desired interior temperature [°C]
  in h  : Real default = 1.5;  // heat transfer coefficient [W/(m²·K)]
  attribute deltaT : Real = targetTemp - ambientTemp;
  return : Real = h * A * deltaT; // resulting heat loss in Watts
}

Notice that the input parameter h for the heat transfer coefficient has a default value of 1.5 W/m²·K, which is typical for an insulated wall.

10 Adding a Requirement Definition for Heating

We add a requirement definition that allows to check if the heater of a room is sufficient for compensating the heat loss due to exterior walls:

requirement def HeatingSufficiency {
  subject room : Room;
  attribute ambientTemp : Real;  // outside temperature [°C]
  attribute targetTemp  : Real;  // desired int. temp. [°C]
  doc /* The heat loss of a room shall not exceed the
         power of its heater. */
  require constraint {
    room.heater.power > HeatLoss( room.areaOfExtWalls,
                                  ambientTemp, targetTemp)
  }
}

This requirement definition is used for typing the Home requirement property heatSuff5, which defines a precondition (ambientTemp > 5) for the requirement in the form of an assume constraint. The satisfaction assertion satisfy heatSuff5 is then added to the livingRoom:

part def Home {
  ...
  part livingRoom : Room {
    attribute :>> outlet :> electricGrid.outlets;
    part heater: Device {attribute :>> ratedCurrent = 20 [A];}
    interface heater2PowerOutlet[1] : Socket2OutletInterface 
      connect heater.socket to outlet;
    satisfy heatSuff5;
    }
  }
  ...
  requirement heatSuff5: HeatingSufficiency {
    attribute :>> ambientTemp = Home::ambientTemp;
    attribute :>> targetTemp = Home::targetTemp;
    assume constraint { ambientTemp > 5 }
  }
}

11 The Complete Model

We can finally show the complete model:

private import ScalarValues::String;
private import SI::V;
private import SI::A;
port def PowerOutletPort {
  out attribute power :> ISQ::electricPower;
  attribute voltage :> ISQ::voltage;
  attribute ratedCurrent : ISQ::ElectricCurrentValue;
}
part def Room {
  attribute name : String;
  attribute size : ISQ::AreaValue;
  port outlet : PowerOutletPort;
}
item def Water;
port def WaterSpigotPort {
  out item water : Water;
}
part def Kitchen :> Room {
  port waterSpigot : WaterSpigotPort;
}
connection def Door {
  end [1] part room1 : Room;
  end [1] part room2 : Room;
  attribute width : ISQ::LengthValue;
  attribute height : ISQ::LengthValue;
}
part def ElectricGrid {
  attribute nominalVoltage :> ISQ::voltage;
  attribute ratedCurrent : ISQ::ElectricCurrentValue;
  port outlets[1..*] : PowerOutletPort {
    attribute :>> voltage default = nominalVoltage;
    assert constraint { (that as PowerOutletPort).ratedCurrent <= ratedCurrent }
  }
}
part def ElectricGridUS :> ElectricGrid {
  attribute :>> voltage = 120 [V];
  attribute :>> ratedCurrent = 200 [A];
}
port def DeviceSocketPort {
  in attribute power :> ISQ::electricPower;
  attribute voltage :> ISQ::voltage;
  attribute ratedCurrent : ISQ::ElectricCurrentValue;
}
part def Device {
  attribute voltage :> ISQ::voltage;
  attribute ratedCurrent : ISQ::ElectricCurrentValue;
  attribute power : Real;
  port socket : DeviceSocketPort {
    attribute :>> voltage = Device::voltage;
    attribute :>> ratedCurrent = Device::ratedCurrent;
  }
}
port def DeviceWaterInletPort {
  in item water : Water;
}
part def Dishwasher :> Device {
  port waterInlet : DeviceWaterInletPort;
}
interface def Socket2OutletInterface {
  end port socket : DeviceSocketPort;
  end port outlet : PowerOutletPort;
}
interface def WaterInlet2SpigotInterface {
  end port waterInlet : DeviceWaterInletPort;
  end port waterSpigot : WaterSpigotPort;
}
item def Person {
  attribute name : String;
}
calc def HeatLoss {
  //  Applying Newton's law of cooling: Q = h * A * ΔT  
  in A  : Real;   // exterior wall area [m²]
  in ambientTemp : Real;  // outside temperature [°C]
  in targetTemp : Real;  // desired interior temperature [°C]
  in h  : Real default = 1.5;  // heat transfer coefficient [W/(m²·K)]
  attribute deltaT : Real = targetTemp - ambientTemp;
  return : Real = h * A * deltaT; // resulting heat loss in Watts
}
requirement def HeatingSufficiency {
  subject room : Room;
  attribute ambientTemp : Real;  // outside temperature [°C]
  attribute targetTemp  : Real;  // desired int. temp. [°C]
  doc /* The heat loss of a room shall not exceed the
         power of its heater. */
  require constraint {
    room.heater.power > HeatLoss( room.areaOfExtWalls,
                                  ambientTemp, targetTemp)
  }
}
part def Home {
  item peoplePresentInHome[*]: Person; 
  part electricGrid : ElectricGridUS;
  part livingRoom : Room {
    attribute :>> outlet :> electricGrid.outlets;
    part heater: Device {
      attribute :>> ratedCurrent = 20 [A];
      attribute :>> power = 2000.0; // [W]
    }
    interface heater2PowerOutlet[1] : Socket2OutletInterface 
      connect heater.socket to outlet;
    satisfy heatSuff5;
  }
  part bedRoom : Room {attribute :>> outlet :> electricGrid.outlets;}
  part kitchen : Kitchen {
    attribute :>> outlet :> electricGrid.outlets;
    part dishwasher : Dishwasher {attribute :>> ratedCurrent = 12 [A];}
    interface dishwasher2PowerOutlet[1] : Socket2OutletInterface 
      connect dishwasher.socket to outlet;
    interface dishwasher2WaterSpigot[1] : WaterInlet2SpigotInterface 
      connect dishwasher.waterInlet to waterSpigot;
  }
  part bathRoom : Room {attribute :>> outlet :> electricGrid.outlets;}

  connection livingRoom2bedRoom[1] : Door
connect livingRoom to bedRoom; connection livingRoom2kitchen[1] : Door connect livingRoom to kitchen; connection livingRoom2bathRoom[1] : Door
connect livingRoom to bathRoom; requirement heatSuff5: HeatingSufficiency { attribute :>> ambientTemp = Home::ambientTemp; attribute :>> targetTemp = Home::targetTemp; assume constraint { ambientTemp > 5 } } }

Notice that due to a semantic constraint in the SysML v2 spec, port properties are allowed to have only referential (non-composite) properties, therefore the assert constraint declaration for the property ElectricGrid.outlets must be prefixed with the keyword "ref" for being able to parse this model. However, in the future, this overly restrictive semantic constraint may be relaxed.