%% Determining Why Simulink Accelerator Is Regenerating Code
%
% Sometimes Simulink(R) regenerates the simulation target for a model at
% the beginning of a simulation in Accelerator Mode (TM) but it is not
% always clear why regeneration happens. This example shows how to use
% Simulink(R) MATLAB(R) commands to determine why Simulink regenerates code
% for Accelerator Mode simulations.
%    
% Simulink's Accelerator Mode speeds up simulation of your model by
% creating an executable version of the model, called a simulation target,
% and running this target instead of interpreting the model, as is done
% during Normal Mode simulation. Accelerator Mode creates the simulation
% target by generating C code from your model and invoking the MATLAB(R)
% mex function to compile and dynamically link the generated code to
% Simulink.
%
% The process of code generation and compilation happens during the first
% time that you simulate your model in Accelerator Mode. Additionally, code
% generation may happen on subsequent simulations, especially if the model
% changes between simulations (for example, after addition of a block).
% Code generation takes times and it is often desirable to avoid it, in
% order to maximize the number of simulations within a given time span.
%
% Simulink uses the model's checksum to determine if the code needs to be
% regenerated. This checksum is an array of four integers computed by using
% an md5 checksum algorithm based on attributes of the model and the blocks
% that it contains. Any change in the model that changes the checksum
% causes Simulink to regenerate the simulation target for Accelerator Mode.
% 
% Sometimes, it is not clear which model change triggered a checksum change
% and hence code regeneration. This example shows how to investigate why
% Simulink needs to regenerate code for Accelerator Mode simulation for a
% given model and its configuration.

% Copyright 2006-2015 The MathWorks, Inc.


%% Create a Temporary Working Directory
% Since simulating in Accelerator Mode creates some files, first move to a
% temporary area.

originalDir = pwd;
tempDir = tempname;
mkdir(tempDir)
cd(tempDir)

