.. _plot: ########################## Plotting and visualization ########################## .. testsetup:: >>> graph = pump_example_graph >>> cfv_path = str(outdir / "cfv_mdm.svg") >>> hierarchy_path = str(outdir / "hierarchy_diagram") >>> functional_dependency_path = str(outdir / "functional_dependency_diagram") >>> functional_context_path = str(outdir / "functional_context_diagram") >>> function_chain_path = str(outdir / "function_chain_diagram") >>> functional_traceability_path = str(outdir / "functional_traceability_diagram") This page describes how to plot and visualize (parts of) a specification using the :obj:`~ragraph.graph.Graph` object returned by the ESL compiler. The :obj:`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 :obj:`raesl.plot.mdm` method. This method is a wrapper around the :obj:`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 :obj:`raesl.plot.mdm` method: .. doctest:: >>> 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" :numref:`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 within the `ESL user manual `_ . .. figure:: /_static/generated/cfv_mdm.svg :alt: Component-function-variable MDM showing functional dependencies. :name: component-function-variable MDM :align: center 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 :obj:`~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 :obj:`~ragraph.graph.Graph`'s hierarchy, or even add some temporary ones to achieve the most intuitive diagram results. In all cases, your original :obj:`~ragraph.graph.Graph` object will be left intact and you will be looking at a diagram of a :obj:`~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 (:obj:`~ragraph.node.Node`'s) for which the hierarchy is expanded and shown up to a given maximum depth. .. doctest:: >>> 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 :numref:`pump hierarchy diagram`. .. figure:: /_static/generated/hierarchy_diagram.svg :alt: A hierarchical decomposition diagram of the pump example. :name: pump hierarchy diagram :align: center 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 :obj:`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 :obj:`raesl.plot.functional_dependency_diagram` method: .. doctest:: >>> 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 :numref:`pump functional dependency diagram`. .. figure:: /_static/generated/functional_dependency_diagram.svg :alt: A functional dependency diagram of the pump example. :name: pump functional dependency diagram :align: center 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 :obj:`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 :obj:`raesl.plot.functional_context_diagram` method: .. doctest:: >>> 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 :numref:`pump functional context diagram`. .. figure:: /_static/generated/functional_context_diagram.svg :alt: A functional context diagram of the pump example. :name: pump functional context diagram :align: center 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 :obj:`raesl.plot.Style`. ++++++++++++++++++++++ Function chain diagram ++++++++++++++++++++++ To visualize one or more specific function chains one can use the :obj:`raesl.plot.function_chain_diagram` method as shown in the following. .. doctest:: >>> 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 :numref:`pump function chain diagram`. .. figure:: /_static/generated/function_chain_diagram.svg :alt: A function chain diagram of the pump example. :name: pump function chain diagram :align: center 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 :math:`i + 1`contribute to fulfilling functions specified at level :math:`i`. One can create a function traceability diagram with the :obj:`raesl.plot.function_traceability_diagram` method as shown in the following listing. .. doctest:: >>> 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 :numref:`pump functional traceability diagram`. .. figure:: /_static/generated/functional_traceability_diagram.svg :alt: A function traceability diagram of the pump example. :name: pump functional traceability diagram :align: center A function traceability diagram of the pump example. ******* Styling ******* All style options are bundled in the :obj:`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 :obj:`raesl.plot.Style` object using: .. doctest:: >>> 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: .. doctest:: >>> 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 :obj:`raesl.plot.Style` object. These options are also described in the reference of :obj:`raesl.plot.generic.DiagramStyle` and can be supplied in the following manner: .. doctest:: >>> import raesl.plot >>> style = raesl.plot.Style(diagram={"show_hierarchy": True})