Every recipe in this chapter runs against one model,
examples/cookbook.sysml, and every output shown
is what the sysml binary printed. The model is a small observatory:
package Cookbook {
private import DocumentQueries::*;
private import KerML::Root::Element;
private import ScalarValues::*;
part def Subsystem {
attribute mass : Real;
}
part def OpticalSubsystem :> Subsystem;
part def MirrorAssembly :> OpticalSubsystem {
attribute :>> mass = 10.0;
}
metadata def Critical;
port def OpticalPort;
port def DataPort;
part telescope {
part primaryMirror : MirrorAssembly {
@Critical;
port opticalOut : OpticalPort;
}
part instrumentCluster : Subsystem {
attribute redefines mass = 4.5;
port opticalIn : OpticalPort;
port dataOut : DataPort;
}
part mountControl : Subsystem {
attribute redefines mass = 15.0;
port dataIn : DataPort;
}
connection opticalPath connect primaryMirror.opticalOut to instrumentCluster.opticalIn;
connection dataPath connect instrumentCluster.dataOut to mountControl.dataIn;
}
part def Computer;
part scienceComputer : Computer;
allocation processing allocate telescope.instrumentCluster to scienceComputer;
requirement massRequirement;
part observatory {
satisfy massRequirement by telescope;
}
verification def MassTest;
verification massVerification : MassTest {
objective {
verify massRequirement;
}
}
// ... the recipe queries below ...
}
Each recipe is a calc def specializing DocumentQueries::Query declared in
the same package. Run one with:
$ sysml docs/manual/examples/cookbook.sysml -run-query "<name> [<parameter>=<expression> ...]"A binding whose expression is a name binds the element it denotes; anything else is evaluated as an expression (strings in quotes, numbers as literals).
calc def MassTable :> Query {
in root : Element; // entry parameters, bound by the caller
Project( // operations compose inside-out
source = PartsByMass(root = root), // ... and queries invoke queries
properties = ("name", "mass", "qualifiedName")
)
}
- A query is a
calc defspecializingDocumentQueries::Query. - Its
inparameters are the entry bindings a caller supplies — an element, a string, a number, a boolean, or a sequence of them. - Its body is one expression composing the library operations;
sourcearguments chain them, innermost first. A name in an argument reads the query's parameter of that name, or binds the model element it refers to (OwnedElements(source = telescope)starts from that part), just as a%run-querybinding does. The element is checked against the parameter's type when the query is planned. - A query can invoke another query by name, with its own bindings. Invocation is dependency-ordered and cycle-checked, with depth and count budgets.
Results are ordered element sequences. Order is the model's declaration order
until an OrderBy says otherwise, and elements are deduplicated by identity,
so a query is deterministic by construction.
An in parameter may declare a default, and a caller that leaves it unbound
gets that default — from %run-query, -run-query, RunDocumentQuery, a
document's content block, or another query's invocation alike:
calc def HeavySubsystems :> Query {
in root : Element = telescope; // a name binds the element it refers to
in threshold : String default "10"; // anything else is evaluated
WhereFeature(
source = Descendants(source = root, maxDepth = 3),
'feature' = "mass", operator = ">=", value = threshold
)
}
calc def LightSubsystems :> HeavySubsystems {
in redefines threshold default "5"; // a redefining default wins
}
- A default follows the binding rule of
%run-query <p>=<expr>: a default that names a model element binds that element; any other default is an expression. The rule applies wherever a value is expected, soin roots : Element[0..*] = (telescope, groundStation);binds both elements, a list may mix element names with parameters and query invocations, andin candidates : Element[0..*] = OwnedElements(source = telescope);starts the traversal from that part. - An expression default is evaluated once per query execution, before any row is
produced, in the scope of the query that declared it — it may name that
query's other parameters (
in candidates : Element[0..*] = OwnedElements(source = root);) or invoke another query, within the usual visit and invocation budgets. Defaults are filled in parameter order after the explicit bindings, so a default may read a parameter bound explicitly or defaulted before it; one that reads a later, still unbound parameter fails as a missing binding. - Defaults are inherited:
LightSubsystemskeepsroot = telescopefromHeavySubsystems, and its ownthresholddefault replaces the inherited one. The nearest default along the redefinition chain wins. - A default is checked against the parameter's type and multiplicity exactly
like an explicit binding, and an explicit binding always overrides the
default. What the default's text already settles is refused when the query is
planned, as
document-query-default-typeordocument-query-default-multiplicitynaming the parameter: a literal or named element of the wrong type, and a list or invocation whose size cannot fit (in source : Element = (telescope, groundStation);). A named element is never a data value —= labelis refused for aStringparameter even whenlabelis aStringattribute — except an enumeration literal, which is a value of its enumeration:in hue : Color = Color::red;binds the literal, and a literal of another enumeration is refused. What only the values decide — a parameter reference whose multiplicity is not known statically — is checked when the default is evaluated, with the failures an explicit binding gets. - A default the plan cannot represent (a form the query expression language has
no operation for) is a planning error naming the parameter, reported with the
other
document-query-*diagnostics rather than at execution time.
calc def Children :> Query {
in root : Element;
OwnedElements(source = root)
}
$ sysml cookbook.sysml -run-query "Cookbook::Children root=Cookbook::telescope"
✓ Query Cookbook::Children returned 5 rows
Row 1: Cookbook::telescope::primaryMirror
Row 2: Cookbook::telescope::instrumentCluster
Row 3: Cookbook::telescope::mountControl
Row 4: Cookbook::telescope::opticalPath
Row 5: Cookbook::telescope::dataPathEverything the element owns is returned — here the three parts and the two connections, in declaration order. Filter afterwards to narrow.
calc def AllParts :> Query {
in root : Element;
WhereType(
source = Descendants(source = root, maxDepth = 10),
type = "PartUsage"
)
}
$ sysml cookbook.sysml -run-query "Cookbook::AllParts root=Cookbook::telescope"
✓ Query Cookbook::AllParts returned 5 rows
Row 1: Cookbook::telescope::primaryMirror
Row 2: Cookbook::telescope::instrumentCluster
Row 3: Cookbook::telescope::mountControl
Row 4: Cookbook::telescope::opticalPath
Row 5: Cookbook::telescope::dataPathmaxDepth bounds the walk; each level is visited in declaration order.
Note that the connections are still here: a connection usage is a
PartUsage in the SysML metamodel (its metaclass conforms to it). Use
a feature or name filter, or type = "ConnectionUsage", to separate them —
see Type filters.
calc def Enclosing :> Query {
in leaf : Element;
Ancestors(source = leaf, maxDepth = 2)
}
$ sysml cookbook.sysml -run-query "Cookbook::Enclosing leaf=Cookbook::telescope::primaryMirror::opticalOut"
✓ Query Cookbook::Enclosing returned 2 rows
Row 1: Cookbook::telescope::primaryMirror
Row 2: Cookbook::telescopeOwners are returned nearest-first, up to maxDepth levels.
WhereType keeps elements whose metamodel type matches — "PartUsage",
"ConnectionUsage", "RequirementUsage", "AttributeUsage", "PortUsage",
"PartDefinition" and so on — including metaclass conformance, so
type = "Usage" keeps every kind of usage. A name that is neither a known
metamodel type nor resolvable in the model is a typed
unknown-classification error rather than a silently-empty result.
calc def Connections :> Query {
in root : Element;
WhereType(
source = Descendants(source = root, maxDepth = 10),
type = "ConnectionUsage"
)
}
$ sysml cookbook.sysml -run-query "Cookbook::Connections root=Cookbook::telescope"
✓ Query Cookbook::Connections returned 2 rows
Row 1: Cookbook::telescope::opticalPath
Row 2: Cookbook::telescope::dataPathTo select by a model-defined classification — "every part typed by
Subsystem" — filter on what distinguishes those elements instead: a
metadata annotation (below) or a characteristic
attribute (property filters).
WhereMetadata keeps elements annotated with a metadata definition, matching
specializations of it too. The model marks primaryMirror with
@Critical:
calc def CriticalParts :> Query {
in root : Element;
WhereMetadata(
source = AllParts(root = root),
'metadata' = "Cookbook::Critical"
)
}
$ sysml cookbook.sysml -run-query "Cookbook::CriticalParts root=Cookbook::telescope"
✓ Query Cookbook::CriticalParts returned 1 row
Row 1: Cookbook::telescope::primaryMirror('metadata' is quoted because metadata is a SysML keyword.)
WhereName compares each element's effective name against a value:
calc def MirrorParts :> Query {
in root : Element;
WhereName(
source = AllParts(root = root),
operator = "contains",
value = "Mirror"
)
}
$ sysml cookbook.sysml -run-query "Cookbook::MirrorParts root=Cookbook::telescope"
✓ Query Cookbook::MirrorParts returned 1 row
Row 1: Cookbook::telescope::primaryMirrorText operators: =/==, !=/<>, contains, startsWith, endsWith
(also spelled starts-with/ends-with), and matches with a regular
expression.
WhereFeature compares an attribute's constant value. The comparison is
typed: numbers compare numerically (<, <=, >, >= and equality, with
* accepted as infinity), booleans by equality, strings with the text
operators above. An element without the attribute simply does not match; a
property no element in the source has is a typed unknown-property error.
calc def HeavyParts :> Query {
in root : Element;
in threshold : String;
WhereFeature(
source = AllParts(root = root),
'feature' = "mass",
operator = ">=",
value = threshold
)
}
$ sysml cookbook.sysml -run-query "Cookbook::HeavyParts root=Cookbook::telescope threshold=\"10\""
✓ Query Cookbook::HeavyParts returned 2 rows
Row 1: Cookbook::telescope::primaryMirror
Row 2: Cookbook::telescope::mountControlTwo details worth noting: value is always written as a string and parsed by
the operator's type, and primaryMirror matches through its definition —
MirrorAssembly fixes mass = 10.0, and the usage inherits it.
The built-in properties of the projection table are features
too, so feature = "shortName" with startsWith selects the requirements
whose identifier shares a prefix, and feature = "documentation" with
contains selects the elements whose doc text mentions a word — any one of
an element's several bodies matching is enough.
An attribute declared with a unit — attribute :>> mass = 2290000 [kg]; — is
a quantity: a magnitude carried with its unit, never a bare number. The
filter's value is a bare number, and it compares against the magnitude in
the attribute's own unit: mass >= "1000000" matches 2290000 [kg], and
would match 1500000 [g] too, because the threshold is read in each element's
unit. Choose the threshold for the unit your model declares, or normalize the
unit in the model. A value carrying a unit of its own ("1000 [kg]") is
not a number and is refused as a typed invalid-argument error.
calc def HeavyStages :> Query {
in root : Element;
WhereFeature(
source = WhereType(source = Descendants(source = root, maxDepth = 1), type = "PartUsage"),
'feature' = "mass",
operator = ">=",
value = "1000000"
)
}
$ sysml units.sysml -run-query "UnitsRepro::HeavyStages root=UnitsRepro::rocket"
✓ Query UnitsRepro::HeavyStages returned 1 row
Row 1: UnitsRepro::rocket::s1The attribute compared need not be a literal: a mass declared as dryMass + propellantMass is evaluated for each element before the comparison, as
derived values describes. Filtering and sorting then follow
the same commensurability rules as literal quantities.
OrderBy sorts by a property with every policy explicit — there are no
defaults to guess:
direction:"ascending"or"descending".missing: where elements without the property go —"first","last", or"error"to refuse them.multiple: which value to sort by when the property has several —"first","last", or"error".
The sort is stable, so equal keys keep their declaration order. Mixing
incomparable value types across elements is a typed invalid-order error.
Quantities sort by converted magnitude when their units are commensurable:
500000 [g] orders below 119000 [kg]. Two Integer magnitudes compare
exactly, so neighbours a Real cannot tell apart (9007199254740993 [kg]
against 9007199254740992 [kg]) keep their order. Quantities of different dimensions
(2290000 [kg] against 42 [m]) are not ordered — that is an
invalid-order error naming both units, never a silent comparison of the
bare magnitudes.
calc def PartsByMass :> Query {
in root : Element;
OrderBy(
source = AllParts(root = root),
property = "mass",
direction = "descending",
missing = "last",
multiple = "error"
)
}
$ sysml cookbook.sysml -run-query "Cookbook::PartsByMass root=Cookbook::telescope"
✓ Query Cookbook::PartsByMass returned 5 rows
Row 1: Cookbook::telescope::mountControl
Row 2: Cookbook::telescope::primaryMirror
Row 3: Cookbook::telescope::instrumentCluster
Row 4: Cookbook::telescope::opticalPath
Row 5: Cookbook::telescope::dataPathThe connections have no mass, so missing = "last" places them after the
sorted parts.
Project turns elements into rows of named, typed cells — what a document
table renders. Beyond the model's own attributes, these built-in properties
are always projectable:
| Property | Value |
|---|---|
name |
The effective name |
declaredName |
The declared name, absent when the name is derived |
shortName |
The effective short name (<'HLR-R001'>), absent when the element has none |
declaredShortName |
The declared short name, absent when the short name is derived |
documentation |
The body of each doc comment in declaration order, delimiters and indentation removed; absent when undocumented |
qualifiedName, @id |
The fully-qualified name |
owner |
The owner's qualified name |
@type |
The metamodel type (PartUsage, ...) |
type |
The declared type's qualified name |
isAbstract |
Boolean |
multiplicityLower, multiplicityUpper |
Integers, * as unbounded |
calc def MassTable :> Query {
in root : Element;
Project(
source = PartsByMass(root = root),
properties = ("name", "mass", "qualifiedName")
)
}
$ sysml cookbook.sysml -run-query "Cookbook::MassTable root=Cookbook::telescope"
✓ Query Cookbook::MassTable returned 5 rows
Columns: name, mass, qualifiedName
Row 1: Cookbook::telescope::mountControl
name = "mountControl"
mass = 15.0
qualifiedName = "Cookbook::telescope::mountControl"
Row 2: Cookbook::telescope::primaryMirror
name = "primaryMirror"
mass = 10.0
qualifiedName = "Cookbook::telescope::primaryMirror"
Row 3: Cookbook::telescope::instrumentCluster
name = "instrumentCluster"
mass = 4.5
qualifiedName = "Cookbook::telescope::instrumentCluster"
Row 4: Cookbook::telescope::opticalPath
name = "opticalPath"
mass = (none)
qualifiedName = "Cookbook::telescope::opticalPath"
Row 5: Cookbook::telescope::dataPath
name = "dataPath"
mass = (none)
qualifiedName = "Cookbook::telescope::dataPath"A cell for a property the element lacks is empty ((none) in the CLI's row
listing, an empty table cell in a document).
A quantity-valued attribute projects as the runtime prints it — magnitude,
then the unit in brackets — in the CLI row listing, a Markdown cell (brackets
escaped as \[kg\], so they read as text) and an HTML cell, whose
<span class="sysml-value" data-value-kind="quantity"> also carries
data-magnitude and data-unit apart. The unit is the one the model spelt
(kg, km/h), not a reduction to base units.
part def Stage {
attribute mass :> ISQ::mass;
}
part def FirstStage :> Stage {
attribute :>> mass = 2290000 [kg];
}
part def UpperStage :> Stage {
attribute :>> mass = 119000 [kg];
}
part rocket {
part s1 : FirstStage;
part s2 : UpperStage;
}
calc def Masses :> Query {
in root : Element;
Project(
source = WhereType(source = Descendants(source = root, maxDepth = 1), type = "PartUsage"),
properties = ("name", "mass")
)
}
$ sysml units.sysml -run-query "UnitsRepro::Masses root=UnitsRepro::rocket"
✓ Query UnitsRepro::Masses returned 2 rows
Columns: name, mass
Row 1: UnitsRepro::rocket::s1
name = "s1"
mass = 2290000 [kg]
Row 2: UnitsRepro::rocket::s2
name = "s2"
mass = 119000 [kg]A value that is a literal, a quantity, or an expression over those alone
(2 [kg] * 3 is 6 [kg], 1 [km] + 500 [m] is 1.5 [km]) is folded once,
statically, when the model is analysed. A value written over other features
— the common shape in a mass or power budget — is instead evaluated by the
runtime as seen from the row's element: each leaf is read through the
redefinition chain of that concrete carrier, so a type-level :>> and a
usage-level :>> both win over the definition's own value, a default applies
where nothing binds the feature, and a feature chain (s1.mass) reads the
owned part's value. Arithmetic, comparisons, conditionals and the library
functions the runtime provides (sum, size, indexing with #, ->collect)
all apply, with the runtime's rules: units are kept and converted, Integer
stays Integer, and a collection-valued attribute projects as one value per
element.
part def Stage {
attribute dryMass :> ISQ::mass;
attribute propellantMass :> ISQ::mass;
attribute mass :> ISQ::mass = dryMass + propellantMass;
}
part def FirstStage :> Stage {
attribute :>> dryMass default = 130000 [kg];
attribute :>> propellantMass = 2160000 [kg];
}
part def Vehicle {
part s1 : FirstStage;
part s2 : FirstStage {
attribute :>> dryMass = 120000 [kg];
}
attribute liftoffMass :> ISQ::mass = s1.mass + s2.mass;
}
part rocket : Vehicle;
$ sysml derived.sysml -run-query "DerivedRepro::Masses root=DerivedRepro::Vehicle"
✓ Query DerivedRepro::Masses returned 2 rows
Columns: name, dryMass, mass
Row 1: DerivedRepro::Vehicle::s1
name = "s1"
dryMass = 130000 [kg]
mass = 2290000 [kg]
Row 2: DerivedRepro::Vehicle::s2
name = "s2"
dryMass = 120000 [kg]
mass = 2280000 [kg]
$ sysml derived.sysml -run-query "DerivedRepro::Vehicles root=DerivedRepro"
✓ Query DerivedRepro::Vehicles returned 1 row
Columns: name, liftoffMass
Row 1: DerivedRepro::rocket
name = "rocket"
liftoffMass = 4570000 [kg]The query and the REPL agree: rocket.s1.mass prints = 2290000 [kg] too.
Note that s2's dryMass overrides a default =; a value written with a
plain = is fixed for every redefinition, and the analyser refuses the
override before any query runs.
What the runtime cannot turn into a value is reported, never guessed:
- A leaf unbound anywhere in the carrier's chain (an abstract
attribute mass :> ISQ::mass;that nothing ever binds) makes the derived value absent — an empty cell, as for a value-less feature — andWhereFeaturedoes not match it,OrderByplaces it by itsmissingpolicy. - A value that genuinely depends on the model running — an
inparameter of a calculation, an action's state, a non-constant function — or that the runtime rejects — a cycle (a = b; b = a;), operands of different dimensions (mass + length), a result no cell can hold such as a part (attribute heart = engine.core;) — is a typedunevaluable-featureerror naming the query, the property, the row element and the runtime's reason. A table never shows a wrong number or a silently empty cell for a value the model does declare, and a??default does not cover it — the feature is present, not absent.
The roll-up a library writes over a possibly empty collection evaluates as
written: sum over no quantities is the zero of the collection's declared
kind, in its coherent SI unit, so mass + sum(subcomponents.totalMass) on a
component whose subcomponents : MassedComponent [*] default null holds
nothing is mass (100 [kg] + 0 [kg]), and a default null collection holds
the parts that subset it (part b1 : Bolt :> subcomponents;, or with
subsets) — the default is only its value where nothing populates it — so
the sum rolls up through them recursively. The kind is the collection's
declared one, and survives a select, reject or collect that leaves
nothing: a collection typed Real[*], or an empty one mapped through an
untyped body parameter (->collect { in x; x * x }), still sums to the
number 0, and 10 [kg] + 0 [m] or 10 [kg] + 5 remain the
incommensurable units error above.
A value written with = holds for as long as the object does, not just on
the first read: it is derived from what the object holds now. When a run
assigns a feature the expression read (assign a := 9;), when a binding
propagates a new value into it, or when a default null collection is
superseded by a part that subsets it, the derived value is dropped and
derived again the next time it is read — through a part or a binding the
expression read through, and on through the values that read it. Nothing is
recomputed until something asks, and a value a run assigned is never
recomputed: assign d := 100; fixes d whatever a does afterwards, while
a dd = d + 1 beside it keeps following d. A probe or transaction that
wrote such a feature is rolled back with the values that read it.
The parameters of a calculation or action usage are the boundary. They are
bound once, when the invocation starts, and stay bound while its outputs are
read — an assignment to a feature an in named does not rebind it for a
later output read of the same invocation. Only the = value of an object's
own feature follows what it read.
A projection may also derive columns: each Column(name, expression) entry
appends a named column whose expression is evaluated once per row over the
row element's declared features. Arithmetic (+, -, *, /), string
concatenation with + and ?? defaults for absent values are supported:
calc def MassBudget :> Query {
in root : Element;
Project(
source = PartsByMass(root = root),
properties = ("name", "mass"),
columns = (
Column(name = "massLbs", expression = (Subsystem::mass ?? 0.0) * 2.2),
Column(name = "label", expression = "part: " + Element::name)
)
)
}
$ sysml cookbook.sysml -run-query "Cookbook::MassBudget root=Cookbook::telescope"
✓ Query Cookbook::MassBudget returned 5 rows
Columns: name, mass, massLbs, label
Row 1: Cookbook::telescope::mountControl
name = "mountControl"
mass = 15.0
massLbs = 33.0
label = "part: mountControl"
...Feature references name the declaring definition (Subsystem::mass,
Element::name); a row element that lacks the feature makes the expression
fail with a typed error naming the query, column and row — unless a ??
default covers it, which is why MassBudget's massLbs defaults to 0.0
for the two connections in its results. Computed names join the projection:
OrderBy can sort by them and a table's groupBy can group by them.
Every built-in property is reachable the same way — Element::shortName,
Element::declaredShortName and Element::documentation included — so
(Element::shortName ?? "—") + ": " + Element::name labels a row by its
identifier. A column is one value per row: an element carrying two doc
bodies fails a column over Element::documentation with a typed
column-cardinality error, where the plain "documentation" projection
above carries both.
Quantities take part in column arithmetic with the runtime's rules, so a
column keeps its unit: Stage::mass * 2 is 4580000 [kg], Stage::mass / 1000 is 2290 [kg], Stage::mass / Stage::length is 54523.8… [kg/m],
and a ratio of like quantities (Stage::length / Stage::length) is a bare
number. Adding or subtracting quantities converts the right operand into the
left operand's unit (1 [km] + 500 [m] is 1.5 [km]); operands of
different dimensions — Stage::mass + Stage::length, or a quantity plus a bare number — are a typed
column-incommensurable error naming the column, the row and both units.
MassTable above already shows it: PartsByMass(root = root) invokes the
other query with its own bindings, and AllParts invokes Children's
sibling the same way. Factoring collection into one base query and deriving
filtered/sorted/projected variants from it is the intended style. The engine
compiles the invocation graph up front: an unknown name, a cycle, or blowing
the depth/count budget is a typed error at that point.
RelatedElements walks one named relationship kind from each source element:
RelatedElements(
source = <elements>,
relationshipKind = "<kind>", // specialization, subsetting, redefinition,
// typing, connection, allocation,
// satisfaction or verification
direction = "<direction>", // outgoing or incoming
maxDepth = <n>
)
Direction is from the relationship's own point of view — outgoing follows
it as declared, incoming follows it backwards. Traversal is breadth-first
to maxDepth, deduplicated, in declaration order, and bounded by a visit
budget so a pathological model terminates with a typed error rather than
hanging.
Connection edges run port to port — traverse from the connector's endpoint, not from the part that owns it:
calc def ConnectedTo :> Query {
in origin : Element;
RelatedElements(
source = origin,
relationshipKind = "connection",
direction = "outgoing",
maxDepth = 1
)
}
$ sysml cookbook.sysml -run-query "Cookbook::ConnectedTo origin=Cookbook::telescope::primaryMirror::opticalOut"
✓ Query Cookbook::ConnectedTo returned 1 row
Row 1: Cookbook::telescope::instrumentCluster::opticalInoutgoing follows connect A to B from A's endpoint to B's; incoming
follows it the other way. Untyped connect clauses carry connection edges
too.
allocate X to Y is outgoing from X, incoming to Y:
calc def AllocatedTargets :> Query {
in origin : Element;
RelatedElements(
source = origin,
relationshipKind = "allocation",
direction = "outgoing",
maxDepth = 1
)
}
$ sysml cookbook.sysml -run-query "Cookbook::AllocatedTargets origin=Cookbook::telescope::instrumentCluster"
✓ Query Cookbook::AllocatedTargets returned 1 row
Row 1: Cookbook::scienceComputersatisfy R by P points from the satisfying element to the requirement, so
"who satisfies this requirement" is an incoming traversal from the
requirement:
calc def SatisfiedBy :> Query {
in req : Element;
RelatedElements(
source = req,
relationshipKind = "satisfaction",
direction = "incoming",
maxDepth = 1
)
}
$ sysml cookbook.sysml -run-query "Cookbook::SatisfiedBy req=Cookbook::massRequirement"
✓ Query Cookbook::SatisfiedBy returned 1 row
Row 1: Cookbook::telescopeLikewise, "which verifications cover this requirement" is incoming from the
requirement; the result is the verification usage whose objective verifys
it:
calc def VerifiedBy :> Query {
in req : Element;
RelatedElements(
source = req,
relationshipKind = "verification",
direction = "incoming",
maxDepth = 1
)
}
$ sysml cookbook.sysml -run-query "Cookbook::VerifiedBy req=Cookbook::massRequirement"
✓ Query Cookbook::VerifiedBy returned 1 row
Row 1: Cookbook::massVerificationspecialization, subsetting, redefinition and typing traverse the
declaration hierarchy. Incoming specialization from a general type finds what
specializes it, transitively to maxDepth:
calc def Specializers :> Query {
in general : Element;
RelatedElements(
source = general,
relationshipKind = "specialization",
direction = "incoming",
maxDepth = 2
)
}
$ sysml cookbook.sysml -run-query "Cookbook::Specializers general=Cookbook::Subsystem"
✓ Query Cookbook::Specializers returned 2 rows
Row 1: Cookbook::OpticalSubsystem
Row 2: Cookbook::MirrorAssemblyTraversal results are elements like any others — feed them into Project for
a traceability table, as the worked example does for its
requirement section.