First Example

This guide provides a practical introduction to SysIDE Automator through a hands-on example.

You’ll learn how to load and validate SysML v2 models, extract and analyze model elements, and modify models programmatically.

Getting Started

Let’s begin by creating a simple SysML v2 model that we’ll use throughout this example. This model represents a basic automobile structure with electrical and mechanical components.

Create a new file named example_model.sysml and paste the following model:

package 'Part Tree Example' {
    part def Electrical;
    part def Mechanical;

    part Automobile {
      part 'Drive Train' {
          part Battery : Electrical;
          part Motor : Electrical;
      }

      part Chassis {
          part Suspension : Mechanical;
          part Body : Mechanical;
      }
    }
}

Model Validation

Before working with the model, it’s important to validate it. To validate your model, open a terminal in the directory containing your model and run:

python -m syside check example_model.sysml

This command will check for any semantic errors or warnings in your model. After running, you should see the following output:

Checks passed!

Note

More about interactive SysIDE Automator usage can be found in the Interactive Version section.

Basic Model Analysis

Let’s create a Python script to analyze our model. We’ll start by creating a simple script that prints all elements in a tree-like structure.

Create a new file right next to the example_model.sysml file named analyze_model.py with the following code:

import syside

# Load the model - this is the first step for any SysIDE Automator script
(model, diagnostics) = syside.load_model(["example_model.sysml"])


def walk_ownership_tree(element: syside.Element, level: int = 0) -> None:
    """Recursively print all elements in the model."""
    if element.name is not None:
        print("  " * level, element.name)
    else:
        print("  " * level, "anonymous element")
    # Recursive walk through child elements
    for child in element.owned_elements.collect():
        walk_ownership_tree(child, level + 1)


# Process each document in the model
for document_resource in model.documents:
    with document_resource.lock() as document:
        print("Walking the ownership tree printing all elements:")
        walk_ownership_tree(document.root_node)

Run the script by running the following command in the terminal:

python analyze_model.py

When you run this script, you’ll see the following output:

Walking the ownership tree printing all elements:
anonymous element
  Part Tree Example
    Electrical
    Mechanical
    Automobile
      Drive Train
        Battery
        Motor
      Chassis
        Suspension
        Body

Working with Part Types

Our model defines two types of parts: Electrical and Mechanical. Let’s enhance our script to identify and display parts by their type.

Add the following function to your script right after the walk_ownership_tree function:

def show_parts_of_type(model: syside.Model, part_type: str) -> None:
    """Display all parts of a specific type in the model."""
    for part in model.nodes(syside.PartUsage):
        for element in part.heritage.elements:
            if element.try_cast(syside.PartDefinition):
                if element.declared_name == part_type:
                    print("- ", part.name)


print("\nElectrical parts in the model:")
show_parts_of_type(model, "Electrical")

print("\nMechanical parts in the model:")
show_parts_of_type(model, "Mechanical")

This will output:

Electrical parts in the model:
-  Battery
-  Motor

Mechanical parts in the model:
-  Suspension
-  Body

Enhancing the Model

Let’s make our model more realistic by adding a mass requirement. We’ll modify the model to include mass attributes for each part and add a requirement that the total mass must not exceed 500 kg.

Update your example_model.sysml file with this enhanced version:

package 'Part Tree Example' {
    private import ScalarValues;
    part def Electrical {
        attribute Mass;
    }
    part def Mechanical {
        attribute Mass;
    }

    part Automobile {
        part 'Drive Train' {
            part Battery : Electrical {
                attribute redefines Mass = 150;
            }
            part Motor : Electrical {
                attribute redefines Mass = 200;
            }
            attribute DriveTrainMass = Battery.Mass + Motor.Mass;
        }
        part Chassis {
            part Suspension : Mechanical {
                attribute redefines Mass = 100;
            }
            part Body : Mechanical {
                attribute redefines Mass = 150;
            }
            attribute ChassisMass = Suspension.Mass + Body.Mass;
        }
        attribute TotalMass = 'Drive Train'.DriveTrainMass + 'Chassis'.ChassisMass;
    }

    requirement def MassLimitation {
        doc /* Total mass of the Automobile must not
            exceed 500 */
        attribute MassActual = Automobile.TotalMass;
        attribute MassLimit = 500;
    }
}

Validating Requirements

Now let’s create a script to validate our mass requirement. Add the following code to your Python script:

def show_part_decomposition(
    element: syside.Element, part_level: int = 0
) -> None:
    """Display a clean part decomposition tree."""
    if element.try_cast(syside.PartUsage):
        print("  " * part_level, element.name)
        new_part_level = part_level + 1
    else:
        new_part_level = part_level
    for child in element.owned_elements.collect():
        show_part_decomposition(child, new_part_level)


# Find total mass and mass requirement:
for attribute in model.nodes(syside.AttributeUsage):
    if attribute.name == "MassActual":
        assert attribute.feature_value_expression is not None
        evaluation = syside.Compiler().evaluate(
            attribute.feature_value_expression
        )
        if evaluation[1].fatal:
            print(f"Error evaluating {attribute.name}")
        else:
            total_mass = evaluation[0]
    if attribute.name == "MassLimit":
        assert attribute.feature_value_expression is not None
        evaluation = syside.Compiler().evaluate(
            attribute.feature_value_expression
        )
        if evaluation[1].fatal:
            print(f"Error evaluating {attribute.name}")
        else:
            mass_limit = evaluation[0]

# Display results
print("\nPart decomposition:")
for doc in model.user_docs:
    with doc.lock() as locked:
        show_part_decomposition(locked.root_node)

print(f"\nTotal mass: {total_mass} kg")
if isinstance(total_mass, (int, float)) and isinstance(
    mass_limit, (int, float)
):
    if total_mass <= mass_limit:
        print("✓ Mass requirement met")
    else:
        print("✗ Mass requirement not met")
else:
    print("✗ Cannot compare mass values - invalid types")

When you run this script, you’ll see:

Part decomposition:
Automobile
  Drive Train
    Battery
    Motor
  Chassis
    Suspension
    Body

Total mass: 600 kg
✗ Mass requirement not met

Summary

In this example, we’ve learned how to:

  1. Create and validate a SysML v2 model

  2. Load and analyze models using SysIDE Automator

  3. Extract and display model elements

  4. Work with part types and hierarchies

  5. Add attributes and requirements to models

  6. Validate requirements programmatically

The example demonstrates that our automobile design exceeds the mass requirement by 100 kg. To meet the requirement, you would need to reduce the mass of some components or redesign the system.

Next Steps

  • Try modifying the mass values to meet the requirement

  • Check out the Examples Collection section for more Automator applications