% This function is part of the NMSM Pipeline, see file for full license.
%
% This function parses the settings tree resulting from xml2struct of the
% Muscle Tendon Personalization Settings XML file.
%
% (struct) -> (string, struct, struct)
% returns the input values for muscle tendon length initialization
function inputs = parseMuscleTendonLengthInitializationSettingsTree( ...
settingsTree)
if strcmp(getFieldByNameOrError(settingsTree, ...
"MuscleTendonLengthInitialization").is_enabled.Text, "true")
inputs = getInputs(settingsTree);
inputs = getMtpModelInputs(inputs);
inputs = getMuscleVolume(inputs);
else
inputs = false;
end
end
function inputs = getInputs(tree)
inputs = parseMtpNcpSharedInputs(tree);
inputs = getPassiveData(getFieldByNameOrError(tree, "MuscleTendonLengthInitialization"), inputs);
inputs = getTask(tree, inputs);
inputs = getNormalizedFiberLengthSettings(tree, inputs);
inputs = reorderPreprocessedDataByMuscleNames(inputs, inputs.muscleNames);
end
function inputs = getPassiveData(tree, inputs)
import org.opensim.modeling.Storage
passiveInputDirectory = getFieldByName(tree, 'passive_data_input_directory').Text;
inputs.passiveMomentDataExists = 0;
if (~isempty(passiveInputDirectory))
if isfolder(passiveInputDirectory)
inputs.passivePrefixes = ...
findPrefixes(tree, passiveInputDirectory);
passiveJointMomentFileNames = ...
findFileListFromPrefixList(fullfile(...
passiveInputDirectory, "IDData"), inputs.passivePrefixes);
inputs.coordinateNames = ...
getStorageColumnNames(Storage(passiveJointMomentFileNames(1)));
inputs.passiveData.inverseDynamicsMoments = ...
parseMtpStandard(passiveJointMomentFileNames);
passiveDirectories = ...
findFirstLevelSubDirectoriesFromPrefixes(fullfile( ...
passiveInputDirectory, "MAData"), inputs.passivePrefixes);
[inputs.passiveMuscleTendonLength, ...
inputs.passiveMuscleTendonLengthColumnNames] = ...
parseFileFromDirectories(passiveDirectories, "Length.sto", ...
Model(inputs.model));
if ~(sum(ismember(inputs.muscleNames, ...
inputs.passiveMuscleTendonLengthColumnNames)) == ...
length(inputs.muscleNames))
throw(MException('', 'Muscle names in passive data do not match muscle names from coordinates.'))
end
inputs.passiveMomentArms = ...
parseMomentArms(passiveDirectories, inputs.model);
includedIndices = ismember( ...
inputs.passiveMuscleTendonLengthColumnNames, inputs.muscleNames);
inputs.passiveMuscleTendonLengthColumnNames = inputs.passiveMuscleTendonLengthColumnNames(includedIndices);
inputs.passiveMomentArms = inputs.passiveMomentArms(:, :, includedIndices, :);
inputs.passiveMuscleTendonLength = inputs.passiveMuscleTendonLength(:, includedIndices, :);
inputs.passiveMomentDataExists = 1;
end
end
end
% (integer, struct, string, struct) -> (struct)
function inputs = getTask(tree, inputs)
inputs.maximumMuscleStressIsIncluded = strcmp( ...
getFieldByNameOrError( ...
tree, ...
'optimize_maximum_muscle_stress' ...
).Text, ...
'true');
optimizeIsometricMaxForce = getFieldByName(tree, ...
'optimize_isometric_max_force').Text;
inputs.optimizeIsometricMaxForce = 0;
if(optimizeIsometricMaxForce == "true")
inputs.optimizeIsometricMaxForce = 1;
end
inputs.costTerms = getCostFunctionTerms(getFieldByNameOrError(tree, ...
'MuscleTendonLengthInitialization'));
maximumMuscleStress = getFieldByName(tree, 'maximum_muscle_stress');
if(isstruct(maximumMuscleStress))
inputs.maximumMuscleStress = str2double(maximumMuscleStress.Text);
else
inputs.maximumMuscleStress = 610e3;
end
maxNormalizedMuscleFiberLength = getFieldByName(tree, ...
'max_normalized_muscle_fiber_length');
if(isstruct(maxNormalizedMuscleFiberLength))
inputs.maxNormalizedMuscleFiberLength = ...
str2double(maxNormalizedMuscleFiberLength.Text);
end
minNormalizedMuscleFiberLength = getFieldByName(tree, ...
'min_normalized_muscle_fiber_length');
if(isstruct(minNormalizedMuscleFiberLength))
inputs.minNormalizedMuscleFiberLength = ...
str2double(minNormalizedMuscleFiberLength.Text);
end
end
function costTerms = getCostFunctionTerms(tree)
costTerms = parseRcnlCostTermSet( ...
tree.RCNLCostTermSet.RCNLCostTerm);
end
function inputs = getMuscleVolume(inputs)
inputs.muscleVolume = (inputs.maxIsometricForce / ...
inputs.maximumMuscleStress) .* inputs.optimalFiberLength;
end
function inputs = getNormalizedFiberLengthSettings(tree, inputs)
normalizedFiberLengthGroupNames = parseSpaceSeparatedList(tree, ...
"normalized_fiber_length_muscle_groups");
inputs.normalizedFiberLengthGroups = groupNamesToGroups( ...
normalizedFiberLengthGroupNames, inputs.model);
inputs.numMuscleGroups = numel(inputs.normalizedFiberLengthGroups);
numMuscles = length(inputs.muscleNames);
for i = 1 : inputs.numMuscleGroups
for j = 1 : numel(inputs.normalizedFiberLengthGroups{i})
inputs.groupedMaxNormalizedFiberLength(...
inputs.normalizedFiberLengthGroups{i}(j)) = i;
end
end
inputs.numMusclesIndividual = 0;
for i = 1 : numMuscles
if isempty(find([inputs.normalizedFiberLengthGroups{:}] == i, 1))
inputs.groupedMaxNormalizedFiberLength(i) = inputs.numMuscleGroups + ...
inputs.numMusclesIndividual + 1;
inputs.numMusclesIndividual = inputs.numMusclesIndividual + 1;
end
end
end