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 .
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.
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.
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.
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.
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.
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})