I was recently discussing different ways to visualize the content of a Simulink model with colleagues. Working in technical support, I receive models from users every day and it's always useful to quickly get a feeling of "what's in that model?".
At some point during the discussion, my colleague Reid Spence said "This would probably be useful to visualize the number of blocks in different subsystems using something like a Sankey diagram". I thought that idea had potential, so I decided to give it a try.
Chart Examples
When I need a fancy type of visualization that does not ship with MATLAB, I usually begin my search with MATLAB File Exchange. In this case, my search led me to Chart Examples by my colleague Ken Deeley. Based on the image on File Exchange, I think this will do what I need! Looking at the examples included, I could quickly see that:
- The function SankeyChart takes a digraph as input
- digraph can take a table as input
- The table needs two variables: EndNodes can be a two-column string array, and Weight, for the weight of each node
Here is a simple example:
EndNodes = ["a","b";"a","c";"b","d";"b","e"];
Weight = [5 2 3 1]';
T = table(EndNodes,Weight);
D = digraph(T);
f = uifigure;
SC = SankeyChart( "Parent", f, "GraphData", D );
Warning: The given graph is unbalanced.
In some sense, it's kind of like a fully expanded Model Browser, with the additional possibility to give weights to each subsystem, corresponding to the number fo blocks they contain.
From Model to Table
The first and most important thing I had to do is create the table. I came up with this simple recursive function:
function T = mdlTable(T,blk)
opts = Simulink.FindOptions;
opts.FollowLinks = true;
opts.SearchDepth = 1;
% Find the subsystems at this level
blks = Simulink.findBlocksOfType(blk,'SubSystem',opts);
% blockpath of the "source" in Sankey
SrcName = string(getfullname(get_param(blk,'handle')));
for i = 1:length(blks)
% blockpath of the "destination" in Sankey
DestName = string(getfullname(blks(i)));
% Number of blocks inside this subsystem
opts.SearchDepth = -1;
Nchilds = max(length(Simulink.findBlocks(blks(i),opts)),1);
% Fill the table
if isempty(T)
T = table([SrcName,DestName], Nchilds,'VariableNames',["EndNodes","Weight"]);
else
T(end+1,:) = {[SrcName,DestName], Nchilds}; %#ok<AGROW>
end
% Run the same on found subsystems
T = mdlTable(T,blks(i));
end
It's probably not the most efficient implementation, especially due to the call to Simulink.findBlocks in the loop, but that will do the job for now.
openExample('simulink_aerospace/AirTrafficControlRadarDesignExample','workDir',pwd)
open_system('aero_atc');
Just looking at the top level, can you guess if there are more blocks inside Radar, Aircraft, or Weather?
T = mdlTable(T,'aero_atc')
T = 10×2 table
| Weight |
---|
1 | 2 |
---|
1 | "aero_atc" | "aero_atc/Aircraft" | 10 |
---|
2 | "aero_atc" | "aero_atc/More Info" | 1 |
---|
3 | "aero_atc" | "aero_atc/Radar" | 94 |
---|
4 | "aero_atc/Radar" | "aero_atc/Radar/Calculate↵SNR" | 7 |
---|
5 | "aero_atc/Radar" | "aero_atc/Radar/Calculate Antenna Area" | 18 |
---|
6 | "aero_atc/Radar" | "aero_atc/Radar/Calculate Antenna Gain" | 7 |
---|
7 | "aero_atc/Radar" | "aero_atc/Radar/Calculate Noise of Antenna" | 12 |
---|
8 | "aero_atc/Radar" | "aero_atc/Radar/Check SNR" | 9 |
---|
9 | "aero_atc/Radar" | "aero_atc/Radar/Estimate Range" | 11 |
---|
10 | "aero_atc" | "aero_atc/Weather" | 17 |
---|
From Table to Sankey
Once the table is created, I can create the digraph and the Sankey diagram:
DG = digraph(T);
f = uifigure;
SC = SankeyChart( "Parent", f, "GraphData", DG );
SC.LabelIncludeTotal = "on";
The last line fixBlkNames is a simple function I created to replace the blockpaths with the block names. Using the full blockpath easily becomes large, cluttering the diagram.
Larger Models
I tried with a few larger models. This made me realize that many small subsystems with just a few blocks are often cluttering the diagram. So I decided to include a threshold and filter out those small subsystems. Here is my final function:
Other Tools
While we are on that topic, here is a list of tools included with Simulink to help capturing what is inside a model:
- Model Maintainability Metrics: Included with Simulink Check, this tool helps you assess your model and other design artifacts with respect to many metrics like size, architecture, and complexity
- Dependency Analyzer: An interactive tool for visualizing the files needed and impacted by a Simulink model.
- sldiagnostics: Has a CountBlocks option to return the number of blocks for each BlockType in a model
- find_mdlrefs: Returns the list of model blocks in a model, and the models they reference
- Simulink.findBlocks and Simulink.findBlocksOfType: If you need to create a custom tool like I did in this post, that's probably the core API you will need to analyze what is in your model.
Now it's your turn
The code presented here is obviously in a rough shape, I only spent an hour putting that together. If you think this is useful and worth polishing, or have suggestions for improvements, let me know in the comments below. If you try my code with your model and run into issues, also let me know in the comments below.
评论
要发表评论,请点击 此处 登录到您的 MathWorks 帐户或创建一个新帐户。