Page 1 of 1

External loads in forward dynamic batch processes

Posted: Mon Feb 15, 2021 5:11 am
by samuelegould
Dear all,

tldr: I'm trying to use setExternalLoads to apply an external load but cannot get it to work.
I am trying to perform a forward dynamic batch process which uses different experimental data in each iteration. Normally when applying an external load in a forward dynamics simulation I would create an ExternalLoad.xml file and add it to the forward tool using the setExternalLoadsFileName() command. I have run a single simulation in this way and it works.

As I am performing a batch process I was hoping to avoid creating and loading a new ExternalLoad.xml file for each iteration by defining the external loads within the MatLab code and adding it directly to the forward tool using the setExternalLoads() command. However I have not been able to get this to work. It has not been applying any external loads. I have compared the results from the simulations using the setExternalLoads() command to simulations without any external loads so the only force present is the body weights and the results (from point kinematics measuring the motion of the bodies and the body kinematics) are identical.

I have check that the external load is correct by printing it out as an .xml file and applying it using the setExternalLoadsFileName() command, the simulation runs and applied the external load. I clear the external load, external force, forward tool, model, point kinematics, and body kinematics on each iteration and have placed initSystem after applying the external loads (as suggested here viewtopicPhpbb.php?f=91&t=4025&p=0&star ... a0191b8fc8 for inverse dynamics).

Below is the code for the batch process, this is part of a larger code which prepares the experimental data and specifies some file paths (such as path2mot), I am confident paths are not the problem as the code runs and outputs all the files to the correct places. Appologies for the length of the code but not knowing where the problem is I wanted to share it all:

Code: Select all

