Hello,
I am trying to re-create the mass rollup example from the Intro to the SysML v2 Language-Textual Notation, as shown in slides 72-75. This is what my implementation looks like, which should be functionally the same but with some irrelevant parts of the original example stripped away.
package MassRollup2 {
private import RealFunctions::sum;
private import ScalarValues::Real;
part def MassedThing {
attribute simpleMass : Real;
attribute totalMass : Real default simpleMass;
}
part compositeThing : MassedThing {
part subcomponents : MassedThing [*];
attribute redefines totalMass = simpleMass + sum(subcomponents.totalMass);
}
}
package CarMassRollupExample2 {
private import MassRollup2::*;
part def CarPart specializes MassedThing;
part car : CarPart subsets compositeThing {
part carParts : CarPart [*] redefines subcomponents;
part engine subsets carParts;
part transmission subsets carParts;
}
// Example usage
part c subsets car {
attribute redefines simpleMass = 1000;
part redefines engine {
attribute redefines simpleMass = 100;
}
part redefines transmission {
attribute redefines simpleMass = 50;
}
}
// c.totalMass --> 1150.0[kg]
}
The python code to evaluate this looks as follows:
model, diagnostics = syside.load_model(["example.sysml"])
c = get_node(model, ["CarMassRollupExample2", "c"])
simpleMass = get_namespace_member(c, "simpleMass")
totalMass = get_namespace_member(c, "totalMass")
print(simpleMass, get_attribute_value(simpleMass, c))
print(totalMass, get_attribute_value(totalMass, c)
When this is executed, the following is printed out:
CarMassRollupExample2::c::simpleMass 1000
MassRollup2::MassedThing::totalMass 1000
The totalMass value here is 1000 instead of the expected 1150. It seems as though the attribute redefinition in the compositeThing part usage does not have the expected effect, and the totalMass of c remains as its default value of simpleMass as written in the MassedThing part definition.
I can confirm this by changing the totalMass attribute redefinition in compositeThing so that it is only driven by the totalMass of the subcomponents and ignores the simpleMass of the compositeThing itself:
part compositeThing : MassedThing {
part subcomponents : MassedThing [*];
attribute redefines totalMass = sum(subcomponents.totalMass);
}
With this change, the printout is unchanged, even though the expected value is 150:
CarMassRollupExample2::c::simpleMass 1000
MassRollup2::MassedThing::totalMass 1000
Do you have any insight into what the issue is here? On the one hand it could be that something is going wrong at the subsetting of engine and transmission in the car part usage, such that engine and transmission don’t end up within the carParts collection? Or could it be that in part car : CarPart subsets compositeThing, car is not properly inheriting the attribute redefinition of totalMass from compositeThing?
Any guidance or tips for troubleshooting this is appreciated. Thanks!
For reference, these are the helper functions being used:
def get_node(model: syside.Model, qualified_name: Sequence[str]) -> syside.Element:
# Using the function provided in syside_helpers.py in the state machine example
def get_attribute_value(node: syside.AttributeUsage, scope=None):
if node.feature_value_expression is None:
return None
if scope is None:
result, _ = syside.Compiler().evaluate(node.feature_value_expression)
else:
result, _ = syside.Compiler().evaluate_feature(
feature=node.feature_value_expression, scope=scope
)
return result
def get_namespace_member(node: syside.Namespace | syside.Element, name: str):
if not isinstance(node, syside.Namespace):
raise TypeError
queue: list[syside.Namespace] = [node]
visited: set[syside.Namespace] = set()
while queue:
element = queue.pop(0)
if isinstance(element, syside.Feature):
element = element.feature_target
if element in visited:
continue
visited.add(element)
child = element.get_member(name)
if child is not None:
return child
if isinstance(element, syside.Type):
queue[0:0] = element.heritage.elements
return None