Plotting and visualization#

This page describes how to plot and visualize (parts of) a specification using the Graph object returned by the ESL compiler. The raesl.plot module contains several built-in matrix and diagram plots.

Matrix plots#

Multi-Domain Matrix#

To create a Multi-Domain Matrix (MDM) plot of an ESL specification one can use the raesl.plot.mdm method. This method is a wrapper around the ragraph.plot.mdm plotting method and ensures that a consistent network of nodes and edges is shown. It exploits the specific graph structure that is generated from an ESL specification by the compiler and migrates edges through the hierarchy so you never miss a dependency.

The following snippet shows how to create a Multi-Domain Matrix view using the raesl.plot.mdm method:

>>> import raesl.plot
>>> figure = raesl.plot.mdm(
...     graph,
...     node_kinds=["component", "function_spec", "variable"],
...     edge_kinds=["functional_dependency", "mapping_dependency"],
...     depth=2,
... )
>>> figure.write_image(cfv_path)  # E.g. "./cfv_mdm.svg"

Figure 1 shows the resulting figure matrix view for the pump example. More information on how to read and interpret this figure can be found within the ESL user manual .

Component-function-variable MDM showing functional dependencies.

Figure 1 Component-function-variable MDM showing functional dependencies.#

Diagrams#

RaESL offers a selection of diagrams that are made using Graphviz. The diagrams extract the required data from the dependency Graph that is generated by the compiler. To provide the most useful views, the diagram methods help to migrate some of the edges through the Graph’s hierarchy, or even add some temporary ones to achieve the most intuitive diagram results.

In all cases, your original Graph object will be left intact and you will be looking at a diagram of a GraphView with tailor made settings for ESL.

Hierarchy diagram#

The hierarchical decomposition diagram can be used to display (a part of) the component hierarchy. You can supply several root components (Node’s) for which the hierarchy is expanded and shown up to a given maximum depth.

>>> import raesl.plot
>>> digraph = raesl.plot.hierarchy_diagram(
...     graph,
...     roots=[graph["world"]],
...     levels=4,
... )
>>> svg_path = digraph.render(filename=hierarchy_path, cleanup=True, format="svg")  # e.g. "./hierarchy_diagram"

which results in Figure 2.

A hierarchical decomposition diagram of the pump example.

Figure 2 A hierarchical decomposition diagram of the pump example.#

One or more sub-branches of the decomposition structure can be shown by providing a list of component nodes which serve as the roots of the hierarchical decomposition diagrams. The number of levels within the diagram can be set using the levels argument. For styling options, please refer to Styling or raesl.plot.Style.

Functional dependency diagram#

The functional dependency diagram of a certain component shows the goal- and transformation requirements of its sub-components in a compact overview and can be created using the raesl.plot.functional_dependency_diagram method:

>>> import raesl.plot
>>> digraph = raesl.plot.functional_dependency_diagram(
...     graph,
...     root=graph["world"],
...     levels=4,
... )
>>> svg_path = digraph.render(filename=functional_dependency_path, cleanup=True, format="svg")  # e.g. "./functional_dependency_diagram"

Here, the root and number of levels within the nested structure can be set by changing the root and levels arguments. The snippert above results in Figure 3.

A functional dependency diagram of the pump example.

Figure 3 A functional dependency diagram of the pump example.#

In a functional dependency diagram, the components are represented by the (nested) blocks, the goal requirements by the diamonds and the transformation requirements by the ellipsis. Behavior requirements are included using dashed lines. For styling options please refer to Styling or raesl.plot.Style.

Functional context diagram#

As your specification grows, the functional dependency diagram may become cluttered. The functional context diagram may be a suitable alternative when that happens. The context diagram enables you to explore your functional dependencies up to a certain degree (e.g. steps between components) instead of up to a certain depth (levels of sub-components).

It can be created using the raesl.plot.functional_context_diagram method:

>>> import raesl.plot
>>> digraph = raesl.plot.functional_context_diagram(
...     graph,
...     root=graph["world.drive-mechanism"],
...     degree=1,
... )
>>> svg_path = digraph.render(filename=functional_context_path, cleanup=True, format="svg")  # e.g. "./functional_context_diagram"