for i= 1:no_data_set
    %% ===================== Call files and folders ======================== %%
    % ----------- Specify results file for current iteration
    data_set_no = sprintf('_%d',i);
    results_folder_current = append(results_folder_base, data_set_no); % create a name for folder which will contain results for current iteration
    mkdir([path2mot results_folder_4_batch results_folder_current{1}])  % create folder for results of current iteration

    % ----------- Load relevant .mot file
    file_no = sprintf('_%d',i);
    CurrentMotFile = [path2mot results_folder_4_batch NameNoExt file_no '.mot']; % path to .mot file
    [~, CurrentMotFileName, extension] = fileparts(CurrentMotFile);    % Create the storage object from the input .mot file
    CurrentMotStor = STOFileAdapter().read(CurrentMotFile);
    % get labels from mot file
    MotTableLabels = CurrentMotStor.getColumnLabels; 
    forceslabels = cell(CurrentMotStor.getNumColumns(),1);
    for i = 0 : CurrentMotStor.getNumColumns() -1 
        forceslabels{i+1,1} = char(MotTableLabels.get(i)); 
    end
    
    %% ========================== OpenSim Model ============================ %%
    % @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ %    
    % Load a model
    % path to model defined outside of loop
    myModel = Model(path2model);
    % initiate model
    myModel.initSystem();
    % ------------------ Get various model components ---------------------- %
    % ------------------------- Bodies
    bodySet = myModel.getBodySet();
    nBodies = bodySet.getSize;
    % ------------------------- Joints
    jointSet = myModel.getJointSet();
    nJoints = jointSet.getSize;
    
    % ------------------------- Muscles
    muscSet = myModel.getMuscles();
    nMuscles = muscSet.getSize;
    % nameMusc = myModel.getStateVariableNames();
    
    % ------------------------- Ground
    gnd = myModel.getGround();
    
    % ------------------------- Actuators
    actSet = myModel.getActuators();
    nact = actSet.getSize;
    
    % ------------------------- Forces
    forceSet = myModel.getForceSet();
    %

    %% ===================== Set External Loads ======================== %%
    % @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ %
    
    % define application of external loads
    LowpassFilt = -1;

    % ---------------- External force
    %
    % externalforce placed within externalload, 
    extforce = ExternalForce();
    extforce.setName(CurrentMotFileName);
    % Identify force and point columns
    forceIDIndex = find( contains(forceslabels,'_vx'));
    pointIDIndex = find( contains(forceslabels,'_px'));
    %
    extforce.set_applied_to_body('top_pot');
    extforce.set_force_expressed_in_body('ground');
    extforce.set_point_expressed_in_body('ground');
    extforce.set_force_identifier(strrep(forceslabels{forceIDIndex},'x','')); 
    extforce.set_point_identifier(strrep(forceslabels{pointIDIndex},'x','')); 
    %
    % ---------------- External load
    %
    extload = ExternalLoads();
    extload.setName(CurrentMotFileName);
    extload.setDataFileName(CurrentMotFile); % provide path to .mot file
    extload.cloneAndAppend(extforce); % add the external force to the external load
    extload.setLowpassCutoffFrequencyForLoadKinematics(LowpassFilt);
    % Create external load xml file
    extLoadsXml = [CurrentMotFileName '_' 'external_loads.xml'];
    % Print the external load xml file
    extload.print([path2mot results_folder_4_batch results_folder_current{1} '\' extLoadsXml]); % must go into an already existing folder
    %
    %
    %% ------------- Set-up of ForwardTool through Matlab -------------- %%
    %
    % setup options 
    
    errorTol = 1.0000000000000001e-05;
    maxIntergratorSteps = 20000;
    minStepSize = 1e-08;
    maxStepSize = 1;
    outputPrecision = 8;
    timeStart = 0;
    timeEnd = 1.24;

    % Apply to forward tool
    ft = ForwardTool(); 
    ft.setName(['set-up_' CurrentMotFileName]); % name of forward tool
    ft.setModel(myModel); % model
    ft.setModelFilename([char(myModel.getName) '_' CurrentMotFileName]); % name model
    ft.setReplaceForceSet(0) % specified force sets are appended 
    % apply set up options
    ft.setStartTime(timeStart);
    ft.setFinalTime(timeEnd);   
    ft.setErrorTolerance(errorTol);
    ft.setMaximumNumberOfSteps(maxIntergratorSteps);
    ft.setMinDT(minStepSize);
    ft.setMaxDT(maxStepSize);
    ft.setOutputPrecision(outputPrecision);
    ft.setSolveForEquilibrium(0); % very important when muscles are involved
    % set the external load of the current loop, this should make no
    % difference as there are no muscles in the model, simulations have
    % shown this is the case
    ft.setExternalLoads(extload); 
    % specify output directory
    ft.setResultsDir([path2mot results_folder_4_batch results_folder_current{1}]) % folder for results, it will be automatically created MotFilePath results_folder_4_batch
    % print out .xml file of forward tool
    FT_SetUp_Xml = [CurrentMotFileName '_' 'SetUp.xml']; 
    ft.print([path2mot results_folder_4_batch results_folder_current{1} '\' FT_SetUp_Xml]); % Write this Object into an XML file of the given name
    %
    % ----------------
    %
    %% ---------------------- Prepare Analysis ------------------------- %%
    % Necessary to define the analysis 
    %
    % ------------------------- Add point kinematics analysis
    %
    % Add analysis for a point in L3
    pk1 = PointKinematics();
    myModel.addAnalysis(pk1);
    pk1.setInDegrees(1)
    pk1.setBody(bodySet.get(1));
    pk1.setRelativeToBody(gnd)
    pk1.setPointName('centre_of_DIC_point_L3')
    pk1.setPoint(Vec3(-0.0395074751100576,0.0471332730104978,0.00651529708770742));
    % Add analysis for a point in L2
    pk2 = PointKinematics();
    myModel.addAnalysis(pk2);
    pk2.setInDegrees(1)
    pk2.setBody(bodySet.get(2));
    pk2.setRelativeToBody(gnd)
    pk2.setPointName('centre_of_DIC_point_L2')
    pk2.setPoint(Vec3(-0.0436541829436566,0.0862211168263703,0.0112360325795324));
    %
    % ------------------------- Add body kinematics
    bk = BodyKinematics();
    bk.setModel(myModel)
    bk.setInDegrees(1)
    bk.setExpressResultsInLocalFrame(1);
    myModel.addAnalysis(bk);
    %
    % --------------------------- Run simulation ---------------------------- %
    %
    myModel.initSystem();
    ft.run();
    clear myModel extload extforce ft pk1 pk2 bk
end

Re: External loads in forward dynamic batch processes

Posted: Thu Feb 18, 2021 10:22 am
by aymanh
Hi Samuele,

You should look at the source code for ForwardTool, for the version of OpenSim you're using, to see when it is valid to call the setExternalLoads method, as the ExternalLoads are added to the model by the Tool at some point and extra wiring/initialization needs to be performed afterwards.
Using the XML file to initialize the tool follows a strict initialization sequence that appears to be different from what you're currently doing using the API. If you find out the difference please report back on this thread so that others can benefit as well.

Best regards,
-Ayman

Re: External loads in forward dynamic batch processes

Posted: Mon Mar 01, 2021 3:37 am
by samuelegould
Hi Ayman,

Thank you for the reply and suggestions. I will look into that and if I find the difference will post it here.

Thanks,
Samuele

Re: External loads in forward dynamic batch processes

Posted: Tue Jul 20, 2021 1:50 am
by st115990
Hello Samuele,

did you find a solution for your problem yet? I'm currently struggeling with a similar issue regarding the AnalyzeTool and how to set additional force set files via Matlab.

Best,
Mike

Re: External loads in forward dynamic batch processes

Posted: Fri Jul 23, 2021 6:40 am
by samuelegould
Hello Mike,

When you say additional force set files do you mean applying multiple forces (ie many .mot files) in a single simulation/analysis or is it a case of running many simulations/analysis each with a different force set (which is what I was attempting)?

If its the first case of applying multiple forces (ie many .mot files) in a single simulation/analysis you can add many force sets (an ExternalForces https://github.com/opensim-org/opensim- ... lForce.cpp) to an ExternalLoad https://github.com/opensim-org/opensim- ... lLoads.cpp. Although I have not tested it, it should just be a case of calling the "cloneAndAppend" command for each external force.

Code: Select all

% ---------------- Define the external force, specifies the body the force is applied to, the reference frames and should be able to also specify the mot file using the command data_source_name (I couldn't)
% externalforce placed within externalload
extforce = ExternalForce ();
% specify the column headings that contains the force magnitude and point of application data 
% specify the body to apply forces to 
% pecify reference frames

% ---------------- External load
extload = ExternalLoads();
% I had problems setting the .mot file in the external force so added it here, a .mot file can contain multiple force sets, you specify which is applied to which body in the external force above
extload.setDataFileName(paths.path2loading);
% this adds the external force to the external load, which then applied the load in the simulation/analysis
extload.cloneAndAppend(extforce);
Hopefully this helps/is clear, ask if not.

If it is the second case I'm afraid I did not solve my problem. In the end I used a work around which was to create a new .xml external load file for each iteration and then apply it with the setExternalLoadsFileName() command. Not elegant or neat but it worked without adding too much time as the files are not very large.

Looking through the source codes for the ForwardTool https://github.com/opensim-org/opensim- ... rdTool.cpp and the Abstract Tool https://github.com/opensim-org/opensim- ... ctTool.cpp I could only found reference to the setExternalLoadsFileName() command, not the setExternalLoads, both of which I think are defined through https://github.com/opensim-org/opensim- ... lLoads.cpp. However, I am sure someone with a better knowledge/understanding of C++ could work it out.

Maybe you can work it out looking through the source codes here: https://github.com/opensim-org/opensim- ... er/OpenSim, for the analyze tool https://github.com/opensim-org/opensim- ... zeTool.cpp.

If you do figure it out I would be very interested to know how.

Hope you figure it out,
Samuele

Re: External loads in forward dynamic batch processes

Posted: Mon Jul 26, 2021 4:42 am
by st115990
Hello Samuele,

many thanks for the detailed answer.
I'm sorry, I didn't specify my problem accurately. I was trying to regenerate the results from Nejads et al.'s paper "The Capacity of Generic Musculoskeletal Simulations to Predict Knee Joint Loading Using the CAMS-Knee Datasets" https://link.springer.com/article/10.10 ... 20-02465-5

In the CAMS-Knee Sample dataset there is a Word document guiding through the OpenSim porcess, of generating the results made in this paper.
Unfortunately these results are only done via the OpenSim GUI, but we needed them from the Matlab API.
Therefore I rewrote the GUI steps into Matlab steps. One of these steps requires a Static Optimization. Here one can add "addtional force set files". You can find them in the GUI under Tools->Static Optimization -> Actuators and External Loads

For me it was possible to set the xml file via the AnalyzeTool method "setForceSetFiles". Unfortunately after I run the AnalyzeTool it didn't see the addtional force set. I needed to do a workaround, following this forum post viewtopicPhpbb.php?f=91&t=13631&p=0&sta ... 6ee4ab047d
For my problem, the actual setup of the AnalyzeTool needed to be stored as setupAnalyzeTool.xml file and then reloaded in Matlab again.(But I dont know why) (I know this is the setup you wanted to avoid.)
For the external loads you are describing, I also only use the setExternalLoadsFileName() command with the specified filename.

Hope my solution can somehow help you.

Best,
Mike