Extract parts

This simple Python script uses Syside to show:

  • the ownership tree of all elements in the model

  • part decomposition that are used in the model

  • all parts that are typed by the Electrical part definition

  • all parts that are typed by the Mechanical part definition

Concepts used

  • load_model is the main function for loading the model. In the example, the parameter paths is used to specify the path to the SysML v2 file. This parameter accepts a list of paths. The list may also hold (url, source) tuples to load SysML v2 or KerML text from memory, the language following the extension of url.

  • Syside supports multithreaded access to the model. Therefore, the model load_model returns holds its documents locked, so that no other thread modifies them while the script reads them.

  • A try_cast(<...>) is used here to assert that extracted element is of desired type.

  • A Model is a SysML v2 model represented using abstract syntax. This is the output of the function load_model(<...>).

  • A root_node is an entry node in the tree. It can be considered as root Namespace. The Namespace is an Element that contains other Elements, that are known as members.

Example 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;
    }
  }
}

Example script

import pathlib

import syside

EXAMPLE_DIR = pathlib.Path(__file__).parent
MODEL_FILE_PATH = EXAMPLE_DIR / "example_model.sysml"

def walk_ownership_tree(element: syside.Element, level: int = 0) -> None:
    """
    Prints out all elements in a model in a tree-like format, where child
    elements appear indented under their parent elements. For example:

    Parent
      Child1
      Child2
        Grandchild

    Args:
        element: The model element to start printing from
        level: How many levels to indent (increases for nested elements)
    """
    if element.name is not None:
        print("  " * level, element.name)
    else:
        print("  " * level, "anonymous element")
    # Recursively call walk_ownership_tree() for each owned element
    # (child element).
    element.owned_elements.for_each(
        lambda owned_element: walk_ownership_tree(owned_element, level + 1)
    )

def show_part_decomposition(
    element: syside.Element, part_level: int = 0
) -> None:
    """
    Prints out a hierarchical view of parts in a model, with indentation
    showing parent-child relationships. The function calls itself repeatedly
    to handle nested parts at deeper levels.

    For example, if a car has an engine and wheels, it would print:
    Car
      Engine
      Wheels

    Args:
        element: The model element to start printing from
        part_level: How many levels of indentation to use (increases for
        nested parts)
    """
    if element.try_cast(syside.PartUsage):  # Check if element is a part usage
        print("  " * part_level, element.name)
        new_part_level = part_level + 1
    else:
        new_part_level = part_level
    # Recursively call show_part_decomposition() for each owned element
    # (child element).
    element.owned_elements.for_each(
        lambda owned_element: show_part_decomposition(
            owned_element, new_part_level
        )
    )

def show_parts_of_type(model: syside.Model, part_type: str) -> None:
    for part in model.nodes(syside.PartUsage):
        for element in part.heritage.elements:
            if (
                element.try_cast(syside.PartDefinition)
                and element.declared_name == part_type
            ):
                print("- ", part.name)

def main() -> None:
    # Raises if Syside reports errors, or with this flag warnings too.
    # Syside may also report informational messages, but not for this
    # example.
    model = syside.load_model([MODEL_FILE_PATH], warnings_as_errors=True)

    print("\nWalk the ownership tree printing all elements.")
    # The model holds its documents locked, so that no other thread
    # modifies them while we are accessing them.
    for doc in model.documents:
        walk_ownership_tree(doc.root_node)

    print("\nShow part decomposition.")
    for doc in model.documents:
        show_part_decomposition(doc.root_node)

    print("\nShow all electrical parts.")
    show_parts_of_type(model, "Electrical")

    print("\nShow all mechanical parts.")
    show_parts_of_type(model, "Mechanical")

if __name__ == "__main__":
    main()

Output

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

Show part decomposition.
 Automobile
   Drive Train
     Battery
     Motor
   Chassis
     Suspension
     Body

Show all electrical parts.
-  Battery
-  Motor

Show all mechanical parts.
-  Suspension
-  Body

Download

Download this example as extract_parts.zip.