Here, the root argument denotes the component node that should be used as the center node of the context diagram. The degree argument denotes up to which degree neighbors of the root component should be added to the diagram. The snippet above results in Figure 4.

A functional context diagram of the pump example.

Figure 4 A functional context diagram of the pump example.#

Which shows as similar overview as the Functional dependency diagram, but instead of displaying the dependencies up to a certain depth, it shows them up to a certain degree instead.

In a functional context diagram, the components are represented by the (nested) blocks, the goal requirements by the diamonds and the transformation requirements by the ellipsis. Behavior requirements are included using dashed lines. For styling options please refer to Styling or raesl.plot.Style.

Function chain diagram#

To visualize one or more specific function chains one can use the raesl.plot.function_chain_diagram method as shown in the following.

>>> from ragraph.datasets import esl
>>> graph = esl.get("pump")
>>> import raesl.plot
>>> start_points = [
...     (
...         graph["world.drive-mechanism.power-source"],
...         [graph["world.drive-mechanism.power-source.convert-potential"]],
...     )
... ]
>>> end_points = [(graph["world.pump"], [graph["world.pump.convert-torque"]])]
>>> digraph = raesl.plot.function_chain_diagram(
...     graph,
...     start_points=start_points,
...     end_points=end_points,
...     levels=2,
... )
>>> svg_path = digraph.render(filename=function_chain_path, cleanup=True, format="svg")  # e.g. "./funcion_chain_diagram"

which results in Figure 5.

A function chain diagram of the pump example.

Figure 5 A function chain diagram of the pump example.#

The start_points and end_points arguments are to lists of tuples that denote the start points and end points of the to be drawn function chains. Start- and end-point tuples consist of a component node name and a list of function spec nodes of which the component node is the active component. All possible paths between the start- and end-points are drawn.

The levels argument indicate in how many levels to decompose the components nodes that are part of a function node between the start- and end-points. This number is relative to the maximum depth of the provided start- and end-point component nodes.

Function traceability diagram#

In systems engineering one often creates function traceability diagrams to see which functions specified at decomposition level \(i + 1`contribute to fulfilling functions specified at level :math:`i\). One can create a function traceability diagram with the raesl.plot.function_traceability_diagram method as shown in the following listing.

>>> import raesl.plot
>>> digraph = raesl.plot.function_traceability_diagram(
...     graph,
...     root=graph["world.drive-mechanism.convert-power-potential"],
...     levels=3,
... )
>>> svg_path = digraph.render(filename=functional_traceability_path, cleanup=True, format="svg")  # e.g. "./functional_traceability_diagram"

which results in Figure 6.

A function traceability diagram of the pump example.

Figure 6 A function traceability diagram of the pump example.#

Styling#

All style options are bundled in the raesl.plot.Style object which can be supplied to the style keyword argument of every plotting method.

Matrix styling#

Matrix plots are currently made using RaGraphs plot module under the hood. Hence, you can include those style changes in your RaESL raesl.plot.Style object using:

>>> import raesl.plot
>>> import ragraph.plot
>>> # Custom RaGraph style:
>>> ragraph_style = ragraph.plot.Style(piemap={"display": "labels", "mode": "relative"})
>>> style = raesl.plot.Style(ragraph=ragraph_style)
>>> # Generate some plot with it:
>>> figure = raesl.plot.mdm(graph, style=style)

But a nested dictionary (without the imported RaGraph module) works, too:

>>> import raesl.plot
>>> style = raesl.plot.Style(ragraph={"piemap": {"display": "labels", "mode": "relative"}})
>>> figure = raesl.plot.mdm(graph, style=style)

Diagram styling#

The allowed customizations for Graphviz plots are included under the diagram key of the raesl.plot.Style object. These options are also described in the reference of raesl.plot.generic.DiagramStyle and can be supplied in the following manner:

>>> import raesl.plot
>>> style = raesl.plot.Style(diagram={"show_hierarchy": True})