Advent of SysML v2 | Lesson 16 – Value Rollups and Advanced Calculations

Hello and welcome back to the Advent of SysML v2 – a daily, hands-on mini-course designed to bring you up to speed with the new SysML v2 standard and the modern Syside tooling that makes you productive from day one.

By the end of this lesson, you will:

  • Understand value rollups and collection-based calculations
  • Build chained calculations with automatic dependency resolution
  • Create conditional calculations that adapt to system state
  • See how model-defined calculations enable system-level analysis

If you want to dive deeper into the world of SysML v2, check out The SysML v2 Book, where this topic is also discussed in more detail.

From Individual Values to System Analysis

In Lesson 15, we learned to evaluate individual attributes: Rudolph’s weight, his nose color, and his energy level. We understood the evaluation scope and how default values get overridden in specializations. This gave us the ability to inspect our models programmatically.

However, systems engineering isn’t just about individual components. It’s about understanding how those components work together as a system: how much total power does Santa’s sleigh team produce? What’s the load distribution? Is the system operating within safety margins?

Today, we move from evaluating individual values to system-level analysis. The key insight – you can define these calculations directly in your SysML model, and Automator will evaluate them automatically, resolving all dependencies.

Let’s see how this works.

The Model

We’ll extend our Santa’s sleigh model with calculated attributes that analyze the complete system. Here’s the structure:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package L16_Advanced_Evaluation {
    private import NumericalFunctions::*;
    private import SequenceFunctions::*;
    private import SI::*;
    private import USCustomaryUnits::*;

    attribute maxSafeLoad subsets ISQ::mass = 375 [kg];

    part def SantaSleigh {
        constant attribute sleighWeight subsets ISQ::mass := 850 [kg];

        abstract ref part reindeer : Reindeer [9] = (
            dasher, dancer, prancer, vixen, comet,
            cupid, donner, blitzen, rudolph
        );

        ref part dasher : Reindeer subsets reindeer {
            constant attribute :>> weight := 180 [kg];
            constant attribute :>> power := 5940 [W];
        }
        ref part dancer : Reindeer subsets reindeer {
            constant attribute :>> weight := 175 [kg];
            constant attribute :>> power := 7 [hp];
        }
        ref part prancer : Reindeer subsets reindeer {
            constant attribute :>> weight := 185 [kg];
            constant attribute :>> power := 5550 [W];
        }
        ref part vixen : Reindeer subsets reindeer {
            constant attribute :>> weight := 160 [kg];
            constant attribute :>> power := 4.8 [kW];
        }
        ref part comet : Reindeer subsets reindeer {
            constant attribute :>> weight := 195 [kg];
            constant attribute :>> power := 6142 [W];
        }
        ref part cupid : Reindeer subsets reindeer {
            constant attribute :>> weight := 190 [kg];
            constant attribute :>> power := 5.7 [kW];
        }
        ref part donner : Reindeer subsets reindeer {
            constant attribute :>> weight := 230 [kg];
            constant attribute :>> power := 7935 [W];
        }
        ref part blitzen : Reindeer subsets reindeer {
            constant attribute :>> weight := 225 [kg];
            constant attribute :>> power := 7762 [W];
        }
        ref part rudolph : Reindeer subsets reindeer {
            attribute :>> energyLevel := 0.2 [kW];
            constant attribute :>> weight := 100 [kg];
            constant attribute :>> power := 4950 [W];
        }

        // Example 1: Collection size and basic aggregation
        attribute reindeerCount : Natural = size(reindeer);
        attribute totalPower subsets ISQ::power := sum(reindeer.power);
        attribute averagePower subsets ISQ::power := totalPower / reindeerCount;

        // Example 2: Chained calculations for load analysis
        attribute totalWeight subsets ISQ::mass := sleighWeight + sum(reindeer.weight);
        attribute loadPerReindeer subsets ISQ::mass := totalWeight / reindeerCount;
        attribute safetyMargin subsets ISQ::mass := maxSafeLoad loadPerReindeer;

        // Example 3: Conditional calculations
        attribute flightMode : FlightMode := if totalWeight > 2500 [kg] ? FlightMode::HeavyHaul else FlightMode::AgileMode;
        attribute maxSpeed = if flightMode == FlightMode::AgileMode ? 140.4 [‘km/h’] else 70.2 [km/h];
    }

    enum def FlightMode { AgileMode; HeavyHaul; }

    part def Reindeer {
        attribute energyLevel subsets ISQ::power default 100.0 [W];
        constant attribute weight subsets ISQ::mass default 110 [kg];
        constant attribute power subsets ISQ::power default 5 [kW];
    }
}

Notice we’ve defined three sets of calculations. Let’s explore each one.

Value Rollups and Collection Size

The first calculation example shows how to work with collections dynamically:

56
57
58
attribute reindeerCount : Natural = size(reindeer);
attribute totalPower subsets ISQ::power := sum(reindeer.power);
attribute averagePower subsets ISQ::power := totalPower / reindeerCount;

reindeerCount = size(reindeer) uses the SequenceFunctions::size function to count how many reindeer are in the team. Using size() instead of hardcoding 9 means the calculation works regardless of team size. This is good engineering practice: avoid magic numbers, let the model adapt to changes.

totalPower = sum(reindeer.power) aggregates the power values across all nine reindeer. Each reindeer has different power values specified in different units: Dancer at 7 hp (horsepower), Vixen at 4.8 kW, Rudolph at 4950 W, and so on. Automator automatically evaluates each reindeer’s power, handles all the unit conversions (hp to W, kW to W), and sums them to get the total system power. You don’t need to write a loop or manually convert units. You define what you want calculated, and Automator figures out how to calculate it.

