%% Rapid Accelerator Simulations Using Parsim
% This example shows the use of Rapid Accelerator in applications that
% require running parallel simulations for a range of input and parameter
% values.
%
% We use the engine idle speed model which simulates the idle speed of an 
% engine. The input of this model is the voltage of the bypass air valve 
% and the output is the idle speed.
%
% We run parallel simulations using |parsim| with two sets of valve
% voltages and by independently varying two of the three gain parameters of
% the transfer functions over a range of two values. The following table
% lists the eight simulations that will be run along with the parameter
% values. The external inputs, |inpSets|, are created in Step 2 below, and
% |gain2| and |gain3| are the variables corresponding to the two gain
% parameters.
%
% <html>
% <table border="1"><tr><th>Run</th><th>ExternalInput</th><th>gain2</th><th>gain3</th></tr>
% <tr><td>1</td><td>inpSets(1)</td><td>25</td><td>20</td></tr>
% <tr><td>2</td><td>inpSets(1)</td><td>25</td><td>30</td> </tr>
% <tr><td>3</td><td>inpSets(1)</td><td>35</td><td>20</td></tr>
% <tr><td>4</td><td>inpSets(1)</td><td>35</td><td>30</td></tr>
% <tr><td>5</td><td>inpSets(2)</td><td>25</td><td>20</td></tr>
% <tr><td>6</td><td>inpSets(2)</td><td>25</td><td>30</td></tr>
% <tr><td>7</td><td>inpSets(2)</td><td>35</td><td>20</td></tr>
% <tr><td>8</td><td>inpSets(2)</td><td>35</td><td>30</td></tr></table>
% </html>
%
% It is easy to customize this example for your own application by
% modifying the script file used to build this example. Click the link in
% the top left corner of this page to edit the script file. Click the link
% in the top right corner to run this example from MATLAB(R).

% Copyright 2005-2018 The MathWorks, Inc.


%% Step 1: Preparation
% First we open the model where the simulation mode has been set to Rapid
% Accelerator. The default input data, and the required parameters are
% preloaded in the base workspace.
%
% Open model:
mdl = 'sldemo_raccel_engine_idle_speed';
open_system(mdl);

%% Step 2: Create Input Sets
% Here we perturb the default input values vector to obtain a new
% input values vector.
%

inpSets(1) = timeseries(inpData, time);
rndPertb = 0.5 + rand(length(time), 1);
inpSets(2) = timeseries(inpData.*rndPertb, time);
numInpSets  = length(inpSets);

%% Step 3: Create Parameter Sets
% We want to see how the idle speed changes for different values of
% parameters |gain2| and |gain3|. Create an array of <matlab:doc('Simulink.SimulationInput')
% |Simulink.SimulationInput|> objects to specify the different parameter
% values and external input for each simulation. The array of
% SimulationInput objects is preallocated for better performance. Also note
% that the external input can be directly specified on the SimulationInput
% object instead of using a model parameter.
%

gain2_vals = 25:10:35;
gain3_vals = 20:10:30;

num_gain2_vals = length(gain2_vals);
num_gain3_vals = length(gain3_vals);

numSims = num_gain2_vals*num_gain3_vals*numInpSets;
in(1:numSims) = Simulink.SimulationInput(mdl);

idx = 1;
for iG2 = 1:num_gain2_vals
    for iG3 = 1:num_gain3_vals
        for inpSetsIdx = 1:numInpSets
            in(idx) = in(idx).setModelParameter('SimulationMode', 'rapid', ...
                'RapidAcceleratorUpToDateCheck', 'off', ...
                'SaveTime', 'on', ...
                'SaveOutput', 'on');
            % Use setVariable to specify a new value for a variable during
            % simulations
            in(idx) = in(idx).setVariable('gain2', gain2_vals(iG2));
            in(idx) = in(idx).setVariable('gain3', gain3_vals(iG3));
            in(idx).ExternalInput = inpSets(inpSetsIdx);
            idx = idx + 1;
        end
    end
end

%%
% Note that we also use the |setModelParameter| method of the
% |SimulationInput| object to set model parameters to run the simulations
% in Rapid Accelerator mode and enable logging. The Rapid Accelerator
% target is built using the <matlab:edit('sldemo_parallel_rapid_accel_sims_script_setup') |SetupFcn|>.
% The Rapid Accelerator target is build once and used by all subsequent
% simulations saving the time required for model compilation. Here is the
% code for the SetupFcn
%
%   function sldemo_parallel_rapid_accel_sims_script_setup(mdl)
%       % Temporarily change the current folder on the workers to an empty
%       % folder so that any existing slprj folder on the client does not
%       % interfere in the build process.
%       currentFolder = pwd;
%       tempDir = tempname;
%       mkdir(tempDir);
%       cd (tempDir);
%       oc = onCleanup(@() cd (currentFolder));
%       Simulink.BlockDiagram.buildRapidAcceleratorTarget(mdl);
%   end
%

%% Step 4: Execute Simulations
% Use the <matlab:doc('parsim') |parsim|> function to execute the
% simulations in parallel. The array of |SimulationInput| objects, |in|,
% created in the last step is passed into the |parsim| function as the
% first argument. Store the simulation output data in a variable, |out|,
% whose value is an array of |Simulink.SimulationOutput| objects. Each
% |SimulationOutput| object contains the logged signal along with the
% |SimulationMetadata|. When running multiple simulations using |parsim|,
% errors are captured so that subsequent simulations can continue to run.
% Any errors would show up in the |ErrorMessage| property of the
% |SimulationOutput| object.
out = parsim(in, 'ShowProgress', 'on', ...
    'SetupFcn', @() sldemo_parallel_rapid_accel_sims_script_setup(mdl));

%% Step 5: Plot Results
% Plot the engine idle speed with respect to time for different
% parameter values and inputs.
% The output is logged in the array format and can be accessed from the
% SimulationOutput object. 
for i = 1:numSims
    simOut = out(i);
    t = simOut.tout;
    y = simOut.yout;
    plot(t, y)
    hold all
end

%% Step 6: Close MATLAB Workers
delete(gcp('nocreate'))
