During the process of model development, it is of general interest (and is also very much encouraged) to reuse existing models. This is particularly useful and effective owing to the extreme modular design of NEML2, i.e., model composition.
However, from times to times, the composed model may become overwhelmingly complicated even for an experienced developer. For example, given a single-crystal crystal plasticity model with decoupled time integration defined in an input file input.i with the following content
It is undoubtedly difficult to understanding the model composition just by reading the input file. To address such challenge, the visualization module is provided as part of the NEML2 Python package to help extract, plot, and customize the dependency graph of a composed model. The generated dependency graph provides a visual understanding of the model composition, helps identify otherwise-difficult-to-find issues, and opens up opportunities for optimizing of the composition.
Rendering non-composed model
NEML2 relies on the Python interface of Graphviz to render the dependency graph. To visualize a basic non-composed model, simply load the model from the input file and pass it to the neml2.visualization.render function.
Model & load_model(const std::filesystem::path &path, const std::string &mname, bool enable_ad)
A convenient function to load an input file and get a model.
Definition Factory.cxx:69
The render function calls graphviz.render behind the scenes, and additional arguments and keyword arguments are forwarded without modification. By default, an intermediate graphviz source file (written in the DOT language) with suffix .gz and the rendered output specified by outfile are generated. Customization of the graph will be discussed later. NEML2 provides some sensible default settings which generates the graph below.
Graph of a basic model
There are three components in the above image:
The input variable represented as a gray rectangular node
The model represented as a blue rectangular node
The output variable represented as a green rectangular node
Additional information are also labeled on each node enclosed within square brackets, such as the variable type (e.g. [Rot], [R2]) and the model type (e.g. [RotationMatrix]). Note that the rounded dashed border around the variables denote sub-axes, and the sub-axis name is also labeled. Edges (arrows) in this directed graph represent information flow, i.e., the input variable is passed into the model, the model performs a set of operations and produces the output variable.
Rendering composed model
The dependency graph of a composed model can also be visualized using a similar routine,
Note the model name is changed to "implicit_rate_1" which is a composed model. The generated graph is shown below.
Graph of a composed model
Each shaded area represents a sub-model in the composed model. When graph constraints allow, the input variables of the composed model are shown at the very top, and the output variables of the composed model are shown at the very bottom.
Note
The svg and pdf output formats allow arbitrary zooming without loss of resolution.
For Visual Studio Code users, the Graphviz Interactive Preview extension can be used to directly preview the graphviz source code. The extension supports useful postprocessing operations such as node highlighting and filtering. For example, the following screenshot demonstrates upstream highlight of an intermediate output variable state/internal/sum_slip_rates.
Graph of a composed model with upstream highlighting
Customizing graph style
The generated graph is highly customizable. All controllable attributes are available in the data class Configuration. The configuration can be modified with an instance of the data class, i.e.