Sinelabore Homepage

Calculations#

Role in SysML v2#

A calculation definition (calc def) is a kind of action definition that produces a single value, its result. It takes in parameters, and its return parameter declares the result’s type and the expression that computes it. An action describes steps that happen over time; a calculation describes a formula. That makes it the place for a formula that several parts, actions or transitions share.

calc def Acceleration {
    in p : PowerValue;
    in m : MassValue;
    in v : SpeedValue;
    return : Real = p / (m * v);
}

The result may also be given a name: return a : Real = p / (m * v);. It may equally be written as the last expression of the body, which is the spec’s other spelling for the same thing:

calc def Acceleration {
    in p : PowerValue;
    in m : MassValue;
    in v : SpeedValue;
    return : Real;
    p / (m * v)
}

A calculation may name intermediate values, and call another calculation:

calc def Area {
    in w : Real;
    in h : Real;
    attribute scale : Real = 2.0;       // a local of the generated function
    return : Real = w * h * scale;
}

Mapping to code#

A calc def becomes a free function in the namespace of its package: an inline function in C++ (-l cppx), a module-level def in Python (-l python). The in parameters become the function’s parameters, in the order written, and the return expression becomes its body. Parameter and result types map as they do for attributes: Integer becomes int, Real becomes double in C++ and float in Python, and Boolean becomes bool.

Because the result is an ordinary function, you call a calculation by name, with its arguments in parentheses and in parameter order: Twice(steps). No instance is needed, and the order the calculations are written in does not matter — one may call another that comes later in the file.

A calc def written inside a part def belongs to that part: it becomes a static member function in C++ and a @staticmethod in Python. Everything the part owns can call it — its attribute values, its actions and its state machine.

A calculation usage applies a calculation to values bound at the usage, and computes its result when it is performed:

action run {
    calc twice : Scale {
        in x = 21;
    }
    first start;
    then twice;                      // performing it computes the result
    then assign scaled := twice.result;
    then done;
}

A usage that names no definition states its own result instead — calc thrice { in v = 14; return : Integer; v * 3 }.

A part may declare a usage too, beside its attributes and actions. Performing it computes the result the part then holds:

part def Runner {
    calc onPart : Scale {
        in x = 10;
    }
}

Example#

SysML v2 source code:

package HeaterControl {
    private import ScalarValues::*;

    calc def Twice {
        in x : Integer;
        return : Integer = x * 2;
    }

    calc def InRange {
        in v : Real;
        in lo : Real;
        in hi : Real;
        return inside : Boolean = v >= lo and v <= hi;
    }

    part def Heater {
        attribute temp : Real default 21.5;
        attribute steps : Integer default 3;
        attribute comfortable : Boolean := InRange(temp, 19.0, 23.0);

        action boost {
            first start;
            then assign steps := Twice(steps);
            then done;
        }
    }
}

C++ source code (excerpt):

inline bool InRange(double v, double lo, double hi) { return v >= lo and v <= hi; }
inline int Twice(int x) { return x * 2; }

// in Heater::init()
comfortable = InRange(temp, 19.0, 23.0);

// in the boost action
steps = Twice(steps);

Python source code (excerpt):

def Twice(x: int) -> int:
    return x * 2

def InRange(v: float, lo: float, hi: float) -> bool:
    return v >= lo and v <= hi

# in Heater.init()
self.comfortable = InRange(self.temp, 19.0, 23.0)

# in the boost action
self._part.steps = Twice(self._part.steps)

An attribute whose value calls a calculation (:= or =) is computed once, in the generated init(). It is not re-evaluated when temp changes later. To recompute it, assign it in an action or on a transition.

Where a calculation can be called#

Called from C++ Python
The value of an attribute (=, :=) Yes Yes
assign in an action Yes Yes
A state machine: guard, entry action, transition effect Yes Yes
Another calculation Yes Yes

In a state machine a calculation reads like any other expression:

state Cold { entry assign steps := Twice(steps); }
transition first Cold if Twice(steps) > 30 then Hot;

Not supported yet#

  • A result type that is not declared. SysML lets the result type be inferred from the expression; the generator does not infer it, because C++ needs the type at the declaration. Say what the calculation computes:

    calc def Sum { in a : Real; in b : Real; a + b }               // W3171
    calc def Sum { in a : Real; in b : Real; return : Real; a + b } // write this instead
  • A visibility keyword before the result expression. MemberPrefix on a ResultExpressionMember (private x * x) is not accepted.

See also Actions and the Missing Features list.