%% 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.
%
% The example uses a model that simulates the idle speed of an engine. The
% model input is the voltage of the bypass air valve. The output is the
% idle speed.
%
% Use |parsim| to run parallel simulations with two sets of valve voltages.
% Independently vary two of the three transfer function gain parameters
% over a range of two values. The following table lists the eight
% simulations, along with the parameter values. In Step 2, create the
% external inputs, |inpSets|. The variables |gain2| and |gain3| correspond
% to the two gain parameters.
%
%    Run 1      inpSets(1)    gain2 = 25     gain3 = 20
%
%    Run 2      inpSets(1)    gain2 = 25     gain3 = 30
%
%    Run 3      inpSets(1)    gain2 = 35     gain3 = 20
%
%    Run 4      inpSets(1)    gain2 = 35     gain3 = 30
%
%    Run 5      inpSets(2)    gain2 = 25     gain3 = 20
%
%    Run 6      inpSets(2)    gain2 = 25     gain3 = 30
%
%    Run 7      inpSets(2)    gain2 = 35     gain3 = 20
%
%    Run 8      inpSets(2)    gain2 = 35     gain3 = 30

% Copyright 2005-2021 The MathWorks, Inc.
%% Step 1: Preparation
% First, open the model. The simulation mode is 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
% 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
% Next, examine how the idle speed changes for different values of
% parameters |gain2| and |gain3|. Create an array of
% <docid:simulink_ref#bvn9b0e 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. Note that you can directly specify the external input 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: This example uses 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 built 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 <docid:simulink_ref#bvnh103 |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'))