Model stop function callback cannot recognise variable when running with parsim

I have a function writeDataToPhobos, which I have added to my model's StopFcn callback. This function works fine when running without parsim. But with parsim, I get the error 'Unrecognized function or variable 'casenumber'.'
writeDataToPhobos(writeToPhobos, modelDirectory, writeToPhobosDebugMode, datenumNow, casenumber, CurveName, Experiment, Project, Database)
I have assigned all variables to the simIn objects as:
simIn(casenumber) = simIn(casenumber).setVariable('writeToPhobos',writeToPhobos);
simIn(casenumber) = simIn(casenumber).setVariable('modelDirectory',modelDirectory);
simIn(casenumber) = simIn(casenumber).setVariable('writeToPhobosDebugMode',writeToPhobosDebugMode);
simIn(casenumber) = simIn(casenumber).setVariable('datenumNow',datenumNow);
simIn(casenumber) = simIn(casenumber).setVariable('casenumber',casenumber);
simIn(casenumber) = simIn(casenumber).setVariable('CurveName',CurveName);
simIn(casenumber) = simIn(casenumber).setVariable('Experiment',Experiment);
simIn(casenumber) = simIn(casenumber).setVariable('Project',Project);
simIn(casenumber) = simIn(casenumber).setVariable('Database',Database);
and also set the post sim function as:
simIn(casenumber) = simIn(casenumber).setPostSimFcn(@(x) writeDataToPhobos(writeToPhobos, modelDirectory, writeToPhobosDebugMode, datenumNow, casenumber, CurveName, Experiment, Project, Database));

2 个评论

Hi Afzal,
Are you trying to use both the StopFcn and the postSimFcn after running a single simulation?
If I understand correctly, you want to call the function writeDataToPhobos after the simulation is complete. The variables writeToPhobos, modelDirectory, etc. are all used to run the simulation? Or are they only used after the simulation stops in the call to writeDataToPhobos?
How is the simulation executed and where are those variables defined when everything "works fine when running without parsim."? Using sim? Some other method?
I'm asking because the solution you've found seems more complicated than it should be; maybe there's an easier way to do this. If interested in potential for alternatives, can you post a code snippet that illustrates the code flow for setting up the simIn array and then running parsim?
Are you trying to use both the StopFcn and the postSimFcn after running a single simulation?
Originally, I wanted to just use the StopFcn. When that didn't work I started using postSimFcn. With the solution I've found below, I no longer require the postSimFcn.
If I understand correctly, you want to call the function writeDataToPhobos after the simulation is complete. The variables writeToPhobos, modelDirectory, etc. are all used to run the simulation? Or are they only used after the simulation stops in the call to writeDataToPhobos?
Only 'casenumber' is used by the simulation. The rest of the variables are used only by the function writeDataToPhobos
How is the simulation executed and where are those variables defined when everything "works fine when running without parsim."? Using sim? Some other method?
The variables are just set in the base workspace by a script, and the model is run using the command 'sim(Model)'.

请先登录,再进行评论。

 采纳的回答

Found the solution here. Had to modify it slighlty. My function:
function setParsimVars(in)
for iVar = 1:length(in.Variables)
assignin('base',in.Variables(iVar).Name,in.Variables(iVar).Value);
end
end
And then set model preSimFcn as:
simIn(casenumber) = simIn(casenumber).setPreSimFcn(@(x) setParsimVars(simIn(casenumber)));

更多回答(1 个)

