# Plotting and visualization

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

!!! tip

    See the [Basic usage](./basic.md) guide for instructions to obtain a [`Graph`] object from your
    ESL specification.

## Matrix plots

### Multi-Domain Matrix

To create a Multi-Domain Matrix (MDM) plot of an ESL specification one can use the
[`raesl.plot.mdm`][raesl.plot.mdm] method. This method is a wrapper around the
[`ragraph.plot.mdm`][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`][raesl.plot.mdm] method:

```python
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"
```

`component-function-variable MDM` shows the resulting figure matrix view for the pump example. More
information on how to read and interpret this figure can be found on the [CASE
Guide](https://guide.ratio-case.nl).

<figure class="align-center">
<img src="../generated/cfv_mdm.svg" id="component-function-variable MDM"
alt="Component-function-variable MDM showing functional dependencies." />
<figcaption>Component-function-variable MDM showing functional dependencies.</figcaption>
</figure>

## Diagrams

RaESL offers a selection of diagrams that are made using Graphviz. The diagrams extract the required
data from the dependency [`Graph`][ragraph.graph.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`][ragraph.graph.Graph]'s hierarchy, or even add some temporary ones to achieve the most intuitive
diagram results.

In all cases, your original [`Graph`][ragraph.graph.Graph] object will be left intact and you will
be looking at a diagram of a [`GraphView`][ragraph.graph.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`][ragraph.node.Node]'s) for which the hierarchy is
expanded and shown up to a given maximum depth.

```python
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 `pump hierarchy diagram`.

<figure class="align-center">
<img src="../generated/hierarchy_diagram.svg" id="pump hierarchy diagram"
alt="A hierarchical decomposition diagram of the pump example." />
<figcaption>A hierarchical decomposition diagram of the pump example.</figcaption>
</figure>

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](#styling) or [`raesl.plot.Style`][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`][raesl.plot.functional_dependency_diagram] method:

```python
import raesl.plot

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

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 `pump
functional dependency diagram`.

<figure class="align-center">
<img src="../generated/functional_dependency_diagram.svg"
id="pump functional dependency diagram"
alt="A functional dependency diagram of the pump example." />
<figcaption>A functional dependency diagram of the pump example.</figcaption>
</figure>

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](#styling) or [`raesl.plot.Style`][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`][raesl.plot.functional_context_diagram] method:

```python
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,  # e.g. "./functional_context_diagram"
    cleanup=True,
    format="svg",
)
```

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 `pump functional context diagram`.

<figure class="align-center">
<img src="/_static/generated/functional_context_diagram.svg" id="pump functional context diagram"
alt="A functional context diagram of the pump example." />
<figcaption>A functional context diagram of the pump example.</figcaption>
</figure>

Which shows as similar overview as the [Functional dependency
diagram](#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](#styling) or [`raesl.plot.Style`][raesl.plot.Style].

### Function chain diagram

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

```python
from ragraph.datasets import esl

import raesl.plot

graph = esl.get("pump")
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,  # e.g. "./funcion_chain_diagram"
    cleanup=True,
    format="svg",
)
```

which results in `pump function chain diagram`.

<figure class="align-center">
<img src="../generated/function_chain_diagram.svg" id="pump function chain diagram"
alt="A function chain diagram of the pump example." />
<figcaption>A function chain diagram of the pump example.</figcaption>
</figure>

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.

```python
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,  # e.g. "./functional_traceability_diagram"
    cleanup=True,
    format="svg",
)
```

which results in `pump functional traceability diagram`.

<figure class="align-center">
<img src="../generated/functional_traceability_diagram.svg"
id="pump functional traceability diagram"
alt="A function traceability diagram of the pump example." />
<figcaption>A function traceability diagram of the pump example.</figcaption>
</figure>

## Styling

All style options are bundled in the [`raesl.plot.Style`][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`][raesl.plot.Style] object using:

```python
import ragraph.plot

import raesl.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:

```python
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`][raesl.plot.Style] object. These options are also described in the reference of
[`raesl.plot.generic.DiagramStyle`][`raesl.plot.generic.DiagramStyle`] and can be supplied in the
following manner:

```python
import raesl.plot

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