%% Open an Example Model 
% We will use a simple model, slAccelDemoWhyRebuild, throughout this
% example.
model = 'slAccelDemoWhyRebuild';
open_system(model)
set_param(model,'AccelVerboseBuild','on');
MessageId = message('RTW:makertw:enterAccelTarget',model);
%%
% The first time the model runs in Accelerator mode, it generates and compiles
% code as expected. 
simOutput = evalc(['sim(''',model,''')']);
if ~isempty(strfind(simOutput,MessageId.getString))
    disp('Built Simulink Accelerator mex file')
else 
    disp('Did not build Simulink Accelerator mex file')
end

%%
% If the simulation is run again, without any changes to the model, we
% expect that Simulink can reuse the existing code and does not need to 
% regenerate the code. Execute the same commands to verify.
simOutput = evalc(['sim(''',model,''')']);
if ~isempty(strfind(simOutput,MessageId.getString))
    disp('Built Simulink Accelerator mex file')
else 
    disp('Did not build Simulink Accelerator mex file')
end

%%
% Now we change some parameters in the model. We will set the following settings 
% for the block 'Integrator':
%   - set 'Ignore limit and reset when linearizing' to 'on'
%   - set 'Initial Condition' to '5'
set_param([model,'/Integrator'],'IgnoreLimit','on');
set_param([model,'/Integrator'],'InitialCondition','5');

%% 
% When we run the simulation again, we see that Simulink regenerates the
% code.
simOutput = evalc(['sim(''',model,''')']);
if ~isempty(strfind(simOutput,MessageId.getString))
    disp('Built Simulink Accelerator mex file')
else 
    disp('Did not build Simulink Accelerator mex file')
end
%%
% We would like to know why.
%
% To determine if the previously generated code is still valid for the
% current model configuration, Simulink compares the checksum of the model
% as used to generate the code to the current checksum. If they are equal,
% the previously generated code is still valid and Simulink Accelerator
% Mode reuses it for the current simulation. If the values differ, Simulink
% Accelerator Mode regenerates and rebuilds the code. Thus, examining the
% details of the checksum computation can reveal why Simulink regenerated
% the code.

%% Get the Checksum Details
% The following command gets the model checksum computation details:
[cs1,csdet1]=Simulink.BlockDiagram.getChecksum(model);

%%
% The first output is the model checksum value itself. 
% The second output gives details of what went into the checksum computation. 
%
% Let us set the modified block parameters to their original values and get the
% checksum and details for that configuration 
set_param([model,'/Integrator'],'IgnoreLimit','off');
set_param([model,'/Integrator'],'InitialCondition','0');
[cs2,csdet2]=Simulink.BlockDiagram.getChecksum(model);

%%
% Comparing these two checksum values is equivalent to determining if the 
% Simulink Accelerator will regenerate code. 
% Note that the checksum values are different, as we expect based on the 
% fact that Simulink Accelerator regenerates code every time it runs.
if (cs1 ~= cs2)
    disp('Checksums are different')
else
    disp('Checksums are the same')
end

%%
% Now that we know that the checksums differ, the next question is why. Many
% things go into the checksum computation, including signal data types, some
% block parameter values, and block connectivity information. To understand why
% the checksums differ, we need to see what has changed about the items used in
% computing the checksum.  The checksum details returned as the second argument
% give that information.
csdet1
%%
% The checksum details is a structure array with four fields. Two of the
% fields are the component checksums of the model checksum (these are
% called ContentsChecksum and InterfaceChecksum) and the other two are the
% corresponding checksum details. These details correspond to various
% information which went into to the computation of the two component
% checksums. The model [structural] checksum is a function of
% ContentsChecksum and InterfaceChecksum.
%
% First, let's determine if the difference lies in the model's contents or the 
% model's interface
if (csdet1.ContentsChecksum.Value ~= csdet2.ContentsChecksum.Value)
    disp('Contents checksums are different')
else
    disp('Contents checksums are the same')
end
if (csdet1.InterfaceChecksum.Value ~= csdet2.InterfaceChecksum.Value)
    disp('Interface checksums are different')
else
    disp('Interface checksums are the same')
end

%% Use the Details to Determine Why Checksum Changed
% Now that we know the change is in the ContentsChecksum, we can look at the
% ContentsChecksumItems to see what has changed.
idxForDifferences=[];
for idx = 1:length(csdet1.ContentsChecksumItems)
    if (~strcmp(csdet1.ContentsChecksumItems(idx).Handle, ...
                csdet2.ContentsChecksumItems(idx).Handle))
        idxForDifferences=[idxForDifferences,idx];
        disp(['Handles different for item ',num2str(idx)]);
    end
    if (~strcmp(csdet1.ContentsChecksumItems(idx).Identifier, ...
                csdet2.ContentsChecksumItems(idx).Identifier))
        disp(['Identifiers different for item ',num2str(idx)]);
        idxForDifferences=[idxForDifferences,idx];
    end
    if(ischar(csdet1.ContentsChecksumItems(idx).Value))
        if (~strcmp(csdet1.ContentsChecksumItems(idx).Value, ...
                    csdet2.ContentsChecksumItems(idx).Value))
            disp(['String Values different for item ',num2str(idx)]);
            idxForDifferences=[idxForDifferences,idx];
        end 
    end
    if(isnumeric(csdet1.ContentsChecksumItems(idx).Value))
        if (csdet1.ContentsChecksumItems(idx).Value ~= ...
            csdet2.ContentsChecksumItems(idx).Value)
            disp(['Numeric values are different for item ',num2str(idx)]);
            idxForDifferences=[idxForDifferences,idx];
        end
    end
end
%%
% Now that we know the differences are in the items at the indices listed in
% idxForDifferences, we can look at those items in the two ContentsChecksumItems
% arrays
blk1 = csdet1.ContentsChecksumItems(idxForDifferences(1)).Handle
blk2 = csdet2.ContentsChecksumItems(idxForDifferences(1)).Handle
id1 = csdet1.ContentsChecksumItems(idxForDifferences(1)).Identifier
id2 = csdet2.ContentsChecksumItems(idxForDifferences(1)).Identifier

%%
% The Handle for both items is 'slAccelDemoWhyRebuild/Integrator', which 
% indicates the block with the changing data. The identifier for both is
% 'IgnoreLimit' which tells us that this was the block setting which changed, 
% resulting in a different checksum for the model. 
% The setting for the Initial Condition of the block does not appear in the
% checksum detail. Therefore, we expect that if only the setting for the
% Initial Condition is modified, that no rebuild will occur.
 
%% Avoid a Rebuild on Successive Simulations
% Now that we found the parameter which causes the checksums to differ for
% this workflow, we can validate the findings by keeping that parameter
% constant and seeing whether rebuild occurs on successive simulations.

%%
% Let's simulate the model in Accelerator mode again. We expect it to rebuild 
% for this simulation because we changed the parameter ('IgnoreLimit') for
% the checksum computation above. 
simOutput = evalc(['sim(''',model,''')']);
if ~isempty(strfind(simOutput,MessageId.getString))
    disp('Built Simulink Accelerator mex file')
else 
    disp('Did not build Simulink Accelerator mex file')
end

%% 
% Now let's only change the Initial Condition setting and simulate again.
% We expect that no rebuild should happen this time.
set_param([model,'/Integrator'],'InitialCondition','-3');
simOutput = evalc(['sim(''',model,''')']);
if ~isempty(strfind(simOutput,MessageId.getString))
    disp('Built Simulink Accelerator mex file')
else 
    disp('Did not build Simulink Accelerator mex file')
end
%%
% As expected from the checksum analysis, changing the parameter for 'Initial
% Condition' does not cause a regeneration of code for simulation in Accelerator
% mode.

%% Cleaning Up
% Close the model and remove the generated files.
bdclose(model)
clear([model,'_acc'])
cd(originalDir)
rmdir(tempDir,'s')


displayEndOfDemoMessage(mfilename)