Having all the variables in the base workspace, using sim('mymodel'), and using the StopFcn works fine because in that case StopFcn can access the base workspace. But things work differently with parsim.
The following code worked fine for me. At first I thought that parsim might require the simIn variables to set in the model workspace to get to the PostSimFcn, but that's not the case. I don't know if the workspace makes a difference as far as the how the simulation actually executes.
The problem can be solved in at least two ways as shown below.
The first option is to use setPostSimFcn to set up a function that simply takes all of the simIn variables and appends them to the original output of the parsim command. The function writeDataToPhobos could be called from inside simpostfunc using the values of the simIn variables, or it could be called after parsim command because all of the simIn variables are stored in y. The advantage of this approach is that it saves all of the simIn variables in the ouput y.
The second approach is commented out but worked fine for me. Just set the postSimFcn to directly call writeDataToPhobos. It seems like this is exactly what you tried; don't know why it works for me but doesn't work for you.
for ii = 1:2
casenumber = ii;
writeToPhobos = true;
simIn(ii) = Simulink.SimulationInput('MyModel');
% experiment to see if the Workspace matters for parsim
% simIn(ii) = setVariable(simIn(ii),'casenumber',casenumber,'Workspace','MyModel');
% simIn(ii) = setVariable(simIn(ii),'writeToPhobos',writeToPhobos,'Workspace','MyModel');
simIn(ii) = setVariable(simIn(ii),'casenumber',casenumber);
simIn(ii) = setVariable(simIn(ii),'writeToPhobos',writeToPhobos);
% both methods work
simIn(ii) = setPostSimFcn(simIn(ii),@(simout) simpostfunc(simIn(ii).Variables));
% simIn(ii) = setPostSimFcn(simIn(ii),@(simout) writeDataToPhobos(casenumber,writeToPhobos));
end
y = parsim(simIn);
function newout = simpostfunc(simvariables)
% simple function to echo the simIn variables to the the simulation output
% call writeDataToPhobos here if desired
newout.simvars = simvariables;
end
function writeDataToPhobos(casenumber,writeToPhobos)
if writeToPhobos
casenumber
end
end
After running the code, y(2) contains
>> y(2)
ans =
Simulink.SimulationOutput:
simvars: [1x2 Simulink.Simulation.Variable]
SimulationMetadata: [1x1 Simulink.SimulationMetadata]
ErrorMessage: [0x0 char]
>> y(2).simvars
ans =
1×2 Variable array with properties:
Name
Value
Workspace
Description
>> y(2).simvars(1)
ans =
Variable with properties:
Name: 'casenumber'
Value: 2
Workspace: 'global-workspace'
Description: ""
>> y(2).simvars(2)
ans =
Variable with properties:
Name: 'writeToPhobos'
Value: 1
Workspace: 'global-workspace'
Description: ""

6 个评论

Just realised the setPostSimFcn approach was actually working. However, I also have other models that have several function callbacks. So I may have to use the solution I found for those models.
Several function call backs after the sim is complete like writeDataToPhobos? In that case, just use a single PostSimFcn like I showed above and call as many functions as needed from there. I guess I don't see any need in any case to poof data into the base workspace and not use the SetVariable method.
One of the models has several block callbacks. These are LoadFcn, which run scripts, not functions. One of the LoadFcn also loads a .mat file. Not sure if that will be an issue with parsim. Haven't tried running this model yet.
Based on my reading of the documentation Run Parallel Simulations, that workflow would look something like this:
% 1) Load model
openExample('sldemo_suspn_3dof');
model = 'sldemo_suspn_3dof';
load_system(model);
% 2) Set up the sweep parameters
Cf_sweep = 2500*(0.05:0.1:0.95);
numSims = numel(Cf_sweep);
% 3) Create an array of SimulationInput objects and specify the sweep value for each simulation
simIn(1:numSims) = Simulink.SimulationInput(model);
for idx = 1:numSims
simIn(idx) = simIn(idx).setBlockParameter([model '/Road-Suspension Interaction'], 'Cf', num2str(Cf_sweep(idx)))
end
% 4) Simulate the model
simOut = parsim(simIn)
The LoadFcn callbacks for the blocks would execute in step 1 and all the commands in those callbacks would execute in the base workspace as normal. After step 1 we have some variables in the base workspace. It seems we have at least two options to get those variables into the simulation for execution in step 4.
One: Add those variables into the simIn array in step 3. That may not be the best approach if those variables are supposed to be the same for each simulation run
Two: call parsim in step 4 using the TransferBaseWorkspaceVariables name/value pair. That way the variables loaded into the base workspace in step 1 are usable by the workers in step 4. I thing with this approach it may be important to ensure that data not needed to run the simulation is not cluttering up the base workspace, otherwise that unnecessary data will also be trasnfered. I guess there's really no downside to that, other than the time/memory wastage.
Disclaimer: I think this is how it's supposed to work, haven't tried it yet. But I plan to, because I'm very curious about how this all works.
Are the LoadFcn block callbacks doing anything more than defining or computing variables for the simulation?
The Question that you linked to in you Answer was focused on a different issue, i.e., how to deal with callbacks that execute while the simualtion is running.
Thanks, I think using the TransferBaseWorkspaceVariables name/value pair argument may be the best approach. The model is quite big, but based on what I've seen, the LoadFcn are just setting variables.
You're welcome. Also, I posted an Answer to the question you linked to that might be of interest.

请先登录,再进行评论。

类别

帮助中心File Exchange 中查找有关 Run Multiple Simulations 的更多信息

产品

版本

R2020b

标签

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by