A usage written directly in a package has no featuring type. The spec is explicit about what that means, SysML v2 7.6.1 “Definition and Usage Overview”:
A usage can also be contained directly in an owning package. In this case, the usage element is considered to be an implicit feature of the most general kernel type Anything. That is, a package-level usage is essentially a generic feature that can be applied in any context, or further specialized in specific contexts.
Anything is the top of the type hierarchy in the Kernel Semantic Library: abstract classifier Anything, which every other type specializes.
So this
package 'My System' {
part def Car;
part myCar : Car;
}
does not say “there is one car in my system”. It says that anything at all, of any kind, may have zero or more Cars. That is nearly vacuous and probably not what you intend.
You can see the difference in Syside:
PartUsage myCar | featuring=[] // at package level
PartUsage myCar | featuring=['Garage'] // inside part def Garage
The multiplicity changes too, which is the part that catches people. The language description, SysML v2 7.6.3 “Usages”, says:
If no tighter constraint is inherited, the effective default is the most general multiplicity [0..*]. However, a tighter default of [1..1] is implicitly declared for the usage if all of the following conditions hold:
- The usage is an attribute usage, an item usage (including a part usage, except if it is a connection usage), or a port usage.
- The usage is owned by a definition or another usage (not a package).
- The usage does not have any explicit owned subsettings or owned redefinitions.
Pay attention to Condition 2; at package level part myCar : Car; is [0..*]. Move the same line inside a definition and it is [1..1].
Give the usage an owner and both problems go away:
package 'My System' {
part def Car;
part def SystemContext {
part myCar : Car;
}
}
Now myCar is featured by SystemContext and has multiplicity [1..1]: exactly one Car per SystemContext.
This is not only a beginner trap. The same mistake reached the standard library, and Vince Molnar raised it against the spec in 2023. From KERML-56, on universalClock:
the Clocks library shows it as a package-level feature, ie, it has no featuring type … enabling everything in the universe (instances of Anything) to identify its own universal clock.
And KERML-57, which is still deferred:
Package-level features do not give featuring types, and some have lower multiplicity greater than zero, meaning everything in the universe (instances of Anything), including every data value, is required to give at least that number of values to them
When package level is right
It is legal, and sometimes correct. The standard library uses it in three places.
Units, in SI.sysml:
attribute <m> metre : LengthUnit;
attribute <kg> kilogram : MassUnit;
Base features meant to be subsetted from a context, in Parts.sysml:
abstract part parts : Part[0..*] nonunique :> items;
And baseType targets for semantic metadata, SysML v2 7.27.3:
action def UserAction;
action userActions : UserAction[*] nonunique;
metadata def CommandMetadata :> SemanticMetadata {
:>> baseType = userActions meta SysML::Usage;
}
The pattern in each case is the same: the usage is deliberately generic, and the [0..*] Anything-domained reading is the one you want. If you are writing part myCar : Car; you almost certainly want the opposite.
This has come up a few times before, most substantially in Mechanism to structure Model and SysML Files.
Adam Layne, PhD