averagePower = totalPower / reindeerCount depends on two other calculated values. Automator resolves these dependencies automatically, evaluating reindeerCount (9), then totalPower (54,000 W), then averagePower (6000 W).

Chained Calculations: Building Complex Metrics

The second calculation example builds a three-level dependency chain for load analysis:

61
62
63
attribute totalWeight subsets ISQ::mass := sleighWeight + sum(reindeer.weight);
attribute loadPerReindeer subsets ISQ::mass := totalWeight / reindeerCount;
attribute safetyMargin subsets ISQ::mass := maxSafeLoad loadPerReindeer;

At the first level, totalWeight combines a constant (the sleigh itself weighs 850 kg) with an aggregated value (sum of all reindeer weights). Each reindeer has a different weight: Rudolph at 100 kg (lightest), most reindeer between 160-195 kg, and Donner and Blitzen at 230 kg and 225 kg (heaviest). The total reindeer weight is 1640 kg, so adding the sleigh gives us 2490 kg.

At the second level, loadPerReindeer depends on both totalWeight and reindeerCount. This tells us how much weight each reindeer pulls: 2490 kg / 9 = 276.67 kg.

At the third level, safetyMargin depends on loadPerReindeer and maxSafeLoad (a constant defined at package level: 375 kg). The safety margin is 375 kg – 276.67 kg = 98.33 kg, showing how much additional load each reindeer could handle before reaching their safe limit.

When we evaluate safetyMargin, Automator automatically resolves the entire dependency graph. It evaluates loadPerReindeer, which requires totalWeight, which requires sum(reindeer.weight), which requires each reindeer’s weight, plus reindeerCount. You don’t manage this dependency chain. Automator does.

Conditional Calculations: State-Dependent Values

The third calculation example shows conditional logic where values change based on system state:

66
67
attribute flightMode : FlightMode := if totalWeight > 2500 [kg] ? FlightMode::HeavyHaul else FlightMode::AgileMode;
attribute maxSpeed = if flightMode == FlightMode::AgileMode ? 140.4 [‘km/h’] else 70.2 [km/h];

flightMode depends on totalWeight (a calculated value from our previous example). If total weight exceeds 2500 kg, the sleigh switches to HeavyHaul mode; otherwise, it operates in AgileMode. Since our total weight is 2490 kg (under the threshold), the sleigh operates in AgileMode.

maxSpeed then depends on flightMode. In AgileMode, max speed is 140.4 km/h; in HeavyHaul mode, it drops to 70.2 km/h for safety. Since we’re in AgileMode, max speed is 140.4 km/h (which equals 39 m/s).

Notice the unit syntax: 140.4 ['km/h'] uses a string literal for the compound unit. SysML v2 supports two forms: string form, like ['km/h'] (units as a string literal), or calculated form, like [km/h] (units composed from base units km and h, both defined in the SI library). Both are valid.

Putting It All Together

Let’s see the complete output from our evaluation script:

1
2
3
4
5
6
7
8
9
10
11
12
=======================================================
Total reindeer pulling the sleigh: 9
Total power: 54.00 kW
Average power per reindeer: 6.00 kW
=======================================================
Total weight of SantaSleigh system: 2490.0 kg
Total load pulled per reindeer: 276.67 kg
Remaining safety margin: 98.33 kg
=======================================================
SantaSleigh is operating in: AgileMode mode
Max speed: 39.00 m/s
=======================================================

We’ve accomplished a complete system-level analysis from model-defined calculations. The script simply evaluates attributes without any loops, aggregation logic, or conditionals in our code. All the complexity lives in the model itself, making it the single source of truth for both structure and analysis.

More importantly, this analysis is alive. Change the model (add more presents, swap out a reindeer, adjust the safety threshold) and the entire analysis updates automatically. If the total weight crosses 2500 kg, the system switches to HeavyHaul mode and reduces the maximum speed for safety. The calculations adapt because they’re defined relationally, not procedurally. You can evaluate them during initial design (“Can this configuration work?“), after modifications (“How did that change affect safety margins?“), or for trade studies (“What if we use lighter reindeer?“).

This same pattern applies to real engineering scenarios. Thermal analysis can chain component power through total heat and cooling requirements to safety margins. Structural analysis can flow from component masses through total loads and connection stresses to structural margins. Resource analysis can aggregate usage across subsystems to track capacity. The model becomes an analytical engine that evolves with your design.

Challenge

Now it’s your turn to practice model-based calculations:

  • Extend the calculations in the model. Experiment with different collection functions, create multi-level dependency chains where one calculation feeds into another, and add new attributes that build on the existing ones.
  • Experiment with conditional logic. Define thresholds based on calculated values and create modes or states that switch based on these conditions, or have other attributes that depend on these modes, to observe how changes propagate through the model.
  • Play with units and conversions. Try expressing the same quantity in different unit systems: combine metric and imperial, convert between power units, or express speeds in different formats. Observe how Automator handles the conversions automatically and where you need to be explicit about units.

Think about how these patterns apply to real engineering scenarios: mass budgets, power budgets, thermal analysis, or any domain where attributes cascade through multiple levels of calculation.

Summary

In this lesson, you learned how to move from evaluating individual attributes to performing system-level analysis. By defining calculations directly in your SysML model (e.g., using collection functions like size() and sum(), building chained dependencies, and adding conditional logic, etc.), you transform your model into an analytical engine. Automator handles the complexity: automatic dependency resolution, unit conversions, and recursive evaluation across collections. The key insight is that calculations become part of the model itself, evolving with your design and serving as the single source of truth for both structure and analysis.

If you haven’t yet done so, head over to Syside Cloud to complete today’s challenge, and we’ll see you for the next lesson!

Cookies