From 27600dd449210515aa0bed7602657de413abaf88 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" Date: Tue, 22 Aug 2023 09:38:00 -0700 Subject: [PATCH 001/365] removed duplicate mex initialization --- .../makeTreatmentOptimizationInputs.m | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/core/TreatmentOptimization/makeTreatmentOptimizationInputs.m b/src/core/TreatmentOptimization/makeTreatmentOptimizationInputs.m index d646dadbd..381dcfe89 100644 --- a/src/core/TreatmentOptimization/makeTreatmentOptimizationInputs.m +++ b/src/core/TreatmentOptimization/makeTreatmentOptimizationInputs.m @@ -1,9 +1,9 @@ % This function is part of the NMSM Pipeline, see file for full license. % % This function prepares the inputs for the all treatment optimization -% modules (tracking, verification, and design optimization. +% modules (tracking, verification, and design optimization. % -% (struct, struct) -> (struct) +% (struct, struct) -> (struct) % ----------------------------------------------------------------------- % % The NMSM Pipeline is a toolkit for model personalization and treatment % @@ -28,10 +28,6 @@ % ----------------------------------------------------------------------- % function inputs = makeTreatmentOptimizationInputs(inputs, params) -if isequal(mexext, 'mexw64') - pointKinematicsMexWindows(inputs.mexModel); - inverseDynamicsMexWindows(inputs.mexModel); -end inputs = getStateDerivatives(inputs); inputs = setupGroundContact(inputs); inputs = getSplines(inputs); From 0f8f3d16b81cda90380c95fa6ad6ecdc6b21f936 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" Date: Tue, 22 Aug 2023 10:37:11 -0700 Subject: [PATCH 002/365] new design variable bounds parse fn --- .../parseDesignOptimizationSettingsTree.m | 63 +++---------------- .../parseTrackingOptimizationSettingsTree.m | 55 ++-------------- ...arseVerificationOptimizationSettingsTree.m | 49 ++------------- .../getDesignVariableInputBounds.m | 24 +++---- src/core/parse/parseDoubleOrAlternate.m | 43 +++++++++++++ ...reatmentOptimizationDesignVariableBounds.m | 43 +++++++++++++ 6 files changed, 117 insertions(+), 160 deletions(-) create mode 100644 src/core/parse/parseDoubleOrAlternate.m create mode 100644 src/core/parse/parseTreatmentOptimizationDesignVariableBounds.m diff --git a/src/DesignOptimization/parseDesignOptimizationSettingsTree.m b/src/DesignOptimization/parseDesignOptimizationSettingsTree.m index 5fccf93a2..101433646 100644 --- a/src/DesignOptimization/parseDesignOptimizationSettingsTree.m +++ b/src/DesignOptimization/parseDesignOptimizationSettingsTree.m @@ -30,7 +30,12 @@ function [inputs, params] = ... parseDesignOptimizationSettingsTree(settingsTree) +inputs = getTreatmentOptimizationInputs(settingsTree); +inputs = parseTreatmentOptimizationDesignVariableBounds(settingsTree, ... + inputs); +inputs = parseDesignSettings(settingsTree, inputs); inputs = getInputs(settingsTree); +inputs.toolName = "DesignOptimization"; params = getParams(settingsTree); inputs = modifyModelForces(inputs); inputs = updateMuscleModelProperties(inputs); @@ -38,8 +43,6 @@ function inputs = getInputs(tree) import org.opensim.modeling.Storage -inputs = getTreatmentOptimizationInputs(tree); -inputs = getDesignVariableBounds(tree, inputs); if strcmpi(inputs.controllerType, 'synergy_driven') inputs.synergyWeights = parseTreatmentOptimizationStandard(... {getTextFromField(getFieldByName(tree, 'synergy_vectors_file'))}); @@ -54,55 +57,8 @@ end end -function inputs = getDesignVariableBounds(tree, inputs) -designVariableTree = getFieldByNameOrError(tree, ... - 'RCNLDesignVariableBoundsTerms'); -jointPositionsMultiple = getFieldByNameOrError(designVariableTree, ... - 'joint_positions_multiple'); -if(isstruct(jointPositionsMultiple)) - inputs.statePositionsMultiple = getDoubleFromField(jointPositionsMultiple); -end -jointVelocitiesMultiple = getFieldByNameOrError(designVariableTree, ... - 'joint_velocities_multiple'); -if(isstruct(jointVelocitiesMultiple)) - inputs.stateVelocitiesMultiple = getDoubleFromField(jointVelocitiesMultiple); -end -jointAccelerationsMultiple = getFieldByNameOrError(designVariableTree, ... - 'joint_accelerations_multiple'); -if(isstruct(jointAccelerationsMultiple)) - inputs.stateAccelerationsMultiple = ... - getDoubleFromField(jointAccelerationsMultiple); -end -jointJerkMultiple = getFieldByNameOrError(designVariableTree, ... - 'joint_jerks_multiple'); -if(isstruct(jointJerkMultiple)) - inputs.controlJerksMultiple = getDoubleFromField(jointJerkMultiple); -end -if strcmp(inputs.controllerType, 'synergy_driven') -maxControlSynergyActivations = getFieldByNameOrError(designVariableTree, ... - 'synergy_activations_max'); -if(isstruct(maxControlSynergyActivations)) - inputs.maxControlSynergyActivations = ... - getDoubleFromField(maxControlSynergyActivations); -end -if inputs.optimizeSynergyVectors - maxParameterSynergyWeights = getFieldByNameOrError(designVariableTree, ... - 'synergy_weights_max'); - if(isstruct(maxParameterSynergyWeights)) - inputs.maxParameterSynergyWeights = ... - getDoubleFromField(maxParameterSynergyWeights); - end -end -else -maxControlTorques = getFieldByName(designVariableTree, ... - 'torque_controls_multiple'); -if(isstruct(maxControlTorques)) - inputs.maxControlTorquesMultiple = getDoubleFromField(maxControlTorques); -else - inputs.maxControlTorquesMultiple = 1; -end -end -finalTimeRange = getFieldByName(designVariableTree, ... +function inputs = parseDesignSettings(tree, inputs) +finalTimeRange = getFieldByName(tree, ... 'final_time_range'); if(isstruct(finalTimeRange)) inputs.finalTimeRange = getDoubleFromField(finalTimeRange); @@ -115,13 +71,12 @@ inputs.numExternalTorqueControls = ... length(inputs.externalControlTorqueNames); inputs.maxExternalTorqueControls = getDoubleFromField( ... - getFieldByNameOrError(designVariableTree, ... + getFieldByNameOrError(tree, ... 'external_torque_control_multiple')); end -inputs.toolName = "DesignOptimization"; end function params = getParams(tree) params.solverSettings = getOptimalControlSolverSettings(... getTextFromField(getFieldByName(tree, 'optimal_control_settings_file'))); -end \ No newline at end of file +end diff --git a/src/TrackingOptimization/parseTrackingOptimizationSettingsTree.m b/src/TrackingOptimization/parseTrackingOptimizationSettingsTree.m index 69de76a0b..2c6daf04b 100644 --- a/src/TrackingOptimization/parseTrackingOptimizationSettingsTree.m +++ b/src/TrackingOptimization/parseTrackingOptimizationSettingsTree.m @@ -14,7 +14,7 @@ % National Institutes of Health (R01 EB030520). % % % % Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % +% Author(s): Marleny Vega, Claire V. Hammond % % % % Licensed under the Apache License, Version 2.0 (the "License"); % % you may not use this file except in compliance with the License. % @@ -31,59 +31,14 @@ function [inputs, params, resultsDirectory] = ... parseTrackingOptimizationSettingsTree(settingsTree) inputs = getTreatmentOptimizationInputs(settingsTree); -inputs = getDesignVariableBounds(settingsTree, inputs); +inputs = parseTreatmentOptimizationDesignVariableBounds(settingsTree, ... + inputs); +inputs.toolName = "TrackingOptimization"; params = getParams(settingsTree); inputs = modifyModelForces(inputs); end -function inputs = getDesignVariableBounds(tree, inputs) -designVariableTree = getFieldByNameOrError(tree, ... - 'RCNLDesignVariableBoundsTerms'); -jointPositionsMultiple = getFieldByNameOrError(designVariableTree, ... - 'joint_positions_multiple'); -if(isstruct(jointPositionsMultiple)) - inputs.statePositionsMultiple = getDoubleFromField(jointPositionsMultiple); -end -jointVelocitiesMultiple = getFieldByNameOrError(designVariableTree, ... - 'joint_velocities_multiple'); -if(isstruct(jointVelocitiesMultiple)) - inputs.stateVelocitiesMultiple = getDoubleFromField(jointVelocitiesMultiple); -end -jointAccelerationsMultiple = getFieldByNameOrError(designVariableTree, ... - 'joint_accelerations_multiple'); -if(isstruct(jointAccelerationsMultiple)) - inputs.stateAccelerationsMultiple = ... - getDoubleFromField(jointAccelerationsMultiple); -end -jointJerkMultiple = getFieldByNameOrError(designVariableTree, ... - 'joint_jerks_multiple'); -if(isstruct(jointJerkMultiple)) - inputs.controlJerksMultiple = getDoubleFromField(jointJerkMultiple); -end -if strcmp(inputs.controllerType, 'synergy_driven') -maxControlSynergyActivations = getFieldByNameOrError(designVariableTree, ... - 'synergy_activations_max'); -if(isstruct(maxControlSynergyActivations)) - inputs.maxControlSynergyActivations = ... - getDoubleFromField(maxControlSynergyActivations); -end -maxParameterSynergyWeights = getFieldByNameOrError(designVariableTree, ... - 'synergy_weights_max'); -if(isstruct(maxParameterSynergyWeights)) - inputs.maxParameterSynergyWeights = ... - getDoubleFromField(maxParameterSynergyWeights); -end -else -maxControlTorques = getFieldByNameOrError(designVariableTree, ... - 'torque_controls_max'); -if(isstruct(maxControlTorques)) - inputs.maxControlTorquesMultiple = getDoubleFromField(maxControlTorques); -end -end -inputs.toolName = "TrackingOptimization"; -end - function params = getParams(tree) params.solverSettings = getOptimalControlSolverSettings(... getTextFromField(getFieldByName(tree, 'optimal_control_settings_file'))); -end \ No newline at end of file +end diff --git a/src/VerificationOptimization/parseVerificationOptimizationSettingsTree.m b/src/VerificationOptimization/parseVerificationOptimizationSettingsTree.m index df0bbbd49..4147dd789 100644 --- a/src/VerificationOptimization/parseVerificationOptimizationSettingsTree.m +++ b/src/VerificationOptimization/parseVerificationOptimizationSettingsTree.m @@ -30,63 +30,24 @@ function [inputs, params] = ... parseVerificationOptimizationSettingsTree(settingsTree) +inputs = getTreatmentOptimizationInputs(settingsTree); +inputs = parseTreatmentOptimizationDesignVariableBounds(settingsTree, ... + inputs); inputs = getInputs(settingsTree); +inputs.toolName = "VerificationOptimization"; params = getParams(settingsTree); inputs = modifyModelForces(inputs); end function inputs = getInputs(tree) import org.opensim.modeling.Storage -inputs = getTreatmentOptimizationInputs(tree); -inputs = getDesignVariableBounds(tree, inputs); if strcmpi(inputs.controllerType, 'synergy_driven') inputs.synergyWeights = parseTreatmentOptimizationStandard(... {getTextFromField(getFieldByName(tree, 'synergy_vectors_file'))}); end end -function inputs = getDesignVariableBounds(tree, inputs) -designVariableTree = getFieldByNameOrError(tree, ... - 'RCNLDesignVariableBoundsTerms'); -jointPositionsMultiple = getFieldByNameOrError(designVariableTree, ... - 'joint_positions_multiple'); -if(isstruct(jointPositionsMultiple)) - inputs.statePositionsMultiple = getDoubleFromField(jointPositionsMultiple); -end -jointVelocitiesMultiple = getFieldByNameOrError(designVariableTree, ... - 'joint_velocities_multiple'); -if(isstruct(jointVelocitiesMultiple)) - inputs.stateVelocitiesMultiple = getDoubleFromField(jointVelocitiesMultiple); -end -jointAccelerationsMultiple = getFieldByNameOrError(designVariableTree, ... - 'joint_accelerations_multiple'); -if(isstruct(jointAccelerationsMultiple)) - inputs.stateAccelerationsMultiple = ... - getDoubleFromField(jointAccelerationsMultiple); -end -jointJerkMultiple = getFieldByNameOrError(designVariableTree, ... - 'joint_jerks_multiple'); -if(isstruct(jointJerkMultiple)) - inputs.controlJerksMultiple = getDoubleFromField(jointJerkMultiple); -end -if strcmp(inputs.controllerType, 'synergy_driven') -maxControlSynergyActivations = getFieldByNameOrError(designVariableTree, ... - 'synergy_activations_max'); -if(isstruct(maxControlSynergyActivations)) - inputs.maxControlSynergyActivations = ... - getDoubleFromField(maxControlSynergyActivations); -end -else -maxControlTorques = getFieldByNameOrError(designVariableTree, ... - 'torque_controls_max'); -if(isstruct(maxControlTorques)) - inputs.maxControlTorquesMultiple = getDoubleFromField(maxControlTorques); -end -end -inputs.toolName = "VerificationOptimization"; -end - function params = getParams(tree) params.solverSettings = getOptimalControlSolverSettings(... getTextFromField(getFieldByName(tree, 'optimal_control_settings_file'))); -end \ No newline at end of file +end diff --git a/src/core/TreatmentOptimization/SetupBounds/getDesignVariableInputBounds.m b/src/core/TreatmentOptimization/SetupBounds/getDesignVariableInputBounds.m index 8f4f2c143..803001d26 100644 --- a/src/core/TreatmentOptimization/SetupBounds/getDesignVariableInputBounds.m +++ b/src/core/TreatmentOptimization/SetupBounds/getDesignVariableInputBounds.m @@ -5,7 +5,7 @@ % based on the multiples value selected by the user times the range of data. % For example, if the angle B has a range of -5 to +5, and state position % multiple is 1, the maximum value of angle B is 15 and the minimum value -% of angle B is -15. +% of angle B is -15. % % (struct) -> (struct) % Computes max and min design variable bounds @@ -41,17 +41,17 @@ inputs.minTime = min(inputs.experimentalTime); maxStatePositions = max(inputs.experimentalJointAngles) + ... - inputs.statePositionsMultiple * range(inputs.experimentalJointAngles); + inputs.jointPositionsMultiple * range(inputs.experimentalJointAngles); minStatePositions = min(inputs.experimentalJointAngles) - ... - inputs.statePositionsMultiple * range(inputs.experimentalJointAngles); + inputs.jointPositionsMultiple * range(inputs.experimentalJointAngles); maxStateVelocities = max(inputs.experimentalJointVelocities) + ... - inputs.stateVelocitiesMultiple * range(inputs.experimentalJointVelocities); + inputs.jointVelocitiesMultiple * range(inputs.experimentalJointVelocities); minStateVelocities = min(inputs.experimentalJointVelocities) - ... - inputs.stateVelocitiesMultiple * range(inputs.experimentalJointVelocities); + inputs.jointVelocitiesMultiple * range(inputs.experimentalJointVelocities); maxStateAccelerations = max(inputs.experimentalJointAccelerations) + ... - inputs.stateAccelerationsMultiple * range(inputs.experimentalJointAccelerations); + inputs.jointAccelerationsMultiple * range(inputs.experimentalJointAccelerations); minStateAccelerations = min(inputs.experimentalJointAccelerations) - ... - inputs.stateAccelerationsMultiple * range(inputs.experimentalJointAccelerations); + inputs.jointAccelerationsMultiple * range(inputs.experimentalJointAccelerations); inputs.maxState = [maxStatePositions maxStateVelocities maxStateAccelerations]; inputs.minState = [minStatePositions minStateVelocities minStateAccelerations]; @@ -61,18 +61,18 @@ minControlJerks = min(inputs.experimentalJointJerks) - ... inputs.controlJerksMultiple * range(inputs.experimentalJointJerks); -if strcmp(inputs.controllerType, 'synergy_driven') +if strcmp(inputs.controllerType, 'synergy_driven') maxControlSynergyActivations = inputs.maxControlSynergyActivations * ... ones(1, inputs.numSynergies); inputs.maxControl = [maxControlJerks maxControlSynergyActivations]; inputs.minControl = [minControlJerks zeros(1, inputs.numSynergies)]; - + if inputs.optimizeSynergyVectors inputs.maxParameter = inputs.maxParameterSynergyWeights * ... ones(1, inputs.numSynergyWeights); inputs.minParameter = zeros(1, inputs.numSynergyWeights); end -elseif strcmp(inputs.controllerType, 'torque_driven') +elseif strcmp(inputs.controllerType, 'torque_driven') for i = 1:length(inputs.controlTorqueNames) indx = find(strcmp(convertCharsToStrings( ... inputs.inverseDynamicMomentLabels), ... @@ -94,7 +94,7 @@ end if isfield(inputs, "enableExternalTorqueControl") && ... - inputs.enableExternalTorqueControl + inputs.enableExternalTorqueControl for i = 1:length(inputs.externalControlTorqueNames) indx = find(strcmp(convertCharsToStrings( ... inputs.inverseDynamicMomentLabels), ... @@ -109,4 +109,4 @@ inputs.maxControl = [inputs.maxControl maxExternalControlTorques]; inputs.minControl = [inputs.minControl minExternalControlTorques]; end -end \ No newline at end of file +end diff --git a/src/core/parse/parseDoubleOrAlternate.m b/src/core/parse/parseDoubleOrAlternate.m new file mode 100644 index 000000000..9b8900e41 --- /dev/null +++ b/src/core/parse/parseDoubleOrAlternate.m @@ -0,0 +1,43 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function attempts to parse a double from a struct field. If the +% field is not a struct or the field cannot be parsed as a double, the +% alternate value is returned. +% +% (struct, string, double) => double +% Parses a double or returns an alternate value + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Claire V. Hammond % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function output = parseDoubleOrAlternate(tree, field, alternate) + field = getFieldByName(tree, field); + if(isstruct(field)) + try + output = str2double(field.Text); + catch + output = alternate; + end + else + output = alternate; + end +end diff --git a/src/core/parse/parseTreatmentOptimizationDesignVariableBounds.m b/src/core/parse/parseTreatmentOptimizationDesignVariableBounds.m new file mode 100644 index 000000000..b90769874 --- /dev/null +++ b/src/core/parse/parseTreatmentOptimizationDesignVariableBounds.m @@ -0,0 +1,43 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Marleny Vega, Claire V. Hammond % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function inputs = parseTreatmentOptimizationDesignVariableBounds( ... + tree, inputs) +inputs.jointPositionsMultiple = parseDoubleOrAlternate(tree, ... + 'joint_positions_multiple', 2); +inputs.jointVelocitiesMultiple = parseDoubleOrAlternate(tree, ... + 'joint_velocities_multiple', 1.5); +inputs.jointAccelerationsMultiple = parseDoubleOrAlternate(tree, ... + 'joint_accelerations_multiple', 1); +inputs.controlJerksMultiple = parseDoubleOrAlternate(tree, ... + 'joint_jerks_multiple', 1); +inputs.maxControlSynergyActivations = parseDoubleOrAlternate(tree, ... + 'synergy_activations_max', 10); +inputs.maxParameterSynergyWeights = parseDoubleOrAlternate(tree, ... + 'synergy_weights_max', 2); +inputs.maxControlTorquesMultiple = parseDoubleOrAlternate(tree, ... + 'torque_controls_max', 1); +end From 27ec0b74e2de2a3d0b5ddca229b31a84f7accac5 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" Date: Tue, 22 Aug 2023 14:33:07 -0700 Subject: [PATCH 003/365] moved muscle folders to single --- src/core/{MuscleModels => muscle}/activeForceLengthCurve.m | 0 src/core/{MuscleCalculations => muscle}/calcMaxIsometricForce.m | 0 src/core/{MuscleCalculations => muscle}/calcMetabolicCost.m | 0 src/core/{MuscleCalculations => muscle}/calcMuscleActivations.m | 0 src/core/{MuscleCalculations => muscle}/calcMuscleExcitations.m | 0 src/core/{MuscleCalculations => muscle}/calcMuscleJointMoments.m | 0 src/core/{MuscleCalculations => muscle}/calcNeuralActivations.m | 0 .../calcNormalizedMuscleFiberLengthsAndVelocities.m | 0 .../{MuscleCalculations => muscle}/calcPassiveForceLengthCurve.m | 0 .../{MuscleCalculations => muscle}/calcPassiveMuscleMoments.m | 0 src/core/{MuscleModels => muscle}/forceVelocityCurve.m | 0 src/core/{MuscleModels => muscle}/passiveForceLengthCurve.m | 0 12 files changed, 0 insertions(+), 0 deletions(-) rename src/core/{MuscleModels => muscle}/activeForceLengthCurve.m (100%) rename src/core/{MuscleCalculations => muscle}/calcMaxIsometricForce.m (100%) rename src/core/{MuscleCalculations => muscle}/calcMetabolicCost.m (100%) rename src/core/{MuscleCalculations => muscle}/calcMuscleActivations.m (100%) rename src/core/{MuscleCalculations => muscle}/calcMuscleExcitations.m (100%) rename src/core/{MuscleCalculations => muscle}/calcMuscleJointMoments.m (100%) rename src/core/{MuscleCalculations => muscle}/calcNeuralActivations.m (100%) rename src/core/{MuscleCalculations => muscle}/calcNormalizedMuscleFiberLengthsAndVelocities.m (100%) rename src/core/{MuscleCalculations => muscle}/calcPassiveForceLengthCurve.m (100%) rename src/core/{MuscleCalculations => muscle}/calcPassiveMuscleMoments.m (100%) rename src/core/{MuscleModels => muscle}/forceVelocityCurve.m (100%) rename src/core/{MuscleModels => muscle}/passiveForceLengthCurve.m (100%) diff --git a/src/core/MuscleModels/activeForceLengthCurve.m b/src/core/muscle/activeForceLengthCurve.m similarity index 100% rename from src/core/MuscleModels/activeForceLengthCurve.m rename to src/core/muscle/activeForceLengthCurve.m diff --git a/src/core/MuscleCalculations/calcMaxIsometricForce.m b/src/core/muscle/calcMaxIsometricForce.m similarity index 100% rename from src/core/MuscleCalculations/calcMaxIsometricForce.m rename to src/core/muscle/calcMaxIsometricForce.m diff --git a/src/core/MuscleCalculations/calcMetabolicCost.m b/src/core/muscle/calcMetabolicCost.m similarity index 100% rename from src/core/MuscleCalculations/calcMetabolicCost.m rename to src/core/muscle/calcMetabolicCost.m diff --git a/src/core/MuscleCalculations/calcMuscleActivations.m b/src/core/muscle/calcMuscleActivations.m similarity index 100% rename from src/core/MuscleCalculations/calcMuscleActivations.m rename to src/core/muscle/calcMuscleActivations.m diff --git a/src/core/MuscleCalculations/calcMuscleExcitations.m b/src/core/muscle/calcMuscleExcitations.m similarity index 100% rename from src/core/MuscleCalculations/calcMuscleExcitations.m rename to src/core/muscle/calcMuscleExcitations.m diff --git a/src/core/MuscleCalculations/calcMuscleJointMoments.m b/src/core/muscle/calcMuscleJointMoments.m similarity index 100% rename from src/core/MuscleCalculations/calcMuscleJointMoments.m rename to src/core/muscle/calcMuscleJointMoments.m diff --git a/src/core/MuscleCalculations/calcNeuralActivations.m b/src/core/muscle/calcNeuralActivations.m similarity index 100% rename from src/core/MuscleCalculations/calcNeuralActivations.m rename to src/core/muscle/calcNeuralActivations.m diff --git a/src/core/MuscleCalculations/calcNormalizedMuscleFiberLengthsAndVelocities.m b/src/core/muscle/calcNormalizedMuscleFiberLengthsAndVelocities.m similarity index 100% rename from src/core/MuscleCalculations/calcNormalizedMuscleFiberLengthsAndVelocities.m rename to src/core/muscle/calcNormalizedMuscleFiberLengthsAndVelocities.m diff --git a/src/core/MuscleCalculations/calcPassiveForceLengthCurve.m b/src/core/muscle/calcPassiveForceLengthCurve.m similarity index 100% rename from src/core/MuscleCalculations/calcPassiveForceLengthCurve.m rename to src/core/muscle/calcPassiveForceLengthCurve.m diff --git a/src/core/MuscleCalculations/calcPassiveMuscleMoments.m b/src/core/muscle/calcPassiveMuscleMoments.m similarity index 100% rename from src/core/MuscleCalculations/calcPassiveMuscleMoments.m rename to src/core/muscle/calcPassiveMuscleMoments.m diff --git a/src/core/MuscleModels/forceVelocityCurve.m b/src/core/muscle/forceVelocityCurve.m similarity index 100% rename from src/core/MuscleModels/forceVelocityCurve.m rename to src/core/muscle/forceVelocityCurve.m diff --git a/src/core/MuscleModels/passiveForceLengthCurve.m b/src/core/muscle/passiveForceLengthCurve.m similarity index 100% rename from src/core/MuscleModels/passiveForceLengthCurve.m rename to src/core/muscle/passiveForceLengthCurve.m From cdcbf30e128bbd9d13ffc98410127a0e29cdbca6 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" Date: Tue, 22 Aug 2023 14:40:35 -0700 Subject: [PATCH 004/365] remove unused calcDerivative and prepareModel --- .../Modeling/prepareModel.m | 52 ------------------- .../TreatmentOptimization/calcDerivative.m | 47 ----------------- 2 files changed, 99 deletions(-) delete mode 100644 src/GroundContactPersonalization/Modeling/prepareModel.m delete mode 100644 src/core/TreatmentOptimization/calcDerivative.m diff --git a/src/GroundContactPersonalization/Modeling/prepareModel.m b/src/GroundContactPersonalization/Modeling/prepareModel.m deleted file mode 100644 index d145d254a..000000000 --- a/src/GroundContactPersonalization/Modeling/prepareModel.m +++ /dev/null @@ -1,52 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function returns an isolated foot model with spring markers and its -% associated kinematics expressed in its seven coordinates (toe angle, -% three hindfoot rotations, three hindfoot translations). -% -% (string, string, string, string, string, string, struct, double, double, -% logical) -> (Model, Array of double, Array of double) -% Create foot model and kinematics in seven coordinates. - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Claire V. Hammond % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function [footModel, footPosition, footVelocity] = prepareModel( ... - modelName, motionFileName, hindfootBodyName, toesBodyName, ... - toesJointName, toesCoordinateName, markerNames, gridWidth, ... - gridHeight, isLeftFoot) - -import org.opensim.modeling.Storage - -model = Model(modelName); -time = findTimeColumn(Storage(motionFileName)); -coordinatesOfInterest = findGCPFreeCoordinates(model, toesBodyName); - -footPosition = makeFootKinematics(model, motionFileName, ... - coordinatesOfInterest, hindfootBodyName, toesCoordinateName); -footVelocity = calcDerivative(time, footPosition); - -footModel = makeFootModel(model, toesJointName); -footModel = addSpringsToModel(footModel, markerNames, gridWidth, ... - gridHeight, hindfootBodyName, toesBodyName, isLeftFoot); -end - diff --git a/src/core/TreatmentOptimization/calcDerivative.m b/src/core/TreatmentOptimization/calcDerivative.m deleted file mode 100644 index ca429a890..000000000 --- a/src/core/TreatmentOptimization/calcDerivative.m +++ /dev/null @@ -1,47 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function calculates the first derivative of the curve provided using -% piecewise polynomials. -% -% (Array of number, array of number) -> (Array of number) -% Returns first derivative - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function [dataDerivative] = calcDerivative(time, data) - -fittingType = fittype('smoothingspline'); -fittingOptions = fitoptions('method', 'SmoothingSpline'); -fittedModel = fit(time, data, fittingType, fittingOptions); -dataCoefficientStructure = fittedModel.p; -% Calculate first derivative of smoothed curve -dataDerivativeCoefficientStructure = dataCoefficientStructure; -dataDerivativeCoefficientStructure.coefs(:,1) = 0; -dataDerivativeCoefficientStructure.coefs(:,2) = ... - 3*dataCoefficientStructure.coefs(:,1); -dataDerivativeCoefficientStructure.coefs(:,3) = ... - 2*dataCoefficientStructure.coefs(:,2); -dataDerivativeCoefficientStructure.coefs(:,4) = ... - dataCoefficientStructure.coefs(:,3); -dataDerivative = ppval(dataDerivativeCoefficientStructure, time); -end \ No newline at end of file From 85172bb3667f8f4d435f4fc9a1d21e2a8f876479 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" Date: Tue, 22 Aug 2023 15:15:03 -0700 Subject: [PATCH 005/365] consolidated dynamic constraint fn --- ...calcDesignOptimizationDynamicsConstraint.m | 36 ------------------- ...puteDesignOptimizationContinuousFunction.m | 6 ++-- ...lcTrackingOptimizationDynamicsConstraint.m | 36 ------------------- ...teTrackingOptimizationContinuousFunction.m | 6 ++-- .../calcDynamicsConstraint.m} | 6 ++-- ...rificationOptimizationContinuousFunction.m | 6 ++-- 6 files changed, 12 insertions(+), 84 deletions(-) delete mode 100644 src/DesignOptimization/calcDesignOptimizationDynamicsConstraint.m delete mode 100644 src/TrackingOptimization/calcTrackingOptimizationDynamicsConstraint.m rename src/{VerificationOptimization/calcVerificationOptimizationDynamicsConstraint.m => TreatmentOptimization/calcDynamicsConstraint.m} (92%) diff --git a/src/DesignOptimization/calcDesignOptimizationDynamicsConstraint.m b/src/DesignOptimization/calcDesignOptimizationDynamicsConstraint.m deleted file mode 100644 index eca7b3af8..000000000 --- a/src/DesignOptimization/calcDesignOptimizationDynamicsConstraint.m +++ /dev/null @@ -1,36 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function calculates the dynamic constraint for design optimization. -% -% (struct, struct) -> (2D matrix) -% Returns the dynamic constraint - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function dynamics = calcDesignOptimizationDynamicsConstraint(values, ... - params) - -dynamics = (params.maxTime - params.minTime) * ... - ([values.stateVelocities values.stateAccelerations ... - values.controlJerks]) ./ (params.maxState - params.minState); -end \ No newline at end of file diff --git a/src/DesignOptimization/computeDesignOptimizationContinuousFunction.m b/src/DesignOptimization/computeDesignOptimizationContinuousFunction.m index f1588cf52..a6b9ee23d 100644 --- a/src/DesignOptimization/computeDesignOptimizationContinuousFunction.m +++ b/src/DesignOptimization/computeDesignOptimizationContinuousFunction.m @@ -2,7 +2,7 @@ % % This function computes the dynamic constraints, path constraints (if any) % and cost function terms (if any) for design optimization. -% +% % (struct) -> (struct) % @@ -35,7 +35,7 @@ modeledValues = calcTorqueBasedModeledValues(values, inputs.auxdata); modeledValues = calcSynergyBasedModeledValues(values, inputs.auxdata, ... modeledValues); -modeledValues.dynamics = calcDesignOptimizationDynamicsConstraint(values, ... +modeledValues.dynamics = calcDynamicConstraint(values, ... inputs.auxdata); if ~isempty(inputs.auxdata.path) modeledValues.path = calcDesignOptimizationPathConstraint(values, ... @@ -43,4 +43,4 @@ end modeledValues.integrand = calcDesignOptimizationIntegrand(values, ... modeledValues, inputs.auxdata); -end \ No newline at end of file +end diff --git a/src/TrackingOptimization/calcTrackingOptimizationDynamicsConstraint.m b/src/TrackingOptimization/calcTrackingOptimizationDynamicsConstraint.m deleted file mode 100644 index d44c31709..000000000 --- a/src/TrackingOptimization/calcTrackingOptimizationDynamicsConstraint.m +++ /dev/null @@ -1,36 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function calculates the dynamic constraint for tracking optimization. -% -% (struct, struct) -> (2D matrix) -% Returns the dynamic constraint - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function dynamics = calcTrackingOptimizationDynamicsConstraint(values, ... - params) - -dynamics = (params.maxTime - params.minTime) * ... - ([values.stateVelocities values.stateAccelerations ... - values.controlJerks]) ./ (params.maxState - params.minState); -end \ No newline at end of file diff --git a/src/TrackingOptimization/computeTrackingOptimizationContinuousFunction.m b/src/TrackingOptimization/computeTrackingOptimizationContinuousFunction.m index b6bd7c732..ebb92f5c7 100644 --- a/src/TrackingOptimization/computeTrackingOptimizationContinuousFunction.m +++ b/src/TrackingOptimization/computeTrackingOptimizationContinuousFunction.m @@ -2,7 +2,7 @@ % % This function computes the dynamic constraints, path constraints (if any) % and cost function terms (if any) for tracking optimization. -% +% % (struct) -> (struct) % @@ -32,10 +32,10 @@ values = getTrackingOptimizationValueStruct(inputs.phase, inputs.auxdata); modeledValues = calcTorqueBasedModeledValues(values, inputs.auxdata); modeledValues = calcSynergyBasedModeledValues(values, inputs.auxdata, modeledValues); -modeledValues.dynamics = calcTrackingOptimizationDynamicsConstraint(values, inputs.auxdata); +modeledValues.dynamics = calcDynamicConstraint(values, inputs.auxdata); path = calcTrackingOptimizationPathConstraint(values, modeledValues, inputs.auxdata); if ~isempty(path) modeledValues.path = path; end modeledValues.integrand = calcTrackingOptimizationIntegrand(values, modeledValues, inputs.auxdata); -end \ No newline at end of file +end diff --git a/src/VerificationOptimization/calcVerificationOptimizationDynamicsConstraint.m b/src/TreatmentOptimization/calcDynamicsConstraint.m similarity index 92% rename from src/VerificationOptimization/calcVerificationOptimizationDynamicsConstraint.m rename to src/TreatmentOptimization/calcDynamicsConstraint.m index 6427cf5f5..c05dc87ba 100644 --- a/src/VerificationOptimization/calcVerificationOptimizationDynamicsConstraint.m +++ b/src/TreatmentOptimization/calcDynamicsConstraint.m @@ -1,6 +1,6 @@ % This function is part of the NMSM Pipeline, see file for full license. % -% This function calculates the dynamic constraint for verification +% This function calculates the dynamic constraint for treatment % optimization. % % (struct, struct) -> (2D matrix) @@ -28,10 +28,10 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function dynamics = calcVerificationOptimizationDynamicsConstraint(values, ... +function dynamics = calcDynamicConstraint(values, ... params) dynamics = (params.maxTime - params.minTime) * ... ([values.stateVelocities values.stateAccelerations ... values.controlJerks]) ./ (params.maxState - params.minState); -end \ No newline at end of file +end diff --git a/src/VerificationOptimization/computeVerificationOptimizationContinuousFunction.m b/src/VerificationOptimization/computeVerificationOptimizationContinuousFunction.m index 614800536..773f5e614 100644 --- a/src/VerificationOptimization/computeVerificationOptimizationContinuousFunction.m +++ b/src/VerificationOptimization/computeVerificationOptimizationContinuousFunction.m @@ -2,7 +2,7 @@ % % This function computes the dynamic constraints, path constraints (if any) % and cost function terms (if any) for verification optimization. -% +% % (struct) -> (struct) % @@ -33,11 +33,11 @@ values = getVerificationOptimizationValueStruct(inputs.phase, inputs.auxdata); modeledValues = calcTorqueBasedModeledValues(values, inputs.auxdata); modeledValues = calcSynergyBasedModeledValues(values, inputs.auxdata, modeledValues); -modeledValues.dynamics = calcVerificationOptimizationDynamicsConstraint(values, inputs.auxdata); +modeledValues.dynamics = calcDynamicConstraint(values, inputs.auxdata); path = calcVerificationOptimizationPathConstraint(values, modeledValues, inputs.auxdata); if ~isempty(path) modeledValues.path = path; end modeledValues.integrand = calcVerificationOptimizationIntegrand(values, ... modeledValues, inputs.auxdata); -end \ No newline at end of file +end From c7888640c48e3e7741ea5feda4a86f66db22438c Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" Date: Tue, 22 Aug 2023 17:17:04 -0700 Subject: [PATCH 006/365] abstracted optimal control solver settings --- .../getOptimalControlSolverSettings.m | 80 ------------------- .../optimal_control/getGpopsSolverSettings.m | 79 ++++++++++++++++++ .../getOptimalControlSolverSettings.m | 37 +++++++++ src/core/parse/parseTextOrAlternate.m | 30 +++++++ 4 files changed, 146 insertions(+), 80 deletions(-) delete mode 100644 src/core/TreatmentOptimization/OptimalControlSetupFunctions/getOptimalControlSolverSettings.m create mode 100644 src/core/optimal_control/getGpopsSolverSettings.m create mode 100644 src/core/optimal_control/getOptimalControlSolverSettings.m create mode 100644 src/core/parse/parseTextOrAlternate.m diff --git a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/getOptimalControlSolverSettings.m b/src/core/TreatmentOptimization/OptimalControlSetupFunctions/getOptimalControlSolverSettings.m deleted file mode 100644 index 86d37a3c4..000000000 --- a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/getOptimalControlSolverSettings.m +++ /dev/null @@ -1,80 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function reads the optimal control settings (GPOPS-II based) from a -% separate XML file. -% -% (struct) -> (Array of string) -% Optimal control solver settings are loaded - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function solverSettings = getOptimalControlSolverSettings(settingsFileName) -solverSettingsTree = xml2struct(settingsFileName); -solverSettings.optimizationFileName = 'trackingOptimizationOutputFile.txt'; -solverSettings.derivativeSupplier = getTextFromField( ... - getFieldByNameOrAlternate(solverSettingsTree, 'setup_derivatives_supplier', 'sparseFD')); -solverSettings.derivativeLevel = getTextFromField( ... - getFieldByNameOrAlternate(solverSettingsTree, 'setup_derivatives_level', 'first')); -solverSettings.derivativeDependencies = getTextFromField( ... - getFieldByNameOrAlternate(solverSettingsTree, 'setup_derivatives_dependencies', 'sparse')); -solverSettings.scaleMethods = getTextFromField( ... - getFieldByNameOrAlternate(solverSettingsTree, 'setup_scales_method', 'none')); -solverSettings.method = getTextFromField( ... - getFieldByNameOrAlternate(solverSettingsTree, 'setup_method', 'RPM-Differentiation')); -solverSettings.meshMethod = getTextFromField( ... - getFieldByNameOrAlternate(solverSettingsTree, 'setup_mesh_method', 'hp-PattersonRao')); -solverSettings.meshTolerance = str2double(getTextFromField( ... - getFieldByNameOrAlternate(solverSettingsTree, 'setup_mesh_tolerance', '10e-3'))); -solverSettings.meshMaxIterations = str2double(getTextFromField( ... - getFieldByNameOrAlternate(solverSettingsTree, 'setup_mesh_max_iterations', '10'))); -solverSettings.meshColpointsMin = str2double(getTextFromField( ... - getFieldByNameOrAlternate(solverSettingsTree, 'setup_mesh_colpoints_min', '3'))); -solverSettings.meshColpointsMax = str2double(getTextFromField( ... - getFieldByNameOrAlternate(solverSettingsTree, 'setup_mesh_colpoints_max', '10'))); -solverSettings.meshSplitMult = str2double(getTextFromField( ... - getFieldByNameOrAlternate(solverSettingsTree, 'setup_mesh_splitmult', '1.2'))); -solverSettings.meshCurveRatio = str2double(getTextFromField( ... - getFieldByNameOrAlternate(solverSettingsTree, 'setup_mesh_curveratio', '2'))); -solverSettings.meshR = str2double(getTextFromField( ... - getFieldByNameOrAlternate(solverSettingsTree, 'setup_mesh_R', '1.2'))); -solverSettings.meshSigma = str2double(getTextFromField( ... - getFieldByNameOrAlternate(solverSettingsTree, 'setup_mesh_sigma', '0.5'))); -solverSettings.numCollocationPoints = str2double(getTextFromField( ... - getFieldByNameOrAlternate(solverSettingsTree, 'setup_mesh_phase_intervals', '6'))); -solverSettings.numIntervals = str2double(getTextFromField( ... - getFieldByNameOrAlternate(solverSettingsTree, 'setup_mesh_phase_colpoints_per_Interval', '10'))); -solverSettings.solverType = getTextFromField(getFieldByNameOrAlternate( ... - solverSettingsTree, 'setup_nlp_solver', 'ipopt')); -solverSettings.linearSolverType = getTextFromField( ... - getFieldByNameOrAlternate(solverSettingsTree, 'setup_nlp_linear_solver', 'ma57')); -solverSettings.solverTolerance = str2double(getTextFromField( ... - getFieldByNameOrAlternate(solverSettingsTree, 'setup_nlp_tolerance', '1e-3'))); -solverSettings.stepSize = str2double(getTextFromField( ... - getFieldByNameOrAlternate(solverSettingsTree, 'setup_nlp_step_size', '1e-8'))); -solverSettings.maxIterations = str2double(getTextFromField( ... - getFieldByNameOrAlternate(solverSettingsTree, 'setup_nlp_max_iterations', '2e4'))); -solverSettings.displayLevel = str2double(getTextFromField( ... - getFieldByNameOrAlternate(solverSettingsTree, 'setup_display_level', '2'))); -solverSettings.integralBound = str2double( ... - parseElementTextByNameOrAlternate(solverSettingsTree, "integral_bound", "1")); -end \ No newline at end of file diff --git a/src/core/optimal_control/getGpopsSolverSettings.m b/src/core/optimal_control/getGpopsSolverSettings.m new file mode 100644 index 000000000..be91fa653 --- /dev/null +++ b/src/core/optimal_control/getGpopsSolverSettings.m @@ -0,0 +1,79 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function reads the optimal control settings (GPOPS-II based) from a +% separate XML file. +% +% (struct) -> (Array of string) +% Optimal control solver settings are loaded + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Marleny Vega, Claire V. Hammond % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function solverSettings = getGpopsSolverSettings(settingsTree) +solverSettings.optimizationFileName = 'trackingOptimizationOutputFile.txt'; +solverSettings.derivativeSupplier = parseTextOrAlternate( ... + settingsTree, 'setup_derivatives_supplier', 'sparseFD'); +solverSettings.derivativeLevel = parseTextOrAlternate( ... + settingsTree, 'setup_derivatives_level', 'first'); +solverSettings.derivativeDependencies = parseTextOrAlternate( ... + settingsTree, 'setup_derivatives_dependencies', 'sparse'); +solverSettings.scaleMethods = parseTextOrAlternate( ... + settingsTree, 'setup_scales_method', 'none'); +solverSettings.method = parseTextOrAlternate( ... + settingsTree, 'setup_method', 'RPM-Differentiation'); +solverSettings.meshMethod = parseTextOrAlternate( ... + settingsTree, 'setup_mesh_method', 'hp-PattersonRao'); +solverSettings.meshTolerance = parseDoubleOrAlternate( ... + settingsTree, 'setup_mesh_tolerance', 10e-3); +solverSettings.meshMaxIterations = parseDoubleOrAlternate( ... + settingsTree, 'setup_mesh_max_iterations', 10); +solverSettings.meshColpointsMin = parseDoubleOrAlternate( ... + settingsTree, 'setup_mesh_colpoints_min', 3); +solverSettings.meshColpointsMax = parseDoubleOrAlternate( ... + settingsTree, 'setup_mesh_colpoints_max', 10); +solverSettings.meshSplitMult = parseDoubleOrAlternate( ... + settingsTree, 'setup_mesh_splitmult', 1.2); +solverSettings.meshCurveRatio = parseDoubleOrAlternate( ... + settingsTree, 'setup_mesh_curveratio', 2); +solverSettings.meshR = parseDoubleOrAlternate( ... + settingsTree, 'setup_mesh_R', 1.2); +solverSettings.meshSigma = parseDoubleOrAlternate( ... + settingsTree, 'setup_mesh_sigma', 0.5); +solverSettings.numCollocationPoints = parseDoubleOrAlternate( ... + settingsTree, 'setup_mesh_phase_intervals', 6); +solverSettings.numIntervals = parseDoubleOrAlternate( ... + settingsTree, 'setup_mesh_phase_colpoints_per_Interval', 10); +solverSettings.solverType = parseTextOrAlternate( ... + settingsTree, 'setup_nlp_solver', 'ipopt')); +solverSettings.linearSolverType = parseTextOrAlternate( ... + settingsTree, 'setup_nlp_linear_solver', 'ma57')); +solverSettings.solverTolerance = parseDoubleOrAlternate( ... + settingsTree, 'setup_nlp_tolerance', 1e-3))); +solverSettings.stepSize = parseDoubleOrAlternate( ... + settingsTree, 'setup_nlp_step_size', 1e-8))); +solverSettings.maxIterations = parseDoubleOrAlternate( ... + settingsTree, 'setup_nlp_max_iterations', 2e4))); +solverSettings.displayLevel = parseDoubleOrAlternate( ... + settingsTree, 'setup_display_level', 2))); +solverSettings.integralBound = parseDoubleOrAlternate( ... + settingsTree, "integral_bound", 1)); +end diff --git a/src/core/optimal_control/getOptimalControlSolverSettings.m b/src/core/optimal_control/getOptimalControlSolverSettings.m new file mode 100644 index 000000000..eace6055c --- /dev/null +++ b/src/core/optimal_control/getOptimalControlSolverSettings.m @@ -0,0 +1,37 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Claire V. Hammond % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function solverSettings = getOptimalControlSolverSettings(settingsFileName) +solverSettingsTree = xml2struct(settingsFileName); +verifyVersion(solverSettingsTree, "OptimalControlSolverSettings"); +tree = solverSettingsTree.NMSMPipelineDocument; +if isfield(tree, 'GpopsSettings') + solverSettings = getGpopsSolverSettings(tree); +end +if isfield(tree, 'MocoSettings') + solverSettings = getMocoSolverSettings(tree); +end +end diff --git a/src/core/parse/parseTextOrAlternate.m b/src/core/parse/parseTextOrAlternate.m new file mode 100644 index 000000000..4d76cdeac --- /dev/null +++ b/src/core/parse/parseTextOrAlternate.m @@ -0,0 +1,30 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Claire V. Hammond % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function text = parseTextOrAlternate(tree, field, alternate) +text = getTextFromField( ... + getFieldByNameOrAlternate(tree, field, alternate)); +end From 81e11cac47f6fe11d8538d9e40058417e875b07f Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" Date: Tue, 22 Aug 2023 17:27:10 -0700 Subject: [PATCH 007/365] added shared params parsing --- .../parseDesignOptimizationSettingsTree.m | 6 +--- .../parseTrackingOptimizationSettingsTree.m | 6 +--- ...arseVerificationOptimizationSettingsTree.m | 6 +--- .../parse/parseTreatmentOptimizationParams.m | 30 +++++++++++++++++++ 4 files changed, 33 insertions(+), 15 deletions(-) create mode 100644 src/core/parse/parseTreatmentOptimizationParams.m diff --git a/src/DesignOptimization/parseDesignOptimizationSettingsTree.m b/src/DesignOptimization/parseDesignOptimizationSettingsTree.m index 101433646..49b752ffc 100644 --- a/src/DesignOptimization/parseDesignOptimizationSettingsTree.m +++ b/src/DesignOptimization/parseDesignOptimizationSettingsTree.m @@ -36,7 +36,7 @@ inputs = parseDesignSettings(settingsTree, inputs); inputs = getInputs(settingsTree); inputs.toolName = "DesignOptimization"; -params = getParams(settingsTree); +params = parseTreatmentOptimizationParams(settingsTree); inputs = modifyModelForces(inputs); inputs = updateMuscleModelProperties(inputs); end @@ -76,7 +76,3 @@ end end -function params = getParams(tree) -params.solverSettings = getOptimalControlSolverSettings(... - getTextFromField(getFieldByName(tree, 'optimal_control_settings_file'))); -end diff --git a/src/TrackingOptimization/parseTrackingOptimizationSettingsTree.m b/src/TrackingOptimization/parseTrackingOptimizationSettingsTree.m index 2c6daf04b..f66926578 100644 --- a/src/TrackingOptimization/parseTrackingOptimizationSettingsTree.m +++ b/src/TrackingOptimization/parseTrackingOptimizationSettingsTree.m @@ -34,11 +34,7 @@ inputs = parseTreatmentOptimizationDesignVariableBounds(settingsTree, ... inputs); inputs.toolName = "TrackingOptimization"; -params = getParams(settingsTree); +params = parseTreatmentOptimizationParams(settingsTree); inputs = modifyModelForces(inputs); end -function params = getParams(tree) -params.solverSettings = getOptimalControlSolverSettings(... - getTextFromField(getFieldByName(tree, 'optimal_control_settings_file'))); -end diff --git a/src/VerificationOptimization/parseVerificationOptimizationSettingsTree.m b/src/VerificationOptimization/parseVerificationOptimizationSettingsTree.m index 4147dd789..12d6877c8 100644 --- a/src/VerificationOptimization/parseVerificationOptimizationSettingsTree.m +++ b/src/VerificationOptimization/parseVerificationOptimizationSettingsTree.m @@ -35,7 +35,7 @@ inputs); inputs = getInputs(settingsTree); inputs.toolName = "VerificationOptimization"; -params = getParams(settingsTree); +params = parseTreatmentOptimizationParams(settingsTree); inputs = modifyModelForces(inputs); end @@ -47,7 +47,3 @@ end end -function params = getParams(tree) -params.solverSettings = getOptimalControlSolverSettings(... - getTextFromField(getFieldByName(tree, 'optimal_control_settings_file'))); -end diff --git a/src/core/parse/parseTreatmentOptimizationParams.m b/src/core/parse/parseTreatmentOptimizationParams.m new file mode 100644 index 000000000..4d7d32343 --- /dev/null +++ b/src/core/parse/parseTreatmentOptimizationParams.m @@ -0,0 +1,30 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Claire V. Hammond % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function params = parseTreatmentOptimizationParams(tree) +params.solverSettings = getOptimalControlSolverSettings(... + getTextFromField(getFieldByName(tree, 'optimal_control_settings_file'))); +end From e45d3bf829ebd050a694bb1e804dfa57dea9e6b9 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" Date: Thu, 24 Aug 2023 19:07:53 -0700 Subject: [PATCH 008/365] added first pass of common optimal control solver --- .../TrackingOptimization.m | 2 +- .../TrackingOptimizationTool.m | 5 ++- .../solveOptimalControlProblem.m | 43 +++++++++++++++++++ 3 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 src/TreatmentOptimization/solveOptimalControlProblem.m diff --git a/src/TrackingOptimization/TrackingOptimization.m b/src/TrackingOptimization/TrackingOptimization.m index 3521a8da6..3204fd164 100644 --- a/src/TrackingOptimization/TrackingOptimization.m +++ b/src/TrackingOptimization/TrackingOptimization.m @@ -1,6 +1,6 @@ % This function is part of the NMSM Pipeline, see file for full license. % -% This function sets up the input variables and mex files (or parallel +% This function sets up the input variables and mex files (or parallel % matlab function) for the main function of tracking optimization. % % (struct, struct) -> (struct, struct) diff --git a/src/TrackingOptimization/TrackingOptimizationTool.m b/src/TrackingOptimization/TrackingOptimizationTool.m index 885dcd756..3632f8ed3 100644 --- a/src/TrackingOptimization/TrackingOptimizationTool.m +++ b/src/TrackingOptimization/TrackingOptimizationTool.m @@ -15,7 +15,7 @@ % National Institutes of Health (R01 EB030520). % % % % Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % +% Author(s): Marleny Vega, Claire V. Hammond % % % % Licensed under the Apache License, Version 2.0 (the "License"); % % you may not use this file except in compliance with the License. % @@ -33,7 +33,8 @@ function TrackingOptimizationTool(settingsFileName) settingsTree = xml2struct(settingsFileName); verifyVersion(settingsTree, "TrackingOptimizationTool"); [inputs, params] = parseTrackingOptimizationSettingsTree(settingsTree); -[outputs, inputs] = TrackingOptimization(inputs, params); +inputs = makeTreatmentOptimizationInputs(inputs, params); +outputs = solveOptimalControlProblem(inputs, params); reportTreatmentOptimizationResults(outputs, inputs); saveTrackingOptimizationResults(outputs, inputs); end diff --git a/src/TreatmentOptimization/solveOptimalControlProblem.m b/src/TreatmentOptimization/solveOptimalControlProblem.m new file mode 100644 index 000000000..c7cbc124c --- /dev/null +++ b/src/TreatmentOptimization/solveOptimalControlProblem.m @@ -0,0 +1,43 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Claire V. Hammond % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function solution = solveOptimalControlProblem(inputs, params) { + switch inputs.solver + case 'gpops' + setup = convertToGpopsInputs(inputs, params); + solution = gpops2(setup); + solution = convertFromGpopsOutputs(solution, inputs, params); + case 'moco' + setup = convertToMocoInputs(inputs, params); + solution = moco(setup); + solution = convertFromMocoOutputs(solution, inputs, params); + otherwise + MException('solveOptimalControlProblem:invalidSolver', ... + 'Invalid solver specified.'); + end +} + + From 78b414e69aefbca498eba46103c3563ddfa9d4f1 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" Date: Thu, 24 Aug 2023 19:13:04 -0700 Subject: [PATCH 009/365] updated solve routing logic --- .../solveOptimalControlProblem.m | 36 +++++++++++++++---- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/src/TreatmentOptimization/solveOptimalControlProblem.m b/src/TreatmentOptimization/solveOptimalControlProblem.m index c7cbc124c..e86b96557 100644 --- a/src/TreatmentOptimization/solveOptimalControlProblem.m +++ b/src/TreatmentOptimization/solveOptimalControlProblem.m @@ -24,20 +24,42 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function solution = solveOptimalControlProblem(inputs, params) { +function solution = solveOptimalControlProblem(inputs, params) +if strcmp(inputs.problemType, "SynergyDriven") switch inputs.solver case 'gpops' - setup = convertToGpopsInputs(inputs, params); + setup = convertToGpopsSynergyDrivenInputs(inputs, params); solution = gpops2(setup); - solution = convertFromGpopsOutputs(solution, inputs, params); + solution = convertFromGpopsSynergyDrivenOutputs(solution, ... + inputs, params); case 'moco' - setup = convertToMocoInputs(inputs, params); + setup = convertToMocoSynergyDrivenInputs(inputs, params); solution = moco(setup); - solution = convertFromMocoOutputs(solution, inputs, params); + solution = convertFromMocoSynergyDrivenOutputs(solution, ... + inputs, params); otherwise MException('solveOptimalControlProblem:invalidSolver', ... 'Invalid solver specified.'); end -} - +elseif strcmp(inputs.problemType, "TorqueDriven") + switch inputs.solver + case 'gpops' + setup = convertToGpopsTorqueDrivenInputs(inputs, params); + solution = gpops2(setup); + solution = convertFromGpopsTorqueDrivenOutputs(solution, ... + inputs, params); + case 'moco' + setup = convertToMocoTorqueDrivenInputs(inputs, params); + solution = moco(setup); + solution = convertFromMocoTorqueDrivenOutputs(solution, ... + inputs, params); + otherwise + MException('solveOptimalControlProblem:invalidSolver', ... + 'Invalid solver specified.'); + end +else + MException('solveOptimalControlProblem:invalidProblemType', ... + 'Invalid problem type specified.'); +end +end From 6b1b2840ab67acacde80fdb74ad4bcd3e1e1e9cb Mon Sep 17 00:00:00 2001 From: Marleny Vega Date: Fri, 25 Aug 2023 14:32:42 -0500 Subject: [PATCH 010/365] Fixed vector shapes in calcSynergyBasedModeledValues --- src/TrackingOptimization/calcSynergyBasedModeledValues.m | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/TrackingOptimization/calcSynergyBasedModeledValues.m b/src/TrackingOptimization/calcSynergyBasedModeledValues.m index d5b33778e..668a151ef 100644 --- a/src/TrackingOptimization/calcSynergyBasedModeledValues.m +++ b/src/TrackingOptimization/calcSynergyBasedModeledValues.m @@ -39,6 +39,9 @@ [params.muscleTendonLength, params.momentArms, ... params.muscleTendonVelocity] = calcSurrogateModel(params, ... jointAngles, jointVelocities); + params.muscleTendonLength = permute(params.muscleTendonLength, [3 2 1]); + params.muscleTendonVelocity = permute(params.muscleTendonVelocity, [3 2 1]); + params.momentArms = permute(params.momentArms, [ 4 2 3 1]); [modeledValues.normalizedFiberLength, modeledValues.normalizedFiberVelocity] = ... calcNormalizedMuscleFiberLengthsAndVelocities(params, ... ones(1, params.numMuscles), ones(1, params.numMuscles)); @@ -46,15 +49,18 @@ muscleJointMoments = calcMuscleJointMoments(params, ... modeledValues.muscleActivations, modeledValues.normalizedFiberLength, ... modeledValues.normalizedFiberVelocity); - modeledValues.muscleJointMoments = muscleJointMoments(:, ... + modeledValues.muscleJointMoments = permute(muscleJointMoments, [3 2 1]); + modeledValues.muscleJointMoments = modeledValues.muscleJointMoments(:, ... params.surrogateModelIndex); modeledValues.muscleJointMoments = modeledValues.muscleJointMoments(:, ... params.dofsActuatedIndex); + modeledValues.muscleActivations = permute(modeledValues.muscleActivations, [3 2 1]); end end function muscleActivations = calcMuscleActivationFromSynergies(values) muscleActivations = values.controlSynergyActivations * values.synergyWeights; +muscleActivations = permute(muscleActivations, [3 2 1]); end function [jointAngles, jointVelocities] = getMuscleActuatedDOFs(values, params) From 54bdced8ad224e39db7dd45da3e22d3d627a157f Mon Sep 17 00:00:00 2001 From: Marleny Vega Date: Fri, 25 Aug 2023 14:34:00 -0500 Subject: [PATCH 011/365] Updated surrogate model creation to check for muscle tendon velocities --- .../SurrogateModelCreation.m | 6 ++++ .../getPolynomialExpressions.m | 30 ++++++++++++++----- .../reportSurrogateModel.m | 27 ++++++++++++++--- 3 files changed, 52 insertions(+), 11 deletions(-) diff --git a/src/SurrogateModelCreation/SurrogateModelCreation.m b/src/SurrogateModelCreation/SurrogateModelCreation.m index 48034c28a..fbb29c822 100644 --- a/src/SurrogateModelCreation/SurrogateModelCreation.m +++ b/src/SurrogateModelCreation/SurrogateModelCreation.m @@ -60,6 +60,12 @@ function SurrogateModelCreation(inputs) inputs.experimentalJointAngles = ... reshape(permute(inputs.experimentalJointAngles, [1 3 2]), [], ... length(inputs.coordinateNames)); +experimentalTime = parseTimeColumn(findFileListFromPrefixList(... + fullfile(inputs.dataDirectory, "IKData"), prefixes))'; +for i = 1:size(inputs.experimentalJointAngles, 2) + [~, inputs.experimentalJointVelocities(:, i)] = SplineSmooth( ... + experimentalTime, inputs.experimentalJointAngles(:, i), 1); +end directories = findFirstLevelSubDirectoriesFromPrefixes(fullfile( ... inputs.dataDirectory, "MAData"), prefixes); diff --git a/src/SurrogateModelCreation/getPolynomialExpressions.m b/src/SurrogateModelCreation/getPolynomialExpressions.m index 34e542a1e..4a9b01d3f 100644 --- a/src/SurrogateModelCreation/getPolynomialExpressions.m +++ b/src/SurrogateModelCreation/getPolynomialExpressions.m @@ -54,21 +54,37 @@ children(expand(polynomialExpressionMuscleTendonLength)); polynomialExpressionMuscleTendonLength = ... cat(2, polynomialExpressionMuscleTendonLength{:}); +% Remove all coefficients +for i = 1 : size(polynomialExpressionMuscleTendonLength, 2) + polynomialExpressionMuscleTendonLength(i) = ... + polynomialExpressionMuscleTendonLength(i) / ... + coeffs(polynomialExpressionMuscleTendonLength(i)); +end % Differentiate muscle tendon length w.r.t. time for i = 1 : size(jointAngles, 2) for j = 1 : size(polynomialExpressionMuscleTendonLength, 2) syms(sprintf('theta%d(t)', i)) theta_t(i) = symfun(eval(sprintf('theta%d(t)', i)), t); - tempPolynomialExpressionMuscleTendonVelocity = subs(polynomialExpressionMuscleTendonLength(1, j), theta(i), theta_t(i)); - tempPolynomialExpressionMuscleTendonVelocity = diff(tempPolynomialExpressionMuscleTendonVelocity, t); - tempPolynomialExpressionMuscleTendonVelocity = subs(tempPolynomialExpressionMuscleTendonVelocity, diff(theta_t(i), t), thetaDot(i)); - polynomialExpressionMuscleTendonVelocity(i, j) = subs(tempPolynomialExpressionMuscleTendonVelocity, theta_t(i), theta(i)); + tempPolynomialExpressionMuscleTendonVelocity = ... + subs(polynomialExpressionMuscleTendonLength(1, j), ... + theta(i), theta_t(i)); + tempPolynomialExpressionMuscleTendonVelocity = ... + diff(tempPolynomialExpressionMuscleTendonVelocity, t); + tempPolynomialExpressionMuscleTendonVelocity = ... + subs(tempPolynomialExpressionMuscleTendonVelocity, ... + diff(theta_t(i), t), thetaDot(i)); + polynomialExpressionMuscleTendonVelocity(i, j) = ... + subs(tempPolynomialExpressionMuscleTendonVelocity, ... + theta_t(i), theta(i)); end end -polynomialExpressionMuscleTendonVelocities = polynomialExpressionMuscleTendonVelocity(1, :); -for i = 1 : size(jointAngles, 2) +polynomialExpressionMuscleTendonVelocities = ... + polynomialExpressionMuscleTendonVelocity(1, :); +for i = 2 : size(jointAngles, 2) for j = 1 : size(polynomialExpressionMuscleTendonLength, 2) - polynomialExpressionMuscleTendonVelocities(1, j) = polynomialExpressionMuscleTendonVelocity(i, j) + polynomialExpressionMuscleTendonVelocities(1, j); + polynomialExpressionMuscleTendonVelocities(1, j) = ... + polynomialExpressionMuscleTendonVelocity(i, j) + ... + polynomialExpressionMuscleTendonVelocities(1, j); end end % Differentiate -muscle tendon length w.r.t. associated joint angle diff --git a/src/SurrogateModelCreation/reportSurrogateModel.m b/src/SurrogateModelCreation/reportSurrogateModel.m index e8e9243d3..34856db83 100644 --- a/src/SurrogateModelCreation/reportSurrogateModel.m +++ b/src/SurrogateModelCreation/reportSurrogateModel.m @@ -27,14 +27,14 @@ function reportSurrogateModel(inputs) -[newMuscleTendonLengths, newMomentArms] = ... +[newMuscleTendonLengths, newMomentArms, newMuscleTendonVelocities] = ... calcSurrogateModel(inputs, inputs.muscleSpecificJointAngles, ... - inputs.muscleSpecificJointAngles); + inputs.muscleSpecificJointVelocities); plotSurrogateModelFitting(inputs, newMuscleTendonLengths, ... - newMomentArms); + newMomentArms, newMuscleTendonVelocities); end function plotSurrogateModelFitting(inputs, newMuscleTendonLengths, ... - newMomentArms) + newMomentArms, newMuscleTendonVelocities) nplots = ceil(sqrt(inputs.numMuscles)); % Plot muscle tendon lengths @@ -55,6 +55,25 @@ function plotSurrogateModelFitting(inputs, newMuscleTendonLengths, ... end end +nplots = ceil(sqrt(inputs.numMuscles)); +% Plot muscle tendon velocities +figure('name', 'Muscle Tendon Velocities') +for i = 1 : inputs.numMuscles +subplot(nplots, nplots, i) +plotData(inputs.muscleTendonVelocities(:, i), newMuscleTendonVelocities(:, i), ... + inputs.muscleNames{i}) +axis([1 size(inputs.muscleTendonVelocities, 1) min(inputs.muscleTendonVelocities, [], ... + 'all') max(inputs.muscleTendonVelocities, [], 'all')]) +if i > inputs.numMuscles - nplots; xlabel('Data Points'); +else xticklabels(''); end +if ismember(i, 1 : nplots : inputs.numMuscles) + ylabel({'Muscle','Tendon Velocities'}); +else yticklabels(''); end +if i == inputs.numMuscles + legend('Original', 'Predicted'); +end +end + % Plot moment arms for j = 1 : length(inputs.surrogateModelCoordinateNames) figure('name', 'Moment Arms') From e4f7977eea22b5e739afa57a9849d5cbbd65a91e Mon Sep 17 00:00:00 2001 From: Marleny Vega Date: Fri, 25 Aug 2023 14:34:58 -0500 Subject: [PATCH 012/365] Fixed free final time --- src/DesignOptimization/DesignOptimization.m | 2 +- .../VerificationOptimization.m | 2 +- .../IntegrandTerms/calcTrackingControllerIntegrand.m | 6 +++--- .../IntegrandTerms/calcTrackingCoordinateIntegrand.m | 4 ++-- .../calcTrackingExternalForcesIntegrand.m | 4 ++-- .../calcTrackingExternalMomentsIntegrand.m | 4 ++-- .../calcTrackingInverseDynamicLoadsIntegrand.m | 4 ++-- .../calcTrackingMuscleActivationIntegrand.m | 4 ++-- src/core/TreatmentOptimization/getSplines.m | 10 +++++----- 9 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/DesignOptimization/DesignOptimization.m b/src/DesignOptimization/DesignOptimization.m index df507bcaa..9651dd5b2 100644 --- a/src/DesignOptimization/DesignOptimization.m +++ b/src/DesignOptimization/DesignOptimization.m @@ -40,7 +40,7 @@ output = computeDesignOptimizationMainFunction(inputs, params); end function inputs = setupMuscleSynergies(inputs) -inputs.splineSynergyActivations = spaps(inputs.initialGuess.time, ... +inputs.splineSynergyActivations = spaps(inputs.initialGuess.time/inputs.initialGuess.time(end), ... inputs.initialGuess.control(:, inputs.numCoordinates + 1 : ... inputs.numCoordinates + inputs.numSynergies)', 0.0000001); inputs.synergyLabels = inputs.initialGuess.controlLabels(:, ... diff --git a/src/VerificationOptimization/VerificationOptimization.m b/src/VerificationOptimization/VerificationOptimization.m index cbfbd3989..1253f2f67 100644 --- a/src/VerificationOptimization/VerificationOptimization.m +++ b/src/VerificationOptimization/VerificationOptimization.m @@ -38,7 +38,7 @@ end function inputs = setupMuscleSynergies(inputs) -inputs.splineSynergyActivations = spaps(inputs.initialGuess.time, ... +inputs.splineSynergyActivations = spaps(inputs.initialGuess.time/inputs.initialGuess.time(end), ... inputs.initialGuess.control(:, inputs.numCoordinates + 1:end)', 0.0000001); inputs.synergyLabels = inputs.initialGuess.controlLabels(:, inputs.numCoordinates + 1:end); end diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m b/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m index 8f22e433a..63528f5b3 100644 --- a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m +++ b/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m @@ -38,7 +38,7 @@ indx = find(strcmp(convertCharsToStrings( ... auxdata.synergyLabels), controllerName)); synergyActivations = ... - fnval(auxdata.splineSynergyActivations, time)'; + fnval(auxdata.splineSynergyActivations, time/time(end))'; cost = calcTrackingCostArrayTerm(synergyActivations, ... values.controlSynergyActivations, indx); case 'torque_driven' @@ -49,10 +49,10 @@ controllerName)); if auxdata.splineJointMoments.dim > 1 experimentalJointMoments = ... - fnval(auxdata.splineJointMoments, time)'; + fnval(auxdata.splineJointMoments, time/time(end))'; else experimentalJointMoments = ... - fnval(auxdata.splineJointMoments, time); + fnval(auxdata.splineJointMoments, time/time(end)); end cost = experimentalJointMoments(:, indx1) - ... values.controlTorques(:, indx2); diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m b/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m index 7c3049d32..78203dd10 100644 --- a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m +++ b/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m @@ -34,9 +34,9 @@ indx = find(strcmp(convertCharsToStrings(auxdata.coordinateNames), ... coordinateName)); if auxdata.splineJointAngles.dim > 1 - experimentalJointAngles = fnval(auxdata.splineJointAngles, time)'; + experimentalJointAngles = fnval(auxdata.splineJointAngles, time/time(end))'; else - experimentalJointAngles = fnval(auxdata.splineJointAngles, time); + experimentalJointAngles = fnval(auxdata.splineJointAngles, time/time(end)); end cost = calcTrackingCostArrayTerm(experimentalJointAngles, ... diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m b/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m index 377a5d0ae..b81582f1d 100644 --- a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m +++ b/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m @@ -38,11 +38,11 @@ if params.splineExperimentalGroundReactionForces{i}.dim > 1 experimentalGroundReactions = ... fnval(params.splineExperimentalGroundReactionForces{i}, ... - time)'; + time/time(end))'; else experimentalGroundReactions = ... fnval(params.splineExperimentalGroundReactionForces{i}, ... - time); + time/time(end)); end cost = calcTrackingCostArrayTerm(... experimentalGroundReactions, groundReactionsForces{i}, ... diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m b/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m index b903723a4..02e4be838 100644 --- a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m +++ b/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m @@ -38,11 +38,11 @@ if params.splineExperimentalGroundReactionMoments{i}.dim > 1 experimentalGroundReactions = ... fnval(params.splineExperimentalGroundReactionMoments{i}, ... - time)'; + time/time(end))'; else experimentalGroundReactions = ... fnval(params.splineExperimentalGroundReactionMoments{i}, ... - time); + time/time(end)); end cost = calcTrackingCostArrayTerm(... experimentalGroundReactions, groundReactionMoments{i}, ... diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m b/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m index 9fb687f49..f60ea4e0e 100644 --- a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m +++ b/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m @@ -37,9 +37,9 @@ loadName)); if params.splineJointMoments.dim > 1 - experimentalJointMoments = fnval(params.splineJointMoments, time)'; + experimentalJointMoments = fnval(params.splineJointMoments, time/time(end))'; else - experimentalJointMoments = fnval(params.splineJointMoments, time); + experimentalJointMoments = fnval(params.splineJointMoments, time/time(end)); end momentLabelsNoSuffix = erase(params.inverseDynamicMomentLabels, '_moment'); diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m b/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m index 392789b9c..c5810a7fd 100644 --- a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m +++ b/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m @@ -36,10 +36,10 @@ if params.splineMuscleActivations.dim > 1 experimentalMuscleActivations = ... - fnval(params.splineMuscleActivations, time)'; + fnval(params.splineMuscleActivations, time/time(end))'; else experimentalMuscleActivations = ... - fnval(params.splineMuscleActivations, time); + fnval(params.splineMuscleActivations, time/time(end)); end experimentalMuscleActivations = fnval(params.splineMuscleActivations, time)'; diff --git a/src/core/TreatmentOptimization/getSplines.m b/src/core/TreatmentOptimization/getSplines.m index 26200073c..e2db99acd 100644 --- a/src/core/TreatmentOptimization/getSplines.m +++ b/src/core/TreatmentOptimization/getSplines.m @@ -30,20 +30,20 @@ % ----------------------------------------------------------------------- % function inputs = getSplines(inputs) -inputs.splineJointAngles = spaps(inputs.experimentalTime, ... +inputs.splineJointAngles = spaps(inputs.experimentalTime/inputs.experimentalTime(end), ... inputs.experimentalJointAngles', 0.0000001); -inputs.splineJointMoments = spaps(inputs.experimentalTime, ... +inputs.splineJointMoments = spaps(inputs.experimentalTime/inputs.experimentalTime(end), ... inputs.experimentalJointMoments', 0.0000001); if strcmp(inputs.controllerType, 'synergy_driven') -inputs.splineMuscleActivations = spaps(inputs.experimentalTime, ... +inputs.splineMuscleActivations = spaps(inputs.experimentalTime/inputs.experimentalTime(end), ... inputs.experimentalMuscleActivations', 0.0000001); end for i = 1:length(inputs.contactSurfaces) inputs.splineExperimentalGroundReactionForces{i} = ... - spaps(inputs.experimentalTime, ... + spaps(inputs.experimentalTime/inputs.experimentalTime(end), ... inputs.contactSurfaces{i}.experimentalGroundReactionForces', 0.0000001); inputs.splineExperimentalGroundReactionMoments{i} = ... - spaps(inputs.experimentalTime, ... + spaps(inputs.experimentalTime/inputs.experimentalTime(end), ... inputs.contactSurfaces{i}.experimentalGroundReactionMoments', 0.0000001); end end \ No newline at end of file From 5b93f724bb6561024357477d10fa30708a1ea446 Mon Sep 17 00:00:00 2001 From: Marleny Vega Date: Fri, 25 Aug 2023 14:35:17 -0500 Subject: [PATCH 013/365] Added tracking functions to design optimization --- src/core/TreatmentOptimization/generateCostTermStruct.m | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/TreatmentOptimization/generateCostTermStruct.m b/src/core/TreatmentOptimization/generateCostTermStruct.m index 8e00962c7..0348bbbd3 100644 --- a/src/core/TreatmentOptimization/generateCostTermStruct.m +++ b/src/core/TreatmentOptimization/generateCostTermStruct.m @@ -61,6 +61,10 @@ case "DesignOptimization" allowedTypes = [ ... "coordinate_tracking", ... + "inverse_dynamics_load_tracking", ... + "external_force_tracking", ... + "external_moment_tracking", ... + "muscle_activation_tracking", ... "controller_tracking", ... "joint_jerk_minimization", ... "metabolic_cost_minimization" ... From 8976cc539b625edef923c08d5221d9689379375b Mon Sep 17 00:00:00 2001 From: Marleny Vega Date: Fri, 25 Aug 2023 14:35:42 -0500 Subject: [PATCH 014/365] Fixed inverse dynamics mex file --- .../OpenSimMex/inverseDynamics.m | 12 ++++++------ .../inverseDynamicsMexWindows.mexw64 | Bin 36352 -> 40960 bytes 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/core/TreatmentOptimization/OpenSimMex/inverseDynamics.m b/src/core/TreatmentOptimization/OpenSimMex/inverseDynamics.m index f42c0a267..3b9adc8e4 100644 --- a/src/core/TreatmentOptimization/OpenSimMex/inverseDynamics.m +++ b/src/core/TreatmentOptimization/OpenSimMex/inverseDynamics.m @@ -32,13 +32,13 @@ function inverseDynamicMoments = inverseDynamics(time, jointAngles, ... jointVelocities, jointAccelerations, coordinateLabels, appliedLoads, ... modelName) -% if isequal(mexext, 'mexw64') -% inverseDynamicMoments = inverseDynamicsMexWindows(time, jointAngles, ... -% jointVelocities, jointAccelerations, coordinateLabels, ... -% appliedLoads); -% else +if isequal(mexext, 'mexw64') + inverseDynamicMoments = inverseDynamicsMexWindows(time, jointAngles, ... + jointVelocities, jointAccelerations, coordinateLabels, ... + appliedLoads); +else inverseDynamicMoments = inverseDynamicsMatlabParallel(time, ... jointAngles, jointVelocities, jointAccelerations, coordinateLabels, ... appliedLoads, modelName); -% end +end end \ No newline at end of file diff --git a/src/core/TreatmentOptimization/OpenSimMex/inverseDynamicsMexWindows.mexw64 b/src/core/TreatmentOptimization/OpenSimMex/inverseDynamicsMexWindows.mexw64 index 61228f3ae522fb060d85a3d736593c75d65c98ed..7fe3199d0947b11447b4a4812fd2b1663fc213ff 100644 GIT binary patch literal 40960 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 MLu17J|C`tU0=e;K!~g&Q literal 36352 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 Date: Fri, 25 Aug 2023 14:38:31 -0500 Subject: [PATCH 015/365] Fixed plotting of muscle activations --- .../TreatmentOptimization/reportTreatmentOptimizationResults.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/TreatmentOptimization/reportTreatmentOptimizationResults.m b/src/core/TreatmentOptimization/reportTreatmentOptimizationResults.m index f51169e41..bd8d38b01 100644 --- a/src/core/TreatmentOptimization/reportTreatmentOptimizationResults.m +++ b/src/core/TreatmentOptimization/reportTreatmentOptimizationResults.m @@ -95,7 +95,7 @@ function plotMuscleActivations(muscleActivations, time, ... subplot(nplots, nplots, i) plot(time, muscleActivations(:, i)); hold on plot(experimentalTime, experimentalMuscleActivations(:, i), 'r') -axis([0 1 0 experimentalTime(end)]) +axis([0 experimentalTime(end) 0 1]) title(muscleLabels(i)) figureXLabels(numel(muscleLabels), nplots, i, "Time") figureYLabels(numel(muscleLabels), nplots, i, ["Muscle" "Activation"]) From 745c8378caf44de8732bf614680d146b6e5f8cb3 Mon Sep 17 00:00:00 2001 From: Marleny Vega Date: Fri, 25 Aug 2023 16:53:33 -0500 Subject: [PATCH 016/365] Added inverse dynamics cpp file --- .../OpenSimMex/inverseDynamicsMexWindows.cpp | 182 ++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 src/core/TreatmentOptimization/OpenSimMex/inverseDynamicsMexWindows.cpp diff --git a/src/core/TreatmentOptimization/OpenSimMex/inverseDynamicsMexWindows.cpp b/src/core/TreatmentOptimization/OpenSimMex/inverseDynamicsMexWindows.cpp new file mode 100644 index 000000000..d4742ff21 --- /dev/null +++ b/src/core/TreatmentOptimization/OpenSimMex/inverseDynamicsMexWindows.cpp @@ -0,0 +1,182 @@ +// This function is part of the NMSM Pipeline, see file for full license. +// +// performs inverse dynamics with openMP + +// ----------------------------------------------------------------------- // +// The NMSM Pipeline is a toolkit for model personalization and treatment // +// optimization of neuromusculoskeletal models through OpenSim. See // +// nmsm.rice.edu and the NOTICE file for more information. The // +// NMSM Pipeline is developed at Rice University and supported by the US // +// National Institutes of Health (R01 EB030520). // +// // +// Copyright (c) 2021 Rice University and the Authors // +// Author(s): Marleny Vega // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// http://www.apache.org/licenses/LICENSE-2.0. // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or // +// implied. See the License for the specific language governing // +// permissions and limitations under the License. // +// ----------------------------------------------------------------------- // + +#include "mex.h" +#include +#include +#include +#include +#include +#include +#include // +#include +#include +#include + +using namespace OpenSim; +using namespace SimTK; +using namespace std; +#define numThreads 20 // + +//______________________________________________________________________________ + +static Model *osimModel[numThreads]; +static State *osimState[numThreads]; +static InverseDynamicsSolver *idSolver[numThreads]; +static bool modelIsLoaded = false; + +void ClearMemory(void){ + for (int i = 0; i < numThreads; i++){ + delete osimModel[i]; + delete idSolver[i]; + } + modelIsLoaded = false; + mexPrintf("Cleared memory from inverseDynamics mex file.\n"); +} + +vector> mexArrayToVector(const mxArray *input) { + const mwSize *dimension; + dimension = mxGetDimensions(input); + int rows = (int) dimension[0]; + int columns = (int) dimension[1]; + double *data = (double *) mxGetData(input); + vector> output(rows, vector(columns)); + for (int i = 0; i < rows; i++) { + for (int j = 0; j < columns; j++) { + memcpy(&output[i][j], data + i + j * rows, sizeof(double) ); + } + } + return output; +} + +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]){ + mexAtExit(ClearMemory); + if (nrhs == 1) { + if (modelIsLoaded == true){ + ClearMemory(); + } + string modelName = mxArrayToString(prhs[0]); + std::streambuf* oldCoutStreamBuf = std::cout.rdbuf(); + std::ostringstream strCout; + std::cout.rdbuf(strCout.rdbuf()); + for (int i = 0; i < numThreads; i++){ + osimModel[i] = new Model(modelName); + osimState[i] = &osimModel[i]->initSystem(); + idSolver[i] = new InverseDynamicsSolver(*osimModel[i]); + } + std::cout.rdbuf(oldCoutStreamBuf); + modelIsLoaded = true; + } + else if (nrhs == 6) { + if (modelIsLoaded == false){ + mexErrMsgTxt("!!!No OpenSim model has been loaded!!!\n"); + } + const int numPts = mxGetM(prhs[0]); + const int numQs = mxGetN(prhs[1]); + const int numControls = mxGetN(prhs[5]); + const int numCoords = osimState[0]->getNQ(); + + double *time = mxGetPr(prhs[0]); + vector> q = mexArrayToVector(prhs[1]); + vector> qp = mexArrayToVector(prhs[2]); + vector> qpp = mexArrayToVector(prhs[3]); + vector> u = mexArrayToVector(prhs[5]); + + const mxArray *cellElementPtr; + char* c_array; + mwIndex k; + mwSize numLabels, buflen; + numLabels = mxGetNumberOfElements(prhs[4]); + int status; + + plhs[0] = mxCreateDoubleMatrix(numPts,numCoords,mxREAL); + double *idLoads = mxGetPr(plhs[0]); + + int numMarkers = osimModel[0]->getNumMarkers(); + mwSize dimensions[3]; + dimensions[0] = numPts; + dimensions[1] = numMarkers; + dimensions[2] = 3; + plhs[1] = mxCreateNumericArray(3, dimensions, mxDOUBLE_CLASS, mxREAL); + double* MarkerGlobalPos = mxGetPr(plhs[1]); + + #pragma omp parallel for num_threads(numThreads) + for (int i = 0; i < numPts; ++i){ + int thread_id = omp_get_thread_num(); + osimState[thread_id]->setTime(time[i]); + + for (int k = 0; k < numLabels; k++){ + cellElementPtr = mxGetCell(prhs[4], k); + buflen = mxGetN(cellElementPtr)*sizeof(mxChar) + 1; + c_array = (char *) mxCalloc(buflen, sizeof(char)); + status = mxGetString(cellElementPtr, c_array, buflen); + osimModel[thread_id]->getCoordinateSet().get(c_array).setValue(*osimState[thread_id], q[i][k], false); + osimModel[thread_id]->getCoordinateSet().get(c_array).setSpeedValue(*osimState[thread_id], qp[i][k]); + mxFree(c_array); + } + osimModel[thread_id]->realizeVelocity(*osimState[thread_id]); + + Vector AccelsVec(numCoords, 0.0); + for (int j = 0; j < numCoords; j++){ + double StateQ = osimState[thread_id]->getQ().get(j); + for (int k = 0; k < numQs; k++){ + if (abs(q[i][k] - StateQ) <= 1e-6){ + AccelsVec.set(j, qpp[i][k]); + } + } + } + + Vector newControls(numControls, 0.0); + for (int j = 0; j < numControls; j++){ + newControls.set(j, u[i][j]); + } + osimModel[thread_id]->setControls(*osimState[thread_id], newControls); + osimModel[thread_id]->markControlsAsValid(*osimState[thread_id]); + osimModel[thread_id]->realizeDynamics(*osimState[thread_id]); + + Vector IDLoadsVec; + IDLoadsVec = idSolver[thread_id]->solve(*osimState[thread_id], AccelsVec); + for (int j = 0; j < numCoords; j++){ + idLoads[i + numPts * j] = IDLoadsVec[j]; + } + + Vec3 tempMarkerGlobalPos; + for(int j = 0; j < numMarkers; j++){ + Marker& tempRefMarker = osimModel[thread_id]->getMarkerSet().get(j); + const PhysicalFrame& tempRefMarkerParentBody = tempRefMarker.getParentFrame(); + Vec3 tempMarkerLocalPos = tempRefMarker.get_location(); + osimModel[thread_id]->getSimbodyEngine().getPosition(*osimState[thread_id], tempRefMarkerParentBody, tempMarkerLocalPos, tempMarkerGlobalPos); + for (int k = 0; k < 3; k++){ + MarkerGlobalPos[numPts * (j + k * numMarkers) + i] = tempMarkerGlobalPos(k); + } + } + } + q.clear(); + qp.clear(); + qpp.clear(); + u.clear(); + } +} \ No newline at end of file From 94eb370022cb4e958be75634453188905ad46daa Mon Sep 17 00:00:00 2001 From: Marleny Vega Date: Fri, 25 Aug 2023 17:49:53 -0500 Subject: [PATCH 017/365] Free final time fix to muscle activation tracking term --- .../IntegrandTerms/calcTrackingMuscleActivationIntegrand.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m b/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m index c5810a7fd..faa377c58 100644 --- a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m +++ b/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m @@ -42,7 +42,7 @@ fnval(params.splineMuscleActivations, time/time(end)); end -experimentalMuscleActivations = fnval(params.splineMuscleActivations, time)'; +experimentalMuscleActivations = fnval(params.splineMuscleActivations, time/time(end))'; cost = calcTrackingCostArrayTerm(experimentalMuscleActivations, ... muscleActivations, indx); end \ No newline at end of file From 5cc2f8c797ac979f7a4b884decc93bbdc1759739 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 27 Aug 2023 13:39:26 -0500 Subject: [PATCH 018/365] move synergy and torque controllers to separate fn --- .../getTreatmentOptimizationInputs.m | 34 +++--------- src/core/parse/parseControllerType.m | 49 +++++++++++++++++ src/core/parse/parseSynergyController.m | 52 +++++++++++++++++++ src/core/parse/parseTorqueController.m | 37 +++++++++++++ 4 files changed, 144 insertions(+), 28 deletions(-) create mode 100644 src/core/parse/parseControllerType.m create mode 100644 src/core/parse/parseSynergyController.m create mode 100644 src/core/parse/parseTorqueController.m diff --git a/src/core/TreatmentOptimization/getTreatmentOptimizationInputs.m b/src/core/TreatmentOptimization/getTreatmentOptimizationInputs.m index 6adee21f7..6d3a087e4 100644 --- a/src/core/TreatmentOptimization/getTreatmentOptimizationInputs.m +++ b/src/core/TreatmentOptimization/getTreatmentOptimizationInputs.m @@ -33,39 +33,17 @@ inputs.resultsDirectory = getTextFromField(getFieldByName(tree, ... 'results_directory')); if(isempty(inputs.resultsDirectory)); inputs.resultsDirectory = pwd; end -inputs.controllerType = getTextFromField(getFieldByNameOrError(tree, ... - 'type_of_controller')); +inputs.controllerType = parseControllerType(tree); inputs.model = parseModel(tree); inputs.osimx = parseOsimxFile(getTextFromField(getFieldByName(tree, ... - 'osimx_file'))); -if strcmp(inputs.controllerType, 'synergy_driven') - inputs.synergyGroups = getSynergyGroups(tree, Model(inputs.model)); - inputs.numSynergies = getNumSynergies(inputs.synergyGroups); - inputs.numSynergyWeights = getNumSynergyWeights(inputs.synergyGroups); - inputs.surrogateModelCoordinateNames = parseSpaceSeparatedList(tree, ... - "coordinate_list"); - inputs.muscleNames = getMusclesFromCoordinates(inputs.model, ... - inputs.surrogateModelCoordinateNames); - inputs.numMuscles = length(inputs.muscleNames); - inputs.epsilon = str2double(parseElementTextByNameOrAlternate(tree, ... - "epsilon", "1e-4")); - inputs.vMaxFactor = str2double(parseElementTextByNameOrAlternate(tree, ... - "v_max_factor", "10")); - surrogateModelCoefficients = load(getTextFromField(getFieldByName(tree, ... - 'surrogate_model_coefficients'))); - inputs.coefficients = surrogateModelCoefficients.coefficients; - inputs = getModelOrOsimxInputs(inputs); -elseif strcmp(inputs.controllerType, 'torque_driven') - inputs.controlTorqueNames = parseSpaceSeparatedList(tree, ... - "coordinate_list"); - inputs.numTorqueControls = length(inputs.controlTorqueNames); + 'input_osimx_file'))); +if strcmp(inputs.controllerType, 'synergy') + inputs = parseSynergyController(tree, inputs); +elseif strcmp(inputs.controllerType, 'torque') + inputs = parseTorqueController(tree, inputs); end -inputs.optimizeSynergyVectors = getBooleanLogic(... - parseElementTextByNameOrAlternate(tree, "optimize_synergy_vectors", 0)); inputs = parseTreatmentOptimizationDataDirectory(tree, inputs); inputs.initialGuess = getGpopsInitialGuess(tree); -% inputs.experimentalTime = inputs.experimentalTime / ... -% inputs.experimentalTime(end); inputs.costTerms = parseRcnlCostTermSet( ... getFieldByNameOrError(tree, 'RCNLCostTermSet').RCNLCostTerm); inputs.path = getPathConstraintTerms(tree); diff --git a/src/core/parse/parseControllerType.m b/src/core/parse/parseControllerType.m new file mode 100644 index 000000000..976fb9fab --- /dev/null +++ b/src/core/parse/parseControllerType.m @@ -0,0 +1,49 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% There are two controllers that can be used to solve optimal control +% problems in the NMSM Pipeline. This function finds the correct element to +% determine which controller is being used. This informs the XML parsing +% logic. +% +% (struct) -> (string) +% returns "synergy" or "torque" depending on the settings file + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Claire V. Hammond % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function controllerType = parseControllerType(tree) +torque = getFieldByName(tree, "RCNLTorqueController"); +synergy = getFieldByName(tree, "RCNLSynergyController"); +if isstruct(torque) && isstruct(synergy) + MException("TreatmentOptimizationSettings:TwoControllers", ... + strcat("Found and ", ... + " - only one controller is allowed.")); +end +if isstruct(torque) + controllerType = "torque"; + return +end +if isstruct(synergy) + controllerType = "synergy"; + return +end +end \ No newline at end of file diff --git a/src/core/parse/parseSynergyController.m b/src/core/parse/parseSynergyController.m new file mode 100644 index 000000000..0832ff162 --- /dev/null +++ b/src/core/parse/parseSynergyController.m @@ -0,0 +1,52 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% There are two controllers that can be used to solve optimal control +% problems in the NMSM Pipeline. This function parses the synergy +% controller settings inside +% +% (struct) -> (struct) +% parses synergy controller settings from XML tree + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Claire V. Hammond % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function inputs = parseSynergyController(tree, inputs) +inputs.synergyGroups = getSynergyGroups(tree, Model(inputs.model)); +inputs.numSynergies = getNumSynergies(inputs.synergyGroups); +inputs.numSynergyWeights = getNumSynergyWeights(inputs.synergyGroups); +inputs.surrogateModelCoordinateNames = parseSpaceSeparatedList(tree, ... + "coordinate_list"); +inputs.muscleNames = getMusclesFromCoordinates(inputs.model, ... + inputs.surrogateModelCoordinateNames); +inputs.numMuscles = length(inputs.muscleNames); +inputs.epsilon = str2double(parseElementTextByNameOrAlternate(tree, ... + "epsilon", "1e-4")); +inputs.vMaxFactor = str2double(parseElementTextByNameOrAlternate(tree, ... + "v_max_factor", "10")); +surrogateModelCoefficients = load(getTextFromField(getFieldByName(tree, ... + 'surrogate_model_coefficients'))); +inputs.coefficients = surrogateModelCoefficients.coefficients; +inputs.optimizeSynergyVectors = getBooleanLogic(... + parseElementTextByNameOrAlternate(tree, "optimize_synergy_vectors", 0)); +inputs = getModelOrOsimxInputs(inputs); +end + diff --git a/src/core/parse/parseTorqueController.m b/src/core/parse/parseTorqueController.m new file mode 100644 index 000000000..d8bb2775a --- /dev/null +++ b/src/core/parse/parseTorqueController.m @@ -0,0 +1,37 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% There are two controllers that can be used to solve optimal control +% problems in the NMSM Pipeline. This function parses the torque +% controller settings inside +% +% (struct) -> (struct) +% parses torque controller settings from XML tree + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Claire V. Hammond % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function inputs = parseTorqueController(tree, inputs) + inputs.controlTorqueNames = parseSpaceSeparatedList(tree, ... + "torque_controller_coordinate_list"); + inputs.numTorqueControls = length(inputs.controlTorqueNames); +end + From b5b6a7d8c6484e4e135ae02d5babc48dde85193d Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 27 Aug 2023 13:39:37 -0500 Subject: [PATCH 019/365] fix bug w/exception --- src/core/parse/parseSpaceSeparatedList.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/parse/parseSpaceSeparatedList.m b/src/core/parse/parseSpaceSeparatedList.m index 3178a147e..7d34e4155 100644 --- a/src/core/parse/parseSpaceSeparatedList.m +++ b/src/core/parse/parseSpaceSeparatedList.m @@ -32,7 +32,7 @@ function prefixes = parseSpaceSeparatedList(tree, elementName) prefixField = getFieldByName(tree, elementName); if ~isfield(prefixField, "Text") - throw(MException('', strcat(prefixField, " is not in the xml file."))) + throw(MException('', strcat(elementName, " is not in the xml file."))) end if ~isempty(prefixField.Text) if(strcmp(prefixField.Text(1), ' ')) From 63b52ca7a56030a6ac4691ea410621f558d5f415 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 27 Aug 2023 13:53:32 -0500 Subject: [PATCH 020/365] update project, fix small xml differences --- .../2C8HNJtYzHbxZ89zTLYPH0eACjkd.xml} | 0 .../2C8HNJtYzHbxZ89zTLYPH0eACjkp.xml} | 0 .../53VFX8re2xrBZK_I6s6coIkEp2od.xml} | 0 .../53VFX8re2xrBZK_I6s6coIkEp2op.xml} | 0 .../APKTMrxvw_tsjjYaM1RWvPNltuId.xml} | 0 .../APKTMrxvw_tsjjYaM1RWvPNltuIp.xml} | 0 .../FxSy9vUXUClSoIRvySSkb9FJ14Yd.xml} | 0 .../FxSy9vUXUClSoIRvySSkb9FJ14Yp.xml} | 0 .../U8NcbPxNm5bF-j3mpONn6IRUmCwd.xml} | 0 .../U8NcbPxNm5bF-j3mpONn6IRUmCwp.xml} | 0 .../aOglYJlxQ7H_dGgajqfDY-e5vqUd.xml} | 0 .../aOglYJlxQ7H_dGgajqfDY-e5vqUp.xml} | 0 .../eIP3YXp23lrMI5ZyMWc-IP4Hqn4d.xml} | 0 .../eIP3YXp23lrMI5ZyMWc-IP4Hqn4p.xml} | 0 .../fQNuH3kIzHIrkOWnRMvBRrzXrjcd.xml} | 0 .../fQNuH3kIzHIrkOWnRMvBRrzXrjcp.xml} | 0 .../ldXR-gx8Eq1a-lR8dwU1YKhDE0Yd.xml} | 0 .../ldXR-gx8Eq1a-lR8dwU1YKhDE0Yp.xml} | 0 .../pISJKup1j7W2w1EMvS_ZPVr8UK0d.xml} | 0 .../pISJKup1j7W2w1EMvS_ZPVr8UK0p.xml} | 0 .../tcrKjx9o59mYBXfda-WZP3oIMO8d.xml} | 0 .../tcrKjx9o59mYBXfda-WZP3oIMO8p.xml} | 0 .../xkVEl8F2a3LBgQKu1b0gRTUSWF8d.xml} | 0 .../xkVEl8F2a3LBgQKu1b0gRTUSWF8p.xml} | 0 .../yXpziHQzwjQvtO_d-DRwjgpNZkMd.xml} | 0 .../yXpziHQzwjQvtO_d-DRwjgpNZkMp.xml} | 0 .../8jsqB_3VpsqnWAtr0EfyFf2PMuwd.xml | 2 ++ .../8jsqB_3VpsqnWAtr0EfyFf2PMuwp.xml | 2 ++ .../LyBMQyMl0k2C6Z1WTP22xTp0Gfwd.xml | 2 ++ .../LyBMQyMl0k2C6Z1WTP22xTp0Gfwp.xml | 2 ++ .../MfYlrrLoLXqxWpDxiiv6n7Tn1Iod.xml | 2 -- .../MfYlrrLoLXqxWpDxiiv6n7Tn1Iop.xml | 2 -- .../QpuyrctMGsaP9MPjnJLumHGZL5wd.xml | 2 ++ .../QpuyrctMGsaP9MPjnJLumHGZL5wp.xml | 2 ++ .../VB5W40RnsorydpOba8tQeVxdTDQd.xml | 2 -- .../VB5W40RnsorydpOba8tQeVxdTDQp.xml | 2 -- .../tkAoR0wDeR9bDqF10NY7YqXFvL4d.xml} | 0 .../tkAoR0wDeR9bDqF10NY7YqXFvL4p.xml | 2 ++ .../EeuwlRNjbNsLmr2_PZkiFY_uHO0d.xml | 6 ++++++ .../EeuwlRNjbNsLmr2_PZkiFY_uHO0p.xml | 2 ++ .../SsTOiEKMfihmt3ZlKtT9xs8reekd.xml} | 0 .../SsTOiEKMfihmt3ZlKtT9xs8reekp.xml} | 0 .../narb6tgAWqrDr4i4dAFlCdqFUpEd.xml | 6 ++++++ .../narb6tgAWqrDr4i4dAFlCdqFUpEp.xml | 2 ++ .../1D-aA_lDjJlRxIHKl8PXRF67F-kd.xml} | 0 .../1D-aA_lDjJlRxIHKl8PXRF67F-kp.xml | 2 ++ .../WBRnwihdNLBXSPBzOwR6sb9ZOfUp.xml | 2 -- .../Zyy4GNpD2r3q5z6rVbyG2A5CwKEd.xml | 2 ++ .../Zyy4GNpD2r3q5z6rVbyG2A5CwKEp.xml | 2 ++ .../rjqiBcq-Lf6PHOzurJu8wmXPRLcp.xml | 2 -- .../VDHHECsIe1Y40sASCbMtnlV8kkEd.xml | 2 ++ .../VDHHECsIe1Y40sASCbMtnlV8kkEp.xml | 2 ++ .../mcX11TzWc7WsF7mLdnD6bv6HYm0d.xml | 6 ++++++ .../mcX11TzWc7WsF7mLdnD6bv6HYm0p.xml | 2 ++ .../xcwXr-V7z1F2VlQN51Es9EbCc6sd.xml | 6 ++++++ .../xcwXr-V7z1F2VlQN51Es9EbCc6sp.xml | 2 ++ .../solveOptimalControlProblem.m | 4 ++-- src/core/optimal_control/getGpopsSolverSettings.m | 14 +++++++------- .../getOptimalControlSolverSettings.m | 8 +++++--- src/core/parse/parseTreatmentOptimizationParams.m | 3 ++- 60 files changed, 72 insertions(+), 25 deletions(-) rename resources/project/{WBRnwihdNLBXSPBzOwR6sb9ZOfU/4512JjanZ08cqBB0PKUSob2CvAId.xml => 1D-aA_lDjJlRxIHKl8PXRF67F-k/2C8HNJtYzHbxZ89zTLYPH0eACjkd.xml} (100%) rename resources/project/{rjqiBcq-Lf6PHOzurJu8wmXPRLc/SSxthfNhlTxzkvbLy5rdNwNoPDcp.xml => 1D-aA_lDjJlRxIHKl8PXRF67F-k/2C8HNJtYzHbxZ89zTLYPH0eACjkp.xml} (100%) rename resources/project/{WBRnwihdNLBXSPBzOwR6sb9ZOfU/fuDD9sOAbHGOuAW1_c2uQ5HVxkId.xml => 1D-aA_lDjJlRxIHKl8PXRF67F-k/53VFX8re2xrBZK_I6s6coIkEp2od.xml} (100%) rename resources/project/{rjqiBcq-Lf6PHOzurJu8wmXPRLc/rsypI2SliEIaygpLi080nfFXTFQp.xml => 1D-aA_lDjJlRxIHKl8PXRF67F-k/53VFX8re2xrBZK_I6s6coIkEp2op.xml} (100%) rename resources/project/{WBRnwihdNLBXSPBzOwR6sb9ZOfU/r1gJ47xS1SI_6x_hhStdKc8rHAAd.xml => 1D-aA_lDjJlRxIHKl8PXRF67F-k/APKTMrxvw_tsjjYaM1RWvPNltuId.xml} (100%) rename resources/project/{rjqiBcq-Lf6PHOzurJu8wmXPRLc/iP5nksVSyCBzfFDmXV4iSEbpBXcp.xml => 1D-aA_lDjJlRxIHKl8PXRF67F-k/APKTMrxvw_tsjjYaM1RWvPNltuIp.xml} (100%) rename resources/project/{rjqiBcq-Lf6PHOzurJu8wmXPRLc/5WwusQudPnq6BB-GF8FRrf89dhAd.xml => 1D-aA_lDjJlRxIHKl8PXRF67F-k/FxSy9vUXUClSoIRvySSkb9FJ14Yd.xml} (100%) rename resources/project/{rjqiBcq-Lf6PHOzurJu8wmXPRLc/ntOpiq0wcrzRF_WbpUd_GXHqXiMp.xml => 1D-aA_lDjJlRxIHKl8PXRF67F-k/FxSy9vUXUClSoIRvySSkb9FJ14Yp.xml} (100%) rename resources/project/{rjqiBcq-Lf6PHOzurJu8wmXPRLc/8fBGPWUvqT-8fICRX_EaE099qkMd.xml => 1D-aA_lDjJlRxIHKl8PXRF67F-k/U8NcbPxNm5bF-j3mpONn6IRUmCwd.xml} (100%) rename resources/project/{rjqiBcq-Lf6PHOzurJu8wmXPRLc/yQ7ukYOEE_Z48S5Kyd3otzmDgHMp.xml => 1D-aA_lDjJlRxIHKl8PXRF67F-k/U8NcbPxNm5bF-j3mpONn6IRUmCwp.xml} (100%) rename resources/project/{rjqiBcq-Lf6PHOzurJu8wmXPRLc/GKhQt7L9QdetWF4sO002MIZCU7Id.xml => 1D-aA_lDjJlRxIHKl8PXRF67F-k/aOglYJlxQ7H_dGgajqfDY-e5vqUd.xml} (100%) rename resources/project/{rjqiBcq-Lf6PHOzurJu8wmXPRLc/XgkWU6kVr3uGt-m8Ww6oQbheT5sp.xml => 1D-aA_lDjJlRxIHKl8PXRF67F-k/aOglYJlxQ7H_dGgajqfDY-e5vqUp.xml} (100%) rename resources/project/{rjqiBcq-Lf6PHOzurJu8wmXPRLc/SSxthfNhlTxzkvbLy5rdNwNoPDcd.xml => 1D-aA_lDjJlRxIHKl8PXRF67F-k/eIP3YXp23lrMI5ZyMWc-IP4Hqn4d.xml} (100%) rename resources/project/{rjqiBcq-Lf6PHOzurJu8wmXPRLc/8fBGPWUvqT-8fICRX_EaE099qkMp.xml => 1D-aA_lDjJlRxIHKl8PXRF67F-k/eIP3YXp23lrMI5ZyMWc-IP4Hqn4p.xml} (100%) rename resources/project/{WBRnwihdNLBXSPBzOwR6sb9ZOfU/yghkeR-GbtXqJCQypYRbzGf6WhMd.xml => 1D-aA_lDjJlRxIHKl8PXRF67F-k/fQNuH3kIzHIrkOWnRMvBRrzXrjcd.xml} (100%) rename resources/project/{WBRnwihdNLBXSPBzOwR6sb9ZOfU/yghkeR-GbtXqJCQypYRbzGf6WhMp.xml => 1D-aA_lDjJlRxIHKl8PXRF67F-k/fQNuH3kIzHIrkOWnRMvBRrzXrjcp.xml} (100%) rename resources/project/{rjqiBcq-Lf6PHOzurJu8wmXPRLc/XgkWU6kVr3uGt-m8Ww6oQbheT5sd.xml => 1D-aA_lDjJlRxIHKl8PXRF67F-k/ldXR-gx8Eq1a-lR8dwU1YKhDE0Yd.xml} (100%) rename resources/project/{WBRnwihdNLBXSPBzOwR6sb9ZOfU/fuDD9sOAbHGOuAW1_c2uQ5HVxkIp.xml => 1D-aA_lDjJlRxIHKl8PXRF67F-k/ldXR-gx8Eq1a-lR8dwU1YKhDE0Yp.xml} (100%) rename resources/project/{rjqiBcq-Lf6PHOzurJu8wmXPRLc/iP5nksVSyCBzfFDmXV4iSEbpBXcd.xml => 1D-aA_lDjJlRxIHKl8PXRF67F-k/pISJKup1j7W2w1EMvS_ZPVr8UK0d.xml} (100%) rename resources/project/{rjqiBcq-Lf6PHOzurJu8wmXPRLc/5WwusQudPnq6BB-GF8FRrf89dhAp.xml => 1D-aA_lDjJlRxIHKl8PXRF67F-k/pISJKup1j7W2w1EMvS_ZPVr8UK0p.xml} (100%) rename resources/project/{rjqiBcq-Lf6PHOzurJu8wmXPRLc/ntOpiq0wcrzRF_WbpUd_GXHqXiMd.xml => 1D-aA_lDjJlRxIHKl8PXRF67F-k/tcrKjx9o59mYBXfda-WZP3oIMO8d.xml} (100%) rename resources/project/{WBRnwihdNLBXSPBzOwR6sb9ZOfU/4512JjanZ08cqBB0PKUSob2CvAIp.xml => 1D-aA_lDjJlRxIHKl8PXRF67F-k/tcrKjx9o59mYBXfda-WZP3oIMO8p.xml} (100%) rename resources/project/{rjqiBcq-Lf6PHOzurJu8wmXPRLc/rsypI2SliEIaygpLi080nfFXTFQd.xml => 1D-aA_lDjJlRxIHKl8PXRF67F-k/xkVEl8F2a3LBgQKu1b0gRTUSWF8d.xml} (100%) rename resources/project/{WBRnwihdNLBXSPBzOwR6sb9ZOfU/r1gJ47xS1SI_6x_hhStdKc8rHAAp.xml => 1D-aA_lDjJlRxIHKl8PXRF67F-k/xkVEl8F2a3LBgQKu1b0gRTUSWF8p.xml} (100%) rename resources/project/{rjqiBcq-Lf6PHOzurJu8wmXPRLc/yQ7ukYOEE_Z48S5Kyd3otzmDgHMd.xml => 1D-aA_lDjJlRxIHKl8PXRF67F-k/yXpziHQzwjQvtO_d-DRwjgpNZkMd.xml} (100%) rename resources/project/{rjqiBcq-Lf6PHOzurJu8wmXPRLc/GKhQt7L9QdetWF4sO002MIZCU7Ip.xml => 1D-aA_lDjJlRxIHKl8PXRF67F-k/yXpziHQzwjQvtO_d-DRwjgpNZkMp.xml} (100%) create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/8jsqB_3VpsqnWAtr0EfyFf2PMuwd.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/8jsqB_3VpsqnWAtr0EfyFf2PMuwp.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/LyBMQyMl0k2C6Z1WTP22xTp0Gfwd.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/LyBMQyMl0k2C6Z1WTP22xTp0Gfwp.xml delete mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/MfYlrrLoLXqxWpDxiiv6n7Tn1Iod.xml delete mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/MfYlrrLoLXqxWpDxiiv6n7Tn1Iop.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/QpuyrctMGsaP9MPjnJLumHGZL5wd.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/QpuyrctMGsaP9MPjnJLumHGZL5wp.xml delete mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/VB5W40RnsorydpOba8tQeVxdTDQd.xml delete mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/VB5W40RnsorydpOba8tQeVxdTDQp.xml rename resources/project/{gK_nb01P4_6XSSogizZJn5zmzQQ/WBRnwihdNLBXSPBzOwR6sb9ZOfUd.xml => R6m7AxFfSX7StHtVJ9WG44h96GM/tkAoR0wDeR9bDqF10NY7YqXFvL4d.xml} (100%) create mode 100644 resources/project/R6m7AxFfSX7StHtVJ9WG44h96GM/tkAoR0wDeR9bDqF10NY7YqXFvL4p.xml create mode 100644 resources/project/Zyy4GNpD2r3q5z6rVbyG2A5CwKE/EeuwlRNjbNsLmr2_PZkiFY_uHO0d.xml create mode 100644 resources/project/Zyy4GNpD2r3q5z6rVbyG2A5CwKE/EeuwlRNjbNsLmr2_PZkiFY_uHO0p.xml rename resources/project/{gK_nb01P4_6XSSogizZJn5zmzQQ/rjqiBcq-Lf6PHOzurJu8wmXPRLcd.xml => Zyy4GNpD2r3q5z6rVbyG2A5CwKE/SsTOiEKMfihmt3ZlKtT9xs8reekd.xml} (100%) rename resources/project/{rjqiBcq-Lf6PHOzurJu8wmXPRLc/Tc7b1ya7BXcwLEcurYQ2pA061MEp.xml => Zyy4GNpD2r3q5z6rVbyG2A5CwKE/SsTOiEKMfihmt3ZlKtT9xs8reekp.xml} (100%) create mode 100644 resources/project/Zyy4GNpD2r3q5z6rVbyG2A5CwKE/narb6tgAWqrDr4i4dAFlCdqFUpEd.xml create mode 100644 resources/project/Zyy4GNpD2r3q5z6rVbyG2A5CwKE/narb6tgAWqrDr4i4dAFlCdqFUpEp.xml rename resources/project/{rjqiBcq-Lf6PHOzurJu8wmXPRLc/Tc7b1ya7BXcwLEcurYQ2pA061MEd.xml => gK_nb01P4_6XSSogizZJn5zmzQQ/1D-aA_lDjJlRxIHKl8PXRF67F-kd.xml} (100%) create mode 100644 resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/1D-aA_lDjJlRxIHKl8PXRF67F-kp.xml delete mode 100644 resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/WBRnwihdNLBXSPBzOwR6sb9ZOfUp.xml create mode 100644 resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/Zyy4GNpD2r3q5z6rVbyG2A5CwKEd.xml create mode 100644 resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/Zyy4GNpD2r3q5z6rVbyG2A5CwKEp.xml delete mode 100644 resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/rjqiBcq-Lf6PHOzurJu8wmXPRLcp.xml create mode 100644 resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/VDHHECsIe1Y40sASCbMtnlV8kkEd.xml create mode 100644 resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/VDHHECsIe1Y40sASCbMtnlV8kkEp.xml create mode 100644 resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/mcX11TzWc7WsF7mLdnD6bv6HYm0d.xml create mode 100644 resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/mcX11TzWc7WsF7mLdnD6bv6HYm0p.xml create mode 100644 resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/xcwXr-V7z1F2VlQN51Es9EbCc6sd.xml create mode 100644 resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/xcwXr-V7z1F2VlQN51Es9EbCc6sp.xml diff --git a/resources/project/WBRnwihdNLBXSPBzOwR6sb9ZOfU/4512JjanZ08cqBB0PKUSob2CvAId.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/2C8HNJtYzHbxZ89zTLYPH0eACjkd.xml similarity index 100% rename from resources/project/WBRnwihdNLBXSPBzOwR6sb9ZOfU/4512JjanZ08cqBB0PKUSob2CvAId.xml rename to resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/2C8HNJtYzHbxZ89zTLYPH0eACjkd.xml diff --git a/resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/SSxthfNhlTxzkvbLy5rdNwNoPDcp.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/2C8HNJtYzHbxZ89zTLYPH0eACjkp.xml similarity index 100% rename from resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/SSxthfNhlTxzkvbLy5rdNwNoPDcp.xml rename to resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/2C8HNJtYzHbxZ89zTLYPH0eACjkp.xml diff --git a/resources/project/WBRnwihdNLBXSPBzOwR6sb9ZOfU/fuDD9sOAbHGOuAW1_c2uQ5HVxkId.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/53VFX8re2xrBZK_I6s6coIkEp2od.xml similarity index 100% rename from resources/project/WBRnwihdNLBXSPBzOwR6sb9ZOfU/fuDD9sOAbHGOuAW1_c2uQ5HVxkId.xml rename to resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/53VFX8re2xrBZK_I6s6coIkEp2od.xml diff --git a/resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/rsypI2SliEIaygpLi080nfFXTFQp.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/53VFX8re2xrBZK_I6s6coIkEp2op.xml similarity index 100% rename from resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/rsypI2SliEIaygpLi080nfFXTFQp.xml rename to resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/53VFX8re2xrBZK_I6s6coIkEp2op.xml diff --git a/resources/project/WBRnwihdNLBXSPBzOwR6sb9ZOfU/r1gJ47xS1SI_6x_hhStdKc8rHAAd.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/APKTMrxvw_tsjjYaM1RWvPNltuId.xml similarity index 100% rename from resources/project/WBRnwihdNLBXSPBzOwR6sb9ZOfU/r1gJ47xS1SI_6x_hhStdKc8rHAAd.xml rename to resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/APKTMrxvw_tsjjYaM1RWvPNltuId.xml diff --git a/resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/iP5nksVSyCBzfFDmXV4iSEbpBXcp.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/APKTMrxvw_tsjjYaM1RWvPNltuIp.xml similarity index 100% rename from resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/iP5nksVSyCBzfFDmXV4iSEbpBXcp.xml rename to resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/APKTMrxvw_tsjjYaM1RWvPNltuIp.xml diff --git a/resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/5WwusQudPnq6BB-GF8FRrf89dhAd.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/FxSy9vUXUClSoIRvySSkb9FJ14Yd.xml similarity index 100% rename from resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/5WwusQudPnq6BB-GF8FRrf89dhAd.xml rename to resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/FxSy9vUXUClSoIRvySSkb9FJ14Yd.xml diff --git a/resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/ntOpiq0wcrzRF_WbpUd_GXHqXiMp.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/FxSy9vUXUClSoIRvySSkb9FJ14Yp.xml similarity index 100% rename from resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/ntOpiq0wcrzRF_WbpUd_GXHqXiMp.xml rename to resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/FxSy9vUXUClSoIRvySSkb9FJ14Yp.xml diff --git a/resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/8fBGPWUvqT-8fICRX_EaE099qkMd.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/U8NcbPxNm5bF-j3mpONn6IRUmCwd.xml similarity index 100% rename from resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/8fBGPWUvqT-8fICRX_EaE099qkMd.xml rename to resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/U8NcbPxNm5bF-j3mpONn6IRUmCwd.xml diff --git a/resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/yQ7ukYOEE_Z48S5Kyd3otzmDgHMp.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/U8NcbPxNm5bF-j3mpONn6IRUmCwp.xml similarity index 100% rename from resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/yQ7ukYOEE_Z48S5Kyd3otzmDgHMp.xml rename to resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/U8NcbPxNm5bF-j3mpONn6IRUmCwp.xml diff --git a/resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/GKhQt7L9QdetWF4sO002MIZCU7Id.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/aOglYJlxQ7H_dGgajqfDY-e5vqUd.xml similarity index 100% rename from resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/GKhQt7L9QdetWF4sO002MIZCU7Id.xml rename to resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/aOglYJlxQ7H_dGgajqfDY-e5vqUd.xml diff --git a/resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/XgkWU6kVr3uGt-m8Ww6oQbheT5sp.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/aOglYJlxQ7H_dGgajqfDY-e5vqUp.xml similarity index 100% rename from resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/XgkWU6kVr3uGt-m8Ww6oQbheT5sp.xml rename to resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/aOglYJlxQ7H_dGgajqfDY-e5vqUp.xml diff --git a/resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/SSxthfNhlTxzkvbLy5rdNwNoPDcd.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/eIP3YXp23lrMI5ZyMWc-IP4Hqn4d.xml similarity index 100% rename from resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/SSxthfNhlTxzkvbLy5rdNwNoPDcd.xml rename to resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/eIP3YXp23lrMI5ZyMWc-IP4Hqn4d.xml diff --git a/resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/8fBGPWUvqT-8fICRX_EaE099qkMp.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/eIP3YXp23lrMI5ZyMWc-IP4Hqn4p.xml similarity index 100% rename from resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/8fBGPWUvqT-8fICRX_EaE099qkMp.xml rename to resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/eIP3YXp23lrMI5ZyMWc-IP4Hqn4p.xml diff --git a/resources/project/WBRnwihdNLBXSPBzOwR6sb9ZOfU/yghkeR-GbtXqJCQypYRbzGf6WhMd.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/fQNuH3kIzHIrkOWnRMvBRrzXrjcd.xml similarity index 100% rename from resources/project/WBRnwihdNLBXSPBzOwR6sb9ZOfU/yghkeR-GbtXqJCQypYRbzGf6WhMd.xml rename to resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/fQNuH3kIzHIrkOWnRMvBRrzXrjcd.xml diff --git a/resources/project/WBRnwihdNLBXSPBzOwR6sb9ZOfU/yghkeR-GbtXqJCQypYRbzGf6WhMp.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/fQNuH3kIzHIrkOWnRMvBRrzXrjcp.xml similarity index 100% rename from resources/project/WBRnwihdNLBXSPBzOwR6sb9ZOfU/yghkeR-GbtXqJCQypYRbzGf6WhMp.xml rename to resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/fQNuH3kIzHIrkOWnRMvBRrzXrjcp.xml diff --git a/resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/XgkWU6kVr3uGt-m8Ww6oQbheT5sd.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/ldXR-gx8Eq1a-lR8dwU1YKhDE0Yd.xml similarity index 100% rename from resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/XgkWU6kVr3uGt-m8Ww6oQbheT5sd.xml rename to resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/ldXR-gx8Eq1a-lR8dwU1YKhDE0Yd.xml diff --git a/resources/project/WBRnwihdNLBXSPBzOwR6sb9ZOfU/fuDD9sOAbHGOuAW1_c2uQ5HVxkIp.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/ldXR-gx8Eq1a-lR8dwU1YKhDE0Yp.xml similarity index 100% rename from resources/project/WBRnwihdNLBXSPBzOwR6sb9ZOfU/fuDD9sOAbHGOuAW1_c2uQ5HVxkIp.xml rename to resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/ldXR-gx8Eq1a-lR8dwU1YKhDE0Yp.xml diff --git a/resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/iP5nksVSyCBzfFDmXV4iSEbpBXcd.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/pISJKup1j7W2w1EMvS_ZPVr8UK0d.xml similarity index 100% rename from resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/iP5nksVSyCBzfFDmXV4iSEbpBXcd.xml rename to resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/pISJKup1j7W2w1EMvS_ZPVr8UK0d.xml diff --git a/resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/5WwusQudPnq6BB-GF8FRrf89dhAp.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/pISJKup1j7W2w1EMvS_ZPVr8UK0p.xml similarity index 100% rename from resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/5WwusQudPnq6BB-GF8FRrf89dhAp.xml rename to resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/pISJKup1j7W2w1EMvS_ZPVr8UK0p.xml diff --git a/resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/ntOpiq0wcrzRF_WbpUd_GXHqXiMd.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/tcrKjx9o59mYBXfda-WZP3oIMO8d.xml similarity index 100% rename from resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/ntOpiq0wcrzRF_WbpUd_GXHqXiMd.xml rename to resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/tcrKjx9o59mYBXfda-WZP3oIMO8d.xml diff --git a/resources/project/WBRnwihdNLBXSPBzOwR6sb9ZOfU/4512JjanZ08cqBB0PKUSob2CvAIp.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/tcrKjx9o59mYBXfda-WZP3oIMO8p.xml similarity index 100% rename from resources/project/WBRnwihdNLBXSPBzOwR6sb9ZOfU/4512JjanZ08cqBB0PKUSob2CvAIp.xml rename to resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/tcrKjx9o59mYBXfda-WZP3oIMO8p.xml diff --git a/resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/rsypI2SliEIaygpLi080nfFXTFQd.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/xkVEl8F2a3LBgQKu1b0gRTUSWF8d.xml similarity index 100% rename from resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/rsypI2SliEIaygpLi080nfFXTFQd.xml rename to resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/xkVEl8F2a3LBgQKu1b0gRTUSWF8d.xml diff --git a/resources/project/WBRnwihdNLBXSPBzOwR6sb9ZOfU/r1gJ47xS1SI_6x_hhStdKc8rHAAp.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/xkVEl8F2a3LBgQKu1b0gRTUSWF8p.xml similarity index 100% rename from resources/project/WBRnwihdNLBXSPBzOwR6sb9ZOfU/r1gJ47xS1SI_6x_hhStdKc8rHAAp.xml rename to resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/xkVEl8F2a3LBgQKu1b0gRTUSWF8p.xml diff --git a/resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/yQ7ukYOEE_Z48S5Kyd3otzmDgHMd.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/yXpziHQzwjQvtO_d-DRwjgpNZkMd.xml similarity index 100% rename from resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/yQ7ukYOEE_Z48S5Kyd3otzmDgHMd.xml rename to resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/yXpziHQzwjQvtO_d-DRwjgpNZkMd.xml diff --git a/resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/GKhQt7L9QdetWF4sO002MIZCU7Ip.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/yXpziHQzwjQvtO_d-DRwjgpNZkMp.xml similarity index 100% rename from resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/GKhQt7L9QdetWF4sO002MIZCU7Ip.xml rename to resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/yXpziHQzwjQvtO_d-DRwjgpNZkMp.xml diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/8jsqB_3VpsqnWAtr0EfyFf2PMuwd.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/8jsqB_3VpsqnWAtr0EfyFf2PMuwd.xml new file mode 100644 index 000000000..97646e4fe --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/8jsqB_3VpsqnWAtr0EfyFf2PMuwd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/8jsqB_3VpsqnWAtr0EfyFf2PMuwp.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/8jsqB_3VpsqnWAtr0EfyFf2PMuwp.xml new file mode 100644 index 000000000..28f06db9a --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/8jsqB_3VpsqnWAtr0EfyFf2PMuwp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/LyBMQyMl0k2C6Z1WTP22xTp0Gfwd.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/LyBMQyMl0k2C6Z1WTP22xTp0Gfwd.xml new file mode 100644 index 000000000..5432e678b --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/LyBMQyMl0k2C6Z1WTP22xTp0Gfwd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/LyBMQyMl0k2C6Z1WTP22xTp0Gfwp.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/LyBMQyMl0k2C6Z1WTP22xTp0Gfwp.xml new file mode 100644 index 000000000..fec192284 --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/LyBMQyMl0k2C6Z1WTP22xTp0Gfwp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/MfYlrrLoLXqxWpDxiiv6n7Tn1Iod.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/MfYlrrLoLXqxWpDxiiv6n7Tn1Iod.xml deleted file mode 100644 index 6212b6b6e..000000000 --- a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/MfYlrrLoLXqxWpDxiiv6n7Tn1Iod.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/MfYlrrLoLXqxWpDxiiv6n7Tn1Iop.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/MfYlrrLoLXqxWpDxiiv6n7Tn1Iop.xml deleted file mode 100644 index 80886e9b4..000000000 --- a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/MfYlrrLoLXqxWpDxiiv6n7Tn1Iop.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/QpuyrctMGsaP9MPjnJLumHGZL5wd.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/QpuyrctMGsaP9MPjnJLumHGZL5wd.xml new file mode 100644 index 000000000..3035a1522 --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/QpuyrctMGsaP9MPjnJLumHGZL5wd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/QpuyrctMGsaP9MPjnJLumHGZL5wp.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/QpuyrctMGsaP9MPjnJLumHGZL5wp.xml new file mode 100644 index 000000000..b8ebb5a99 --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/QpuyrctMGsaP9MPjnJLumHGZL5wp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/VB5W40RnsorydpOba8tQeVxdTDQd.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/VB5W40RnsorydpOba8tQeVxdTDQd.xml deleted file mode 100644 index 1e7c427b8..000000000 --- a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/VB5W40RnsorydpOba8tQeVxdTDQd.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/VB5W40RnsorydpOba8tQeVxdTDQp.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/VB5W40RnsorydpOba8tQeVxdTDQp.xml deleted file mode 100644 index e2a0a0fcc..000000000 --- a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/VB5W40RnsorydpOba8tQeVxdTDQp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/WBRnwihdNLBXSPBzOwR6sb9ZOfUd.xml b/resources/project/R6m7AxFfSX7StHtVJ9WG44h96GM/tkAoR0wDeR9bDqF10NY7YqXFvL4d.xml similarity index 100% rename from resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/WBRnwihdNLBXSPBzOwR6sb9ZOfUd.xml rename to resources/project/R6m7AxFfSX7StHtVJ9WG44h96GM/tkAoR0wDeR9bDqF10NY7YqXFvL4d.xml diff --git a/resources/project/R6m7AxFfSX7StHtVJ9WG44h96GM/tkAoR0wDeR9bDqF10NY7YqXFvL4p.xml b/resources/project/R6m7AxFfSX7StHtVJ9WG44h96GM/tkAoR0wDeR9bDqF10NY7YqXFvL4p.xml new file mode 100644 index 000000000..d9f3deee4 --- /dev/null +++ b/resources/project/R6m7AxFfSX7StHtVJ9WG44h96GM/tkAoR0wDeR9bDqF10NY7YqXFvL4p.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/Zyy4GNpD2r3q5z6rVbyG2A5CwKE/EeuwlRNjbNsLmr2_PZkiFY_uHO0d.xml b/resources/project/Zyy4GNpD2r3q5z6rVbyG2A5CwKE/EeuwlRNjbNsLmr2_PZkiFY_uHO0d.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/Zyy4GNpD2r3q5z6rVbyG2A5CwKE/EeuwlRNjbNsLmr2_PZkiFY_uHO0d.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/Zyy4GNpD2r3q5z6rVbyG2A5CwKE/EeuwlRNjbNsLmr2_PZkiFY_uHO0p.xml b/resources/project/Zyy4GNpD2r3q5z6rVbyG2A5CwKE/EeuwlRNjbNsLmr2_PZkiFY_uHO0p.xml new file mode 100644 index 000000000..be998f16c --- /dev/null +++ b/resources/project/Zyy4GNpD2r3q5z6rVbyG2A5CwKE/EeuwlRNjbNsLmr2_PZkiFY_uHO0p.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/rjqiBcq-Lf6PHOzurJu8wmXPRLcd.xml b/resources/project/Zyy4GNpD2r3q5z6rVbyG2A5CwKE/SsTOiEKMfihmt3ZlKtT9xs8reekd.xml similarity index 100% rename from resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/rjqiBcq-Lf6PHOzurJu8wmXPRLcd.xml rename to resources/project/Zyy4GNpD2r3q5z6rVbyG2A5CwKE/SsTOiEKMfihmt3ZlKtT9xs8reekd.xml diff --git a/resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/Tc7b1ya7BXcwLEcurYQ2pA061MEp.xml b/resources/project/Zyy4GNpD2r3q5z6rVbyG2A5CwKE/SsTOiEKMfihmt3ZlKtT9xs8reekp.xml similarity index 100% rename from resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/Tc7b1ya7BXcwLEcurYQ2pA061MEp.xml rename to resources/project/Zyy4GNpD2r3q5z6rVbyG2A5CwKE/SsTOiEKMfihmt3ZlKtT9xs8reekp.xml diff --git a/resources/project/Zyy4GNpD2r3q5z6rVbyG2A5CwKE/narb6tgAWqrDr4i4dAFlCdqFUpEd.xml b/resources/project/Zyy4GNpD2r3q5z6rVbyG2A5CwKE/narb6tgAWqrDr4i4dAFlCdqFUpEd.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/Zyy4GNpD2r3q5z6rVbyG2A5CwKE/narb6tgAWqrDr4i4dAFlCdqFUpEd.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/Zyy4GNpD2r3q5z6rVbyG2A5CwKE/narb6tgAWqrDr4i4dAFlCdqFUpEp.xml b/resources/project/Zyy4GNpD2r3q5z6rVbyG2A5CwKE/narb6tgAWqrDr4i4dAFlCdqFUpEp.xml new file mode 100644 index 000000000..4fed95bc9 --- /dev/null +++ b/resources/project/Zyy4GNpD2r3q5z6rVbyG2A5CwKE/narb6tgAWqrDr4i4dAFlCdqFUpEp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/Tc7b1ya7BXcwLEcurYQ2pA061MEd.xml b/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/1D-aA_lDjJlRxIHKl8PXRF67F-kd.xml similarity index 100% rename from resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/Tc7b1ya7BXcwLEcurYQ2pA061MEd.xml rename to resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/1D-aA_lDjJlRxIHKl8PXRF67F-kd.xml diff --git a/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/1D-aA_lDjJlRxIHKl8PXRF67F-kp.xml b/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/1D-aA_lDjJlRxIHKl8PXRF67F-kp.xml new file mode 100644 index 000000000..31e224555 --- /dev/null +++ b/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/1D-aA_lDjJlRxIHKl8PXRF67F-kp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/WBRnwihdNLBXSPBzOwR6sb9ZOfUp.xml b/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/WBRnwihdNLBXSPBzOwR6sb9ZOfUp.xml deleted file mode 100644 index cf935bc9b..000000000 --- a/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/WBRnwihdNLBXSPBzOwR6sb9ZOfUp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/Zyy4GNpD2r3q5z6rVbyG2A5CwKEd.xml b/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/Zyy4GNpD2r3q5z6rVbyG2A5CwKEd.xml new file mode 100644 index 000000000..a75f7a81b --- /dev/null +++ b/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/Zyy4GNpD2r3q5z6rVbyG2A5CwKEd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/Zyy4GNpD2r3q5z6rVbyG2A5CwKEp.xml b/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/Zyy4GNpD2r3q5z6rVbyG2A5CwKEp.xml new file mode 100644 index 000000000..c9bb82bdd --- /dev/null +++ b/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/Zyy4GNpD2r3q5z6rVbyG2A5CwKEp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/rjqiBcq-Lf6PHOzurJu8wmXPRLcp.xml b/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/rjqiBcq-Lf6PHOzurJu8wmXPRLcp.xml deleted file mode 100644 index 52e6c84fe..000000000 --- a/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/rjqiBcq-Lf6PHOzurJu8wmXPRLcp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/VDHHECsIe1Y40sASCbMtnlV8kkEd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/VDHHECsIe1Y40sASCbMtnlV8kkEd.xml new file mode 100644 index 000000000..a75f7a81b --- /dev/null +++ b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/VDHHECsIe1Y40sASCbMtnlV8kkEd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/VDHHECsIe1Y40sASCbMtnlV8kkEp.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/VDHHECsIe1Y40sASCbMtnlV8kkEp.xml new file mode 100644 index 000000000..842de6ab3 --- /dev/null +++ b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/VDHHECsIe1Y40sASCbMtnlV8kkEp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/mcX11TzWc7WsF7mLdnD6bv6HYm0d.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/mcX11TzWc7WsF7mLdnD6bv6HYm0d.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/mcX11TzWc7WsF7mLdnD6bv6HYm0d.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/mcX11TzWc7WsF7mLdnD6bv6HYm0p.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/mcX11TzWc7WsF7mLdnD6bv6HYm0p.xml new file mode 100644 index 000000000..bf362906b --- /dev/null +++ b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/mcX11TzWc7WsF7mLdnD6bv6HYm0p.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/xcwXr-V7z1F2VlQN51Es9EbCc6sd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/xcwXr-V7z1F2VlQN51Es9EbCc6sd.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/xcwXr-V7z1F2VlQN51Es9EbCc6sd.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/xcwXr-V7z1F2VlQN51Es9EbCc6sp.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/xcwXr-V7z1F2VlQN51Es9EbCc6sp.xml new file mode 100644 index 000000000..cdd4d3dcb --- /dev/null +++ b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/xcwXr-V7z1F2VlQN51Es9EbCc6sp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/TreatmentOptimization/solveOptimalControlProblem.m b/src/TreatmentOptimization/solveOptimalControlProblem.m index e86b96557..9efcea8bb 100644 --- a/src/TreatmentOptimization/solveOptimalControlProblem.m +++ b/src/TreatmentOptimization/solveOptimalControlProblem.m @@ -25,7 +25,7 @@ % ----------------------------------------------------------------------- % function solution = solveOptimalControlProblem(inputs, params) -if strcmp(inputs.problemType, "SynergyDriven") +if strcmp(inputs.controllerType, "synergy") switch inputs.solver case 'gpops' setup = convertToGpopsSynergyDrivenInputs(inputs, params); @@ -41,7 +41,7 @@ MException('solveOptimalControlProblem:invalidSolver', ... 'Invalid solver specified.'); end -elseif strcmp(inputs.problemType, "TorqueDriven") +elseif strcmp(inputs.controllerType, "torque") switch inputs.solver case 'gpops' setup = convertToGpopsTorqueDrivenInputs(inputs, params); diff --git a/src/core/optimal_control/getGpopsSolverSettings.m b/src/core/optimal_control/getGpopsSolverSettings.m index be91fa653..17d51011c 100644 --- a/src/core/optimal_control/getGpopsSolverSettings.m +++ b/src/core/optimal_control/getGpopsSolverSettings.m @@ -63,17 +63,17 @@ solverSettings.numIntervals = parseDoubleOrAlternate( ... settingsTree, 'setup_mesh_phase_colpoints_per_Interval', 10); solverSettings.solverType = parseTextOrAlternate( ... - settingsTree, 'setup_nlp_solver', 'ipopt')); + settingsTree, 'setup_nlp_solver', 'ipopt'); solverSettings.linearSolverType = parseTextOrAlternate( ... - settingsTree, 'setup_nlp_linear_solver', 'ma57')); + settingsTree, 'setup_nlp_linear_solver', 'ma57'); solverSettings.solverTolerance = parseDoubleOrAlternate( ... - settingsTree, 'setup_nlp_tolerance', 1e-3))); + settingsTree, 'setup_nlp_tolerance', 1e-3); solverSettings.stepSize = parseDoubleOrAlternate( ... - settingsTree, 'setup_nlp_step_size', 1e-8))); + settingsTree, 'setup_nlp_step_size', 1e-8); solverSettings.maxIterations = parseDoubleOrAlternate( ... - settingsTree, 'setup_nlp_max_iterations', 2e4))); + settingsTree, 'setup_nlp_max_iterations', 2e4); solverSettings.displayLevel = parseDoubleOrAlternate( ... - settingsTree, 'setup_display_level', 2))); + settingsTree, 'setup_display_level', 2); solverSettings.integralBound = parseDoubleOrAlternate( ... - settingsTree, "integral_bound", 1)); + settingsTree, "integral_bound", 1); end diff --git a/src/core/optimal_control/getOptimalControlSolverSettings.m b/src/core/optimal_control/getOptimalControlSolverSettings.m index eace6055c..a27be3fed 100644 --- a/src/core/optimal_control/getOptimalControlSolverSettings.m +++ b/src/core/optimal_control/getOptimalControlSolverSettings.m @@ -27,11 +27,13 @@ function solverSettings = getOptimalControlSolverSettings(settingsFileName) solverSettingsTree = xml2struct(settingsFileName); verifyVersion(solverSettingsTree, "OptimalControlSolverSettings"); -tree = solverSettingsTree.NMSMPipelineDocument; +tree = solverSettingsTree.NMSMPipelineDocument.OptimalControlSolverSettings; if isfield(tree, 'GpopsSettings') solverSettings = getGpopsSolverSettings(tree); -end -if isfield(tree, 'MocoSettings') +elseif isfield(tree, 'MocoSettings') solverSettings = getMocoSolverSettings(tree); +else + throw(MException("OptimalControlSolverSettings:UnsupportedSolver", ... + "Only and are supported")) end end diff --git a/src/core/parse/parseTreatmentOptimizationParams.m b/src/core/parse/parseTreatmentOptimizationParams.m index 4d7d32343..07b89aab8 100644 --- a/src/core/parse/parseTreatmentOptimizationParams.m +++ b/src/core/parse/parseTreatmentOptimizationParams.m @@ -26,5 +26,6 @@ function params = parseTreatmentOptimizationParams(tree) params.solverSettings = getOptimalControlSolverSettings(... - getTextFromField(getFieldByName(tree, 'optimal_control_settings_file'))); + getTextFromField(getFieldByName(tree, ... + 'optimal_control_solver_settings_file'))); end From d4ece12fcc6fec648bfd0876d2725bcb1467f891 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 27 Aug 2023 17:46:36 -0500 Subject: [PATCH 021/365] fixes tool name fn --- .../parseDesignOptimizationSettingsTree.m | 3 +- .../parseTrackingOptimizationSettingsTree.m | 3 +- ...arseVerificationOptimizationSettingsTree.m | 3 +- src/core/parse/findToolName.m | 42 +++++++++++++++++++ 4 files changed, 45 insertions(+), 6 deletions(-) create mode 100644 src/core/parse/findToolName.m diff --git a/src/DesignOptimization/parseDesignOptimizationSettingsTree.m b/src/DesignOptimization/parseDesignOptimizationSettingsTree.m index 49b752ffc..93953100b 100644 --- a/src/DesignOptimization/parseDesignOptimizationSettingsTree.m +++ b/src/DesignOptimization/parseDesignOptimizationSettingsTree.m @@ -30,12 +30,11 @@ function [inputs, params] = ... parseDesignOptimizationSettingsTree(settingsTree) -inputs = getTreatmentOptimizationInputs(settingsTree); +inputs = parseTreatmentOptimizationInputs(settingsTree); inputs = parseTreatmentOptimizationDesignVariableBounds(settingsTree, ... inputs); inputs = parseDesignSettings(settingsTree, inputs); inputs = getInputs(settingsTree); -inputs.toolName = "DesignOptimization"; params = parseTreatmentOptimizationParams(settingsTree); inputs = modifyModelForces(inputs); inputs = updateMuscleModelProperties(inputs); diff --git a/src/TrackingOptimization/parseTrackingOptimizationSettingsTree.m b/src/TrackingOptimization/parseTrackingOptimizationSettingsTree.m index f66926578..86a76538f 100644 --- a/src/TrackingOptimization/parseTrackingOptimizationSettingsTree.m +++ b/src/TrackingOptimization/parseTrackingOptimizationSettingsTree.m @@ -30,10 +30,9 @@ function [inputs, params, resultsDirectory] = ... parseTrackingOptimizationSettingsTree(settingsTree) -inputs = getTreatmentOptimizationInputs(settingsTree); +inputs = parseTreatmentOptimizationInputs(settingsTree); inputs = parseTreatmentOptimizationDesignVariableBounds(settingsTree, ... inputs); -inputs.toolName = "TrackingOptimization"; params = parseTreatmentOptimizationParams(settingsTree); inputs = modifyModelForces(inputs); end diff --git a/src/VerificationOptimization/parseVerificationOptimizationSettingsTree.m b/src/VerificationOptimization/parseVerificationOptimizationSettingsTree.m index 12d6877c8..297b9f0be 100644 --- a/src/VerificationOptimization/parseVerificationOptimizationSettingsTree.m +++ b/src/VerificationOptimization/parseVerificationOptimizationSettingsTree.m @@ -30,11 +30,10 @@ function [inputs, params] = ... parseVerificationOptimizationSettingsTree(settingsTree) -inputs = getTreatmentOptimizationInputs(settingsTree); +inputs = parseTreatmentOptimizationInputs(settingsTree); inputs = parseTreatmentOptimizationDesignVariableBounds(settingsTree, ... inputs); inputs = getInputs(settingsTree); -inputs.toolName = "VerificationOptimization"; params = parseTreatmentOptimizationParams(settingsTree); inputs = modifyModelForces(inputs); end diff --git a/src/core/parse/findToolName.m b/src/core/parse/findToolName.m new file mode 100644 index 000000000..466934cd2 --- /dev/null +++ b/src/core/parse/findToolName.m @@ -0,0 +1,42 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% (struct) -> (string) +% returns the name of the tool from the settings tree + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Claire V. Hammond % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function toolName = findToolName(tree) +if ~isfield(tree, "NMSMPipelineDocument") + throw(MException("XMLParse:IncorrectDocument", ... + "XML files does not include Date: Sun, 27 Aug 2023 17:47:21 -0500 Subject: [PATCH 022/365] update treatment optimization parse --- .../parseTreatmentOptimizationInputs.m} | 12 ++++++++---- src/core/parse/parseTreatmentOptimizationParams.m | 4 +--- 2 files changed, 9 insertions(+), 7 deletions(-) rename src/core/{TreatmentOptimization/getTreatmentOptimizationInputs.m => parse/parseTreatmentOptimizationInputs.m} (87%) diff --git a/src/core/TreatmentOptimization/getTreatmentOptimizationInputs.m b/src/core/parse/parseTreatmentOptimizationInputs.m similarity index 87% rename from src/core/TreatmentOptimization/getTreatmentOptimizationInputs.m rename to src/core/parse/parseTreatmentOptimizationInputs.m index 6d3a087e4..1f95fae40 100644 --- a/src/core/TreatmentOptimization/getTreatmentOptimizationInputs.m +++ b/src/core/parse/parseTreatmentOptimizationInputs.m @@ -29,7 +29,8 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function inputs = getTreatmentOptimizationInputs(tree) +function inputs = parseTreatmentOptimizationInputs(tree) +inputs.toolName = findToolName(tree); inputs.resultsDirectory = getTextFromField(getFieldByName(tree, ... 'results_directory')); if(isempty(inputs.resultsDirectory)); inputs.resultsDirectory = pwd; end @@ -43,11 +44,14 @@ inputs = parseTorqueController(tree, inputs); end inputs = parseTreatmentOptimizationDataDirectory(tree, inputs); -inputs.initialGuess = getGpopsInitialGuess(tree); +inputs = parseOptimalControlSolverSettings( ... + getTextFromField(getFieldByNameOrError(tree, ... + 'optimal_control_solver_settings_file')), inputs); inputs.costTerms = parseRcnlCostTermSet( ... getFieldByNameOrError(tree, 'RCNLCostTermSet').RCNLCostTerm); -inputs.path = getPathConstraintTerms(tree); -inputs.terminal = getTerminalConstraintTerms(tree); +[inputs.path, inputs.terminal] = parseRcnlConstraintTermSet( ... + getFieldByNameOrError(tree, 'RCNLConstraintTermSet') ... + .RCNLConstraintTerm, inputs.toolName, inputs.controllerType); contactSurfaces = getFieldByName(inputs.osimx, "contactSurface"); if (isstruct(contactSurfaces) || iscell(contactSurfaces)) && ... isfield(inputs, "grfFileName") diff --git a/src/core/parse/parseTreatmentOptimizationParams.m b/src/core/parse/parseTreatmentOptimizationParams.m index 07b89aab8..995d90fa2 100644 --- a/src/core/parse/parseTreatmentOptimizationParams.m +++ b/src/core/parse/parseTreatmentOptimizationParams.m @@ -25,7 +25,5 @@ % ----------------------------------------------------------------------- % function params = parseTreatmentOptimizationParams(tree) -params.solverSettings = getOptimalControlSolverSettings(... - getTextFromField(getFieldByName(tree, ... - 'optimal_control_solver_settings_file'))); +params = struct(); end From 27c146f23e60cd2b4b930adc1fe27eced7b76f53 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 27 Aug 2023 17:48:26 -0500 Subject: [PATCH 023/365] update optimal control logic flow --- .../_DZOlgCat9UGn1zVAOqi8Vqtd40d.xml | 2 + .../_DZOlgCat9UGn1zVAOqi8Vqtd40p.xml | 2 + .../2XppXtEHR0PCZWWNfUwQdRTKlDcd.xml | 2 + .../2XppXtEHR0PCZWWNfUwQdRTKlDcp.xml | 2 + .../oq4yCbIVISuOld7-sgX9uJKIS1wd.xml | 6 +++ .../oq4yCbIVISuOld7-sgX9uJKIS1wp.xml | 2 + .../o2cKHtbeLCI5dGNR0Lwpzdptr4sd.xml | 2 + .../o2cKHtbeLCI5dGNR0Lwpzdptr4sp.xml | 2 + .../gpops/convertToGpopsTorqueDrivenInputs.m | 31 +++++++++++ .../solveOptimalControlProblem.m | 54 +++++++++---------- ...rSettings.m => parseGpopsSolverSettings.m} | 2 +- ....m => parseOptimalControlSolverSettings.m} | 10 ++-- 12 files changed, 84 insertions(+), 33 deletions(-) create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/_DZOlgCat9UGn1zVAOqi8Vqtd40d.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/_DZOlgCat9UGn1zVAOqi8Vqtd40p.xml create mode 100644 resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/2XppXtEHR0PCZWWNfUwQdRTKlDcd.xml create mode 100644 resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/2XppXtEHR0PCZWWNfUwQdRTKlDcp.xml create mode 100644 resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/oq4yCbIVISuOld7-sgX9uJKIS1wd.xml create mode 100644 resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/oq4yCbIVISuOld7-sgX9uJKIS1wp.xml create mode 100644 resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/o2cKHtbeLCI5dGNR0Lwpzdptr4sd.xml create mode 100644 resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/o2cKHtbeLCI5dGNR0Lwpzdptr4sp.xml create mode 100644 src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m rename src/core/optimal_control/{getGpopsSolverSettings.m => parseGpopsSolverSettings.m} (98%) rename src/core/optimal_control/{getOptimalControlSolverSettings.m => parseOptimalControlSolverSettings.m} (88%) diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/_DZOlgCat9UGn1zVAOqi8Vqtd40d.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/_DZOlgCat9UGn1zVAOqi8Vqtd40d.xml new file mode 100644 index 000000000..d66932c0e --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/_DZOlgCat9UGn1zVAOqi8Vqtd40d.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/_DZOlgCat9UGn1zVAOqi8Vqtd40p.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/_DZOlgCat9UGn1zVAOqi8Vqtd40p.xml new file mode 100644 index 000000000..89d2cc464 --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/_DZOlgCat9UGn1zVAOqi8Vqtd40p.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/2XppXtEHR0PCZWWNfUwQdRTKlDcd.xml b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/2XppXtEHR0PCZWWNfUwQdRTKlDcd.xml new file mode 100644 index 000000000..a75f7a81b --- /dev/null +++ b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/2XppXtEHR0PCZWWNfUwQdRTKlDcd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/2XppXtEHR0PCZWWNfUwQdRTKlDcp.xml b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/2XppXtEHR0PCZWWNfUwQdRTKlDcp.xml new file mode 100644 index 000000000..842de6ab3 --- /dev/null +++ b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/2XppXtEHR0PCZWWNfUwQdRTKlDcp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/oq4yCbIVISuOld7-sgX9uJKIS1wd.xml b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/oq4yCbIVISuOld7-sgX9uJKIS1wd.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/oq4yCbIVISuOld7-sgX9uJKIS1wd.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/oq4yCbIVISuOld7-sgX9uJKIS1wp.xml b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/oq4yCbIVISuOld7-sgX9uJKIS1wp.xml new file mode 100644 index 000000000..43ee7b3dc --- /dev/null +++ b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/oq4yCbIVISuOld7-sgX9uJKIS1wp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/o2cKHtbeLCI5dGNR0Lwpzdptr4sd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/o2cKHtbeLCI5dGNR0Lwpzdptr4sd.xml new file mode 100644 index 000000000..a75f7a81b --- /dev/null +++ b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/o2cKHtbeLCI5dGNR0Lwpzdptr4sd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/o2cKHtbeLCI5dGNR0Lwpzdptr4sp.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/o2cKHtbeLCI5dGNR0Lwpzdptr4sp.xml new file mode 100644 index 000000000..7ae5b00c2 --- /dev/null +++ b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/o2cKHtbeLCI5dGNR0Lwpzdptr4sp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m b/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m new file mode 100644 index 000000000..4e1912aa2 --- /dev/null +++ b/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m @@ -0,0 +1,31 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Claire V. Hammond % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function setup = convertToGpopsTorqueDrivenInputs(inputs, params) +inputs.initialGuess = getGpopsInitialGuess(tree); + +end + diff --git a/src/TreatmentOptimization/solveOptimalControlProblem.m b/src/TreatmentOptimization/solveOptimalControlProblem.m index 9efcea8bb..1d30b617e 100644 --- a/src/TreatmentOptimization/solveOptimalControlProblem.m +++ b/src/TreatmentOptimization/solveOptimalControlProblem.m @@ -26,36 +26,34 @@ function solution = solveOptimalControlProblem(inputs, params) if strcmp(inputs.controllerType, "synergy") - switch inputs.solver - case 'gpops' - setup = convertToGpopsSynergyDrivenInputs(inputs, params); - solution = gpops2(setup); - solution = convertFromGpopsSynergyDrivenOutputs(solution, ... - inputs, params); - case 'moco' - setup = convertToMocoSynergyDrivenInputs(inputs, params); - solution = moco(setup); - solution = convertFromMocoSynergyDrivenOutputs(solution, ... - inputs, params); - otherwise - MException('solveOptimalControlProblem:invalidSolver', ... - 'Invalid solver specified.'); + if isfield(inputs, "gpops") + setup = convertToGpopsSynergyDrivenInputs(inputs, params); + solution = gpops2(setup); + solution = convertFromGpopsSynergyDrivenOutputs(solution, ... + inputs, params); + elseif isfield(inputs, "moco") + setup = convertToMocoSynergyDrivenInputs(inputs, params); + solution = moco(setup); + solution = convertFromMocoSynergyDrivenOutputs(solution, ... + inputs, params); + else + MException('solveOptimalControlProblem:invalidSolver', ... + 'Invalid solver specified.'); end elseif strcmp(inputs.controllerType, "torque") - switch inputs.solver - case 'gpops' - setup = convertToGpopsTorqueDrivenInputs(inputs, params); - solution = gpops2(setup); - solution = convertFromGpopsTorqueDrivenOutputs(solution, ... - inputs, params); - case 'moco' - setup = convertToMocoTorqueDrivenInputs(inputs, params); - solution = moco(setup); - solution = convertFromMocoTorqueDrivenOutputs(solution, ... - inputs, params); - otherwise - MException('solveOptimalControlProblem:invalidSolver', ... - 'Invalid solver specified.'); + if isfield(inputs, "gpops") + setup = convertToGpopsTorqueDrivenInputs(inputs, params); + solution = gpops2(setup); + solution = convertFromGpopsTorqueDrivenOutputs(solution, ... + inputs, params); + elseif isfield(inputs, "moco") + setup = convertToMocoTorqueDrivenInputs(inputs, params); + solution = moco(setup); + solution = convertFromMocoTorqueDrivenOutputs(solution, ... + inputs, params); + else + MException('solveOptimalControlProblem:invalidSolver', ... + 'Invalid solver specified.'); end else MException('solveOptimalControlProblem:invalidProblemType', ... diff --git a/src/core/optimal_control/getGpopsSolverSettings.m b/src/core/optimal_control/parseGpopsSolverSettings.m similarity index 98% rename from src/core/optimal_control/getGpopsSolverSettings.m rename to src/core/optimal_control/parseGpopsSolverSettings.m index 17d51011c..03139bc99 100644 --- a/src/core/optimal_control/getGpopsSolverSettings.m +++ b/src/core/optimal_control/parseGpopsSolverSettings.m @@ -28,7 +28,7 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function solverSettings = getGpopsSolverSettings(settingsTree) +function solverSettings = parseGpopsSolverSettings(settingsTree) solverSettings.optimizationFileName = 'trackingOptimizationOutputFile.txt'; solverSettings.derivativeSupplier = parseTextOrAlternate( ... settingsTree, 'setup_derivatives_supplier', 'sparseFD'); diff --git a/src/core/optimal_control/getOptimalControlSolverSettings.m b/src/core/optimal_control/parseOptimalControlSolverSettings.m similarity index 88% rename from src/core/optimal_control/getOptimalControlSolverSettings.m rename to src/core/optimal_control/parseOptimalControlSolverSettings.m index a27be3fed..60a225072 100644 --- a/src/core/optimal_control/getOptimalControlSolverSettings.m +++ b/src/core/optimal_control/parseOptimalControlSolverSettings.m @@ -24,14 +24,16 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function solverSettings = getOptimalControlSolverSettings(settingsFileName) +function inputs = parseOptimalControlSolverSettings( ... + settingsFileName, inputs) solverSettingsTree = xml2struct(settingsFileName); verifyVersion(solverSettingsTree, "OptimalControlSolverSettings"); -tree = solverSettingsTree.NMSMPipelineDocument.OptimalControlSolverSettings; +tree = ... + solverSettingsTree.NMSMPipelineDocument.OptimalControlSolverSettings; if isfield(tree, 'GpopsSettings') - solverSettings = getGpopsSolverSettings(tree); + inputs.gpops = parseGpopsSolverSettings(tree); elseif isfield(tree, 'MocoSettings') - solverSettings = getMocoSolverSettings(tree); + inputs.moco = parseMocoSolverSettings(tree); else throw(MException("OptimalControlSolverSettings:UnsupportedSolver", ... "Only and are supported")) From 41a6d57042d3e05fdb1cd61b1dc010c28b9a844e Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 27 Aug 2023 17:48:34 -0500 Subject: [PATCH 024/365] revamp constraint term set --- .../getPathConstraintTerms.m | 45 ------ .../getTerminalConstraintTerms.m | 44 ------ src/core/parse/parseRcnlConstraintTermSet.m | 143 ++++++++++++++++-- 3 files changed, 132 insertions(+), 100 deletions(-) delete mode 100644 src/core/TreatmentOptimization/getPathConstraintTerms.m delete mode 100644 src/core/TreatmentOptimization/getTerminalConstraintTerms.m diff --git a/src/core/TreatmentOptimization/getPathConstraintTerms.m b/src/core/TreatmentOptimization/getPathConstraintTerms.m deleted file mode 100644 index d56aef764..000000000 --- a/src/core/TreatmentOptimization/getPathConstraintTerms.m +++ /dev/null @@ -1,45 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% Parses XML settings for a RCNLPathConstraintTerms, adding all fields -% included in the xml block. -% -% (struct) -> (struct) -% Parses settings from a RCNLPathConstraintTerms. - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega, Claire V. Hammond % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function path = getPathConstraintTerms(tree) -pathConstraintTermsTree = getFieldByName(tree, ... - 'RCNLPathConstraintTerms'); -if(isstruct(pathConstraintTermsTree)) - pathConstraints = getFieldByName(pathConstraintTermsTree, ... - 'RCNLConstraintTerm'); - if isstruct(pathConstraints) || iscell(pathConstraints) - path = parseRcnlConstraintTermSet(pathConstraints); - else - path = {}; - end -else - path = {}; -end -end \ No newline at end of file diff --git a/src/core/TreatmentOptimization/getTerminalConstraintTerms.m b/src/core/TreatmentOptimization/getTerminalConstraintTerms.m deleted file mode 100644 index 8f532ae19..000000000 --- a/src/core/TreatmentOptimization/getTerminalConstraintTerms.m +++ /dev/null @@ -1,44 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% Parses XML settings for a RCNLTerminalConstraintTerms, adding all fields -% included in the xml block. -% -% (struct) -> (struct) -% Parses settings from a RCNLTerminalConstraintTerms. - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega, Claire V. Hammond % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function terminal = getTerminalConstraintTerms(tree) -terminalConstraintTermsTree = getFieldByName(tree, ... - 'RCNLTerminalConstraintTerms'); -if(isstruct(terminalConstraintTermsTree)) - terminalConstraints = getFieldByName(terminalConstraintTermsTree, ... - "RCNLConstraintTerm"); - if isstruct(terminalConstraints) || iscell(terminalConstraints) - terminal = parseRcnlConstraintTermSet(terminalConstraints); - else - terminal = {}; - end -else - terminal = {}; -end \ No newline at end of file diff --git a/src/core/parse/parseRcnlConstraintTermSet.m b/src/core/parse/parseRcnlConstraintTermSet.m index 208a78f72..f1c94749e 100644 --- a/src/core/parse/parseRcnlConstraintTermSet.m +++ b/src/core/parse/parseRcnlConstraintTermSet.m @@ -1,7 +1,7 @@ % This function is part of the NMSM Pipeline, see file for full license. % -% Parses XML settings for a RCNLCostTermSet, adding all fields included in -% the xml block. +% Parses XML settings for a RCNLCostTermSet, adding all fields included in +% the xml block. % % (struct) -> (struct) % Parses settings from a RCNLCostTermSet. @@ -14,7 +14,7 @@ % National Institutes of Health (R01 EB030520). % % % % Copyright (c) 2022 Rice University and the Authors % -% Author(s): Marleny Vega % +% Author(s): Claire V. Hammond, Spencer Williams % % % % Licensed under the Apache License, Version 2.0 (the "License"); % % you may not use this file except in compliance with the License. % @@ -28,8 +28,10 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function costTerms = parseRcnlConstraintTermSet(tree) -costTerms = cell(1, length(tree)); +function [path, terminal] = parseRcnlConstraintTermSet(tree, tool, ... + controllerType) +path = {}; +terminal = {}; for term = 1:length(tree) if length(tree) == 1 currentTerm = tree; @@ -37,14 +39,20 @@ currentTerm = tree{term}; end % Find general cost term elements - costTerms{term}.type = getTextFromField(getFieldByNameOrError( ... + tempTerm.type = getTextFromField(getFieldByNameOrError( ... currentTerm, 'type')); + [isValid, isPath] = isTypeValid(tempTerm.type, tool, controllerType); + if ~isValid + throw(MException("ConstraintTermSet:InvalidType", ... + strcat(tempTerm.type, " is not a valid constraint", ... + " term for tool ", tool))); + end enabled = getTextFromField(getFieldByNameOrAlternate( ... currentTerm, 'is_enabled', 'false')); - costTerms{term}.isEnabled = strcmpi(enabled, 'true'); - costTerms{term}.maxError = str2double(getTextFromField( ... + tempTerm.isEnabled = strcmpi(enabled, 'true'); + tempTerm.maxError = str2double(getTextFromField( ... getFieldByNameOrAlternate(currentTerm, 'max_error', '1'))); - costTerms{term}.minError = str2double(getTextFromField( ... + tempTerm.minError = str2double(getTextFromField( ... getFieldByNameOrAlternate(currentTerm, 'min_error', '-1'))); % Find other cost term elements termElements = fieldnames(currentTerm); @@ -60,9 +68,122 @@ elseif ~isnan(str2double(contents)) contents = str2double(contents); end - costTerms{term}.(termElements{element}) = contents; + tempTerm.(termElements{element}) = contents; end - end + end + if isPath + path{end + 1} = tempTerm; + else + terminal{end + 1} = tempTerm; + end end end +function [isValid, isPath] = isTypeValid(type, tool, controllerType) +if strcmp(controllerType, "torque") + switch tool + case "TrackingOptimization" + pathTypes = [ ... + "root_segment_residual_load", ... + "torque_model_moment_consistency", ... + ]; + terminalTypes = [ ... + "state_position_periodicity", ... + "state_velocity_periodicity", ... + "root_segment_residual_load_periodicity", ... + "external_force_periodicity", ... + "external_moment_periodicity", ... + ]; + case "VerificationOptimization" + pathTypes = [ ... + "root_segment_residual_load", ... + "torque_model_moment_consistency", ... + ]; + terminalTypes = [ ... + "state_position_periodicity", ... + "state_velocity_periodicity", ... + "root_segment_residual_load_periodicity", ... + "external_force_periodicity", ... + "external_moment_periodicity", ... + ]; + case "DesignOptimization" + pathTypes = [ ... + "root_segment_residual_load", ... + "torque_model_moment_consistency", ... + "limit_normalized_fiber_length", ... + ]; + terminalTypes = [ ... + "state_position_periodicity", ... + "state_velocity_periodicity", ... + "root_segment_residual_load_periodicity", ... + "external_force_periodicity", ... + "external_moment_periodicity", ... + "final_state_position", ... + "final_state_velocity", ... + "final_point_position", ... + "final_point_velocity", ... + ]; + otherwise + throw(MException("TreatmentOptimization:InvalidTool", ... + "Tool term is not valid.")) + end + isValid = any(strcmp(type, pathTypes)); + isPath = isValid; + isValid = isValid || any(strcmp(type, terminalTypes)); + return +elseif strcmp(controllerType, "synergy") + switch tool + case "TrackingOptimization" + pathTypes = [ ... + "root_segment_residual_load", ... + "muscle_model_moment_consistency", ... + ]; + terminalTypes = [ ... + "state_position_periodicity", ... + "state_velocity_periodicity", ... + "root_segment_residual_load_periodicity", ... + "external_force_periodicity", ... + "external_moment_periodicity", ... + "synergy_weight_sum", ... + ]; + case "VerificationOptimization" + pathTypes = [ ... + "root_segment_residual_load", ... + "muscle_model_moment_consistency", ... + ]; + terminalTypes = [ ... + "state_position_periodicity", ... + "state_velocity_periodicity", ... + "root_segment_residual_load_periodicity", ... + "external_force_periodicity", ... + "external_moment_periodicity", ... + ]; + case "DesignOptimization" + pathTypes = [ ... + "root_segment_residual_load", ... + "muscle_model_moment_consistency", ... + "limit_muscle_activation", ... + "limit_normalized_fiber_length", ... + "external_control_muscle_moment_consistency", ... + ]; + terminalTypes = [ ... + "state_position_periodicity", ... + "state_velocity_periodicity", ... + "root_segment_residual_load_periodicity", ... + "external_force_periodicity", ... + "external_moment_periodicity", ... + "synergy_weight_sum", ... + "final_state_position", ... + "final_state_velocity", ... + "final_point_position", ... + "final_point_velocity", ... + ]; + otherwise + throw(MException("TreatmentOptimization:InvalidTool", ... + "Tool term is not valid.")) + end + isValid = any(strcmp(type, pathTypes)); + isPath = isValid; + isValid = isValid || any(strcmp(type, terminalTypes)); +end +end From 32658804169e0dfadd3c88b4be568ee5247334e4 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 27 Aug 2023 18:16:39 -0500 Subject: [PATCH 025/365] move ground contact parse to new fn --- ...ctSurfaces.m => parseGroundContactSurfaces.m} | 16 +++++++++++++++- .../parse/parseTreatmentOptimizationInputs.m | 9 +-------- 2 files changed, 16 insertions(+), 9 deletions(-) rename src/core/TreatmentOptimization/{prepareGroundContactSurfaces.m => parseGroundContactSurfaces.m} (90%) diff --git a/src/core/TreatmentOptimization/prepareGroundContactSurfaces.m b/src/core/TreatmentOptimization/parseGroundContactSurfaces.m similarity index 90% rename from src/core/TreatmentOptimization/prepareGroundContactSurfaces.m rename to src/core/TreatmentOptimization/parseGroundContactSurfaces.m index be84b8ca3..3e9b98e43 100644 --- a/src/core/TreatmentOptimization/prepareGroundContactSurfaces.m +++ b/src/core/TreatmentOptimization/parseGroundContactSurfaces.m @@ -25,7 +25,19 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function output = prepareGroundContactSurfaces(osimModel, contactSurfaces, grfFileName) +function contactSurfaces = parseGroundContactSurfaces(inputs) +contactSurfacesField = getFieldByName(inputs.osimx, "contactSurface"); +if (isstruct(contactSurfacesField) || iscell(contactSurfacesField)) && ... + isfield(inputs, "grfFileName") + contactSurfaces = prepareGroundContactSurfaces(inputs.model, ... + contactSurfacesField, inputs.grfFileName); +else + contactSurfaces = {}; +end +end + +function output = prepareGroundContactSurfaces(osimModel, ... + contactSurfaces, grfFileName) import org.opensim.modeling.Model osimModel = Model(osimModel); osimModel.finalizeConnections(); @@ -48,6 +60,7 @@ grfFileName, output{i}); end end + function output = getParentChildSprings(osimModel, contactSurfaces) output = contactSurfaces; output.parentSpringPointsOnBody = []; @@ -70,6 +83,7 @@ end end end + function output = parseGroundReactionDataWithoutTime(model, grfFile, output) import org.opensim.modeling.Storage [grfColumnNames, ~, grfData] = parseMotToComponents(model, Storage(grfFile)); diff --git a/src/core/parse/parseTreatmentOptimizationInputs.m b/src/core/parse/parseTreatmentOptimizationInputs.m index 1f95fae40..55dbd5922 100644 --- a/src/core/parse/parseTreatmentOptimizationInputs.m +++ b/src/core/parse/parseTreatmentOptimizationInputs.m @@ -52,12 +52,5 @@ [inputs.path, inputs.terminal] = parseRcnlConstraintTermSet( ... getFieldByNameOrError(tree, 'RCNLConstraintTermSet') ... .RCNLConstraintTerm, inputs.toolName, inputs.controllerType); -contactSurfaces = getFieldByName(inputs.osimx, "contactSurface"); -if (isstruct(contactSurfaces) || iscell(contactSurfaces)) && ... - isfield(inputs, "grfFileName") - inputs.contactSurfaces = prepareGroundContactSurfaces(inputs.model, ... - contactSurfaces, inputs.grfFileName); -else - inputs.contactSurfaces = {}; -end +inputs.contactSurfaces = parseGroundContactSurfaces(inputs); end From fb9ecb50b582da8ddb97af4e93e6c871cd8e5f7e Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 27 Aug 2023 18:50:19 -0500 Subject: [PATCH 026/365] initial gpops torque inputs fn --- .../gpops/convertToGpopsTorqueDrivenInputs.m | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m b/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m index 4e1912aa2..3d107763b 100644 --- a/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m +++ b/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m @@ -25,7 +25,25 @@ % ----------------------------------------------------------------------- % function setup = convertToGpopsTorqueDrivenInputs(inputs, params) -inputs.initialGuess = getGpopsInitialGuess(tree); +bounds = setupProblemBounds(inputs, params); +guess = setupCommonOptimalControlInitialGuess(inputs); +initializeMexOrMatlabParallelFunctions(inputs.model); +setup = setupCommonOptimalControlSolverSettings(inputs, ... + bounds, guess, params, ... + @computeTrackingOptimizationContinuousFunction, ... + @computeTrackingOptimizationEndpointFunction); +checkInitialGuess(guess, inputs, ... + @computeTrackingOptimizationContinuousFunction); +end +function bounds = setupProblemBounds(inputs, params) +bounds = setupCommonOptimalControlBounds(inputs, params); +% setup parameter bounds +if strcmp(inputs.controllerType, 'synergy_driven') + if inputs.optimizeSynergyVectors + bounds.parameter.lower = -0.5 * ones(1, length(inputs.minParameter)); + bounds.parameter.upper = 0.5 * ones(1, length(inputs.minParameter)); + end +end end From a4f231d6c4f0a6796d94d9295d6cf0dd39e2c915 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 27 Aug 2023 18:50:54 -0500 Subject: [PATCH 027/365] initial gpops torque driven output --- .../convertFromGpopsTorqueDrivenOutputs.m | 37 +++++++++++++++++++ .../solveOptimalControlProblem.m | 10 ++--- 2 files changed, 42 insertions(+), 5 deletions(-) create mode 100644 src/TreatmentOptimization/gpops/convertFromGpopsTorqueDrivenOutputs.m diff --git a/src/TreatmentOptimization/gpops/convertFromGpopsTorqueDrivenOutputs.m b/src/TreatmentOptimization/gpops/convertFromGpopsTorqueDrivenOutputs.m new file mode 100644 index 000000000..5dacb3084 --- /dev/null +++ b/src/TreatmentOptimization/gpops/convertFromGpopsTorqueDrivenOutputs.m @@ -0,0 +1,37 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Claire V. Hammond % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function output = convertFromGpopsTorqueDrivenOutputs(solution, ... + inputs, params) +solution = solution.result.solution; +solution.auxdata = inputs; +if isfield(inputs, "optimizeSynergyVectors") + solution.phase.parameter = solution.parameter; +end +output = computeTrackingOptimizationContinuousFunction(solution); +output.solution = solution; +end + diff --git a/src/TreatmentOptimization/solveOptimalControlProblem.m b/src/TreatmentOptimization/solveOptimalControlProblem.m index 1d30b617e..9ebac85e7 100644 --- a/src/TreatmentOptimization/solveOptimalControlProblem.m +++ b/src/TreatmentOptimization/solveOptimalControlProblem.m @@ -24,17 +24,17 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function solution = solveOptimalControlProblem(inputs, params) +function output = solveOptimalControlProblem(inputs, params) if strcmp(inputs.controllerType, "synergy") if isfield(inputs, "gpops") setup = convertToGpopsSynergyDrivenInputs(inputs, params); solution = gpops2(setup); - solution = convertFromGpopsSynergyDrivenOutputs(solution, ... + output = convertFromGpopsSynergyDrivenOutputs(solution, ... inputs, params); elseif isfield(inputs, "moco") setup = convertToMocoSynergyDrivenInputs(inputs, params); solution = moco(setup); - solution = convertFromMocoSynergyDrivenOutputs(solution, ... + output = convertFromMocoSynergyDrivenOutputs(solution, ... inputs, params); else MException('solveOptimalControlProblem:invalidSolver', ... @@ -44,12 +44,12 @@ if isfield(inputs, "gpops") setup = convertToGpopsTorqueDrivenInputs(inputs, params); solution = gpops2(setup); - solution = convertFromGpopsTorqueDrivenOutputs(solution, ... + output = convertFromGpopsTorqueDrivenOutputs(solution, ... inputs, params); elseif isfield(inputs, "moco") setup = convertToMocoTorqueDrivenInputs(inputs, params); solution = moco(setup); - solution = convertFromMocoTorqueDrivenOutputs(solution, ... + output = convertFromMocoTorqueDrivenOutputs(solution, ... inputs, params); else MException('solveOptimalControlProblem:invalidSolver', ... From 70fe7a80ae5e603e8d43679af0ceed714edded6e Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 27 Aug 2023 18:51:05 -0500 Subject: [PATCH 028/365] swaps name back --- .../{calcDynamicsConstraint.m => calcDynamicConstraint.m} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/TreatmentOptimization/{calcDynamicsConstraint.m => calcDynamicConstraint.m} (100%) diff --git a/src/TreatmentOptimization/calcDynamicsConstraint.m b/src/TreatmentOptimization/calcDynamicConstraint.m similarity index 100% rename from src/TreatmentOptimization/calcDynamicsConstraint.m rename to src/TreatmentOptimization/calcDynamicConstraint.m From 50f46feb768ace8840e88675ce0a577fa9d11312 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 27 Aug 2023 18:51:54 -0500 Subject: [PATCH 029/365] fixes initial guess impl --- .../parseInitialGuess.m} | 20 ++++++++++++------- .../parse/parseTreatmentOptimizationInputs.m | 1 + 2 files changed, 14 insertions(+), 7 deletions(-) rename src/core/{TreatmentOptimization/OptimalControlSetupFunctions/getGpopsInitialGuess.m => parse/parseInitialGuess.m} (81%) diff --git a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/getGpopsInitialGuess.m b/src/core/parse/parseInitialGuess.m similarity index 81% rename from src/core/TreatmentOptimization/OptimalControlSetupFunctions/getGpopsInitialGuess.m rename to src/core/parse/parseInitialGuess.m index fd804d2b3..42d577865 100644 --- a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/getGpopsInitialGuess.m +++ b/src/core/parse/parseInitialGuess.m @@ -28,26 +28,32 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function initialGuess = getGpopsInitialGuess(tree) +function initialGuess = parseInitialGuess(tree) import org.opensim.modeling.Storage initialGuess = []; statesFileName = getTextFromField(getFieldByNameOrAlternate(tree, ... 'initial_states_file', '')); if ~isempty(statesFileName) initialGuess.time = parseTimeColumn({statesFileName})'; - initialGuess.stateLabels = getStorageColumnNames(Storage({statesFileName})); - initialGuess.state = parseTreatmentOptimizationStandard({statesFileName}); + initialGuess.stateLabels = getStorageColumnNames(Storage( ... + {statesFileName})); + initialGuess.state = parseTreatmentOptimizationStandard( ... + {statesFileName}); end controlsFileName = getTextFromField(getFieldByNameOrAlternate(tree, ... 'initial_controls_file', '')); if ~isempty(controlsFileName) - initialGuess.controlLabels = getStorageColumnNames(Storage({controlsFileName})); - initialGuess.control = parseTreatmentOptimizationStandard({controlsFileName}); + initialGuess.controlLabels = getStorageColumnNames(Storage( ... + {controlsFileName})); + initialGuess.control = parseTreatmentOptimizationStandard( ... + {controlsFileName}); end parametersFileName = getTextFromField(getFieldByNameOrAlternate(tree, ... 'initial_parameters_file', '')); if ~isempty(parametersFileName) - initialGuess.parameterLabels = getStorageColumnNames(Storage({parametersFileName})); - initialGuess.parameter = parseTreatmentOptimizationStandard({parametersFileName}); + initialGuess.parameterLabels = getStorageColumnNames( ... + Storage({parametersFileName})); + initialGuess.parameter = parseTreatmentOptimizationStandard( ... + {parametersFileName}); end end \ No newline at end of file diff --git a/src/core/parse/parseTreatmentOptimizationInputs.m b/src/core/parse/parseTreatmentOptimizationInputs.m index 55dbd5922..9ba397a52 100644 --- a/src/core/parse/parseTreatmentOptimizationInputs.m +++ b/src/core/parse/parseTreatmentOptimizationInputs.m @@ -44,6 +44,7 @@ inputs = parseTorqueController(tree, inputs); end inputs = parseTreatmentOptimizationDataDirectory(tree, inputs); +inputs.initialGuess = parseInitialGuess(tree); inputs = parseOptimalControlSolverSettings( ... getTextFromField(getFieldByNameOrError(tree, ... 'optimal_control_solver_settings_file')), inputs); From 6494a4e7e6f33dd3b2235d64d78f80e843456fa4 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 27 Aug 2023 18:53:45 -0500 Subject: [PATCH 030/365] moved variable bounds inside controller settings --- .../parseTrackingOptimizationSettingsTree.m | 4 +--- src/core/parse/parseSynergyController.m | 2 ++ src/core/parse/parseTorqueController.m | 8 +++++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/TrackingOptimization/parseTrackingOptimizationSettingsTree.m b/src/TrackingOptimization/parseTrackingOptimizationSettingsTree.m index 86a76538f..d5a492ee3 100644 --- a/src/TrackingOptimization/parseTrackingOptimizationSettingsTree.m +++ b/src/TrackingOptimization/parseTrackingOptimizationSettingsTree.m @@ -28,11 +28,9 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function [inputs, params, resultsDirectory] = ... +function [inputs, params] = ... parseTrackingOptimizationSettingsTree(settingsTree) inputs = parseTreatmentOptimizationInputs(settingsTree); -inputs = parseTreatmentOptimizationDesignVariableBounds(settingsTree, ... - inputs); params = parseTreatmentOptimizationParams(settingsTree); inputs = modifyModelForces(inputs); end diff --git a/src/core/parse/parseSynergyController.m b/src/core/parse/parseSynergyController.m index 0832ff162..15529a052 100644 --- a/src/core/parse/parseSynergyController.m +++ b/src/core/parse/parseSynergyController.m @@ -48,5 +48,7 @@ inputs.optimizeSynergyVectors = getBooleanLogic(... parseElementTextByNameOrAlternate(tree, "optimize_synergy_vectors", 0)); inputs = getModelOrOsimxInputs(inputs); +inputs = parseTreatmentOptimizationDesignVariableBounds(tree, ... + inputs); end diff --git a/src/core/parse/parseTorqueController.m b/src/core/parse/parseTorqueController.m index d8bb2775a..f76babbad 100644 --- a/src/core/parse/parseTorqueController.m +++ b/src/core/parse/parseTorqueController.m @@ -30,8 +30,10 @@ % ----------------------------------------------------------------------- % function inputs = parseTorqueController(tree, inputs) - inputs.controlTorqueNames = parseSpaceSeparatedList(tree, ... - "torque_controller_coordinate_list"); - inputs.numTorqueControls = length(inputs.controlTorqueNames); +inputs.controlTorqueNames = parseSpaceSeparatedList(tree, ... + "torque_controller_coordinate_list"); +inputs.numTorqueControls = length(inputs.controlTorqueNames); +inputs = parseTreatmentOptimizationDesignVariableBounds(tree, ... + inputs); end From 03f34d127776fe5fbe424cc945e5baf2c77464bb Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 27 Aug 2023 18:54:07 -0500 Subject: [PATCH 031/365] fixes small name differences --- .../getTrackingOptimizationValueStruct.m | 6 +-- .../setupCommonOptimalControlBounds.m | 3 +- .../setupCommonOptimalControlInitialGuess.m | 4 +- .../setupCommonOptimalControlSolverSettings.m | 52 +++++++++---------- .../getDesignVariableInputBounds.m | 4 +- .../getTreatmentOptimizationValueStruct.m | 12 ++--- 6 files changed, 41 insertions(+), 40 deletions(-) diff --git a/src/TrackingOptimization/getTrackingOptimizationValueStruct.m b/src/TrackingOptimization/getTrackingOptimizationValueStruct.m index 8b6a857c4..98f83ea97 100644 --- a/src/TrackingOptimization/getTrackingOptimizationValueStruct.m +++ b/src/TrackingOptimization/getTrackingOptimizationValueStruct.m @@ -29,11 +29,11 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function values = getTrackingOptimizationValueStruct(inputs, params) -values = getTreatmentOptimizationValueStruct(inputs, params); +function values = getTrackingOptimizationValueStruct(phase, params) +values = getTreatmentOptimizationValueStruct(phase, params); if strcmp(params.controllerType, 'synergy_driven') if params.optimizeSynergyVectors - synergyWeights = scaleToOriginal(inputs.parameter(1,:), ... + synergyWeights = scaleToOriginal(phase.parameter(1,:), ... params.maxParameter, params.minParameter); values.synergyWeights = getSynergyWeightsFromGroups(... synergyWeights, params); diff --git a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlBounds.m b/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlBounds.m index c24af6d92..41a6374e5 100644 --- a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlBounds.m +++ b/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlBounds.m @@ -50,7 +50,8 @@ bounds.phase.control.upper = 0.5 * ones(1, length(inputs.minControl)); % setup integral bounds bounds.phase.integral.lower = zeros(1, length(inputs.minIntegral)); -bounds.phase.integral.upper = params.solverSettings.integralBound * ones(1, length(inputs.minIntegral)); +bounds.phase.integral.upper = inputs.gpops.integralBound * ... + ones(1, length(inputs.minIntegral)); % setup terminal constraint bounds if ~isempty(inputs.minTerminal) bounds.eventgroup.lower = inputs.minTerminal; diff --git a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlInitialGuess.m b/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlInitialGuess.m index c3fd7153f..3c7d884eb 100644 --- a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlInitialGuess.m +++ b/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlInitialGuess.m @@ -43,7 +43,7 @@ guess.phase.time = scaleToBounds(inputs.experimentalTime, inputs.maxTime, ... inputs.minTime); end -if strcmp(inputs.controllerType, 'synergy_driven') +if strcmp(inputs.controllerType, 'synergy') if isfield(inputs.initialGuess, 'control') guess.phase.control = scaleToBounds(inputs.initialGuess.control, ... inputs.maxControl, inputs.minControl); @@ -56,7 +56,7 @@ guess.parameter = scaleToBounds(inputs.synergyWeightsGuess, ... inputs.maxParameter, inputs.minParameter); end -elseif strcmp(inputs.controllerType, 'torque_driven') +elseif strcmp(inputs.controllerType, 'torque') if isfield(inputs.initialGuess, 'control') guess.phase.control = scaleToBounds(inputs.initialGuess.control, ... inputs.maxControl, inputs.minControl); diff --git a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlSolverSettings.m b/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlSolverSettings.m index 6f2798ed3..f639da709 100644 --- a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlSolverSettings.m +++ b/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlSolverSettings.m @@ -32,38 +32,38 @@ function setup = setupCommonOptimalControlSolverSettings(inputs, ... bounds, guess, params, continuousFunction, endpointFunction) -setup.name = params.solverSettings.optimizationFileName; +setup.name = inputs.gpops.optimizationFileName; setup.functions.continuous = continuousFunction; auxdata.ContinuousFunc = setup.functions.continuous; setup.functions.endpoint = endpointFunction; setup.auxdata = inputs; setup.bounds = bounds; setup.guess = guess; -setup.derivatives.supplier = params.solverSettings.derivativeSupplier; -setup.derivatives.derivativelevel = params.solverSettings.derivativeLevel; -setup.derivatives.dependencies = params.solverSettings.derivativeDependencies; -setup.derivatives.stepsize1 = params.solverSettings.stepSize; -setup.scales.method = params.solverSettings.scaleMethods; -setup.method = params.solverSettings.method; -setup.mesh.method = params.solverSettings.meshMethod; -setup.mesh.tolerance = params.solverSettings.meshTolerance; -setup.mesh.maxiterations = params.solverSettings.meshMaxIterations; -setup.mesh.colpointsmin = params.solverSettings.meshColpointsMin; -setup.mesh.colpointsmax = params.solverSettings.meshColpointsMax; -setup.mesh.splitmult = params.solverSettings.meshSplitMult; -setup.mesh.curveRatio = params.solverSettings.meshCurveRatio; -setup.mesh.R = params.solverSettings.meshR; -setup.mesh.sigma = params.solverSettings.meshSigma; -N = params.solverSettings.numCollocationPoints; -P = params.solverSettings.numIntervals; +setup.derivatives.supplier = inputs.gpops.derivativeSupplier; +setup.derivatives.derivativelevel = inputs.gpops.derivativeLevel; +setup.derivatives.dependencies = inputs.gpops.derivativeDependencies; +setup.derivatives.stepsize1 = inputs.gpops.stepSize; +setup.scales.method = inputs.gpops.scaleMethods; +setup.method = inputs.gpops.method; +setup.mesh.method = inputs.gpops.meshMethod; +setup.mesh.tolerance = inputs.gpops.meshTolerance; +setup.mesh.maxiterations = inputs.gpops.meshMaxIterations; +setup.mesh.colpointsmin = inputs.gpops.meshColpointsMin; +setup.mesh.colpointsmax = inputs.gpops.meshColpointsMax; +setup.mesh.splitmult = inputs.gpops.meshSplitMult; +setup.mesh.curveRatio = inputs.gpops.meshCurveRatio; +setup.mesh.R = inputs.gpops.meshR; +setup.mesh.sigma = inputs.gpops.meshSigma; +N = inputs.gpops.numCollocationPoints; +P = inputs.gpops.numIntervals; setup.mesh.phase.colpoints = P * ones(1, N); setup.mesh.phase.fraction = ones(1, N) / N; -setup.nlp.solver = params.solverSettings.solverType; -setup.nlp.ipoptoptions.linear_solver = params.solverSettings.linearSolverType; -setup.nlp.ipoptoptions.tolerance = params.solverSettings.solverTolerance; -setup.nlp.ipoptoptions.maxiterations = params.solverSettings.maxIterations; -setup.nlp.snoptoptions.linear_solver = params.solverSettings.linearSolverType; -setup.nlp.snoptoptions.tolerance = params.solverSettings.solverTolerance; -setup.nlp.snoptoptions.maxiterations = params.solverSettings.maxIterations; -setup.displaylevel = params.solverSettings.displayLevel; +setup.nlp.solver = inputs.gpops.solverType; +setup.nlp.ipoptoptions.linear_solver = inputs.gpops.linearSolverType; +setup.nlp.ipoptoptions.tolerance = inputs.gpops.solverTolerance; +setup.nlp.ipoptoptions.maxiterations = inputs.gpops.maxIterations; +setup.nlp.snoptoptions.linear_solver = inputs.gpops.linearSolverType; +setup.nlp.snoptoptions.tolerance = inputs.gpops.solverTolerance; +setup.nlp.snoptoptions.maxiterations = inputs.gpops.maxIterations; +setup.displaylevel = inputs.gpops.displayLevel; end \ No newline at end of file diff --git a/src/core/TreatmentOptimization/SetupBounds/getDesignVariableInputBounds.m b/src/core/TreatmentOptimization/SetupBounds/getDesignVariableInputBounds.m index 803001d26..f78a15c26 100644 --- a/src/core/TreatmentOptimization/SetupBounds/getDesignVariableInputBounds.m +++ b/src/core/TreatmentOptimization/SetupBounds/getDesignVariableInputBounds.m @@ -61,7 +61,7 @@ minControlJerks = min(inputs.experimentalJointJerks) - ... inputs.controlJerksMultiple * range(inputs.experimentalJointJerks); -if strcmp(inputs.controllerType, 'synergy_driven') +if strcmp(inputs.controllerType, 'synergy') maxControlSynergyActivations = inputs.maxControlSynergyActivations * ... ones(1, inputs.numSynergies); inputs.maxControl = [maxControlJerks maxControlSynergyActivations]; @@ -72,7 +72,7 @@ ones(1, inputs.numSynergyWeights); inputs.minParameter = zeros(1, inputs.numSynergyWeights); end -elseif strcmp(inputs.controllerType, 'torque_driven') +elseif strcmp(inputs.controllerType, 'torque') for i = 1:length(inputs.controlTorqueNames) indx = find(strcmp(convertCharsToStrings( ... inputs.inverseDynamicMomentLabels), ... diff --git a/src/core/TreatmentOptimization/getTreatmentOptimizationValueStruct.m b/src/core/TreatmentOptimization/getTreatmentOptimizationValueStruct.m index 5d526ba2d..e1e829531 100644 --- a/src/core/TreatmentOptimization/getTreatmentOptimizationValueStruct.m +++ b/src/core/TreatmentOptimization/getTreatmentOptimizationValueStruct.m @@ -30,14 +30,14 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function values = getTreatmentOptimizationValueStruct(inputs, params) +function values = getTreatmentOptimizationValueStruct(phase, params) -values.time = scaleToOriginal(inputs.time, params.maxTime, ... +values.time = scaleToOriginal(phase.time, params.maxTime, ... params.minTime); -state = scaleToOriginal(inputs.state, ones(size(inputs.state, 1), 1) .* ... - params.maxState, ones(size(inputs.state, 1), 1) .* params.minState); -control = scaleToOriginal(inputs.control, ones(size(inputs.control, 1), 1) .* ... - params.maxControl, ones(size(inputs.control, 1), 1) .* params.minControl); +state = scaleToOriginal(phase.state, ones(size(phase.state, 1), 1) .* ... + params.maxState, ones(size(phase.state, 1), 1) .* params.minState); +control = scaleToOriginal(phase.control, ones(size(phase.control, 1), 1) .* ... + params.maxControl, ones(size(phase.control, 1), 1) .* params.minControl); values.statePositions = getCorrectStates(state, 1, params.numCoordinates); values.stateVelocities = getCorrectStates(state, 2, params.numCoordinates); values.stateAccelerations = getCorrectStates(state, 3, params.numCoordinates); From fcc0580880fbbfab63b31a190e3c54c067ae53ca Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Wed, 30 Aug 2023 16:55:04 -0500 Subject: [PATCH 032/365] fixes torque driven to match xml file --- .../parseTrackingOptimizationSettingsTree.m | 2 +- src/core/parse/parseController.m | 46 +++++++++++++++++++ src/core/parse/parseControllerType.m | 15 +++--- src/core/parse/parseSynergyController.m | 10 ++-- src/core/parse/parseTorqueController.m | 5 +- ...reatmentOptimizationDesignVariableBounds.m | 14 ++---- .../parse/parseTreatmentOptimizationInputs.m | 6 +-- 7 files changed, 65 insertions(+), 33 deletions(-) create mode 100644 src/core/parse/parseController.m diff --git a/src/TrackingOptimization/parseTrackingOptimizationSettingsTree.m b/src/TrackingOptimization/parseTrackingOptimizationSettingsTree.m index d5a492ee3..9bc6c2fe1 100644 --- a/src/TrackingOptimization/parseTrackingOptimizationSettingsTree.m +++ b/src/TrackingOptimization/parseTrackingOptimizationSettingsTree.m @@ -14,7 +14,7 @@ % National Institutes of Health (R01 EB030520). % % % % Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega, Claire V. Hammond % +% Author(s): Marleny Vega, Claire V. Hammond % % % % Licensed under the Apache License, Version 2.0 (the "License"); % % you may not use this file except in compliance with the License. % diff --git a/src/core/parse/parseController.m b/src/core/parse/parseController.m new file mode 100644 index 000000000..13a63e1fe --- /dev/null +++ b/src/core/parse/parseController.m @@ -0,0 +1,46 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% There are two controllers that can be used to solve optimal control +% problems in the NMSM Pipeline. This function parses the shared inputs and +% requests the correct subtools to be parsed. +% +% (struct) -> (struct) +% parses shared controller settings from XML tree + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Claire V. Hammond % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function inputs = parseController(tree, inputs) +inputs = parseTreatmentOptimizationDesignVariableBounds(tree, ... + inputs); +inputs.statesCoordinates = parseSpaceSeparatedList(tree, ... + "states_coordinate_list"); + +torqueTree = getFieldByName(tree, "RCNLTorqueController"); +if isstruct(torqueTree) + inputs = parseTorqueController(torqueTree, inputs); +end +synergyTree = getFieldByName(tree, "RCNLSynergyController"); +if isstruct(synergyTree) + inputs = parseSynergyController(synergyTree, inputs); +end +end diff --git a/src/core/parse/parseControllerType.m b/src/core/parse/parseControllerType.m index 976fb9fab..9299847ec 100644 --- a/src/core/parse/parseControllerType.m +++ b/src/core/parse/parseControllerType.m @@ -31,19 +31,16 @@ % ----------------------------------------------------------------------- % function controllerType = parseControllerType(tree) -torque = getFieldByName(tree, "RCNLTorqueController"); synergy = getFieldByName(tree, "RCNLSynergyController"); -if isstruct(torque) && isstruct(synergy) - MException("TreatmentOptimizationSettings:TwoControllers", ... - strcat("Found and ", ... - " - only one controller is allowed.")); +if isstruct(synergy) + controllerType = "synergy"; + return end +torque = getFieldByName(tree, "RCNLTorqueController"); if isstruct(torque) controllerType = "torque"; return end -if isstruct(synergy) - controllerType = "synergy"; - return -end +throw(MException("ParseTreatmentOptimization:NoController", ... + "Could not find or ")) end \ No newline at end of file diff --git a/src/core/parse/parseSynergyController.m b/src/core/parse/parseSynergyController.m index 15529a052..847f0e222 100644 --- a/src/core/parse/parseSynergyController.m +++ b/src/core/parse/parseSynergyController.m @@ -41,14 +41,14 @@ inputs.epsilon = str2double(parseElementTextByNameOrAlternate(tree, ... "epsilon", "1e-4")); inputs.vMaxFactor = str2double(parseElementTextByNameOrAlternate(tree, ... - "v_max_factor", "10")); -surrogateModelCoefficients = load(getTextFromField(getFieldByName(tree, ... - 'surrogate_model_coefficients'))); + "maximum_shortening_velocity_multiplier", "10")); inputs.coefficients = surrogateModelCoefficients.coefficients; inputs.optimizeSynergyVectors = getBooleanLogic(... parseElementTextByNameOrAlternate(tree, "optimize_synergy_vectors", 0)); +inputs.maxControlSynergyActivations = parseDoubleOrAlternate(tree, ... + 'maximum_allowable_synergy_activation', 10); +inputs.maxParameterSynergyWeights = parseDoubleOrAlternate(tree, ... + 'maximum_allowable_synergy_vector_weight', 2); inputs = getModelOrOsimxInputs(inputs); -inputs = parseTreatmentOptimizationDesignVariableBounds(tree, ... - inputs); end diff --git a/src/core/parse/parseTorqueController.m b/src/core/parse/parseTorqueController.m index f76babbad..c606e36f2 100644 --- a/src/core/parse/parseTorqueController.m +++ b/src/core/parse/parseTorqueController.m @@ -32,8 +32,7 @@ function inputs = parseTorqueController(tree, inputs) inputs.controlTorqueNames = parseSpaceSeparatedList(tree, ... "torque_controller_coordinate_list"); +inputs.maxControlTorquesMultiple = parseDoubleOrAlternate(tree, ... + 'torque_controls_max', 1); inputs.numTorqueControls = length(inputs.controlTorqueNames); -inputs = parseTreatmentOptimizationDesignVariableBounds(tree, ... - inputs); end - diff --git a/src/core/parse/parseTreatmentOptimizationDesignVariableBounds.m b/src/core/parse/parseTreatmentOptimizationDesignVariableBounds.m index b90769874..89585c45f 100644 --- a/src/core/parse/parseTreatmentOptimizationDesignVariableBounds.m +++ b/src/core/parse/parseTreatmentOptimizationDesignVariableBounds.m @@ -27,17 +27,11 @@ function inputs = parseTreatmentOptimizationDesignVariableBounds( ... tree, inputs) inputs.jointPositionsMultiple = parseDoubleOrAlternate(tree, ... - 'joint_positions_multiple', 2); + 'joint_position_range_scale_factor', 2); inputs.jointVelocitiesMultiple = parseDoubleOrAlternate(tree, ... - 'joint_velocities_multiple', 1.5); + 'joint_velocity_range_scale_factor', 1.5); inputs.jointAccelerationsMultiple = parseDoubleOrAlternate(tree, ... - 'joint_accelerations_multiple', 1); + 'joint_acceleration_range_scale_factor', 1); inputs.controlJerksMultiple = parseDoubleOrAlternate(tree, ... - 'joint_jerks_multiple', 1); -inputs.maxControlSynergyActivations = parseDoubleOrAlternate(tree, ... - 'synergy_activations_max', 10); -inputs.maxParameterSynergyWeights = parseDoubleOrAlternate(tree, ... - 'synergy_weights_max', 2); -inputs.maxControlTorquesMultiple = parseDoubleOrAlternate(tree, ... - 'torque_controls_max', 1); + 'joint_jerk_range_scale_factor', 1); end diff --git a/src/core/parse/parseTreatmentOptimizationInputs.m b/src/core/parse/parseTreatmentOptimizationInputs.m index 9ba397a52..2011d6d2d 100644 --- a/src/core/parse/parseTreatmentOptimizationInputs.m +++ b/src/core/parse/parseTreatmentOptimizationInputs.m @@ -38,11 +38,7 @@ inputs.model = parseModel(tree); inputs.osimx = parseOsimxFile(getTextFromField(getFieldByName(tree, ... 'input_osimx_file'))); -if strcmp(inputs.controllerType, 'synergy') - inputs = parseSynergyController(tree, inputs); -elseif strcmp(inputs.controllerType, 'torque') - inputs = parseTorqueController(tree, inputs); -end +inputs = parseController(tree, inputs); inputs = parseTreatmentOptimizationDataDirectory(tree, inputs); inputs.initialGuess = parseInitialGuess(tree); inputs = parseOptimalControlSolverSettings( ... From bc84bc1d9fa43f0aa0ab81fc0f7e8b84a2a45509 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Wed, 30 Aug 2023 17:43:55 -0500 Subject: [PATCH 033/365] adds SynergySet as output from NCP osimx --- .../saveNeuralControlPersonalizationResults.m | 7 ++- src/core/osimx/buildOsimxFromOsimxStruct.m | 3 ++ src/core/osimx/buildSynergyGroupOsimx.m | 48 +++++++++++++++++++ ...iteNeuralControlPersonalizationOsimxFile.m | 3 +- 4 files changed, 56 insertions(+), 5 deletions(-) create mode 100644 src/core/osimx/buildSynergyGroupOsimx.m diff --git a/src/NeuralControlPersonalization/saveNeuralControlPersonalizationResults.m b/src/NeuralControlPersonalization/saveNeuralControlPersonalizationResults.m index bd3f33f26..6d6904986 100644 --- a/src/NeuralControlPersonalization/saveNeuralControlPersonalizationResults.m +++ b/src/NeuralControlPersonalization/saveNeuralControlPersonalizationResults.m @@ -61,8 +61,7 @@ function saveNeuralControlPersonalizationResults(synergyWeights, ... ) ... ) end -if isstruct(precalInputs) - writeNeuralControlPersonalizationOsimxFile(inputs, ... - resultsDirectory, precalInputs) -end +writeNeuralControlPersonalizationOsimxFile(inputs, ... + resultsDirectory, precalInputs) + end diff --git a/src/core/osimx/buildOsimxFromOsimxStruct.m b/src/core/osimx/buildOsimxFromOsimxStruct.m index ff1f838c0..b7a421d7c 100644 --- a/src/core/osimx/buildOsimxFromOsimxStruct.m +++ b/src/core/osimx/buildOsimxFromOsimxStruct.m @@ -40,5 +40,8 @@ if isfield(osimxStruct, "groundContact") osimx = buildGcpOsimx(osimx, osimxStruct.groundContact); end +if isfield(osimxStruct, "synergyGroups") + osimx = buildSynergyGroupOsimx(osimx, osimxStruct.synergyGroups); +end end diff --git a/src/core/osimx/buildSynergyGroupOsimx.m b/src/core/osimx/buildSynergyGroupOsimx.m new file mode 100644 index 000000000..94e25da9a --- /dev/null +++ b/src/core/osimx/buildSynergyGroupOsimx.m @@ -0,0 +1,48 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function converts the synergyGroup portion of a parsed .osimx file +% into a new .osimx struct to be printed with writeOsimxFile(). See +% buildOsimxFromOsimxStruct() for reference. +% +% The expected format of the synergyGroups comes from getSynergyGroups() as +% used in parseNeuralControlPersonalization() +% +% (struct, struct) -> (struct) +% Adds synergyGroups to .osimxStruct + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Claire V. Hammond % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function osimx = buildSynergyGroupOsimx(osimx, synergyGroups) +osimx.NMSMPipelineDocument.OsimxModel.RCNLSynergySet.Comment = ... + 'The set of synergies from NCP to find the values in this osimx file'; +for i = 1:length(synergyGroups) + synergyGroup.muscle_group_name.Comment = ['The name of the muscle ' ... + 'group associated with this synergy']; + synergyGroup.muscle_group_name.Text = synergyGroups{i}.muscleGroupName; + synergyGroup.num_synergies.Comment = ['The number of synergies ' ... + 'used by this synergy group']; + synergyGroup.num_synergies.Text = num2str(synergyGroups{i}.numSynergies); + osimx.NMSMPipelineDocument.OsimxModel.RCNLSynergySet.RCNLSynergy{i} = synergyGroup; +end +end + diff --git a/src/core/osimx/writeNeuralControlPersonalizationOsimxFile.m b/src/core/osimx/writeNeuralControlPersonalizationOsimxFile.m index 80fb34f7f..bba519baa 100644 --- a/src/core/osimx/writeNeuralControlPersonalizationOsimxFile.m +++ b/src/core/osimx/writeNeuralControlPersonalizationOsimxFile.m @@ -68,12 +68,13 @@ function writeNeuralControlPersonalizationOsimxFile(inputs, ... osimx.muscles.(inputs.muscleTendonColumnNames(i)) ... .tendonSlackLength = inputs.tendonSlackLength(i); end - if precalInputs.optimizeIsometricMaxForce && ... + if isstruct(precalInputs) && precalInputs.optimizeIsometricMaxForce && ... ~isfield(osimx.muscles.(inputs.muscleTendonColumnNames(i)), "maxIsometricForce") osimx.muscles.(inputs.muscleTendonColumnNames(i)) ... .maxIsometricForce = inputs.maxIsometricForce(i); end end +osimx.synergyGroups = inputs.synergyGroups; writeOsimxFile(buildOsimxFromOsimxStruct(osimx), outfile) end From 81012c79915dabfa20fb1a0b803f166d9d6dd2a8 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Thu, 31 Aug 2023 15:43:23 -0500 Subject: [PATCH 034/365] move torque driven to top --- .../solveOptimalControlProblem.m | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/TreatmentOptimization/solveOptimalControlProblem.m b/src/TreatmentOptimization/solveOptimalControlProblem.m index 9ebac85e7..9b134c6c2 100644 --- a/src/TreatmentOptimization/solveOptimalControlProblem.m +++ b/src/TreatmentOptimization/solveOptimalControlProblem.m @@ -25,31 +25,31 @@ % ----------------------------------------------------------------------- % function output = solveOptimalControlProblem(inputs, params) -if strcmp(inputs.controllerType, "synergy") +if strcmp(inputs.controllerType, "torque") if isfield(inputs, "gpops") - setup = convertToGpopsSynergyDrivenInputs(inputs, params); + setup = convertToGpopsTorqueDrivenInputs(inputs, params); solution = gpops2(setup); - output = convertFromGpopsSynergyDrivenOutputs(solution, ... + output = convertFromGpopsTorqueDrivenOutputs(solution, ... inputs, params); elseif isfield(inputs, "moco") - setup = convertToMocoSynergyDrivenInputs(inputs, params); + setup = convertToMocoTorqueDrivenInputs(inputs, params); solution = moco(setup); - output = convertFromMocoSynergyDrivenOutputs(solution, ... + output = convertFromMocoTorqueDrivenOutputs(solution, ... inputs, params); else MException('solveOptimalControlProblem:invalidSolver', ... 'Invalid solver specified.'); end -elseif strcmp(inputs.controllerType, "torque") +elseif strcmp(inputs.controllerType, "synergy") if isfield(inputs, "gpops") - setup = convertToGpopsTorqueDrivenInputs(inputs, params); + setup = convertToGpopsSynergyDrivenInputs(inputs, params); solution = gpops2(setup); - output = convertFromGpopsTorqueDrivenOutputs(solution, ... + output = convertFromGpopsSynergyDrivenOutputs(solution, ... inputs, params); elseif isfield(inputs, "moco") - setup = convertToMocoTorqueDrivenInputs(inputs, params); + setup = convertToMocoSynergyDrivenInputs(inputs, params); solution = moco(setup); - output = convertFromMocoTorqueDrivenOutputs(solution, ... + output = convertFromMocoSynergyDrivenOutputs(solution, ... inputs, params); else MException('solveOptimalControlProblem:invalidSolver', ... From d719068c89e3fed150ea2dce21f0573b895455e3 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Thu, 31 Aug 2023 16:11:25 -0500 Subject: [PATCH 035/365] mex model shown to be faster --- .../gpops/convertToGpopsTorqueDrivenInputs.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m b/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m index 3d107763b..28585b801 100644 --- a/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m +++ b/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m @@ -27,7 +27,7 @@ function setup = convertToGpopsTorqueDrivenInputs(inputs, params) bounds = setupProblemBounds(inputs, params); guess = setupCommonOptimalControlInitialGuess(inputs); -initializeMexOrMatlabParallelFunctions(inputs.model); +initializeMexOrMatlabParallelFunctions(inputs.mexModel); setup = setupCommonOptimalControlSolverSettings(inputs, ... bounds, guess, params, ... @computeTrackingOptimizationContinuousFunction, ... From df38c42ef6a9ac41842f6ba69c187d19bd4f6c21 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Thu, 31 Aug 2023 16:19:54 -0500 Subject: [PATCH 036/365] update name to gpops from common --- ...mmonOptimalControlSolverSettings.m => setupGpopsSettings.m} | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) rename src/core/TreatmentOptimization/OptimalControlSetupFunctions/{setupCommonOptimalControlSolverSettings.m => setupGpopsSettings.m} (96%) diff --git a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlSolverSettings.m b/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsSettings.m similarity index 96% rename from src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlSolverSettings.m rename to src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsSettings.m index f639da709..4445aaf64 100644 --- a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlSolverSettings.m +++ b/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsSettings.m @@ -30,11 +30,10 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function setup = setupCommonOptimalControlSolverSettings(inputs, ... +function setup = setupGpopsSettings(inputs, ... bounds, guess, params, continuousFunction, endpointFunction) setup.name = inputs.gpops.optimizationFileName; setup.functions.continuous = continuousFunction; -auxdata.ContinuousFunc = setup.functions.continuous; setup.functions.endpoint = endpointFunction; setup.auxdata = inputs; setup.bounds = bounds; From 66c3c84eabfdf0a5dc36dca810c058272bcdcfb9 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Thu, 31 Aug 2023 16:44:53 -0500 Subject: [PATCH 037/365] add combined gpops struct builder --- .../gpops/makeGpopsValuesAsStruct.m | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m diff --git a/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m b/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m new file mode 100644 index 000000000..111c03a30 --- /dev/null +++ b/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m @@ -0,0 +1,117 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function takes the raw values from gpops and turns them into a +% struct that can be interacted with more easily during calculations +% +% (struct, struct) -> (struct) +% + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Claire V. Hammond % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function values = makeGpopsValuesAsStruct(phase, inputs) +values.time = scaleToOriginal(phase.time, inputs.maxTime, ... + inputs.minTime); +state = scaleToOriginal(phase.state, ones(size(phase.state, 1), 1) .* ... + inputs.maxState, ones(size(phase.state, 1), 1) .* inputs.minState); +control = scaleToOriginal(phase.control, ones(size(phase.control, 1), 1) .* ... + inputs.maxControl, ones(size(phase.control, 1), 1) .* inputs.minControl); +values.statePositions = getCorrectStates(state, 1, inputs.numCoordinates); +values.stateVelocities = getCorrectStates(state, 2, inputs.numCoordinates); +values.stateAccelerations = getCorrectStates(state, 3, inputs.numCoordinates); +values.controlJerks = control(:, 1 : inputs.numCoordinates); + +if ~strcmp(inputs.controllerType, 'synergy_driven') + values.controlTorques = control(:, inputs.numCoordinates + 1 : ... + inputs.numCoordinates + inputs.numTorqueControls); +else + values.controlSynergyActivations = control(:, ... + inputs.numCoordinates + 1 : inputs.numCoordinates + inputs.numSynergies); +end + +if strcmp(inputs.toolName, "TreatmentOptimization") + if strcmp(inputs.controllerType, 'synergy_driven') + if inputs.optimizeSynergyVectors + synergyWeights = scaleToOriginal(phase.parameter(1,:), ... + inputs.maxParameter, inputs.minParameter); + values.synergyWeights = getSynergyWeightsFromGroups(... + synergyWeights, inputs); + else + values.synergyWeights = getSynergyWeightsFromGroups(... + inputs.synergyWeightsGuess, inputs); + end + end +end +if strcmp(inputs.toolName, "VerificationOptimization") + if strcmp(inputs.controllerType, 'synergy_driven') + values.synergyWeights = getSynergyWeightsFromGroups(... + inputs.synergyWeightsGuess, inputs); + end +end +if strcmp(inputs.toolName, "DesignOptimization") + numParameters = 0; + if strcmp(params.controllerType, 'synergy_driven') + if params.optimizeSynergyVectors + values.synergyWeights = scaleToOriginal(inputs.parameter(1, ... + 1 : params.numSynergyWeights), ... + params.maxParameter, params.minParameter); + values.synergyWeights = getSynergyWeightsFromGroups(... + values.synergyWeights, params); + numParameters = params.numSynergyWeights; + else + values.synergyWeights = getSynergyWeightsFromGroups(... + params.synergyWeightsGuess, params); + end + if params.splineSynergyActivations.dim > 1 + values.controlSynergyActivations = ... + fnval(params.splineSynergyActivations, values.time)'; + else + values.controlSynergyActivations = ... + fnval(params.splineSynergyActivations, values.time); + end + if params.enableExternalTorqueControl + controls = scaleToOriginal(inputs.control, ones(size( ... + inputs.control, 1), 1) .* params.maxControl, ... + ones(size(inputs.control, 1), 1) .* params.minControl); + values.externalTorqueControls = controls(:, params.numCoordinates + ... + params.numSynergies + 1 : end); + end + else + if isfield(params, "enableExternalTorqueControl") && ... + params.enableExternalTorqueControl + controls = scaleToOriginal(inputs.control, ones(size( ... + inputs.control, 1), 1) .* params.maxControl, ... + ones(size(inputs.control, 1), 1) .* params.minControl); + values.externalTorqueControls = controls(:, params.numCoordinates + ... + params.numTorqueControls + 1 : end); + end + end + if isfield(params, 'userDefinedVariables') + for i = 1:length(params.userDefinedVariables) + values.(params.userDefinedVariables{i}.type)(i) = scaleToOriginal( ... + inputs.parameter(1, i + numParameters), ... + params.userDefinedVariables{i}.upper_bounds, ... + params.userDefinedVariables{i}.lower_bounds); + end + end +end +end From 29a316442ffd59c69dec92667e9092aafaee7724 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Fri, 1 Sep 2023 17:03:59 -0500 Subject: [PATCH 038/365] Calc number of B-spline nodes needed for initial state derivs --- src/core/TreatmentOptimization/getStateDerivatives.m | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/TreatmentOptimization/getStateDerivatives.m b/src/core/TreatmentOptimization/getStateDerivatives.m index 697897800..3580fdb43 100644 --- a/src/core/TreatmentOptimization/getStateDerivatives.m +++ b/src/core/TreatmentOptimization/getStateDerivatives.m @@ -31,11 +31,13 @@ function inputs = getStateDerivatives(inputs) points = length(inputs.experimentalTime); interval = inputs.experimentalTime(2) - inputs.experimentalTime(1); -[N, Np, Npp] = BSplineMatrices(5, 10, points, interval); +numNodes = splFitWithCutoff(inputs.experimentalTime', ... + inputs.experimentalJointAngles', 10, 5); +[N, Np, Npp] = BSplineMatrices(5, numNodes, points, interval); Nodes = N\inputs.experimentalJointAngles; inputs.experimentalJointVelocities = Np * Nodes; inputs.experimentalJointAccelerations = Npp * Nodes; inputs.experimentalJointJerks = calcBSplineDerivative( ... inputs.experimentalTime, inputs.experimentalJointAccelerations, ... - 2, 10); + 5, numNodes); end From cad4fca1d0b2969244e0a133d6e090a1d19e4b06 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Fri, 1 Sep 2023 19:16:09 -0500 Subject: [PATCH 039/365] create new constraint organization --- .../gpops/calcGpopsConstraint.m | 50 ++++ .../generateConstraintTermStruct.m | 272 ++++++++++++++++++ src/core/parse/parseRcnlConstraintTermSet.m | 124 +------- 3 files changed, 337 insertions(+), 109 deletions(-) create mode 100644 src/TreatmentOptimization/gpops/calcGpopsConstraint.m create mode 100644 src/core/TreatmentOptimization/generateConstraintTermStruct.m diff --git a/src/TreatmentOptimization/gpops/calcGpopsConstraint.m b/src/TreatmentOptimization/gpops/calcGpopsConstraint.m new file mode 100644 index 000000000..d0525261d --- /dev/null +++ b/src/TreatmentOptimization/gpops/calcGpopsConstraint.m @@ -0,0 +1,50 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function computes path constraints (if any) for a gpops2 problem +% +% (struct) -> (struct) +% + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Claire V. Hammond % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function path = calcGpopsConstraint(constraintTermCalculations, ... + allowedTypes, values, modeledValues, inputs) +path = []; +for i = 1:length(inputs.path) + constraintTerm = inputs.path{i}; + if constraintTerm.isEnabled + if isfield(constraintTermCalculations, constraintTerm.type) && ... + any(ismember(allowedTypes, constraintTerm.type)) + fn = constraintTermCalculations.(constraintTerm.type); + path = cat(2, path, ... + fn(values, modeledValues, inputs, constraintTerm)); + else + throw(MException('ConstraintTerms:IllegalTerm', ... + strcat("Constraint term ", constraintTerm.type, ... + " is not allowed for this tool"))) + end + end +end +path = scaleToBounds(path, inputs.maxPath, inputs.minPath); +end + diff --git a/src/core/TreatmentOptimization/generateConstraintTermStruct.m b/src/core/TreatmentOptimization/generateConstraintTermStruct.m new file mode 100644 index 000000000..7098f9b31 --- /dev/null +++ b/src/core/TreatmentOptimization/generateConstraintTermStruct.m @@ -0,0 +1,272 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function returns all of the constraint term calculation methods +% Tools use this function for the discrete and continuous constraint +% calculations. +% +% inputs: +% constraintTermType - one of ["path", "terminal"] +% controllerType - one of ["torque", "synergy"] +% toolName - one of ["TrackingOptimization", "TreatmentOptimization", ... +% "DesignOptimization"] +% +% (string, string) -> (struct of function handles, Array of string) +% + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Marleny Vega, Claire V. Hammond % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function [constraintTermCalculations, allowedTypes] = ... + generateConstraintTermStruct(constraintTermType, controllerType, ... + toolName) +allowedTypes = getAllowedTypes(constraintTermType, controllerType, ... + toolName); +constraintTermCalculations = ... + getConstraintTermCalculations(); +end + +function allowedTypes = getAllowedTypes(constraintTermType, ... + controllerType, toolName) +allowedTypes = []; +if strcmp(controllerType, "torque") + switch toolName + case "TrackingOptimization" + if strcmp(constraintTermType, "path") + allowedTypes = [ ... + "root_segment_residual_load", ... + "torque_model_moment_consistency", ... + ]; + end + if strcmp(constraintTermType, "terminal") + allowedTypes = [ ... + "state_position_periodicity", ... + "state_velocity_periodicity", ... + "root_segment_residual_load_periodicity", ... + "external_force_periodicity", ... + "external_moment_periodicity", ... + ]; + end + case "VerificationOptimization" + if strcmp(constraintTermType, "path") + allowedTypes = [ ... + "root_segment_residual_load", ... + "torque_model_moment_consistency", ... + ]; + end + if strcmp(constraintTermType, "terminal") + allowedTypes = [ ... + "state_position_periodicity", ... + "state_velocity_periodicity", ... + "root_segment_residual_load_periodicity", ... + "external_force_periodicity", ... + "external_moment_periodicity", ... + ]; + end + case "DesignOptimization" + if strcmp(constraintTermType, "path") + allowedTypes = [ ... + "root_segment_residual_load", ... + "torque_model_moment_consistency", ... + "limit_normalized_fiber_length", ... + ]; + end + if strcmp(constraintTermType, "terminal") + allowedTypes = [ ... + "state_position_periodicity", ... + "state_velocity_periodicity", ... + "root_segment_residual_load_periodicity", ... + "external_force_periodicity", ... + "external_moment_periodicity", ... + "final_state_position", ... + "final_state_velocity", ... + "final_point_position", ... + "final_point_velocity", ... + ]; + end + end +end +if strcmp(controllerType, "synergy") + switch toolName + case "TrackingOptimization" + if strcmp(constraintTermType, "path") + allowedTypes = [ ... + "root_segment_residual_load", ... + "muscle_model_moment_consistency", ... + ]; + end + if strcmp(constraintTermType, "terminal") + allowedTypes = [ ... + "state_position_periodicity", ... + "state_velocity_periodicity", ... + "root_segment_residual_load_periodicity", ... + "external_force_periodicity", ... + "external_moment_periodicity", ... + "synergy_weight_sum", ... + ]; + end + case "VerificationOptimization" + if strcmp(constraintTermType, "path") + allowedTypes = [ ... + "root_segment_residual_load", ... + "muscle_model_moment_consistency", ... + ]; + end + if strcmp(constraintTermType, "terminal") + allowedTypes = [ ... + "state_position_periodicity", ... + "state_velocity_periodicity", ... + "root_segment_residual_load_periodicity", ... + "external_force_periodicity", ... + "external_moment_periodicity", ... + ]; + end + case "DesignOptimization" + if strcmp(constraintTermType, "path") + allowedTypes = [ ... + "root_segment_residual_load", ... + "muscle_model_moment_consistency", ... + "limit_muscle_activation", ... + "limit_normalized_fiber_length", ... + "external_control_muscle_moment_consistency", ... + ]; + end + if strcmp(constraintTermType, "terminal") + allowedTypes = [ ... + "state_position_periodicity", ... + "state_velocity_periodicity", ... + "root_segment_residual_load_periodicity", ... + "external_force_periodicity", ... + "external_moment_periodicity", ... + "synergy_weight_sum", ... + "final_state_position", ... + "final_state_velocity", ... + "final_point_position", ... + "final_point_velocity", ... + ]; + end + end +end +end + +function constraintTermCalculations = getConstraintTermCalculations() + +constraintTermCalculations.root_segment_residual_load = @(values, modeledValues, auxdata, constraintTerm) ... + calcRootSegmentResidualsPathConstraints( ... + constraintTerm.load, ... + auxdata.inverseDynamicMomentLabels, ... + modeledValues.inverseDynamicMoments ... + ); + +constraintTermCalculations.muscle_model_moment_consistency = @(values, modeledValues, auxdata, constraintTerm) ... + calcMuscleActuatedMomentsPathConstraints( ... + auxdata, ... + modeledValues, ... + constraintTerm.load ... + ); + +constraintTermCalculations.torque_model_moment_consistency = @(values, modeledValues, auxdata, constraintTerm) ... + calcTorqueActuatedMomentsPathConstraints( ... + auxdata, ... + modeledValues, ... + values.controlTorques, ... + constraintTerm.load ... + ); + +constraintTermCalculations.limit_muscle_activation = @(values, modeledValues, auxdata, constraintTerm) ... + calcMuscleActivationsPathConstraint( ... + auxdata, ... + modeledValues, ... + constraintTerm.muscle); + +constraintTermCalculations.limit_normalized_fiber_length = @(values, modeledValues, auxdata, constraintTerm) ... + calcNormalizedFiberLengthPathConstraint( ... + auxdata, ... + modeledValues, ... + constraintTerm.muscle ... + ); + +constraintTermCalculations.external_control_muscle_moment_consistency = @(values, modeledValues, auxdata, constraintTerm) ... + calcMuscleActuatedMomentsWithExternalAidPathConstraints( ... + auxdata, ... + modeledValues, ... + values.externalTorqueControls, ... + contraintTerm.coordinate ... + ); + +constraintTermCalculations.state_position_periodicity = @(values, modeledValues, auxdata, constraintTerm) ... + calcStatePositionPeriodicity( ... + values.statePositions, ... + auxdata.coordinateNames, ... + constraintTerm.coordinate ... + ); + +constraintTermCalculations.state_velocity_periodicity = @(values, modeledValues, auxdata, constraintTerm) ... + calcStateVelocityPeriodicity( ... + values.stateVelocities, ... + auxdata.coordinateNames, ... + constraintTerm.coordinate ... + ); + +constraintTermCalculations.root_segment_residual_load_periodicity = @(values, modeledValues, auxdata, constraintTerm) ... + calcRootSegmentResidualsPeriodicity(... + modeledValues.inverseDynamicMoments, ... + auxdata.inverseDynamicMomentLabels, ... + constraintTerm.load); + +constraintTermCalculations.external_force_periodicity = @(values, modeledValues, auxdata, constraintTerm) ... + calcExternalForcesPeriodicity(... + modeledValues.groundReactionsLab.forces, ... + auxdata.contactSurfaces, ... + constraintTerm.force); + +constraintTermCalculations.external_moment_periodicity = @(values, modeledValues, auxdata, constraintTerm) ... + calcExternalMomentsPeriodicity(... + modeledValues.groundReactionsLab.moments, ... + auxdata.contactSurfaces, ... + constraintTerm.moment); + +constraintTermCalculations.final_state_position = @(values, modeledValues, auxdata, constraintTerm) ... + calcFinalStatePosition( ... + values.statePositions, ... + auxdata.coordinateNames, ... + constraintTerm); + +constraintTermCalculations.final_state_velocity = @(values, modeledValues, auxdata, constraintTerm) ... + calcFinalStateVelocity(values.stateVelocities, ... + auxdata.coordinateNames, ... + constraintTerm); + +constraintTermCalculations.final_point_position = @(values, modeledValues, auxdata, constraintTerm) ... + calcFinalPointPosition(auxdata, values, ... + constraintTerm); + +constraintTermCalculations.final_point_velocity = @(values, modeledValues, auxdata, constraintTerm) ... + calcFinalPointVelocity(auxdata, values, ... + constraintTerm); + +constraintTermCalculations.synergy_weight_sum = @(values, modeledValues, auxdata, constraintTerm) ... + calcSynergyWeightsSum(... + values.synergyWeights, ... + params.synergyGroups, ... + constraintTerm.synergy_group); + +end \ No newline at end of file diff --git a/src/core/parse/parseRcnlConstraintTermSet.m b/src/core/parse/parseRcnlConstraintTermSet.m index f1c94749e..55870b915 100644 --- a/src/core/parse/parseRcnlConstraintTermSet.m +++ b/src/core/parse/parseRcnlConstraintTermSet.m @@ -28,7 +28,7 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function [path, terminal] = parseRcnlConstraintTermSet(tree, tool, ... +function [path, terminal] = parseRcnlConstraintTermSet(tree, toolName, ... controllerType) path = {}; terminal = {}; @@ -41,11 +41,11 @@ % Find general cost term elements tempTerm.type = getTextFromField(getFieldByNameOrError( ... currentTerm, 'type')); - [isValid, isPath] = isTypeValid(tempTerm.type, tool, controllerType); + [isValid, isPath] = isTypeValid(tempTerm.type, controllerType, toolName); if ~isValid throw(MException("ConstraintTermSet:InvalidType", ... strcat(tempTerm.type, " is not a valid constraint", ... - " term for tool ", tool))); + " term for tool ", toolName))); end enabled = getTextFromField(getFieldByNameOrAlternate( ... currentTerm, 'is_enabled', 'false')); @@ -79,111 +79,17 @@ end end -function [isValid, isPath] = isTypeValid(type, tool, controllerType) -if strcmp(controllerType, "torque") - switch tool - case "TrackingOptimization" - pathTypes = [ ... - "root_segment_residual_load", ... - "torque_model_moment_consistency", ... - ]; - terminalTypes = [ ... - "state_position_periodicity", ... - "state_velocity_periodicity", ... - "root_segment_residual_load_periodicity", ... - "external_force_periodicity", ... - "external_moment_periodicity", ... - ]; - case "VerificationOptimization" - pathTypes = [ ... - "root_segment_residual_load", ... - "torque_model_moment_consistency", ... - ]; - terminalTypes = [ ... - "state_position_periodicity", ... - "state_velocity_periodicity", ... - "root_segment_residual_load_periodicity", ... - "external_force_periodicity", ... - "external_moment_periodicity", ... - ]; - case "DesignOptimization" - pathTypes = [ ... - "root_segment_residual_load", ... - "torque_model_moment_consistency", ... - "limit_normalized_fiber_length", ... - ]; - terminalTypes = [ ... - "state_position_periodicity", ... - "state_velocity_periodicity", ... - "root_segment_residual_load_periodicity", ... - "external_force_periodicity", ... - "external_moment_periodicity", ... - "final_state_position", ... - "final_state_velocity", ... - "final_point_position", ... - "final_point_velocity", ... - ]; - otherwise - throw(MException("TreatmentOptimization:InvalidTool", ... - "Tool term is not valid.")) - end - isValid = any(strcmp(type, pathTypes)); - isPath = isValid; - isValid = isValid || any(strcmp(type, terminalTypes)); - return -elseif strcmp(controllerType, "synergy") - switch tool - case "TrackingOptimization" - pathTypes = [ ... - "root_segment_residual_load", ... - "muscle_model_moment_consistency", ... - ]; - terminalTypes = [ ... - "state_position_periodicity", ... - "state_velocity_periodicity", ... - "root_segment_residual_load_periodicity", ... - "external_force_periodicity", ... - "external_moment_periodicity", ... - "synergy_weight_sum", ... - ]; - case "VerificationOptimization" - pathTypes = [ ... - "root_segment_residual_load", ... - "muscle_model_moment_consistency", ... - ]; - terminalTypes = [ ... - "state_position_periodicity", ... - "state_velocity_periodicity", ... - "root_segment_residual_load_periodicity", ... - "external_force_periodicity", ... - "external_moment_periodicity", ... - ]; - case "DesignOptimization" - pathTypes = [ ... - "root_segment_residual_load", ... - "muscle_model_moment_consistency", ... - "limit_muscle_activation", ... - "limit_normalized_fiber_length", ... - "external_control_muscle_moment_consistency", ... - ]; - terminalTypes = [ ... - "state_position_periodicity", ... - "state_velocity_periodicity", ... - "root_segment_residual_load_periodicity", ... - "external_force_periodicity", ... - "external_moment_periodicity", ... - "synergy_weight_sum", ... - "final_state_position", ... - "final_state_velocity", ... - "final_point_position", ... - "final_point_velocity", ... - ]; - otherwise - throw(MException("TreatmentOptimization:InvalidTool", ... - "Tool term is not valid.")) - end - isValid = any(strcmp(type, pathTypes)); - isPath = isValid; - isValid = isValid || any(strcmp(type, terminalTypes)); +function [isValid, isPath] = isTypeValid(type, toolName, controllerType) +[~, allowedTypes] = ... + generateConstraintTermStruct("path", controllerType, ... + toolName); +isPath = true; +isValid = any(strcmp(type, allowedTypes)); +if ~isValid + [~, allowedTypes] = ... + generateConstraintTermStruct("terminal", controllerType, ... + toolName); + isPath = false; + isValid = any(strcmp(type, allowedTypes)); end end From 8e9dedc0ec81cc6623f1f51c400e1d5e68f34e1f Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Fri, 1 Sep 2023 19:16:32 -0500 Subject: [PATCH 040/365] move to generic gpops impl --- .../gpops/calcGpopsIntegrand.m | 38 +++++++++++++++ .../gpops/computeGpopsContinuousFunction.m | 46 +++++++++++++++++++ .../gpops/convertToGpopsTorqueDrivenInputs.m | 6 +-- 3 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 src/TreatmentOptimization/gpops/calcGpopsIntegrand.m create mode 100644 src/TreatmentOptimization/gpops/computeGpopsContinuousFunction.m diff --git a/src/TreatmentOptimization/gpops/calcGpopsIntegrand.m b/src/TreatmentOptimization/gpops/calcGpopsIntegrand.m new file mode 100644 index 000000000..c990e447f --- /dev/null +++ b/src/TreatmentOptimization/gpops/calcGpopsIntegrand.m @@ -0,0 +1,38 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function calculates the integrand for gpops. +% +% (struct, struct, struct) -> (2D matrix) +% Returns scaled integrand + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Marleny Vega % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function integrand = calcGpopsIntegrand(values, modeledValues, auxdata) +[costTermCalculations, allowedTypes] = ... + generateCostTermStruct("continuous", auxdata.toolName); +integrand = calcTreatmentOptimizationCost( ... + costTermCalculations, allowedTypes, values, modeledValues, auxdata); +integrand = integrand ./ (auxdata.maxIntegral - auxdata.minIntegral); +integrand = integrand .^ 2; +end + diff --git a/src/TreatmentOptimization/gpops/computeGpopsContinuousFunction.m b/src/TreatmentOptimization/gpops/computeGpopsContinuousFunction.m new file mode 100644 index 000000000..f3f65eaf1 --- /dev/null +++ b/src/TreatmentOptimization/gpops/computeGpopsContinuousFunction.m @@ -0,0 +1,46 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function computes the dynamic constraints, path constraints (if any) +% and cost function terms (if any) for gpops2. +% +% (struct) -> (struct) +% + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Claire V. Hammond % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function modeledValues = computeGpopsContinuousFunction(setup) +values = makeGpopsValuesAsStruct(setup.phase, setup.auxdata); +modeledValues = calcTorqueBasedModeledValues(values, setup.auxdata); +modeledValues = calcSynergyBasedModeledValues(values, setup.auxdata, ... + modeledValues); +modeledValues.dynamics = calcDynamicConstraint(values, setup.auxdata); +if ~isempty(setup.auxdata.path) + [constraintTermCalculations, allowedTypes] = ... + generateConstraintTermStruct("path", ... + setup.auxdata.controllerType, setup.auxdata.toolName); + modeledValues.path = calcGpopsConstraint( ... + constraintTermCalculations, allowedTypes, values, ... + modeledValues, setup.auxdata); +end +modeledValues.integrand = calcGpopsIntegrand(values, modeledValues, setup.auxdata); +end diff --git a/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m b/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m index 28585b801..c2cfef7ea 100644 --- a/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m +++ b/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m @@ -28,12 +28,12 @@ bounds = setupProblemBounds(inputs, params); guess = setupCommonOptimalControlInitialGuess(inputs); initializeMexOrMatlabParallelFunctions(inputs.mexModel); -setup = setupCommonOptimalControlSolverSettings(inputs, ... +setup = setupGpopsSettings(inputs, ... bounds, guess, params, ... - @computeTrackingOptimizationContinuousFunction, ... + @computeGpopsContinuousFunction, ... @computeTrackingOptimizationEndpointFunction); checkInitialGuess(guess, inputs, ... - @computeTrackingOptimizationContinuousFunction); + @computeGpopsContinuousFunction); end function bounds = setupProblemBounds(inputs, params) From 0378f45196a5e647d5c25230b9045aff852df40d Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Fri, 1 Sep 2023 19:16:47 -0500 Subject: [PATCH 041/365] fix argument order --- .../PathTerms/calcRootSegmentResidualsPathConstraints.m | 6 ++---- src/core/parse/parseTreatmentOptimizationInputs.m | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/core/TreatmentOptimization/PathTerms/calcRootSegmentResidualsPathConstraints.m b/src/core/TreatmentOptimization/PathTerms/calcRootSegmentResidualsPathConstraints.m index 084180cfe..0f1b26f24 100644 --- a/src/core/TreatmentOptimization/PathTerms/calcRootSegmentResidualsPathConstraints.m +++ b/src/core/TreatmentOptimization/PathTerms/calcRootSegmentResidualsPathConstraints.m @@ -30,8 +30,6 @@ function pathTerm = calcRootSegmentResidualsPathConstraints(loadName, ... inverseDynamicMomentLabels, inverseDynamicMoments) - -indx = find(strcmp(convertCharsToStrings(inverseDynamicMomentLabels), ... - loadName)); -pathTerm = inverseDynamicMoments(:, indx); +pathTerm = inverseDynamicMoments(:, ... + find(strcmp(inverseDynamicMomentLabels, loadName))); end \ No newline at end of file diff --git a/src/core/parse/parseTreatmentOptimizationInputs.m b/src/core/parse/parseTreatmentOptimizationInputs.m index 2011d6d2d..827905481 100644 --- a/src/core/parse/parseTreatmentOptimizationInputs.m +++ b/src/core/parse/parseTreatmentOptimizationInputs.m @@ -48,6 +48,6 @@ getFieldByNameOrError(tree, 'RCNLCostTermSet').RCNLCostTerm); [inputs.path, inputs.terminal] = parseRcnlConstraintTermSet( ... getFieldByNameOrError(tree, 'RCNLConstraintTermSet') ... - .RCNLConstraintTerm, inputs.toolName, inputs.controllerType); + .RCNLConstraintTerm, inputs.controllerType, inputs.toolName); inputs.contactSurfaces = parseGroundContactSurfaces(inputs); end From f9ffb1ef69988192cd8bdab4d79bc605e5230602 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Fri, 1 Sep 2023 19:38:28 -0500 Subject: [PATCH 042/365] add share gpops endpoint fn --- .../gpops/computeGpopsEndpointFunction.m | 66 +++++++++++++++++++ .../gpops/convertToGpopsTorqueDrivenInputs.m | 2 +- 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m diff --git a/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m b/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m new file mode 100644 index 000000000..5a2a771f5 --- /dev/null +++ b/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m @@ -0,0 +1,66 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function computes the terminal constraint (if any), and total cost +% function objective for gpops treatment optimization. +% +% (struct) -> (struct) +% + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Claire V. Hammond % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function output = computeGpopsEndpointFunction(setup) +if ~isempty(setup.auxdata.terminal) || strcmp(setup.auxdata.toolName, "DesignOptimization") + setup.phase.state = [setup.phase.initialstate; setup.phase.finalstate]; + setup.phase.time = [setup.phase.initialtime; setup.phase.finaltime]; + setup.phase.control = ones(size(setup.phase.time,1),length(params.minControl)); + if isfield(inputs, "parameter") + setup.phase.parameter = setup.parameter; + end + values = makeGpopsValuesAsStruct(setup.phase, setup.auxdata); + modeledValues = calcTorqueBasedModeledValues(values, params); + [constraintTermCalculations, allowedTypes] = ... + generateConstraintTermStruct("terminal", ... + setup.auxdata.controllerType, setup.auxdata.toolName); + output.eventgroup.event = ... + calcGpopsConstraint(constraintTermCalculations, allowedTypes, ... + values, modeledValues, setup.auxdata); +end + +if strcmp(setup.auxdata.toolName, "DesignOptimization") + [costTermCalculations, allowedTypes] = ... + generateCostTermStruct("discrete", "DesignOptimization"); + discrete = calcTreatmentOptimizationCost( ... + costTermCalculations, allowedTypes, values, modeledValues, auxdata); + discreteObjective = sum(discrete) / length(discrete); + if isnan(discreteObjective); discreteObjective = 0; end +else + discreteObjective = 0; +end + +continuousObjective = sum(setup.phase.integral) / length(setup.phase.integral); +if isfield(setup.auxdata, "finalTimeRange") + continuousObjective = continuousObjective / values.time(end); +end + +output.objective = continuousObjective + discreteObjective; +end \ No newline at end of file diff --git a/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m b/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m index c2cfef7ea..f9c6170b8 100644 --- a/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m +++ b/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m @@ -31,7 +31,7 @@ setup = setupGpopsSettings(inputs, ... bounds, guess, params, ... @computeGpopsContinuousFunction, ... - @computeTrackingOptimizationEndpointFunction); + @computeGpopsEndpointFunction); checkInitialGuess(guess, inputs, ... @computeGpopsContinuousFunction); end From c49507d163e689d7f019c226b2f2ccd6ffe98597 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Fri, 1 Sep 2023 19:44:14 -0500 Subject: [PATCH 043/365] update to torque_controls_range_scale_factor --- src/core/parse/parseTorqueController.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/parse/parseTorqueController.m b/src/core/parse/parseTorqueController.m index c606e36f2..c1ed1aa65 100644 --- a/src/core/parse/parseTorqueController.m +++ b/src/core/parse/parseTorqueController.m @@ -33,6 +33,6 @@ inputs.controlTorqueNames = parseSpaceSeparatedList(tree, ... "torque_controller_coordinate_list"); inputs.maxControlTorquesMultiple = parseDoubleOrAlternate(tree, ... - 'torque_controls_max', 1); + 'torque_controls_range_scale_factor', 1); inputs.numTorqueControls = length(inputs.controlTorqueNames); end From c4d72071d8f77aa93c073c6c955adbc04b71d7e8 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Fri, 1 Sep 2023 19:59:54 -0500 Subject: [PATCH 044/365] fix verification optimization torque driven --- .../gpops/convertFromGpopsTorqueDrivenOutputs.m | 2 +- .../VerificationOptimizationTool.m | 3 ++- .../parseVerificationOptimizationSettingsTree.m | 8 +++----- .../IntegrandTerms/calcTrackingControllerIntegrand.m | 4 ++-- .../TreatmentOptimization/calcTreatmentOptimizationCost.m | 6 +++--- 5 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/TreatmentOptimization/gpops/convertFromGpopsTorqueDrivenOutputs.m b/src/TreatmentOptimization/gpops/convertFromGpopsTorqueDrivenOutputs.m index 5dacb3084..b154eff90 100644 --- a/src/TreatmentOptimization/gpops/convertFromGpopsTorqueDrivenOutputs.m +++ b/src/TreatmentOptimization/gpops/convertFromGpopsTorqueDrivenOutputs.m @@ -31,7 +31,7 @@ if isfield(inputs, "optimizeSynergyVectors") solution.phase.parameter = solution.parameter; end -output = computeTrackingOptimizationContinuousFunction(solution); +output = computeGpopsContinuousFunction(solution); output.solution = solution; end diff --git a/src/VerificationOptimization/VerificationOptimizationTool.m b/src/VerificationOptimization/VerificationOptimizationTool.m index 55fe31f62..113ed4019 100644 --- a/src/VerificationOptimization/VerificationOptimizationTool.m +++ b/src/VerificationOptimization/VerificationOptimizationTool.m @@ -33,7 +33,8 @@ function VerificationOptimizationTool(settingsFileName) settingsTree = xml2struct(settingsFileName); verifyVersion(settingsTree, "VerificationOptimizationTool"); [inputs, params] = parseVerificationOptimizationSettingsTree(settingsTree); -[outputs, inputs] = VerificationOptimization(inputs, params); +inputs = makeTreatmentOptimizationInputs(inputs, params); +outputs = solveOptimalControlProblem(inputs, params); reportTreatmentOptimizationResults(outputs, inputs); saveVerificationOptimizationResults(outputs, inputs); end diff --git a/src/VerificationOptimization/parseVerificationOptimizationSettingsTree.m b/src/VerificationOptimization/parseVerificationOptimizationSettingsTree.m index 297b9f0be..63af7b9e9 100644 --- a/src/VerificationOptimization/parseVerificationOptimizationSettingsTree.m +++ b/src/VerificationOptimization/parseVerificationOptimizationSettingsTree.m @@ -14,7 +14,7 @@ % National Institutes of Health (R01 EB030520). % % % % Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % +% Author(s): Marleny Vega, Claire V. Hammond % % % % Licensed under the Apache License, Version 2.0 (the "License"); % % you may not use this file except in compliance with the License. % @@ -31,14 +31,12 @@ function [inputs, params] = ... parseVerificationOptimizationSettingsTree(settingsTree) inputs = parseTreatmentOptimizationInputs(settingsTree); -inputs = parseTreatmentOptimizationDesignVariableBounds(settingsTree, ... - inputs); -inputs = getInputs(settingsTree); +inputs = getInputs(settingsTree, inputs); params = parseTreatmentOptimizationParams(settingsTree); inputs = modifyModelForces(inputs); end -function inputs = getInputs(tree) +function inputs = getInputs(tree, inputs) import org.opensim.modeling.Storage if strcmpi(inputs.controllerType, 'synergy_driven') inputs.synergyWeights = parseTreatmentOptimizationStandard(... diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m b/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m index 63528f5b3..c9c25de79 100644 --- a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m +++ b/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m @@ -34,14 +34,14 @@ controllerName) switch auxdata.controllerType - case 'synergy_driven' + case 'synergy' indx = find(strcmp(convertCharsToStrings( ... auxdata.synergyLabels), controllerName)); synergyActivations = ... fnval(auxdata.splineSynergyActivations, time/time(end))'; cost = calcTrackingCostArrayTerm(synergyActivations, ... values.controlSynergyActivations, indx); - case 'torque_driven' + case 'torque' indx1 = find(strcmp(convertCharsToStrings( ... auxdata.inverseDynamicMomentLabels), controllerName)); indx2 = find(strcmp(convertCharsToStrings( ... diff --git a/src/core/TreatmentOptimization/calcTreatmentOptimizationCost.m b/src/core/TreatmentOptimization/calcTreatmentOptimizationCost.m index 6463a0b56..1d7789a51 100644 --- a/src/core/TreatmentOptimization/calcTreatmentOptimizationCost.m +++ b/src/core/TreatmentOptimization/calcTreatmentOptimizationCost.m @@ -40,9 +40,9 @@ cost = cat(2, ... cost, ... fn(values, modeledValues, auxdata, costTerm)); -% else -% throw(MException('', ['Cost term type ' costTerm.type ... -% ' does not exist for this tool.'])) + else + throw(MException('', ['Cost term type ' costTerm.type ... + ' does not exist for this tool.'])) end end end From 5f2ceac460dde64ce738f17af4d2cc46ab21ce34 Mon Sep 17 00:00:00 2001 From: RobSalati Date: Fri, 1 Sep 2023 20:40:41 -0500 Subject: [PATCH 045/365] Added functions to save passive force data, and muscle excitations/activations Stripped reportMuscleTendonPersonalizationResults of all plotting and used it as a skeleton to save data in place of plotting. --- .../MuscleTendonPersonalizationTool.m | 2 + ...reportMuscleTendonPersonalizationResults.m | 18 +- .../saveMuscleTendonOptimizationResults.m | 196 ++++++++++++++++++ 3 files changed, 208 insertions(+), 8 deletions(-) create mode 100644 src/MuscleTendonPersonalization/saveMuscleTendonOptimizationResults.m diff --git a/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m b/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m index 9e843563d..87396a0d7 100644 --- a/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m +++ b/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m @@ -35,6 +35,7 @@ function MuscleTendonPersonalizationTool(settingsFileName) [inputs, params, resultsDirectory] = ... parseMuscleTendonPersonalizationSettingsTree(settingsTree); precalInputs = parseMuscleTendonLengthInitializationSettingsTree(settingsTree); + if isstruct(precalInputs) optimizedInitialGuess = MuscleTendonLengthInitialization(precalInputs); inputs = updateMtpInitialGuess(inputs, precalInputs, ... @@ -50,6 +51,7 @@ function MuscleTendonPersonalizationTool(settingsFileName) else reportMuscleTendonPersonalizationResults(optimizedParams, inputs); end + finalValues = makeMtpValuesAsStruct([], optimizedParams, zeros(1, 7)); if precalInputs.optimizeIsometricMaxForce finalValues.maxIsometricForce = inputs.maxIsometricForce; diff --git a/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResults.m b/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResults.m index 48926ecab..738751875 100644 --- a/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResults.m +++ b/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResults.m @@ -57,6 +57,7 @@ function reportMuscleTendonPersonalizationResults(optimizedParams, ... getValuesToReport(mtpInputs, precalInputs, optimizedParams) finalValues = makeMtpValuesAsStruct([], optimizedParams, zeros(1, 7)); +save('finalvalues.mat', 'finalValues') resultsSynx = calcMtpSynXModeledValues(finalValues, mtpInputs, struct()); finalValues.synergyWeights(mtpInputs.numberOfExtrapolationWeights + 1 : end) = 0; resultsSynxNoResiduals = calcMtpSynXModeledValues(finalValues, mtpInputs, struct()); @@ -68,20 +69,21 @@ function reportMuscleTendonPersonalizationResults(optimizedParams, ... resultsSynx.muscleExcitations = resultsSynx.muscleExcitations(:, :, ... mtpInputs.numPaddingFrames + 1 : end - mtpInputs.numPaddingFrames); if ~isempty(precalInputs) -finalOptimalFiberLength = ... - finalValues.optimalFiberLengthScaleFactors .* mtpInputs.optimalFiberLength; -finalValues.optimalFiberLengthScaleFactors = ... - finalOptimalFiberLength ./ precalInputs.optimalFiberLength; -finalTendonSlackLength = ... - finalValues.tendonSlackLengthScaleFactors .* mtpInputs.tendonSlackLength; -finalValues.tendonSlackLengthScaleFactors = ... - finalTendonSlackLength ./ precalInputs.tendonSlackLength; +% finalOptimalFiberLength = ... +% finalValues.optimalFiberLengthScaleFactors .* mtpInputs.optimalFiberLength; +% finalValues.optimalFiberLengthScaleFactors = ... +% finalOptimalFiberLength ./ precalInputs.optimalFiberLength; +% finalTendonSlackLength = ... +% finalValues.tendonSlackLengthScaleFactors .* mtpInputs.tendonSlackLength; +% finalValues.tendonSlackLengthScaleFactors = ... +% finalTendonSlackLength ./ precalInputs.tendonSlackLength; end end function plotMuscleTendonLengthInitializationResults(precalInputs, mtpInputs) tempValues.optimalFiberLengthScaleFactors = ... mtpInputs.optimalFiberLength ./ precalInputs.optimalFiberLength; +mtpInputs.optimalFiberLength tempValues.tendonSlackLengthScaleFactors = ... mtpInputs.tendonSlackLength ./ precalInputs.tendonSlackLength; precalInputs.maxIsometricForce = mtpInputs.maxIsometricForce; diff --git a/src/MuscleTendonPersonalization/saveMuscleTendonOptimizationResults.m b/src/MuscleTendonPersonalization/saveMuscleTendonOptimizationResults.m new file mode 100644 index 000000000..2909a6916 --- /dev/null +++ b/src/MuscleTendonPersonalization/saveMuscleTendonOptimizationResults.m @@ -0,0 +1,196 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function saves optimization results to .sto files to be plotted by +% the user in either MATLAB or Opensim +% +% (struct, struct, struct, string) -> (None) +% Saves data from optimization to 6 .sto files + +function saveMuscleTendonOptimizationResults(optimizedParams, ... + mtpInputs, precalInputs, resultsDirectory) + if nargin < 3; precalInputs = []; end + [finalValues, results, resultsSynx, resultsSynxNoResiduals] = ... + getValuesToReport(mtpInputs, precalInputs, optimizedParams) + if ~isempty(precalInputs) + saveMuscleTendonLengthInitializationResults(precalInputs, mtpInputs, ... + resultsDirectory) % Change to just save the data + end + + printJointMomentMatchingError(resultsSynx.muscleJointMoments, ... + mtpInputs.inverseDynamicsMoments); % Keep this + + saveExcitationAndActivationData(results, resultsSynx, mtpInputs, ... + mtpInputs.synergyExtrapolation, resultsDirectory); % Change to just save the data + + % makeModelParameterPlots(finalValues, mtpInputs, ... + % mtpInputs.synergyExtrapolation) % Change to just save the data + + % makeTaskSpecificMomentMatchingPlots(... + % permute(resultsSynxNoResiduals.muscleJointMoments, [3 1 2]), ... + % permute(resultsSynx.muscleJointMoments, [3 1 2]), ... + % permute(mtpInputs.inverseDynamicsMoments, [3 1 2]), ... + % mtpInputs.coordinateNames, mtpInputs.synergyExtrapolation) % Change to just save the data + + % makeTaskSpecificNormalizedFiberLengthsPlots( ... + % permute(resultsSynx.normalizedFiberLength, [3 1 2]), ... + % mtpInputs, mtpInputs.synergyExtrapolation) % Change to just save the data +end + +function [finalValues, results, resultsSynx, resultsSynxNoResiduals] = ... + getValuesToReport(mtpInputs, precalInputs, optimizedParams) % Need to downscale excitation arrays + + finalValues = makeMtpValuesAsStruct([], optimizedParams, zeros(1, 7)); + save('finalvalues.mat', 'finalValues') + resultsSynx = calcMtpSynXModeledValues(finalValues, mtpInputs, struct()); + finalValues.synergyWeights(mtpInputs.numberOfExtrapolationWeights + 1 : end) = 0; + resultsSynxNoResiduals = calcMtpSynXModeledValues(finalValues, mtpInputs, struct()); + results = calcMtpModeledValues(finalValues, mtpInputs, struct()); + results.time = mtpInputs.emgTime(:, mtpInputs.numPaddingFrames + 1 : ... + end - mtpInputs.numPaddingFrames); + results.muscleExcitations = results.muscleExcitations(:, :, ... + mtpInputs.numPaddingFrames + 1 : end - mtpInputs.numPaddingFrames); + resultsSynx.muscleExcitations = resultsSynx.muscleExcitations(:, :, ... + mtpInputs.numPaddingFrames + 1 : end - mtpInputs.numPaddingFrames); +end + +function saveMuscleTendonLengthInitializationResults(... + precalInputs, mtpInputs, resultsDirectory) + + tempValues.optimalFiberLengthScaleFactors = ... + mtpInputs.optimalFiberLength ./ precalInputs.optimalFiberLength; + tempValues.tendonSlackLengthScaleFactors = ... + mtpInputs.tendonSlackLength ./ precalInputs.tendonSlackLength; + precalInputs.maxIsometricForce = mtpInputs.maxIsometricForce; + precalInputs.optimizeIsometricMaxForce = 0; + modeledValues = calcMuscleTendonLengthInitializationModeledValues(tempValues, precalInputs); + savePassiveForceData(permute(modeledValues.passiveForce, [3 1 2]), ... + precalInputs.muscleNames, resultsDirectory); +% if precalInputs.passiveMomentDataExists +% plotPassiveMomentData(permute(modeledValues.passiveModelMoments, [3 1 2]), ... +% permute(precalInputs.passiveData.inverseDynamicsMoments, [3 1 2]), ... +% precalInputs.passivePrefixes) +% end +end + +function savePassiveForceData(modeledValue, muscleNames, resultsDirectory) + + meanModeledValue = squeeze(mean(modeledValue, 2)); + stdModeledValue = squeeze(std(modeledValue, [], 2)); + + data = {meanModeledValue, stdModeledValue}; + dataNames = ["meanModeledValue", "stdModeledValue"]; + + [stoDataArray, stoColumnLabels] = groupDataByMuscle(data, dataNames, muscleNames); + + writeToSto(stoColumnLabels, 1:1:length(stoDataArray), stoDataArray, ... + strcat(resultsDirectory, "/passiveForceData/", "_passiveForceData.sto")) % Maybe needs counter for gait cycles? +end + +function printJointMomentMatchingError(muscleJointMoments, inverseDynamicsMoments) % Good + + for i = 1 : size(muscleJointMoments, 2) + jointMomentsRmse(i) = sqrt(sum((muscleJointMoments(:, i, :) - ... + inverseDynamicsMoments(:, i, :)) .^ 2, 'all') / ... + (numel(inverseDynamicsMoments(:, 1, :)) - 1)); + jointMomentsMae(i) = sum(abs(muscleJointMoments(:, i, :) - ... + inverseDynamicsMoments(:, i, :)) / ... + numel(inverseDynamicsMoments(:, 1, :)), 'all'); + end + fprintf(['The root mean sqrt (RMS) errors between model-predicted ' ... + 'and inverse dynamic moments are: \n' ]); + fprintf(['\n ' num2str(jointMomentsRmse) ' \n']); + fprintf(['The mean absolute errors (MAEs) between model-predicted ' ... + 'and inverse dynamic moments are: \n' ]); + fprintf(['\n ' num2str(jointMomentsMae) ' \n']); +end + +function saveExcitationAndActivationData(results, resultsSynx, ... + experimentalData, synergyParameters, resultsDirectory) + muscleLabels = getSynxMuscleNames(experimentalData.muscleNames, ... + synergyParameters.missingEmgChannelGroups); + for i = 1 : numel(synergyParameters.taskNames) + + [stoDataArray, stoColumnLabels] = formatMuscleExcitationsAndActivations(... + results.muscleExcitations(synergyParameters.trialIndex{i}, :, :), ... + resultsSynx.muscleExcitations(synergyParameters.trialIndex{i}, :, :), ... + results.muscleActivations(synergyParameters.trialIndex{i}, :, :), ... + resultsSynx.muscleActivations(synergyParameters.trialIndex{i}, :, :), ... + muscleLabels); + writeToSto(stoColumnLabels, 1:1:length(stoDataArray), stoDataArray, ... + strcat(resultsDirectory, "/muscleExcitationsAndActivations/", ... + synergyParameters.taskNames{i}, "_muscleExcitationsActivations.sto")) + end +end + +function [stoDataArray, stoColumnLabels] = formatMuscleExcitationsAndActivations( ... + muscleExcitations, muscleExcitationsSynx, muscleActivations, ... + muscleActivationsSynx, muscleLabels) + + meanMuscleExcitation = permute(mean(muscleExcitations, 1), [3 2 1]); + stdMuscleExcitation = permute(std(muscleExcitations, [], 1), [3 2 1]); + meanMuscleExcitationSynx = permute(mean(muscleExcitationsSynx, 1), [3 2 1]); + stdMuscleExcitationSynx = permute(std(muscleExcitationsSynx, [], 1), [3 2 1]); + meanMuscleActivation = permute(mean(muscleActivations, 1), [3 2 1]); + stdMuscleActivation = permute(std(muscleActivations, [], 1), [3 2 1]); + meanMuscleActivationSynx = permute(mean(muscleActivationsSynx, 1), [3 2 1]); + stdMuscleActivationSynx = permute(std(muscleActivationsSynx, [], 1), [3 2 1]); + excitationsAndActivations = { + meanMuscleExcitation, stdMuscleExcitation, ... + meanMuscleExcitationSynx, stdMuscleExcitationSynx, ... + meanMuscleActivation, stdMuscleActivation, ... + meanMuscleActivationSynx, stdMuscleActivationSynx}; + dataNames = ["meanMuscleExcitation", "stdMuscleExcitation", ... + "meanMuscleExcitationSynx", "stdMuscleExcitationSynx", ... + "meanMuscleActivation", "stdMuscleActivation", ... + "meanMuscleActivationSynx", "stdMuscleActivationSynx"]; + [stoDataArray, stoColumnLabels] = groupDataByMuscle(excitationsAndActivations, ... + dataNames, muscleLabels); +% for i = 0 : numel(muscleLabels) - 1 +% stoDataArray(:, i*8+1) = meanMuscleExcitation(1:101, i + 1); +% stoDataArray(:, i*8+2) = stdMuscleExcitation(1:101, i+1); +% stoDataArray(:, i*8+3) = meanMuscleExcitationSynx(1:101, i+1); +% stoDataArray(:, i*8+4) = stdMuscleExcitationSynx(1:101, i+1); +% stoDataArray(:, i*8+5) = meanMuscleActivation(1:101, i+1); +% stoDataArray(:, i*8+6) = stdMuscleActivation(1:101, i+1); +% stoDataArray(:, i*8+7) = meanMuscleActivationSynx(1:101, i+1); +% stoDataArray(:, i*8+8) = stdMuscleActivationSynx(1:101, i+1); +% end +% +% stoColumnLabels = strings(8*numel(muscleLabels),1); +% for i=0:numel(muscleLabels)-1 +% stoColumnLabels(8*i+1) = strcat(muscleLabels{i+1}, "_", "meanMuscleExcitation"); +% stoColumnLabels(8*i+2) = strcat(muscleLabels{i+1}, "_", "stdMuscleExcitation"); +% stoColumnLabels(8*i+3) = strcat(muscleLabels{i+1}, "_", "meanMuscleExcitationSynx"); +% stoColumnLabels(8*i+4) = strcat(muscleLabels{i+1}, "_", "stdMuscleExcitationSynx"); +% stoColumnLabels(8*i+5) = strcat(muscleLabels{i+1}, "_", "meanMuscleActivation"); +% stoColumnLabels(8*i+6) = strcat(muscleLabels{i+1}, "_", "stdMuscleActivation"); +% stoColumnLabels(8*i+7) = strcat(muscleLabels{i+1}, "_", "meanMuscleActivationSynx"); +% stoColumnLabels(8*i+8) = strcat(muscleLabels{i+1}, "_", "stdMuscleActivationSynx"); +end + +function [dataArray, columnNames] = groupDataByMuscle(... + data, dataNames, muscleNames) + + lengthMuscleNames = numel(muscleNames); + lengthDataNames = numel(dataNames); + dataArray = zeros(length(data{1}), lengthMuscleNames*lengthDataNames); + columnNames = strings(lengthDataNames*lengthMuscleNames, 1); + for i = 0 : lengthMuscleNames - 1 + for j = 1 : lengthDataNames + dataArray(:, lengthDataNames * i + j) = data{j}(:, i + 1); + columnNames(lengthDataNames * i + j) = strcat(muscleNames(i + 1), "_", dataNames(j)); + end + end +end + +function muscleLabels = getSynxMuscleNames(muscleNames, ... + missingEmgChannelGroups) + + for i = 1 : numel(muscleNames) + if ismember(i, [missingEmgChannelGroups{:}]) + muscleLabels{i} = [muscleNames{i} '(*)']; + else + muscleLabels{i} = muscleNames{i}; + end + end +end \ No newline at end of file From ac44b54333f056350509f23f7df04394654196b9 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Fri, 1 Sep 2023 21:01:08 -0500 Subject: [PATCH 046/365] first impl of new design optimization --- src/DesignOptimization/DesignOptimization.m | 2 + .../DesignOptimizationTool.m | 3 +- .../computeDesignOptimizationMainFunction.m | 1 + .../parseDesignOptimizationSettingsTree.m | 8 +-- .../updateSystemFromUserDefinedFunctions.m | 1 + .../gpops/computeGpopsContinuousFunction.m | 3 + .../gpops/computeGpopsEndpointFunction.m | 11 ++-- .../convertFromGpopsTorqueDrivenOutputs.m | 3 + .../gpops/convertToGpopsTorqueDrivenInputs.m | 35 +++++++++++ .../gpops/makeGpopsValuesAsStruct.m | 60 +++++++++---------- .../calcTreatmentOptimizationCost.m | 6 +- src/core/parse/parseInitialGuess.m | 30 +++++++--- .../parse/parseTreatmentOptimizationInputs.m | 2 +- 13 files changed, 112 insertions(+), 53 deletions(-) diff --git a/src/DesignOptimization/DesignOptimization.m b/src/DesignOptimization/DesignOptimization.m index 9651dd5b2..33cfb0d46 100644 --- a/src/DesignOptimization/DesignOptimization.m +++ b/src/DesignOptimization/DesignOptimization.m @@ -39,6 +39,7 @@ end output = computeDesignOptimizationMainFunction(inputs, params); end + function inputs = setupMuscleSynergies(inputs) inputs.splineSynergyActivations = spaps(inputs.initialGuess.time/inputs.initialGuess.time(end), ... inputs.initialGuess.control(:, inputs.numCoordinates + 1 : ... @@ -46,6 +47,7 @@ inputs.synergyLabels = inputs.initialGuess.controlLabels(:, ... inputs.numCoordinates + 1 : inputs.numCoordinates + inputs.numSynergies); end + function inputs = setupExternalTorqueControls(inputs) if size(inputs.initialGuess.control, 2) ~= length(inputs.maxControl) inputs.initialGuess.control = [inputs.initialGuess.control ... diff --git a/src/DesignOptimization/DesignOptimizationTool.m b/src/DesignOptimization/DesignOptimizationTool.m index a9f20347e..4aaa4d9bf 100644 --- a/src/DesignOptimization/DesignOptimizationTool.m +++ b/src/DesignOptimization/DesignOptimizationTool.m @@ -33,7 +33,8 @@ function DesignOptimizationTool(settingsFileName) settingsTree = xml2struct(settingsFileName); verifyVersion(settingsTree, "DesignOptimizationTool"); [inputs, params] = parseDesignOptimizationSettingsTree(settingsTree); -[outputs, inputs] = DesignOptimization(inputs, params); +inputs = makeTreatmentOptimizationInputs(inputs, params); +outputs = solveOptimalControlProblem(inputs, params); reportTreatmentOptimizationResults(outputs, inputs); saveDesignOptimizationResults(outputs, inputs); end diff --git a/src/DesignOptimization/computeDesignOptimizationMainFunction.m b/src/DesignOptimization/computeDesignOptimizationMainFunction.m index e4366c65c..d4196160e 100644 --- a/src/DesignOptimization/computeDesignOptimizationMainFunction.m +++ b/src/DesignOptimization/computeDesignOptimizationMainFunction.m @@ -74,6 +74,7 @@ bounds.phase.finaltime.upper = 0.5; end end + function guess = addUserDefinedTermsToGuess(guess, inputs) for i = 1:length(inputs.userDefinedVariables) variable = inputs.userDefinedVariables{i}; diff --git a/src/DesignOptimization/parseDesignOptimizationSettingsTree.m b/src/DesignOptimization/parseDesignOptimizationSettingsTree.m index 93953100b..57ce8fee8 100644 --- a/src/DesignOptimization/parseDesignOptimizationSettingsTree.m +++ b/src/DesignOptimization/parseDesignOptimizationSettingsTree.m @@ -31,18 +31,16 @@ function [inputs, params] = ... parseDesignOptimizationSettingsTree(settingsTree) inputs = parseTreatmentOptimizationInputs(settingsTree); -inputs = parseTreatmentOptimizationDesignVariableBounds(settingsTree, ... - inputs); inputs = parseDesignSettings(settingsTree, inputs); -inputs = getInputs(settingsTree); +inputs = getInputs(settingsTree, inputs); params = parseTreatmentOptimizationParams(settingsTree); inputs = modifyModelForces(inputs); inputs = updateMuscleModelProperties(inputs); end -function inputs = getInputs(tree) +function inputs = getInputs(tree, inputs) import org.opensim.modeling.Storage -if strcmpi(inputs.controllerType, 'synergy_driven') +if strcmpi(inputs.controllerType, 'synergy') inputs.synergyWeights = parseTreatmentOptimizationStandard(... {getTextFromField(getFieldByName(tree, 'synergy_vectors_file'))}); end diff --git a/src/DesignOptimization/updateSystemFromUserDefinedFunctions.m b/src/DesignOptimization/updateSystemFromUserDefinedFunctions.m index 3649e31d5..180e08c9a 100644 --- a/src/DesignOptimization/updateSystemFromUserDefinedFunctions.m +++ b/src/DesignOptimization/updateSystemFromUserDefinedFunctions.m @@ -29,6 +29,7 @@ % ----------------------------------------------------------------------- % function inputs = updateSystemFromUserDefinedFunctions(inputs, values) +values for i = 1:length(inputs.auxdata.systemFns) func = str2func(inputs.auxdata.systemFns(i)); inputs = func(inputs, values); diff --git a/src/TreatmentOptimization/gpops/computeGpopsContinuousFunction.m b/src/TreatmentOptimization/gpops/computeGpopsContinuousFunction.m index f3f65eaf1..9a8171c88 100644 --- a/src/TreatmentOptimization/gpops/computeGpopsContinuousFunction.m +++ b/src/TreatmentOptimization/gpops/computeGpopsContinuousFunction.m @@ -30,6 +30,9 @@ function modeledValues = computeGpopsContinuousFunction(setup) values = makeGpopsValuesAsStruct(setup.phase, setup.auxdata); +if strcmp(setup.auxdata.toolName, "DesignOptimization") + setup = updateSystemFromUserDefinedFunctions(setup, values); +end modeledValues = calcTorqueBasedModeledValues(values, setup.auxdata); modeledValues = calcSynergyBasedModeledValues(values, setup.auxdata, ... modeledValues); diff --git a/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m b/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m index 5a2a771f5..f43e24e22 100644 --- a/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m +++ b/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m @@ -32,12 +32,15 @@ if ~isempty(setup.auxdata.terminal) || strcmp(setup.auxdata.toolName, "DesignOptimization") setup.phase.state = [setup.phase.initialstate; setup.phase.finalstate]; setup.phase.time = [setup.phase.initialtime; setup.phase.finaltime]; - setup.phase.control = ones(size(setup.phase.time,1),length(params.minControl)); - if isfield(inputs, "parameter") + setup.phase.control = ones(size(setup.phase.time,1),length(setup.auxdata.minControl)); + if isfield(setup, "parameter") setup.phase.parameter = setup.parameter; end values = makeGpopsValuesAsStruct(setup.phase, setup.auxdata); - modeledValues = calcTorqueBasedModeledValues(values, params); + modeledValues = calcTorqueBasedModeledValues(values, setup.auxdata); +end + +if ~isempty(setup.auxdata.terminal) [constraintTermCalculations, allowedTypes] = ... generateConstraintTermStruct("terminal", ... setup.auxdata.controllerType, setup.auxdata.toolName); @@ -50,7 +53,7 @@ [costTermCalculations, allowedTypes] = ... generateCostTermStruct("discrete", "DesignOptimization"); discrete = calcTreatmentOptimizationCost( ... - costTermCalculations, allowedTypes, values, modeledValues, auxdata); + costTermCalculations, allowedTypes, values, modeledValues, setup.auxdata); discreteObjective = sum(discrete) / length(discrete); if isnan(discreteObjective); discreteObjective = 0; end else diff --git a/src/TreatmentOptimization/gpops/convertFromGpopsTorqueDrivenOutputs.m b/src/TreatmentOptimization/gpops/convertFromGpopsTorqueDrivenOutputs.m index b154eff90..86871c743 100644 --- a/src/TreatmentOptimization/gpops/convertFromGpopsTorqueDrivenOutputs.m +++ b/src/TreatmentOptimization/gpops/convertFromGpopsTorqueDrivenOutputs.m @@ -28,6 +28,9 @@ inputs, params) solution = solution.result.solution; solution.auxdata = inputs; +if isfield(solution, 'parameter') + solution.phase.parameter = [solution.parameter]; +end if isfield(inputs, "optimizeSynergyVectors") solution.phase.parameter = solution.parameter; end diff --git a/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m b/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m index f9c6170b8..9a3acb79d 100644 --- a/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m +++ b/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m @@ -27,6 +27,9 @@ function setup = convertToGpopsTorqueDrivenInputs(inputs, params) bounds = setupProblemBounds(inputs, params); guess = setupCommonOptimalControlInitialGuess(inputs); +if strcmp(inputs.toolName, "DesignOptimization") + guess = addUserDefinedTermsToGuess(guess, inputs); +end initializeMexOrMatlabParallelFunctions(inputs.mexModel); setup = setupGpopsSettings(inputs, ... bounds, guess, params, ... @@ -45,5 +48,37 @@ bounds.parameter.upper = 0.5 * ones(1, length(inputs.minParameter)); end end +if strcmp(inputs.toolName, "DesignOptimization") + for i = 1:length(inputs.userDefinedVariables) + variable = inputs.userDefinedVariables{i}; + if ~isfield(bounds, "parameter") || ... + ~isfield(bounds.parameter, "lower") + bounds.parameter.lower = [-0.5]; + bounds.parameter.upper = [0.5]; + else + bounds.parameter.lower = [bounds.parameter.lower, ... + -0.5]; + bounds.parameter.upper = [bounds.parameter.upper, ... + 0.5]; + end + end + if isfield(inputs, "finalTimeRange") + bounds.phase.finaltime.lower = guess.phase.time(end) - (0.5 - guess.phase.time(end)); + bounds.phase.finaltime.upper = 0.5; + end +end end +function guess = addUserDefinedTermsToGuess(guess, inputs) +for i = 1:length(inputs.userDefinedVariables) + variable = inputs.userDefinedVariables{i}; + if ~isfield(guess, "parameter") + guess.parameter = []; + end + guess.parameter = [guess.parameter, ... + scaleToBounds( ... + variable.initial_values, ... + variable.upper_bounds, ... + variable.lower_bounds)]; +end +end diff --git a/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m b/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m index 111c03a30..af060a86b 100644 --- a/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m +++ b/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m @@ -69,48 +69,48 @@ end if strcmp(inputs.toolName, "DesignOptimization") numParameters = 0; - if strcmp(params.controllerType, 'synergy_driven') - if params.optimizeSynergyVectors - values.synergyWeights = scaleToOriginal(inputs.parameter(1, ... - 1 : params.numSynergyWeights), ... - params.maxParameter, params.minParameter); + if strcmp(inputs.controllerType, 'synergy_driven') + if inputs.optimizeSynergyVectors + values.synergyWeights = scaleToOriginal(phase.parameter(1, ... + 1 : inputs.numSynergyWeights), ... + inputs.maxParameter, inputs.minParameter); values.synergyWeights = getSynergyWeightsFromGroups(... - values.synergyWeights, params); - numParameters = params.numSynergyWeights; + values.synergyWeights, inputs); + numParameters = inputs.numSynergyWeights; else values.synergyWeights = getSynergyWeightsFromGroups(... - params.synergyWeightsGuess, params); + inputs.synergyWeightsGuess, inputs); end - if params.splineSynergyActivations.dim > 1 + if inputs.splineSynergyActivations.dim > 1 values.controlSynergyActivations = ... - fnval(params.splineSynergyActivations, values.time)'; + fnval(inputs.splineSynergyActivations, values.time)'; else values.controlSynergyActivations = ... - fnval(params.splineSynergyActivations, values.time); + fnval(inputs.splineSynergyActivations, values.time); end - if params.enableExternalTorqueControl - controls = scaleToOriginal(inputs.control, ones(size( ... - inputs.control, 1), 1) .* params.maxControl, ... - ones(size(inputs.control, 1), 1) .* params.minControl); - values.externalTorqueControls = controls(:, params.numCoordinates + ... - params.numSynergies + 1 : end); + if inputs.enableExternalTorqueControl + controls = scaleToOriginal(phase.control, ones(size( ... + phase.control, 1), 1) .* inputs.maxControl, ... + ones(size(phase.control, 1), 1) .* inputs.minControl); + values.externalTorqueControls = controls(:, inputs.numCoordinates + ... + inputs.numSynergies + 1 : end); end else - if isfield(params, "enableExternalTorqueControl") && ... - params.enableExternalTorqueControl - controls = scaleToOriginal(inputs.control, ones(size( ... - inputs.control, 1), 1) .* params.maxControl, ... - ones(size(inputs.control, 1), 1) .* params.minControl); - values.externalTorqueControls = controls(:, params.numCoordinates + ... - params.numTorqueControls + 1 : end); + if isfield(inputs, "enableExternalTorqueControl") && ... + inputs.enableExternalTorqueControl + controls = scaleToOriginal(phase.control, ones(size( ... + phase.control, 1), 1) .* inputs.maxControl, ... + ones(size(phase.control, 1), 1) .* inputs.minControl); + values.externalTorqueControls = controls(:, inputs.numCoordinates + ... + inputs.numTorqueControls + 1 : end); end end - if isfield(params, 'userDefinedVariables') - for i = 1:length(params.userDefinedVariables) - values.(params.userDefinedVariables{i}.type)(i) = scaleToOriginal( ... - inputs.parameter(1, i + numParameters), ... - params.userDefinedVariables{i}.upper_bounds, ... - params.userDefinedVariables{i}.lower_bounds); + if isfield(inputs, 'userDefinedVariables') + for i = 1:length(inputs.userDefinedVariables) + values.(inputs.userDefinedVariables{i}.type)(i) = scaleToOriginal( ... + phase.parameter(1, i + numParameters), ... + inputs.userDefinedVariables{i}.upper_bounds, ... + inputs.userDefinedVariables{i}.lower_bounds); end end end diff --git a/src/core/TreatmentOptimization/calcTreatmentOptimizationCost.m b/src/core/TreatmentOptimization/calcTreatmentOptimizationCost.m index 1d7789a51..00d291069 100644 --- a/src/core/TreatmentOptimization/calcTreatmentOptimizationCost.m +++ b/src/core/TreatmentOptimization/calcTreatmentOptimizationCost.m @@ -40,9 +40,9 @@ cost = cat(2, ... cost, ... fn(values, modeledValues, auxdata, costTerm)); - else - throw(MException('', ['Cost term type ' costTerm.type ... - ' does not exist for this tool.'])) +% else +% throw(MException('', ['Cost term type ' costTerm.type ... +% ' does not exist for this tool.'])) end end end diff --git a/src/core/parse/parseInitialGuess.m b/src/core/parse/parseInitialGuess.m index 42d577865..fa06fb190 100644 --- a/src/core/parse/parseInitialGuess.m +++ b/src/core/parse/parseInitialGuess.m @@ -4,7 +4,7 @@ % specified), controls (if specified), and parameters (if specified) % % (struct) -> (struct) -% +% % ----------------------------------------------------------------------- % % The NMSM Pipeline is a toolkit for model personalization and treatment % @@ -28,7 +28,7 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function initialGuess = parseInitialGuess(tree) +function initialGuess = parseInitialGuess(tree, controllerType) import org.opensim.modeling.Storage initialGuess = []; statesFileName = getTextFromField(getFieldByNameOrAlternate(tree, ... @@ -40,13 +40,25 @@ initialGuess.state = parseTreatmentOptimizationStandard( ... {statesFileName}); end -controlsFileName = getTextFromField(getFieldByNameOrAlternate(tree, ... - 'initial_controls_file', '')); -if ~isempty(controlsFileName) - initialGuess.controlLabels = getStorageColumnNames(Storage( ... - {controlsFileName})); - initialGuess.control = parseTreatmentOptimizationStandard( ... - {controlsFileName}); +if strcmp(controllerType, "torque") + controlsFileName = getTextFromField(getFieldByNameOrAlternate(tree, ... + 'initial_torque_controls_file', '')); + if ~isempty(controlsFileName) + initialGuess.controlLabels = getStorageColumnNames(Storage( ... + {controlsFileName})); + initialGuess.control = parseTreatmentOptimizationStandard( ... + {controlsFileName}); + end +end +if strcmp(controllerType, "synergy") + controlsFileName = getTextFromField(getFieldByNameOrAlternate(tree, ... + 'initial_controls_file', '')); + if ~isempty(controlsFileName) + initialGuess.controlLabels = getStorageColumnNames(Storage( ... + {controlsFileName})); + initialGuess.control = parseTreatmentOptimizationStandard( ... + {controlsFileName}); + end end parametersFileName = getTextFromField(getFieldByNameOrAlternate(tree, ... 'initial_parameters_file', '')); diff --git a/src/core/parse/parseTreatmentOptimizationInputs.m b/src/core/parse/parseTreatmentOptimizationInputs.m index 827905481..19bad9a32 100644 --- a/src/core/parse/parseTreatmentOptimizationInputs.m +++ b/src/core/parse/parseTreatmentOptimizationInputs.m @@ -40,7 +40,7 @@ 'input_osimx_file'))); inputs = parseController(tree, inputs); inputs = parseTreatmentOptimizationDataDirectory(tree, inputs); -inputs.initialGuess = parseInitialGuess(tree); +inputs.initialGuess = parseInitialGuess(tree, inputs.controllerType); inputs = parseOptimalControlSolverSettings( ... getTextFromField(getFieldByNameOrError(tree, ... 'optimal_control_solver_settings_file')), inputs); From b412b71b2165d8d8b5d5686cb0eb7bccf73c35ba Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Fri, 1 Sep 2023 21:18:45 -0500 Subject: [PATCH 047/365] fix design opt user def impl --- src/DesignOptimization/updateSystemFromUserDefinedFunctions.m | 1 - src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m | 3 +++ .../gpops/convertToGpopsTorqueDrivenInputs.m | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/DesignOptimization/updateSystemFromUserDefinedFunctions.m b/src/DesignOptimization/updateSystemFromUserDefinedFunctions.m index 180e08c9a..3649e31d5 100644 --- a/src/DesignOptimization/updateSystemFromUserDefinedFunctions.m +++ b/src/DesignOptimization/updateSystemFromUserDefinedFunctions.m @@ -29,7 +29,6 @@ % ----------------------------------------------------------------------- % function inputs = updateSystemFromUserDefinedFunctions(inputs, values) -values for i = 1:length(inputs.auxdata.systemFns) func = str2func(inputs.auxdata.systemFns(i)); inputs = func(inputs, values); diff --git a/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m b/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m index f43e24e22..9bf416b10 100644 --- a/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m +++ b/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m @@ -37,6 +37,9 @@ setup.phase.parameter = setup.parameter; end values = makeGpopsValuesAsStruct(setup.phase, setup.auxdata); + if strcmp(setup.auxdata.toolName, "DesignOptimization") + setup = updateSystemFromUserDefinedFunctions(setup, values); + end modeledValues = calcTorqueBasedModeledValues(values, setup.auxdata); end diff --git a/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m b/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m index 9a3acb79d..aa499924c 100644 --- a/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m +++ b/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m @@ -42,7 +42,7 @@ function bounds = setupProblemBounds(inputs, params) bounds = setupCommonOptimalControlBounds(inputs, params); % setup parameter bounds -if strcmp(inputs.controllerType, 'synergy_driven') +if strcmp(inputs.controllerType, 'synergy') if inputs.optimizeSynergyVectors bounds.parameter.lower = -0.5 * ones(1, length(inputs.minParameter)); bounds.parameter.upper = 0.5 * ones(1, length(inputs.minParameter)); From 910a07e7e873e03fda3fd214c1156443e22e5604 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Mon, 4 Sep 2023 20:43:30 -0500 Subject: [PATCH 048/365] remove getSynergyGroups as helper --- ...NeuralControlPersonalizationSettingsTree.m | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/src/NeuralControlPersonalization/parseNeuralControlPersonalizationSettingsTree.m b/src/NeuralControlPersonalization/parseNeuralControlPersonalizationSettingsTree.m index 62337e9d9..4cc52a34d 100644 --- a/src/NeuralControlPersonalization/parseNeuralControlPersonalizationSettingsTree.m +++ b/src/NeuralControlPersonalization/parseNeuralControlPersonalizationSettingsTree.m @@ -132,29 +132,6 @@ '1e6'))); end -function groups = getSynergyGroups(tree, model) -synergySetTree = getFieldByNameOrError(tree, "RCNLSynergySet"); -groupsTree = getFieldByNameOrError(synergySetTree, "RCNLSynergy"); -groups = {}; -for i=1:length(groupsTree) - if(length(groupsTree) == 1) - group = groupsTree; - else - group = groupsTree{i}; - end - groups{i}.numSynergies = ... - str2double(group.num_synergies.Text); - groupMembers = model.getForceSet().getGroup( ... - group.muscle_group_name.Text).getMembers(); - muscleNames = string([]); - for j=0:groupMembers.getSize() - 1 - muscleNames(end + 1) = groupMembers.get(j); - end - groups{i}.muscleNames = muscleNames; - groups{i}.muscleGroupName = group.muscle_group_name.Text; -end -end - function [optimalFiberLengthScaleFactors, ... tendonSlackLengthScaleFactors, maxIsometricForce] = ... getMtpDataInputs(inputs) From ea2a3aa659409e2222c8a0530f2e24e4859e5356 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Mon, 4 Sep 2023 20:50:22 -0500 Subject: [PATCH 049/365] updated name to parse from get --- .../_7uFynh3_lSMn19yjVfyMcvTrvsp.xml | 2 +- .../LUTCgzJ-pVvvop9t9kpRDPo21OQp.xml | 2 +- .../parseNeuralControlPersonalizationSettingsTree.m | 2 +- src/core/osimx/buildSynergyGroupOsimx.m | 2 +- src/core/parse/parseSynergyController.m | 2 +- src/core/synergies/{getSynergyGroups.m => parseSynergyGroups.m} | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) rename src/core/synergies/{getSynergyGroups.m => parseSynergyGroups.m} (98%) diff --git a/resources/project/q_1LkE6edoqMkydCSKtU4WisoNo/_7uFynh3_lSMn19yjVfyMcvTrvsp.xml b/resources/project/q_1LkE6edoqMkydCSKtU4WisoNo/_7uFynh3_lSMn19yjVfyMcvTrvsp.xml index d2e13e4a9..a0fe9a8cb 100644 --- a/resources/project/q_1LkE6edoqMkydCSKtU4WisoNo/_7uFynh3_lSMn19yjVfyMcvTrvsp.xml +++ b/resources/project/q_1LkE6edoqMkydCSKtU4WisoNo/_7uFynh3_lSMn19yjVfyMcvTrvsp.xml @@ -1,2 +1,2 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/resources/project/y98yrTc3pwfcIzBAdQ_9LFtWOBI/LUTCgzJ-pVvvop9t9kpRDPo21OQp.xml b/resources/project/y98yrTc3pwfcIzBAdQ_9LFtWOBI/LUTCgzJ-pVvvop9t9kpRDPo21OQp.xml index d2e13e4a9..a0fe9a8cb 100644 --- a/resources/project/y98yrTc3pwfcIzBAdQ_9LFtWOBI/LUTCgzJ-pVvvop9t9kpRDPo21OQp.xml +++ b/resources/project/y98yrTc3pwfcIzBAdQ_9LFtWOBI/LUTCgzJ-pVvvop9t9kpRDPo21OQp.xml @@ -1,2 +1,2 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/NeuralControlPersonalization/parseNeuralControlPersonalizationSettingsTree.m b/src/NeuralControlPersonalization/parseNeuralControlPersonalizationSettingsTree.m index 4cc52a34d..c8a980b50 100644 --- a/src/NeuralControlPersonalization/parseNeuralControlPersonalizationSettingsTree.m +++ b/src/NeuralControlPersonalization/parseNeuralControlPersonalizationSettingsTree.m @@ -41,7 +41,7 @@ function inputs = getInputs(tree) inputs = parseMtpNcpSharedInputs(tree); -inputs.synergyGroups = getSynergyGroups(tree, Model(inputs.model)); +inputs.synergyGroups = parseSynergyGroups(tree, Model(inputs.model)); inputs = matchMuscleNamesFromCoordinatesAndSynergyGroups(inputs); inputs = reorderPreprocessedDataByMuscleNames(inputs, inputs.muscleNames); [inputs.maxIsometricForce, inputs.optimalFiberLength, ... diff --git a/src/core/osimx/buildSynergyGroupOsimx.m b/src/core/osimx/buildSynergyGroupOsimx.m index 94e25da9a..8d5eb8ba9 100644 --- a/src/core/osimx/buildSynergyGroupOsimx.m +++ b/src/core/osimx/buildSynergyGroupOsimx.m @@ -4,7 +4,7 @@ % into a new .osimx struct to be printed with writeOsimxFile(). See % buildOsimxFromOsimxStruct() for reference. % -% The expected format of the synergyGroups comes from getSynergyGroups() as +% The expected format of the synergyGroups comes from parseSynergyGroups() as % used in parseNeuralControlPersonalization() % % (struct, struct) -> (struct) diff --git a/src/core/parse/parseSynergyController.m b/src/core/parse/parseSynergyController.m index 847f0e222..de52e85dd 100644 --- a/src/core/parse/parseSynergyController.m +++ b/src/core/parse/parseSynergyController.m @@ -30,7 +30,7 @@ % ----------------------------------------------------------------------- % function inputs = parseSynergyController(tree, inputs) -inputs.synergyGroups = getSynergyGroups(tree, Model(inputs.model)); +inputs.synergyGroups = parseSynergyGroups(tree, Model(inputs.model)); inputs.numSynergies = getNumSynergies(inputs.synergyGroups); inputs.numSynergyWeights = getNumSynergyWeights(inputs.synergyGroups); inputs.surrogateModelCoordinateNames = parseSpaceSeparatedList(tree, ... diff --git a/src/core/synergies/getSynergyGroups.m b/src/core/synergies/parseSynergyGroups.m similarity index 98% rename from src/core/synergies/getSynergyGroups.m rename to src/core/synergies/parseSynergyGroups.m index c48917c9d..ac8abdc00 100644 --- a/src/core/synergies/getSynergyGroups.m +++ b/src/core/synergies/parseSynergyGroups.m @@ -27,7 +27,7 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function groups = getSynergyGroups(tree, model) +function groups = parseSynergyGroups(tree, model) synergySetTree = getFieldByNameOrError(tree, "RCNLSynergySet"); groupsTree = getFieldByNameOrError(synergySetTree, "RCNLSynergy"); groups = {}; From 6c57f165cb986e85c8cd036da2984eb7138bcba2 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Mon, 4 Sep 2023 20:53:28 -0500 Subject: [PATCH 050/365] add model to parseOsimxFile for synergies --- .../Saving/writeGroundContactPersonalizationOsimxFile.m | 2 +- .../parseNeuralControlPersonalizationSettingsTree.m | 2 +- src/core/osimx/buildOsimxFromOsimxStruct.m | 6 +++--- src/core/osimx/writeMuscleTendonPersonalizationOsimxFile.m | 2 +- src/core/osimx/writeNeuralControlPersonalizationOsimxFile.m | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/GroundContactPersonalization/Saving/writeGroundContactPersonalizationOsimxFile.m b/src/GroundContactPersonalization/Saving/writeGroundContactPersonalizationOsimxFile.m index 86c9db2b3..f0f460827 100644 --- a/src/GroundContactPersonalization/Saving/writeGroundContactPersonalizationOsimxFile.m +++ b/src/GroundContactPersonalization/Saving/writeGroundContactPersonalizationOsimxFile.m @@ -33,7 +33,7 @@ function writeGroundContactPersonalizationOsimxFile(inputs, ... model = Model(modelFileName); if isfile(osimxFileName) - osimx = parseOsimxFile(inputs.inputOsimxFile); + osimx = parseOsimxFile(inputs.inputOsimxFile, model); [~, name, ~] = fileparts(inputs.inputOsimxFile); outfile = fullfile(resultsDirectory, strcat(name, "_gcp.xml")); else diff --git a/src/NeuralControlPersonalization/parseNeuralControlPersonalizationSettingsTree.m b/src/NeuralControlPersonalization/parseNeuralControlPersonalizationSettingsTree.m index c8a980b50..eb7fe35b8 100644 --- a/src/NeuralControlPersonalization/parseNeuralControlPersonalizationSettingsTree.m +++ b/src/NeuralControlPersonalization/parseNeuralControlPersonalizationSettingsTree.m @@ -71,7 +71,7 @@ % if ~isstruct(osimxFileName) || isempty(osimxFileName.Text) % throw(MException('', 'An input .osimx file is required if using data from MTP.')) % end -inputs.mtpMuscleData = parseOsimxFile(osimxFileName.Text); +inputs.mtpMuscleData = parseOsimxFile(osimxFileName.Text, inputs.model); % Remove activations of muscles from coordinates not included includedSubset = ismember(inputs.mtpActivationsColumnNames, ... inputs.muscleTendonColumnNames); diff --git a/src/core/osimx/buildOsimxFromOsimxStruct.m b/src/core/osimx/buildOsimxFromOsimxStruct.m index b7a421d7c..d0369fa20 100644 --- a/src/core/osimx/buildOsimxFromOsimxStruct.m +++ b/src/core/osimx/buildOsimxFromOsimxStruct.m @@ -1,8 +1,8 @@ % This function is part of the NMSM Pipeline, see file for full license. % -% This function takes the output of parseOsimxFile(filename) and produces -% a struct that can passed directly into writeOsimxFile() and replicate the -% input file. +% This function takes the output of parseOsimxFile(filename, model) and +% produces a struct that can passed directly into writeOsimxFile() and +% replicate the input file. % % This function is most commonly used to add values to an existing .osimx % file. diff --git a/src/core/osimx/writeMuscleTendonPersonalizationOsimxFile.m b/src/core/osimx/writeMuscleTendonPersonalizationOsimxFile.m index 7bacaccd2..78455a417 100644 --- a/src/core/osimx/writeMuscleTendonPersonalizationOsimxFile.m +++ b/src/core/osimx/writeMuscleTendonPersonalizationOsimxFile.m @@ -33,7 +33,7 @@ function writeMuscleTendonPersonalizationOsimxFile(modelFileName, ... model = Model(modelFileName); if isfile(osimxFileName) - osimx = parseOsimxFile(osimxFileName); + osimx = parseOsimxFile(osimxFileName, model); [~, name, ~] = fileparts(osimxFileName); outfile = fullfile(results_directory, strcat(name, "_mtp.xml")); else diff --git a/src/core/osimx/writeNeuralControlPersonalizationOsimxFile.m b/src/core/osimx/writeNeuralControlPersonalizationOsimxFile.m index bba519baa..4950091bd 100644 --- a/src/core/osimx/writeNeuralControlPersonalizationOsimxFile.m +++ b/src/core/osimx/writeNeuralControlPersonalizationOsimxFile.m @@ -36,7 +36,7 @@ function writeNeuralControlPersonalizationOsimxFile(inputs, ... buildFromExisting = false; if isfield(inputs, 'osimxFileName') if isfile(inputs.osimxFileName) - osimx = parseOsimxFile(inputs.osimxFileName); + osimx = parseOsimxFile(inputs.osimxFileName, model); [~, name, ~] = fileparts(inputs.osimxFileName); outfile = fullfile(resultsDirectory, strcat(name, "_ncp.xml")); buildFromExisting = true; From a8449ca16244b9d5b92760355eb1308593882b37 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Mon, 4 Sep 2023 20:53:45 -0500 Subject: [PATCH 051/365] update parseOsimxFile to include synergies --- src/core/osimx/parseOsimxFile.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core/osimx/parseOsimxFile.m b/src/core/osimx/parseOsimxFile.m index 442c8fd4c..884c07c10 100644 --- a/src/core/osimx/parseOsimxFile.m +++ b/src/core/osimx/parseOsimxFile.m @@ -27,7 +27,7 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function osimx = parseOsimxFile(osimxFileName) +function osimx = parseOsimxFile(osimxFileName, model) if strcmp(osimxFileName, "") osimx = struct(); @@ -87,6 +87,8 @@ end end end + +osimx.synergyGroups = parseSynergyGroups(tree, model); end function contactSurface = parseContactSurface(tree) From 2e84f72bdc83792a505951e3875bdfbc9a34e4d9 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Mon, 4 Sep 2023 20:57:13 -0500 Subject: [PATCH 052/365] remove reparse of synergyset --- src/core/parse/parseSynergyController.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/parse/parseSynergyController.m b/src/core/parse/parseSynergyController.m index de52e85dd..9bab8767f 100644 --- a/src/core/parse/parseSynergyController.m +++ b/src/core/parse/parseSynergyController.m @@ -30,7 +30,7 @@ % ----------------------------------------------------------------------- % function inputs = parseSynergyController(tree, inputs) -inputs.synergyGroups = parseSynergyGroups(tree, Model(inputs.model)); +inputs.synergyGroups = inputs.osimx.synergyGroups; inputs.numSynergies = getNumSynergies(inputs.synergyGroups); inputs.numSynergyWeights = getNumSynergyWeights(inputs.synergyGroups); inputs.surrogateModelCoordinateNames = parseSpaceSeparatedList(tree, ... From 45eeb0d5dd865e8bcf0571fe2b56998020f38cf6 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Mon, 4 Sep 2023 21:43:30 -0500 Subject: [PATCH 053/365] update .model reference --- ...rseJointModelPersonalizationSettingsTree.m | 2 +- .../calcTorqueBasedModeledValues.m | 21 +++++++++--------- .../disableModelMuscles.m | 1 - .../TreatmentOptimization/modifyModelForces.m | 9 +++----- src/core/parse/getModelOrOsimxInputs.m | 4 +++- src/core/parse/parseController.m | 4 ++-- src/core/parse/parseInitialGuess.m | 22 ++++++++++--------- src/core/parse/parseModel.m | 6 +++-- src/core/parse/parseMtpNcpSharedInputs.m | 2 +- .../parse/parseTreatmentOptimizationInputs.m | 4 ++-- 10 files changed, 39 insertions(+), 36 deletions(-) diff --git a/src/JointModelPersonalization/parseJointModelPersonalizationSettingsTree.m b/src/JointModelPersonalization/parseJointModelPersonalizationSettingsTree.m index f0c9199f2..b2b45872b 100644 --- a/src/JointModelPersonalization/parseJointModelPersonalizationSettingsTree.m +++ b/src/JointModelPersonalization/parseJointModelPersonalizationSettingsTree.m @@ -55,7 +55,7 @@ end function inputs = getInputs(tree) -inputs.model = parseModel(tree); +inputs = parseModel(tree, struct()); model = Model(inputs.model); inputs.tasks = getTasks(model, tree); inputs.desiredError = ... diff --git a/src/TrackingOptimization/calcTorqueBasedModeledValues.m b/src/TrackingOptimization/calcTorqueBasedModeledValues.m index 2976ee9de..c4121e0b8 100644 --- a/src/TrackingOptimization/calcTorqueBasedModeledValues.m +++ b/src/TrackingOptimization/calcTorqueBasedModeledValues.m @@ -16,7 +16,7 @@ % National Institutes of Health (R01 EB030520). % % % % Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % +% Author(s): Marleny Vega, Claire V. Hammond % % % % Licensed under the Apache License, Version 2.0 (the "License"); % % you may not use this file except in compliance with the License. % @@ -30,25 +30,26 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function modeledValues = calcTorqueBasedModeledValues(values, params) -appliedLoads = [zeros(length(values.time), params.numTotalMuscles)]; -if ~isempty(params.contactSurfaces) +function modeledValues = calcTorqueBasedModeledValues(values, inputs) +appliedLoads = [zeros(length(values.time), ... + model.getForceSet().getMuscles().getSize())]; +if ~isempty(inputs.contactSurfaces) clear pointKinematics [springPositions, springVelocities] = getSpringLocations( ... - values.time, values.statePositions, values.stateVelocities, params); + values.time, values.statePositions, values.stateVelocities, inputs); modeledValues.bodyLocations = getBodyLocations(values.time, .... - values.statePositions, values.stateVelocities, params); + values.statePositions, values.stateVelocities, inputs); groundReactions = calcFootGroundReactions(springPositions, ... - springVelocities, params, modeledValues.bodyLocations); + springVelocities, inputs, modeledValues.bodyLocations); groundReactionsBody = tranferGroundReactionMoments( ... - modeledValues.bodyLocations, groundReactions, params); + modeledValues.bodyLocations, groundReactions, inputs); modeledValues.groundReactionsLab = calcGroundReactionsLab(groundReactions); appliedLoads = [appliedLoads groundReactionsBody]; end modeledValues.inverseDynamicMoments = inverseDynamics(values.time, ... values.statePositions, values.stateVelocities, ... - values.stateAccelerations, params.coordinateNames, appliedLoads, ... - params.mexModel); + values.stateAccelerations, inputs.coordinateNames, appliedLoads, ... + inputs.mexModel); end function [springPositions, springVelocities] = getSpringLocations(time, .... diff --git a/src/core/TreatmentOptimization/disableModelMuscles.m b/src/core/TreatmentOptimization/disableModelMuscles.m index df39b7064..7d876a1bf 100644 --- a/src/core/TreatmentOptimization/disableModelMuscles.m +++ b/src/core/TreatmentOptimization/disableModelMuscles.m @@ -29,7 +29,6 @@ function [model, inputs] = disableModelMuscles(inputs, model) import org.opensim.modeling.Model -inputs.numTotalMuscles = model.getForceSet().getMuscles().getSize(); for i = 0:model.getForceSet().getMuscles().getSize()-1 if model.getForceSet().getMuscles().get(i).get_appliesForce() model.getForceSet().getMuscles().get(i).set_appliesForce(0); diff --git a/src/core/TreatmentOptimization/modifyModelForces.m b/src/core/TreatmentOptimization/modifyModelForces.m index 01082b6a6..65f16a70a 100644 --- a/src/core/TreatmentOptimization/modifyModelForces.m +++ b/src/core/TreatmentOptimization/modifyModelForces.m @@ -15,7 +15,7 @@ % National Institutes of Health (R01 EB030520). % % % % Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % +% Author(s): Marleny Vega, Claire V. Hammond % % % % Licensed under the Apache License, Version 2.0 (the "License"); % % you may not use this file except in compliance with the License. % @@ -30,11 +30,8 @@ % ----------------------------------------------------------------------- % function inputs = modifyModelForces(inputs) -if ~isa(inputs.model, 'org.opensim.modeling.Model') - model = Model(inputs.model); -end -[model, inputs] = disableModelMuscles(inputs, model); +[model, inputs] = disableModelMuscles(inputs, inputs.model); model = addContactSurfaceActuators(inputs, model); -inputs.mexModel = strcat(strrep(inputs.model,'.osim',''), '_inactiveMuscles.osim'); +inputs.mexModel = strcat(strrep(inputs.modelFileName,'.osim',''), '_inactiveMuscles.osim'); model.print(inputs.mexModel); end \ No newline at end of file diff --git a/src/core/parse/getModelOrOsimxInputs.m b/src/core/parse/getModelOrOsimxInputs.m index 60504fefd..88192bf83 100644 --- a/src/core/parse/getModelOrOsimxInputs.m +++ b/src/core/parse/getModelOrOsimxInputs.m @@ -11,7 +11,7 @@ % National Institutes of Health (R01 EB030520). % % % % Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % +% Author(s): Marleny Vega, Claire V. Hammond % % % % Licensed under the Apache License, Version 2.0 (the "License"); % % you may not use this file except in compliance with the License. % @@ -28,6 +28,8 @@ function inputs = getModelOrOsimxInputs(inputs) if ~isa(inputs.model, 'org.opensim.modeling.Model') model = Model(inputs.model); +else + model = inputs.model; end inputs.optimalFiberLength = []; inputs.tendonSlackLength = []; diff --git a/src/core/parse/parseController.m b/src/core/parse/parseController.m index 13a63e1fe..ef7ce3729 100644 --- a/src/core/parse/parseController.m +++ b/src/core/parse/parseController.m @@ -37,10 +37,10 @@ torqueTree = getFieldByName(tree, "RCNLTorqueController"); if isstruct(torqueTree) - inputs = parseTorqueController(torqueTree, inputs); + inputs = parseTorqueController(tree, inputs); end synergyTree = getFieldByName(tree, "RCNLSynergyController"); if isstruct(synergyTree) - inputs = parseSynergyController(synergyTree, inputs); + inputs = parseSynergyController(tree, inputs); end end diff --git a/src/core/parse/parseInitialGuess.m b/src/core/parse/parseInitialGuess.m index fa06fb190..da96642f7 100644 --- a/src/core/parse/parseInitialGuess.m +++ b/src/core/parse/parseInitialGuess.m @@ -49,8 +49,7 @@ initialGuess.control = parseTreatmentOptimizationStandard( ... {controlsFileName}); end -end -if strcmp(controllerType, "synergy") +elseif strcmp(controllerType, "synergy") controlsFileName = getTextFromField(getFieldByNameOrAlternate(tree, ... 'initial_controls_file', '')); if ~isempty(controlsFileName) @@ -59,13 +58,16 @@ initialGuess.control = parseTreatmentOptimizationStandard( ... {controlsFileName}); end -end -parametersFileName = getTextFromField(getFieldByNameOrAlternate(tree, ... - 'initial_parameters_file', '')); -if ~isempty(parametersFileName) - initialGuess.parameterLabels = getStorageColumnNames( ... - Storage({parametersFileName})); - initialGuess.parameter = parseTreatmentOptimizationStandard( ... - {parametersFileName}); + parametersFileName = getTextFromField(getFieldByNameOrAlternate(tree, ... + 'initial_parameters_file', '')); + if ~isempty(parametersFileName) + initialGuess.parameterLabels = getStorageColumnNames( ... + Storage({parametersFileName})); + initialGuess.parameter = parseTreatmentOptimizationStandard( ... + {parametersFileName}); + end +else + throw(MException("IncorrectControllerType", ... + "controllerType must be 'torque' or 'synergy'")); end end \ No newline at end of file diff --git a/src/core/parse/parseModel.m b/src/core/parse/parseModel.m index db2b325fb..b4834a6c5 100644 --- a/src/core/parse/parseModel.m +++ b/src/core/parse/parseModel.m @@ -1,3 +1,5 @@ -function model = parseModel(tree) -model = parseElementTextByName(tree, 'input_model_file'); +function inputs = parseModel(tree, inputs) +fileName = parseElementTextByName(tree, 'input_model_file'); +inputs.model = Model(fileName); +inputs.modelFileName = fileName; end diff --git a/src/core/parse/parseMtpNcpSharedInputs.m b/src/core/parse/parseMtpNcpSharedInputs.m index d39d703ac..251308797 100644 --- a/src/core/parse/parseMtpNcpSharedInputs.m +++ b/src/core/parse/parseMtpNcpSharedInputs.m @@ -6,7 +6,7 @@ function inputs = getInputs(tree) -inputs.model = parseModel(tree); +inputs = parseModel(tree, struct()); inputs.osimxFileName = parseElementTextByName(tree, "input_osimx_file"); inputs.coordinateNames = parseSpaceSeparatedList(tree, "coordinate_list"); inputs.muscleNames = getMusclesFromCoordinates(inputs.model, ... diff --git a/src/core/parse/parseTreatmentOptimizationInputs.m b/src/core/parse/parseTreatmentOptimizationInputs.m index 19bad9a32..038eb68d9 100644 --- a/src/core/parse/parseTreatmentOptimizationInputs.m +++ b/src/core/parse/parseTreatmentOptimizationInputs.m @@ -35,9 +35,9 @@ 'results_directory')); if(isempty(inputs.resultsDirectory)); inputs.resultsDirectory = pwd; end inputs.controllerType = parseControllerType(tree); -inputs.model = parseModel(tree); +inputs = parseModel(tree, inputs); inputs.osimx = parseOsimxFile(getTextFromField(getFieldByName(tree, ... - 'input_osimx_file'))); + 'input_osimx_file')), inputs.model); inputs = parseController(tree, inputs); inputs = parseTreatmentOptimizationDataDirectory(tree, inputs); inputs.initialGuess = parseInitialGuess(tree, inputs.controllerType); From 564d289951792356ef333ef347356e305d18a052 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Mon, 4 Sep 2023 21:43:42 -0500 Subject: [PATCH 054/365] fix small name issues --- src/core/parse/parseSynergyController.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core/parse/parseSynergyController.m b/src/core/parse/parseSynergyController.m index 9bab8767f..63c583b86 100644 --- a/src/core/parse/parseSynergyController.m +++ b/src/core/parse/parseSynergyController.m @@ -34,7 +34,7 @@ inputs.numSynergies = getNumSynergies(inputs.synergyGroups); inputs.numSynergyWeights = getNumSynergyWeights(inputs.synergyGroups); inputs.surrogateModelCoordinateNames = parseSpaceSeparatedList(tree, ... - "coordinate_list"); + "states_coordinate_list"); inputs.muscleNames = getMusclesFromCoordinates(inputs.model, ... inputs.surrogateModelCoordinateNames); inputs.numMuscles = length(inputs.muscleNames); @@ -42,6 +42,8 @@ "epsilon", "1e-4")); inputs.vMaxFactor = str2double(parseElementTextByNameOrAlternate(tree, ... "maximum_shortening_velocity_multiplier", "10")); +surrogateModelCoefficients = load(getTextFromField(getFieldByName(tree, ... + 'surrogate_model_coefficients'))); inputs.coefficients = surrogateModelCoefficients.coefficients; inputs.optimizeSynergyVectors = getBooleanLogic(... parseElementTextByNameOrAlternate(tree, "optimize_synergy_vectors", 0)); From 083495e09aec6d132eb5323e4bf28fb30472ec2b Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Tue, 5 Sep 2023 15:26:42 -0500 Subject: [PATCH 055/365] Fix derivatives --- src/SurrogateModelCreation/SurrogateModelCreation.m | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/SurrogateModelCreation/SurrogateModelCreation.m b/src/SurrogateModelCreation/SurrogateModelCreation.m index fbb29c822..9628eeca4 100644 --- a/src/SurrogateModelCreation/SurrogateModelCreation.m +++ b/src/SurrogateModelCreation/SurrogateModelCreation.m @@ -62,10 +62,12 @@ function SurrogateModelCreation(inputs) length(inputs.coordinateNames)); experimentalTime = parseTimeColumn(findFileListFromPrefixList(... fullfile(inputs.dataDirectory, "IKData"), prefixes))'; -for i = 1:size(inputs.experimentalJointAngles, 2) - [~, inputs.experimentalJointVelocities(:, i)] = SplineSmooth( ... - experimentalTime, inputs.experimentalJointAngles(:, i), 1); -end +inputs.experimentalJointVelocities = calcBSplineDerivative( ... + experimentalTime', inputs.experimentalJointAngles', 5, 20)'; +% for i = 1:size(inputs.experimentalJointAngles, 2) +% [~, inputs.experimentalJointVelocities(:, i)] = SplineSmooth( ... +% experimentalTime, inputs.experimentalJointAngles(:, i), 1); +% end directories = findFirstLevelSubDirectoriesFromPrefixes(fullfile( ... inputs.dataDirectory, "MAData"), prefixes); From 68a294e03e861a7dbae4a9ef1638b2cf2839f781 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Tue, 5 Sep 2023 17:47:02 -0500 Subject: [PATCH 056/365] synergy_driven to synergy --- src/DesignOptimization/DesignOptimization.m | 2 +- .../computeDesignOptimizationMainFunction.m | 2 +- .../getDesignOptimizationValueStruct.m | 2 +- .../calcSynergyBasedModeledValues.m | 2 +- .../computeTrackingOptimizationMainFunction.m | 2 +- .../getTrackingOptimizationValueStruct.m | 2 +- .../saveTrackingOptimizationResults.m | 2 +- .../gpops/makeGpopsValuesAsStruct.m | 10 +++++----- .../VerificationOptimization.m | 2 +- .../getVerificationOptimizationValueStruct.m | 2 +- .../parseVerificationOptimizationSettingsTree.m | 2 +- .../OptimalControlSetupFunctions/checkParameterGuess.m | 2 +- src/core/TreatmentOptimization/getSplines.m | 2 +- .../getTreatmentOptimizationValueStruct.m | 2 +- .../reportTreatmentOptimizationResults.m | 2 +- .../saveCommonOptimalControlResults.m | 2 +- .../updateMuscleModelProperties.m | 2 +- .../parse/parseTreatmentOptimizationDataDirectory.m | 8 ++++---- 18 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/DesignOptimization/DesignOptimization.m b/src/DesignOptimization/DesignOptimization.m index 33cfb0d46..98f33b265 100644 --- a/src/DesignOptimization/DesignOptimization.m +++ b/src/DesignOptimization/DesignOptimization.m @@ -31,7 +31,7 @@ function [output, inputs] = DesignOptimization(inputs, params) inputs = makeTreatmentOptimizationInputs(inputs, params); initializeMexOrMatlabParallelFunctions(inputs.mexModel); -if strcmp(inputs.controllerType, 'synergy_driven') +if strcmp(inputs.controllerType, 'synergy') inputs = setupMuscleSynergies(inputs); end if inputs.enableExternalTorqueControl diff --git a/src/DesignOptimization/computeDesignOptimizationMainFunction.m b/src/DesignOptimization/computeDesignOptimizationMainFunction.m index d4196160e..1ffbf145e 100644 --- a/src/DesignOptimization/computeDesignOptimizationMainFunction.m +++ b/src/DesignOptimization/computeDesignOptimizationMainFunction.m @@ -50,7 +50,7 @@ function bounds = setupProblemBounds(inputs, params, guess) bounds = setupCommonOptimalControlBounds(inputs, params); % setup parameter bounds -if strcmp(inputs.controllerType, 'synergy_driven') +if strcmp(inputs.controllerType, 'synergy') if inputs.optimizeSynergyVectors bounds.parameter.lower = -0.5 * ones(1, length(inputs.minParameter)); bounds.parameter.upper = 0.5 * ones(1, length(inputs.minParameter)); diff --git a/src/DesignOptimization/getDesignOptimizationValueStruct.m b/src/DesignOptimization/getDesignOptimizationValueStruct.m index dfab7f79a..f77cbcf88 100644 --- a/src/DesignOptimization/getDesignOptimizationValueStruct.m +++ b/src/DesignOptimization/getDesignOptimizationValueStruct.m @@ -36,7 +36,7 @@ values = getTreatmentOptimizationValueStruct(inputs, params); numParameters = 0; -if strcmp(params.controllerType, 'synergy_driven') +if strcmp(params.controllerType, 'synergy') if params.optimizeSynergyVectors values.synergyWeights = scaleToOriginal(inputs.parameter(1, ... 1 : params.numSynergyWeights), ... diff --git a/src/TrackingOptimization/calcSynergyBasedModeledValues.m b/src/TrackingOptimization/calcSynergyBasedModeledValues.m index 668a151ef..99641c30e 100644 --- a/src/TrackingOptimization/calcSynergyBasedModeledValues.m +++ b/src/TrackingOptimization/calcSynergyBasedModeledValues.m @@ -34,7 +34,7 @@ function modeledValues = calcSynergyBasedModeledValues(values, params, ... modeledValues) -if strcmp(params.controllerType, 'synergy_driven') +if strcmp(params.controllerType, 'synergy') [jointAngles, jointVelocities] = getMuscleActuatedDOFs(values, params); [params.muscleTendonLength, params.momentArms, ... params.muscleTendonVelocity] = calcSurrogateModel(params, ... diff --git a/src/TrackingOptimization/computeTrackingOptimizationMainFunction.m b/src/TrackingOptimization/computeTrackingOptimizationMainFunction.m index ba06489af..bc3f132c9 100644 --- a/src/TrackingOptimization/computeTrackingOptimizationMainFunction.m +++ b/src/TrackingOptimization/computeTrackingOptimizationMainFunction.m @@ -49,7 +49,7 @@ function bounds = setupProblemBounds(inputs, params) bounds = setupCommonOptimalControlBounds(inputs, params); % setup parameter bounds -if strcmp(inputs.controllerType, 'synergy_driven') +if strcmp(inputs.controllerType, 'synergy') if inputs.optimizeSynergyVectors bounds.parameter.lower = -0.5 * ones(1, length(inputs.minParameter)); bounds.parameter.upper = 0.5 * ones(1, length(inputs.minParameter)); diff --git a/src/TrackingOptimization/getTrackingOptimizationValueStruct.m b/src/TrackingOptimization/getTrackingOptimizationValueStruct.m index 98f83ea97..94d1aa931 100644 --- a/src/TrackingOptimization/getTrackingOptimizationValueStruct.m +++ b/src/TrackingOptimization/getTrackingOptimizationValueStruct.m @@ -31,7 +31,7 @@ function values = getTrackingOptimizationValueStruct(phase, params) values = getTreatmentOptimizationValueStruct(phase, params); -if strcmp(params.controllerType, 'synergy_driven') +if strcmp(params.controllerType, 'synergy') if params.optimizeSynergyVectors synergyWeights = scaleToOriginal(phase.parameter(1,:), ... params.maxParameter, params.minParameter); diff --git a/src/TrackingOptimization/saveTrackingOptimizationResults.m b/src/TrackingOptimization/saveTrackingOptimizationResults.m index 5850ee81c..5179cc5c1 100644 --- a/src/TrackingOptimization/saveTrackingOptimizationResults.m +++ b/src/TrackingOptimization/saveTrackingOptimizationResults.m @@ -31,7 +31,7 @@ function saveTrackingOptimizationResults(solution, inputs) values = getTrackingOptimizationValueStruct(solution.solution.phase, inputs); saveCommonOptimalControlResults(solution, inputs, values); -if strcmp(inputs.controllerType, 'synergy_driven') +if strcmp(inputs.controllerType, 'synergy') writeToSto(inputs.muscleLabels, linspace(1, inputs.numSynergies, ... inputs.numSynergies), [values.synergyWeights], ... fullfile(inputs.resultsDirectory, "parameterSolution.sto")); diff --git a/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m b/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m index af060a86b..3956c79f2 100644 --- a/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m +++ b/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m @@ -40,7 +40,7 @@ values.stateAccelerations = getCorrectStates(state, 3, inputs.numCoordinates); values.controlJerks = control(:, 1 : inputs.numCoordinates); -if ~strcmp(inputs.controllerType, 'synergy_driven') +if ~strcmp(inputs.controllerType, 'synergy') values.controlTorques = control(:, inputs.numCoordinates + 1 : ... inputs.numCoordinates + inputs.numTorqueControls); else @@ -48,8 +48,8 @@ inputs.numCoordinates + 1 : inputs.numCoordinates + inputs.numSynergies); end -if strcmp(inputs.toolName, "TreatmentOptimization") - if strcmp(inputs.controllerType, 'synergy_driven') +if strcmp(inputs.toolName, "TrackingOptimization") + if strcmp(inputs.controllerType, 'synergy') if inputs.optimizeSynergyVectors synergyWeights = scaleToOriginal(phase.parameter(1,:), ... inputs.maxParameter, inputs.minParameter); @@ -62,14 +62,14 @@ end end if strcmp(inputs.toolName, "VerificationOptimization") - if strcmp(inputs.controllerType, 'synergy_driven') + if strcmp(inputs.controllerType, 'synergy') values.synergyWeights = getSynergyWeightsFromGroups(... inputs.synergyWeightsGuess, inputs); end end if strcmp(inputs.toolName, "DesignOptimization") numParameters = 0; - if strcmp(inputs.controllerType, 'synergy_driven') + if strcmp(inputs.controllerType, 'synergy') if inputs.optimizeSynergyVectors values.synergyWeights = scaleToOriginal(phase.parameter(1, ... 1 : inputs.numSynergyWeights), ... diff --git a/src/VerificationOptimization/VerificationOptimization.m b/src/VerificationOptimization/VerificationOptimization.m index 1253f2f67..8652ddd03 100644 --- a/src/VerificationOptimization/VerificationOptimization.m +++ b/src/VerificationOptimization/VerificationOptimization.m @@ -31,7 +31,7 @@ function [output, inputs] = VerificationOptimization(inputs, params) inputs = makeTreatmentOptimizationInputs(inputs, params); initializeMexOrMatlabParallelFunctions(inputs.mexModel); -if strcmp(inputs.controllerType, 'synergy_driven') +if strcmp(inputs.controllerType, 'synergy') inputs = setupMuscleSynergies(inputs); end output = computeVerificationOptimizationMainFunction(inputs, params); diff --git a/src/VerificationOptimization/getVerificationOptimizationValueStruct.m b/src/VerificationOptimization/getVerificationOptimizationValueStruct.m index f3709571a..ce22ae998 100644 --- a/src/VerificationOptimization/getVerificationOptimizationValueStruct.m +++ b/src/VerificationOptimization/getVerificationOptimizationValueStruct.m @@ -31,7 +31,7 @@ function values = getVerificationOptimizationValueStruct(inputs, params) values = getTreatmentOptimizationValueStruct(inputs, params); -if strcmp(params.controllerType, 'synergy_driven') +if strcmp(params.controllerType, 'synergy') values.synergyWeights = getSynergyWeightsFromGroups(... params.synergyWeightsGuess, params); end diff --git a/src/VerificationOptimization/parseVerificationOptimizationSettingsTree.m b/src/VerificationOptimization/parseVerificationOptimizationSettingsTree.m index 63af7b9e9..a36ad6b0d 100644 --- a/src/VerificationOptimization/parseVerificationOptimizationSettingsTree.m +++ b/src/VerificationOptimization/parseVerificationOptimizationSettingsTree.m @@ -38,7 +38,7 @@ function inputs = getInputs(tree, inputs) import org.opensim.modeling.Storage -if strcmpi(inputs.controllerType, 'synergy_driven') +if strcmpi(inputs.controllerType, 'synergy') inputs.synergyWeights = parseTreatmentOptimizationStandard(... {getTextFromField(getFieldByName(tree, 'synergy_vectors_file'))}); end diff --git a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/checkParameterGuess.m b/src/core/TreatmentOptimization/OptimalControlSetupFunctions/checkParameterGuess.m index f7e38d727..74afcbd03 100644 --- a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/checkParameterGuess.m +++ b/src/core/TreatmentOptimization/OptimalControlSetupFunctions/checkParameterGuess.m @@ -61,7 +61,7 @@ end inputs.synergyWeightsGuess = synergyWeightsFlattened; end -if strcmp(inputs.controllerType, 'synergy_driven') +if strcmp(inputs.controllerType, 'synergy') inputs = getMuscleSynergiesInitialGuess(inputs); for i = 1 : length(inputs.coordinateNames) for j = 1 : length(inputs.surrogateModelCoordinateNames) diff --git a/src/core/TreatmentOptimization/getSplines.m b/src/core/TreatmentOptimization/getSplines.m index e2db99acd..b93fe255f 100644 --- a/src/core/TreatmentOptimization/getSplines.m +++ b/src/core/TreatmentOptimization/getSplines.m @@ -34,7 +34,7 @@ inputs.experimentalJointAngles', 0.0000001); inputs.splineJointMoments = spaps(inputs.experimentalTime/inputs.experimentalTime(end), ... inputs.experimentalJointMoments', 0.0000001); -if strcmp(inputs.controllerType, 'synergy_driven') +if strcmp(inputs.controllerType, 'synergy') inputs.splineMuscleActivations = spaps(inputs.experimentalTime/inputs.experimentalTime(end), ... inputs.experimentalMuscleActivations', 0.0000001); end diff --git a/src/core/TreatmentOptimization/getTreatmentOptimizationValueStruct.m b/src/core/TreatmentOptimization/getTreatmentOptimizationValueStruct.m index e1e829531..9aa58da17 100644 --- a/src/core/TreatmentOptimization/getTreatmentOptimizationValueStruct.m +++ b/src/core/TreatmentOptimization/getTreatmentOptimizationValueStruct.m @@ -43,7 +43,7 @@ values.stateAccelerations = getCorrectStates(state, 3, params.numCoordinates); values.controlJerks = control(:, 1 : params.numCoordinates); -if ~strcmp(params.controllerType, 'synergy_driven') +if ~strcmp(params.controllerType, 'synergy') values.controlTorques = control(:, params.numCoordinates + 1 : ... params.numCoordinates + params.numTorqueControls); else diff --git a/src/core/TreatmentOptimization/reportTreatmentOptimizationResults.m b/src/core/TreatmentOptimization/reportTreatmentOptimizationResults.m index bd8d38b01..5e14f8945 100644 --- a/src/core/TreatmentOptimization/reportTreatmentOptimizationResults.m +++ b/src/core/TreatmentOptimization/reportTreatmentOptimizationResults.m @@ -37,7 +37,7 @@ function reportTreatmentOptimizationResults(solution, inputs) end end values = getTreatmentOptimizationValueStruct(solution.solution.phase, inputs); -if strcmp(inputs.controllerType, 'synergy_driven') +if strcmp(inputs.controllerType, 'synergy') % plot Muscle Activations plotMuscleActivations(solution.muscleActivations, values.time, ... inputs.experimentalMuscleActivations, inputs.experimentalTime, ... diff --git a/src/core/TreatmentOptimization/saveCommonOptimalControlResults.m b/src/core/TreatmentOptimization/saveCommonOptimalControlResults.m index 6ca194c34..4fae99897 100644 --- a/src/core/TreatmentOptimization/saveCommonOptimalControlResults.m +++ b/src/core/TreatmentOptimization/saveCommonOptimalControlResults.m @@ -71,7 +71,7 @@ function saveCommonOptimalControlResults(solution, inputs, values) writeToSto(stateLabels, values.time, [values.statePositions ... values.stateVelocities values.stateAccelerations], ... fullfile(inputs.resultsDirectory, "statesSolution.sto")); -if strcmp(inputs.controllerType, 'synergy_driven') +if strcmp(inputs.controllerType, 'synergy') controlLabels = inputs.coordinateNames; for i = 1 : inputs.numSynergies controlLabels{end + 1} = strcat('synergy_activation', num2str(i)); diff --git a/src/core/TreatmentOptimization/updateMuscleModelProperties.m b/src/core/TreatmentOptimization/updateMuscleModelProperties.m index 42d9fa3ca..4422f2ad2 100644 --- a/src/core/TreatmentOptimization/updateMuscleModelProperties.m +++ b/src/core/TreatmentOptimization/updateMuscleModelProperties.m @@ -34,7 +34,7 @@ if ~isa(inputs.model, 'org.opensim.modeling.Model') inputs.model = Model(inputs.model); end -if strcmp(inputs.controllerType, 'synergy_driven') +if strcmp(inputs.controllerType, 'synergy') for i = 1 : inputs.numMuscles inputs.model.getForceSet().getMuscles().get(inputs.muscleNames(i)). ... setOptimalFiberLength(inputs.optimalFiberLength(i)); diff --git a/src/core/parse/parseTreatmentOptimizationDataDirectory.m b/src/core/parse/parseTreatmentOptimizationDataDirectory.m index 1862754b4..bc2372ce6 100644 --- a/src/core/parse/parseTreatmentOptimizationDataDirectory.m +++ b/src/core/parse/parseTreatmentOptimizationDataDirectory.m @@ -16,7 +16,7 @@ % National Institutes of Health (R01 EB030520). % % % % Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % +% Author(s): Marleny Vega, Claire V. Hammond % % % % Licensed under the Apache License, Version 2.0 (the "License"); % % you may not use this file except in compliance with the License. % @@ -62,7 +62,7 @@ inputs.grfFileName = findFileListFromPrefixList(... directory, "groundReactions"); end - if strcmp(inputs.controllerType, 'synergy_driven') + if strcmp(inputs.controllerType, 'synergy') [inputs.experimentalMuscleActivations, inputs.muscleLabels] = ... parseTreatmentOptimizationData(directory, 'muscleActivations', model); end @@ -84,14 +84,14 @@ inputs.grfFileName = findFileListFromPrefixList(... fullfile(dataDirectory, "GRFData"), prefix); end - if strcmp(inputs.controllerType, 'synergy_driven') + if strcmp(inputs.controllerType, 'synergy') directory = findFirstLevelSubDirectoriesFromPrefixes(dataDirectory, "ActData"); [inputs.experimentalMuscleActivations, inputs.muscleLabels] = ... parseTreatmentOptimizationData(directory, prefix, model); end end -if strcmp(inputs.controllerType, 'synergy_driven') +if strcmp(inputs.controllerType, 'synergy') directories = findFirstLevelSubDirectoriesFromPrefixes(fullfile( ... dataDirectory, "MAData"), prefix); inputs.momentArms = parseSelectMomentArms(directories, ... From 210a3c171b6db2c7b123e01422d119c9841a1bd4 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Tue, 5 Sep 2023 17:50:06 -0500 Subject: [PATCH 057/365] update element names --- src/core/parse/parseInitialGuess.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/parse/parseInitialGuess.m b/src/core/parse/parseInitialGuess.m index da96642f7..5ebbe4617 100644 --- a/src/core/parse/parseInitialGuess.m +++ b/src/core/parse/parseInitialGuess.m @@ -51,7 +51,7 @@ end elseif strcmp(controllerType, "synergy") controlsFileName = getTextFromField(getFieldByNameOrAlternate(tree, ... - 'initial_controls_file', '')); + 'initial_synergy_controls_file', '')); if ~isempty(controlsFileName) initialGuess.controlLabels = getStorageColumnNames(Storage( ... {controlsFileName})); @@ -59,7 +59,7 @@ {controlsFileName}); end parametersFileName = getTextFromField(getFieldByNameOrAlternate(tree, ... - 'initial_parameters_file', '')); + 'initial_synergy_vectors_file', '')); if ~isempty(parametersFileName) initialGuess.parameterLabels = getStorageColumnNames( ... Storage({parametersFileName})); From 4ba69284cd392a07afce75b248e560d7af2274c9 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Tue, 5 Sep 2023 17:50:19 -0500 Subject: [PATCH 058/365] fix constraint name bug --- src/core/TreatmentOptimization/generateConstraintTermStruct.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/TreatmentOptimization/generateConstraintTermStruct.m b/src/core/TreatmentOptimization/generateConstraintTermStruct.m index 7098f9b31..f2bd2459f 100644 --- a/src/core/TreatmentOptimization/generateConstraintTermStruct.m +++ b/src/core/TreatmentOptimization/generateConstraintTermStruct.m @@ -266,7 +266,7 @@ constraintTermCalculations.synergy_weight_sum = @(values, modeledValues, auxdata, constraintTerm) ... calcSynergyWeightsSum(... values.synergyWeights, ... - params.synergyGroups, ... + auxdata.synergyGroups, ... constraintTerm.synergy_group); end \ No newline at end of file From b7ebd346685aeaca43b1b318af5dd1827777ce60 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Tue, 5 Sep 2023 17:50:58 -0500 Subject: [PATCH 059/365] first pass of synergy TO --- .../calcTorqueBasedModeledValues.m | 2 +- .../gpops/calcGpopsConstraint.m | 20 ++--- .../gpops/computeGpopsContinuousFunction.m | 6 +- .../gpops/computeGpopsEndpointFunction.m | 12 ++- .../convertFromGpopsSynergyDrivenOutputs.m | 43 ++++++++++ .../gpops/convertToGpopsSynergyDrivenInputs.m | 84 +++++++++++++++++++ .../setupCommonOptimalControlInitialGuess.m | 3 +- .../getDesignVariableInputBounds.m | 6 +- 8 files changed, 155 insertions(+), 21 deletions(-) create mode 100644 src/TreatmentOptimization/gpops/convertFromGpopsSynergyDrivenOutputs.m create mode 100644 src/TreatmentOptimization/gpops/convertToGpopsSynergyDrivenInputs.m diff --git a/src/TrackingOptimization/calcTorqueBasedModeledValues.m b/src/TrackingOptimization/calcTorqueBasedModeledValues.m index c4121e0b8..b900b2f16 100644 --- a/src/TrackingOptimization/calcTorqueBasedModeledValues.m +++ b/src/TrackingOptimization/calcTorqueBasedModeledValues.m @@ -32,7 +32,7 @@ function modeledValues = calcTorqueBasedModeledValues(values, inputs) appliedLoads = [zeros(length(values.time), ... - model.getForceSet().getMuscles().getSize())]; + inputs.model.getForceSet().getMuscles().getSize())]; if ~isempty(inputs.contactSurfaces) clear pointKinematics [springPositions, springVelocities] = getSpringLocations( ... diff --git a/src/TreatmentOptimization/gpops/calcGpopsConstraint.m b/src/TreatmentOptimization/gpops/calcGpopsConstraint.m index d0525261d..217ba5a34 100644 --- a/src/TreatmentOptimization/gpops/calcGpopsConstraint.m +++ b/src/TreatmentOptimization/gpops/calcGpopsConstraint.m @@ -27,24 +27,24 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function path = calcGpopsConstraint(constraintTermCalculations, ... - allowedTypes, values, modeledValues, inputs) -path = []; -for i = 1:length(inputs.path) - constraintTerm = inputs.path{i}; +function constraint = calcGpopsConstraint(constraints, ... + constraintTermCalculations, allowedTypes, values, modeledValues, ... + inputs) +constraint = []; +for i = 1:length(constraints) + constraintTerm = constraints{i}; if constraintTerm.isEnabled if isfield(constraintTermCalculations, constraintTerm.type) && ... any(ismember(allowedTypes, constraintTerm.type)) fn = constraintTermCalculations.(constraintTerm.type); - path = cat(2, path, ... + constraint = cat(2, constraint, ... fn(values, modeledValues, inputs, constraintTerm)); else - throw(MException('ConstraintTerms:IllegalTerm', ... - strcat("Constraint term ", constraintTerm.type, ... - " is not allowed for this tool"))) +% throw(MException('ConstraintTerms:IllegalTerm', ... +% strcat("Constraint term ", constraintTerm.type, ... +% " is not allowed for this tool"))) end end end -path = scaleToBounds(path, inputs.maxPath, inputs.minPath); end diff --git a/src/TreatmentOptimization/gpops/computeGpopsContinuousFunction.m b/src/TreatmentOptimization/gpops/computeGpopsContinuousFunction.m index 9a8171c88..29ca7dcfc 100644 --- a/src/TreatmentOptimization/gpops/computeGpopsContinuousFunction.m +++ b/src/TreatmentOptimization/gpops/computeGpopsContinuousFunction.m @@ -42,8 +42,10 @@ generateConstraintTermStruct("path", ... setup.auxdata.controllerType, setup.auxdata.toolName); modeledValues.path = calcGpopsConstraint( ... - constraintTermCalculations, allowedTypes, values, ... - modeledValues, setup.auxdata); + setup.auxdata.path, constraintTermCalculations, allowedTypes, ... + values, modeledValues, setup.auxdata); + modeledValues.path = scaleToBounds( ... + modeledValues.path, setup.auxdata.maxPath, setup.auxdata.minPath); end modeledValues.integrand = calcGpopsIntegrand(values, modeledValues, setup.auxdata); end diff --git a/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m b/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m index 9bf416b10..9a7fef709 100644 --- a/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m +++ b/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m @@ -41,15 +41,21 @@ setup = updateSystemFromUserDefinedFunctions(setup, values); end modeledValues = calcTorqueBasedModeledValues(values, setup.auxdata); + modeledValues = calcSynergyBasedModeledValues(values, setup.auxdata, ... + modeledValues); end if ~isempty(setup.auxdata.terminal) [constraintTermCalculations, allowedTypes] = ... generateConstraintTermStruct("terminal", ... setup.auxdata.controllerType, setup.auxdata.toolName); - output.eventgroup.event = ... - calcGpopsConstraint(constraintTermCalculations, allowedTypes, ... - values, modeledValues, setup.auxdata); + event = ... + calcGpopsConstraint(setup.auxdata.terminal, ... + constraintTermCalculations, allowedTypes, values, ... + modeledValues, setup.auxdata); + if ~isempty(event) + output.eventgroup.event = event; + end end if strcmp(setup.auxdata.toolName, "DesignOptimization") diff --git a/src/TreatmentOptimization/gpops/convertFromGpopsSynergyDrivenOutputs.m b/src/TreatmentOptimization/gpops/convertFromGpopsSynergyDrivenOutputs.m new file mode 100644 index 000000000..f9346ed79 --- /dev/null +++ b/src/TreatmentOptimization/gpops/convertFromGpopsSynergyDrivenOutputs.m @@ -0,0 +1,43 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Claire V. Hammond % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function output = convertFromGpopsSynergyDrivenOutputs(solution, ... + inputs, params) +solution = solution.result.solution; +solution.auxdata = inputs; +if strcmp(inputs.toolName, "DesignOptimization") + if isfield(solution, 'parameter') + solution.phase.parameter = [solution.parameter]; + end +else + if inputs.optimizeSynergyVectors + solution.phase.parameter = solution.parameter; + end +end +output = computeGpopsContinuousFunction(solution); +output.solution = solution; +end + diff --git a/src/TreatmentOptimization/gpops/convertToGpopsSynergyDrivenInputs.m b/src/TreatmentOptimization/gpops/convertToGpopsSynergyDrivenInputs.m new file mode 100644 index 000000000..f7c221be9 --- /dev/null +++ b/src/TreatmentOptimization/gpops/convertToGpopsSynergyDrivenInputs.m @@ -0,0 +1,84 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Claire V. Hammond % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function setup = convertToGpopsSynergyDrivenInputs(inputs, params) +bounds = setupProblemBounds(inputs, params); +guess = setupCommonOptimalControlInitialGuess(inputs); +if strcmp(inputs.toolName, "DesignOptimization") + guess = addUserDefinedTermsToGuess(guess, inputs); +end +initializeMexOrMatlabParallelFunctions(inputs.mexModel); +setup = setupGpopsSettings(inputs, ... + bounds, guess, params, ... + @computeGpopsContinuousFunction, ... + @computeGpopsEndpointFunction); +checkInitialGuess(guess, inputs, ... + @computeGpopsContinuousFunction); +end + +function bounds = setupProblemBounds(inputs, params) +bounds = setupCommonOptimalControlBounds(inputs, params); +% setup parameter bounds +if strcmp(inputs.controllerType, 'synergy') + if inputs.optimizeSynergyVectors + bounds.parameter.lower = -0.5 * ones(1, length(inputs.minParameter)); + bounds.parameter.upper = 0.5 * ones(1, length(inputs.minParameter)); + end +end +if strcmp(inputs.toolName, "DesignOptimization") + for i = 1:length(inputs.userDefinedVariables) + variable = inputs.userDefinedVariables{i}; + if ~isfield(bounds, "parameter") || ... + ~isfield(bounds.parameter, "lower") + bounds.parameter.lower = [-0.5]; + bounds.parameter.upper = [0.5]; + else + bounds.parameter.lower = [bounds.parameter.lower, ... + -0.5]; + bounds.parameter.upper = [bounds.parameter.upper, ... + 0.5]; + end + end + if isfield(inputs, "finalTimeRange") + bounds.phase.finaltime.lower = guess.phase.time(end) - (0.5 - guess.phase.time(end)); + bounds.phase.finaltime.upper = 0.5; + end +end +end + +function guess = addUserDefinedTermsToGuess(guess, inputs) +for i = 1:length(inputs.userDefinedVariables) + variable = inputs.userDefinedVariables{i}; + if ~isfield(guess, "parameter") + guess.parameter = []; + end + guess.parameter = [guess.parameter, ... + scaleToBounds( ... + variable.initial_values, ... + variable.upper_bounds, ... + variable.lower_bounds)]; +end +end diff --git a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlInitialGuess.m b/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlInitialGuess.m index 3c7d884eb..4058922f4 100644 --- a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlInitialGuess.m +++ b/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlInitialGuess.m @@ -51,8 +51,7 @@ guess.phase.control = scaleToBounds([inputs.experimentalJointJerks ... inputs.synergyActivationsGuess], inputs.maxControl, inputs.minControl); end - if isfield(inputs, "optimizeSynergyVectors") && ... - inputs.optimizeSynergyVectors + if inputs.optimizeSynergyVectors guess.parameter = scaleToBounds(inputs.synergyWeightsGuess, ... inputs.maxParameter, inputs.minParameter); end diff --git a/src/core/TreatmentOptimization/SetupBounds/getDesignVariableInputBounds.m b/src/core/TreatmentOptimization/SetupBounds/getDesignVariableInputBounds.m index f78a15c26..b6dd9b7e3 100644 --- a/src/core/TreatmentOptimization/SetupBounds/getDesignVariableInputBounds.m +++ b/src/core/TreatmentOptimization/SetupBounds/getDesignVariableInputBounds.m @@ -68,9 +68,9 @@ inputs.minControl = [minControlJerks zeros(1, inputs.numSynergies)]; if inputs.optimizeSynergyVectors - inputs.maxParameter = inputs.maxParameterSynergyWeights * ... - ones(1, inputs.numSynergyWeights); - inputs.minParameter = zeros(1, inputs.numSynergyWeights); + inputs.maxParameter = inputs.maxParameterSynergyWeights * ... + ones(1, inputs.numSynergyWeights); + inputs.minParameter = zeros(1, inputs.numSynergyWeights); end elseif strcmp(inputs.controllerType, 'torque') for i = 1:length(inputs.controlTorqueNames) From af0653e5e439853fa41b58608b5bbf0b9a808cd6 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Wed, 6 Sep 2023 02:32:18 -0500 Subject: [PATCH 060/365] added license --- .../parse/parseElementTextByNameOrAlternate.m | 49 ++++++++++++++----- 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/src/core/parse/parseElementTextByNameOrAlternate.m b/src/core/parse/parseElementTextByNameOrAlternate.m index 67367ab5d..0fcc20ea4 100644 --- a/src/core/parse/parseElementTextByNameOrAlternate.m +++ b/src/core/parse/parseElementTextByNameOrAlternate.m @@ -1,18 +1,45 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% (struct, string, string) -> (string) +% Returns text or alternate + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Claire V. Hammond % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + function textOrAlternate = parseElementTextByNameOrAlternate(tree, ... - elementStringOrStringArray, alternate) - try -elements = elementStringOrStringArray; + elementStringOrStringArray, alternate) +try + elements = elementStringOrStringArray; if ischar(elementStringOrStringArray) || isstring(elementStringOrStringArray) elements = string([elementStringOrStringArray]); end for i = 1:length(elements) - if ~strcmp(elements(i), elements(end)) - tree = getFieldByNameOrError(tree, elements(i)); - else - textOrAlternate = getFieldByNameOrError(tree, elements(i)).Text; - end - end - catch - textOrAlternate = alternate; + if ~strcmp(elements(i), elements(end)) + tree = getFieldByNameOrError(tree, elements(i)); + else + textOrAlternate = getFieldByNameOrError(tree, elements(i)).Text; + end end +catch + textOrAlternate = alternate; +end end From aaf7fc2033fc2bf7bf915d3820d2c56d5513fd5e Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Wed, 6 Sep 2023 02:32:43 -0500 Subject: [PATCH 061/365] Create parseTrialData.m --- src/core/parse/parseTrialData.m | 56 +++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 src/core/parse/parseTrialData.m diff --git a/src/core/parse/parseTrialData.m b/src/core/parse/parseTrialData.m new file mode 100644 index 000000000..66485b30d --- /dev/null +++ b/src/core/parse/parseTrialData.m @@ -0,0 +1,56 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function looks in the given directory for all files that match the +% trialName, if there is only one, parse the file into data and data labels +% +% (string, string, Model) -> (2D matrix of number, 1D array of string) +% returns a 3D matrix of the loaded muscle tendon length data + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Claire V. Hammond % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function [data, dataLabels, time] = parseTrialData(directory, ... + trialName, model) +files = findDirectoryFileNames(directory); +matchedFiles = []; +for i = 1:length(files) + [~, name, ~] = fileparts(files(i)); + if contains(name, trialName) + matchedFiles(end + 1) = i; + end +end +if isempty(matchedFiles) + throw(MException("ParseError:TrialNameFile", ... + strcat("Cannot find file: ", trialName, " in directory ", ... + strrep(directory, '\', '\\')))) +end +if length(matchedFiles) ~= 1 + throw(MException("ParseError:TrialNameFile", ... + strcat("Multiple files contain: ", trialName, " in directory ", ... + strrep(directory, '\', '\\')))) +end +[dataLabels, time, data] = parseMotToComponents(model, ... + org.opensim.modeling.Storage(files(matchedFiles(1)))); +data = data'; +time = time'; +end + From c37ff9a93ce97b4e457d57cce7158a536a298fd6 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Wed, 6 Sep 2023 02:32:48 -0500 Subject: [PATCH 062/365] Create parseTrialName.m --- src/core/parse/parseTrialName.m | 34 +++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 src/core/parse/parseTrialName.m diff --git a/src/core/parse/parseTrialName.m b/src/core/parse/parseTrialName.m new file mode 100644 index 000000000..485baa353 --- /dev/null +++ b/src/core/parse/parseTrialName.m @@ -0,0 +1,34 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% trialName indicates which trial is used for Treatment Optimization and +% this indicates which file should be parsed from the various data sources. +% +% (struct) -> (string) +% parses the element + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Claire V. Hammond % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function trialName = parseTrialName(tree) +trialName = getFieldByNameOrError(tree, "trial_name").Text; +end + From 4b71abc9aad57746296c7165a5ab6bd84eced9d1 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Wed, 6 Sep 2023 02:33:13 -0500 Subject: [PATCH 063/365] add error on no osimx --- .../parse/parseTreatmentOptimizationInputs.m | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/core/parse/parseTreatmentOptimizationInputs.m b/src/core/parse/parseTreatmentOptimizationInputs.m index 038eb68d9..0269200ff 100644 --- a/src/core/parse/parseTreatmentOptimizationInputs.m +++ b/src/core/parse/parseTreatmentOptimizationInputs.m @@ -36,11 +36,10 @@ if(isempty(inputs.resultsDirectory)); inputs.resultsDirectory = pwd; end inputs.controllerType = parseControllerType(tree); inputs = parseModel(tree, inputs); -inputs.osimx = parseOsimxFile(getTextFromField(getFieldByName(tree, ... - 'input_osimx_file')), inputs.model); +inputs.osimx = parseOsimxFileWithCondition(tree, inputs); inputs = parseController(tree, inputs); inputs = parseTreatmentOptimizationDataDirectory(tree, inputs); -inputs.initialGuess = parseInitialGuess(tree, inputs.controllerType); +inputs.initialGuess = parseInitialGuess(inputs); inputs = parseOptimalControlSolverSettings( ... getTextFromField(getFieldByNameOrError(tree, ... 'optimal_control_solver_settings_file')), inputs); @@ -51,3 +50,20 @@ .RCNLConstraintTerm, inputs.controllerType, inputs.toolName); inputs.contactSurfaces = parseGroundContactSurfaces(inputs); end + +function osimx = parseOsimxFileWithCondition(tree, inputs) +osimxFileName = parseTextOrAlternate(tree, "input_osimx_file", ""); +osimx = parseOsimxFile(osimxFileName, inputs.model); +if strcmp(inputs.controllerType, "synergy") + if strcmp(osimxFileName, "") + throw(MException("", ... + strcat(" must be specified", ... + " for "))) + end + if ~isfield(osimx, "synergyGroups") + throw(MException("", ... + strcat(" must be specified in the", ... + " osimx file for . Have you run NCP yet?"))) + end +end +end \ No newline at end of file From dbeeceda7d09ec077522d0067818e27337eb6e63 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Wed, 6 Sep 2023 02:33:32 -0500 Subject: [PATCH 064/365] update name, later add parameter check --- ...uess.m => checkControlAndParameterGuess.m} | 40 ++++++++++++++----- .../makeTreatmentOptimizationInputs.m | 2 +- 2 files changed, 31 insertions(+), 11 deletions(-) rename src/core/TreatmentOptimization/OptimalControlSetupFunctions/{checkControlGuess.m => checkControlAndParameterGuess.m} (54%) diff --git a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/checkControlGuess.m b/src/core/TreatmentOptimization/OptimalControlSetupFunctions/checkControlAndParameterGuess.m similarity index 54% rename from src/core/TreatmentOptimization/OptimalControlSetupFunctions/checkControlGuess.m rename to src/core/TreatmentOptimization/OptimalControlSetupFunctions/checkControlAndParameterGuess.m index d91d6e5b2..c895d9df1 100644 --- a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/checkControlGuess.m +++ b/src/core/TreatmentOptimization/OptimalControlSetupFunctions/checkControlAndParameterGuess.m @@ -1,10 +1,10 @@ % This function is part of the NMSM Pipeline, see file for full license. % -% This function checks that the initial guess control file is in the +% This function checks that the initial guess control file is in the % correct order % % (struct) -> (struct) -% +% % ----------------------------------------------------------------------- % % The NMSM Pipeline is a toolkit for model personalization and treatment % @@ -28,16 +28,36 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function inputs = checkControlGuess(inputs) +function inputs = checkControlAndParameterGuess(inputs) if isfield(inputs.initialGuess, 'control') - for i = 1 : inputs.numCoordinates - for k = 1 : length(inputs.initialGuess.controlLabels) - if strcmpi(inputs.coordinateNames(i), inputs.initialGuess.controlLabels(k)) - controlIndex(i) = k; + newControls = zeros(size(inputs.initialGuess.control, 1), 0); + newLabels = string([]); + for i = 1 : length(inputs.coordinateNames) + index = find(ismember(inputs.initialGuess.controlLabels, inputs.coordinateNames(i))); + if isempty(index) + newControls(:, end + 1) = zeros(size(inputs.initialGuess.control, 1), 1); + else + newControls(:, end + 1) = inputs.initialGuess.control(:, index); + end + newLabels(end + 1) = inputs.coordinateNames(i); + end + if strcmp(inputs.controllerType, "synergy") + for i = 1 : length(inputs.osimx.synergyGroups) + for j = 1 : inputs.osimx.synergyGroups{i}.numSynergies + index = find(ismember(inputs.initialGuess.controlLabels, ... + strcat(inputs.osimx.synergyGroups{i}.muscleGroupName, "_", num2str(j)))); + if isempty(index) + throw(MException("ParseError:SynergyCommands", ... + strcat("All synergy commands are required: ", ... + strcat(inputs.osimx.synergyGroups{i}.muscleGroupName, "_", num2str(j))))); + else + newControls(:, end + 1) = inputs.initialGuess.control(:, index); + end + newLabels(end + 1) = strcat(inputs.osimx.synergyGroups{i}.muscleGroupName, "_", num2str(j)); end - end + end end - inputs.initialGuess.control(:, 1:inputs.numCoordinates) = ... - inputs.initialGuess.control(:, controlIndex); + inputs.initialGuess.control = newControls; + inputs.initialGuess.controlLabels = newLabels; end end \ No newline at end of file diff --git a/src/core/TreatmentOptimization/makeTreatmentOptimizationInputs.m b/src/core/TreatmentOptimization/makeTreatmentOptimizationInputs.m index 381dcfe89..866f469a9 100644 --- a/src/core/TreatmentOptimization/makeTreatmentOptimizationInputs.m +++ b/src/core/TreatmentOptimization/makeTreatmentOptimizationInputs.m @@ -32,7 +32,7 @@ inputs = setupGroundContact(inputs); inputs = getSplines(inputs); inputs = checkStateGuess(inputs); -inputs = checkControlGuess(inputs); +inputs = checkControlAndParameterGuess(inputs); inputs = checkParameterGuess(inputs); inputs = getIntegralBounds(inputs); inputs = getPathConstraintBounds(inputs); From a7dce2f3f69929ddefa89cec8fabee5ff70fda92 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Wed, 6 Sep 2023 02:33:46 -0500 Subject: [PATCH 065/365] fix organization --- .../saveCommonOptimalControlResults.m | 98 +++++++++++-------- 1 file changed, 58 insertions(+), 40 deletions(-) diff --git a/src/core/TreatmentOptimization/saveCommonOptimalControlResults.m b/src/core/TreatmentOptimization/saveCommonOptimalControlResults.m index 4fae99897..43ddf16db 100644 --- a/src/core/TreatmentOptimization/saveCommonOptimalControlResults.m +++ b/src/core/TreatmentOptimization/saveCommonOptimalControlResults.m @@ -1,8 +1,8 @@ % This function is part of the NMSM Pipeline, see file for full license. % % This function saves and prints the unscaled results from all -% treatment optimization modules (tracking, verification, and design -% optimization. +% treatment optimization modules (tracking, verification, and design +% optimization. % % (struct, struct) -> (None) % Prints treatment optimization results @@ -30,37 +30,14 @@ % ----------------------------------------------------------------------- % function saveCommonOptimalControlResults(solution, inputs, values) -outputDirectory = fullfile(inputs.resultsDirectory, 'optimal'); -if ~exist(outputDirectory, 'dir') - mkdir(outputDirectory) -end -writeToSto(inputs.coordinateNames, values.time, values.statePositions, ... - fullfile(outputDirectory, "inverseKinematics.sto")); -writeToSto(inputs.inverseDynamicMomentLabels, values.time, ... - solution.inverseDynamicMoments, fullfile(outputDirectory, ... - "inverseDynamics.sto")); -groundContactLabels = []; -groundContactData = []; -for i = 1:length(inputs.contactSurfaces) - groundContactLabels = cat(2, groundContactLabels, ... - [inputs.contactSurfaces{i}.forceColumns, ... - inputs.contactSurfaces{i}.momentColumns, ... - inputs.contactSurfaces{i}.electricalCenterColumns]); - midfootSuperiorLocation = pointKinematics(values.time, ... - values.statePositions, values.stateVelocities, ... - inputs.contactSurfaces{i}.midfootSuperiorPointOnBody', ... - inputs.contactSurfaces{i}.midfootSuperiorBody, inputs.coordinateNames); - midfootSuperiorLocation(:, 2) = 0; - groundContactData = [groundContactData, ... - solution.groundReactionsLab.forces{i}, ... - solution.groundReactionsLab.moments{i}, ... - midfootSuperiorLocation]; -end -if ~isempty(groundContactData) - writeToSto(groundContactLabels, values.time, ... - groundContactData, fullfile(outputDirectory, ... - "groundReactions.sto")); +if ~exist(inputs.resultsDirectory, 'dir') + mkdir(inputs.resultsDirectory) end + +saveInverseKinematicsResults(inputs, values, inputs.resultsDirectory); +saveInverseDynamicsResults(solution, inputs, values, inputs.resultsDirectory); +saveGroundReactionResults(solution, inputs, values, inputs.resultsDirectory); + stateLabels = inputs.coordinateNames; for i = 1 : length(inputs.coordinateNames) stateLabels{end + 1} = strcat(inputs.coordinateNames{i}, '_u'); @@ -70,20 +47,16 @@ function saveCommonOptimalControlResults(solution, inputs, values) end writeToSto(stateLabels, values.time, [values.statePositions ... values.stateVelocities values.stateAccelerations], ... - fullfile(inputs.resultsDirectory, "statesSolution.sto")); + fullfile(inputs.resultsDirectory, strcat(inputs.trialName, "_states.sto"))); if strcmp(inputs.controllerType, 'synergy') controlLabels = inputs.coordinateNames; for i = 1 : inputs.numSynergies controlLabels{end + 1} = strcat('synergy_activation', num2str(i)); end - if isfield(values, "controlNeuralCommands") - commands = values.controlNeuralCommands; - else - commands = values.controlSynergyActivations; - end + commands = values.controlSynergyActivations; writeToSto(controlLabels, values.time, [values.controlJerks ... commands], fullfile(inputs.resultsDirectory, ... - "controlSolution.sto")); + strcat(inputs.trialName, "_synergyCommands.sto"))); elseif strcmp(inputs.controllerType, 'torque_driven') controlLabels = inputs.coordinateNames; for i = 1 : inputs.numTorqueControls @@ -91,7 +64,52 @@ function saveCommonOptimalControlResults(solution, inputs, values) end writeToSto(controlLabels, values.time, [values.controlJerks ... values.controlTorques], fullfile(inputs.resultsDirectory, ... - "controlSolution.sto")); + strcat(inputs.trialName, "_torqueControls.sto"))); end delete(inputs.mexModel); +end + +function saveInverseKinematicsResults(inputs, values, outputDirectory) +if ~exist(fullfile(outputDirectory, "IKData"), "dir") + mkdir(fullfile(outputDirectory, "IKData")) +end +writeToSto(inputs.coordinateNames, values.time, values.statePositions, ... + fullfile(outputDirectory, "IKData", strcat(inputs.trialName, ".sto"))); +end + +function saveInverseDynamicsResults(solution, inputs, values, outputDirectory) +if ~exist(fullfile(outputDirectory, "IDData"), "dir") + mkdir(fullfile(outputDirectory, "IDData")) +end +writeToSto(inputs.inverseDynamicMomentLabels, values.time, ... + solution.inverseDynamicMoments, fullfile(outputDirectory, "IDData", ... + strcat(inputs.trialName, ".sto"))); +end + +function saveGroundReactionResults(solution, inputs, values, outputDirectory) +groundContactLabels = []; +groundContactData = []; +for i = 1:length(inputs.contactSurfaces) + groundContactLabels = cat(2, groundContactLabels, ... + [inputs.contactSurfaces{i}.forceColumns, ... + inputs.contactSurfaces{i}.momentColumns, ... + inputs.contactSurfaces{i}.electricalCenterColumns]); + midfootSuperiorLocation = pointKinematics(values.time, ... + values.statePositions, values.stateVelocities, ... + inputs.contactSurfaces{i}.midfootSuperiorPointOnBody', ... + inputs.contactSurfaces{i}.midfootSuperiorBody, inputs.coordinateNames); + midfootSuperiorLocation(:, 2) = 0; + groundContactData = [groundContactData, ... + solution.groundReactionsLab.forces{i}, ... + solution.groundReactionsLab.moments{i}, ... + midfootSuperiorLocation]; +end +if ~isempty(groundContactData) + if ~exist(fullfile(outputDirectory, "GRFData"), "dir") + mkdir(fullfile(outputDirectory, "GRFData")) + end + writeToSto(groundContactLabels, values.time, ... + groundContactData, fullfile(inputs.resultsDirectory, "GRFData", ... + strcat(inputs.trialName, ".sto"))); +end end \ No newline at end of file From d3639bb92bbd2747396c1223d5b41450671b683e Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Wed, 6 Sep 2023 02:34:07 -0500 Subject: [PATCH 066/365] revamp parsing --- ...arseVerificationOptimizationSettingsTree.m | 8 -- .../parseTreatmentOptimizationDataDirectory.m | 91 +++++++------------ 2 files changed, 33 insertions(+), 66 deletions(-) diff --git a/src/VerificationOptimization/parseVerificationOptimizationSettingsTree.m b/src/VerificationOptimization/parseVerificationOptimizationSettingsTree.m index a36ad6b0d..972c6e9da 100644 --- a/src/VerificationOptimization/parseVerificationOptimizationSettingsTree.m +++ b/src/VerificationOptimization/parseVerificationOptimizationSettingsTree.m @@ -31,16 +31,8 @@ function [inputs, params] = ... parseVerificationOptimizationSettingsTree(settingsTree) inputs = parseTreatmentOptimizationInputs(settingsTree); -inputs = getInputs(settingsTree, inputs); params = parseTreatmentOptimizationParams(settingsTree); inputs = modifyModelForces(inputs); end -function inputs = getInputs(tree, inputs) -import org.opensim.modeling.Storage -if strcmpi(inputs.controllerType, 'synergy') -inputs.synergyWeights = parseTreatmentOptimizationStandard(... - {getTextFromField(getFieldByName(tree, 'synergy_vectors_file'))}); -end -end diff --git a/src/core/parse/parseTreatmentOptimizationDataDirectory.m b/src/core/parse/parseTreatmentOptimizationDataDirectory.m index bc2372ce6..9d165afe6 100644 --- a/src/core/parse/parseTreatmentOptimizationDataDirectory.m +++ b/src/core/parse/parseTreatmentOptimizationDataDirectory.m @@ -31,67 +31,28 @@ % ----------------------------------------------------------------------- % function inputs = parseTreatmentOptimizationDataDirectory(tree, inputs) -dataDirectory = parseDataDirectory(tree); -previousResultsDirectoryElement = getFieldByName(tree, 'previous_results_directory'); -if isstruct(previousResultsDirectoryElement) - previousResultsDirectory = previousResultsDirectoryElement.Text; -else - previousResultsDirectory = []; -end -if strcmp(previousResultsDirectory, "") - previousResultsDirectory = []; -end +[dataDirectory, inputs.previousResultsDirectory] = findDataDirectory(tree, inputs); +inputs.trialName = parseTrialName(tree); prefix = findPrefixes(tree, dataDirectory); - -if ~isempty(previousResultsDirectory) && ... - exist(fullfile(previousResultsDirectory, "optimal"), 'dir') - directory = findFirstLevelSubDirectoriesFromPrefixes( ... - previousResultsDirectory, "optimal"); - if ~isempty(directory) - model = Model(inputs.model); - [inputs.experimentalJointMoments, inputs.inverseDynamicMomentLabels] = ... - parseTreatmentOptimizationData(directory, 'inverseDynamics', model); - [inputs.experimentalJointAngles, inputs.coordinateNames] = ... - parseTreatmentOptimizationData(directory, 'inverseKinematics', model); - experimentalTime = parseTimeColumn(findFileListFromPrefixList(... - directory, "inverseKinematics"))'; - inputs.kinematicsFile = findFileListFromPrefixList(... - directory, "inverseKinematics"); - inputs.experimentalTime = experimentalTime - experimentalTime(1); - if exist(fullfile(dataDirectory, "groundReactions"), 'dir') - inputs.grfFileName = findFileListFromPrefixList(... - directory, "groundReactions"); - end - if strcmp(inputs.controllerType, 'synergy') - [inputs.experimentalMuscleActivations, inputs.muscleLabels] = ... - parseTreatmentOptimizationData(directory, 'muscleActivations', model); - end - end -else - directory = findFirstLevelSubDirectoriesFromPrefixes(dataDirectory, "IDData"); - model = Model(inputs.model); - [inputs.experimentalJointMoments, inputs.inverseDynamicMomentLabels] = ... - parseTreatmentOptimizationData(directory, prefix, model); - directory = findFirstLevelSubDirectoriesFromPrefixes(dataDirectory, "IKData"); - [inputs.experimentalJointAngles, inputs.coordinateNames] = ... - parseTreatmentOptimizationData(directory, prefix, model); - experimentalTime = parseTimeColumn(findFileListFromPrefixList(... - fullfile(dataDirectory, "IKData"), prefix))'; - inputs.kinematicsFile = findFileListFromPrefixList( ... - fullfile(dataDirectory, "IKData"), prefix); - inputs.experimentalTime = experimentalTime - experimentalTime(1); - if exist(fullfile(dataDirectory, "GRFData"), 'dir') - inputs.grfFileName = findFileListFromPrefixList(... - fullfile(dataDirectory, "GRFData"), prefix); - end - if strcmp(inputs.controllerType, 'synergy') - directory = findFirstLevelSubDirectoriesFromPrefixes(dataDirectory, "ActData"); - [inputs.experimentalMuscleActivations, inputs.muscleLabels] = ... - parseTreatmentOptimizationData(directory, prefix, model); - end +[inputs.experimentalJointMoments, ... + inputs.inverseDynamicMomentLabels] = ... + parseTrialData(fullfile(dataDirectory, "IDData"), ... + inputs.trialName, inputs.model); +[inputs.experimentalJointAngles, inputs.coordinateNames, ... + experimentalTime] = parseTrialData(... + fullfile(dataDirectory, "IKData"), inputs.trialName, inputs.model); +inputs.coordinateNames = cellstr(inputs.coordinateNames); +inputs.experimentalTime = experimentalTime - experimentalTime(1); +if exist(fullfile(dataDirectory, "GRFData"), 'dir') + inputs.grfFileName = findFileListFromPrefixList(... + fullfile(dataDirectory, "GRFData"), prefix); end - if strcmp(inputs.controllerType, 'synergy') + [inputs.experimentalMuscleActivations, inputs.muscleLabels] = ... + parseTrialData(inputs.previousResultsDirectory, ... + strcat(inputs.trialName, "_combinedActivations"), inputs.model); + inputs.synergyWeights = parseTrialData(inputs.previousResultsDirectory, ... + "synergyWeights", inputs.model); directories = findFirstLevelSubDirectoriesFromPrefixes(fullfile( ... dataDirectory, "MAData"), prefix); inputs.momentArms = parseSelectMomentArms(directories, ... @@ -101,4 +62,18 @@ inputs = getMuscleSpecificSurrogateModelData(inputs); end inputs.numCoordinates = size(inputs.experimentalJointAngles, 2); +end + +function [dataDirectory, previousResultsDirectory] = ... + findDataDirectory(tree, inputs) +dataDirectory = parseDataDirectory(tree); +previousResultsDirectory = ... + parseTextOrAlternate(tree, "previous_results_directory", ""); +if strcmp(previousResultsDirectory, "") && ... + strcmp(inputs.controllerType, 'synergy') + throw(MException("ParseError:RequiredElement", ... + strcat("Element required", ... + " for , this can be an NCP", ... + " or Treatment Optimization results directory"))) +end end \ No newline at end of file From 858cecff4f3fca70e25f04178b0e9300da1f173d Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Wed, 6 Sep 2023 02:34:55 -0500 Subject: [PATCH 067/365] organized initial guess parse --- src/core/parse/parseInitialGuess.m | 59 ++++++++++++------------------ 1 file changed, 23 insertions(+), 36 deletions(-) diff --git a/src/core/parse/parseInitialGuess.m b/src/core/parse/parseInitialGuess.m index 5ebbe4617..e1f47ddf1 100644 --- a/src/core/parse/parseInitialGuess.m +++ b/src/core/parse/parseInitialGuess.m @@ -28,44 +28,31 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function initialGuess = parseInitialGuess(tree, controllerType) +function initialGuess = parseInitialGuess(inputs) import org.opensim.modeling.Storage initialGuess = []; -statesFileName = getTextFromField(getFieldByNameOrAlternate(tree, ... - 'initial_states_file', '')); -if ~isempty(statesFileName) - initialGuess.time = parseTimeColumn({statesFileName})'; - initialGuess.stateLabels = getStorageColumnNames(Storage( ... - {statesFileName})); - initialGuess.state = parseTreatmentOptimizationStandard( ... - {statesFileName}); -end -if strcmp(controllerType, "torque") - controlsFileName = getTextFromField(getFieldByNameOrAlternate(tree, ... - 'initial_torque_controls_file', '')); - if ~isempty(controlsFileName) - initialGuess.controlLabels = getStorageColumnNames(Storage( ... - {controlsFileName})); - initialGuess.control = parseTreatmentOptimizationStandard( ... - {controlsFileName}); - end -elseif strcmp(controllerType, "synergy") - controlsFileName = getTextFromField(getFieldByNameOrAlternate(tree, ... - 'initial_synergy_controls_file', '')); - if ~isempty(controlsFileName) - initialGuess.controlLabels = getStorageColumnNames(Storage( ... - {controlsFileName})); - initialGuess.control = parseTreatmentOptimizationStandard( ... - {controlsFileName}); - end - parametersFileName = getTextFromField(getFieldByNameOrAlternate(tree, ... - 'initial_synergy_vectors_file', '')); - if ~isempty(parametersFileName) - initialGuess.parameterLabels = getStorageColumnNames( ... - Storage({parametersFileName})); - initialGuess.parameter = parseTreatmentOptimizationStandard( ... - {parametersFileName}); - end +try + [initialGuess.state, initialGuess.stateLabels, initialGuess.time] = ... + parseTrialData(inputs.previousResultsDirectory, ... + strcat(inputs.trialName, "_states"), inputs.model); +catch; end +if strcmp(inputs.controllerType, "torque") + try + [initialGuess.control, initialGuess.controlLabels] = ... + parseTrialData(inputs.previousResultsDirectory, ... + strcat(inputs.trialName, "_torqueControls"), inputs.model); + catch;end +elseif strcmp(inputs.controllerType, "synergy") + try + [initialGuess.control, initialGuess.controlLabels] = ... + parseTrialData(inputs.previousResultsDirectory, ... + strcat(inputs.trialName, "_synergyCommands"), inputs.model); + catch;end + try + [initialGuess.parameter, initialGuess.parameterLabels] = ... + parseTrialData(inputs.previousResultsDirectory, ... + "synergyWeights", inputs.model); + catch;end else throw(MException("IncorrectControllerType", ... "controllerType must be 'torque' or 'synergy'")); From d045fd6eb6758b9e1fed07e7ec53103ca71d0178 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Wed, 6 Sep 2023 02:35:02 -0500 Subject: [PATCH 068/365] changed save file names --- src/TrackingOptimization/saveTrackingOptimizationResults.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/TrackingOptimization/saveTrackingOptimizationResults.m b/src/TrackingOptimization/saveTrackingOptimizationResults.m index 5179cc5c1..4a155f1e7 100644 --- a/src/TrackingOptimization/saveTrackingOptimizationResults.m +++ b/src/TrackingOptimization/saveTrackingOptimizationResults.m @@ -34,9 +34,9 @@ function saveTrackingOptimizationResults(solution, inputs) if strcmp(inputs.controllerType, 'synergy') writeToSto(inputs.muscleLabels, linspace(1, inputs.numSynergies, ... inputs.numSynergies), [values.synergyWeights], ... - fullfile(inputs.resultsDirectory, "parameterSolution.sto")); + fullfile(inputs.resultsDirectory, "synergyWeights.sto")); writeToSto(inputs.muscleLabels, values.time, ... solution.muscleActivations, ... - fullfile(inputs.resultsDirectory, "optimal", "muscleActivations.sto")); + fullfile(inputs.resultsDirectory, strcat(inputs.trialName, "_combinedActivations.sto"))); end end \ No newline at end of file From 90e2f9be56b61e6aebd1f8d0e9e9ed4581a3d5fd Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Wed, 6 Sep 2023 03:00:05 -0500 Subject: [PATCH 069/365] fixed column names for activation --- .../saveCommonOptimalControlResults.m | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/core/TreatmentOptimization/saveCommonOptimalControlResults.m b/src/core/TreatmentOptimization/saveCommonOptimalControlResults.m index 43ddf16db..5fc6d2875 100644 --- a/src/core/TreatmentOptimization/saveCommonOptimalControlResults.m +++ b/src/core/TreatmentOptimization/saveCommonOptimalControlResults.m @@ -50,8 +50,12 @@ function saveCommonOptimalControlResults(solution, inputs, values) fullfile(inputs.resultsDirectory, strcat(inputs.trialName, "_states.sto"))); if strcmp(inputs.controllerType, 'synergy') controlLabels = inputs.coordinateNames; - for i = 1 : inputs.numSynergies - controlLabels{end + 1} = strcat('synergy_activation', num2str(i)); + for i = 1 : length(inputs.osimx.synergyGroups) + for j = 1 : inputs.osimx.synergyGroups{i}.numSynergies + controlLabels{end + 1} = convertStringsToChars( ... + strcat(inputs.osimx.synergyGroups{i}.muscleGroupName, ... + "_", num2str(j))); + end end commands = values.controlSynergyActivations; writeToSto(controlLabels, values.time, [values.controlJerks ... From b2a51b621f01215735d35d3644859143ccfc4d7e Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Wed, 6 Sep 2023 03:00:17 -0500 Subject: [PATCH 070/365] temp fix for time issue --- src/core/TreatmentOptimization/getSplines.m | 5 +++++ src/core/parse/parseTreatmentOptimizationDataDirectory.m | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/core/TreatmentOptimization/getSplines.m b/src/core/TreatmentOptimization/getSplines.m index b93fe255f..a6731e021 100644 --- a/src/core/TreatmentOptimization/getSplines.m +++ b/src/core/TreatmentOptimization/getSplines.m @@ -35,8 +35,13 @@ inputs.splineJointMoments = spaps(inputs.experimentalTime/inputs.experimentalTime(end), ... inputs.experimentalJointMoments', 0.0000001); if strcmp(inputs.controllerType, 'synergy') + try % if using NCP results, this works, if using Treatment Optimization, go to step 2 inputs.splineMuscleActivations = spaps(inputs.experimentalTime/inputs.experimentalTime(end), ... inputs.experimentalMuscleActivations', 0.0000001); + catch + inputs.splineMuscleActivations = spaps(inputs.muscleActivationsTime/inputs.experimentalTime(end), ... + inputs.experimentalMuscleActivations', 0.0000001); + end end for i = 1:length(inputs.contactSurfaces) inputs.splineExperimentalGroundReactionForces{i} = ... diff --git a/src/core/parse/parseTreatmentOptimizationDataDirectory.m b/src/core/parse/parseTreatmentOptimizationDataDirectory.m index 9d165afe6..5ac955a0f 100644 --- a/src/core/parse/parseTreatmentOptimizationDataDirectory.m +++ b/src/core/parse/parseTreatmentOptimizationDataDirectory.m @@ -48,7 +48,8 @@ fullfile(dataDirectory, "GRFData"), prefix); end if strcmp(inputs.controllerType, 'synergy') - [inputs.experimentalMuscleActivations, inputs.muscleLabels] = ... + [inputs.experimentalMuscleActivations, inputs.muscleLabels, ... + inputs.muscleActivationsTime] = ... parseTrialData(inputs.previousResultsDirectory, ... strcat(inputs.trialName, "_combinedActivations"), inputs.model); inputs.synergyWeights = parseTrialData(inputs.previousResultsDirectory, ... From 1e3e06b19ae16647229693ff82bc4996fe57a932 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Wed, 6 Sep 2023 03:00:32 -0500 Subject: [PATCH 071/365] move setupMuscleSynergies --- src/VerificationOptimization/VerificationOptimization.m | 7 +------ .../VerificationOptimizationTool.m | 9 +++++++++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/VerificationOptimization/VerificationOptimization.m b/src/VerificationOptimization/VerificationOptimization.m index 8652ddd03..48b047978 100644 --- a/src/VerificationOptimization/VerificationOptimization.m +++ b/src/VerificationOptimization/VerificationOptimization.m @@ -32,13 +32,8 @@ inputs = makeTreatmentOptimizationInputs(inputs, params); initializeMexOrMatlabParallelFunctions(inputs.mexModel); if strcmp(inputs.controllerType, 'synergy') - inputs = setupMuscleSynergies(inputs); end output = computeVerificationOptimizationMainFunction(inputs, params); end -function inputs = setupMuscleSynergies(inputs) -inputs.splineSynergyActivations = spaps(inputs.initialGuess.time/inputs.initialGuess.time(end), ... - inputs.initialGuess.control(:, inputs.numCoordinates + 1:end)', 0.0000001); -inputs.synergyLabels = inputs.initialGuess.controlLabels(:, inputs.numCoordinates + 1:end); -end + diff --git a/src/VerificationOptimization/VerificationOptimizationTool.m b/src/VerificationOptimization/VerificationOptimizationTool.m index 113ed4019..7dc9e4f66 100644 --- a/src/VerificationOptimization/VerificationOptimizationTool.m +++ b/src/VerificationOptimization/VerificationOptimizationTool.m @@ -34,7 +34,16 @@ function VerificationOptimizationTool(settingsFileName) verifyVersion(settingsTree, "VerificationOptimizationTool"); [inputs, params] = parseVerificationOptimizationSettingsTree(settingsTree); inputs = makeTreatmentOptimizationInputs(inputs, params); +inputs = setupMuscleSynergies(inputs); outputs = solveOptimalControlProblem(inputs, params); reportTreatmentOptimizationResults(outputs, inputs); saveVerificationOptimizationResults(outputs, inputs); end + +function inputs = setupMuscleSynergies(inputs) +if strcmp(inputs.controllerType, 'synergy') + inputs.splineSynergyActivations = spaps(inputs.initialGuess.time/inputs.initialGuess.time(end), ... + inputs.initialGuess.control(:, inputs.numCoordinates + 1:end)', 0.0000001); + inputs.synergyLabels = inputs.initialGuess.controlLabels(:, inputs.numCoordinates + 1:end); +end +end \ No newline at end of file From 102d3d7d4ec61946bca1461300734bda4a7fadb3 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Wed, 6 Sep 2023 15:21:42 -0500 Subject: [PATCH 072/365] Revert "temp fix for time issue" This reverts commit b2a51b621f01215735d35d3644859143ccfc4d7e. --- src/core/TreatmentOptimization/getSplines.m | 5 ----- src/core/parse/parseTreatmentOptimizationDataDirectory.m | 3 +-- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/core/TreatmentOptimization/getSplines.m b/src/core/TreatmentOptimization/getSplines.m index a6731e021..b93fe255f 100644 --- a/src/core/TreatmentOptimization/getSplines.m +++ b/src/core/TreatmentOptimization/getSplines.m @@ -35,13 +35,8 @@ inputs.splineJointMoments = spaps(inputs.experimentalTime/inputs.experimentalTime(end), ... inputs.experimentalJointMoments', 0.0000001); if strcmp(inputs.controllerType, 'synergy') - try % if using NCP results, this works, if using Treatment Optimization, go to step 2 inputs.splineMuscleActivations = spaps(inputs.experimentalTime/inputs.experimentalTime(end), ... inputs.experimentalMuscleActivations', 0.0000001); - catch - inputs.splineMuscleActivations = spaps(inputs.muscleActivationsTime/inputs.experimentalTime(end), ... - inputs.experimentalMuscleActivations', 0.0000001); - end end for i = 1:length(inputs.contactSurfaces) inputs.splineExperimentalGroundReactionForces{i} = ... diff --git a/src/core/parse/parseTreatmentOptimizationDataDirectory.m b/src/core/parse/parseTreatmentOptimizationDataDirectory.m index 5ac955a0f..9d165afe6 100644 --- a/src/core/parse/parseTreatmentOptimizationDataDirectory.m +++ b/src/core/parse/parseTreatmentOptimizationDataDirectory.m @@ -48,8 +48,7 @@ fullfile(dataDirectory, "GRFData"), prefix); end if strcmp(inputs.controllerType, 'synergy') - [inputs.experimentalMuscleActivations, inputs.muscleLabels, ... - inputs.muscleActivationsTime] = ... + [inputs.experimentalMuscleActivations, inputs.muscleLabels] = ... parseTrialData(inputs.previousResultsDirectory, ... strcat(inputs.trialName, "_combinedActivations"), inputs.model); inputs.synergyWeights = parseTrialData(inputs.previousResultsDirectory, ... From 3da0b2549b602e66e582d761245f085f0b2799b3 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Wed, 6 Sep 2023 15:52:55 -0500 Subject: [PATCH 073/365] add logic for previous_result_dir --- .../parseTreatmentOptimizationDataDirectory.m | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/core/parse/parseTreatmentOptimizationDataDirectory.m b/src/core/parse/parseTreatmentOptimizationDataDirectory.m index 9d165afe6..ab6e74b18 100644 --- a/src/core/parse/parseTreatmentOptimizationDataDirectory.m +++ b/src/core/parse/parseTreatmentOptimizationDataDirectory.m @@ -36,10 +36,12 @@ prefix = findPrefixes(tree, dataDirectory); [inputs.experimentalJointMoments, ... inputs.inverseDynamicMomentLabels] = ... - parseTrialData(fullfile(dataDirectory, "IDData"), ... - inputs.trialName, inputs.model); + parseTrialDataTryDirectories( ... + fullfile(inputs.previousResultsDirectory, "IDData"), ... + fullfile(dataDirectory, "IDData"), inputs.trialName, inputs.model); [inputs.experimentalJointAngles, inputs.coordinateNames, ... - experimentalTime] = parseTrialData(... + experimentalTime] = parseTrialDataTryDirectories( ... + fullfile(inputs.previousResultsDirectory, "IKData"), ... fullfile(dataDirectory, "IKData"), inputs.trialName, inputs.model); inputs.coordinateNames = cellstr(inputs.coordinateNames); inputs.experimentalTime = experimentalTime - experimentalTime(1); @@ -76,4 +78,19 @@ " for , this can be an NCP", ... " or Treatment Optimization results directory"))) end +end + +function [data, labels, time] = parseTrialDataTryDirectories( ... + previousResultsDirectory, dataDirectory, trialName, model) +if ~strcmp(previousResultsDirectory, "") + try + [data, labels, time] = parseTrialData(... + previousResultsDirectory, trialName, model); + catch + [data, labels, time] = parseTrialData( ... + dataDirectory, trialName, model); + end +else + [data, labels, time] = parseTrialData(dataDirectory, trialName, model); +end end \ No newline at end of file From 2778e78c7fc4d4d6203c191a7165049c87531300 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Wed, 6 Sep 2023 15:53:04 -0500 Subject: [PATCH 074/365] fixed argument name --- .../PathTerms/calcMuscleActuatedMomentsPathConstraints.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/TreatmentOptimization/PathTerms/calcMuscleActuatedMomentsPathConstraints.m b/src/core/TreatmentOptimization/PathTerms/calcMuscleActuatedMomentsPathConstraints.m index 396b5ec15..c802b49c1 100644 --- a/src/core/TreatmentOptimization/PathTerms/calcMuscleActuatedMomentsPathConstraints.m +++ b/src/core/TreatmentOptimization/PathTerms/calcMuscleActuatedMomentsPathConstraints.m @@ -29,11 +29,11 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function pathTerm = calcMuscleActuatedMomentsPathConstraints(params, ... +function pathTerm = calcMuscleActuatedMomentsPathConstraints(inputs, ... modeledValues, loadName) -indx1 = find(strcmp(convertCharsToStrings(params.inverseDynamicMomentLabels), ... +indx1 = find(strcmp(convertCharsToStrings(inputs.inverseDynamicMomentLabels), ... loadName)); -indx2 = find(strcmp(strcat(params.surrogateModelCoordinateNames, ... +indx2 = find(strcmp(strcat(inputs.surrogateModelCoordinateNames, ... '_moment'), loadName)); pathTerm = modeledValues.inverseDynamicMoments(:, indx1) - ... modeledValues.muscleJointMoments(:, indx2); From 1dfc7481c89d46866882b5f7d281582180aef213 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Wed, 6 Sep 2023 15:53:34 -0500 Subject: [PATCH 075/365] update DO for new xml --- src/DesignOptimization/DesignOptimizationTool.m | 9 +++++++++ .../parseDesignOptimizationSettingsTree.m | 4 ---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/DesignOptimization/DesignOptimizationTool.m b/src/DesignOptimization/DesignOptimizationTool.m index 4aaa4d9bf..e15101ed3 100644 --- a/src/DesignOptimization/DesignOptimizationTool.m +++ b/src/DesignOptimization/DesignOptimizationTool.m @@ -33,8 +33,17 @@ function DesignOptimizationTool(settingsFileName) settingsTree = xml2struct(settingsFileName); verifyVersion(settingsTree, "DesignOptimizationTool"); [inputs, params] = parseDesignOptimizationSettingsTree(settingsTree); +inputs = setupMuscleSynergies(inputs); inputs = makeTreatmentOptimizationInputs(inputs, params); outputs = solveOptimalControlProblem(inputs, params); reportTreatmentOptimizationResults(outputs, inputs); saveDesignOptimizationResults(outputs, inputs); end + +function inputs = setupMuscleSynergies(inputs) +if strcmp(inputs.controllerType, 'synergy') + inputs.splineSynergyActivations = spaps(inputs.initialGuess.time/inputs.initialGuess.time(end), ... + inputs.initialGuess.control(:, inputs.numCoordinates + 1:end)', 0.0000001); + inputs.synergyLabels = inputs.initialGuess.controlLabels(:, inputs.numCoordinates + 1:end); +end +end \ No newline at end of file diff --git a/src/DesignOptimization/parseDesignOptimizationSettingsTree.m b/src/DesignOptimization/parseDesignOptimizationSettingsTree.m index 57ce8fee8..5b65a5e16 100644 --- a/src/DesignOptimization/parseDesignOptimizationSettingsTree.m +++ b/src/DesignOptimization/parseDesignOptimizationSettingsTree.m @@ -40,10 +40,6 @@ function inputs = getInputs(tree, inputs) import org.opensim.modeling.Storage -if strcmpi(inputs.controllerType, 'synergy') -inputs.synergyWeights = parseTreatmentOptimizationStandard(... - {getTextFromField(getFieldByName(tree, 'synergy_vectors_file'))}); -end inputs.systemFns = parseSpaceSeparatedList(tree, "model_functions"); parameterTree = getFieldByNameOrError(tree, "RCNLParameterTermSet"); if isstruct(parameterTree) && isfield(parameterTree, "RCNLParameterTerm") From 81df986473ad5b1d98b7fadb171375c336abb76d Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Wed, 6 Sep 2023 15:53:47 -0500 Subject: [PATCH 076/365] move muscle synergies up --- src/VerificationOptimization/VerificationOptimizationTool.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/VerificationOptimization/VerificationOptimizationTool.m b/src/VerificationOptimization/VerificationOptimizationTool.m index 7dc9e4f66..201871504 100644 --- a/src/VerificationOptimization/VerificationOptimizationTool.m +++ b/src/VerificationOptimization/VerificationOptimizationTool.m @@ -33,8 +33,8 @@ function VerificationOptimizationTool(settingsFileName) settingsTree = xml2struct(settingsFileName); verifyVersion(settingsTree, "VerificationOptimizationTool"); [inputs, params] = parseVerificationOptimizationSettingsTree(settingsTree); -inputs = makeTreatmentOptimizationInputs(inputs, params); inputs = setupMuscleSynergies(inputs); +inputs = makeTreatmentOptimizationInputs(inputs, params); outputs = solveOptimalControlProblem(inputs, params); reportTreatmentOptimizationResults(outputs, inputs); saveVerificationOptimizationResults(outputs, inputs); From 06fdf7796cedbd972b531a967ca4df8186c226b2 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Thu, 7 Sep 2023 16:05:54 -0500 Subject: [PATCH 077/365] Save NCP combined activations at ncpResults level --- .../saveNeuralControlPersonalizationResults.m | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/NeuralControlPersonalization/saveNeuralControlPersonalizationResults.m b/src/NeuralControlPersonalization/saveNeuralControlPersonalizationResults.m index 6d6904986..bd1dbb5bd 100644 --- a/src/NeuralControlPersonalization/saveNeuralControlPersonalizationResults.m +++ b/src/NeuralControlPersonalization/saveNeuralControlPersonalizationResults.m @@ -4,9 +4,6 @@ function saveNeuralControlPersonalizationResults(synergyWeights, ... if ~exist(resultsDirectory, "dir") mkdir(resultsDirectory); end -if ~exist(fullfile(resultsDirectory, "combinedMuscleActivations")) - mkdir(fullfile(resultsDirectory, "combinedMuscleActivations")) -end if ~exist(fullfile(resultsDirectory, "modelMoments")) mkdir(fullfile(resultsDirectory, "modelMoments")) end @@ -36,7 +33,6 @@ function saveNeuralControlPersonalizationResults(synergyWeights, ... tempActivations, ... fullfile( ... resultsDirectory, ... - "combinedMuscleActivations", ... inputs.prefixes(i) + "_combinedActivations.sto" ... ) ... ) From ea0836bbc5fe8176592714e4ab3b81d41ef39f8f Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Thu, 7 Sep 2023 16:12:27 -0500 Subject: [PATCH 078/365] Save synergy commands with group column names --- .../saveNeuralControlPersonalizationResults.m | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/NeuralControlPersonalization/saveNeuralControlPersonalizationResults.m b/src/NeuralControlPersonalization/saveNeuralControlPersonalizationResults.m index bd1dbb5bd..463c0f34e 100644 --- a/src/NeuralControlPersonalization/saveNeuralControlPersonalizationResults.m +++ b/src/NeuralControlPersonalization/saveNeuralControlPersonalizationResults.m @@ -13,8 +13,13 @@ function saveNeuralControlPersonalizationResults(synergyWeights, ... synergyWeights, ... fullfile(resultsDirectory, "synergyWeights.sto")); commandColumns = []; -for i = 1 : size(synergyWeights, 1) - commandColumns = [commandColumns convertCharsToStrings(num2str(i))]; +for j = 1 : length(inputs.synergyGroups) + for i = 1 : inputs.synergyGroups{j}.numSynergies + commandColumns = [commandColumns ... + convertCharsToStrings( ... + inputs.synergyGroups{j}.muscleGroupName) + ... + "_" + convertCharsToStrings(num2str(i))]; + end end for i = 1 : size(synergyCommands, 1) writeToSto( ... From 8e68570c59c36604ed16e6763d90ac6c4ed17bfd Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Thu, 7 Sep 2023 18:08:16 -0500 Subject: [PATCH 079/365] first impl of new jmp elements --- .../JointModelPersonalization.m | 17 ++++++++++++----- .../functions/adjustBodyScaling.m | 19 ++++++++++++------- .../functions/makeScalingFunction.m | 5 +++-- ...rseJointModelPersonalizationSettingsTree.m | 10 +++++++++- 4 files changed, 36 insertions(+), 15 deletions(-) diff --git a/src/JointModelPersonalization/JointModelPersonalization.m b/src/JointModelPersonalization/JointModelPersonalization.m index 90e10b173..9a896b345 100644 --- a/src/JointModelPersonalization/JointModelPersonalization.m +++ b/src/JointModelPersonalization/JointModelPersonalization.m @@ -34,8 +34,9 @@ outputModel = Model(inputs.model); for i=1:length(inputs.tasks) functions = makeFunctions(inputs.tasks{i}.parameters, ... - inputs.tasks{i}.scaling, inputs.tasks{i}.markers); - params.markerNames = getMarkersOnJoints(outputModel, ... + inputs.tasks{i}.scaling, inputs.tasks{i}.markers, ... + inputs.tasks{i}.anatomicalMarkers); + params.markerNames = getMarkersInTask(outputModel, ... inputs.tasks{i}); taskParams = mergeStructs(inputs.tasks{i}, params); optimizedValues = computeKinematicCalibration(inputs.model, ... @@ -97,14 +98,16 @@ function verifyParam(params, fieldName, fn, message) end end -function functions = makeFunctions(parameters, scaling, markers) +function functions = makeFunctions(... + parameters, scaling, markers, anatomicalMarkers) functions = {}; for i=1:length(parameters) p = parameters{i}; functions{i} = makeJointFunction(p{1}, p{2}, p{3}, p{4}); end for i=1:length(scaling) - functions{end + 1} = makeScalingFunction(scaling(i)); + functions{end + 1} = makeScalingFunction( ... + scaling(i), anatomicalMarkers); end for i=1:length(markers) marker = markers{i}; @@ -122,8 +125,12 @@ function verifyParam(params, fieldName, fn, message) end end -function markerNames = getMarkersOnJoints(model, task) +function markerNames = getMarkersInTask(model, task) import org.opensim.modeling.* +if isfield(task, "markerNames") + markerNames = task.markerNames; + return +end parameters = task.parameters; bodies = task.scaling; markerNames = {}; diff --git a/src/JointModelPersonalization/KinematicCalibration/functions/adjustBodyScaling.m b/src/JointModelPersonalization/KinematicCalibration/functions/adjustBodyScaling.m index 5d7beef86..15e17367c 100644 --- a/src/JointModelPersonalization/KinematicCalibration/functions/adjustBodyScaling.m +++ b/src/JointModelPersonalization/KinematicCalibration/functions/adjustBodyScaling.m @@ -29,12 +29,15 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function adjustBodyScaling(model, bodyName, value) +function adjustBodyScaling(model, bodyName, value, anatomicalMarkers) -markers = getMarkersFromBody(model, bodyName); -markerLocations = {}; -for i = 1:length(markers) - markerLocations{i} = org.opensim.modeling.Vec3(model.getMarkerSet().get(markers{i}).get_location()); +if ~anatomicalMarkers + markers = getMarkersFromBody(model, bodyName); + markerLocations = {}; + for i = 1:length(markers) + markerLocations{i} = org.opensim.modeling.Vec3( ... + model.getMarkerSet().get(markers{i}).get_location()); + end end state = initializeState(model); @@ -48,8 +51,10 @@ function adjustBodyScaling(model, bodyName, value) scaleSet.cloneAndAppend(scale); model.scale(state, scaleSet, true, -1.0); -for i = 1:length(markers) - model.getMarkerSet().get(markers{i}).set_location(markerLocations{i}); +if ~anatomicalMarkers + for i = 1:length(markers) + model.getMarkerSet().get(markers{i}).set_location(markerLocations{i}); + end end end diff --git a/src/JointModelPersonalization/KinematicCalibration/functions/makeScalingFunction.m b/src/JointModelPersonalization/KinematicCalibration/functions/makeScalingFunction.m index 3308cca4b..374b2016c 100644 --- a/src/JointModelPersonalization/KinematicCalibration/functions/makeScalingFunction.m +++ b/src/JointModelPersonalization/KinematicCalibration/functions/makeScalingFunction.m @@ -28,6 +28,7 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function fn = makeScalingFunction(bodyName) -fn = @(value, model) adjustBodyScaling(model, bodyName, value); +function fn = makeScalingFunction(bodyName, anatomicalMarkers) +fn = @(value, model) adjustBodyScaling( ... + model, bodyName, value, anatomicalMarkers); end diff --git a/src/JointModelPersonalization/parseJointModelPersonalizationSettingsTree.m b/src/JointModelPersonalization/parseJointModelPersonalizationSettingsTree.m index f0c9199f2..97cb2e4a4 100644 --- a/src/JointModelPersonalization/parseJointModelPersonalizationSettingsTree.m +++ b/src/JointModelPersonalization/parseJointModelPersonalizationSettingsTree.m @@ -80,13 +80,21 @@ end function output = getTask(model, tree) -output.markerFile = tree.marker_file_name.Text; +output.markerFile = parseElementTextByName(tree, "marker_file_name"); +output.anatomicalMarkers = getBooleanLogicFromField( ... + getFieldByName(tree, "anatomical_markers")); timeRange = getFieldByName(tree, 'time_range'); if(isstruct(timeRange)) timeRange = strsplit(timeRange.Text, ' '); output.startTime = str2double(timeRange{1}); output.finishTime = str2double(timeRange{2}); end +try +markerNames = parseSpaceSeparatedList(tree, "marker_names"); +if ~isempty(markerNames) + output.markerNames = markerNames; +end +catch; end output.parameters = {}; if(isstruct(getFieldByName(tree, "JMPJointSet"))) output.parameters = getJointParameters(tree.JMPJointSet); From 0c40d66b14d782ce94d695aae917c095cb4d22c4 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Fri, 8 Sep 2023 14:52:36 -0500 Subject: [PATCH 080/365] Surrogate model as function handle --- .../SurrogateModelCreation.m | 11 ++- .../createSurrogateModel.m | 68 ++++++++++++++++--- 2 files changed, 62 insertions(+), 17 deletions(-) diff --git a/src/SurrogateModelCreation/SurrogateModelCreation.m b/src/SurrogateModelCreation/SurrogateModelCreation.m index 9628eeca4..d7cc6e4ab 100644 --- a/src/SurrogateModelCreation/SurrogateModelCreation.m +++ b/src/SurrogateModelCreation/SurrogateModelCreation.m @@ -25,7 +25,7 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function SurrogateModelCreation(inputs) +function surrogateMuscles = SurrogateModelCreation(inputs) inputs = getData(inputs); @@ -34,12 +34,9 @@ function SurrogateModelCreation(inputs) inputs.experimentalJointAngles] = performLhsSampling(inputs); end inputs = getMuscleSpecificSurrogateModelData(inputs); -[inputs.polynomialExpressionMuscleTendonLengths, ... - inputs.polynomialExpressionMuscleTendonVelocities, ... - inputs.polynomialExpressionMomentArms, inputs.coefficients] = ... - createSurrogateModel(inputs.muscleSpecificJointAngles, ... - inputs.muscleTendonLengths, inputs.muscleSpecificMomentArms, ... - inputs.polynomialDegree); +surrogateMuscles = createSurrogateModel( ... + inputs.muscleSpecificJointAngles, inputs.muscleTendonLengths, ... + inputs.muscleSpecificMomentArms, inputs.polynomialDegree); saveSurrogateModel(inputs); reportSurrogateModel(inputs); diff --git a/src/SurrogateModelCreation/createSurrogateModel.m b/src/SurrogateModelCreation/createSurrogateModel.m index 7715d4d8c..852009e3e 100644 --- a/src/SurrogateModelCreation/createSurrogateModel.m +++ b/src/SurrogateModelCreation/createSurrogateModel.m @@ -38,18 +38,66 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- -function [polynomialExpressionMuscleTendonLengths, ... - polynomialExpressionMuscleTendonVelocities, ... - polynomialExpressionMomentArms, coefficients] = ... - createSurrogateModel(jointAngles, muscleTendonLengths, ... - momentArms, polynomialDegree) - +function surrogateMuscles = createSurrogateModel(jointAngles, ... + muscleTendonLengths, momentArms, polynomialDegree) +surrogateMuscles = cell(1, size(muscleTendonLengths, 2)); % Create surorogate model for all muscles for i = 1 : size(muscleTendonLengths, 2) -[polynomialExpressionMuscleTendonLengths{i}, ... - polynomialExpressionMuscleTendonVelocities{i}, ... - polynomialExpressionMomentArms{i}, coefficients{i}] = ... +[polynomialExpressionMuscleTendonLengths, ... + polynomialExpressionMuscleTendonVelocities, ... + polynomialExpressionMomentArms, coefficients] = ... createMuscleSpecificSurrogateModel(jointAngles{i}, ... muscleTendonLengths(:, i), momentArms{i}, polynomialDegree); + +surrogateMuscles{i} = @(jointAngles, jointVelocities)evaluateSurrogate( ... + jointAngles, jointVelocities, ... + polynomialExpressionMuscleTendonLengths, ... + polynomialExpressionMuscleTendonVelocities, ... + polynomialExpressionMomentArms, coefficients); +end end -end \ No newline at end of file + +function [muscleTendonLength, muscleTendonVelocity, momentArms] = ... + evaluateSurrogate(jointAngles, jointVelocities, ... + polynomialExpressionMuscleTendonLength, ... + polynomialExpressionMuscleTendonVelocity, ... + polynomialExpressionMomentArms, coefficients) + +% Values are set to match symbolic expressions in polynomials +for i = 1 : size(jointAngles, 2) + eval(['theta' num2str(i) ' = jointAngles(:,' num2str(i) ');']); + eval(['thetaDot' num2str(i) ' = jointVelocities(:,' num2str(i) ');']); +end +muscleTendonLengthMatrix = zeros(size(jointAngles, 1), ... + size(polynomialExpressionMomentArms, 2)); +muscleTendonVelocityMatrix = zeros(size(jointVelocities, 1), ... + size(polynomialExpressionMomentArms, 2)); +momentArmsMatrix = zeros(size(jointAngles, 1), size(jointAngles, 2), ... + size(polynomialExpressionMomentArms, 2)); +for j = 1 : size(polynomialExpressionMomentArms, 2) + muscleTendonLengthMatrix(:, j) = ... + eval(polynomialExpressionMuscleTendonLength(1, j)) .* ... + ones(size(jointAngles, 1), 1); + muscleTendonVelocityMatrix(:, j) = ... + eval(polynomialExpressionMuscleTendonVelocity(1, j)) .* ... + ones(size(jointAngles, 1), 1); + for k = 1 : size(jointAngles, 2) + momentArmsMatrix(:, k, j) = ... + eval(polynomialExpressionMomentArms(k, j)) .* ... + ones(size(jointAngles, 1), 1); + end +end +fullMatrix = [muscleTendonLengthMatrix; muscleTendonVelocityMatrix; ... + reshape(momentArmsMatrix, [], ... + size(polynomialExpressionMomentArms, 2))]; + +modeledValues = fullMatrix * coefficients; +muscleTendonLength = modeledValues(1 : size(jointAngles, 1)); +muscleTendonVelocity = modeledValues(1 + size(jointAngles, 1) : ... + size(jointAngles, 1) * 2); +for i = 1 : size(jointAngles, 2) + momentArms(:, i) = modeledValues(1 + size(jointAngles, 1) * (1 + i) ... + : size(jointAngles, 1) * (2 + i)); +end +end + From 38a5ba307fa1b97154578af4ed6af4cdb3762a14 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Fri, 8 Sep 2023 15:32:41 -0500 Subject: [PATCH 081/365] update calcSurrogateModel --- .../SurrogateModelCreation.m | 6 ++++-- src/SurrogateModelCreation/calcSurrogateModel.m | 15 +++++---------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/SurrogateModelCreation/SurrogateModelCreation.m b/src/SurrogateModelCreation/SurrogateModelCreation.m index d7cc6e4ab..bd44db9f9 100644 --- a/src/SurrogateModelCreation/SurrogateModelCreation.m +++ b/src/SurrogateModelCreation/SurrogateModelCreation.m @@ -38,8 +38,10 @@ inputs.muscleSpecificJointAngles, inputs.muscleTendonLengths, ... inputs.muscleSpecificMomentArms, inputs.polynomialDegree); -saveSurrogateModel(inputs); -reportSurrogateModel(inputs); +if valueOrAlternate(inputs, 'plotResults', false) + inputs.surrogateMuscles = surrogateMuscles; + reportSurrogateModel(inputs); +end end function inputs = getData(inputs) diff --git a/src/SurrogateModelCreation/calcSurrogateModel.m b/src/SurrogateModelCreation/calcSurrogateModel.m index 3b639573d..af2d4d9ad 100644 --- a/src/SurrogateModelCreation/calcSurrogateModel.m +++ b/src/SurrogateModelCreation/calcSurrogateModel.m @@ -44,19 +44,14 @@ newMomentArms = zeros(size(jointAngles{1}, 1), ... length(params.coordinateNames), size(jointAngles, 2)); for i = 1 : size(jointAngles, 2) - % Get A matrix - matrix = PatientSpecificSurrogateModel(jointAngles{i}, jointVelocities{i}, i); - % Caculate new muscle tendon lengths and moment arms - vector = matrix * params.coefficients{i}; - newMuscleTendonLengths(:, i) = vector(1 : size(jointAngles{i}, 1)); - newMuscleTendonVelocities(:, i) = vector(1 + ... - size(jointAngles{i}, 1) : size(jointAngles{i}, 1) * 2); - index = 2; + [newMuscleTendonLengths(:, i), newMuscleTendonVelocities(:, i), ... + momentArms] = params.surrogateMuscles{i}(jointAngles{i}, ... + jointVelocities{i}); + index = 1; for j = 1 : length(params.coordinateNames) for k = 1 : length(params.surrogateModelLabels{i}) if strcmp(params.coordinateNames(j), params.surrogateModelLabels{i}(k)) - newMomentArms(:, j, i) = vector(size(jointAngles{i}, 1) * ... - index + 1 : size(jointAngles{i}, 1) * (index + 1)); + newMomentArms(:, j, i) = momentArms(:, index); index = index + 1; end end From 51bdc10ddbfbe724249448e95164c686dfbfe2e3 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Fri, 8 Sep 2023 15:42:18 -0500 Subject: [PATCH 082/365] Fix epsilon threshold --- .../getMuscleSpecificSurrogateModelData.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SurrogateModelCreation/getMuscleSpecificSurrogateModelData.m b/src/SurrogateModelCreation/getMuscleSpecificSurrogateModelData.m index b98f76d9d..14e8cef40 100644 --- a/src/SurrogateModelCreation/getMuscleSpecificSurrogateModelData.m +++ b/src/SurrogateModelCreation/getMuscleSpecificSurrogateModelData.m @@ -32,7 +32,7 @@ for j = 1:length(inputs.coordinateNames) for k = 1:length(inputs.surrogateModelCoordinateNames) if strcmp(inputs.coordinateNames(j), inputs.surrogateModelCoordinateNames(k)) - if range(inputs.momentArms(:,k,i)) > inputs.epsilon + if max(abs(inputs.momentArms(:,k,i))) > inputs.epsilon inputs.surrogateModelLabels{i}(counter) = ... inputs.coordinateNames(j); inputs.muscleSpecificJointAngles{i}(:,counter) = ... From 0410d56c5ffaac37850f79a8784946c195a69988 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Fri, 8 Sep 2023 15:50:14 -0500 Subject: [PATCH 083/365] update author list --- src/SurrogateModelCreation/SurrogateModelCreation.m | 2 +- src/SurrogateModelCreation/calcSurrogateModel.m | 2 +- src/SurrogateModelCreation/createSurrogateModel.m | 2 +- .../getMuscleSpecificSurrogateModelData.m | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/SurrogateModelCreation/SurrogateModelCreation.m b/src/SurrogateModelCreation/SurrogateModelCreation.m index bd44db9f9..750195297 100644 --- a/src/SurrogateModelCreation/SurrogateModelCreation.m +++ b/src/SurrogateModelCreation/SurrogateModelCreation.m @@ -11,7 +11,7 @@ % National Institutes of Health (R01 EB030520). % % % % Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % +% Author(s): Marleny Vega, Spencer Williams % % % % Licensed under the Apache License, Version 2.0 (the "License"); % % you may not use this file except in compliance with the License. % diff --git a/src/SurrogateModelCreation/calcSurrogateModel.m b/src/SurrogateModelCreation/calcSurrogateModel.m index af2d4d9ad..bdd5dff8a 100644 --- a/src/SurrogateModelCreation/calcSurrogateModel.m +++ b/src/SurrogateModelCreation/calcSurrogateModel.m @@ -23,7 +23,7 @@ % National Institutes of Health (R01 EB030520). % % % % Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % +% Author(s): Marleny Vega, Spencer Williams % % % % Licensed under the Apache License, Version 2.0 (the "License"); % % you may not use this file except in compliance with the License. % diff --git a/src/SurrogateModelCreation/createSurrogateModel.m b/src/SurrogateModelCreation/createSurrogateModel.m index 852009e3e..6d12ee3c4 100644 --- a/src/SurrogateModelCreation/createSurrogateModel.m +++ b/src/SurrogateModelCreation/createSurrogateModel.m @@ -24,7 +24,7 @@ % National Institutes of Health (R01 EB030520). % % % % Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % +% Author(s): Marleny Vega, Spencer Williams % % % % Licensed under the Apache License, Version 2.0 (the "License"); % % you may not use this file except in compliance with the License. % diff --git a/src/SurrogateModelCreation/getMuscleSpecificSurrogateModelData.m b/src/SurrogateModelCreation/getMuscleSpecificSurrogateModelData.m index 14e8cef40..14783388f 100644 --- a/src/SurrogateModelCreation/getMuscleSpecificSurrogateModelData.m +++ b/src/SurrogateModelCreation/getMuscleSpecificSurrogateModelData.m @@ -11,7 +11,7 @@ % National Institutes of Health (R01 EB030520). % % % % Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % +% Author(s): Marleny Vega, Spencer Williams % % % % Licensed under the Apache License, Version 2.0 (the "License"); % % you may not use this file except in compliance with the License. % From 98e70451b2ecadad7237d7615c0880c8b1a454ad Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Fri, 8 Sep 2023 16:03:35 -0500 Subject: [PATCH 084/365] Remove surrogate model save/print functions --- .../AzXr7-nnbKkLY4Ocyr-4XyD7ZbEd.xml | 6 -- .../AzXr7-nnbKkLY4Ocyr-4XyD7ZbEp.xml | 2 - .../Ke0uWSdo7yBLrhSQYRC9LeEAY_Md.xml | 6 -- .../Ke0uWSdo7yBLrhSQYRC9LeEAY_Mp.xml | 2 - .../printSurrogateModel.m | 88 ------------------- .../saveSurrogateModel.m | 37 -------- 6 files changed, 141 deletions(-) delete mode 100644 resources/project/5Sv8sFw_LYuwxJg20iZ-lSPNe9g/AzXr7-nnbKkLY4Ocyr-4XyD7ZbEd.xml delete mode 100644 resources/project/5Sv8sFw_LYuwxJg20iZ-lSPNe9g/AzXr7-nnbKkLY4Ocyr-4XyD7ZbEp.xml delete mode 100644 resources/project/5Sv8sFw_LYuwxJg20iZ-lSPNe9g/Ke0uWSdo7yBLrhSQYRC9LeEAY_Md.xml delete mode 100644 resources/project/5Sv8sFw_LYuwxJg20iZ-lSPNe9g/Ke0uWSdo7yBLrhSQYRC9LeEAY_Mp.xml delete mode 100644 src/SurrogateModelCreation/printSurrogateModel.m delete mode 100644 src/SurrogateModelCreation/saveSurrogateModel.m diff --git a/resources/project/5Sv8sFw_LYuwxJg20iZ-lSPNe9g/AzXr7-nnbKkLY4Ocyr-4XyD7ZbEd.xml b/resources/project/5Sv8sFw_LYuwxJg20iZ-lSPNe9g/AzXr7-nnbKkLY4Ocyr-4XyD7ZbEd.xml deleted file mode 100644 index 7a6326b99..000000000 --- a/resources/project/5Sv8sFw_LYuwxJg20iZ-lSPNe9g/AzXr7-nnbKkLY4Ocyr-4XyD7ZbEd.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/5Sv8sFw_LYuwxJg20iZ-lSPNe9g/AzXr7-nnbKkLY4Ocyr-4XyD7ZbEp.xml b/resources/project/5Sv8sFw_LYuwxJg20iZ-lSPNe9g/AzXr7-nnbKkLY4Ocyr-4XyD7ZbEp.xml deleted file mode 100644 index f1071d7e7..000000000 --- a/resources/project/5Sv8sFw_LYuwxJg20iZ-lSPNe9g/AzXr7-nnbKkLY4Ocyr-4XyD7ZbEp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/5Sv8sFw_LYuwxJg20iZ-lSPNe9g/Ke0uWSdo7yBLrhSQYRC9LeEAY_Md.xml b/resources/project/5Sv8sFw_LYuwxJg20iZ-lSPNe9g/Ke0uWSdo7yBLrhSQYRC9LeEAY_Md.xml deleted file mode 100644 index 7a6326b99..000000000 --- a/resources/project/5Sv8sFw_LYuwxJg20iZ-lSPNe9g/Ke0uWSdo7yBLrhSQYRC9LeEAY_Md.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/5Sv8sFw_LYuwxJg20iZ-lSPNe9g/Ke0uWSdo7yBLrhSQYRC9LeEAY_Mp.xml b/resources/project/5Sv8sFw_LYuwxJg20iZ-lSPNe9g/Ke0uWSdo7yBLrhSQYRC9LeEAY_Mp.xml deleted file mode 100644 index 6e813b5ae..000000000 --- a/resources/project/5Sv8sFw_LYuwxJg20iZ-lSPNe9g/Ke0uWSdo7yBLrhSQYRC9LeEAY_Mp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/src/SurrogateModelCreation/printSurrogateModel.m b/src/SurrogateModelCreation/printSurrogateModel.m deleted file mode 100644 index 7348b323e..000000000 --- a/src/SurrogateModelCreation/printSurrogateModel.m +++ /dev/null @@ -1,88 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% () -> () -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function printSurrogateModel(numMuscles, ... - polynomialExpressionMuscleTendonLengths, ... - polynomialExpressionMuscleTendonVelocities, ... - polynomialExpressionMomentArms, jointAngles, resultsDirectory) - -fid = fopen(fullfile(resultsDirectory, 'PatientSpecificSurrogateModel.m'), 'wt'); -fprintf(fid, 'function [matrix] = PatientSpecificSurrogateModel(jointAngles, jointVelocities, numMuscles)\n'); -fprintf(fid, 'onesCol = ones(size(jointAngles, 1), 1);\n'); -fprintf(fid, 'zeroCol = zeros(size(jointAngles, 1), 1);\n'); -fprintf(fid, 'switch numMuscles\n'); -for i=1:numMuscles - fprintf(fid, [' case ' num2str(i) '\n']); - for j=1:size(jointAngles{i}, 2) - fprintf(fid, [' theta' num2str(j) ' = jointAngles(:, ' num2str(j) ');\n']); - end - for j=1:size(jointAngles{i}, 2) - fprintf(fid, [' thetaDot' num2str(j) ' = jointVelocities(:, ' num2str(j) ');\n']); - end - fprintf(fid, ' muscleTendonLengths = ['); - for j=1:size(polynomialExpressionMuscleTendonLengths{i}, 2) - if polynomialExpressionMuscleTendonLengths{i}(1,j) == 1 - fprintf(fid, ' onesCol,'); - else - fprintf(fid, [' ' strrep(strrep(char(polynomialExpressionMuscleTendonLengths{i}(1,j)),'*','.*'),'^','.^') ',']); - end - end - fprintf(fid, '];\n'); - fprintf(fid, ' muscleTendonVelocities = ['); - for j=1:size(polynomialExpressionMuscleTendonVelocities{i}, 2) - if polynomialExpressionMuscleTendonVelocities{i}(1,j) == 1 - fprintf(fid, ' onesCol,'); - elseif polynomialExpressionMuscleTendonVelocities{i}(1,j) == 0 - fprintf(fid, ' zeroCol,'); - else - fprintf(fid, [' ' strrep(strrep(char(polynomialExpressionMuscleTendonVelocities{i}(1,j)),'*','.*'),'^','.^') ',']); - end - end - fprintf(fid, '];\n'); - for k=1:size(polynomialExpressionMomentArms{i},1) - fprintf(fid, [' momentArms' num2str(k) ' = [']); - for j=1:size(polynomialExpressionMomentArms{i},2) - if polynomialExpressionMomentArms{i}(k,j) == 0 - fprintf(fid, ' zeroCol,'); - elseif polynomialExpressionMomentArms{i}(k,j) / coeffs(polynomialExpressionMomentArms{i}(k,j)) == 1 - fprintf(fid, [char(polynomialExpressionMomentArms{i}(k,j)) '*onesCol,']); - else - fprintf(fid, [' ' strrep(strrep(char(polynomialExpressionMomentArms{i}(k,j)),'*','.*'),'^','.^') ',']); - end - end - fprintf(fid, '];\n'); - end - fprintf(fid, ' matrix = [muscleTendonLengths; muscleTendonVelocities;'); - for k=1:size(polynomialExpressionMomentArms{i}, 1) - fprintf(fid, ['momentArms' num2str(k) '; ']); - end - fprintf(fid, '];\n'); -end -fprintf(fid, 'end\n'); -fprintf(fid, 'end\n'); -fclose(fid); -end \ No newline at end of file diff --git a/src/SurrogateModelCreation/saveSurrogateModel.m b/src/SurrogateModelCreation/saveSurrogateModel.m deleted file mode 100644 index ca616143b..000000000 --- a/src/SurrogateModelCreation/saveSurrogateModel.m +++ /dev/null @@ -1,37 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% () -> () -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function saveSurrogateModel(inputs) - -printSurrogateModel(inputs.numMuscles, ... - inputs.polynomialExpressionMuscleTendonLengths, ... - inputs.polynomialExpressionMuscleTendonVelocities, ... - inputs.polynomialExpressionMomentArms, ... - inputs.muscleSpecificJointAngles, inputs.resultsDirectory); -coefficients = inputs.coefficients; -save("coefficients.mat", "coefficients") -end \ No newline at end of file From 5cd13d048f12d086626388bcd5356a3765d06936 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 11 Sep 2023 14:32:54 -0500 Subject: [PATCH 085/365] Rename params to inputs in calcSurrogateModel --- src/SurrogateModelCreation/calcSurrogateModel.m | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/SurrogateModelCreation/calcSurrogateModel.m b/src/SurrogateModelCreation/calcSurrogateModel.m index bdd5dff8a..11c31a3eb 100644 --- a/src/SurrogateModelCreation/calcSurrogateModel.m +++ b/src/SurrogateModelCreation/calcSurrogateModel.m @@ -38,19 +38,19 @@ % ----------------------------------------------------------------------- function [newMuscleTendonLengths, newMomentArms, ... - newMuscleTendonVelocities] = calcSurrogateModel(params, jointAngles, ... + newMuscleTendonVelocities] = calcSurrogateModel(inputs, jointAngles, ... jointVelocities) newMomentArms = zeros(size(jointAngles{1}, 1), ... - length(params.coordinateNames), size(jointAngles, 2)); + length(inputs.coordinateNames), size(jointAngles, 2)); for i = 1 : size(jointAngles, 2) [newMuscleTendonLengths(:, i), newMuscleTendonVelocities(:, i), ... - momentArms] = params.surrogateMuscles{i}(jointAngles{i}, ... + momentArms] = inputs.surrogateMuscles{i}(jointAngles{i}, ... jointVelocities{i}); index = 1; - for j = 1 : length(params.coordinateNames) - for k = 1 : length(params.surrogateModelLabels{i}) - if strcmp(params.coordinateNames(j), params.surrogateModelLabels{i}(k)) + for j = 1 : length(inputs.coordinateNames) + for k = 1 : length(inputs.surrogateModelLabels{i}) + if strcmp(inputs.coordinateNames(j), inputs.surrogateModelLabels{i}(k)) newMomentArms(:, j, i) = momentArms(:, index); index = index + 1; end From 1c71ea9207c0d2ca18d74590123f7b12a29b4a29 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 11 Sep 2023 16:58:58 -0500 Subject: [PATCH 086/365] Make RCNLSynergySet optional element in osimx struct --- src/core/osimx/parseOsimxFile.m | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/osimx/parseOsimxFile.m b/src/core/osimx/parseOsimxFile.m index 884c07c10..448fb1ead 100644 --- a/src/core/osimx/parseOsimxFile.m +++ b/src/core/osimx/parseOsimxFile.m @@ -88,7 +88,10 @@ end end -osimx.synergyGroups = parseSynergyGroups(tree, model); +rcnlSynergySetTree = getFieldByName(tree, "RCNLSynergySet"); +if isstruct(rcnlSynergySetTree) + osimx.synergyGroups = parseSynergyGroups(tree, model); +end end function contactSurface = parseContactSurface(tree) From 16fe377b78fa430805a69dea072e7593f0247c06 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 11 Sep 2023 20:51:50 -0500 Subject: [PATCH 087/365] Revert "Free final time fix to muscle activation tracking term" This reverts commit 94eb370022cb4e958be75634453188905ad46daa. --- .../IntegrandTerms/calcTrackingMuscleActivationIntegrand.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m b/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m index faa377c58..c5810a7fd 100644 --- a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m +++ b/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m @@ -42,7 +42,7 @@ fnval(params.splineMuscleActivations, time/time(end)); end -experimentalMuscleActivations = fnval(params.splineMuscleActivations, time/time(end))'; +experimentalMuscleActivations = fnval(params.splineMuscleActivations, time)'; cost = calcTrackingCostArrayTerm(experimentalMuscleActivations, ... muscleActivations, indx); end \ No newline at end of file From 7eba2da129ce5ba61808772e997a0ef2eab21220 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 11 Sep 2023 20:51:55 -0500 Subject: [PATCH 088/365] Revert "Fixed free final time" This reverts commit e4f7977eea22b5e739afa57a9849d5cbbd65a91e. --- src/DesignOptimization/DesignOptimization.m | 2 +- .../VerificationOptimization.m | 2 +- .../IntegrandTerms/calcTrackingControllerIntegrand.m | 6 +++--- .../IntegrandTerms/calcTrackingCoordinateIntegrand.m | 4 ++-- .../calcTrackingExternalForcesIntegrand.m | 4 ++-- .../calcTrackingExternalMomentsIntegrand.m | 4 ++-- .../calcTrackingInverseDynamicLoadsIntegrand.m | 4 ++-- .../calcTrackingMuscleActivationIntegrand.m | 4 ++-- src/core/TreatmentOptimization/getSplines.m | 10 +++++----- 9 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/DesignOptimization/DesignOptimization.m b/src/DesignOptimization/DesignOptimization.m index 9651dd5b2..df507bcaa 100644 --- a/src/DesignOptimization/DesignOptimization.m +++ b/src/DesignOptimization/DesignOptimization.m @@ -40,7 +40,7 @@ output = computeDesignOptimizationMainFunction(inputs, params); end function inputs = setupMuscleSynergies(inputs) -inputs.splineSynergyActivations = spaps(inputs.initialGuess.time/inputs.initialGuess.time(end), ... +inputs.splineSynergyActivations = spaps(inputs.initialGuess.time, ... inputs.initialGuess.control(:, inputs.numCoordinates + 1 : ... inputs.numCoordinates + inputs.numSynergies)', 0.0000001); inputs.synergyLabels = inputs.initialGuess.controlLabels(:, ... diff --git a/src/VerificationOptimization/VerificationOptimization.m b/src/VerificationOptimization/VerificationOptimization.m index 1253f2f67..cbfbd3989 100644 --- a/src/VerificationOptimization/VerificationOptimization.m +++ b/src/VerificationOptimization/VerificationOptimization.m @@ -38,7 +38,7 @@ end function inputs = setupMuscleSynergies(inputs) -inputs.splineSynergyActivations = spaps(inputs.initialGuess.time/inputs.initialGuess.time(end), ... +inputs.splineSynergyActivations = spaps(inputs.initialGuess.time, ... inputs.initialGuess.control(:, inputs.numCoordinates + 1:end)', 0.0000001); inputs.synergyLabels = inputs.initialGuess.controlLabels(:, inputs.numCoordinates + 1:end); end diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m b/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m index 63528f5b3..8f22e433a 100644 --- a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m +++ b/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m @@ -38,7 +38,7 @@ indx = find(strcmp(convertCharsToStrings( ... auxdata.synergyLabels), controllerName)); synergyActivations = ... - fnval(auxdata.splineSynergyActivations, time/time(end))'; + fnval(auxdata.splineSynergyActivations, time)'; cost = calcTrackingCostArrayTerm(synergyActivations, ... values.controlSynergyActivations, indx); case 'torque_driven' @@ -49,10 +49,10 @@ controllerName)); if auxdata.splineJointMoments.dim > 1 experimentalJointMoments = ... - fnval(auxdata.splineJointMoments, time/time(end))'; + fnval(auxdata.splineJointMoments, time)'; else experimentalJointMoments = ... - fnval(auxdata.splineJointMoments, time/time(end)); + fnval(auxdata.splineJointMoments, time); end cost = experimentalJointMoments(:, indx1) - ... values.controlTorques(:, indx2); diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m b/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m index 78203dd10..7c3049d32 100644 --- a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m +++ b/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m @@ -34,9 +34,9 @@ indx = find(strcmp(convertCharsToStrings(auxdata.coordinateNames), ... coordinateName)); if auxdata.splineJointAngles.dim > 1 - experimentalJointAngles = fnval(auxdata.splineJointAngles, time/time(end))'; + experimentalJointAngles = fnval(auxdata.splineJointAngles, time)'; else - experimentalJointAngles = fnval(auxdata.splineJointAngles, time/time(end)); + experimentalJointAngles = fnval(auxdata.splineJointAngles, time); end cost = calcTrackingCostArrayTerm(experimentalJointAngles, ... diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m b/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m index b81582f1d..377a5d0ae 100644 --- a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m +++ b/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m @@ -38,11 +38,11 @@ if params.splineExperimentalGroundReactionForces{i}.dim > 1 experimentalGroundReactions = ... fnval(params.splineExperimentalGroundReactionForces{i}, ... - time/time(end))'; + time)'; else experimentalGroundReactions = ... fnval(params.splineExperimentalGroundReactionForces{i}, ... - time/time(end)); + time); end cost = calcTrackingCostArrayTerm(... experimentalGroundReactions, groundReactionsForces{i}, ... diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m b/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m index 02e4be838..b903723a4 100644 --- a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m +++ b/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m @@ -38,11 +38,11 @@ if params.splineExperimentalGroundReactionMoments{i}.dim > 1 experimentalGroundReactions = ... fnval(params.splineExperimentalGroundReactionMoments{i}, ... - time/time(end))'; + time)'; else experimentalGroundReactions = ... fnval(params.splineExperimentalGroundReactionMoments{i}, ... - time/time(end)); + time); end cost = calcTrackingCostArrayTerm(... experimentalGroundReactions, groundReactionMoments{i}, ... diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m b/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m index f60ea4e0e..9fb687f49 100644 --- a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m +++ b/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m @@ -37,9 +37,9 @@ loadName)); if params.splineJointMoments.dim > 1 - experimentalJointMoments = fnval(params.splineJointMoments, time/time(end))'; + experimentalJointMoments = fnval(params.splineJointMoments, time)'; else - experimentalJointMoments = fnval(params.splineJointMoments, time/time(end)); + experimentalJointMoments = fnval(params.splineJointMoments, time); end momentLabelsNoSuffix = erase(params.inverseDynamicMomentLabels, '_moment'); diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m b/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m index c5810a7fd..392789b9c 100644 --- a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m +++ b/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m @@ -36,10 +36,10 @@ if params.splineMuscleActivations.dim > 1 experimentalMuscleActivations = ... - fnval(params.splineMuscleActivations, time/time(end))'; + fnval(params.splineMuscleActivations, time)'; else experimentalMuscleActivations = ... - fnval(params.splineMuscleActivations, time/time(end)); + fnval(params.splineMuscleActivations, time); end experimentalMuscleActivations = fnval(params.splineMuscleActivations, time)'; diff --git a/src/core/TreatmentOptimization/getSplines.m b/src/core/TreatmentOptimization/getSplines.m index e2db99acd..26200073c 100644 --- a/src/core/TreatmentOptimization/getSplines.m +++ b/src/core/TreatmentOptimization/getSplines.m @@ -30,20 +30,20 @@ % ----------------------------------------------------------------------- % function inputs = getSplines(inputs) -inputs.splineJointAngles = spaps(inputs.experimentalTime/inputs.experimentalTime(end), ... +inputs.splineJointAngles = spaps(inputs.experimentalTime, ... inputs.experimentalJointAngles', 0.0000001); -inputs.splineJointMoments = spaps(inputs.experimentalTime/inputs.experimentalTime(end), ... +inputs.splineJointMoments = spaps(inputs.experimentalTime, ... inputs.experimentalJointMoments', 0.0000001); if strcmp(inputs.controllerType, 'synergy_driven') -inputs.splineMuscleActivations = spaps(inputs.experimentalTime/inputs.experimentalTime(end), ... +inputs.splineMuscleActivations = spaps(inputs.experimentalTime, ... inputs.experimentalMuscleActivations', 0.0000001); end for i = 1:length(inputs.contactSurfaces) inputs.splineExperimentalGroundReactionForces{i} = ... - spaps(inputs.experimentalTime/inputs.experimentalTime(end), ... + spaps(inputs.experimentalTime, ... inputs.contactSurfaces{i}.experimentalGroundReactionForces', 0.0000001); inputs.splineExperimentalGroundReactionMoments{i} = ... - spaps(inputs.experimentalTime/inputs.experimentalTime(end), ... + spaps(inputs.experimentalTime, ... inputs.contactSurfaces{i}.experimentalGroundReactionMoments', 0.0000001); end end \ No newline at end of file From 7fe93178b97052bef9b35d50212052b26dd861b2 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Mon, 11 Sep 2023 20:54:32 -0500 Subject: [PATCH 089/365] fixed small bugs --- src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m | 2 +- src/core/TreatmentOptimization/getStateDerivatives.m | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m b/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m index 3956c79f2..334f2cc8a 100644 --- a/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m +++ b/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m @@ -40,7 +40,7 @@ values.stateAccelerations = getCorrectStates(state, 3, inputs.numCoordinates); values.controlJerks = control(:, 1 : inputs.numCoordinates); -if ~strcmp(inputs.controllerType, 'synergy') +if strcmp(inputs.controllerType, 'torque') values.controlTorques = control(:, inputs.numCoordinates + 1 : ... inputs.numCoordinates + inputs.numTorqueControls); else diff --git a/src/core/TreatmentOptimization/getStateDerivatives.m b/src/core/TreatmentOptimization/getStateDerivatives.m index 3580fdb43..c93c7d314 100644 --- a/src/core/TreatmentOptimization/getStateDerivatives.m +++ b/src/core/TreatmentOptimization/getStateDerivatives.m @@ -32,7 +32,7 @@ points = length(inputs.experimentalTime); interval = inputs.experimentalTime(2) - inputs.experimentalTime(1); numNodes = splFitWithCutoff(inputs.experimentalTime', ... - inputs.experimentalJointAngles', 10, 5); + inputs.experimentalJointAngles', 4, 5); [N, Np, Npp] = BSplineMatrices(5, numNodes, points, interval); Nodes = N\inputs.experimentalJointAngles; inputs.experimentalJointVelocities = Np * Nodes; From 6cb09eed97d4b9c9ba78aed2edba421ebef18029 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" Date: Tue, 12 Sep 2023 22:41:52 -0700 Subject: [PATCH 090/365] move parseInitialGuess inside parse --- src/core/parse/parseInitialGuess.m | 60 ------------------ .../parseTreatmentOptimizationDataDirectory.m | 61 ++++++++++++++----- 2 files changed, 47 insertions(+), 74 deletions(-) delete mode 100644 src/core/parse/parseInitialGuess.m diff --git a/src/core/parse/parseInitialGuess.m b/src/core/parse/parseInitialGuess.m deleted file mode 100644 index e1f47ddf1..000000000 --- a/src/core/parse/parseInitialGuess.m +++ /dev/null @@ -1,60 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function parses and loads the initial guesses for states (if -% specified), controls (if specified), and parameters (if specified) -% -% (struct) -> (struct) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function initialGuess = parseInitialGuess(inputs) -import org.opensim.modeling.Storage -initialGuess = []; -try - [initialGuess.state, initialGuess.stateLabels, initialGuess.time] = ... - parseTrialData(inputs.previousResultsDirectory, ... - strcat(inputs.trialName, "_states"), inputs.model); -catch; end -if strcmp(inputs.controllerType, "torque") - try - [initialGuess.control, initialGuess.controlLabels] = ... - parseTrialData(inputs.previousResultsDirectory, ... - strcat(inputs.trialName, "_torqueControls"), inputs.model); - catch;end -elseif strcmp(inputs.controllerType, "synergy") - try - [initialGuess.control, initialGuess.controlLabels] = ... - parseTrialData(inputs.previousResultsDirectory, ... - strcat(inputs.trialName, "_synergyCommands"), inputs.model); - catch;end - try - [initialGuess.parameter, initialGuess.parameterLabels] = ... - parseTrialData(inputs.previousResultsDirectory, ... - "synergyWeights", inputs.model); - catch;end -else - throw(MException("IncorrectControllerType", ... - "controllerType must be 'torque' or 'synergy'")); -end -end \ No newline at end of file diff --git a/src/core/parse/parseTreatmentOptimizationDataDirectory.m b/src/core/parse/parseTreatmentOptimizationDataDirectory.m index ab6e74b18..05e75d0e3 100644 --- a/src/core/parse/parseTreatmentOptimizationDataDirectory.m +++ b/src/core/parse/parseTreatmentOptimizationDataDirectory.m @@ -33,7 +33,27 @@ function inputs = parseTreatmentOptimizationDataDirectory(tree, inputs) [dataDirectory, inputs.previousResultsDirectory] = findDataDirectory(tree, inputs); inputs.trialName = parseTrialName(tree); -prefix = findPrefixes(tree, dataDirectory); +inputs = parseExperimentalData(tree, inputs); +inputs = parseSynergyExperimentalData(tree, inputs); +inputs = parseInitialValues(tree, inputs); +inputs.numCoordinates = size(inputs.experimentalJointAngles, 2); +end + +function [dataDirectory, previousResultsDirectory] = ... + findDataDirectory(tree, inputs) +dataDirectory = parseDataDirectory(tree); +previousResultsDirectory = ... + parseTextOrAlternate(tree, "previous_results_directory", ""); +if strcmp(previousResultsDirectory, "") && ... + strcmp(inputs.controllerType, 'synergy') + throw(MException("ParseError:RequiredElement", ... + strcat("Element required", ... + " for , this can be an NCP", ... + " or Treatment Optimization results directory"))) +end +end + +function inputs = parseExperimentalData(tree, inputs) [inputs.experimentalJointMoments, ... inputs.inverseDynamicMomentLabels] = ... parseTrialDataTryDirectories( ... @@ -49,6 +69,9 @@ inputs.grfFileName = findFileListFromPrefixList(... fullfile(dataDirectory, "GRFData"), prefix); end +end + +function inputs = parseSynergyExperimentalData(tree, inputs) if strcmp(inputs.controllerType, 'synergy') [inputs.experimentalMuscleActivations, inputs.muscleLabels] = ... parseTrialData(inputs.previousResultsDirectory, ... @@ -63,20 +86,30 @@ length(inputs.surrogateModelCoordinateNames), length(inputs.muscleNames)); inputs = getMuscleSpecificSurrogateModelData(inputs); end -inputs.numCoordinates = size(inputs.experimentalJointAngles, 2); end -function [dataDirectory, previousResultsDirectory] = ... - findDataDirectory(tree, inputs) -dataDirectory = parseDataDirectory(tree); -previousResultsDirectory = ... - parseTextOrAlternate(tree, "previous_results_directory", ""); -if strcmp(previousResultsDirectory, "") && ... - strcmp(inputs.controllerType, 'synergy') - throw(MException("ParseError:RequiredElement", ... - strcat("Element required", ... - " for , this can be an NCP", ... - " or Treatment Optimization results directory"))) +function inputs = parseInitialValues(tree, inputs) +initialGuess = []; +try + [inputs.initialState, inputs.initialStateLabels] = ... + parseTrialData(inputs.previousResultsDirectory, ... + strcat(inputs.trialName, "_states"), inputs.model); +catch; end +if strcmp(inputs.controllerType, "torque") + try + [inputs.initialControl, inputs.initialControlLabels] = ... + parseTrialData(inputs.previousResultsDirectory, ... + strcat(inputs.trialName, "_torqueControls"), inputs.model); + catch;end +elseif strcmp(inputs.controllerType, "synergy") + try + [inputs.initialControl, inputs.initialControlLabels] = ... + parseTrialData(inputs.previousResultsDirectory, ... + strcat(inputs.trialName, "_synergyCommands"), inputs.model); + catch;end +else + throw(MException("IncorrectControllerType", ... + "controllerType must be 'torque' or 'synergy'")); end end @@ -93,4 +126,4 @@ else [data, labels, time] = parseTrialData(dataDirectory, trialName, model); end -end \ No newline at end of file +end From d31a65eee20f8ee0e8e5ef0000859f3e472a067a Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" Date: Tue, 12 Sep 2023 22:42:24 -0700 Subject: [PATCH 091/365] organize parse --- .../parseOptimalControlSolverSettings.m | 4 +++- .../parse/parseTreatmentOptimizationInputs.m | 18 +++++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/core/optimal_control/parseOptimalControlSolverSettings.m b/src/core/optimal_control/parseOptimalControlSolverSettings.m index 60a225072..11122c4b0 100644 --- a/src/core/optimal_control/parseOptimalControlSolverSettings.m +++ b/src/core/optimal_control/parseOptimalControlSolverSettings.m @@ -25,7 +25,9 @@ % ----------------------------------------------------------------------- % function inputs = parseOptimalControlSolverSettings( ... - settingsFileName, inputs) + tree, inputs) +settingsFileName = getTextFromField(getFieldByNameOrError(tree, ... + 'optimal_control_solver_settings_file')) solverSettingsTree = xml2struct(settingsFileName); verifyVersion(solverSettingsTree, "OptimalControlSolverSettings"); tree = ... diff --git a/src/core/parse/parseTreatmentOptimizationInputs.m b/src/core/parse/parseTreatmentOptimizationInputs.m index 0269200ff..fbb2fd623 100644 --- a/src/core/parse/parseTreatmentOptimizationInputs.m +++ b/src/core/parse/parseTreatmentOptimizationInputs.m @@ -30,12 +30,7 @@ % ----------------------------------------------------------------------- % function inputs = parseTreatmentOptimizationInputs(tree) -inputs.toolName = findToolName(tree); -inputs.resultsDirectory = getTextFromField(getFieldByName(tree, ... - 'results_directory')); -if(isempty(inputs.resultsDirectory)); inputs.resultsDirectory = pwd; end -inputs.controllerType = parseControllerType(tree); -inputs = parseModel(tree, inputs); +inputs = parseBasicInputs(tree); inputs.osimx = parseOsimxFileWithCondition(tree, inputs); inputs = parseController(tree, inputs); inputs = parseTreatmentOptimizationDataDirectory(tree, inputs); @@ -51,6 +46,15 @@ inputs.contactSurfaces = parseGroundContactSurfaces(inputs); end +function inputs = parseBasicInputs(tree) +inputs.toolName = findToolName(tree); +inputs.resultsDirectory = getTextFromField(getFieldByName(tree, ... + 'results_directory')); +if(isempty(inputs.resultsDirectory)); inputs.resultsDirectory = pwd; end +inputs.controllerType = parseControllerType(tree); +inputs = parseModel(tree, inputs); +end + function osimx = parseOsimxFileWithCondition(tree, inputs) osimxFileName = parseTextOrAlternate(tree, "input_osimx_file", ""); osimx = parseOsimxFile(osimxFileName, inputs.model); @@ -66,4 +70,4 @@ " osimx file for . Have you run NCP yet?"))) end end -end \ No newline at end of file +end From ede8f1c24bcc2856036969c0a4a53e33e80522b6 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" Date: Tue, 12 Sep 2023 23:27:30 -0700 Subject: [PATCH 092/365] rename functions --- .../calcDesignOptimizationObjective.m | 7 ++---- .../computeDesignOptimizationMainFunction.m | 12 ++++------ .../gpops/calcGpopsIntegrand.m | 8 +++---- .../gpops/computeGpopsEndpointFunction.m | 5 +--- .../gpops/convertToGpopsSynergyDrivenInputs.m | 4 ---- .../gpops/convertToGpopsTorqueDrivenInputs.m | 4 ---- .../setupCommonOptimalControlInitialGuess.m | 6 ++--- ...tegralBounds.m => makeMaxAllowableError.m} | 24 +++++++++---------- ...putBounds.m => makeOptimalControlBounds.m} | 12 ++++++---- ...intBounds.m => makePathConstraintBounds.m} | 6 ++--- ...ounds.m => makeTerminalConstraintBounds.m} | 6 ++--- ...plines.m => makeExperimentalDataSplines.m} | 8 +++---- ...teDerivatives.m => makeStateDerivatives.m} | 0 .../makeTreatmentOptimizationInputs.m | 15 ++++++------ .../parse/parseTreatmentOptimizationParams.m | 5 +++- 15 files changed, 53 insertions(+), 69 deletions(-) rename src/core/TreatmentOptimization/SetupBounds/{getIntegralBounds.m => makeMaxAllowableError.m} (79%) rename src/core/TreatmentOptimization/SetupBounds/{getDesignVariableInputBounds.m => makeOptimalControlBounds.m} (96%) rename src/core/TreatmentOptimization/SetupBounds/{getPathConstraintBounds.m => makePathConstraintBounds.m} (96%) rename src/core/TreatmentOptimization/SetupBounds/{getTerminalConstraintBounds.m => makeTerminalConstraintBounds.m} (97%) rename src/core/TreatmentOptimization/{getSplines.m => makeExperimentalDataSplines.m} (95%) rename src/core/TreatmentOptimization/{getStateDerivatives.m => makeStateDerivatives.m} (100%) diff --git a/src/DesignOptimization/calcDesignOptimizationObjective.m b/src/DesignOptimization/calcDesignOptimizationObjective.m index 977e809cf..5528ddc0e 100644 --- a/src/DesignOptimization/calcDesignOptimizationObjective.m +++ b/src/DesignOptimization/calcDesignOptimizationObjective.m @@ -1,6 +1,6 @@ % This function is part of the NMSM Pipeline, see file for full license. % -% This function calculates the cost function objective for design +% This function calculates the cost function objective for design % optimization. % % (Number, Array of number, Number, struct) -> (Number) @@ -31,10 +31,7 @@ function objective = calcDesignOptimizationObjective(discrete, ... continuous, finalTime, inputs) continuousObjective = sum(continuous) / length(continuous); -if isfield(inputs, "finalTimeRange") - continuousObjective = continuousObjective / finalTime; -end discreteObjective = sum(discrete) / length(discrete); if isnan(discreteObjective); discreteObjective = 0; end objective = continuousObjective + discreteObjective; -end \ No newline at end of file +end diff --git a/src/DesignOptimization/computeDesignOptimizationMainFunction.m b/src/DesignOptimization/computeDesignOptimizationMainFunction.m index 1ffbf145e..d0365f789 100644 --- a/src/DesignOptimization/computeDesignOptimizationMainFunction.m +++ b/src/DesignOptimization/computeDesignOptimizationMainFunction.m @@ -1,9 +1,9 @@ % This function is part of the NMSM Pipeline, see file for full license. % % This function sets up GPOPS-II to run Design Optimization. -% +% % (struct) -> (struct, struct) -% Assigns optimal control settings and runs Design Optimization +% Assigns optimal control settings and runs Design Optimization % ----------------------------------------------------------------------- % % The NMSM Pipeline is a toolkit for model personalization and treatment % @@ -69,16 +69,12 @@ 0.5]; end end -if isfield(inputs, "finalTimeRange") - bounds.phase.finaltime.lower = guess.phase.time(end) - (0.5 - guess.phase.time(end)); - bounds.phase.finaltime.upper = 0.5; -end end function guess = addUserDefinedTermsToGuess(guess, inputs) for i = 1:length(inputs.userDefinedVariables) variable = inputs.userDefinedVariables{i}; - if ~isfield(guess, "parameter") + if ~isfield(guess, "parameter") guess.parameter = []; end guess.parameter = [guess.parameter, ... @@ -87,4 +83,4 @@ variable.upper_bounds, ... variable.lower_bounds)]; end -end \ No newline at end of file +end diff --git a/src/TreatmentOptimization/gpops/calcGpopsIntegrand.m b/src/TreatmentOptimization/gpops/calcGpopsIntegrand.m index c990e447f..52ad31611 100644 --- a/src/TreatmentOptimization/gpops/calcGpopsIntegrand.m +++ b/src/TreatmentOptimization/gpops/calcGpopsIntegrand.m @@ -27,12 +27,12 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function integrand = calcGpopsIntegrand(values, modeledValues, auxdata) +function integrand = calcGpopsIntegrand(values, modeledValues, inputs) [costTermCalculations, allowedTypes] = ... - generateCostTermStruct("continuous", auxdata.toolName); + generateCostTermStruct("continuous", inputs.toolName); integrand = calcTreatmentOptimizationCost( ... - costTermCalculations, allowedTypes, values, modeledValues, auxdata); -integrand = integrand ./ (auxdata.maxIntegral - auxdata.minIntegral); + costTermCalculations, allowedTypes, values, modeledValues, inputs); +integrand = integrand ./ inputs.maxAllowableError; integrand = integrand .^ 2; end diff --git a/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m b/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m index 9a7fef709..06eeb680f 100644 --- a/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m +++ b/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m @@ -70,9 +70,6 @@ end continuousObjective = sum(setup.phase.integral) / length(setup.phase.integral); -if isfield(setup.auxdata, "finalTimeRange") - continuousObjective = continuousObjective / values.time(end); -end output.objective = continuousObjective + discreteObjective; -end \ No newline at end of file +end diff --git a/src/TreatmentOptimization/gpops/convertToGpopsSynergyDrivenInputs.m b/src/TreatmentOptimization/gpops/convertToGpopsSynergyDrivenInputs.m index f7c221be9..f3ac70b9f 100644 --- a/src/TreatmentOptimization/gpops/convertToGpopsSynergyDrivenInputs.m +++ b/src/TreatmentOptimization/gpops/convertToGpopsSynergyDrivenInputs.m @@ -62,10 +62,6 @@ 0.5]; end end - if isfield(inputs, "finalTimeRange") - bounds.phase.finaltime.lower = guess.phase.time(end) - (0.5 - guess.phase.time(end)); - bounds.phase.finaltime.upper = 0.5; - end end end diff --git a/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m b/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m index aa499924c..ff4454011 100644 --- a/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m +++ b/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m @@ -62,10 +62,6 @@ 0.5]; end end - if isfield(inputs, "finalTimeRange") - bounds.phase.finaltime.lower = guess.phase.time(end) - (0.5 - guess.phase.time(end)); - bounds.phase.finaltime.upper = 0.5; - end end end diff --git a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlInitialGuess.m b/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlInitialGuess.m index 4058922f4..c8b9cddd2 100644 --- a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlInitialGuess.m +++ b/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlInitialGuess.m @@ -75,6 +75,6 @@ controlTorquesGuess], inputs.maxControl, inputs.minControl); end end -guess.phase.integral = scaleToBounds(1e1, inputs.maxIntegral, ... - inputs.minIntegral); -end \ No newline at end of file +guess.phase.integral = scaleToBounds(1e1, inputs.maxAllowableError, ... + zeros(size(inputs.maxAllowableError))); +end diff --git a/src/core/TreatmentOptimization/SetupBounds/getIntegralBounds.m b/src/core/TreatmentOptimization/SetupBounds/makeMaxAllowableError.m similarity index 79% rename from src/core/TreatmentOptimization/SetupBounds/getIntegralBounds.m rename to src/core/TreatmentOptimization/SetupBounds/makeMaxAllowableError.m index a7d046336..6abd0f8db 100644 --- a/src/core/TreatmentOptimization/SetupBounds/getIntegralBounds.m +++ b/src/core/TreatmentOptimization/SetupBounds/makeMaxAllowableError.m @@ -28,30 +28,28 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function inputs = getIntegralBounds(inputs) -[~, continuousAllowedTypes] = generateCostTermStruct("continuous", inputs.toolName); -[~, discreteAllowedTypes] = generateCostTermStruct("discrete", inputs.toolName); +function maxAllowableError = getIntegralBounds(toolName, costTerms) +[~, continuousAllowedTypes] = generateCostTermStruct("continuous", toolName); +[~, discreteAllowedTypes] = generateCostTermStruct("discrete", toolName); -inputs.maxIntegral = []; -inputs.minIntegral = []; -for i = 1:length(inputs.costTerms) - costTerm = inputs.costTerms{i}; +maxAllowableError = []; +for i = 1:length(costTerms) + costTerm = costTerms{i}; if costTerm.isEnabled if any(ismember(costTerm.type, continuousAllowedTypes)) && ... ~strcmp(costTerm.type, "user_defined") - inputs.maxIntegral = cat(2, inputs.maxIntegral, ... + maxAllowableError = cat(2, maxAllowableError, ... costTerm.maxAllowableError); elseif strcmp(costTerm.type, "user_defined") if strcmp(costTerm.cost_term_type, "continuous") - inputs.maxIntegral = cat(2, inputs.maxIntegral, ... + maxAllowableError = cat(2, maxAllowableError, ... costTerm.maxAllowableError); end - elseif any(ismember(costTerm.type, continuousAllowedTypes)) && ... - any(ismember(costTerm.type, discreteAllowedTypes)) + elseif ~any(ismember(costTerm.type, continuousAllowedTypes)) || ... + ~any(ismember(costTerm.type, discreteAllowedTypes)) throw(MException('', ['Cost term type ' costTerm.type ... ' does not exist for this tool.'])) end end end -inputs.minIntegral = zeros(1, length(inputs.maxIntegral)); -end \ No newline at end of file +end diff --git a/src/core/TreatmentOptimization/SetupBounds/getDesignVariableInputBounds.m b/src/core/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m similarity index 96% rename from src/core/TreatmentOptimization/SetupBounds/getDesignVariableInputBounds.m rename to src/core/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m index b6dd9b7e3..6f50e0e4c 100644 --- a/src/core/TreatmentOptimization/SetupBounds/getDesignVariableInputBounds.m +++ b/src/core/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m @@ -32,12 +32,12 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function inputs = getDesignVariableInputBounds(inputs) -if isfield(inputs, "finalTimeRange") - inputs.maxTime = max(inputs.experimentalTime) + inputs.finalTimeRange; -else - inputs.maxTime = max(inputs.experimentalTime); +function inputs = makeOptimalControlBounds(inputs) +inputs = makeStateBounds(inputs); +inputs = makeControlBounds(inputs); end + +function inputs = makeStateBounds(inputs) inputs.minTime = min(inputs.experimentalTime); maxStatePositions = max(inputs.experimentalJointAngles) + ... @@ -55,7 +55,9 @@ inputs.maxState = [maxStatePositions maxStateVelocities maxStateAccelerations]; inputs.minState = [minStatePositions minStateVelocities minStateAccelerations]; +end +function inputs = makeControlBounds(inputs) maxControlJerks = max(inputs.experimentalJointJerks) + ... inputs.controlJerksMultiple * range(inputs.experimentalJointJerks); minControlJerks = min(inputs.experimentalJointJerks) - ... diff --git a/src/core/TreatmentOptimization/SetupBounds/getPathConstraintBounds.m b/src/core/TreatmentOptimization/SetupBounds/makePathConstraintBounds.m similarity index 96% rename from src/core/TreatmentOptimization/SetupBounds/getPathConstraintBounds.m rename to src/core/TreatmentOptimization/SetupBounds/makePathConstraintBounds.m index cda10fc8f..3a0d966b7 100644 --- a/src/core/TreatmentOptimization/SetupBounds/getPathConstraintBounds.m +++ b/src/core/TreatmentOptimization/SetupBounds/makePathConstraintBounds.m @@ -1,7 +1,7 @@ % This function is part of the NMSM Pipeline, see file for full license. % % This function gathers the maximum and minimum bounds for all path -% constraint terms. +% constraint terms. % % (struct) -> (struct) % Computes max and min path bounds @@ -28,7 +28,7 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function inputs = getPathConstraintBounds(inputs) +function inputs = makePathConstraintBounds(inputs) inputs.maxPath = []; inputs.minPath = []; for i = 1:length(inputs.path) @@ -40,4 +40,4 @@ constraintTerm.minError); end end -end \ No newline at end of file +end diff --git a/src/core/TreatmentOptimization/SetupBounds/getTerminalConstraintBounds.m b/src/core/TreatmentOptimization/SetupBounds/makeTerminalConstraintBounds.m similarity index 97% rename from src/core/TreatmentOptimization/SetupBounds/getTerminalConstraintBounds.m rename to src/core/TreatmentOptimization/SetupBounds/makeTerminalConstraintBounds.m index 4317d4fa3..cd4dad8c8 100644 --- a/src/core/TreatmentOptimization/SetupBounds/getTerminalConstraintBounds.m +++ b/src/core/TreatmentOptimization/SetupBounds/makeTerminalConstraintBounds.m @@ -1,7 +1,7 @@ % This function is part of the NMSM Pipeline, see file for full license. % % This function gathers the maximum and minimum bounds for all terminal -% constraint terms. +% constraint terms. % % (struct) -> (struct) % Computes max and min terminal bounds @@ -28,7 +28,7 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function inputs = getTerminalConstraintBounds(inputs) +function inputs = makeTerminalConstraintBounds(inputs) inputs.maxTerminal = []; inputs.minTerminal = []; for i = 1:length(inputs.terminal) @@ -54,4 +54,4 @@ end end end -end \ No newline at end of file +end diff --git a/src/core/TreatmentOptimization/getSplines.m b/src/core/TreatmentOptimization/makeExperimentalDataSplines.m similarity index 95% rename from src/core/TreatmentOptimization/getSplines.m rename to src/core/TreatmentOptimization/makeExperimentalDataSplines.m index a971b409e..4c2173dfc 100644 --- a/src/core/TreatmentOptimization/getSplines.m +++ b/src/core/TreatmentOptimization/makeExperimentalDataSplines.m @@ -1,7 +1,7 @@ % This function is part of the NMSM Pipeline, see file for full license. % % This function calculates the splines for all experimental data. These -% splines are evaluated at the collocation points to allow tracking of +% splines are evaluated at the collocation points to allow tracking of % these quanitites during treatment optimization % % (struct) -> (struct) @@ -15,7 +15,7 @@ % National Institutes of Health (R01 EB030520). % % % % Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % +% Author(s): Marleny Vega, Claire V. Hammond % % % % Licensed under the Apache License, Version 2.0 (the "License"); % % you may not use this file except in compliance with the License. % @@ -29,7 +29,7 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function inputs = getSplines(inputs) +function inputs = makeExperimentalDataSplines(inputs) inputs.splineJointAngles = spaps(inputs.experimentalTime, ... inputs.experimentalJointAngles', 0.0000001); inputs.splineJointMoments = spaps(inputs.experimentalTime, ... @@ -46,4 +46,4 @@ spaps(inputs.experimentalTime, ... inputs.contactSurfaces{i}.experimentalGroundReactionMoments', 0.0000001); end -end \ No newline at end of file +end diff --git a/src/core/TreatmentOptimization/getStateDerivatives.m b/src/core/TreatmentOptimization/makeStateDerivatives.m similarity index 100% rename from src/core/TreatmentOptimization/getStateDerivatives.m rename to src/core/TreatmentOptimization/makeStateDerivatives.m diff --git a/src/core/TreatmentOptimization/makeTreatmentOptimizationInputs.m b/src/core/TreatmentOptimization/makeTreatmentOptimizationInputs.m index 866f469a9..4c7c02e5b 100644 --- a/src/core/TreatmentOptimization/makeTreatmentOptimizationInputs.m +++ b/src/core/TreatmentOptimization/makeTreatmentOptimizationInputs.m @@ -28,15 +28,14 @@ % ----------------------------------------------------------------------- % function inputs = makeTreatmentOptimizationInputs(inputs, params) -inputs = getStateDerivatives(inputs); +inputs = makeStateDerivatives(inputs, params); inputs = setupGroundContact(inputs); -inputs = getSplines(inputs); -inputs = checkStateGuess(inputs); -inputs = checkControlAndParameterGuess(inputs); -inputs = checkParameterGuess(inputs); -inputs = getIntegralBounds(inputs); +inputs = makeExperimentalDataSplines(inputs); +inputs = checkInitialGuess(inputs); +inputs.makeMaxAllowableError = ... + makeMaxAllowableError(inputs.toolName, inputs.costTerms); inputs = getPathConstraintBounds(inputs); -inputs = getTerminalConstraintBounds(inputs); -inputs = getDesignVariableInputBounds(inputs); +inputs = makeTerminalConstraintBounds(inputs); +inputs = makeOptimalControlBounds(inputs); end diff --git a/src/core/parse/parseTreatmentOptimizationParams.m b/src/core/parse/parseTreatmentOptimizationParams.m index 995d90fa2..b16c60463 100644 --- a/src/core/parse/parseTreatmentOptimizationParams.m +++ b/src/core/parse/parseTreatmentOptimizationParams.m @@ -25,5 +25,8 @@ % ----------------------------------------------------------------------- % function params = parseTreatmentOptimizationParams(tree) -params = struct(); +experimentalBSplineCutoffFrequency = getFieldByName(tree, 'experimental_spline_cutoff_frequency'); +if isstruct(params.experimentalBSplineCutoffFrequency) + params.experimentalBSplineCutoffFrequency = experimentalBSplineCutoffFrequency.Text; +end end From c9ead9b1b744508f8836f8e922e474946d241d0b Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" Date: Tue, 12 Sep 2023 23:29:25 -0700 Subject: [PATCH 093/365] fix name --- .../TreatmentOptimization/makeTreatmentOptimizationInputs.m | 2 +- src/core/parse/parseTreatmentOptimizationInputs.m | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/core/TreatmentOptimization/makeTreatmentOptimizationInputs.m b/src/core/TreatmentOptimization/makeTreatmentOptimizationInputs.m index 4c7c02e5b..fea9f8ded 100644 --- a/src/core/TreatmentOptimization/makeTreatmentOptimizationInputs.m +++ b/src/core/TreatmentOptimization/makeTreatmentOptimizationInputs.m @@ -34,7 +34,7 @@ inputs = checkInitialGuess(inputs); inputs.makeMaxAllowableError = ... makeMaxAllowableError(inputs.toolName, inputs.costTerms); -inputs = getPathConstraintBounds(inputs); +inputs = makePathConstraintBounds(inputs); inputs = makeTerminalConstraintBounds(inputs); inputs = makeOptimalControlBounds(inputs); end diff --git a/src/core/parse/parseTreatmentOptimizationInputs.m b/src/core/parse/parseTreatmentOptimizationInputs.m index fbb2fd623..991b13142 100644 --- a/src/core/parse/parseTreatmentOptimizationInputs.m +++ b/src/core/parse/parseTreatmentOptimizationInputs.m @@ -34,10 +34,7 @@ inputs.osimx = parseOsimxFileWithCondition(tree, inputs); inputs = parseController(tree, inputs); inputs = parseTreatmentOptimizationDataDirectory(tree, inputs); -inputs.initialGuess = parseInitialGuess(inputs); -inputs = parseOptimalControlSolverSettings( ... - getTextFromField(getFieldByNameOrError(tree, ... - 'optimal_control_solver_settings_file')), inputs); +inputs = parseOptimalControlSolverSettings(tree, inputs); inputs.costTerms = parseRcnlCostTermSet( ... getFieldByNameOrError(tree, 'RCNLCostTermSet').RCNLCostTerm); [inputs.path, inputs.terminal] = parseRcnlConstraintTermSet( ... From 46dbb597faa7fb43644f18f88bbb421ccf96a30f Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" Date: Wed, 13 Sep 2023 00:27:35 -0700 Subject: [PATCH 094/365] change to gpops initial guess --- .../gpops/convertToGpopsTorqueDrivenInputs.m | 2 +- ...nitialGuess.m => setupGpopsInitialGuess.m} | 0 .../parseTreatmentOptimizationDataDirectory.m | 22 +++++++++---------- 3 files changed, 11 insertions(+), 13 deletions(-) rename src/core/TreatmentOptimization/OptimalControlSetupFunctions/{setupCommonOptimalControlInitialGuess.m => setupGpopsInitialGuess.m} (100%) diff --git a/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m b/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m index ff4454011..df8ee3abe 100644 --- a/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m +++ b/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m @@ -26,7 +26,7 @@ function setup = convertToGpopsTorqueDrivenInputs(inputs, params) bounds = setupProblemBounds(inputs, params); -guess = setupCommonOptimalControlInitialGuess(inputs); +guess = setupGpopsInitialGuess(inputs); if strcmp(inputs.toolName, "DesignOptimization") guess = addUserDefinedTermsToGuess(guess, inputs); end diff --git a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlInitialGuess.m b/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m similarity index 100% rename from src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlInitialGuess.m rename to src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m diff --git a/src/core/parse/parseTreatmentOptimizationDataDirectory.m b/src/core/parse/parseTreatmentOptimizationDataDirectory.m index 05e75d0e3..2831fbd88 100644 --- a/src/core/parse/parseTreatmentOptimizationDataDirectory.m +++ b/src/core/parse/parseTreatmentOptimizationDataDirectory.m @@ -95,18 +95,16 @@ parseTrialData(inputs.previousResultsDirectory, ... strcat(inputs.trialName, "_states"), inputs.model); catch; end -if strcmp(inputs.controllerType, "torque") - try - [inputs.initialControl, inputs.initialControlLabels] = ... - parseTrialData(inputs.previousResultsDirectory, ... - strcat(inputs.trialName, "_torqueControls"), inputs.model); - catch;end -elseif strcmp(inputs.controllerType, "synergy") - try - [inputs.initialControl, inputs.initialControlLabels] = ... - parseTrialData(inputs.previousResultsDirectory, ... - strcat(inputs.trialName, "_synergyCommands"), inputs.model); - catch;end +try + [inputs.initialTorqueControl, inputs.initialTorqueControlLabels] = ... + parseTrialData(inputs.previousResultsDirectory, ... + strcat(inputs.trialName, "_torqueControls"), inputs.model); +catch;end +try + [inputs.initialSynergyControl, inputs.initialSynergyControlLabels] = ... + parseTrialData(inputs.previousResultsDirectory, ... + strcat(inputs.trialName, "_synergyCommands"), inputs.model); +catch;end else throw(MException("IncorrectControllerType", ... "controllerType must be 'torque' or 'synergy'")); From 75807b727e794d53bea6d4e5bf0ed2d5348c2809 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" Date: Wed, 13 Sep 2023 00:34:12 -0700 Subject: [PATCH 095/365] fix torque-driven to torque --- .../TreatmentOptimization/saveCommonOptimalControlResults.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/TreatmentOptimization/saveCommonOptimalControlResults.m b/src/core/TreatmentOptimization/saveCommonOptimalControlResults.m index 5fc6d2875..16ac51ed9 100644 --- a/src/core/TreatmentOptimization/saveCommonOptimalControlResults.m +++ b/src/core/TreatmentOptimization/saveCommonOptimalControlResults.m @@ -61,7 +61,7 @@ function saveCommonOptimalControlResults(solution, inputs, values) writeToSto(controlLabels, values.time, [values.controlJerks ... commands], fullfile(inputs.resultsDirectory, ... strcat(inputs.trialName, "_synergyCommands.sto"))); -elseif strcmp(inputs.controllerType, 'torque_driven') +elseif strcmp(inputs.controllerType, 'torque') controlLabels = inputs.coordinateNames; for i = 1 : inputs.numTorqueControls controlLabels{end + 1} = strcat('torqueControl', num2str(i)); @@ -116,4 +116,4 @@ function saveGroundReactionResults(solution, inputs, values, outputDirectory) groundContactData, fullfile(inputs.resultsDirectory, "GRFData", ... strcat(inputs.trialName, ".sto"))); end -end \ No newline at end of file +end From b3e9b3b0a7d617d4de5d6e1c4ec363f52054d135 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Wed, 13 Sep 2023 17:52:25 -0500 Subject: [PATCH 096/365] fix torque controller parse --- src/core/parse/parseController.m | 2 +- src/core/parse/parseTorqueController.m | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/core/parse/parseController.m b/src/core/parse/parseController.m index ef7ce3729..5ff89a1ab 100644 --- a/src/core/parse/parseController.m +++ b/src/core/parse/parseController.m @@ -37,7 +37,7 @@ torqueTree = getFieldByName(tree, "RCNLTorqueController"); if isstruct(torqueTree) - inputs = parseTorqueController(tree, inputs); + inputs = parseTorqueController(torqueTree, inputs); end synergyTree = getFieldByName(tree, "RCNLSynergyController"); if isstruct(synergyTree) diff --git a/src/core/parse/parseTorqueController.m b/src/core/parse/parseTorqueController.m index c1ed1aa65..5646f9413 100644 --- a/src/core/parse/parseTorqueController.m +++ b/src/core/parse/parseTorqueController.m @@ -30,9 +30,8 @@ % ----------------------------------------------------------------------- % function inputs = parseTorqueController(tree, inputs) -inputs.controlTorqueNames = parseSpaceSeparatedList(tree, ... - "torque_controller_coordinate_list"); +inputs.torqueControllerCoordinateNames = parseSpaceSeparatedList(tree, ... + "coordinate_list"); inputs.maxControlTorquesMultiple = parseDoubleOrAlternate(tree, ... 'torque_controls_range_scale_factor', 1); -inputs.numTorqueControls = length(inputs.controlTorqueNames); end From cfc11c6a21a479d7416a1ca1935fa09961b2eaea Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Wed, 13 Sep 2023 17:53:33 -0500 Subject: [PATCH 097/365] fix fn name --- src/TrackingOptimization/saveTrackingOptimizationResults.m | 2 +- .../saveVerificationOptimizationResults.m | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/TrackingOptimization/saveTrackingOptimizationResults.m b/src/TrackingOptimization/saveTrackingOptimizationResults.m index 4a155f1e7..886efe82c 100644 --- a/src/TrackingOptimization/saveTrackingOptimizationResults.m +++ b/src/TrackingOptimization/saveTrackingOptimizationResults.m @@ -30,7 +30,7 @@ function saveTrackingOptimizationResults(solution, inputs) values = getTrackingOptimizationValueStruct(solution.solution.phase, inputs); -saveCommonOptimalControlResults(solution, inputs, values); +saveTreatmentOptimizationResults(solution, inputs, values); if strcmp(inputs.controllerType, 'synergy') writeToSto(inputs.muscleLabels, linspace(1, inputs.numSynergies, ... inputs.numSynergies), [values.synergyWeights], ... diff --git a/src/VerificationOptimization/saveVerificationOptimizationResults.m b/src/VerificationOptimization/saveVerificationOptimizationResults.m index eb8daf0be..51f5b4354 100644 --- a/src/VerificationOptimization/saveVerificationOptimizationResults.m +++ b/src/VerificationOptimization/saveVerificationOptimizationResults.m @@ -30,5 +30,5 @@ function saveVerificationOptimizationResults(solution, inputs) values = getVerificationOptimizationValueStruct( ... solution.solution.phase, inputs); -saveCommonOptimalControlResults(solution, inputs, values) +saveTreatmentOptimizationResults(solution, inputs, values) end \ No newline at end of file From c4a24d1a928708dd8d88d4b7d63f8608cfc4938c Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Wed, 13 Sep 2023 17:55:00 -0500 Subject: [PATCH 098/365] fix names --- .../getDesignOptimizationValueStruct.m | 2 +- .../gpops/convertToGpopsSynergyDrivenInputs.m | 2 +- .../calcTrackingControllerIntegrand.m | 2 +- .../setupCommonOptimalControlBounds.m | 4 +-- ...calcTorqueActuatedMomentsPathConstraints.m | 8 ++--- .../SetupBounds/makeMaxAllowableError.m | 2 +- .../SetupBounds/makeOptimalControlBounds.m | 7 +++-- .../makeTreatmentOptimizationInputs.m | 3 +- .../reportTreatmentOptimizationResults.m | 2 +- .../parseTreatmentOptimizationDataDirectory.m | 29 ++++++++++--------- 10 files changed, 31 insertions(+), 30 deletions(-) diff --git a/src/DesignOptimization/getDesignOptimizationValueStruct.m b/src/DesignOptimization/getDesignOptimizationValueStruct.m index f77cbcf88..f09ae5d3e 100644 --- a/src/DesignOptimization/getDesignOptimizationValueStruct.m +++ b/src/DesignOptimization/getDesignOptimizationValueStruct.m @@ -69,7 +69,7 @@ inputs.control, 1), 1) .* params.maxControl, ... ones(size(inputs.control, 1), 1) .* params.minControl); values.externalTorqueControls = controls(:, params.numCoordinates + ... - params.numTorqueControls + 1 : end); + length(inputs.torqueControllerCoordinateNames) + 1 : end); end end if isfield(params, 'userDefinedVariables') diff --git a/src/TreatmentOptimization/gpops/convertToGpopsSynergyDrivenInputs.m b/src/TreatmentOptimization/gpops/convertToGpopsSynergyDrivenInputs.m index f3ac70b9f..16cc077e0 100644 --- a/src/TreatmentOptimization/gpops/convertToGpopsSynergyDrivenInputs.m +++ b/src/TreatmentOptimization/gpops/convertToGpopsSynergyDrivenInputs.m @@ -26,7 +26,7 @@ function setup = convertToGpopsSynergyDrivenInputs(inputs, params) bounds = setupProblemBounds(inputs, params); -guess = setupCommonOptimalControlInitialGuess(inputs); +guess = setupGpopsInitialGuess(inputs); if strcmp(inputs.toolName, "DesignOptimization") guess = addUserDefinedTermsToGuess(guess, inputs); end diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m b/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m index 9e600d495..78e4460d9 100644 --- a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m +++ b/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m @@ -45,7 +45,7 @@ indx1 = find(strcmp(convertCharsToStrings( ... auxdata.inverseDynamicMomentLabels), controllerName)); indx2 = find(strcmp(convertCharsToStrings( ... - strcat(auxdata.controlTorqueNames, '_moment')), ... + strcat(auxdata.torqueControllerCoordinateNames, '_moment')), ... controllerName)); if auxdata.splineJointMoments.dim > 1 experimentalJointMoments = ... diff --git a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlBounds.m b/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlBounds.m index 41a6374e5..58c7c35e3 100644 --- a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlBounds.m +++ b/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlBounds.m @@ -49,9 +49,9 @@ bounds.phase.control.lower = -0.5 * ones(1, length(inputs.minControl)); bounds.phase.control.upper = 0.5 * ones(1, length(inputs.minControl)); % setup integral bounds -bounds.phase.integral.lower = zeros(1, length(inputs.minIntegral)); +bounds.phase.integral.lower = zeros(1, length(inputs.maxAllowableError)); bounds.phase.integral.upper = inputs.gpops.integralBound * ... - ones(1, length(inputs.minIntegral)); + ones(1, length(inputs.maxAllowableError)); % setup terminal constraint bounds if ~isempty(inputs.minTerminal) bounds.eventgroup.lower = inputs.minTerminal; diff --git a/src/core/TreatmentOptimization/PathTerms/calcTorqueActuatedMomentsPathConstraints.m b/src/core/TreatmentOptimization/PathTerms/calcTorqueActuatedMomentsPathConstraints.m index 8f05a4972..a97145e4f 100644 --- a/src/core/TreatmentOptimization/PathTerms/calcTorqueActuatedMomentsPathConstraints.m +++ b/src/core/TreatmentOptimization/PathTerms/calcTorqueActuatedMomentsPathConstraints.m @@ -29,14 +29,14 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function pathTerm = calcTorqueActuatedMomentsPathConstraints(params, ... +function pathTerm = calcTorqueActuatedMomentsPathConstraints(inputs, ... modeledValues, controlTorques, loadName) loadName = erase(loadName, '_moment'); loadName = erase(loadName, '_force'); -indx1 = find(cellfun(@isequal, params.coordinateNames, ... - repmat({loadName}, 1, length(params.coordinateNames)))); -indx2 = find(strcmp(params.controlTorqueNames, loadName)); +indx1 = find(cellfun(@isequal, inputs.coordinateNames, ... + repmat({loadName}, 1, length(inputs.coordinateNames)))); +indx2 = find(strcmp(inputs.torqueControllerCoordinateNames, loadName)); pathTerm = modeledValues.inverseDynamicMoments(:, indx1) - ... controlTorques(:, indx2); end \ No newline at end of file diff --git a/src/core/TreatmentOptimization/SetupBounds/makeMaxAllowableError.m b/src/core/TreatmentOptimization/SetupBounds/makeMaxAllowableError.m index 6abd0f8db..ab7a53602 100644 --- a/src/core/TreatmentOptimization/SetupBounds/makeMaxAllowableError.m +++ b/src/core/TreatmentOptimization/SetupBounds/makeMaxAllowableError.m @@ -28,7 +28,7 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function maxAllowableError = getIntegralBounds(toolName, costTerms) +function maxAllowableError = makeMaxAllowableError(toolName, costTerms) [~, continuousAllowedTypes] = generateCostTermStruct("continuous", toolName); [~, discreteAllowedTypes] = generateCostTermStruct("discrete", toolName); diff --git a/src/core/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m b/src/core/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m index 6f50e0e4c..8b0149391 100644 --- a/src/core/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m +++ b/src/core/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m @@ -38,6 +38,7 @@ end function inputs = makeStateBounds(inputs) +inputs.maxTime = max(inputs.experimentalTime); inputs.minTime = min(inputs.experimentalTime); maxStatePositions = max(inputs.experimentalJointAngles) + ... @@ -75,14 +76,14 @@ inputs.minParameter = zeros(1, inputs.numSynergyWeights); end elseif strcmp(inputs.controllerType, 'torque') - for i = 1:length(inputs.controlTorqueNames) + for i = 1:length(inputs.torqueControllerCoordinateNames) indx = find(strcmp(convertCharsToStrings( ... inputs.inverseDynamicMomentLabels), ... - strcat(inputs.controlTorqueNames(i), '_moment'))); + strcat(inputs.torqueControllerCoordinateNames(i), '_moment'))); if isempty(indx) indx = find(strcmp(convertCharsToStrings( ... inputs.inverseDynamicMomentLabels), ... - strcat(inputs.controlTorqueNames(i), '_force'))); + strcat(inputs.torqueControllerCoordinateNames(i), '_force'))); end maxControlTorques(i) = max(inputs.experimentalJointMoments(:, ... indx)) + inputs.maxControlTorquesMultiple * ... diff --git a/src/core/TreatmentOptimization/makeTreatmentOptimizationInputs.m b/src/core/TreatmentOptimization/makeTreatmentOptimizationInputs.m index fea9f8ded..24c78e4ce 100644 --- a/src/core/TreatmentOptimization/makeTreatmentOptimizationInputs.m +++ b/src/core/TreatmentOptimization/makeTreatmentOptimizationInputs.m @@ -31,8 +31,7 @@ inputs = makeStateDerivatives(inputs, params); inputs = setupGroundContact(inputs); inputs = makeExperimentalDataSplines(inputs); -inputs = checkInitialGuess(inputs); -inputs.makeMaxAllowableError = ... +inputs.maxAllowableError = ... makeMaxAllowableError(inputs.toolName, inputs.costTerms); inputs = makePathConstraintBounds(inputs); inputs = makeTerminalConstraintBounds(inputs); diff --git a/src/core/TreatmentOptimization/reportTreatmentOptimizationResults.m b/src/core/TreatmentOptimization/reportTreatmentOptimizationResults.m index 5e14f8945..4ab4213b6 100644 --- a/src/core/TreatmentOptimization/reportTreatmentOptimizationResults.m +++ b/src/core/TreatmentOptimization/reportTreatmentOptimizationResults.m @@ -52,7 +52,7 @@ function reportTreatmentOptimizationResults(solution, inputs) else % plot torque controls plotResultsWithOutComparison(values.controlTorques, values.time, ... - inputs.controlTorqueNames, ["Torque" "Controls"]); + inputs.torqueControllerCoordinateNames, ["Torque" "Controls"]); end % plot external torque controls if isfield(inputs, 'enableExternalTorqueControl') diff --git a/src/core/parse/parseTreatmentOptimizationDataDirectory.m b/src/core/parse/parseTreatmentOptimizationDataDirectory.m index 2831fbd88..68de9b21a 100644 --- a/src/core/parse/parseTreatmentOptimizationDataDirectory.m +++ b/src/core/parse/parseTreatmentOptimizationDataDirectory.m @@ -33,8 +33,8 @@ function inputs = parseTreatmentOptimizationDataDirectory(tree, inputs) [dataDirectory, inputs.previousResultsDirectory] = findDataDirectory(tree, inputs); inputs.trialName = parseTrialName(tree); -inputs = parseExperimentalData(tree, inputs); -inputs = parseSynergyExperimentalData(tree, inputs); +inputs = parseExperimentalData(tree, inputs, dataDirectory); +inputs = parseSynergyExperimentalData(tree, inputs, dataDirectory); inputs = parseInitialValues(tree, inputs); inputs.numCoordinates = size(inputs.experimentalJointAngles, 2); end @@ -53,7 +53,7 @@ end end -function inputs = parseExperimentalData(tree, inputs) +function inputs = parseExperimentalData(tree, inputs, dataDirectory) [inputs.experimentalJointMoments, ... inputs.inverseDynamicMomentLabels] = ... parseTrialDataTryDirectories( ... @@ -71,15 +71,15 @@ end end -function inputs = parseSynergyExperimentalData(tree, inputs) -if strcmp(inputs.controllerType, 'synergy') +function inputs = parseSynergyExperimentalData(tree, inputs, dataDirectory) +if strcmp(inputs.controllerType, "synergy") [inputs.experimentalMuscleActivations, inputs.muscleLabels] = ... parseTrialData(inputs.previousResultsDirectory, ... strcat(inputs.trialName, "_combinedActivations"), inputs.model); inputs.synergyWeights = parseTrialData(inputs.previousResultsDirectory, ... "synergyWeights", inputs.model); directories = findFirstLevelSubDirectoriesFromPrefixes(fullfile( ... - dataDirectory, "MAData"), prefix); + dataDirectory, "MAData"), inputs.trialName); inputs.momentArms = parseSelectMomentArms(directories, ... inputs.surrogateModelCoordinateNames, inputs.muscleNames); inputs.momentArms = reshape(permute(inputs.momentArms, [1 4 2 3]), [], ... @@ -91,23 +91,24 @@ function inputs = parseInitialValues(tree, inputs) initialGuess = []; try - [inputs.initialState, inputs.initialStateLabels] = ... + [inputs.initialStates, inputs.initialStatesLabels, inputs.initialTime] = ... parseTrialData(inputs.previousResultsDirectory, ... strcat(inputs.trialName, "_states"), inputs.model); catch; end try - [inputs.initialTorqueControl, inputs.initialTorqueControlLabels] = ... + [inputs.initialJerks, inputs.initialJerksLabels] = ... + parseTrialData(inputs.previousResultsDirectory, ... + strcat(inputs.trialName, "_jerks"), inputs.model); +catch; end +try + [inputs.initialTorqueControls, inputs.initialTorqueControlsLabels] = ... parseTrialData(inputs.previousResultsDirectory, ... strcat(inputs.trialName, "_torqueControls"), inputs.model); catch;end -try - [inputs.initialSynergyControl, inputs.initialSynergyControlLabels] = ... +if strcmp(inputs.controllerType, "synergy") + [inputs.initialSynergyControls, inputs.initialSynergyControlsLabels] = ... parseTrialData(inputs.previousResultsDirectory, ... strcat(inputs.trialName, "_synergyCommands"), inputs.model); -catch;end -else - throw(MException("IncorrectControllerType", ... - "controllerType must be 'torque' or 'synergy'")); end end From d68a06c0b6c901f95bfe77f45b5e5139d474af49 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Wed, 13 Sep 2023 17:55:25 -0500 Subject: [PATCH 099/365] add bspline cutoff element --- src/core/TreatmentOptimization/makeStateDerivatives.m | 6 ++++-- .../optimal_control/parseOptimalControlSolverSettings.m | 2 +- src/core/parse/parseTreatmentOptimizationParams.m | 3 ++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/core/TreatmentOptimization/makeStateDerivatives.m b/src/core/TreatmentOptimization/makeStateDerivatives.m index c93c7d314..64cea68ca 100644 --- a/src/core/TreatmentOptimization/makeStateDerivatives.m +++ b/src/core/TreatmentOptimization/makeStateDerivatives.m @@ -28,11 +28,13 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function inputs = getStateDerivatives(inputs) +function inputs = makeStateDerivatives(inputs, params) points = length(inputs.experimentalTime); interval = inputs.experimentalTime(2) - inputs.experimentalTime(1); +cutoffFrequency = ... + valueOrAlternate(params, "experimentalBSplineCutoffFrequency", 6); numNodes = splFitWithCutoff(inputs.experimentalTime', ... - inputs.experimentalJointAngles', 4, 5); + inputs.experimentalJointAngles', cutoffFrequency, 5); [N, Np, Npp] = BSplineMatrices(5, numNodes, points, interval); Nodes = N\inputs.experimentalJointAngles; inputs.experimentalJointVelocities = Np * Nodes; diff --git a/src/core/optimal_control/parseOptimalControlSolverSettings.m b/src/core/optimal_control/parseOptimalControlSolverSettings.m index 11122c4b0..fea19b709 100644 --- a/src/core/optimal_control/parseOptimalControlSolverSettings.m +++ b/src/core/optimal_control/parseOptimalControlSolverSettings.m @@ -27,7 +27,7 @@ function inputs = parseOptimalControlSolverSettings( ... tree, inputs) settingsFileName = getTextFromField(getFieldByNameOrError(tree, ... - 'optimal_control_solver_settings_file')) + 'optimal_control_solver_settings_file')); solverSettingsTree = xml2struct(settingsFileName); verifyVersion(solverSettingsTree, "OptimalControlSolverSettings"); tree = ... diff --git a/src/core/parse/parseTreatmentOptimizationParams.m b/src/core/parse/parseTreatmentOptimizationParams.m index b16c60463..f1d5e34e5 100644 --- a/src/core/parse/parseTreatmentOptimizationParams.m +++ b/src/core/parse/parseTreatmentOptimizationParams.m @@ -25,8 +25,9 @@ % ----------------------------------------------------------------------- % function params = parseTreatmentOptimizationParams(tree) +params = struct(); experimentalBSplineCutoffFrequency = getFieldByName(tree, 'experimental_spline_cutoff_frequency'); -if isstruct(params.experimentalBSplineCutoffFrequency) +if isstruct(experimentalBSplineCutoffFrequency) params.experimentalBSplineCutoffFrequency = experimentalBSplineCutoffFrequency.Text; end end From ba4c10b8a70175dadec3741a0d1219def25ec4f9 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Wed, 13 Sep 2023 17:55:41 -0500 Subject: [PATCH 100/365] update initial guess fn --- .../DesignOptimizationTool.m | 2 +- .../gpops/makeGpopsValuesAsStruct.m | 4 +- ...ndParameterGuess.m => checkControlGuess.m} | 2 +- .../setupGpopsInitialGuess.m | 79 ++++++++++--------- .../getTreatmentOptimizationValueStruct.m | 26 +++--- ...s.m => saveTreatmentOptimizationResults.m} | 24 +++--- 6 files changed, 72 insertions(+), 65 deletions(-) rename src/core/TreatmentOptimization/OptimalControlSetupFunctions/{checkControlAndParameterGuess.m => checkControlGuess.m} (98%) rename src/core/TreatmentOptimization/{saveCommonOptimalControlResults.m => saveTreatmentOptimizationResults.m} (87%) diff --git a/src/DesignOptimization/DesignOptimizationTool.m b/src/DesignOptimization/DesignOptimizationTool.m index e15101ed3..d30257b79 100644 --- a/src/DesignOptimization/DesignOptimizationTool.m +++ b/src/DesignOptimization/DesignOptimizationTool.m @@ -42,7 +42,7 @@ function DesignOptimizationTool(settingsFileName) function inputs = setupMuscleSynergies(inputs) if strcmp(inputs.controllerType, 'synergy') - inputs.splineSynergyActivations = spaps(inputs.initialGuess.time/inputs.initialGuess.time(end), ... + inputs.splineSynergyActivations = spaps(inputs.initialGuess.time, ... inputs.initialGuess.control(:, inputs.numCoordinates + 1:end)', 0.0000001); inputs.synergyLabels = inputs.initialGuess.controlLabels(:, inputs.numCoordinates + 1:end); end diff --git a/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m b/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m index 334f2cc8a..7a33413f6 100644 --- a/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m +++ b/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m @@ -42,7 +42,7 @@ if strcmp(inputs.controllerType, 'torque') values.controlTorques = control(:, inputs.numCoordinates + 1 : ... - inputs.numCoordinates + inputs.numTorqueControls); + inputs.numCoordinates + length(inputs.torqueControllerCoordinateNames)); else values.controlSynergyActivations = control(:, ... inputs.numCoordinates + 1 : inputs.numCoordinates + inputs.numSynergies); @@ -102,7 +102,7 @@ phase.control, 1), 1) .* inputs.maxControl, ... ones(size(phase.control, 1), 1) .* inputs.minControl); values.externalTorqueControls = controls(:, inputs.numCoordinates + ... - inputs.numTorqueControls + 1 : end); + length(inputs.torqueControllerCoordinateNames) + 1 : end); end end if isfield(inputs, 'userDefinedVariables') diff --git a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/checkControlAndParameterGuess.m b/src/core/TreatmentOptimization/OptimalControlSetupFunctions/checkControlGuess.m similarity index 98% rename from src/core/TreatmentOptimization/OptimalControlSetupFunctions/checkControlAndParameterGuess.m rename to src/core/TreatmentOptimization/OptimalControlSetupFunctions/checkControlGuess.m index c895d9df1..4d601edc4 100644 --- a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/checkControlAndParameterGuess.m +++ b/src/core/TreatmentOptimization/OptimalControlSetupFunctions/checkControlGuess.m @@ -28,7 +28,7 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function inputs = checkControlAndParameterGuess(inputs) +function inputs = checkControlGuess(inputs) if isfield(inputs.initialGuess, 'control') newControls = zeros(size(inputs.initialGuess.control, 1), 0); newLabels = string([]); diff --git a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m b/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m index c8b9cddd2..e4112496b 100644 --- a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m +++ b/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m @@ -15,7 +15,7 @@ % National Institutes of Health (R01 EB030520). % % % % Copyright (c) 2021 Rice University and the Authors % -% Author(s): Claire V. Hammond, Marleny Vega % +% Author(s): Claire V. Hammond % % % % Licensed under the Apache License, Version 2.0 (the "License"); % % you may not use this file except in compliance with the License. % @@ -29,12 +29,21 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function guess = setupCommonOptimalControlInitialGuess(inputs) -if isfield(inputs.initialGuess, 'state') - guess.phase.time = scaleToBounds(inputs.initialGuess.time, inputs.maxTime, ... - inputs.minTime); - guess.phase.state = scaleToBounds(inputs.initialGuess.state, ... +function guess = setupGpopsInitialGuess(inputs) +guess = struct(); +guess = setupInitialStatesGuess(inputs, guess); +guess = setupInitialControlsGuess(inputs, guess); +guess = setupInitialParametersGuess(inputs, guess); +guess.phase.integral = scaleToBounds(1e1, inputs.maxAllowableError, ... + zeros(size(inputs.maxAllowableError))); +end + +function guess = setupInitialStatesGuess(inputs, guess) +if isfield(inputs, "initialState") + guess.phase.state = scaleToBounds(inputs.initialState, ... inputs.maxState, inputs.minState); + guess.phase.time = scaleToBounds(inputs.initialTime, inputs.maxTime, ... + inputs.minTime); else guess.phase.state = scaleToBounds([inputs.experimentalJointAngles ... inputs.experimentalJointVelocities ... @@ -43,38 +52,34 @@ guess.phase.time = scaleToBounds(inputs.experimentalTime, inputs.maxTime, ... inputs.minTime); end -if strcmp(inputs.controllerType, 'synergy') - if isfield(inputs.initialGuess, 'control') - guess.phase.control = scaleToBounds(inputs.initialGuess.control, ... - inputs.maxControl, inputs.minControl); - else - guess.phase.control = scaleToBounds([inputs.experimentalJointJerks ... - inputs.synergyActivationsGuess], inputs.maxControl, inputs.minControl); - end - if inputs.optimizeSynergyVectors - guess.parameter = scaleToBounds(inputs.synergyWeightsGuess, ... - inputs.maxParameter, inputs.minParameter); - end -elseif strcmp(inputs.controllerType, 'torque') - if isfield(inputs.initialGuess, 'control') - guess.phase.control = scaleToBounds(inputs.initialGuess.control, ... - inputs.maxControl, inputs.minControl); +end + +function guess = setupInitialControlsGuess(inputs, guess) +if isfield(inputs, "initialJerks") + controls = inputs.initialJerks; +else + controls = inputs.experimentalJointJerks; +end +if strcmp(inputs.controllerType, "synergy") + if isfield(inputs, "initialSynergyControls") + controls = [controls, inputs.initialSynergyControls]; else - for i = 1:length(inputs.controlTorqueNames) - indx = find(strcmp(convertCharsToStrings( ... - inputs.inverseDynamicMomentLabels), ... - strcat(inputs.controlTorqueNames(i), '_moment'))); - if isempty(indx) - indx = find(strcmp(convertCharsToStrings( ... - inputs.inverseDynamicMomentLabels), ... - strcat(inputs.controlTorqueNames(i), '_force'))); - end - controlTorquesGuess(:, i) = inputs.experimentalJointMoments(:, indx); - end - guess.phase.control = scaleToBounds([inputs.experimentalJointJerks ... - controlTorquesGuess], inputs.maxControl, inputs.minControl); + throw(MException("NoInitialSynergyControls", ... + "initial synergy controls required for synergy driven")) end end -guess.phase.integral = scaleToBounds(1e1, inputs.maxAllowableError, ... - zeros(size(inputs.maxAllowableError))); +if isfield(inputs, "initialTorqueControls") + controls = [controls, inputs.initialTorqueControls]; +else + controls = [controls, inputs.experimentalJointMoments]; +end +guess.phase.control = scaleToBounds(controls, inputs.maxControl, ... + inputs.minControl); +end + +function guess = setupInitialParametersGuess(inputs, guess) +if valueOrAlternate(inputs, "optimizeSynergyVectors", false) + guess.parameter = scaleToBounds(inputs.synergyWeights, ... + inputs.maxParameter, inputs.minParameter); end +end \ No newline at end of file diff --git a/src/core/TreatmentOptimization/getTreatmentOptimizationValueStruct.m b/src/core/TreatmentOptimization/getTreatmentOptimizationValueStruct.m index 9aa58da17..28571dcfe 100644 --- a/src/core/TreatmentOptimization/getTreatmentOptimizationValueStruct.m +++ b/src/core/TreatmentOptimization/getTreatmentOptimizationValueStruct.m @@ -30,24 +30,24 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function values = getTreatmentOptimizationValueStruct(phase, params) +function values = getTreatmentOptimizationValueStruct(phase, inputs) -values.time = scaleToOriginal(phase.time, params.maxTime, ... - params.minTime); +values.time = scaleToOriginal(phase.time, inputs.maxTime, ... + inputs.minTime); state = scaleToOriginal(phase.state, ones(size(phase.state, 1), 1) .* ... - params.maxState, ones(size(phase.state, 1), 1) .* params.minState); + inputs.maxState, ones(size(phase.state, 1), 1) .* inputs.minState); control = scaleToOriginal(phase.control, ones(size(phase.control, 1), 1) .* ... - params.maxControl, ones(size(phase.control, 1), 1) .* params.minControl); -values.statePositions = getCorrectStates(state, 1, params.numCoordinates); -values.stateVelocities = getCorrectStates(state, 2, params.numCoordinates); -values.stateAccelerations = getCorrectStates(state, 3, params.numCoordinates); -values.controlJerks = control(:, 1 : params.numCoordinates); + inputs.maxControl, ones(size(phase.control, 1), 1) .* inputs.minControl); +values.statePositions = getCorrectStates(state, 1, inputs.numCoordinates); +values.stateVelocities = getCorrectStates(state, 2, inputs.numCoordinates); +values.stateAccelerations = getCorrectStates(state, 3, inputs.numCoordinates); +values.controlJerks = control(:, 1 : inputs.numCoordinates); -if ~strcmp(params.controllerType, 'synergy') - values.controlTorques = control(:, params.numCoordinates + 1 : ... - params.numCoordinates + params.numTorqueControls); +if ~strcmp(inputs.controllerType, 'synergy') + values.controlTorques = control(:, inputs.numCoordinates + 1 : ... + inputs.numCoordinates + length(inputs.torqueControllerCoordinateNames)); else values.controlSynergyActivations = control(:, ... - params.numCoordinates + 1 : params.numCoordinates + params.numSynergies); + inputs.numCoordinates + 1 : inputs.numCoordinates + inputs.numSynergies); end end \ No newline at end of file diff --git a/src/core/TreatmentOptimization/saveCommonOptimalControlResults.m b/src/core/TreatmentOptimization/saveTreatmentOptimizationResults.m similarity index 87% rename from src/core/TreatmentOptimization/saveCommonOptimalControlResults.m rename to src/core/TreatmentOptimization/saveTreatmentOptimizationResults.m index 16ac51ed9..3422f5338 100644 --- a/src/core/TreatmentOptimization/saveCommonOptimalControlResults.m +++ b/src/core/TreatmentOptimization/saveTreatmentOptimizationResults.m @@ -29,7 +29,7 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function saveCommonOptimalControlResults(solution, inputs, values) +function saveTreatmentOptimizationResults(solution, inputs, values) if ~exist(inputs.resultsDirectory, 'dir') mkdir(inputs.resultsDirectory) end @@ -48,8 +48,10 @@ function saveCommonOptimalControlResults(solution, inputs, values) writeToSto(stateLabels, values.time, [values.statePositions ... values.stateVelocities values.stateAccelerations], ... fullfile(inputs.resultsDirectory, strcat(inputs.trialName, "_states.sto"))); +writeToSto(inputs.coordinateNames, values.time, values.controlJerks, ... + fullfile(inputs.resultsDirectory, strcat(inputs.trialName, "_jerks.sto"))); if strcmp(inputs.controllerType, 'synergy') - controlLabels = inputs.coordinateNames; + controlLabels = {}; for i = 1 : length(inputs.osimx.synergyGroups) for j = 1 : inputs.osimx.synergyGroups{i}.numSynergies controlLabels{end + 1} = convertStringsToChars( ... @@ -57,17 +59,17 @@ function saveCommonOptimalControlResults(solution, inputs, values) "_", num2str(j))); end end - commands = values.controlSynergyActivations; - writeToSto(controlLabels, values.time, [values.controlJerks ... - commands], fullfile(inputs.resultsDirectory, ... + writeToSto(controlLabels, values.time, values.controlSynergyActivations, ... + fullfile(inputs.resultsDirectory, ... strcat(inputs.trialName, "_synergyCommands.sto"))); -elseif strcmp(inputs.controllerType, 'torque') - controlLabels = inputs.coordinateNames; - for i = 1 : inputs.numTorqueControls - controlLabels{end + 1} = strcat('torqueControl', num2str(i)); +end +if ~isempty(inputs.torqueControllerCoordinateNames) + controlLabels = {}; + for i = 1 : length(inputs.torqueControllerCoordinateNames) + controlLabels{end + 1} = inputs.torqueControllerCoordinateNames{i}; end - writeToSto(controlLabels, values.time, [values.controlJerks ... - values.controlTorques], fullfile(inputs.resultsDirectory, ... + writeToSto(controlLabels, values.time, values.controlTorques, ... + fullfile(inputs.resultsDirectory, ... strcat(inputs.trialName, "_torqueControls.sto"))); end delete(inputs.mexModel); From 634cee239160b2705d1c72f6640b5c4c726a4a44 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Wed, 13 Sep 2023 20:48:09 -0500 Subject: [PATCH 101/365] fix initial guess bugs --- .../getTrackingOptimizationValueStruct.m | 2 +- .../gpops/makeGpopsValuesAsStruct.m | 4 +- .../VerificationOptimizationTool.m | 6 +-- .../checkParameterGuess.m | 1 + .../setupGpopsInitialGuess.m | 4 +- .../makeSurrogateModel.m | 49 +++++++++++++++++++ .../makeTreatmentOptimizationInputs.m | 1 + .../saveTreatmentOptimizationResults.m | 2 +- 8 files changed, 61 insertions(+), 8 deletions(-) create mode 100644 src/core/TreatmentOptimization/makeSurrogateModel.m diff --git a/src/TrackingOptimization/getTrackingOptimizationValueStruct.m b/src/TrackingOptimization/getTrackingOptimizationValueStruct.m index 94d1aa931..2c840ab0c 100644 --- a/src/TrackingOptimization/getTrackingOptimizationValueStruct.m +++ b/src/TrackingOptimization/getTrackingOptimizationValueStruct.m @@ -39,7 +39,7 @@ synergyWeights, params); else values.synergyWeights = getSynergyWeightsFromGroups(... - params.synergyWeightsGuess, params); + params.synergyWeights, params); end end end diff --git a/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m b/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m index 7a33413f6..d12586cc3 100644 --- a/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m +++ b/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m @@ -57,14 +57,14 @@ synergyWeights, inputs); else values.synergyWeights = getSynergyWeightsFromGroups(... - inputs.synergyWeightsGuess, inputs); + inputs.synergyWeights, inputs); end end end if strcmp(inputs.toolName, "VerificationOptimization") if strcmp(inputs.controllerType, 'synergy') values.synergyWeights = getSynergyWeightsFromGroups(... - inputs.synergyWeightsGuess, inputs); + inputs.synergyWeights, inputs); end end if strcmp(inputs.toolName, "DesignOptimization") diff --git a/src/VerificationOptimization/VerificationOptimizationTool.m b/src/VerificationOptimization/VerificationOptimizationTool.m index 201871504..4fa04f29c 100644 --- a/src/VerificationOptimization/VerificationOptimizationTool.m +++ b/src/VerificationOptimization/VerificationOptimizationTool.m @@ -42,8 +42,8 @@ function VerificationOptimizationTool(settingsFileName) function inputs = setupMuscleSynergies(inputs) if strcmp(inputs.controllerType, 'synergy') - inputs.splineSynergyActivations = spaps(inputs.initialGuess.time/inputs.initialGuess.time(end), ... - inputs.initialGuess.control(:, inputs.numCoordinates + 1:end)', 0.0000001); - inputs.synergyLabels = inputs.initialGuess.controlLabels(:, inputs.numCoordinates + 1:end); + inputs.splineSynergyActivations = spaps(inputs.initialTime, ... + inputs.initialSynergyControls', 0.0000001); + inputs.synergyLabels = inputs.initialSynergyControlsLabels; end end \ No newline at end of file diff --git a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/checkParameterGuess.m b/src/core/TreatmentOptimization/OptimalControlSetupFunctions/checkParameterGuess.m index 74afcbd03..1b0a0959f 100644 --- a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/checkParameterGuess.m +++ b/src/core/TreatmentOptimization/OptimalControlSetupFunctions/checkParameterGuess.m @@ -81,6 +81,7 @@ end end end + function inputs = getMuscleSynergiesInitialGuess(inputs) if isfield(inputs.initialGuess,"parameter") || isfield(inputs,"synergyWeights") synergyWeights = getSynergyWeightsFromGroups(inputs.synergyWeightsGuess, inputs); diff --git a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m b/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m index e4112496b..60fdaff51 100644 --- a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m +++ b/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m @@ -71,7 +71,9 @@ if isfield(inputs, "initialTorqueControls") controls = [controls, inputs.initialTorqueControls]; else - controls = [controls, inputs.experimentalJointMoments]; + if ~isempty(valueOrAlternate(inputs, "torqueControllerCoordinateNames", [])) + controls = [controls, inputs.experimentalJointMoments]; + end end guess.phase.control = scaleToBounds(controls, inputs.maxControl, ... inputs.minControl); diff --git a/src/core/TreatmentOptimization/makeSurrogateModel.m b/src/core/TreatmentOptimization/makeSurrogateModel.m new file mode 100644 index 000000000..3afbaaf87 --- /dev/null +++ b/src/core/TreatmentOptimization/makeSurrogateModel.m @@ -0,0 +1,49 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function prepares the inputs for the all treatment optimization +% modules (tracking, verification, and design optimization. +% +% (struct, struct) -> (struct) + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Claire V. Hammond % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function inputs = makeSurrogateModel(inputs) +if strcmp(inputs.controllerType, 'synergy') + for i = 1 : length(inputs.coordinateNames) + for j = 1 : length(inputs.surrogateModelCoordinateNames) + if strcmp(inputs.coordinateNames(i), inputs.surrogateModelCoordinateNames(j)) + inputs.surrogateModelIndex(j) = i; + end + end + end + inputs.dofsActuatedIndex = []; + for i = 1 : length(inputs.inverseDynamicMomentLabels) + for j = 1 : length(inputs.surrogateModelCoordinateNames) + if strcmp(inputs.inverseDynamicMomentLabels(i), ... + strcat(inputs.surrogateModelCoordinateNames(j), '_moment')) + inputs.dofsActuatedIndex(end+1) = j; + end + end + end +end +end diff --git a/src/core/TreatmentOptimization/makeTreatmentOptimizationInputs.m b/src/core/TreatmentOptimization/makeTreatmentOptimizationInputs.m index 24c78e4ce..9fac41322 100644 --- a/src/core/TreatmentOptimization/makeTreatmentOptimizationInputs.m +++ b/src/core/TreatmentOptimization/makeTreatmentOptimizationInputs.m @@ -31,6 +31,7 @@ inputs = makeStateDerivatives(inputs, params); inputs = setupGroundContact(inputs); inputs = makeExperimentalDataSplines(inputs); +inputs = makeSurrogateModel(inputs); inputs.maxAllowableError = ... makeMaxAllowableError(inputs.toolName, inputs.costTerms); inputs = makePathConstraintBounds(inputs); diff --git a/src/core/TreatmentOptimization/saveTreatmentOptimizationResults.m b/src/core/TreatmentOptimization/saveTreatmentOptimizationResults.m index 3422f5338..592b38c89 100644 --- a/src/core/TreatmentOptimization/saveTreatmentOptimizationResults.m +++ b/src/core/TreatmentOptimization/saveTreatmentOptimizationResults.m @@ -63,7 +63,7 @@ function saveTreatmentOptimizationResults(solution, inputs, values) fullfile(inputs.resultsDirectory, ... strcat(inputs.trialName, "_synergyCommands.sto"))); end -if ~isempty(inputs.torqueControllerCoordinateNames) +if ~isempty(valueOrAlternate(inputs, "torqueControllerCoordinateNames", [])) controlLabels = {}; for i = 1 : length(inputs.torqueControllerCoordinateNames) controlLabels{end + 1} = inputs.torqueControllerCoordinateNames{i}; From dca854604d6cac55efb33517541e92a63dca58cc Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Thu, 14 Sep 2023 18:00:19 -0500 Subject: [PATCH 102/365] Fix parsing B-spline cutoff frequency --- src/core/parse/parseTreatmentOptimizationParams.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/parse/parseTreatmentOptimizationParams.m b/src/core/parse/parseTreatmentOptimizationParams.m index f1d5e34e5..7e2808880 100644 --- a/src/core/parse/parseTreatmentOptimizationParams.m +++ b/src/core/parse/parseTreatmentOptimizationParams.m @@ -28,6 +28,6 @@ params = struct(); experimentalBSplineCutoffFrequency = getFieldByName(tree, 'experimental_spline_cutoff_frequency'); if isstruct(experimentalBSplineCutoffFrequency) - params.experimentalBSplineCutoffFrequency = experimentalBSplineCutoffFrequency.Text; + params.experimentalBSplineCutoffFrequency = str2double(experimentalBSplineCutoffFrequency.Text); end end From 07aa36370015fe43ea79469fcb061c99ce9d7afc Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Thu, 14 Sep 2023 18:00:47 -0500 Subject: [PATCH 103/365] Fix initial state derivatives --- .../setupGpopsInitialGuess.m | 4 +- .../makeStateDerivatives.m | 39 ++++++++++++------- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m b/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m index 60fdaff51..93e3c2b83 100644 --- a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m +++ b/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m @@ -39,8 +39,8 @@ end function guess = setupInitialStatesGuess(inputs, guess) -if isfield(inputs, "initialState") - guess.phase.state = scaleToBounds(inputs.initialState, ... +if isfield(inputs, "initialStates") + guess.phase.state = scaleToBounds(inputs.initialStates, ... inputs.maxState, inputs.minState); guess.phase.time = scaleToBounds(inputs.initialTime, inputs.maxTime, ... inputs.minTime); diff --git a/src/core/TreatmentOptimization/makeStateDerivatives.m b/src/core/TreatmentOptimization/makeStateDerivatives.m index 64cea68ca..7c4158766 100644 --- a/src/core/TreatmentOptimization/makeStateDerivatives.m +++ b/src/core/TreatmentOptimization/makeStateDerivatives.m @@ -29,17 +29,30 @@ % ----------------------------------------------------------------------- % function inputs = makeStateDerivatives(inputs, params) -points = length(inputs.experimentalTime); -interval = inputs.experimentalTime(2) - inputs.experimentalTime(1); -cutoffFrequency = ... - valueOrAlternate(params, "experimentalBSplineCutoffFrequency", 6); -numNodes = splFitWithCutoff(inputs.experimentalTime', ... - inputs.experimentalJointAngles', cutoffFrequency, 5); -[N, Np, Npp] = BSplineMatrices(5, numNodes, points, interval); -Nodes = N\inputs.experimentalJointAngles; -inputs.experimentalJointVelocities = Np * Nodes; -inputs.experimentalJointAccelerations = Npp * Nodes; -inputs.experimentalJointJerks = calcBSplineDerivative( ... - inputs.experimentalTime, inputs.experimentalJointAccelerations, ... - 5, numNodes); +jointAnglesSpline = spaps(inputs.experimentalTime, ... + inputs.experimentalJointAngles, eps, [], 3); +inputs.experimentalJointVelocities = fnval(fnder(jointAnglesSpline, 1), ... + inputs.experimentalTime); +jointVelocitiesSpline = spaps(inputs.experimentalTime, ... + inputs.experimentalJointVelocities, eps, [], 3); +inputs.experimentalJointAccelerations = fnval( ... + fnder(jointVelocitiesSpline, 1), inputs.experimentalTime); +jointAccelerationsSpline = spaps(inputs.experimentalTime, ... + inputs.experimentalJointAccelerations, eps, [], 3); +inputs.experimentalJointJerks = fnval(fnder(jointAccelerationsSpline, ... + 1), inputs.experimentalTime); + +% points = length(inputs.experimentalTime); +% interval = inputs.experimentalTime(2) - inputs.experimentalTime(1); +% cutoffFrequency = ... +% valueOrAlternate(params, "experimentalBSplineCutoffFrequency", 6); +% numNodes = splFitWithCutoff(inputs.experimentalTime', ... +% inputs.experimentalJointAngles', cutoffFrequency, 5); +% [N, Np, Npp] = BSplineMatrices(5, numNodes, points, interval); +% Nodes = N\inputs.experimentalJointAngles; +% inputs.experimentalJointVelocities = Np * Nodes; +% inputs.experimentalJointAccelerations = Npp * Nodes; +% inputs.experimentalJointJerks = calcBSplineDerivative( ... +% inputs.experimentalTime, inputs.experimentalJointAccelerations, ... +% 2, numNodes); end From 258e9cccb552c9f832c690e6ec4135016f36dc8f Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Thu, 14 Sep 2023 18:18:53 -0500 Subject: [PATCH 104/365] Update saveDesignOptimizationResults.m --- src/DesignOptimization/saveDesignOptimizationResults.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DesignOptimization/saveDesignOptimizationResults.m b/src/DesignOptimization/saveDesignOptimizationResults.m index 2cc6b6bf1..a9a4daa12 100644 --- a/src/DesignOptimization/saveDesignOptimizationResults.m +++ b/src/DesignOptimization/saveDesignOptimizationResults.m @@ -31,7 +31,7 @@ function saveDesignOptimizationResults(solution, inputs) values = getDesignOptimizationValueStruct(solution.solution.phase, inputs); -saveCommonOptimalControlResults(solution, inputs, values) +saveTreatmentOptimizationResults(solution, inputs, values) if isfield(inputs, "systemFns") values = getDesignOptimizationValueStruct(solution.solution.phase, inputs); inputs.auxdata = inputs; From 06639afca2ce0fe5aa19949f40f3f538033e60be Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Thu, 14 Sep 2023 18:40:13 -0500 Subject: [PATCH 105/365] Remove B-spline cutoff frequency field Co-Authored-By: Claire V. Hammond <61138239+cvhammond@users.noreply.github.com> --- src/core/parse/parseTreatmentOptimizationParams.m | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/core/parse/parseTreatmentOptimizationParams.m b/src/core/parse/parseTreatmentOptimizationParams.m index 7e2808880..995d90fa2 100644 --- a/src/core/parse/parseTreatmentOptimizationParams.m +++ b/src/core/parse/parseTreatmentOptimizationParams.m @@ -26,8 +26,4 @@ function params = parseTreatmentOptimizationParams(tree) params = struct(); -experimentalBSplineCutoffFrequency = getFieldByName(tree, 'experimental_spline_cutoff_frequency'); -if isstruct(experimentalBSplineCutoffFrequency) - params.experimentalBSplineCutoffFrequency = str2double(experimentalBSplineCutoffFrequency.Text); -end end From 5debf677aad7ee88f3cf19134195cbb3fb49bf0b Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Thu, 14 Sep 2023 18:40:29 -0500 Subject: [PATCH 106/365] Fix saving TO results Co-Authored-By: Claire V. Hammond <61138239+cvhammond@users.noreply.github.com> --- .../getVerificationOptimizationValueStruct.m | 8 ++++---- src/core/synergies/getSynergyWeightsFromGroups.m | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/VerificationOptimization/getVerificationOptimizationValueStruct.m b/src/VerificationOptimization/getVerificationOptimizationValueStruct.m index ce22ae998..d400956b5 100644 --- a/src/VerificationOptimization/getVerificationOptimizationValueStruct.m +++ b/src/VerificationOptimization/getVerificationOptimizationValueStruct.m @@ -29,10 +29,10 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function values = getVerificationOptimizationValueStruct(inputs, params) -values = getTreatmentOptimizationValueStruct(inputs, params); -if strcmp(params.controllerType, 'synergy') +function values = getVerificationOptimizationValueStruct(setup, inputs) +values = getTreatmentOptimizationValueStruct(setup, inputs); +if strcmp(inputs.controllerType, 'synergy') values.synergyWeights = getSynergyWeightsFromGroups(... - params.synergyWeightsGuess, params); + inputs.synergyWeights, inputs); end end diff --git a/src/core/synergies/getSynergyWeightsFromGroups.m b/src/core/synergies/getSynergyWeightsFromGroups.m index 6c670c457..3aa39f9dc 100644 --- a/src/core/synergies/getSynergyWeightsFromGroups.m +++ b/src/core/synergies/getSynergyWeightsFromGroups.m @@ -29,20 +29,20 @@ % ----------------------------------------------------------------------- % function synergyWeightsReformatted = getSynergyWeightsFromGroups(... - synergyWeights, params) -synergyWeightsReformatted = zeros(params.numSynergies, params.numMuscles); + synergyWeights, inputs) +synergyWeightsReformatted = zeros(inputs.numSynergies, inputs.numMuscles); valuesIndex = 1; row = 1; column = 1; % the sum of the muscles in the previous synergy groups -for i = 1:length(params.synergyGroups) - for j = 1: params.synergyGroups{i}.numSynergies +for i = 1:length(inputs.synergyGroups) + for j = 1: inputs.synergyGroups{i}.numSynergies synergyWeightsReformatted(row, column : ... - column + length(params.synergyGroups{i}.muscleNames) - 1) = ... + column + length(inputs.synergyGroups{i}.muscleNames) - 1) = ... synergyWeights(valuesIndex : ... - valuesIndex + length(params.synergyGroups{i}.muscleNames) - 1); - valuesIndex = valuesIndex + length(params.synergyGroups{i}.muscleNames); + valuesIndex + length(inputs.synergyGroups{i}.muscleNames) - 1); + valuesIndex = valuesIndex + length(inputs.synergyGroups{i}.muscleNames); row = row + 1; end - column = column + length(params.synergyGroups{i}.muscleNames); + column = column + length(inputs.synergyGroups{i}.muscleNames); end end \ No newline at end of file From 3abdb7ba18049571c0da56f8e3811d1f3723ca7f Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Thu, 14 Sep 2023 19:32:21 -0500 Subject: [PATCH 107/365] Deprecate getSynergyWeightsFromGroups Co-Authored-By: Claire V. Hammond <61138239+cvhammond@users.noreply.github.com> --- .../getTrackingOptimizationValueStruct.m | 16 +++++++--------- .../gpops/makeGpopsValuesAsStruct.m | 14 ++++---------- .../saveVerificationOptimizationResults.m | 8 ++++++++ .../calcTrackingSynergyVectorsDiscrete.m | 3 +-- 4 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/TrackingOptimization/getTrackingOptimizationValueStruct.m b/src/TrackingOptimization/getTrackingOptimizationValueStruct.m index 2c840ab0c..f7e9b2604 100644 --- a/src/TrackingOptimization/getTrackingOptimizationValueStruct.m +++ b/src/TrackingOptimization/getTrackingOptimizationValueStruct.m @@ -29,17 +29,15 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function values = getTrackingOptimizationValueStruct(phase, params) -values = getTreatmentOptimizationValueStruct(phase, params); -if strcmp(params.controllerType, 'synergy') - if params.optimizeSynergyVectors +function values = getTrackingOptimizationValueStruct(phase, inputs) +values = getTreatmentOptimizationValueStruct(phase, inputs); +if strcmp(inputs.controllerType, 'synergy') + if inputs.optimizeSynergyVectors synergyWeights = scaleToOriginal(phase.parameter(1,:), ... - params.maxParameter, params.minParameter); - values.synergyWeights = getSynergyWeightsFromGroups(... - synergyWeights, params); + inputs.maxParameter, inputs.minParameter); + values.synergyWeights = synergyWeights; else - values.synergyWeights = getSynergyWeightsFromGroups(... - params.synergyWeights, params); + values.synergyWeights = inputs.synergyWeights; end end end diff --git a/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m b/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m index d12586cc3..c00ca0b23 100644 --- a/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m +++ b/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m @@ -53,18 +53,15 @@ if inputs.optimizeSynergyVectors synergyWeights = scaleToOriginal(phase.parameter(1,:), ... inputs.maxParameter, inputs.minParameter); - values.synergyWeights = getSynergyWeightsFromGroups(... - synergyWeights, inputs); + values.synergyWeights = synergyWeights; else - values.synergyWeights = getSynergyWeightsFromGroups(... - inputs.synergyWeights, inputs); + values.synergyWeights = inputs.synergyWeights; end end end if strcmp(inputs.toolName, "VerificationOptimization") if strcmp(inputs.controllerType, 'synergy') - values.synergyWeights = getSynergyWeightsFromGroups(... - inputs.synergyWeights, inputs); + values.synergyWeights = inputs.synergyWeights; end end if strcmp(inputs.toolName, "DesignOptimization") @@ -74,12 +71,9 @@ values.synergyWeights = scaleToOriginal(phase.parameter(1, ... 1 : inputs.numSynergyWeights), ... inputs.maxParameter, inputs.minParameter); - values.synergyWeights = getSynergyWeightsFromGroups(... - values.synergyWeights, inputs); numParameters = inputs.numSynergyWeights; else - values.synergyWeights = getSynergyWeightsFromGroups(... - inputs.synergyWeightsGuess, inputs); + values.synergyWeights = inputs.synergyWeights; end if inputs.splineSynergyActivations.dim > 1 values.controlSynergyActivations = ... diff --git a/src/VerificationOptimization/saveVerificationOptimizationResults.m b/src/VerificationOptimization/saveVerificationOptimizationResults.m index 51f5b4354..bea8e5bcb 100644 --- a/src/VerificationOptimization/saveVerificationOptimizationResults.m +++ b/src/VerificationOptimization/saveVerificationOptimizationResults.m @@ -31,4 +31,12 @@ function saveVerificationOptimizationResults(solution, inputs) values = getVerificationOptimizationValueStruct( ... solution.solution.phase, inputs); saveTreatmentOptimizationResults(solution, inputs, values) +if strcmp(inputs.controllerType, 'synergy') + writeToSto(inputs.muscleLabels, linspace(1, inputs.numSynergies, ... + inputs.numSynergies), [values.synergyWeights], ... + fullfile(inputs.resultsDirectory, "synergyWeights.sto")); + writeToSto(inputs.muscleLabels, values.time, ... + solution.muscleActivations, ... + fullfile(inputs.resultsDirectory, strcat(inputs.trialName, "_combinedActivations.sto"))); +end end \ No newline at end of file diff --git a/src/core/TreatmentOptimization/DiscreteTerms/calcTrackingSynergyVectorsDiscrete.m b/src/core/TreatmentOptimization/DiscreteTerms/calcTrackingSynergyVectorsDiscrete.m index c2abeb7c2..0f68b6825 100644 --- a/src/core/TreatmentOptimization/DiscreteTerms/calcTrackingSynergyVectorsDiscrete.m +++ b/src/core/TreatmentOptimization/DiscreteTerms/calcTrackingSynergyVectorsDiscrete.m @@ -31,8 +31,7 @@ function cost = calcTrackingSynergyVectorsDiscrete(synergyWeights, ... params, costTerm) -origSynergyWeights = getSynergyWeightsFromGroups(... - params.synergyWeightsGuess, params); +origSynergyWeights = params.initialSynergyWeights; origSynergyWeights(origSynergyWeights==0) = []; synergyWeights(synergyWeights==0) = []; cost = calcTrackingCostArrayTerm(synergyWeights, ... From 9c5465af646a5bdbbc36601a0cfca27212a3a85f Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Thu, 14 Sep 2023 19:32:32 -0500 Subject: [PATCH 108/365] Update DesignOptimizationTool.m Co-Authored-By: Claire V. Hammond <61138239+cvhammond@users.noreply.github.com> --- src/DesignOptimization/DesignOptimizationTool.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/DesignOptimization/DesignOptimizationTool.m b/src/DesignOptimization/DesignOptimizationTool.m index d30257b79..78f06d576 100644 --- a/src/DesignOptimization/DesignOptimizationTool.m +++ b/src/DesignOptimization/DesignOptimizationTool.m @@ -42,8 +42,8 @@ function DesignOptimizationTool(settingsFileName) function inputs = setupMuscleSynergies(inputs) if strcmp(inputs.controllerType, 'synergy') - inputs.splineSynergyActivations = spaps(inputs.initialGuess.time, ... - inputs.initialGuess.control(:, inputs.numCoordinates + 1:end)', 0.0000001); - inputs.synergyLabels = inputs.initialGuess.controlLabels(:, inputs.numCoordinates + 1:end); + inputs.splineSynergyActivations = spaps(inputs.initialTime, ... + inputs.initialSynergyControls', 0.0000001); + inputs.synergyLabels = inputs.initialSynergyControlsLabels; end end \ No newline at end of file From c7663e3c13adf0fddcf91856ba8b9e5fef6a1005 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Sun, 17 Sep 2023 18:49:24 -0500 Subject: [PATCH 109/365] Fix saving persistent model --- src/JointModelPersonalization/JointModelPersonalization.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/JointModelPersonalization/JointModelPersonalization.m b/src/JointModelPersonalization/JointModelPersonalization.m index 9a896b345..42953d4b9 100644 --- a/src/JointModelPersonalization/JointModelPersonalization.m +++ b/src/JointModelPersonalization/JointModelPersonalization.m @@ -39,7 +39,7 @@ params.markerNames = getMarkersInTask(outputModel, ... inputs.tasks{i}); taskParams = mergeStructs(inputs.tasks{i}, params); - optimizedValues = computeKinematicCalibration(inputs.model, ... + optimizedValues = computeKinematicCalibration(inputs.modelFileName, ... inputs.tasks{i}.markerFile, functions, inputs.desiredError, ... taskParams); outputModel = adjustModelFromOptimizerOutput(outputModel, ... From b234767af533c8eb06769081c22d466490e855cf Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 18 Sep 2023 22:39:09 -0500 Subject: [PATCH 110/365] Fix DO for toy arm Co-Authored-By: Claire V. Hammond <61138239+cvhammond@users.noreply.github.com> --- .../parseDesignOptimizationSettingsTree.m | 5 +- .../saveDesignOptimizationResults.m | 3 +- .../gpops/makeGpopsValuesAsStruct.m | 46 +++++++++---------- .../saveVerificationOptimizationResults.m | 2 +- 4 files changed, 27 insertions(+), 29 deletions(-) diff --git a/src/DesignOptimization/parseDesignOptimizationSettingsTree.m b/src/DesignOptimization/parseDesignOptimizationSettingsTree.m index 5b65a5e16..15198fac0 100644 --- a/src/DesignOptimization/parseDesignOptimizationSettingsTree.m +++ b/src/DesignOptimization/parseDesignOptimizationSettingsTree.m @@ -32,13 +32,12 @@ parseDesignOptimizationSettingsTree(settingsTree) inputs = parseTreatmentOptimizationInputs(settingsTree); inputs = parseDesignSettings(settingsTree, inputs); -inputs = getInputs(settingsTree, inputs); +inputs = parseUserDefinedFunctions(settingsTree, inputs); params = parseTreatmentOptimizationParams(settingsTree); inputs = modifyModelForces(inputs); -inputs = updateMuscleModelProperties(inputs); end -function inputs = getInputs(tree, inputs) +function inputs = parseUserDefinedFunctions(tree, inputs) import org.opensim.modeling.Storage inputs.systemFns = parseSpaceSeparatedList(tree, "model_functions"); parameterTree = getFieldByNameOrError(tree, "RCNLParameterTermSet"); diff --git a/src/DesignOptimization/saveDesignOptimizationResults.m b/src/DesignOptimization/saveDesignOptimizationResults.m index a9a4daa12..737d3d974 100644 --- a/src/DesignOptimization/saveDesignOptimizationResults.m +++ b/src/DesignOptimization/saveDesignOptimizationResults.m @@ -30,10 +30,9 @@ % ----------------------------------------------------------------------- % function saveDesignOptimizationResults(solution, inputs) -values = getDesignOptimizationValueStruct(solution.solution.phase, inputs); +values = makeGpopsValuesAsStruct(solution.solution.phase, inputs); saveTreatmentOptimizationResults(solution, inputs, values) if isfield(inputs, "systemFns") - values = getDesignOptimizationValueStruct(solution.solution.phase, inputs); inputs.auxdata = inputs; inputs = updateSystemFromUserDefinedFunctions(inputs, values); model = Model(inputs.auxdata.model); diff --git a/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m b/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m index c00ca0b23..9dbe0bf28 100644 --- a/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m +++ b/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m @@ -75,29 +75,29 @@ else values.synergyWeights = inputs.synergyWeights; end - if inputs.splineSynergyActivations.dim > 1 - values.controlSynergyActivations = ... - fnval(inputs.splineSynergyActivations, values.time)'; - else - values.controlSynergyActivations = ... - fnval(inputs.splineSynergyActivations, values.time); - end - if inputs.enableExternalTorqueControl - controls = scaleToOriginal(phase.control, ones(size( ... - phase.control, 1), 1) .* inputs.maxControl, ... - ones(size(phase.control, 1), 1) .* inputs.minControl); - values.externalTorqueControls = controls(:, inputs.numCoordinates + ... - inputs.numSynergies + 1 : end); - end - else - if isfield(inputs, "enableExternalTorqueControl") && ... - inputs.enableExternalTorqueControl - controls = scaleToOriginal(phase.control, ones(size( ... - phase.control, 1), 1) .* inputs.maxControl, ... - ones(size(phase.control, 1), 1) .* inputs.minControl); - values.externalTorqueControls = controls(:, inputs.numCoordinates + ... - length(inputs.torqueControllerCoordinateNames) + 1 : end); - end +% if inputs.splineSynergyActivations.dim > 1 +% values.controlSynergyActivations = ... +% fnval(inputs.splineSynergyActivations, values.time)'; +% else +% values.controlSynergyActivations = ... +% fnval(inputs.splineSynergyActivations, values.time); +% end +% if inputs.enableExternalTorqueControl +% controls = scaleToOriginal(phase.control, ones(size( ... +% phase.control, 1), 1) .* inputs.maxControl, ... +% ones(size(phase.control, 1), 1) .* inputs.minControl); +% values.externalTorqueControls = controls(:, inputs.numCoordinates + ... +% inputs.numSynergies + 1 : end); +% end +% else +% if isfield(inputs, "enableExternalTorqueControl") && ... +% inputs.enableExternalTorqueControl +% controls = scaleToOriginal(phase.control, ones(size( ... +% phase.control, 1), 1) .* inputs.maxControl, ... +% ones(size(phase.control, 1), 1) .* inputs.minControl); +% values.externalTorqueControls = controls(:, inputs.numCoordinates + ... +% length(inputs.torqueControllerCoordinateNames) + 1 : end); +% end end if isfield(inputs, 'userDefinedVariables') for i = 1:length(inputs.userDefinedVariables) diff --git a/src/VerificationOptimization/saveVerificationOptimizationResults.m b/src/VerificationOptimization/saveVerificationOptimizationResults.m index bea8e5bcb..fed65e921 100644 --- a/src/VerificationOptimization/saveVerificationOptimizationResults.m +++ b/src/VerificationOptimization/saveVerificationOptimizationResults.m @@ -28,7 +28,7 @@ % ----------------------------------------------------------------------- % function saveVerificationOptimizationResults(solution, inputs) -values = getVerificationOptimizationValueStruct( ... +values = makeGpopsValuesAsStruct( ... solution.solution.phase, inputs); saveTreatmentOptimizationResults(solution, inputs, values) if strcmp(inputs.controllerType, 'synergy') From c1d8ac6db20ed2eab8c3ec74d8020f8e6816bee7 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" Date: Tue, 19 Sep 2023 17:42:49 -0700 Subject: [PATCH 111/365] reorganize and delete Treatment Optimization files --- src/DesignOptimization/DesignOptimization.m | 57 ----------- .../calcDesignOptimizationDiscreteObjective.m | 37 ------- .../calcDesignOptimizationIntegrand.m | 38 -------- .../calcDesignOptimizationObjective.m | 37 ------- .../calcDesignOptimizationPathConstraint.m | 71 -------------- ...calcDesignOptimizationTerminalConstraint.m | 90 ------------------ ...puteDesignOptimizationContinuousFunction.m | 46 --------- ...omputeDesignOptimizationEndpointFunction.m | 68 ------------- .../computeDesignOptimizationMainFunction.m | 86 ----------------- .../getDesignOptimizationValueStruct.m | 83 ---------------- .../TrackingOptimization.m | 35 ------- .../calcTrackingOptimizationIntegrand.m | 38 -------- .../calcTrackingOptimizationObjective.m | 34 ------- .../calcTrackingOptimizationPathConstraint.m | 58 ----------- ...lcTrackingOptimizationTerminalConstraint.m | 87 ----------------- ...teTrackingOptimizationContinuousFunction.m | 41 -------- ...puteTrackingOptimizationEndpointFunction.m | 39 -------- .../computeTrackingOptimizationMainFunction.m | 58 ----------- .../adjustMaxIsometricForce.m | 0 .../adjustModelMaxIsometricForce.m | 0 .../adjustModelOptimalFiberLength.m | 0 .../adjustModelTendonSlackLength.m | 0 .../adjustOptimalFiberLength.m | 0 .../adjustTendonSlackLength.m | 0 .../applyBeltSpeedParameter.m | 0 .../applyMaxIsometricForceParameter.m | 0 .../applyOptimalFiberLengthParameter.m | 0 .../applyTendonSlackLengthParameter.m | 0 .../DesignOptimizationTool.m | 0 .../parseDesignOptimizationSettingsTree.m | 0 .../reportingGaitSpecificMeasurements.m | 0 .../saveDesignOptimizationResults.m | 0 .../updateSystemFromUserDefinedFunctions.m | 0 .../calcTrackingSynergyVectorsDiscrete.m | 0 .../calcMaximizingCostArrayTerm.m | 0 .../calcMinimizingCostArrayTerm.m | 0 .../calcTrackingCostArrayTerm.m | 0 .../calcGoalBeltSpeedDiscrete.m | 0 .../calcGoalSingleSupportTimeIntegrand.m | 0 .../calcGoalWalkingSpeedIntegrand.m | 0 .../calcKinematicSymmetryIntegrand.m | 0 .../IntegrandTerms/calcMassCenterVelocity.m | 0 .../calcMaximizingBreakingForceIntegrand.m | 0 .../calcMaximizingMuscleActivationIntegrand.m | 0 .../calcMaximizingPropulsiveForceIntegrand.m | 0 ...calcMaximizingSingleSupportTimeIntegrand.m | 0 .../calcMaximizingStepLengthIntegrand.m | 0 ...calcMaximizingTrailingLimbAngleIntegrand.m | 0 .../calcMinimizingBreakingForceIntegrand.m | 0 .../calcMinimizingExternalTorqueControl.m | 0 .../calcMinimizingJointJerkIntegrand.m | 0 .../calcMinimizingJointPowerIntegrand.m | 0 ...lcMinimizingMassCenterVelocityXIntegrand.m | 0 ...lcMinimizingMassCenterVelocityYIntegrand.m | 0 ...lcMinimizingMassCenterVelocityZIntegrand.m | 0 .../calcMinimizingMetabolicCost.m | 0 .../calcMinimizingMuscleActivationIntegrand.m | 0 .../calcMinimizingPropulsiveForceIntegrand.m | 0 ...calcMinimizingTrailingLimbAngleIntegrand.m | 0 .../IntegrandTerms/calcSingleSupportTime.m | 0 .../IntegrandTerms/calcStepLength.m | 0 .../IntegrandTerms/calcStepLengthAsymmetry.m | 0 .../calcStepLengthAsymmetryIntegrand.m | 0 .../IntegrandTerms/calcStepTimeAsymmetry.m | 0 .../calcStepTimeAsymmetryIntegrand.m | 0 .../IntegrandTerms/calcStrideLength.m | 0 .../calcTrackingControllerIntegrand.m | 0 .../calcTrackingCoordinateIntegrand.m | 0 .../calcTrackingExternalForcesIntegrand.m | 0 .../calcTrackingExternalMomentsIntegrand.m | 0 ...calcTrackingInverseDynamicLoadsIntegrand.m | 0 .../calcTrackingMuscleActivationIntegrand.m | 0 .../IntegrandTerms/calcTrailingLimb.m | 0 .../IntegrandTerms/getBreakingForce.m | 0 .../IntegrandTerms/getHeelStrikeEvent.m | 0 .../IntegrandTerms/getPropulsiveForce.m | 0 .../IntegrandTerms/getToeOffEvent.m | 0 .../checkControlGuess.m | 0 .../checkParameterGuess.m | 0 .../checkStateGuess.m | 0 .../setupCommonOptimalControlBounds.m | 0 .../setupGpopsInitialGuess.m | 0 .../setupGpopsSettings.m | 0 .../setupGroundContact.m | 0 .../calcMuscleActivationsPathConstraint.m | 0 ...calcMuscleActuatedMomentsPathConstraints.m | 0 ...tedMomentsWithExternalAidPathConstraints.m | 0 .../calcNormalizedFiberLengthPathConstraint.m | 0 .../calcRootSegmentResidualsPathConstraints.m | 0 ...calcTorqueActuatedMomentsPathConstraints.m | 0 .../SetupBounds/makeMaxAllowableError.m | 0 .../SetupBounds/makeOptimalControlBounds.m | 0 .../SetupBounds/makePathConstraintBounds.m | 0 .../makeTerminalConstraintBounds.m | 0 .../calcExternalForcesPeriodicity.m | 0 .../calcExternalMomentsPeriodicity.m | 0 .../TerminalTerms/calcFinalPointPosition.m | 0 .../TerminalTerms/calcFinalPointVelocity.m | 0 .../TerminalTerms/calcFinalStatePosition.m | 0 .../TerminalTerms/calcFinalStateVelocity.m | 0 .../calcRootSegmentResidualsPeriodicity.m | 0 .../calcStatePositionPeriodicity.m | 0 .../calcStateVelocityPeriodicity.m | 0 .../TerminalTerms/calcSynergyWeightsSum.m | 0 .../TrackingOptimizationTool.m | 0 .../calcFootGroundReactions.m | 0 .../calcSynergyBasedModeledValues.m | 0 .../calcTorqueBasedModeledValues.m | 0 .../getTrackingOptimizationValueStruct.m | 0 .../parseTrackingOptimizationSettingsTree.m | 0 .../saveTrackingOptimizationResults.m | 4 +- .../VerificationOptimizationTool.m | 0 .../getVerificationOptimizationValueStruct.m | 0 ...arseVerificationOptimizationSettingsTree.m | 0 .../saveVerificationOptimizationResults.m | 0 .../addContactSurfaceActuators.m | 0 .../calcTreatmentOptimizationCost.m | 0 .../TreatmentOptimization/checkInitialGuess.m | 0 .../disableModelMuscles.m | 0 .../generateConstraintTermStruct.m | 0 .../generateCostTermStruct.m | 0 .../TreatmentOptimization/getCorrectStates.m | 0 .../getTreatmentOptimizationValueStruct.m | 0 .../makeExperimentalDataSplines.m | 0 .../makeStateDerivatives.m | 0 .../makeSurrogateModel.m | 0 .../makeTreatmentOptimizationInputs.m | 0 .../TreatmentOptimization/modifyModelForces.m | 0 .../parseGroundContactSurfaces.m | 0 .../reportTreatmentOptimizationResults.m | 0 .../saveTreatmentOptimizationResults.m | 0 .../TreatmentOptimization/scaleToBounds.m | 0 .../TreatmentOptimization/scaleToOriginal.m | 0 .../TreatmentOptimization/transferMoments.m | 0 .../VerificationOptimization.m | 38 -------- .../calcVerificationOptimizationIntegrand.m | 38 -------- .../calcVerificationOptimizationObjective.m | 34 ------- ...lcVerificationOptimizationPathConstraint.m | 59 ------------ ...rificationOptimizationTerminalConstraint.m | 78 --------------- ...rificationOptimizationContinuousFunction.m | 43 --------- ...VerificationOptimizationEndpointFunction.m | 39 -------- ...puteVerificationOptimizationMainFunction.m | 48 ---------- .../updateMuscleModelProperties.m | 46 --------- .../OpenSimMex => mex}/calcBodyLocation.m | 0 .../OpenSimMex => mex}/calcBodyVelocity.m | 0 .../initializeMexOrMatlabParallelFunctions.m | 0 .../OpenSimMex => mex}/inverseDynamics.m | 0 .../inverseDynamicsMatlab.m | 0 .../inverseDynamicsMatlabParallel.m | 0 .../inverseDynamicsMexWindows.cpp | 0 .../inverseDynamicsMexWindows.mexw64 | Bin .../OpenSimMex => mex}/pointKinematics.m | 0 .../pointKinematicsMatlabParallel.m | 0 .../pointKinematicsMexWindows.mexw64 | Bin .../setOsimStateBySingleFrame.m | 0 155 files changed, 2 insertions(+), 1428 deletions(-) delete mode 100644 src/DesignOptimization/DesignOptimization.m delete mode 100644 src/DesignOptimization/calcDesignOptimizationDiscreteObjective.m delete mode 100644 src/DesignOptimization/calcDesignOptimizationIntegrand.m delete mode 100644 src/DesignOptimization/calcDesignOptimizationObjective.m delete mode 100644 src/DesignOptimization/calcDesignOptimizationPathConstraint.m delete mode 100644 src/DesignOptimization/calcDesignOptimizationTerminalConstraint.m delete mode 100644 src/DesignOptimization/computeDesignOptimizationContinuousFunction.m delete mode 100644 src/DesignOptimization/computeDesignOptimizationEndpointFunction.m delete mode 100644 src/DesignOptimization/computeDesignOptimizationMainFunction.m delete mode 100644 src/DesignOptimization/getDesignOptimizationValueStruct.m delete mode 100644 src/TrackingOptimization/TrackingOptimization.m delete mode 100644 src/TrackingOptimization/calcTrackingOptimizationIntegrand.m delete mode 100644 src/TrackingOptimization/calcTrackingOptimizationObjective.m delete mode 100644 src/TrackingOptimization/calcTrackingOptimizationPathConstraint.m delete mode 100644 src/TrackingOptimization/calcTrackingOptimizationTerminalConstraint.m delete mode 100644 src/TrackingOptimization/computeTrackingOptimizationContinuousFunction.m delete mode 100644 src/TrackingOptimization/computeTrackingOptimizationEndpointFunction.m delete mode 100644 src/TrackingOptimization/computeTrackingOptimizationMainFunction.m rename src/{core => }/TreatmentOptimization/BuiltInParameters/adjustMaxIsometricForce.m (100%) rename src/{core => }/TreatmentOptimization/BuiltInParameters/adjustModelMaxIsometricForce.m (100%) rename src/{core => }/TreatmentOptimization/BuiltInParameters/adjustModelOptimalFiberLength.m (100%) rename src/{core => }/TreatmentOptimization/BuiltInParameters/adjustModelTendonSlackLength.m (100%) rename src/{core => }/TreatmentOptimization/BuiltInParameters/adjustOptimalFiberLength.m (100%) rename src/{core => }/TreatmentOptimization/BuiltInParameters/adjustTendonSlackLength.m (100%) rename src/{core => }/TreatmentOptimization/BuiltInParameters/applyBeltSpeedParameter.m (100%) rename src/{core => }/TreatmentOptimization/BuiltInParameters/applyMaxIsometricForceParameter.m (100%) rename src/{core => }/TreatmentOptimization/BuiltInParameters/applyOptimalFiberLengthParameter.m (100%) rename src/{core => }/TreatmentOptimization/BuiltInParameters/applyTendonSlackLengthParameter.m (100%) rename src/{ => TreatmentOptimization}/DesignOptimization/DesignOptimizationTool.m (100%) rename src/{ => TreatmentOptimization}/DesignOptimization/parseDesignOptimizationSettingsTree.m (100%) rename src/{ => TreatmentOptimization}/DesignOptimization/reportingGaitSpecificMeasurements.m (100%) rename src/{ => TreatmentOptimization}/DesignOptimization/saveDesignOptimizationResults.m (100%) rename src/{ => TreatmentOptimization}/DesignOptimization/updateSystemFromUserDefinedFunctions.m (100%) rename src/{core => }/TreatmentOptimization/DiscreteTerms/calcTrackingSynergyVectorsDiscrete.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandComputations/calcMaximizingCostArrayTerm.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandComputations/calcMinimizingCostArrayTerm.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandComputations/calcTrackingCostArrayTerm.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/calcGoalBeltSpeedDiscrete.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/calcGoalSingleSupportTimeIntegrand.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/calcGoalWalkingSpeedIntegrand.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/calcKinematicSymmetryIntegrand.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/calcMassCenterVelocity.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/calcMaximizingBreakingForceIntegrand.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/calcMaximizingMuscleActivationIntegrand.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/calcMaximizingPropulsiveForceIntegrand.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/calcMaximizingSingleSupportTimeIntegrand.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/calcMaximizingStepLengthIntegrand.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/calcMaximizingTrailingLimbAngleIntegrand.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/calcMinimizingBreakingForceIntegrand.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/calcMinimizingExternalTorqueControl.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/calcMinimizingJointJerkIntegrand.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/calcMinimizingJointPowerIntegrand.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityXIntegrand.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityYIntegrand.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityZIntegrand.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/calcMinimizingMetabolicCost.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/calcMinimizingMuscleActivationIntegrand.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/calcMinimizingPropulsiveForceIntegrand.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/calcMinimizingTrailingLimbAngleIntegrand.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/calcSingleSupportTime.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/calcStepLength.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/calcStepLengthAsymmetry.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/calcStepLengthAsymmetryIntegrand.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/calcStepTimeAsymmetry.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/calcStepTimeAsymmetryIntegrand.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/calcStrideLength.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/calcTrailingLimb.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/getBreakingForce.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/getHeelStrikeEvent.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/getPropulsiveForce.m (100%) rename src/{core => }/TreatmentOptimization/IntegrandTerms/getToeOffEvent.m (100%) rename src/{core => }/TreatmentOptimization/OptimalControlSetupFunctions/checkControlGuess.m (100%) rename src/{core => }/TreatmentOptimization/OptimalControlSetupFunctions/checkParameterGuess.m (100%) rename src/{core => }/TreatmentOptimization/OptimalControlSetupFunctions/checkStateGuess.m (100%) rename src/{core => }/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlBounds.m (100%) rename src/{core => }/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m (100%) rename src/{core => }/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsSettings.m (100%) rename src/{core => }/TreatmentOptimization/OptimalControlSetupFunctions/setupGroundContact.m (100%) rename src/{core => }/TreatmentOptimization/PathTerms/calcMuscleActivationsPathConstraint.m (100%) rename src/{core => }/TreatmentOptimization/PathTerms/calcMuscleActuatedMomentsPathConstraints.m (100%) rename src/{core => }/TreatmentOptimization/PathTerms/calcMuscleActuatedMomentsWithExternalAidPathConstraints.m (100%) rename src/{core => }/TreatmentOptimization/PathTerms/calcNormalizedFiberLengthPathConstraint.m (100%) rename src/{core => }/TreatmentOptimization/PathTerms/calcRootSegmentResidualsPathConstraints.m (100%) rename src/{core => }/TreatmentOptimization/PathTerms/calcTorqueActuatedMomentsPathConstraints.m (100%) rename src/{core => }/TreatmentOptimization/SetupBounds/makeMaxAllowableError.m (100%) rename src/{core => }/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m (100%) rename src/{core => }/TreatmentOptimization/SetupBounds/makePathConstraintBounds.m (100%) rename src/{core => }/TreatmentOptimization/SetupBounds/makeTerminalConstraintBounds.m (100%) rename src/{core => }/TreatmentOptimization/TerminalTerms/calcExternalForcesPeriodicity.m (100%) rename src/{core => }/TreatmentOptimization/TerminalTerms/calcExternalMomentsPeriodicity.m (100%) rename src/{core => }/TreatmentOptimization/TerminalTerms/calcFinalPointPosition.m (100%) rename src/{core => }/TreatmentOptimization/TerminalTerms/calcFinalPointVelocity.m (100%) rename src/{core => }/TreatmentOptimization/TerminalTerms/calcFinalStatePosition.m (100%) rename src/{core => }/TreatmentOptimization/TerminalTerms/calcFinalStateVelocity.m (100%) rename src/{core => }/TreatmentOptimization/TerminalTerms/calcRootSegmentResidualsPeriodicity.m (100%) rename src/{core => }/TreatmentOptimization/TerminalTerms/calcStatePositionPeriodicity.m (100%) rename src/{core => }/TreatmentOptimization/TerminalTerms/calcStateVelocityPeriodicity.m (100%) rename src/{core => }/TreatmentOptimization/TerminalTerms/calcSynergyWeightsSum.m (100%) rename src/{ => TreatmentOptimization}/TrackingOptimization/TrackingOptimizationTool.m (100%) rename src/{ => TreatmentOptimization}/TrackingOptimization/calcFootGroundReactions.m (100%) rename src/{ => TreatmentOptimization}/TrackingOptimization/calcSynergyBasedModeledValues.m (100%) rename src/{ => TreatmentOptimization}/TrackingOptimization/calcTorqueBasedModeledValues.m (100%) rename src/{ => TreatmentOptimization}/TrackingOptimization/getTrackingOptimizationValueStruct.m (100%) rename src/{ => TreatmentOptimization}/TrackingOptimization/parseTrackingOptimizationSettingsTree.m (100%) rename src/{ => TreatmentOptimization}/TrackingOptimization/saveTrackingOptimizationResults.m (96%) rename src/{ => TreatmentOptimization}/VerificationOptimization/VerificationOptimizationTool.m (100%) rename src/{ => TreatmentOptimization}/VerificationOptimization/getVerificationOptimizationValueStruct.m (100%) rename src/{ => TreatmentOptimization}/VerificationOptimization/parseVerificationOptimizationSettingsTree.m (100%) rename src/{ => TreatmentOptimization}/VerificationOptimization/saveVerificationOptimizationResults.m (100%) rename src/{core => }/TreatmentOptimization/addContactSurfaceActuators.m (100%) rename src/{core => }/TreatmentOptimization/calcTreatmentOptimizationCost.m (100%) rename src/{core => }/TreatmentOptimization/checkInitialGuess.m (100%) rename src/{core => }/TreatmentOptimization/disableModelMuscles.m (100%) rename src/{core => }/TreatmentOptimization/generateConstraintTermStruct.m (100%) rename src/{core => }/TreatmentOptimization/generateCostTermStruct.m (100%) rename src/{core => }/TreatmentOptimization/getCorrectStates.m (100%) rename src/{core => }/TreatmentOptimization/getTreatmentOptimizationValueStruct.m (100%) rename src/{core => }/TreatmentOptimization/makeExperimentalDataSplines.m (100%) rename src/{core => }/TreatmentOptimization/makeStateDerivatives.m (100%) rename src/{core => }/TreatmentOptimization/makeSurrogateModel.m (100%) rename src/{core => }/TreatmentOptimization/makeTreatmentOptimizationInputs.m (100%) rename src/{core => }/TreatmentOptimization/modifyModelForces.m (100%) rename src/{core => }/TreatmentOptimization/parseGroundContactSurfaces.m (100%) rename src/{core => }/TreatmentOptimization/reportTreatmentOptimizationResults.m (100%) rename src/{core => }/TreatmentOptimization/saveTreatmentOptimizationResults.m (100%) rename src/{core => }/TreatmentOptimization/scaleToBounds.m (100%) rename src/{core => }/TreatmentOptimization/scaleToOriginal.m (100%) rename src/{core => }/TreatmentOptimization/transferMoments.m (100%) delete mode 100644 src/VerificationOptimization/VerificationOptimization.m delete mode 100644 src/VerificationOptimization/calcVerificationOptimizationIntegrand.m delete mode 100644 src/VerificationOptimization/calcVerificationOptimizationObjective.m delete mode 100644 src/VerificationOptimization/calcVerificationOptimizationPathConstraint.m delete mode 100644 src/VerificationOptimization/calcVerificationOptimizationTerminalConstraint.m delete mode 100644 src/VerificationOptimization/computeVerificationOptimizationContinuousFunction.m delete mode 100644 src/VerificationOptimization/computeVerificationOptimizationEndpointFunction.m delete mode 100644 src/VerificationOptimization/computeVerificationOptimizationMainFunction.m delete mode 100644 src/core/TreatmentOptimization/updateMuscleModelProperties.m rename src/core/{TreatmentOptimization/OpenSimMex => mex}/calcBodyLocation.m (100%) rename src/core/{TreatmentOptimization/OpenSimMex => mex}/calcBodyVelocity.m (100%) rename src/core/{TreatmentOptimization/OpenSimMex => mex}/initializeMexOrMatlabParallelFunctions.m (100%) rename src/core/{TreatmentOptimization/OpenSimMex => mex}/inverseDynamics.m (100%) rename src/core/{TreatmentOptimization/OpenSimMex => mex}/inverseDynamicsMatlab.m (100%) rename src/core/{TreatmentOptimization/OpenSimMex => mex}/inverseDynamicsMatlabParallel.m (100%) rename src/core/{TreatmentOptimization/OpenSimMex => mex}/inverseDynamicsMexWindows.cpp (100%) rename src/core/{TreatmentOptimization/OpenSimMex => mex}/inverseDynamicsMexWindows.mexw64 (100%) rename src/core/{TreatmentOptimization/OpenSimMex => mex}/pointKinematics.m (100%) rename src/core/{TreatmentOptimization/OpenSimMex => mex}/pointKinematicsMatlabParallel.m (100%) rename src/core/{TreatmentOptimization/OpenSimMex => mex}/pointKinematicsMexWindows.mexw64 (100%) rename src/core/{TreatmentOptimization/OpenSimMex => mex}/setOsimStateBySingleFrame.m (100%) diff --git a/src/DesignOptimization/DesignOptimization.m b/src/DesignOptimization/DesignOptimization.m deleted file mode 100644 index be2f73683..000000000 --- a/src/DesignOptimization/DesignOptimization.m +++ /dev/null @@ -1,57 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function sets up the input variables and mex files (or parallel -% matlab function) for the main function of design optimization -% -% (struct, struct) -> (struct, struct) -% Inputs for the main function are setup - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega, Claire V. Hammond % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function [output, inputs] = DesignOptimization(inputs, params) -inputs = makeTreatmentOptimizationInputs(inputs, params); -initializeMexOrMatlabParallelFunctions(inputs.mexModel); -if strcmp(inputs.controllerType, 'synergy') - inputs = setupMuscleSynergies(inputs); -end -if inputs.enableExternalTorqueControl - inputs = setupExternalTorqueControls(inputs); -end -output = computeDesignOptimizationMainFunction(inputs, params); -end - -function inputs = setupMuscleSynergies(inputs) -inputs.splineSynergyActivations = spaps(inputs.initialGuess.time, ... - inputs.initialGuess.control(:, inputs.numCoordinates + 1 : ... - inputs.numCoordinates + inputs.numSynergies)', 0.0000001); -inputs.synergyLabels = inputs.initialGuess.controlLabels(:, ... - inputs.numCoordinates + 1 : inputs.numCoordinates + inputs.numSynergies); -end - -function inputs = setupExternalTorqueControls(inputs) -if size(inputs.initialGuess.control, 2) ~= length(inputs.maxControl) - inputs.initialGuess.control = [inputs.initialGuess.control ... - zeros(size(inputs.initialGuess.control, 1), ... - inputs.numExternalTorqueControls)]; -end -end \ No newline at end of file diff --git a/src/DesignOptimization/calcDesignOptimizationDiscreteObjective.m b/src/DesignOptimization/calcDesignOptimizationDiscreteObjective.m deleted file mode 100644 index c801b9acb..000000000 --- a/src/DesignOptimization/calcDesignOptimizationDiscreteObjective.m +++ /dev/null @@ -1,37 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function calculates the discrete objectives for design optimization. -% -% (struct, struct, struct) -> (Number) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function discrete = calcDesignOptimizationDiscreteObjective(values, ... - modeledValues, auxdata) - -[costTermCalculations, allowedTypes] = ... - generateCostTermStruct("discrete", "DesignOptimization"); -discrete = calcTreatmentOptimizationCost( ... - costTermCalculations, allowedTypes, values, modeledValues, auxdata); -end \ No newline at end of file diff --git a/src/DesignOptimization/calcDesignOptimizationIntegrand.m b/src/DesignOptimization/calcDesignOptimizationIntegrand.m deleted file mode 100644 index 8774f7f18..000000000 --- a/src/DesignOptimization/calcDesignOptimizationIntegrand.m +++ /dev/null @@ -1,38 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function calculates the integrand for design optimization. -% -% (struct, struct, struct) -> (2D matrix) -% Returns scaled integrand - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega, Claire V. Hammond % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function integrand = calcDesignOptimizationIntegrand(values, ... - modeledValues, auxdata) -[costTermCalculations, allowedTypes] = ... - generateCostTermStruct("continuous", "DesignOptimization"); -integrand = calcTreatmentOptimizationCost( ... - costTermCalculations, allowedTypes, values, modeledValues, auxdata); -integrand = integrand ./ (auxdata.maxIntegral - auxdata.minIntegral); -integrand = integrand .^ 2; -end \ No newline at end of file diff --git a/src/DesignOptimization/calcDesignOptimizationObjective.m b/src/DesignOptimization/calcDesignOptimizationObjective.m deleted file mode 100644 index 5528ddc0e..000000000 --- a/src/DesignOptimization/calcDesignOptimizationObjective.m +++ /dev/null @@ -1,37 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function calculates the cost function objective for design -% optimization. -% -% (Number, Array of number, Number, struct) -> (Number) -% Returns objective - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function objective = calcDesignOptimizationObjective(discrete, ... - continuous, finalTime, inputs) -continuousObjective = sum(continuous) / length(continuous); -discreteObjective = sum(discrete) / length(discrete); -if isnan(discreteObjective); discreteObjective = 0; end -objective = continuousObjective + discreteObjective; -end diff --git a/src/DesignOptimization/calcDesignOptimizationPathConstraint.m b/src/DesignOptimization/calcDesignOptimizationPathConstraint.m deleted file mode 100644 index ebb2b7e23..000000000 --- a/src/DesignOptimization/calcDesignOptimizationPathConstraint.m +++ /dev/null @@ -1,71 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function calculates the path constraint for design optimization. -% -% (struct, struct, struct) -> (2D matrix) -% Returns path constraint - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function path = calcDesignOptimizationPathConstraint(values, modeledValues, ... - params) -path = []; -for i = 1:length(params.path) - constraintTerm = params.path{i}; - if constraintTerm.isEnabled - switch constraintTerm.type - case "root_segment_residual_load" - path = cat(2, path, ... - calcRootSegmentResidualsPathConstraints(... - constraintTerm.load, ... - params.inverseDynamicMomentLabels, ... - modeledValues.inverseDynamicMoments)); - case "muscle_model_moment_consistency" - path = cat(2, path, ... - calcMuscleActuatedMomentsPathConstraints(params, ... - modeledValues, constraintTerm.load)); - case "torque_model_moment_consistency" - path = cat(2, path, ... - calcTorqueActuatedMomentsPathConstraints(params, ... - modeledValues, values.controlTorques, constraintTerm.load)); - case "limit_muscle_activation" - path = cat(2, path, ... - calcMuscleActivationsPathConstraint(params, ... - modeledValues, constraintTerm.muscle)); - case "limit_normalized_fiber_length" - path = cat(2, path, ... - calcNormalizedFiberLengthPathConstraint(params, ... - modeledValues, constraintTerm.muscle)); - case "external_control_muscle_moment_consistency" - path = cat(2, path, ... - calcMuscleActuatedMomentsWithExternalAidPathConstraints( ... - params, modeledValues, values.externalTorqueControls, ... - constraintTerm.coordinate)); - otherwise - throw(MException('', ['Constraint term type ' ... - constraintTerm.type ' does not exist for this tool.'])) - end - end -end -path = scaleToBounds(path, params.maxPath, params.minPath); -end \ No newline at end of file diff --git a/src/DesignOptimization/calcDesignOptimizationTerminalConstraint.m b/src/DesignOptimization/calcDesignOptimizationTerminalConstraint.m deleted file mode 100644 index c496a23db..000000000 --- a/src/DesignOptimization/calcDesignOptimizationTerminalConstraint.m +++ /dev/null @@ -1,90 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function calculates the terminal constraint for design optimization -% -% (struct, struct, struct) -> (Array of number) -% Returns terminal constraint - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function event = calcDesignOptimizationTerminalConstraint(values, ... - modeledValues, params) - -event = []; -for i = 1:length(params.terminal) - constraintTerm = params.terminal{i}; - if constraintTerm.isEnabled - switch constraintTerm.type - case "state_position_periodicity" - event = cat(2, event, ... - calcStatePositionPeriodicity(values.statePositions, ... - params.coordinateNames, ... - constraintTerm.coordinate)); - case "state_velocity_periodicity" - event = cat(2, event, ... - calcStateVelocityPeriodicity(values.stateVelocities, ... - params.coordinateNames, ... - constraintTerm.coordinate)); - case "root_segment_residual_load_periodicity" - event = cat(2, event, ... - calcRootSegmentResidualsPeriodicity(... - modeledValues.inverseDynamicMoments, ... - params.inverseDynamicMomentLabels, ... - constraintTerm.load)); - case "external_force_periodicity" - event = cat(2, event, ... - calcExternalForcesPeriodicity(... - modeledValues.groundReactionsLab.forces, ... - params.contactSurfaces, ... - constraintTerm.force)); - case "external_moment_periodicity" - event = cat(2, event, ... - calcExternalMomentsPeriodicity(... - modeledValues.groundReactionsLab.moments, ... - params.contactSurfaces, ... - constraintTerm.moment)); - case "final_state_position" - event = cat(2, event, ... - calcFinalStatePosition(values.statePositions, ... - params.coordinateNames, ... - constraintTerm)); - case "final_state_velocity" - event = cat(2, event, ... - calcFinalStateVelocity(values.stateVelocities, ... - params.coordinateNames, ... - constraintTerm)); - case "final_point_position" - event = cat(2, event, ... - calcFinalPointPosition(params, values, ... - constraintTerm)); - case "final_point_velocity" - event = cat(2, event, ... - calcFinalPointVelocity(params, values, ... - constraintTerm)); - otherwise - throw(MException('', ['Constraint term type ' ... - constraintTerm.type ' does not exist for this tool.'])) - end - end -end -end \ No newline at end of file diff --git a/src/DesignOptimization/computeDesignOptimizationContinuousFunction.m b/src/DesignOptimization/computeDesignOptimizationContinuousFunction.m deleted file mode 100644 index a6b9ee23d..000000000 --- a/src/DesignOptimization/computeDesignOptimizationContinuousFunction.m +++ /dev/null @@ -1,46 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function computes the dynamic constraints, path constraints (if any) -% and cost function terms (if any) for design optimization. -% -% (struct) -> (struct) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function modeledValues = computeDesignOptimizationContinuousFunction(inputs) - -values = getDesignOptimizationValueStruct(inputs.phase, inputs.auxdata); -inputs = updateSystemFromUserDefinedFunctions(inputs, values); -modeledValues = calcTorqueBasedModeledValues(values, inputs.auxdata); -modeledValues = calcSynergyBasedModeledValues(values, inputs.auxdata, ... - modeledValues); -modeledValues.dynamics = calcDynamicConstraint(values, ... - inputs.auxdata); -if ~isempty(inputs.auxdata.path) - modeledValues.path = calcDesignOptimizationPathConstraint(values, ... - modeledValues, inputs.auxdata); -end -modeledValues.integrand = calcDesignOptimizationIntegrand(values, ... - modeledValues, inputs.auxdata); -end diff --git a/src/DesignOptimization/computeDesignOptimizationEndpointFunction.m b/src/DesignOptimization/computeDesignOptimizationEndpointFunction.m deleted file mode 100644 index b8f1210df..000000000 --- a/src/DesignOptimization/computeDesignOptimizationEndpointFunction.m +++ /dev/null @@ -1,68 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function computes the terminal constraint (if any), discrete -% objective (if any), and total cost function objective for design -% optimization. -% -% (struct) -> (struct) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega, Claire V. Hammond % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function output = computeDesignOptimizationEndpointFunction(inputs) - -inputs.phase.state = [inputs.phase.initialstate; inputs.phase.finalstate]; -inputs.phase.time = [inputs.phase.initialtime; inputs.phase.finaltime]; -inputs.phase.control = ones(size(inputs.phase.time,1), ... - length(inputs.auxdata.minControl)); -phase = inputs.phase; -if isfield(inputs, "parameter") - phase.parameter = inputs.parameter; -end -values = getDesignOptimizationValueStruct(phase, inputs.auxdata); -inputs = updateSystemFromUserDefinedFunctions(inputs, values); -modeledValues = calcTorqueBasedModeledValues(values, inputs.auxdata); - -if ~isempty(inputs.auxdata.terminal) - output.eventgroup.event = calcDesignOptimizationTerminalConstraint( ... - values, modeledValues, inputs.auxdata); -end -discrete = calcDesignOptimizationDiscreteObjective(values, ... - modeledValues, inputs.auxdata); -% discrete = computeStaticParameterCost(inputs); -output.objective = calcDesignOptimizationObjective(discrete, ... - inputs.phase.integral, values.time(end), inputs.auxdata); -end - -function cost = computeStaticParameterCost(inputs) -costTerms = inputs.auxdata.costTerms; -cost = 0; -for i = 1:length(costTerms) - costTerm = costTerms{i}; - if strcmp(costTerm.type, "user_defined") && ... - strcmp(costTerm.cost_term_type, "discrete") - func = str2func(costTerm.function_name); - cost = cost + func(inputs); - end -end -end \ No newline at end of file diff --git a/src/DesignOptimization/computeDesignOptimizationMainFunction.m b/src/DesignOptimization/computeDesignOptimizationMainFunction.m deleted file mode 100644 index d0365f789..000000000 --- a/src/DesignOptimization/computeDesignOptimizationMainFunction.m +++ /dev/null @@ -1,86 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function sets up GPOPS-II to run Design Optimization. -% -% (struct) -> (struct, struct) -% Assigns optimal control settings and runs Design Optimization - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function output = computeDesignOptimizationMainFunction(inputs, params) -guess = setupCommonOptimalControlInitialGuess(inputs); -bounds = setupProblemBounds(inputs, params, guess); -guess = addUserDefinedTermsToGuess(guess, inputs); -setup = setupCommonOptimalControlSolverSettings(inputs, ... - bounds, guess, params, ... - @computeDesignOptimizationContinuousFunction, ... - @computeDesignOptimizationEndpointFunction); -checkInitialGuess(guess, inputs, ... - @computeDesignOptimizationContinuousFunction); -solution = gpops2(setup); -solution = solution.result.solution; -solution.auxdata = inputs; -if isfield(solution, 'parameter') - solution.phase.parameter = [solution.parameter]; -end -output = computeDesignOptimizationContinuousFunction(solution); -output.solution = solution; -end - -function bounds = setupProblemBounds(inputs, params, guess) -bounds = setupCommonOptimalControlBounds(inputs, params); -% setup parameter bounds -if strcmp(inputs.controllerType, 'synergy') - if inputs.optimizeSynergyVectors - bounds.parameter.lower = -0.5 * ones(1, length(inputs.minParameter)); - bounds.parameter.upper = 0.5 * ones(1, length(inputs.minParameter)); - end -end -for i = 1:length(inputs.userDefinedVariables) - variable = inputs.userDefinedVariables{i}; - if ~isfield(bounds, "parameter") || ... - ~isfield(bounds.parameter, "lower") - bounds.parameter.lower = [-0.5]; - bounds.parameter.upper = [0.5]; - else - bounds.parameter.lower = [bounds.parameter.lower, ... - -0.5]; - bounds.parameter.upper = [bounds.parameter.upper, ... - 0.5]; - end -end -end - -function guess = addUserDefinedTermsToGuess(guess, inputs) -for i = 1:length(inputs.userDefinedVariables) - variable = inputs.userDefinedVariables{i}; - if ~isfield(guess, "parameter") - guess.parameter = []; - end - guess.parameter = [guess.parameter, ... - scaleToBounds( ... - variable.initial_values, ... - variable.upper_bounds, ... - variable.lower_bounds)]; -end -end diff --git a/src/DesignOptimization/getDesignOptimizationValueStruct.m b/src/DesignOptimization/getDesignOptimizationValueStruct.m deleted file mode 100644 index f09ae5d3e..000000000 --- a/src/DesignOptimization/getDesignOptimizationValueStruct.m +++ /dev/null @@ -1,83 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function parses and scales the design variables specific to -% Design Optimization. If the model is synergy driven, synergy weights are -% properly calculated if they are fixed or being optimized. If the model -% has user defined parameters, they are parsed and scaled. Lastly, if -% the model has external torque actuators, they are parsed and -% scaled. -% -% (struct, struct) -> (struct) -% Design variables specific to Design Optimization are parsed and scaled - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega, Claire V. Hammond % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function values = getDesignOptimizationValueStruct(inputs, params) -values = getTreatmentOptimizationValueStruct(inputs, params); - -numParameters = 0; -if strcmp(params.controllerType, 'synergy') - if params.optimizeSynergyVectors - values.synergyWeights = scaleToOriginal(inputs.parameter(1, ... - 1 : params.numSynergyWeights), ... - params.maxParameter, params.minParameter); - values.synergyWeights = getSynergyWeightsFromGroups(... - values.synergyWeights, params); - numParameters = params.numSynergyWeights; - else - values.synergyWeights = getSynergyWeightsFromGroups(... - params.synergyWeightsGuess, params); - end - if params.splineSynergyActivations.dim > 1 - values.controlSynergyActivations = ... - fnval(params.splineSynergyActivations, values.time)'; - else - values.controlSynergyActivations = ... - fnval(params.splineSynergyActivations, values.time); - end - if params.enableExternalTorqueControl - controls = scaleToOriginal(inputs.control, ones(size( ... - inputs.control, 1), 1) .* params.maxControl, ... - ones(size(inputs.control, 1), 1) .* params.minControl); - values.externalTorqueControls = controls(:, params.numCoordinates + ... - params.numSynergies + 1 : end); - end -else - if isfield(params, "enableExternalTorqueControl") && ... - params.enableExternalTorqueControl - controls = scaleToOriginal(inputs.control, ones(size( ... - inputs.control, 1), 1) .* params.maxControl, ... - ones(size(inputs.control, 1), 1) .* params.minControl); - values.externalTorqueControls = controls(:, params.numCoordinates + ... - length(inputs.torqueControllerCoordinateNames) + 1 : end); - end -end -if isfield(params, 'userDefinedVariables') - for i = 1:length(params.userDefinedVariables) - values.(params.userDefinedVariables{i}.type)(i) = scaleToOriginal( ... - inputs.parameter(1, i + numParameters), ... - params.userDefinedVariables{i}.upper_bounds, ... - params.userDefinedVariables{i}.lower_bounds); - end -end -end \ No newline at end of file diff --git a/src/TrackingOptimization/TrackingOptimization.m b/src/TrackingOptimization/TrackingOptimization.m deleted file mode 100644 index 3204fd164..000000000 --- a/src/TrackingOptimization/TrackingOptimization.m +++ /dev/null @@ -1,35 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function sets up the input variables and mex files (or parallel -% matlab function) for the main function of tracking optimization. -% -% (struct, struct) -> (struct, struct) -% Inputs for the main function are setup - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega, Claire V. Hammond % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function [output, inputs] = TrackingOptimization(inputs, params) -inputs = makeTreatmentOptimizationInputs(inputs, params); -initializeMexOrMatlabParallelFunctions(inputs.mexModel); -output = computeTrackingOptimizationMainFunction(inputs, params); -end diff --git a/src/TrackingOptimization/calcTrackingOptimizationIntegrand.m b/src/TrackingOptimization/calcTrackingOptimizationIntegrand.m deleted file mode 100644 index a25d721c4..000000000 --- a/src/TrackingOptimization/calcTrackingOptimizationIntegrand.m +++ /dev/null @@ -1,38 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function calculates the integrand for tracking optimization. -% -% (struct, struct, struct) -> (2D matrix) -% Returns scaled integrand - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function integrand = calcTrackingOptimizationIntegrand(values, ... - modeledValues, auxdata) -[costTermCalculations, allowedTypes] = ... - generateCostTermStruct("continuous", "TrackingOptimization"); -integrand = calcTreatmentOptimizationCost( ... - costTermCalculations, allowedTypes, values, modeledValues, auxdata); -integrand = integrand ./ (auxdata.maxIntegral - auxdata.minIntegral); -integrand = integrand .^ 2; -end \ No newline at end of file diff --git a/src/TrackingOptimization/calcTrackingOptimizationObjective.m b/src/TrackingOptimization/calcTrackingOptimizationObjective.m deleted file mode 100644 index 6b8eef71d..000000000 --- a/src/TrackingOptimization/calcTrackingOptimizationObjective.m +++ /dev/null @@ -1,34 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function calculates the cost function objective for tracking -% optimization. -% -% (Number, Array of number, Number, struct) -> (Number) -% Returns objective - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function objective = calcTrackingOptimizationObjective(integral) -continuousObjective = sum(integral) / length(integral); -objective = continuousObjective; -end \ No newline at end of file diff --git a/src/TrackingOptimization/calcTrackingOptimizationPathConstraint.m b/src/TrackingOptimization/calcTrackingOptimizationPathConstraint.m deleted file mode 100644 index b6796f09c..000000000 --- a/src/TrackingOptimization/calcTrackingOptimizationPathConstraint.m +++ /dev/null @@ -1,58 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function calculates the path constraint for tracking optimization. -% -% (struct, struct, struct) -> (2D matrix) -% Returns path constraint - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function path = calcTrackingOptimizationPathConstraint(values, modeledValues, ... - params) -path = []; -for i = 1:length(params.path) - constraintTerm = params.path{i}; - if constraintTerm.isEnabled - switch constraintTerm.type - case "root_segment_residual_load" - path = cat(2, path, ... - calcRootSegmentResidualsPathConstraints(... - constraintTerm.load, ... - params.inverseDynamicMomentLabels, ... - modeledValues.inverseDynamicMoments)); - case "muscle_model_moment_consistency" - path = cat(2, path, ... - calcMuscleActuatedMomentsPathConstraints(params, ... - modeledValues, constraintTerm.load)); - case "torque_model_moment_consistency" - path = cat(2, path, ... - calcTorqueActuatedMomentsPathConstraints(params, ... - modeledValues, values.controlTorques, constraintTerm.load)); - otherwise - throw(MException('', ['Constraint term type ' ... - constraintTerm.type ' does not exist for this tool.'])) - end - end -end -path = scaleToBounds(path, params.maxPath, params.minPath); -end \ No newline at end of file diff --git a/src/TrackingOptimization/calcTrackingOptimizationTerminalConstraint.m b/src/TrackingOptimization/calcTrackingOptimizationTerminalConstraint.m deleted file mode 100644 index 5ca95148a..000000000 --- a/src/TrackingOptimization/calcTrackingOptimizationTerminalConstraint.m +++ /dev/null @@ -1,87 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function calculates the terminal constraint for tracking -% optimization -% -% (struct, struct, struct) -> (Array of number) -% Returns terminal constraint - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function event = calcTrackingOptimizationTerminalConstraint(inputs, params) - -inputs.phase.state = [inputs.phase.initialstate; inputs.phase.finalstate]; -inputs.phase.time = [inputs.phase.initialtime; inputs.phase.finaltime]; -inputs.phase.control = ones(size(inputs.phase.time,1),length(params.minControl)); -if inputs.auxdata.optimizeSynergyVectors - inputs.phase.parameter = inputs.parameter; -end -values = getTrackingOptimizationValueStruct(inputs.phase, params); -modeledValues = calcTorqueBasedModeledValues(values, params); - -event = []; -for i = 1:length(params.terminal) - constraintTerm = params.terminal{i}; - if constraintTerm.isEnabled - switch constraintTerm.type - case "state_position_periodicity" - event = cat(2, event, ... - calcStatePositionPeriodicity(values.statePositions, ... - params.coordinateNames, ... - constraintTerm.coordinate)); - case "state_velocity_periodicity" - event = cat(2, event, ... - calcStateVelocityPeriodicity(values.stateVelocities, ... - params.coordinateNames, ... - constraintTerm.coordinate)); - case "root_segment_residual_load_periodicity" - event = cat(2, event, ... - calcRootSegmentResidualsPeriodicity(... - modeledValues.inverseDynamicMoments, ... - params.inverseDynamicMomentLabels, ... - constraintTerm.load)); - case "external_force_tracking_periodicity" - event = cat(2, event, ... - calcExternalForcesPeriodicity(... - modeledValues.groundReactionsLab.forces, ... - params.contactSurfaces, ... - constraintTerm.force)); - case "external_moment_tracking_periodicity" - event = cat(2, event, ... - calcExternalMomentsPeriodicity(... - modeledValues.groundReactionsLab.moments, ... - params.contactSurfaces, ... - constraintTerm.moment)); - case "synergy_weight_sum" - event = cat(2, event, ... - calcSynergyWeightsSum(... - values.synergyWeights, ... - params.synergyGroups, ... - constraintTerm.synergy_group)); - otherwise - throw(MException('', ['Constraint term type ' ... - constraintTerm.type ' does not exist for this tool.'])) - end - end -end -end \ No newline at end of file diff --git a/src/TrackingOptimization/computeTrackingOptimizationContinuousFunction.m b/src/TrackingOptimization/computeTrackingOptimizationContinuousFunction.m deleted file mode 100644 index ebb92f5c7..000000000 --- a/src/TrackingOptimization/computeTrackingOptimizationContinuousFunction.m +++ /dev/null @@ -1,41 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function computes the dynamic constraints, path constraints (if any) -% and cost function terms (if any) for tracking optimization. -% -% (struct) -> (struct) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function modeledValues = computeTrackingOptimizationContinuousFunction(inputs) -values = getTrackingOptimizationValueStruct(inputs.phase, inputs.auxdata); -modeledValues = calcTorqueBasedModeledValues(values, inputs.auxdata); -modeledValues = calcSynergyBasedModeledValues(values, inputs.auxdata, modeledValues); -modeledValues.dynamics = calcDynamicConstraint(values, inputs.auxdata); -path = calcTrackingOptimizationPathConstraint(values, modeledValues, inputs.auxdata); -if ~isempty(path) - modeledValues.path = path; -end -modeledValues.integrand = calcTrackingOptimizationIntegrand(values, modeledValues, inputs.auxdata); -end diff --git a/src/TrackingOptimization/computeTrackingOptimizationEndpointFunction.m b/src/TrackingOptimization/computeTrackingOptimizationEndpointFunction.m deleted file mode 100644 index 75dc6cc77..000000000 --- a/src/TrackingOptimization/computeTrackingOptimizationEndpointFunction.m +++ /dev/null @@ -1,39 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function computes the terminal constraint (if any) and total cost -% function objective for tracking optimization. -% -% (struct) -> (struct) - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function output = computeTrackingOptimizationEndpointFunction(inputs) -if ~isempty(inputs.auxdata.terminal) - event = calcTrackingOptimizationTerminalConstraint( ... - inputs, inputs.auxdata); - if ~isempty(event) - output.eventgroup.event = event; - end -end -output.objective = calcTrackingOptimizationObjective(inputs.phase.integral); -end \ No newline at end of file diff --git a/src/TrackingOptimization/computeTrackingOptimizationMainFunction.m b/src/TrackingOptimization/computeTrackingOptimizationMainFunction.m deleted file mode 100644 index bc3f132c9..000000000 --- a/src/TrackingOptimization/computeTrackingOptimizationMainFunction.m +++ /dev/null @@ -1,58 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function sets up GPOPS-II to run Tracking Optimization. -% -% (struct) -> (struct, struct) -% Assigns optimal control settings and runs Tracking Optimization - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function output = computeTrackingOptimizationMainFunction(inputs, params) -bounds = setupProblemBounds(inputs, params); -guess = setupCommonOptimalControlInitialGuess(inputs); -setup = setupCommonOptimalControlSolverSettings(inputs, ... - bounds, guess, params, ... - @computeTrackingOptimizationContinuousFunction, ... - @computeTrackingOptimizationEndpointFunction); -checkInitialGuess(guess, inputs, ... - @computeTrackingOptimizationContinuousFunction); -solution = gpops2(setup); -solution = solution.result.solution; -solution.auxdata = inputs; -if inputs.optimizeSynergyVectors - solution.phase.parameter = solution.parameter; -end -output = computeTrackingOptimizationContinuousFunction(solution); -output.solution = solution; -end - -function bounds = setupProblemBounds(inputs, params) -bounds = setupCommonOptimalControlBounds(inputs, params); -% setup parameter bounds -if strcmp(inputs.controllerType, 'synergy') - if inputs.optimizeSynergyVectors - bounds.parameter.lower = -0.5 * ones(1, length(inputs.minParameter)); - bounds.parameter.upper = 0.5 * ones(1, length(inputs.minParameter)); - end -end -end diff --git a/src/core/TreatmentOptimization/BuiltInParameters/adjustMaxIsometricForce.m b/src/TreatmentOptimization/BuiltInParameters/adjustMaxIsometricForce.m similarity index 100% rename from src/core/TreatmentOptimization/BuiltInParameters/adjustMaxIsometricForce.m rename to src/TreatmentOptimization/BuiltInParameters/adjustMaxIsometricForce.m diff --git a/src/core/TreatmentOptimization/BuiltInParameters/adjustModelMaxIsometricForce.m b/src/TreatmentOptimization/BuiltInParameters/adjustModelMaxIsometricForce.m similarity index 100% rename from src/core/TreatmentOptimization/BuiltInParameters/adjustModelMaxIsometricForce.m rename to src/TreatmentOptimization/BuiltInParameters/adjustModelMaxIsometricForce.m diff --git a/src/core/TreatmentOptimization/BuiltInParameters/adjustModelOptimalFiberLength.m b/src/TreatmentOptimization/BuiltInParameters/adjustModelOptimalFiberLength.m similarity index 100% rename from src/core/TreatmentOptimization/BuiltInParameters/adjustModelOptimalFiberLength.m rename to src/TreatmentOptimization/BuiltInParameters/adjustModelOptimalFiberLength.m diff --git a/src/core/TreatmentOptimization/BuiltInParameters/adjustModelTendonSlackLength.m b/src/TreatmentOptimization/BuiltInParameters/adjustModelTendonSlackLength.m similarity index 100% rename from src/core/TreatmentOptimization/BuiltInParameters/adjustModelTendonSlackLength.m rename to src/TreatmentOptimization/BuiltInParameters/adjustModelTendonSlackLength.m diff --git a/src/core/TreatmentOptimization/BuiltInParameters/adjustOptimalFiberLength.m b/src/TreatmentOptimization/BuiltInParameters/adjustOptimalFiberLength.m similarity index 100% rename from src/core/TreatmentOptimization/BuiltInParameters/adjustOptimalFiberLength.m rename to src/TreatmentOptimization/BuiltInParameters/adjustOptimalFiberLength.m diff --git a/src/core/TreatmentOptimization/BuiltInParameters/adjustTendonSlackLength.m b/src/TreatmentOptimization/BuiltInParameters/adjustTendonSlackLength.m similarity index 100% rename from src/core/TreatmentOptimization/BuiltInParameters/adjustTendonSlackLength.m rename to src/TreatmentOptimization/BuiltInParameters/adjustTendonSlackLength.m diff --git a/src/core/TreatmentOptimization/BuiltInParameters/applyBeltSpeedParameter.m b/src/TreatmentOptimization/BuiltInParameters/applyBeltSpeedParameter.m similarity index 100% rename from src/core/TreatmentOptimization/BuiltInParameters/applyBeltSpeedParameter.m rename to src/TreatmentOptimization/BuiltInParameters/applyBeltSpeedParameter.m diff --git a/src/core/TreatmentOptimization/BuiltInParameters/applyMaxIsometricForceParameter.m b/src/TreatmentOptimization/BuiltInParameters/applyMaxIsometricForceParameter.m similarity index 100% rename from src/core/TreatmentOptimization/BuiltInParameters/applyMaxIsometricForceParameter.m rename to src/TreatmentOptimization/BuiltInParameters/applyMaxIsometricForceParameter.m diff --git a/src/core/TreatmentOptimization/BuiltInParameters/applyOptimalFiberLengthParameter.m b/src/TreatmentOptimization/BuiltInParameters/applyOptimalFiberLengthParameter.m similarity index 100% rename from src/core/TreatmentOptimization/BuiltInParameters/applyOptimalFiberLengthParameter.m rename to src/TreatmentOptimization/BuiltInParameters/applyOptimalFiberLengthParameter.m diff --git a/src/core/TreatmentOptimization/BuiltInParameters/applyTendonSlackLengthParameter.m b/src/TreatmentOptimization/BuiltInParameters/applyTendonSlackLengthParameter.m similarity index 100% rename from src/core/TreatmentOptimization/BuiltInParameters/applyTendonSlackLengthParameter.m rename to src/TreatmentOptimization/BuiltInParameters/applyTendonSlackLengthParameter.m diff --git a/src/DesignOptimization/DesignOptimizationTool.m b/src/TreatmentOptimization/DesignOptimization/DesignOptimizationTool.m similarity index 100% rename from src/DesignOptimization/DesignOptimizationTool.m rename to src/TreatmentOptimization/DesignOptimization/DesignOptimizationTool.m diff --git a/src/DesignOptimization/parseDesignOptimizationSettingsTree.m b/src/TreatmentOptimization/DesignOptimization/parseDesignOptimizationSettingsTree.m similarity index 100% rename from src/DesignOptimization/parseDesignOptimizationSettingsTree.m rename to src/TreatmentOptimization/DesignOptimization/parseDesignOptimizationSettingsTree.m diff --git a/src/DesignOptimization/reportingGaitSpecificMeasurements.m b/src/TreatmentOptimization/DesignOptimization/reportingGaitSpecificMeasurements.m similarity index 100% rename from src/DesignOptimization/reportingGaitSpecificMeasurements.m rename to src/TreatmentOptimization/DesignOptimization/reportingGaitSpecificMeasurements.m diff --git a/src/DesignOptimization/saveDesignOptimizationResults.m b/src/TreatmentOptimization/DesignOptimization/saveDesignOptimizationResults.m similarity index 100% rename from src/DesignOptimization/saveDesignOptimizationResults.m rename to src/TreatmentOptimization/DesignOptimization/saveDesignOptimizationResults.m diff --git a/src/DesignOptimization/updateSystemFromUserDefinedFunctions.m b/src/TreatmentOptimization/DesignOptimization/updateSystemFromUserDefinedFunctions.m similarity index 100% rename from src/DesignOptimization/updateSystemFromUserDefinedFunctions.m rename to src/TreatmentOptimization/DesignOptimization/updateSystemFromUserDefinedFunctions.m diff --git a/src/core/TreatmentOptimization/DiscreteTerms/calcTrackingSynergyVectorsDiscrete.m b/src/TreatmentOptimization/DiscreteTerms/calcTrackingSynergyVectorsDiscrete.m similarity index 100% rename from src/core/TreatmentOptimization/DiscreteTerms/calcTrackingSynergyVectorsDiscrete.m rename to src/TreatmentOptimization/DiscreteTerms/calcTrackingSynergyVectorsDiscrete.m diff --git a/src/core/TreatmentOptimization/IntegrandComputations/calcMaximizingCostArrayTerm.m b/src/TreatmentOptimization/IntegrandComputations/calcMaximizingCostArrayTerm.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandComputations/calcMaximizingCostArrayTerm.m rename to src/TreatmentOptimization/IntegrandComputations/calcMaximizingCostArrayTerm.m diff --git a/src/core/TreatmentOptimization/IntegrandComputations/calcMinimizingCostArrayTerm.m b/src/TreatmentOptimization/IntegrandComputations/calcMinimizingCostArrayTerm.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandComputations/calcMinimizingCostArrayTerm.m rename to src/TreatmentOptimization/IntegrandComputations/calcMinimizingCostArrayTerm.m diff --git a/src/core/TreatmentOptimization/IntegrandComputations/calcTrackingCostArrayTerm.m b/src/TreatmentOptimization/IntegrandComputations/calcTrackingCostArrayTerm.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandComputations/calcTrackingCostArrayTerm.m rename to src/TreatmentOptimization/IntegrandComputations/calcTrackingCostArrayTerm.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcGoalBeltSpeedDiscrete.m b/src/TreatmentOptimization/IntegrandTerms/calcGoalBeltSpeedDiscrete.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/calcGoalBeltSpeedDiscrete.m rename to src/TreatmentOptimization/IntegrandTerms/calcGoalBeltSpeedDiscrete.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcGoalSingleSupportTimeIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcGoalSingleSupportTimeIntegrand.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/calcGoalSingleSupportTimeIntegrand.m rename to src/TreatmentOptimization/IntegrandTerms/calcGoalSingleSupportTimeIntegrand.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcGoalWalkingSpeedIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcGoalWalkingSpeedIntegrand.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/calcGoalWalkingSpeedIntegrand.m rename to src/TreatmentOptimization/IntegrandTerms/calcGoalWalkingSpeedIntegrand.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcKinematicSymmetryIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcKinematicSymmetryIntegrand.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/calcKinematicSymmetryIntegrand.m rename to src/TreatmentOptimization/IntegrandTerms/calcKinematicSymmetryIntegrand.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcMassCenterVelocity.m b/src/TreatmentOptimization/IntegrandTerms/calcMassCenterVelocity.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/calcMassCenterVelocity.m rename to src/TreatmentOptimization/IntegrandTerms/calcMassCenterVelocity.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcMaximizingBreakingForceIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMaximizingBreakingForceIntegrand.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/calcMaximizingBreakingForceIntegrand.m rename to src/TreatmentOptimization/IntegrandTerms/calcMaximizingBreakingForceIntegrand.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcMaximizingMuscleActivationIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMaximizingMuscleActivationIntegrand.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/calcMaximizingMuscleActivationIntegrand.m rename to src/TreatmentOptimization/IntegrandTerms/calcMaximizingMuscleActivationIntegrand.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcMaximizingPropulsiveForceIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMaximizingPropulsiveForceIntegrand.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/calcMaximizingPropulsiveForceIntegrand.m rename to src/TreatmentOptimization/IntegrandTerms/calcMaximizingPropulsiveForceIntegrand.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcMaximizingSingleSupportTimeIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMaximizingSingleSupportTimeIntegrand.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/calcMaximizingSingleSupportTimeIntegrand.m rename to src/TreatmentOptimization/IntegrandTerms/calcMaximizingSingleSupportTimeIntegrand.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcMaximizingStepLengthIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMaximizingStepLengthIntegrand.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/calcMaximizingStepLengthIntegrand.m rename to src/TreatmentOptimization/IntegrandTerms/calcMaximizingStepLengthIntegrand.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcMaximizingTrailingLimbAngleIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMaximizingTrailingLimbAngleIntegrand.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/calcMaximizingTrailingLimbAngleIntegrand.m rename to src/TreatmentOptimization/IntegrandTerms/calcMaximizingTrailingLimbAngleIntegrand.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingBreakingForceIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingBreakingForceIntegrand.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingBreakingForceIntegrand.m rename to src/TreatmentOptimization/IntegrandTerms/calcMinimizingBreakingForceIntegrand.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingExternalTorqueControl.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingExternalTorqueControl.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingExternalTorqueControl.m rename to src/TreatmentOptimization/IntegrandTerms/calcMinimizingExternalTorqueControl.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingJointJerkIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointJerkIntegrand.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingJointJerkIntegrand.m rename to src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointJerkIntegrand.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingJointPowerIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointPowerIntegrand.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingJointPowerIntegrand.m rename to src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointPowerIntegrand.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityXIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityXIntegrand.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityXIntegrand.m rename to src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityXIntegrand.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityYIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityYIntegrand.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityYIntegrand.m rename to src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityYIntegrand.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityZIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityZIntegrand.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityZIntegrand.m rename to src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityZIntegrand.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingMetabolicCost.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMetabolicCost.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingMetabolicCost.m rename to src/TreatmentOptimization/IntegrandTerms/calcMinimizingMetabolicCost.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingMuscleActivationIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMuscleActivationIntegrand.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingMuscleActivationIntegrand.m rename to src/TreatmentOptimization/IntegrandTerms/calcMinimizingMuscleActivationIntegrand.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingPropulsiveForceIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingPropulsiveForceIntegrand.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingPropulsiveForceIntegrand.m rename to src/TreatmentOptimization/IntegrandTerms/calcMinimizingPropulsiveForceIntegrand.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingTrailingLimbAngleIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingTrailingLimbAngleIntegrand.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingTrailingLimbAngleIntegrand.m rename to src/TreatmentOptimization/IntegrandTerms/calcMinimizingTrailingLimbAngleIntegrand.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcSingleSupportTime.m b/src/TreatmentOptimization/IntegrandTerms/calcSingleSupportTime.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/calcSingleSupportTime.m rename to src/TreatmentOptimization/IntegrandTerms/calcSingleSupportTime.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcStepLength.m b/src/TreatmentOptimization/IntegrandTerms/calcStepLength.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/calcStepLength.m rename to src/TreatmentOptimization/IntegrandTerms/calcStepLength.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcStepLengthAsymmetry.m b/src/TreatmentOptimization/IntegrandTerms/calcStepLengthAsymmetry.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/calcStepLengthAsymmetry.m rename to src/TreatmentOptimization/IntegrandTerms/calcStepLengthAsymmetry.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcStepLengthAsymmetryIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcStepLengthAsymmetryIntegrand.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/calcStepLengthAsymmetryIntegrand.m rename to src/TreatmentOptimization/IntegrandTerms/calcStepLengthAsymmetryIntegrand.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcStepTimeAsymmetry.m b/src/TreatmentOptimization/IntegrandTerms/calcStepTimeAsymmetry.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/calcStepTimeAsymmetry.m rename to src/TreatmentOptimization/IntegrandTerms/calcStepTimeAsymmetry.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcStepTimeAsymmetryIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcStepTimeAsymmetryIntegrand.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/calcStepTimeAsymmetryIntegrand.m rename to src/TreatmentOptimization/IntegrandTerms/calcStepTimeAsymmetryIntegrand.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcStrideLength.m b/src/TreatmentOptimization/IntegrandTerms/calcStrideLength.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/calcStrideLength.m rename to src/TreatmentOptimization/IntegrandTerms/calcStrideLength.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m rename to src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m rename to src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m rename to src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m rename to src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m rename to src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m rename to src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcTrailingLimb.m b/src/TreatmentOptimization/IntegrandTerms/calcTrailingLimb.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/calcTrailingLimb.m rename to src/TreatmentOptimization/IntegrandTerms/calcTrailingLimb.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/getBreakingForce.m b/src/TreatmentOptimization/IntegrandTerms/getBreakingForce.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/getBreakingForce.m rename to src/TreatmentOptimization/IntegrandTerms/getBreakingForce.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/getHeelStrikeEvent.m b/src/TreatmentOptimization/IntegrandTerms/getHeelStrikeEvent.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/getHeelStrikeEvent.m rename to src/TreatmentOptimization/IntegrandTerms/getHeelStrikeEvent.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/getPropulsiveForce.m b/src/TreatmentOptimization/IntegrandTerms/getPropulsiveForce.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/getPropulsiveForce.m rename to src/TreatmentOptimization/IntegrandTerms/getPropulsiveForce.m diff --git a/src/core/TreatmentOptimization/IntegrandTerms/getToeOffEvent.m b/src/TreatmentOptimization/IntegrandTerms/getToeOffEvent.m similarity index 100% rename from src/core/TreatmentOptimization/IntegrandTerms/getToeOffEvent.m rename to src/TreatmentOptimization/IntegrandTerms/getToeOffEvent.m diff --git a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/checkControlGuess.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/checkControlGuess.m similarity index 100% rename from src/core/TreatmentOptimization/OptimalControlSetupFunctions/checkControlGuess.m rename to src/TreatmentOptimization/OptimalControlSetupFunctions/checkControlGuess.m diff --git a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/checkParameterGuess.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/checkParameterGuess.m similarity index 100% rename from src/core/TreatmentOptimization/OptimalControlSetupFunctions/checkParameterGuess.m rename to src/TreatmentOptimization/OptimalControlSetupFunctions/checkParameterGuess.m diff --git a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/checkStateGuess.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/checkStateGuess.m similarity index 100% rename from src/core/TreatmentOptimization/OptimalControlSetupFunctions/checkStateGuess.m rename to src/TreatmentOptimization/OptimalControlSetupFunctions/checkStateGuess.m diff --git a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlBounds.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlBounds.m similarity index 100% rename from src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlBounds.m rename to src/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlBounds.m diff --git a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m similarity index 100% rename from src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m rename to src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m diff --git a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsSettings.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsSettings.m similarity index 100% rename from src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsSettings.m rename to src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsSettings.m diff --git a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupGroundContact.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGroundContact.m similarity index 100% rename from src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupGroundContact.m rename to src/TreatmentOptimization/OptimalControlSetupFunctions/setupGroundContact.m diff --git a/src/core/TreatmentOptimization/PathTerms/calcMuscleActivationsPathConstraint.m b/src/TreatmentOptimization/PathTerms/calcMuscleActivationsPathConstraint.m similarity index 100% rename from src/core/TreatmentOptimization/PathTerms/calcMuscleActivationsPathConstraint.m rename to src/TreatmentOptimization/PathTerms/calcMuscleActivationsPathConstraint.m diff --git a/src/core/TreatmentOptimization/PathTerms/calcMuscleActuatedMomentsPathConstraints.m b/src/TreatmentOptimization/PathTerms/calcMuscleActuatedMomentsPathConstraints.m similarity index 100% rename from src/core/TreatmentOptimization/PathTerms/calcMuscleActuatedMomentsPathConstraints.m rename to src/TreatmentOptimization/PathTerms/calcMuscleActuatedMomentsPathConstraints.m diff --git a/src/core/TreatmentOptimization/PathTerms/calcMuscleActuatedMomentsWithExternalAidPathConstraints.m b/src/TreatmentOptimization/PathTerms/calcMuscleActuatedMomentsWithExternalAidPathConstraints.m similarity index 100% rename from src/core/TreatmentOptimization/PathTerms/calcMuscleActuatedMomentsWithExternalAidPathConstraints.m rename to src/TreatmentOptimization/PathTerms/calcMuscleActuatedMomentsWithExternalAidPathConstraints.m diff --git a/src/core/TreatmentOptimization/PathTerms/calcNormalizedFiberLengthPathConstraint.m b/src/TreatmentOptimization/PathTerms/calcNormalizedFiberLengthPathConstraint.m similarity index 100% rename from src/core/TreatmentOptimization/PathTerms/calcNormalizedFiberLengthPathConstraint.m rename to src/TreatmentOptimization/PathTerms/calcNormalizedFiberLengthPathConstraint.m diff --git a/src/core/TreatmentOptimization/PathTerms/calcRootSegmentResidualsPathConstraints.m b/src/TreatmentOptimization/PathTerms/calcRootSegmentResidualsPathConstraints.m similarity index 100% rename from src/core/TreatmentOptimization/PathTerms/calcRootSegmentResidualsPathConstraints.m rename to src/TreatmentOptimization/PathTerms/calcRootSegmentResidualsPathConstraints.m diff --git a/src/core/TreatmentOptimization/PathTerms/calcTorqueActuatedMomentsPathConstraints.m b/src/TreatmentOptimization/PathTerms/calcTorqueActuatedMomentsPathConstraints.m similarity index 100% rename from src/core/TreatmentOptimization/PathTerms/calcTorqueActuatedMomentsPathConstraints.m rename to src/TreatmentOptimization/PathTerms/calcTorqueActuatedMomentsPathConstraints.m diff --git a/src/core/TreatmentOptimization/SetupBounds/makeMaxAllowableError.m b/src/TreatmentOptimization/SetupBounds/makeMaxAllowableError.m similarity index 100% rename from src/core/TreatmentOptimization/SetupBounds/makeMaxAllowableError.m rename to src/TreatmentOptimization/SetupBounds/makeMaxAllowableError.m diff --git a/src/core/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m b/src/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m similarity index 100% rename from src/core/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m rename to src/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m diff --git a/src/core/TreatmentOptimization/SetupBounds/makePathConstraintBounds.m b/src/TreatmentOptimization/SetupBounds/makePathConstraintBounds.m similarity index 100% rename from src/core/TreatmentOptimization/SetupBounds/makePathConstraintBounds.m rename to src/TreatmentOptimization/SetupBounds/makePathConstraintBounds.m diff --git a/src/core/TreatmentOptimization/SetupBounds/makeTerminalConstraintBounds.m b/src/TreatmentOptimization/SetupBounds/makeTerminalConstraintBounds.m similarity index 100% rename from src/core/TreatmentOptimization/SetupBounds/makeTerminalConstraintBounds.m rename to src/TreatmentOptimization/SetupBounds/makeTerminalConstraintBounds.m diff --git a/src/core/TreatmentOptimization/TerminalTerms/calcExternalForcesPeriodicity.m b/src/TreatmentOptimization/TerminalTerms/calcExternalForcesPeriodicity.m similarity index 100% rename from src/core/TreatmentOptimization/TerminalTerms/calcExternalForcesPeriodicity.m rename to src/TreatmentOptimization/TerminalTerms/calcExternalForcesPeriodicity.m diff --git a/src/core/TreatmentOptimization/TerminalTerms/calcExternalMomentsPeriodicity.m b/src/TreatmentOptimization/TerminalTerms/calcExternalMomentsPeriodicity.m similarity index 100% rename from src/core/TreatmentOptimization/TerminalTerms/calcExternalMomentsPeriodicity.m rename to src/TreatmentOptimization/TerminalTerms/calcExternalMomentsPeriodicity.m diff --git a/src/core/TreatmentOptimization/TerminalTerms/calcFinalPointPosition.m b/src/TreatmentOptimization/TerminalTerms/calcFinalPointPosition.m similarity index 100% rename from src/core/TreatmentOptimization/TerminalTerms/calcFinalPointPosition.m rename to src/TreatmentOptimization/TerminalTerms/calcFinalPointPosition.m diff --git a/src/core/TreatmentOptimization/TerminalTerms/calcFinalPointVelocity.m b/src/TreatmentOptimization/TerminalTerms/calcFinalPointVelocity.m similarity index 100% rename from src/core/TreatmentOptimization/TerminalTerms/calcFinalPointVelocity.m rename to src/TreatmentOptimization/TerminalTerms/calcFinalPointVelocity.m diff --git a/src/core/TreatmentOptimization/TerminalTerms/calcFinalStatePosition.m b/src/TreatmentOptimization/TerminalTerms/calcFinalStatePosition.m similarity index 100% rename from src/core/TreatmentOptimization/TerminalTerms/calcFinalStatePosition.m rename to src/TreatmentOptimization/TerminalTerms/calcFinalStatePosition.m diff --git a/src/core/TreatmentOptimization/TerminalTerms/calcFinalStateVelocity.m b/src/TreatmentOptimization/TerminalTerms/calcFinalStateVelocity.m similarity index 100% rename from src/core/TreatmentOptimization/TerminalTerms/calcFinalStateVelocity.m rename to src/TreatmentOptimization/TerminalTerms/calcFinalStateVelocity.m diff --git a/src/core/TreatmentOptimization/TerminalTerms/calcRootSegmentResidualsPeriodicity.m b/src/TreatmentOptimization/TerminalTerms/calcRootSegmentResidualsPeriodicity.m similarity index 100% rename from src/core/TreatmentOptimization/TerminalTerms/calcRootSegmentResidualsPeriodicity.m rename to src/TreatmentOptimization/TerminalTerms/calcRootSegmentResidualsPeriodicity.m diff --git a/src/core/TreatmentOptimization/TerminalTerms/calcStatePositionPeriodicity.m b/src/TreatmentOptimization/TerminalTerms/calcStatePositionPeriodicity.m similarity index 100% rename from src/core/TreatmentOptimization/TerminalTerms/calcStatePositionPeriodicity.m rename to src/TreatmentOptimization/TerminalTerms/calcStatePositionPeriodicity.m diff --git a/src/core/TreatmentOptimization/TerminalTerms/calcStateVelocityPeriodicity.m b/src/TreatmentOptimization/TerminalTerms/calcStateVelocityPeriodicity.m similarity index 100% rename from src/core/TreatmentOptimization/TerminalTerms/calcStateVelocityPeriodicity.m rename to src/TreatmentOptimization/TerminalTerms/calcStateVelocityPeriodicity.m diff --git a/src/core/TreatmentOptimization/TerminalTerms/calcSynergyWeightsSum.m b/src/TreatmentOptimization/TerminalTerms/calcSynergyWeightsSum.m similarity index 100% rename from src/core/TreatmentOptimization/TerminalTerms/calcSynergyWeightsSum.m rename to src/TreatmentOptimization/TerminalTerms/calcSynergyWeightsSum.m diff --git a/src/TrackingOptimization/TrackingOptimizationTool.m b/src/TreatmentOptimization/TrackingOptimization/TrackingOptimizationTool.m similarity index 100% rename from src/TrackingOptimization/TrackingOptimizationTool.m rename to src/TreatmentOptimization/TrackingOptimization/TrackingOptimizationTool.m diff --git a/src/TrackingOptimization/calcFootGroundReactions.m b/src/TreatmentOptimization/TrackingOptimization/calcFootGroundReactions.m similarity index 100% rename from src/TrackingOptimization/calcFootGroundReactions.m rename to src/TreatmentOptimization/TrackingOptimization/calcFootGroundReactions.m diff --git a/src/TrackingOptimization/calcSynergyBasedModeledValues.m b/src/TreatmentOptimization/TrackingOptimization/calcSynergyBasedModeledValues.m similarity index 100% rename from src/TrackingOptimization/calcSynergyBasedModeledValues.m rename to src/TreatmentOptimization/TrackingOptimization/calcSynergyBasedModeledValues.m diff --git a/src/TrackingOptimization/calcTorqueBasedModeledValues.m b/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m similarity index 100% rename from src/TrackingOptimization/calcTorqueBasedModeledValues.m rename to src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m diff --git a/src/TrackingOptimization/getTrackingOptimizationValueStruct.m b/src/TreatmentOptimization/TrackingOptimization/getTrackingOptimizationValueStruct.m similarity index 100% rename from src/TrackingOptimization/getTrackingOptimizationValueStruct.m rename to src/TreatmentOptimization/TrackingOptimization/getTrackingOptimizationValueStruct.m diff --git a/src/TrackingOptimization/parseTrackingOptimizationSettingsTree.m b/src/TreatmentOptimization/TrackingOptimization/parseTrackingOptimizationSettingsTree.m similarity index 100% rename from src/TrackingOptimization/parseTrackingOptimizationSettingsTree.m rename to src/TreatmentOptimization/TrackingOptimization/parseTrackingOptimizationSettingsTree.m diff --git a/src/TrackingOptimization/saveTrackingOptimizationResults.m b/src/TreatmentOptimization/TrackingOptimization/saveTrackingOptimizationResults.m similarity index 96% rename from src/TrackingOptimization/saveTrackingOptimizationResults.m rename to src/TreatmentOptimization/TrackingOptimization/saveTrackingOptimizationResults.m index 886efe82c..9d9124743 100644 --- a/src/TrackingOptimization/saveTrackingOptimizationResults.m +++ b/src/TreatmentOptimization/TrackingOptimization/saveTrackingOptimizationResults.m @@ -29,7 +29,7 @@ % ----------------------------------------------------------------------- % function saveTrackingOptimizationResults(solution, inputs) -values = getTrackingOptimizationValueStruct(solution.solution.phase, inputs); +values = makeGpopsValuesAsStruct(solution.solution.phase, inputs); saveTreatmentOptimizationResults(solution, inputs, values); if strcmp(inputs.controllerType, 'synergy') writeToSto(inputs.muscleLabels, linspace(1, inputs.numSynergies, ... @@ -39,4 +39,4 @@ function saveTrackingOptimizationResults(solution, inputs) solution.muscleActivations, ... fullfile(inputs.resultsDirectory, strcat(inputs.trialName, "_combinedActivations.sto"))); end -end \ No newline at end of file +end diff --git a/src/VerificationOptimization/VerificationOptimizationTool.m b/src/TreatmentOptimization/VerificationOptimization/VerificationOptimizationTool.m similarity index 100% rename from src/VerificationOptimization/VerificationOptimizationTool.m rename to src/TreatmentOptimization/VerificationOptimization/VerificationOptimizationTool.m diff --git a/src/VerificationOptimization/getVerificationOptimizationValueStruct.m b/src/TreatmentOptimization/VerificationOptimization/getVerificationOptimizationValueStruct.m similarity index 100% rename from src/VerificationOptimization/getVerificationOptimizationValueStruct.m rename to src/TreatmentOptimization/VerificationOptimization/getVerificationOptimizationValueStruct.m diff --git a/src/VerificationOptimization/parseVerificationOptimizationSettingsTree.m b/src/TreatmentOptimization/VerificationOptimization/parseVerificationOptimizationSettingsTree.m similarity index 100% rename from src/VerificationOptimization/parseVerificationOptimizationSettingsTree.m rename to src/TreatmentOptimization/VerificationOptimization/parseVerificationOptimizationSettingsTree.m diff --git a/src/VerificationOptimization/saveVerificationOptimizationResults.m b/src/TreatmentOptimization/VerificationOptimization/saveVerificationOptimizationResults.m similarity index 100% rename from src/VerificationOptimization/saveVerificationOptimizationResults.m rename to src/TreatmentOptimization/VerificationOptimization/saveVerificationOptimizationResults.m diff --git a/src/core/TreatmentOptimization/addContactSurfaceActuators.m b/src/TreatmentOptimization/addContactSurfaceActuators.m similarity index 100% rename from src/core/TreatmentOptimization/addContactSurfaceActuators.m rename to src/TreatmentOptimization/addContactSurfaceActuators.m diff --git a/src/core/TreatmentOptimization/calcTreatmentOptimizationCost.m b/src/TreatmentOptimization/calcTreatmentOptimizationCost.m similarity index 100% rename from src/core/TreatmentOptimization/calcTreatmentOptimizationCost.m rename to src/TreatmentOptimization/calcTreatmentOptimizationCost.m diff --git a/src/core/TreatmentOptimization/checkInitialGuess.m b/src/TreatmentOptimization/checkInitialGuess.m similarity index 100% rename from src/core/TreatmentOptimization/checkInitialGuess.m rename to src/TreatmentOptimization/checkInitialGuess.m diff --git a/src/core/TreatmentOptimization/disableModelMuscles.m b/src/TreatmentOptimization/disableModelMuscles.m similarity index 100% rename from src/core/TreatmentOptimization/disableModelMuscles.m rename to src/TreatmentOptimization/disableModelMuscles.m diff --git a/src/core/TreatmentOptimization/generateConstraintTermStruct.m b/src/TreatmentOptimization/generateConstraintTermStruct.m similarity index 100% rename from src/core/TreatmentOptimization/generateConstraintTermStruct.m rename to src/TreatmentOptimization/generateConstraintTermStruct.m diff --git a/src/core/TreatmentOptimization/generateCostTermStruct.m b/src/TreatmentOptimization/generateCostTermStruct.m similarity index 100% rename from src/core/TreatmentOptimization/generateCostTermStruct.m rename to src/TreatmentOptimization/generateCostTermStruct.m diff --git a/src/core/TreatmentOptimization/getCorrectStates.m b/src/TreatmentOptimization/getCorrectStates.m similarity index 100% rename from src/core/TreatmentOptimization/getCorrectStates.m rename to src/TreatmentOptimization/getCorrectStates.m diff --git a/src/core/TreatmentOptimization/getTreatmentOptimizationValueStruct.m b/src/TreatmentOptimization/getTreatmentOptimizationValueStruct.m similarity index 100% rename from src/core/TreatmentOptimization/getTreatmentOptimizationValueStruct.m rename to src/TreatmentOptimization/getTreatmentOptimizationValueStruct.m diff --git a/src/core/TreatmentOptimization/makeExperimentalDataSplines.m b/src/TreatmentOptimization/makeExperimentalDataSplines.m similarity index 100% rename from src/core/TreatmentOptimization/makeExperimentalDataSplines.m rename to src/TreatmentOptimization/makeExperimentalDataSplines.m diff --git a/src/core/TreatmentOptimization/makeStateDerivatives.m b/src/TreatmentOptimization/makeStateDerivatives.m similarity index 100% rename from src/core/TreatmentOptimization/makeStateDerivatives.m rename to src/TreatmentOptimization/makeStateDerivatives.m diff --git a/src/core/TreatmentOptimization/makeSurrogateModel.m b/src/TreatmentOptimization/makeSurrogateModel.m similarity index 100% rename from src/core/TreatmentOptimization/makeSurrogateModel.m rename to src/TreatmentOptimization/makeSurrogateModel.m diff --git a/src/core/TreatmentOptimization/makeTreatmentOptimizationInputs.m b/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m similarity index 100% rename from src/core/TreatmentOptimization/makeTreatmentOptimizationInputs.m rename to src/TreatmentOptimization/makeTreatmentOptimizationInputs.m diff --git a/src/core/TreatmentOptimization/modifyModelForces.m b/src/TreatmentOptimization/modifyModelForces.m similarity index 100% rename from src/core/TreatmentOptimization/modifyModelForces.m rename to src/TreatmentOptimization/modifyModelForces.m diff --git a/src/core/TreatmentOptimization/parseGroundContactSurfaces.m b/src/TreatmentOptimization/parseGroundContactSurfaces.m similarity index 100% rename from src/core/TreatmentOptimization/parseGroundContactSurfaces.m rename to src/TreatmentOptimization/parseGroundContactSurfaces.m diff --git a/src/core/TreatmentOptimization/reportTreatmentOptimizationResults.m b/src/TreatmentOptimization/reportTreatmentOptimizationResults.m similarity index 100% rename from src/core/TreatmentOptimization/reportTreatmentOptimizationResults.m rename to src/TreatmentOptimization/reportTreatmentOptimizationResults.m diff --git a/src/core/TreatmentOptimization/saveTreatmentOptimizationResults.m b/src/TreatmentOptimization/saveTreatmentOptimizationResults.m similarity index 100% rename from src/core/TreatmentOptimization/saveTreatmentOptimizationResults.m rename to src/TreatmentOptimization/saveTreatmentOptimizationResults.m diff --git a/src/core/TreatmentOptimization/scaleToBounds.m b/src/TreatmentOptimization/scaleToBounds.m similarity index 100% rename from src/core/TreatmentOptimization/scaleToBounds.m rename to src/TreatmentOptimization/scaleToBounds.m diff --git a/src/core/TreatmentOptimization/scaleToOriginal.m b/src/TreatmentOptimization/scaleToOriginal.m similarity index 100% rename from src/core/TreatmentOptimization/scaleToOriginal.m rename to src/TreatmentOptimization/scaleToOriginal.m diff --git a/src/core/TreatmentOptimization/transferMoments.m b/src/TreatmentOptimization/transferMoments.m similarity index 100% rename from src/core/TreatmentOptimization/transferMoments.m rename to src/TreatmentOptimization/transferMoments.m diff --git a/src/VerificationOptimization/VerificationOptimization.m b/src/VerificationOptimization/VerificationOptimization.m deleted file mode 100644 index 23dc0ac7e..000000000 --- a/src/VerificationOptimization/VerificationOptimization.m +++ /dev/null @@ -1,38 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function sets up the input variables and mex files (or parallel -% matlab function) for the main function verification optimization. -% -% (struct, struct) -> (struct, struct) -% Inputs for the main function are setup - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega, Claire V. Hammond % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function [output, inputs] = VerificationOptimization(inputs, params) -inputs = makeTreatmentOptimizationInputs(inputs, params); -initializeMexOrMatlabParallelFunctions(inputs.mexModel); -if strcmp(inputs.controllerType, 'synergy') -end -output = computeVerificationOptimizationMainFunction(inputs, params); -end - diff --git a/src/VerificationOptimization/calcVerificationOptimizationIntegrand.m b/src/VerificationOptimization/calcVerificationOptimizationIntegrand.m deleted file mode 100644 index d8128427b..000000000 --- a/src/VerificationOptimization/calcVerificationOptimizationIntegrand.m +++ /dev/null @@ -1,38 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function calculates the integrand for verification optimization. -% -% (struct, struct, struct) -> (2D matrix) -% Returns scaled integrand - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function integrand = calcVerificationOptimizationIntegrand(values, ... - modeledValues, auxdata) -[costTermCalculations, allowedTypes] = ... - generateCostTermStruct("continuous", "VerificationOptimization"); -integrand = calcTreatmentOptimizationCost( ... - costTermCalculations, allowedTypes, values, modeledValues, auxdata); -integrand = integrand ./ (auxdata.maxIntegral - auxdata.minIntegral); -integrand = integrand .^ 2; -end \ No newline at end of file diff --git a/src/VerificationOptimization/calcVerificationOptimizationObjective.m b/src/VerificationOptimization/calcVerificationOptimizationObjective.m deleted file mode 100644 index f12b2aa69..000000000 --- a/src/VerificationOptimization/calcVerificationOptimizationObjective.m +++ /dev/null @@ -1,34 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function calculates the cost function objective for verification -% optimization. -% -% (Number, Array of number, Number, struct) -> (Number) -% Returns objective - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function objective = calcVerificationOptimizationObjective(integral) -continuousObjective = sum(integral) / length(integral); -objective = continuousObjective; -end \ No newline at end of file diff --git a/src/VerificationOptimization/calcVerificationOptimizationPathConstraint.m b/src/VerificationOptimization/calcVerificationOptimizationPathConstraint.m deleted file mode 100644 index fe37276f0..000000000 --- a/src/VerificationOptimization/calcVerificationOptimizationPathConstraint.m +++ /dev/null @@ -1,59 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function calculates the path constraint for verification -% optimization. -% -% (struct, struct, struct) -> (2D matrix) -% Returns path constraint - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function path = calcVerificationOptimizationPathConstraint(values, modeledValues, ... - params) -path = []; -for i = 1:length(params.path) - constraintTerm = params.path{i}; - if constraintTerm.isEnabled - switch constraintTerm.type - case "root_segment_residual_load" - path = cat(2, path, ... - calcRootSegmentResidualsPathConstraints(... - constraintTerm.load, ... - params.inverseDynamicMomentLabels, ... - modeledValues.inverseDynamicMoments)); - case "muscle_model_moment_consistency" - path = cat(2, path, ... - calcMuscleActuatedMomentsPathConstraints(params, ... - modeledValues, constraintTerm.load)); - case "torque_model_moment_consistency" - path = cat(2, path, ... - calcTorqueActuatedMomentsPathConstraints(params, ... - modeledValues, values.controlTorques, constraintTerm.load)); - otherwise - throw(MException('', ['Constraint term type ' ... - constraintTerm.type ' does not exist for this tool.'])) - end - end -end -path = scaleToBounds(path, params.maxPath, params.minPath); -end \ No newline at end of file diff --git a/src/VerificationOptimization/calcVerificationOptimizationTerminalConstraint.m b/src/VerificationOptimization/calcVerificationOptimizationTerminalConstraint.m deleted file mode 100644 index bc82b7b81..000000000 --- a/src/VerificationOptimization/calcVerificationOptimizationTerminalConstraint.m +++ /dev/null @@ -1,78 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function calculates the terminal constraint for verification -% optimization -% -% (struct, struct, struct) -> (Array of number) -% Returns terminal constraint - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function event = calcVerificationOptimizationTerminalConstraint(inputs, params) - -inputs.phase.state = [inputs.phase.initialstate; inputs.phase.finalstate]; -inputs.phase.time = [inputs.phase.initialtime; inputs.phase.finaltime]; -inputs.phase.control = ones(size(inputs.phase.time,1),length(params.minControl)); -values = getVerificationOptimizationValueStruct(inputs.phase, params); -modeledValues = calcTorqueBasedModeledValues(values, params); - -event = []; -for i = 1:length(params.terminal) - constraintTerm = params.terminal{i}; - if constraintTerm.isEnabled - switch constraintTerm.type - case "state_position_periodicity" - event = cat(2, event, ... - calcStatePositionPeriodicity(values.statePositions, ... - params.coordinateNames, ... - constraintTerm.coordinate)); - case "state_velocity_periodicity" - event = cat(2, event, ... - calcStateVelocityPeriodicity(values.stateVelocities, ... - params.coordinateNames, ... - constraintTerm.coordinate)); - case "root_segment_residual_load_periodicity" - event = cat(2, event, ... - calcRootSegmentResidualsPeriodicity(... - modeledValues.inverseDynamicMoments, ... - params.inverseDynamicMomentLabels, ... - constraintTerm.load)); - case "external_force_periodicity" - event = cat(2, event, ... - calcExternalForcesPeriodicity(... - modeledValues.groundReactionsLab.forces, ... - params.contactSurfaces, ... - constraintTerm.force)); - case "external_moment_periodicity" - event = cat(2, event, ... - calcExternalMomentsPeriodicity(... - modeledValues.groundReactionsLab.moments, ... - params.contactSurfaces, ... - constraintTerm.moment)); - otherwise - throw(MException('', ['Constraint term type ' ... - constraintTerm.type ' does not exist for this tool.'])) - end - end -end -end \ No newline at end of file diff --git a/src/VerificationOptimization/computeVerificationOptimizationContinuousFunction.m b/src/VerificationOptimization/computeVerificationOptimizationContinuousFunction.m deleted file mode 100644 index 773f5e614..000000000 --- a/src/VerificationOptimization/computeVerificationOptimizationContinuousFunction.m +++ /dev/null @@ -1,43 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function computes the dynamic constraints, path constraints (if any) -% and cost function terms (if any) for verification optimization. -% -% (struct) -> (struct) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function modeledValues = computeVerificationOptimizationContinuousFunction(inputs) - -values = getVerificationOptimizationValueStruct(inputs.phase, inputs.auxdata); -modeledValues = calcTorqueBasedModeledValues(values, inputs.auxdata); -modeledValues = calcSynergyBasedModeledValues(values, inputs.auxdata, modeledValues); -modeledValues.dynamics = calcDynamicConstraint(values, inputs.auxdata); -path = calcVerificationOptimizationPathConstraint(values, modeledValues, inputs.auxdata); -if ~isempty(path) - modeledValues.path = path; -end -modeledValues.integrand = calcVerificationOptimizationIntegrand(values, ... - modeledValues, inputs.auxdata); -end diff --git a/src/VerificationOptimization/computeVerificationOptimizationEndpointFunction.m b/src/VerificationOptimization/computeVerificationOptimizationEndpointFunction.m deleted file mode 100644 index 7820b6e96..000000000 --- a/src/VerificationOptimization/computeVerificationOptimizationEndpointFunction.m +++ /dev/null @@ -1,39 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function computes the terminal constraint (if any), and total cost -% function objective for verification optimization. -% -% (struct) -> (struct) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function output = computeVerificationOptimizationEndpointFunction(inputs) -if ~isempty(inputs.auxdata.terminal) - output.eventgroup.event = ... - calcVerificationOptimizationTerminalConstraint(inputs, ... - inputs.auxdata); -end -output.objective = ... - calcVerificationOptimizationObjective(inputs.phase.integral); -end \ No newline at end of file diff --git a/src/VerificationOptimization/computeVerificationOptimizationMainFunction.m b/src/VerificationOptimization/computeVerificationOptimizationMainFunction.m deleted file mode 100644 index ec4b76e32..000000000 --- a/src/VerificationOptimization/computeVerificationOptimizationMainFunction.m +++ /dev/null @@ -1,48 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function sets up GPOPS-II to run Verification Optimization. -% -% (struct) -> (struct, struct) -% Assigns optimal control settings and runs Verification Optimization - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function output = computeVerificationOptimizationMainFunction(inputs, params) -bounds = setupProblemBounds(inputs, params); -guess = setupCommonOptimalControlInitialGuess(inputs); -setup = setupCommonOptimalControlSolverSettings(inputs, ... - bounds, guess, params, ... - @computeVerificationOptimizationContinuousFunction, ... - @computeVerificationOptimizationEndpointFunction); -checkInitialGuess(guess, inputs, ... - @computeVerificationOptimizationContinuousFunction); -solution = gpops2(setup); -solution = solution.result.solution; -solution.auxdata = inputs; -output = computeVerificationOptimizationContinuousFunction(solution); -output.solution = solution; -end - -function bounds = setupProblemBounds(inputs, params) -bounds = setupCommonOptimalControlBounds(inputs, params); -end diff --git a/src/core/TreatmentOptimization/updateMuscleModelProperties.m b/src/core/TreatmentOptimization/updateMuscleModelProperties.m deleted file mode 100644 index 4422f2ad2..000000000 --- a/src/core/TreatmentOptimization/updateMuscleModelProperties.m +++ /dev/null @@ -1,46 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% If muscles are present, this model updates the optimal fiber length, max -% isometric force, and tendon slack length values based on changes -% resulting from design optimization or from updated values from the -% osimx file. -% -% (struct) -> (struct) -% Updated muscle model properties in the osim model - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function inputs = updateMuscleModelProperties(inputs) -if ~isa(inputs.model, 'org.opensim.modeling.Model') - inputs.model = Model(inputs.model); -end -if strcmp(inputs.controllerType, 'synergy') -for i = 1 : inputs.numMuscles - inputs.model.getForceSet().getMuscles().get(inputs.muscleNames(i)). ... - setOptimalFiberLength(inputs.optimalFiberLength(i)); - inputs.model.getForceSet().getMuscles().get(inputs.muscleNames(i)). ... - setTendonSlackLength(inputs.tendonSlackLength(i)); - inputs.model.getForceSet().getMuscles().get(inputs.muscleNames(i)). ... - setMaxIsometricForce(inputs.maxIsometricForce(i)); -end -end \ No newline at end of file diff --git a/src/core/TreatmentOptimization/OpenSimMex/calcBodyLocation.m b/src/core/mex/calcBodyLocation.m similarity index 100% rename from src/core/TreatmentOptimization/OpenSimMex/calcBodyLocation.m rename to src/core/mex/calcBodyLocation.m diff --git a/src/core/TreatmentOptimization/OpenSimMex/calcBodyVelocity.m b/src/core/mex/calcBodyVelocity.m similarity index 100% rename from src/core/TreatmentOptimization/OpenSimMex/calcBodyVelocity.m rename to src/core/mex/calcBodyVelocity.m diff --git a/src/core/TreatmentOptimization/OpenSimMex/initializeMexOrMatlabParallelFunctions.m b/src/core/mex/initializeMexOrMatlabParallelFunctions.m similarity index 100% rename from src/core/TreatmentOptimization/OpenSimMex/initializeMexOrMatlabParallelFunctions.m rename to src/core/mex/initializeMexOrMatlabParallelFunctions.m diff --git a/src/core/TreatmentOptimization/OpenSimMex/inverseDynamics.m b/src/core/mex/inverseDynamics.m similarity index 100% rename from src/core/TreatmentOptimization/OpenSimMex/inverseDynamics.m rename to src/core/mex/inverseDynamics.m diff --git a/src/core/TreatmentOptimization/OpenSimMex/inverseDynamicsMatlab.m b/src/core/mex/inverseDynamicsMatlab.m similarity index 100% rename from src/core/TreatmentOptimization/OpenSimMex/inverseDynamicsMatlab.m rename to src/core/mex/inverseDynamicsMatlab.m diff --git a/src/core/TreatmentOptimization/OpenSimMex/inverseDynamicsMatlabParallel.m b/src/core/mex/inverseDynamicsMatlabParallel.m similarity index 100% rename from src/core/TreatmentOptimization/OpenSimMex/inverseDynamicsMatlabParallel.m rename to src/core/mex/inverseDynamicsMatlabParallel.m diff --git a/src/core/TreatmentOptimization/OpenSimMex/inverseDynamicsMexWindows.cpp b/src/core/mex/inverseDynamicsMexWindows.cpp similarity index 100% rename from src/core/TreatmentOptimization/OpenSimMex/inverseDynamicsMexWindows.cpp rename to src/core/mex/inverseDynamicsMexWindows.cpp diff --git a/src/core/TreatmentOptimization/OpenSimMex/inverseDynamicsMexWindows.mexw64 b/src/core/mex/inverseDynamicsMexWindows.mexw64 similarity index 100% rename from src/core/TreatmentOptimization/OpenSimMex/inverseDynamicsMexWindows.mexw64 rename to src/core/mex/inverseDynamicsMexWindows.mexw64 diff --git a/src/core/TreatmentOptimization/OpenSimMex/pointKinematics.m b/src/core/mex/pointKinematics.m similarity index 100% rename from src/core/TreatmentOptimization/OpenSimMex/pointKinematics.m rename to src/core/mex/pointKinematics.m diff --git a/src/core/TreatmentOptimization/OpenSimMex/pointKinematicsMatlabParallel.m b/src/core/mex/pointKinematicsMatlabParallel.m similarity index 100% rename from src/core/TreatmentOptimization/OpenSimMex/pointKinematicsMatlabParallel.m rename to src/core/mex/pointKinematicsMatlabParallel.m diff --git a/src/core/TreatmentOptimization/OpenSimMex/pointKinematicsMexWindows.mexw64 b/src/core/mex/pointKinematicsMexWindows.mexw64 similarity index 100% rename from src/core/TreatmentOptimization/OpenSimMex/pointKinematicsMexWindows.mexw64 rename to src/core/mex/pointKinematicsMexWindows.mexw64 diff --git a/src/core/TreatmentOptimization/OpenSimMex/setOsimStateBySingleFrame.m b/src/core/mex/setOsimStateBySingleFrame.m similarity index 100% rename from src/core/TreatmentOptimization/OpenSimMex/setOsimStateBySingleFrame.m rename to src/core/mex/setOsimStateBySingleFrame.m From 57cfde7525232e992d563223f7811b47cb37df0f Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Tue, 19 Sep 2023 21:01:26 -0500 Subject: [PATCH 112/365] update project --- .../3OaAbed4F4CnFYNp5AkGzyUZHUsd.xml} | 0 .../3OaAbed4F4CnFYNp5AkGzyUZHUsp.xml} | 0 .../KjY44G7WJ_jinQPfkCzkmqHUBu8d.xml} | 0 .../KjY44G7WJ_jinQPfkCzkmqHUBu8p.xml} | 0 .../NFORnXmhiVQSgHA5Autj49jPftYd.xml} | 0 .../NFORnXmhiVQSgHA5Autj49jPftYp.xml} | 0 .../oHpXnrNixlZjK9qYnwuV6eX9gfcd.xml} | 0 .../oHpXnrNixlZjK9qYnwuV6eX9gfcp.xml} | 0 .../sgdsvhsCbARvZJ55oWpjfMFPi3Qd.xml} | 0 .../sgdsvhsCbARvZJ55oWpjfMFPi3Qp.xml} | 0 .../tkgt0Y0wPwlAY1DVmERxblQgvVod.xml} | 0 .../tkgt0Y0wPwlAY1DVmERxblQgvVop.xml} | 0 .../zf1841WvvYarScoTMS3MPj4JW9cd.xml} | 0 .../zf1841WvvYarScoTMS3MPj4JW9cp.xml} | 0 .../3oidn4LRL5GwwDzzeMZiSp7A2O0p.xml | 2 -- .../LzzgRGTWkydILioLs0JPaa-Nu40p.xml | 2 -- .../gDdlibUiOY4zsCI36J4PKMPypIIp.xml | 2 -- .../tfTJ4kPklZp2baElxH8WVDN7PYkp.xml | 2 -- .../2EnTnZEx-4v1jC3AJZADMDpEO1gp.xml | 2 -- .../5LEawZsxA1aBTeqG0NlpCtR6q6kp.xml | 2 -- .../8uH7KtfLIR_hLVyUlkUrouETOeop.xml | 2 -- .../JlNlt7b52D5lndUS8QIFBXuyn8sp.xml | 2 -- .../V7dcj86ZsVvV3wlDWv_EePrkW7wp.xml | 2 -- .../XDDpmW-ahmfRGLbHVnihtWagQDEp.xml | 2 -- .../rzCzLkJQsijCRt28FvlpiZrjfZ4p.xml | 2 -- .../vyBBHUVgKzM20Od7DNtH6-JNEiAp.xml | 2 -- .../z8PCXMWInzrlFrKj4KQg_4fVTZYp.xml | 2 -- .../0amoX63LRAKETLDqV7_F13CuX74p.xml | 2 -- .../BEHll27af-LsZgmvfXGuLyYJyU0p.xml | 2 -- .../CgrwWh_FTjN531XPRoLVTVqLbZEp.xml | 2 -- .../GSxmGWKoE8yVg8Dt0_WIVqZwfq8p.xml | 2 -- .../GmURLyn29gRlewEo8pphTKMkvfcp.xml | 2 -- .../K7gRh-Ets4DP9XdM-8WQS6MYiTAp.xml | 2 -- .../QQxpEF0mWnNdoQybWJnETf5i2zgp.xml | 2 -- .../UlAa7CZxUr955fLFiY8150xe-0Yp.xml | 2 -- .../kyvnzrdUBXgtJ7QWloYIMdgg3f0p.xml | 2 -- .../mIYcND4GVDO0WgnXvKhJVZw4Qdsp.xml | 2 -- .../wOcCxJrf1WxhN3mYz0ASwydFXsMp.xml | 2 -- .../3SN3sghzDXjWjn7o9l7cAa0O5KAd.xml | 2 -- .../3SN3sghzDXjWjn7o9l7cAa0O5KAp.xml | 2 -- .../E21V15DpmDB-ZQFfMLZLCGa_5lId.xml | 2 -- .../E21V15DpmDB-ZQFfMLZLCGa_5lIp.xml | 2 -- .../IjYbiW-Ya6w-T2Z7_m6dd_k13wwd.xml | 2 -- .../IjYbiW-Ya6w-T2Z7_m6dd_k13wwp.xml | 2 -- .../MR_L_h1ZG2GwTSuc8zZrWnvy1Jkd.xml | 2 -- .../MR_L_h1ZG2GwTSuc8zZrWnvy1Jkp.xml | 2 -- .../NVX46-wyblASglEOArlOVFEQTfkd.xml | 2 ++ .../NVX46-wyblASglEOArlOVFEQTfkp.xml | 2 ++ .../NsgPl7VSSufo3svKEWfckxlnRO0d.xml | 2 ++ .../NsgPl7VSSufo3svKEWfckxlnRO0p.xml | 2 ++ .../PGaTJl94-dUtK-5YVG-7nT6UaKQd.xml | 2 -- .../PGaTJl94-dUtK-5YVG-7nT6UaKQp.xml | 2 -- .../QWMJ0IlUTrzTjwye6g1ffgLjfGYd.xml | 2 -- .../QWMJ0IlUTrzTjwye6g1ffgLjfGYp.xml | 2 -- .../Uo9aVDJOhqlyX0wNM67-Vr7U4PYd.xml | 2 -- .../Uo9aVDJOhqlyX0wNM67-Vr7U4PYp.xml | 2 -- .../VbvFCIQVsoRbd9GRTBzN_omsupMd.xml | 2 -- .../VbvFCIQVsoRbd9GRTBzN_omsupMp.xml | 2 -- .../ZTjokurFNXtQ1ojGBXgvq_kwksId.xml | 2 ++ .../ZTjokurFNXtQ1ojGBXgvq_kwksIp.xml | 2 ++ .../ZYZcp25adoHaOiOfAGtsGRGGUlod.xml | 2 ++ .../ZYZcp25adoHaOiOfAGtsGRGGUlop.xml | 2 ++ .../Zu3w1_x1xrK1iAVqy0N_K78YUSQd.xml | 2 -- .../Zu3w1_x1xrK1iAVqy0N_K78YUSQp.xml | 2 -- .../fDXmO3TGZWioHy3oJ1Pc44eEwx4d.xml | 2 ++ .../fDXmO3TGZWioHy3oJ1Pc44eEwx4p.xml | 2 ++ .../fJawufIe31Xaj5x2aG6mddyYxqgd.xml | 2 ++ .../fJawufIe31Xaj5x2aG6mddyYxqgp.xml | 2 ++ .../i5tvtXjf-liajJnRRTHy0WQWyBQd.xml | 2 -- .../i5tvtXjf-liajJnRRTHy0WQWyBQp.xml | 2 -- .../jTTvt3Bp2bTBwpSRN3WIq48HTY8d.xml | 2 ++ .../jTTvt3Bp2bTBwpSRN3WIq48HTY8p.xml | 2 ++ .../kLundQjBc0lkyBnd7dU88QI9eCYd.xml | 2 ++ .../kLundQjBc0lkyBnd7dU88QI9eCYp.xml | 2 ++ .../pdndsCkuDwLksOKofxkONBmGSu0d.xml | 2 ++ .../pdndsCkuDwLksOKofxkONBmGSu0p.xml | 2 ++ .../qomN9EMLc7gSakLKy3wL9wrIpacd.xml | 2 -- .../qomN9EMLc7gSakLKy3wL9wrIpacp.xml | 2 -- .../r49BYN-QImuasJYtp4M0VMhEspQd.xml | 2 -- .../r49BYN-QImuasJYtp4M0VMhEspQp.xml | 2 -- .../tzNeWTtKSZS7T3vujfMV7O83bhgd.xml | 2 ++ .../tzNeWTtKSZS7T3vujfMV7O83bhgp.xml | 2 ++ .../v0w6uQCEov7noSqWPgoe4xwGBF0d.xml | 2 ++ .../v0w6uQCEov7noSqWPgoe4xwGBF0p.xml | 2 ++ .../vidY_3j1oRGddcnaqqAhcbXSRdEd.xml | 2 ++ .../vidY_3j1oRGddcnaqqAhcbXSRdEp.xml | 2 ++ .../xc55aMCO0KDaUwfDNG4gUONyFQMd.xml | 2 -- .../xc55aMCO0KDaUwfDNG4gUONyFQMp.xml | 2 -- .../7SrjXNQDv9BGhC0GEOKQspr9Ocgd.xml} | 0 .../7SrjXNQDv9BGhC0GEOKQspr9Ocgp.xml} | 0 .../82flsbuQG_B85H3tl7ti_QR4mXId.xml} | 0 .../82flsbuQG_B85H3tl7ti_QR4mXIp.xml} | 0 .../a0lBcQv35pXv1BECWbcE4pI6X88d.xml} | 0 .../a0lBcQv35pXv1BECWbcE4pI6X88p.xml} | 0 .../bV1SapNLHXOJuqxs4eM5UgXt1DMd.xml} | 0 .../bV1SapNLHXOJuqxs4eM5UgXt1DMp.xml} | 0 .../wfGjJ216NLwm4I2ndZI3_86uhgQd.xml} | 0 .../wfGjJ216NLwm4I2ndZI3_86uhgQp.xml} | 0 .../Eb3URaLnOadzaECH9JhrMNGK0cYd.xml} | 0 .../Eb3URaLnOadzaECH9JhrMNGK0cYp.xml} | 0 .../GUD97Kgby-Nbe7a4937yGMYWqoQd.xml} | 0 .../GUD97Kgby-Nbe7a4937yGMYWqoQp.xml | 2 ++ .../IXrhsh0KA0Hd3dEHjxbDlSwV_9wd.xml} | 0 .../IXrhsh0KA0Hd3dEHjxbDlSwV_9wp.xml} | 0 .../_cZck6op8VccwtbtBtqU08jEoNgd.xml} | 0 .../_cZck6op8VccwtbtBtqU08jEoNgp.xml} | 0 .../poWgzYIOpdVLERL5CAxxonZKagod.xml} | 0 .../poWgzYIOpdVLERL5CAxxonZKagop.xml} | 0 .../veTEyGEjchushQj-PUakRWJ1Rfwd.xml} | 0 .../veTEyGEjchushQj-PUakRWJ1Rfwp.xml | 2 ++ .../wXFztAEfZBGlWkniv1yDOZUPMLkd.xml} | 0 .../wXFztAEfZBGlWkniv1yDOZUPMLkp.xml} | 0 .../yG6FN90UXfIzKIRJPeCgEK9wsiAd.xml} | 0 .../yG6FN90UXfIzKIRJPeCgEK9wsiAp.xml} | 0 .../G2x_hSFRzJNj2RwsZ4N6TWYsHmYp.xml | 2 -- .../rx1MnRhyaD-dcP1I9WcseP2PGiwp.xml | 2 -- .../wSDXMLBIRWsCGVgqLP9ER---lO4p.xml | 2 -- .../yrvDAxHTmqAbl2v6-rWp7tgRl4Ep.xml | 2 -- .../-Lw-S9LvX_FYDRFafe-L_l8cTMsd.xml} | 0 .../-Lw-S9LvX_FYDRFafe-L_l8cTMsp.xml} | 0 .../4JNkAFleLVxY3nQGnz12ugS4ZTEd.xml} | 0 .../4JNkAFleLVxY3nQGnz12ugS4ZTEp.xml} | 0 .../4hmmxZ0XovlFSmc-Rsau_8EpzNkd.xml} | 0 .../4hmmxZ0XovlFSmc-Rsau_8EpzNkp.xml} | 0 .../5lEWuAPjsUhI7_hCP31KFq7iDlUd.xml} | 0 .../5lEWuAPjsUhI7_hCP31KFq7iDlUp.xml} | 0 .../CZ1F4Twl7ZiFGyGb8gS7TX0pkTQd.xml} | 0 .../CZ1F4Twl7ZiFGyGb8gS7TX0pkTQp.xml} | 0 .../DK5W7ZmXIHOHuwnXHtwNGL22p4Md.xml} | 0 .../DK5W7ZmXIHOHuwnXHtwNGL22p4Mp.xml} | 0 .../GPtTjnSsEKTMO1S-sg_FP4dxbbEd.xml} | 0 .../GPtTjnSsEKTMO1S-sg_FP4dxbbEp.xml} | 0 .../J4NSMqZEAmFZH35SWLSatqliZmkd.xml} | 0 .../J4NSMqZEAmFZH35SWLSatqliZmkp.xml} | 0 .../eOoHRDjyUSBj8-WLEX9mxUx6ePUd.xml} | 0 .../eOoHRDjyUSBj8-WLEX9mxUx6ePUp.xml} | 0 .../j0R75YjB0BcweDo5AYcMWiq3P6Md.xml} | 0 .../j0R75YjB0BcweDo5AYcMWiq3P6Mp.xml} | 0 .../mjVTgOO6IWMthsneycvZqWzxD_gd.xml} | 0 .../mjVTgOO6IWMthsneycvZqWzxD_gp.xml} | 0 .../nVExW6oxyLwVGXPnnA3XaUC50Jcd.xml} | 0 .../nVExW6oxyLwVGXPnnA3XaUC50Jcp.xml} | 0 .../6tuQeWy9960NGsG8vq2g6XEbfs8d.xml} | 0 .../6tuQeWy9960NGsG8vq2g6XEbfs8p.xml | 2 ++ .../A8ncUuxWwwZj8vBXXThduDRmo-Id.xml} | 0 .../A8ncUuxWwwZj8vBXXThduDRmo-Ip.xml | 2 ++ .../J95nrDPGFnMMRe-j9IrlrD3MJIwd.xml} | 0 .../J95nrDPGFnMMRe-j9IrlrD3MJIwp.xml | 2 ++ .../cmZk236s6Qn7CSYmgTSLERo6-Fod.xml} | 0 .../cmZk236s6Qn7CSYmgTSLERo6-Fop.xml} | 0 .../gdgc-JZm4OTNPgAUaiKWok7sS8Qd.xml} | 0 .../gdgc-JZm4OTNPgAUaiKWok7sS8Qp.xml | 2 ++ .../-K_tTgFkd3KuOQq4OW0mOmsQS44d.xml} | 0 .../-K_tTgFkd3KuOQq4OW0mOmsQS44p.xml} | 0 .../-ZOcmw4c1B4bINk-xUWH7ahRKJod.xml} | 0 .../-ZOcmw4c1B4bINk-xUWH7ahRKJop.xml} | 0 .../00BmM5mTpuxcj5K8jG604UO7-Ewd.xml} | 0 .../00BmM5mTpuxcj5K8jG604UO7-Ewp.xml} | 0 .../0TH8tA8-wfGMXu-ho-n1gCC4wJsd.xml} | 0 .../0TH8tA8-wfGMXu-ho-n1gCC4wJsp.xml} | 0 .../2lZWGayY8sTSf6C_9MNt1ciblccd.xml} | 0 .../2lZWGayY8sTSf6C_9MNt1ciblccp.xml} | 0 .../4NaiUnhsCfsLNNAYb_2Nre3n59Ud.xml} | 0 .../4NaiUnhsCfsLNNAYb_2Nre3n59Up.xml} | 0 .../4edCiafrq1BIvyyh61g6Z-ot9R4d.xml} | 0 .../4edCiafrq1BIvyyh61g6Z-ot9R4p.xml} | 0 .../4x4IMS_bCxBIpOSTkeIwMAKuTawd.xml} | 0 .../4x4IMS_bCxBIpOSTkeIwMAKuTawp.xml} | 0 .../5G1NQ5WxNVqz_u56bBC5V8s_cnMd.xml} | 0 .../5G1NQ5WxNVqz_u56bBC5V8s_cnMp.xml} | 0 .../5OtYPmi9zdVJFCfszj3XmwBOicQd.xml} | 0 .../5OtYPmi9zdVJFCfszj3XmwBOicQp.xml} | 0 .../AiCl4Nv-4TcAuyccq2bcsaO4hGId.xml} | 0 .../AiCl4Nv-4TcAuyccq2bcsaO4hGIp.xml} | 0 .../BzVxW2mFaL8w6fW-2i_Ks4AtAywd.xml} | 0 .../BzVxW2mFaL8w6fW-2i_Ks4AtAywp.xml} | 0 .../CwijU_1EfQOTRpwgKbRX29DCRqod.xml} | 0 .../CwijU_1EfQOTRpwgKbRX29DCRqop.xml} | 0 .../D6bNMzhGpxvqGj045koQvCPRgSgd.xml} | 0 .../D6bNMzhGpxvqGj045koQvCPRgSgp.xml} | 0 .../FhhflOTx83dyAZA7C5LZFylsG3Qd.xml} | 0 .../FhhflOTx83dyAZA7C5LZFylsG3Qp.xml} | 0 .../G8qY3zLR3J2KmeRH1uVSXh4Feewd.xml} | 0 .../G8qY3zLR3J2KmeRH1uVSXh4Feewp.xml} | 0 .../L2wh56GmocdhTwvqoEVyLSPjn8Id.xml} | 0 .../L2wh56GmocdhTwvqoEVyLSPjn8Ip.xml} | 0 .../NgzKbmuSLrYttzedhNh5jGcAk2od.xml} | 0 .../NgzKbmuSLrYttzedhNh5jGcAk2op.xml} | 0 .../NrbvTPDfIXgykDJkfPdIZyDvtVgd.xml} | 0 .../NrbvTPDfIXgykDJkfPdIZyDvtVgp.xml} | 0 .../P0FXd6AwKsPzBiYwIZLsEUD5VrAd.xml} | 0 .../P0FXd6AwKsPzBiYwIZLsEUD5VrAp.xml} | 0 .../TAq2STpvx67m5-UfPyxz4oWjUz8d.xml} | 0 .../TAq2STpvx67m5-UfPyxz4oWjUz8p.xml} | 0 .../Tl-XR5iwVw4PrAcBNW_SjCclo-kd.xml} | 0 .../Tl-XR5iwVw4PrAcBNW_SjCclo-kp.xml} | 0 .../V_NWUez2zLHkRy0FgbfpoMa561Ed.xml} | 0 .../V_NWUez2zLHkRy0FgbfpoMa561Ep.xml} | 0 .../X1p2mbSsnW7dQJV2bKNOREnOyY4d.xml} | 0 .../X1p2mbSsnW7dQJV2bKNOREnOyY4p.xml} | 0 .../YkEXBoi2NKObNUB8qZYLCaOkxj4d.xml} | 0 .../YkEXBoi2NKObNUB8qZYLCaOkxj4p.xml} | 0 .../aukBLYh0oaCR-vLwva9Y2OCkQzUd.xml} | 0 .../aukBLYh0oaCR-vLwva9Y2OCkQzUp.xml} | 0 .../b14wfAQdUmLf7zWEQ4kujKfjTQYd.xml} | 0 .../b14wfAQdUmLf7zWEQ4kujKfjTQYp.xml} | 0 .../dPt59MZaEXeu_JPlWh7RDKtusxUd.xml} | 0 .../dPt59MZaEXeu_JPlWh7RDKtusxUp.xml} | 0 .../eCuRoIFEDG25vAewgv-nf3GqNI0d.xml} | 0 .../eCuRoIFEDG25vAewgv-nf3GqNI0p.xml} | 0 .../iYmVif7LRRnGYE-Zeun_xHMvrVUd.xml} | 0 .../iYmVif7LRRnGYE-Zeun_xHMvrVUp.xml} | 0 .../iqU8I5FyZGvsOpUbA1lqu65aWYsd.xml} | 0 .../iqU8I5FyZGvsOpUbA1lqu65aWYsp.xml} | 0 .../j6pwo65gaiPoea-wYOGH4DewgEcd.xml} | 0 .../j6pwo65gaiPoea-wYOGH4DewgEcp.xml} | 0 .../lsw_ecgYyohO6CkqAqCk2bJqNuMd.xml} | 0 .../lsw_ecgYyohO6CkqAqCk2bJqNuMp.xml} | 0 .../ny2aRTbOyjYb8JDFd4dItADtN4Id.xml} | 0 .../ny2aRTbOyjYb8JDFd4dItADtN4Ip.xml} | 0 .../oLERgIPUw52TmzFNtvUaq1v3Hykd.xml} | 0 .../oLERgIPUw52TmzFNtvUaq1v3Hykp.xml} | 0 .../pegGFSZoGR96d4YxbbK-T9-uicsd.xml} | 0 .../pegGFSZoGR96d4YxbbK-T9-uicsp.xml} | 0 .../qMOfCxA6oAYXN36hOE_emzY7-K4d.xml} | 0 .../qMOfCxA6oAYXN36hOE_emzY7-K4p.xml} | 0 .../sgs-V8op5u7eBxQMg_dpYIMx_r0d.xml} | 0 .../sgs-V8op5u7eBxQMg_dpYIMx_r0p.xml} | 0 .../wlzHFGaUXvZky7Iz4g5QWWPjTZod.xml} | 0 .../wlzHFGaUXvZky7Iz4g5QWWPjTZop.xml} | 0 .../xjfA1VBG4pGbE5V9ER3DoEdSXfcd.xml} | 0 .../xjfA1VBG4pGbE5V9ER3DoEdSXfcp.xml} | 0 .../zE7jRze1WGVc9GxZNzvBJKkpyN0d.xml} | 0 .../zE7jRze1WGVc9GxZNzvBJKkpyN0p.xml} | 0 .../3HnVzJ8uifpbOyg21-U4RCJN4GQd.xml} | 0 .../3HnVzJ8uifpbOyg21-U4RCJN4GQp.xml} | 0 .../7zdx9TfzV1uKVmlJRWzGdJyAcSYd.xml} | 0 .../7zdx9TfzV1uKVmlJRWzGdJyAcSYp.xml} | 0 .../DAP2fOCUoOsvQKZryp7QTWtvdegd.xml} | 0 .../DAP2fOCUoOsvQKZryp7QTWtvdegp.xml} | 0 .../ccZjrHRR1lFL21N1ZWdseSM37NQd.xml} | 0 .../ccZjrHRR1lFL21N1ZWdseSM37NQp.xml} | 0 .../omg-Qmyr2Hpg5ztIiWxX0q9_f_sd.xml} | 0 .../omg-Qmyr2Hpg5ztIiWxX0q9_f_sp.xml} | 0 .../rNLU-8Md7l99hBksJZlE3as2Y98d.xml} | 0 .../rNLU-8Md7l99hBksJZlE3as2Y98p.xml} | 0 .../8FB9zoKe0-Lw7d_EeJv8WYCX-50d.xml} | 0 .../8FB9zoKe0-Lw7d_EeJv8WYCX-50p.xml} | 0 .../90YW2GOol29Apu9oHzv3q7eI8ZMd.xml} | 0 .../90YW2GOol29Apu9oHzv3q7eI8ZMp.xml} | 0 .../F1UMXHKWKyyeAiNCKU0ON-Vy2ugd.xml} | 0 .../F1UMXHKWKyyeAiNCKU0ON-Vy2ugp.xml} | 0 .../SkcnLeu8zuRRgbMYy4nnaoFftigd.xml} | 0 .../SkcnLeu8zuRRgbMYy4nnaoFftigp.xml} | 0 .../X3U50T46GsNoWWkV12vFPTU5IkAd.xml} | 0 .../X3U50T46GsNoWWkV12vFPTU5IkAp.xml} | 0 .../_p4C4Xaqbc2CySSpIr_XPlz39_8d.xml} | 0 .../_p4C4Xaqbc2CySSpIr_XPlz39_8p.xml} | 0 .../bApZCS4bpdgHa0xKhfxkSKy3kIcd.xml} | 0 .../bApZCS4bpdgHa0xKhfxkSKy3kIcp.xml} | 0 .../dC8vrHmGxRVrn1Vw7pYBaCb25tUd.xml} | 0 .../dC8vrHmGxRVrn1Vw7pYBaCb25tUp.xml} | 0 .../slGRI8GHw3J7mTWp953EhtGCuB8d.xml} | 0 .../slGRI8GHw3J7mTWp953EhtGCuB8p.xml} | 0 .../uIWV6l4leGqpetubaJSYVYzic4Ad.xml} | 0 .../uIWV6l4leGqpetubaJSYVYzic4Ap.xml} | 0 .../wbJvniNn9pHSMYYk4-SDjMEBinAd.xml} | 0 .../wbJvniNn9pHSMYYk4-SDjMEBinAp.xml} | 0 .../4lF9gcxRF1S305BdGqOMAHZcYgop.xml | 2 -- .../BXX9SqJTGGGO_CDj2AJCCAtV84kp.xml | 2 -- .../BkckfovEtaH9G6qBgDPgKm8zKxgp.xml | 2 -- .../EIE_vdm0VClM0NZZV517-DBaczEp.xml | 2 -- .../IWnqwSbZEiavwf7_s2jeWFKRUyMp.xml | 2 -- .../WeWWFR_CW_fJH3v0Spn0rCmP6ccp.xml | 2 -- .../Z_VaFjtshHcOoAprv8kGzuWPan8p.xml | 2 -- .../lKVscSUfLpUJ7DMGKxZpl2cj8Cwp.xml | 2 -- .../sbvxc69GTsaxWR2ghAIgyxsRoK0p.xml | 2 -- .../u9fylSJ1j_4ckhUg5rn809_XS8cd.xml | 6 ------ .../znnoG0GBXl0gWrZCcSzmlPlsYO4d.xml | 6 ------ .../znnoG0GBXl0gWrZCcSzmlPlsYO4p.xml | 2 -- .../BlsnL99CKaby8ck6n_pFKfx8TOEd.xml} | 0 .../BlsnL99CKaby8ck6n_pFKfx8TOEp.xml} | 0 .../F3Ymj-J16s_9PdjyJ8NmuydN3yId.xml} | 0 .../F3Ymj-J16s_9PdjyJ8NmuydN3yIp.xml} | 0 .../GcqEp1PedF_qduXiylZH_t2MP1Qd.xml} | 0 .../GcqEp1PedF_qduXiylZH_t2MP1Qp.xml} | 0 .../NiQW_7jWHSEL9pyWwCHpUX03D48d.xml} | 0 .../NiQW_7jWHSEL9pyWwCHpUX03D48p.xml} | 0 .../c9tjuQNdCrSrZPk-bi5TqtaIYCkd.xml} | 0 .../c9tjuQNdCrSrZPk-bi5TqtaIYCkp.xml} | 0 .../iCK3aCczRLHuNzxcr2qrQVK9PZ8d.xml} | 0 .../iCK3aCczRLHuNzxcr2qrQVK9PZ8p.xml} | 0 .../rt7-0zPCi_bp1caPhGK26hxMFlgd.xml} | 0 .../rt7-0zPCi_bp1caPhGK26hxMFlgp.xml} | 0 .../ur9Ezr2ljVCKuJkzloOS3DSlTPQd.xml} | 0 .../ur9Ezr2ljVCKuJkzloOS3DSlTPQp.xml} | 0 .../1U6jA8VW8gquO5tfdlwgKz-0Kjsd.xml} | 0 .../1U6jA8VW8gquO5tfdlwgKz-0Kjsp.xml} | 0 .../4tnQjmzGbGyuuipQczDui-LkM4Yd.xml} | 0 .../4tnQjmzGbGyuuipQczDui-LkM4Yp.xml} | 0 .../GuR32HWNIsfL9QJxccN3h7ueHWUd.xml} | 0 .../GuR32HWNIsfL9QJxccN3h7ueHWUp.xml} | 0 .../N0H1Yk2c0hrEVlplassPmyyQIkgd.xml} | 0 .../N0H1Yk2c0hrEVlplassPmyyQIkgp.xml} | 0 .../ZhQd5_-iD72EYvyetRmslkcILwgd.xml} | 0 .../ZhQd5_-iD72EYvyetRmslkcILwgp.xml} | 0 .../_DTS8noz4eF6eofLBJF_kJI36Rsd.xml} | 0 .../_DTS8noz4eF6eofLBJF_kJI36Rsp.xml} | 0 .../ctkpfB9aQsWsqTtrG0AiSWOSsMgd.xml} | 0 .../ctkpfB9aQsWsqTtrG0AiSWOSsMgp.xml} | 0 .../jaWSShTlT3hy9m_6EXad-HfUEqEd.xml} | 0 .../jaWSShTlT3hy9m_6EXad-HfUEqEp.xml} | 0 .../se_u97Hhf2JBUhc4KXrIUB6oWjUd.xml} | 0 .../se_u97Hhf2JBUhc4KXrIUB6oWjUp.xml} | 0 .../vScIuMZR1UoxCROtefKx0SSqHksd.xml} | 0 .../vScIuMZR1UoxCROtefKx0SSqHksp.xml} | 0 .../xBDRN0xkCxYYkWS1cMcY9ajEQ5od.xml} | 0 .../xBDRN0xkCxYYkWS1cMcY9ajEQ5op.xml} | 0 .../7U58VHMhrmTgIuozFzLR1XBJo8Id.xml | 6 ------ .../9ULi2Alw-JmXjkCxz2ppBf5D2nUd.xml | 6 ------ .../9ULi2Alw-JmXjkCxz2ppBf5D2nUp.xml | 2 -- .../9orjn84dnpAKkkuchjb6ZXb4lYod.xml | 6 ------ .../Ckzn7bzQCXjaNEGiflifGGEk0-Ad.xml | 6 ------ .../Ckzn7bzQCXjaNEGiflifGGEk0-Ap.xml | 2 -- .../Fuv4y78db8mLAjl6Fcl6HGZDRo0d.xml | 6 ------ .../Fuv4y78db8mLAjl6Fcl6HGZDRo0p.xml | 2 -- .../Q_eYts4Jx8lwh_76CdRDIxbbdlkd.xml | 6 ------ .../T3r09B-NxK1oaHW6j-rPgd2Qj6Ud.xml | 6 ------ .../T3r09B-NxK1oaHW6j-rPgd2Qj6Up.xml | 2 -- .../WtpDLoJHlDx008KY8cwkBcD2AT8d.xml | 6 ------ .../WtpDLoJHlDx008KY8cwkBcD2AT8p.xml | 2 -- .../XbzuDmDFV-LdTDKCBgtbIoNIeDAd.xml | 6 ------ .../XbzuDmDFV-LdTDKCBgtbIoNIeDAp.xml | 2 -- .../aN6B9nVEXMPwk5nipy2pReqfe9kd.xml | 6 ------ .../aN6B9nVEXMPwk5nipy2pReqfe9kp.xml | 2 -- .../dKRagt73aqMStWRIRrbSi8CTiWEd.xml | 6 ------ .../dKRagt73aqMStWRIRrbSi8CTiWEp.xml | 2 -- .../jjYwKTXjk1ClTK05aLas9VzF5YAd.xml | 6 ------ .../jjYwKTXjk1ClTK05aLas9VzF5YAp.xml | 2 -- .../vok99kMS3Be0cBX4FPi-Zspy6Y0d.xml | 6 ------ .../1BSDFN_bp_8Wq1u5OO4MK7xHWFEd.xml} | 0 .../1BSDFN_bp_8Wq1u5OO4MK7xHWFEp.xml} | 0 .../5mzsc4L1QMwcGyCp10S_0jXV0UYd.xml} | 0 .../5mzsc4L1QMwcGyCp10S_0jXV0UYp.xml} | 0 .../nYxqFWWUsPUVCBz8zZlw7QYCOvod.xml} | 0 .../nYxqFWWUsPUVCBz8zZlw7QYCOvop.xml} | 0 .../uR7UAO4rsdHCU9T8lZjoYW7a32Ud.xml} | 0 .../uR7UAO4rsdHCU9T8lZjoYW7a32Up.xml} | 0 .../PL5Ga2JyphJjO2f0V1lt1v9mOfQd.xml} | 0 .../PL5Ga2JyphJjO2f0V1lt1v9mOfQp.xml} | 2 +- .../_2w7KV6EHc1SMHPJ5sv5BJ_TE_4p.xml | 2 -- .../gU6nQ9al6ASHLbEF0HmBERcYd4Md.xml | 6 ------ .../ogNzaRYtwam4lR-ZN7eI6m_SXeYd.xml | 6 ------ .../xmaLmb7wr2-EmOlPBo1ae7Q1eEwd.xml | 6 ------ .../-IEsxSr0pUR0RE2Q2b5wpQX0KkEd.xml} | 0 .../-IEsxSr0pUR0RE2Q2b5wpQX0KkEp.xml | 2 ++ .../3DhOOe0Tl7hipLJtC66oxocIr9cd.xml} | 0 .../3DhOOe0Tl7hipLJtC66oxocIr9cp.xml | 2 ++ .../AxKLxQn_Q5ExtyTFauEpYntfsssd.xml} | 0 .../AxKLxQn_Q5ExtyTFauEpYntfsssp.xml | 2 ++ .../IDetplA4BCvExeTDnnCPfg2MhBwd.xml} | 0 .../IDetplA4BCvExeTDnnCPfg2MhBwp.xml | 2 ++ .../QqOZDkdIGwDJT1oKEd1m5cJ1l88d.xml} | 0 .../QqOZDkdIGwDJT1oKEd1m5cJ1l88p.xml | 2 ++ .../SJJLTV1fLQ-ZA32hIB2BTOYk5TYd.xml} | 0 .../SJJLTV1fLQ-ZA32hIB2BTOYk5TYp.xml | 2 ++ .../gK1b_iZQx1Km0kq0Da4qWwP0JpUd.xml} | 0 .../gK1b_iZQx1Km0kq0Da4qWwP0JpUp.xml | 2 ++ .../zMWI0fKvYbz3vT7BQxp-7H6ez4wd.xml} | 0 .../zMWI0fKvYbz3vT7BQxp-7H6ez4wp.xml | 2 ++ .../FcqvamgK2GFBncjelWFYWpbic6sd.xml | 6 ------ .../Q9BY7Fa1-dMNQpWUMw61LYZlnpkd.xml | 6 ------ .../TMwS0Ehc1hOj1QS3kXBWqYH2cIYd.xml | 6 ------ .../XA-33X-Ml7fASA-h03h0NoRtubAd.xml | 6 ------ .../ccgGxetCHn-i1g49iWGxy2eRT9gd.xml | 6 ------ .../wv-PgmFd28kiUsYZVe0yPM4yMXEd.xml | 6 ------ .../l8FwG4TnKj8gyMwHPAtYvu1WQGUd.xml | 6 ------ .../zxz-H_H66S_yiImwKMo7C6xbqr0d.xml | 2 -- .../zxz-H_H66S_yiImwKMo7C6xbqr0p.xml | 2 -- .../2BNsbxotz8yS1dkQ-k4TtTtfElkd.xml} | 0 .../2BNsbxotz8yS1dkQ-k4TtTtfElkp.xml} | 0 .../41tDM4fkWcly5KHLFGBD7TnUF1kd.xml} | 0 .../41tDM4fkWcly5KHLFGBD7TnUF1kp.xml | 2 ++ .../6AGol90mQGOIatxNSyMhdaH59gAd.xml} | 0 .../6AGol90mQGOIatxNSyMhdaH59gAp.xml} | 0 .../BYuUEuuSEpTxFNYwIIct-_focKkd.xml} | 0 .../BYuUEuuSEpTxFNYwIIct-_focKkp.xml} | 0 .../FDdGHhZvwjpYxttUpuTOcZx3JmMd.xml} | 0 .../FDdGHhZvwjpYxttUpuTOcZx3JmMp.xml | 2 ++ .../FjEfBzqcYgSPB2oA26wfyEdBqo0d.xml} | 0 .../FjEfBzqcYgSPB2oA26wfyEdBqo0p.xml} | 0 .../H72L659GuP9YjS6Dt9kIH5c6HnQd.xml} | 0 .../H72L659GuP9YjS6Dt9kIH5c6HnQp.xml} | 0 .../HvUMVTzlhZFbsfNl34oO8xkwaekd.xml} | 0 .../HvUMVTzlhZFbsfNl34oO8xkwaekp.xml | 2 ++ .../MxOdJHAcr56wMe-tpEEz-LoiQlYd.xml} | 0 .../MxOdJHAcr56wMe-tpEEz-LoiQlYp.xml} | 0 .../NEI1fXIv8KRUL_rDHprZREmiW34d.xml} | 0 .../NEI1fXIv8KRUL_rDHprZREmiW34p.xml} | 0 .../NFReMHJQuHGlCss_fLSqFSIT1ZYd.xml} | 0 .../NFReMHJQuHGlCss_fLSqFSIT1ZYp.xml | 2 ++ .../PMTiY8ii3Ycb5X733RMaGCNVItAd.xml} | 0 .../PMTiY8ii3Ycb5X733RMaGCNVItAp.xml} | 0 .../PQTYqrLotgfQlXwRwcTgmkIRabcd.xml} | 0 .../PQTYqrLotgfQlXwRwcTgmkIRabcp.xml} | 0 .../Q-DZerJncmEPNpWIO9dYINnubhAd.xml} | 0 .../Q-DZerJncmEPNpWIO9dYINnubhAp.xml | 2 ++ .../R08oPdcWNyOGmE1Nvdwwvs71M1kd.xml} | 0 .../R08oPdcWNyOGmE1Nvdwwvs71M1kp.xml | 2 ++ .../Rx9X9wAt2wpjFi2e7541ezE-nWwd.xml} | 0 .../Rx9X9wAt2wpjFi2e7541ezE-nWwp.xml} | 0 .../SJuj0lQEL2kv8DN93wpDxprjL2Qd.xml} | 0 .../SJuj0lQEL2kv8DN93wpDxprjL2Qp.xml} | 0 .../TMR2PLz8-jf_t72xY3w-eiAkMwcd.xml} | 0 .../TMR2PLz8-jf_t72xY3w-eiAkMwcp.xml} | 0 .../UeukJVHJUwLZ1oSw-yBfMdSLcNId.xml} | 0 .../UeukJVHJUwLZ1oSw-yBfMdSLcNIp.xml} | 0 .../V4lqQmJ8gZjfEk0BSGK5rhmhQ7Qd.xml} | 0 .../V4lqQmJ8gZjfEk0BSGK5rhmhQ7Qp.xml} | 0 .../W3r3wIq1F_aH5Bg_6WiksWHWjnsd.xml} | 0 .../W3r3wIq1F_aH5Bg_6WiksWHWjnsp.xml} | 0 .../Yzm_5cRIP1TNTAYJFwi1gFJMvrYd.xml} | 0 .../Yzm_5cRIP1TNTAYJFwi1gFJMvrYp.xml | 2 ++ .../aQn5VXjSyIOSf20IuNhzQTcJKLEd.xml} | 0 .../aQn5VXjSyIOSf20IuNhzQTcJKLEp.xml} | 0 .../alAG1z_nIzafOha6mXt5pH8WAmgd.xml} | 0 .../alAG1z_nIzafOha6mXt5pH8WAmgp.xml} | 0 .../d52ncQukW_9fnTuQsihy5s3Rp9kd.xml} | 0 .../d52ncQukW_9fnTuQsihy5s3Rp9kp.xml} | 0 .../fkKGPyMFLRd9q8sWiOz4qApnVgkd.xml} | 0 .../fkKGPyMFLRd9q8sWiOz4qApnVgkp.xml} | 0 .../jcRKP1GWD0s2vbmJjnRVU0UNQuEd.xml} | 0 .../jcRKP1GWD0s2vbmJjnRVU0UNQuEp.xml} | 0 .../lIsawu5M1S3Y4h7oJkYpw3jue7Ed.xml} | 0 .../lIsawu5M1S3Y4h7oJkYpw3jue7Ep.xml} | 0 .../ljel_A4AHLbljnmRTR2O3qqpy8Ed.xml} | 0 .../ljel_A4AHLbljnmRTR2O3qqpy8Ep.xml} | 0 .../scEHrBcR_4DaGlInftNFgaoYFTMd.xml} | 0 .../scEHrBcR_4DaGlInftNFgaoYFTMp.xml} | 0 .../ux2HJV4G7O8gtSZERsaoTt6GWQkd.xml} | 0 .../ux2HJV4G7O8gtSZERsaoTt6GWQkp.xml} | 0 .../xcwXr-V7z1F2VlQN51Es9EbCc6sd.xml | 6 ------ .../xcwXr-V7z1F2VlQN51Es9EbCc6sp.xml | 2 -- .../GNyWdCWXwy_FnEw-XS9bcAQnRkId.xml} | 0 .../GNyWdCWXwy_FnEw-XS9bcAQnRkIp.xml} | 0 .../STP0mSEMvKb0C99CAKfVG-FjFdUd.xml} | 0 .../STP0mSEMvKb0C99CAKfVG-FjFdUp.xml} | 0 447 files changed, 91 insertions(+), 311 deletions(-) rename resources/project/{7AZXpb090n98KOSsx01-zOUfRQg/DvZOqACkqxwPDMpa6RdAuXS_l1sd.xml => 6AGol90mQGOIatxNSyMhdaH59gA/3OaAbed4F4CnFYNp5AkGzyUZHUsd.xml} (100%) rename resources/project/{7AZXpb090n98KOSsx01-zOUfRQg/DvZOqACkqxwPDMpa6RdAuXS_l1sp.xml => 6AGol90mQGOIatxNSyMhdaH59gA/3OaAbed4F4CnFYNp5AkGzyUZHUsp.xml} (100%) rename resources/project/{7AZXpb090n98KOSsx01-zOUfRQg/3oidn4LRL5GwwDzzeMZiSp7A2O0d.xml => 6AGol90mQGOIatxNSyMhdaH59gA/KjY44G7WJ_jinQPfkCzkmqHUBu8d.xml} (100%) rename resources/project/{oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/Q9BY7Fa1-dMNQpWUMw61LYZlnpkp.xml => 6AGol90mQGOIatxNSyMhdaH59gA/KjY44G7WJ_jinQPfkCzkmqHUBu8p.xml} (100%) rename resources/project/{7AZXpb090n98KOSsx01-zOUfRQg/LzzgRGTWkydILioLs0JPaa-Nu40d.xml => 6AGol90mQGOIatxNSyMhdaH59gA/NFORnXmhiVQSgHA5Autj49jPftYd.xml} (100%) rename resources/project/{oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/TMwS0Ehc1hOj1QS3kXBWqYH2cIYp.xml => 6AGol90mQGOIatxNSyMhdaH59gA/NFORnXmhiVQSgHA5Autj49jPftYp.xml} (100%) rename resources/project/{7AZXpb090n98KOSsx01-zOUfRQg/gDdlibUiOY4zsCI36J4PKMPypIId.xml => 6AGol90mQGOIatxNSyMhdaH59gA/oHpXnrNixlZjK9qYnwuV6eX9gfcd.xml} (100%) rename resources/project/{oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/FcqvamgK2GFBncjelWFYWpbic6sp.xml => 6AGol90mQGOIatxNSyMhdaH59gA/oHpXnrNixlZjK9qYnwuV6eX9gfcp.xml} (100%) rename resources/project/{7AZXpb090n98KOSsx01-zOUfRQg/tfTJ4kPklZp2baElxH8WVDN7PYkd.xml => 6AGol90mQGOIatxNSyMhdaH59gA/sgdsvhsCbARvZJ55oWpjfMFPi3Qd.xml} (100%) rename resources/project/{oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/ccgGxetCHn-i1g49iWGxy2eRT9gp.xml => 6AGol90mQGOIatxNSyMhdaH59gA/sgdsvhsCbARvZJ55oWpjfMFPi3Qp.xml} (100%) rename resources/project/{BXX9SqJTGGGO_CDj2AJCCAtV84k/BWtL89f91xbulioiaYLBB_yHOgMd.xml => 6AGol90mQGOIatxNSyMhdaH59gA/tkgt0Y0wPwlAY1DVmERxblQgvVod.xml} (100%) rename resources/project/{oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/wv-PgmFd28kiUsYZVe0yPM4yMXEp.xml => 6AGol90mQGOIatxNSyMhdaH59gA/tkgt0Y0wPwlAY1DVmERxblQgvVop.xml} (100%) rename resources/project/{BXX9SqJTGGGO_CDj2AJCCAtV84k/G0w81rMIRi8PzlrGVbGmoa0FV9gd.xml => 6AGol90mQGOIatxNSyMhdaH59gA/zf1841WvvYarScoTMS3MPj4JW9cd.xml} (100%) rename resources/project/{oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/XA-33X-Ml7fASA-h03h0NoRtubAp.xml => 6AGol90mQGOIatxNSyMhdaH59gA/zf1841WvvYarScoTMS3MPj4JW9cp.xml} (100%) delete mode 100644 resources/project/7AZXpb090n98KOSsx01-zOUfRQg/3oidn4LRL5GwwDzzeMZiSp7A2O0p.xml delete mode 100644 resources/project/7AZXpb090n98KOSsx01-zOUfRQg/LzzgRGTWkydILioLs0JPaa-Nu40p.xml delete mode 100644 resources/project/7AZXpb090n98KOSsx01-zOUfRQg/gDdlibUiOY4zsCI36J4PKMPypIIp.xml delete mode 100644 resources/project/7AZXpb090n98KOSsx01-zOUfRQg/tfTJ4kPklZp2baElxH8WVDN7PYkp.xml delete mode 100644 resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/2EnTnZEx-4v1jC3AJZADMDpEO1gp.xml delete mode 100644 resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/5LEawZsxA1aBTeqG0NlpCtR6q6kp.xml delete mode 100644 resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/8uH7KtfLIR_hLVyUlkUrouETOeop.xml delete mode 100644 resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/JlNlt7b52D5lndUS8QIFBXuyn8sp.xml delete mode 100644 resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/V7dcj86ZsVvV3wlDWv_EePrkW7wp.xml delete mode 100644 resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/XDDpmW-ahmfRGLbHVnihtWagQDEp.xml delete mode 100644 resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/rzCzLkJQsijCRt28FvlpiZrjfZ4p.xml delete mode 100644 resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/vyBBHUVgKzM20Od7DNtH6-JNEiAp.xml delete mode 100644 resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/z8PCXMWInzrlFrKj4KQg_4fVTZYp.xml delete mode 100644 resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/0amoX63LRAKETLDqV7_F13CuX74p.xml delete mode 100644 resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/BEHll27af-LsZgmvfXGuLyYJyU0p.xml delete mode 100644 resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/CgrwWh_FTjN531XPRoLVTVqLbZEp.xml delete mode 100644 resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/GSxmGWKoE8yVg8Dt0_WIVqZwfq8p.xml delete mode 100644 resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/GmURLyn29gRlewEo8pphTKMkvfcp.xml delete mode 100644 resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/K7gRh-Ets4DP9XdM-8WQS6MYiTAp.xml delete mode 100644 resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/QQxpEF0mWnNdoQybWJnETf5i2zgp.xml delete mode 100644 resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/UlAa7CZxUr955fLFiY8150xe-0Yp.xml delete mode 100644 resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/kyvnzrdUBXgtJ7QWloYIMdgg3f0p.xml delete mode 100644 resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/mIYcND4GVDO0WgnXvKhJVZw4Qdsp.xml delete mode 100644 resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/wOcCxJrf1WxhN3mYz0ASwydFXsMp.xml delete mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/3SN3sghzDXjWjn7o9l7cAa0O5KAd.xml delete mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/3SN3sghzDXjWjn7o9l7cAa0O5KAp.xml delete mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/E21V15DpmDB-ZQFfMLZLCGa_5lId.xml delete mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/E21V15DpmDB-ZQFfMLZLCGa_5lIp.xml delete mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/IjYbiW-Ya6w-T2Z7_m6dd_k13wwd.xml delete mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/IjYbiW-Ya6w-T2Z7_m6dd_k13wwp.xml delete mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/MR_L_h1ZG2GwTSuc8zZrWnvy1Jkd.xml delete mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/MR_L_h1ZG2GwTSuc8zZrWnvy1Jkp.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/NVX46-wyblASglEOArlOVFEQTfkd.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/NVX46-wyblASglEOArlOVFEQTfkp.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/NsgPl7VSSufo3svKEWfckxlnRO0d.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/NsgPl7VSSufo3svKEWfckxlnRO0p.xml delete mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/PGaTJl94-dUtK-5YVG-7nT6UaKQd.xml delete mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/PGaTJl94-dUtK-5YVG-7nT6UaKQp.xml delete mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/QWMJ0IlUTrzTjwye6g1ffgLjfGYd.xml delete mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/QWMJ0IlUTrzTjwye6g1ffgLjfGYp.xml delete mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/Uo9aVDJOhqlyX0wNM67-Vr7U4PYd.xml delete mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/Uo9aVDJOhqlyX0wNM67-Vr7U4PYp.xml delete mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/VbvFCIQVsoRbd9GRTBzN_omsupMd.xml delete mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/VbvFCIQVsoRbd9GRTBzN_omsupMp.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/ZTjokurFNXtQ1ojGBXgvq_kwksId.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/ZTjokurFNXtQ1ojGBXgvq_kwksIp.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/ZYZcp25adoHaOiOfAGtsGRGGUlod.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/ZYZcp25adoHaOiOfAGtsGRGGUlop.xml delete mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/Zu3w1_x1xrK1iAVqy0N_K78YUSQd.xml delete mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/Zu3w1_x1xrK1iAVqy0N_K78YUSQp.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/fDXmO3TGZWioHy3oJ1Pc44eEwx4d.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/fDXmO3TGZWioHy3oJ1Pc44eEwx4p.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/fJawufIe31Xaj5x2aG6mddyYxqgd.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/fJawufIe31Xaj5x2aG6mddyYxqgp.xml delete mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/i5tvtXjf-liajJnRRTHy0WQWyBQd.xml delete mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/i5tvtXjf-liajJnRRTHy0WQWyBQp.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/jTTvt3Bp2bTBwpSRN3WIq48HTY8d.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/jTTvt3Bp2bTBwpSRN3WIq48HTY8p.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/kLundQjBc0lkyBnd7dU88QI9eCYd.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/kLundQjBc0lkyBnd7dU88QI9eCYp.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/pdndsCkuDwLksOKofxkONBmGSu0d.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/pdndsCkuDwLksOKofxkONBmGSu0p.xml delete mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/qomN9EMLc7gSakLKy3wL9wrIpacd.xml delete mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/qomN9EMLc7gSakLKy3wL9wrIpacp.xml delete mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/r49BYN-QImuasJYtp4M0VMhEspQd.xml delete mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/r49BYN-QImuasJYtp4M0VMhEspQp.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/tzNeWTtKSZS7T3vujfMV7O83bhgd.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/tzNeWTtKSZS7T3vujfMV7O83bhgp.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/v0w6uQCEov7noSqWPgoe4xwGBF0d.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/v0w6uQCEov7noSqWPgoe4xwGBF0p.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/vidY_3j1oRGddcnaqqAhcbXSRdEd.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/vidY_3j1oRGddcnaqqAhcbXSRdEp.xml delete mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/xc55aMCO0KDaUwfDNG4gUONyFQMd.xml delete mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/xc55aMCO0KDaUwfDNG4gUONyFQMp.xml rename resources/project/{BXX9SqJTGGGO_CDj2AJCCAtV84k/HdiZGhv_rKHUyETdzYHpE0xs7AUd.xml => H72L659GuP9YjS6Dt9kIH5c6HnQ/7SrjXNQDv9BGhC0GEOKQspr9Ocgd.xml} (100%) rename resources/project/{dYvx6wp33Ts8hX1S_rzdiQ3NP4A/9orjn84dnpAKkkuchjb6ZXb4lYop.xml => H72L659GuP9YjS6Dt9kIH5c6HnQ/7SrjXNQDv9BGhC0GEOKQspr9Ocgp.xml} (100%) rename resources/project/{BXX9SqJTGGGO_CDj2AJCCAtV84k/Onj0w7QprQSOKpr22MXMZdoROBAd.xml => H72L659GuP9YjS6Dt9kIH5c6HnQ/82flsbuQG_B85H3tl7ti_QR4mXId.xml} (100%) rename resources/project/{dYvx6wp33Ts8hX1S_rzdiQ3NP4A/Q_eYts4Jx8lwh_76CdRDIxbbdlkp.xml => H72L659GuP9YjS6Dt9kIH5c6HnQ/82flsbuQG_B85H3tl7ti_QR4mXIp.xml} (100%) rename resources/project/{BXX9SqJTGGGO_CDj2AJCCAtV84k/Zoc8C9B4lYxsGTJUXBoNY2847rsd.xml => H72L659GuP9YjS6Dt9kIH5c6HnQ/a0lBcQv35pXv1BECWbcE4pI6X88d.xml} (100%) rename resources/project/{BXX9SqJTGGGO_CDj2AJCCAtV84k/Zoc8C9B4lYxsGTJUXBoNY2847rsp.xml => H72L659GuP9YjS6Dt9kIH5c6HnQ/a0lBcQv35pXv1BECWbcE4pI6X88p.xml} (100%) rename resources/project/{BXX9SqJTGGGO_CDj2AJCCAtV84k/Qc2wVp_95I6ELlZy_on0j8jGrt4d.xml => H72L659GuP9YjS6Dt9kIH5c6HnQ/bV1SapNLHXOJuqxs4eM5UgXt1DMd.xml} (100%) rename resources/project/{dYvx6wp33Ts8hX1S_rzdiQ3NP4A/7U58VHMhrmTgIuozFzLR1XBJo8Ip.xml => H72L659GuP9YjS6Dt9kIH5c6HnQ/bV1SapNLHXOJuqxs4eM5UgXt1DMp.xml} (100%) rename resources/project/{BXX9SqJTGGGO_CDj2AJCCAtV84k/cUlln2uasX69yptYpRT0FIXMmN4d.xml => H72L659GuP9YjS6Dt9kIH5c6HnQ/wfGjJ216NLwm4I2ndZI3_86uhgQd.xml} (100%) rename resources/project/{dYvx6wp33Ts8hX1S_rzdiQ3NP4A/vok99kMS3Be0cBX4FPi-Zspy6Y0p.xml => H72L659GuP9YjS6Dt9kIH5c6HnQ/wfGjJ216NLwm4I2ndZI3_86uhgQp.xml} (100%) rename resources/project/{BXX9SqJTGGGO_CDj2AJCCAtV84k/fbFT1H1Yjhkv8Mn3gVUZJX6bG_od.xml => MxOdJHAcr56wMe-tpEEz-LoiQlY/Eb3URaLnOadzaECH9JhrMNGK0cYd.xml} (100%) rename resources/project/{OLuX-8c-UOqeJnrM2amm241SKTs/0oyoesBN2_oayH0i7gtHJp890Esp.xml => MxOdJHAcr56wMe-tpEEz-LoiQlY/Eb3URaLnOadzaECH9JhrMNGK0cYp.xml} (100%) rename resources/project/{BXX9SqJTGGGO_CDj2AJCCAtV84k/mpAb6JLvq2630l0ar-o34Ybxq50d.xml => MxOdJHAcr56wMe-tpEEz-LoiQlY/GUD97Kgby-Nbe7a4937yGMYWqoQd.xml} (100%) create mode 100644 resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/GUD97Kgby-Nbe7a4937yGMYWqoQp.xml rename resources/project/{BXX9SqJTGGGO_CDj2AJCCAtV84k/qXqP2bORsPzSqGsTBWvUFdPwO04d.xml => MxOdJHAcr56wMe-tpEEz-LoiQlY/IXrhsh0KA0Hd3dEHjxbDlSwV_9wd.xml} (100%) rename resources/project/{OLuX-8c-UOqeJnrM2amm241SKTs/kJV53JKRAFPnFBgXx06VMpdr0ssp.xml => MxOdJHAcr56wMe-tpEEz-LoiQlY/IXrhsh0KA0Hd3dEHjxbDlSwV_9wp.xml} (100%) rename resources/project/{D7EGYpvIiO_nGXQuTOYjsSP_JQw/2EnTnZEx-4v1jC3AJZADMDpEO1gd.xml => MxOdJHAcr56wMe-tpEEz-LoiQlY/_cZck6op8VccwtbtBtqU08jEoNgd.xml} (100%) rename resources/project/{OLuX-8c-UOqeJnrM2amm241SKTs/jupI72lBOnUNFR-WjgobwXS0zMop.xml => MxOdJHAcr56wMe-tpEEz-LoiQlY/_cZck6op8VccwtbtBtqU08jEoNgp.xml} (100%) rename resources/project/{D7EGYpvIiO_nGXQuTOYjsSP_JQw/5LEawZsxA1aBTeqG0NlpCtR6q6kd.xml => MxOdJHAcr56wMe-tpEEz-LoiQlY/poWgzYIOpdVLERL5CAxxonZKagod.xml} (100%) rename resources/project/{OLuX-8c-UOqeJnrM2amm241SKTs/T9wikiXK6MjHu7XyyDA0kuCTP_kp.xml => MxOdJHAcr56wMe-tpEEz-LoiQlY/poWgzYIOpdVLERL5CAxxonZKagop.xml} (100%) rename resources/project/{D7EGYpvIiO_nGXQuTOYjsSP_JQw/5TVbg-DxiEwvaH5Bj9CH0ewcKu8d.xml => MxOdJHAcr56wMe-tpEEz-LoiQlY/veTEyGEjchushQj-PUakRWJ1Rfwd.xml} (100%) create mode 100644 resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/veTEyGEjchushQj-PUakRWJ1Rfwp.xml rename resources/project/{D7EGYpvIiO_nGXQuTOYjsSP_JQw/8uH7KtfLIR_hLVyUlkUrouETOeod.xml => MxOdJHAcr56wMe-tpEEz-LoiQlY/wXFztAEfZBGlWkniv1yDOZUPMLkd.xml} (100%) rename resources/project/{OLuX-8c-UOqeJnrM2amm241SKTs/-xMsha79hmE0qOhn3z2Y4wwJaNkp.xml => MxOdJHAcr56wMe-tpEEz-LoiQlY/wXFztAEfZBGlWkniv1yDOZUPMLkp.xml} (100%) rename resources/project/{D7EGYpvIiO_nGXQuTOYjsSP_JQw/nW4p5Z6gDt0MyfrwKAnI3MioETUd.xml => MxOdJHAcr56wMe-tpEEz-LoiQlY/yG6FN90UXfIzKIRJPeCgEK9wsiAd.xml} (100%) rename resources/project/{D7EGYpvIiO_nGXQuTOYjsSP_JQw/nW4p5Z6gDt0MyfrwKAnI3MioETUp.xml => MxOdJHAcr56wMe-tpEEz-LoiQlY/yG6FN90UXfIzKIRJPeCgEK9wsiAp.xml} (100%) delete mode 100644 resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/G2x_hSFRzJNj2RwsZ4N6TWYsHmYp.xml delete mode 100644 resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/rx1MnRhyaD-dcP1I9WcseP2PGiwp.xml delete mode 100644 resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/wSDXMLBIRWsCGVgqLP9ER---lO4p.xml delete mode 100644 resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/yrvDAxHTmqAbl2v6-rWp7tgRl4Ep.xml rename resources/project/{D7EGYpvIiO_nGXQuTOYjsSP_JQw/DpnGV8GaSUAjtUgrpQ5nUXOT5wYd.xml => PL5Ga2JyphJjO2f0V1lt1v9mOfQ/-Lw-S9LvX_FYDRFafe-L_l8cTMsd.xml} (100%) rename resources/project/{BXX9SqJTGGGO_CDj2AJCCAtV84k/qXqP2bORsPzSqGsTBWvUFdPwO04p.xml => PL5Ga2JyphJjO2f0V1lt1v9mOfQ/-Lw-S9LvX_FYDRFafe-L_l8cTMsp.xml} (100%) rename resources/project/{D7EGYpvIiO_nGXQuTOYjsSP_JQw/J8XPAkZbsecOIp7IVKNIsrQZCHEd.xml => PL5Ga2JyphJjO2f0V1lt1v9mOfQ/4JNkAFleLVxY3nQGnz12ugS4ZTEd.xml} (100%) rename resources/project/{BXX9SqJTGGGO_CDj2AJCCAtV84k/BWtL89f91xbulioiaYLBB_yHOgMp.xml => PL5Ga2JyphJjO2f0V1lt1v9mOfQ/4JNkAFleLVxY3nQGnz12ugS4ZTEp.xml} (100%) rename resources/project/{D7EGYpvIiO_nGXQuTOYjsSP_JQw/JlNlt7b52D5lndUS8QIFBXuyn8sd.xml => PL5Ga2JyphJjO2f0V1lt1v9mOfQ/4hmmxZ0XovlFSmc-Rsau_8EpzNkd.xml} (100%) rename resources/project/{BXX9SqJTGGGO_CDj2AJCCAtV84k/Qc2wVp_95I6ELlZy_on0j8jGrt4p.xml => PL5Ga2JyphJjO2f0V1lt1v9mOfQ/4hmmxZ0XovlFSmc-Rsau_8EpzNkp.xml} (100%) rename resources/project/{D7EGYpvIiO_nGXQuTOYjsSP_JQw/Nh99zm6VyLbCagFVKH0kx0irGckd.xml => PL5Ga2JyphJjO2f0V1lt1v9mOfQ/5lEWuAPjsUhI7_hCP31KFq7iDlUd.xml} (100%) rename resources/project/{BXX9SqJTGGGO_CDj2AJCCAtV84k/cUlln2uasX69yptYpRT0FIXMmN4p.xml => PL5Ga2JyphJjO2f0V1lt1v9mOfQ/5lEWuAPjsUhI7_hCP31KFq7iDlUp.xml} (100%) rename resources/project/{BXX9SqJTGGGO_CDj2AJCCAtV84k/Bk_xMGlhpZAWbMPYXgyuKxor6wod.xml => PL5Ga2JyphJjO2f0V1lt1v9mOfQ/CZ1F4Twl7ZiFGyGb8gS7TX0pkTQd.xml} (100%) rename resources/project/{BXX9SqJTGGGO_CDj2AJCCAtV84k/Bk_xMGlhpZAWbMPYXgyuKxor6wop.xml => PL5Ga2JyphJjO2f0V1lt1v9mOfQ/CZ1F4Twl7ZiFGyGb8gS7TX0pkTQp.xml} (100%) rename resources/project/{D7EGYpvIiO_nGXQuTOYjsSP_JQw/PUXt85kSoxl2GhAVUeaGOA1ui0sd.xml => PL5Ga2JyphJjO2f0V1lt1v9mOfQ/DK5W7ZmXIHOHuwnXHtwNGL22p4Md.xml} (100%) rename resources/project/{BXX9SqJTGGGO_CDj2AJCCAtV84k/HdiZGhv_rKHUyETdzYHpE0xs7AUp.xml => PL5Ga2JyphJjO2f0V1lt1v9mOfQ/DK5W7ZmXIHOHuwnXHtwNGL22p4Mp.xml} (100%) rename resources/project/{Drp6kay-WW1Z2AQWI8KW60j9HlQ/6Fwf5cB_poUbMFePFS_ylV5Ftusd.xml => PL5Ga2JyphJjO2f0V1lt1v9mOfQ/GPtTjnSsEKTMO1S-sg_FP4dxbbEd.xml} (100%) rename resources/project/{Drp6kay-WW1Z2AQWI8KW60j9HlQ/6Fwf5cB_poUbMFePFS_ylV5Ftusp.xml => PL5Ga2JyphJjO2f0V1lt1v9mOfQ/GPtTjnSsEKTMO1S-sg_FP4dxbbEp.xml} (100%) rename resources/project/{D7EGYpvIiO_nGXQuTOYjsSP_JQw/SLN8kBxZYH73a64wQLgTpFlpH-Ud.xml => PL5Ga2JyphJjO2f0V1lt1v9mOfQ/J4NSMqZEAmFZH35SWLSatqliZmkd.xml} (100%) rename resources/project/{BXX9SqJTGGGO_CDj2AJCCAtV84k/mpAb6JLvq2630l0ar-o34Ybxq50p.xml => PL5Ga2JyphJjO2f0V1lt1v9mOfQ/J4NSMqZEAmFZH35SWLSatqliZmkp.xml} (100%) rename resources/project/{D7EGYpvIiO_nGXQuTOYjsSP_JQw/V7dcj86ZsVvV3wlDWv_EePrkW7wd.xml => PL5Ga2JyphJjO2f0V1lt1v9mOfQ/eOoHRDjyUSBj8-WLEX9mxUx6ePUd.xml} (100%) rename resources/project/{BXX9SqJTGGGO_CDj2AJCCAtV84k/fbFT1H1Yjhkv8Mn3gVUZJX6bG_op.xml => PL5Ga2JyphJjO2f0V1lt1v9mOfQ/eOoHRDjyUSBj8-WLEX9mxUx6ePUp.xml} (100%) rename resources/project/{BXX9SqJTGGGO_CDj2AJCCAtV84k/tQLIewmcrCq5k7qM-X0N0mag3d8d.xml => PL5Ga2JyphJjO2f0V1lt1v9mOfQ/j0R75YjB0BcweDo5AYcMWiq3P6Md.xml} (100%) rename resources/project/{BXX9SqJTGGGO_CDj2AJCCAtV84k/tQLIewmcrCq5k7qM-X0N0mag3d8p.xml => PL5Ga2JyphJjO2f0V1lt1v9mOfQ/j0R75YjB0BcweDo5AYcMWiq3P6Mp.xml} (100%) rename resources/project/{D7EGYpvIiO_nGXQuTOYjsSP_JQw/XDDpmW-ahmfRGLbHVnihtWagQDEd.xml => PL5Ga2JyphJjO2f0V1lt1v9mOfQ/mjVTgOO6IWMthsneycvZqWzxD_gd.xml} (100%) rename resources/project/{BXX9SqJTGGGO_CDj2AJCCAtV84k/Onj0w7QprQSOKpr22MXMZdoROBAp.xml => PL5Ga2JyphJjO2f0V1lt1v9mOfQ/mjVTgOO6IWMthsneycvZqWzxD_gp.xml} (100%) rename resources/project/{D7EGYpvIiO_nGXQuTOYjsSP_JQw/rzCzLkJQsijCRt28FvlpiZrjfZ4d.xml => PL5Ga2JyphJjO2f0V1lt1v9mOfQ/nVExW6oxyLwVGXPnnA3XaUC50Jcd.xml} (100%) rename resources/project/{BXX9SqJTGGGO_CDj2AJCCAtV84k/G0w81rMIRi8PzlrGVbGmoa0FV9gp.xml => PL5Ga2JyphJjO2f0V1lt1v9mOfQ/nVExW6oxyLwVGXPnnA3XaUC50Jcp.xml} (100%) rename resources/project/{D7EGYpvIiO_nGXQuTOYjsSP_JQw/vyBBHUVgKzM20Od7DNtH6-JNEiAd.xml => PMTiY8ii3Ycb5X733RMaGCNVItA/6tuQeWy9960NGsG8vq2g6XEbfs8d.xml} (100%) create mode 100644 resources/project/PMTiY8ii3Ycb5X733RMaGCNVItA/6tuQeWy9960NGsG8vq2g6XEbfs8p.xml rename resources/project/{D7EGYpvIiO_nGXQuTOYjsSP_JQw/z8PCXMWInzrlFrKj4KQg_4fVTZYd.xml => PMTiY8ii3Ycb5X733RMaGCNVItA/A8ncUuxWwwZj8vBXXThduDRmo-Id.xml} (100%) create mode 100644 resources/project/PMTiY8ii3Ycb5X733RMaGCNVItA/A8ncUuxWwwZj8vBXXThduDRmo-Ip.xml rename resources/project/{D7EGYpvIiO_nGXQuTOYjsSP_JQw/zWhjsX1e0jX9kixjwjfDU_xQo9Qd.xml => PMTiY8ii3Ycb5X733RMaGCNVItA/J95nrDPGFnMMRe-j9IrlrD3MJIwd.xml} (100%) create mode 100644 resources/project/PMTiY8ii3Ycb5X733RMaGCNVItA/J95nrDPGFnMMRe-j9IrlrD3MJIwp.xml rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/MMRr3Fv-bYGGSvC6xcJnwOYvzGwd.xml => PMTiY8ii3Ycb5X733RMaGCNVItA/cmZk236s6Qn7CSYmgTSLERo6-Fod.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/MMRr3Fv-bYGGSvC6xcJnwOYvzGwp.xml => PMTiY8ii3Ycb5X733RMaGCNVItA/cmZk236s6Qn7CSYmgTSLERo6-Fop.xml} (100%) rename resources/project/{Drp6kay-WW1Z2AQWI8KW60j9HlQ/0amoX63LRAKETLDqV7_F13CuX74d.xml => PMTiY8ii3Ycb5X733RMaGCNVItA/gdgc-JZm4OTNPgAUaiKWok7sS8Qd.xml} (100%) create mode 100644 resources/project/PMTiY8ii3Ycb5X733RMaGCNVItA/gdgc-JZm4OTNPgAUaiKWok7sS8Qp.xml rename resources/project/{Drp6kay-WW1Z2AQWI8KW60j9HlQ/6GJfsBaU_5DGhbimCNcp3Txx-v8d.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/-K_tTgFkd3KuOQq4OW0mOmsQS44d.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/LN9DpJcyR6CE_I14ywn3ncJAeXIp.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/-K_tTgFkd3KuOQq4OW0mOmsQS44p.xml} (100%) rename resources/project/{Drp6kay-WW1Z2AQWI8KW60j9HlQ/888MRa7OWq3oIOyCKPp0I2UIiKAd.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/-ZOcmw4c1B4bINk-xUWH7ahRKJod.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/aSbr7z_N-_xgJQSr5hrZLyKPz4cp.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/-ZOcmw4c1B4bINk-xUWH7ahRKJop.xml} (100%) rename resources/project/{Drp6kay-WW1Z2AQWI8KW60j9HlQ/BEHll27af-LsZgmvfXGuLyYJyU0d.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/00BmM5mTpuxcj5K8jG604UO7-Ewd.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/T2fYvrqhpvDyuKc8Y7wsu2vaPcUp.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/00BmM5mTpuxcj5K8jG604UO7-Ewp.xml} (100%) rename resources/project/{Drp6kay-WW1Z2AQWI8KW60j9HlQ/CgrwWh_FTjN531XPRoLVTVqLbZEd.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/0TH8tA8-wfGMXu-ho-n1gCC4wJsd.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/G6IjDtcuN86KCffyBg9hhLwxfM8p.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/0TH8tA8-wfGMXu-ho-n1gCC4wJsp.xml} (100%) rename resources/project/{Drp6kay-WW1Z2AQWI8KW60j9HlQ/GSxmGWKoE8yVg8Dt0_WIVqZwfq8d.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/2lZWGayY8sTSf6C_9MNt1ciblccd.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/LK5LTNCFyhwaQZUlosrnunT2xZIp.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/2lZWGayY8sTSf6C_9MNt1ciblccp.xml} (100%) rename resources/project/{Drp6kay-WW1Z2AQWI8KW60j9HlQ/GmURLyn29gRlewEo8pphTKMkvfcd.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/4NaiUnhsCfsLNNAYb_2Nre3n59Ud.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/n6yybQJW-SCbwLJB8CxlywM5r38p.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/4NaiUnhsCfsLNNAYb_2Nre3n59Up.xml} (100%) rename resources/project/{Drp6kay-WW1Z2AQWI8KW60j9HlQ/HinGYaWsjaNlqTK_j5cQYUlmn_4d.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/4edCiafrq1BIvyyh61g6Z-ot9R4d.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/00XLiFvyqFehoPigl9ogalyjfbAp.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/4edCiafrq1BIvyyh61g6Z-ot9R4p.xml} (100%) rename resources/project/{Drp6kay-WW1Z2AQWI8KW60j9HlQ/K7gRh-Ets4DP9XdM-8WQS6MYiTAd.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/4x4IMS_bCxBIpOSTkeIwMAKuTawd.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/Vfp2GuJhjHgccgR8Joly_nEH3uUp.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/4x4IMS_bCxBIpOSTkeIwMAKuTawp.xml} (100%) rename resources/project/{Drp6kay-WW1Z2AQWI8KW60j9HlQ/QQxpEF0mWnNdoQybWJnETf5i2zgd.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/5G1NQ5WxNVqz_u56bBC5V8s_cnMd.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/AFQGXDiIsf-NM9eQ8n4eWMrVd1Yp.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/5G1NQ5WxNVqz_u56bBC5V8s_cnMp.xml} (100%) rename resources/project/{Drp6kay-WW1Z2AQWI8KW60j9HlQ/UlAa7CZxUr955fLFiY8150xe-0Yd.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/5OtYPmi9zdVJFCfszj3XmwBOicQd.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/3vPrjfHdpkKcTeJZLymuPjPeJLop.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/5OtYPmi9zdVJFCfszj3XmwBOicQp.xml} (100%) rename resources/project/{Drp6kay-WW1Z2AQWI8KW60j9HlQ/iPx7LMwL8D8SvprpBzhO3qxwy0Id.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/AiCl4Nv-4TcAuyccq2bcsaO4hGId.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/R9uo8TWlIFmDZ_U2L3brdMMNpmUp.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/AiCl4Nv-4TcAuyccq2bcsaO4hGIp.xml} (100%) rename resources/project/{Drp6kay-WW1Z2AQWI8KW60j9HlQ/kyvnzrdUBXgtJ7QWloYIMdgg3f0d.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/BzVxW2mFaL8w6fW-2i_Ks4AtAywd.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/Dg1e3Am5YrqqMqqkIYDk9ZjR1RYp.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/BzVxW2mFaL8w6fW-2i_Ks4AtAywp.xml} (100%) rename resources/project/{Drp6kay-WW1Z2AQWI8KW60j9HlQ/mIYcND4GVDO0WgnXvKhJVZw4Qdsd.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/CwijU_1EfQOTRpwgKbRX29DCRqod.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/aj3y-ODStp8yUzt7MUXbgCsT1OYp.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/CwijU_1EfQOTRpwgKbRX29DCRqop.xml} (100%) rename resources/project/{Drp6kay-WW1Z2AQWI8KW60j9HlQ/wOcCxJrf1WxhN3mYz0ASwydFXsMd.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/D6bNMzhGpxvqGj045koQvCPRgSgd.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/XeBJu8m_oaoL52pMHUpwH6lM5Fop.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/D6bNMzhGpxvqGj045koQvCPRgSgp.xml} (100%) rename resources/project/{Drp6kay-WW1Z2AQWI8KW60j9HlQ/zRzAsvX6Os0bPYij1y_5pG-94Dgd.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/FhhflOTx83dyAZA7C5LZFylsG3Qd.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/TcR3rZOrVrHqDGA6Ls0qBpAOHD0p.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/FhhflOTx83dyAZA7C5LZFylsG3Qp.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/-3QAULuhtItSD7_pHpYyutizqKcd.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/G8qY3zLR3J2KmeRH1uVSXh4Feewd.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/j0ffgGF4ix7R_1weXjtRLJ2m60op.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/G8qY3zLR3J2KmeRH1uVSXh4Feewp.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/-44zf-4QbDVnfxvdMOlS-62neokd.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/L2wh56GmocdhTwvqoEVyLSPjn8Id.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/6zc8a3V6P8-0WJxogD3UrSBw7mEp.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/L2wh56GmocdhTwvqoEVyLSPjn8Ip.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/-pKErZtYRVQsLNI-s6dhlLE9rN0d.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/NgzKbmuSLrYttzedhNh5jGcAk2od.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/2FIZqjX_-rJNGXzNKJW_vC24H0Up.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/NgzKbmuSLrYttzedhNh5jGcAk2op.xml} (100%) rename resources/project/{Hk1Vip6VmTG9gpBtNOXKEU56oUc/H7mS2g0N2I6TXTkSevNbSUWfbRgd.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/NrbvTPDfIXgykDJkfPdIZyDvtVgd.xml} (100%) rename resources/project/{Hk1Vip6VmTG9gpBtNOXKEU56oUc/H7mS2g0N2I6TXTkSevNbSUWfbRgp.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/NrbvTPDfIXgykDJkfPdIZyDvtVgp.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/00XLiFvyqFehoPigl9ogalyjfbAd.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/P0FXd6AwKsPzBiYwIZLsEUD5VrAd.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/ZqK39Xv3A0I-0ohwRkWV6jbAW9Mp.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/P0FXd6AwKsPzBiYwIZLsEUD5VrAp.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/0EOTP3mEDOzkS1q64hgbqKxftLId.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/TAq2STpvx67m5-UfPyxz4oWjUz8d.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/-44zf-4QbDVnfxvdMOlS-62neokp.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/TAq2STpvx67m5-UfPyxz4oWjUz8p.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/1AUPZjF-DkxHBgstLlITifCjfHAd.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/Tl-XR5iwVw4PrAcBNW_SjCclo-kd.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/SS1oWs8uWRjwpAsBEkjHKIdli3op.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/Tl-XR5iwVw4PrAcBNW_SjCclo-kp.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/2FIZqjX_-rJNGXzNKJW_vC24H0Ud.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/V_NWUez2zLHkRy0FgbfpoMa561Ed.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/Rgu_Xpn3p1FJCz3744RsTXGnSX4p.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/V_NWUez2zLHkRy0FgbfpoMa561Ep.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/3vPrjfHdpkKcTeJZLymuPjPeJLod.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/X1p2mbSsnW7dQJV2bKNOREnOyY4d.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/0EOTP3mEDOzkS1q64hgbqKxftLIp.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/X1p2mbSsnW7dQJV2bKNOREnOyY4p.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/6zc8a3V6P8-0WJxogD3UrSBw7mEd.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/YkEXBoi2NKObNUB8qZYLCaOkxj4d.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/-pKErZtYRVQsLNI-s6dhlLE9rN0p.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/YkEXBoi2NKObNUB8qZYLCaOkxj4p.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/9K4DX640tmrkmoBLflTkiNfPiZId.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/aukBLYh0oaCR-vLwva9Y2OCkQzUd.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/9K4DX640tmrkmoBLflTkiNfPiZIp.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/aukBLYh0oaCR-vLwva9Y2OCkQzUp.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/AFQGXDiIsf-NM9eQ8n4eWMrVd1Yd.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/b14wfAQdUmLf7zWEQ4kujKfjTQYd.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/MgsQiSKNdqjh2tb2GSg9xpGhlMgp.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/b14wfAQdUmLf7zWEQ4kujKfjTQYp.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/BNAgggSUW1I0e_gemsww4ojmprsd.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/dPt59MZaEXeu_JPlWh7RDKtusxUd.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/eZwFGli01gSYZ44Ebb44wZJEZbMp.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/dPt59MZaEXeu_JPlWh7RDKtusxUp.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/Dg1e3Am5YrqqMqqkIYDk9ZjR1RYd.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/eCuRoIFEDG25vAewgv-nf3GqNI0d.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/l0P1VTKdxzsxQxR0_jCuzcfb2UEp.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/eCuRoIFEDG25vAewgv-nf3GqNI0p.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/G6IjDtcuN86KCffyBg9hhLwxfM8d.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/iYmVif7LRRnGYE-Zeun_xHMvrVUd.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/oaOhkesibNoJGQ_fNXCCFrNYNekp.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/iYmVif7LRRnGYE-Zeun_xHMvrVUp.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/K2LUhs-8CtBjbprCCg-2KajWW0Qd.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/iqU8I5FyZGvsOpUbA1lqu65aWYsd.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/jCqmmsqwCvwa4Nzg0x8RYjbjld8p.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/iqU8I5FyZGvsOpUbA1lqu65aWYsp.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/LK5LTNCFyhwaQZUlosrnunT2xZId.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/j6pwo65gaiPoea-wYOGH4DewgEcd.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/smVcGh3dB4hFNj3JUwzxfAKA5NQp.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/j6pwo65gaiPoea-wYOGH4DewgEcp.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/LN9DpJcyR6CE_I14ywn3ncJAeXId.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/lsw_ecgYyohO6CkqAqCk2bJqNuMd.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/P6lyIvtEaIp3hBgXmHLrvNf9nhop.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/lsw_ecgYyohO6CkqAqCk2bJqNuMp.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/MgsQiSKNdqjh2tb2GSg9xpGhlMgd.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/ny2aRTbOyjYb8JDFd4dItADtN4Id.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/mZYgr5isIaGLkE3fNy5zsSlr_38p.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/ny2aRTbOyjYb8JDFd4dItADtN4Ip.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/P6lyIvtEaIp3hBgXmHLrvNf9nhod.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/oLERgIPUw52TmzFNtvUaq1v3Hykd.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/1AUPZjF-DkxHBgstLlITifCjfHAp.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/oLERgIPUw52TmzFNtvUaq1v3Hykp.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/R9uo8TWlIFmDZ_U2L3brdMMNpmUd.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/pegGFSZoGR96d4YxbbK-T9-uicsd.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/-3QAULuhtItSD7_pHpYyutizqKcp.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/pegGFSZoGR96d4YxbbK-T9-uicsp.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/Rgu_Xpn3p1FJCz3744RsTXGnSX4d.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/qMOfCxA6oAYXN36hOE_emzY7-K4d.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/BNAgggSUW1I0e_gemsww4ojmprsp.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/qMOfCxA6oAYXN36hOE_emzY7-K4p.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/SS1oWs8uWRjwpAsBEkjHKIdli3od.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/sgs-V8op5u7eBxQMg_dpYIMx_r0d.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/K2LUhs-8CtBjbprCCg-2KajWW0Qp.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/sgs-V8op5u7eBxQMg_dpYIMx_r0p.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/T2fYvrqhpvDyuKc8Y7wsu2vaPcUd.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/wlzHFGaUXvZky7Iz4g5QWWPjTZod.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/YAj-Sg2BTfUPlNkFYhjGuYYGyWIp.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/wlzHFGaUXvZky7Iz4g5QWWPjTZop.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/TcR3rZOrVrHqDGA6Ls0qBpAOHD0d.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/xjfA1VBG4pGbE5V9ER3DoEdSXfcd.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/lrtTrV3gL6zhRL5HQl_mNRj1jiIp.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/xjfA1VBG4pGbE5V9ER3DoEdSXfcp.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/Vfp2GuJhjHgccgR8Joly_nEH3uUd.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/zE7jRze1WGVc9GxZNzvBJKkpyN0d.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/YhkhMqMTT1rlsumwiMqdjRVcg2Qp.xml => SJuj0lQEL2kv8DN93wpDxprjL2Q/zE7jRze1WGVc9GxZNzvBJKkpyN0p.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/XeBJu8m_oaoL52pMHUpwH6lM5Fod.xml => TMR2PLz8-jf_t72xY3w-eiAkMwc/3HnVzJ8uifpbOyg21-U4RCJN4GQd.xml} (100%) rename resources/project/{Drp6kay-WW1Z2AQWI8KW60j9HlQ/888MRa7OWq3oIOyCKPp0I2UIiKAp.xml => TMR2PLz8-jf_t72xY3w-eiAkMwc/3HnVzJ8uifpbOyg21-U4RCJN4GQp.xml} (100%) rename resources/project/{NAFZMRUtFQYPd30MjCtFUUPa5w4/rfOlYVfPM98QhiJbvbuozkKdZQkd.xml => TMR2PLz8-jf_t72xY3w-eiAkMwc/7zdx9TfzV1uKVmlJRWzGdJyAcSYd.xml} (100%) rename resources/project/{NAFZMRUtFQYPd30MjCtFUUPa5w4/rfOlYVfPM98QhiJbvbuozkKdZQkp.xml => TMR2PLz8-jf_t72xY3w-eiAkMwc/7zdx9TfzV1uKVmlJRWzGdJyAcSYp.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/YAj-Sg2BTfUPlNkFYhjGuYYGyWId.xml => TMR2PLz8-jf_t72xY3w-eiAkMwc/DAP2fOCUoOsvQKZryp7QTWtvdegd.xml} (100%) rename resources/project/{Drp6kay-WW1Z2AQWI8KW60j9HlQ/zRzAsvX6Os0bPYij1y_5pG-94Dgp.xml => TMR2PLz8-jf_t72xY3w-eiAkMwc/DAP2fOCUoOsvQKZryp7QTWtvdegp.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/YhkhMqMTT1rlsumwiMqdjRVcg2Qd.xml => TMR2PLz8-jf_t72xY3w-eiAkMwc/ccZjrHRR1lFL21N1ZWdseSM37NQd.xml} (100%) rename resources/project/{Drp6kay-WW1Z2AQWI8KW60j9HlQ/6GJfsBaU_5DGhbimCNcp3Txx-v8p.xml => TMR2PLz8-jf_t72xY3w-eiAkMwc/ccZjrHRR1lFL21N1ZWdseSM37NQp.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/ZqK39Xv3A0I-0ohwRkWV6jbAW9Md.xml => TMR2PLz8-jf_t72xY3w-eiAkMwc/omg-Qmyr2Hpg5ztIiWxX0q9_f_sd.xml} (100%) rename resources/project/{Drp6kay-WW1Z2AQWI8KW60j9HlQ/iPx7LMwL8D8SvprpBzhO3qxwy0Ip.xml => TMR2PLz8-jf_t72xY3w-eiAkMwc/omg-Qmyr2Hpg5ztIiWxX0q9_f_sp.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/aSbr7z_N-_xgJQSr5hrZLyKPz4cd.xml => TMR2PLz8-jf_t72xY3w-eiAkMwc/rNLU-8Md7l99hBksJZlE3as2Y98d.xml} (100%) rename resources/project/{Drp6kay-WW1Z2AQWI8KW60j9HlQ/HinGYaWsjaNlqTK_j5cQYUlmn_4p.xml => TMR2PLz8-jf_t72xY3w-eiAkMwc/rNLU-8Md7l99hBksJZlE3as2Y98p.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/aj3y-ODStp8yUzt7MUXbgCsT1OYd.xml => UeukJVHJUwLZ1oSw-yBfMdSLcNI/8FB9zoKe0-Lw7d_EeJv8WYCX-50d.xml} (100%) rename resources/project/{Hk1Vip6VmTG9gpBtNOXKEU56oUc/YDXlBTZHLxkb4xHzi3Nh_OCFRwYp.xml => UeukJVHJUwLZ1oSw-yBfMdSLcNI/8FB9zoKe0-Lw7d_EeJv8WYCX-50p.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/eZwFGli01gSYZ44Ebb44wZJEZbMd.xml => UeukJVHJUwLZ1oSw-yBfMdSLcNI/90YW2GOol29Apu9oHzv3q7eI8ZMd.xml} (100%) rename resources/project/{Hk1Vip6VmTG9gpBtNOXKEU56oUc/418XLOPub0IQmO32_fwbHEZ9aWQp.xml => UeukJVHJUwLZ1oSw-yBfMdSLcNI/90YW2GOol29Apu9oHzv3q7eI8ZMp.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/j0ffgGF4ix7R_1weXjtRLJ2m60od.xml => UeukJVHJUwLZ1oSw-yBfMdSLcNI/F1UMXHKWKyyeAiNCKU0ON-Vy2ugd.xml} (100%) rename resources/project/{Hk1Vip6VmTG9gpBtNOXKEU56oUc/jL11JwpwvA44_BZZe0gbxo4n_7cp.xml => UeukJVHJUwLZ1oSw-yBfMdSLcNI/F1UMXHKWKyyeAiNCKU0ON-Vy2ugp.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/jCqmmsqwCvwa4Nzg0x8RYjbjld8d.xml => UeukJVHJUwLZ1oSw-yBfMdSLcNI/SkcnLeu8zuRRgbMYy4nnaoFftigd.xml} (100%) rename resources/project/{Hk1Vip6VmTG9gpBtNOXKEU56oUc/zAMhJ-uBIliAXbSkNK3rdwXDaHop.xml => UeukJVHJUwLZ1oSw-yBfMdSLcNI/SkcnLeu8zuRRgbMYy4nnaoFftigp.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/l0P1VTKdxzsxQxR0_jCuzcfb2UEd.xml => UeukJVHJUwLZ1oSw-yBfMdSLcNI/X3U50T46GsNoWWkV12vFPTU5IkAd.xml} (100%) rename resources/project/{Hk1Vip6VmTG9gpBtNOXKEU56oUc/anHfLHy8dClzx6n_jtHyG6OnAKgp.xml => UeukJVHJUwLZ1oSw-yBfMdSLcNI/X3U50T46GsNoWWkV12vFPTU5IkAp.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/lrtTrV3gL6zhRL5HQl_mNRj1jiId.xml => UeukJVHJUwLZ1oSw-yBfMdSLcNI/_p4C4Xaqbc2CySSpIr_XPlz39_8d.xml} (100%) rename resources/project/{Hk1Vip6VmTG9gpBtNOXKEU56oUc/fEvuKgmkueTDlYz7GM2hOxML--sp.xml => UeukJVHJUwLZ1oSw-yBfMdSLcNI/_p4C4Xaqbc2CySSpIr_XPlz39_8p.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/mZYgr5isIaGLkE3fNy5zsSlr_38d.xml => UeukJVHJUwLZ1oSw-yBfMdSLcNI/bApZCS4bpdgHa0xKhfxkSKy3kIcd.xml} (100%) rename resources/project/{Hk1Vip6VmTG9gpBtNOXKEU56oUc/AikK6lr_xPm04OOxDRyD3uEU6LYp.xml => UeukJVHJUwLZ1oSw-yBfMdSLcNI/bApZCS4bpdgHa0xKhfxkSKy3kIcp.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/n6yybQJW-SCbwLJB8CxlywM5r38d.xml => UeukJVHJUwLZ1oSw-yBfMdSLcNI/dC8vrHmGxRVrn1Vw7pYBaCb25tUd.xml} (100%) rename resources/project/{Hk1Vip6VmTG9gpBtNOXKEU56oUc/upiRJjWx9gByawb3bF6KtCjux-sp.xml => UeukJVHJUwLZ1oSw-yBfMdSLcNI/dC8vrHmGxRVrn1Vw7pYBaCb25tUp.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/oaOhkesibNoJGQ_fNXCCFrNYNekd.xml => UeukJVHJUwLZ1oSw-yBfMdSLcNI/slGRI8GHw3J7mTWp953EhtGCuB8d.xml} (100%) rename resources/project/{Hk1Vip6VmTG9gpBtNOXKEU56oUc/7YGtSs8Fx6KVMJ6VnXcbLgutukUp.xml => UeukJVHJUwLZ1oSw-yBfMdSLcNI/slGRI8GHw3J7mTWp953EhtGCuB8p.xml} (100%) rename resources/project/{OLuX-8c-UOqeJnrM2amm241SKTs/NVEzjmOMA7-YV0Y8D0XmF7N5Qe4d.xml => UeukJVHJUwLZ1oSw-yBfMdSLcNI/uIWV6l4leGqpetubaJSYVYzic4Ad.xml} (100%) rename resources/project/{OLuX-8c-UOqeJnrM2amm241SKTs/NVEzjmOMA7-YV0Y8D0XmF7N5Qe4p.xml => UeukJVHJUwLZ1oSw-yBfMdSLcNI/uIWV6l4leGqpetubaJSYVYzic4Ap.xml} (100%) rename resources/project/{HV6_iJQOFZrKU26X_hF7trjvkeM/smVcGh3dB4hFNj3JUwzxfAKA5NQd.xml => UeukJVHJUwLZ1oSw-yBfMdSLcNI/wbJvniNn9pHSMYYk4-SDjMEBinAd.xml} (100%) rename resources/project/{Hk1Vip6VmTG9gpBtNOXKEU56oUc/2ReW6jdy5OFPurN6zA_O_5XHgZ4p.xml => UeukJVHJUwLZ1oSw-yBfMdSLcNI/wbJvniNn9pHSMYYk4-SDjMEBinAp.xml} (100%) delete mode 100644 resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/4lF9gcxRF1S305BdGqOMAHZcYgop.xml delete mode 100644 resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/BXX9SqJTGGGO_CDj2AJCCAtV84kp.xml delete mode 100644 resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/BkckfovEtaH9G6qBgDPgKm8zKxgp.xml delete mode 100644 resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/EIE_vdm0VClM0NZZV517-DBaczEp.xml delete mode 100644 resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/IWnqwSbZEiavwf7_s2jeWFKRUyMp.xml delete mode 100644 resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/WeWWFR_CW_fJH3v0Spn0rCmP6ccp.xml delete mode 100644 resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/Z_VaFjtshHcOoAprv8kGzuWPan8p.xml delete mode 100644 resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/lKVscSUfLpUJ7DMGKxZpl2cj8Cwp.xml delete mode 100644 resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/sbvxc69GTsaxWR2ghAIgyxsRoK0p.xml delete mode 100644 resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/u9fylSJ1j_4ckhUg5rn809_XS8cd.xml delete mode 100644 resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/znnoG0GBXl0gWrZCcSzmlPlsYO4d.xml delete mode 100644 resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/znnoG0GBXl0gWrZCcSzmlPlsYO4p.xml rename resources/project/{R6m7AxFfSX7StHtVJ9WG44h96GM/D7EGYpvIiO_nGXQuTOYjsSP_JQwd.xml => alAG1z_nIzafOha6mXt5pH8WAmg/BlsnL99CKaby8ck6n_pFKfx8TOEd.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/REL0fj6PT2tNiayKyTrZEt9--acp.xml => alAG1z_nIzafOha6mXt5pH8WAmg/BlsnL99CKaby8ck6n_pFKfx8TOEp.xml} (100%) rename resources/project/{Hk1Vip6VmTG9gpBtNOXKEU56oUc/2ReW6jdy5OFPurN6zA_O_5XHgZ4d.xml => alAG1z_nIzafOha6mXt5pH8WAmg/F3Ymj-J16s_9PdjyJ8NmuydN3yId.xml} (100%) rename resources/project/{D7EGYpvIiO_nGXQuTOYjsSP_JQw/DpnGV8GaSUAjtUgrpQ5nUXOT5wYp.xml => alAG1z_nIzafOha6mXt5pH8WAmg/F3Ymj-J16s_9PdjyJ8NmuydN3yIp.xml} (100%) rename resources/project/{Hk1Vip6VmTG9gpBtNOXKEU56oUc/418XLOPub0IQmO32_fwbHEZ9aWQd.xml => alAG1z_nIzafOha6mXt5pH8WAmg/GcqEp1PedF_qduXiylZH_t2MP1Qd.xml} (100%) rename resources/project/{D7EGYpvIiO_nGXQuTOYjsSP_JQw/J8XPAkZbsecOIp7IVKNIsrQZCHEp.xml => alAG1z_nIzafOha6mXt5pH8WAmg/GcqEp1PedF_qduXiylZH_t2MP1Qp.xml} (100%) rename resources/project/{Hk1Vip6VmTG9gpBtNOXKEU56oUc/7YGtSs8Fx6KVMJ6VnXcbLgutukUd.xml => alAG1z_nIzafOha6mXt5pH8WAmg/NiQW_7jWHSEL9pyWwCHpUX03D48d.xml} (100%) rename resources/project/{D7EGYpvIiO_nGXQuTOYjsSP_JQw/PUXt85kSoxl2GhAVUeaGOA1ui0sp.xml => alAG1z_nIzafOha6mXt5pH8WAmg/NiQW_7jWHSEL9pyWwCHpUX03D48p.xml} (100%) rename resources/project/{Hk1Vip6VmTG9gpBtNOXKEU56oUc/AikK6lr_xPm04OOxDRyD3uEU6LYd.xml => alAG1z_nIzafOha6mXt5pH8WAmg/c9tjuQNdCrSrZPk-bi5TqtaIYCkd.xml} (100%) rename resources/project/{D7EGYpvIiO_nGXQuTOYjsSP_JQw/Nh99zm6VyLbCagFVKH0kx0irGckp.xml => alAG1z_nIzafOha6mXt5pH8WAmg/c9tjuQNdCrSrZPk-bi5TqtaIYCkp.xml} (100%) rename resources/project/{Hk1Vip6VmTG9gpBtNOXKEU56oUc/YDXlBTZHLxkb4xHzi3Nh_OCFRwYd.xml => alAG1z_nIzafOha6mXt5pH8WAmg/iCK3aCczRLHuNzxcr2qrQVK9PZ8d.xml} (100%) rename resources/project/{D7EGYpvIiO_nGXQuTOYjsSP_JQw/zWhjsX1e0jX9kixjwjfDU_xQo9Qp.xml => alAG1z_nIzafOha6mXt5pH8WAmg/iCK3aCczRLHuNzxcr2qrQVK9PZ8p.xml} (100%) rename resources/project/{Hk1Vip6VmTG9gpBtNOXKEU56oUc/anHfLHy8dClzx6n_jtHyG6OnAKgd.xml => alAG1z_nIzafOha6mXt5pH8WAmg/rt7-0zPCi_bp1caPhGK26hxMFlgd.xml} (100%) rename resources/project/{D7EGYpvIiO_nGXQuTOYjsSP_JQw/5TVbg-DxiEwvaH5Bj9CH0ewcKu8p.xml => alAG1z_nIzafOha6mXt5pH8WAmg/rt7-0zPCi_bp1caPhGK26hxMFlgp.xml} (100%) rename resources/project/{Hk1Vip6VmTG9gpBtNOXKEU56oUc/fEvuKgmkueTDlYz7GM2hOxML--sd.xml => alAG1z_nIzafOha6mXt5pH8WAmg/ur9Ezr2ljVCKuJkzloOS3DSlTPQd.xml} (100%) rename resources/project/{D7EGYpvIiO_nGXQuTOYjsSP_JQw/SLN8kBxZYH73a64wQLgTpFlpH-Up.xml => alAG1z_nIzafOha6mXt5pH8WAmg/ur9Ezr2ljVCKuJkzloOS3DSlTPQp.xml} (100%) rename resources/project/{Hk1Vip6VmTG9gpBtNOXKEU56oUc/jL11JwpwvA44_BZZe0gbxo4n_7cd.xml => d52ncQukW_9fnTuQsihy5s3Rp9k/1U6jA8VW8gquO5tfdlwgKz-0Kjsd.xml} (100%) rename resources/project/{NAFZMRUtFQYPd30MjCtFUUPa5w4/1Fsij9kn0jCc5z8BpveJ5Pixotwp.xml => d52ncQukW_9fnTuQsihy5s3Rp9k/1U6jA8VW8gquO5tfdlwgKz-0Kjsp.xml} (100%) rename resources/project/{Hk1Vip6VmTG9gpBtNOXKEU56oUc/upiRJjWx9gByawb3bF6KtCjux-sd.xml => d52ncQukW_9fnTuQsihy5s3Rp9k/4tnQjmzGbGyuuipQczDui-LkM4Yd.xml} (100%) rename resources/project/{NAFZMRUtFQYPd30MjCtFUUPa5w4/dnwHH5dkTAu93LmDUIIdJRW11zUp.xml => d52ncQukW_9fnTuQsihy5s3Rp9k/4tnQjmzGbGyuuipQczDui-LkM4Yp.xml} (100%) rename resources/project/{Hk1Vip6VmTG9gpBtNOXKEU56oUc/zAMhJ-uBIliAXbSkNK3rdwXDaHod.xml => d52ncQukW_9fnTuQsihy5s3Rp9k/GuR32HWNIsfL9QJxccN3h7ueHWUd.xml} (100%) rename resources/project/{NAFZMRUtFQYPd30MjCtFUUPa5w4/29H25KnJzx-eH6hkyDOSNHx0NX0p.xml => d52ncQukW_9fnTuQsihy5s3Rp9k/GuR32HWNIsfL9QJxccN3h7ueHWUp.xml} (100%) rename resources/project/{R6m7AxFfSX7StHtVJ9WG44h96GM/Drp6kay-WW1Z2AQWI8KW60j9HlQd.xml => d52ncQukW_9fnTuQsihy5s3Rp9k/N0H1Yk2c0hrEVlplassPmyyQIkgd.xml} (100%) rename resources/project/{dYvx6wp33Ts8hX1S_rzdiQ3NP4A/kRJYdCzyq5zNeYLPJeKdZmiLSJEp.xml => d52ncQukW_9fnTuQsihy5s3Rp9k/N0H1Yk2c0hrEVlplassPmyyQIkgp.xml} (100%) rename resources/project/{NAFZMRUtFQYPd30MjCtFUUPa5w4/1Fsij9kn0jCc5z8BpveJ5Pixotwd.xml => d52ncQukW_9fnTuQsihy5s3Rp9k/ZhQd5_-iD72EYvyetRmslkcILwgd.xml} (100%) rename resources/project/{NAFZMRUtFQYPd30MjCtFUUPa5w4/qkgsXBKP6tNxSqIGkUXqoIOmfAYp.xml => d52ncQukW_9fnTuQsihy5s3Rp9k/ZhQd5_-iD72EYvyetRmslkcILwgp.xml} (100%) rename resources/project/{NAFZMRUtFQYPd30MjCtFUUPa5w4/29H25KnJzx-eH6hkyDOSNHx0NX0d.xml => d52ncQukW_9fnTuQsihy5s3Rp9k/_DTS8noz4eF6eofLBJF_kJI36Rsd.xml} (100%) rename resources/project/{NAFZMRUtFQYPd30MjCtFUUPa5w4/3zKgrWYZ4jPOoOXQS_i9ShO4XZcp.xml => d52ncQukW_9fnTuQsihy5s3Rp9k/_DTS8noz4eF6eofLBJF_kJI36Rsp.xml} (100%) rename resources/project/{NAFZMRUtFQYPd30MjCtFUUPa5w4/2NE3JJ7Cwpfj1Twd1MZdwGK46R8d.xml => d52ncQukW_9fnTuQsihy5s3Rp9k/ctkpfB9aQsWsqTtrG0AiSWOSsMgd.xml} (100%) rename resources/project/{NAFZMRUtFQYPd30MjCtFUUPa5w4/cfs6_1u5uuKLLIe6KzyVp8pn_mUp.xml => d52ncQukW_9fnTuQsihy5s3Rp9k/ctkpfB9aQsWsqTtrG0AiSWOSsMgp.xml} (100%) rename resources/project/{NAFZMRUtFQYPd30MjCtFUUPa5w4/3L_f_vjNe-AFLP06A5juF8kMGLEd.xml => d52ncQukW_9fnTuQsihy5s3Rp9k/jaWSShTlT3hy9m_6EXad-HfUEqEd.xml} (100%) rename resources/project/{NAFZMRUtFQYPd30MjCtFUUPa5w4/3L_f_vjNe-AFLP06A5juF8kMGLEp.xml => d52ncQukW_9fnTuQsihy5s3Rp9k/jaWSShTlT3hy9m_6EXad-HfUEqEp.xml} (100%) rename resources/project/{NAFZMRUtFQYPd30MjCtFUUPa5w4/3zKgrWYZ4jPOoOXQS_i9ShO4XZcd.xml => d52ncQukW_9fnTuQsihy5s3Rp9k/se_u97Hhf2JBUhc4KXrIUB6oWjUd.xml} (100%) rename resources/project/{NAFZMRUtFQYPd30MjCtFUUPa5w4/rnnM4E9PpZDSGfoRKAwKIIqTIq0p.xml => d52ncQukW_9fnTuQsihy5s3Rp9k/se_u97Hhf2JBUhc4KXrIUB6oWjUp.xml} (100%) rename resources/project/{NAFZMRUtFQYPd30MjCtFUUPa5w4/a95L4gsH7xba014Rtg5I-RtazfUd.xml => d52ncQukW_9fnTuQsihy5s3Rp9k/vScIuMZR1UoxCROtefKx0SSqHksd.xml} (100%) rename resources/project/{NAFZMRUtFQYPd30MjCtFUUPa5w4/a95L4gsH7xba014Rtg5I-RtazfUp.xml => d52ncQukW_9fnTuQsihy5s3Rp9k/vScIuMZR1UoxCROtefKx0SSqHksp.xml} (100%) rename resources/project/{NAFZMRUtFQYPd30MjCtFUUPa5w4/cfs6_1u5uuKLLIe6KzyVp8pn_mUd.xml => d52ncQukW_9fnTuQsihy5s3Rp9k/xBDRN0xkCxYYkWS1cMcY9ajEQ5od.xml} (100%) rename resources/project/{NAFZMRUtFQYPd30MjCtFUUPa5w4/2NE3JJ7Cwpfj1Twd1MZdwGK46R8p.xml => d52ncQukW_9fnTuQsihy5s3Rp9k/xBDRN0xkCxYYkWS1cMcY9ajEQ5op.xml} (100%) delete mode 100644 resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/7U58VHMhrmTgIuozFzLR1XBJo8Id.xml delete mode 100644 resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/9ULi2Alw-JmXjkCxz2ppBf5D2nUd.xml delete mode 100644 resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/9ULi2Alw-JmXjkCxz2ppBf5D2nUp.xml delete mode 100644 resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/9orjn84dnpAKkkuchjb6ZXb4lYod.xml delete mode 100644 resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/Ckzn7bzQCXjaNEGiflifGGEk0-Ad.xml delete mode 100644 resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/Ckzn7bzQCXjaNEGiflifGGEk0-Ap.xml delete mode 100644 resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/Fuv4y78db8mLAjl6Fcl6HGZDRo0d.xml delete mode 100644 resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/Fuv4y78db8mLAjl6Fcl6HGZDRo0p.xml delete mode 100644 resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/Q_eYts4Jx8lwh_76CdRDIxbbdlkd.xml delete mode 100644 resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/T3r09B-NxK1oaHW6j-rPgd2Qj6Ud.xml delete mode 100644 resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/T3r09B-NxK1oaHW6j-rPgd2Qj6Up.xml delete mode 100644 resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/WtpDLoJHlDx008KY8cwkBcD2AT8d.xml delete mode 100644 resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/WtpDLoJHlDx008KY8cwkBcD2AT8p.xml delete mode 100644 resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/XbzuDmDFV-LdTDKCBgtbIoNIeDAd.xml delete mode 100644 resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/XbzuDmDFV-LdTDKCBgtbIoNIeDAp.xml delete mode 100644 resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/aN6B9nVEXMPwk5nipy2pReqfe9kd.xml delete mode 100644 resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/aN6B9nVEXMPwk5nipy2pReqfe9kp.xml delete mode 100644 resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/dKRagt73aqMStWRIRrbSi8CTiWEd.xml delete mode 100644 resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/dKRagt73aqMStWRIRrbSi8CTiWEp.xml delete mode 100644 resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/jjYwKTXjk1ClTK05aLas9VzF5YAd.xml delete mode 100644 resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/jjYwKTXjk1ClTK05aLas9VzF5YAp.xml delete mode 100644 resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/vok99kMS3Be0cBX4FPi-Zspy6Y0d.xml rename resources/project/{R6m7AxFfSX7StHtVJ9WG44h96GM/dYvx6wp33Ts8hX1S_rzdiQ3NP4Ad.xml => fkKGPyMFLRd9q8sWiOz4qApnVgk/1BSDFN_bp_8Wq1u5OO4MK7xHWFEd.xml} (100%) rename resources/project/{ieR1AbFuIuiTNyC8z83kLz-KjxQ/QgeUCp8CiwXPaszmjk6YesnqDGkp.xml => fkKGPyMFLRd9q8sWiOz4qApnVgk/1BSDFN_bp_8Wq1u5OO4MK7xHWFEp.xml} (100%) rename resources/project/{NAFZMRUtFQYPd30MjCtFUUPa5w4/dnwHH5dkTAu93LmDUIIdJRW11zUd.xml => fkKGPyMFLRd9q8sWiOz4qApnVgk/5mzsc4L1QMwcGyCp10S_0jXV0UYd.xml} (100%) rename resources/project/{ieR1AbFuIuiTNyC8z83kLz-KjxQ/ogNzaRYtwam4lR-ZN7eI6m_SXeYp.xml => fkKGPyMFLRd9q8sWiOz4qApnVgk/5mzsc4L1QMwcGyCp10S_0jXV0UYp.xml} (100%) rename resources/project/{NAFZMRUtFQYPd30MjCtFUUPa5w4/qkgsXBKP6tNxSqIGkUXqoIOmfAYd.xml => fkKGPyMFLRd9q8sWiOz4qApnVgk/nYxqFWWUsPUVCBz8zZlw7QYCOvod.xml} (100%) rename resources/project/{ieR1AbFuIuiTNyC8z83kLz-KjxQ/gU6nQ9al6ASHLbEF0HmBERcYd4Mp.xml => fkKGPyMFLRd9q8sWiOz4qApnVgk/nYxqFWWUsPUVCBz8zZlw7QYCOvop.xml} (100%) rename resources/project/{NAFZMRUtFQYPd30MjCtFUUPa5w4/rnnM4E9PpZDSGfoRKAwKIIqTIq0d.xml => fkKGPyMFLRd9q8sWiOz4qApnVgk/uR7UAO4rsdHCU9T8lZjoYW7a32Ud.xml} (100%) rename resources/project/{ieR1AbFuIuiTNyC8z83kLz-KjxQ/xmaLmb7wr2-EmOlPBo1ae7Q1eEwp.xml => fkKGPyMFLRd9q8sWiOz4qApnVgk/uR7UAO4rsdHCU9T8lZjoYW7a32Up.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/7AZXpb090n98KOSsx01-zOUfRQgd.xml => gK_nb01P4_6XSSogizZJn5zmzQQ/PL5Ga2JyphJjO2f0V1lt1v9mOfQd.xml} (100%) rename resources/project/{oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/wTRZ9nfLywZT2m22SOEZoykCg4Qd.xml => gK_nb01P4_6XSSogizZJn5zmzQQ/PL5Ga2JyphJjO2f0V1lt1v9mOfQp.xml} (53%) delete mode 100644 resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4p.xml delete mode 100644 resources/project/ieR1AbFuIuiTNyC8z83kLz-KjxQ/gU6nQ9al6ASHLbEF0HmBERcYd4Md.xml delete mode 100644 resources/project/ieR1AbFuIuiTNyC8z83kLz-KjxQ/ogNzaRYtwam4lR-ZN7eI6m_SXeYd.xml delete mode 100644 resources/project/ieR1AbFuIuiTNyC8z83kLz-KjxQ/xmaLmb7wr2-EmOlPBo1ae7Q1eEwd.xml rename resources/project/{OLuX-8c-UOqeJnrM2amm241SKTs/-xMsha79hmE0qOhn3z2Y4wwJaNkd.xml => o2cKHtbeLCI5dGNR0Lwpzdptr4s/-IEsxSr0pUR0RE2Q2b5wpQX0KkEd.xml} (100%) create mode 100644 resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/-IEsxSr0pUR0RE2Q2b5wpQX0KkEp.xml rename resources/project/{OLuX-8c-UOqeJnrM2amm241SKTs/0oyoesBN2_oayH0i7gtHJp890Esd.xml => o2cKHtbeLCI5dGNR0Lwpzdptr4s/3DhOOe0Tl7hipLJtC66oxocIr9cd.xml} (100%) create mode 100644 resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/3DhOOe0Tl7hipLJtC66oxocIr9cp.xml rename resources/project/{OLuX-8c-UOqeJnrM2amm241SKTs/G2x_hSFRzJNj2RwsZ4N6TWYsHmYd.xml => o2cKHtbeLCI5dGNR0Lwpzdptr4s/AxKLxQn_Q5ExtyTFauEpYntfsssd.xml} (100%) create mode 100644 resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/AxKLxQn_Q5ExtyTFauEpYntfsssp.xml rename resources/project/{OLuX-8c-UOqeJnrM2amm241SKTs/T9wikiXK6MjHu7XyyDA0kuCTP_kd.xml => o2cKHtbeLCI5dGNR0Lwpzdptr4s/IDetplA4BCvExeTDnnCPfg2MhBwd.xml} (100%) create mode 100644 resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/IDetplA4BCvExeTDnnCPfg2MhBwp.xml rename resources/project/{OLuX-8c-UOqeJnrM2amm241SKTs/jupI72lBOnUNFR-WjgobwXS0zMod.xml => o2cKHtbeLCI5dGNR0Lwpzdptr4s/QqOZDkdIGwDJT1oKEd1m5cJ1l88d.xml} (100%) create mode 100644 resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/QqOZDkdIGwDJT1oKEd1m5cJ1l88p.xml rename resources/project/{OLuX-8c-UOqeJnrM2amm241SKTs/kJV53JKRAFPnFBgXx06VMpdr0ssd.xml => o2cKHtbeLCI5dGNR0Lwpzdptr4s/SJJLTV1fLQ-ZA32hIB2BTOYk5TYd.xml} (100%) create mode 100644 resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/SJJLTV1fLQ-ZA32hIB2BTOYk5TYp.xml rename resources/project/{OLuX-8c-UOqeJnrM2amm241SKTs/rx1MnRhyaD-dcP1I9WcseP2PGiwd.xml => o2cKHtbeLCI5dGNR0Lwpzdptr4s/gK1b_iZQx1Km0kq0Da4qWwP0JpUd.xml} (100%) create mode 100644 resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/gK1b_iZQx1Km0kq0Da4qWwP0JpUp.xml rename resources/project/{OLuX-8c-UOqeJnrM2amm241SKTs/wSDXMLBIRWsCGVgqLP9ER---lO4d.xml => o2cKHtbeLCI5dGNR0Lwpzdptr4s/zMWI0fKvYbz3vT7BQxp-7H6ez4wd.xml} (100%) create mode 100644 resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/zMWI0fKvYbz3vT7BQxp-7H6ez4wp.xml delete mode 100644 resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/FcqvamgK2GFBncjelWFYWpbic6sd.xml delete mode 100644 resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/Q9BY7Fa1-dMNQpWUMw61LYZlnpkd.xml delete mode 100644 resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/TMwS0Ehc1hOj1QS3kXBWqYH2cIYd.xml delete mode 100644 resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/XA-33X-Ml7fASA-h03h0NoRtubAd.xml delete mode 100644 resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/ccgGxetCHn-i1g49iWGxy2eRT9gd.xml delete mode 100644 resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/wv-PgmFd28kiUsYZVe0yPM4yMXEd.xml delete mode 100644 resources/project/pDfDXpP-F2Cd6B91DGM44psBFJw/l8FwG4TnKj8gyMwHPAtYvu1WQGUd.xml delete mode 100644 resources/project/pDfDXpP-F2Cd6B91DGM44psBFJw/zxz-H_H66S_yiImwKMo7C6xbqr0d.xml delete mode 100644 resources/project/pDfDXpP-F2Cd6B91DGM44psBFJw/zxz-H_H66S_yiImwKMo7C6xbqr0p.xml rename resources/project/{OLuX-8c-UOqeJnrM2amm241SKTs/yrvDAxHTmqAbl2v6-rWp7tgRl4Ed.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/2BNsbxotz8yS1dkQ-k4TtTtfElkd.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/u9fylSJ1j_4ckhUg5rn809_XS8cp.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/2BNsbxotz8yS1dkQ-k4TtTtfElkp.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/1uG3tiqfOuVrZXVhllRAYYH-wtod.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/41tDM4fkWcly5KHLFGBD7TnUF1kd.xml} (100%) create mode 100644 resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/41tDM4fkWcly5KHLFGBD7TnUF1kp.xml rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/BXX9SqJTGGGO_CDj2AJCCAtV84kd.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/6AGol90mQGOIatxNSyMhdaH59gAd.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/oeLLBB_jdP6pKF_DZjKp0QJVZ5Qp.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/6AGol90mQGOIatxNSyMhdaH59gAp.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/4lF9gcxRF1S305BdGqOMAHZcYgod.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/BYuUEuuSEpTxFNYwIIct-_focKkd.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/u6YyGgN6Hd7S4WFttol0-zYdc8cp.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/BYuUEuuSEpTxFNYwIIct-_focKkp.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/8N6cWgm0bCQbFtUjwVpJKMr3DlEd.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/FDdGHhZvwjpYxttUpuTOcZx3JmMd.xml} (100%) create mode 100644 resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/FDdGHhZvwjpYxttUpuTOcZx3JmMp.xml rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/BkckfovEtaH9G6qBgDPgKm8zKxgd.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/FjEfBzqcYgSPB2oA26wfyEdBqo0d.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/ojkhqvbsn_quEDp4WaQJ3vMFy2Yp.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/FjEfBzqcYgSPB2oA26wfyEdBqo0p.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/HV6_iJQOFZrKU26X_hF7trjvkeMd.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/H72L659GuP9YjS6Dt9kIH5c6HnQd.xml} (100%) rename resources/project/{R6m7AxFfSX7StHtVJ9WG44h96GM/dYvx6wp33Ts8hX1S_rzdiQ3NP4Ap.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/H72L659GuP9YjS6Dt9kIH5c6HnQp.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/D2MuV2keQQWbNaEvWpyyDv8QkMsd.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/HvUMVTzlhZFbsfNl34oO8xkwaekd.xml} (100%) create mode 100644 resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/HvUMVTzlhZFbsfNl34oO8xkwaekp.xml rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/Hk1Vip6VmTG9gpBtNOXKEU56oUcd.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/MxOdJHAcr56wMe-tpEEz-LoiQlYd.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/OLuX-8c-UOqeJnrM2amm241SKTsp.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/MxOdJHAcr56wMe-tpEEz-LoiQlYp.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/EIE_vdm0VClM0NZZV517-DBaczEd.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/NEI1fXIv8KRUL_rDHprZREmiW34d.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/gneWb5IhW594iwk9mWJJwtxdpyMp.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/NEI1fXIv8KRUL_rDHprZREmiW34p.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/Hp4Wu5EduwyItwUIb1uZUV98wGEd.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/NFReMHJQuHGlCss_fLSqFSIT1ZYd.xml} (100%) create mode 100644 resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/NFReMHJQuHGlCss_fLSqFSIT1ZYp.xml rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/NAFZMRUtFQYPd30MjCtFUUPa5w4d.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/PMTiY8ii3Ycb5X733RMaGCNVItAd.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/7AZXpb090n98KOSsx01-zOUfRQgp.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/PMTiY8ii3Ycb5X733RMaGCNVItAp.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/IWnqwSbZEiavwf7_s2jeWFKRUyMd.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/PQTYqrLotgfQlXwRwcTgmkIRabcd.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/gBXVGBJ76fvQ_X7J0ZQGLmXEcqEp.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/PQTYqrLotgfQlXwRwcTgmkIRabcp.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/Q55VV6m6MizdVBAaXHDg8O7if2gd.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/Q-DZerJncmEPNpWIO9dYINnubhAd.xml} (100%) create mode 100644 resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/Q-DZerJncmEPNpWIO9dYINnubhAp.xml rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/QNBeGKx711B7_RF8ns22o2sra6od.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/R08oPdcWNyOGmE1Nvdwwvs71M1kd.xml} (100%) create mode 100644 resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/R08oPdcWNyOGmE1Nvdwwvs71M1kp.xml rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/S0FtAOjO_bt3aSHMltKOmsdvzZkd.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/Rx9X9wAt2wpjFi2e7541ezE-nWwd.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/Q55VV6m6MizdVBAaXHDg8O7if2gp.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/Rx9X9wAt2wpjFi2e7541ezE-nWwp.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/OLuX-8c-UOqeJnrM2amm241SKTsd.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/SJuj0lQEL2kv8DN93wpDxprjL2Qd.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/HV6_iJQOFZrKU26X_hF7trjvkeMp.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/SJuj0lQEL2kv8DN93wpDxprjL2Qp.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/REL0fj6PT2tNiayKyTrZEt9--acd.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/TMR2PLz8-jf_t72xY3w-eiAkMwcd.xml} (100%) rename resources/project/{R6m7AxFfSX7StHtVJ9WG44h96GM/Drp6kay-WW1Z2AQWI8KW60j9HlQp.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/TMR2PLz8-jf_t72xY3w-eiAkMwcp.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/ieR1AbFuIuiTNyC8z83kLz-KjxQd.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/UeukJVHJUwLZ1oSw-yBfMdSLcNId.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/Hk1Vip6VmTG9gpBtNOXKEU56oUcp.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/UeukJVHJUwLZ1oSw-yBfMdSLcNIp.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/UPM_TyetjaXWJKkGaorucXMQrKAd.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/V4lqQmJ8gZjfEk0BSGK5rhmhQ7Qd.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/UPM_TyetjaXWJKkGaorucXMQrKAp.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/V4lqQmJ8gZjfEk0BSGK5rhmhQ7Qp.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/WeWWFR_CW_fJH3v0Spn0rCmP6ccd.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/W3r3wIq1F_aH5Bg_6WiksWHWjnsd.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/1uG3tiqfOuVrZXVhllRAYYH-wtop.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/W3r3wIq1F_aH5Bg_6WiksWHWjnsp.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/Z_VaFjtshHcOoAprv8kGzuWPan8d.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/Yzm_5cRIP1TNTAYJFwi1gFJMvrYd.xml} (100%) create mode 100644 resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/Yzm_5cRIP1TNTAYJFwi1gFJMvrYp.xml rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/gBXVGBJ76fvQ_X7J0ZQGLmXEcqEd.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/aQn5VXjSyIOSf20IuNhzQTcJKLEd.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/S0FtAOjO_bt3aSHMltKOmsdvzZkp.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/aQn5VXjSyIOSf20IuNhzQTcJKLEp.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/oeLLBB_jdP6pKF_DZjKp0QJVZ5Qd.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/alAG1z_nIzafOha6mXt5pH8WAmgd.xml} (100%) rename resources/project/{R6m7AxFfSX7StHtVJ9WG44h96GM/D7EGYpvIiO_nGXQuTOYjsSP_JQwp.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/alAG1z_nIzafOha6mXt5pH8WAmgp.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/pDfDXpP-F2Cd6B91DGM44psBFJwd.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/d52ncQukW_9fnTuQsihy5s3Rp9kd.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/NAFZMRUtFQYPd30MjCtFUUPa5w4p.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/d52ncQukW_9fnTuQsihy5s3Rp9kp.xml} (100%) rename resources/project/{dYvx6wp33Ts8hX1S_rzdiQ3NP4A/kRJYdCzyq5zNeYLPJeKdZmiLSJEd.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/fkKGPyMFLRd9q8sWiOz4qApnVgkd.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/ieR1AbFuIuiTNyC8z83kLz-KjxQp.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/fkKGPyMFLRd9q8sWiOz4qApnVgkp.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/gneWb5IhW594iwk9mWJJwtxdpyMd.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/jcRKP1GWD0s2vbmJjnRVU0UNQuEd.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/D2MuV2keQQWbNaEvWpyyDv8QkMsp.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/jcRKP1GWD0s2vbmJjnRVU0UNQuEp.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/lKVscSUfLpUJ7DMGKxZpl2cj8Cwd.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/lIsawu5M1S3Y4h7oJkYpw3jue7Ed.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/Hp4Wu5EduwyItwUIb1uZUV98wGEp.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/lIsawu5M1S3Y4h7oJkYpw3jue7Ep.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/ojkhqvbsn_quEDp4WaQJ3vMFy2Yd.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/ljel_A4AHLbljnmRTR2O3qqpy8Ed.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/QNBeGKx711B7_RF8ns22o2sra6op.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/ljel_A4AHLbljnmRTR2O3qqpy8Ep.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/sbvxc69GTsaxWR2ghAIgyxsRoK0d.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/scEHrBcR_4DaGlInftNFgaoYFTMd.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/8N6cWgm0bCQbFtUjwVpJKMr3DlEp.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/scEHrBcR_4DaGlInftNFgaoYFTMp.xml} (100%) rename resources/project/{gK_nb01P4_6XSSogizZJn5zmzQQ/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4d.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/ux2HJV4G7O8gtSZERsaoTt6GWQkd.xml} (100%) rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/pDfDXpP-F2Cd6B91DGM44psBFJwp.xml => tkAoR0wDeR9bDqF10NY7YqXFvL4/ux2HJV4G7O8gtSZERsaoTt6GWQkp.xml} (100%) delete mode 100644 resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/xcwXr-V7z1F2VlQN51Es9EbCc6sd.xml delete mode 100644 resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/xcwXr-V7z1F2VlQN51Es9EbCc6sp.xml rename resources/project/{_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/u6YyGgN6Hd7S4WFttol0-zYdc8cd.xml => ux2HJV4G7O8gtSZERsaoTt6GWQk/GNyWdCWXwy_FnEw-XS9bcAQnRkId.xml} (100%) rename resources/project/{pDfDXpP-F2Cd6B91DGM44psBFJw/l8FwG4TnKj8gyMwHPAtYvu1WQGUp.xml => ux2HJV4G7O8gtSZERsaoTt6GWQk/GNyWdCWXwy_FnEw-XS9bcAQnRkIp.xml} (100%) rename resources/project/{ieR1AbFuIuiTNyC8z83kLz-KjxQ/QgeUCp8CiwXPaszmjk6YesnqDGkd.xml => ux2HJV4G7O8gtSZERsaoTt6GWQk/STP0mSEMvKb0C99CAKfVG-FjFdUd.xml} (100%) rename resources/project/{oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/wTRZ9nfLywZT2m22SOEZoykCg4Qp.xml => ux2HJV4G7O8gtSZERsaoTt6GWQk/STP0mSEMvKb0C99CAKfVG-FjFdUp.xml} (100%) diff --git a/resources/project/7AZXpb090n98KOSsx01-zOUfRQg/DvZOqACkqxwPDMpa6RdAuXS_l1sd.xml b/resources/project/6AGol90mQGOIatxNSyMhdaH59gA/3OaAbed4F4CnFYNp5AkGzyUZHUsd.xml similarity index 100% rename from resources/project/7AZXpb090n98KOSsx01-zOUfRQg/DvZOqACkqxwPDMpa6RdAuXS_l1sd.xml rename to resources/project/6AGol90mQGOIatxNSyMhdaH59gA/3OaAbed4F4CnFYNp5AkGzyUZHUsd.xml diff --git a/resources/project/7AZXpb090n98KOSsx01-zOUfRQg/DvZOqACkqxwPDMpa6RdAuXS_l1sp.xml b/resources/project/6AGol90mQGOIatxNSyMhdaH59gA/3OaAbed4F4CnFYNp5AkGzyUZHUsp.xml similarity index 100% rename from resources/project/7AZXpb090n98KOSsx01-zOUfRQg/DvZOqACkqxwPDMpa6RdAuXS_l1sp.xml rename to resources/project/6AGol90mQGOIatxNSyMhdaH59gA/3OaAbed4F4CnFYNp5AkGzyUZHUsp.xml diff --git a/resources/project/7AZXpb090n98KOSsx01-zOUfRQg/3oidn4LRL5GwwDzzeMZiSp7A2O0d.xml b/resources/project/6AGol90mQGOIatxNSyMhdaH59gA/KjY44G7WJ_jinQPfkCzkmqHUBu8d.xml similarity index 100% rename from resources/project/7AZXpb090n98KOSsx01-zOUfRQg/3oidn4LRL5GwwDzzeMZiSp7A2O0d.xml rename to resources/project/6AGol90mQGOIatxNSyMhdaH59gA/KjY44G7WJ_jinQPfkCzkmqHUBu8d.xml diff --git a/resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/Q9BY7Fa1-dMNQpWUMw61LYZlnpkp.xml b/resources/project/6AGol90mQGOIatxNSyMhdaH59gA/KjY44G7WJ_jinQPfkCzkmqHUBu8p.xml similarity index 100% rename from resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/Q9BY7Fa1-dMNQpWUMw61LYZlnpkp.xml rename to resources/project/6AGol90mQGOIatxNSyMhdaH59gA/KjY44G7WJ_jinQPfkCzkmqHUBu8p.xml diff --git a/resources/project/7AZXpb090n98KOSsx01-zOUfRQg/LzzgRGTWkydILioLs0JPaa-Nu40d.xml b/resources/project/6AGol90mQGOIatxNSyMhdaH59gA/NFORnXmhiVQSgHA5Autj49jPftYd.xml similarity index 100% rename from resources/project/7AZXpb090n98KOSsx01-zOUfRQg/LzzgRGTWkydILioLs0JPaa-Nu40d.xml rename to resources/project/6AGol90mQGOIatxNSyMhdaH59gA/NFORnXmhiVQSgHA5Autj49jPftYd.xml diff --git a/resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/TMwS0Ehc1hOj1QS3kXBWqYH2cIYp.xml b/resources/project/6AGol90mQGOIatxNSyMhdaH59gA/NFORnXmhiVQSgHA5Autj49jPftYp.xml similarity index 100% rename from resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/TMwS0Ehc1hOj1QS3kXBWqYH2cIYp.xml rename to resources/project/6AGol90mQGOIatxNSyMhdaH59gA/NFORnXmhiVQSgHA5Autj49jPftYp.xml diff --git a/resources/project/7AZXpb090n98KOSsx01-zOUfRQg/gDdlibUiOY4zsCI36J4PKMPypIId.xml b/resources/project/6AGol90mQGOIatxNSyMhdaH59gA/oHpXnrNixlZjK9qYnwuV6eX9gfcd.xml similarity index 100% rename from resources/project/7AZXpb090n98KOSsx01-zOUfRQg/gDdlibUiOY4zsCI36J4PKMPypIId.xml rename to resources/project/6AGol90mQGOIatxNSyMhdaH59gA/oHpXnrNixlZjK9qYnwuV6eX9gfcd.xml diff --git a/resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/FcqvamgK2GFBncjelWFYWpbic6sp.xml b/resources/project/6AGol90mQGOIatxNSyMhdaH59gA/oHpXnrNixlZjK9qYnwuV6eX9gfcp.xml similarity index 100% rename from resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/FcqvamgK2GFBncjelWFYWpbic6sp.xml rename to resources/project/6AGol90mQGOIatxNSyMhdaH59gA/oHpXnrNixlZjK9qYnwuV6eX9gfcp.xml diff --git a/resources/project/7AZXpb090n98KOSsx01-zOUfRQg/tfTJ4kPklZp2baElxH8WVDN7PYkd.xml b/resources/project/6AGol90mQGOIatxNSyMhdaH59gA/sgdsvhsCbARvZJ55oWpjfMFPi3Qd.xml similarity index 100% rename from resources/project/7AZXpb090n98KOSsx01-zOUfRQg/tfTJ4kPklZp2baElxH8WVDN7PYkd.xml rename to resources/project/6AGol90mQGOIatxNSyMhdaH59gA/sgdsvhsCbARvZJ55oWpjfMFPi3Qd.xml diff --git a/resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/ccgGxetCHn-i1g49iWGxy2eRT9gp.xml b/resources/project/6AGol90mQGOIatxNSyMhdaH59gA/sgdsvhsCbARvZJ55oWpjfMFPi3Qp.xml similarity index 100% rename from resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/ccgGxetCHn-i1g49iWGxy2eRT9gp.xml rename to resources/project/6AGol90mQGOIatxNSyMhdaH59gA/sgdsvhsCbARvZJ55oWpjfMFPi3Qp.xml diff --git a/resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/BWtL89f91xbulioiaYLBB_yHOgMd.xml b/resources/project/6AGol90mQGOIatxNSyMhdaH59gA/tkgt0Y0wPwlAY1DVmERxblQgvVod.xml similarity index 100% rename from resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/BWtL89f91xbulioiaYLBB_yHOgMd.xml rename to resources/project/6AGol90mQGOIatxNSyMhdaH59gA/tkgt0Y0wPwlAY1DVmERxblQgvVod.xml diff --git a/resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/wv-PgmFd28kiUsYZVe0yPM4yMXEp.xml b/resources/project/6AGol90mQGOIatxNSyMhdaH59gA/tkgt0Y0wPwlAY1DVmERxblQgvVop.xml similarity index 100% rename from resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/wv-PgmFd28kiUsYZVe0yPM4yMXEp.xml rename to resources/project/6AGol90mQGOIatxNSyMhdaH59gA/tkgt0Y0wPwlAY1DVmERxblQgvVop.xml diff --git a/resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/G0w81rMIRi8PzlrGVbGmoa0FV9gd.xml b/resources/project/6AGol90mQGOIatxNSyMhdaH59gA/zf1841WvvYarScoTMS3MPj4JW9cd.xml similarity index 100% rename from resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/G0w81rMIRi8PzlrGVbGmoa0FV9gd.xml rename to resources/project/6AGol90mQGOIatxNSyMhdaH59gA/zf1841WvvYarScoTMS3MPj4JW9cd.xml diff --git a/resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/XA-33X-Ml7fASA-h03h0NoRtubAp.xml b/resources/project/6AGol90mQGOIatxNSyMhdaH59gA/zf1841WvvYarScoTMS3MPj4JW9cp.xml similarity index 100% rename from resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/XA-33X-Ml7fASA-h03h0NoRtubAp.xml rename to resources/project/6AGol90mQGOIatxNSyMhdaH59gA/zf1841WvvYarScoTMS3MPj4JW9cp.xml diff --git a/resources/project/7AZXpb090n98KOSsx01-zOUfRQg/3oidn4LRL5GwwDzzeMZiSp7A2O0p.xml b/resources/project/7AZXpb090n98KOSsx01-zOUfRQg/3oidn4LRL5GwwDzzeMZiSp7A2O0p.xml deleted file mode 100644 index 9b194c4a7..000000000 --- a/resources/project/7AZXpb090n98KOSsx01-zOUfRQg/3oidn4LRL5GwwDzzeMZiSp7A2O0p.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/7AZXpb090n98KOSsx01-zOUfRQg/LzzgRGTWkydILioLs0JPaa-Nu40p.xml b/resources/project/7AZXpb090n98KOSsx01-zOUfRQg/LzzgRGTWkydILioLs0JPaa-Nu40p.xml deleted file mode 100644 index abdee63ae..000000000 --- a/resources/project/7AZXpb090n98KOSsx01-zOUfRQg/LzzgRGTWkydILioLs0JPaa-Nu40p.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/7AZXpb090n98KOSsx01-zOUfRQg/gDdlibUiOY4zsCI36J4PKMPypIIp.xml b/resources/project/7AZXpb090n98KOSsx01-zOUfRQg/gDdlibUiOY4zsCI36J4PKMPypIIp.xml deleted file mode 100644 index 78ae28492..000000000 --- a/resources/project/7AZXpb090n98KOSsx01-zOUfRQg/gDdlibUiOY4zsCI36J4PKMPypIIp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/7AZXpb090n98KOSsx01-zOUfRQg/tfTJ4kPklZp2baElxH8WVDN7PYkp.xml b/resources/project/7AZXpb090n98KOSsx01-zOUfRQg/tfTJ4kPklZp2baElxH8WVDN7PYkp.xml deleted file mode 100644 index 0f2885946..000000000 --- a/resources/project/7AZXpb090n98KOSsx01-zOUfRQg/tfTJ4kPklZp2baElxH8WVDN7PYkp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/2EnTnZEx-4v1jC3AJZADMDpEO1gp.xml b/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/2EnTnZEx-4v1jC3AJZADMDpEO1gp.xml deleted file mode 100644 index 0fdd569b1..000000000 --- a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/2EnTnZEx-4v1jC3AJZADMDpEO1gp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/5LEawZsxA1aBTeqG0NlpCtR6q6kp.xml b/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/5LEawZsxA1aBTeqG0NlpCtR6q6kp.xml deleted file mode 100644 index 07ec264a0..000000000 --- a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/5LEawZsxA1aBTeqG0NlpCtR6q6kp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/8uH7KtfLIR_hLVyUlkUrouETOeop.xml b/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/8uH7KtfLIR_hLVyUlkUrouETOeop.xml deleted file mode 100644 index cda33aa4d..000000000 --- a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/8uH7KtfLIR_hLVyUlkUrouETOeop.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/JlNlt7b52D5lndUS8QIFBXuyn8sp.xml b/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/JlNlt7b52D5lndUS8QIFBXuyn8sp.xml deleted file mode 100644 index dfd1493b3..000000000 --- a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/JlNlt7b52D5lndUS8QIFBXuyn8sp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/V7dcj86ZsVvV3wlDWv_EePrkW7wp.xml b/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/V7dcj86ZsVvV3wlDWv_EePrkW7wp.xml deleted file mode 100644 index b2169993b..000000000 --- a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/V7dcj86ZsVvV3wlDWv_EePrkW7wp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/XDDpmW-ahmfRGLbHVnihtWagQDEp.xml b/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/XDDpmW-ahmfRGLbHVnihtWagQDEp.xml deleted file mode 100644 index 11714313f..000000000 --- a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/XDDpmW-ahmfRGLbHVnihtWagQDEp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/rzCzLkJQsijCRt28FvlpiZrjfZ4p.xml b/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/rzCzLkJQsijCRt28FvlpiZrjfZ4p.xml deleted file mode 100644 index 4cc79d35e..000000000 --- a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/rzCzLkJQsijCRt28FvlpiZrjfZ4p.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/vyBBHUVgKzM20Od7DNtH6-JNEiAp.xml b/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/vyBBHUVgKzM20Od7DNtH6-JNEiAp.xml deleted file mode 100644 index 7421be41e..000000000 --- a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/vyBBHUVgKzM20Od7DNtH6-JNEiAp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/z8PCXMWInzrlFrKj4KQg_4fVTZYp.xml b/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/z8PCXMWInzrlFrKj4KQg_4fVTZYp.xml deleted file mode 100644 index 2bd44669c..000000000 --- a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/z8PCXMWInzrlFrKj4KQg_4fVTZYp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/0amoX63LRAKETLDqV7_F13CuX74p.xml b/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/0amoX63LRAKETLDqV7_F13CuX74p.xml deleted file mode 100644 index eeb0f2656..000000000 --- a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/0amoX63LRAKETLDqV7_F13CuX74p.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/BEHll27af-LsZgmvfXGuLyYJyU0p.xml b/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/BEHll27af-LsZgmvfXGuLyYJyU0p.xml deleted file mode 100644 index 4e459171b..000000000 --- a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/BEHll27af-LsZgmvfXGuLyYJyU0p.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/CgrwWh_FTjN531XPRoLVTVqLbZEp.xml b/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/CgrwWh_FTjN531XPRoLVTVqLbZEp.xml deleted file mode 100644 index 18ff2fdcd..000000000 --- a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/CgrwWh_FTjN531XPRoLVTVqLbZEp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/GSxmGWKoE8yVg8Dt0_WIVqZwfq8p.xml b/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/GSxmGWKoE8yVg8Dt0_WIVqZwfq8p.xml deleted file mode 100644 index d55f27558..000000000 --- a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/GSxmGWKoE8yVg8Dt0_WIVqZwfq8p.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/GmURLyn29gRlewEo8pphTKMkvfcp.xml b/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/GmURLyn29gRlewEo8pphTKMkvfcp.xml deleted file mode 100644 index bd914945a..000000000 --- a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/GmURLyn29gRlewEo8pphTKMkvfcp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/K7gRh-Ets4DP9XdM-8WQS6MYiTAp.xml b/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/K7gRh-Ets4DP9XdM-8WQS6MYiTAp.xml deleted file mode 100644 index 55aa8eaec..000000000 --- a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/K7gRh-Ets4DP9XdM-8WQS6MYiTAp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/QQxpEF0mWnNdoQybWJnETf5i2zgp.xml b/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/QQxpEF0mWnNdoQybWJnETf5i2zgp.xml deleted file mode 100644 index 1ac0501dd..000000000 --- a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/QQxpEF0mWnNdoQybWJnETf5i2zgp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/UlAa7CZxUr955fLFiY8150xe-0Yp.xml b/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/UlAa7CZxUr955fLFiY8150xe-0Yp.xml deleted file mode 100644 index 0bd256fe6..000000000 --- a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/UlAa7CZxUr955fLFiY8150xe-0Yp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/kyvnzrdUBXgtJ7QWloYIMdgg3f0p.xml b/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/kyvnzrdUBXgtJ7QWloYIMdgg3f0p.xml deleted file mode 100644 index 63f1c852b..000000000 --- a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/kyvnzrdUBXgtJ7QWloYIMdgg3f0p.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/mIYcND4GVDO0WgnXvKhJVZw4Qdsp.xml b/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/mIYcND4GVDO0WgnXvKhJVZw4Qdsp.xml deleted file mode 100644 index bcb14ce91..000000000 --- a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/mIYcND4GVDO0WgnXvKhJVZw4Qdsp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/wOcCxJrf1WxhN3mYz0ASwydFXsMp.xml b/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/wOcCxJrf1WxhN3mYz0ASwydFXsMp.xml deleted file mode 100644 index 018eb4d44..000000000 --- a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/wOcCxJrf1WxhN3mYz0ASwydFXsMp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/3SN3sghzDXjWjn7o9l7cAa0O5KAd.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/3SN3sghzDXjWjn7o9l7cAa0O5KAd.xml deleted file mode 100644 index 4d3230cf9..000000000 --- a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/3SN3sghzDXjWjn7o9l7cAa0O5KAd.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/3SN3sghzDXjWjn7o9l7cAa0O5KAp.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/3SN3sghzDXjWjn7o9l7cAa0O5KAp.xml deleted file mode 100644 index 8c0935ccf..000000000 --- a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/3SN3sghzDXjWjn7o9l7cAa0O5KAp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/E21V15DpmDB-ZQFfMLZLCGa_5lId.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/E21V15DpmDB-ZQFfMLZLCGa_5lId.xml deleted file mode 100644 index 4f400aac2..000000000 --- a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/E21V15DpmDB-ZQFfMLZLCGa_5lId.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/E21V15DpmDB-ZQFfMLZLCGa_5lIp.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/E21V15DpmDB-ZQFfMLZLCGa_5lIp.xml deleted file mode 100644 index 9f834054a..000000000 --- a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/E21V15DpmDB-ZQFfMLZLCGa_5lIp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/IjYbiW-Ya6w-T2Z7_m6dd_k13wwd.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/IjYbiW-Ya6w-T2Z7_m6dd_k13wwd.xml deleted file mode 100644 index b3c45cfa0..000000000 --- a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/IjYbiW-Ya6w-T2Z7_m6dd_k13wwd.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/IjYbiW-Ya6w-T2Z7_m6dd_k13wwp.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/IjYbiW-Ya6w-T2Z7_m6dd_k13wwp.xml deleted file mode 100644 index ba5944b36..000000000 --- a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/IjYbiW-Ya6w-T2Z7_m6dd_k13wwp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/MR_L_h1ZG2GwTSuc8zZrWnvy1Jkd.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/MR_L_h1ZG2GwTSuc8zZrWnvy1Jkd.xml deleted file mode 100644 index 65f8e3884..000000000 --- a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/MR_L_h1ZG2GwTSuc8zZrWnvy1Jkd.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/MR_L_h1ZG2GwTSuc8zZrWnvy1Jkp.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/MR_L_h1ZG2GwTSuc8zZrWnvy1Jkp.xml deleted file mode 100644 index 768f91adb..000000000 --- a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/MR_L_h1ZG2GwTSuc8zZrWnvy1Jkp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/NVX46-wyblASglEOArlOVFEQTfkd.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/NVX46-wyblASglEOArlOVFEQTfkd.xml new file mode 100644 index 000000000..c5064bb83 --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/NVX46-wyblASglEOArlOVFEQTfkd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/NVX46-wyblASglEOArlOVFEQTfkp.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/NVX46-wyblASglEOArlOVFEQTfkp.xml new file mode 100644 index 000000000..3508228fe --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/NVX46-wyblASglEOArlOVFEQTfkp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/NsgPl7VSSufo3svKEWfckxlnRO0d.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/NsgPl7VSSufo3svKEWfckxlnRO0d.xml new file mode 100644 index 000000000..806700088 --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/NsgPl7VSSufo3svKEWfckxlnRO0d.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/NsgPl7VSSufo3svKEWfckxlnRO0p.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/NsgPl7VSSufo3svKEWfckxlnRO0p.xml new file mode 100644 index 000000000..f2a646326 --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/NsgPl7VSSufo3svKEWfckxlnRO0p.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/PGaTJl94-dUtK-5YVG-7nT6UaKQd.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/PGaTJl94-dUtK-5YVG-7nT6UaKQd.xml deleted file mode 100644 index d13c0679c..000000000 --- a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/PGaTJl94-dUtK-5YVG-7nT6UaKQd.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/PGaTJl94-dUtK-5YVG-7nT6UaKQp.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/PGaTJl94-dUtK-5YVG-7nT6UaKQp.xml deleted file mode 100644 index 7f888d10f..000000000 --- a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/PGaTJl94-dUtK-5YVG-7nT6UaKQp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/QWMJ0IlUTrzTjwye6g1ffgLjfGYd.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/QWMJ0IlUTrzTjwye6g1ffgLjfGYd.xml deleted file mode 100644 index 2662131a7..000000000 --- a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/QWMJ0IlUTrzTjwye6g1ffgLjfGYd.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/QWMJ0IlUTrzTjwye6g1ffgLjfGYp.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/QWMJ0IlUTrzTjwye6g1ffgLjfGYp.xml deleted file mode 100644 index 9ba09a7ea..000000000 --- a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/QWMJ0IlUTrzTjwye6g1ffgLjfGYp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/Uo9aVDJOhqlyX0wNM67-Vr7U4PYd.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/Uo9aVDJOhqlyX0wNM67-Vr7U4PYd.xml deleted file mode 100644 index a0094715d..000000000 --- a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/Uo9aVDJOhqlyX0wNM67-Vr7U4PYd.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/Uo9aVDJOhqlyX0wNM67-Vr7U4PYp.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/Uo9aVDJOhqlyX0wNM67-Vr7U4PYp.xml deleted file mode 100644 index fc8b9bb11..000000000 --- a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/Uo9aVDJOhqlyX0wNM67-Vr7U4PYp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/VbvFCIQVsoRbd9GRTBzN_omsupMd.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/VbvFCIQVsoRbd9GRTBzN_omsupMd.xml deleted file mode 100644 index 42781ea97..000000000 --- a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/VbvFCIQVsoRbd9GRTBzN_omsupMd.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/VbvFCIQVsoRbd9GRTBzN_omsupMp.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/VbvFCIQVsoRbd9GRTBzN_omsupMp.xml deleted file mode 100644 index 615ee80ea..000000000 --- a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/VbvFCIQVsoRbd9GRTBzN_omsupMp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/ZTjokurFNXtQ1ojGBXgvq_kwksId.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/ZTjokurFNXtQ1ojGBXgvq_kwksId.xml new file mode 100644 index 000000000..331ee41a0 --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/ZTjokurFNXtQ1ojGBXgvq_kwksId.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/ZTjokurFNXtQ1ojGBXgvq_kwksIp.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/ZTjokurFNXtQ1ojGBXgvq_kwksIp.xml new file mode 100644 index 000000000..0157b4df7 --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/ZTjokurFNXtQ1ojGBXgvq_kwksIp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/ZYZcp25adoHaOiOfAGtsGRGGUlod.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/ZYZcp25adoHaOiOfAGtsGRGGUlod.xml new file mode 100644 index 000000000..e7c85f82d --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/ZYZcp25adoHaOiOfAGtsGRGGUlod.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/ZYZcp25adoHaOiOfAGtsGRGGUlop.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/ZYZcp25adoHaOiOfAGtsGRGGUlop.xml new file mode 100644 index 000000000..3d795e7f5 --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/ZYZcp25adoHaOiOfAGtsGRGGUlop.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/Zu3w1_x1xrK1iAVqy0N_K78YUSQd.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/Zu3w1_x1xrK1iAVqy0N_K78YUSQd.xml deleted file mode 100644 index 3d43bcdf4..000000000 --- a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/Zu3w1_x1xrK1iAVqy0N_K78YUSQd.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/Zu3w1_x1xrK1iAVqy0N_K78YUSQp.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/Zu3w1_x1xrK1iAVqy0N_K78YUSQp.xml deleted file mode 100644 index b36c2dcdd..000000000 --- a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/Zu3w1_x1xrK1iAVqy0N_K78YUSQp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/fDXmO3TGZWioHy3oJ1Pc44eEwx4d.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/fDXmO3TGZWioHy3oJ1Pc44eEwx4d.xml new file mode 100644 index 000000000..d87f0d6ba --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/fDXmO3TGZWioHy3oJ1Pc44eEwx4d.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/fDXmO3TGZWioHy3oJ1Pc44eEwx4p.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/fDXmO3TGZWioHy3oJ1Pc44eEwx4p.xml new file mode 100644 index 000000000..383a720c4 --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/fDXmO3TGZWioHy3oJ1Pc44eEwx4p.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/fJawufIe31Xaj5x2aG6mddyYxqgd.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/fJawufIe31Xaj5x2aG6mddyYxqgd.xml new file mode 100644 index 000000000..224bbcd44 --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/fJawufIe31Xaj5x2aG6mddyYxqgd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/fJawufIe31Xaj5x2aG6mddyYxqgp.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/fJawufIe31Xaj5x2aG6mddyYxqgp.xml new file mode 100644 index 000000000..d7ea436a3 --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/fJawufIe31Xaj5x2aG6mddyYxqgp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/i5tvtXjf-liajJnRRTHy0WQWyBQd.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/i5tvtXjf-liajJnRRTHy0WQWyBQd.xml deleted file mode 100644 index 717641345..000000000 --- a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/i5tvtXjf-liajJnRRTHy0WQWyBQd.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/i5tvtXjf-liajJnRRTHy0WQWyBQp.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/i5tvtXjf-liajJnRRTHy0WQWyBQp.xml deleted file mode 100644 index dfea93486..000000000 --- a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/i5tvtXjf-liajJnRRTHy0WQWyBQp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/jTTvt3Bp2bTBwpSRN3WIq48HTY8d.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/jTTvt3Bp2bTBwpSRN3WIq48HTY8d.xml new file mode 100644 index 000000000..9a9e44082 --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/jTTvt3Bp2bTBwpSRN3WIq48HTY8d.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/jTTvt3Bp2bTBwpSRN3WIq48HTY8p.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/jTTvt3Bp2bTBwpSRN3WIq48HTY8p.xml new file mode 100644 index 000000000..e9d236b42 --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/jTTvt3Bp2bTBwpSRN3WIq48HTY8p.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/kLundQjBc0lkyBnd7dU88QI9eCYd.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/kLundQjBc0lkyBnd7dU88QI9eCYd.xml new file mode 100644 index 000000000..386a8301a --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/kLundQjBc0lkyBnd7dU88QI9eCYd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/kLundQjBc0lkyBnd7dU88QI9eCYp.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/kLundQjBc0lkyBnd7dU88QI9eCYp.xml new file mode 100644 index 000000000..ce7f09e35 --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/kLundQjBc0lkyBnd7dU88QI9eCYp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/pdndsCkuDwLksOKofxkONBmGSu0d.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/pdndsCkuDwLksOKofxkONBmGSu0d.xml new file mode 100644 index 000000000..d5d2429cb --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/pdndsCkuDwLksOKofxkONBmGSu0d.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/pdndsCkuDwLksOKofxkONBmGSu0p.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/pdndsCkuDwLksOKofxkONBmGSu0p.xml new file mode 100644 index 000000000..b40febf34 --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/pdndsCkuDwLksOKofxkONBmGSu0p.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/qomN9EMLc7gSakLKy3wL9wrIpacd.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/qomN9EMLc7gSakLKy3wL9wrIpacd.xml deleted file mode 100644 index e6617532e..000000000 --- a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/qomN9EMLc7gSakLKy3wL9wrIpacd.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/qomN9EMLc7gSakLKy3wL9wrIpacp.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/qomN9EMLc7gSakLKy3wL9wrIpacp.xml deleted file mode 100644 index e718cc2ed..000000000 --- a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/qomN9EMLc7gSakLKy3wL9wrIpacp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/r49BYN-QImuasJYtp4M0VMhEspQd.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/r49BYN-QImuasJYtp4M0VMhEspQd.xml deleted file mode 100644 index 0c5744fd6..000000000 --- a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/r49BYN-QImuasJYtp4M0VMhEspQd.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/r49BYN-QImuasJYtp4M0VMhEspQp.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/r49BYN-QImuasJYtp4M0VMhEspQp.xml deleted file mode 100644 index b57c2d010..000000000 --- a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/r49BYN-QImuasJYtp4M0VMhEspQp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/tzNeWTtKSZS7T3vujfMV7O83bhgd.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/tzNeWTtKSZS7T3vujfMV7O83bhgd.xml new file mode 100644 index 000000000..b36d1d598 --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/tzNeWTtKSZS7T3vujfMV7O83bhgd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/tzNeWTtKSZS7T3vujfMV7O83bhgp.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/tzNeWTtKSZS7T3vujfMV7O83bhgp.xml new file mode 100644 index 000000000..dc02a15cc --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/tzNeWTtKSZS7T3vujfMV7O83bhgp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/v0w6uQCEov7noSqWPgoe4xwGBF0d.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/v0w6uQCEov7noSqWPgoe4xwGBF0d.xml new file mode 100644 index 000000000..56a7e9c5a --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/v0w6uQCEov7noSqWPgoe4xwGBF0d.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/v0w6uQCEov7noSqWPgoe4xwGBF0p.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/v0w6uQCEov7noSqWPgoe4xwGBF0p.xml new file mode 100644 index 000000000..a20301066 --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/v0w6uQCEov7noSqWPgoe4xwGBF0p.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/vidY_3j1oRGddcnaqqAhcbXSRdEd.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/vidY_3j1oRGddcnaqqAhcbXSRdEd.xml new file mode 100644 index 000000000..fbddfe383 --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/vidY_3j1oRGddcnaqqAhcbXSRdEd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/vidY_3j1oRGddcnaqqAhcbXSRdEp.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/vidY_3j1oRGddcnaqqAhcbXSRdEp.xml new file mode 100644 index 000000000..560fc2987 --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/vidY_3j1oRGddcnaqqAhcbXSRdEp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/xc55aMCO0KDaUwfDNG4gUONyFQMd.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/xc55aMCO0KDaUwfDNG4gUONyFQMd.xml deleted file mode 100644 index 2bff93b41..000000000 --- a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/xc55aMCO0KDaUwfDNG4gUONyFQMd.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/xc55aMCO0KDaUwfDNG4gUONyFQMp.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/xc55aMCO0KDaUwfDNG4gUONyFQMp.xml deleted file mode 100644 index 1013e5e03..000000000 --- a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/xc55aMCO0KDaUwfDNG4gUONyFQMp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/HdiZGhv_rKHUyETdzYHpE0xs7AUd.xml b/resources/project/H72L659GuP9YjS6Dt9kIH5c6HnQ/7SrjXNQDv9BGhC0GEOKQspr9Ocgd.xml similarity index 100% rename from resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/HdiZGhv_rKHUyETdzYHpE0xs7AUd.xml rename to resources/project/H72L659GuP9YjS6Dt9kIH5c6HnQ/7SrjXNQDv9BGhC0GEOKQspr9Ocgd.xml diff --git a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/9orjn84dnpAKkkuchjb6ZXb4lYop.xml b/resources/project/H72L659GuP9YjS6Dt9kIH5c6HnQ/7SrjXNQDv9BGhC0GEOKQspr9Ocgp.xml similarity index 100% rename from resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/9orjn84dnpAKkkuchjb6ZXb4lYop.xml rename to resources/project/H72L659GuP9YjS6Dt9kIH5c6HnQ/7SrjXNQDv9BGhC0GEOKQspr9Ocgp.xml diff --git a/resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/Onj0w7QprQSOKpr22MXMZdoROBAd.xml b/resources/project/H72L659GuP9YjS6Dt9kIH5c6HnQ/82flsbuQG_B85H3tl7ti_QR4mXId.xml similarity index 100% rename from resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/Onj0w7QprQSOKpr22MXMZdoROBAd.xml rename to resources/project/H72L659GuP9YjS6Dt9kIH5c6HnQ/82flsbuQG_B85H3tl7ti_QR4mXId.xml diff --git a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/Q_eYts4Jx8lwh_76CdRDIxbbdlkp.xml b/resources/project/H72L659GuP9YjS6Dt9kIH5c6HnQ/82flsbuQG_B85H3tl7ti_QR4mXIp.xml similarity index 100% rename from resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/Q_eYts4Jx8lwh_76CdRDIxbbdlkp.xml rename to resources/project/H72L659GuP9YjS6Dt9kIH5c6HnQ/82flsbuQG_B85H3tl7ti_QR4mXIp.xml diff --git a/resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/Zoc8C9B4lYxsGTJUXBoNY2847rsd.xml b/resources/project/H72L659GuP9YjS6Dt9kIH5c6HnQ/a0lBcQv35pXv1BECWbcE4pI6X88d.xml similarity index 100% rename from resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/Zoc8C9B4lYxsGTJUXBoNY2847rsd.xml rename to resources/project/H72L659GuP9YjS6Dt9kIH5c6HnQ/a0lBcQv35pXv1BECWbcE4pI6X88d.xml diff --git a/resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/Zoc8C9B4lYxsGTJUXBoNY2847rsp.xml b/resources/project/H72L659GuP9YjS6Dt9kIH5c6HnQ/a0lBcQv35pXv1BECWbcE4pI6X88p.xml similarity index 100% rename from resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/Zoc8C9B4lYxsGTJUXBoNY2847rsp.xml rename to resources/project/H72L659GuP9YjS6Dt9kIH5c6HnQ/a0lBcQv35pXv1BECWbcE4pI6X88p.xml diff --git a/resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/Qc2wVp_95I6ELlZy_on0j8jGrt4d.xml b/resources/project/H72L659GuP9YjS6Dt9kIH5c6HnQ/bV1SapNLHXOJuqxs4eM5UgXt1DMd.xml similarity index 100% rename from resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/Qc2wVp_95I6ELlZy_on0j8jGrt4d.xml rename to resources/project/H72L659GuP9YjS6Dt9kIH5c6HnQ/bV1SapNLHXOJuqxs4eM5UgXt1DMd.xml diff --git a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/7U58VHMhrmTgIuozFzLR1XBJo8Ip.xml b/resources/project/H72L659GuP9YjS6Dt9kIH5c6HnQ/bV1SapNLHXOJuqxs4eM5UgXt1DMp.xml similarity index 100% rename from resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/7U58VHMhrmTgIuozFzLR1XBJo8Ip.xml rename to resources/project/H72L659GuP9YjS6Dt9kIH5c6HnQ/bV1SapNLHXOJuqxs4eM5UgXt1DMp.xml diff --git a/resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/cUlln2uasX69yptYpRT0FIXMmN4d.xml b/resources/project/H72L659GuP9YjS6Dt9kIH5c6HnQ/wfGjJ216NLwm4I2ndZI3_86uhgQd.xml similarity index 100% rename from resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/cUlln2uasX69yptYpRT0FIXMmN4d.xml rename to resources/project/H72L659GuP9YjS6Dt9kIH5c6HnQ/wfGjJ216NLwm4I2ndZI3_86uhgQd.xml diff --git a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/vok99kMS3Be0cBX4FPi-Zspy6Y0p.xml b/resources/project/H72L659GuP9YjS6Dt9kIH5c6HnQ/wfGjJ216NLwm4I2ndZI3_86uhgQp.xml similarity index 100% rename from resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/vok99kMS3Be0cBX4FPi-Zspy6Y0p.xml rename to resources/project/H72L659GuP9YjS6Dt9kIH5c6HnQ/wfGjJ216NLwm4I2ndZI3_86uhgQp.xml diff --git a/resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/fbFT1H1Yjhkv8Mn3gVUZJX6bG_od.xml b/resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/Eb3URaLnOadzaECH9JhrMNGK0cYd.xml similarity index 100% rename from resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/fbFT1H1Yjhkv8Mn3gVUZJX6bG_od.xml rename to resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/Eb3URaLnOadzaECH9JhrMNGK0cYd.xml diff --git a/resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/0oyoesBN2_oayH0i7gtHJp890Esp.xml b/resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/Eb3URaLnOadzaECH9JhrMNGK0cYp.xml similarity index 100% rename from resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/0oyoesBN2_oayH0i7gtHJp890Esp.xml rename to resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/Eb3URaLnOadzaECH9JhrMNGK0cYp.xml diff --git a/resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/mpAb6JLvq2630l0ar-o34Ybxq50d.xml b/resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/GUD97Kgby-Nbe7a4937yGMYWqoQd.xml similarity index 100% rename from resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/mpAb6JLvq2630l0ar-o34Ybxq50d.xml rename to resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/GUD97Kgby-Nbe7a4937yGMYWqoQd.xml diff --git a/resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/GUD97Kgby-Nbe7a4937yGMYWqoQp.xml b/resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/GUD97Kgby-Nbe7a4937yGMYWqoQp.xml new file mode 100644 index 000000000..c4e237353 --- /dev/null +++ b/resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/GUD97Kgby-Nbe7a4937yGMYWqoQp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/qXqP2bORsPzSqGsTBWvUFdPwO04d.xml b/resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/IXrhsh0KA0Hd3dEHjxbDlSwV_9wd.xml similarity index 100% rename from resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/qXqP2bORsPzSqGsTBWvUFdPwO04d.xml rename to resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/IXrhsh0KA0Hd3dEHjxbDlSwV_9wd.xml diff --git a/resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/kJV53JKRAFPnFBgXx06VMpdr0ssp.xml b/resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/IXrhsh0KA0Hd3dEHjxbDlSwV_9wp.xml similarity index 100% rename from resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/kJV53JKRAFPnFBgXx06VMpdr0ssp.xml rename to resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/IXrhsh0KA0Hd3dEHjxbDlSwV_9wp.xml diff --git a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/2EnTnZEx-4v1jC3AJZADMDpEO1gd.xml b/resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/_cZck6op8VccwtbtBtqU08jEoNgd.xml similarity index 100% rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/2EnTnZEx-4v1jC3AJZADMDpEO1gd.xml rename to resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/_cZck6op8VccwtbtBtqU08jEoNgd.xml diff --git a/resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/jupI72lBOnUNFR-WjgobwXS0zMop.xml b/resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/_cZck6op8VccwtbtBtqU08jEoNgp.xml similarity index 100% rename from resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/jupI72lBOnUNFR-WjgobwXS0zMop.xml rename to resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/_cZck6op8VccwtbtBtqU08jEoNgp.xml diff --git a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/5LEawZsxA1aBTeqG0NlpCtR6q6kd.xml b/resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/poWgzYIOpdVLERL5CAxxonZKagod.xml similarity index 100% rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/5LEawZsxA1aBTeqG0NlpCtR6q6kd.xml rename to resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/poWgzYIOpdVLERL5CAxxonZKagod.xml diff --git a/resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/T9wikiXK6MjHu7XyyDA0kuCTP_kp.xml b/resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/poWgzYIOpdVLERL5CAxxonZKagop.xml similarity index 100% rename from resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/T9wikiXK6MjHu7XyyDA0kuCTP_kp.xml rename to resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/poWgzYIOpdVLERL5CAxxonZKagop.xml diff --git a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/5TVbg-DxiEwvaH5Bj9CH0ewcKu8d.xml b/resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/veTEyGEjchushQj-PUakRWJ1Rfwd.xml similarity index 100% rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/5TVbg-DxiEwvaH5Bj9CH0ewcKu8d.xml rename to resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/veTEyGEjchushQj-PUakRWJ1Rfwd.xml diff --git a/resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/veTEyGEjchushQj-PUakRWJ1Rfwp.xml b/resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/veTEyGEjchushQj-PUakRWJ1Rfwp.xml new file mode 100644 index 000000000..364fc96d9 --- /dev/null +++ b/resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/veTEyGEjchushQj-PUakRWJ1Rfwp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/8uH7KtfLIR_hLVyUlkUrouETOeod.xml b/resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/wXFztAEfZBGlWkniv1yDOZUPMLkd.xml similarity index 100% rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/8uH7KtfLIR_hLVyUlkUrouETOeod.xml rename to resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/wXFztAEfZBGlWkniv1yDOZUPMLkd.xml diff --git a/resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/-xMsha79hmE0qOhn3z2Y4wwJaNkp.xml b/resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/wXFztAEfZBGlWkniv1yDOZUPMLkp.xml similarity index 100% rename from resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/-xMsha79hmE0qOhn3z2Y4wwJaNkp.xml rename to resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/wXFztAEfZBGlWkniv1yDOZUPMLkp.xml diff --git a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/nW4p5Z6gDt0MyfrwKAnI3MioETUd.xml b/resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/yG6FN90UXfIzKIRJPeCgEK9wsiAd.xml similarity index 100% rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/nW4p5Z6gDt0MyfrwKAnI3MioETUd.xml rename to resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/yG6FN90UXfIzKIRJPeCgEK9wsiAd.xml diff --git a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/nW4p5Z6gDt0MyfrwKAnI3MioETUp.xml b/resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/yG6FN90UXfIzKIRJPeCgEK9wsiAp.xml similarity index 100% rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/nW4p5Z6gDt0MyfrwKAnI3MioETUp.xml rename to resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/yG6FN90UXfIzKIRJPeCgEK9wsiAp.xml diff --git a/resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/G2x_hSFRzJNj2RwsZ4N6TWYsHmYp.xml b/resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/G2x_hSFRzJNj2RwsZ4N6TWYsHmYp.xml deleted file mode 100644 index 457647c2b..000000000 --- a/resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/G2x_hSFRzJNj2RwsZ4N6TWYsHmYp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/rx1MnRhyaD-dcP1I9WcseP2PGiwp.xml b/resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/rx1MnRhyaD-dcP1I9WcseP2PGiwp.xml deleted file mode 100644 index 9657268ce..000000000 --- a/resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/rx1MnRhyaD-dcP1I9WcseP2PGiwp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/wSDXMLBIRWsCGVgqLP9ER---lO4p.xml b/resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/wSDXMLBIRWsCGVgqLP9ER---lO4p.xml deleted file mode 100644 index 4fed95bc9..000000000 --- a/resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/wSDXMLBIRWsCGVgqLP9ER---lO4p.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/yrvDAxHTmqAbl2v6-rWp7tgRl4Ep.xml b/resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/yrvDAxHTmqAbl2v6-rWp7tgRl4Ep.xml deleted file mode 100644 index f11c44a14..000000000 --- a/resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/yrvDAxHTmqAbl2v6-rWp7tgRl4Ep.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/DpnGV8GaSUAjtUgrpQ5nUXOT5wYd.xml b/resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/-Lw-S9LvX_FYDRFafe-L_l8cTMsd.xml similarity index 100% rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/DpnGV8GaSUAjtUgrpQ5nUXOT5wYd.xml rename to resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/-Lw-S9LvX_FYDRFafe-L_l8cTMsd.xml diff --git a/resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/qXqP2bORsPzSqGsTBWvUFdPwO04p.xml b/resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/-Lw-S9LvX_FYDRFafe-L_l8cTMsp.xml similarity index 100% rename from resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/qXqP2bORsPzSqGsTBWvUFdPwO04p.xml rename to resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/-Lw-S9LvX_FYDRFafe-L_l8cTMsp.xml diff --git a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/J8XPAkZbsecOIp7IVKNIsrQZCHEd.xml b/resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/4JNkAFleLVxY3nQGnz12ugS4ZTEd.xml similarity index 100% rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/J8XPAkZbsecOIp7IVKNIsrQZCHEd.xml rename to resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/4JNkAFleLVxY3nQGnz12ugS4ZTEd.xml diff --git a/resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/BWtL89f91xbulioiaYLBB_yHOgMp.xml b/resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/4JNkAFleLVxY3nQGnz12ugS4ZTEp.xml similarity index 100% rename from resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/BWtL89f91xbulioiaYLBB_yHOgMp.xml rename to resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/4JNkAFleLVxY3nQGnz12ugS4ZTEp.xml diff --git a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/JlNlt7b52D5lndUS8QIFBXuyn8sd.xml b/resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/4hmmxZ0XovlFSmc-Rsau_8EpzNkd.xml similarity index 100% rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/JlNlt7b52D5lndUS8QIFBXuyn8sd.xml rename to resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/4hmmxZ0XovlFSmc-Rsau_8EpzNkd.xml diff --git a/resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/Qc2wVp_95I6ELlZy_on0j8jGrt4p.xml b/resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/4hmmxZ0XovlFSmc-Rsau_8EpzNkp.xml similarity index 100% rename from resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/Qc2wVp_95I6ELlZy_on0j8jGrt4p.xml rename to resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/4hmmxZ0XovlFSmc-Rsau_8EpzNkp.xml diff --git a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/Nh99zm6VyLbCagFVKH0kx0irGckd.xml b/resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/5lEWuAPjsUhI7_hCP31KFq7iDlUd.xml similarity index 100% rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/Nh99zm6VyLbCagFVKH0kx0irGckd.xml rename to resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/5lEWuAPjsUhI7_hCP31KFq7iDlUd.xml diff --git a/resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/cUlln2uasX69yptYpRT0FIXMmN4p.xml b/resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/5lEWuAPjsUhI7_hCP31KFq7iDlUp.xml similarity index 100% rename from resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/cUlln2uasX69yptYpRT0FIXMmN4p.xml rename to resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/5lEWuAPjsUhI7_hCP31KFq7iDlUp.xml diff --git a/resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/Bk_xMGlhpZAWbMPYXgyuKxor6wod.xml b/resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/CZ1F4Twl7ZiFGyGb8gS7TX0pkTQd.xml similarity index 100% rename from resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/Bk_xMGlhpZAWbMPYXgyuKxor6wod.xml rename to resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/CZ1F4Twl7ZiFGyGb8gS7TX0pkTQd.xml diff --git a/resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/Bk_xMGlhpZAWbMPYXgyuKxor6wop.xml b/resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/CZ1F4Twl7ZiFGyGb8gS7TX0pkTQp.xml similarity index 100% rename from resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/Bk_xMGlhpZAWbMPYXgyuKxor6wop.xml rename to resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/CZ1F4Twl7ZiFGyGb8gS7TX0pkTQp.xml diff --git a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/PUXt85kSoxl2GhAVUeaGOA1ui0sd.xml b/resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/DK5W7ZmXIHOHuwnXHtwNGL22p4Md.xml similarity index 100% rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/PUXt85kSoxl2GhAVUeaGOA1ui0sd.xml rename to resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/DK5W7ZmXIHOHuwnXHtwNGL22p4Md.xml diff --git a/resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/HdiZGhv_rKHUyETdzYHpE0xs7AUp.xml b/resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/DK5W7ZmXIHOHuwnXHtwNGL22p4Mp.xml similarity index 100% rename from resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/HdiZGhv_rKHUyETdzYHpE0xs7AUp.xml rename to resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/DK5W7ZmXIHOHuwnXHtwNGL22p4Mp.xml diff --git a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/6Fwf5cB_poUbMFePFS_ylV5Ftusd.xml b/resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/GPtTjnSsEKTMO1S-sg_FP4dxbbEd.xml similarity index 100% rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/6Fwf5cB_poUbMFePFS_ylV5Ftusd.xml rename to resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/GPtTjnSsEKTMO1S-sg_FP4dxbbEd.xml diff --git a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/6Fwf5cB_poUbMFePFS_ylV5Ftusp.xml b/resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/GPtTjnSsEKTMO1S-sg_FP4dxbbEp.xml similarity index 100% rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/6Fwf5cB_poUbMFePFS_ylV5Ftusp.xml rename to resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/GPtTjnSsEKTMO1S-sg_FP4dxbbEp.xml diff --git a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/SLN8kBxZYH73a64wQLgTpFlpH-Ud.xml b/resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/J4NSMqZEAmFZH35SWLSatqliZmkd.xml similarity index 100% rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/SLN8kBxZYH73a64wQLgTpFlpH-Ud.xml rename to resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/J4NSMqZEAmFZH35SWLSatqliZmkd.xml diff --git a/resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/mpAb6JLvq2630l0ar-o34Ybxq50p.xml b/resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/J4NSMqZEAmFZH35SWLSatqliZmkp.xml similarity index 100% rename from resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/mpAb6JLvq2630l0ar-o34Ybxq50p.xml rename to resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/J4NSMqZEAmFZH35SWLSatqliZmkp.xml diff --git a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/V7dcj86ZsVvV3wlDWv_EePrkW7wd.xml b/resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/eOoHRDjyUSBj8-WLEX9mxUx6ePUd.xml similarity index 100% rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/V7dcj86ZsVvV3wlDWv_EePrkW7wd.xml rename to resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/eOoHRDjyUSBj8-WLEX9mxUx6ePUd.xml diff --git a/resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/fbFT1H1Yjhkv8Mn3gVUZJX6bG_op.xml b/resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/eOoHRDjyUSBj8-WLEX9mxUx6ePUp.xml similarity index 100% rename from resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/fbFT1H1Yjhkv8Mn3gVUZJX6bG_op.xml rename to resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/eOoHRDjyUSBj8-WLEX9mxUx6ePUp.xml diff --git a/resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/tQLIewmcrCq5k7qM-X0N0mag3d8d.xml b/resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/j0R75YjB0BcweDo5AYcMWiq3P6Md.xml similarity index 100% rename from resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/tQLIewmcrCq5k7qM-X0N0mag3d8d.xml rename to resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/j0R75YjB0BcweDo5AYcMWiq3P6Md.xml diff --git a/resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/tQLIewmcrCq5k7qM-X0N0mag3d8p.xml b/resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/j0R75YjB0BcweDo5AYcMWiq3P6Mp.xml similarity index 100% rename from resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/tQLIewmcrCq5k7qM-X0N0mag3d8p.xml rename to resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/j0R75YjB0BcweDo5AYcMWiq3P6Mp.xml diff --git a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/XDDpmW-ahmfRGLbHVnihtWagQDEd.xml b/resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/mjVTgOO6IWMthsneycvZqWzxD_gd.xml similarity index 100% rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/XDDpmW-ahmfRGLbHVnihtWagQDEd.xml rename to resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/mjVTgOO6IWMthsneycvZqWzxD_gd.xml diff --git a/resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/Onj0w7QprQSOKpr22MXMZdoROBAp.xml b/resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/mjVTgOO6IWMthsneycvZqWzxD_gp.xml similarity index 100% rename from resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/Onj0w7QprQSOKpr22MXMZdoROBAp.xml rename to resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/mjVTgOO6IWMthsneycvZqWzxD_gp.xml diff --git a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/rzCzLkJQsijCRt28FvlpiZrjfZ4d.xml b/resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/nVExW6oxyLwVGXPnnA3XaUC50Jcd.xml similarity index 100% rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/rzCzLkJQsijCRt28FvlpiZrjfZ4d.xml rename to resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/nVExW6oxyLwVGXPnnA3XaUC50Jcd.xml diff --git a/resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/G0w81rMIRi8PzlrGVbGmoa0FV9gp.xml b/resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/nVExW6oxyLwVGXPnnA3XaUC50Jcp.xml similarity index 100% rename from resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/G0w81rMIRi8PzlrGVbGmoa0FV9gp.xml rename to resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/nVExW6oxyLwVGXPnnA3XaUC50Jcp.xml diff --git a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/vyBBHUVgKzM20Od7DNtH6-JNEiAd.xml b/resources/project/PMTiY8ii3Ycb5X733RMaGCNVItA/6tuQeWy9960NGsG8vq2g6XEbfs8d.xml similarity index 100% rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/vyBBHUVgKzM20Od7DNtH6-JNEiAd.xml rename to resources/project/PMTiY8ii3Ycb5X733RMaGCNVItA/6tuQeWy9960NGsG8vq2g6XEbfs8d.xml diff --git a/resources/project/PMTiY8ii3Ycb5X733RMaGCNVItA/6tuQeWy9960NGsG8vq2g6XEbfs8p.xml b/resources/project/PMTiY8ii3Ycb5X733RMaGCNVItA/6tuQeWy9960NGsG8vq2g6XEbfs8p.xml new file mode 100644 index 000000000..94a2c7579 --- /dev/null +++ b/resources/project/PMTiY8ii3Ycb5X733RMaGCNVItA/6tuQeWy9960NGsG8vq2g6XEbfs8p.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/z8PCXMWInzrlFrKj4KQg_4fVTZYd.xml b/resources/project/PMTiY8ii3Ycb5X733RMaGCNVItA/A8ncUuxWwwZj8vBXXThduDRmo-Id.xml similarity index 100% rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/z8PCXMWInzrlFrKj4KQg_4fVTZYd.xml rename to resources/project/PMTiY8ii3Ycb5X733RMaGCNVItA/A8ncUuxWwwZj8vBXXThduDRmo-Id.xml diff --git a/resources/project/PMTiY8ii3Ycb5X733RMaGCNVItA/A8ncUuxWwwZj8vBXXThduDRmo-Ip.xml b/resources/project/PMTiY8ii3Ycb5X733RMaGCNVItA/A8ncUuxWwwZj8vBXXThduDRmo-Ip.xml new file mode 100644 index 000000000..3b38f697b --- /dev/null +++ b/resources/project/PMTiY8ii3Ycb5X733RMaGCNVItA/A8ncUuxWwwZj8vBXXThduDRmo-Ip.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/zWhjsX1e0jX9kixjwjfDU_xQo9Qd.xml b/resources/project/PMTiY8ii3Ycb5X733RMaGCNVItA/J95nrDPGFnMMRe-j9IrlrD3MJIwd.xml similarity index 100% rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/zWhjsX1e0jX9kixjwjfDU_xQo9Qd.xml rename to resources/project/PMTiY8ii3Ycb5X733RMaGCNVItA/J95nrDPGFnMMRe-j9IrlrD3MJIwd.xml diff --git a/resources/project/PMTiY8ii3Ycb5X733RMaGCNVItA/J95nrDPGFnMMRe-j9IrlrD3MJIwp.xml b/resources/project/PMTiY8ii3Ycb5X733RMaGCNVItA/J95nrDPGFnMMRe-j9IrlrD3MJIwp.xml new file mode 100644 index 000000000..f2dda6460 --- /dev/null +++ b/resources/project/PMTiY8ii3Ycb5X733RMaGCNVItA/J95nrDPGFnMMRe-j9IrlrD3MJIwp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/MMRr3Fv-bYGGSvC6xcJnwOYvzGwd.xml b/resources/project/PMTiY8ii3Ycb5X733RMaGCNVItA/cmZk236s6Qn7CSYmgTSLERo6-Fod.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/MMRr3Fv-bYGGSvC6xcJnwOYvzGwd.xml rename to resources/project/PMTiY8ii3Ycb5X733RMaGCNVItA/cmZk236s6Qn7CSYmgTSLERo6-Fod.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/MMRr3Fv-bYGGSvC6xcJnwOYvzGwp.xml b/resources/project/PMTiY8ii3Ycb5X733RMaGCNVItA/cmZk236s6Qn7CSYmgTSLERo6-Fop.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/MMRr3Fv-bYGGSvC6xcJnwOYvzGwp.xml rename to resources/project/PMTiY8ii3Ycb5X733RMaGCNVItA/cmZk236s6Qn7CSYmgTSLERo6-Fop.xml diff --git a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/0amoX63LRAKETLDqV7_F13CuX74d.xml b/resources/project/PMTiY8ii3Ycb5X733RMaGCNVItA/gdgc-JZm4OTNPgAUaiKWok7sS8Qd.xml similarity index 100% rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/0amoX63LRAKETLDqV7_F13CuX74d.xml rename to resources/project/PMTiY8ii3Ycb5X733RMaGCNVItA/gdgc-JZm4OTNPgAUaiKWok7sS8Qd.xml diff --git a/resources/project/PMTiY8ii3Ycb5X733RMaGCNVItA/gdgc-JZm4OTNPgAUaiKWok7sS8Qp.xml b/resources/project/PMTiY8ii3Ycb5X733RMaGCNVItA/gdgc-JZm4OTNPgAUaiKWok7sS8Qp.xml new file mode 100644 index 000000000..c96fbd0e6 --- /dev/null +++ b/resources/project/PMTiY8ii3Ycb5X733RMaGCNVItA/gdgc-JZm4OTNPgAUaiKWok7sS8Qp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/6GJfsBaU_5DGhbimCNcp3Txx-v8d.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/-K_tTgFkd3KuOQq4OW0mOmsQS44d.xml similarity index 100% rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/6GJfsBaU_5DGhbimCNcp3Txx-v8d.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/-K_tTgFkd3KuOQq4OW0mOmsQS44d.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/LN9DpJcyR6CE_I14ywn3ncJAeXIp.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/-K_tTgFkd3KuOQq4OW0mOmsQS44p.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/LN9DpJcyR6CE_I14ywn3ncJAeXIp.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/-K_tTgFkd3KuOQq4OW0mOmsQS44p.xml diff --git a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/888MRa7OWq3oIOyCKPp0I2UIiKAd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/-ZOcmw4c1B4bINk-xUWH7ahRKJod.xml similarity index 100% rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/888MRa7OWq3oIOyCKPp0I2UIiKAd.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/-ZOcmw4c1B4bINk-xUWH7ahRKJod.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/aSbr7z_N-_xgJQSr5hrZLyKPz4cp.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/-ZOcmw4c1B4bINk-xUWH7ahRKJop.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/aSbr7z_N-_xgJQSr5hrZLyKPz4cp.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/-ZOcmw4c1B4bINk-xUWH7ahRKJop.xml diff --git a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/BEHll27af-LsZgmvfXGuLyYJyU0d.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/00BmM5mTpuxcj5K8jG604UO7-Ewd.xml similarity index 100% rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/BEHll27af-LsZgmvfXGuLyYJyU0d.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/00BmM5mTpuxcj5K8jG604UO7-Ewd.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/T2fYvrqhpvDyuKc8Y7wsu2vaPcUp.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/00BmM5mTpuxcj5K8jG604UO7-Ewp.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/T2fYvrqhpvDyuKc8Y7wsu2vaPcUp.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/00BmM5mTpuxcj5K8jG604UO7-Ewp.xml diff --git a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/CgrwWh_FTjN531XPRoLVTVqLbZEd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/0TH8tA8-wfGMXu-ho-n1gCC4wJsd.xml similarity index 100% rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/CgrwWh_FTjN531XPRoLVTVqLbZEd.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/0TH8tA8-wfGMXu-ho-n1gCC4wJsd.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/G6IjDtcuN86KCffyBg9hhLwxfM8p.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/0TH8tA8-wfGMXu-ho-n1gCC4wJsp.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/G6IjDtcuN86KCffyBg9hhLwxfM8p.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/0TH8tA8-wfGMXu-ho-n1gCC4wJsp.xml diff --git a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/GSxmGWKoE8yVg8Dt0_WIVqZwfq8d.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/2lZWGayY8sTSf6C_9MNt1ciblccd.xml similarity index 100% rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/GSxmGWKoE8yVg8Dt0_WIVqZwfq8d.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/2lZWGayY8sTSf6C_9MNt1ciblccd.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/LK5LTNCFyhwaQZUlosrnunT2xZIp.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/2lZWGayY8sTSf6C_9MNt1ciblccp.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/LK5LTNCFyhwaQZUlosrnunT2xZIp.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/2lZWGayY8sTSf6C_9MNt1ciblccp.xml diff --git a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/GmURLyn29gRlewEo8pphTKMkvfcd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/4NaiUnhsCfsLNNAYb_2Nre3n59Ud.xml similarity index 100% rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/GmURLyn29gRlewEo8pphTKMkvfcd.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/4NaiUnhsCfsLNNAYb_2Nre3n59Ud.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/n6yybQJW-SCbwLJB8CxlywM5r38p.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/4NaiUnhsCfsLNNAYb_2Nre3n59Up.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/n6yybQJW-SCbwLJB8CxlywM5r38p.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/4NaiUnhsCfsLNNAYb_2Nre3n59Up.xml diff --git a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/HinGYaWsjaNlqTK_j5cQYUlmn_4d.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/4edCiafrq1BIvyyh61g6Z-ot9R4d.xml similarity index 100% rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/HinGYaWsjaNlqTK_j5cQYUlmn_4d.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/4edCiafrq1BIvyyh61g6Z-ot9R4d.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/00XLiFvyqFehoPigl9ogalyjfbAp.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/4edCiafrq1BIvyyh61g6Z-ot9R4p.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/00XLiFvyqFehoPigl9ogalyjfbAp.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/4edCiafrq1BIvyyh61g6Z-ot9R4p.xml diff --git a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/K7gRh-Ets4DP9XdM-8WQS6MYiTAd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/4x4IMS_bCxBIpOSTkeIwMAKuTawd.xml similarity index 100% rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/K7gRh-Ets4DP9XdM-8WQS6MYiTAd.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/4x4IMS_bCxBIpOSTkeIwMAKuTawd.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/Vfp2GuJhjHgccgR8Joly_nEH3uUp.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/4x4IMS_bCxBIpOSTkeIwMAKuTawp.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/Vfp2GuJhjHgccgR8Joly_nEH3uUp.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/4x4IMS_bCxBIpOSTkeIwMAKuTawp.xml diff --git a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/QQxpEF0mWnNdoQybWJnETf5i2zgd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/5G1NQ5WxNVqz_u56bBC5V8s_cnMd.xml similarity index 100% rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/QQxpEF0mWnNdoQybWJnETf5i2zgd.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/5G1NQ5WxNVqz_u56bBC5V8s_cnMd.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/AFQGXDiIsf-NM9eQ8n4eWMrVd1Yp.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/5G1NQ5WxNVqz_u56bBC5V8s_cnMp.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/AFQGXDiIsf-NM9eQ8n4eWMrVd1Yp.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/5G1NQ5WxNVqz_u56bBC5V8s_cnMp.xml diff --git a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/UlAa7CZxUr955fLFiY8150xe-0Yd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/5OtYPmi9zdVJFCfszj3XmwBOicQd.xml similarity index 100% rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/UlAa7CZxUr955fLFiY8150xe-0Yd.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/5OtYPmi9zdVJFCfszj3XmwBOicQd.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/3vPrjfHdpkKcTeJZLymuPjPeJLop.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/5OtYPmi9zdVJFCfszj3XmwBOicQp.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/3vPrjfHdpkKcTeJZLymuPjPeJLop.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/5OtYPmi9zdVJFCfszj3XmwBOicQp.xml diff --git a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/iPx7LMwL8D8SvprpBzhO3qxwy0Id.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/AiCl4Nv-4TcAuyccq2bcsaO4hGId.xml similarity index 100% rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/iPx7LMwL8D8SvprpBzhO3qxwy0Id.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/AiCl4Nv-4TcAuyccq2bcsaO4hGId.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/R9uo8TWlIFmDZ_U2L3brdMMNpmUp.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/AiCl4Nv-4TcAuyccq2bcsaO4hGIp.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/R9uo8TWlIFmDZ_U2L3brdMMNpmUp.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/AiCl4Nv-4TcAuyccq2bcsaO4hGIp.xml diff --git a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/kyvnzrdUBXgtJ7QWloYIMdgg3f0d.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/BzVxW2mFaL8w6fW-2i_Ks4AtAywd.xml similarity index 100% rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/kyvnzrdUBXgtJ7QWloYIMdgg3f0d.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/BzVxW2mFaL8w6fW-2i_Ks4AtAywd.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/Dg1e3Am5YrqqMqqkIYDk9ZjR1RYp.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/BzVxW2mFaL8w6fW-2i_Ks4AtAywp.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/Dg1e3Am5YrqqMqqkIYDk9ZjR1RYp.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/BzVxW2mFaL8w6fW-2i_Ks4AtAywp.xml diff --git a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/mIYcND4GVDO0WgnXvKhJVZw4Qdsd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/CwijU_1EfQOTRpwgKbRX29DCRqod.xml similarity index 100% rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/mIYcND4GVDO0WgnXvKhJVZw4Qdsd.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/CwijU_1EfQOTRpwgKbRX29DCRqod.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/aj3y-ODStp8yUzt7MUXbgCsT1OYp.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/CwijU_1EfQOTRpwgKbRX29DCRqop.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/aj3y-ODStp8yUzt7MUXbgCsT1OYp.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/CwijU_1EfQOTRpwgKbRX29DCRqop.xml diff --git a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/wOcCxJrf1WxhN3mYz0ASwydFXsMd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/D6bNMzhGpxvqGj045koQvCPRgSgd.xml similarity index 100% rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/wOcCxJrf1WxhN3mYz0ASwydFXsMd.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/D6bNMzhGpxvqGj045koQvCPRgSgd.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/XeBJu8m_oaoL52pMHUpwH6lM5Fop.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/D6bNMzhGpxvqGj045koQvCPRgSgp.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/XeBJu8m_oaoL52pMHUpwH6lM5Fop.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/D6bNMzhGpxvqGj045koQvCPRgSgp.xml diff --git a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/zRzAsvX6Os0bPYij1y_5pG-94Dgd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/FhhflOTx83dyAZA7C5LZFylsG3Qd.xml similarity index 100% rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/zRzAsvX6Os0bPYij1y_5pG-94Dgd.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/FhhflOTx83dyAZA7C5LZFylsG3Qd.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/TcR3rZOrVrHqDGA6Ls0qBpAOHD0p.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/FhhflOTx83dyAZA7C5LZFylsG3Qp.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/TcR3rZOrVrHqDGA6Ls0qBpAOHD0p.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/FhhflOTx83dyAZA7C5LZFylsG3Qp.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/-3QAULuhtItSD7_pHpYyutizqKcd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/G8qY3zLR3J2KmeRH1uVSXh4Feewd.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/-3QAULuhtItSD7_pHpYyutizqKcd.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/G8qY3zLR3J2KmeRH1uVSXh4Feewd.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/j0ffgGF4ix7R_1weXjtRLJ2m60op.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/G8qY3zLR3J2KmeRH1uVSXh4Feewp.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/j0ffgGF4ix7R_1weXjtRLJ2m60op.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/G8qY3zLR3J2KmeRH1uVSXh4Feewp.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/-44zf-4QbDVnfxvdMOlS-62neokd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/L2wh56GmocdhTwvqoEVyLSPjn8Id.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/-44zf-4QbDVnfxvdMOlS-62neokd.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/L2wh56GmocdhTwvqoEVyLSPjn8Id.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/6zc8a3V6P8-0WJxogD3UrSBw7mEp.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/L2wh56GmocdhTwvqoEVyLSPjn8Ip.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/6zc8a3V6P8-0WJxogD3UrSBw7mEp.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/L2wh56GmocdhTwvqoEVyLSPjn8Ip.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/-pKErZtYRVQsLNI-s6dhlLE9rN0d.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/NgzKbmuSLrYttzedhNh5jGcAk2od.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/-pKErZtYRVQsLNI-s6dhlLE9rN0d.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/NgzKbmuSLrYttzedhNh5jGcAk2od.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/2FIZqjX_-rJNGXzNKJW_vC24H0Up.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/NgzKbmuSLrYttzedhNh5jGcAk2op.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/2FIZqjX_-rJNGXzNKJW_vC24H0Up.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/NgzKbmuSLrYttzedhNh5jGcAk2op.xml diff --git a/resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/H7mS2g0N2I6TXTkSevNbSUWfbRgd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/NrbvTPDfIXgykDJkfPdIZyDvtVgd.xml similarity index 100% rename from resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/H7mS2g0N2I6TXTkSevNbSUWfbRgd.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/NrbvTPDfIXgykDJkfPdIZyDvtVgd.xml diff --git a/resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/H7mS2g0N2I6TXTkSevNbSUWfbRgp.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/NrbvTPDfIXgykDJkfPdIZyDvtVgp.xml similarity index 100% rename from resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/H7mS2g0N2I6TXTkSevNbSUWfbRgp.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/NrbvTPDfIXgykDJkfPdIZyDvtVgp.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/00XLiFvyqFehoPigl9ogalyjfbAd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/P0FXd6AwKsPzBiYwIZLsEUD5VrAd.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/00XLiFvyqFehoPigl9ogalyjfbAd.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/P0FXd6AwKsPzBiYwIZLsEUD5VrAd.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/ZqK39Xv3A0I-0ohwRkWV6jbAW9Mp.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/P0FXd6AwKsPzBiYwIZLsEUD5VrAp.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/ZqK39Xv3A0I-0ohwRkWV6jbAW9Mp.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/P0FXd6AwKsPzBiYwIZLsEUD5VrAp.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/0EOTP3mEDOzkS1q64hgbqKxftLId.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/TAq2STpvx67m5-UfPyxz4oWjUz8d.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/0EOTP3mEDOzkS1q64hgbqKxftLId.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/TAq2STpvx67m5-UfPyxz4oWjUz8d.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/-44zf-4QbDVnfxvdMOlS-62neokp.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/TAq2STpvx67m5-UfPyxz4oWjUz8p.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/-44zf-4QbDVnfxvdMOlS-62neokp.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/TAq2STpvx67m5-UfPyxz4oWjUz8p.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/1AUPZjF-DkxHBgstLlITifCjfHAd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/Tl-XR5iwVw4PrAcBNW_SjCclo-kd.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/1AUPZjF-DkxHBgstLlITifCjfHAd.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/Tl-XR5iwVw4PrAcBNW_SjCclo-kd.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/SS1oWs8uWRjwpAsBEkjHKIdli3op.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/Tl-XR5iwVw4PrAcBNW_SjCclo-kp.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/SS1oWs8uWRjwpAsBEkjHKIdli3op.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/Tl-XR5iwVw4PrAcBNW_SjCclo-kp.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/2FIZqjX_-rJNGXzNKJW_vC24H0Ud.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/V_NWUez2zLHkRy0FgbfpoMa561Ed.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/2FIZqjX_-rJNGXzNKJW_vC24H0Ud.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/V_NWUez2zLHkRy0FgbfpoMa561Ed.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/Rgu_Xpn3p1FJCz3744RsTXGnSX4p.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/V_NWUez2zLHkRy0FgbfpoMa561Ep.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/Rgu_Xpn3p1FJCz3744RsTXGnSX4p.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/V_NWUez2zLHkRy0FgbfpoMa561Ep.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/3vPrjfHdpkKcTeJZLymuPjPeJLod.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/X1p2mbSsnW7dQJV2bKNOREnOyY4d.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/3vPrjfHdpkKcTeJZLymuPjPeJLod.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/X1p2mbSsnW7dQJV2bKNOREnOyY4d.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/0EOTP3mEDOzkS1q64hgbqKxftLIp.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/X1p2mbSsnW7dQJV2bKNOREnOyY4p.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/0EOTP3mEDOzkS1q64hgbqKxftLIp.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/X1p2mbSsnW7dQJV2bKNOREnOyY4p.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/6zc8a3V6P8-0WJxogD3UrSBw7mEd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/YkEXBoi2NKObNUB8qZYLCaOkxj4d.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/6zc8a3V6P8-0WJxogD3UrSBw7mEd.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/YkEXBoi2NKObNUB8qZYLCaOkxj4d.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/-pKErZtYRVQsLNI-s6dhlLE9rN0p.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/YkEXBoi2NKObNUB8qZYLCaOkxj4p.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/-pKErZtYRVQsLNI-s6dhlLE9rN0p.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/YkEXBoi2NKObNUB8qZYLCaOkxj4p.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/9K4DX640tmrkmoBLflTkiNfPiZId.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/aukBLYh0oaCR-vLwva9Y2OCkQzUd.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/9K4DX640tmrkmoBLflTkiNfPiZId.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/aukBLYh0oaCR-vLwva9Y2OCkQzUd.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/9K4DX640tmrkmoBLflTkiNfPiZIp.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/aukBLYh0oaCR-vLwva9Y2OCkQzUp.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/9K4DX640tmrkmoBLflTkiNfPiZIp.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/aukBLYh0oaCR-vLwva9Y2OCkQzUp.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/AFQGXDiIsf-NM9eQ8n4eWMrVd1Yd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/b14wfAQdUmLf7zWEQ4kujKfjTQYd.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/AFQGXDiIsf-NM9eQ8n4eWMrVd1Yd.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/b14wfAQdUmLf7zWEQ4kujKfjTQYd.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/MgsQiSKNdqjh2tb2GSg9xpGhlMgp.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/b14wfAQdUmLf7zWEQ4kujKfjTQYp.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/MgsQiSKNdqjh2tb2GSg9xpGhlMgp.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/b14wfAQdUmLf7zWEQ4kujKfjTQYp.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/BNAgggSUW1I0e_gemsww4ojmprsd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/dPt59MZaEXeu_JPlWh7RDKtusxUd.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/BNAgggSUW1I0e_gemsww4ojmprsd.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/dPt59MZaEXeu_JPlWh7RDKtusxUd.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/eZwFGli01gSYZ44Ebb44wZJEZbMp.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/dPt59MZaEXeu_JPlWh7RDKtusxUp.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/eZwFGli01gSYZ44Ebb44wZJEZbMp.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/dPt59MZaEXeu_JPlWh7RDKtusxUp.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/Dg1e3Am5YrqqMqqkIYDk9ZjR1RYd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/eCuRoIFEDG25vAewgv-nf3GqNI0d.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/Dg1e3Am5YrqqMqqkIYDk9ZjR1RYd.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/eCuRoIFEDG25vAewgv-nf3GqNI0d.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/l0P1VTKdxzsxQxR0_jCuzcfb2UEp.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/eCuRoIFEDG25vAewgv-nf3GqNI0p.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/l0P1VTKdxzsxQxR0_jCuzcfb2UEp.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/eCuRoIFEDG25vAewgv-nf3GqNI0p.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/G6IjDtcuN86KCffyBg9hhLwxfM8d.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/iYmVif7LRRnGYE-Zeun_xHMvrVUd.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/G6IjDtcuN86KCffyBg9hhLwxfM8d.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/iYmVif7LRRnGYE-Zeun_xHMvrVUd.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/oaOhkesibNoJGQ_fNXCCFrNYNekp.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/iYmVif7LRRnGYE-Zeun_xHMvrVUp.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/oaOhkesibNoJGQ_fNXCCFrNYNekp.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/iYmVif7LRRnGYE-Zeun_xHMvrVUp.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/K2LUhs-8CtBjbprCCg-2KajWW0Qd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/iqU8I5FyZGvsOpUbA1lqu65aWYsd.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/K2LUhs-8CtBjbprCCg-2KajWW0Qd.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/iqU8I5FyZGvsOpUbA1lqu65aWYsd.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/jCqmmsqwCvwa4Nzg0x8RYjbjld8p.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/iqU8I5FyZGvsOpUbA1lqu65aWYsp.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/jCqmmsqwCvwa4Nzg0x8RYjbjld8p.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/iqU8I5FyZGvsOpUbA1lqu65aWYsp.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/LK5LTNCFyhwaQZUlosrnunT2xZId.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/j6pwo65gaiPoea-wYOGH4DewgEcd.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/LK5LTNCFyhwaQZUlosrnunT2xZId.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/j6pwo65gaiPoea-wYOGH4DewgEcd.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/smVcGh3dB4hFNj3JUwzxfAKA5NQp.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/j6pwo65gaiPoea-wYOGH4DewgEcp.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/smVcGh3dB4hFNj3JUwzxfAKA5NQp.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/j6pwo65gaiPoea-wYOGH4DewgEcp.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/LN9DpJcyR6CE_I14ywn3ncJAeXId.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/lsw_ecgYyohO6CkqAqCk2bJqNuMd.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/LN9DpJcyR6CE_I14ywn3ncJAeXId.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/lsw_ecgYyohO6CkqAqCk2bJqNuMd.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/P6lyIvtEaIp3hBgXmHLrvNf9nhop.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/lsw_ecgYyohO6CkqAqCk2bJqNuMp.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/P6lyIvtEaIp3hBgXmHLrvNf9nhop.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/lsw_ecgYyohO6CkqAqCk2bJqNuMp.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/MgsQiSKNdqjh2tb2GSg9xpGhlMgd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/ny2aRTbOyjYb8JDFd4dItADtN4Id.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/MgsQiSKNdqjh2tb2GSg9xpGhlMgd.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/ny2aRTbOyjYb8JDFd4dItADtN4Id.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/mZYgr5isIaGLkE3fNy5zsSlr_38p.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/ny2aRTbOyjYb8JDFd4dItADtN4Ip.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/mZYgr5isIaGLkE3fNy5zsSlr_38p.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/ny2aRTbOyjYb8JDFd4dItADtN4Ip.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/P6lyIvtEaIp3hBgXmHLrvNf9nhod.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/oLERgIPUw52TmzFNtvUaq1v3Hykd.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/P6lyIvtEaIp3hBgXmHLrvNf9nhod.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/oLERgIPUw52TmzFNtvUaq1v3Hykd.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/1AUPZjF-DkxHBgstLlITifCjfHAp.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/oLERgIPUw52TmzFNtvUaq1v3Hykp.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/1AUPZjF-DkxHBgstLlITifCjfHAp.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/oLERgIPUw52TmzFNtvUaq1v3Hykp.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/R9uo8TWlIFmDZ_U2L3brdMMNpmUd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/pegGFSZoGR96d4YxbbK-T9-uicsd.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/R9uo8TWlIFmDZ_U2L3brdMMNpmUd.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/pegGFSZoGR96d4YxbbK-T9-uicsd.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/-3QAULuhtItSD7_pHpYyutizqKcp.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/pegGFSZoGR96d4YxbbK-T9-uicsp.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/-3QAULuhtItSD7_pHpYyutizqKcp.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/pegGFSZoGR96d4YxbbK-T9-uicsp.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/Rgu_Xpn3p1FJCz3744RsTXGnSX4d.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/qMOfCxA6oAYXN36hOE_emzY7-K4d.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/Rgu_Xpn3p1FJCz3744RsTXGnSX4d.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/qMOfCxA6oAYXN36hOE_emzY7-K4d.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/BNAgggSUW1I0e_gemsww4ojmprsp.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/qMOfCxA6oAYXN36hOE_emzY7-K4p.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/BNAgggSUW1I0e_gemsww4ojmprsp.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/qMOfCxA6oAYXN36hOE_emzY7-K4p.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/SS1oWs8uWRjwpAsBEkjHKIdli3od.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/sgs-V8op5u7eBxQMg_dpYIMx_r0d.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/SS1oWs8uWRjwpAsBEkjHKIdli3od.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/sgs-V8op5u7eBxQMg_dpYIMx_r0d.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/K2LUhs-8CtBjbprCCg-2KajWW0Qp.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/sgs-V8op5u7eBxQMg_dpYIMx_r0p.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/K2LUhs-8CtBjbprCCg-2KajWW0Qp.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/sgs-V8op5u7eBxQMg_dpYIMx_r0p.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/T2fYvrqhpvDyuKc8Y7wsu2vaPcUd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/wlzHFGaUXvZky7Iz4g5QWWPjTZod.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/T2fYvrqhpvDyuKc8Y7wsu2vaPcUd.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/wlzHFGaUXvZky7Iz4g5QWWPjTZod.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/YAj-Sg2BTfUPlNkFYhjGuYYGyWIp.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/wlzHFGaUXvZky7Iz4g5QWWPjTZop.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/YAj-Sg2BTfUPlNkFYhjGuYYGyWIp.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/wlzHFGaUXvZky7Iz4g5QWWPjTZop.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/TcR3rZOrVrHqDGA6Ls0qBpAOHD0d.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/xjfA1VBG4pGbE5V9ER3DoEdSXfcd.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/TcR3rZOrVrHqDGA6Ls0qBpAOHD0d.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/xjfA1VBG4pGbE5V9ER3DoEdSXfcd.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/lrtTrV3gL6zhRL5HQl_mNRj1jiIp.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/xjfA1VBG4pGbE5V9ER3DoEdSXfcp.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/lrtTrV3gL6zhRL5HQl_mNRj1jiIp.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/xjfA1VBG4pGbE5V9ER3DoEdSXfcp.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/Vfp2GuJhjHgccgR8Joly_nEH3uUd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/zE7jRze1WGVc9GxZNzvBJKkpyN0d.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/Vfp2GuJhjHgccgR8Joly_nEH3uUd.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/zE7jRze1WGVc9GxZNzvBJKkpyN0d.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/YhkhMqMTT1rlsumwiMqdjRVcg2Qp.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/zE7jRze1WGVc9GxZNzvBJKkpyN0p.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/YhkhMqMTT1rlsumwiMqdjRVcg2Qp.xml rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/zE7jRze1WGVc9GxZNzvBJKkpyN0p.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/XeBJu8m_oaoL52pMHUpwH6lM5Fod.xml b/resources/project/TMR2PLz8-jf_t72xY3w-eiAkMwc/3HnVzJ8uifpbOyg21-U4RCJN4GQd.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/XeBJu8m_oaoL52pMHUpwH6lM5Fod.xml rename to resources/project/TMR2PLz8-jf_t72xY3w-eiAkMwc/3HnVzJ8uifpbOyg21-U4RCJN4GQd.xml diff --git a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/888MRa7OWq3oIOyCKPp0I2UIiKAp.xml b/resources/project/TMR2PLz8-jf_t72xY3w-eiAkMwc/3HnVzJ8uifpbOyg21-U4RCJN4GQp.xml similarity index 100% rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/888MRa7OWq3oIOyCKPp0I2UIiKAp.xml rename to resources/project/TMR2PLz8-jf_t72xY3w-eiAkMwc/3HnVzJ8uifpbOyg21-U4RCJN4GQp.xml diff --git a/resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/rfOlYVfPM98QhiJbvbuozkKdZQkd.xml b/resources/project/TMR2PLz8-jf_t72xY3w-eiAkMwc/7zdx9TfzV1uKVmlJRWzGdJyAcSYd.xml similarity index 100% rename from resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/rfOlYVfPM98QhiJbvbuozkKdZQkd.xml rename to resources/project/TMR2PLz8-jf_t72xY3w-eiAkMwc/7zdx9TfzV1uKVmlJRWzGdJyAcSYd.xml diff --git a/resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/rfOlYVfPM98QhiJbvbuozkKdZQkp.xml b/resources/project/TMR2PLz8-jf_t72xY3w-eiAkMwc/7zdx9TfzV1uKVmlJRWzGdJyAcSYp.xml similarity index 100% rename from resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/rfOlYVfPM98QhiJbvbuozkKdZQkp.xml rename to resources/project/TMR2PLz8-jf_t72xY3w-eiAkMwc/7zdx9TfzV1uKVmlJRWzGdJyAcSYp.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/YAj-Sg2BTfUPlNkFYhjGuYYGyWId.xml b/resources/project/TMR2PLz8-jf_t72xY3w-eiAkMwc/DAP2fOCUoOsvQKZryp7QTWtvdegd.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/YAj-Sg2BTfUPlNkFYhjGuYYGyWId.xml rename to resources/project/TMR2PLz8-jf_t72xY3w-eiAkMwc/DAP2fOCUoOsvQKZryp7QTWtvdegd.xml diff --git a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/zRzAsvX6Os0bPYij1y_5pG-94Dgp.xml b/resources/project/TMR2PLz8-jf_t72xY3w-eiAkMwc/DAP2fOCUoOsvQKZryp7QTWtvdegp.xml similarity index 100% rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/zRzAsvX6Os0bPYij1y_5pG-94Dgp.xml rename to resources/project/TMR2PLz8-jf_t72xY3w-eiAkMwc/DAP2fOCUoOsvQKZryp7QTWtvdegp.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/YhkhMqMTT1rlsumwiMqdjRVcg2Qd.xml b/resources/project/TMR2PLz8-jf_t72xY3w-eiAkMwc/ccZjrHRR1lFL21N1ZWdseSM37NQd.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/YhkhMqMTT1rlsumwiMqdjRVcg2Qd.xml rename to resources/project/TMR2PLz8-jf_t72xY3w-eiAkMwc/ccZjrHRR1lFL21N1ZWdseSM37NQd.xml diff --git a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/6GJfsBaU_5DGhbimCNcp3Txx-v8p.xml b/resources/project/TMR2PLz8-jf_t72xY3w-eiAkMwc/ccZjrHRR1lFL21N1ZWdseSM37NQp.xml similarity index 100% rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/6GJfsBaU_5DGhbimCNcp3Txx-v8p.xml rename to resources/project/TMR2PLz8-jf_t72xY3w-eiAkMwc/ccZjrHRR1lFL21N1ZWdseSM37NQp.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/ZqK39Xv3A0I-0ohwRkWV6jbAW9Md.xml b/resources/project/TMR2PLz8-jf_t72xY3w-eiAkMwc/omg-Qmyr2Hpg5ztIiWxX0q9_f_sd.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/ZqK39Xv3A0I-0ohwRkWV6jbAW9Md.xml rename to resources/project/TMR2PLz8-jf_t72xY3w-eiAkMwc/omg-Qmyr2Hpg5ztIiWxX0q9_f_sd.xml diff --git a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/iPx7LMwL8D8SvprpBzhO3qxwy0Ip.xml b/resources/project/TMR2PLz8-jf_t72xY3w-eiAkMwc/omg-Qmyr2Hpg5ztIiWxX0q9_f_sp.xml similarity index 100% rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/iPx7LMwL8D8SvprpBzhO3qxwy0Ip.xml rename to resources/project/TMR2PLz8-jf_t72xY3w-eiAkMwc/omg-Qmyr2Hpg5ztIiWxX0q9_f_sp.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/aSbr7z_N-_xgJQSr5hrZLyKPz4cd.xml b/resources/project/TMR2PLz8-jf_t72xY3w-eiAkMwc/rNLU-8Md7l99hBksJZlE3as2Y98d.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/aSbr7z_N-_xgJQSr5hrZLyKPz4cd.xml rename to resources/project/TMR2PLz8-jf_t72xY3w-eiAkMwc/rNLU-8Md7l99hBksJZlE3as2Y98d.xml diff --git a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/HinGYaWsjaNlqTK_j5cQYUlmn_4p.xml b/resources/project/TMR2PLz8-jf_t72xY3w-eiAkMwc/rNLU-8Md7l99hBksJZlE3as2Y98p.xml similarity index 100% rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/HinGYaWsjaNlqTK_j5cQYUlmn_4p.xml rename to resources/project/TMR2PLz8-jf_t72xY3w-eiAkMwc/rNLU-8Md7l99hBksJZlE3as2Y98p.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/aj3y-ODStp8yUzt7MUXbgCsT1OYd.xml b/resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/8FB9zoKe0-Lw7d_EeJv8WYCX-50d.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/aj3y-ODStp8yUzt7MUXbgCsT1OYd.xml rename to resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/8FB9zoKe0-Lw7d_EeJv8WYCX-50d.xml diff --git a/resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/YDXlBTZHLxkb4xHzi3Nh_OCFRwYp.xml b/resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/8FB9zoKe0-Lw7d_EeJv8WYCX-50p.xml similarity index 100% rename from resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/YDXlBTZHLxkb4xHzi3Nh_OCFRwYp.xml rename to resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/8FB9zoKe0-Lw7d_EeJv8WYCX-50p.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/eZwFGli01gSYZ44Ebb44wZJEZbMd.xml b/resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/90YW2GOol29Apu9oHzv3q7eI8ZMd.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/eZwFGli01gSYZ44Ebb44wZJEZbMd.xml rename to resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/90YW2GOol29Apu9oHzv3q7eI8ZMd.xml diff --git a/resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/418XLOPub0IQmO32_fwbHEZ9aWQp.xml b/resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/90YW2GOol29Apu9oHzv3q7eI8ZMp.xml similarity index 100% rename from resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/418XLOPub0IQmO32_fwbHEZ9aWQp.xml rename to resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/90YW2GOol29Apu9oHzv3q7eI8ZMp.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/j0ffgGF4ix7R_1weXjtRLJ2m60od.xml b/resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/F1UMXHKWKyyeAiNCKU0ON-Vy2ugd.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/j0ffgGF4ix7R_1weXjtRLJ2m60od.xml rename to resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/F1UMXHKWKyyeAiNCKU0ON-Vy2ugd.xml diff --git a/resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/jL11JwpwvA44_BZZe0gbxo4n_7cp.xml b/resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/F1UMXHKWKyyeAiNCKU0ON-Vy2ugp.xml similarity index 100% rename from resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/jL11JwpwvA44_BZZe0gbxo4n_7cp.xml rename to resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/F1UMXHKWKyyeAiNCKU0ON-Vy2ugp.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/jCqmmsqwCvwa4Nzg0x8RYjbjld8d.xml b/resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/SkcnLeu8zuRRgbMYy4nnaoFftigd.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/jCqmmsqwCvwa4Nzg0x8RYjbjld8d.xml rename to resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/SkcnLeu8zuRRgbMYy4nnaoFftigd.xml diff --git a/resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/zAMhJ-uBIliAXbSkNK3rdwXDaHop.xml b/resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/SkcnLeu8zuRRgbMYy4nnaoFftigp.xml similarity index 100% rename from resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/zAMhJ-uBIliAXbSkNK3rdwXDaHop.xml rename to resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/SkcnLeu8zuRRgbMYy4nnaoFftigp.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/l0P1VTKdxzsxQxR0_jCuzcfb2UEd.xml b/resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/X3U50T46GsNoWWkV12vFPTU5IkAd.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/l0P1VTKdxzsxQxR0_jCuzcfb2UEd.xml rename to resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/X3U50T46GsNoWWkV12vFPTU5IkAd.xml diff --git a/resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/anHfLHy8dClzx6n_jtHyG6OnAKgp.xml b/resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/X3U50T46GsNoWWkV12vFPTU5IkAp.xml similarity index 100% rename from resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/anHfLHy8dClzx6n_jtHyG6OnAKgp.xml rename to resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/X3U50T46GsNoWWkV12vFPTU5IkAp.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/lrtTrV3gL6zhRL5HQl_mNRj1jiId.xml b/resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/_p4C4Xaqbc2CySSpIr_XPlz39_8d.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/lrtTrV3gL6zhRL5HQl_mNRj1jiId.xml rename to resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/_p4C4Xaqbc2CySSpIr_XPlz39_8d.xml diff --git a/resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/fEvuKgmkueTDlYz7GM2hOxML--sp.xml b/resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/_p4C4Xaqbc2CySSpIr_XPlz39_8p.xml similarity index 100% rename from resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/fEvuKgmkueTDlYz7GM2hOxML--sp.xml rename to resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/_p4C4Xaqbc2CySSpIr_XPlz39_8p.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/mZYgr5isIaGLkE3fNy5zsSlr_38d.xml b/resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/bApZCS4bpdgHa0xKhfxkSKy3kIcd.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/mZYgr5isIaGLkE3fNy5zsSlr_38d.xml rename to resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/bApZCS4bpdgHa0xKhfxkSKy3kIcd.xml diff --git a/resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/AikK6lr_xPm04OOxDRyD3uEU6LYp.xml b/resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/bApZCS4bpdgHa0xKhfxkSKy3kIcp.xml similarity index 100% rename from resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/AikK6lr_xPm04OOxDRyD3uEU6LYp.xml rename to resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/bApZCS4bpdgHa0xKhfxkSKy3kIcp.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/n6yybQJW-SCbwLJB8CxlywM5r38d.xml b/resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/dC8vrHmGxRVrn1Vw7pYBaCb25tUd.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/n6yybQJW-SCbwLJB8CxlywM5r38d.xml rename to resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/dC8vrHmGxRVrn1Vw7pYBaCb25tUd.xml diff --git a/resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/upiRJjWx9gByawb3bF6KtCjux-sp.xml b/resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/dC8vrHmGxRVrn1Vw7pYBaCb25tUp.xml similarity index 100% rename from resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/upiRJjWx9gByawb3bF6KtCjux-sp.xml rename to resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/dC8vrHmGxRVrn1Vw7pYBaCb25tUp.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/oaOhkesibNoJGQ_fNXCCFrNYNekd.xml b/resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/slGRI8GHw3J7mTWp953EhtGCuB8d.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/oaOhkesibNoJGQ_fNXCCFrNYNekd.xml rename to resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/slGRI8GHw3J7mTWp953EhtGCuB8d.xml diff --git a/resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/7YGtSs8Fx6KVMJ6VnXcbLgutukUp.xml b/resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/slGRI8GHw3J7mTWp953EhtGCuB8p.xml similarity index 100% rename from resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/7YGtSs8Fx6KVMJ6VnXcbLgutukUp.xml rename to resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/slGRI8GHw3J7mTWp953EhtGCuB8p.xml diff --git a/resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/NVEzjmOMA7-YV0Y8D0XmF7N5Qe4d.xml b/resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/uIWV6l4leGqpetubaJSYVYzic4Ad.xml similarity index 100% rename from resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/NVEzjmOMA7-YV0Y8D0XmF7N5Qe4d.xml rename to resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/uIWV6l4leGqpetubaJSYVYzic4Ad.xml diff --git a/resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/NVEzjmOMA7-YV0Y8D0XmF7N5Qe4p.xml b/resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/uIWV6l4leGqpetubaJSYVYzic4Ap.xml similarity index 100% rename from resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/NVEzjmOMA7-YV0Y8D0XmF7N5Qe4p.xml rename to resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/uIWV6l4leGqpetubaJSYVYzic4Ap.xml diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/smVcGh3dB4hFNj3JUwzxfAKA5NQd.xml b/resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/wbJvniNn9pHSMYYk4-SDjMEBinAd.xml similarity index 100% rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/smVcGh3dB4hFNj3JUwzxfAKA5NQd.xml rename to resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/wbJvniNn9pHSMYYk4-SDjMEBinAd.xml diff --git a/resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/2ReW6jdy5OFPurN6zA_O_5XHgZ4p.xml b/resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/wbJvniNn9pHSMYYk4-SDjMEBinAp.xml similarity index 100% rename from resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/2ReW6jdy5OFPurN6zA_O_5XHgZ4p.xml rename to resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/wbJvniNn9pHSMYYk4-SDjMEBinAp.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/4lF9gcxRF1S305BdGqOMAHZcYgop.xml b/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/4lF9gcxRF1S305BdGqOMAHZcYgop.xml deleted file mode 100644 index 52948ae6d..000000000 --- a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/4lF9gcxRF1S305BdGqOMAHZcYgop.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/BXX9SqJTGGGO_CDj2AJCCAtV84kp.xml b/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/BXX9SqJTGGGO_CDj2AJCCAtV84kp.xml deleted file mode 100644 index 2fe31f857..000000000 --- a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/BXX9SqJTGGGO_CDj2AJCCAtV84kp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/BkckfovEtaH9G6qBgDPgKm8zKxgp.xml b/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/BkckfovEtaH9G6qBgDPgKm8zKxgp.xml deleted file mode 100644 index 3af797218..000000000 --- a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/BkckfovEtaH9G6qBgDPgKm8zKxgp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/EIE_vdm0VClM0NZZV517-DBaczEp.xml b/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/EIE_vdm0VClM0NZZV517-DBaczEp.xml deleted file mode 100644 index d98285461..000000000 --- a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/EIE_vdm0VClM0NZZV517-DBaczEp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/IWnqwSbZEiavwf7_s2jeWFKRUyMp.xml b/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/IWnqwSbZEiavwf7_s2jeWFKRUyMp.xml deleted file mode 100644 index aeced752b..000000000 --- a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/IWnqwSbZEiavwf7_s2jeWFKRUyMp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/WeWWFR_CW_fJH3v0Spn0rCmP6ccp.xml b/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/WeWWFR_CW_fJH3v0Spn0rCmP6ccp.xml deleted file mode 100644 index d568c1d5d..000000000 --- a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/WeWWFR_CW_fJH3v0Spn0rCmP6ccp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/Z_VaFjtshHcOoAprv8kGzuWPan8p.xml b/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/Z_VaFjtshHcOoAprv8kGzuWPan8p.xml deleted file mode 100644 index c90400873..000000000 --- a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/Z_VaFjtshHcOoAprv8kGzuWPan8p.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/lKVscSUfLpUJ7DMGKxZpl2cj8Cwp.xml b/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/lKVscSUfLpUJ7DMGKxZpl2cj8Cwp.xml deleted file mode 100644 index afbcaa4fe..000000000 --- a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/lKVscSUfLpUJ7DMGKxZpl2cj8Cwp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/sbvxc69GTsaxWR2ghAIgyxsRoK0p.xml b/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/sbvxc69GTsaxWR2ghAIgyxsRoK0p.xml deleted file mode 100644 index ef4268645..000000000 --- a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/sbvxc69GTsaxWR2ghAIgyxsRoK0p.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/u9fylSJ1j_4ckhUg5rn809_XS8cd.xml b/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/u9fylSJ1j_4ckhUg5rn809_XS8cd.xml deleted file mode 100644 index 7a6326b99..000000000 --- a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/u9fylSJ1j_4ckhUg5rn809_XS8cd.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/znnoG0GBXl0gWrZCcSzmlPlsYO4d.xml b/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/znnoG0GBXl0gWrZCcSzmlPlsYO4d.xml deleted file mode 100644 index 7a6326b99..000000000 --- a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/znnoG0GBXl0gWrZCcSzmlPlsYO4d.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/znnoG0GBXl0gWrZCcSzmlPlsYO4p.xml b/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/znnoG0GBXl0gWrZCcSzmlPlsYO4p.xml deleted file mode 100644 index c34538777..000000000 --- a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/znnoG0GBXl0gWrZCcSzmlPlsYO4p.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/R6m7AxFfSX7StHtVJ9WG44h96GM/D7EGYpvIiO_nGXQuTOYjsSP_JQwd.xml b/resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/BlsnL99CKaby8ck6n_pFKfx8TOEd.xml similarity index 100% rename from resources/project/R6m7AxFfSX7StHtVJ9WG44h96GM/D7EGYpvIiO_nGXQuTOYjsSP_JQwd.xml rename to resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/BlsnL99CKaby8ck6n_pFKfx8TOEd.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/REL0fj6PT2tNiayKyTrZEt9--acp.xml b/resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/BlsnL99CKaby8ck6n_pFKfx8TOEp.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/REL0fj6PT2tNiayKyTrZEt9--acp.xml rename to resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/BlsnL99CKaby8ck6n_pFKfx8TOEp.xml diff --git a/resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/2ReW6jdy5OFPurN6zA_O_5XHgZ4d.xml b/resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/F3Ymj-J16s_9PdjyJ8NmuydN3yId.xml similarity index 100% rename from resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/2ReW6jdy5OFPurN6zA_O_5XHgZ4d.xml rename to resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/F3Ymj-J16s_9PdjyJ8NmuydN3yId.xml diff --git a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/DpnGV8GaSUAjtUgrpQ5nUXOT5wYp.xml b/resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/F3Ymj-J16s_9PdjyJ8NmuydN3yIp.xml similarity index 100% rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/DpnGV8GaSUAjtUgrpQ5nUXOT5wYp.xml rename to resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/F3Ymj-J16s_9PdjyJ8NmuydN3yIp.xml diff --git a/resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/418XLOPub0IQmO32_fwbHEZ9aWQd.xml b/resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/GcqEp1PedF_qduXiylZH_t2MP1Qd.xml similarity index 100% rename from resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/418XLOPub0IQmO32_fwbHEZ9aWQd.xml rename to resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/GcqEp1PedF_qduXiylZH_t2MP1Qd.xml diff --git a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/J8XPAkZbsecOIp7IVKNIsrQZCHEp.xml b/resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/GcqEp1PedF_qduXiylZH_t2MP1Qp.xml similarity index 100% rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/J8XPAkZbsecOIp7IVKNIsrQZCHEp.xml rename to resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/GcqEp1PedF_qduXiylZH_t2MP1Qp.xml diff --git a/resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/7YGtSs8Fx6KVMJ6VnXcbLgutukUd.xml b/resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/NiQW_7jWHSEL9pyWwCHpUX03D48d.xml similarity index 100% rename from resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/7YGtSs8Fx6KVMJ6VnXcbLgutukUd.xml rename to resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/NiQW_7jWHSEL9pyWwCHpUX03D48d.xml diff --git a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/PUXt85kSoxl2GhAVUeaGOA1ui0sp.xml b/resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/NiQW_7jWHSEL9pyWwCHpUX03D48p.xml similarity index 100% rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/PUXt85kSoxl2GhAVUeaGOA1ui0sp.xml rename to resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/NiQW_7jWHSEL9pyWwCHpUX03D48p.xml diff --git a/resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/AikK6lr_xPm04OOxDRyD3uEU6LYd.xml b/resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/c9tjuQNdCrSrZPk-bi5TqtaIYCkd.xml similarity index 100% rename from resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/AikK6lr_xPm04OOxDRyD3uEU6LYd.xml rename to resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/c9tjuQNdCrSrZPk-bi5TqtaIYCkd.xml diff --git a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/Nh99zm6VyLbCagFVKH0kx0irGckp.xml b/resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/c9tjuQNdCrSrZPk-bi5TqtaIYCkp.xml similarity index 100% rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/Nh99zm6VyLbCagFVKH0kx0irGckp.xml rename to resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/c9tjuQNdCrSrZPk-bi5TqtaIYCkp.xml diff --git a/resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/YDXlBTZHLxkb4xHzi3Nh_OCFRwYd.xml b/resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/iCK3aCczRLHuNzxcr2qrQVK9PZ8d.xml similarity index 100% rename from resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/YDXlBTZHLxkb4xHzi3Nh_OCFRwYd.xml rename to resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/iCK3aCczRLHuNzxcr2qrQVK9PZ8d.xml diff --git a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/zWhjsX1e0jX9kixjwjfDU_xQo9Qp.xml b/resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/iCK3aCczRLHuNzxcr2qrQVK9PZ8p.xml similarity index 100% rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/zWhjsX1e0jX9kixjwjfDU_xQo9Qp.xml rename to resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/iCK3aCczRLHuNzxcr2qrQVK9PZ8p.xml diff --git a/resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/anHfLHy8dClzx6n_jtHyG6OnAKgd.xml b/resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/rt7-0zPCi_bp1caPhGK26hxMFlgd.xml similarity index 100% rename from resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/anHfLHy8dClzx6n_jtHyG6OnAKgd.xml rename to resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/rt7-0zPCi_bp1caPhGK26hxMFlgd.xml diff --git a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/5TVbg-DxiEwvaH5Bj9CH0ewcKu8p.xml b/resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/rt7-0zPCi_bp1caPhGK26hxMFlgp.xml similarity index 100% rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/5TVbg-DxiEwvaH5Bj9CH0ewcKu8p.xml rename to resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/rt7-0zPCi_bp1caPhGK26hxMFlgp.xml diff --git a/resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/fEvuKgmkueTDlYz7GM2hOxML--sd.xml b/resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/ur9Ezr2ljVCKuJkzloOS3DSlTPQd.xml similarity index 100% rename from resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/fEvuKgmkueTDlYz7GM2hOxML--sd.xml rename to resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/ur9Ezr2ljVCKuJkzloOS3DSlTPQd.xml diff --git a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/SLN8kBxZYH73a64wQLgTpFlpH-Up.xml b/resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/ur9Ezr2ljVCKuJkzloOS3DSlTPQp.xml similarity index 100% rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/SLN8kBxZYH73a64wQLgTpFlpH-Up.xml rename to resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/ur9Ezr2ljVCKuJkzloOS3DSlTPQp.xml diff --git a/resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/jL11JwpwvA44_BZZe0gbxo4n_7cd.xml b/resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/1U6jA8VW8gquO5tfdlwgKz-0Kjsd.xml similarity index 100% rename from resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/jL11JwpwvA44_BZZe0gbxo4n_7cd.xml rename to resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/1U6jA8VW8gquO5tfdlwgKz-0Kjsd.xml diff --git a/resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/1Fsij9kn0jCc5z8BpveJ5Pixotwp.xml b/resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/1U6jA8VW8gquO5tfdlwgKz-0Kjsp.xml similarity index 100% rename from resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/1Fsij9kn0jCc5z8BpveJ5Pixotwp.xml rename to resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/1U6jA8VW8gquO5tfdlwgKz-0Kjsp.xml diff --git a/resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/upiRJjWx9gByawb3bF6KtCjux-sd.xml b/resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/4tnQjmzGbGyuuipQczDui-LkM4Yd.xml similarity index 100% rename from resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/upiRJjWx9gByawb3bF6KtCjux-sd.xml rename to resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/4tnQjmzGbGyuuipQczDui-LkM4Yd.xml diff --git a/resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/dnwHH5dkTAu93LmDUIIdJRW11zUp.xml b/resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/4tnQjmzGbGyuuipQczDui-LkM4Yp.xml similarity index 100% rename from resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/dnwHH5dkTAu93LmDUIIdJRW11zUp.xml rename to resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/4tnQjmzGbGyuuipQczDui-LkM4Yp.xml diff --git a/resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/zAMhJ-uBIliAXbSkNK3rdwXDaHod.xml b/resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/GuR32HWNIsfL9QJxccN3h7ueHWUd.xml similarity index 100% rename from resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/zAMhJ-uBIliAXbSkNK3rdwXDaHod.xml rename to resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/GuR32HWNIsfL9QJxccN3h7ueHWUd.xml diff --git a/resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/29H25KnJzx-eH6hkyDOSNHx0NX0p.xml b/resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/GuR32HWNIsfL9QJxccN3h7ueHWUp.xml similarity index 100% rename from resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/29H25KnJzx-eH6hkyDOSNHx0NX0p.xml rename to resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/GuR32HWNIsfL9QJxccN3h7ueHWUp.xml diff --git a/resources/project/R6m7AxFfSX7StHtVJ9WG44h96GM/Drp6kay-WW1Z2AQWI8KW60j9HlQd.xml b/resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/N0H1Yk2c0hrEVlplassPmyyQIkgd.xml similarity index 100% rename from resources/project/R6m7AxFfSX7StHtVJ9WG44h96GM/Drp6kay-WW1Z2AQWI8KW60j9HlQd.xml rename to resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/N0H1Yk2c0hrEVlplassPmyyQIkgd.xml diff --git a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/kRJYdCzyq5zNeYLPJeKdZmiLSJEp.xml b/resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/N0H1Yk2c0hrEVlplassPmyyQIkgp.xml similarity index 100% rename from resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/kRJYdCzyq5zNeYLPJeKdZmiLSJEp.xml rename to resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/N0H1Yk2c0hrEVlplassPmyyQIkgp.xml diff --git a/resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/1Fsij9kn0jCc5z8BpveJ5Pixotwd.xml b/resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/ZhQd5_-iD72EYvyetRmslkcILwgd.xml similarity index 100% rename from resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/1Fsij9kn0jCc5z8BpveJ5Pixotwd.xml rename to resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/ZhQd5_-iD72EYvyetRmslkcILwgd.xml diff --git a/resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/qkgsXBKP6tNxSqIGkUXqoIOmfAYp.xml b/resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/ZhQd5_-iD72EYvyetRmslkcILwgp.xml similarity index 100% rename from resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/qkgsXBKP6tNxSqIGkUXqoIOmfAYp.xml rename to resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/ZhQd5_-iD72EYvyetRmslkcILwgp.xml diff --git a/resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/29H25KnJzx-eH6hkyDOSNHx0NX0d.xml b/resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/_DTS8noz4eF6eofLBJF_kJI36Rsd.xml similarity index 100% rename from resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/29H25KnJzx-eH6hkyDOSNHx0NX0d.xml rename to resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/_DTS8noz4eF6eofLBJF_kJI36Rsd.xml diff --git a/resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/3zKgrWYZ4jPOoOXQS_i9ShO4XZcp.xml b/resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/_DTS8noz4eF6eofLBJF_kJI36Rsp.xml similarity index 100% rename from resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/3zKgrWYZ4jPOoOXQS_i9ShO4XZcp.xml rename to resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/_DTS8noz4eF6eofLBJF_kJI36Rsp.xml diff --git a/resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/2NE3JJ7Cwpfj1Twd1MZdwGK46R8d.xml b/resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/ctkpfB9aQsWsqTtrG0AiSWOSsMgd.xml similarity index 100% rename from resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/2NE3JJ7Cwpfj1Twd1MZdwGK46R8d.xml rename to resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/ctkpfB9aQsWsqTtrG0AiSWOSsMgd.xml diff --git a/resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/cfs6_1u5uuKLLIe6KzyVp8pn_mUp.xml b/resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/ctkpfB9aQsWsqTtrG0AiSWOSsMgp.xml similarity index 100% rename from resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/cfs6_1u5uuKLLIe6KzyVp8pn_mUp.xml rename to resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/ctkpfB9aQsWsqTtrG0AiSWOSsMgp.xml diff --git a/resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/3L_f_vjNe-AFLP06A5juF8kMGLEd.xml b/resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/jaWSShTlT3hy9m_6EXad-HfUEqEd.xml similarity index 100% rename from resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/3L_f_vjNe-AFLP06A5juF8kMGLEd.xml rename to resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/jaWSShTlT3hy9m_6EXad-HfUEqEd.xml diff --git a/resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/3L_f_vjNe-AFLP06A5juF8kMGLEp.xml b/resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/jaWSShTlT3hy9m_6EXad-HfUEqEp.xml similarity index 100% rename from resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/3L_f_vjNe-AFLP06A5juF8kMGLEp.xml rename to resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/jaWSShTlT3hy9m_6EXad-HfUEqEp.xml diff --git a/resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/3zKgrWYZ4jPOoOXQS_i9ShO4XZcd.xml b/resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/se_u97Hhf2JBUhc4KXrIUB6oWjUd.xml similarity index 100% rename from resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/3zKgrWYZ4jPOoOXQS_i9ShO4XZcd.xml rename to resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/se_u97Hhf2JBUhc4KXrIUB6oWjUd.xml diff --git a/resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/rnnM4E9PpZDSGfoRKAwKIIqTIq0p.xml b/resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/se_u97Hhf2JBUhc4KXrIUB6oWjUp.xml similarity index 100% rename from resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/rnnM4E9PpZDSGfoRKAwKIIqTIq0p.xml rename to resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/se_u97Hhf2JBUhc4KXrIUB6oWjUp.xml diff --git a/resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/a95L4gsH7xba014Rtg5I-RtazfUd.xml b/resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/vScIuMZR1UoxCROtefKx0SSqHksd.xml similarity index 100% rename from resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/a95L4gsH7xba014Rtg5I-RtazfUd.xml rename to resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/vScIuMZR1UoxCROtefKx0SSqHksd.xml diff --git a/resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/a95L4gsH7xba014Rtg5I-RtazfUp.xml b/resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/vScIuMZR1UoxCROtefKx0SSqHksp.xml similarity index 100% rename from resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/a95L4gsH7xba014Rtg5I-RtazfUp.xml rename to resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/vScIuMZR1UoxCROtefKx0SSqHksp.xml diff --git a/resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/cfs6_1u5uuKLLIe6KzyVp8pn_mUd.xml b/resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/xBDRN0xkCxYYkWS1cMcY9ajEQ5od.xml similarity index 100% rename from resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/cfs6_1u5uuKLLIe6KzyVp8pn_mUd.xml rename to resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/xBDRN0xkCxYYkWS1cMcY9ajEQ5od.xml diff --git a/resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/2NE3JJ7Cwpfj1Twd1MZdwGK46R8p.xml b/resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/xBDRN0xkCxYYkWS1cMcY9ajEQ5op.xml similarity index 100% rename from resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/2NE3JJ7Cwpfj1Twd1MZdwGK46R8p.xml rename to resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/xBDRN0xkCxYYkWS1cMcY9ajEQ5op.xml diff --git a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/7U58VHMhrmTgIuozFzLR1XBJo8Id.xml b/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/7U58VHMhrmTgIuozFzLR1XBJo8Id.xml deleted file mode 100644 index 7a6326b99..000000000 --- a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/7U58VHMhrmTgIuozFzLR1XBJo8Id.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/9ULi2Alw-JmXjkCxz2ppBf5D2nUd.xml b/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/9ULi2Alw-JmXjkCxz2ppBf5D2nUd.xml deleted file mode 100644 index 7a6326b99..000000000 --- a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/9ULi2Alw-JmXjkCxz2ppBf5D2nUd.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/9ULi2Alw-JmXjkCxz2ppBf5D2nUp.xml b/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/9ULi2Alw-JmXjkCxz2ppBf5D2nUp.xml deleted file mode 100644 index 369746ec3..000000000 --- a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/9ULi2Alw-JmXjkCxz2ppBf5D2nUp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/9orjn84dnpAKkkuchjb6ZXb4lYod.xml b/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/9orjn84dnpAKkkuchjb6ZXb4lYod.xml deleted file mode 100644 index 7a6326b99..000000000 --- a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/9orjn84dnpAKkkuchjb6ZXb4lYod.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/Ckzn7bzQCXjaNEGiflifGGEk0-Ad.xml b/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/Ckzn7bzQCXjaNEGiflifGGEk0-Ad.xml deleted file mode 100644 index 7a6326b99..000000000 --- a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/Ckzn7bzQCXjaNEGiflifGGEk0-Ad.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/Ckzn7bzQCXjaNEGiflifGGEk0-Ap.xml b/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/Ckzn7bzQCXjaNEGiflifGGEk0-Ap.xml deleted file mode 100644 index 28a359efa..000000000 --- a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/Ckzn7bzQCXjaNEGiflifGGEk0-Ap.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/Fuv4y78db8mLAjl6Fcl6HGZDRo0d.xml b/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/Fuv4y78db8mLAjl6Fcl6HGZDRo0d.xml deleted file mode 100644 index 7a6326b99..000000000 --- a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/Fuv4y78db8mLAjl6Fcl6HGZDRo0d.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/Fuv4y78db8mLAjl6Fcl6HGZDRo0p.xml b/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/Fuv4y78db8mLAjl6Fcl6HGZDRo0p.xml deleted file mode 100644 index cb241ac34..000000000 --- a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/Fuv4y78db8mLAjl6Fcl6HGZDRo0p.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/Q_eYts4Jx8lwh_76CdRDIxbbdlkd.xml b/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/Q_eYts4Jx8lwh_76CdRDIxbbdlkd.xml deleted file mode 100644 index 7a6326b99..000000000 --- a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/Q_eYts4Jx8lwh_76CdRDIxbbdlkd.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/T3r09B-NxK1oaHW6j-rPgd2Qj6Ud.xml b/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/T3r09B-NxK1oaHW6j-rPgd2Qj6Ud.xml deleted file mode 100644 index 7a6326b99..000000000 --- a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/T3r09B-NxK1oaHW6j-rPgd2Qj6Ud.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/T3r09B-NxK1oaHW6j-rPgd2Qj6Up.xml b/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/T3r09B-NxK1oaHW6j-rPgd2Qj6Up.xml deleted file mode 100644 index 4855201ea..000000000 --- a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/T3r09B-NxK1oaHW6j-rPgd2Qj6Up.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/WtpDLoJHlDx008KY8cwkBcD2AT8d.xml b/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/WtpDLoJHlDx008KY8cwkBcD2AT8d.xml deleted file mode 100644 index 7a6326b99..000000000 --- a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/WtpDLoJHlDx008KY8cwkBcD2AT8d.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/WtpDLoJHlDx008KY8cwkBcD2AT8p.xml b/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/WtpDLoJHlDx008KY8cwkBcD2AT8p.xml deleted file mode 100644 index e191e3610..000000000 --- a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/WtpDLoJHlDx008KY8cwkBcD2AT8p.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/XbzuDmDFV-LdTDKCBgtbIoNIeDAd.xml b/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/XbzuDmDFV-LdTDKCBgtbIoNIeDAd.xml deleted file mode 100644 index 7a6326b99..000000000 --- a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/XbzuDmDFV-LdTDKCBgtbIoNIeDAd.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/XbzuDmDFV-LdTDKCBgtbIoNIeDAp.xml b/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/XbzuDmDFV-LdTDKCBgtbIoNIeDAp.xml deleted file mode 100644 index 538dec7d5..000000000 --- a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/XbzuDmDFV-LdTDKCBgtbIoNIeDAp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/aN6B9nVEXMPwk5nipy2pReqfe9kd.xml b/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/aN6B9nVEXMPwk5nipy2pReqfe9kd.xml deleted file mode 100644 index 7a6326b99..000000000 --- a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/aN6B9nVEXMPwk5nipy2pReqfe9kd.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/aN6B9nVEXMPwk5nipy2pReqfe9kp.xml b/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/aN6B9nVEXMPwk5nipy2pReqfe9kp.xml deleted file mode 100644 index 4691a4ff7..000000000 --- a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/aN6B9nVEXMPwk5nipy2pReqfe9kp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/dKRagt73aqMStWRIRrbSi8CTiWEd.xml b/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/dKRagt73aqMStWRIRrbSi8CTiWEd.xml deleted file mode 100644 index 7a6326b99..000000000 --- a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/dKRagt73aqMStWRIRrbSi8CTiWEd.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/dKRagt73aqMStWRIRrbSi8CTiWEp.xml b/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/dKRagt73aqMStWRIRrbSi8CTiWEp.xml deleted file mode 100644 index 5ce97afbd..000000000 --- a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/dKRagt73aqMStWRIRrbSi8CTiWEp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/jjYwKTXjk1ClTK05aLas9VzF5YAd.xml b/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/jjYwKTXjk1ClTK05aLas9VzF5YAd.xml deleted file mode 100644 index 7a6326b99..000000000 --- a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/jjYwKTXjk1ClTK05aLas9VzF5YAd.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/jjYwKTXjk1ClTK05aLas9VzF5YAp.xml b/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/jjYwKTXjk1ClTK05aLas9VzF5YAp.xml deleted file mode 100644 index 2d8335d28..000000000 --- a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/jjYwKTXjk1ClTK05aLas9VzF5YAp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/vok99kMS3Be0cBX4FPi-Zspy6Y0d.xml b/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/vok99kMS3Be0cBX4FPi-Zspy6Y0d.xml deleted file mode 100644 index 7a6326b99..000000000 --- a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/vok99kMS3Be0cBX4FPi-Zspy6Y0d.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/R6m7AxFfSX7StHtVJ9WG44h96GM/dYvx6wp33Ts8hX1S_rzdiQ3NP4Ad.xml b/resources/project/fkKGPyMFLRd9q8sWiOz4qApnVgk/1BSDFN_bp_8Wq1u5OO4MK7xHWFEd.xml similarity index 100% rename from resources/project/R6m7AxFfSX7StHtVJ9WG44h96GM/dYvx6wp33Ts8hX1S_rzdiQ3NP4Ad.xml rename to resources/project/fkKGPyMFLRd9q8sWiOz4qApnVgk/1BSDFN_bp_8Wq1u5OO4MK7xHWFEd.xml diff --git a/resources/project/ieR1AbFuIuiTNyC8z83kLz-KjxQ/QgeUCp8CiwXPaszmjk6YesnqDGkp.xml b/resources/project/fkKGPyMFLRd9q8sWiOz4qApnVgk/1BSDFN_bp_8Wq1u5OO4MK7xHWFEp.xml similarity index 100% rename from resources/project/ieR1AbFuIuiTNyC8z83kLz-KjxQ/QgeUCp8CiwXPaszmjk6YesnqDGkp.xml rename to resources/project/fkKGPyMFLRd9q8sWiOz4qApnVgk/1BSDFN_bp_8Wq1u5OO4MK7xHWFEp.xml diff --git a/resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/dnwHH5dkTAu93LmDUIIdJRW11zUd.xml b/resources/project/fkKGPyMFLRd9q8sWiOz4qApnVgk/5mzsc4L1QMwcGyCp10S_0jXV0UYd.xml similarity index 100% rename from resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/dnwHH5dkTAu93LmDUIIdJRW11zUd.xml rename to resources/project/fkKGPyMFLRd9q8sWiOz4qApnVgk/5mzsc4L1QMwcGyCp10S_0jXV0UYd.xml diff --git a/resources/project/ieR1AbFuIuiTNyC8z83kLz-KjxQ/ogNzaRYtwam4lR-ZN7eI6m_SXeYp.xml b/resources/project/fkKGPyMFLRd9q8sWiOz4qApnVgk/5mzsc4L1QMwcGyCp10S_0jXV0UYp.xml similarity index 100% rename from resources/project/ieR1AbFuIuiTNyC8z83kLz-KjxQ/ogNzaRYtwam4lR-ZN7eI6m_SXeYp.xml rename to resources/project/fkKGPyMFLRd9q8sWiOz4qApnVgk/5mzsc4L1QMwcGyCp10S_0jXV0UYp.xml diff --git a/resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/qkgsXBKP6tNxSqIGkUXqoIOmfAYd.xml b/resources/project/fkKGPyMFLRd9q8sWiOz4qApnVgk/nYxqFWWUsPUVCBz8zZlw7QYCOvod.xml similarity index 100% rename from resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/qkgsXBKP6tNxSqIGkUXqoIOmfAYd.xml rename to resources/project/fkKGPyMFLRd9q8sWiOz4qApnVgk/nYxqFWWUsPUVCBz8zZlw7QYCOvod.xml diff --git a/resources/project/ieR1AbFuIuiTNyC8z83kLz-KjxQ/gU6nQ9al6ASHLbEF0HmBERcYd4Mp.xml b/resources/project/fkKGPyMFLRd9q8sWiOz4qApnVgk/nYxqFWWUsPUVCBz8zZlw7QYCOvop.xml similarity index 100% rename from resources/project/ieR1AbFuIuiTNyC8z83kLz-KjxQ/gU6nQ9al6ASHLbEF0HmBERcYd4Mp.xml rename to resources/project/fkKGPyMFLRd9q8sWiOz4qApnVgk/nYxqFWWUsPUVCBz8zZlw7QYCOvop.xml diff --git a/resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/rnnM4E9PpZDSGfoRKAwKIIqTIq0d.xml b/resources/project/fkKGPyMFLRd9q8sWiOz4qApnVgk/uR7UAO4rsdHCU9T8lZjoYW7a32Ud.xml similarity index 100% rename from resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/rnnM4E9PpZDSGfoRKAwKIIqTIq0d.xml rename to resources/project/fkKGPyMFLRd9q8sWiOz4qApnVgk/uR7UAO4rsdHCU9T8lZjoYW7a32Ud.xml diff --git a/resources/project/ieR1AbFuIuiTNyC8z83kLz-KjxQ/xmaLmb7wr2-EmOlPBo1ae7Q1eEwp.xml b/resources/project/fkKGPyMFLRd9q8sWiOz4qApnVgk/uR7UAO4rsdHCU9T8lZjoYW7a32Up.xml similarity index 100% rename from resources/project/ieR1AbFuIuiTNyC8z83kLz-KjxQ/xmaLmb7wr2-EmOlPBo1ae7Q1eEwp.xml rename to resources/project/fkKGPyMFLRd9q8sWiOz4qApnVgk/uR7UAO4rsdHCU9T8lZjoYW7a32Up.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/7AZXpb090n98KOSsx01-zOUfRQgd.xml b/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/PL5Ga2JyphJjO2f0V1lt1v9mOfQd.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/7AZXpb090n98KOSsx01-zOUfRQgd.xml rename to resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/PL5Ga2JyphJjO2f0V1lt1v9mOfQd.xml diff --git a/resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/wTRZ9nfLywZT2m22SOEZoykCg4Qd.xml b/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/PL5Ga2JyphJjO2f0V1lt1v9mOfQp.xml similarity index 53% rename from resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/wTRZ9nfLywZT2m22SOEZoykCg4Qd.xml rename to resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/PL5Ga2JyphJjO2f0V1lt1v9mOfQp.xml index a75f7a81b..17b3aa625 100644 --- a/resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/wTRZ9nfLywZT2m22SOEZoykCg4Qd.xml +++ b/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/PL5Ga2JyphJjO2f0V1lt1v9mOfQp.xml @@ -1,2 +1,2 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4p.xml b/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4p.xml deleted file mode 100644 index d9f3deee4..000000000 --- a/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4p.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/ieR1AbFuIuiTNyC8z83kLz-KjxQ/gU6nQ9al6ASHLbEF0HmBERcYd4Md.xml b/resources/project/ieR1AbFuIuiTNyC8z83kLz-KjxQ/gU6nQ9al6ASHLbEF0HmBERcYd4Md.xml deleted file mode 100644 index 7a6326b99..000000000 --- a/resources/project/ieR1AbFuIuiTNyC8z83kLz-KjxQ/gU6nQ9al6ASHLbEF0HmBERcYd4Md.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/ieR1AbFuIuiTNyC8z83kLz-KjxQ/ogNzaRYtwam4lR-ZN7eI6m_SXeYd.xml b/resources/project/ieR1AbFuIuiTNyC8z83kLz-KjxQ/ogNzaRYtwam4lR-ZN7eI6m_SXeYd.xml deleted file mode 100644 index 7a6326b99..000000000 --- a/resources/project/ieR1AbFuIuiTNyC8z83kLz-KjxQ/ogNzaRYtwam4lR-ZN7eI6m_SXeYd.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/ieR1AbFuIuiTNyC8z83kLz-KjxQ/xmaLmb7wr2-EmOlPBo1ae7Q1eEwd.xml b/resources/project/ieR1AbFuIuiTNyC8z83kLz-KjxQ/xmaLmb7wr2-EmOlPBo1ae7Q1eEwd.xml deleted file mode 100644 index 7a6326b99..000000000 --- a/resources/project/ieR1AbFuIuiTNyC8z83kLz-KjxQ/xmaLmb7wr2-EmOlPBo1ae7Q1eEwd.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/-xMsha79hmE0qOhn3z2Y4wwJaNkd.xml b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/-IEsxSr0pUR0RE2Q2b5wpQX0KkEd.xml similarity index 100% rename from resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/-xMsha79hmE0qOhn3z2Y4wwJaNkd.xml rename to resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/-IEsxSr0pUR0RE2Q2b5wpQX0KkEd.xml diff --git a/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/-IEsxSr0pUR0RE2Q2b5wpQX0KkEp.xml b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/-IEsxSr0pUR0RE2Q2b5wpQX0KkEp.xml new file mode 100644 index 000000000..a0d520a12 --- /dev/null +++ b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/-IEsxSr0pUR0RE2Q2b5wpQX0KkEp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/0oyoesBN2_oayH0i7gtHJp890Esd.xml b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/3DhOOe0Tl7hipLJtC66oxocIr9cd.xml similarity index 100% rename from resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/0oyoesBN2_oayH0i7gtHJp890Esd.xml rename to resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/3DhOOe0Tl7hipLJtC66oxocIr9cd.xml diff --git a/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/3DhOOe0Tl7hipLJtC66oxocIr9cp.xml b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/3DhOOe0Tl7hipLJtC66oxocIr9cp.xml new file mode 100644 index 000000000..ec17f7ecc --- /dev/null +++ b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/3DhOOe0Tl7hipLJtC66oxocIr9cp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/G2x_hSFRzJNj2RwsZ4N6TWYsHmYd.xml b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/AxKLxQn_Q5ExtyTFauEpYntfsssd.xml similarity index 100% rename from resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/G2x_hSFRzJNj2RwsZ4N6TWYsHmYd.xml rename to resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/AxKLxQn_Q5ExtyTFauEpYntfsssd.xml diff --git a/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/AxKLxQn_Q5ExtyTFauEpYntfsssp.xml b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/AxKLxQn_Q5ExtyTFauEpYntfsssp.xml new file mode 100644 index 000000000..7b2091a91 --- /dev/null +++ b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/AxKLxQn_Q5ExtyTFauEpYntfsssp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/T9wikiXK6MjHu7XyyDA0kuCTP_kd.xml b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/IDetplA4BCvExeTDnnCPfg2MhBwd.xml similarity index 100% rename from resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/T9wikiXK6MjHu7XyyDA0kuCTP_kd.xml rename to resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/IDetplA4BCvExeTDnnCPfg2MhBwd.xml diff --git a/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/IDetplA4BCvExeTDnnCPfg2MhBwp.xml b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/IDetplA4BCvExeTDnnCPfg2MhBwp.xml new file mode 100644 index 000000000..1bb18e491 --- /dev/null +++ b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/IDetplA4BCvExeTDnnCPfg2MhBwp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/jupI72lBOnUNFR-WjgobwXS0zMod.xml b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/QqOZDkdIGwDJT1oKEd1m5cJ1l88d.xml similarity index 100% rename from resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/jupI72lBOnUNFR-WjgobwXS0zMod.xml rename to resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/QqOZDkdIGwDJT1oKEd1m5cJ1l88d.xml diff --git a/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/QqOZDkdIGwDJT1oKEd1m5cJ1l88p.xml b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/QqOZDkdIGwDJT1oKEd1m5cJ1l88p.xml new file mode 100644 index 000000000..4dafddba9 --- /dev/null +++ b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/QqOZDkdIGwDJT1oKEd1m5cJ1l88p.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/kJV53JKRAFPnFBgXx06VMpdr0ssd.xml b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/SJJLTV1fLQ-ZA32hIB2BTOYk5TYd.xml similarity index 100% rename from resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/kJV53JKRAFPnFBgXx06VMpdr0ssd.xml rename to resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/SJJLTV1fLQ-ZA32hIB2BTOYk5TYd.xml diff --git a/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/SJJLTV1fLQ-ZA32hIB2BTOYk5TYp.xml b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/SJJLTV1fLQ-ZA32hIB2BTOYk5TYp.xml new file mode 100644 index 000000000..8aa9cc84d --- /dev/null +++ b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/SJJLTV1fLQ-ZA32hIB2BTOYk5TYp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/rx1MnRhyaD-dcP1I9WcseP2PGiwd.xml b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/gK1b_iZQx1Km0kq0Da4qWwP0JpUd.xml similarity index 100% rename from resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/rx1MnRhyaD-dcP1I9WcseP2PGiwd.xml rename to resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/gK1b_iZQx1Km0kq0Da4qWwP0JpUd.xml diff --git a/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/gK1b_iZQx1Km0kq0Da4qWwP0JpUp.xml b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/gK1b_iZQx1Km0kq0Da4qWwP0JpUp.xml new file mode 100644 index 000000000..f93efa787 --- /dev/null +++ b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/gK1b_iZQx1Km0kq0Da4qWwP0JpUp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/wSDXMLBIRWsCGVgqLP9ER---lO4d.xml b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/zMWI0fKvYbz3vT7BQxp-7H6ez4wd.xml similarity index 100% rename from resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/wSDXMLBIRWsCGVgqLP9ER---lO4d.xml rename to resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/zMWI0fKvYbz3vT7BQxp-7H6ez4wd.xml diff --git a/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/zMWI0fKvYbz3vT7BQxp-7H6ez4wp.xml b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/zMWI0fKvYbz3vT7BQxp-7H6ez4wp.xml new file mode 100644 index 000000000..76215b6d7 --- /dev/null +++ b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/zMWI0fKvYbz3vT7BQxp-7H6ez4wp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/FcqvamgK2GFBncjelWFYWpbic6sd.xml b/resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/FcqvamgK2GFBncjelWFYWpbic6sd.xml deleted file mode 100644 index 7a6326b99..000000000 --- a/resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/FcqvamgK2GFBncjelWFYWpbic6sd.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/Q9BY7Fa1-dMNQpWUMw61LYZlnpkd.xml b/resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/Q9BY7Fa1-dMNQpWUMw61LYZlnpkd.xml deleted file mode 100644 index 7a6326b99..000000000 --- a/resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/Q9BY7Fa1-dMNQpWUMw61LYZlnpkd.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/TMwS0Ehc1hOj1QS3kXBWqYH2cIYd.xml b/resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/TMwS0Ehc1hOj1QS3kXBWqYH2cIYd.xml deleted file mode 100644 index 7a6326b99..000000000 --- a/resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/TMwS0Ehc1hOj1QS3kXBWqYH2cIYd.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/XA-33X-Ml7fASA-h03h0NoRtubAd.xml b/resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/XA-33X-Ml7fASA-h03h0NoRtubAd.xml deleted file mode 100644 index 7a6326b99..000000000 --- a/resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/XA-33X-Ml7fASA-h03h0NoRtubAd.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/ccgGxetCHn-i1g49iWGxy2eRT9gd.xml b/resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/ccgGxetCHn-i1g49iWGxy2eRT9gd.xml deleted file mode 100644 index 7a6326b99..000000000 --- a/resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/ccgGxetCHn-i1g49iWGxy2eRT9gd.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/wv-PgmFd28kiUsYZVe0yPM4yMXEd.xml b/resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/wv-PgmFd28kiUsYZVe0yPM4yMXEd.xml deleted file mode 100644 index 7a6326b99..000000000 --- a/resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/wv-PgmFd28kiUsYZVe0yPM4yMXEd.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/pDfDXpP-F2Cd6B91DGM44psBFJw/l8FwG4TnKj8gyMwHPAtYvu1WQGUd.xml b/resources/project/pDfDXpP-F2Cd6B91DGM44psBFJw/l8FwG4TnKj8gyMwHPAtYvu1WQGUd.xml deleted file mode 100644 index 7a6326b99..000000000 --- a/resources/project/pDfDXpP-F2Cd6B91DGM44psBFJw/l8FwG4TnKj8gyMwHPAtYvu1WQGUd.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/pDfDXpP-F2Cd6B91DGM44psBFJw/zxz-H_H66S_yiImwKMo7C6xbqr0d.xml b/resources/project/pDfDXpP-F2Cd6B91DGM44psBFJw/zxz-H_H66S_yiImwKMo7C6xbqr0d.xml deleted file mode 100644 index a75f7a81b..000000000 --- a/resources/project/pDfDXpP-F2Cd6B91DGM44psBFJw/zxz-H_H66S_yiImwKMo7C6xbqr0d.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/pDfDXpP-F2Cd6B91DGM44psBFJw/zxz-H_H66S_yiImwKMo7C6xbqr0p.xml b/resources/project/pDfDXpP-F2Cd6B91DGM44psBFJw/zxz-H_H66S_yiImwKMo7C6xbqr0p.xml deleted file mode 100644 index 842de6ab3..000000000 --- a/resources/project/pDfDXpP-F2Cd6B91DGM44psBFJw/zxz-H_H66S_yiImwKMo7C6xbqr0p.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/yrvDAxHTmqAbl2v6-rWp7tgRl4Ed.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/2BNsbxotz8yS1dkQ-k4TtTtfElkd.xml similarity index 100% rename from resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/yrvDAxHTmqAbl2v6-rWp7tgRl4Ed.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/2BNsbxotz8yS1dkQ-k4TtTtfElkd.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/u9fylSJ1j_4ckhUg5rn809_XS8cp.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/2BNsbxotz8yS1dkQ-k4TtTtfElkp.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/u9fylSJ1j_4ckhUg5rn809_XS8cp.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/2BNsbxotz8yS1dkQ-k4TtTtfElkp.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/1uG3tiqfOuVrZXVhllRAYYH-wtod.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/41tDM4fkWcly5KHLFGBD7TnUF1kd.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/1uG3tiqfOuVrZXVhllRAYYH-wtod.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/41tDM4fkWcly5KHLFGBD7TnUF1kd.xml diff --git a/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/41tDM4fkWcly5KHLFGBD7TnUF1kp.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/41tDM4fkWcly5KHLFGBD7TnUF1kp.xml new file mode 100644 index 000000000..f5d08a516 --- /dev/null +++ b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/41tDM4fkWcly5KHLFGBD7TnUF1kp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/BXX9SqJTGGGO_CDj2AJCCAtV84kd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/6AGol90mQGOIatxNSyMhdaH59gAd.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/BXX9SqJTGGGO_CDj2AJCCAtV84kd.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/6AGol90mQGOIatxNSyMhdaH59gAd.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/oeLLBB_jdP6pKF_DZjKp0QJVZ5Qp.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/6AGol90mQGOIatxNSyMhdaH59gAp.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/oeLLBB_jdP6pKF_DZjKp0QJVZ5Qp.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/6AGol90mQGOIatxNSyMhdaH59gAp.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/4lF9gcxRF1S305BdGqOMAHZcYgod.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/BYuUEuuSEpTxFNYwIIct-_focKkd.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/4lF9gcxRF1S305BdGqOMAHZcYgod.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/BYuUEuuSEpTxFNYwIIct-_focKkd.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/u6YyGgN6Hd7S4WFttol0-zYdc8cp.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/BYuUEuuSEpTxFNYwIIct-_focKkp.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/u6YyGgN6Hd7S4WFttol0-zYdc8cp.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/BYuUEuuSEpTxFNYwIIct-_focKkp.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/8N6cWgm0bCQbFtUjwVpJKMr3DlEd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/FDdGHhZvwjpYxttUpuTOcZx3JmMd.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/8N6cWgm0bCQbFtUjwVpJKMr3DlEd.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/FDdGHhZvwjpYxttUpuTOcZx3JmMd.xml diff --git a/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/FDdGHhZvwjpYxttUpuTOcZx3JmMp.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/FDdGHhZvwjpYxttUpuTOcZx3JmMp.xml new file mode 100644 index 000000000..7a1ad1ea2 --- /dev/null +++ b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/FDdGHhZvwjpYxttUpuTOcZx3JmMp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/BkckfovEtaH9G6qBgDPgKm8zKxgd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/FjEfBzqcYgSPB2oA26wfyEdBqo0d.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/BkckfovEtaH9G6qBgDPgKm8zKxgd.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/FjEfBzqcYgSPB2oA26wfyEdBqo0d.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/ojkhqvbsn_quEDp4WaQJ3vMFy2Yp.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/FjEfBzqcYgSPB2oA26wfyEdBqo0p.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/ojkhqvbsn_quEDp4WaQJ3vMFy2Yp.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/FjEfBzqcYgSPB2oA26wfyEdBqo0p.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/HV6_iJQOFZrKU26X_hF7trjvkeMd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/H72L659GuP9YjS6Dt9kIH5c6HnQd.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/HV6_iJQOFZrKU26X_hF7trjvkeMd.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/H72L659GuP9YjS6Dt9kIH5c6HnQd.xml diff --git a/resources/project/R6m7AxFfSX7StHtVJ9WG44h96GM/dYvx6wp33Ts8hX1S_rzdiQ3NP4Ap.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/H72L659GuP9YjS6Dt9kIH5c6HnQp.xml similarity index 100% rename from resources/project/R6m7AxFfSX7StHtVJ9WG44h96GM/dYvx6wp33Ts8hX1S_rzdiQ3NP4Ap.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/H72L659GuP9YjS6Dt9kIH5c6HnQp.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/D2MuV2keQQWbNaEvWpyyDv8QkMsd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/HvUMVTzlhZFbsfNl34oO8xkwaekd.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/D2MuV2keQQWbNaEvWpyyDv8QkMsd.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/HvUMVTzlhZFbsfNl34oO8xkwaekd.xml diff --git a/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/HvUMVTzlhZFbsfNl34oO8xkwaekp.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/HvUMVTzlhZFbsfNl34oO8xkwaekp.xml new file mode 100644 index 000000000..70c40cfa0 --- /dev/null +++ b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/HvUMVTzlhZFbsfNl34oO8xkwaekp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/Hk1Vip6VmTG9gpBtNOXKEU56oUcd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/MxOdJHAcr56wMe-tpEEz-LoiQlYd.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/Hk1Vip6VmTG9gpBtNOXKEU56oUcd.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/MxOdJHAcr56wMe-tpEEz-LoiQlYd.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/OLuX-8c-UOqeJnrM2amm241SKTsp.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/MxOdJHAcr56wMe-tpEEz-LoiQlYp.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/OLuX-8c-UOqeJnrM2amm241SKTsp.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/MxOdJHAcr56wMe-tpEEz-LoiQlYp.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/EIE_vdm0VClM0NZZV517-DBaczEd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/NEI1fXIv8KRUL_rDHprZREmiW34d.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/EIE_vdm0VClM0NZZV517-DBaczEd.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/NEI1fXIv8KRUL_rDHprZREmiW34d.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/gneWb5IhW594iwk9mWJJwtxdpyMp.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/NEI1fXIv8KRUL_rDHprZREmiW34p.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/gneWb5IhW594iwk9mWJJwtxdpyMp.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/NEI1fXIv8KRUL_rDHprZREmiW34p.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/Hp4Wu5EduwyItwUIb1uZUV98wGEd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/NFReMHJQuHGlCss_fLSqFSIT1ZYd.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/Hp4Wu5EduwyItwUIb1uZUV98wGEd.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/NFReMHJQuHGlCss_fLSqFSIT1ZYd.xml diff --git a/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/NFReMHJQuHGlCss_fLSqFSIT1ZYp.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/NFReMHJQuHGlCss_fLSqFSIT1ZYp.xml new file mode 100644 index 000000000..73be416fc --- /dev/null +++ b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/NFReMHJQuHGlCss_fLSqFSIT1ZYp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/NAFZMRUtFQYPd30MjCtFUUPa5w4d.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/PMTiY8ii3Ycb5X733RMaGCNVItAd.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/NAFZMRUtFQYPd30MjCtFUUPa5w4d.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/PMTiY8ii3Ycb5X733RMaGCNVItAd.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/7AZXpb090n98KOSsx01-zOUfRQgp.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/PMTiY8ii3Ycb5X733RMaGCNVItAp.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/7AZXpb090n98KOSsx01-zOUfRQgp.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/PMTiY8ii3Ycb5X733RMaGCNVItAp.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/IWnqwSbZEiavwf7_s2jeWFKRUyMd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/PQTYqrLotgfQlXwRwcTgmkIRabcd.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/IWnqwSbZEiavwf7_s2jeWFKRUyMd.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/PQTYqrLotgfQlXwRwcTgmkIRabcd.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/gBXVGBJ76fvQ_X7J0ZQGLmXEcqEp.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/PQTYqrLotgfQlXwRwcTgmkIRabcp.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/gBXVGBJ76fvQ_X7J0ZQGLmXEcqEp.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/PQTYqrLotgfQlXwRwcTgmkIRabcp.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/Q55VV6m6MizdVBAaXHDg8O7if2gd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/Q-DZerJncmEPNpWIO9dYINnubhAd.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/Q55VV6m6MizdVBAaXHDg8O7if2gd.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/Q-DZerJncmEPNpWIO9dYINnubhAd.xml diff --git a/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/Q-DZerJncmEPNpWIO9dYINnubhAp.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/Q-DZerJncmEPNpWIO9dYINnubhAp.xml new file mode 100644 index 000000000..752b58233 --- /dev/null +++ b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/Q-DZerJncmEPNpWIO9dYINnubhAp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/QNBeGKx711B7_RF8ns22o2sra6od.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/R08oPdcWNyOGmE1Nvdwwvs71M1kd.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/QNBeGKx711B7_RF8ns22o2sra6od.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/R08oPdcWNyOGmE1Nvdwwvs71M1kd.xml diff --git a/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/R08oPdcWNyOGmE1Nvdwwvs71M1kp.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/R08oPdcWNyOGmE1Nvdwwvs71M1kp.xml new file mode 100644 index 000000000..9adc9cb68 --- /dev/null +++ b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/R08oPdcWNyOGmE1Nvdwwvs71M1kp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/S0FtAOjO_bt3aSHMltKOmsdvzZkd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/Rx9X9wAt2wpjFi2e7541ezE-nWwd.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/S0FtAOjO_bt3aSHMltKOmsdvzZkd.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/Rx9X9wAt2wpjFi2e7541ezE-nWwd.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/Q55VV6m6MizdVBAaXHDg8O7if2gp.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/Rx9X9wAt2wpjFi2e7541ezE-nWwp.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/Q55VV6m6MizdVBAaXHDg8O7if2gp.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/Rx9X9wAt2wpjFi2e7541ezE-nWwp.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/OLuX-8c-UOqeJnrM2amm241SKTsd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/SJuj0lQEL2kv8DN93wpDxprjL2Qd.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/OLuX-8c-UOqeJnrM2amm241SKTsd.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/SJuj0lQEL2kv8DN93wpDxprjL2Qd.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/HV6_iJQOFZrKU26X_hF7trjvkeMp.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/SJuj0lQEL2kv8DN93wpDxprjL2Qp.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/HV6_iJQOFZrKU26X_hF7trjvkeMp.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/SJuj0lQEL2kv8DN93wpDxprjL2Qp.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/REL0fj6PT2tNiayKyTrZEt9--acd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/TMR2PLz8-jf_t72xY3w-eiAkMwcd.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/REL0fj6PT2tNiayKyTrZEt9--acd.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/TMR2PLz8-jf_t72xY3w-eiAkMwcd.xml diff --git a/resources/project/R6m7AxFfSX7StHtVJ9WG44h96GM/Drp6kay-WW1Z2AQWI8KW60j9HlQp.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/TMR2PLz8-jf_t72xY3w-eiAkMwcp.xml similarity index 100% rename from resources/project/R6m7AxFfSX7StHtVJ9WG44h96GM/Drp6kay-WW1Z2AQWI8KW60j9HlQp.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/TMR2PLz8-jf_t72xY3w-eiAkMwcp.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/ieR1AbFuIuiTNyC8z83kLz-KjxQd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/UeukJVHJUwLZ1oSw-yBfMdSLcNId.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/ieR1AbFuIuiTNyC8z83kLz-KjxQd.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/UeukJVHJUwLZ1oSw-yBfMdSLcNId.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/Hk1Vip6VmTG9gpBtNOXKEU56oUcp.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/UeukJVHJUwLZ1oSw-yBfMdSLcNIp.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/Hk1Vip6VmTG9gpBtNOXKEU56oUcp.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/UeukJVHJUwLZ1oSw-yBfMdSLcNIp.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/UPM_TyetjaXWJKkGaorucXMQrKAd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/V4lqQmJ8gZjfEk0BSGK5rhmhQ7Qd.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/UPM_TyetjaXWJKkGaorucXMQrKAd.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/V4lqQmJ8gZjfEk0BSGK5rhmhQ7Qd.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/UPM_TyetjaXWJKkGaorucXMQrKAp.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/V4lqQmJ8gZjfEk0BSGK5rhmhQ7Qp.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/UPM_TyetjaXWJKkGaorucXMQrKAp.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/V4lqQmJ8gZjfEk0BSGK5rhmhQ7Qp.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/WeWWFR_CW_fJH3v0Spn0rCmP6ccd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/W3r3wIq1F_aH5Bg_6WiksWHWjnsd.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/WeWWFR_CW_fJH3v0Spn0rCmP6ccd.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/W3r3wIq1F_aH5Bg_6WiksWHWjnsd.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/1uG3tiqfOuVrZXVhllRAYYH-wtop.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/W3r3wIq1F_aH5Bg_6WiksWHWjnsp.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/1uG3tiqfOuVrZXVhllRAYYH-wtop.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/W3r3wIq1F_aH5Bg_6WiksWHWjnsp.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/Z_VaFjtshHcOoAprv8kGzuWPan8d.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/Yzm_5cRIP1TNTAYJFwi1gFJMvrYd.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/Z_VaFjtshHcOoAprv8kGzuWPan8d.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/Yzm_5cRIP1TNTAYJFwi1gFJMvrYd.xml diff --git a/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/Yzm_5cRIP1TNTAYJFwi1gFJMvrYp.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/Yzm_5cRIP1TNTAYJFwi1gFJMvrYp.xml new file mode 100644 index 000000000..5ee72c781 --- /dev/null +++ b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/Yzm_5cRIP1TNTAYJFwi1gFJMvrYp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/gBXVGBJ76fvQ_X7J0ZQGLmXEcqEd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/aQn5VXjSyIOSf20IuNhzQTcJKLEd.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/gBXVGBJ76fvQ_X7J0ZQGLmXEcqEd.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/aQn5VXjSyIOSf20IuNhzQTcJKLEd.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/S0FtAOjO_bt3aSHMltKOmsdvzZkp.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/aQn5VXjSyIOSf20IuNhzQTcJKLEp.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/S0FtAOjO_bt3aSHMltKOmsdvzZkp.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/aQn5VXjSyIOSf20IuNhzQTcJKLEp.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/oeLLBB_jdP6pKF_DZjKp0QJVZ5Qd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/alAG1z_nIzafOha6mXt5pH8WAmgd.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/oeLLBB_jdP6pKF_DZjKp0QJVZ5Qd.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/alAG1z_nIzafOha6mXt5pH8WAmgd.xml diff --git a/resources/project/R6m7AxFfSX7StHtVJ9WG44h96GM/D7EGYpvIiO_nGXQuTOYjsSP_JQwp.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/alAG1z_nIzafOha6mXt5pH8WAmgp.xml similarity index 100% rename from resources/project/R6m7AxFfSX7StHtVJ9WG44h96GM/D7EGYpvIiO_nGXQuTOYjsSP_JQwp.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/alAG1z_nIzafOha6mXt5pH8WAmgp.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/pDfDXpP-F2Cd6B91DGM44psBFJwd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/d52ncQukW_9fnTuQsihy5s3Rp9kd.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/pDfDXpP-F2Cd6B91DGM44psBFJwd.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/d52ncQukW_9fnTuQsihy5s3Rp9kd.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/NAFZMRUtFQYPd30MjCtFUUPa5w4p.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/d52ncQukW_9fnTuQsihy5s3Rp9kp.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/NAFZMRUtFQYPd30MjCtFUUPa5w4p.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/d52ncQukW_9fnTuQsihy5s3Rp9kp.xml diff --git a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/kRJYdCzyq5zNeYLPJeKdZmiLSJEd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/fkKGPyMFLRd9q8sWiOz4qApnVgkd.xml similarity index 100% rename from resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/kRJYdCzyq5zNeYLPJeKdZmiLSJEd.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/fkKGPyMFLRd9q8sWiOz4qApnVgkd.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/ieR1AbFuIuiTNyC8z83kLz-KjxQp.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/fkKGPyMFLRd9q8sWiOz4qApnVgkp.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/ieR1AbFuIuiTNyC8z83kLz-KjxQp.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/fkKGPyMFLRd9q8sWiOz4qApnVgkp.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/gneWb5IhW594iwk9mWJJwtxdpyMd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/jcRKP1GWD0s2vbmJjnRVU0UNQuEd.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/gneWb5IhW594iwk9mWJJwtxdpyMd.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/jcRKP1GWD0s2vbmJjnRVU0UNQuEd.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/D2MuV2keQQWbNaEvWpyyDv8QkMsp.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/jcRKP1GWD0s2vbmJjnRVU0UNQuEp.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/D2MuV2keQQWbNaEvWpyyDv8QkMsp.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/jcRKP1GWD0s2vbmJjnRVU0UNQuEp.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/lKVscSUfLpUJ7DMGKxZpl2cj8Cwd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/lIsawu5M1S3Y4h7oJkYpw3jue7Ed.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/lKVscSUfLpUJ7DMGKxZpl2cj8Cwd.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/lIsawu5M1S3Y4h7oJkYpw3jue7Ed.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/Hp4Wu5EduwyItwUIb1uZUV98wGEp.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/lIsawu5M1S3Y4h7oJkYpw3jue7Ep.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/Hp4Wu5EduwyItwUIb1uZUV98wGEp.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/lIsawu5M1S3Y4h7oJkYpw3jue7Ep.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/ojkhqvbsn_quEDp4WaQJ3vMFy2Yd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/ljel_A4AHLbljnmRTR2O3qqpy8Ed.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/ojkhqvbsn_quEDp4WaQJ3vMFy2Yd.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/ljel_A4AHLbljnmRTR2O3qqpy8Ed.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/QNBeGKx711B7_RF8ns22o2sra6op.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/ljel_A4AHLbljnmRTR2O3qqpy8Ep.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/QNBeGKx711B7_RF8ns22o2sra6op.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/ljel_A4AHLbljnmRTR2O3qqpy8Ep.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/sbvxc69GTsaxWR2ghAIgyxsRoK0d.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/scEHrBcR_4DaGlInftNFgaoYFTMd.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/sbvxc69GTsaxWR2ghAIgyxsRoK0d.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/scEHrBcR_4DaGlInftNFgaoYFTMd.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/8N6cWgm0bCQbFtUjwVpJKMr3DlEp.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/scEHrBcR_4DaGlInftNFgaoYFTMp.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/8N6cWgm0bCQbFtUjwVpJKMr3DlEp.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/scEHrBcR_4DaGlInftNFgaoYFTMp.xml diff --git a/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4d.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/ux2HJV4G7O8gtSZERsaoTt6GWQkd.xml similarity index 100% rename from resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4d.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/ux2HJV4G7O8gtSZERsaoTt6GWQkd.xml diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/pDfDXpP-F2Cd6B91DGM44psBFJwp.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/ux2HJV4G7O8gtSZERsaoTt6GWQkp.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/pDfDXpP-F2Cd6B91DGM44psBFJwp.xml rename to resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/ux2HJV4G7O8gtSZERsaoTt6GWQkp.xml diff --git a/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/xcwXr-V7z1F2VlQN51Es9EbCc6sd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/xcwXr-V7z1F2VlQN51Es9EbCc6sd.xml deleted file mode 100644 index 7a6326b99..000000000 --- a/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/xcwXr-V7z1F2VlQN51Es9EbCc6sd.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/xcwXr-V7z1F2VlQN51Es9EbCc6sp.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/xcwXr-V7z1F2VlQN51Es9EbCc6sp.xml deleted file mode 100644 index cdd4d3dcb..000000000 --- a/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/xcwXr-V7z1F2VlQN51Es9EbCc6sp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/u6YyGgN6Hd7S4WFttol0-zYdc8cd.xml b/resources/project/ux2HJV4G7O8gtSZERsaoTt6GWQk/GNyWdCWXwy_FnEw-XS9bcAQnRkId.xml similarity index 100% rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/u6YyGgN6Hd7S4WFttol0-zYdc8cd.xml rename to resources/project/ux2HJV4G7O8gtSZERsaoTt6GWQk/GNyWdCWXwy_FnEw-XS9bcAQnRkId.xml diff --git a/resources/project/pDfDXpP-F2Cd6B91DGM44psBFJw/l8FwG4TnKj8gyMwHPAtYvu1WQGUp.xml b/resources/project/ux2HJV4G7O8gtSZERsaoTt6GWQk/GNyWdCWXwy_FnEw-XS9bcAQnRkIp.xml similarity index 100% rename from resources/project/pDfDXpP-F2Cd6B91DGM44psBFJw/l8FwG4TnKj8gyMwHPAtYvu1WQGUp.xml rename to resources/project/ux2HJV4G7O8gtSZERsaoTt6GWQk/GNyWdCWXwy_FnEw-XS9bcAQnRkIp.xml diff --git a/resources/project/ieR1AbFuIuiTNyC8z83kLz-KjxQ/QgeUCp8CiwXPaszmjk6YesnqDGkd.xml b/resources/project/ux2HJV4G7O8gtSZERsaoTt6GWQk/STP0mSEMvKb0C99CAKfVG-FjFdUd.xml similarity index 100% rename from resources/project/ieR1AbFuIuiTNyC8z83kLz-KjxQ/QgeUCp8CiwXPaszmjk6YesnqDGkd.xml rename to resources/project/ux2HJV4G7O8gtSZERsaoTt6GWQk/STP0mSEMvKb0C99CAKfVG-FjFdUd.xml diff --git a/resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/wTRZ9nfLywZT2m22SOEZoykCg4Qp.xml b/resources/project/ux2HJV4G7O8gtSZERsaoTt6GWQk/STP0mSEMvKb0C99CAKfVG-FjFdUp.xml similarity index 100% rename from resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/wTRZ9nfLywZT2m22SOEZoykCg4Qp.xml rename to resources/project/ux2HJV4G7O8gtSZERsaoTt6GWQk/STP0mSEMvKb0C99CAKfVG-FjFdUp.xml From 81f08720539c52c82eaf3937f2bb0050c87a5d28 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Tue, 19 Sep 2023 21:01:40 -0500 Subject: [PATCH 113/365] fix matrix shape --- .../calcSurrogateModel.m | 12 ++++----- .../calcSynergyBasedModeledValues.m | 26 +++++++++---------- .../makeStateDerivatives.m | 12 ++++----- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/SurrogateModelCreation/calcSurrogateModel.m b/src/SurrogateModelCreation/calcSurrogateModel.m index 3b639573d..6ce7574dd 100644 --- a/src/SurrogateModelCreation/calcSurrogateModel.m +++ b/src/SurrogateModelCreation/calcSurrogateModel.m @@ -38,23 +38,23 @@ % ----------------------------------------------------------------------- function [newMuscleTendonLengths, newMomentArms, ... - newMuscleTendonVelocities] = calcSurrogateModel(params, jointAngles, ... + newMuscleTendonVelocities] = calcSurrogateModel(inputs, jointAngles, ... jointVelocities) newMomentArms = zeros(size(jointAngles{1}, 1), ... - length(params.coordinateNames), size(jointAngles, 2)); + length(inputs.coordinateNames), size(jointAngles, 2)); for i = 1 : size(jointAngles, 2) % Get A matrix matrix = PatientSpecificSurrogateModel(jointAngles{i}, jointVelocities{i}, i); % Caculate new muscle tendon lengths and moment arms - vector = matrix * params.coefficients{i}; + vector = matrix * inputs.coefficients{i}; newMuscleTendonLengths(:, i) = vector(1 : size(jointAngles{i}, 1)); newMuscleTendonVelocities(:, i) = vector(1 + ... size(jointAngles{i}, 1) : size(jointAngles{i}, 1) * 2); index = 2; - for j = 1 : length(params.coordinateNames) - for k = 1 : length(params.surrogateModelLabels{i}) - if strcmp(params.coordinateNames(j), params.surrogateModelLabels{i}(k)) + for j = 1 : length(inputs.coordinateNames) + for k = 1 : length(inputs.surrogateModelLabels{i}) + if strcmp(inputs.coordinateNames(j), inputs.surrogateModelLabels{i}(k)) newMomentArms(:, j, i) = vector(size(jointAngles{i}, 1) * ... index + 1 : size(jointAngles{i}, 1) * (index + 1)); index = index + 1; diff --git a/src/TreatmentOptimization/TrackingOptimization/calcSynergyBasedModeledValues.m b/src/TreatmentOptimization/TrackingOptimization/calcSynergyBasedModeledValues.m index 99641c30e..b8cf445da 100644 --- a/src/TreatmentOptimization/TrackingOptimization/calcSynergyBasedModeledValues.m +++ b/src/TreatmentOptimization/TrackingOptimization/calcSynergyBasedModeledValues.m @@ -32,28 +32,28 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function modeledValues = calcSynergyBasedModeledValues(values, params, ... +function modeledValues = calcSynergyBasedModeledValues(values, inputs, ... modeledValues) -if strcmp(params.controllerType, 'synergy') - [jointAngles, jointVelocities] = getMuscleActuatedDOFs(values, params); - [params.muscleTendonLength, params.momentArms, ... - params.muscleTendonVelocity] = calcSurrogateModel(params, ... +if strcmp(inputs.controllerType, 'synergy') + [jointAngles, jointVelocities] = getMuscleActuatedDOFs(values, inputs); + [inputs.muscleTendonLength, inputs.momentArms, ... + inputs.muscleTendonVelocity] = calcSurrogateModel(inputs, ... jointAngles, jointVelocities); - params.muscleTendonLength = permute(params.muscleTendonLength, [3 2 1]); - params.muscleTendonVelocity = permute(params.muscleTendonVelocity, [3 2 1]); - params.momentArms = permute(params.momentArms, [ 4 2 3 1]); + inputs.muscleTendonLength = permute(inputs.muscleTendonLength, [3 2 1]); + inputs.muscleTendonVelocity = permute(inputs.muscleTendonVelocity, [3 2 1]); + inputs.momentArms = permute(inputs.momentArms, [ 4 2 3 1]); [modeledValues.normalizedFiberLength, modeledValues.normalizedFiberVelocity] = ... - calcNormalizedMuscleFiberLengthsAndVelocities(params, ... - ones(1, params.numMuscles), ones(1, params.numMuscles)); + calcNormalizedMuscleFiberLengthsAndVelocities(inputs, ... + ones(1, inputs.numMuscles), ones(1, inputs.numMuscles)); modeledValues.muscleActivations = calcMuscleActivationFromSynergies(values); - muscleJointMoments = calcMuscleJointMoments(params, ... + muscleJointMoments = calcMuscleJointMoments(inputs, ... modeledValues.muscleActivations, modeledValues.normalizedFiberLength, ... modeledValues.normalizedFiberVelocity); modeledValues.muscleJointMoments = permute(muscleJointMoments, [3 2 1]); modeledValues.muscleJointMoments = modeledValues.muscleJointMoments(:, ... - params.surrogateModelIndex); + inputs.surrogateModelIndex); modeledValues.muscleJointMoments = modeledValues.muscleJointMoments(:, ... - params.dofsActuatedIndex); + inputs.dofsActuatedIndex); modeledValues.muscleActivations = permute(modeledValues.muscleActivations, [3 2 1]); end end diff --git a/src/TreatmentOptimization/makeStateDerivatives.m b/src/TreatmentOptimization/makeStateDerivatives.m index 7c4158766..9db64b7e9 100644 --- a/src/TreatmentOptimization/makeStateDerivatives.m +++ b/src/TreatmentOptimization/makeStateDerivatives.m @@ -30,17 +30,17 @@ function inputs = makeStateDerivatives(inputs, params) jointAnglesSpline = spaps(inputs.experimentalTime, ... - inputs.experimentalJointAngles, eps, [], 3); + inputs.experimentalJointAngles', eps, [], 3); inputs.experimentalJointVelocities = fnval(fnder(jointAnglesSpline, 1), ... - inputs.experimentalTime); + inputs.experimentalTime)'; jointVelocitiesSpline = spaps(inputs.experimentalTime, ... - inputs.experimentalJointVelocities, eps, [], 3); + inputs.experimentalJointVelocities', eps, [], 3); inputs.experimentalJointAccelerations = fnval( ... - fnder(jointVelocitiesSpline, 1), inputs.experimentalTime); + fnder(jointVelocitiesSpline, 1), inputs.experimentalTime)'; jointAccelerationsSpline = spaps(inputs.experimentalTime, ... - inputs.experimentalJointAccelerations, eps, [], 3); + inputs.experimentalJointAccelerations', eps, [], 3); inputs.experimentalJointJerks = fnval(fnder(jointAccelerationsSpline, ... - 1), inputs.experimentalTime); + 1), inputs.experimentalTime)'; % points = length(inputs.experimentalTime); % interval = inputs.experimentalTime(2) - inputs.experimentalTime(1); From 4ed83e7d85d0d40aac0dd073ea2134aebf4c5d5a Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Tue, 19 Sep 2023 21:01:50 -0500 Subject: [PATCH 114/365] parse states_coordinate_list --- src/core/parse/parseTreatmentOptimizationInputs.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/parse/parseTreatmentOptimizationInputs.m b/src/core/parse/parseTreatmentOptimizationInputs.m index 991b13142..37a07bbe4 100644 --- a/src/core/parse/parseTreatmentOptimizationInputs.m +++ b/src/core/parse/parseTreatmentOptimizationInputs.m @@ -50,6 +50,8 @@ if(isempty(inputs.resultsDirectory)); inputs.resultsDirectory = pwd; end inputs.controllerType = parseControllerType(tree); inputs = parseModel(tree, inputs); +inputs.statesCoordinateNames = parseSpaceSeparatedList(tree, ... + "states_coordinate_list"); end function osimx = parseOsimxFileWithCondition(tree, inputs) From 48d4ffa73ec45f12500fc2af8be2835b21a937e6 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Wed, 20 Sep 2023 02:17:14 -0500 Subject: [PATCH 115/365] fix states_coordinate_list import --- src/core/parse/parseController.m | 2 +- src/core/parse/parseTreatmentOptimizationInputs.m | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/core/parse/parseController.m b/src/core/parse/parseController.m index 5ff89a1ab..195f6f6ce 100644 --- a/src/core/parse/parseController.m +++ b/src/core/parse/parseController.m @@ -32,7 +32,7 @@ function inputs = parseController(tree, inputs) inputs = parseTreatmentOptimizationDesignVariableBounds(tree, ... inputs); -inputs.statesCoordinates = parseSpaceSeparatedList(tree, ... +inputs.statesCoordinateNames = parseSpaceSeparatedList(tree, ... "states_coordinate_list"); torqueTree = getFieldByName(tree, "RCNLTorqueController"); diff --git a/src/core/parse/parseTreatmentOptimizationInputs.m b/src/core/parse/parseTreatmentOptimizationInputs.m index 37a07bbe4..991b13142 100644 --- a/src/core/parse/parseTreatmentOptimizationInputs.m +++ b/src/core/parse/parseTreatmentOptimizationInputs.m @@ -50,8 +50,6 @@ if(isempty(inputs.resultsDirectory)); inputs.resultsDirectory = pwd; end inputs.controllerType = parseControllerType(tree); inputs = parseModel(tree, inputs); -inputs.statesCoordinateNames = parseSpaceSeparatedList(tree, ... - "states_coordinate_list"); end function osimx = parseOsimxFileWithCondition(tree, inputs) From 29e1213b7e891bc715739aa58f3074d3992097d4 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Wed, 20 Sep 2023 02:17:32 -0500 Subject: [PATCH 116/365] fix epsilon impl --- .../getMuscleSpecificSurrogateModelData.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SurrogateModelCreation/getMuscleSpecificSurrogateModelData.m b/src/SurrogateModelCreation/getMuscleSpecificSurrogateModelData.m index b98f76d9d..14e8cef40 100644 --- a/src/SurrogateModelCreation/getMuscleSpecificSurrogateModelData.m +++ b/src/SurrogateModelCreation/getMuscleSpecificSurrogateModelData.m @@ -32,7 +32,7 @@ for j = 1:length(inputs.coordinateNames) for k = 1:length(inputs.surrogateModelCoordinateNames) if strcmp(inputs.coordinateNames(j), inputs.surrogateModelCoordinateNames(k)) - if range(inputs.momentArms(:,k,i)) > inputs.epsilon + if max(abs(inputs.momentArms(:,k,i))) > inputs.epsilon inputs.surrogateModelLabels{i}(counter) = ... inputs.coordinateNames(j); inputs.muscleSpecificJointAngles{i}(:,counter) = ... From 1eb2605c52ea4ad8e321b9d45300db4bda4a0c92 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Wed, 20 Sep 2023 02:18:10 -0500 Subject: [PATCH 117/365] add allowable error to joint jerk --- .../calcMinimizingJointJerkIntegrand.m | 13 ++++++++----- src/TreatmentOptimization/generateCostTermStruct.m | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointJerkIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointJerkIntegrand.m index 0fc708907..70251b0d0 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointJerkIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointJerkIntegrand.m @@ -27,10 +27,13 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcMinimizingJointJerkIntegrand(jointJerks, params, ... - coordinateName) +function cost = calcMinimizingJointJerkIntegrand(jointJerks, inputs, ... + costTerm) -indx = find(strcmp(convertCharsToStrings(params.coordinateNames), ... - coordinateName)); -cost = calcMinimizingCostArrayTerm(jointJerks(:, indx)); +indx = find(strcmp(convertCharsToStrings(inputs.statesCoordinateNames), ... + costTerm.coordinate)); +% cost = calcMinimizingCostArrayTerm(jointJerks(:, indx)); +errorCenter = valueOrAlternate(costTerm, "errorCenter", 0); +maximumAllowableError = valueOrAlternate(costTerm, "maxAllowableError", 10); +cost = jointJerks(:, indx) / maximumAllowableError; end \ No newline at end of file diff --git a/src/TreatmentOptimization/generateCostTermStruct.m b/src/TreatmentOptimization/generateCostTermStruct.m index 0348bbbd3..d3318d6c4 100644 --- a/src/TreatmentOptimization/generateCostTermStruct.m +++ b/src/TreatmentOptimization/generateCostTermStruct.m @@ -140,7 +140,7 @@ calcMinimizingJointJerkIntegrand( ... values.controlJerks, ... auxdata, ... - costTerm.coordinate ... + costTerm ... ); costTermCalculations.metabolic_cost_minimization = @(values, modeledValues, auxdata, costTerm) ... From 6dd6fdd9919f6a45c87502ca2d85fe483b6e1c7c Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Wed, 20 Sep 2023 02:19:25 -0500 Subject: [PATCH 118/365] implement states as subset of all coordinates --- .../setupGpopsInitialGuess.m | 27 +++++-- .../SetupBounds/makeOptimalControlBounds.m | 71 +++++++++-------- .../gpops/makeGpopsValuesAsStruct.m | 79 ++++++++++++------- 3 files changed, 109 insertions(+), 68 deletions(-) diff --git a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m index 93e3c2b83..1fe1056df 100644 --- a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m +++ b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m @@ -45,9 +45,20 @@ guess.phase.time = scaleToBounds(inputs.initialTime, inputs.maxTime, ... inputs.minTime); else - guess.phase.state = scaleToBounds([inputs.experimentalJointAngles ... - inputs.experimentalJointVelocities ... - inputs.experimentalJointAccelerations], inputs.maxState, ... + stateJointAngles = subsetDataByCoordinates( ... + inputs.experimentalJointAngles, ... + inputs.coordinateNames, ... + inputs.statesCoordinateNames); + stateJointVelocities = subsetDataByCoordinates( ... + inputs.experimentalJointVelocities, ... + inputs.coordinateNames, ... + inputs.statesCoordinateNames); + stateJointAccelerations = subsetDataByCoordinates( ... + inputs.experimentalJointAccelerations, ... + inputs.coordinateNames, ... + inputs.statesCoordinateNames); + guess.phase.state = scaleToBounds([stateJointAngles, ... + stateJointVelocities, stateJointAccelerations], inputs.maxState, ... inputs.minState); guess.phase.time = scaleToBounds(inputs.experimentalTime, inputs.maxTime, ... inputs.minTime); @@ -58,14 +69,20 @@ if isfield(inputs, "initialJerks") controls = inputs.initialJerks; else - controls = inputs.experimentalJointJerks; + stateJointJerks = subsetDataByCoordinates( ... + inputs.experimentalJointJerks, ... + inputs.coordinateNames, ... + inputs.statesCoordinateNames); + + controls = stateJointJerks; end if strcmp(inputs.controllerType, "synergy") if isfield(inputs, "initialSynergyControls") controls = [controls, inputs.initialSynergyControls]; else throw(MException("NoInitialSynergyControls", ... - "initial synergy controls required for synergy driven")) + strcat("initial synergy controls required for synergy", ... + " driven, have you run NCP?"))); end end if isfield(inputs, "initialTorqueControls") diff --git a/src/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m b/src/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m index 8b0149391..158ea8bef 100644 --- a/src/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m +++ b/src/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m @@ -41,28 +41,47 @@ inputs.maxTime = max(inputs.experimentalTime); inputs.minTime = min(inputs.experimentalTime); -maxStatePositions = max(inputs.experimentalJointAngles) + ... - inputs.jointPositionsMultiple * range(inputs.experimentalJointAngles); -minStatePositions = min(inputs.experimentalJointAngles) - ... - inputs.jointPositionsMultiple * range(inputs.experimentalJointAngles); -maxStateVelocities = max(inputs.experimentalJointVelocities) + ... - inputs.jointVelocitiesMultiple * range(inputs.experimentalJointVelocities); -minStateVelocities = min(inputs.experimentalJointVelocities) - ... - inputs.jointVelocitiesMultiple * range(inputs.experimentalJointVelocities); -maxStateAccelerations = max(inputs.experimentalJointAccelerations) + ... - inputs.jointAccelerationsMultiple * range(inputs.experimentalJointAccelerations); -minStateAccelerations = min(inputs.experimentalJointAccelerations) - ... - inputs.jointAccelerationsMultiple * range(inputs.experimentalJointAccelerations); +stateJointAngles = subsetDataByCoordinates( ... + inputs.experimentalJointAngles, ... + inputs.coordinateNames, ... + inputs.statesCoordinateNames); +stateJointVelocities = subsetDataByCoordinates( ... + inputs.experimentalJointVelocities, ... + inputs.coordinateNames, ... + inputs.statesCoordinateNames); +stateJointAccelerations = subsetDataByCoordinates( ... + inputs.experimentalJointAccelerations, ... + inputs.coordinateNames, ... + inputs.statesCoordinateNames); + +maxStatePositions = max(stateJointAngles) + ... + inputs.jointPositionsMultiple * range(stateJointAngles); +minStatePositions = min(stateJointAngles) - ... + inputs.jointPositionsMultiple * range(stateJointAngles); +maxStateVelocities = max(stateJointVelocities) + ... + inputs.jointVelocitiesMultiple * range(stateJointVelocities); +minStateVelocities = min(stateJointVelocities) - ... + inputs.jointVelocitiesMultiple * range(stateJointVelocities); +maxStateAccelerations = max(stateJointAccelerations) + ... + inputs.jointAccelerationsMultiple * range(stateJointAccelerations); +minStateAccelerations = min(stateJointAccelerations) - ... + inputs.jointAccelerationsMultiple * range(stateJointAccelerations); inputs.maxState = [maxStatePositions maxStateVelocities maxStateAccelerations]; inputs.minState = [minStatePositions minStateVelocities minStateAccelerations]; end function inputs = makeControlBounds(inputs) -maxControlJerks = max(inputs.experimentalJointJerks) + ... - inputs.controlJerksMultiple * range(inputs.experimentalJointJerks); -minControlJerks = min(inputs.experimentalJointJerks) - ... - inputs.controlJerksMultiple * range(inputs.experimentalJointJerks); + +stateJointJerks = subsetDataByCoordinates( ... + inputs.experimentalJointJerks, ... + inputs.coordinateNames, ... + inputs.statesCoordinateNames); + +maxControlJerks = max(stateJointJerks) + ... + inputs.controlJerksMultiple * range(stateJointJerks); +minControlJerks = min(stateJointJerks) - ... + inputs.controlJerksMultiple * range(stateJointJerks); if strcmp(inputs.controllerType, 'synergy') maxControlSynergyActivations = inputs.maxControlSynergyActivations * ... @@ -75,7 +94,8 @@ ones(1, inputs.numSynergyWeights); inputs.minParameter = zeros(1, inputs.numSynergyWeights); end -elseif strcmp(inputs.controllerType, 'torque') +end +if isfield(inputs, "torqueControllerCoordinateNames") for i = 1:length(inputs.torqueControllerCoordinateNames) indx = find(strcmp(convertCharsToStrings( ... inputs.inverseDynamicMomentLabels), ... @@ -95,21 +115,4 @@ inputs.maxControl = [maxControlJerks maxControlTorques]; inputs.minControl = [minControlJerks minControlTorques]; end - -if isfield(inputs, "enableExternalTorqueControl") && ... - inputs.enableExternalTorqueControl - for i = 1:length(inputs.externalControlTorqueNames) - indx = find(strcmp(convertCharsToStrings( ... - inputs.inverseDynamicMomentLabels), ... - strcat(inputs.externalControlTorqueNames(i), '_moment'))); - maxExternalControlTorques(i) = max(inputs.experimentalJointMoments(:, ... - indx)) + inputs.maxExternalTorqueControls * ... - range(inputs.experimentalJointMoments(:, indx)); - minExternalControlTorques(i) = min(inputs.experimentalJointMoments(:, ... - indx)) - inputs.maxExternalTorqueControls * ... - range(inputs.experimentalJointMoments(:, indx)); - end - inputs.maxControl = [inputs.maxControl maxExternalControlTorques]; - inputs.minControl = [inputs.minControl minExternalControlTorques]; -end end diff --git a/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m b/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m index 9dbe0bf28..10b365b82 100644 --- a/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m +++ b/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m @@ -35,17 +35,22 @@ inputs.maxState, ones(size(phase.state, 1), 1) .* inputs.minState); control = scaleToOriginal(phase.control, ones(size(phase.control, 1), 1) .* ... inputs.maxControl, ones(size(phase.control, 1), 1) .* inputs.minControl); -values.statePositions = getCorrectStates(state, 1, inputs.numCoordinates); -values.stateVelocities = getCorrectStates(state, 2, inputs.numCoordinates); -values.stateAccelerations = getCorrectStates(state, 3, inputs.numCoordinates); -values.controlJerks = control(:, 1 : inputs.numCoordinates); - +values.statePositions = getCorrectStates( ... + state, 1, length(inputs.statesCoordinateNames)); +values.stateVelocities = getCorrectStates( ... + state, 2, length(inputs.statesCoordinateNames)); +values.stateAccelerations = getCorrectStates( ... + state, 3, length(inputs.statesCoordinateNames)); +values.controlJerks = control(:, 1 : length(inputs.statesCoordinateNames)); +[values.positions, values.velocities, ... + values.accelerations] = recombineFullState(values, inputs); if strcmp(inputs.controllerType, 'torque') values.controlTorques = control(:, inputs.numCoordinates + 1 : ... inputs.numCoordinates + length(inputs.torqueControllerCoordinateNames)); else values.controlSynergyActivations = control(:, ... - inputs.numCoordinates + 1 : inputs.numCoordinates + inputs.numSynergies); + length(inputs.statesCoordinateNames) + 1 : ... + length(inputs.statesCoordinateNames) + inputs.numSynergies); end if strcmp(inputs.toolName, "TrackingOptimization") @@ -75,29 +80,29 @@ else values.synergyWeights = inputs.synergyWeights; end -% if inputs.splineSynergyActivations.dim > 1 -% values.controlSynergyActivations = ... -% fnval(inputs.splineSynergyActivations, values.time)'; -% else -% values.controlSynergyActivations = ... -% fnval(inputs.splineSynergyActivations, values.time); -% end -% if inputs.enableExternalTorqueControl -% controls = scaleToOriginal(phase.control, ones(size( ... -% phase.control, 1), 1) .* inputs.maxControl, ... -% ones(size(phase.control, 1), 1) .* inputs.minControl); -% values.externalTorqueControls = controls(:, inputs.numCoordinates + ... -% inputs.numSynergies + 1 : end); -% end -% else -% if isfield(inputs, "enableExternalTorqueControl") && ... -% inputs.enableExternalTorqueControl -% controls = scaleToOriginal(phase.control, ones(size( ... -% phase.control, 1), 1) .* inputs.maxControl, ... -% ones(size(phase.control, 1), 1) .* inputs.minControl); -% values.externalTorqueControls = controls(:, inputs.numCoordinates + ... -% length(inputs.torqueControllerCoordinateNames) + 1 : end); -% end + % if inputs.splineSynergyActivations.dim > 1 + % values.controlSynergyActivations = ... + % fnval(inputs.splineSynergyActivations, values.time)'; + % else + % values.controlSynergyActivations = ... + % fnval(inputs.splineSynergyActivations, values.time); + % end + % if inputs.enableExternalTorqueControl + % controls = scaleToOriginal(phase.control, ones(size( ... + % phase.control, 1), 1) .* inputs.maxControl, ... + % ones(size(phase.control, 1), 1) .* inputs.minControl); + % values.externalTorqueControls = controls(:, inputs.numCoordinates + ... + % inputs.numSynergies + 1 : end); + % end + % else + % if isfield(inputs, "enableExternalTorqueControl") && ... + % inputs.enableExternalTorqueControl + % controls = scaleToOriginal(phase.control, ones(size( ... + % phase.control, 1), 1) .* inputs.maxControl, ... + % ones(size(phase.control, 1), 1) .* inputs.minControl); + % values.externalTorqueControls = controls(:, inputs.numCoordinates + ... + % length(inputs.torqueControllerCoordinateNames) + 1 : end); + % end end if isfield(inputs, 'userDefinedVariables') for i = 1:length(inputs.userDefinedVariables) @@ -109,3 +114,19 @@ end end end + +function [positions, velocities, accelerations] = recombineFullState( ... + values, inputs) +positions = fnval(inputs.splineJointAngles, values.time)'; +velocities = fnval(inputs.splineJointVelocities, values.time)'; +accelerations = fnval(inputs.splineJointAccelerations, values.time)'; +for i = 1:length(inputs.coordinateNames) + index = find(ismember( ... + inputs.statesCoordinateNames, inputs.coordinateNames{i})); + if ~isempty(index) + positions(:, i) = values.statePositions(:, index); + velocities(:, i) = values.stateVelocities(:, index); + accelerations(:, i) = values.stateAccelerations(:, index); + end +end +end From 89ce49787c8f8eebe141100233e606ab89cc3b0e Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Wed, 20 Sep 2023 02:20:01 -0500 Subject: [PATCH 119/365] fix states as subset --- ...calcTrackingInverseDynamicLoadsIntegrand.m | 14 +++---- .../calcSynergyBasedModeledValues.m | 18 ++++----- .../calcTorqueBasedModeledValues.m | 4 +- .../generateCostTermStruct.m | 6 +-- .../makeExperimentalDataSplines.m | 4 ++ .../reportTreatmentOptimizationResults.m | 8 ++-- .../saveTreatmentOptimizationResults.m | 8 ++-- .../subsetDataByCoordinates.m | 38 +++++++++++++++++++ 8 files changed, 72 insertions(+), 28 deletions(-) create mode 100644 src/TreatmentOptimization/subsetDataByCoordinates.m diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m index 9fb687f49..8a8c73c4e 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m @@ -28,23 +28,23 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcTrackingInverseDynamicLoadsIntegrand(params, time, ... +function cost = calcTrackingInverseDynamicLoadsIntegrand(inputs, time, ... inverseDynamicMoments, loadName) loadName = erase(loadName, '_moment'); loadName = erase(loadName, '_force'); -indx = find(strcmp(convertCharsToStrings(params.coordinateNames), ... +indx = find(strcmp(convertCharsToStrings(inputs.coordinateNames), ... loadName)); -if params.splineJointMoments.dim > 1 - experimentalJointMoments = fnval(params.splineJointMoments, time)'; +if inputs.splineJointMoments.dim > 1 + experimentalJointMoments = fnval(inputs.splineJointMoments, time)'; else - experimentalJointMoments = fnval(params.splineJointMoments, time); + experimentalJointMoments = fnval(inputs.splineJointMoments, time); end -momentLabelsNoSuffix = erase(params.inverseDynamicMomentLabels, '_moment'); +momentLabelsNoSuffix = erase(inputs.inverseDynamicMomentLabels, '_moment'); momentLabelsNoSuffix = erase(momentLabelsNoSuffix, '_force'); -includedJointMomentCols = ismember(momentLabelsNoSuffix, convertCharsToStrings(params.coordinateNames)); +includedJointMomentCols = ismember(momentLabelsNoSuffix, convertCharsToStrings(inputs.coordinateNames)); if size(inverseDynamicMoments, 2) ~= size(experimentalJointMoments, 2) experimentalJointMoments = experimentalJointMoments(:, includedJointMomentCols); end diff --git a/src/TreatmentOptimization/TrackingOptimization/calcSynergyBasedModeledValues.m b/src/TreatmentOptimization/TrackingOptimization/calcSynergyBasedModeledValues.m index b8cf445da..f572a5e33 100644 --- a/src/TreatmentOptimization/TrackingOptimization/calcSynergyBasedModeledValues.m +++ b/src/TreatmentOptimization/TrackingOptimization/calcSynergyBasedModeledValues.m @@ -18,7 +18,7 @@ % National Institutes of Health (R01 EB030520). % % % % Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % +% Author(s): Marleny Vega, Claire V. Hammond % % % % Licensed under the Apache License, Version 2.0 (the "License"); % % you may not use this file except in compliance with the License. % @@ -41,7 +41,7 @@ jointAngles, jointVelocities); inputs.muscleTendonLength = permute(inputs.muscleTendonLength, [3 2 1]); inputs.muscleTendonVelocity = permute(inputs.muscleTendonVelocity, [3 2 1]); - inputs.momentArms = permute(inputs.momentArms, [ 4 2 3 1]); + inputs.momentArms = permute(inputs.momentArms, [4 2 3 1]); [modeledValues.normalizedFiberLength, modeledValues.normalizedFiberVelocity] = ... calcNormalizedMuscleFiberLengthsAndVelocities(inputs, ... ones(1, inputs.numMuscles), ones(1, inputs.numMuscles)); @@ -63,14 +63,14 @@ muscleActivations = permute(muscleActivations, [3 2 1]); end -function [jointAngles, jointVelocities] = getMuscleActuatedDOFs(values, params) -for i = 1:params.numMuscles +function [jointAngles, jointVelocities] = getMuscleActuatedDOFs(values, inputs) +for i = 1 : inputs.numMuscles counter = 1; - for j = 1:length(params.coordinateNames) - for k = 1:length(params.surrogateModelLabels{i}) - if strcmp(params.coordinateNames(j), params.surrogateModelLabels{i}{k}) - jointAngles{i}(:,counter) = values.statePositions(:,j); - jointVelocities{i}(:,counter) = values.stateVelocities(:,j); + for j = 1:length(inputs.coordinateNames) + for k = 1:length(inputs.surrogateModelLabels{i}) + if strcmp(inputs.coordinateNames(j), inputs.surrogateModelLabels{i}{k}) + jointAngles{i}(:, counter) = values.positions(:, j); + jointVelocities{i}(:, counter) = values.velocities(:, j); counter = counter + 1; end end diff --git a/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m b/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m index b900b2f16..051c5d0e3 100644 --- a/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m +++ b/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m @@ -47,8 +47,8 @@ appliedLoads = [appliedLoads groundReactionsBody]; end modeledValues.inverseDynamicMoments = inverseDynamics(values.time, ... - values.statePositions, values.stateVelocities, ... - values.stateAccelerations, inputs.coordinateNames, appliedLoads, ... + values.positions, values.velocities, ... + values.accelerations, inputs.coordinateNames, appliedLoads, ... inputs.mexModel); end diff --git a/src/TreatmentOptimization/generateCostTermStruct.m b/src/TreatmentOptimization/generateCostTermStruct.m index d3318d6c4..839949a3f 100644 --- a/src/TreatmentOptimization/generateCostTermStruct.m +++ b/src/TreatmentOptimization/generateCostTermStruct.m @@ -124,7 +124,7 @@ calcTrackingCoordinateIntegrand( ... auxdata, ... values.time, ... - values.statePositions, ... + values.positions, ... costTerm.coordinate ... ); @@ -240,7 +240,7 @@ costTermCalculations.joint_power_minimization = @(values, modeledValues, auxdata, costTerm) ... calcMinimizingJointPowerIntegrand( ... - values.stateVelocities, ... + values.velocities, ... modeledValues.inverseDynamicMoments, ... auxdata, ... costTerm.load ... @@ -293,7 +293,7 @@ costTermCalculations.kinematic_symmetry = @(values, modeledValues, auxdata, costTerm) ... calcKinematicSymmetryIntegrand( ... - values.statePositions, ... + values.positions, ... auxdata, ... costTerm); diff --git a/src/TreatmentOptimization/makeExperimentalDataSplines.m b/src/TreatmentOptimization/makeExperimentalDataSplines.m index 4c2173dfc..eb376742f 100644 --- a/src/TreatmentOptimization/makeExperimentalDataSplines.m +++ b/src/TreatmentOptimization/makeExperimentalDataSplines.m @@ -32,6 +32,10 @@ function inputs = makeExperimentalDataSplines(inputs) inputs.splineJointAngles = spaps(inputs.experimentalTime, ... inputs.experimentalJointAngles', 0.0000001); +inputs.splineJointVelocities = spaps(inputs.experimentalTime, ... + inputs.experimentalJointVelocities', 0.0000001); +inputs.splineJointAccelerations = spaps(inputs.experimentalTime, ... + inputs.experimentalJointAccelerations', 0.0000001); inputs.splineJointMoments = spaps(inputs.experimentalTime, ... inputs.experimentalJointMoments', 0.0000001); if strcmp(inputs.controllerType, 'synergy') diff --git a/src/TreatmentOptimization/reportTreatmentOptimizationResults.m b/src/TreatmentOptimization/reportTreatmentOptimizationResults.m index 4ab4213b6..3ca279de9 100644 --- a/src/TreatmentOptimization/reportTreatmentOptimizationResults.m +++ b/src/TreatmentOptimization/reportTreatmentOptimizationResults.m @@ -36,7 +36,7 @@ function reportTreatmentOptimizationResults(solution, inputs) inputs.userDefinedVariables{i}.lower_bounds) end end -values = getTreatmentOptimizationValueStruct(solution.solution.phase, inputs); +values = makeGpopsValuesAsStruct(solution.solution.phase, inputs); if strcmp(inputs.controllerType, 'synergy') % plot Muscle Activations plotMuscleActivations(solution.muscleActivations, values.time, ... @@ -61,10 +61,12 @@ function reportTreatmentOptimizationResults(solution, inputs) inputs.externalControlTorqueNames, ["External" "Torque Controls"]); end end +jointAngles = subsetDataByCoordinates(inputs.experimentalJointAngles, ... + inputs.coordinateNames, inputs.statesCoordinateNames); % plot joint angles plotResultsWithComparison(values.statePositions, values.time, ... - inputs.experimentalJointAngles, inputs.experimentalTime, ... - strcat(inputs.coordinateNames, ' [rad]'), ["Joint" "Angles [rad]"]); + jointAngles, inputs.experimentalTime, ... + strcat(inputs.statesCoordinateNames, ' [rad]'), ["Joint" "Angles [rad]"]); % plot joint moments plotResultsWithComparison(solution.inverseDynamicMoments, values.time, ... inputs.experimentalJointMoments, inputs.experimentalTime, ... diff --git a/src/TreatmentOptimization/saveTreatmentOptimizationResults.m b/src/TreatmentOptimization/saveTreatmentOptimizationResults.m index 592b38c89..94a3359eb 100644 --- a/src/TreatmentOptimization/saveTreatmentOptimizationResults.m +++ b/src/TreatmentOptimization/saveTreatmentOptimizationResults.m @@ -45,10 +45,10 @@ function saveTreatmentOptimizationResults(solution, inputs, values) for i = 1 : length(inputs.coordinateNames) stateLabels{end + 1} = strcat(inputs.coordinateNames{i}, '_dudt'); end -writeToSto(stateLabels, values.time, [values.statePositions ... - values.stateVelocities values.stateAccelerations], ... +writeToSto(stateLabels, values.time, [values.positions ... + values.velocities values.accelerations], ... fullfile(inputs.resultsDirectory, strcat(inputs.trialName, "_states.sto"))); -writeToSto(inputs.coordinateNames, values.time, values.controlJerks, ... +writeToSto(inputs.statesCoordinateNames, values.time, values.controlJerks, ... fullfile(inputs.resultsDirectory, strcat(inputs.trialName, "_jerks.sto"))); if strcmp(inputs.controllerType, 'synergy') controlLabels = {}; @@ -79,7 +79,7 @@ function saveInverseKinematicsResults(inputs, values, outputDirectory) if ~exist(fullfile(outputDirectory, "IKData"), "dir") mkdir(fullfile(outputDirectory, "IKData")) end -writeToSto(inputs.coordinateNames, values.time, values.statePositions, ... +writeToSto(inputs.coordinateNames, values.time, values.positions, ... fullfile(outputDirectory, "IKData", strcat(inputs.trialName, ".sto"))); end diff --git a/src/TreatmentOptimization/subsetDataByCoordinates.m b/src/TreatmentOptimization/subsetDataByCoordinates.m new file mode 100644 index 000000000..791cb8a59 --- /dev/null +++ b/src/TreatmentOptimization/subsetDataByCoordinates.m @@ -0,0 +1,38 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This convenience function simply returns the subset of values that are +% included in the second coordinate list from the first. This is ordered +% assuming Treatment Optimization ordering, not Model Personalization +% ordering. +% +% (matrix, array of string, array of string) -> (matrix) +% return a set of setup values common to all optimal control problems + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Claire V. Hammond % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function output = subsetDataByCoordinates(data, coordinateNames, ... + subsetOfCoordinateNames) +includedSubset = ismember(coordinateNames, subsetOfCoordinateNames); +output = data(:, includedSubset); +end + From 8cb241c13cd42633f4ab41dfd717ddc8b1597a01 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Thu, 21 Sep 2023 00:03:52 -0500 Subject: [PATCH 120/365] Get surrogate coordinates from synergy groups --- .../SurrogateModelCreation.m | 38 +++++++++++++++++-- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/src/SurrogateModelCreation/SurrogateModelCreation.m b/src/SurrogateModelCreation/SurrogateModelCreation.m index 750195297..64a8fd028 100644 --- a/src/SurrogateModelCreation/SurrogateModelCreation.m +++ b/src/SurrogateModelCreation/SurrogateModelCreation.m @@ -46,11 +46,11 @@ function inputs = getData(inputs) -tree.trial_prefixes = inputs.trialPrefixes; +tree.trial_prefixes = inputs.trialName; prefixes = findPrefixes(tree.trial_prefixes, inputs.dataDirectory); -inputs.muscleNames = getMusclesFromCoordinates(inputs.model, ... - inputs.surrogateModelCoordinateNames); -inputs.numMuscles = length(inputs.muscleNames); +% inputs.muscleNames = getMusclesFromCoordinates(inputs.model, ... +% inputs.surrogateModelCoordinateNames); +% inputs.numMuscles = length(inputs.muscleNames); inverseKinematicsFileNames = findFileListFromPrefixList(fullfile( ... inputs.dataDirectory, "IKData"), prefixes); @@ -72,6 +72,9 @@ inputs.dataDirectory, "MAData"), prefixes); [inputs.muscleTendonLengths, inputs.muscleTendonColumnNames] = ... parseFileFromDirectories(directories, "Length.sto", Model(inputs.model)); +inputs.muscleNames = findMusclesFromSynergyGroups(inputs); +inputs.surrogateModelCoordinateNames = findSurrogateModelCoordinates( ... + inputs, directories); inputs.muscleTendonLengths = findSpecificMusclesInData( ... inputs.muscleTendonLengths, inputs.muscleTendonColumnNames, ... inputs.muscleNames); @@ -109,6 +112,33 @@ end end +function coordinates = findSurrogateModelCoordinates(inputs, ... + directories) +coordinates = string([]); +includedMuscles = ismember(inputs.muscleTendonColumnNames, ... + inputs.muscleNames); +[data, fullCoordinates] = parseMomentArms(directories, ... + Model(inputs.model)); +for i = 1 : length(fullCoordinates) + if any(any(any(data(:, i, includedMuscles, :) > inputs.epsilon))) + coordinates(end+1) = fullCoordinates(i); + end +end +end + +function muscleNames = findMusclesFromSynergyGroups(inputs) +muscleNames = string([]); +model = Model(inputs.model); +for i = 1 : length(inputs.synergyGroups) + group = model.getForceSet.getGroup( ... + inputs.synergyGroups{i}.muscleGroupName).getMembers(); + for j = 0 : group.getSize() - 1 + muscleNames(end+1) = convertCharsToStrings( ... + group.get(j).getName.toCharArray'); + end +end +end + function [muscleTendonLengths, momentArms, ... experimentalJointAngles] = performLhsSampling(inputs) From 9e683534f5bf7679fee7725242b92aaa47f7f7f1 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Thu, 21 Sep 2023 00:03:56 -0500 Subject: [PATCH 121/365] Create SurrogateModelPreviewTool.m --- .../SurrogateModelPreviewTool.m | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 src/SurrogateModelCreation/SurrogateModelPreviewTool.m diff --git a/src/SurrogateModelCreation/SurrogateModelPreviewTool.m b/src/SurrogateModelCreation/SurrogateModelPreviewTool.m new file mode 100644 index 000000000..193471b00 --- /dev/null +++ b/src/SurrogateModelCreation/SurrogateModelPreviewTool.m @@ -0,0 +1,51 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function takes a properly formatted XML file and fits surrogate +% muscle geometry to validate the surrogate muscle model settings. +% +% (string) -> (None) +% Create and plot surrogate model from Treatment Optimization settings file + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Spencer Williams % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function SurrogateModelPreviewTool(settingsFileName) +settingsTree = xml2struct(settingsFileName); +toolFields = fieldnames(settingsTree.NMSMPipelineDocument); +switch toolFields{1} + case 'TrackingOptimizationTool' + verifyVersion(settingsTree, "TrackingOptimizationTool"); + [inputs, params] = parseTrackingOptimizationSettingsTree(settingsTree); + case 'VerificationOptimizationTool' + verifyVersion(settingsTree, "VerificationOptimizationTool"); + [inputs, params] = parseVerificationOptimizationSettingsTree(settingsTree); + case 'DesignOptimizationTool' + verifyVersion(settingsTree, "DesignOptimizationTool"); + [inputs, params] = parseDesignOptimizationSettingsTree(settingsTree); + otherwise + MException('Incorrect Settings File', ... + "Invalid settings file type. Valid types include " + ... + "TrackingOptimizationTool, VerificationOptimizationTool," + ... + " and DesignOptimizationTool.") +inputs.plotResults = true; +SurrogateModelCreation(inputs); +end From d481588a15c8e38374ac2a3350c082ce48f32477 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Thu, 21 Sep 2023 00:42:19 -0500 Subject: [PATCH 122/365] Parse surrogate model options from TO --- .../SurrogateModelPreviewTool.m | 1 + src/core/parse/parseSynergyController.m | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/SurrogateModelCreation/SurrogateModelPreviewTool.m b/src/SurrogateModelCreation/SurrogateModelPreviewTool.m index 193471b00..44e3e0cdc 100644 --- a/src/SurrogateModelCreation/SurrogateModelPreviewTool.m +++ b/src/SurrogateModelCreation/SurrogateModelPreviewTool.m @@ -46,6 +46,7 @@ function SurrogateModelPreviewTool(settingsFileName) "Invalid settings file type. Valid types include " + ... "TrackingOptimizationTool, VerificationOptimizationTool," + ... " and DesignOptimizationTool.") +end inputs.plotResults = true; SurrogateModelCreation(inputs); end diff --git a/src/core/parse/parseSynergyController.m b/src/core/parse/parseSynergyController.m index 63c583b86..767041f82 100644 --- a/src/core/parse/parseSynergyController.m +++ b/src/core/parse/parseSynergyController.m @@ -40,11 +40,20 @@ inputs.numMuscles = length(inputs.muscleNames); inputs.epsilon = str2double(parseElementTextByNameOrAlternate(tree, ... "epsilon", "1e-4")); +inputs.polynomialDegree = str2double(parseElementTextByNameOrAlternate( ... + tree, "surrogate_model_polynomial_degree", "5")); +inputs.performLatinHyperCubeSampling = strcmpi( ... + parseElementTextByNameOrAlternate(tree, ... + "perform_latin_hypercube_sampling", "false"), "true"); +if inputs.performLatinHyperCubeSampling + inputs.lhsRangeMultiplier = str2double( ... + parseElementTextByName(tree, "latin_hypercube_range_multiplier")); + inputs.lhsNumPoints = str2double( ... + parseElementTextByName(tree, "latin_hypercube_number_of_points")); +end +inputs.dataDirectory = parseElementTextByName(tree, "data_directory"); inputs.vMaxFactor = str2double(parseElementTextByNameOrAlternate(tree, ... "maximum_shortening_velocity_multiplier", "10")); -surrogateModelCoefficients = load(getTextFromField(getFieldByName(tree, ... - 'surrogate_model_coefficients'))); -inputs.coefficients = surrogateModelCoefficients.coefficients; inputs.optimizeSynergyVectors = getBooleanLogic(... parseElementTextByNameOrAlternate(tree, "optimize_synergy_vectors", 0)); inputs.maxControlSynergyActivations = parseDoubleOrAlternate(tree, ... From 3fbd26779ce7013e162165c3edb7ee0bc6124ed1 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Thu, 21 Sep 2023 13:46:08 -0500 Subject: [PATCH 123/365] Fix TO parsing for surrogate models --- .../getMuscleSpecificSurrogateModelData.m | 2 +- src/core/parse/parseTreatmentOptimizationDataDirectory.m | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SurrogateModelCreation/getMuscleSpecificSurrogateModelData.m b/src/SurrogateModelCreation/getMuscleSpecificSurrogateModelData.m index 14783388f..215d67e7f 100644 --- a/src/SurrogateModelCreation/getMuscleSpecificSurrogateModelData.m +++ b/src/SurrogateModelCreation/getMuscleSpecificSurrogateModelData.m @@ -34,7 +34,7 @@ if strcmp(inputs.coordinateNames(j), inputs.surrogateModelCoordinateNames(k)) if max(abs(inputs.momentArms(:,k,i))) > inputs.epsilon inputs.surrogateModelLabels{i}(counter) = ... - inputs.coordinateNames(j); + {convertStringsToChars(inputs.coordinateNames(j))}; inputs.muscleSpecificJointAngles{i}(:,counter) = ... inputs.experimentalJointAngles(:,j); inputs.muscleSpecificMomentArms{i}(:,counter) = ... diff --git a/src/core/parse/parseTreatmentOptimizationDataDirectory.m b/src/core/parse/parseTreatmentOptimizationDataDirectory.m index 68de9b21a..8cc1fa21c 100644 --- a/src/core/parse/parseTreatmentOptimizationDataDirectory.m +++ b/src/core/parse/parseTreatmentOptimizationDataDirectory.m @@ -79,7 +79,7 @@ inputs.synergyWeights = parseTrialData(inputs.previousResultsDirectory, ... "synergyWeights", inputs.model); directories = findFirstLevelSubDirectoriesFromPrefixes(fullfile( ... - dataDirectory, "MAData"), inputs.trialName); + dataDirectory, "MAData"), convertCharsToStrings(inputs.trialName)); inputs.momentArms = parseSelectMomentArms(directories, ... inputs.surrogateModelCoordinateNames, inputs.muscleNames); inputs.momentArms = reshape(permute(inputs.momentArms, [1 4 2 3]), [], ... From b229f4832e85f06a9cfd89357c3c8b39f7028fc7 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Thu, 21 Sep 2023 14:01:02 -0500 Subject: [PATCH 124/365] Add surrogate muscle cell array in TO inputs --- src/TreatmentOptimization/makeTreatmentOptimizationInputs.m | 1 + 1 file changed, 1 insertion(+) diff --git a/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m b/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m index 9fac41322..39404f1e9 100644 --- a/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m +++ b/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m @@ -37,5 +37,6 @@ inputs = makePathConstraintBounds(inputs); inputs = makeTerminalConstraintBounds(inputs); inputs = makeOptimalControlBounds(inputs); +inputs.surrogateMuscles = SurrogateModelCreation(inputs); end From f01bc8efc4557662553174fe62452c588adea1de Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 24 Sep 2023 13:09:41 -0500 Subject: [PATCH 125/365] fix small bugs with state subset --- .../calcMinimizingJointJerkIntegrand.m | 7 ++++++- .../calcTrackingCoordinateIntegrand.m | 7 ++++++- .../setupGpopsInitialGuess.m | 19 ++++++++++++++++++- .../TerminalTerms/calcFinalStatePosition.m | 5 +++++ .../generateConstraintTermStruct.m | 2 +- 5 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointJerkIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointJerkIntegrand.m index 70251b0d0..ffc2e3537 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointJerkIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointJerkIntegrand.m @@ -32,8 +32,13 @@ indx = find(strcmp(convertCharsToStrings(inputs.statesCoordinateNames), ... costTerm.coordinate)); +if isempty(indx) + throw(MException('CostTermError:CoordinateNotInState', ... + strcat("Coordinate ", costTerm.coordinate, " is not in the ", ... + ""))) +end % cost = calcMinimizingCostArrayTerm(jointJerks(:, indx)); errorCenter = valueOrAlternate(costTerm, "errorCenter", 0); -maximumAllowableError = valueOrAlternate(costTerm, "maxAllowableError", 10); +maximumAllowableError = valueOrAlternate(costTerm, "maxAllowableError", 10000); cost = jointJerks(:, indx) / maximumAllowableError; end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m index 7c3049d32..0364bcaef 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m @@ -31,8 +31,13 @@ function cost = calcTrackingCoordinateIntegrand(auxdata, time, ... statePositions, coordinateName) -indx = find(strcmp(convertCharsToStrings(auxdata.coordinateNames), ... +indx = find(strcmp(convertCharsToStrings(auxdata.statesCoordinateNames), ... coordinateName)); +if isempty(indx) + throw(MException('CostTermError:CoordinateNotInState', ... + strcat("Coordinate ", coordinateName, " is not in the ", ... + ""))) +end if auxdata.splineJointAngles.dim > 1 experimentalJointAngles = fnval(auxdata.splineJointAngles, time)'; else diff --git a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m index 1fe1056df..025e8554c 100644 --- a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m +++ b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m @@ -40,7 +40,11 @@ function guess = setupInitialStatesGuess(inputs, guess) if isfield(inputs, "initialStates") - guess.phase.state = scaleToBounds(inputs.initialStates, ... + states = subsetInitialStatesDataByCoordinates( ... + inputs.initialStates, ... + inputs.initialStatesLabels, ... + inputs.statesCoordinateNames); + guess.phase.state = scaleToBounds(states, ... inputs.maxState, inputs.minState); guess.phase.time = scaleToBounds(inputs.initialTime, inputs.maxTime, ... inputs.minTime); @@ -101,4 +105,17 @@ guess.parameter = scaleToBounds(inputs.synergyWeights, ... inputs.maxParameter, inputs.minParameter); end +end + +function output = subsetInitialStatesDataByCoordinates(data, ... + coordinateNames, subsetOfCoordinateNames) +includedSubset = ismember(coordinateNames, subsetOfCoordinateNames); +numCoordinates = length(includedSubset) / 3; +for i = 1:numCoordinates + if includedSubset(i) + includedSubset(i + numCoordinates) = true; + includedSubset(i + (2 * numCoordinates)) = true; + end +end +output = data(:, includedSubset); end \ No newline at end of file diff --git a/src/TreatmentOptimization/TerminalTerms/calcFinalStatePosition.m b/src/TreatmentOptimization/TerminalTerms/calcFinalStatePosition.m index d212e50d9..78be6acdb 100644 --- a/src/TreatmentOptimization/TerminalTerms/calcFinalStatePosition.m +++ b/src/TreatmentOptimization/TerminalTerms/calcFinalStatePosition.m @@ -32,5 +32,10 @@ statePositions, coordinateNames, constraintTerm) indx = find(strcmp(convertCharsToStrings(coordinateNames), ... constraintTerm.coordinate)); +if isempty(indx) + throw(MException('ConstraintTermError:CoordinateNotInState', ... + strcat("Coordinate ", constraintTerm.coordinate, " is not in the ", ... + ""))) +end finalStatePosition = statePositions(end, indx) - constraintTerm.target_error; end \ No newline at end of file diff --git a/src/TreatmentOptimization/generateConstraintTermStruct.m b/src/TreatmentOptimization/generateConstraintTermStruct.m index f2bd2459f..c7ecd7f6d 100644 --- a/src/TreatmentOptimization/generateConstraintTermStruct.m +++ b/src/TreatmentOptimization/generateConstraintTermStruct.m @@ -247,7 +247,7 @@ constraintTermCalculations.final_state_position = @(values, modeledValues, auxdata, constraintTerm) ... calcFinalStatePosition( ... values.statePositions, ... - auxdata.coordinateNames, ... + auxdata.statesCoordinateNames, ... constraintTerm); constraintTermCalculations.final_state_velocity = @(values, modeledValues, auxdata, constraintTerm) ... From c88a4bf5652001c8450b53b2377458923dda52e9 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" Date: Sun, 24 Sep 2023 11:25:43 -0700 Subject: [PATCH 126/365] fix inverseDynamic to inverseDynamics --- .../calcMinimizingJointPowerIntegrand.m | 6 ++-- .../calcTrackingControllerIntegrand.m | 4 +-- ...calcTrackingInverseDynamicLoadsIntegrand.m | 10 +++---- .../checkParameterGuess.m | 26 ++++++++-------- ...calcMuscleActuatedMomentsPathConstraints.m | 12 ++++---- ...tedMomentsWithExternalAidPathConstraints.m | 10 +++---- .../calcRootSegmentResidualsPathConstraints.m | 10 +++---- ...calcTorqueActuatedMomentsPathConstraints.m | 6 ++-- .../SetupBounds/makeOptimalControlBounds.m | 4 +-- .../calcRootSegmentResidualsPeriodicity.m | 12 ++++---- .../calcTorqueBasedModeledValues.m | 4 +-- .../generateConstraintTermStruct.m | 10 +++---- .../generateCostTermStruct.m | 8 ++--- .../makeSurrogateModel.m | 10 +++---- .../reportTreatmentOptimizationResults.m | 6 ++-- .../saveTreatmentOptimizationResults.m | 4 +-- src/core/mex/inverseDynamics.m | 12 ++++---- src/core/mex/inverseDynamicsMatlabParallel.m | 30 +++++++++---------- .../parseTreatmentOptimizationDataDirectory.m | 2 +- 19 files changed, 93 insertions(+), 93 deletions(-) diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointPowerIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointPowerIntegrand.m index 39673ae16..41d7d49fe 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointPowerIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointPowerIntegrand.m @@ -1,7 +1,7 @@ % This function is part of the NMSM Pipeline, see file for full license. % % This function calculates and minimizes the joint power for the specified -% coordinate. +% coordinate. % % (2D matrix, 2D matrix, struct, Array of string) -> (Array of number) @@ -34,7 +34,7 @@ loadName = erase(loadName, '_force'); indx = find(strcmp(convertCharsToStrings(params.coordinateNames), ... loadName)); -momentLabelsNoSuffix = erase(params.inverseDynamicMomentLabels, '_moment'); +momentLabelsNoSuffix = erase(params.inverseDynamicsMomentLabels, '_moment'); momentLabelsNoSuffix = erase(momentLabelsNoSuffix, '_force'); includedJointMomentCols = ismember(momentLabelsNoSuffix, ... convertCharsToStrings(params.coordinateNames)); @@ -43,4 +43,4 @@ end jointPower = jointMoment(:, indx) .* jointVelocity(:, indx); cost = calcMinimizingCostArrayTerm(jointPower); -end \ No newline at end of file +end diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m index 78e4460d9..126a8a1cd 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m @@ -43,7 +43,7 @@ values.controlSynergyActivations, indx); case 'torque' indx1 = find(strcmp(convertCharsToStrings( ... - auxdata.inverseDynamicMomentLabels), controllerName)); + auxdata.inverseDynamicsMomentLabels), controllerName)); indx2 = find(strcmp(convertCharsToStrings( ... strcat(auxdata.torqueControllerCoordinateNames, '_moment')), ... controllerName)); @@ -56,4 +56,4 @@ end cost = experimentalJointMoments(:, indx1) - ... values.controlTorques(:, indx2); -end \ No newline at end of file +end diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m index 8a8c73c4e..9b19d0d50 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m @@ -29,7 +29,7 @@ % ----------------------------------------------------------------------- % function cost = calcTrackingInverseDynamicLoadsIntegrand(inputs, time, ... - inverseDynamicMoments, loadName) + inverseDynamicsMoments, loadName) loadName = erase(loadName, '_moment'); loadName = erase(loadName, '_force'); @@ -42,12 +42,12 @@ experimentalJointMoments = fnval(inputs.splineJointMoments, time); end -momentLabelsNoSuffix = erase(inputs.inverseDynamicMomentLabels, '_moment'); +momentLabelsNoSuffix = erase(inputs.inverseDynamicsMomentLabels, '_moment'); momentLabelsNoSuffix = erase(momentLabelsNoSuffix, '_force'); includedJointMomentCols = ismember(momentLabelsNoSuffix, convertCharsToStrings(inputs.coordinateNames)); -if size(inverseDynamicMoments, 2) ~= size(experimentalJointMoments, 2) +if size(inverseDynamicsMoments, 2) ~= size(experimentalJointMoments, 2) experimentalJointMoments = experimentalJointMoments(:, includedJointMomentCols); end cost = calcTrackingCostArrayTerm(experimentalJointMoments, ... - inverseDynamicMoments, indx); -end \ No newline at end of file + inverseDynamicsMoments, indx); +end diff --git a/src/TreatmentOptimization/OptimalControlSetupFunctions/checkParameterGuess.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/checkParameterGuess.m index 1b0a0959f..65160a0fe 100644 --- a/src/TreatmentOptimization/OptimalControlSetupFunctions/checkParameterGuess.m +++ b/src/TreatmentOptimization/OptimalControlSetupFunctions/checkParameterGuess.m @@ -1,6 +1,6 @@ % This function is part of the NMSM Pipeline, see file for full license. % -% This function checks that the initial guess parameters file is in the +% This function checks that the initial guess parameters file is in the % correct order % % (struct) -> (struct) @@ -29,10 +29,10 @@ % ----------------------------------------------------------------------- % function inputs = checkParameterGuess(inputs) -if isfield(inputs.initialGuess, 'parameter') || isfield(inputs,"synergyWeights") +if isfield(inputs.initialGuess, 'parameter') || isfield(inputs,"synergyWeights") if isfield(inputs.initialGuess, 'parameter') inputs.synergyWeightsGuess = inputs.initialGuess.parameter; - elseif isfield(inputs,"synergyWeights") + elseif isfield(inputs,"synergyWeights") inputs.synergyWeightsGuess = inputs.synergyWeights; end @@ -41,14 +41,14 @@ for j = 1 : inputs.numMuscles for k = 1 : length(inputs.synergyGroups{i}.muscleNames) if strcmpi(inputs.muscleNames(j), inputs.synergyGroups{i}.muscleNames(k)) - if i <= 1 + if i <= 1 parameterIndex(i, k) = j; else parameterIndex(i, k + length(inputs.synergyGroups{i}.muscleNames)) = j; end end end - end + end end synergyWeightsFlattened = []; numSynergiesIndex = 0; @@ -61,31 +61,31 @@ end inputs.synergyWeightsGuess = synergyWeightsFlattened; end -if strcmp(inputs.controllerType, 'synergy') +if strcmp(inputs.controllerType, 'synergy') inputs = getMuscleSynergiesInitialGuess(inputs); for i = 1 : length(inputs.coordinateNames) for j = 1 : length(inputs.surrogateModelCoordinateNames) if strcmp(inputs.coordinateNames(i), inputs.surrogateModelCoordinateNames(j)) inputs.surrogateModelIndex(j) = i; end - end + end end inputs.dofsActuatedIndex = []; - for i = 1 : length(inputs.inverseDynamicMomentLabels) + for i = 1 : length(inputs.inverseDynamicsMomentLabels) for j = 1 : length(inputs.surrogateModelCoordinateNames) - if strcmp(inputs.inverseDynamicMomentLabels(i), ... + if strcmp(inputs.inverseDynamicsMomentLabels(i), ... strcat(inputs.surrogateModelCoordinateNames(j), '_moment')) inputs.dofsActuatedIndex(end+1) = j; end - end + end end end end function inputs = getMuscleSynergiesInitialGuess(inputs) -if isfield(inputs.initialGuess,"parameter") || isfield(inputs,"synergyWeights") +if isfield(inputs.initialGuess,"parameter") || isfield(inputs,"synergyWeights") synergyWeights = getSynergyWeightsFromGroups(inputs.synergyWeightsGuess, inputs); - inputs.synergyActivationsGuess = inputs.experimentalMuscleActivations / synergyWeights; + inputs.synergyActivationsGuess = inputs.experimentalMuscleActivations / synergyWeights; else inputs.mtpActivationsColumnNames = inputs.muscleLabels; inputs.mtpActivations = permute(inputs.experimentalMuscleActivations, [3 2 1]); @@ -93,4 +93,4 @@ synergyWeights = getSynergyWeightsFromGroups(inputs.synergyWeightsGuess, inputs); inputs.synergyActivationsGuess = inputs.experimentalMuscleActivations / synergyWeights; end -end \ No newline at end of file +end diff --git a/src/TreatmentOptimization/PathTerms/calcMuscleActuatedMomentsPathConstraints.m b/src/TreatmentOptimization/PathTerms/calcMuscleActuatedMomentsPathConstraints.m index c802b49c1..b12e66b84 100644 --- a/src/TreatmentOptimization/PathTerms/calcMuscleActuatedMomentsPathConstraints.m +++ b/src/TreatmentOptimization/PathTerms/calcMuscleActuatedMomentsPathConstraints.m @@ -1,9 +1,9 @@ % This function is part of the NMSM Pipeline, see file for full license. -% +% % This function calculates the difference between the inverse dynamic -% moments and the muscle produced moments for the specified coordinate. +% moments and the muscle produced moments for the specified coordinate. % Applicable only if the model is synergy driven. -% +% % (struct, struct, Array of string) -> (Array of number) % @@ -31,10 +31,10 @@ function pathTerm = calcMuscleActuatedMomentsPathConstraints(inputs, ... modeledValues, loadName) -indx1 = find(strcmp(convertCharsToStrings(inputs.inverseDynamicMomentLabels), ... +indx1 = find(strcmp(convertCharsToStrings(inputs.inverseDynamicsMomentLabels), ... loadName)); indx2 = find(strcmp(strcat(inputs.surrogateModelCoordinateNames, ... '_moment'), loadName)); -pathTerm = modeledValues.inverseDynamicMoments(:, indx1) - ... +pathTerm = modeledValues.inverseDynamicsMoments(:, indx1) - ... modeledValues.muscleJointMoments(:, indx2); -end \ No newline at end of file +end diff --git a/src/TreatmentOptimization/PathTerms/calcMuscleActuatedMomentsWithExternalAidPathConstraints.m b/src/TreatmentOptimization/PathTerms/calcMuscleActuatedMomentsWithExternalAidPathConstraints.m index 438b7962d..da01e026b 100644 --- a/src/TreatmentOptimization/PathTerms/calcMuscleActuatedMomentsWithExternalAidPathConstraints.m +++ b/src/TreatmentOptimization/PathTerms/calcMuscleActuatedMomentsWithExternalAidPathConstraints.m @@ -1,12 +1,12 @@ % This function is part of the NMSM Pipeline, see file for full license. % % This function calculates the difference between the inverse dynamic -% moments and the muscle produced moments with the aid of an external +% moments and the muscle produced moments with the aid of an external % torque controller for the specified coordinate. Applicable only if the % model is synergy driven and if an external torque controller is present. % % (struct, struct, 2D matrix, Array of string) -> (Array of number) -% +% % ----------------------------------------------------------------------- % % The NMSM Pipeline is a toolkit for model personalization and treatment % @@ -34,14 +34,14 @@ calcMuscleActuatedMomentsWithExternalAidPathConstraints(params, ... modeledValues, externalControlTorques, coordinate) -indx1 = find(strcmp(convertCharsToStrings(params.inverseDynamicMomentLabels), ... +indx1 = find(strcmp(convertCharsToStrings(params.inverseDynamicsMomentLabels), ... [coordinate '_moment'])); indx2 = find(strcmp(params.surrogateModelCoordinateNames, ... coordinate)); indx3 = find(strcmp(params.externalControlTorqueNames, ... coordinate)); -pathTerm = modeledValues.inverseDynamicMoments(:, indx1) - ... +pathTerm = modeledValues.inverseDynamicsMoments(:, indx1) - ... (modeledValues.muscleJointMoments(:, indx2) + ... externalControlTorques(:, indx3)); -end \ No newline at end of file +end diff --git a/src/TreatmentOptimization/PathTerms/calcRootSegmentResidualsPathConstraints.m b/src/TreatmentOptimization/PathTerms/calcRootSegmentResidualsPathConstraints.m index 0f1b26f24..01b5840e9 100644 --- a/src/TreatmentOptimization/PathTerms/calcRootSegmentResidualsPathConstraints.m +++ b/src/TreatmentOptimization/PathTerms/calcRootSegmentResidualsPathConstraints.m @@ -4,7 +4,7 @@ % specified by the user for the specified root semgment loads. % % (Array of string, Cell, 2D matrix) -> (Array of number) -% +% % ----------------------------------------------------------------------- % % The NMSM Pipeline is a toolkit for model personalization and treatment % @@ -29,7 +29,7 @@ % ----------------------------------------------------------------------- % function pathTerm = calcRootSegmentResidualsPathConstraints(loadName, ... - inverseDynamicMomentLabels, inverseDynamicMoments) -pathTerm = inverseDynamicMoments(:, ... - find(strcmp(inverseDynamicMomentLabels, loadName))); -end \ No newline at end of file + inverseDynamicsMomentLabels, inverseDynamicsMoments) +pathTerm = inverseDynamicsMoments(:, ... + find(strcmp(inverseDynamicsMomentLabels, loadName))); +end diff --git a/src/TreatmentOptimization/PathTerms/calcTorqueActuatedMomentsPathConstraints.m b/src/TreatmentOptimization/PathTerms/calcTorqueActuatedMomentsPathConstraints.m index a97145e4f..1dde4930f 100644 --- a/src/TreatmentOptimization/PathTerms/calcTorqueActuatedMomentsPathConstraints.m +++ b/src/TreatmentOptimization/PathTerms/calcTorqueActuatedMomentsPathConstraints.m @@ -5,7 +5,7 @@ % Applicable only if the model is torque driven. % % (struct, struct, 2D matrix, Array of string) -> (Array of number) -% +% % ----------------------------------------------------------------------- % % The NMSM Pipeline is a toolkit for model personalization and treatment % @@ -37,6 +37,6 @@ indx1 = find(cellfun(@isequal, inputs.coordinateNames, ... repmat({loadName}, 1, length(inputs.coordinateNames)))); indx2 = find(strcmp(inputs.torqueControllerCoordinateNames, loadName)); -pathTerm = modeledValues.inverseDynamicMoments(:, indx1) - ... +pathTerm = modeledValues.inverseDynamicsMoments(:, indx1) - ... controlTorques(:, indx2); -end \ No newline at end of file +end diff --git a/src/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m b/src/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m index 158ea8bef..c51a6943c 100644 --- a/src/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m +++ b/src/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m @@ -98,11 +98,11 @@ if isfield(inputs, "torqueControllerCoordinateNames") for i = 1:length(inputs.torqueControllerCoordinateNames) indx = find(strcmp(convertCharsToStrings( ... - inputs.inverseDynamicMomentLabels), ... + inputs.inverseDynamicsMomentLabels), ... strcat(inputs.torqueControllerCoordinateNames(i), '_moment'))); if isempty(indx) indx = find(strcmp(convertCharsToStrings( ... - inputs.inverseDynamicMomentLabels), ... + inputs.inverseDynamicsMomentLabels), ... strcat(inputs.torqueControllerCoordinateNames(i), '_force'))); end maxControlTorques(i) = max(inputs.experimentalJointMoments(:, ... diff --git a/src/TreatmentOptimization/TerminalTerms/calcRootSegmentResidualsPeriodicity.m b/src/TreatmentOptimization/TerminalTerms/calcRootSegmentResidualsPeriodicity.m index 27dbc3d4e..6eb2140ae 100644 --- a/src/TreatmentOptimization/TerminalTerms/calcRootSegmentResidualsPeriodicity.m +++ b/src/TreatmentOptimization/TerminalTerms/calcRootSegmentResidualsPeriodicity.m @@ -1,10 +1,10 @@ % This function is part of the NMSM Pipeline, see file for full license. % % This function calculates the difference between the starting and final -% inverse dynamic moment for the specified coordinate. +% inverse dynamic moment for the specified coordinate. % % (2D matrix, Cell, Array of string) -> (Number) -% +% % ----------------------------------------------------------------------- % % The NMSM Pipeline is a toolkit for model personalization and treatment % @@ -29,8 +29,8 @@ % ----------------------------------------------------------------------- % function rootSegmentResidualsPeriodicity = calcRootSegmentResidualsPeriodicity( ... - inverseDynamicMoments, inverseDynamicMomentLabels, loadNames) -indx = find(strcmp(convertCharsToStrings(inverseDynamicMomentLabels), ... + inverseDynamicsMoments, inverseDynamicsMomentLabels, loadNames) +indx = find(strcmp(convertCharsToStrings(inverseDynamicsMomentLabels), ... loadNames)); -rootSegmentResidualsPeriodicity = diff(inverseDynamicMoments(:, indx)); -end \ No newline at end of file +rootSegmentResidualsPeriodicity = diff(inverseDynamicsMoments(:, indx)); +end diff --git a/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m b/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m index 051c5d0e3..ca436c938 100644 --- a/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m +++ b/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m @@ -46,7 +46,7 @@ modeledValues.groundReactionsLab = calcGroundReactionsLab(groundReactions); appliedLoads = [appliedLoads groundReactionsBody]; end -modeledValues.inverseDynamicMoments = inverseDynamics(values.time, ... +modeledValues.inverseDynamicsMoments = inverseDynamics(values.time, ... values.positions, values.velocities, ... values.accelerations, inputs.coordinateNames, appliedLoads, ... inputs.mexModel); @@ -115,4 +115,4 @@ groundReactionsInLab.moments{i} = ... groundReactions.parentMoments{i} + groundReactions.childMoments{i}; end -end \ No newline at end of file +end diff --git a/src/TreatmentOptimization/generateConstraintTermStruct.m b/src/TreatmentOptimization/generateConstraintTermStruct.m index c7ecd7f6d..1a9958f2c 100644 --- a/src/TreatmentOptimization/generateConstraintTermStruct.m +++ b/src/TreatmentOptimization/generateConstraintTermStruct.m @@ -172,8 +172,8 @@ constraintTermCalculations.root_segment_residual_load = @(values, modeledValues, auxdata, constraintTerm) ... calcRootSegmentResidualsPathConstraints( ... constraintTerm.load, ... - auxdata.inverseDynamicMomentLabels, ... - modeledValues.inverseDynamicMoments ... + auxdata.inverseDynamicsMomentLabels, ... + modeledValues.inverseDynamicsMoments ... ); constraintTermCalculations.muscle_model_moment_consistency = @(values, modeledValues, auxdata, constraintTerm) ... @@ -228,8 +228,8 @@ constraintTermCalculations.root_segment_residual_load_periodicity = @(values, modeledValues, auxdata, constraintTerm) ... calcRootSegmentResidualsPeriodicity(... - modeledValues.inverseDynamicMoments, ... - auxdata.inverseDynamicMomentLabels, ... + modeledValues.inverseDynamicsMoments, ... + auxdata.inverseDynamicsMomentLabels, ... constraintTerm.load); constraintTermCalculations.external_force_periodicity = @(values, modeledValues, auxdata, constraintTerm) ... @@ -269,4 +269,4 @@ auxdata.synergyGroups, ... constraintTerm.synergy_group); -end \ No newline at end of file +end diff --git a/src/TreatmentOptimization/generateCostTermStruct.m b/src/TreatmentOptimization/generateCostTermStruct.m index 839949a3f..4545a10ac 100644 --- a/src/TreatmentOptimization/generateCostTermStruct.m +++ b/src/TreatmentOptimization/generateCostTermStruct.m @@ -72,7 +72,7 @@ "propulsive_force_minimization" ... "breaking_force_maximization" ... "breaking_force_minimization" ... - "step_length_maximization" ... + "step_length_maximization" ... "step_length_asymmetry_minimization" ... "single_support_time_maximization" ... "single_support_time_goal" ... @@ -153,7 +153,7 @@ calcTrackingInverseDynamicLoadsIntegrand( ... auxdata, ... values.time, ... - modeledValues.inverseDynamicMoments, ... + modeledValues.inverseDynamicsMoments, ... costTerm.load ... ); @@ -241,7 +241,7 @@ costTermCalculations.joint_power_minimization = @(values, modeledValues, auxdata, costTerm) ... calcMinimizingJointPowerIntegrand( ... values.velocities, ... - modeledValues.inverseDynamicMoments, ... + modeledValues.inverseDynamicsMoments, ... auxdata, ... costTerm.load ... ); @@ -309,7 +309,7 @@ values.externalTorqueControls, ... auxdata, ... costTerm.coordinate); - + costTermCalculations.synergy_vector_tracking = @(values, modeledValues, auxdata, costTerm) ... calcTrackingSynergyVectorsDiscrete( ... values.synergyWeights, ... diff --git a/src/TreatmentOptimization/makeSurrogateModel.m b/src/TreatmentOptimization/makeSurrogateModel.m index 3afbaaf87..411c4f22f 100644 --- a/src/TreatmentOptimization/makeSurrogateModel.m +++ b/src/TreatmentOptimization/makeSurrogateModel.m @@ -28,22 +28,22 @@ % ----------------------------------------------------------------------- % function inputs = makeSurrogateModel(inputs) -if strcmp(inputs.controllerType, 'synergy') +if strcmp(inputs.controllerType, 'synergy') for i = 1 : length(inputs.coordinateNames) for j = 1 : length(inputs.surrogateModelCoordinateNames) if strcmp(inputs.coordinateNames(i), inputs.surrogateModelCoordinateNames(j)) inputs.surrogateModelIndex(j) = i; end - end + end end inputs.dofsActuatedIndex = []; - for i = 1 : length(inputs.inverseDynamicMomentLabels) + for i = 1 : length(inputs.inverseDynamicsMomentLabels) for j = 1 : length(inputs.surrogateModelCoordinateNames) - if strcmp(inputs.inverseDynamicMomentLabels(i), ... + if strcmp(inputs.inverseDynamicsMomentLabels(i), ... strcat(inputs.surrogateModelCoordinateNames(j), '_moment')) inputs.dofsActuatedIndex(end+1) = j; end - end + end end end end diff --git a/src/TreatmentOptimization/reportTreatmentOptimizationResults.m b/src/TreatmentOptimization/reportTreatmentOptimizationResults.m index 3ca279de9..ee8d53bf6 100644 --- a/src/TreatmentOptimization/reportTreatmentOptimizationResults.m +++ b/src/TreatmentOptimization/reportTreatmentOptimizationResults.m @@ -49,7 +49,7 @@ function reportTreatmentOptimizationResults(solution, inputs) end plotResultsWithOutComparison(values.controlSynergyActivations, values.time, ... synergyTitles, ["Synergy" "Activations"]); -else +else % plot torque controls plotResultsWithOutComparison(values.controlTorques, values.time, ... inputs.torqueControllerCoordinateNames, ["Torque" "Controls"]); @@ -68,9 +68,9 @@ function reportTreatmentOptimizationResults(solution, inputs) jointAngles, inputs.experimentalTime, ... strcat(inputs.statesCoordinateNames, ' [rad]'), ["Joint" "Angles [rad]"]); % plot joint moments -plotResultsWithComparison(solution.inverseDynamicMoments, values.time, ... +plotResultsWithComparison(solution.inverseDynamicsMoments, values.time, ... inputs.experimentalJointMoments, inputs.experimentalTime, ... - strcat(inputs.inverseDynamicMomentLabels, ' [Nm]'), ["Joint" "Moments"]); + strcat(inputs.inverseDynamicsMomentLabels, ' [Nm]'), ["Joint" "Moments"]); % plot ground reactions for i = 1:length(inputs.contactSurfaces) plotResultsWithComparison(solution.groundReactionsLab.forces{i}, values.time, ... diff --git a/src/TreatmentOptimization/saveTreatmentOptimizationResults.m b/src/TreatmentOptimization/saveTreatmentOptimizationResults.m index 94a3359eb..e9fab5f91 100644 --- a/src/TreatmentOptimization/saveTreatmentOptimizationResults.m +++ b/src/TreatmentOptimization/saveTreatmentOptimizationResults.m @@ -87,8 +87,8 @@ function saveInverseDynamicsResults(solution, inputs, values, outputDirectory) if ~exist(fullfile(outputDirectory, "IDData"), "dir") mkdir(fullfile(outputDirectory, "IDData")) end -writeToSto(inputs.inverseDynamicMomentLabels, values.time, ... - solution.inverseDynamicMoments, fullfile(outputDirectory, "IDData", ... +writeToSto(inputs.inverseDynamicsMomentLabels, values.time, ... + solution.inverseDynamicsMoments, fullfile(outputDirectory, "IDData", ... strcat(inputs.trialName, ".sto"))); end diff --git a/src/core/mex/inverseDynamics.m b/src/core/mex/inverseDynamics.m index 3b9adc8e4..cb77cf3fe 100644 --- a/src/core/mex/inverseDynamics.m +++ b/src/core/mex/inverseDynamics.m @@ -2,8 +2,8 @@ % % This function uses a mex file or a matlab function with parallel workers % to calculate inverse dynamics moments. -% -% (Array of number, 2D matrix, 2D matrix, 2D matrix, Cell, 2D matrix, +% +% (Array of number, 2D matrix, 2D matrix, 2D matrix, Cell, 2D matrix, % Array of string) -> (2D matrix) % Returns inverse dynamic moments @@ -29,16 +29,16 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function inverseDynamicMoments = inverseDynamics(time, jointAngles, ... +function inverseDynamicsMoments = inverseDynamics(time, jointAngles, ... jointVelocities, jointAccelerations, coordinateLabels, appliedLoads, ... modelName) if isequal(mexext, 'mexw64') - inverseDynamicMoments = inverseDynamicsMexWindows(time, jointAngles, ... + inverseDynamicsMoments = inverseDynamicsMexWindows(time, jointAngles, ... jointVelocities, jointAccelerations, coordinateLabels, ... appliedLoads); else - inverseDynamicMoments = inverseDynamicsMatlabParallel(time, ... + inverseDynamicsMoments = inverseDynamicsMatlabParallel(time, ... jointAngles, jointVelocities, jointAccelerations, coordinateLabels, ... appliedLoads, modelName); end -end \ No newline at end of file +end diff --git a/src/core/mex/inverseDynamicsMatlabParallel.m b/src/core/mex/inverseDynamicsMatlabParallel.m index 5a090dfef..0fe2dfea4 100644 --- a/src/core/mex/inverseDynamicsMatlabParallel.m +++ b/src/core/mex/inverseDynamicsMatlabParallel.m @@ -3,8 +3,8 @@ % This function uses OpenSim's inverse dynamics solver to calculate inverse % dynamics moments. Parallel workers are used to speed up computational % time. -% -% (Array of number, 2D matrix, 2D matrix, 2D matrix, Cell, 2D matrix, +% +% (Array of number, 2D matrix, 2D matrix, 2D matrix, Cell, 2D matrix, % Array of string) -> (2D matrix) % Returns inverse dynamic moments @@ -30,7 +30,7 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function inverseDynamicMoments = inverseDynamicsMatlabParallel(time, ... +function inverseDynamicsMoments = inverseDynamicsMatlabParallel(time, ... jointAngles, jointVelocities, jointAccelerations, coordinateLabels, ... appliedLoads, modelName) @@ -40,24 +40,24 @@ numCoords = length(coordinateLabels); % Split time points into parallel problems numWorkers = gcp().NumWorkers; -inverseDynamicJobs = cell(1, numWorkers); +inverseDynamicsJobs = cell(1, numWorkers); parfor worker = 1:numWorkers - inverseDynamicJobs{worker} = idWorkerHelper(modelName, numPts, ... + inverseDynamicsJobs{worker} = idWorkerHelper(modelName, numPts, ... numControls, numCoords, numWorkers, ... time, jointAngles, jointVelocities, jointAccelerations, ... coordinateLabels,appliedLoads,worker); end -inverseDynamicMoments = inverseDynamicJobs{1}; +inverseDynamicsMoments = inverseDynamicsJobs{1}; for job = 2 : numWorkers - inverseDynamicMoments = cat(1, inverseDynamicMoments, ... - inverseDynamicJobs{job}); + inverseDynamicsMoments = cat(1, inverseDynamicsMoments, ... + inverseDynamicsJobs{job}); end end -function inverseDynamicJobs = idWorkerHelper(modelName, numPts, ... +function inverseDynamicsJobs = idWorkerHelper(modelName, numPts, ... numControls, numCoords, numWorkers, time, jointAngles, ... jointVelocities, jointAccelerations, coordinateLabels, appliedLoads, ... worker) - + import org.opensim.modeling.*; persistent osimModel; persistent osimState; @@ -67,7 +67,7 @@ osimState = osimModel.initSystem(); inverseDynamicsSolver = InverseDynamicsSolver(osimModel); end -inverseDynamicJobs = []; +inverseDynamicsJobs = []; start = round((numPts/numWorkers) * (worker-1)) + 1; stop = round((numPts/numWorkers) * (worker)); for j = start : stop @@ -98,7 +98,7 @@ osimModel.setControls(osimState, newControls); osimModel.markControlsAsValid(osimState); osimModel.realizeDynamics(osimState); - + accelsVec = Vector(osimState.getNQ, 0); includedQIndex = 1; for i=0:osimState.getNQ-1 @@ -107,10 +107,10 @@ accelsVec.set(i, accelsTempVec(includedQIndex)); includedQIndex = includedQIndex + 1; % end - end + end IDLoadsVec = inverseDynamicsSolver.solve(osimState, accelsVec); for i=0 : numCoords - 1 - inverseDynamicJobs(j-start + 1, i + 1) = IDLoadsVec.get(i); + inverseDynamicsJobs(j-start + 1, i + 1) = IDLoadsVec.get(i); end end -end \ No newline at end of file +end diff --git a/src/core/parse/parseTreatmentOptimizationDataDirectory.m b/src/core/parse/parseTreatmentOptimizationDataDirectory.m index 68de9b21a..fb01fd085 100644 --- a/src/core/parse/parseTreatmentOptimizationDataDirectory.m +++ b/src/core/parse/parseTreatmentOptimizationDataDirectory.m @@ -55,7 +55,7 @@ function inputs = parseExperimentalData(tree, inputs, dataDirectory) [inputs.experimentalJointMoments, ... - inputs.inverseDynamicMomentLabels] = ... + inputs.inverseDynamicsMomentLabels] = ... parseTrialDataTryDirectories( ... fullfile(inputs.previousResultsDirectory, "IDData"), ... fullfile(dataDirectory, "IDData"), inputs.trialName, inputs.model); From d30b0585cc30bc57e82e336a9a0a75d572e547a6 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 24 Sep 2023 15:01:52 -0500 Subject: [PATCH 127/365] fix ma directory parse --- src/core/parse/parseSelectMomentArms.m | 15 ++++++--------- .../parseTreatmentOptimizationDataDirectory.m | 5 ++--- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/core/parse/parseSelectMomentArms.m b/src/core/parse/parseSelectMomentArms.m index 256516ef7..b7b1493d7 100644 --- a/src/core/parse/parseSelectMomentArms.m +++ b/src/core/parse/parseSelectMomentArms.m @@ -11,7 +11,7 @@ % National Institutes of Health (R01 EB030520). % % % % Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % +% Author(s): Marleny Vega, Claire V. Hammond % % % % Licensed under the Apache License, Version 2.0 (the "License"); % % you may not use this file except in compliance with the License. % @@ -25,22 +25,19 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cells = parseSelectMomentArms(directories, coordinateNames, muscleNames) +function cells = parseSelectMomentArms(directory, coordinateNames, muscleNames) import org.opensim.modeling.Storage -firstTrial = parseSpecificMuscleAnalysisCoordinates(directories(1), ... +firstTrial = parseSpecificMuscleAnalysisCoordinates(directory, ... coordinateNames, muscleNames); -cells = zeros([length(directories) size(firstTrial)]); +cells = zeros([1 size(firstTrial)]); cells(1, :, :, :) = firstTrial; -for i=2:length(directories) - cells(i, :, :, :) = parseSpecificMuscleAnalysisCoordinates(directories(i), ... - coordinateNames, muscleNames); -end end function cells = parseSpecificMuscleAnalysisCoordinates(inputDirectory, ... coordinateNames, muscleNames) import org.opensim.modeling.Storage -coordFileNames = findSpecificMuscleAnalysisCoordinateFiles(inputDirectory, coordinateNames); +coordFileNames = findSpecificMuscleAnalysisCoordinateFiles( ... + inputDirectory, coordinateNames); firstFile = storageToDoubleMatrix(Storage(coordFileNames(1))); columnNames = getStorageColumnNames(Storage(coordFileNames(1))); cells = zeros([length(coordFileNames) size(firstFile)]); diff --git a/src/core/parse/parseTreatmentOptimizationDataDirectory.m b/src/core/parse/parseTreatmentOptimizationDataDirectory.m index fb01fd085..2a6f974d7 100644 --- a/src/core/parse/parseTreatmentOptimizationDataDirectory.m +++ b/src/core/parse/parseTreatmentOptimizationDataDirectory.m @@ -78,9 +78,8 @@ strcat(inputs.trialName, "_combinedActivations"), inputs.model); inputs.synergyWeights = parseTrialData(inputs.previousResultsDirectory, ... "synergyWeights", inputs.model); - directories = findFirstLevelSubDirectoriesFromPrefixes(fullfile( ... - dataDirectory, "MAData"), inputs.trialName); - inputs.momentArms = parseSelectMomentArms(directories, ... + directory = fullfile(dataDirectory, "MAData", inputs.trialName); + inputs.momentArms = parseSelectMomentArms(directory, ... inputs.surrogateModelCoordinateNames, inputs.muscleNames); inputs.momentArms = reshape(permute(inputs.momentArms, [1 4 2 3]), [], ... length(inputs.surrogateModelCoordinateNames), length(inputs.muscleNames)); From 97773cba78ce840d850d03d08221110eb1e3eca0 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 24 Sep 2023 15:02:14 -0500 Subject: [PATCH 128/365] remove unused fns --- .../checkControlGuess.m | 63 ------------ .../checkParameterGuess.m | 96 ------------------- .../checkStateGuess.m | 43 --------- 3 files changed, 202 deletions(-) delete mode 100644 src/TreatmentOptimization/OptimalControlSetupFunctions/checkControlGuess.m delete mode 100644 src/TreatmentOptimization/OptimalControlSetupFunctions/checkParameterGuess.m delete mode 100644 src/TreatmentOptimization/OptimalControlSetupFunctions/checkStateGuess.m diff --git a/src/TreatmentOptimization/OptimalControlSetupFunctions/checkControlGuess.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/checkControlGuess.m deleted file mode 100644 index 4d601edc4..000000000 --- a/src/TreatmentOptimization/OptimalControlSetupFunctions/checkControlGuess.m +++ /dev/null @@ -1,63 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function checks that the initial guess control file is in the -% correct order -% -% (struct) -> (struct) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function inputs = checkControlGuess(inputs) -if isfield(inputs.initialGuess, 'control') - newControls = zeros(size(inputs.initialGuess.control, 1), 0); - newLabels = string([]); - for i = 1 : length(inputs.coordinateNames) - index = find(ismember(inputs.initialGuess.controlLabels, inputs.coordinateNames(i))); - if isempty(index) - newControls(:, end + 1) = zeros(size(inputs.initialGuess.control, 1), 1); - else - newControls(:, end + 1) = inputs.initialGuess.control(:, index); - end - newLabels(end + 1) = inputs.coordinateNames(i); - end - if strcmp(inputs.controllerType, "synergy") - for i = 1 : length(inputs.osimx.synergyGroups) - for j = 1 : inputs.osimx.synergyGroups{i}.numSynergies - index = find(ismember(inputs.initialGuess.controlLabels, ... - strcat(inputs.osimx.synergyGroups{i}.muscleGroupName, "_", num2str(j)))); - if isempty(index) - throw(MException("ParseError:SynergyCommands", ... - strcat("All synergy commands are required: ", ... - strcat(inputs.osimx.synergyGroups{i}.muscleGroupName, "_", num2str(j))))); - else - newControls(:, end + 1) = inputs.initialGuess.control(:, index); - end - newLabels(end + 1) = strcat(inputs.osimx.synergyGroups{i}.muscleGroupName, "_", num2str(j)); - end - end - end - inputs.initialGuess.control = newControls; - inputs.initialGuess.controlLabels = newLabels; -end -end \ No newline at end of file diff --git a/src/TreatmentOptimization/OptimalControlSetupFunctions/checkParameterGuess.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/checkParameterGuess.m deleted file mode 100644 index 65160a0fe..000000000 --- a/src/TreatmentOptimization/OptimalControlSetupFunctions/checkParameterGuess.m +++ /dev/null @@ -1,96 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function checks that the initial guess parameters file is in the -% correct order -% -% (struct) -> (struct) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function inputs = checkParameterGuess(inputs) -if isfield(inputs.initialGuess, 'parameter') || isfield(inputs,"synergyWeights") - if isfield(inputs.initialGuess, 'parameter') - inputs.synergyWeightsGuess = inputs.initialGuess.parameter; - elseif isfield(inputs,"synergyWeights") - inputs.synergyWeightsGuess = inputs.synergyWeights; - end - - parameterIndex = zeros(length(inputs.synergyGroups), inputs.numMuscles); - for i = 1 : length(inputs.synergyGroups) - for j = 1 : inputs.numMuscles - for k = 1 : length(inputs.synergyGroups{i}.muscleNames) - if strcmpi(inputs.muscleNames(j), inputs.synergyGroups{i}.muscleNames(k)) - if i <= 1 - parameterIndex(i, k) = j; - else - parameterIndex(i, k + length(inputs.synergyGroups{i}.muscleNames)) = j; - end - end - end - end - end - synergyWeightsFlattened = []; - numSynergiesIndex = 0; - for j = 1 : length(inputs.synergyGroups) - synergyWeightsFlattened = cat(2, synergyWeightsFlattened, ... - reshape(inputs.synergyWeightsGuess(1 + numSynergiesIndex: ... - inputs.synergyGroups{j}.numSynergies + numSynergiesIndex, ... - nonzeros(parameterIndex(j, :)))', 1, [])); - numSynergiesIndex = numSynergiesIndex + inputs.synergyGroups{j}.numSynergies; - end - inputs.synergyWeightsGuess = synergyWeightsFlattened; -end -if strcmp(inputs.controllerType, 'synergy') - inputs = getMuscleSynergiesInitialGuess(inputs); - for i = 1 : length(inputs.coordinateNames) - for j = 1 : length(inputs.surrogateModelCoordinateNames) - if strcmp(inputs.coordinateNames(i), inputs.surrogateModelCoordinateNames(j)) - inputs.surrogateModelIndex(j) = i; - end - end - end - inputs.dofsActuatedIndex = []; - for i = 1 : length(inputs.inverseDynamicsMomentLabels) - for j = 1 : length(inputs.surrogateModelCoordinateNames) - if strcmp(inputs.inverseDynamicsMomentLabels(i), ... - strcat(inputs.surrogateModelCoordinateNames(j), '_moment')) - inputs.dofsActuatedIndex(end+1) = j; - end - end - end -end -end - -function inputs = getMuscleSynergiesInitialGuess(inputs) -if isfield(inputs.initialGuess,"parameter") || isfield(inputs,"synergyWeights") - synergyWeights = getSynergyWeightsFromGroups(inputs.synergyWeightsGuess, inputs); - inputs.synergyActivationsGuess = inputs.experimentalMuscleActivations / synergyWeights; -else - inputs.mtpActivationsColumnNames = inputs.muscleLabels; - inputs.mtpActivations = permute(inputs.experimentalMuscleActivations, [3 2 1]); - inputs.synergyWeightsGuess = prepareNonNegativeMatrixFactorizationInitialValues(inputs, inputs)'; - synergyWeights = getSynergyWeightsFromGroups(inputs.synergyWeightsGuess, inputs); - inputs.synergyActivationsGuess = inputs.experimentalMuscleActivations / synergyWeights; -end -end diff --git a/src/TreatmentOptimization/OptimalControlSetupFunctions/checkStateGuess.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/checkStateGuess.m deleted file mode 100644 index 987783638..000000000 --- a/src/TreatmentOptimization/OptimalControlSetupFunctions/checkStateGuess.m +++ /dev/null @@ -1,43 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function checks that the initial guess states file is in the correct -% order -% -% (struct) -> (struct) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function inputs = checkStateGuess(inputs) -if isfield(inputs.initialGuess, 'state') - for i = 1 : inputs.numCoordinates - for j = 1 : length(inputs.initialGuess.stateLabels) - if strcmpi(inputs.coordinateNames(i), inputs.initialGuess.stateLabels(j)) - stateIndex(i) = j; - end - end - end - inputs.initialGuess.state = inputs.initialGuess.state(:, [stateIndex ... - stateIndex + inputs.numCoordinates stateIndex + inputs.numCoordinates * 2]); -end -end \ No newline at end of file From 057d784430db681a69e43a361fc148f5581da968 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 24 Sep 2023 15:18:05 -0500 Subject: [PATCH 129/365] fix warning on osimx writing --- src/core/osimx/writeNeuralControlPersonalizationOsimxFile.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/osimx/writeNeuralControlPersonalizationOsimxFile.m b/src/core/osimx/writeNeuralControlPersonalizationOsimxFile.m index 4950091bd..d956ab32e 100644 --- a/src/core/osimx/writeNeuralControlPersonalizationOsimxFile.m +++ b/src/core/osimx/writeNeuralControlPersonalizationOsimxFile.m @@ -30,7 +30,7 @@ function writeNeuralControlPersonalizationOsimxFile(inputs, ... resultsDirectory, precalInputs) -modelFileName = inputs.model; +modelFileName = inputs.modelFileName; model = Model(modelFileName); buildFromExisting = false; From 814e5066b213e406a66feec69f04c3db6ff0cb68 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 24 Sep 2023 20:36:54 -0500 Subject: [PATCH 130/365] add parameter solution to xml --- .../parseDesignOptimizationSettingsTree.m | 19 ++++++++++ .../saveDesignOptimizationResults.m | 26 +++++++++++++- .../setupGpopsInitialGuess.m | 15 ++++++-- ...s.m => setupTreatmentOptimizationBounds.m} | 18 +++++++++- .../gpops/convertToGpopsSynergyDrivenInputs.m | 30 +--------------- .../gpops/convertToGpopsTorqueDrivenInputs.m | 35 ++----------------- .../gpops/makeGpopsValuesAsStruct.m | 13 +++++-- .../makeStateDerivatives.m | 9 +++++ .../reportTreatmentOptimizationResults.m | 8 ----- 9 files changed, 97 insertions(+), 76 deletions(-) rename src/TreatmentOptimization/OptimalControlSetupFunctions/{setupCommonOptimalControlBounds.m => setupTreatmentOptimizationBounds.m} (81%) diff --git a/src/TreatmentOptimization/DesignOptimization/parseDesignOptimizationSettingsTree.m b/src/TreatmentOptimization/DesignOptimization/parseDesignOptimizationSettingsTree.m index 15198fac0..1bb7960f0 100644 --- a/src/TreatmentOptimization/DesignOptimization/parseDesignOptimizationSettingsTree.m +++ b/src/TreatmentOptimization/DesignOptimization/parseDesignOptimizationSettingsTree.m @@ -44,11 +44,30 @@ if isstruct(parameterTree) && isfield(parameterTree, "RCNLParameterTerm") inputs.userDefinedVariables = parseRcnlCostTermSet( ... parameterTree.RCNLParameterTerm); + for i = 1:length(inputs.userDefinedVariables) + + inputs.userDefinedVariables{i}.initial_values = ... + stringToSpaceSeparatedList(inputs.userDefinedVariables{i}.initial_values); + inputs.userDefinedVariables{i}.upper_bounds = ... + stringToSpaceSeparatedList(inputs.userDefinedVariables{i}.upper_bounds); + inputs.userDefinedVariables{i}.lower_bounds = ... + stringToSpaceSeparatedList(inputs.userDefinedVariables{i}.lower_bounds); + end else inputs.userDefinedVariables = {}; end end +function output = stringToSpaceSeparatedList(string) +if isnumeric(string) + output = string; + return; +end +string = strtrim(string); +output = strsplit(string, ' '); +output = str2double(output); +end + function inputs = parseDesignSettings(tree, inputs) finalTimeRange = getFieldByName(tree, ... 'final_time_range'); diff --git a/src/TreatmentOptimization/DesignOptimization/saveDesignOptimizationResults.m b/src/TreatmentOptimization/DesignOptimization/saveDesignOptimizationResults.m index 737d3d974..f09ff6038 100644 --- a/src/TreatmentOptimization/DesignOptimization/saveDesignOptimizationResults.m +++ b/src/TreatmentOptimization/DesignOptimization/saveDesignOptimizationResults.m @@ -1,6 +1,6 @@ % This function is part of the NMSM Pipeline, see file for full license. % -% This function saves and prints the unscaled results from +% This function saves and prints the unscaled results from % Design Optimization. An osim model may also be printed if model specific % values were optimized % @@ -38,4 +38,28 @@ function saveDesignOptimizationResults(solution, inputs) model = Model(inputs.auxdata.model); model.print(strrep(inputs.mexModel, '_inactiveMuscles.osim', 'DesignOpt.osim')); end +printUserDefinedVariablesToXml(solution, inputs); +end + +function printUserDefinedVariablesToXml(solution, inputs) +if isfield(inputs, 'userDefinedVariables') + counter = 1; + for i = 1:length(inputs.userDefinedVariables) + numParameters = length(inputs.userDefinedVariables{i}.initial_values); + parameterResults = scaleToOriginal( ... + solution.solution.phase.parameter( ... + counter : counter + numParameters - 1), ... + inputs.userDefinedVariables{i}.upper_bounds, ... + inputs.userDefinedVariables{i}.lower_bounds); + counter = counter + numParameters; + valuesStr = num2str(parameterResults(1)); + for j = 2:length(parameterResults) + strcat(valuesStr, " ", num2str(parameterResults(j))); + end + parameters.NMSMPipelineDocument.RCNLParameters{i}.RCNLParameterSet.type = inputs.userDefinedVariables{i}.type; + parameters.NMSMPipelineDocument.RCNLParameters{i}.RCNLParameterSet.values = valuesStr; + struct2xml(parameters, fullfile(inputs.resultsDirectory, ... + strcat(inputs.trialName, "_parameterSolution.xml"))); + end +end end \ No newline at end of file diff --git a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m index 025e8554c..e9cb115b2 100644 --- a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m +++ b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m @@ -74,9 +74,9 @@ controls = inputs.initialJerks; else stateJointJerks = subsetDataByCoordinates( ... - inputs.experimentalJointJerks, ... - inputs.coordinateNames, ... - inputs.statesCoordinateNames); + inputs.experimentalJointJerks, ... + inputs.coordinateNames, ... + inputs.statesCoordinateNames); controls = stateJointJerks; end @@ -105,6 +105,15 @@ guess.parameter = scaleToBounds(inputs.synergyWeights, ... inputs.maxParameter, inputs.minParameter); end +if strcmp(inputs.toolName, "DesignOptimization") + guess.parameter = []; + for i = 1:length(inputs.userDefinedVariables) + guess.parameter = [guess.parameter, scaleToBounds( ... + inputs.userDefinedVariables{i}.initial_values, ... + inputs.userDefinedVariables{i}.upper_bounds, ... + inputs.userDefinedVariables{i}.lower_bounds)]; + end +end end function output = subsetInitialStatesDataByCoordinates(data, ... diff --git a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlBounds.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupTreatmentOptimizationBounds.m similarity index 81% rename from src/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlBounds.m rename to src/TreatmentOptimization/OptimalControlSetupFunctions/setupTreatmentOptimizationBounds.m index 58c7c35e3..254a3f1c7 100644 --- a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlBounds.m +++ b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupTreatmentOptimizationBounds.m @@ -29,7 +29,7 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function bounds = setupCommonOptimalControlBounds(inputs, params) +function bounds = setupTreatmentOptimizationBounds(inputs, params) % setup time bounds bounds.phase.initialtime.lower = -0.5; bounds.phase.initialtime.upper = -0.5; @@ -59,5 +59,21 @@ if ~isempty(inputs.maxTerminal) bounds.eventgroup.upper = inputs.maxTerminal; end +if strcmp(inputs.toolName, "DesignOptimization") + for i = 1:length(inputs.userDefinedVariables) + lower = -0.5 * ones(1, length(inputs.userDefinedVariables{i}.initial_values)); + upper = 0.5 * ones(1, length(inputs.userDefinedVariables{i}.initial_values)); + if ~isfield(bounds, "parameter") || ... + ~isfield(bounds.parameter, "lower") + bounds.parameter.lower = lower; + bounds.parameter.upper = upper; + else + bounds.parameter.lower = [bounds.parameter.lower, ... + lower]; + bounds.parameter.upper = [bounds.parameter.upper, ... + upper]; + end + end +end end diff --git a/src/TreatmentOptimization/gpops/convertToGpopsSynergyDrivenInputs.m b/src/TreatmentOptimization/gpops/convertToGpopsSynergyDrivenInputs.m index 16cc077e0..55e846152 100644 --- a/src/TreatmentOptimization/gpops/convertToGpopsSynergyDrivenInputs.m +++ b/src/TreatmentOptimization/gpops/convertToGpopsSynergyDrivenInputs.m @@ -40,7 +40,7 @@ end function bounds = setupProblemBounds(inputs, params) -bounds = setupCommonOptimalControlBounds(inputs, params); +bounds = setupTreatmentOptimizationBounds(inputs, params); % setup parameter bounds if strcmp(inputs.controllerType, 'synergy') if inputs.optimizeSynergyVectors @@ -48,33 +48,5 @@ bounds.parameter.upper = 0.5 * ones(1, length(inputs.minParameter)); end end -if strcmp(inputs.toolName, "DesignOptimization") - for i = 1:length(inputs.userDefinedVariables) - variable = inputs.userDefinedVariables{i}; - if ~isfield(bounds, "parameter") || ... - ~isfield(bounds.parameter, "lower") - bounds.parameter.lower = [-0.5]; - bounds.parameter.upper = [0.5]; - else - bounds.parameter.lower = [bounds.parameter.lower, ... - -0.5]; - bounds.parameter.upper = [bounds.parameter.upper, ... - 0.5]; - end - end -end end -function guess = addUserDefinedTermsToGuess(guess, inputs) -for i = 1:length(inputs.userDefinedVariables) - variable = inputs.userDefinedVariables{i}; - if ~isfield(guess, "parameter") - guess.parameter = []; - end - guess.parameter = [guess.parameter, ... - scaleToBounds( ... - variable.initial_values, ... - variable.upper_bounds, ... - variable.lower_bounds)]; -end -end diff --git a/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m b/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m index df8ee3abe..a49704b42 100644 --- a/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m +++ b/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m @@ -27,9 +27,6 @@ function setup = convertToGpopsTorqueDrivenInputs(inputs, params) bounds = setupProblemBounds(inputs, params); guess = setupGpopsInitialGuess(inputs); -if strcmp(inputs.toolName, "DesignOptimization") - guess = addUserDefinedTermsToGuess(guess, inputs); -end initializeMexOrMatlabParallelFunctions(inputs.mexModel); setup = setupGpopsSettings(inputs, ... bounds, guess, params, ... @@ -40,7 +37,7 @@ end function bounds = setupProblemBounds(inputs, params) -bounds = setupCommonOptimalControlBounds(inputs, params); +bounds = setupTreatmentOptimizationBounds(inputs, params); % setup parameter bounds if strcmp(inputs.controllerType, 'synergy') if inputs.optimizeSynergyVectors @@ -48,33 +45,7 @@ bounds.parameter.upper = 0.5 * ones(1, length(inputs.minParameter)); end end -if strcmp(inputs.toolName, "DesignOptimization") - for i = 1:length(inputs.userDefinedVariables) - variable = inputs.userDefinedVariables{i}; - if ~isfield(bounds, "parameter") || ... - ~isfield(bounds.parameter, "lower") - bounds.parameter.lower = [-0.5]; - bounds.parameter.upper = [0.5]; - else - bounds.parameter.lower = [bounds.parameter.lower, ... - -0.5]; - bounds.parameter.upper = [bounds.parameter.upper, ... - 0.5]; - end - end -end end -function guess = addUserDefinedTermsToGuess(guess, inputs) -for i = 1:length(inputs.userDefinedVariables) - variable = inputs.userDefinedVariables{i}; - if ~isfield(guess, "parameter") - guess.parameter = []; - end - guess.parameter = [guess.parameter, ... - scaleToBounds( ... - variable.initial_values, ... - variable.upper_bounds, ... - variable.lower_bounds)]; -end -end + + diff --git a/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m b/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m index 10b365b82..d9a8d9b53 100644 --- a/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m +++ b/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m @@ -105,11 +105,15 @@ % end end if isfield(inputs, 'userDefinedVariables') + counter = 1; for i = 1:length(inputs.userDefinedVariables) - values.(inputs.userDefinedVariables{i}.type)(i) = scaleToOriginal( ... - phase.parameter(1, i + numParameters), ... + numParameters = length(inputs.userDefinedVariables{i}.initial_values); + values.parameters.(inputs.userDefinedVariables{i}.type) ... + = scaleToOriginal( ... + phase.parameter(counter : counter + numParameters - 1), ... inputs.userDefinedVariables{i}.upper_bounds, ... inputs.userDefinedVariables{i}.lower_bounds); + counter = counter + numParameters; end end end @@ -120,6 +124,11 @@ positions = fnval(inputs.splineJointAngles, values.time)'; velocities = fnval(inputs.splineJointVelocities, values.time)'; accelerations = fnval(inputs.splineJointAccelerations, values.time)'; +if length(inputs.statesCoordinateNames) == 1 + positions = positions'; + velocities = velocities'; + accelerations = accelerations'; +end for i = 1:length(inputs.coordinateNames) index = find(ismember( ... inputs.statesCoordinateNames, inputs.coordinateNames{i})); diff --git a/src/TreatmentOptimization/makeStateDerivatives.m b/src/TreatmentOptimization/makeStateDerivatives.m index 9db64b7e9..bf1bbaa5d 100644 --- a/src/TreatmentOptimization/makeStateDerivatives.m +++ b/src/TreatmentOptimization/makeStateDerivatives.m @@ -33,14 +33,23 @@ inputs.experimentalJointAngles', eps, [], 3); inputs.experimentalJointVelocities = fnval(fnder(jointAnglesSpline, 1), ... inputs.experimentalTime)'; +if length(inputs.statesCoordinateNames) == 1 + inputs.experimentalJointVelocities = inputs.experimentalJointVelocities'; +end jointVelocitiesSpline = spaps(inputs.experimentalTime, ... inputs.experimentalJointVelocities', eps, [], 3); inputs.experimentalJointAccelerations = fnval( ... fnder(jointVelocitiesSpline, 1), inputs.experimentalTime)'; +if length(inputs.statesCoordinateNames) == 1 + inputs.experimentalJointAccelerations = inputs.experimentalJointAccelerations'; +end jointAccelerationsSpline = spaps(inputs.experimentalTime, ... inputs.experimentalJointAccelerations', eps, [], 3); inputs.experimentalJointJerks = fnval(fnder(jointAccelerationsSpline, ... 1), inputs.experimentalTime)'; +if length(inputs.statesCoordinateNames) == 1 + inputs.experimentalJointJerks = inputs.experimentalJointJerks'; +end % points = length(inputs.experimentalTime); % interval = inputs.experimentalTime(2) - inputs.experimentalTime(1); diff --git a/src/TreatmentOptimization/reportTreatmentOptimizationResults.m b/src/TreatmentOptimization/reportTreatmentOptimizationResults.m index ee8d53bf6..e51c34bf6 100644 --- a/src/TreatmentOptimization/reportTreatmentOptimizationResults.m +++ b/src/TreatmentOptimization/reportTreatmentOptimizationResults.m @@ -28,14 +28,6 @@ % ----------------------------------------------------------------------- % function reportTreatmentOptimizationResults(solution, inputs) -if isfield(inputs, 'userDefinedVariables') - for i = 1:length(inputs.userDefinedVariables) - parameterResults = scaleToOriginal( ... - solution.solution.phase.parameter(i, 1), ... - inputs.userDefinedVariables{i}.upper_bounds, ... - inputs.userDefinedVariables{i}.lower_bounds) - end -end values = makeGpopsValuesAsStruct(solution.solution.phase, inputs); if strcmp(inputs.controllerType, 'synergy') % plot Muscle Activations From 5bcfd7a5e668c7959a034f1a6bdc23575095e0b3 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 24 Sep 2023 20:41:13 -0500 Subject: [PATCH 131/365] fix output log name --- src/core/optimal_control/parseGpopsSolverSettings.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/optimal_control/parseGpopsSolverSettings.m b/src/core/optimal_control/parseGpopsSolverSettings.m index 03139bc99..5777a4af5 100644 --- a/src/core/optimal_control/parseGpopsSolverSettings.m +++ b/src/core/optimal_control/parseGpopsSolverSettings.m @@ -29,7 +29,7 @@ % ----------------------------------------------------------------------- % function solverSettings = parseGpopsSolverSettings(settingsTree) -solverSettings.optimizationFileName = 'trackingOptimizationOutputFile.txt'; +solverSettings.optimizationFileName = 'TreatmentOptimization'; solverSettings.derivativeSupplier = parseTextOrAlternate( ... settingsTree, 'setup_derivatives_supplier', 'sparseFD'); solverSettings.derivativeLevel = parseTextOrAlternate( ... From fe8c2162ffe2006e6a6fc2e999b2d31c8ffd8df5 Mon Sep 17 00:00:00 2001 From: Spencer Williams <54556034+SpencerTWilliams@users.noreply.github.com> Date: Mon, 25 Sep 2023 21:20:56 -0500 Subject: [PATCH 132/365] Add maximum value for height --- .../ModelCalculation/calcModeledVerticalGroundReactionForce.m | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/GroundContactPersonalization/Optimizations/ModelCalculation/calcModeledVerticalGroundReactionForce.m b/src/GroundContactPersonalization/Optimizations/ModelCalculation/calcModeledVerticalGroundReactionForce.m index 53993f83a..ed7068b37 100644 --- a/src/GroundContactPersonalization/Optimizations/ModelCalculation/calcModeledVerticalGroundReactionForce.m +++ b/src/GroundContactPersonalization/Optimizations/ModelCalculation/calcModeledVerticalGroundReactionForce.m @@ -49,6 +49,9 @@ ymax = 1e-2; Kval = springConstants(i); height = height - springRestingLength; + if height > 0.354237930036971 + height = 0.354237930036971; + end numFrames = length(height); v = ones(numFrames, 1)' .* ((Kval + klow) ./ (Kval - klow)); s = ones(numFrames, 1)' .* ((Kval - klow) ./ 2); From a07228901f3c98140a86b18f5efaa0dcf0fc3ab9 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Fri, 29 Sep 2023 20:07:49 -0500 Subject: [PATCH 133/365] fix argument bug --- src/core/parse/parseRcnlConstraintTermSet.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/parse/parseRcnlConstraintTermSet.m b/src/core/parse/parseRcnlConstraintTermSet.m index 55870b915..cd606fbd1 100644 --- a/src/core/parse/parseRcnlConstraintTermSet.m +++ b/src/core/parse/parseRcnlConstraintTermSet.m @@ -41,7 +41,7 @@ % Find general cost term elements tempTerm.type = getTextFromField(getFieldByNameOrError( ... currentTerm, 'type')); - [isValid, isPath] = isTypeValid(tempTerm.type, controllerType, toolName); + [isValid, isPath] = isTypeValid(tempTerm.type, toolName, controllerType); if ~isValid throw(MException("ConstraintTermSet:InvalidType", ... strcat(tempTerm.type, " is not a valid constraint", ... From fe8524a395dfa8d70d8929bc6a7dadbe8d497d10 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Fri, 29 Sep 2023 20:08:01 -0500 Subject: [PATCH 134/365] add default numSynergies value --- src/core/parse/parseTorqueController.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/parse/parseTorqueController.m b/src/core/parse/parseTorqueController.m index 5646f9413..91e7db087 100644 --- a/src/core/parse/parseTorqueController.m +++ b/src/core/parse/parseTorqueController.m @@ -32,6 +32,7 @@ function inputs = parseTorqueController(tree, inputs) inputs.torqueControllerCoordinateNames = parseSpaceSeparatedList(tree, ... "coordinate_list"); -inputs.maxControlTorquesMultiple = parseDoubleOrAlternate(tree, ... +inputs.maxTorqueControlsMultiple = parseDoubleOrAlternate(tree, ... 'torque_controls_range_scale_factor', 1); +inputs.numSynergies = 0; end From 136aa58c45b5749840f7868834f9f56602960dfc Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Fri, 29 Sep 2023 20:08:17 -0500 Subject: [PATCH 135/365] make constraint and cost term parsing robust --- .../parse/parseTreatmentOptimizationInputs.m | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/src/core/parse/parseTreatmentOptimizationInputs.m b/src/core/parse/parseTreatmentOptimizationInputs.m index 991b13142..c75177447 100644 --- a/src/core/parse/parseTreatmentOptimizationInputs.m +++ b/src/core/parse/parseTreatmentOptimizationInputs.m @@ -35,11 +35,11 @@ inputs = parseController(tree, inputs); inputs = parseTreatmentOptimizationDataDirectory(tree, inputs); inputs = parseOptimalControlSolverSettings(tree, inputs); -inputs.costTerms = parseRcnlCostTermSet( ... - getFieldByNameOrError(tree, 'RCNLCostTermSet').RCNLCostTerm); -[inputs.path, inputs.terminal] = parseRcnlConstraintTermSet( ... - getFieldByNameOrError(tree, 'RCNLConstraintTermSet') ... - .RCNLConstraintTerm, inputs.controllerType, inputs.toolName); +inputs.costTerms = parseRcnlCostTermSetHelper( ... + getFieldByNameOrError(tree, 'RCNLCostTermSet')); +[inputs.path, inputs.terminal] = parseRcnlConstraintTermSetHelper( ... + getFieldByNameOrError(tree, 'RCNLConstraintTermSet'), ... + inputs.controllerType, inputs.toolName); inputs.contactSurfaces = parseGroundContactSurfaces(inputs); end @@ -68,3 +68,22 @@ end end end + +function costTerms = parseRcnlCostTermSetHelper(tree) +if isfield(tree, "RCNLCostTerm") + costTerms = parseRcnlCostTermSet(tree.RCNLCostTerm); +else + costTerms = parseRcnlCostTermSet({}); +end +end + +function [path, terminal] = parseRcnlConstraintTermSetHelper(tree, ... + controllerType, toolName) +if isfield(tree, "RCNLConstraintTerm") + [path, terminal] = parseRcnlConstraintTermSet( ... + tree.RCNLConstraintTerm, toolName, controllerType); +else + [path, terminal] = parseRcnlConstraintTermSet({}, controllerType, ... + toolName); +end +end \ No newline at end of file From 63e1c727d0b368374584f64eafeb82e449162946 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Fri, 29 Sep 2023 20:08:41 -0500 Subject: [PATCH 136/365] add shared torque controls and muscle synergies --- .../DesignOptimizationTool.m | 9 +---- .../setupMuscleSynergies.m | 37 +++++++++++++++++++ .../setupTorqueControls.m | 37 +++++++++++++++++++ .../VerificationOptimizationTool.m | 1 + 4 files changed, 76 insertions(+), 8 deletions(-) create mode 100644 src/TreatmentOptimization/OptimalControlSetupFunctions/setupMuscleSynergies.m create mode 100644 src/TreatmentOptimization/OptimalControlSetupFunctions/setupTorqueControls.m diff --git a/src/TreatmentOptimization/DesignOptimization/DesignOptimizationTool.m b/src/TreatmentOptimization/DesignOptimization/DesignOptimizationTool.m index 78f06d576..2f6196a35 100644 --- a/src/TreatmentOptimization/DesignOptimization/DesignOptimizationTool.m +++ b/src/TreatmentOptimization/DesignOptimization/DesignOptimizationTool.m @@ -34,16 +34,9 @@ function DesignOptimizationTool(settingsFileName) verifyVersion(settingsTree, "DesignOptimizationTool"); [inputs, params] = parseDesignOptimizationSettingsTree(settingsTree); inputs = setupMuscleSynergies(inputs); +inputs = setupTorqueControls(inputs); inputs = makeTreatmentOptimizationInputs(inputs, params); outputs = solveOptimalControlProblem(inputs, params); reportTreatmentOptimizationResults(outputs, inputs); saveDesignOptimizationResults(outputs, inputs); end - -function inputs = setupMuscleSynergies(inputs) -if strcmp(inputs.controllerType, 'synergy') - inputs.splineSynergyActivations = spaps(inputs.initialTime, ... - inputs.initialSynergyControls', 0.0000001); - inputs.synergyLabels = inputs.initialSynergyControlsLabels; -end -end \ No newline at end of file diff --git a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupMuscleSynergies.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupMuscleSynergies.m new file mode 100644 index 000000000..62249ac91 --- /dev/null +++ b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupMuscleSynergies.m @@ -0,0 +1,37 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function stores the initial synergy activations as a spline for use +% in cost terms for Treatment Optimization +% +% (string) -> (None) +% Spline input synergy activations + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Claire V. Hammond % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function inputs = setupMuscleSynergies(inputs) +if strcmp(inputs.controllerType, 'synergy') + inputs.splineSynergyActivations = spaps(inputs.initialTime, ... + inputs.initialSynergyControls', 0.0000001); + inputs.synergyLabels = inputs.initialSynergyControlsLabels; +end +end \ No newline at end of file diff --git a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupTorqueControls.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupTorqueControls.m new file mode 100644 index 000000000..41bd23ddd --- /dev/null +++ b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupTorqueControls.m @@ -0,0 +1,37 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function stores the initial control torques as a spline for use +% in cost terms for Treatment Optimization +% +% (string) -> (None) +% Spline input control torques + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Claire V. Hammond % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function inputs = setupTorqueControls(inputs) +if isfield(inputs, "torqueControllerCoordinateNames") +inputs.splineTorqueControls = spaps(inputs.initialTime, ... + inputs.initialTorqueControls', 0.0000001); +inputs.torqueLabels = inputs.initialTorqueControlsLabels; +end +end \ No newline at end of file diff --git a/src/TreatmentOptimization/VerificationOptimization/VerificationOptimizationTool.m b/src/TreatmentOptimization/VerificationOptimization/VerificationOptimizationTool.m index 4fa04f29c..5f11da9c7 100644 --- a/src/TreatmentOptimization/VerificationOptimization/VerificationOptimizationTool.m +++ b/src/TreatmentOptimization/VerificationOptimization/VerificationOptimizationTool.m @@ -34,6 +34,7 @@ function VerificationOptimizationTool(settingsFileName) verifyVersion(settingsTree, "VerificationOptimizationTool"); [inputs, params] = parseVerificationOptimizationSettingsTree(settingsTree); inputs = setupMuscleSynergies(inputs); +inputs = setupTorqueControls(inputs); inputs = makeTreatmentOptimizationInputs(inputs, params); outputs = solveOptimalControlProblem(inputs, params); reportTreatmentOptimizationResults(outputs, inputs); From e43091403f4ac0b8f62a95e89713c250c7272b8a Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Fri, 29 Sep 2023 20:08:54 -0500 Subject: [PATCH 137/365] remove unused functions --- .../getTrackingOptimizationValueStruct.m | 43 --------------- .../getVerificationOptimizationValueStruct.m | 38 ------------- .../getTreatmentOptimizationValueStruct.m | 53 ------------------- 3 files changed, 134 deletions(-) delete mode 100644 src/TreatmentOptimization/TrackingOptimization/getTrackingOptimizationValueStruct.m delete mode 100644 src/TreatmentOptimization/VerificationOptimization/getVerificationOptimizationValueStruct.m delete mode 100644 src/TreatmentOptimization/getTreatmentOptimizationValueStruct.m diff --git a/src/TreatmentOptimization/TrackingOptimization/getTrackingOptimizationValueStruct.m b/src/TreatmentOptimization/TrackingOptimization/getTrackingOptimizationValueStruct.m deleted file mode 100644 index f7e9b2604..000000000 --- a/src/TreatmentOptimization/TrackingOptimization/getTrackingOptimizationValueStruct.m +++ /dev/null @@ -1,43 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function parses and scales the design variables specific to -% Tracking Optimization. If the model is synergy driven, synergy weights -% are properly calculated if they are fixed or being optimized. -% -% (struct, struct) -> (struct) -% Design variables specific to Tracking Optimization are parsed and scaled - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega, Claire V. Hammond % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function values = getTrackingOptimizationValueStruct(phase, inputs) -values = getTreatmentOptimizationValueStruct(phase, inputs); -if strcmp(inputs.controllerType, 'synergy') - if inputs.optimizeSynergyVectors - synergyWeights = scaleToOriginal(phase.parameter(1,:), ... - inputs.maxParameter, inputs.minParameter); - values.synergyWeights = synergyWeights; - else - values.synergyWeights = inputs.synergyWeights; - end -end -end diff --git a/src/TreatmentOptimization/VerificationOptimization/getVerificationOptimizationValueStruct.m b/src/TreatmentOptimization/VerificationOptimization/getVerificationOptimizationValueStruct.m deleted file mode 100644 index d400956b5..000000000 --- a/src/TreatmentOptimization/VerificationOptimization/getVerificationOptimizationValueStruct.m +++ /dev/null @@ -1,38 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function parses and scales the design variables specific to -% Verification Optimization. If the model is synergy driven, synergy -% weights are properly calculated and fixed. -% -% (struct, struct) -> (struct) -% Design variables specific to Verification Optimization are parsed and scaled - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega, Claire V. Hammond % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function values = getVerificationOptimizationValueStruct(setup, inputs) -values = getTreatmentOptimizationValueStruct(setup, inputs); -if strcmp(inputs.controllerType, 'synergy') - values.synergyWeights = getSynergyWeightsFromGroups(... - inputs.synergyWeights, inputs); -end -end diff --git a/src/TreatmentOptimization/getTreatmentOptimizationValueStruct.m b/src/TreatmentOptimization/getTreatmentOptimizationValueStruct.m deleted file mode 100644 index 28571dcfe..000000000 --- a/src/TreatmentOptimization/getTreatmentOptimizationValueStruct.m +++ /dev/null @@ -1,53 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function parses and scales the design variables common to all -% treatment optimization modules (tracking, verification, and design -% optimization). Time, states, and controls are parsed and scaled back to -% their original values. -% -% (struct, struct) -> (struct) -% Design variables common to all treatment optimization modules are parsed - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega, Claire V. Hammond % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function values = getTreatmentOptimizationValueStruct(phase, inputs) - -values.time = scaleToOriginal(phase.time, inputs.maxTime, ... - inputs.minTime); -state = scaleToOriginal(phase.state, ones(size(phase.state, 1), 1) .* ... - inputs.maxState, ones(size(phase.state, 1), 1) .* inputs.minState); -control = scaleToOriginal(phase.control, ones(size(phase.control, 1), 1) .* ... - inputs.maxControl, ones(size(phase.control, 1), 1) .* inputs.minControl); -values.statePositions = getCorrectStates(state, 1, inputs.numCoordinates); -values.stateVelocities = getCorrectStates(state, 2, inputs.numCoordinates); -values.stateAccelerations = getCorrectStates(state, 3, inputs.numCoordinates); -values.controlJerks = control(:, 1 : inputs.numCoordinates); - -if ~strcmp(inputs.controllerType, 'synergy') - values.controlTorques = control(:, inputs.numCoordinates + 1 : ... - inputs.numCoordinates + length(inputs.torqueControllerCoordinateNames)); -else - values.controlSynergyActivations = control(:, ... - inputs.numCoordinates + 1 : inputs.numCoordinates + inputs.numSynergies); -end -end \ No newline at end of file From 3cf85dcf104c9c6c2dc4b4fe2879e0ed3c3dbd3d Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Fri, 29 Sep 2023 20:09:19 -0500 Subject: [PATCH 138/365] fix bounds for synergy and torque controls --- .../SetupBounds/makeOptimalControlBounds.m | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m b/src/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m index c51a6943c..a9fc337e4 100644 --- a/src/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m +++ b/src/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m @@ -78,16 +78,16 @@ inputs.coordinateNames, ... inputs.statesCoordinateNames); -maxControlJerks = max(stateJointJerks) + ... +inputs.maxControl = max(stateJointJerks) + ... inputs.controlJerksMultiple * range(stateJointJerks); -minControlJerks = min(stateJointJerks) - ... +inputs.minControl = min(stateJointJerks) - ... inputs.controlJerksMultiple * range(stateJointJerks); if strcmp(inputs.controllerType, 'synergy') maxControlSynergyActivations = inputs.maxControlSynergyActivations * ... ones(1, inputs.numSynergies); - inputs.maxControl = [maxControlJerks maxControlSynergyActivations]; - inputs.minControl = [minControlJerks zeros(1, inputs.numSynergies)]; + inputs.maxControl = [inputs.maxControl maxControlSynergyActivations]; + inputs.minControl = [inputs.minControl zeros(1, inputs.numSynergies)]; if inputs.optimizeSynergyVectors inputs.maxParameter = inputs.maxParameterSynergyWeights * ... @@ -96,6 +96,8 @@ end end if isfield(inputs, "torqueControllerCoordinateNames") + maxTorqueControls = []; + minTorqueControls = []; for i = 1:length(inputs.torqueControllerCoordinateNames) indx = find(strcmp(convertCharsToStrings( ... inputs.inverseDynamicsMomentLabels), ... @@ -105,14 +107,14 @@ inputs.inverseDynamicsMomentLabels), ... strcat(inputs.torqueControllerCoordinateNames(i), '_force'))); end - maxControlTorques(i) = max(inputs.experimentalJointMoments(:, ... - indx)) + inputs.maxControlTorquesMultiple * ... + maxTorqueControls(i) = max(inputs.experimentalJointMoments(:, ... + indx)) + inputs.maxTorqueControlsMultiple * ... range(inputs.experimentalJointMoments(:, indx)); - minControlTorques(i) = min(inputs.experimentalJointMoments(:, ... - indx)) - inputs.maxControlTorquesMultiple * ... + minTorqueControls(i) = min(inputs.experimentalJointMoments(:, ... + indx)) - inputs.maxTorqueControlsMultiple * ... range(inputs.experimentalJointMoments(:, indx)); end - inputs.maxControl = [maxControlJerks maxControlTorques]; - inputs.minControl = [minControlJerks minControlTorques]; + inputs.maxControl = [inputs.maxControl maxTorqueControls]; + inputs.minControl = [inputs.minControl minTorqueControls]; end end From ccc0604fba501941669e6e19719700f03bbd287e Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Fri, 29 Sep 2023 20:09:33 -0500 Subject: [PATCH 139/365] added state_control_path_constraint --- .../calcStateControlPathConstraint.m | 52 +++++++++++++++++++ .../generateConstraintTermStruct.m | 16 +++++- 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 src/TreatmentOptimization/PathTerms/calcStateControlPathConstraint.m diff --git a/src/TreatmentOptimization/PathTerms/calcStateControlPathConstraint.m b/src/TreatmentOptimization/PathTerms/calcStateControlPathConstraint.m new file mode 100644 index 000000000..8734fa7be --- /dev/null +++ b/src/TreatmentOptimization/PathTerms/calcStateControlPathConstraint.m @@ -0,0 +1,52 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function calculates the difference between the inverse dynamic +% moments and the sum of the torque and synergy controller produced moments +% for the specified coordinate. +% +% (struct, struct, string) -> (number) +% + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Claire V. Hammond % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function pathTerm = calcStateControlPathConstraint(inputs, ... + modeledValues, torqueControls, loadName) +inverseDynamicsIndex = find(strcmp(convertCharsToStrings(inputs.inverseDynamicsMomentLabels), ... + loadName)); +synergyIndex = find(strcmp(strcat(inputs.surrogateModelCoordinateNames, ... + '_moment'), loadName)); +torqueIndex = find(strcmp(strcat(inputs.torqueControllerCoordinateNames, ... + '_moment'), loadName)); +if isempty(synergyIndex) + synergyLoad = 0; +else + synergyLoad = modeledValues.muscleJointMoments(:, synergyIndex); +end +if isempty(torqueIndex) + torqueLoad = 0; +else + torqueLoad = torqueControls(:, torqueIndex); +end +pathTerm = modeledValues.inverseDynamicsMoments(:, inverseDynamicsIndex) - ... + (synergyLoad + torqueLoad); +end diff --git a/src/TreatmentOptimization/generateConstraintTermStruct.m b/src/TreatmentOptimization/generateConstraintTermStruct.m index 1a9958f2c..e58b9a1b2 100644 --- a/src/TreatmentOptimization/generateConstraintTermStruct.m +++ b/src/TreatmentOptimization/generateConstraintTermStruct.m @@ -54,6 +54,7 @@ allowedTypes = [ ... "root_segment_residual_load", ... "torque_model_moment_consistency", ... + "state_control_consistency", ... ]; end if strcmp(constraintTermType, "terminal") @@ -70,6 +71,7 @@ allowedTypes = [ ... "root_segment_residual_load", ... "torque_model_moment_consistency", ... + "state_control_consistency", ... ]; end if strcmp(constraintTermType, "terminal") @@ -86,6 +88,7 @@ allowedTypes = [ ... "root_segment_residual_load", ... "torque_model_moment_consistency", ... + "state_control_consistency", ... "limit_normalized_fiber_length", ... ]; end @@ -111,6 +114,7 @@ allowedTypes = [ ... "root_segment_residual_load", ... "muscle_model_moment_consistency", ... + "state_control_consistency", ... ]; end if strcmp(constraintTermType, "terminal") @@ -128,6 +132,7 @@ allowedTypes = [ ... "root_segment_residual_load", ... "muscle_model_moment_consistency", ... + "state_control_consistency", ... ]; end if strcmp(constraintTermType, "terminal") @@ -144,6 +149,7 @@ allowedTypes = [ ... "root_segment_residual_load", ... "muscle_model_moment_consistency", ... + "state_control_consistency", ... "limit_muscle_activation", ... "limit_normalized_fiber_length", ... "external_control_muscle_moment_consistency", ... @@ -187,7 +193,15 @@ calcTorqueActuatedMomentsPathConstraints( ... auxdata, ... modeledValues, ... - values.controlTorques, ... + values.torqueControls, ... + constraintTerm.load ... + ); + +constraintTermCalculations.state_control_consistency = @(values, modeledValues, auxdata, constraintTerm) ... + calcStateControlPathConstraint( ... + auxdata, ... + modeledValues, ... + values.torqueControls, ... constraintTerm.load ... ); From 4229386409717d1b6eda2e27b04617ef95e1c5f6 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Fri, 29 Sep 2023 20:09:48 -0500 Subject: [PATCH 140/365] remove forgotten portion --- .../gpops/convertToGpopsSynergyDrivenInputs.m | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/TreatmentOptimization/gpops/convertToGpopsSynergyDrivenInputs.m b/src/TreatmentOptimization/gpops/convertToGpopsSynergyDrivenInputs.m index 55e846152..94a573182 100644 --- a/src/TreatmentOptimization/gpops/convertToGpopsSynergyDrivenInputs.m +++ b/src/TreatmentOptimization/gpops/convertToGpopsSynergyDrivenInputs.m @@ -27,9 +27,6 @@ function setup = convertToGpopsSynergyDrivenInputs(inputs, params) bounds = setupProblemBounds(inputs, params); guess = setupGpopsInitialGuess(inputs); -if strcmp(inputs.toolName, "DesignOptimization") - guess = addUserDefinedTermsToGuess(guess, inputs); -end initializeMexOrMatlabParallelFunctions(inputs.mexModel); setup = setupGpopsSettings(inputs, ... bounds, guess, params, ... From 95007ceaaf03919278ade545ae59b79914df7128 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Fri, 29 Sep 2023 20:10:23 -0500 Subject: [PATCH 141/365] fix for shared controller option --- .../gpops/makeGpopsValuesAsStruct.m | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m b/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m index d9a8d9b53..ae19218a3 100644 --- a/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m +++ b/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m @@ -44,14 +44,15 @@ values.controlJerks = control(:, 1 : length(inputs.statesCoordinateNames)); [values.positions, values.velocities, ... values.accelerations] = recombineFullState(values, inputs); -if strcmp(inputs.controllerType, 'torque') - values.controlTorques = control(:, inputs.numCoordinates + 1 : ... - inputs.numCoordinates + length(inputs.torqueControllerCoordinateNames)); -else +if strcmp(inputs.controllerType, 'synergy') values.controlSynergyActivations = control(:, ... length(inputs.statesCoordinateNames) + 1 : ... length(inputs.statesCoordinateNames) + inputs.numSynergies); end +values.torqueControls = control(:, ... + inputs.numCoordinates + 1 + inputs.numSynergies : ... + inputs.numCoordinates + inputs.numSynergies + ... + length(inputs.torqueControllerCoordinateNames)); if strcmp(inputs.toolName, "TrackingOptimization") if strcmp(inputs.controllerType, 'synergy') From 98fca17c7e879985686c91ca00e3241c3a040e4b Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Fri, 29 Sep 2023 20:10:50 -0500 Subject: [PATCH 142/365] fixed controlTorques to torqueControls --- .../calcMuscleActuatedMomentsWithExternalAidPathConstraints.m | 4 ++-- .../PathTerms/calcTorqueActuatedMomentsPathConstraints.m | 4 ++-- src/TreatmentOptimization/saveTreatmentOptimizationResults.m | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/TreatmentOptimization/PathTerms/calcMuscleActuatedMomentsWithExternalAidPathConstraints.m b/src/TreatmentOptimization/PathTerms/calcMuscleActuatedMomentsWithExternalAidPathConstraints.m index da01e026b..2ffcd8562 100644 --- a/src/TreatmentOptimization/PathTerms/calcMuscleActuatedMomentsWithExternalAidPathConstraints.m +++ b/src/TreatmentOptimization/PathTerms/calcMuscleActuatedMomentsWithExternalAidPathConstraints.m @@ -32,7 +32,7 @@ function pathTerm = ... calcMuscleActuatedMomentsWithExternalAidPathConstraints(params, ... - modeledValues, externalControlTorques, coordinate) + modeledValues, externalTorqueControls, coordinate) indx1 = find(strcmp(convertCharsToStrings(params.inverseDynamicsMomentLabels), ... [coordinate '_moment'])); @@ -43,5 +43,5 @@ pathTerm = modeledValues.inverseDynamicsMoments(:, indx1) - ... (modeledValues.muscleJointMoments(:, indx2) + ... - externalControlTorques(:, indx3)); + externalTorqueControls(:, indx3)); end diff --git a/src/TreatmentOptimization/PathTerms/calcTorqueActuatedMomentsPathConstraints.m b/src/TreatmentOptimization/PathTerms/calcTorqueActuatedMomentsPathConstraints.m index 1dde4930f..2a66bc3a2 100644 --- a/src/TreatmentOptimization/PathTerms/calcTorqueActuatedMomentsPathConstraints.m +++ b/src/TreatmentOptimization/PathTerms/calcTorqueActuatedMomentsPathConstraints.m @@ -30,7 +30,7 @@ % ----------------------------------------------------------------------- % function pathTerm = calcTorqueActuatedMomentsPathConstraints(inputs, ... - modeledValues, controlTorques, loadName) + modeledValues, torqueControls, loadName) loadName = erase(loadName, '_moment'); loadName = erase(loadName, '_force'); @@ -38,5 +38,5 @@ repmat({loadName}, 1, length(inputs.coordinateNames)))); indx2 = find(strcmp(inputs.torqueControllerCoordinateNames, loadName)); pathTerm = modeledValues.inverseDynamicsMoments(:, indx1) - ... - controlTorques(:, indx2); + torqueControls(:, indx2); end diff --git a/src/TreatmentOptimization/saveTreatmentOptimizationResults.m b/src/TreatmentOptimization/saveTreatmentOptimizationResults.m index e9fab5f91..f79d6cc88 100644 --- a/src/TreatmentOptimization/saveTreatmentOptimizationResults.m +++ b/src/TreatmentOptimization/saveTreatmentOptimizationResults.m @@ -68,7 +68,7 @@ function saveTreatmentOptimizationResults(solution, inputs, values) for i = 1 : length(inputs.torqueControllerCoordinateNames) controlLabels{end + 1} = inputs.torqueControllerCoordinateNames{i}; end - writeToSto(controlLabels, values.time, values.controlTorques, ... + writeToSto(controlLabels, values.time, values.torqueControls, ... fullfile(inputs.resultsDirectory, ... strcat(inputs.trialName, "_torqueControls.sto"))); end From 869402fc3565e6312cece658a917fe6320008e32 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Fri, 29 Sep 2023 20:11:03 -0500 Subject: [PATCH 143/365] report torque on synergy driven if necessary --- .../reportTreatmentOptimizationResults.m | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/TreatmentOptimization/reportTreatmentOptimizationResults.m b/src/TreatmentOptimization/reportTreatmentOptimizationResults.m index e51c34bf6..56c020ff2 100644 --- a/src/TreatmentOptimization/reportTreatmentOptimizationResults.m +++ b/src/TreatmentOptimization/reportTreatmentOptimizationResults.m @@ -41,9 +41,10 @@ function reportTreatmentOptimizationResults(solution, inputs) end plotResultsWithOutComparison(values.controlSynergyActivations, values.time, ... synergyTitles, ["Synergy" "Activations"]); -else +end % plot torque controls -plotResultsWithOutComparison(values.controlTorques, values.time, ... +if isfield(inputs, 'torqueControllerCoordinateNames') +plotResultsWithOutComparison(values.torqueControls, values.time, ... inputs.torqueControllerCoordinateNames, ["Torque" "Controls"]); end % plot external torque controls From c52c015f4499fa09fc8e0e44f496a79b49014062 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Fri, 29 Sep 2023 20:11:21 -0500 Subject: [PATCH 144/365] fix cost term to use initial states/controls --- .../calcTrackingControllerIntegrand.m | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m index 126a8a1cd..3e1eb476d 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m @@ -33,27 +33,29 @@ function cost = calcTrackingControllerIntegrand(auxdata, values, time, ... controllerName) -switch auxdata.controllerType - case 'synergy' - indx = find(strcmp(convertCharsToStrings( ... - auxdata.synergyLabels), controllerName)); +if strcmp(auxdata.controllerType, 'synergy') + indx = find(strcmp(convertCharsToStrings( ... + auxdata.synergyLabels), controllerName)); + if ~isempty(indx) synergyActivations = ... fnval(auxdata.splineSynergyActivations, time)'; cost = calcTrackingCostArrayTerm(synergyActivations, ... values.controlSynergyActivations, indx); - case 'torque' - indx1 = find(strcmp(convertCharsToStrings( ... - auxdata.inverseDynamicsMomentLabels), controllerName)); - indx2 = find(strcmp(convertCharsToStrings( ... - strcat(auxdata.torqueControllerCoordinateNames, '_moment')), ... - controllerName)); - if auxdata.splineJointMoments.dim > 1 - experimentalJointMoments = ... - fnval(auxdata.splineJointMoments, time)'; - else - experimentalJointMoments = ... - fnval(auxdata.splineJointMoments, time); - end - cost = experimentalJointMoments(:, indx1) - ... - values.controlTorques(:, indx2); + return + end +end +indx1 = find(strcmp(convertCharsToStrings( ... + strcat(auxdata.torqueLabels, '_moment')), controllerName)); +indx2 = find(strcmp(convertCharsToStrings( ... + strcat(auxdata.torqueControllerCoordinateNames, '_moment')), ... + controllerName)); +if auxdata.splineTorqueControls.dim > 1 + experimentalJointMoments = ... + fnval(auxdata.splineTorqueControls, time)'; +else + experimentalJointMoments = ... + fnval(auxdata.splineTorqueControls, time); +end +cost = experimentalJointMoments(:, indx1) - ... + values.torqueControls(:, indx2); end From cddc0dfae52a97bc2e898c904cb459b4f140eebb Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sat, 30 Sep 2023 19:52:10 -0500 Subject: [PATCH 145/365] err if VO uses optimize_synergy_vectors --- .../parseVerificationOptimizationSettingsTree.m | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/TreatmentOptimization/VerificationOptimization/parseVerificationOptimizationSettingsTree.m b/src/TreatmentOptimization/VerificationOptimization/parseVerificationOptimizationSettingsTree.m index 972c6e9da..21595503a 100644 --- a/src/TreatmentOptimization/VerificationOptimization/parseVerificationOptimizationSettingsTree.m +++ b/src/TreatmentOptimization/VerificationOptimization/parseVerificationOptimizationSettingsTree.m @@ -31,6 +31,11 @@ function [inputs, params] = ... parseVerificationOptimizationSettingsTree(settingsTree) inputs = parseTreatmentOptimizationInputs(settingsTree); +if isfield(inputs, "optimizeSynergyVectors") && ... + inputs.optimizeSynergyVectors + throw(MException('VerificationOptimizationTool:parseVerificationOptimizationSettingsTree', ... + 'Verification Optimization does not support synergy vector optimization.')); +end params = parseTreatmentOptimizationParams(settingsTree); inputs = modifyModelForces(inputs); end From f9c15d32aa7d604e531cefacc783a4c58f738585 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sat, 30 Sep 2023 19:52:35 -0500 Subject: [PATCH 146/365] consolidate synergyWeight saving --- .../saveTrackingOptimizationResults.m | 8 -------- .../saveVerificationOptimizationResults.m | 8 -------- .../saveTreatmentOptimizationResults.m | 8 ++++++++ 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/src/TreatmentOptimization/TrackingOptimization/saveTrackingOptimizationResults.m b/src/TreatmentOptimization/TrackingOptimization/saveTrackingOptimizationResults.m index 9d9124743..b64192e6b 100644 --- a/src/TreatmentOptimization/TrackingOptimization/saveTrackingOptimizationResults.m +++ b/src/TreatmentOptimization/TrackingOptimization/saveTrackingOptimizationResults.m @@ -31,12 +31,4 @@ function saveTrackingOptimizationResults(solution, inputs) values = makeGpopsValuesAsStruct(solution.solution.phase, inputs); saveTreatmentOptimizationResults(solution, inputs, values); -if strcmp(inputs.controllerType, 'synergy') - writeToSto(inputs.muscleLabels, linspace(1, inputs.numSynergies, ... - inputs.numSynergies), [values.synergyWeights], ... - fullfile(inputs.resultsDirectory, "synergyWeights.sto")); - writeToSto(inputs.muscleLabels, values.time, ... - solution.muscleActivations, ... - fullfile(inputs.resultsDirectory, strcat(inputs.trialName, "_combinedActivations.sto"))); -end end diff --git a/src/TreatmentOptimization/VerificationOptimization/saveVerificationOptimizationResults.m b/src/TreatmentOptimization/VerificationOptimization/saveVerificationOptimizationResults.m index fed65e921..147a911ca 100644 --- a/src/TreatmentOptimization/VerificationOptimization/saveVerificationOptimizationResults.m +++ b/src/TreatmentOptimization/VerificationOptimization/saveVerificationOptimizationResults.m @@ -31,12 +31,4 @@ function saveVerificationOptimizationResults(solution, inputs) values = makeGpopsValuesAsStruct( ... solution.solution.phase, inputs); saveTreatmentOptimizationResults(solution, inputs, values) -if strcmp(inputs.controllerType, 'synergy') - writeToSto(inputs.muscleLabels, linspace(1, inputs.numSynergies, ... - inputs.numSynergies), [values.synergyWeights], ... - fullfile(inputs.resultsDirectory, "synergyWeights.sto")); - writeToSto(inputs.muscleLabels, values.time, ... - solution.muscleActivations, ... - fullfile(inputs.resultsDirectory, strcat(inputs.trialName, "_combinedActivations.sto"))); -end end \ No newline at end of file diff --git a/src/TreatmentOptimization/saveTreatmentOptimizationResults.m b/src/TreatmentOptimization/saveTreatmentOptimizationResults.m index f79d6cc88..51ee52aae 100644 --- a/src/TreatmentOptimization/saveTreatmentOptimizationResults.m +++ b/src/TreatmentOptimization/saveTreatmentOptimizationResults.m @@ -72,6 +72,14 @@ function saveTreatmentOptimizationResults(solution, inputs, values) fullfile(inputs.resultsDirectory, ... strcat(inputs.trialName, "_torqueControls.sto"))); end +if strcmp(inputs.controllerType, 'synergy') + writeToSto(inputs.muscleLabels, linspace(1, inputs.numSynergies, ... + inputs.numSynergies), [values.synergyWeights], ... + fullfile(inputs.resultsDirectory, "synergyWeights.sto")); + writeToSto(inputs.muscleLabels, values.time, ... + solution.muscleActivations, ... + fullfile(inputs.resultsDirectory, strcat(inputs.trialName, "_combinedActivations.sto"))); +end delete(inputs.mexModel); end From 9d56cd8765c5160e5a765277d27dc7e33ed8f02a Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sat, 30 Sep 2023 19:53:11 -0500 Subject: [PATCH 147/365] setup synergy weight bounds --- .../setupTreatmentOptimizationBounds.m | 6 +++++ .../SetupBounds/makeOptimalControlBounds.m | 25 ++++++++++++++++--- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupTreatmentOptimizationBounds.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupTreatmentOptimizationBounds.m index 254a3f1c7..6f514385e 100644 --- a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupTreatmentOptimizationBounds.m +++ b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupTreatmentOptimizationBounds.m @@ -59,6 +59,12 @@ if ~isempty(inputs.maxTerminal) bounds.eventgroup.upper = inputs.maxTerminal; end +if strcmp(inputs.controllerType, 'synergy') + if inputs.optimizeSynergyVectors + bounds.parameter.lower = -0.5 * ones(1, length(inputs.minParameter)); + bounds.parameter.upper = 0.5 * ones(1, length(inputs.minParameter)); + end +end if strcmp(inputs.toolName, "DesignOptimization") for i = 1:length(inputs.userDefinedVariables) lower = -0.5 * ones(1, length(inputs.userDefinedVariables{i}.initial_values)); diff --git a/src/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m b/src/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m index a9fc337e4..4097a024c 100644 --- a/src/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m +++ b/src/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m @@ -18,7 +18,7 @@ % National Institutes of Health (R01 EB030520). % % % % Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % +% Author(s): Marleny Vega, Claire V. Hammond % % % % Licensed under the Apache License, Version 2.0 (the "License"); % % you may not use this file except in compliance with the License. % @@ -90,9 +90,26 @@ inputs.minControl = [inputs.minControl zeros(1, inputs.numSynergies)]; if inputs.optimizeSynergyVectors - inputs.maxParameter = inputs.maxParameterSynergyWeights * ... - ones(1, inputs.numSynergyWeights); - inputs.minParameter = zeros(1, inputs.numSynergyWeights); + numParameters = 0; + for i = 1 : length(inputs.synergyGroups) + numParameters = numParameters + ... + inputs.synergyGroups{i}.numSynergies * ... + length(inputs.synergyGroups{i}.muscleNames); + end + inputs.maxParameter = ones(1, numParameters); + inputs.minParameter = zeros(1, numParameters); + end +end +if strcmp(inputs.toolName, "DesignOptimization") + if ~isfield(inputs, "maxParameter") + inputs.maxParameter = []; + inputs.minParameter = []; + end + for i = 1:length(inputs.userDefinedVariables) + inputs.maxParameter = [inputs.maxParameter ... + inputs.userDefinedVariables{i}.upper_bounds]; + inputs.minParameter = [inputs.minParameter ... + inputs.userDefinedVariables{i}.lower_bounds]; end end if isfield(inputs, "torqueControllerCoordinateNames") From 0a775f83b03fa201a49b79d8ba7a0b64e900a3ed Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sat, 30 Sep 2023 19:53:24 -0500 Subject: [PATCH 148/365] fix bug with shared controllers --- src/core/parse/parseSynergyController.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/parse/parseSynergyController.m b/src/core/parse/parseSynergyController.m index 63c583b86..3b0952061 100644 --- a/src/core/parse/parseSynergyController.m +++ b/src/core/parse/parseSynergyController.m @@ -32,7 +32,6 @@ function inputs = parseSynergyController(tree, inputs) inputs.synergyGroups = inputs.osimx.synergyGroups; inputs.numSynergies = getNumSynergies(inputs.synergyGroups); -inputs.numSynergyWeights = getNumSynergyWeights(inputs.synergyGroups); inputs.surrogateModelCoordinateNames = parseSpaceSeparatedList(tree, ... "states_coordinate_list"); inputs.muscleNames = getMusclesFromCoordinates(inputs.model, ... @@ -49,8 +48,9 @@ parseElementTextByNameOrAlternate(tree, "optimize_synergy_vectors", 0)); inputs.maxControlSynergyActivations = parseDoubleOrAlternate(tree, ... 'maximum_allowable_synergy_activation', 10); -inputs.maxParameterSynergyWeights = parseDoubleOrAlternate(tree, ... - 'maximum_allowable_synergy_vector_weight', 2); +if ~isfield(inputs, "torqueControllerCoordinateNames") + inputs.torqueControllerCoordinateNames = []; +end inputs = getModelOrOsimxInputs(inputs); end From 38bd4d9ce2a38decfbe54a7bc17ec4e870205a98 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sat, 30 Sep 2023 19:53:44 -0500 Subject: [PATCH 149/365] add synergy data normalization --- .../DesignOptimizationTool.m | 1 + .../normalizeSynergyData.m | 42 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 src/TreatmentOptimization/normalizeSynergyData.m diff --git a/src/TreatmentOptimization/DesignOptimization/DesignOptimizationTool.m b/src/TreatmentOptimization/DesignOptimization/DesignOptimizationTool.m index 2f6196a35..dc8e467e8 100644 --- a/src/TreatmentOptimization/DesignOptimization/DesignOptimizationTool.m +++ b/src/TreatmentOptimization/DesignOptimization/DesignOptimizationTool.m @@ -33,6 +33,7 @@ function DesignOptimizationTool(settingsFileName) settingsTree = xml2struct(settingsFileName); verifyVersion(settingsTree, "DesignOptimizationTool"); [inputs, params] = parseDesignOptimizationSettingsTree(settingsTree); +inputs = normalizeSynergyData(inputs); inputs = setupMuscleSynergies(inputs); inputs = setupTorqueControls(inputs); inputs = makeTreatmentOptimizationInputs(inputs, params); diff --git a/src/TreatmentOptimization/normalizeSynergyData.m b/src/TreatmentOptimization/normalizeSynergyData.m new file mode 100644 index 000000000..e3ad6d933 --- /dev/null +++ b/src/TreatmentOptimization/normalizeSynergyData.m @@ -0,0 +1,42 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function ensures the synergy weights are normalized to sum to one +% and the synergy commands are scaled in proportion +% +% (struct) -> (struct) +% scale synergy weights and commands for sum of weights to equal one + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Claire V. Hammond % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function inputs = normalizeSynergyData(inputs) +if strcmp(inputs.controllerType, "synergy") + for i = 1:size(inputs.synergyWeights, 1) + total = sum(inputs.synergyWeights(i, :)); + inputs.synergyWeights(i, :) = ... + inputs.synergyWeights(i, :) / total; + inputs.initialSynergyControls(:, i) = ... + inputs.initialSynergyControls(:, i) * total; + end +end +end + From 2f295349f29d859efc98f4b22fbf861d7538053d Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sat, 30 Sep 2023 19:54:48 -0500 Subject: [PATCH 150/365] consolidate gpops inputs --- .../convertFromGpopsSynergyDrivenOutputs.m | 43 ---------------- .../convertFromGpopsTorqueDrivenOutputs.m | 40 --------------- ...yDrivenInputs.m => convertToGpopsInputs.m} | 16 +----- .../gpops/convertToGpopsTorqueDrivenInputs.m | 51 ------------------- 4 files changed, 2 insertions(+), 148 deletions(-) delete mode 100644 src/TreatmentOptimization/gpops/convertFromGpopsSynergyDrivenOutputs.m delete mode 100644 src/TreatmentOptimization/gpops/convertFromGpopsTorqueDrivenOutputs.m rename src/TreatmentOptimization/gpops/{convertToGpopsSynergyDrivenInputs.m => convertToGpopsInputs.m} (82%) delete mode 100644 src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m diff --git a/src/TreatmentOptimization/gpops/convertFromGpopsSynergyDrivenOutputs.m b/src/TreatmentOptimization/gpops/convertFromGpopsSynergyDrivenOutputs.m deleted file mode 100644 index f9346ed79..000000000 --- a/src/TreatmentOptimization/gpops/convertFromGpopsSynergyDrivenOutputs.m +++ /dev/null @@ -1,43 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Claire V. Hammond % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function output = convertFromGpopsSynergyDrivenOutputs(solution, ... - inputs, params) -solution = solution.result.solution; -solution.auxdata = inputs; -if strcmp(inputs.toolName, "DesignOptimization") - if isfield(solution, 'parameter') - solution.phase.parameter = [solution.parameter]; - end -else - if inputs.optimizeSynergyVectors - solution.phase.parameter = solution.parameter; - end -end -output = computeGpopsContinuousFunction(solution); -output.solution = solution; -end - diff --git a/src/TreatmentOptimization/gpops/convertFromGpopsTorqueDrivenOutputs.m b/src/TreatmentOptimization/gpops/convertFromGpopsTorqueDrivenOutputs.m deleted file mode 100644 index 86871c743..000000000 --- a/src/TreatmentOptimization/gpops/convertFromGpopsTorqueDrivenOutputs.m +++ /dev/null @@ -1,40 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Claire V. Hammond % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function output = convertFromGpopsTorqueDrivenOutputs(solution, ... - inputs, params) -solution = solution.result.solution; -solution.auxdata = inputs; -if isfield(solution, 'parameter') - solution.phase.parameter = [solution.parameter]; -end -if isfield(inputs, "optimizeSynergyVectors") - solution.phase.parameter = solution.parameter; -end -output = computeGpopsContinuousFunction(solution); -output.solution = solution; -end - diff --git a/src/TreatmentOptimization/gpops/convertToGpopsSynergyDrivenInputs.m b/src/TreatmentOptimization/gpops/convertToGpopsInputs.m similarity index 82% rename from src/TreatmentOptimization/gpops/convertToGpopsSynergyDrivenInputs.m rename to src/TreatmentOptimization/gpops/convertToGpopsInputs.m index 94a573182..c38dc0763 100644 --- a/src/TreatmentOptimization/gpops/convertToGpopsSynergyDrivenInputs.m +++ b/src/TreatmentOptimization/gpops/convertToGpopsInputs.m @@ -24,8 +24,8 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function setup = convertToGpopsSynergyDrivenInputs(inputs, params) -bounds = setupProblemBounds(inputs, params); +function setup = convertToGpopsInputs(inputs, params) +bounds = setupTreatmentOptimizationBounds(inputs, params); guess = setupGpopsInitialGuess(inputs); initializeMexOrMatlabParallelFunctions(inputs.mexModel); setup = setupGpopsSettings(inputs, ... @@ -35,15 +35,3 @@ checkInitialGuess(guess, inputs, ... @computeGpopsContinuousFunction); end - -function bounds = setupProblemBounds(inputs, params) -bounds = setupTreatmentOptimizationBounds(inputs, params); -% setup parameter bounds -if strcmp(inputs.controllerType, 'synergy') - if inputs.optimizeSynergyVectors - bounds.parameter.lower = -0.5 * ones(1, length(inputs.minParameter)); - bounds.parameter.upper = 0.5 * ones(1, length(inputs.minParameter)); - end -end -end - diff --git a/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m b/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m deleted file mode 100644 index a49704b42..000000000 --- a/src/TreatmentOptimization/gpops/convertToGpopsTorqueDrivenInputs.m +++ /dev/null @@ -1,51 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Claire V. Hammond % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function setup = convertToGpopsTorqueDrivenInputs(inputs, params) -bounds = setupProblemBounds(inputs, params); -guess = setupGpopsInitialGuess(inputs); -initializeMexOrMatlabParallelFunctions(inputs.mexModel); -setup = setupGpopsSettings(inputs, ... - bounds, guess, params, ... - @computeGpopsContinuousFunction, ... - @computeGpopsEndpointFunction); -checkInitialGuess(guess, inputs, ... - @computeGpopsContinuousFunction); -end - -function bounds = setupProblemBounds(inputs, params) -bounds = setupTreatmentOptimizationBounds(inputs, params); -% setup parameter bounds -if strcmp(inputs.controllerType, 'synergy') - if inputs.optimizeSynergyVectors - bounds.parameter.lower = -0.5 * ones(1, length(inputs.minParameter)); - bounds.parameter.upper = 0.5 * ones(1, length(inputs.minParameter)); - end -end -end - - - From ea81b1c227dacf800d6462b8a798e793d4bf508e Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sat, 30 Sep 2023 19:54:53 -0500 Subject: [PATCH 151/365] Delete getNumSynergyWeights.m --- src/core/synergies/getNumSynergyWeights.m | 34 ----------------------- 1 file changed, 34 deletions(-) delete mode 100644 src/core/synergies/getNumSynergyWeights.m diff --git a/src/core/synergies/getNumSynergyWeights.m b/src/core/synergies/getNumSynergyWeights.m deleted file mode 100644 index 66ef3d04f..000000000 --- a/src/core/synergies/getNumSynergyWeights.m +++ /dev/null @@ -1,34 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% () -> () -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function numSynergyWeights = getNumSynergyWeights(synergyGroups) -numSynergyWeights = 0; -for i = 1 : length(synergyGroups) - numSynergyWeights = numSynergyWeights + ... - length(synergyGroups{i}.muscleNames) * synergyGroups{i}.numSynergies; -end -end \ No newline at end of file From 1b0cedb81d4d7175e1d1bad4772bb171919ab972 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sat, 30 Sep 2023 19:55:02 -0500 Subject: [PATCH 152/365] fix print bug --- .../DesignOptimization/saveDesignOptimizationResults.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/TreatmentOptimization/DesignOptimization/saveDesignOptimizationResults.m b/src/TreatmentOptimization/DesignOptimization/saveDesignOptimizationResults.m index f09ff6038..ac16976be 100644 --- a/src/TreatmentOptimization/DesignOptimization/saveDesignOptimizationResults.m +++ b/src/TreatmentOptimization/DesignOptimization/saveDesignOptimizationResults.m @@ -54,10 +54,10 @@ function printUserDefinedVariablesToXml(solution, inputs) counter = counter + numParameters; valuesStr = num2str(parameterResults(1)); for j = 2:length(parameterResults) - strcat(valuesStr, " ", num2str(parameterResults(j))); + valuesStr = strcat(valuesStr, " ", num2str(parameterResults(j))); end parameters.NMSMPipelineDocument.RCNLParameters{i}.RCNLParameterSet.type = inputs.userDefinedVariables{i}.type; - parameters.NMSMPipelineDocument.RCNLParameters{i}.RCNLParameterSet.values = valuesStr; + parameters.NMSMPipelineDocument.RCNLParameters{i}.RCNLParameterSet.values = convertStringsToChars(valuesStr); struct2xml(parameters, fullfile(inputs.resultsDirectory, ... strcat(inputs.trialName, "_parameterSolution.xml"))); end From 178b66d6baedfa6c1604cc624332945838b48800 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sat, 30 Sep 2023 19:55:26 -0500 Subject: [PATCH 153/365] fix bug with shared controllers --- .../OptimalControlSetupFunctions/setupTorqueControls.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupTorqueControls.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupTorqueControls.m index 41bd23ddd..a92b771eb 100644 --- a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupTorqueControls.m +++ b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupTorqueControls.m @@ -29,7 +29,8 @@ % ----------------------------------------------------------------------- % function inputs = setupTorqueControls(inputs) -if isfield(inputs, "torqueControllerCoordinateNames") +if isfield(inputs, "torqueControllerCoordinateNames") && ... + ~isempty(inputs.torqueControllerCoordinateNames) inputs.splineTorqueControls = spaps(inputs.initialTime, ... inputs.initialTorqueControls', 0.0000001); inputs.torqueLabels = inputs.initialTorqueControlsLabels; From 707ab76af138aeb5df575988ed612eabb7aa5d99 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sat, 30 Sep 2023 19:55:39 -0500 Subject: [PATCH 154/365] consolidate --- .../solveOptimalControlProblem.m | 39 ++----------------- 1 file changed, 4 insertions(+), 35 deletions(-) diff --git a/src/TreatmentOptimization/solveOptimalControlProblem.m b/src/TreatmentOptimization/solveOptimalControlProblem.m index 9b134c6c2..0f1ddf9aa 100644 --- a/src/TreatmentOptimization/solveOptimalControlProblem.m +++ b/src/TreatmentOptimization/solveOptimalControlProblem.m @@ -25,39 +25,8 @@ % ----------------------------------------------------------------------- % function output = solveOptimalControlProblem(inputs, params) -if strcmp(inputs.controllerType, "torque") - if isfield(inputs, "gpops") - setup = convertToGpopsTorqueDrivenInputs(inputs, params); - solution = gpops2(setup); - output = convertFromGpopsTorqueDrivenOutputs(solution, ... - inputs, params); - elseif isfield(inputs, "moco") - setup = convertToMocoTorqueDrivenInputs(inputs, params); - solution = moco(setup); - output = convertFromMocoTorqueDrivenOutputs(solution, ... - inputs, params); - else - MException('solveOptimalControlProblem:invalidSolver', ... - 'Invalid solver specified.'); - end -elseif strcmp(inputs.controllerType, "synergy") - if isfield(inputs, "gpops") - setup = convertToGpopsSynergyDrivenInputs(inputs, params); - solution = gpops2(setup); - output = convertFromGpopsSynergyDrivenOutputs(solution, ... - inputs, params); - elseif isfield(inputs, "moco") - setup = convertToMocoSynergyDrivenInputs(inputs, params); - solution = moco(setup); - output = convertFromMocoSynergyDrivenOutputs(solution, ... - inputs, params); - else - MException('solveOptimalControlProblem:invalidSolver', ... - 'Invalid solver specified.'); - end -else - MException('solveOptimalControlProblem:invalidProblemType', ... - 'Invalid problem type specified.'); +setup = convertToGpopsInputs(inputs, params); +solution = gpops2(setup); +output = convertFromGpopsOutputs(solution, ... + inputs, params); end -end - From b3e7d1719a2a5309abf067275e3bfca03a1fed3c Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sat, 30 Sep 2023 19:56:00 -0500 Subject: [PATCH 155/365] redo synergy weight sum constraint term --- .../TerminalTerms/calcSynergyWeightsSum.m | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/TreatmentOptimization/TerminalTerms/calcSynergyWeightsSum.m b/src/TreatmentOptimization/TerminalTerms/calcSynergyWeightsSum.m index ffba690bc..780da5f39 100644 --- a/src/TreatmentOptimization/TerminalTerms/calcSynergyWeightsSum.m +++ b/src/TreatmentOptimization/TerminalTerms/calcSynergyWeightsSum.m @@ -13,7 +13,7 @@ % National Institutes of Health (R01 EB030520). % % % % Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % +% Author(s): Claire V. Hammond % % % % Licensed under the Apache License, Version 2.0 (the "License"); % % you may not use this file except in compliance with the License. % @@ -30,22 +30,17 @@ function synergyWeightsSum = calcSynergyWeightsSum(synergyWeights, ... synergyGroups, synergyGroupName) -numSynergiesIndex(1) = 0; -numMusclesIndex(1) = 0; +counter = 1; for i = 1 : length(synergyGroups) - temp = find(strcmp(convertCharsToStrings(synergyGroups{i}.muscleGroupName), ... - synergyGroupName)); - if ~isempty(temp) - indx = i; + if strcmp(synergyGroups{i}.muscleGroupName, synergyGroupName) + break; end + counter = counter + synergyGroups{i}.numSynergies; end -for j = 1 : indx - numSynergiesIndex(end+1) = numSynergiesIndex(end) + ... - synergyGroups{j}.numSynergies; - numMusclesIndex(end+1) = numMusclesIndex(end) + ... - size(synergyGroups{j}.muscleNames, 2); + +numSynergies = synergyGroups{i}.numSynergies; +synergyWeightsSum = zeros(numSynergies, 1); +for j = counter : counter + numSynergies - 1 + synergyWeightsSum(j - counter + 1) = sum(synergyWeights(j, :)); end -synergyWeightsSum = sum(synergyWeights(1 + numSynergiesIndex(end-1): ... - numSynergiesIndex(end), 1 + numMusclesIndex(end-1) : ... - numMusclesIndex(end)), 2)'; end \ No newline at end of file From 488781b83d385281940367b7c69346f74a6e72ce Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sat, 30 Sep 2023 19:56:21 -0500 Subject: [PATCH 156/365] add shared gpops outputs --- .../gpops/convertFromGpopsOutputs.m | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 src/TreatmentOptimization/gpops/convertFromGpopsOutputs.m diff --git a/src/TreatmentOptimization/gpops/convertFromGpopsOutputs.m b/src/TreatmentOptimization/gpops/convertFromGpopsOutputs.m new file mode 100644 index 000000000..6886b6356 --- /dev/null +++ b/src/TreatmentOptimization/gpops/convertFromGpopsOutputs.m @@ -0,0 +1,44 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Claire V. Hammond % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function output = convertFromGpopsOutputs(solution, ... + inputs, params) +solution = solution.result.solution; +solution.auxdata = inputs; +if isfield(inputs, "optimizeSynergyVectors") && ... + inputs.optimizeSynergyVectors + solution.phase.parameter = [solution.parameter]; +else + if strcmp(inputs.toolName, "DesignOptimization") + if isfield(solution, 'parameter') + solution.phase.parameter = [solution.parameter]; + end + end +end +output = computeGpopsContinuousFunction(solution); +output.solution = solution; +end + From b80e8b38766b7c463a72b9112da65e3265e952d6 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sat, 30 Sep 2023 19:56:41 -0500 Subject: [PATCH 157/365] add normalize synergy data to TO/VO --- .../TrackingOptimization/TrackingOptimizationTool.m | 1 + .../VerificationOptimizationTool.m | 9 +-------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/TreatmentOptimization/TrackingOptimization/TrackingOptimizationTool.m b/src/TreatmentOptimization/TrackingOptimization/TrackingOptimizationTool.m index 3632f8ed3..b0023be31 100644 --- a/src/TreatmentOptimization/TrackingOptimization/TrackingOptimizationTool.m +++ b/src/TreatmentOptimization/TrackingOptimization/TrackingOptimizationTool.m @@ -33,6 +33,7 @@ function TrackingOptimizationTool(settingsFileName) settingsTree = xml2struct(settingsFileName); verifyVersion(settingsTree, "TrackingOptimizationTool"); [inputs, params] = parseTrackingOptimizationSettingsTree(settingsTree); +inputs = normalizeSynergyData(inputs); inputs = makeTreatmentOptimizationInputs(inputs, params); outputs = solveOptimalControlProblem(inputs, params); reportTreatmentOptimizationResults(outputs, inputs); diff --git a/src/TreatmentOptimization/VerificationOptimization/VerificationOptimizationTool.m b/src/TreatmentOptimization/VerificationOptimization/VerificationOptimizationTool.m index 5f11da9c7..aa86cc9c9 100644 --- a/src/TreatmentOptimization/VerificationOptimization/VerificationOptimizationTool.m +++ b/src/TreatmentOptimization/VerificationOptimization/VerificationOptimizationTool.m @@ -33,6 +33,7 @@ function VerificationOptimizationTool(settingsFileName) settingsTree = xml2struct(settingsFileName); verifyVersion(settingsTree, "VerificationOptimizationTool"); [inputs, params] = parseVerificationOptimizationSettingsTree(settingsTree); +inputs = normalizeSynergyData(inputs); inputs = setupMuscleSynergies(inputs); inputs = setupTorqueControls(inputs); inputs = makeTreatmentOptimizationInputs(inputs, params); @@ -40,11 +41,3 @@ function VerificationOptimizationTool(settingsFileName) reportTreatmentOptimizationResults(outputs, inputs); saveVerificationOptimizationResults(outputs, inputs); end - -function inputs = setupMuscleSynergies(inputs) -if strcmp(inputs.controllerType, 'synergy') - inputs.splineSynergyActivations = spaps(inputs.initialTime, ... - inputs.initialSynergyControls', 0.0000001); - inputs.synergyLabels = inputs.initialSynergyControlsLabels; -end -end \ No newline at end of file From 147b7654b06cccdb997627b3b996eece3620fea1 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sat, 30 Sep 2023 19:56:55 -0500 Subject: [PATCH 158/365] add parameter logic to gpops values as struct --- .../gpops/makeGpopsValuesAsStruct.m | 70 ++++++++----------- 1 file changed, 30 insertions(+), 40 deletions(-) diff --git a/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m b/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m index ae19218a3..6c05d77c8 100644 --- a/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m +++ b/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m @@ -56,12 +56,20 @@ if strcmp(inputs.toolName, "TrackingOptimization") if strcmp(inputs.controllerType, 'synergy') + values.synergyWeights = inputs.synergyWeights; if inputs.optimizeSynergyVectors - synergyWeights = scaleToOriginal(phase.parameter(1,:), ... + parameters = scaleToOriginal(phase.parameter(1,:), ... inputs.maxParameter, inputs.minParameter); - values.synergyWeights = synergyWeights; - else - values.synergyWeights = inputs.synergyWeights; + counter = 1; + for i = 1 : length(inputs.synergyGroups) + for j = 1 : length(inputs.synergyGroups{i}.muscleNames) + index = find(ismember(inputs.synergyWeightsLabels, ... + inputs.synergyGroups{i}.muscleNames{j})); + values.synergyWeights(i, index) = ... + parameters(counter); + counter = counter + 1; + end + end end end end @@ -71,50 +79,32 @@ end end if strcmp(inputs.toolName, "DesignOptimization") - numParameters = 0; + counter = 1; if strcmp(inputs.controllerType, 'synergy') + values.synergyWeights = inputs.synergyWeights; if inputs.optimizeSynergyVectors - values.synergyWeights = scaleToOriginal(phase.parameter(1, ... - 1 : inputs.numSynergyWeights), ... + parameters = scaleToOriginal(phase.parameter(1,:), ... inputs.maxParameter, inputs.minParameter); - numParameters = inputs.numSynergyWeights; - else - values.synergyWeights = inputs.synergyWeights; + for i = 1 : length(inputs.synergyGroups) + for j = 1 : length(inputs.synergyGroups{i}.muscleNames) + index = find(ismember(inputs.synergyWeightsLabels, ... + inputs.synergyGroups{i}.muscleNames{j})); + values.synergyWeights(i, index) = ... + parameters(counter); + counter = counter + 1; + end + end end - % if inputs.splineSynergyActivations.dim > 1 - % values.controlSynergyActivations = ... - % fnval(inputs.splineSynergyActivations, values.time)'; - % else - % values.controlSynergyActivations = ... - % fnval(inputs.splineSynergyActivations, values.time); - % end - % if inputs.enableExternalTorqueControl - % controls = scaleToOriginal(phase.control, ones(size( ... - % phase.control, 1), 1) .* inputs.maxControl, ... - % ones(size(phase.control, 1), 1) .* inputs.minControl); - % values.externalTorqueControls = controls(:, inputs.numCoordinates + ... - % inputs.numSynergies + 1 : end); - % end - % else - % if isfield(inputs, "enableExternalTorqueControl") && ... - % inputs.enableExternalTorqueControl - % controls = scaleToOriginal(phase.control, ones(size( ... - % phase.control, 1), 1) .* inputs.maxControl, ... - % ones(size(phase.control, 1), 1) .* inputs.minControl); - % values.externalTorqueControls = controls(:, inputs.numCoordinates + ... - % length(inputs.torqueControllerCoordinateNames) + 1 : end); - % end end if isfield(inputs, 'userDefinedVariables') - counter = 1; + parameters = scaleToOriginal(phase.parameter(1,:), ... + inputs.maxParameter, inputs.minParameter); for i = 1:length(inputs.userDefinedVariables) - numParameters = length(inputs.userDefinedVariables{i}.initial_values); values.parameters.(inputs.userDefinedVariables{i}.type) ... - = scaleToOriginal( ... - phase.parameter(counter : counter + numParameters - 1), ... - inputs.userDefinedVariables{i}.upper_bounds, ... - inputs.userDefinedVariables{i}.lower_bounds); - counter = counter + numParameters; + = parameters(counter : counter + ... + length(inputs.userDefinedVariables{i}.initial_values) - 1); + counter = counter + ... + length(inputs.userDefinedVariables{i}.initial_values); end end end From 4829dd7f8563e692ba5bfa9da52d2b4f6f1c1829 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sat, 30 Sep 2023 19:57:15 -0500 Subject: [PATCH 159/365] synergy weight labels and initial guess logic --- .../setupGpopsInitialGuess.m | 24 +++++++++++++------ .../parseTreatmentOptimizationDataDirectory.m | 3 ++- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m index e9cb115b2..a0eb54f79 100644 --- a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m +++ b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m @@ -102,18 +102,28 @@ function guess = setupInitialParametersGuess(inputs, guess) if valueOrAlternate(inputs, "optimizeSynergyVectors", false) - guess.parameter = scaleToBounds(inputs.synergyWeights, ... - inputs.maxParameter, inputs.minParameter); + guess.parameter = []; + for i = 1 : length(inputs.synergyGroups) + for j = 1 : length(inputs.synergyGroups{i}.muscleNames) + index = find(ismember(inputs.synergyWeightsLabels, ... + inputs.synergyGroups{i}.muscleNames{j})); + guess.parameter(end + 1) = inputs.synergyWeights(i, index); + end + end end if strcmp(inputs.toolName, "DesignOptimization") - guess.parameter = []; for i = 1:length(inputs.userDefinedVariables) - guess.parameter = [guess.parameter, scaleToBounds( ... - inputs.userDefinedVariables{i}.initial_values, ... - inputs.userDefinedVariables{i}.upper_bounds, ... - inputs.userDefinedVariables{i}.lower_bounds)]; + if ~isfield(guess, "parameter") + guess.parameter = []; + end + guess.parameter = [guess.parameter, ... + inputs.userDefinedVariables{i}.initial_values]; end end +if isfield(guess, "parameter") + guess.parameter = scaleToBounds(guess.parameter, inputs.maxParameter, ... + inputs.minParameter); +end end function output = subsetInitialStatesDataByCoordinates(data, ... diff --git a/src/core/parse/parseTreatmentOptimizationDataDirectory.m b/src/core/parse/parseTreatmentOptimizationDataDirectory.m index 2a6f974d7..c50544be9 100644 --- a/src/core/parse/parseTreatmentOptimizationDataDirectory.m +++ b/src/core/parse/parseTreatmentOptimizationDataDirectory.m @@ -76,7 +76,8 @@ [inputs.experimentalMuscleActivations, inputs.muscleLabels] = ... parseTrialData(inputs.previousResultsDirectory, ... strcat(inputs.trialName, "_combinedActivations"), inputs.model); - inputs.synergyWeights = parseTrialData(inputs.previousResultsDirectory, ... + [inputs.synergyWeights, inputs.synergyWeightsLabels] = ... + parseTrialData(inputs.previousResultsDirectory, ... "synergyWeights", inputs.model); directory = fullfile(dataDirectory, "MAData", inputs.trialName); inputs.momentArms = parseSelectMomentArms(directory, ... From 0e84749d5fb9dd2c44edb8c161fcedb3688353cb Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sat, 30 Sep 2023 20:46:50 -0500 Subject: [PATCH 160/365] initial free_final_time implementation --- .../parseDesignOptimizationSettingsTree.m | 14 +------------- .../setupTreatmentOptimizationBounds.m | 6 ++++++ .../SetupBounds/makeOptimalControlBounds.m | 6 +++++- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/TreatmentOptimization/DesignOptimization/parseDesignOptimizationSettingsTree.m b/src/TreatmentOptimization/DesignOptimization/parseDesignOptimizationSettingsTree.m index 1bb7960f0..3cb991b12 100644 --- a/src/TreatmentOptimization/DesignOptimization/parseDesignOptimizationSettingsTree.m +++ b/src/TreatmentOptimization/DesignOptimization/parseDesignOptimizationSettingsTree.m @@ -14,7 +14,7 @@ % National Institutes of Health (R01 EB030520). % % % % Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % +% Author(s): Marleny Vega, Claire V. Hammond % % % % Licensed under the Apache License, Version 2.0 (the "License"); % % you may not use this file except in compliance with the License. % @@ -45,7 +45,6 @@ inputs.userDefinedVariables = parseRcnlCostTermSet( ... parameterTree.RCNLParameterTerm); for i = 1:length(inputs.userDefinedVariables) - inputs.userDefinedVariables{i}.initial_values = ... stringToSpaceSeparatedList(inputs.userDefinedVariables{i}.initial_values); inputs.userDefinedVariables{i}.upper_bounds = ... @@ -74,16 +73,5 @@ if(isstruct(finalTimeRange)) inputs.finalTimeRange = getDoubleFromField(finalTimeRange); end -inputs.enableExternalTorqueControl = getBooleanLogicFromField( ... - getFieldByName(tree, "enable_external_torque_controls")); -if inputs.enableExternalTorqueControl - inputs.externalControlTorqueNames = parseSpaceSeparatedList(tree, ... - "external_control_coordinate_list"); - inputs.numExternalTorqueControls = ... - length(inputs.externalControlTorqueNames); - inputs.maxExternalTorqueControls = getDoubleFromField( ... - getFieldByNameOrError(tree, ... - 'external_torque_control_multiple')); -end end diff --git a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupTreatmentOptimizationBounds.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupTreatmentOptimizationBounds.m index 6f514385e..d60bcf375 100644 --- a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupTreatmentOptimizationBounds.m +++ b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupTreatmentOptimizationBounds.m @@ -35,6 +35,12 @@ bounds.phase.initialtime.upper = -0.5; bounds.phase.finaltime.lower = 0.5; bounds.phase.finaltime.upper = 0.5; +if isfield(inputs, "finalTimeRange") + scaledTime = scaleToBounds(inputs.initialTime, inputs.maxTime, ... + inputs.minTime); + bounds.phase.finaltime.lower = ... + scaledTime(end) - (0.5 - scaledTime(end)); +end % setup state bounds bounds.phase.initialstate.lower = -0.5 * ones(1, length(inputs.minState)); bounds.phase.initialstate.upper = 0.5 * ones(1, length(inputs.minState)); diff --git a/src/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m b/src/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m index 4097a024c..a2f15f40e 100644 --- a/src/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m +++ b/src/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m @@ -38,7 +38,11 @@ end function inputs = makeStateBounds(inputs) -inputs.maxTime = max(inputs.experimentalTime); +if isfield(inputs, "finalTimeRange") + inputs.maxTime = max(inputs.experimentalTime) + inputs.finalTimeRange; +else + inputs.maxTime = max(inputs.experimentalTime); +end inputs.minTime = min(inputs.experimentalTime); stateJointAngles = subsetDataByCoordinates( ... From 232ef17ed275088e19550c821ddd782f4a1f735a Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 2 Oct 2023 20:05:59 -0500 Subject: [PATCH 161/365] Set up contact surfaces --- .../parseDesignOptimizationSettingsTree.m | 1 - .../setupGroundContact.m | 6 +- .../parseTrackingOptimizationSettingsTree.m | 1 - ...arseVerificationOptimizationSettingsTree.m | 1 - .../gpops/convertToGpopsInputs.m | 1 - .../makeTreatmentOptimizationInputs.m | 4 ++ .../prepareGroundContactSurfaces.m | 68 +++++++++++++++++++ .../parseTreatmentOptimizationDataDirectory.m | 44 +++++++++++- .../parse/parseTreatmentOptimizationInputs.m | 1 - 9 files changed, 116 insertions(+), 11 deletions(-) create mode 100644 src/TreatmentOptimization/prepareGroundContactSurfaces.m diff --git a/src/TreatmentOptimization/DesignOptimization/parseDesignOptimizationSettingsTree.m b/src/TreatmentOptimization/DesignOptimization/parseDesignOptimizationSettingsTree.m index 3cb991b12..e8f14cbb3 100644 --- a/src/TreatmentOptimization/DesignOptimization/parseDesignOptimizationSettingsTree.m +++ b/src/TreatmentOptimization/DesignOptimization/parseDesignOptimizationSettingsTree.m @@ -34,7 +34,6 @@ inputs = parseDesignSettings(settingsTree, inputs); inputs = parseUserDefinedFunctions(settingsTree, inputs); params = parseTreatmentOptimizationParams(settingsTree); -inputs = modifyModelForces(inputs); end function inputs = parseUserDefinedFunctions(tree, inputs) diff --git a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGroundContact.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGroundContact.m index 098aefc94..5b3522121 100644 --- a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGroundContact.m +++ b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGroundContact.m @@ -34,8 +34,8 @@ midfootSuperiorLocation = pointKinematics(inputs.experimentalTime, ... inputs.experimentalJointAngles, inputs.experimentalJointVelocities, ... inputs.contactSurfaces{i}.midfootSuperiorPointOnBody, ... - inputs.contactSurfaces{i}.midfootSuperiorBody, inputs.mexModel, ... - inputs.coordinateNames); + inputs.contactSurfaces{i}.midfootSuperiorBody, ... + inputs.modelFileName, inputs.coordinateNames); midfootSuperiorLocation(:, 2) = 0; inputs.contactSurfaces{i}.experimentalGroundReactionMoments = ... transferMoments(inputs.contactSurfaces{i}.electricalCenter, ... @@ -43,4 +43,4 @@ inputs.contactSurfaces{i}.experimentalGroundReactionMoments, ... inputs.contactSurfaces{i}.experimentalGroundReactionForces); end -end \ No newline at end of file +end diff --git a/src/TreatmentOptimization/TrackingOptimization/parseTrackingOptimizationSettingsTree.m b/src/TreatmentOptimization/TrackingOptimization/parseTrackingOptimizationSettingsTree.m index 9bc6c2fe1..71fb268f8 100644 --- a/src/TreatmentOptimization/TrackingOptimization/parseTrackingOptimizationSettingsTree.m +++ b/src/TreatmentOptimization/TrackingOptimization/parseTrackingOptimizationSettingsTree.m @@ -32,6 +32,5 @@ parseTrackingOptimizationSettingsTree(settingsTree) inputs = parseTreatmentOptimizationInputs(settingsTree); params = parseTreatmentOptimizationParams(settingsTree); -inputs = modifyModelForces(inputs); end diff --git a/src/TreatmentOptimization/VerificationOptimization/parseVerificationOptimizationSettingsTree.m b/src/TreatmentOptimization/VerificationOptimization/parseVerificationOptimizationSettingsTree.m index 21595503a..82dad5a83 100644 --- a/src/TreatmentOptimization/VerificationOptimization/parseVerificationOptimizationSettingsTree.m +++ b/src/TreatmentOptimization/VerificationOptimization/parseVerificationOptimizationSettingsTree.m @@ -37,7 +37,6 @@ 'Verification Optimization does not support synergy vector optimization.')); end params = parseTreatmentOptimizationParams(settingsTree); -inputs = modifyModelForces(inputs); end diff --git a/src/TreatmentOptimization/gpops/convertToGpopsInputs.m b/src/TreatmentOptimization/gpops/convertToGpopsInputs.m index c38dc0763..5d5e86058 100644 --- a/src/TreatmentOptimization/gpops/convertToGpopsInputs.m +++ b/src/TreatmentOptimization/gpops/convertToGpopsInputs.m @@ -27,7 +27,6 @@ function setup = convertToGpopsInputs(inputs, params) bounds = setupTreatmentOptimizationBounds(inputs, params); guess = setupGpopsInitialGuess(inputs); -initializeMexOrMatlabParallelFunctions(inputs.mexModel); setup = setupGpopsSettings(inputs, ... bounds, guess, params, ... @computeGpopsContinuousFunction, ... diff --git a/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m b/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m index 9fac41322..4444c65d4 100644 --- a/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m +++ b/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m @@ -29,6 +29,10 @@ function inputs = makeTreatmentOptimizationInputs(inputs, params) inputs = makeStateDerivatives(inputs, params); +inputs.contactSurfaces = prepareGroundContactSurfaces( ... + inputs.modelFileName, inputs.contactSurfaces); +inputs = modifyModelForces(inputs); +initializeMexOrMatlabParallelFunctions(inputs.mexModel); inputs = setupGroundContact(inputs); inputs = makeExperimentalDataSplines(inputs); inputs = makeSurrogateModel(inputs); diff --git a/src/TreatmentOptimization/prepareGroundContactSurfaces.m b/src/TreatmentOptimization/prepareGroundContactSurfaces.m new file mode 100644 index 000000000..eeb169c58 --- /dev/null +++ b/src/TreatmentOptimization/prepareGroundContactSurfaces.m @@ -0,0 +1,68 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% +% (struct) -> (struct) +% Prepares the ground contact surface parameters and springs + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Marleny Vega % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function contactSurfaces = prepareGroundContactSurfaces(osimModel, ... + contactSurfaces) +import org.opensim.modeling.Model +osimModel = Model(osimModel); +osimModel.finalizeConnections(); + +for i=1:length(contactSurfaces) + contactSurfaces{i} = getParentChildSprings(osimModel, contactSurfaces{i}); + contactSurfaces{i}.midfootSuperiorPointOnBody = Vec3ToArray(osimModel ... + .getMarkerSet.get(contactSurfaces{i}.midfootSuperiorMarker).get_location()); + contactSurfaces{i}.midfootSuperiorBody = osimModel.getBodySet.getIndex( ... + osimModel.getMarkerSet.get(contactSurfaces{i}.midfootSuperiorMarker). ... + getParentFrame().getName()); + contactSurfaces{i}.childBody = osimModel.getBodySet.getIndex(contactSurfaces{i}.childBodyName); + contactSurfaces{i}.parentBody = osimModel.getBodySet. ... + getIndex(contactSurfaces{i}.parentBodyName); +end +end + +function contactSurface = getParentChildSprings(osimModel, contactSurface) +contactSurface.parentSpringPointsOnBody = []; +contactSurface.parentSpringConstants = []; +contactSurface.childSpringPointsOnBody = []; +contactSurface.childSpringConstants = []; +[contactSurface.parentBodyName, contactSurface.childBodyName] = ... + getJointBodyNames(osimModel, contactSurface.toesJointName); +for j = 1:length(contactSurface.springs) + if strcmp(contactSurface.springs{j}.parentBody, contactSurface.parentBodyName) + contactSurface.parentSpringPointsOnBody(end+1, :) = ... + contactSurface.springs{j}.location; + contactSurface.parentSpringConstants(end+1) = ... + contactSurface.springs{j}.springConstant; + elseif strcmp(contactSurface.springs{j}.parentBody, contactSurface.childBodyName) + contactSurface.childSpringPointsOnBody(end+1, :) = ... + contactSurface.springs{j}.location; + contactSurface.childSpringConstants(end+1) = ... + contactSurface.springs{j}.springConstant; + end +end +end \ No newline at end of file diff --git a/src/core/parse/parseTreatmentOptimizationDataDirectory.m b/src/core/parse/parseTreatmentOptimizationDataDirectory.m index c50544be9..cafc21757 100644 --- a/src/core/parse/parseTreatmentOptimizationDataDirectory.m +++ b/src/core/parse/parseTreatmentOptimizationDataDirectory.m @@ -65,9 +65,18 @@ fullfile(dataDirectory, "IKData"), inputs.trialName, inputs.model); inputs.coordinateNames = cellstr(inputs.coordinateNames); inputs.experimentalTime = experimentalTime - experimentalTime(1); -if exist(fullfile(dataDirectory, "GRFData"), 'dir') - inputs.grfFileName = findFileListFromPrefixList(... - fullfile(dataDirectory, "GRFData"), prefix); +if isfield(inputs.osimx, 'groundContact') && ... + isfield(inputs.osimx.groundContact, 'contactSurface') + inputs.contactSurfaces = inputs.osimx.groundContact.contactSurface; + for surfaceIndex = 1:length(inputs.contactSurfaces) + [inputs.contactSurfaces{surfaceIndex} ... + .experimentalGroundReactionForces, ... + inputs.contactSurfaces{surfaceIndex} ... + .experimentalGroundReactionMoments, ... + inputs.contactSurfaces{surfaceIndex} ... + .electricalCenter] = parseGroundReactionDataWithoutTime( ... + inputs, dataDirectory, surfaceIndex); + end end end @@ -126,3 +135,32 @@ [data, labels, time] = parseTrialData(dataDirectory, trialName, model); end end + +function [forces, moments, ec] = parseGroundReactionDataWithoutTime( ... + inputs, dataDirectory, surfaceIndex) +import org.opensim.modeling.Storage +[grfData, grfColumnNames, ~] = parseTrialDataTryDirectories( ... + fullfile(inputs.previousResultsDirectory, "GRFData"), ... + fullfile(dataDirectory, "GRFData"), inputs.trialName, inputs.model); +for i=1:size(grfColumnNames') + label = grfColumnNames(i); + for j = 1:3 + if strcmpi(label, inputs.osimx.groundContact ... + .contactSurface{surfaceIndex}.forceColumns(j)) + forces(:, j) = grfData(:, i); + end + if strcmpi(label, inputs.osimx.groundContact ... + .contactSurface{surfaceIndex}.momentColumns(j)) + moments(:, j) = grfData(:, i); + end + if strcmpi(label, inputs.osimx.groundContact ... + .contactSurface{surfaceIndex}.electricalCenterColumns(j)) + ec(:, j) = grfData(:, i); + end + end +end +if any([isnan(forces) isnan(moments) isnan(ec)]) + throw(MException('', ['Unable to parse GRF file, check that ' ... + 'all necessary column labels are present'])) +end +end diff --git a/src/core/parse/parseTreatmentOptimizationInputs.m b/src/core/parse/parseTreatmentOptimizationInputs.m index c75177447..79db857ca 100644 --- a/src/core/parse/parseTreatmentOptimizationInputs.m +++ b/src/core/parse/parseTreatmentOptimizationInputs.m @@ -40,7 +40,6 @@ [inputs.path, inputs.terminal] = parseRcnlConstraintTermSetHelper( ... getFieldByNameOrError(tree, 'RCNLConstraintTermSet'), ... inputs.controllerType, inputs.toolName); -inputs.contactSurfaces = parseGroundContactSurfaces(inputs); end function inputs = parseBasicInputs(tree) From 9459208d945c7a04dd711dffd6de719c7bfbc46b Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 2 Oct 2023 20:44:23 -0500 Subject: [PATCH 162/365] Point kinematics cpp --- src/core/mex/PointKinematics.cpp | 139 +++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 src/core/mex/PointKinematics.cpp diff --git a/src/core/mex/PointKinematics.cpp b/src/core/mex/PointKinematics.cpp new file mode 100644 index 000000000..b11d21a11 --- /dev/null +++ b/src/core/mex/PointKinematics.cpp @@ -0,0 +1,139 @@ +#include "mex.h" +#include +#include +#include +#include +#include + +using namespace OpenSim; +using namespace SimTK; +using namespace std; +#define NTHREADS 20 + +//______________________________________________________________________________ + + +static Model *osimModel[NTHREADS]; +static State *osimState[NTHREADS]; +static bool modelIsLoaded = false; + +void ClearMemory(void) +{ + for (int i = 0; iinitSystem(); + } + + std::cout.rdbuf(oldCoutStreamBuf); + modelIsLoaded = true; + } + else if (nrhs == 6) + { + if (modelIsLoaded == false) + { + mexErrMsgTxt("!!!No OpenSim model has been loaded!!!\n"); + } + + const int numPts = mxGetM(prhs[0]); // get number of rows of time vector + const int numSprings = mxGetN(prhs[3]); // get number of bodies springs are located on + const int numLabels = mxGetN(prhs[5]); + + double *time = mxGetPr(prhs[0]); // time vector + double *q = mxGetPr(prhs[1]); // joint angles matrix + double *qp = mxGetPr(prhs[2]); // joint velocities matrix + double *SpringMat = mxGetPr(prhs[3]); // spring locations within body + double *SpringBodyMat = mxGetPr(prhs[4]); // body number index for springs + + OpenSim::BodySet *refBodySet[NTHREADS]; + for (int i = 0; iupdBodySet(); + } + + mwSize dims[3]; + dims[0] = numPts; + dims[1] = 3; + dims[2] = numSprings; + plhs[0] = mxCreateNumericArray(3, dims, mxDOUBLE_CLASS, mxREAL); + plhs[1] = mxCreateNumericArray(3, dims, mxDOUBLE_CLASS, mxREAL); + //plhs[0] = mxCreateDoubleMatrix(numPts * numSprings, 3, mxREAL); + //plhs[1] = mxCreateDoubleMatrix(numPts * numSprings, 3, mxREAL); + double *sp_pos = mxGetPr(plhs[0]); + double *sp_vel = mxGetPr(plhs[1]); + + const mxArray *cell_element_ptr; + char* c_array; + mwIndex k; + mwSize buflen; + + #pragma omp parallel for num_threads(NTHREADS) + for (int i = 0; igetCoordinateSet().get(c_array).get_locked()) + { + osimModel[thread_id]->getCoordinateSet().get(c_array).setValue(*osimState[thread_id], q[k*numPts + i]); + osimModel[thread_id]->getCoordinateSet().get(c_array).setSpeedValue(*osimState[thread_id], qp[k*numPts + i]); + } + mxFree(c_array); + } + + osimModel[thread_id]->realizeVelocity(*osimState[thread_id]); + + for (int j = 0; jget(SpringBodyMat[j]); + Vec3 tempLocalPos; + tempLocalPos.set(0, SpringMat[j * 3]); + tempLocalPos.set(1, SpringMat[j * 3 + 1]); + tempLocalPos.set(2, SpringMat[j * 3 + 2]); + + Vec3 tempGlobalPos; + Vec3 tempGlobalVel; + + osimModel[thread_id]->getSimbodyEngine().getPosition(*osimState[thread_id], tempRefParentBody, tempLocalPos, tempGlobalPos); + osimModel[thread_id]->getSimbodyEngine().getVelocity(*osimState[thread_id], tempRefParentBody, tempLocalPos, tempGlobalVel); + + sp_pos[i + j * numPts * 3 + numPts * 0] = tempGlobalPos(0); + sp_pos[i + j * numPts * 3 + numPts * 1] = tempGlobalPos(1); + sp_pos[i + j * numPts * 3 + numPts * 2] = tempGlobalPos(2); + + sp_vel[i + j * numPts * 3 + numPts * 0] = tempGlobalVel(0); + sp_vel[i + j * numPts * 3 + numPts * 1] = tempGlobalVel(1); + sp_vel[i + j * numPts * 3 + numPts * 2] = tempGlobalVel(2); + + } + } + } +} \ No newline at end of file From 8e9eb27aa0bf5fad33b288ff51a87120b95f6324 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 2 Oct 2023 20:57:35 -0500 Subject: [PATCH 163/365] Serialized point kinematics --- src/core/mex/PointKinematicsMatlab.m | 47 ++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/core/mex/PointKinematicsMatlab.m diff --git a/src/core/mex/PointKinematicsMatlab.m b/src/core/mex/PointKinematicsMatlab.m new file mode 100644 index 000000000..e6e3bb4eb --- /dev/null +++ b/src/core/mex/PointKinematicsMatlab.m @@ -0,0 +1,47 @@ +function [SpringPos, SpringVel] = PointKinematics_Matlab(time,q,qp,SpringMat,SpringBodyMat,... + modelFile,IKLabels) + + % Load Library + import org.opensim.modeling.*; + + % Open a Model by name + osimModel = Model(modelFile); + + % Initialize the system and get the initial state + osimState = osimModel.initSystem(); + + % Get the number of coords and markers + numPts = size(time,1); + numSprings = size(SpringMat,1); + + refBodySet = osimModel.getBodySet; + + for j=1:numPts + osimState.setTime(time(j,1)); + for k=1:size(IKLabels,2) + if ~osimModel.getCoordinateSet.get(IKLabels{k}).get_locked + osimModel.getCoordinateSet.get(IKLabels{k}).setValue(osimState,q(j,k)); + osimModel.getCoordinateSet.get(IKLabels{k}).setSpeedValue(osimState,qp(j,k)); + end + end + osimModel.realizeVelocity(osimState); + + for i=1:numSprings + tempRefParentBody = refBodySet.get(SpringBodyMat(i)); + tempLocalPos = Vec3(3,0); + tempGlobalPos = Vec3(3,0); + tempGlobalVel = Vec3(3,0); + tempLocalPos.set(0,SpringMat(i,1)); + tempLocalPos.set(1,SpringMat(i,2)); + tempLocalPos.set(2,SpringMat(i,3)); + + osimModel.getSimbodyEngine.getPosition(osimState,tempRefParentBody,tempLocalPos,tempGlobalPos); + osimModel.getSimbodyEngine.getVelocity(osimState,tempRefParentBody,tempLocalPos,tempGlobalVel); + + for k=0:2 + SpringPos(j,(i-1)*3+k+1)=tempGlobalPos.get(k); + SpringVel(j,(i-1)*3+k+1)=tempGlobalVel.get(k); + end + end + end +end \ No newline at end of file From 36fbf73c3ec701b1ebd26f9962a4dd6a410db051 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Mon, 2 Oct 2023 22:05:38 -0500 Subject: [PATCH 164/365] update ground contact modeled values --- .../calcTorqueBasedModeledValues.m | 42 +++++++++---------- src/core/mex/PointKinematicsMatlab.m | 2 +- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m b/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m index ca436c938..c882b7ce3 100644 --- a/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m +++ b/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m @@ -36,9 +36,9 @@ if ~isempty(inputs.contactSurfaces) clear pointKinematics [springPositions, springVelocities] = getSpringLocations( ... - values.time, values.statePositions, values.stateVelocities, inputs); + values.time, values.positions, values.velocities, inputs); modeledValues.bodyLocations = getBodyLocations(values.time, .... - values.statePositions, values.stateVelocities, inputs); + values.positions, values.velocities, inputs); groundReactions = calcFootGroundReactions(springPositions, ... springVelocities, inputs, modeledValues.bodyLocations); groundReactionsBody = tranferGroundReactionMoments( ... @@ -53,40 +53,40 @@ end function [springPositions, springVelocities] = getSpringLocations(time, .... - statePositions, stateVelocities, params) + statePositions, stateVelocities, inputs) -for i = 1:length(params.contactSurfaces) +for i = 1:length(inputs.contactSurfaces) [springPositions.parent{i}, springVelocities.parent{i}] = ... pointKinematics(time, statePositions, stateVelocities, ... - params.contactSurfaces{i}.parentSpringPointsOnBody, ... - params.contactSurfaces{i}.parentBody * ones(1, ... - size(params.contactSurfaces{i}.parentSpringPointsOnBody, 1)), ... - params.mexModel, params.coordinateNames); + inputs.contactSurfaces{i}.parentSpringPointsOnBody, ... + inputs.contactSurfaces{i}.parentBody * ones(1, ... + size(inputs.contactSurfaces{i}.parentSpringPointsOnBody, 1)), ... + inputs.mexModel, inputs.coordinateNames); [springPositions.child{i}, springVelocities.child{i}] = ... pointKinematics(time, statePositions, stateVelocities, ... - params.contactSurfaces{i}.childSpringPointsOnBody, ... - params.contactSurfaces{i}.childBody * ones(1, ... - size(params.contactSurfaces{i}.childSpringPointsOnBody, 1)), ... - params.mexModel, params.coordinateNames); + inputs.contactSurfaces{i}.childSpringPointsOnBody, ... + inputs.contactSurfaces{i}.childBody * ones(1, ... + size(inputs.contactSurfaces{i}.childSpringPointsOnBody, 1)), ... + inputs.mexModel, inputs.coordinateNames); end end function bodyLocations = getBodyLocations(time, statePositions, ... - stateVelocities, params) + stateVelocities, inputs) -for i = 1:length(params.contactSurfaces) +for i = 1:length(inputs.contactSurfaces) bodyLocations.midfootSuperior{i} = pointKinematics(time, ... statePositions, stateVelocities, ... - params.contactSurfaces{i}.midfootSuperiorPointOnBody, ... - params.contactSurfaces{i}.midfootSuperiorBody, ... - params.mexModel, params.coordinateNames); + inputs.contactSurfaces{i}.midfootSuperiorPointOnBody, ... + inputs.contactSurfaces{i}.midfootSuperiorBody, ... + inputs.mexModel, inputs.coordinateNames); bodyLocations.midfootSuperior{i}(:, 2) = 0; bodyLocations.parent{i} = pointKinematics(time, statePositions, ... - stateVelocities, [0 0 0], params.contactSurfaces{i}.parentBody, ... - params.mexModel, params.coordinateNames); + stateVelocities, [0 0 0], inputs.contactSurfaces{i}.parentBody, ... + inputs.mexModel, inputs.coordinateNames); bodyLocations.child{i} = pointKinematics(time, statePositions, ... - stateVelocities, [0 0 0], params.contactSurfaces{i}.childBody, ... - params.mexModel, params.coordinateNames); + stateVelocities, [0 0 0], inputs.contactSurfaces{i}.childBody, ... + inputs.mexModel, inputs.coordinateNames); end end diff --git a/src/core/mex/PointKinematicsMatlab.m b/src/core/mex/PointKinematicsMatlab.m index e6e3bb4eb..1b977216f 100644 --- a/src/core/mex/PointKinematicsMatlab.m +++ b/src/core/mex/PointKinematicsMatlab.m @@ -1,4 +1,4 @@ -function [SpringPos, SpringVel] = PointKinematics_Matlab(time,q,qp,SpringMat,SpringBodyMat,... +function [SpringPos, SpringVel] = pointKinematicsMatlab(time,q,qp,SpringMat,SpringBodyMat,... modelFile,IKLabels) % Load Library From a425dbfb2a6b8235da6dceb1c39f06b1a9c758a1 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Fri, 6 Oct 2023 14:52:16 -0500 Subject: [PATCH 165/365] re-add bspline impl --- .../makeStateDerivatives.m | 48 +++++-------------- 1 file changed, 13 insertions(+), 35 deletions(-) diff --git a/src/TreatmentOptimization/makeStateDerivatives.m b/src/TreatmentOptimization/makeStateDerivatives.m index bf1bbaa5d..6053ac002 100644 --- a/src/TreatmentOptimization/makeStateDerivatives.m +++ b/src/TreatmentOptimization/makeStateDerivatives.m @@ -29,39 +29,17 @@ % ----------------------------------------------------------------------- % function inputs = makeStateDerivatives(inputs, params) -jointAnglesSpline = spaps(inputs.experimentalTime, ... - inputs.experimentalJointAngles', eps, [], 3); -inputs.experimentalJointVelocities = fnval(fnder(jointAnglesSpline, 1), ... - inputs.experimentalTime)'; -if length(inputs.statesCoordinateNames) == 1 - inputs.experimentalJointVelocities = inputs.experimentalJointVelocities'; -end -jointVelocitiesSpline = spaps(inputs.experimentalTime, ... - inputs.experimentalJointVelocities', eps, [], 3); -inputs.experimentalJointAccelerations = fnval( ... - fnder(jointVelocitiesSpline, 1), inputs.experimentalTime)'; -if length(inputs.statesCoordinateNames) == 1 - inputs.experimentalJointAccelerations = inputs.experimentalJointAccelerations'; -end -jointAccelerationsSpline = spaps(inputs.experimentalTime, ... - inputs.experimentalJointAccelerations', eps, [], 3); -inputs.experimentalJointJerks = fnval(fnder(jointAccelerationsSpline, ... - 1), inputs.experimentalTime)'; -if length(inputs.statesCoordinateNames) == 1 - inputs.experimentalJointJerks = inputs.experimentalJointJerks'; -end - -% points = length(inputs.experimentalTime); -% interval = inputs.experimentalTime(2) - inputs.experimentalTime(1); -% cutoffFrequency = ... -% valueOrAlternate(params, "experimentalBSplineCutoffFrequency", 6); -% numNodes = splFitWithCutoff(inputs.experimentalTime', ... -% inputs.experimentalJointAngles', cutoffFrequency, 5); -% [N, Np, Npp] = BSplineMatrices(5, numNodes, points, interval); -% Nodes = N\inputs.experimentalJointAngles; -% inputs.experimentalJointVelocities = Np * Nodes; -% inputs.experimentalJointAccelerations = Npp * Nodes; -% inputs.experimentalJointJerks = calcBSplineDerivative( ... -% inputs.experimentalTime, inputs.experimentalJointAccelerations, ... -% 2, numNodes); +points = length(inputs.experimentalTime); +interval = inputs.experimentalTime(2) - inputs.experimentalTime(1); +cutoffFrequency = ... + valueOrAlternate(params, "experimentalBSplineCutoffFrequency", 6); +numNodes = splFitWithCutoff(inputs.experimentalTime', ... + inputs.experimentalJointAngles', cutoffFrequency, 5); +[N, Np, Npp] = BSplineMatrices(5, numNodes, points, interval); +Nodes = N\inputs.experimentalJointAngles; +inputs.experimentalJointVelocities = Np * Nodes; +inputs.experimentalJointAccelerations = Npp * Nodes; +inputs.experimentalJointJerks = calcBSplineDerivative( ... + inputs.experimentalTime, inputs.experimentalJointAccelerations, ... + 2, numNodes); end From dca657ddbdd3523f4f924db9cd8a3e4960a21be2 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 8 Oct 2023 12:49:57 -0500 Subject: [PATCH 166/365] fix for no contact surfaces --- src/core/parse/parseTreatmentOptimizationDataDirectory.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/parse/parseTreatmentOptimizationDataDirectory.m b/src/core/parse/parseTreatmentOptimizationDataDirectory.m index cafc21757..d12fed93a 100644 --- a/src/core/parse/parseTreatmentOptimizationDataDirectory.m +++ b/src/core/parse/parseTreatmentOptimizationDataDirectory.m @@ -77,6 +77,8 @@ .electricalCenter] = parseGroundReactionDataWithoutTime( ... inputs, dataDirectory, surfaceIndex); end +else +inputs.contactSurfaces = {}; end end From 51c190b46f4ca032edeedc9722795c6b12015d5a Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 8 Oct 2023 14:16:49 -0500 Subject: [PATCH 167/365] add generalized speed tracking cost term --- .../calcTrackingSpeedIntegrand.m | 49 +++++++++++++++++++ .../generateCostTermStruct.m | 8 +++ 2 files changed, 57 insertions(+) create mode 100644 src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m new file mode 100644 index 000000000..b63b1feb5 --- /dev/null +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m @@ -0,0 +1,49 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function calculates the difference between the experimental and +% predicted joint angles for the specified coordinate. +% +% (struct, Array of number, 2D matrix, Array of string) -> (Array of number) +% + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Marleny Vega % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function cost = calcTrackingSpeedIntegrand(auxdata, time, ... + stateVelocities, coordinateName) + +indx = find(strcmp(convertCharsToStrings(auxdata.statesCoordinateNames), ... + coordinateName)); +if isempty(indx) + throw(MException('CostTermError:CoordinateNotInState', ... + strcat("Coordinate ", coordinateName, " is not in the ", ... + ""))) +end +if auxdata.splineJointVelocities.dim > 1 + experimentalJointVelocities = fnval(auxdata.splineJointVelocities, time)'; +else + experimentalJointVelocities = fnval(auxdata.splineJointVelocities, time); +end + +cost = calcTrackingCostArrayTerm(experimentalJointVelocities, ... + stateVelocities, indx); +end \ No newline at end of file diff --git a/src/TreatmentOptimization/generateCostTermStruct.m b/src/TreatmentOptimization/generateCostTermStruct.m index 4545a10ac..5fc1c892c 100644 --- a/src/TreatmentOptimization/generateCostTermStruct.m +++ b/src/TreatmentOptimization/generateCostTermStruct.m @@ -128,6 +128,14 @@ costTerm.coordinate ... ); +costTermCalculations.speed_tracking = @(values, modeledValues, auxdata, costTerm) ... + calcTrackingSpeedIntegrand( ... + auxdata, ... + values.time, ... + values.velocities, ... + costTerm.coordinate ... + ); + costTermCalculations.controller_tracking = @(values, modeledValues, auxdata, costTerm) ... calcTrackingControllerIntegrand( ... auxdata, ... From cefd75d75b4701d9c7a0b744c0ca4c4d9666ed4c Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 8 Oct 2023 15:35:08 -0500 Subject: [PATCH 168/365] add time normalization to output --- .../reportTreatmentOptimizationResults.m | 84 +++++++++---------- .../saveTreatmentOptimizationResults.m | 33 +++++--- .../splineToEvenlySpaced.m | 37 ++++++++ 3 files changed, 98 insertions(+), 56 deletions(-) create mode 100644 src/TreatmentOptimization/splineToEvenlySpaced.m diff --git a/src/TreatmentOptimization/reportTreatmentOptimizationResults.m b/src/TreatmentOptimization/reportTreatmentOptimizationResults.m index 56c020ff2..91ef81a37 100644 --- a/src/TreatmentOptimization/reportTreatmentOptimizationResults.m +++ b/src/TreatmentOptimization/reportTreatmentOptimizationResults.m @@ -30,29 +30,23 @@ function reportTreatmentOptimizationResults(solution, inputs) values = makeGpopsValuesAsStruct(solution.solution.phase, inputs); if strcmp(inputs.controllerType, 'synergy') -% plot Muscle Activations -plotMuscleActivations(solution.muscleActivations, values.time, ... - inputs.experimentalMuscleActivations, inputs.experimentalTime, ... - inputs.muscleLabels); -% plot synergy activations -synergyTitles = {}; -for i = 1 : inputs.numSynergies - synergyTitles{end + 1} = strcat('synergy activation', num2str(i)); -end -plotResultsWithOutComparison(values.controlSynergyActivations, values.time, ... - synergyTitles, ["Synergy" "Activations"]); + % plot Muscle Activations + plotMuscleActivations(solution.muscleActivations, values.time, ... + inputs.experimentalMuscleActivations, inputs.experimentalTime, ... + inputs.muscleLabels); + % plot synergy activations + synergyTitles = {}; + for i = 1 : inputs.numSynergies + synergyTitles{end + 1} = strcat('synergy activation', num2str(i)); + end + plotResultsWithOutComparison(values.controlSynergyActivations, values.time, ... + synergyTitles, ["Synergy" "Activations"]); end % plot torque controls -if isfield(inputs, 'torqueControllerCoordinateNames') -plotResultsWithOutComparison(values.torqueControls, values.time, ... - inputs.torqueControllerCoordinateNames, ["Torque" "Controls"]); -end -% plot external torque controls -if isfield(inputs, 'enableExternalTorqueControl') - if inputs.enableExternalTorqueControl - plotResultsWithOutComparison(values.externalTorqueControls, values.time, ... - inputs.externalControlTorqueNames, ["External" "Torque Controls"]); - end +if isfield(inputs, 'torqueControllerCoordinateNames') && ... + ~isempty(inputs.torqueControllerCoordinateNames) + plotResultsWithOutComparison(values.torqueControls, values.time, ... + inputs.torqueControllerCoordinateNames, ["Torque" "Controls"]); end jointAngles = subsetDataByCoordinates(inputs.experimentalJointAngles, ... inputs.coordinateNames, inputs.statesCoordinateNames); @@ -66,12 +60,12 @@ function reportTreatmentOptimizationResults(solution, inputs) strcat(inputs.inverseDynamicsMomentLabels, ' [Nm]'), ["Joint" "Moments"]); % plot ground reactions for i = 1:length(inputs.contactSurfaces) -plotResultsWithComparison(solution.groundReactionsLab.forces{i}, values.time, ... - inputs.contactSurfaces{i}.experimentalGroundReactionForces, inputs.experimentalTime, ... - ["GRFx", "GRFy", "GRFz"], ["Ground" "Reaction Forces"]); -plotResultsWithComparison(solution.groundReactionsLab.moments{i}, values.time, ... - inputs.contactSurfaces{i}.experimentalGroundReactionMoments, inputs.experimentalTime, ... - ["GRTx", "GRTy", "GRTz"], ["Ground" "Reaction Moments"]); + plotResultsWithComparison(solution.groundReactionsLab.forces{i}, values.time, ... + inputs.contactSurfaces{i}.experimentalGroundReactionForces, inputs.experimentalTime, ... + ["GRFx", "GRFy", "GRFz"], ["Ground" "Reaction Forces"]); + plotResultsWithComparison(solution.groundReactionsLab.moments{i}, values.time, ... + inputs.contactSurfaces{i}.experimentalGroundReactionMoments, inputs.experimentalTime, ... + ["GRTx", "GRTy", "GRTz"], ["Ground" "Reaction Moments"]); end % if strcmp(inputs.toolName, 'DesignOptimization') @@ -87,13 +81,13 @@ function plotMuscleActivations(muscleActivations, time, ... figure('name', 'Muscle Activations') nplots = ceil(sqrt(size(muscleActivations, 2))); for i = 1 : size(muscleActivations,2) -subplot(nplots, nplots, i) -plot(time, muscleActivations(:, i)); hold on -plot(experimentalTime, experimentalMuscleActivations(:, i), 'r') -axis([0 experimentalTime(end) 0 1]) -title(muscleLabels(i)) -figureXLabels(numel(muscleLabels), nplots, i, "Time") -figureYLabels(numel(muscleLabels), nplots, i, ["Muscle" "Activation"]) + subplot(nplots, nplots, i) + plot(time, muscleActivations(:, i)); hold on + plot(experimentalTime, experimentalMuscleActivations(:, i), 'r') + axis([0 experimentalTime(end) 0 1]) + title(muscleLabels(i)) + figureXLabels(numel(muscleLabels), nplots, i, "Time") + figureYLabels(numel(muscleLabels), nplots, i, ["Muscle" "Activation"]) end end function plotResultsWithComparison(solutionData, solutionTime, ... @@ -102,12 +96,12 @@ function plotResultsWithComparison(solutionData, solutionTime, ... figure('name', strjoin(figureTitle)) nplots = ceil(sqrt(numel(labels))); for i = 1 : size(solutionData,2) -subplot(nplots, nplots, i) -plot(solutionTime, solutionData(:, i)); hold on -plot(experimentalTime, experimentalData(:, i), 'r') -xlim([0 experimentalTime(end)]) -title(strrep(labels(i), "_", " ")) -figureXLabels(numel(labels), nplots, i, "Time") + subplot(nplots, nplots, i) + plot(solutionTime, solutionData(:, i)); hold on + plot(experimentalTime, experimentalData(:, i), 'r') + xlim([0 experimentalTime(end)]) + title(strrep(labels(i), "_", " ")) + figureXLabels(numel(labels), nplots, i, "Time") end end function plotResultsWithOutComparison(solutionData, solutionTime, ... @@ -115,11 +109,11 @@ function plotResultsWithOutComparison(solutionData, solutionTime, ... figure('name', strjoin(figureTitle)) for i = 1 : size(solutionData,2) -subplot(numel(labels), 1, i) -plot(solutionTime, solutionData(:, i)); hold on -xlim([0 solutionTime(end)]) -title(labels(i)) -figureXLabels(numel(labels), 1, i, "Time") + subplot(numel(labels), 1, i) + plot(solutionTime, solutionData(:, i)); hold on + xlim([0 solutionTime(end)]) + title(labels(i)) + figureXLabels(numel(labels), 1, i, "Time") end end function figureXLabels(numTotalPlots, numColumnPlots, plotIndex, xLabel) diff --git a/src/TreatmentOptimization/saveTreatmentOptimizationResults.m b/src/TreatmentOptimization/saveTreatmentOptimizationResults.m index 51ee52aae..045e86e6b 100644 --- a/src/TreatmentOptimization/saveTreatmentOptimizationResults.m +++ b/src/TreatmentOptimization/saveTreatmentOptimizationResults.m @@ -45,10 +45,12 @@ function saveTreatmentOptimizationResults(solution, inputs, values) for i = 1 : length(inputs.coordinateNames) stateLabels{end + 1} = strcat(inputs.coordinateNames{i}, '_dudt'); end -writeToSto(stateLabels, values.time, [values.positions ... - values.velocities values.accelerations], ... +[time, data] = splineToEvenlySpaced(values.time, [values.positions ... + values.velocities values.accelerations]); +writeToSto(stateLabels, time, data, ... fullfile(inputs.resultsDirectory, strcat(inputs.trialName, "_states.sto"))); -writeToSto(inputs.statesCoordinateNames, values.time, values.controlJerks, ... +[time, jerks] = splineToEvenlySpaced(values.time, values.controlJerks); +writeToSto(inputs.statesCoordinateNames, time, jerks, ... fullfile(inputs.resultsDirectory, strcat(inputs.trialName, "_jerks.sto"))); if strcmp(inputs.controllerType, 'synergy') controlLabels = {}; @@ -59,7 +61,9 @@ function saveTreatmentOptimizationResults(solution, inputs, values) "_", num2str(j))); end end - writeToSto(controlLabels, values.time, values.controlSynergyActivations, ... + [time, controls] = splineToEvenlySpaced( ... + values.time, values.controlSynergyActivations); + writeToSto(controlLabels, time, controls, ... fullfile(inputs.resultsDirectory, ... strcat(inputs.trialName, "_synergyCommands.sto"))); end @@ -68,7 +72,9 @@ function saveTreatmentOptimizationResults(solution, inputs, values) for i = 1 : length(inputs.torqueControllerCoordinateNames) controlLabels{end + 1} = inputs.torqueControllerCoordinateNames{i}; end - writeToSto(controlLabels, values.time, values.torqueControls, ... + [time, controls] = splineToEvenlySpaced( ... + values.time, values.torqueControls); + writeToSto(controlLabels, time, controls, ... fullfile(inputs.resultsDirectory, ... strcat(inputs.trialName, "_torqueControls.sto"))); end @@ -76,9 +82,11 @@ function saveTreatmentOptimizationResults(solution, inputs, values) writeToSto(inputs.muscleLabels, linspace(1, inputs.numSynergies, ... inputs.numSynergies), [values.synergyWeights], ... fullfile(inputs.resultsDirectory, "synergyWeights.sto")); - writeToSto(inputs.muscleLabels, values.time, ... - solution.muscleActivations, ... - fullfile(inputs.resultsDirectory, strcat(inputs.trialName, "_combinedActivations.sto"))); + [time, activations] = splineToEvenlySpaced(values.time, ... + solution.muscleActivations); + writeToSto(inputs.muscleLabels, time, ... + activations, fullfile(inputs.resultsDirectory, ... + strcat(inputs.trialName, "_combinedActivations.sto"))); end delete(inputs.mexModel); end @@ -87,7 +95,8 @@ function saveInverseKinematicsResults(inputs, values, outputDirectory) if ~exist(fullfile(outputDirectory, "IKData"), "dir") mkdir(fullfile(outputDirectory, "IKData")) end -writeToSto(inputs.coordinateNames, values.time, values.positions, ... +[time, positions] = splineToEvenlySpaced(values.time, values.positions); +writeToSto(inputs.coordinateNames, time, positions, ... fullfile(outputDirectory, "IKData", strcat(inputs.trialName, ".sto"))); end @@ -95,8 +104,10 @@ function saveInverseDynamicsResults(solution, inputs, values, outputDirectory) if ~exist(fullfile(outputDirectory, "IDData"), "dir") mkdir(fullfile(outputDirectory, "IDData")) end -writeToSto(inputs.inverseDynamicsMomentLabels, values.time, ... - solution.inverseDynamicsMoments, fullfile(outputDirectory, "IDData", ... +[time, moments] = splineToEvenlySpaced(values.time, ... + solution.inverseDynamicsMoments); +writeToSto(inputs.inverseDynamicsMomentLabels, time, ... + moments, fullfile(outputDirectory, "IDData", ... strcat(inputs.trialName, ".sto"))); end diff --git a/src/TreatmentOptimization/splineToEvenlySpaced.m b/src/TreatmentOptimization/splineToEvenlySpaced.m new file mode 100644 index 000000000..a512ecd6f --- /dev/null +++ b/src/TreatmentOptimization/splineToEvenlySpaced.m @@ -0,0 +1,37 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% calcStateDerivatives() requires evenly spaced points for the b-spline +% derivatives. This function should be used to make the points evenly +% spaced via splining +% +% (struct, struct) -> (None) +% Splines results to make them evenly spaced + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Claire V. Hammond % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function [newTime, evenlySpacedData] = splineToEvenlySpaced(time, data) +newTime = linspace(time(1), time(end), length(time)); +spline = spaps(time, data', 1e-7); +evenlySpacedData = fnval(spline, newTime)'; +end + From 4fbbff9743885a537102ed34bbd7c38b32026005 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 8 Oct 2023 15:49:06 -0500 Subject: [PATCH 169/365] switch to bspline experimental impl --- .../makeStateDerivatives.m | 48 +++++-------------- .../splineToEvenlySpaced.m | 2 +- 2 files changed, 14 insertions(+), 36 deletions(-) diff --git a/src/TreatmentOptimization/makeStateDerivatives.m b/src/TreatmentOptimization/makeStateDerivatives.m index bf1bbaa5d..6053ac002 100644 --- a/src/TreatmentOptimization/makeStateDerivatives.m +++ b/src/TreatmentOptimization/makeStateDerivatives.m @@ -29,39 +29,17 @@ % ----------------------------------------------------------------------- % function inputs = makeStateDerivatives(inputs, params) -jointAnglesSpline = spaps(inputs.experimentalTime, ... - inputs.experimentalJointAngles', eps, [], 3); -inputs.experimentalJointVelocities = fnval(fnder(jointAnglesSpline, 1), ... - inputs.experimentalTime)'; -if length(inputs.statesCoordinateNames) == 1 - inputs.experimentalJointVelocities = inputs.experimentalJointVelocities'; -end -jointVelocitiesSpline = spaps(inputs.experimentalTime, ... - inputs.experimentalJointVelocities', eps, [], 3); -inputs.experimentalJointAccelerations = fnval( ... - fnder(jointVelocitiesSpline, 1), inputs.experimentalTime)'; -if length(inputs.statesCoordinateNames) == 1 - inputs.experimentalJointAccelerations = inputs.experimentalJointAccelerations'; -end -jointAccelerationsSpline = spaps(inputs.experimentalTime, ... - inputs.experimentalJointAccelerations', eps, [], 3); -inputs.experimentalJointJerks = fnval(fnder(jointAccelerationsSpline, ... - 1), inputs.experimentalTime)'; -if length(inputs.statesCoordinateNames) == 1 - inputs.experimentalJointJerks = inputs.experimentalJointJerks'; -end - -% points = length(inputs.experimentalTime); -% interval = inputs.experimentalTime(2) - inputs.experimentalTime(1); -% cutoffFrequency = ... -% valueOrAlternate(params, "experimentalBSplineCutoffFrequency", 6); -% numNodes = splFitWithCutoff(inputs.experimentalTime', ... -% inputs.experimentalJointAngles', cutoffFrequency, 5); -% [N, Np, Npp] = BSplineMatrices(5, numNodes, points, interval); -% Nodes = N\inputs.experimentalJointAngles; -% inputs.experimentalJointVelocities = Np * Nodes; -% inputs.experimentalJointAccelerations = Npp * Nodes; -% inputs.experimentalJointJerks = calcBSplineDerivative( ... -% inputs.experimentalTime, inputs.experimentalJointAccelerations, ... -% 2, numNodes); +points = length(inputs.experimentalTime); +interval = inputs.experimentalTime(2) - inputs.experimentalTime(1); +cutoffFrequency = ... + valueOrAlternate(params, "experimentalBSplineCutoffFrequency", 6); +numNodes = splFitWithCutoff(inputs.experimentalTime', ... + inputs.experimentalJointAngles', cutoffFrequency, 5); +[N, Np, Npp] = BSplineMatrices(5, numNodes, points, interval); +Nodes = N\inputs.experimentalJointAngles; +inputs.experimentalJointVelocities = Np * Nodes; +inputs.experimentalJointAccelerations = Npp * Nodes; +inputs.experimentalJointJerks = calcBSplineDerivative( ... + inputs.experimentalTime, inputs.experimentalJointAccelerations, ... + 2, numNodes); end diff --git a/src/TreatmentOptimization/splineToEvenlySpaced.m b/src/TreatmentOptimization/splineToEvenlySpaced.m index a512ecd6f..433d0af8e 100644 --- a/src/TreatmentOptimization/splineToEvenlySpaced.m +++ b/src/TreatmentOptimization/splineToEvenlySpaced.m @@ -1,6 +1,6 @@ % This function is part of the NMSM Pipeline, see file for full license. % -% calcStateDerivatives() requires evenly spaced points for the b-spline +% makeStateDerivatives() requires evenly spaced points for the b-spline % derivatives. This function should be used to make the points evenly % spaced via splining % From fd1a7bb53c3a4b404899724441716f1c8dd7db9d Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 8 Oct 2023 15:56:40 -0500 Subject: [PATCH 170/365] add experimental_bspline_cutoff xml term --- src/core/parse/parseTreatmentOptimizationParams.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/parse/parseTreatmentOptimizationParams.m b/src/core/parse/parseTreatmentOptimizationParams.m index 995d90fa2..e45215032 100644 --- a/src/core/parse/parseTreatmentOptimizationParams.m +++ b/src/core/parse/parseTreatmentOptimizationParams.m @@ -25,5 +25,6 @@ % ----------------------------------------------------------------------- % function params = parseTreatmentOptimizationParams(tree) -params = struct(); +params.experimentalBSplineCutoffFrequency = ... + parseDoubleOrAlternate(tree, "experimental_bspline_cutoff_frequency", 6); end From 9a375ab4e7b2a7138d7aedec94801b8d9ef67da4 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 8 Oct 2023 16:29:11 -0500 Subject: [PATCH 171/365] updated to kinetic consistency --- ...hConstraint.m => calcKineticPathConstraint.m} | 2 +- .../generateConstraintTermStruct.m | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) rename src/TreatmentOptimization/PathTerms/{calcStateControlPathConstraint.m => calcKineticPathConstraint.m} (97%) diff --git a/src/TreatmentOptimization/PathTerms/calcStateControlPathConstraint.m b/src/TreatmentOptimization/PathTerms/calcKineticPathConstraint.m similarity index 97% rename from src/TreatmentOptimization/PathTerms/calcStateControlPathConstraint.m rename to src/TreatmentOptimization/PathTerms/calcKineticPathConstraint.m index 8734fa7be..f8bc042b4 100644 --- a/src/TreatmentOptimization/PathTerms/calcStateControlPathConstraint.m +++ b/src/TreatmentOptimization/PathTerms/calcKineticPathConstraint.m @@ -29,7 +29,7 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function pathTerm = calcStateControlPathConstraint(inputs, ... +function pathTerm = calcKineticPathConstraint(inputs, ... modeledValues, torqueControls, loadName) inverseDynamicsIndex = find(strcmp(convertCharsToStrings(inputs.inverseDynamicsMomentLabels), ... loadName)); diff --git a/src/TreatmentOptimization/generateConstraintTermStruct.m b/src/TreatmentOptimization/generateConstraintTermStruct.m index e58b9a1b2..eea94aa38 100644 --- a/src/TreatmentOptimization/generateConstraintTermStruct.m +++ b/src/TreatmentOptimization/generateConstraintTermStruct.m @@ -54,7 +54,7 @@ allowedTypes = [ ... "root_segment_residual_load", ... "torque_model_moment_consistency", ... - "state_control_consistency", ... + "kinetic_consistency", ... ]; end if strcmp(constraintTermType, "terminal") @@ -71,7 +71,7 @@ allowedTypes = [ ... "root_segment_residual_load", ... "torque_model_moment_consistency", ... - "state_control_consistency", ... + "kinetic_consistency", ... ]; end if strcmp(constraintTermType, "terminal") @@ -88,7 +88,7 @@ allowedTypes = [ ... "root_segment_residual_load", ... "torque_model_moment_consistency", ... - "state_control_consistency", ... + "kinetic_consistency", ... "limit_normalized_fiber_length", ... ]; end @@ -114,7 +114,7 @@ allowedTypes = [ ... "root_segment_residual_load", ... "muscle_model_moment_consistency", ... - "state_control_consistency", ... + "kinetic_consistency", ... ]; end if strcmp(constraintTermType, "terminal") @@ -132,7 +132,7 @@ allowedTypes = [ ... "root_segment_residual_load", ... "muscle_model_moment_consistency", ... - "state_control_consistency", ... + "kinetic_consistency", ... ]; end if strcmp(constraintTermType, "terminal") @@ -149,7 +149,7 @@ allowedTypes = [ ... "root_segment_residual_load", ... "muscle_model_moment_consistency", ... - "state_control_consistency", ... + "kinetic_consistency", ... "limit_muscle_activation", ... "limit_normalized_fiber_length", ... "external_control_muscle_moment_consistency", ... @@ -197,8 +197,8 @@ constraintTerm.load ... ); -constraintTermCalculations.state_control_consistency = @(values, modeledValues, auxdata, constraintTerm) ... - calcStateControlPathConstraint( ... +constraintTermCalculations.kinetic_consistency = @(values, modeledValues, auxdata, constraintTerm) ... + calcKineticPathConstraint( ... auxdata, ... modeledValues, ... values.torqueControls, ... From 46372fe6aba6bbb2ace00e3d842f284d390792cd Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 8 Oct 2023 20:36:42 -0500 Subject: [PATCH 172/365] fix mex and add instructions --- src/core/mex/howToMakeMex.txt | 21 ++++++ src/core/mex/inverseDynamicsMexWindows.cpp | 64 ++++++------------ src/core/mex/inverseDynamicsMexWindows.mexw64 | Bin 40960 -> 34304 bytes 3 files changed, 41 insertions(+), 44 deletions(-) create mode 100644 src/core/mex/howToMakeMex.txt diff --git a/src/core/mex/howToMakeMex.txt b/src/core/mex/howToMakeMex.txt new file mode 100644 index 000000000..689ea3eef --- /dev/null +++ b/src/core/mex/howToMakeMex.txt @@ -0,0 +1,21 @@ +mex CXXFLAGS="/$CXXFLAGS -fopenmp" LDFLAGS="/$LDFLAGS -fopenmp"... + COMPFLAGS="/openmp $COMPFLAGS"... + inverseDynamicsMexWindows.cpp... + -L'C:\OpenSim 4.4\sdk\lib'... + -L'C:\OpenSim 4.4\sdk\Simbody\lib'... + -L'C:\OpenSim 4.4\sdk\spdlog\lib\spdlog'... + -L'C:\OpenSim 4.4\sdk\spdlog\include'... + -L'C:\OpenSim 4.4\sdk\spdlog\include\spdlog'... + -losimCommon -losimSimulation... + -losimAnalyses -losimActuators -losimTools... + -lSimTKcommon -lSimTKmath... + -lSimTKsimbody -lliblapack... + -llibblas -losimJavaJNI -losimLepton... + -lspdlog... + -I'C:\OpenSim 4.4\sdk\include'... + -I'C:\OpenSim 4.4\sdk\include\OpenSim'... + -I'C:\OpenSim 4.4\sdk\Simbody\include'... + -I'C:\OpenSim 4.4\sdk\include\OpenSim\Simulation'... + -I'C:\OpenSim 4.4\sdk\spdlog\include'... + -DWIN32 -D_WINDOWS -DNDEBUG... + ; \ No newline at end of file diff --git a/src/core/mex/inverseDynamicsMexWindows.cpp b/src/core/mex/inverseDynamicsMexWindows.cpp index d4742ff21..3f81ef92b 100644 --- a/src/core/mex/inverseDynamicsMexWindows.cpp +++ b/src/core/mex/inverseDynamicsMexWindows.cpp @@ -43,16 +43,14 @@ using namespace std; //______________________________________________________________________________ -static Model *osimModel[numThreads]; -static State *osimState[numThreads]; -static InverseDynamicsSolver *idSolver[numThreads]; +static Model *osimModel; +static State *osimState; +static InverseDynamicsSolver *idSolver; static bool modelIsLoaded = false; void ClearMemory(void){ - for (int i = 0; i < numThreads; i++){ - delete osimModel[i]; - delete idSolver[i]; - } + delete osimModel; + delete idSolver; modelIsLoaded = false; mexPrintf("Cleared memory from inverseDynamics mex file.\n"); } @@ -82,11 +80,9 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]){ std::streambuf* oldCoutStreamBuf = std::cout.rdbuf(); std::ostringstream strCout; std::cout.rdbuf(strCout.rdbuf()); - for (int i = 0; i < numThreads; i++){ - osimModel[i] = new Model(modelName); - osimState[i] = &osimModel[i]->initSystem(); - idSolver[i] = new InverseDynamicsSolver(*osimModel[i]); - } + osimModel = new Model(modelName); + osimState = &osimModel->initSystem(); + idSolver = new InverseDynamicsSolver(*osimModel); std::cout.rdbuf(oldCoutStreamBuf); modelIsLoaded = true; } @@ -97,7 +93,7 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]){ const int numPts = mxGetM(prhs[0]); const int numQs = mxGetN(prhs[1]); const int numControls = mxGetN(prhs[5]); - const int numCoords = osimState[0]->getNQ(); + const int numCoords = osimState[0].getNQ(); double *time = mxGetPr(prhs[0]); vector> q = mexArrayToVector(prhs[1]); @@ -106,7 +102,6 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]){ vector> u = mexArrayToVector(prhs[5]); const mxArray *cellElementPtr; - char* c_array; mwIndex k; mwSize numLabels, buflen; numLabels = mxGetNumberOfElements(prhs[4]); @@ -115,33 +110,24 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]){ plhs[0] = mxCreateDoubleMatrix(numPts,numCoords,mxREAL); double *idLoads = mxGetPr(plhs[0]); - int numMarkers = osimModel[0]->getNumMarkers(); - mwSize dimensions[3]; - dimensions[0] = numPts; - dimensions[1] = numMarkers; - dimensions[2] = 3; - plhs[1] = mxCreateNumericArray(3, dimensions, mxDOUBLE_CLASS, mxREAL); - double* MarkerGlobalPos = mxGetPr(plhs[1]); - - #pragma omp parallel for num_threads(numThreads) for (int i = 0; i < numPts; ++i){ - int thread_id = omp_get_thread_num(); - osimState[thread_id]->setTime(time[i]); + osimState->setTime(time[i]); for (int k = 0; k < numLabels; k++){ cellElementPtr = mxGetCell(prhs[4], k); buflen = mxGetN(cellElementPtr)*sizeof(mxChar) + 1; + char* c_array; c_array = (char *) mxCalloc(buflen, sizeof(char)); status = mxGetString(cellElementPtr, c_array, buflen); - osimModel[thread_id]->getCoordinateSet().get(c_array).setValue(*osimState[thread_id], q[i][k], false); - osimModel[thread_id]->getCoordinateSet().get(c_array).setSpeedValue(*osimState[thread_id], qp[i][k]); + osimModel->getCoordinateSet().get(c_array).setValue(*osimState, q[i][k], false); + osimModel->getCoordinateSet().get(c_array).setSpeedValue(*osimState, qp[i][k]); mxFree(c_array); } - osimModel[thread_id]->realizeVelocity(*osimState[thread_id]); + osimModel->realizeVelocity(*osimState); Vector AccelsVec(numCoords, 0.0); for (int j = 0; j < numCoords; j++){ - double StateQ = osimState[thread_id]->getQ().get(j); + double StateQ = osimState->getQ().get(j); for (int k = 0; k < numQs; k++){ if (abs(q[i][k] - StateQ) <= 1e-6){ AccelsVec.set(j, qpp[i][k]); @@ -153,26 +139,16 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]){ for (int j = 0; j < numControls; j++){ newControls.set(j, u[i][j]); } - osimModel[thread_id]->setControls(*osimState[thread_id], newControls); - osimModel[thread_id]->markControlsAsValid(*osimState[thread_id]); - osimModel[thread_id]->realizeDynamics(*osimState[thread_id]); + osimModel->setControls(*osimState, newControls); + osimModel->markControlsAsValid(*osimState); + osimModel->realizeDynamics(*osimState); Vector IDLoadsVec; - IDLoadsVec = idSolver[thread_id]->solve(*osimState[thread_id], AccelsVec); + IDLoadsVec = idSolver->solve(*osimState, AccelsVec); for (int j = 0; j < numCoords; j++){ idLoads[i + numPts * j] = IDLoadsVec[j]; } - - Vec3 tempMarkerGlobalPos; - for(int j = 0; j < numMarkers; j++){ - Marker& tempRefMarker = osimModel[thread_id]->getMarkerSet().get(j); - const PhysicalFrame& tempRefMarkerParentBody = tempRefMarker.getParentFrame(); - Vec3 tempMarkerLocalPos = tempRefMarker.get_location(); - osimModel[thread_id]->getSimbodyEngine().getPosition(*osimState[thread_id], tempRefMarkerParentBody, tempMarkerLocalPos, tempMarkerGlobalPos); - for (int k = 0; k < 3; k++){ - MarkerGlobalPos[numPts * (j + k * numMarkers) + i] = tempMarkerGlobalPos(k); - } - } + } q.clear(); qp.clear(); diff --git a/src/core/mex/inverseDynamicsMexWindows.mexw64 b/src/core/mex/inverseDynamicsMexWindows.mexw64 index 7fe3199d0947b11447b4a4812fd2b1663fc213ff..9937d94138558a5ea4317dbc4ff08706e24f935a 100644 GIT binary patch literal 34304 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 N52o&-BL6R4{}+;PM5F)! literal 40960 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 MLu17J|C`tU0=e;K!~g&Q From cb901f04c5f738604342a48fe9c771160a3e0aae Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 8 Oct 2023 20:37:04 -0500 Subject: [PATCH 173/365] fix grf applied loads --- .../calcTorqueBasedModeledValues.m | 11 ++++++--- .../addContactSurfaceActuators.m | 24 +++++++++---------- .../disableModelMuscles.m | 3 +-- src/TreatmentOptimization/modifyModelForces.m | 2 +- 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m b/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m index c882b7ce3..0faeb3a79 100644 --- a/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m +++ b/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m @@ -31,8 +31,7 @@ % ----------------------------------------------------------------------- % function modeledValues = calcTorqueBasedModeledValues(values, inputs) -appliedLoads = [zeros(length(values.time), ... - inputs.model.getForceSet().getMuscles().getSize())]; +appliedLoads = []; if ~isempty(inputs.contactSurfaces) clear pointKinematics [springPositions, springVelocities] = getSpringLocations( ... @@ -44,7 +43,13 @@ groundReactionsBody = tranferGroundReactionMoments( ... modeledValues.bodyLocations, groundReactions, inputs); modeledValues.groundReactionsLab = calcGroundReactionsLab(groundReactions); - appliedLoads = [appliedLoads groundReactionsBody]; + numCoordinateLoads = inputs.model.getForceSet().getSize() - ... + inputs.model.getForceSet().getMuscles().getSize() - ... + (12 * length(inputs.contactSurfaces)); + coordinateLoads = zeros(length(values.time), numCoordinateLoads); + appliedLoads = [zeros(length(values.time), ... + inputs.model.getForceSet().getMuscles().getSize()) ... + appliedLoads coordinateLoads groundReactionsBody]; end modeledValues.inverseDynamicsMoments = inverseDynamics(values.time, ... values.positions, values.velocities, ... diff --git a/src/TreatmentOptimization/addContactSurfaceActuators.m b/src/TreatmentOptimization/addContactSurfaceActuators.m index fab90bec1..17b9df83e 100644 --- a/src/TreatmentOptimization/addContactSurfaceActuators.m +++ b/src/TreatmentOptimization/addContactSurfaceActuators.m @@ -32,18 +32,18 @@ function model = addContactSurfaceActuators(inputs, model) import org.opensim.modeling.Vec3 for i = 1:length(inputs.contactSurfaces) -addPointActuator(model, string(inputs.contactSurfaces{i}.parentBodyName), Vec3(1, 0, 0)); -addPointActuator(model, string(inputs.contactSurfaces{i}.parentBodyName), Vec3(0, 1, 0)); -addPointActuator(model, string(inputs.contactSurfaces{i}.parentBodyName), Vec3(0, 0, 1)); -addPointActuator(model, string(inputs.contactSurfaces{i}.childBodyName), Vec3(1, 0, 0)); -addPointActuator(model, string(inputs.contactSurfaces{i}.childBodyName), Vec3(0, 1, 0)); -addPointActuator(model, string(inputs.contactSurfaces{i}.childBodyName), Vec3(0, 0, 1)); -addTorqueActuator(model, string(inputs.contactSurfaces{i}.parentBodyName), Vec3(1, 0, 0)); -addTorqueActuator(model, string(inputs.contactSurfaces{i}.parentBodyName), Vec3(0, 1, 0)); -addTorqueActuator(model, string(inputs.contactSurfaces{i}.parentBodyName), Vec3(0, 0, 1)); -addTorqueActuator(model, string(inputs.contactSurfaces{i}.childBodyName), Vec3(1, 0, 0)); -addTorqueActuator(model, string(inputs.contactSurfaces{i}.childBodyName), Vec3(0, 1, 0)); -addTorqueActuator(model, string(inputs.contactSurfaces{i}.childBodyName), Vec3(0, 0, 1)); + addPointActuator(model, string(inputs.contactSurfaces{i}.parentBodyName), Vec3(1, 0, 0)); + addPointActuator(model, string(inputs.contactSurfaces{i}.parentBodyName), Vec3(0, 1, 0)); + addPointActuator(model, string(inputs.contactSurfaces{i}.parentBodyName), Vec3(0, 0, 1)); + addPointActuator(model, string(inputs.contactSurfaces{i}.childBodyName), Vec3(1, 0, 0)); + addPointActuator(model, string(inputs.contactSurfaces{i}.childBodyName), Vec3(0, 1, 0)); + addPointActuator(model, string(inputs.contactSurfaces{i}.childBodyName), Vec3(0, 0, 1)); + addTorqueActuator(model, string(inputs.contactSurfaces{i}.parentBodyName), Vec3(1, 0, 0)); + addTorqueActuator(model, string(inputs.contactSurfaces{i}.parentBodyName), Vec3(0, 1, 0)); + addTorqueActuator(model, string(inputs.contactSurfaces{i}.parentBodyName), Vec3(0, 0, 1)); + addTorqueActuator(model, string(inputs.contactSurfaces{i}.childBodyName), Vec3(1, 0, 0)); + addTorqueActuator(model, string(inputs.contactSurfaces{i}.childBodyName), Vec3(0, 1, 0)); + addTorqueActuator(model, string(inputs.contactSurfaces{i}.childBodyName), Vec3(0, 0, 1)); end end function addPointActuator(model, bodyName, direction) diff --git a/src/TreatmentOptimization/disableModelMuscles.m b/src/TreatmentOptimization/disableModelMuscles.m index 7d876a1bf..74916e911 100644 --- a/src/TreatmentOptimization/disableModelMuscles.m +++ b/src/TreatmentOptimization/disableModelMuscles.m @@ -27,8 +27,7 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function [model, inputs] = disableModelMuscles(inputs, model) -import org.opensim.modeling.Model +function model = disableModelMuscles(model) for i = 0:model.getForceSet().getMuscles().getSize()-1 if model.getForceSet().getMuscles().get(i).get_appliesForce() model.getForceSet().getMuscles().get(i).set_appliesForce(0); diff --git a/src/TreatmentOptimization/modifyModelForces.m b/src/TreatmentOptimization/modifyModelForces.m index 65f16a70a..c333e73c9 100644 --- a/src/TreatmentOptimization/modifyModelForces.m +++ b/src/TreatmentOptimization/modifyModelForces.m @@ -30,7 +30,7 @@ % ----------------------------------------------------------------------- % function inputs = modifyModelForces(inputs) -[model, inputs] = disableModelMuscles(inputs, inputs.model); +model = disableModelMuscles(inputs.model); model = addContactSurfaceActuators(inputs, model); inputs.mexModel = strcat(strrep(inputs.modelFileName,'.osim',''), '_inactiveMuscles.osim'); model.print(inputs.mexModel); From 483223c8e13a456084607f8e4ff7123e33d675b5 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 8 Oct 2023 20:37:27 -0500 Subject: [PATCH 174/365] added generalized to names --- src/TreatmentOptimization/generateCostTermStruct.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/TreatmentOptimization/generateCostTermStruct.m b/src/TreatmentOptimization/generateCostTermStruct.m index 5fc1c892c..da017be7a 100644 --- a/src/TreatmentOptimization/generateCostTermStruct.m +++ b/src/TreatmentOptimization/generateCostTermStruct.m @@ -120,7 +120,7 @@ function costTermCalculations = getCostTermCalculations(costTermType) -costTermCalculations.coordinate_tracking = @(values, modeledValues, auxdata, costTerm) ... +costTermCalculations.generalized_coordinate_tracking = @(values, modeledValues, auxdata, costTerm) ... calcTrackingCoordinateIntegrand( ... auxdata, ... values.time, ... @@ -128,7 +128,7 @@ costTerm.coordinate ... ); -costTermCalculations.speed_tracking = @(values, modeledValues, auxdata, costTerm) ... +costTermCalculations.generalized_speed_tracking = @(values, modeledValues, auxdata, costTerm) ... calcTrackingSpeedIntegrand( ... auxdata, ... values.time, ... From 3b0dcb62171c568c5bfcc7d0ff7fd6ad3c55a0c7 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 9 Oct 2023 15:12:25 -0500 Subject: [PATCH 175/365] Tracked markers in modeled values --- .../calcTorqueBasedModeledValues.m | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m b/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m index 0faeb3a79..869ea525c 100644 --- a/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m +++ b/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m @@ -31,6 +31,18 @@ % ----------------------------------------------------------------------- % function modeledValues = calcTorqueBasedModeledValues(values, inputs) +modeledValues = struct(); +[appliedLoads, modeledValues] = setupAppliedLoads(values, inputs, ... + modeledValues); +modeledValues.markerPositions = calcTrackedMarkerPositions(values, inputs); +modeledValues.inverseDynamicsMoments = inverseDynamics(values.time, ... + values.positions, values.velocities, ... + values.accelerations, inputs.coordinateNames, appliedLoads, ... + inputs.mexModel); +end + +function [appliedLoads, modeledValues] = setupAppliedLoads(values, ... + inputs, modeledValues) appliedLoads = []; if ~isempty(inputs.contactSurfaces) clear pointKinematics @@ -51,10 +63,6 @@ inputs.model.getForceSet().getMuscles().getSize()) ... appliedLoads coordinateLoads groundReactionsBody]; end -modeledValues.inverseDynamicsMoments = inverseDynamics(values.time, ... - values.positions, values.velocities, ... - values.accelerations, inputs.coordinateNames, appliedLoads, ... - inputs.mexModel); end function [springPositions, springVelocities] = getSpringLocations(time, .... @@ -121,3 +129,15 @@ groundReactions.parentMoments{i} + groundReactions.childMoments{i}; end end + +function markerPositions = calcTrackedMarkerPositions(values, inputs) +markerPositions = []; +if isfield(inputs, 'trackedMarkerNames') ... + && ~isempty(inputs.trackedMarkerNames) + clear pointKinematics + markerPositions = pointKinematics(values.time, values.positions, ... + values.velocities, inputs.trackedMarkerLocationsOnBodies, ... + inputs.trackedMarkerBodyIndices, inputs.mexModel, ... + inputs.coordinateNames); +end +end From 921e38c4c143bbcd1208c2368304258a5a7eee41 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 9 Oct 2023 16:31:12 -0500 Subject: [PATCH 176/365] evaluateSurrogate in separate file --- .../createSurrogateModel.m | 47 +---------- .../evaluateSurrogate.m | 79 +++++++++++++++++++ 2 files changed, 80 insertions(+), 46 deletions(-) create mode 100644 src/SurrogateModelCreation/evaluateSurrogate.m diff --git a/src/SurrogateModelCreation/createSurrogateModel.m b/src/SurrogateModelCreation/createSurrogateModel.m index 6d12ee3c4..ba501acdf 100644 --- a/src/SurrogateModelCreation/createSurrogateModel.m +++ b/src/SurrogateModelCreation/createSurrogateModel.m @@ -36,7 +36,7 @@ % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % % implied. See the License for the specific language governing % % permissions and limitations under the License. % -% ----------------------------------------------------------------------- +% ----------------------------------------------------------------------- % function surrogateMuscles = createSurrogateModel(jointAngles, ... muscleTendonLengths, momentArms, polynomialDegree) @@ -56,48 +56,3 @@ polynomialExpressionMomentArms, coefficients); end end - -function [muscleTendonLength, muscleTendonVelocity, momentArms] = ... - evaluateSurrogate(jointAngles, jointVelocities, ... - polynomialExpressionMuscleTendonLength, ... - polynomialExpressionMuscleTendonVelocity, ... - polynomialExpressionMomentArms, coefficients) - -% Values are set to match symbolic expressions in polynomials -for i = 1 : size(jointAngles, 2) - eval(['theta' num2str(i) ' = jointAngles(:,' num2str(i) ');']); - eval(['thetaDot' num2str(i) ' = jointVelocities(:,' num2str(i) ');']); -end -muscleTendonLengthMatrix = zeros(size(jointAngles, 1), ... - size(polynomialExpressionMomentArms, 2)); -muscleTendonVelocityMatrix = zeros(size(jointVelocities, 1), ... - size(polynomialExpressionMomentArms, 2)); -momentArmsMatrix = zeros(size(jointAngles, 1), size(jointAngles, 2), ... - size(polynomialExpressionMomentArms, 2)); -for j = 1 : size(polynomialExpressionMomentArms, 2) - muscleTendonLengthMatrix(:, j) = ... - eval(polynomialExpressionMuscleTendonLength(1, j)) .* ... - ones(size(jointAngles, 1), 1); - muscleTendonVelocityMatrix(:, j) = ... - eval(polynomialExpressionMuscleTendonVelocity(1, j)) .* ... - ones(size(jointAngles, 1), 1); - for k = 1 : size(jointAngles, 2) - momentArmsMatrix(:, k, j) = ... - eval(polynomialExpressionMomentArms(k, j)) .* ... - ones(size(jointAngles, 1), 1); - end -end -fullMatrix = [muscleTendonLengthMatrix; muscleTendonVelocityMatrix; ... - reshape(momentArmsMatrix, [], ... - size(polynomialExpressionMomentArms, 2))]; - -modeledValues = fullMatrix * coefficients; -muscleTendonLength = modeledValues(1 : size(jointAngles, 1)); -muscleTendonVelocity = modeledValues(1 + size(jointAngles, 1) : ... - size(jointAngles, 1) * 2); -for i = 1 : size(jointAngles, 2) - momentArms(:, i) = modeledValues(1 + size(jointAngles, 1) * (1 + i) ... - : size(jointAngles, 1) * (2 + i)); -end -end - diff --git a/src/SurrogateModelCreation/evaluateSurrogate.m b/src/SurrogateModelCreation/evaluateSurrogate.m new file mode 100644 index 000000000..22bd61e3d --- /dev/null +++ b/src/SurrogateModelCreation/evaluateSurrogate.m @@ -0,0 +1,79 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function evaluates a surrogate model using polynomials and +% coefficents. This function is only intended to be used as a saved +% function handle, with one handle saved for each surrogate muscle. The +% polynomials and coefficients belonging to each muscle are stored in the +% handle. +% +% (2D Array of double, 2D Array of double, Array of symbol, +% Array of symbol, 2D Array of symbol, 2D Array of double) -> +% (Array of double, Array of double, 2D Array of double) +% +% Evaluates the surrogate model for a single muscle. + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Spencer Williams % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function [muscleTendonLength, muscleTendonVelocity, momentArms] = ... + evaluateSurrogate(jointAngles, jointVelocities, ... + polynomialExpressionMuscleTendonLength, ... + polynomialExpressionMuscleTendonVelocity, ... + polynomialExpressionMomentArms, coefficients) + +% Values are set to match symbolic expressions in polynomials +for i = 1 : size(jointAngles, 2) + eval(['theta' num2str(i) ' = jointAngles(:,' num2str(i) ');']); + eval(['thetaDot' num2str(i) ' = jointVelocities(:,' num2str(i) ');']); +end +muscleTendonLengthMatrix = zeros(size(jointAngles, 1), ... + size(polynomialExpressionMomentArms, 2)); +muscleTendonVelocityMatrix = zeros(size(jointVelocities, 1), ... + size(polynomialExpressionMomentArms, 2)); +momentArmsMatrix = zeros(size(jointAngles, 1), size(jointAngles, 2), ... + size(polynomialExpressionMomentArms, 2)); +for j = 1 : size(polynomialExpressionMomentArms, 2) + muscleTendonLengthMatrix(:, j) = ... + eval(polynomialExpressionMuscleTendonLength(1, j)) .* ... + ones(size(jointAngles, 1), 1); + muscleTendonVelocityMatrix(:, j) = ... + eval(polynomialExpressionMuscleTendonVelocity(1, j)) .* ... + ones(size(jointAngles, 1), 1); + for k = 1 : size(jointAngles, 2) + momentArmsMatrix(:, k, j) = ... + eval(polynomialExpressionMomentArms(k, j)) .* ... + ones(size(jointAngles, 1), 1); + end +end +fullMatrix = [muscleTendonLengthMatrix; muscleTendonVelocityMatrix; ... + reshape(momentArmsMatrix, [], ... + size(polynomialExpressionMomentArms, 2))]; + +modeledValues = fullMatrix * coefficients; +muscleTendonLength = modeledValues(1 : size(jointAngles, 1)); +muscleTendonVelocity = modeledValues(1 + size(jointAngles, 1) : ... + size(jointAngles, 1) * 2); +for i = 1 : size(jointAngles, 2) + momentArms(:, i) = modeledValues(1 + size(jointAngles, 1) * (1 + i) ... + : size(jointAngles, 1) * (2 + i)); +end +end From 230838b57f6460669f344adf015f7980faf1ddf7 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 9 Oct 2023 20:26:30 -0500 Subject: [PATCH 177/365] Fix 'generalized_coordinate_tracking' term name --- src/TreatmentOptimization/generateCostTermStruct.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/TreatmentOptimization/generateCostTermStruct.m b/src/TreatmentOptimization/generateCostTermStruct.m index da017be7a..1ca43305a 100644 --- a/src/TreatmentOptimization/generateCostTermStruct.m +++ b/src/TreatmentOptimization/generateCostTermStruct.m @@ -45,7 +45,7 @@ switch toolName case "TrackingOptimization" allowedTypes = [ ... - "coordinate_tracking", ... + "generalized_coordinate_tracking", ... "inverse_dynamics_load_tracking", ... "external_force_tracking", ... "external_moment_tracking", ... @@ -54,13 +54,13 @@ ]; case "VerificationOptimization" allowedTypes = [ ... - "coordinate_tracking", ... + "generalized_coordinate_tracking", ... "controller_tracking", ... "joint_jerk_minimization", ... ]; case "DesignOptimization" allowedTypes = [ ... - "coordinate_tracking", ... + "generalized_coordinate_tracking", ... "inverse_dynamics_load_tracking", ... "external_force_tracking", ... "external_moment_tracking", ... From d0ce1bffc03deb2d6d4971392841be3c68d8d3bf Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Mon, 9 Oct 2023 20:26:31 -0500 Subject: [PATCH 178/365] added normalized_by_final_time --- .../calcGoalSingleSupportTimeIntegrand.m | 18 +++++++----- .../calcGoalWalkingSpeedIntegrand.m | 10 +++++-- .../calcKinematicSymmetryIntegrand.m | 10 +++++-- .../calcMaximizingBreakingForceIntegrand.m | 8 +++-- .../calcMaximizingMuscleActivationIntegrand.m | 8 +++-- .../calcMaximizingPropulsiveForceIntegrand.m | 8 +++-- ...calcMaximizingSingleSupportTimeIntegrand.m | 14 +++++---- .../calcMaximizingStepLengthIntegrand.m | 10 +++++-- ...calcMaximizingTrailingLimbAngleIntegrand.m | 8 +++-- .../calcMinimizingBreakingForceIntegrand.m | 8 +++-- .../calcMinimizingExternalTorqueControl.m | 8 +++-- .../calcMinimizingJointJerkIntegrand.m | 10 +++++-- .../calcMinimizingJointPowerIntegrand.m | 8 +++-- ...lcMinimizingMassCenterVelocityXIntegrand.m | 9 ++++-- ...lcMinimizingMassCenterVelocityYIntegrand.m | 9 ++++-- ...lcMinimizingMassCenterVelocityZIntegrand.m | 9 ++++-- .../calcMinimizingMetabolicCost.m | 8 +++-- .../calcMinimizingMuscleActivationIntegrand.m | 8 +++-- .../calcMinimizingPropulsiveForceIntegrand.m | 8 +++-- ...calcMinimizingTrailingLimbAngleIntegrand.m | 10 +++++-- .../calcStepLengthAsymmetryIntegrand.m | 9 ++++-- .../calcStepTimeAsymmetryIntegrand.m | 12 +++++--- .../calcTrackingControllerIntegrand.m | 6 +++- .../calcTrackingCoordinateIntegrand.m | 7 ++++- .../calcTrackingExternalForcesIntegrand.m | 6 +++- .../calcTrackingExternalMomentsIntegrand.m | 6 +++- ...calcTrackingInverseDynamicLoadsIntegrand.m | 6 +++- .../calcTrackingMuscleActivationIntegrand.m | 6 +++- .../calcTrackingSpeedIntegrand.m | 7 ++++- .../generateCostTermStruct.m | 29 +++++++++++++++---- 30 files changed, 210 insertions(+), 73 deletions(-) diff --git a/src/TreatmentOptimization/IntegrandTerms/calcGoalSingleSupportTimeIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcGoalSingleSupportTimeIntegrand.m index 6c7ed2674..47a3ff8d0 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcGoalSingleSupportTimeIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcGoalSingleSupportTimeIntegrand.m @@ -28,25 +28,29 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcGoalSingleSupportTimeIntegrand(values, ... +function cost = calcGoalSingleSupportTimeIntegrand(time, ... modeledValues, params, costTerm) - +normalizeByFinalTime = valueOrAlternate(costTerm, ... + "normalize_by_final_time", false); errorCenter = valueOrAlternate(costTerm, "error_center", 0.38); for i = 1:length(params.contactSurfaces) if params.contactSurfaces{i}.isLeftFoot == costTerm.is_left_foot if i == 1 singleSupportTime = calcSingleSupportTime( ... modeledValues.groundReactionsLab.forces{i + 1}(:, 2), ... - values.time); + time); else singleSupportTime = calcSingleSupportTime( ... modeledValues.groundReactionsLab.forces{i - 1}(:, 2), ... - values.time); + time); end end end -percentSingleSupportTime = singleSupportTime / values.time(end); +percentSingleSupportTime = singleSupportTime / time(end); cost = calcTrackingCostArrayTerm(percentSingleSupportTime * ... - ones(length(values.time), 1), errorCenter * ... - ones(length(values.time), 1), 1); + ones(length(time), 1), errorCenter * ... + ones(length(time), 1), 1); +if normalizeByFinalTime + cost = cost / time(end); +end end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcGoalWalkingSpeedIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcGoalWalkingSpeedIntegrand.m index b46467729..fb4479245 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcGoalWalkingSpeedIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcGoalWalkingSpeedIntegrand.m @@ -29,13 +29,17 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcGoalWalkingSpeedIntegrand(modeledValues,... - params, costTerm) - +function cost = calcGoalWalkingSpeedIntegrand(modeledValues, values, ... + time, params, costTerm) +normalizeByFinalTime = valueOrAlternate(costTerm, ... + "normalize_by_final_time", true); errorCenter = valueOrAlternate(costTerm, "errorCenter", 1.3); strideLength = calcStrideLength(values, modeledValues,... params); speed = strideLength / values.time(end); cost = calcTrackingCostArrayTerm(speed * ones(length(values.time), 1), ... errorCenter * ones(length(values.time), 1), 1); +if normalizeByFinalTime + cost = cost / time(end); +end end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcKinematicSymmetryIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcKinematicSymmetryIntegrand.m index 9da220a19..1af48ba31 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcKinematicSymmetryIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcKinematicSymmetryIntegrand.m @@ -33,9 +33,10 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcKinematicSymmetryIntegrand(statePositions, auxdata, ... - costTerm) - +function cost = calcKinematicSymmetryIntegrand(statePositions, time, ... + auxdata, costTerm) +normalizeByFinalTime = valueOrAlternate(costTerm, ... + "normalize_by_final_time", true); halfWayFrame = size(statePositions, 1)/2; indx1 = find(strcmp(convertCharsToStrings(auxdata.coordinateNames), ... @@ -46,4 +47,7 @@ cost = calcTrackingCostArrayTerm(statePositions(:,indx1), ... [statePositions(1 + round(halfWayFrame):end, indx2); ... statePositions(1:round(halfWayFrame), indx2)], 1); +if normalizeByFinalTime + cost = cost / time(end); +end end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMaximizingBreakingForceIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMaximizingBreakingForceIntegrand.m index 561829a9c..2dc529e92 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMaximizingBreakingForceIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMaximizingBreakingForceIntegrand.m @@ -28,8 +28,9 @@ % ----------------------------------------------------------------------- % function cost = calcMaximizingBreakingForceIntegrand(modeledValues, ... - params, costTerm) - + time, params, costTerm) +normalizeByFinalTime = valueOrAlternate(costTerm, ... + "normalize_by_final_time", false); for i = 1:length(params.contactSurfaces) if params.contactSurfaces{i}.isLeftFoot == costTerm.is_left_foot breakingForce = getBreakingForce( ... @@ -37,4 +38,7 @@ end end cost = calcMaximizingCostArrayTerm(breakingForce); +if normalizeByFinalTime + cost = cost / time(end); +end end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMaximizingMuscleActivationIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMaximizingMuscleActivationIntegrand.m index 8ec37297d..51256cf07 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMaximizingMuscleActivationIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMaximizingMuscleActivationIntegrand.m @@ -27,10 +27,14 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcMaximizingMuscleActivationIntegrand(... +function cost = calcMaximizingMuscleActivationIntegrand(time, ... muscleActivations, params, muscleName) - +normalizeByFinalTime = valueOrAlternate(costTerm, ... + "normalize_by_final_time", true); indx = find(strcmp(convertCharsToStrings(params.muscleNames), ... muscleName)); cost = calcMaximizingCostArrayTerm(muscleActivations(:, indx)); +if normalizeByFinalTime + cost = cost / time(end); +end end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMaximizingPropulsiveForceIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMaximizingPropulsiveForceIntegrand.m index 54a6f8e74..e93ee0879 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMaximizingPropulsiveForceIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMaximizingPropulsiveForceIntegrand.m @@ -28,8 +28,9 @@ % ----------------------------------------------------------------------- % function cost = calcMaximizingPropulsiveForceIntegrand(modeledValues, ... - params, costTerm) - + time, params, costTerm) +normalizeByFinalTime = valueOrAlternate(costTerm, ... + "normalize_by_final_time", false); for i = 1:length(params.contactSurfaces) if params.contactSurfaces{i}.isLeftFoot == costTerm.is_left_foot propulsiveForce = getPropulsiveForce( ... @@ -37,4 +38,7 @@ end end cost = calcMaximizingCostArrayTerm(propulsiveForce); +if normalizeByFinalTime + cost = cost / time(end); +end end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMaximizingSingleSupportTimeIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMaximizingSingleSupportTimeIntegrand.m index 9d8089cff..945af2596 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMaximizingSingleSupportTimeIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMaximizingSingleSupportTimeIntegrand.m @@ -27,22 +27,26 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcMaximizingSingleSupportTimeIntegrand(values, ... +function cost = calcMaximizingSingleSupportTimeIntegrand(time, ... modeledValues, params, costTerm) - +normalizeByFinalTime = valueOrAlternate(costTerm, ... + "normalize_by_final_time", false); for i = 1:length(params.contactSurfaces) if params.contactSurfaces{i}.isLeftFoot == costTerm.is_left_foot if i == 1 singleSupportTime = calcSingleSupportTime( ... modeledValues.groundReactionsLab.forces{i + 1}(:, 2), ... - values.time); + time); else singleSupportTime = calcSingleSupportTime( ... modeledValues.groundReactionsLab.forces{i - 1}(:, 2), ... - values.time); + time); end end end cost = calcMaximizingCostArrayTerm(singleSupportTime * ... - ones(length(values.time), 1)); + ones(length(time), 1)); +if normalizeByFinalTime + cost = cost / time(end); +end end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMaximizingStepLengthIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMaximizingStepLengthIntegrand.m index a3b5ef3d7..a633f90b0 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMaximizingStepLengthIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMaximizingStepLengthIntegrand.m @@ -27,9 +27,10 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcMaximizingStepLengthIntegrand(values, modeledValues,... +function cost = calcMaximizingStepLengthIntegrand(time, modeledValues,... params, costTerm) - +normalizeByFinalTime = valueOrAlternate(costTerm, ... + "normalize_by_final_time", true); for i = 1:length(params.contactSurfaces) if params.contactSurfaces{i}.isLeftFoot == costTerm.is_left_foot if i == 1 @@ -46,5 +47,8 @@ end end cost = calcMaximizingCostArrayTerm(stepLength * ... - ones(length(values.time), 1)); + ones(length(time), 1)); +if normalizeByFinalTime + cost = cost / time(end); +end end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMaximizingTrailingLimbAngleIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMaximizingTrailingLimbAngleIntegrand.m index f4c7ac140..3e2a4308b 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMaximizingTrailingLimbAngleIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMaximizingTrailingLimbAngleIntegrand.m @@ -27,9 +27,10 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcMaximizingTrailingLimbAngleIntegrand(values, ... +function cost = calcMaximizingTrailingLimbAngleIntegrand(values, time, ... modeledValues, params, costTerm) - +normalizeByFinalTime = valueOrAlternate(costTerm, ... + "normalize_by_final_time", true); for i = 1:length(params.contactSurfaces) if params.contactSurfaces{i}.isLeftFoot == costTerm.is_left_foot normalForce = ... @@ -40,4 +41,7 @@ normalForce, params); cost = calcMaximizingCostArrayTerm(trailingLimbAngle * ... ones(length(values.time), 1)); +if normalizeByFinalTime + cost = cost / time(end); +end end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingBreakingForceIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingBreakingForceIntegrand.m index a77b5524c..5a7842312 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingBreakingForceIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingBreakingForceIntegrand.m @@ -28,8 +28,9 @@ % ----------------------------------------------------------------------- % function cost = calcMinimizingBreakingForceIntegrand(modeledValues, ... - params, costTerm) - + time, params, costTerm) +normalizeByFinalTime = valueOrAlternate(costTerm, ... + "normalize_by_final_time", false); for i = 1:length(params.contactSurfaces) if params.contactSurfaces{i}.isLeftFoot == costTerm.is_left_foot breakingForce = getBreakingForce( ... @@ -37,4 +38,7 @@ end end cost = calcMinimizingCostArrayTerm(breakingForce); +if normalizeByFinalTime + cost = cost / time(end); +end end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingExternalTorqueControl.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingExternalTorqueControl.m index 2b5ccb068..5719af4b6 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingExternalTorqueControl.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingExternalTorqueControl.m @@ -29,9 +29,13 @@ % ----------------------------------------------------------------------- % function cost = calcMinimizingExternalTorqueControl(... - externalTorqueControl, params, coordinate) - + externalTorqueControl, time, params, coordinate) +normalizeByFinalTime = valueOrAlternate(costTerm, ... + "normalize_by_final_time", true); indx = find(strcmp(convertCharsToStrings( ... params.externalControlTorqueNames), coordinate)); cost = calcMinimizingCostArrayTerm(externalTorqueControl(:, indx)); +if normalizeByFinalTime + cost = cost / time(end); +end end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointJerkIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointJerkIntegrand.m index ffc2e3537..34a46961f 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointJerkIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointJerkIntegrand.m @@ -27,9 +27,10 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcMinimizingJointJerkIntegrand(jointJerks, inputs, ... - costTerm) - +function cost = calcMinimizingJointJerkIntegrand(jointJerks, time, ... + inputs, costTerm) +normalizeByFinalTime = valueOrAlternate(costTerm, ... + "normalize_by_final_time", true); indx = find(strcmp(convertCharsToStrings(inputs.statesCoordinateNames), ... costTerm.coordinate)); if isempty(indx) @@ -41,4 +42,7 @@ errorCenter = valueOrAlternate(costTerm, "errorCenter", 0); maximumAllowableError = valueOrAlternate(costTerm, "maxAllowableError", 10000); cost = jointJerks(:, indx) / maximumAllowableError; +if normalizeByFinalTime + cost = cost / time(end); +end end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointPowerIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointPowerIntegrand.m index 41d7d49fe..1685c621e 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointPowerIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointPowerIntegrand.m @@ -27,9 +27,10 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcMinimizingJointPowerIntegrand(jointVelocity, ... +function cost = calcMinimizingJointPowerIntegrand(jointVelocity, time, ... jointMoment, params, loadName) - +normalizeByFinalTime = valueOrAlternate(costTerm, ... + "normalize_by_final_time", true); loadName = erase(loadName, '_moment'); loadName = erase(loadName, '_force'); indx = find(strcmp(convertCharsToStrings(params.coordinateNames), ... @@ -43,4 +44,7 @@ end jointPower = jointMoment(:, indx) .* jointVelocity(:, indx); cost = calcMinimizingCostArrayTerm(jointPower); +if normalizeByFinalTime + cost = cost / time(end); +end end diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityXIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityXIntegrand.m index 831fa0b34..2b274ae6f 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityXIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityXIntegrand.m @@ -27,9 +27,14 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcMinimizingMassCenterVelocityXIntegrand(values, params) - +function cost = calcMinimizingMassCenterVelocityXIntegrand(values, ... + time, params) +normalizeByFinalTime = valueOrAlternate(costTerm, ... + "normalize_by_final_time", true); massCenterVelocity = calcMassCenterVelocity(values, params.model, ... params.coordinateNames); cost = calcMinimizingCostArrayTerm(massCenterVelocity(:, 1)); +if normalizeByFinalTime + cost = cost / time(end); +end end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityYIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityYIntegrand.m index 3438a0a38..8b0f3d0af 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityYIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityYIntegrand.m @@ -27,9 +27,14 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcMinimizingMassCenterVelocityYIntegrand(values, params) - +function cost = calcMinimizingMassCenterVelocityYIntegrand(values, ... + time, params) +normalizeByFinalTime = valueOrAlternate(costTerm, ... + "normalize_by_final_time", true); massCenterVelocity = calcMassCenterVelocity(values, params.model, ... params.coordinateNames); cost = calcMinimizingCostArrayTerm(massCenterVelocity(:, 2)); +if normalizeByFinalTime + cost = cost / time(end); +end end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityZIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityZIntegrand.m index 25afe9742..5be0df909 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityZIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityZIntegrand.m @@ -27,9 +27,14 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcMinimizingMassCenterVelocityZIntegrand(values, params) - +function cost = calcMinimizingMassCenterVelocityZIntegrand(values, ... + time, params) +normalizeByFinalTime = valueOrAlternate(costTerm, ... + "normalize_by_final_time", true); massCenterVelocity = calcMassCenterVelocity(values, params.model, ... params.coordinateNames); cost = calcMinimizingCostArrayTerm(massCenterVelocity(:, 3)); +if normalizeByFinalTime + cost = cost / time(end); +end end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMetabolicCost.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMetabolicCost.m index 0e9c64d9b..cc3fa8328 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMetabolicCost.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMetabolicCost.m @@ -26,10 +26,14 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcMinimizingMetabolicCost(values, modeledValues, ... +function cost = calcMinimizingMetabolicCost(values, time, modeledValues, ... params) - +normalizeByFinalTime = valueOrAlternate(costTerm, ... + "normalize_by_final_time", false); metabolicCost = calcMetabolicCost(values.time, ... values.statePositions, modeledValues.muscleActivations, params); cost = calcMinimizingCostArrayTerm(metabolicCost); +if normalizeByFinalTime + cost = cost / time(end); +end end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMuscleActivationIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMuscleActivationIntegrand.m index 0cccf99a1..cd0ef83b2 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMuscleActivationIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMuscleActivationIntegrand.m @@ -27,10 +27,14 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcMinimizingMuscleActivationIntegrand(... +function cost = calcMinimizingMuscleActivationIntegrand(time, ... muscleActivations, params, muscleName) - +normalizeByFinalTime = valueOrAlternate(costTerm, ... + "normalize_by_final_time", true); indx = find(strcmp(convertCharsToStrings(params.muscleNames), ... muscleName)); cost = calcMinimizingCostArrayTerm(muscleActivations(:, indx)); +if normalizeByFinalTime + cost = cost / time(end); +end end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingPropulsiveForceIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingPropulsiveForceIntegrand.m index 65659a815..454995931 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingPropulsiveForceIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingPropulsiveForceIntegrand.m @@ -28,8 +28,9 @@ % ----------------------------------------------------------------------- % function cost = calcMinimizingPropulsiveForceIntegrand(modeledValues, ... - params, costTerm) - + time, params, costTerm) +normalizeByFinalTime = valueOrAlternate(costTerm, ... + "normalize_by_final_time", false); for i = 1:length(params.contactSurfaces) if params.contactSurfaces{i}.isLeftFoot == costTerm.is_left_foot propulsiveForce = getPropulsiveForce(... @@ -37,4 +38,7 @@ end end cost = calcMinimizingCostArrayTerm(propulsiveForce); +if normalizeByFinalTime + cost = cost / time(end); +end end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingTrailingLimbAngleIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingTrailingLimbAngleIntegrand.m index ea2b804f8..067a53244 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingTrailingLimbAngleIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingTrailingLimbAngleIntegrand.m @@ -27,9 +27,10 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcMinimizingTrailingLimbAngleIntegrand(values, ... +function cost = calcMinimizingTrailingLimbAngleIntegrand(values, time, ... modeledValues, params, costTerm) - +normalizeByFinalTime = valueOrAlternate(costTerm, ... + "normalize_by_final_time", true); for i = 1:length(params.contactSurfaces) if params.contactSurfaces{i}.isLeftFoot == costTerm.is_left_foot normalForce = ... @@ -39,5 +40,8 @@ trailingLimbAngle = calcTrailingLimb(costTerm, values, ... normalForce, params); cost = calcMinimizingCostArrayTerm(trailingLimbAngle * ... - ones(length(values.time), 1)); + ones(length(time), 1)); +if normalizeByFinalTime + cost = cost / time(end); +end end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcStepLengthAsymmetryIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcStepLengthAsymmetryIntegrand.m index 95eb8f2f5..fa4e4da4b 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcStepLengthAsymmetryIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcStepLengthAsymmetryIntegrand.m @@ -29,13 +29,16 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcStepLengthAsymmetryIntegrand(values, modeledValues,... +function cost = calcStepLengthAsymmetryIntegrand(time, modeledValues,... params, costTerm) errorCenter = valueOrAlternate(costTerm, "errorCenter", 1); stepLengthAsymmetry = calcStepLengthAsymmetry(modeledValues,... params, costTerm.reference_body); cost = calcTrackingCostArrayTerm(stepLengthAsymmetry * ... - ones(length(values.time), 1), errorCenter * ... - ones(length(values.time), 1), 1); + ones(length(time), 1), errorCenter * ... + ones(length(time), 1), 1); +if normalizeByFinalTime + cost = cost / time(end); +end end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcStepTimeAsymmetryIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcStepTimeAsymmetryIntegrand.m index eac8181e9..822c374dd 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcStepTimeAsymmetryIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcStepTimeAsymmetryIntegrand.m @@ -29,13 +29,17 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcStepTimeAsymmetryIntegrand(values, ... +function cost = calcStepTimeAsymmetryIntegrand(values, time, ... modeledValues, params, costTerm) - +normalizeByFinalTime = valueOrAlternate(costTerm, ... + "normalize_by_final_time", false); errorCenter = valueOrAlternate(costTerm, "errorCenter", 1); stepTimeAsymmetry = calcStepTimeAsymmetry(values, ... modeledValues, params); cost = calcTrackingCostArrayTerm(stepTimeAsymmetry * ... - ones(length(values.time), 1), errorCenter * ... - ones(length(values.time), 1), 1); + ones(length(time), 1), errorCenter * ... + ones(length(time), 1), 1); +if normalizeByFinalTime + cost = cost / time(end); +end end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m index 3e1eb476d..ece67930a 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m @@ -32,7 +32,8 @@ function cost = calcTrackingControllerIntegrand(auxdata, values, time, ... controllerName) - +normalizeByFinalTime = valueOrAlternate(costTerm, ... + "normalize_by_final_time", true); if strcmp(auxdata.controllerType, 'synergy') indx = find(strcmp(convertCharsToStrings( ... auxdata.synergyLabels), controllerName)); @@ -58,4 +59,7 @@ end cost = experimentalJointMoments(:, indx1) - ... values.torqueControls(:, indx2); +if normalizeByFinalTime + cost = cost / time(end); +end end diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m index 0364bcaef..332179d30 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m @@ -30,7 +30,8 @@ function cost = calcTrackingCoordinateIntegrand(auxdata, time, ... statePositions, coordinateName) - +normalizeByFinalTime = valueOrAlternate(costTerm, ... + "normalize_by_final_time", true); indx = find(strcmp(convertCharsToStrings(auxdata.statesCoordinateNames), ... coordinateName)); if isempty(indx) @@ -46,4 +47,8 @@ cost = calcTrackingCostArrayTerm(experimentalJointAngles, ... statePositions, indx); + +if normalizeByFinalTime + cost = cost / time(end); +end end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m index 377a5d0ae..1f1ed40a7 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m @@ -30,7 +30,8 @@ function cost = calcTrackingExternalForcesIntegrand(params, ... groundReactionsForces, time, forceName) - +normalizeByFinalTime = valueOrAlternate(costTerm, ... + "normalize_by_final_time", true); for i = 1:length(params.contactSurfaces) indx = find(strcmp(convertCharsToStrings(params.contactSurfaces{i}. ... forceColumns), forceName)); @@ -49,4 +50,7 @@ indx); end end +if normalizeByFinalTime + cost = cost / time(end); +end end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m index b903723a4..07849a07e 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m @@ -30,7 +30,8 @@ function cost = calcTrackingExternalMomentsIntegrand(params, ... groundReactionMoments, time, loadName) - +normalizeByFinalTime = valueOrAlternate(costTerm, ... + "normalize_by_final_time", true); for i = 1:length(params.contactSurfaces) indx = find(strcmp(convertCharsToStrings(params.contactSurfaces{i}. ... momentColumns), loadName)); @@ -49,4 +50,7 @@ indx); end end +if normalizeByFinalTime + cost = cost / time(end); +end end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m index 9b19d0d50..50c962b86 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m @@ -30,7 +30,8 @@ function cost = calcTrackingInverseDynamicLoadsIntegrand(inputs, time, ... inverseDynamicsMoments, loadName) - +normalizeByFinalTime = valueOrAlternate(costTerm, ... + "normalize_by_final_time", true); loadName = erase(loadName, '_moment'); loadName = erase(loadName, '_force'); indx = find(strcmp(convertCharsToStrings(inputs.coordinateNames), ... @@ -50,4 +51,7 @@ end cost = calcTrackingCostArrayTerm(experimentalJointMoments, ... inverseDynamicsMoments, indx); +if normalizeByFinalTime + cost = cost / time(end); +end end diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m index 392789b9c..3febdff6d 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m @@ -30,7 +30,8 @@ function cost = calcTrackingMuscleActivationIntegrand(muscleActivations, ... time, params, muscleName) - +normalizeByFinalTime = valueOrAlternate(costTerm, ... + "normalize_by_final_time", true); indx = find(strcmp(convertCharsToStrings(params.muscleNames), ... muscleName)); @@ -45,4 +46,7 @@ experimentalMuscleActivations = fnval(params.splineMuscleActivations, time)'; cost = calcTrackingCostArrayTerm(experimentalMuscleActivations, ... muscleActivations, indx); +if normalizeByFinalTime + cost = cost / time(end); +end end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m index b63b1feb5..08462676f 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m @@ -30,7 +30,8 @@ function cost = calcTrackingSpeedIntegrand(auxdata, time, ... stateVelocities, coordinateName) - +normalizeByFinalTime = valueOrAlternate(costTerm, ... + "normalize_by_final_time", true); indx = find(strcmp(convertCharsToStrings(auxdata.statesCoordinateNames), ... coordinateName)); if isempty(indx) @@ -46,4 +47,8 @@ cost = calcTrackingCostArrayTerm(experimentalJointVelocities, ... stateVelocities, indx); + +if normalizeByFinalTime + cost = cost / time(end); +end end \ No newline at end of file diff --git a/src/TreatmentOptimization/generateCostTermStruct.m b/src/TreatmentOptimization/generateCostTermStruct.m index da017be7a..be3ccc066 100644 --- a/src/TreatmentOptimization/generateCostTermStruct.m +++ b/src/TreatmentOptimization/generateCostTermStruct.m @@ -147,6 +147,7 @@ costTermCalculations.joint_jerk_minimization = @(values, modeledValues, auxdata, costTerm) ... calcMinimizingJointJerkIntegrand( ... values.controlJerks, ... + values.time, ... auxdata, ... costTerm ... ); @@ -154,6 +155,7 @@ costTermCalculations.metabolic_cost_minimization = @(values, modeledValues, auxdata, costTerm) ... calcMinimizingMetabolicCost( ... values, ... + values.time, ... modeledValues, ... auxdata); @@ -189,52 +191,55 @@ costTermCalculations.propulsive_force_maximization = @(values, modeledValues, auxdata, costTerm) ... calcMaximizingPropulsiveForceIntegrand( ... modeledValues, ... + values.time, ... auxdata, ... costTerm); costTermCalculations.propulsive_force_minimization = @(values, modeledValues, auxdata, costTerm) ... calcMinimizingPropulsiveForceIntegrand( ... modeledValues, ... + values.time, ... auxdata, ... costTerm); costTermCalculations.breaking_force_maximization = @(values, modeledValues, auxdata, costTerm) ... calcMaximizingBreakingForceIntegrand( ... modeledValues, ... + values.time, ... auxdata, ... costTerm); costTermCalculations.breaking_force_minimization = @(values, modeledValues, auxdata, costTerm) ... calcMinimizingBreakingForceIntegrand( ... modeledValues, ... + values.time, ... auxdata, ... costTerm); costTermCalculations.step_length_maximization = @(values, modeledValues, auxdata, costTerm) ... calcMaximizingStepLengthIntegrand( ... - values, ... + values.time, ... modeledValues, ... auxdata, ... costTerm); costTermCalculations.step_length_asymmetry_minimization = @(values, modeledValues, auxdata, costTerm) ... calcStepLengthAsymmetryIntegrand( ... - values, ... + values.time, ... modeledValues, ... auxdata, ... costTerm); costTermCalculations.single_support_time_maximization = @(values, modeledValues, auxdata, costTerm) ... calcMaximizingSingleSupportTimeIntegrand( ... - values, ... + values.time, ... modeledValues, ... auxdata, ... costTerm); - costTermCalculations.single_support_time_goal = @(values, modeledValues, auxdata, costTerm) ... calcGoalSingleSupportTimeIntegrand( ... - values, ... + values.time, ... modeledValues, ... auxdata, ... costTerm); @@ -242,6 +247,7 @@ costTermCalculations.step_time_asymmetry_minimization = @(values, modeledValues, auxdata, costTerm) ... calcStepTimeAsymmetryIntegrand( ... values, ... + values.time, ... modeledValues, ... auxdata, ... costTerm); @@ -249,6 +255,7 @@ costTermCalculations.joint_power_minimization = @(values, modeledValues, auxdata, costTerm) ... calcMinimizingJointPowerIntegrand( ... values.velocities, ... + values.time, ... modeledValues.inverseDynamicsMoments, ... auxdata, ... costTerm.load ... @@ -257,6 +264,7 @@ costTermCalculations.trailing_limb_angle_minimization = @(values, modeledValues, auxdata, costTerm) ... calcMinimizingTrailingLimbAngleIntegrand( ... values, ... + values.time, ... modeledValues, ... auxdata, ... costTerm ... @@ -265,6 +273,7 @@ costTermCalculations.trailing_limb_angle_maximization = @(values, modeledValues, auxdata, costTerm) ... calcMaximizingTrailingLimbAngleIntegrand( ... values, ... + values.time, ... modeledValues, ... auxdata, ... costTerm ... @@ -272,6 +281,7 @@ costTermCalculations.muscle_activation_minimization = @(values, modeledValues, auxdata, costTerm) ... calcMinimizingMuscleActivationIntegrand( ... + values.time, ... modeledValues.muscleActivations, ... auxdata, ... costTerm.muscle ... @@ -279,6 +289,7 @@ costTermCalculations.muscle_activation_maximization = @(values, modeledValues, auxdata, costTerm) ... calcMaximizingMuscleActivationIntegrand( ... + values.time, ... modeledValues.muscleActivations, ... auxdata, ... costTerm.muscle ... @@ -287,34 +298,40 @@ costTermCalculations.center_mass_velocity_x_minimization = @(values, modeledValues, auxdata, costTerm) ... calcMinimizingMassCenterVelocityXIntegrand( ... values, ... + values.time, ... auxdata); costTermCalculations.center_mass_velocity_y_minimization = @(values, modeledValues, auxdata, costTerm) ... calcMinimizingMassCenterVelocityYIntegrand( ... values, ... + values.time, ... auxdata); costTermCalculations.center_mass_velocity_z_minimization = @(values, modeledValues, auxdata, costTerm) ... calcMinimizingMassCenterVelocityZIntegrand( ... values, ... + values.time, ... auxdata); costTermCalculations.kinematic_symmetry = @(values, modeledValues, auxdata, costTerm) ... calcKinematicSymmetryIntegrand( ... values.positions, ... + values.time, ... auxdata, ... costTerm); costTermCalculations.walking_speed_goal = @(values, modeledValues, auxdata, costTerm) ... calcGoalWalkingSpeedIntegrand( ... - values, ... modeledValues, ... + values, ... + values.time, ... auxdata, ... costTerm); costTermCalculations.external_torque_control_minimization = @(values, modeledValues, auxdata, costTerm) ... calcMinimizingExternalTorqueControl( ... values.externalTorqueControls, ... + values.time, ... auxdata, ... costTerm.coordinate); From 54643c597ca3730a2e0d355b1fef2dc0df76754c Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 9 Oct 2023 20:36:23 -0500 Subject: [PATCH 179/365] Fix discrete cost max allowable error --- .../setupGpopsInitialGuess.m | 4 ++-- .../setupTreatmentOptimizationBounds.m | 4 ++-- .../SetupBounds/makeMaxAllowableError.m | 16 +++++++++++----- .../gpops/calcGpopsIntegrand.m | 2 +- .../gpops/computeGpopsEndpointFunction.m | 1 + .../makeTreatmentOptimizationInputs.m | 4 ++-- 6 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m index a0eb54f79..1b9f7dc76 100644 --- a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m +++ b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m @@ -34,8 +34,8 @@ guess = setupInitialStatesGuess(inputs, guess); guess = setupInitialControlsGuess(inputs, guess); guess = setupInitialParametersGuess(inputs, guess); -guess.phase.integral = scaleToBounds(1e1, inputs.maxAllowableError, ... - zeros(size(inputs.maxAllowableError))); +guess.phase.integral = scaleToBounds(1e1, inputs.continuousMaxAllowableError, ... + zeros(size(inputs.continuousMaxAllowableError))); end function guess = setupInitialStatesGuess(inputs, guess) diff --git a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupTreatmentOptimizationBounds.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupTreatmentOptimizationBounds.m index d60bcf375..436eaba90 100644 --- a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupTreatmentOptimizationBounds.m +++ b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupTreatmentOptimizationBounds.m @@ -55,9 +55,9 @@ bounds.phase.control.lower = -0.5 * ones(1, length(inputs.minControl)); bounds.phase.control.upper = 0.5 * ones(1, length(inputs.minControl)); % setup integral bounds -bounds.phase.integral.lower = zeros(1, length(inputs.maxAllowableError)); +bounds.phase.integral.lower = zeros(1, length(inputs.continuousMaxAllowableError)); bounds.phase.integral.upper = inputs.gpops.integralBound * ... - ones(1, length(inputs.maxAllowableError)); + ones(1, length(inputs.continuousMaxAllowableError)); % setup terminal constraint bounds if ~isempty(inputs.minTerminal) bounds.eventgroup.lower = inputs.minTerminal; diff --git a/src/TreatmentOptimization/SetupBounds/makeMaxAllowableError.m b/src/TreatmentOptimization/SetupBounds/makeMaxAllowableError.m index ab7a53602..03213aad4 100644 --- a/src/TreatmentOptimization/SetupBounds/makeMaxAllowableError.m +++ b/src/TreatmentOptimization/SetupBounds/makeMaxAllowableError.m @@ -28,23 +28,29 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function maxAllowableError = makeMaxAllowableError(toolName, costTerms) +function [continuousMaxAllowableError, discreteMaxAllowableError] = ... + makeMaxAllowableError(toolName, costTerms) [~, continuousAllowedTypes] = generateCostTermStruct("continuous", toolName); [~, discreteAllowedTypes] = generateCostTermStruct("discrete", toolName); -maxAllowableError = []; +continuousMaxAllowableError = []; +discreteMaxAllowableError = []; for i = 1:length(costTerms) costTerm = costTerms{i}; if costTerm.isEnabled if any(ismember(costTerm.type, continuousAllowedTypes)) && ... ~strcmp(costTerm.type, "user_defined") - maxAllowableError = cat(2, maxAllowableError, ... - costTerm.maxAllowableError); + continuousMaxAllowableError = cat(2, ... + continuousMaxAllowableError, costTerm.maxAllowableError); elseif strcmp(costTerm.type, "user_defined") if strcmp(costTerm.cost_term_type, "continuous") - maxAllowableError = cat(2, maxAllowableError, ... + continuousMaxAllowableError = cat(2, ... + continuousMaxAllowableError, ... costTerm.maxAllowableError); end + elseif any(ismember(costTerm.type, discreteAllowedTypes)) + discreteMaxAllowableError = cat(2, ... + discreteMaxAllowableError, costTerm.maxAllowableError); elseif ~any(ismember(costTerm.type, continuousAllowedTypes)) || ... ~any(ismember(costTerm.type, discreteAllowedTypes)) throw(MException('', ['Cost term type ' costTerm.type ... diff --git a/src/TreatmentOptimization/gpops/calcGpopsIntegrand.m b/src/TreatmentOptimization/gpops/calcGpopsIntegrand.m index 52ad31611..6798d4bf1 100644 --- a/src/TreatmentOptimization/gpops/calcGpopsIntegrand.m +++ b/src/TreatmentOptimization/gpops/calcGpopsIntegrand.m @@ -32,7 +32,7 @@ generateCostTermStruct("continuous", inputs.toolName); integrand = calcTreatmentOptimizationCost( ... costTermCalculations, allowedTypes, values, modeledValues, inputs); -integrand = integrand ./ inputs.maxAllowableError; +integrand = integrand ./ inputs.continuousMaxAllowableError; integrand = integrand .^ 2; end diff --git a/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m b/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m index 06eeb680f..31fdd03de 100644 --- a/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m +++ b/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m @@ -63,6 +63,7 @@ generateCostTermStruct("discrete", "DesignOptimization"); discrete = calcTreatmentOptimizationCost( ... costTermCalculations, allowedTypes, values, modeledValues, setup.auxdata); + discrete = discrete ./ setup.auxdata.discreteMaxAllowableError; discreteObjective = sum(discrete) / length(discrete); if isnan(discreteObjective); discreteObjective = 0; end else diff --git a/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m b/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m index 4444c65d4..72c6e35bd 100644 --- a/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m +++ b/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m @@ -36,8 +36,8 @@ inputs = setupGroundContact(inputs); inputs = makeExperimentalDataSplines(inputs); inputs = makeSurrogateModel(inputs); -inputs.maxAllowableError = ... - makeMaxAllowableError(inputs.toolName, inputs.costTerms); +[inputs.continuousMaxAllowableError, inputs.discreteMaxAllowableError] ... + = makeMaxAllowableError(inputs.toolName, inputs.costTerms); inputs = makePathConstraintBounds(inputs); inputs = makeTerminalConstraintBounds(inputs); inputs = makeOptimalControlBounds(inputs); From e8cd8cb4dc5981c119615c5a9441f4231b1c0901 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 9 Oct 2023 20:41:11 -0500 Subject: [PATCH 180/365] Remove redundant allowable error from synergy vector tracking --- .../DiscreteTerms/calcTrackingSynergyVectorsDiscrete.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TreatmentOptimization/DiscreteTerms/calcTrackingSynergyVectorsDiscrete.m b/src/TreatmentOptimization/DiscreteTerms/calcTrackingSynergyVectorsDiscrete.m index 0f68b6825..4f3929673 100644 --- a/src/TreatmentOptimization/DiscreteTerms/calcTrackingSynergyVectorsDiscrete.m +++ b/src/TreatmentOptimization/DiscreteTerms/calcTrackingSynergyVectorsDiscrete.m @@ -36,5 +36,5 @@ synergyWeights(synergyWeights==0) = []; cost = calcTrackingCostArrayTerm(synergyWeights, ... origSynergyWeights, 1:size(synergyWeights, 2)); -cost = cost(:) / costTerm.maxAllowableError; +cost = cost(:); end \ No newline at end of file From 056312b536a238d597c1431c7fac7683ec882f14 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 9 Oct 2023 20:43:59 -0500 Subject: [PATCH 181/365] Remove redundant allowable error in joint jerk cost --- .../IntegrandTerms/calcMinimizingJointJerkIntegrand.m | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointJerkIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointJerkIntegrand.m index 34a46961f..f2873cd5e 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointJerkIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointJerkIntegrand.m @@ -39,8 +39,6 @@ ""))) end % cost = calcMinimizingCostArrayTerm(jointJerks(:, indx)); -errorCenter = valueOrAlternate(costTerm, "errorCenter", 0); -maximumAllowableError = valueOrAlternate(costTerm, "maxAllowableError", 10000); cost = jointJerks(:, indx) / maximumAllowableError; if normalizeByFinalTime cost = cost / time(end); From 379c96f5893ff89504f0df3b8dea26d158b3f9da Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Tue, 10 Oct 2023 00:33:37 -0500 Subject: [PATCH 182/365] add marker tracking preprocessing Co-Authored-By: Spencer Williams <54556034+SpencerTWilliams@users.noreply.github.com> --- .../calcTorqueBasedModeledValues.m | 2 +- .../makeMarkerTracking.m | 59 +++++++++++++++++++ .../makeTreatmentOptimizationInputs.m | 1 + 3 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 src/TreatmentOptimization/makeMarkerTracking.m diff --git a/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m b/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m index 869ea525c..9599a46a7 100644 --- a/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m +++ b/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m @@ -136,7 +136,7 @@ && ~isempty(inputs.trackedMarkerNames) clear pointKinematics markerPositions = pointKinematics(values.time, values.positions, ... - values.velocities, inputs.trackedMarkerLocationsOnBodies, ... + values.velocities, inputs.trackedMarkerLocations, ... inputs.trackedMarkerBodyIndices, inputs.mexModel, ... inputs.coordinateNames); end diff --git a/src/TreatmentOptimization/makeMarkerTracking.m b/src/TreatmentOptimization/makeMarkerTracking.m new file mode 100644 index 000000000..638862fb7 --- /dev/null +++ b/src/TreatmentOptimization/makeMarkerTracking.m @@ -0,0 +1,59 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function parses the settings tree resulting from xml2struct from the +% settings XML file common to all treatment optimizatin modules (trackning, +% verification, and design optimization). +% +% (struct) -> (struct, struct) +% returns the input values for all treatment optimization modules + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Claire V. Hammond % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function inputs = makeMarkerTracking(inputs) +names = string([]); +locations = []; +bodies = []; +for i = 1:length(inputs.costTerms) + costTerm = inputs.costTerms{i}; + if strcmp(costTerm.type, "marker_position_tracking") + names(end + 1) = convertCharsToStrings(inputs.model.getMarkerSet().get( ... + costTerm.marker).getName().toCharArray()'); + locations = cat(1, locations, ... + Vec3ToArray(inputs.model.getMarkerSet().get(... + costTerm.marker).get_location())); + bodies(end + 1) = inputs.model.getBodySet().getIndex( ... + getMarkerBodyName(inputs.model, costTerm.marker)); + end +end +inputs.trackedMarkerNames = names; +inputs.trackedMarkerLocations = locations; +inputs.trackedMarkerBodyIndices = bodies; +inputs.experimentalMarkerPositions = pointKinematics( ... + inputs.experimentalTime, inputs.experimentalJointAngles, ... + inputs.experimentalJointVelocities, inputs.trackedMarkerLocations, ... + inputs.trackedMarkerBodyIndices, inputs.mexModel, ... + inputs.coordinateNames); +inputs.splineMarkerPositions = spaps(inputs.experimentalTime, ... + inputs.experimentalMarkerPositions', 0.0000001); +end + diff --git a/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m b/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m index 72c6e35bd..bee0899e8 100644 --- a/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m +++ b/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m @@ -38,6 +38,7 @@ inputs = makeSurrogateModel(inputs); [inputs.continuousMaxAllowableError, inputs.discreteMaxAllowableError] ... = makeMaxAllowableError(inputs.toolName, inputs.costTerms); +inputs = makeMarkerTracking(inputs); inputs = makePathConstraintBounds(inputs); inputs = makeTerminalConstraintBounds(inputs); inputs = makeOptimalControlBounds(inputs); From 160646bc979a5f54f52e9c727d9c17b99eaeedd7 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Tue, 10 Oct 2023 00:34:45 -0500 Subject: [PATCH 183/365] fix costTerm bug Co-Authored-By: Spencer Williams <54556034+SpencerTWilliams@users.noreply.github.com> --- .../IntegrandTerms/calcMaximizingMuscleActivationIntegrand.m | 2 +- .../IntegrandTerms/calcMinimizingExternalTorqueControl.m | 2 +- .../IntegrandTerms/calcMinimizingJointPowerIntegrand.m | 4 ++-- .../calcMinimizingMassCenterVelocityXIntegrand.m | 4 ++-- .../calcMinimizingMassCenterVelocityYIntegrand.m | 4 ++-- .../calcMinimizingMassCenterVelocityZIntegrand.m | 4 ++-- .../IntegrandTerms/calcMinimizingMetabolicCost.m | 4 ++-- .../IntegrandTerms/calcMinimizingMuscleActivationIntegrand.m | 2 +- .../IntegrandTerms/calcTrackingControllerIntegrand.m | 4 ++-- .../IntegrandTerms/calcTrackingCoordinateIntegrand.m | 2 +- .../IntegrandTerms/calcTrackingExternalForcesIntegrand.m | 2 +- .../IntegrandTerms/calcTrackingExternalMomentsIntegrand.m | 2 +- .../IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m | 4 ++-- .../IntegrandTerms/calcTrackingMuscleActivationIntegrand.m | 4 ++-- .../IntegrandTerms/calcTrackingSpeedIntegrand.m | 2 +- 15 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMaximizingMuscleActivationIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMaximizingMuscleActivationIntegrand.m index 51256cf07..a1e14c183 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMaximizingMuscleActivationIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMaximizingMuscleActivationIntegrand.m @@ -27,7 +27,7 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcMaximizingMuscleActivationIntegrand(time, ... +function cost = calcMaximizingMuscleActivationIntegrand(costTerm, time, ... muscleActivations, params, muscleName) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingExternalTorqueControl.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingExternalTorqueControl.m index 5719af4b6..db74dc3b8 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingExternalTorqueControl.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingExternalTorqueControl.m @@ -28,7 +28,7 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcMinimizingExternalTorqueControl(... +function cost = calcMinimizingExternalTorqueControl(costTerm, ... externalTorqueControl, time, params, coordinate) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointPowerIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointPowerIntegrand.m index 1685c621e..4feb0fed3 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointPowerIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointPowerIntegrand.m @@ -27,8 +27,8 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcMinimizingJointPowerIntegrand(jointVelocity, time, ... - jointMoment, params, loadName) +function cost = calcMinimizingJointPowerIntegrand(costTerm, ... + jointVelocity, time, jointMoment, params, loadName) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); loadName = erase(loadName, '_moment'); diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityXIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityXIntegrand.m index 2b274ae6f..5e9cb0455 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityXIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityXIntegrand.m @@ -27,8 +27,8 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcMinimizingMassCenterVelocityXIntegrand(values, ... - time, params) +function cost = calcMinimizingMassCenterVelocityXIntegrand(costTerm, ... + values, time, params) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); massCenterVelocity = calcMassCenterVelocity(values, params.model, ... diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityYIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityYIntegrand.m index 8b0f3d0af..2dda31c63 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityYIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityYIntegrand.m @@ -27,8 +27,8 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcMinimizingMassCenterVelocityYIntegrand(values, ... - time, params) +function cost = calcMinimizingMassCenterVelocityYIntegrand(costTerm, ... + values, time, params) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); massCenterVelocity = calcMassCenterVelocity(values, params.model, ... diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityZIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityZIntegrand.m index 5be0df909..6d0a8b190 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityZIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityZIntegrand.m @@ -27,8 +27,8 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcMinimizingMassCenterVelocityZIntegrand(values, ... - time, params) +function cost = calcMinimizingMassCenterVelocityZIntegrand(costTerm, ... + values, time, params) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); massCenterVelocity = calcMassCenterVelocity(values, params.model, ... diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMetabolicCost.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMetabolicCost.m index cc3fa8328..5dcf371a5 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMetabolicCost.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMetabolicCost.m @@ -26,8 +26,8 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcMinimizingMetabolicCost(values, time, modeledValues, ... - params) +function cost = calcMinimizingMetabolicCost(costTerm, values, time, ... + modeledValues, params) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", false); metabolicCost = calcMetabolicCost(values.time, ... diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMuscleActivationIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMuscleActivationIntegrand.m index cd0ef83b2..aa8b74e5c 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMuscleActivationIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMuscleActivationIntegrand.m @@ -27,7 +27,7 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcMinimizingMuscleActivationIntegrand(time, ... +function cost = calcMinimizingMuscleActivationIntegrand(costTerm, time, ... muscleActivations, params, muscleName) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m index ece67930a..07455b2b7 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m @@ -30,8 +30,8 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcTrackingControllerIntegrand(auxdata, values, time, ... - controllerName) +function cost = calcTrackingControllerIntegrand(costTerm, auxdata, ... + values, time, controllerName) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); if strcmp(auxdata.controllerType, 'synergy') diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m index 332179d30..507341603 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m @@ -28,7 +28,7 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcTrackingCoordinateIntegrand(auxdata, time, ... +function cost = calcTrackingCoordinateIntegrand(costTerm, auxdata, time, ... statePositions, coordinateName) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m index 1f1ed40a7..b1bd8bdbc 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m @@ -28,7 +28,7 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcTrackingExternalForcesIntegrand(params, ... +function cost = calcTrackingExternalForcesIntegrand(costTerm, params, ... groundReactionsForces, time, forceName) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m index 07849a07e..af57e99bb 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m @@ -28,7 +28,7 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcTrackingExternalMomentsIntegrand(params, ... +function cost = calcTrackingExternalMomentsIntegrand(costTerm, params, ... groundReactionMoments, time, loadName) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m index 50c962b86..6b30c5507 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m @@ -28,8 +28,8 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcTrackingInverseDynamicLoadsIntegrand(inputs, time, ... - inverseDynamicsMoments, loadName) +function cost = calcTrackingInverseDynamicLoadsIntegrand(costTerm, ... + inputs, time, inverseDynamicsMoments, loadName) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); loadName = erase(loadName, '_moment'); diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m index 3febdff6d..56e061bc2 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m @@ -28,8 +28,8 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcTrackingMuscleActivationIntegrand(muscleActivations, ... - time, params, muscleName) +function cost = calcTrackingMuscleActivationIntegrand(costTerm, ... + muscleActivations, time, params, muscleName) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); indx = find(strcmp(convertCharsToStrings(params.muscleNames), ... diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m index 08462676f..914cd8b32 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m @@ -28,7 +28,7 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcTrackingSpeedIntegrand(auxdata, time, ... +function cost = calcTrackingSpeedIntegrand(costTerm, auxdata, time, ... stateVelocities, coordinateName) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); From ed1d2b6182fe107c8ed1932209f869683c3afdd8 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Tue, 10 Oct 2023 00:35:16 -0500 Subject: [PATCH 184/365] fix bug with kinetic_consistency Co-Authored-By: Spencer Williams <54556034+SpencerTWilliams@users.noreply.github.com> --- src/core/parse/parseTorqueController.m | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/parse/parseTorqueController.m b/src/core/parse/parseTorqueController.m index 91e7db087..cc41fe910 100644 --- a/src/core/parse/parseTorqueController.m +++ b/src/core/parse/parseTorqueController.m @@ -35,4 +35,5 @@ inputs.maxTorqueControlsMultiple = parseDoubleOrAlternate(tree, ... 'torque_controls_range_scale_factor', 1); inputs.numSynergies = 0; +inputs.surrogateModelCoordinateNames = []; end From d547884ed740b9f22f7467444996593a28669576 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Tue, 10 Oct 2023 00:35:47 -0500 Subject: [PATCH 185/365] fix allowed types Co-Authored-By: Spencer Williams <54556034+SpencerTWilliams@users.noreply.github.com> --- .../generateCostTermStruct.m | 29 +++++++++++++++++++ .../parse/parseTreatmentOptimizationInputs.m | 11 +++---- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/src/TreatmentOptimization/generateCostTermStruct.m b/src/TreatmentOptimization/generateCostTermStruct.m index 0dc50c654..a142e60ae 100644 --- a/src/TreatmentOptimization/generateCostTermStruct.m +++ b/src/TreatmentOptimization/generateCostTermStruct.m @@ -46,6 +46,8 @@ case "TrackingOptimization" allowedTypes = [ ... "generalized_coordinate_tracking", ... + "generalized_speed_tracking", ... + "marker_position_tracking", ... "inverse_dynamics_load_tracking", ... "external_force_tracking", ... "external_moment_tracking", ... @@ -55,12 +57,16 @@ case "VerificationOptimization" allowedTypes = [ ... "generalized_coordinate_tracking", ... + "generalized_speed_tracking", ... + "marker_position_tracking", ... "controller_tracking", ... "joint_jerk_minimization", ... ]; case "DesignOptimization" allowedTypes = [ ... "generalized_coordinate_tracking", ... + "generalized_speed_tracking", ... + "marker_position_tracking", ... "inverse_dynamics_load_tracking", ... "external_force_tracking", ... "external_moment_tracking", ... @@ -122,6 +128,7 @@ costTermCalculations.generalized_coordinate_tracking = @(values, modeledValues, auxdata, costTerm) ... calcTrackingCoordinateIntegrand( ... + costTerm, ... auxdata, ... values.time, ... values.positions, ... @@ -130,14 +137,24 @@ costTermCalculations.generalized_speed_tracking = @(values, modeledValues, auxdata, costTerm) ... calcTrackingSpeedIntegrand( ... + costTerm, ... auxdata, ... values.time, ... values.velocities, ... costTerm.coordinate ... ); +costTermCalculations.marker_position_tracking = @(values, modeledValues, auxdata, costTerm) ... + calcTrackingMarkerPosition( ... + costTerm, ... + values.time, ... + modeledValues.markerPositions, ... + auxdata ... + ); + costTermCalculations.controller_tracking = @(values, modeledValues, auxdata, costTerm) ... calcTrackingControllerIntegrand( ... + costTerm, ... auxdata, ... values, ... values.time, ... @@ -154,6 +171,7 @@ costTermCalculations.metabolic_cost_minimization = @(values, modeledValues, auxdata, costTerm) ... calcMinimizingMetabolicCost( ... + costTerm, ... values, ... values.time, ... modeledValues, ... @@ -161,6 +179,7 @@ costTermCalculations.inverse_dynamics_load_tracking = @(values, modeledValues, auxdata, costTerm) ... calcTrackingInverseDynamicLoadsIntegrand( ... + costTerm, ... auxdata, ... values.time, ... modeledValues.inverseDynamicsMoments, ... @@ -169,6 +188,7 @@ costTermCalculations.external_force_tracking = @(values, modeledValues, auxdata, costTerm) ... calcTrackingExternalForcesIntegrand( ... + costTerm, ... auxdata, ... modeledValues.groundReactionsLab.forces, ... values.time, ... @@ -176,6 +196,7 @@ costTermCalculations.external_moment_tracking = @(values, modeledValues, auxdata, costTerm) ... calcTrackingExternalMomentsIntegrand( ... + costTerm, ... auxdata, ... modeledValues.groundReactionsLab.moments, ... values.time, ... @@ -183,6 +204,7 @@ costTermCalculations.muscle_activation_tracking = @(values, modeledValues, auxdata, costTerm) ... calcTrackingMuscleActivationIntegrand( ... + costTerm, ... modeledValues.muscleActivations, ... values.time, ... auxdata, ... @@ -254,6 +276,7 @@ costTermCalculations.joint_power_minimization = @(values, modeledValues, auxdata, costTerm) ... calcMinimizingJointPowerIntegrand( ... + costTerm, ... values.velocities, ... values.time, ... modeledValues.inverseDynamicsMoments, ... @@ -281,6 +304,7 @@ costTermCalculations.muscle_activation_minimization = @(values, modeledValues, auxdata, costTerm) ... calcMinimizingMuscleActivationIntegrand( ... + costTerm, ... values.time, ... modeledValues.muscleActivations, ... auxdata, ... @@ -289,6 +313,7 @@ costTermCalculations.muscle_activation_maximization = @(values, modeledValues, auxdata, costTerm) ... calcMaximizingMuscleActivationIntegrand( ... + costTerm, ... values.time, ... modeledValues.muscleActivations, ... auxdata, ... @@ -297,18 +322,21 @@ costTermCalculations.center_mass_velocity_x_minimization = @(values, modeledValues, auxdata, costTerm) ... calcMinimizingMassCenterVelocityXIntegrand( ... + costTerm, ... values, ... values.time, ... auxdata); costTermCalculations.center_mass_velocity_y_minimization = @(values, modeledValues, auxdata, costTerm) ... calcMinimizingMassCenterVelocityYIntegrand( ... + costTerm, ... values, ... values.time, ... auxdata); costTermCalculations.center_mass_velocity_z_minimization = @(values, modeledValues, auxdata, costTerm) ... calcMinimizingMassCenterVelocityZIntegrand( ... + costTerm, ... values, ... values.time, ... auxdata); @@ -330,6 +358,7 @@ costTermCalculations.external_torque_control_minimization = @(values, modeledValues, auxdata, costTerm) ... calcMinimizingExternalTorqueControl( ... + costTerm, ... values.externalTorqueControls, ... values.time, ... auxdata, ... diff --git a/src/core/parse/parseTreatmentOptimizationInputs.m b/src/core/parse/parseTreatmentOptimizationInputs.m index 79db857ca..f3a8c6380 100644 --- a/src/core/parse/parseTreatmentOptimizationInputs.m +++ b/src/core/parse/parseTreatmentOptimizationInputs.m @@ -57,13 +57,13 @@ if strcmp(inputs.controllerType, "synergy") if strcmp(osimxFileName, "") throw(MException("", ... - strcat(" must be specified", ... - " for "))) + strcat(" must be specified", ... + " for "))) end if ~isfield(osimx, "synergyGroups") throw(MException("", ... - strcat(" must be specified in the", ... - " osimx file for . Have you run NCP yet?"))) + strcat(" must be specified in the", ... + " osimx file for . Have you run NCP yet?"))) end end end @@ -85,4 +85,5 @@ [path, terminal] = parseRcnlConstraintTermSet({}, controllerType, ... toolName); end -end \ No newline at end of file +end + From 76a8ba0efb3196f454784cf3cb1b6440ebf22eeb Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Tue, 10 Oct 2023 00:36:05 -0500 Subject: [PATCH 186/365] add marker position cost fn Co-Authored-By: Spencer Williams <54556034+SpencerTWilliams@users.noreply.github.com> --- .../calcTrackingMarkerPosition.m | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m new file mode 100644 index 000000000..be68d8e4d --- /dev/null +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m @@ -0,0 +1,53 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function calculates the error in marker tracking +% +% (struct, Array of number, 2D matrix, Array of string) -> (Array of number) +% returns the distance between the experimental and calculated markers. + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Claire V. Hammond % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function cost = calcTrackingMarkerPosition(costTerm, time, ... + markerPositions, auxdata) +normalizeByFinalTime = valueOrAlternate(costTerm, ... + "normalize_by_final_time", true); +indx = find(strcmp(convertCharsToStrings(auxdata.trackedMarkerNames), ... + costTerm.marker)) * 3 - 2; +if isempty(indx) + throw(MException('CostTermError:MarkerDoesNotExist', ... + strcat("Marker ", costTerm.marker, " is not in the ", ... + "list of tracked markers"))) +end +experimentalMarkerPositions = ... + fnval(auxdata.splineMarkerPositions, time)'; +cost = calcTrackingCostArrayTerm(experimentalMarkerPositions, ... + markerPositions, indx); +cost = cost + calcTrackingCostArrayTerm(experimentalMarkerPositions, ... + markerPositions, indx + 1); +cost = cost + calcTrackingCostArrayTerm(experimentalMarkerPositions, ... + markerPositions, indx + 2); +if normalizeByFinalTime + cost = cost / time(end); +end +end + From a7683615247ef4216518d9fe6c639f87666ad866 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Tue, 10 Oct 2023 00:36:13 -0500 Subject: [PATCH 187/365] fix silly bug Co-Authored-By: Spencer Williams <54556034+SpencerTWilliams@users.noreply.github.com> --- .../IntegrandTerms/calcMinimizingJointJerkIntegrand.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointJerkIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointJerkIntegrand.m index f2873cd5e..13eb021d5 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointJerkIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointJerkIntegrand.m @@ -39,7 +39,7 @@ ""))) end % cost = calcMinimizingCostArrayTerm(jointJerks(:, indx)); -cost = jointJerks(:, indx) / maximumAllowableError; +cost = jointJerks(:, indx); if normalizeByFinalTime cost = cost / time(end); end From 36102276d9b1fd9515535acd62d7408322a4b34e Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Tue, 10 Oct 2023 15:37:35 -0500 Subject: [PATCH 188/365] fix marker position tracking bug --- .../calcTrackingMarkerPosition.m | 4 ++-- .../makeMarkerTracking.m | 21 ++++++++++++------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m index be68d8e4d..ce1d7e9d6 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m @@ -32,14 +32,14 @@ normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); indx = find(strcmp(convertCharsToStrings(auxdata.trackedMarkerNames), ... - costTerm.marker)) * 3 - 2; + costTerm.marker)); if isempty(indx) throw(MException('CostTermError:MarkerDoesNotExist', ... strcat("Marker ", costTerm.marker, " is not in the ", ... "list of tracked markers"))) end experimentalMarkerPositions = ... - fnval(auxdata.splineMarkerPositions, time)'; + fnval(auxdata.splineMarkerPositions{indx}, time)'; cost = calcTrackingCostArrayTerm(experimentalMarkerPositions, ... markerPositions, indx); cost = cost + calcTrackingCostArrayTerm(experimentalMarkerPositions, ... diff --git a/src/TreatmentOptimization/makeMarkerTracking.m b/src/TreatmentOptimization/makeMarkerTracking.m index 638862fb7..4f8c00c5d 100644 --- a/src/TreatmentOptimization/makeMarkerTracking.m +++ b/src/TreatmentOptimization/makeMarkerTracking.m @@ -48,12 +48,19 @@ inputs.trackedMarkerNames = names; inputs.trackedMarkerLocations = locations; inputs.trackedMarkerBodyIndices = bodies; -inputs.experimentalMarkerPositions = pointKinematics( ... - inputs.experimentalTime, inputs.experimentalJointAngles, ... - inputs.experimentalJointVelocities, inputs.trackedMarkerLocations, ... - inputs.trackedMarkerBodyIndices, inputs.mexModel, ... - inputs.coordinateNames); -inputs.splineMarkerPositions = spaps(inputs.experimentalTime, ... - inputs.experimentalMarkerPositions', 0.0000001); +if ~isempty(inputs.trackedMarkerNames) + inputs.experimentalMarkerPositions = pointKinematics( ... + inputs.experimentalTime, inputs.experimentalJointAngles, ... + inputs.experimentalJointVelocities, inputs.trackedMarkerLocations, ... + inputs.trackedMarkerBodyIndices, inputs.mexModel, ... + inputs.coordinateNames); + for i = 1:size(inputs.experimentalMarkerPositions, 3) + experimentalMarkerPositions = ... + reshape(inputs.experimentalMarkerPositions(:, :, i), ... + size(inputs.experimentalMarkerPositions, 1), []); + inputs.splineMarkerPositions{i} = spaps(inputs.experimentalTime, ... + experimentalMarkerPositions', 0.0000001); + end +end end From 809f63206bffb77cc79bc920f8d0ef4b9596dffa Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Tue, 10 Oct 2023 15:49:43 -0500 Subject: [PATCH 189/365] Update pointKinematicsMexWindows.mexw64 --- src/core/mex/pointKinematicsMexWindows.mexw64 | Bin 31744 -> 29184 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/core/mex/pointKinematicsMexWindows.mexw64 b/src/core/mex/pointKinematicsMexWindows.mexw64 index b8ca6b73ff4ebabcec6ebb4571420156aaf6c7dd..11c9a1511ad8b425faaf2ea0186192c9bbd97d4a 100644 GIT binary patch literal 29184 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%0D8PDS`fzg~l?sc?ze`V6?p2-QzX%2gF zW=$pZlt#Z|)+F?vNVGZNsgO^x_;Z_xuM2r` z=j(CAuJuOn?Kq^x3cU>tl~?8EvL9$^d{OPwVx=K=En~@vn;F6 zJ|)Qo-kNI99auAst#$sqy4O14>59ld97!(N8jOU#0E=r=3W8sZLn+$v1%+UDpdB^Q z#^BjE1dfj!EIXJ&Yu~M(ivej93p#MQ5pDT`>R|uLCHZ_!;gUd*=e{Tp-kSBW{ZiZy z^?V+!2ow7oqlNndSb9&s6xYUuZrfi^DG1flDAH~Fj3sFwgDQ;+gfk2VzPw{!`y4en z&$=d(ERVbESw?Sh##ZTmBHLIBOEtK|#?RK|_2gmBP9e)2d?)ucV~_n=b?Lo!GPwZT zBy1k_Ueg3Uqc`M-K{VUaMMly;vKaV)1o*zLw^?~h;)&AzQ z9^Rzl%Jrnd}biKJvzf|C#J|nFC)~N;a1l zmM&Y7tF3`9VU;*8sIA1QV7Ms(B@$`B8vW?XShj6uwQa#)_lSL;TXfudp&4^m=@CIJ2nNNEpl3CBj*3C0*OE z6!la#;Ea~BYls78Eqtdf+_Yt5S#gPLtTjeVD;c3rANQWY z*^i^DNHm0VUPyyc*nkV}^=uw%6sxx@sjM$T%3)WH$1AK9HNJC+D5IXT2|53*gqy0heaX~ zT89-fWW`ZKm=su>a(&brYA*GLa0rP}?m;V{1^k9$(dpcaYuQo*7WV6{t_6~rk|Bw9 zkVD7J&M+F_r5L;1gk2GJhequ7)r9ba4_fPo*^Xq|@QI@xH%wImJN9izTSUJ>+3F4H zQf_c<+URoUE!XT*{|fO}_`gL1yItCSeX|hw@r3k~BAp`gdqf&n(fBivKtCwbX(Ink zk*0eT^sIdG`DgzxzFOA;9jkj?RrSA~65Ta^Z-i(}c#}A>u0btHczQC5C;q-- zNrAH*zfpl#rn3vU{7P-Bt4*h$K~JaVI?)t-$7anl&?SDq*g0?3qkohQ%#@^Ys~vTP#NS;Q|1<1h_{R#)?z8xuag#BH z94J?bg9Wdl`=*e$vN#Z~aY#l&(a6t|iaXB54dDLSrFbiFr7{C&UHXu%L1aAxCqkxNX7i|U612&2{-A_A)m;QFu zC}1z{)a?Qd-BEiVZx-To-*v;exGM!dRe&GjeG_>6#*(eTTLe6Q*T|wG-U8T%cN9Da z0Zmp-&J4g@yrm=qUxT30~649|7ICyGimBtic`2CxNd8RPc@>PH-OX zQWjO<|33lVh?it&0qn#}I_V}H#X(Q-2aE6H@>b2pH z|0=u`Ukw-)@g~5>MVw%-h#v-gPs9nDmS|;D0BiC72lx~Groa>I7IA_f;3YYK4|q53 zc~ZOuFe?{jP?q3oyo4v{74as(zGaXdG=~A%a?O4St`>0z;Cp#^L4)6;vv1%XLL9$I zXZ!Ighz|jNigyrwM>n&bcqvZzW$8XF#R<|~Sc(&*`>YfvNOw{jz=z=P@ji+8|HemK z-T!28ycpL*LV<9gF0$AYXuP^Q+_+@hvV}J6aeQ^yhL>wMM%OG{wj_7qHR&^K zw$+gkepp_NUGAUBEl(?ll40yJ@ss#w&c}vlHQh}`E#6vuH`s>F&{l7FEYq0n;u}rI+X8oZLkn$DA6-%jqiuE74Po!XYgR8E<2|wP7=HuBX|=Cj zIziUeOUD|G;;WbH`n;ypS?Vk*D&BZ)MTLlbku=>&J&wMWc9-t1*j=@|es|OEw%x~f z5A9ZVkM2ITo9#*4ld&gjk8MxRp4>h8dmMYDJ*9gp_Ehbu-xJ)^w5MfH+n$a+yY_VK z-L(#BcI9_Dx}>hsu8OXzuKKQES5sF@S6f#{*RHP4 zu6x(2(BcMWwZU87y6x>$EwcSd(sx2-#;JGVQ(+tDp`mv&clS9RBS2fLfP zTe{o2JGysucXoGor#+hSXzru=k9I%W^XR~%rH|D=7JRJfv6jc$9wSS|u!d92=(Kg_ zb~-vsJF7bPeRt@)X^-SSQt?R3Bl{j1dc^U)w(kvmZ}fY%-7UKZb`$ykwdel>Az880 literal 31744 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 tZ_7Xn`?~4t Date: Sat, 21 Oct 2023 02:20:16 -0500 Subject: [PATCH 190/365] make JMP function order independent --- .../KinematicCalibration/computeInnerOptimization.m | 2 +- .../KinematicCalibration/computeKinematicCalibration.m | 4 ++-- .../KinematicCalibration/functions/adjustBodyScaling.m | 6 ++++-- .../KinematicCalibration/functions/adjustChildOrientation.m | 5 ++++- .../KinematicCalibration/functions/adjustChildTranslation.m | 5 ++++- .../functions/adjustParentOrientation.m | 5 ++++- .../functions/adjustParentTranslation.m | 5 ++++- .../adjustModelFromOptimizerOutput.m | 2 +- .../CostComputations/calcMuscleTendonNonLinearConstraints.m | 2 +- 9 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/JointModelPersonalization/KinematicCalibration/computeInnerOptimization.m b/src/JointModelPersonalization/KinematicCalibration/computeInnerOptimization.m index c218362b5..1245d4354 100644 --- a/src/JointModelPersonalization/KinematicCalibration/computeInnerOptimization.m +++ b/src/JointModelPersonalization/KinematicCalibration/computeInnerOptimization.m @@ -37,7 +37,7 @@ end modelCopy = Model(model); for i = 1:length(values) - functions{i}(values(i), modelCopy); + functions{i}(round(values(i), 10), modelCopy); end markersReference = makeJmpMarkerRef(modelCopy, markerFileName, params); error = computeInnerOptimizationHeuristic(modelCopy, markersReference, ... diff --git a/src/JointModelPersonalization/KinematicCalibration/computeKinematicCalibration.m b/src/JointModelPersonalization/KinematicCalibration/computeKinematicCalibration.m index e8ac341ab..371574ddc 100644 --- a/src/JointModelPersonalization/KinematicCalibration/computeKinematicCalibration.m +++ b/src/JointModelPersonalization/KinematicCalibration/computeKinematicCalibration.m @@ -55,9 +55,9 @@ function [lowerBounds, upperBounds] = prepareKinematicCalibrationBounds(... initialValues, params) lowerBounds = valueOrAlternate(params, 'lowerBounds', ... - initialValues - 0.1); + initialValues - 10); upperBounds = valueOrAlternate(params, 'upperBounds', ... - initialValues + 0.1); + initialValues + 10); end % (struct) -> (struct) diff --git a/src/JointModelPersonalization/KinematicCalibration/functions/adjustBodyScaling.m b/src/JointModelPersonalization/KinematicCalibration/functions/adjustBodyScaling.m index 15e17367c..3b00230bf 100644 --- a/src/JointModelPersonalization/KinematicCalibration/functions/adjustBodyScaling.m +++ b/src/JointModelPersonalization/KinematicCalibration/functions/adjustBodyScaling.m @@ -29,8 +29,10 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function adjustBodyScaling(model, bodyName, value, anatomicalMarkers) - +function [body, joint, coordinate] = adjustBodyScaling(model, bodyName, value, anatomicalMarkers) +body = bodyName; +joint = ""; +coordinate = 0; if ~anatomicalMarkers markers = getMarkersFromBody(model, bodyName); markerLocations = {}; diff --git a/src/JointModelPersonalization/KinematicCalibration/functions/adjustChildOrientation.m b/src/JointModelPersonalization/KinematicCalibration/functions/adjustChildOrientation.m index e0ecc2575..543373a50 100644 --- a/src/JointModelPersonalization/KinematicCalibration/functions/adjustChildOrientation.m +++ b/src/JointModelPersonalization/KinematicCalibration/functions/adjustChildOrientation.m @@ -28,7 +28,10 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function adjustChildOrientation(model, jointName, coordinateNum, value) +function [body, joint, coordinate] = adjustChildOrientation(model, jointName, coordinateNum, value) +body = "child_orientation"; +joint = jointName; +coordinate = coordinateNum; import org.opensim.modeling.* frame = model.getJointSet().get(jointName).getChildFrame(); offsetFrame = PhysicalOffsetFrame.safeDownCast(frame); diff --git a/src/JointModelPersonalization/KinematicCalibration/functions/adjustChildTranslation.m b/src/JointModelPersonalization/KinematicCalibration/functions/adjustChildTranslation.m index 365c39ed7..5a95305c2 100644 --- a/src/JointModelPersonalization/KinematicCalibration/functions/adjustChildTranslation.m +++ b/src/JointModelPersonalization/KinematicCalibration/functions/adjustChildTranslation.m @@ -28,7 +28,10 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function adjustChildTranslation(model, jointName, coordinateNum, value) +function [body, joint, coordinate] = adjustChildTranslation(model, jointName, coordinateNum, value) +body = "child_translation"; +joint = jointName; +coordinate = coordinateNum; import org.opensim.modeling.* frame = model.getJointSet().get(jointName).getChildFrame(); offsetFrame = PhysicalOffsetFrame.safeDownCast(frame); diff --git a/src/JointModelPersonalization/KinematicCalibration/functions/adjustParentOrientation.m b/src/JointModelPersonalization/KinematicCalibration/functions/adjustParentOrientation.m index 3b9792047..26b384da7 100644 --- a/src/JointModelPersonalization/KinematicCalibration/functions/adjustParentOrientation.m +++ b/src/JointModelPersonalization/KinematicCalibration/functions/adjustParentOrientation.m @@ -28,7 +28,10 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function adjustParentOrientation(model, jointName, coordinateNum, value) +function [body, joint, coordinate] = adjustParentOrientation(model, jointName, coordinateNum, value) +body = "parent_orientation"; +joint = jointName; +coordinate = coordinateNum; import org.opensim.modeling.* frame = model.getJointSet().get(jointName).getParentFrame(); offsetFrame = PhysicalOffsetFrame.safeDownCast(frame); diff --git a/src/JointModelPersonalization/KinematicCalibration/functions/adjustParentTranslation.m b/src/JointModelPersonalization/KinematicCalibration/functions/adjustParentTranslation.m index b983a49d1..7f51b0eee 100644 --- a/src/JointModelPersonalization/KinematicCalibration/functions/adjustParentTranslation.m +++ b/src/JointModelPersonalization/KinematicCalibration/functions/adjustParentTranslation.m @@ -28,7 +28,10 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function adjustParentTranslation(model, jointName, coordinateNum, value) +function [body, joint, coordinate] = adjustParentTranslation(model, jointName, coordinateNum, value) +body = "parent_translation"; +joint = jointName; +coordinate = coordinateNum; import org.opensim.modeling.* frame = model.getJointSet().get(jointName).getParentFrame(); offsetFrame = PhysicalOffsetFrame.safeDownCast(frame); diff --git a/src/JointModelPersonalization/adjustModelFromOptimizerOutput.m b/src/JointModelPersonalization/adjustModelFromOptimizerOutput.m index 60312a51a..435bfdf40 100644 --- a/src/JointModelPersonalization/adjustModelFromOptimizerOutput.m +++ b/src/JointModelPersonalization/adjustModelFromOptimizerOutput.m @@ -32,6 +32,6 @@ values) outputModel = Model(model); for i = 1:length(values) - functions{i}(values(i), outputModel); + functions{i}(round(values(i), 10), outputModel); end end \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/Optimization/CostComputations/calcMuscleTendonNonLinearConstraints.m b/src/MuscleTendonPersonalization/Optimization/CostComputations/calcMuscleTendonNonLinearConstraints.m index f364e7474..6b70d1f98 100644 --- a/src/MuscleTendonPersonalization/Optimization/CostComputations/calcMuscleTendonNonLinearConstraints.m +++ b/src/MuscleTendonPersonalization/Optimization/CostComputations/calcMuscleTendonNonLinearConstraints.m @@ -25,7 +25,7 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function [c,ceq] = calcMuscleTendonNonLinearConstraints(values, ... +function [c, ceq] = calcMuscleTendonNonLinearConstraints(values, ... primaryValues, isIncluded, experimentalData, params) ceq = []; From e004392aa669672b6ecb1c9f7a8e0e00fd5508cb Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sat, 21 Oct 2023 16:32:28 -0500 Subject: [PATCH 191/365] change toe_coordinate to hindfoot_body Co-Authored-By: Spencer Williams <54556034+SpencerTWilliams@users.noreply.github.com> --- ...iteGroundContactPersonalizationOsimxFile.m | 5 +- ...GroundContactPersonalizationSettingsTree.m | 88 ++++++++++--------- ...repareGroundContactPersonalizationInputs.m | 17 +++- .../parseGroundContactSurfaces.m | 47 ++++++---- src/core/osimx/buildGcpContactSurface.m | 8 +- src/core/osimx/parseOsimxFile.m | 8 +- 6 files changed, 99 insertions(+), 74 deletions(-) diff --git a/src/GroundContactPersonalization/Saving/writeGroundContactPersonalizationOsimxFile.m b/src/GroundContactPersonalization/Saving/writeGroundContactPersonalizationOsimxFile.m index f0f460827..f5d8277d0 100644 --- a/src/GroundContactPersonalization/Saving/writeGroundContactPersonalizationOsimxFile.m +++ b/src/GroundContactPersonalization/Saving/writeGroundContactPersonalizationOsimxFile.m @@ -61,9 +61,8 @@ function writeGroundContactPersonalizationOsimxFile(inputs, ... newSurface.momentColumns = string(inputs.surfaces{foot}.momentColumns)'; newSurface.electricalCenterColumns = string(inputs.surfaces{foot} ... .electricalCenterColumns)'; - newSurface.toesCoordinateName = inputs.surfaces{foot} ... - .toesCoordinateName; - newSurface.toesJointName = inputs.surfaces{foot}.toesJointName; + newSurface.hindfootBodyName = inputs.surfaces{foot} ... + .hindfootBodyName; newSurface.toeMarker = inputs.surfaces{foot}.markerNames.toe; newSurface.medialMarker = inputs.surfaces{foot}.markerNames.medial; newSurface.lateralMarker = inputs.surfaces{foot}.markerNames.lateral; diff --git a/src/GroundContactPersonalization/parseGroundContactPersonalizationSettingsTree.m b/src/GroundContactPersonalization/parseGroundContactPersonalizationSettingsTree.m index 96974cdae..0e446e6bd 100644 --- a/src/GroundContactPersonalization/parseGroundContactPersonalizationSettingsTree.m +++ b/src/GroundContactPersonalization/parseGroundContactPersonalizationSettingsTree.m @@ -1,7 +1,7 @@ % This function is part of the NMSM Pipeline, see file for full license. % % Parses XML settings for Ground contact personalization to determine -% intial inputs and parameters for optimization. +% intial inputs and parameters for optimization. % % (struct) -> (struct, struct, string) % Returns the input values for Ground Contact Personalization. @@ -53,13 +53,13 @@ getFieldByNameOrAlternate(tree, 'latching_velocity', '0.05'))); inputs.gridWidth = str2double(getTextFromField( ... getFieldByNameOrAlternate(tree, 'grid_width', '5'))); -if isempty(inputs.gridWidth) || isnan(inputs.gridWidth) - inputs.gridWidth = 5; +if isempty(inputs.gridWidth) || isnan(inputs.gridWidth) + inputs.gridWidth = 5; end inputs.gridHeight = str2double(getTextFromField( ... getFieldByNameOrAlternate(tree, 'grid_height', '15'))); if isempty(inputs.gridHeight) || isnan(inputs.gridHeight) - inputs.gridHeight = 15; + inputs.gridHeight = 15; end if(~isempty(inputDirectory)) try @@ -86,7 +86,7 @@ % (struct, struct) -> (struct) % Gets inputs specific to each foot, such as experimental kinematics and -% ground reactions and foot marker names. +% ground reactions and foot marker names. function output = getContactSurfaces(inputs, tree) contactSurfaces = getFieldByNameOrError(tree, 'GCPContactSurfaceSet') ... .GCPContactSurface; @@ -112,7 +112,7 @@ % (Model, string, struct) -> (struct) % Determines the first and last included time point based on the input -% kinematics motion file and start and end times given for the foot. +% kinematics motion file and start and end times given for the foot. function task = getMotionTime(bodyModel, motionFile, task) import org.opensim.modeling.Storage [~, ikTime, ~] = parseMotToComponents(... @@ -124,7 +124,7 @@ % (Model, string, struct) -> (struct) % Parses ground reaction data from a file. This will throw an exception if -% any needed column is missing. +% any needed column is missing. function task = getGroundReactions(bodyModel, grfFile, task) import org.opensim.modeling.Storage [grfColumnNames, grfTime, grfData] = parseMotToComponents(... @@ -160,47 +160,51 @@ % (struct, struct, struct) -> (struct) % Gets foot-specific options directly included in XML file, including -% names of markers and ground reaction columns. +% names of markers and ground reaction columns. function task = getFootData(tree) - task.isLeftFoot = strcmpi('true', ... - getFieldByNameOrError(tree, 'is_left_foot').Text); - task.toesCoordinateName = getFieldByNameOrError(tree, ... - 'toe_coordinate').Text; - task.markerNames.toe = getFieldByNameOrError(tree, ... - 'toe_marker').Text; - task.markerNames.medial = getFieldByNameOrError(tree, ... - 'medial_marker').Text; - task.markerNames.lateral = getFieldByNameOrError(tree, ... - 'lateral_marker').Text; - task.markerNames.heel = getFieldByNameOrError(tree, ... - 'heel_marker').Text; - task.markerNames.midfootSuperior = getFieldByNameOrError(tree, ... - 'midfoot_superior_marker').Text; - task.startTime = str2double(getFieldByNameOrError(tree, ... - 'start_time').Text); - task.endTime = str2double(getFieldByNameOrError(tree, ... - 'end_time').Text); - task.beltSpeed = str2double(getFieldByNameOrError(tree, ... - 'belt_speed').Text); - task.forceColumns = cell2mat(split(getFieldByNameOrError(tree, ... - 'force_columns').Text)); - task.momentColumns = cell2mat(split(getFieldByNameOrError(tree, ... - 'moment_columns').Text)); - task.electricalCenterColumns = cell2mat(split( ... - getFieldByNameOrError(tree, 'electrical_center_columns').Text)); +task.isLeftFoot = strcmpi('true', ... + getFieldByNameOrError(tree, 'is_left_foot').Text); +hindfootBodyName = getFieldByName(tree, "hindfoot_body").Text; +if ~hindfootBodyName + throw(MException('', " is replaced by in the GCP settings file.")) +else + task.hindfootBodyName = hindfootBodyName; +end +task.markerNames.toe = getFieldByNameOrError(tree, ... + 'toe_marker').Text; +task.markerNames.medial = getFieldByNameOrError(tree, ... + 'medial_marker').Text; +task.markerNames.lateral = getFieldByNameOrError(tree, ... + 'lateral_marker').Text; +task.markerNames.heel = getFieldByNameOrError(tree, ... + 'heel_marker').Text; +task.markerNames.midfootSuperior = getFieldByNameOrError(tree, ... + 'midfoot_superior_marker').Text; +task.startTime = str2double(getFieldByNameOrError(tree, ... + 'start_time').Text); +task.endTime = str2double(getFieldByNameOrError(tree, ... + 'end_time').Text); +task.beltSpeed = str2double(getFieldByNameOrError(tree, ... + 'belt_speed').Text); +task.forceColumns = cell2mat(split(getFieldByNameOrError(tree, ... + 'force_columns').Text)); +task.momentColumns = cell2mat(split(getFieldByNameOrError(tree, ... + 'moment_columns').Text)); +task.electricalCenterColumns = cell2mat(split( ... + getFieldByNameOrError(tree, 'electrical_center_columns').Text)); end % (Array of double, Array of double) -> (None) % Confirms that the time points from ground reaction and kinematics data -% match in length and value. +% match in length and value. function verifyTime(grfTime, ikTime) - if size(ikTime) ~= size(grfTime) - throw(MException('', ['IK and GRF time columns have ' ... - 'different lengths'])) - end - if any(abs(ikTime - grfTime) > 0.005) - throw(MException('', 'IK and GRF time points are not equal')) - end +if size(ikTime) ~= size(grfTime) + throw(MException('', ['IK and GRF time columns have ' ... + 'different lengths'])) +end +if any(abs(ikTime - grfTime) > 0.005) + throw(MException('', 'IK and GRF time points are not equal')) +end end % Parses initial values. diff --git a/src/GroundContactPersonalization/prepareGroundContactPersonalizationInputs.m b/src/GroundContactPersonalization/prepareGroundContactPersonalizationInputs.m index 109be671f..6358a7f9b 100644 --- a/src/GroundContactPersonalization/prepareGroundContactPersonalizationInputs.m +++ b/src/GroundContactPersonalization/prepareGroundContactPersonalizationInputs.m @@ -57,9 +57,20 @@ % Prepares optimization values specific to a foot. function surface = prepareInputsForFoot(surface, inputs, ... meanMarkerLocations, surfaceNumber) -surface.toesJointName = char(Model(inputs.bodyModel ... - ).getCoordinateSet().get(surface.toesCoordinateName).getJoint( ... - ).getName()); +model = Model(inputs.bodyModel); +joints = getBodyJointNames(model, surface.hindfootBodyName); +assert(length(joints) == 2, "GCP supports two segment foot models only"); +for i = 1 : length(joints) + [parent, ~] = getJointBodyNames(model, joints(i)); + if strcmp(parent, surface.hindfootBodyName) + surface.toesJointName = joints(i); + break + end +end +assert(model.getJointSet().get(surface.toesJointName) ... + .numCoordinates() == 1, "GCP toes joint must be a pin joint"); +surface.toesCoordinateName = model.getJointSet() ... + .get(surface.toesJointName).getCoordinate().getName().toCharArray()'; [surface.hindfootBodyName, surface.toesBodyName] = ... getJointBodyNames(Model(inputs.bodyModel), surface.toesJointName); surface.coordinatesOfInterest = findGCPFreeCoordinates(... diff --git a/src/TreatmentOptimization/parseGroundContactSurfaces.m b/src/TreatmentOptimization/parseGroundContactSurfaces.m index 3e9b98e43..495238d73 100644 --- a/src/TreatmentOptimization/parseGroundContactSurfaces.m +++ b/src/TreatmentOptimization/parseGroundContactSurfaces.m @@ -61,25 +61,34 @@ end end -function output = getParentChildSprings(osimModel, contactSurfaces) -output = contactSurfaces; -output.parentSpringPointsOnBody = []; -output.parentSpringConstants = []; -output.childSpringPointsOnBody = []; -output.childSpringConstants = []; -[output.parentBodyName, output.childBodyName] = ... - getJointBodyNames(osimModel, contactSurfaces.toesJointName); -for j = 1:length(contactSurfaces.springs) - if strcmp(contactSurfaces.springs{j}.parentBody, output.parentBodyName) - output.parentSpringPointsOnBody(end+1, :) = ... - contactSurfaces.springs{j}.location; - output.parentSpringConstants(end+1) = ... - contactSurfaces.springs{j}.springConstant; - elseif strcmp(contactSurfaces.springs{j}.parentBody, output.childBodyName) - output.childSpringPointsOnBody(end+1, :) = ... - contactSurfaces.springs{j}.location; - output.childSpringConstants(end+1) = ... - contactSurfaces.springs{j}.springConstant; +function contactSurface = getParentChildSprings(osimModel, contactSurface) +contactSurface.parentSpringPointsOnBody = []; +contactSurface.parentSpringConstants = []; +contactSurface.childSpringPointsOnBody = []; +contactSurface.childSpringConstants = []; +joints = getBodyJointNames(osimModel, contactSurface.hindfootBodyName); +assert(length(joints) == 2, ... + "Treatment Optimization supports two segment foot models only"); +for i = 1 : length(joints) + [parent, ~] = getJointBodyNames(osimModel, joints(i)); + if strcmp(parent, contactSurface.hindfootBodyName) + contactSurface.toesJointName = joints(i); + break + end +end +[contactSurface.parentBodyName, contactSurface.childBodyName] = ... + getJointBodyNames(osimModel, contactSurface.toesJointName); +for j = 1:length(contactSurface.springs) + if strcmp(contactSurface.springs{j}.parentBody, contactSurface.parentBodyName) + contactSurface.parentSpringPointsOnBody(end+1, :) = ... + contactSurface.springs{j}.location; + contactSurface.parentSpringConstants(end+1) = ... + contactSurface.springs{j}.springConstant; + elseif strcmp(contactSurface.springs{j}.parentBody, contactSurface.childBodyName) + contactSurface.childSpringPointsOnBody(end+1, :) = ... + contactSurface.springs{j}.location; + contactSurface.childSpringConstants(end+1) = ... + contactSurface.springs{j}.springConstant; end end end diff --git a/src/core/osimx/buildGcpContactSurface.m b/src/core/osimx/buildGcpContactSurface.m index 4287bb85f..f0aeef189 100644 --- a/src/core/osimx/buildGcpContactSurface.m +++ b/src/core/osimx/buildGcpContactSurface.m @@ -65,11 +65,9 @@ contact{i}.electrical_center_columns.Text = ... convertStringsToChars(strjoin(contactSurface.electricalCenterColumns)); -contact{i}.toes_coordinate.Comment = ... - 'Name of the toe angle coordinate in the model file'; -contact{i}.toes_coordinate.Text = contactSurface.toesCoordinateName; -contact{i}.toes_joint.Comment = 'Name of the toe joint in the model file'; -contact{i}.toes_joint.Text = contactSurface.toesJointName; +contact{i}.hindfoot_body.Comment = ... + 'Name of the hindfoot body in the model file'; +contact{i}.hindfoot_body.Text = contactSurface.hindfootBodyName; contact{i}.toe_marker.Comment = ... 'Names of the markers used to define the foot area'; diff --git a/src/core/osimx/parseOsimxFile.m b/src/core/osimx/parseOsimxFile.m index 448fb1ead..2c7215502 100644 --- a/src/core/osimx/parseOsimxFile.m +++ b/src/core/osimx/parseOsimxFile.m @@ -103,8 +103,12 @@ contactSurface.momentColumns = parseSpaceSeparatedList(tree, "moment_columns"); contactSurface.electricalCenterColumns = parseSpaceSeparatedList(tree, "electrical_center_columns"); -contactSurface.toesCoordinateName = getFieldByNameOrError(tree, "toes_coordinate").Text; -contactSurface.toesJointName = getFieldByNameOrError(tree, "toes_joint").Text; +hindfootBodyName = getFieldByName(tree, "hindfoot_body").Text; +if ~hindfootBodyName + throw(MException('', " is replaced by in the osimx file. Please update your osimx file.")) +else +contactSurface.hindfootBodyName = hindfootBodyName; +end contactSurface.toeMarker = getFieldByNameOrError(tree, "toe_marker").Text; contactSurface.medialMarker = getFieldByNameOrError(tree, "medial_marker").Text; From 51f41dac801ab49e0b7c0ba9d160c9137f62e9ca Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sat, 28 Oct 2023 20:54:45 -0500 Subject: [PATCH 192/365] fix assembly error breaking jmp --- .../computeInverseKinematicsSquaredError.m | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/JointModelPersonalization/KinematicCalibration/computeInverseKinematicsSquaredError.m b/src/JointModelPersonalization/KinematicCalibration/computeInverseKinematicsSquaredError.m index 8d49b7b12..16a7b1369 100644 --- a/src/JointModelPersonalization/KinematicCalibration/computeInverseKinematicsSquaredError.m +++ b/src/JointModelPersonalization/KinematicCalibration/computeInverseKinematicsSquaredError.m @@ -37,23 +37,29 @@ times = markerTable.getIndependentColumn(); error = []; frameCounter = 0; -for i=1:numFrames %start time is set so start with recording error - ikSolver.track(state); - error = [error calculateFrameSquaredError(ikSolver)]; - frameCounter = frameCounter + 1; - if(state.getTime() + 1/frequency > finishTime);break;end - time = times.get(markerTable.getNearestRowIndexForTime( ... - state.getTime() + 1/frequency - 0.00001)); - state.setTime(double(time)); +persistent errorSize; +try + for i=1:numFrames %start time is set so start with recording error + ikSolver.track(state); + error = [error calculateFrameSquaredError(ikSolver)]; + frameCounter = frameCounter + 1; + if(state.getTime() + 1/frequency > finishTime);break;end + time = times.get(markerTable.getNearestRowIndexForTime( ... + state.getTime() + 1/frequency - 0.00001)); + state.setTime(double(time)); + end + error = error / sqrt(frameCounter); + errorSize = size(error); +catch + error = 1e2 * ones(errorSize); end -error = error / sqrt(frameCounter); end % (Model, InverseKinematicsSolver, MarkersReference, struct) => % (State, number, number, number) % Parses params for the IKSolver function [state, numFrames, frequency, finishTime] = ... - prepareFrameIterations(model, ikSolver, markersReference, params) + prepareFrameIterations(model, ikSolver, markersReference, params) state = initModelSystem(model); state.setTime(valueOrAlternate(params, 'startTime', ... markersReference.getValidTimeRange().get(0))); From 42f70f2cc809feb320e87e4abfd406da719c6fb9 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Sun, 29 Oct 2023 22:40:31 -0500 Subject: [PATCH 193/365] GCV spline utility functions --- src/core/GCVSpline/evaluateGcvSpline.m | 52 ++++++++++++++++++++ src/core/GCVSpline/makeGcvSplineSet.m | 66 ++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 src/core/GCVSpline/evaluateGcvSpline.m create mode 100644 src/core/GCVSpline/makeGcvSplineSet.m diff --git a/src/core/GCVSpline/evaluateGcvSpline.m b/src/core/GCVSpline/evaluateGcvSpline.m new file mode 100644 index 000000000..b3464e5c4 --- /dev/null +++ b/src/core/GCVSpline/evaluateGcvSpline.m @@ -0,0 +1,52 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% Evaluates a GCV spline for its fitted values or a derivative at a set of +% time points. The spline to evaluate is chosen by the given column label, +% which can be provided as either an integer (the raw index in the spline +% set) or a string defining a column label that should exist in the spline +% set. The derivative level is given as an integer (0 for position, 1 for +% velocity, 2 for acceleration, etc.). +% +% (GCVSplineSet, string OR int, Array of double, int) -> (Array of double) +% Evaluate GCV spline values or derivatives at a set of time points. + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Spencer Williams % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function values = evaluateGcvSpline(splineSet, columnLabel, time, ... + derivative) +values = zeros(1, length(time)); + +if isstring(columnLabel) || ischar(columnLabel) + columnLabel = splineSet.getIndex(columnLabel); + assert(index > -1, "The specified coordinate is not included in" + ... + " the spline set.") +end + +if nargin < 4 + derivative = 0; +end + +for i = 1 : length(time) + values(i) = splineSet.evaluate(columnLabel, derivative, time(i)); +end +end diff --git a/src/core/GCVSpline/makeGcvSplineSet.m b/src/core/GCVSpline/makeGcvSplineSet.m new file mode 100644 index 000000000..13ef9f72e --- /dev/null +++ b/src/core/GCVSpline/makeGcvSplineSet.m @@ -0,0 +1,66 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% Fit data with a GCVSplineSet, a set of GCV splines defined by the OpenSim +% API. The arguments represent a time column, data to fit, column labels, +% the degree of splines to fit, and a smoothing parameter. The first three +% arguments are required, and the last two are optional, defaulting to +% degree 5 and no smoothing. +% +% (Array of double, 2D Array of double, Array of string, int, double) -> +% (GCVSplineSet) +% +% Fit data with a set of GCV splines. + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Spencer Williams % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function splineSet = makeGcvSplineSet(time, data, columnLabels, degree, ... + smoothingParameter) +import org.opensim.modeling.* + +if length(time) ~= size(data, 1) + data = data'; +end +assert(length(time) == size(data, 1), "Length of time column must" + ... + " equal number of frames of data to spline-fit.") +assert(length(columnLabels)==size(data, 2), "Number of column labels" + ... + " must match number of data columns to spline-fit.") + +timeVec = StdVectorDouble(time); +matrix = Matrix.createFromMat(data); +labels = StdVectorString(columnLabels); +table = TimeSeriesTable(timeVec, matrix, labels); + +if nargin < 4 + degree = 5; +elseif isempty(degree) + degree = 5; +end + +if nargin < 5 + smoothingParameter = 0; +elseif isempty(smoothingParameter) + smoothingParameter = 0; +end + +splineSet = GCVSplineSet(table, labels, degree, smoothingParameter); +end From 8dbae4bfa59765bbd059c42e00eca7cf0a3a8df4 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Sun, 29 Oct 2023 22:46:07 -0500 Subject: [PATCH 194/365] Update project path --- .../BMJUj3XqFRkbFNxbqjFRrHNTbQUd.xml | 2 ++ .../BMJUj3XqFRkbFNxbqjFRrHNTbQUp.xml | 2 ++ .../XSNprDeGA4i4IQx_G-seoboQ75Id.xml | 2 ++ .../XSNprDeGA4i4IQx_G-seoboQ75Ip.xml | 2 ++ .../yrfuaX-0wpNxMk8pjXQImwzryuAd.xml | 2 ++ .../yrfuaX-0wpNxMk8pjXQImwzryuAp.xml | 2 ++ 6 files changed, 12 insertions(+) create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/BMJUj3XqFRkbFNxbqjFRrHNTbQUd.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/BMJUj3XqFRkbFNxbqjFRrHNTbQUp.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/XSNprDeGA4i4IQx_G-seoboQ75Id.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/XSNprDeGA4i4IQx_G-seoboQ75Ip.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/yrfuaX-0wpNxMk8pjXQImwzryuAd.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/yrfuaX-0wpNxMk8pjXQImwzryuAp.xml diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/BMJUj3XqFRkbFNxbqjFRrHNTbQUd.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/BMJUj3XqFRkbFNxbqjFRrHNTbQUd.xml new file mode 100644 index 000000000..2662131a7 --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/BMJUj3XqFRkbFNxbqjFRrHNTbQUd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/BMJUj3XqFRkbFNxbqjFRrHNTbQUp.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/BMJUj3XqFRkbFNxbqjFRrHNTbQUp.xml new file mode 100644 index 000000000..caf822a3c --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/BMJUj3XqFRkbFNxbqjFRrHNTbQUp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/XSNprDeGA4i4IQx_G-seoboQ75Id.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/XSNprDeGA4i4IQx_G-seoboQ75Id.xml new file mode 100644 index 000000000..94852e23a --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/XSNprDeGA4i4IQx_G-seoboQ75Id.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/XSNprDeGA4i4IQx_G-seoboQ75Ip.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/XSNprDeGA4i4IQx_G-seoboQ75Ip.xml new file mode 100644 index 000000000..bcd50304e --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/XSNprDeGA4i4IQx_G-seoboQ75Ip.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/yrfuaX-0wpNxMk8pjXQImwzryuAd.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/yrfuaX-0wpNxMk8pjXQImwzryuAd.xml new file mode 100644 index 000000000..b3c45cfa0 --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/yrfuaX-0wpNxMk8pjXQImwzryuAd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/yrfuaX-0wpNxMk8pjXQImwzryuAp.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/yrfuaX-0wpNxMk8pjXQImwzryuAp.xml new file mode 100644 index 000000000..4456ca36e --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/yrfuaX-0wpNxMk8pjXQImwzryuAp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file From 36ddd6f66da48b0af40cb46a5a6ad3b286618ef4 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Sun, 29 Oct 2023 22:46:20 -0500 Subject: [PATCH 195/365] Fix checking for column label --- src/core/GCVSpline/evaluateGcvSpline.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/GCVSpline/evaluateGcvSpline.m b/src/core/GCVSpline/evaluateGcvSpline.m index b3464e5c4..10e9969b3 100644 --- a/src/core/GCVSpline/evaluateGcvSpline.m +++ b/src/core/GCVSpline/evaluateGcvSpline.m @@ -38,7 +38,7 @@ if isstring(columnLabel) || ischar(columnLabel) columnLabel = splineSet.getIndex(columnLabel); - assert(index > -1, "The specified coordinate is not included in" + ... + assert(columnLabel > -1, "The specified coordinate is not included in" + ... " the spline set.") end From cabcbba9e464f0f35ad16d638b61a881be500233 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 6 Nov 2023 22:34:55 -0600 Subject: [PATCH 196/365] Evaluate multiple GCV spline coordinates Co-Authored-By: Claire V. Hammond <61138239+cvhammond@users.noreply.github.com> --- ...aluateGcvSpline.m => evaluateGcvSplines.m} | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) rename src/core/GCVSpline/{evaluateGcvSpline.m => evaluateGcvSplines.m} (77%) diff --git a/src/core/GCVSpline/evaluateGcvSpline.m b/src/core/GCVSpline/evaluateGcvSplines.m similarity index 77% rename from src/core/GCVSpline/evaluateGcvSpline.m rename to src/core/GCVSpline/evaluateGcvSplines.m index 10e9969b3..8e6a83372 100644 --- a/src/core/GCVSpline/evaluateGcvSpline.m +++ b/src/core/GCVSpline/evaluateGcvSplines.m @@ -32,21 +32,29 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function values = evaluateGcvSpline(splineSet, columnLabel, time, ... +function values = evaluateGcvSplines(splineSet, columnLabels, time, ... derivative) -values = zeros(1, length(time)); +values = zeros(length(time), length(columnLabels)); -if isstring(columnLabel) || ischar(columnLabel) - columnLabel = splineSet.getIndex(columnLabel); - assert(columnLabel > -1, "The specified coordinate is not included in" + ... - " the spline set.") +if iscell(columnLabels) + columnLabels = string(columnLabels); +end +if isstring(columnLabels) || ischar(columnLabels) + for i = 1 : length(columnLabels) + columnLabels(i) = splineSet.getIndex(columnLabels(i)); + assert(str2double(columnLabels(i)) > -1, "The specified coordinate is not included in" + ... + " the spline set.") + end + columnLabels = str2double(columnLabels); end if nargin < 4 derivative = 0; end -for i = 1 : length(time) - values(i) = splineSet.evaluate(columnLabel, derivative, time(i)); +for i = 1 : length(columnLabels) + for j = 1 : length(time) + values(j, i) = splineSet.evaluate(columnLabels(i), derivative, time(j)); + end end end From b6507fbe43a625aa5556ea71cb6594384ee1e6c1 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 6 Nov 2023 22:35:07 -0600 Subject: [PATCH 197/365] Remove GCV smoothing parameter Co-Authored-By: Claire V. Hammond <61138239+cvhammond@users.noreply.github.com> --- src/core/GCVSpline/makeGcvSplineSet.m | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/core/GCVSpline/makeGcvSplineSet.m b/src/core/GCVSpline/makeGcvSplineSet.m index 13ef9f72e..1cc9643c3 100644 --- a/src/core/GCVSpline/makeGcvSplineSet.m +++ b/src/core/GCVSpline/makeGcvSplineSet.m @@ -33,8 +33,7 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function splineSet = makeGcvSplineSet(time, data, columnLabels, degree, ... - smoothingParameter) +function splineSet = makeGcvSplineSet(time, data, columnLabels, degree) import org.opensim.modeling.* if length(time) ~= size(data, 1) @@ -56,11 +55,5 @@ degree = 5; end -if nargin < 5 - smoothingParameter = 0; -elseif isempty(smoothingParameter) - smoothingParameter = 0; -end - -splineSet = GCVSplineSet(table, labels, degree, smoothingParameter); +splineSet = GCVSplineSet(table, labels, degree); end From 55e07eb532a5639203dfa03f9d343833a46b3055 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 6 Nov 2023 22:35:47 -0600 Subject: [PATCH 198/365] Fix finding discrete user-defined cost term allowable error Co-Authored-By: Claire V. Hammond <61138239+cvhammond@users.noreply.github.com> --- .../SetupBounds/makeMaxAllowableError.m | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/TreatmentOptimization/SetupBounds/makeMaxAllowableError.m b/src/TreatmentOptimization/SetupBounds/makeMaxAllowableError.m index 03213aad4..e0d4ac2e2 100644 --- a/src/TreatmentOptimization/SetupBounds/makeMaxAllowableError.m +++ b/src/TreatmentOptimization/SetupBounds/makeMaxAllowableError.m @@ -47,6 +47,14 @@ continuousMaxAllowableError = cat(2, ... continuousMaxAllowableError, ... costTerm.maxAllowableError); + elseif strcmp(costTerm.cost_term_type, "discrete") + discreteMaxAllowableError = cat(2, ... + discreteMaxAllowableError, ... + costTerm.maxAllowableError); + else + throw(MException('', "User-defined cost terms must " + ... + "be specified as either discrete or continuous " + ... + "in the cost_term_type field.")) end elseif any(ismember(costTerm.type, discreteAllowedTypes)) discreteMaxAllowableError = cat(2, ... From b5b750b4a200cc7114384ede61b21378703d50ce Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 6 Nov 2023 22:36:18 -0600 Subject: [PATCH 199/365] Fix torque control bounds Co-Authored-By: Claire V. Hammond <61138239+cvhammond@users.noreply.github.com> --- .../OptimalControlSetupFunctions/setupGpopsInitialGuess.m | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m index 1b9f7dc76..d2b4f2599 100644 --- a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m +++ b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m @@ -93,7 +93,11 @@ controls = [controls, inputs.initialTorqueControls]; else if ~isempty(valueOrAlternate(inputs, "torqueControllerCoordinateNames", [])) - controls = [controls, inputs.experimentalJointMoments]; + stateTorqueControls = subsetDataByCoordinates( ... + inputs.experimentalJointMoments, ... + inputs.coordinateNames, ... + inputs.torqueControllerCoordinateNames); + controls = [controls, stateTorqueControls]; end end guess.phase.control = scaleToBounds(controls, inputs.maxControl, ... From d67d3c4e0c9dcb7d151daa3f64261fbbf2cf42c6 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 6 Nov 2023 22:36:34 -0600 Subject: [PATCH 200/365] GCV splines in treatment optimization Co-Authored-By: Claire V. Hammond <61138239+cvhammond@users.noreply.github.com> --- .../calcTrackingControllerIntegrand.m | 23 ++++++++---------- .../calcTrackingCoordinateIntegrand.m | 12 ++++------ .../calcTrackingExternalForcesIntegrand.m | 13 ++++------ .../calcTrackingExternalMomentsIntegrand.m | 17 +++++-------- ...calcTrackingInverseDynamicLoadsIntegrand.m | 9 ++----- .../calcTrackingMarkerPosition.m | 7 +++--- .../calcTrackingMuscleActivationIntegrand.m | 16 ++++--------- .../calcTrackingSpeedIntegrand.m | 17 ++++++------- .../setupMuscleSynergies.m | 5 ++-- .../setupTorqueControls.m | 4 ++-- .../gpops/makeGpopsValuesAsStruct.m | 18 +++++++------- .../makeExperimentalDataSplines.m | 24 ++++++++----------- .../makeMarkerTracking.m | 4 ++-- .../makeStateDerivatives.m | 24 +++++++++---------- 14 files changed, 77 insertions(+), 116 deletions(-) diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m index 07455b2b7..b966a3bf0 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m @@ -30,33 +30,30 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcTrackingControllerIntegrand(costTerm, auxdata, ... +function cost = calcTrackingControllerIntegrand(costTerm, inputs, ... values, time, controllerName) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); -if strcmp(auxdata.controllerType, 'synergy') +if strcmp(inputs.controllerType, 'synergy') indx = find(strcmp(convertCharsToStrings( ... - auxdata.synergyLabels), controllerName)); + inputs.synergyLabels), controllerName)); if ~isempty(indx) synergyActivations = ... - fnval(auxdata.splineSynergyActivations, time)'; + evaluateGcvSplines(inputs.splineSynergyActivations, ... + inputs.synergyLabels, time); cost = calcTrackingCostArrayTerm(synergyActivations, ... values.controlSynergyActivations, indx); return end end indx1 = find(strcmp(convertCharsToStrings( ... - strcat(auxdata.torqueLabels, '_moment')), controllerName)); + strcat(inputs.torqueLabels, '_moment')), controllerName)); indx2 = find(strcmp(convertCharsToStrings( ... - strcat(auxdata.torqueControllerCoordinateNames, '_moment')), ... + strcat(inputs.torqueControllerCoordinateNames, '_moment')), ... controllerName)); -if auxdata.splineTorqueControls.dim > 1 - experimentalJointMoments = ... - fnval(auxdata.splineTorqueControls, time)'; -else - experimentalJointMoments = ... - fnval(auxdata.splineTorqueControls, time); -end +experimentalJointMoments = ... + evaluateGcvSplines(inputs.splineTorqueControls, ... + inputs.torqueLabels, time); cost = experimentalJointMoments(:, indx1) - ... values.torqueControls(:, indx2); if normalizeByFinalTime diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m index 507341603..440e305c4 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m @@ -28,23 +28,19 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcTrackingCoordinateIntegrand(costTerm, auxdata, time, ... +function cost = calcTrackingCoordinateIntegrand(costTerm, inputs, time, ... statePositions, coordinateName) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); -indx = find(strcmp(convertCharsToStrings(auxdata.statesCoordinateNames), ... +indx = find(strcmp(convertCharsToStrings(inputs.statesCoordinateNames), ... coordinateName)); if isempty(indx) throw(MException('CostTermError:CoordinateNotInState', ... strcat("Coordinate ", coordinateName, " is not in the ", ... ""))) end -if auxdata.splineJointAngles.dim > 1 - experimentalJointAngles = fnval(auxdata.splineJointAngles, time)'; -else - experimentalJointAngles = fnval(auxdata.splineJointAngles, time); -end - +experimentalJointAngles = evaluateGcvSplines( ... + inputs.splineJointAngles, inputs.coordinateNames, time); cost = calcTrackingCostArrayTerm(experimentalJointAngles, ... statePositions, indx); diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m index b1bd8bdbc..171edf400 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m @@ -36,15 +36,10 @@ indx = find(strcmp(convertCharsToStrings(params.contactSurfaces{i}. ... forceColumns), forceName)); if ~isempty(indx) - if params.splineExperimentalGroundReactionForces{i}.dim > 1 - experimentalGroundReactions = ... - fnval(params.splineExperimentalGroundReactionForces{i}, ... - time)'; - else - experimentalGroundReactions = ... - fnval(params.splineExperimentalGroundReactionForces{i}, ... - time); - end + experimentalGroundReactions = ... + evaluateGcvSplines( ... + params.splineExperimentalGroundReactionForces{i}, 0:2, ... + time); cost = calcTrackingCostArrayTerm(... experimentalGroundReactions, groundReactionsForces{i}, ... indx); diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m index af57e99bb..f84979545 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m @@ -1,10 +1,10 @@ % This function is part of the NMSM Pipeline, see file for full license. % % This function calculates the difference between the experimental and -% predicted ground reaction moments for the specified moment. +% predicted ground reaction moments for the specified moment. % % (struct, 2D matrix, Array of number, Array of string) -> (Array of number) -% +% % ----------------------------------------------------------------------- % % The NMSM Pipeline is a toolkit for model personalization and treatment % @@ -36,15 +36,10 @@ indx = find(strcmp(convertCharsToStrings(params.contactSurfaces{i}. ... momentColumns), loadName)); if ~isempty(indx) - if params.splineExperimentalGroundReactionMoments{i}.dim > 1 - experimentalGroundReactions = ... - fnval(params.splineExperimentalGroundReactionMoments{i}, ... - time)'; - else - experimentalGroundReactions = ... - fnval(params.splineExperimentalGroundReactionMoments{i}, ... - time); - end + experimentalGroundReactions = ... + evaluateGcvSplines( ... + params.splineExperimentalGroundReactionMoments{i}, 0:2, ... + time); cost = calcTrackingCostArrayTerm(... experimentalGroundReactions, groundReactionMoments{i}, ... indx); diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m index 6b30c5507..5a2c9d5a2 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m @@ -36,13 +36,8 @@ loadName = erase(loadName, '_force'); indx = find(strcmp(convertCharsToStrings(inputs.coordinateNames), ... loadName)); - -if inputs.splineJointMoments.dim > 1 - experimentalJointMoments = fnval(inputs.splineJointMoments, time)'; -else - experimentalJointMoments = fnval(inputs.splineJointMoments, time); -end - +experimentalJointMoments = evaluateGcvSplines( ... + inputs.splineJointMoments, inputs.inverseDynamicsMomentLabels, time); momentLabelsNoSuffix = erase(inputs.inverseDynamicsMomentLabels, '_moment'); momentLabelsNoSuffix = erase(momentLabelsNoSuffix, '_force'); includedJointMomentCols = ismember(momentLabelsNoSuffix, convertCharsToStrings(inputs.coordinateNames)); diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m index ce1d7e9d6..ec36e0228 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m @@ -28,10 +28,10 @@ % ----------------------------------------------------------------------- % function cost = calcTrackingMarkerPosition(costTerm, time, ... - markerPositions, auxdata) + markerPositions, inputs) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); -indx = find(strcmp(convertCharsToStrings(auxdata.trackedMarkerNames), ... +indx = find(strcmp(convertCharsToStrings(inputs.trackedMarkerNames), ... costTerm.marker)); if isempty(indx) throw(MException('CostTermError:MarkerDoesNotExist', ... @@ -39,7 +39,8 @@ "list of tracked markers"))) end experimentalMarkerPositions = ... - fnval(auxdata.splineMarkerPositions{indx}, time)'; + evaluateGcvSplines(inputs.splineMarkerPositions{indx}, ... + 0:2, time); cost = calcTrackingCostArrayTerm(experimentalMarkerPositions, ... markerPositions, indx); cost = cost + calcTrackingCostArrayTerm(experimentalMarkerPositions, ... diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m index 56e061bc2..d469726ef 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m @@ -29,21 +29,13 @@ % ----------------------------------------------------------------------- % function cost = calcTrackingMuscleActivationIntegrand(costTerm, ... - muscleActivations, time, params, muscleName) + muscleActivations, time, inputs, muscleName) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); -indx = find(strcmp(convertCharsToStrings(params.muscleNames), ... +indx = find(strcmp(convertCharsToStrings(inputs.muscleNames), ... muscleName)); - -if params.splineMuscleActivations.dim > 1 - experimentalMuscleActivations = ... - fnval(params.splineMuscleActivations, time)'; -else - experimentalMuscleActivations = ... - fnval(params.splineMuscleActivations, time); -end - -experimentalMuscleActivations = fnval(params.splineMuscleActivations, time)'; +experimentalMuscleActivations = evaluateGcvSplines( ... + inputs.splineMuscleActivations, inputs.muscleNames, time); cost = calcTrackingCostArrayTerm(experimentalMuscleActivations, ... muscleActivations, indx); if normalizeByFinalTime diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m index 914cd8b32..b1f1a9474 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m @@ -1,10 +1,10 @@ % This function is part of the NMSM Pipeline, see file for full license. % % This function calculates the difference between the experimental and -% predicted joint angles for the specified coordinate. +% predicted joint angles for the specified coordinate. % % (struct, Array of number, 2D matrix, Array of string) -> (Array of number) -% +% % ----------------------------------------------------------------------- % % The NMSM Pipeline is a toolkit for model personalization and treatment % @@ -28,26 +28,23 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcTrackingSpeedIntegrand(costTerm, auxdata, time, ... +function cost = calcTrackingSpeedIntegrand(costTerm, inputs, time, ... stateVelocities, coordinateName) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); -indx = find(strcmp(convertCharsToStrings(auxdata.statesCoordinateNames), ... +indx = find(strcmp(convertCharsToStrings(inputs.statesCoordinateNames), ... coordinateName)); if isempty(indx) throw(MException('CostTermError:CoordinateNotInState', ... strcat("Coordinate ", coordinateName, " is not in the ", ... ""))) end -if auxdata.splineJointVelocities.dim > 1 - experimentalJointVelocities = fnval(auxdata.splineJointVelocities, time)'; -else - experimentalJointVelocities = fnval(auxdata.splineJointVelocities, time); -end +experimentalJointVelocities = evaluateGcvSplines( ... + inputs.splineJointAngles, inputs.coordinateNames, time, 1); cost = calcTrackingCostArrayTerm(experimentalJointVelocities, ... stateVelocities, indx); - + if normalizeByFinalTime cost = cost / time(end); end diff --git a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupMuscleSynergies.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupMuscleSynergies.m index 62249ac91..677f1c11b 100644 --- a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupMuscleSynergies.m +++ b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupMuscleSynergies.m @@ -30,8 +30,9 @@ function inputs = setupMuscleSynergies(inputs) if strcmp(inputs.controllerType, 'synergy') - inputs.splineSynergyActivations = spaps(inputs.initialTime, ... - inputs.initialSynergyControls', 0.0000001); + inputs.splineSynergyActivations = makeGcvSplineSet( ... + inputs.initialTime, inputs.initialSynergyControls', ... + inputs.initialSynergyControlsLabels); inputs.synergyLabels = inputs.initialSynergyControlsLabels; end end \ No newline at end of file diff --git a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupTorqueControls.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupTorqueControls.m index a92b771eb..265298d4f 100644 --- a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupTorqueControls.m +++ b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupTorqueControls.m @@ -31,8 +31,8 @@ function inputs = setupTorqueControls(inputs) if isfield(inputs, "torqueControllerCoordinateNames") && ... ~isempty(inputs.torqueControllerCoordinateNames) -inputs.splineTorqueControls = spaps(inputs.initialTime, ... - inputs.initialTorqueControls', 0.0000001); +inputs.splineTorqueControls = makeGcvSplineSet(inputs.initialTime, ... + inputs.initialTorqueControls', inputs.initialTorqueControlsLabels); inputs.torqueLabels = inputs.initialTorqueControlsLabels; end end \ No newline at end of file diff --git a/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m b/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m index 6c05d77c8..138024047 100644 --- a/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m +++ b/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m @@ -50,8 +50,8 @@ length(inputs.statesCoordinateNames) + inputs.numSynergies); end values.torqueControls = control(:, ... - inputs.numCoordinates + 1 + inputs.numSynergies : ... - inputs.numCoordinates + inputs.numSynergies + ... + length(inputs.statesCoordinateNames) + 1 + inputs.numSynergies : ... + length(inputs.statesCoordinateNames) + inputs.numSynergies + ... length(inputs.torqueControllerCoordinateNames)); if strcmp(inputs.toolName, "TrackingOptimization") @@ -112,14 +112,12 @@ function [positions, velocities, accelerations] = recombineFullState( ... values, inputs) -positions = fnval(inputs.splineJointAngles, values.time)'; -velocities = fnval(inputs.splineJointVelocities, values.time)'; -accelerations = fnval(inputs.splineJointAccelerations, values.time)'; -if length(inputs.statesCoordinateNames) == 1 - positions = positions'; - velocities = velocities'; - accelerations = accelerations'; -end +positions = evaluateGcvSplines(inputs.splineJointAngles, ... + inputs.coordinateNames, values.time); +velocities = evaluateGcvSplines(inputs.splineJointAngles, ... + inputs.coordinateNames, values.time, 1); +accelerations = evaluateGcvSplines(inputs.splineJointAngles, ... + inputs.coordinateNames, values.time, 2); for i = 1:length(inputs.coordinateNames) index = find(ismember( ... inputs.statesCoordinateNames, inputs.coordinateNames{i})); diff --git a/src/TreatmentOptimization/makeExperimentalDataSplines.m b/src/TreatmentOptimization/makeExperimentalDataSplines.m index eb376742f..784be860d 100644 --- a/src/TreatmentOptimization/makeExperimentalDataSplines.m +++ b/src/TreatmentOptimization/makeExperimentalDataSplines.m @@ -30,24 +30,20 @@ % ----------------------------------------------------------------------- % function inputs = makeExperimentalDataSplines(inputs) -inputs.splineJointAngles = spaps(inputs.experimentalTime, ... - inputs.experimentalJointAngles', 0.0000001); -inputs.splineJointVelocities = spaps(inputs.experimentalTime, ... - inputs.experimentalJointVelocities', 0.0000001); -inputs.splineJointAccelerations = spaps(inputs.experimentalTime, ... - inputs.experimentalJointAccelerations', 0.0000001); -inputs.splineJointMoments = spaps(inputs.experimentalTime, ... - inputs.experimentalJointMoments', 0.0000001); +inputs.splineJointMoments = makeGcvSplineSet(inputs.experimentalTime, ... + inputs.experimentalJointMoments, ... + inputs.inverseDynamicsMomentLabels); if strcmp(inputs.controllerType, 'synergy') -inputs.splineMuscleActivations = spaps(inputs.experimentalTime, ... - inputs.experimentalMuscleActivations', 0.0000001); + inputs.splineMuscleActivations = makeGcvSplineSet( ... + inputs.experimentalTime, inputs.experimentalMuscleActivations, ... + inputs.muscleLabels); end for i = 1:length(inputs.contactSurfaces) inputs.splineExperimentalGroundReactionForces{i} = ... - spaps(inputs.experimentalTime, ... - inputs.contactSurfaces{i}.experimentalGroundReactionForces', 0.0000001); + makeGcvSplineSet(inputs.experimentalTime, ... + inputs.contactSurfaces{i}.experimentalGroundReactionForces, 0:2); inputs.splineExperimentalGroundReactionMoments{i} = ... - spaps(inputs.experimentalTime, ... - inputs.contactSurfaces{i}.experimentalGroundReactionMoments', 0.0000001); + makeGcvSplineSet(inputs.experimentalTime, ... + inputs.contactSurfaces{i}.experimentalGroundReactionMoments, 0:2); end end diff --git a/src/TreatmentOptimization/makeMarkerTracking.m b/src/TreatmentOptimization/makeMarkerTracking.m index 4f8c00c5d..89cc013c4 100644 --- a/src/TreatmentOptimization/makeMarkerTracking.m +++ b/src/TreatmentOptimization/makeMarkerTracking.m @@ -58,8 +58,8 @@ experimentalMarkerPositions = ... reshape(inputs.experimentalMarkerPositions(:, :, i), ... size(inputs.experimentalMarkerPositions, 1), []); - inputs.splineMarkerPositions{i} = spaps(inputs.experimentalTime, ... - experimentalMarkerPositions', 0.0000001); + inputs.splineMarkerPositions{i} = makeGcvSplineSet(inputs.experimentalTime, ... + experimentalMarkerPositions, ["x", "y", "z"]); end end end diff --git a/src/TreatmentOptimization/makeStateDerivatives.m b/src/TreatmentOptimization/makeStateDerivatives.m index 6053ac002..03f872989 100644 --- a/src/TreatmentOptimization/makeStateDerivatives.m +++ b/src/TreatmentOptimization/makeStateDerivatives.m @@ -29,17 +29,15 @@ % ----------------------------------------------------------------------- % function inputs = makeStateDerivatives(inputs, params) -points = length(inputs.experimentalTime); -interval = inputs.experimentalTime(2) - inputs.experimentalTime(1); -cutoffFrequency = ... - valueOrAlternate(params, "experimentalBSplineCutoffFrequency", 6); -numNodes = splFitWithCutoff(inputs.experimentalTime', ... - inputs.experimentalJointAngles', cutoffFrequency, 5); -[N, Np, Npp] = BSplineMatrices(5, numNodes, points, interval); -Nodes = N\inputs.experimentalJointAngles; -inputs.experimentalJointVelocities = Np * Nodes; -inputs.experimentalJointAccelerations = Npp * Nodes; -inputs.experimentalJointJerks = calcBSplineDerivative( ... - inputs.experimentalTime, inputs.experimentalJointAccelerations, ... - 2, numNodes); +inputs.splineJointAngles = makeGcvSplineSet(inputs.experimentalTime, ... + inputs.experimentalJointAngles', inputs.coordinateNames); +inputs.experimentalJointVelocities = evaluateGcvSplines( ... + inputs.splineJointAngles, inputs.coordinateNames, ... + inputs.experimentalTime, 1); +inputs.experimentalJointAccelerations = evaluateGcvSplines( ... + inputs.splineJointAngles, inputs.coordinateNames, ... + inputs.experimentalTime, 2); +inputs.experimentalJointJerks = evaluateGcvSplines( ... + inputs.splineJointAngles, inputs.coordinateNames, ... + inputs.experimentalTime, 3); end From 3ff976dfdd881e7326d6315ab2d2e1daae73bd77 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 6 Nov 2023 22:43:21 -0600 Subject: [PATCH 201/365] Section data files with GCV splines Co-Authored-By: Claire V. Hammond <61138239+cvhammond@users.noreply.github.com> --- src/Preprocessing/sectionDataFiles.m | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Preprocessing/sectionDataFiles.m b/src/Preprocessing/sectionDataFiles.m index 505efb316..5e8219865 100644 --- a/src/Preprocessing/sectionDataFiles.m +++ b/src/Preprocessing/sectionDataFiles.m @@ -43,7 +43,7 @@ function sectionDataFiles(fileNames, timePairs, numRows, prefix) for j=1:size(timePairs, 1) [filepath, name, ext] = fileparts(fileNames(i)); [newData, newTime] = cutData(data, time, timePairs(j,1), ... - timePairs(j,2), numRows); + timePairs(j,2), numRows, columnNames); newFileName = insertAfter(name, prefix, "_" + num2str(j)); writeToSto(columnNames, newTime, newData', fullfile(filepath, ... newFileName + ext)); @@ -52,8 +52,9 @@ function sectionDataFiles(fileNames, timePairs, numRows, prefix) end function [newData, newTime] = cutData(data, time, startTime, endTime, ... - numRows) + numRows, columnNames) newTime = linspace(startTime, endTime, numRows); -newData = spline(time, data, newTime); +splineSet = makeGcvSplineSet(time, data, columnNames); +newData = evaluateGcvSplines(splineSet, columnNames, newTime)'; end From 2802f208ef7d16bd73f8a102c5f53c3d52fb7c22 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Tue, 7 Nov 2023 22:41:58 -0600 Subject: [PATCH 202/365] Update lowpassFilter.m --- .../lowpassFilter.m | 39 ++++++++----------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/src/core/BSpline/CutoffFrequencyToNumberOfNodes/lowpassFilter.m b/src/core/BSpline/CutoffFrequencyToNumberOfNodes/lowpassFilter.m index 85129cfab..67f9fbfd3 100644 --- a/src/core/BSpline/CutoffFrequencyToNumberOfNodes/lowpassFilter.m +++ b/src/core/BSpline/CutoffFrequencyToNumberOfNodes/lowpassFilter.m @@ -29,41 +29,36 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function dataOut = lowpassFilter(timeIn,dataIn,order,fCutoff,plotFlag) - -% Pad front and back of data with complete data set to eliminate -% filter transients at the start and end -% This step assumes the data are periodic -npts = length(dataIn); -dt = timeIn(2,1)-timeIn(1,1); -dataInLong = [dataIn(1:npts-1,1); dataIn; dataIn(2:npts,1)]; - -% dataMirrored = zeros(npts-1,1); -% for i = 1:npts-1 -% dataMirrored(i,1) = dataIn(npts-i,1); -% end -% dataInLong = [dataMirrored; dataIn; dataMirrored]; -% dataInLong = [dataIn(1,1)*ones(npts-1,1); dataIn; dataIn(npts,1)*ones(npts-1,1)]; % Old choice -% dataInLong = [dataIn(npts,1)*ones(npts-1,1); dataIn; dataIn(1,1)*ones(npts-1,1)]; +function yFilt = lowpassFilter(t,y,order,fCutoff,plotFlag) +% Perform zero phase-lag lowpass Butterworth filter on input data % Set up filter inputs +dt = t(2)-t(1); fSample = 1/dt; % Sampling frequency in Hz normCutoff = fCutoff/(fSample/2); -% Create butterworth filer +% Create butterworth filter [b,a] = butter(order,normCutoff); -% Use filtfilt to perform zero phase-lag filtering -dataOutLong = filtfilt(b,a,dataInLong); -dataOut = dataOutLong(npts:2*npts-1); +% Demean input data to minimize edge effects +yMean = mean(y); +yDemeaned = y-yMean; + +% Use filtfilt to perform zero phase-lag filtering on demeaned data +yFiltDemeaned = filtfilt(b,a,yDemeaned); + +% Add mean value back into filtered data +yFilt = yFiltDemeaned+yMean; % Plot original and filtered data if desired if plotFlag - plot(timeIn,dataIn,'k-') + plot(t,y,'k-') hold on - plot(timeIn,dataOut,'b-') + plot(t,yFilt,'b-') xlabel('time') ylabel('data') pause close all +end + end \ No newline at end of file From e0da8c5c2056534abb596b24ce788e4e64f8156d Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Tue, 7 Nov 2023 22:51:19 -0600 Subject: [PATCH 203/365] GCV splines and filtering in preprocessing --- .../createMuscleTendonVelocity.m | 15 +++----- src/Preprocessing/sectionDataFiles.m | 6 ++- src/Preprocessing/splitIntoTrials.m | 38 ++++++++++++------- 3 files changed, 34 insertions(+), 25 deletions(-) diff --git a/src/Preprocessing/createMuscleTendonVelocity.m b/src/Preprocessing/createMuscleTendonVelocity.m index 10cf27158..c0f01d8a0 100644 --- a/src/Preprocessing/createMuscleTendonVelocity.m +++ b/src/Preprocessing/createMuscleTendonVelocity.m @@ -33,20 +33,15 @@ function createMuscleTendonVelocity(muscleTendonLengthFileName, ... storage = org.opensim.modeling.Storage(muscleTendonLengthFileName); lengthData = storageToDoubleMatrix(storage); time = findTimeColumn(storage); -isLongFile = length(time) > 200; +columnNames = getStorageColumnNames(storage); -if isLongFile - numNodes = splFitWithCutoff(time(1:200), lengthData(:, 1:200), ... - cutoffFrequency, 4); - numNodes = numNodes * length(time) / 200; -else - numNodes = splFitWithCutoff(time, lengthData, cutoffFrequency, 4); -end -velocityData = calcBSplineDerivative(time, lengthData, 4, numNodes); +lengthData = lowpassFilter(time, lengthData', 4, cutoffFrequency, 0); +splineSet = makeGcvSplineSet(time, lengthData, columnNames); +velocityData = evaluateGcvSplines(splineSet, columnNames, time, 1)'; [filepath, name, ext] = fileparts(muscleTendonLengthFileName); name = strrep(name, "_Length", ""); -writeToSto(getStorageColumnNames(storage), time, velocityData', ... +writeToSto(columnNames, time, velocityData', ... fullfile(filepath, strcat(name, "_Velocity", ext))); end diff --git a/src/Preprocessing/sectionDataFiles.m b/src/Preprocessing/sectionDataFiles.m index 5e8219865..8dcedc983 100644 --- a/src/Preprocessing/sectionDataFiles.m +++ b/src/Preprocessing/sectionDataFiles.m @@ -33,12 +33,16 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function sectionDataFiles(fileNames, timePairs, numRows, prefix) +function sectionDataFiles(fileNames, timePairs, numRows, prefix, ... + filesToFilter, cutoffFrequency) import org.opensim.modeling.Storage for i=1:length(fileNames) storage = Storage(fileNames(i)); data = storageToDoubleMatrix(storage); time = findTimeColumn(storage); + if filesToFilter(i) + data = lowpassFilter(time, data', 4, cutoffFrequency, 0); + end columnNames = getStorageColumnNames(storage); for j=1:size(timePairs, 1) [filepath, name, ext] = fileparts(fileNames(i)); diff --git a/src/Preprocessing/splitIntoTrials.m b/src/Preprocessing/splitIntoTrials.m index c54f3d7ea..cc26d34ab 100644 --- a/src/Preprocessing/splitIntoTrials.m +++ b/src/Preprocessing/splitIntoTrials.m @@ -54,6 +54,7 @@ function splitIntoTrials(timePairs, inputSettings, outputSettings) 'trial'); outputDir = getFieldByNameOrAlternate(outputSettings, ... 'resultsDirectory', 'preprocessed'); +cutoffFrequency = valueOrAlternate(inputSettings, 'cutoffFrequency', 6); model = Model(model); ikOutputDir = 'IKData'; @@ -90,16 +91,19 @@ function splitIntoTrials(timePairs, inputSettings, outputSettings) preprocessDataFile(emgFileName, outputDir, emgOutputDir, trialName) end -filesToSection = makeFilesToSection(outputDir, ikOutputDir, ... - idOutputDir, maOutputDir, grfOutputDir, trialName, coordinates, ... - included); -sectionDataFiles(filesToSection, timePairs, 101, trialName); +[filesToSection, filesToFilter] = makeFilesToSection(outputDir, ... + ikOutputDir, idOutputDir, maOutputDir, grfOutputDir, trialName, ... + coordinates, included); +sectionDataFiles(filesToSection, timePairs, rowsPerTrial, trialName, ... + filesToFilter, cutoffFrequency); -numBufferRows = calcNumPaddingFrames(timePairs); -paddedTimePairs = addBufferToTimePairs(timePairs, numBufferRows); +numBufferRows = calcNumPaddingFrames(timePairs, rowsPerTrial); +paddedTimePairs = addBufferToTimePairs(timePairs, numBufferRows, ... + rowsPerTrial); sectionDataFiles( ... [fullfile(outputDir, emgOutputDir, trialName + ".sto")], ... - paddedTimePairs, (2 * numBufferRows) + 101, trialName) + paddedTimePairs, (2 * numBufferRows) + rowsPerTrial, trialName, ... + false, cutoffFrequency); for i=1:length(filesToSection) delete(filesToSection(i)); end @@ -167,34 +171,40 @@ function preprocessMuscleAnalysisData(outputDir, muscleAnalysisDirectory, ... end end -function filesToSection = makeFilesToSection(outputDir, ikOutputDir, ... - idOutputDir, maOutputDir, grfOutputDir, trialName, coordinates, ... - included) +function [filesToSection, filesToFilter] = makeFilesToSection( ... + outputDir, ikOutputDir, idOutputDir, maOutputDir, grfOutputDir, ... + trialName, coordinates, included) filesToSection = []; +filesToFilter = []; if included.ma filesToSection = [ ... fullfile(outputDir, maOutputDir, trialName + ... "_Length.sto"), ... fullfile(outputDir, maOutputDir, trialName + ... "_Velocity.sto")]; + filesToFilter = [true, false]; end if included.ik filesToSection(end+1) = ... fullfile(outputDir, ikOutputDir, trialName + ".sto"); + filesToFilter(end+1) = true; end if included.id filesToSection(end+1) = ... fullfile(outputDir, idOutputDir, trialName + ".sto"); + filesToFilter(end+1) = true; end if included.grf filesToSection(end+1) = fullfile(outputDir, grfOutputDir, ... trialName + ".sto"); + filesToFilter(end+1) = false; end for i=1:length(coordinates) if isfile(fullfile(outputDir, maOutputDir, ... trialName + "_MomentArm_" + coordinates(i) + ".sto")) filesToSection(end+1) = fullfile(outputDir, maOutputDir, ... trialName + "_MomentArm_" + coordinates(i) + ".sto"); + filesToFilter(end+1) = true; end end end @@ -232,8 +242,8 @@ function throwCantFindMAFileException(fileName) throw(MException('', "Cannot find Muscle Analysis file for " + fileName)); end -function numFramesBuffer = calcNumPaddingFrames(timePairs) -normalizedNumDataPoints = 101; +function numFramesBuffer = calcNumPaddingFrames(timePairs, ... + normalizedNumDataPoints) shortestTrialLength = timePairs(1,2) - timePairs(1,1); for i=2:size(timePairs, 1) if(timePairs(i,2) - timePairs(i,1) < shortestTrialLength) @@ -244,8 +254,8 @@ function throwCantFindMAFileException(fileName) numFramesBuffer = ceil(0.2 / timePerFrame); end -function newTimePairs = addBufferToTimePairs(timePairs, numBufferRows) -rowsPerTrial = 101; +function newTimePairs = addBufferToTimePairs(timePairs, numBufferRows, ... + rowsPerTrial) for i=1:size(timePairs, 1) trialTime = timePairs(i,2) - timePairs(i,1); timePairs(i,1) = timePairs(i,1) - (numBufferRows / ... From 40f8e63fa22eeade6862ef4d7d2e181dac116344 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Tue, 7 Nov 2023 22:51:46 -0600 Subject: [PATCH 204/365] Set number of time points in preprocessing trials --- src/Preprocessing/splitIntoTrials.m | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Preprocessing/splitIntoTrials.m b/src/Preprocessing/splitIntoTrials.m index cc26d34ab..7b5ecc799 100644 --- a/src/Preprocessing/splitIntoTrials.m +++ b/src/Preprocessing/splitIntoTrials.m @@ -55,6 +55,7 @@ function splitIntoTrials(timePairs, inputSettings, outputSettings) outputDir = getFieldByNameOrAlternate(outputSettings, ... 'resultsDirectory', 'preprocessed'); cutoffFrequency = valueOrAlternate(inputSettings, 'cutoffFrequency', 6); +rowsPerTrial = valueOrAlternate(inputSettings, 'rowsPerTrial', 101); model = Model(model); ikOutputDir = 'IKData'; From d3b9bf552580e77e2e0f4befe94be8786be7397e Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 13 Nov 2023 16:44:31 -0600 Subject: [PATCH 205/365] Joint energy absorption and generation terms --- .../calcJointEnergyAbsorptionGoalIntegrand.m | 54 +++++++++++++++++++ .../calcJointEnergyGenerationGoalIntegrand.m | 54 +++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 src/TreatmentOptimization/IntegrandTerms/calcJointEnergyAbsorptionGoalIntegrand.m create mode 100644 src/TreatmentOptimization/IntegrandTerms/calcJointEnergyGenerationGoalIntegrand.m diff --git a/src/TreatmentOptimization/IntegrandTerms/calcJointEnergyAbsorptionGoalIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcJointEnergyAbsorptionGoalIntegrand.m new file mode 100644 index 000000000..1351f21d8 --- /dev/null +++ b/src/TreatmentOptimization/IntegrandTerms/calcJointEnergyAbsorptionGoalIntegrand.m @@ -0,0 +1,54 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function calculates the joint power error for the specified +% coordinate and absorption goal. Only negative power (absorbed energy) is +% included in the output. +% +% (2D matrix, 2D matrix, struct, Array of string) -> (Array of number) + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Spencer Williams, Marleny Vega % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function cost = calcJointEnergyGenerationGoalIntegrand(costTerm, ... + jointVelocity, time, jointMoment, params, loadName) +normalizeByFinalTime = valueOrAlternate(costTerm, ... + "normalize_by_final_time", true); +loadName = erase(loadName, '_moment'); +loadName = erase(loadName, '_force'); +indx = find(strcmp(convertCharsToStrings(params.coordinateNames), ... + loadName)); +momentLabelsNoSuffix = erase(params.inverseDynamicsMomentLabels, '_moment'); +momentLabelsNoSuffix = erase(momentLabelsNoSuffix, '_force'); +includedJointMomentCols = ismember(momentLabelsNoSuffix, ... + convertCharsToStrings(params.coordinateNames)); +if isequal(mexext, 'mexw64') + jointMoment = jointMoment(:, includedJointMomentCols); +end +jointPower = jointMoment(:, indx) .* jointVelocity(:, indx); + +cost = imag(sqrt((jointPower - costTerm.errorCenter) / ... + costTerm.maxAllowableError)); + +if normalizeByFinalTime + cost = cost / time(end); +end +end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcJointEnergyGenerationGoalIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcJointEnergyGenerationGoalIntegrand.m new file mode 100644 index 000000000..9d1089504 --- /dev/null +++ b/src/TreatmentOptimization/IntegrandTerms/calcJointEnergyGenerationGoalIntegrand.m @@ -0,0 +1,54 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function calculates the joint power error for the specified +% coordinate and generation goal. Only positive power (generated energy) is +% included in the output. +% +% (2D matrix, 2D matrix, struct, Array of string) -> (Array of number) + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Spencer Williams, Marleny Vega % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function cost = calcJointEnergyGenerationGoalIntegrand(costTerm, ... + jointVelocity, time, jointMoment, params, loadName) +normalizeByFinalTime = valueOrAlternate(costTerm, ... + "normalize_by_final_time", true); +loadName = erase(loadName, '_moment'); +loadName = erase(loadName, '_force'); +indx = find(strcmp(convertCharsToStrings(params.coordinateNames), ... + loadName)); +momentLabelsNoSuffix = erase(params.inverseDynamicsMomentLabels, '_moment'); +momentLabelsNoSuffix = erase(momentLabelsNoSuffix, '_force'); +includedJointMomentCols = ismember(momentLabelsNoSuffix, ... + convertCharsToStrings(params.coordinateNames)); +if isequal(mexext, 'mexw64') + jointMoment = jointMoment(:, includedJointMomentCols); +end +jointPower = jointMoment(:, indx) .* jointVelocity(:, indx); + +cost = real(sqrt((jointPower - costTerm.errorCenter) / ... + costTerm.maxAllowableError)); + +if normalizeByFinalTime + cost = cost / time(end); +end +end \ No newline at end of file From 78910e888594b571665941e5ff1381547ccc8b40 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 13 Nov 2023 16:47:17 -0600 Subject: [PATCH 206/365] Add joint energy terms to generateCostTermStruct --- .../generateCostTermStruct.m | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/TreatmentOptimization/generateCostTermStruct.m b/src/TreatmentOptimization/generateCostTermStruct.m index a142e60ae..a19088e87 100644 --- a/src/TreatmentOptimization/generateCostTermStruct.m +++ b/src/TreatmentOptimization/generateCostTermStruct.m @@ -84,6 +84,8 @@ "single_support_time_goal" ... "step_time_asymmetry_minimization" ... "joint_power_minimization" ... + "joint_energy_generation_goal" ... + "joint_energy_absorption_goal" ... "trailing_limb_angle_minimization" ... "trailing_limb_angle_maximization" ... "muscle_activation_minimization" ... @@ -284,6 +286,26 @@ costTerm.load ... ); +costTermCalculations.joint_energy_generation_goal = @(values, modeledValues, auxdata, costTerm) ... + calcJointEnergyGenerationGoalIntegrand( ... + costTerm, ... + values.velocities, ... + values.time, ... + modeledValues.inverseDynamicsMoments, ... + auxdata, ... + costTerm.load ... + ); + +costTermCalculations.joint_energy_absorption_goal = @(values, modeledValues, auxdata, costTerm) ... + calcJointEnergyAbsorptionGoalIntegrand( ... + costTerm, ... + values.velocities, ... + values.time, ... + modeledValues.inverseDynamicsMoments, ... + auxdata, ... + costTerm.load ... + ); + costTermCalculations.trailing_limb_angle_minimization = @(values, modeledValues, auxdata, costTerm) ... calcMinimizingTrailingLimbAngleIntegrand( ... values, ... From 3ec36d6af1fc9dd525bf7c20ddea4d24a3a4dc2e Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 13 Nov 2023 16:48:04 -0600 Subject: [PATCH 207/365] Fix absorption function name --- .../IntegrandTerms/calcJointEnergyAbsorptionGoalIntegrand.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TreatmentOptimization/IntegrandTerms/calcJointEnergyAbsorptionGoalIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcJointEnergyAbsorptionGoalIntegrand.m index 1351f21d8..7c1e1fde0 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcJointEnergyAbsorptionGoalIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcJointEnergyAbsorptionGoalIntegrand.m @@ -28,7 +28,7 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcJointEnergyGenerationGoalIntegrand(costTerm, ... +function cost = calcJointEnergyAbsorptionGoalIntegrand(costTerm, ... jointVelocity, time, jointMoment, params, loadName) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); From 7f3fa345e8f4d24084d65acb9790a773609cbc3c Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Sun, 19 Nov 2023 17:11:19 -0600 Subject: [PATCH 208/365] Update energy absorption definition --- .../IntegrandTerms/calcJointEnergyAbsorptionGoalIntegrand.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TreatmentOptimization/IntegrandTerms/calcJointEnergyAbsorptionGoalIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcJointEnergyAbsorptionGoalIntegrand.m index 7c1e1fde0..f3113c385 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcJointEnergyAbsorptionGoalIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcJointEnergyAbsorptionGoalIntegrand.m @@ -45,7 +45,7 @@ end jointPower = jointMoment(:, indx) .* jointVelocity(:, indx); -cost = imag(sqrt((jointPower - costTerm.errorCenter) / ... +cost = real(sqrt((-jointPower - costTerm.errorCenter) / ... costTerm.maxAllowableError)); if normalizeByFinalTime From 461e631fa3965245819cc033144439aaa1c975e1 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Sun, 19 Nov 2023 17:29:43 -0600 Subject: [PATCH 209/365] Propulsive and braking impulse cost terms --- .../calcBrakingImpulseGoalIntegrand.m | 47 +++++++++++++++++++ .../calcPropulsiveImpulseGoalIntegrand.m | 47 +++++++++++++++++++ .../generateCostTermStruct.m | 18 +++++++ 3 files changed, 112 insertions(+) create mode 100644 src/TreatmentOptimization/IntegrandTerms/calcBrakingImpulseGoalIntegrand.m create mode 100644 src/TreatmentOptimization/IntegrandTerms/calcPropulsiveImpulseGoalIntegrand.m diff --git a/src/TreatmentOptimization/IntegrandTerms/calcBrakingImpulseGoalIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcBrakingImpulseGoalIntegrand.m new file mode 100644 index 000000000..1de0b1f36 --- /dev/null +++ b/src/TreatmentOptimization/IntegrandTerms/calcBrakingImpulseGoalIntegrand.m @@ -0,0 +1,47 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function calculates the error in braking impulse for the side +% defined in the cost term. Only the braking impulse (negative) is +% included in the output. +% +% (struct, Array of double, struct, struct) -> (Array of double) + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Spencer Williams % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function cost = calcBrakingImpulseGoalIntegrand(modeledValues, ... + time, inputs, costTerm) +normalizeByFinalTime = valueOrAlternate(costTerm, ... + "normalize_by_final_time", false); +braking = []; +for i = 1:length(inputs.contactSurfaces) + if inputs.contactSurfaces{i}.isLeftFoot == strcmpi(costTerm.side, "left") + braking = -modeledValues.groundReactionsLab.forces{i}(:,1); + end +end +assert(~isempty(braking), "Unable to find braking force.") +cost = real(sqrt((braking - costTerm.errorCenter) / ... + costTerm.maxAllowableError)); +if normalizeByFinalTime + cost = cost / time(end); +end +end diff --git a/src/TreatmentOptimization/IntegrandTerms/calcPropulsiveImpulseGoalIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcPropulsiveImpulseGoalIntegrand.m new file mode 100644 index 000000000..8f6cc4b9b --- /dev/null +++ b/src/TreatmentOptimization/IntegrandTerms/calcPropulsiveImpulseGoalIntegrand.m @@ -0,0 +1,47 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function calculates the error in propulsive impulse for the side +% defined in the cost term. Only the positive impulse (propulsive) is +% included in the output. +% +% (struct, Array of double, struct, struct) -> (Array of double) + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Spencer Williams % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function cost = calcPropulsiveImpulseGoalIntegrand(modeledValues, ... + time, inputs, costTerm) +normalizeByFinalTime = valueOrAlternate(costTerm, ... + "normalize_by_final_time", false); +propulsiveForce = []; +for i = 1:length(inputs.contactSurfaces) + if inputs.contactSurfaces{i}.isLeftFoot == strcmpi(costTerm.side, "left") + propulsiveForce = modeledValues.groundReactionsLab.forces{i}(:,1); + end +end +assert(~isempty(propulsiveForce), "Unable to find propulsive force.") +cost = real(sqrt((propulsiveForce - costTerm.errorCenter) / ... + costTerm.maxAllowableError)); +if normalizeByFinalTime + cost = cost / time(end); +end +end diff --git a/src/TreatmentOptimization/generateCostTermStruct.m b/src/TreatmentOptimization/generateCostTermStruct.m index a19088e87..75eee926f 100644 --- a/src/TreatmentOptimization/generateCostTermStruct.m +++ b/src/TreatmentOptimization/generateCostTermStruct.m @@ -86,6 +86,8 @@ "joint_power_minimization" ... "joint_energy_generation_goal" ... "joint_energy_absorption_goal" ... + "propulsive_impulse_goal" ... + "braking_impulse_goal" ... "trailing_limb_angle_minimization" ... "trailing_limb_angle_maximization" ... "muscle_activation_minimization" ... @@ -306,6 +308,22 @@ costTerm.load ... ); +costTermCalculations.propulsive_impulse_goal = @(values, modeledValues, auxdata, costTerm) ... + calcJointEnergyAbsorptionGoalIntegrand( ... + modeledValues, ... + values.time, ... + auxdata, ... + costTerm ... + ); + +costTermCalculations.braking_impulse_goal = @(values, modeledValues, auxdata, costTerm) ... + calcJointEnergyAbsorptionGoalIntegrand( ... + modeledValues, ... + values.time, ... + auxdata, ... + costTerm ... + ); + costTermCalculations.trailing_limb_angle_minimization = @(values, modeledValues, auxdata, costTerm) ... calcMinimizingTrailingLimbAngleIntegrand( ... values, ... From 8b9b99461856d4945b46fd69e29fb61d9883106f Mon Sep 17 00:00:00 2001 From: RobSalati Date: Fri, 24 Nov 2023 15:20:06 -0600 Subject: [PATCH 210/365] saveMuscleTendonOptimizationResults done Have a function to save optimization parameters. Currently not integrated directly with MTP. --- .../MuscleTendonPersonalizationTool.m | 4 + .../saveMuscleTendonOptimizationResults.m | 354 +++++++++--------- 2 files changed, 190 insertions(+), 168 deletions(-) diff --git a/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m b/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m index 87396a0d7..e74df65f7 100644 --- a/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m +++ b/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m @@ -63,4 +63,8 @@ function MuscleTendonPersonalizationTool(settingsFileName) saveMuscleTendonPersonalizationResults(inputs.model, ... inputs.osimxFileName, inputs.prefixes, inputs.coordinateNames, ... finalValues, results, resultsDirectory, inputs.muscleTendonColumnNames); +save("params.mat", "params") +save("optimizedParams.mat", "optimizedParams") +save("precalInputs.mat", "precalInputs") +save("inputs.mat", "inputs") end diff --git a/src/MuscleTendonPersonalization/saveMuscleTendonOptimizationResults.m b/src/MuscleTendonPersonalization/saveMuscleTendonOptimizationResults.m index 2909a6916..d127b9346 100644 --- a/src/MuscleTendonPersonalization/saveMuscleTendonOptimizationResults.m +++ b/src/MuscleTendonPersonalization/saveMuscleTendonOptimizationResults.m @@ -7,190 +7,208 @@ % Saves data from optimization to 6 .sto files function saveMuscleTendonOptimizationResults(optimizedParams, ... - mtpInputs, precalInputs, resultsDirectory) - if nargin < 3; precalInputs = []; end - [finalValues, results, resultsSynx, resultsSynxNoResiduals] = ... - getValuesToReport(mtpInputs, precalInputs, optimizedParams) - if ~isempty(precalInputs) - saveMuscleTendonLengthInitializationResults(precalInputs, mtpInputs, ... - resultsDirectory) % Change to just save the data - end + mtpInputs, precalInputs, resultsDirectory) +if nargin < 3 + precalInputs = []; +end +[finalValues, results, resultsSynx, resultsSynxNoResiduals] = ... + getValuesToReport(mtpInputs, precalInputs, optimizedParams); +if ~isempty(precalInputs) + modeledValues = getMuscleTendonLengthInitializationData(precalInputs, ... + mtpInputs); % Modeled passive force data & params from experimental data. +end + +printJointMomentMatchingError(resultsSynx.muscleJointMoments, ... + mtpInputs.inverseDynamicsMoments); % Keep this + +%% Save excitations and activations +saveActivationAndExcitationData(mtpInputs, results, resultsSynx, resultsDirectory); + +%% Save Noramlized Fiber lengths +saveAnalysisData(mtpInputs.muscleNames, mtpInputs.prefixes, results.normalizedFiberLength, ... + strcat(resultsDirectory, "\normalizedFiberLengths"), "_normalizedFiberLengths.sto") + +%% Save Joint moments +saveJointMomentData(mtpInputs, results, resultsSynx, resultsSynxNoResiduals, resultsDirectory); + +%% Save hill type model params +saveMuscleModelParameters(finalValues, fullfile(resultsDirectory, ... + "muscleModelParemeters")); - printJointMomentMatchingError(resultsSynx.muscleJointMoments, ... - mtpInputs.inverseDynamicsMoments); % Keep this - - saveExcitationAndActivationData(results, resultsSynx, mtpInputs, ... - mtpInputs.synergyExtrapolation, resultsDirectory); % Change to just save the data +%% Save Passive moments +savePassiveMomentData(precalInputs, modeledValues, resultsDirectory); - % makeModelParameterPlots(finalValues, mtpInputs, ... - % mtpInputs.synergyExtrapolation) % Change to just save the data +%% Save passive forces +savePassiveForceData(mtpInputs, modeledValues, results, resultsSynx, ... + resultsSynxNoResiduals, resultsDirectory); +end + +function savePassiveMomentData(precalInputs, modeledValues, resultsDirectory) +% Passive moments need processing first. +modelPassiveMoments = permute(modeledValues.passiveModelMoments, [3 1 2]); +sizeTemp = size(modelPassiveMoments,1); +experimentalPassiveMoments = permute(precalInputs.passiveData.inverseDynamicsMoments, [3 1 2]); + +columnsWithAllZeros = all(experimentalPassiveMoments == 0, 1); + +experimentalPassiveMoments = experimentalPassiveMoments(repmat(~columnsWithAllZeros, ... + size(experimentalPassiveMoments, 1), 1, 1)); + +modelPassiveMoments = modelPassiveMoments(repmat(~columnsWithAllZeros, ... + size(modelPassiveMoments, 1), 1, 1)); + +experimentalPassiveMoments = ... + reshape(experimentalPassiveMoments, sizeTemp, []); +experimentalPassiveMoments = ... + reshape(experimentalPassiveMoments', 1, 12, 101); % Want a better way to do this +modelPassiveMoments = ... + reshape(modelPassiveMoments, sizeTemp, []); +modelPassiveMoments = ... + reshape(modelPassiveMoments', 1, 12, 101); % Want a better way to do this +saveAnalysisData(precalInputs.passivePrefixes, precalInputs.prefixes, ... + experimentalPassiveMoments, fullfile(resultsDirectory, ... + "passiveJointMomentsExperimental"), "_passiveJointMomentsExperimental.sto") +saveAnalysisData(precalInputs.passivePrefixes, precalInputs.prefixes, ... + modelPassiveMoments, fullfile(resultsDirectory, ... + "passiveJointMomentsModeled"), "_passiveJointMomentsModeled.sto") +end + +function saveJointMomentData(mtpInputs, results, resultsSynx, ... + resultsSynxNoResiduals, resultsDirectory) +saveAnalysisData(mtpInputs.coordinateNames, mtpInputs.prefixes, ... + results.muscleJointMoments, strcat(resultsDirectory, ... + "\modelJointMomentsNoSynx"), "_modelJointMomentsNoSynx.sto") +saveAnalysisData(mtpInputs.coordinateNames, mtpInputs.prefixes, ... + resultsSynx.muscleJointMoments, strcat(resultsDirectory, ... + "\modelJointMomentsSynx"), "_modelJointMomentsSynx.sto") +saveAnalysisData(mtpInputs.coordinateNames, mtpInputs.prefixes, ... + resultsSynxNoResiduals.muscleJointMoments, strcat(resultsDirectory, ... + "\modelJointMomentsSynxNoResiduals"), "_modelJointMomentsSynxNoResiduals.sto") +saveAnalysisData(mtpInputs.coordinateNames, mtpInputs.prefixes, ... + mtpInputs.inverseDynamicsMoments, strcat(resultsDirectory, ... + "\inverseDynamicsJointMoments"), "_inverseDynamicsJointMoments.sto") +end + +function saveActivationAndExcitationData(mtpInputs, results, ... + resultsSynx, resultsDirectory) +saveAnalysisData(mtpInputs.muscleNames, mtpInputs.prefixes, ... + results.muscleExcitations, strcat(resultsDirectory, ... + "\muscleExcitations"), "_muscleExcitations.sto") +saveAnalysisData(mtpInputs.muscleNames, mtpInputs.prefixes, ... + resultsSynx.muscleExcitations, strcat(resultsDirectory, ... + "\muscleExcitationsSynx"), "_muscleExcitationsSynx.sto") +saveAnalysisData(mtpInputs.muscleNames, mtpInputs.prefixes, ... + results.muscleActivations, strcat(resultsDirectory, ... + "\muscleActivations"), "_muscleActivations.sto") +saveAnalysisData(mtpInputs.muscleNames, mtpInputs.prefixes, ... + resultsSynx.muscleActivations, strcat(resultsDirectory, ... + "\muscleActivationsSynx"), "_muscleActivationsSynx.sto") +end - % makeTaskSpecificMomentMatchingPlots(... - % permute(resultsSynxNoResiduals.muscleJointMoments, [3 1 2]), ... - % permute(resultsSynx.muscleJointMoments, [3 1 2]), ... - % permute(mtpInputs.inverseDynamicsMoments, [3 1 2]), ... - % mtpInputs.coordinateNames, mtpInputs.synergyExtrapolation) % Change to just save the data +function saveMuscleModelParameters(finalValues, resultsDirectory) +if ~exist(resultsDirectory, "dir") + mkdir(resultsDirectory); +end +columnLabels = ["Activation Time Constant", "Activation Nonlinearity", ... + "Electromechanical Time Delay", "EMG Scaling Factor", ... + "Optimal Fiber Length Scaling Factor", "Tendon Slack Length Scaling Factor"]; + +dataPoints = [finalValues.activationTimeConstants; + finalValues.activationNonlinearityConstants; + finalValues.electromechanicalDelays; + finalValues.emgScaleFactors; + finalValues.optimalFiberLengthScaleFactors; + finalValues.tendonSlackLengthScaleFactors]'; + +writeToSto(columnLabels, 1:1:size(dataPoints,1), dataPoints, ... + fullfile(resultsDirectory, "muscleModelParameters.sto")); +end - % makeTaskSpecificNormalizedFiberLengthsPlots( ... - % permute(resultsSynx.normalizedFiberLength, [3 1 2]), ... - % mtpInputs, mtpInputs.synergyExtrapolation) % Change to just save the data +function savePassiveForceData(mtpInputs, modeledValues, results, resultsSynx, ... + resultsSynxNoResiduals, resultsDirectory) +saveAnalysisData(mtpInputs.muscleNames, mtpInputs.prefixes, ... + modeledValues.passiveForce, strcat(resultsDirectory, ... + "\passiveForcesExperimental"), "_passiveForcesExperimental.sto"); +saveAnalysisData(mtpInputs.muscleNames, mtpInputs.prefixes, ... + results.passiveForce, strcat(resultsDirectory, ... + "\passiveForcesModel"), "_passiveForcesModel.sto"); +saveAnalysisData(mtpInputs.muscleNames, mtpInputs.prefixes, ... + resultsSynx.passiveForce, strcat(resultsDirectory, ... + "\passiveForcesModelSynx"), "_passiveForcesModelSynx.sto"); +saveAnalysisData(mtpInputs.muscleNames, mtpInputs.prefixes, ... + resultsSynxNoResiduals.passiveForce, strcat(resultsDirectory, ... + "\passiveForcesModelSynxNoResiduals"), "_passiveForcesModelSynxNoResiduals.sto"); end function [finalValues, results, resultsSynx, resultsSynxNoResiduals] = ... - getValuesToReport(mtpInputs, precalInputs, optimizedParams) % Need to downscale excitation arrays - - finalValues = makeMtpValuesAsStruct([], optimizedParams, zeros(1, 7)); - save('finalvalues.mat', 'finalValues') - resultsSynx = calcMtpSynXModeledValues(finalValues, mtpInputs, struct()); - finalValues.synergyWeights(mtpInputs.numberOfExtrapolationWeights + 1 : end) = 0; - resultsSynxNoResiduals = calcMtpSynXModeledValues(finalValues, mtpInputs, struct()); - results = calcMtpModeledValues(finalValues, mtpInputs, struct()); - results.time = mtpInputs.emgTime(:, mtpInputs.numPaddingFrames + 1 : ... - end - mtpInputs.numPaddingFrames); - results.muscleExcitations = results.muscleExcitations(:, :, ... - mtpInputs.numPaddingFrames + 1 : end - mtpInputs.numPaddingFrames); - resultsSynx.muscleExcitations = resultsSynx.muscleExcitations(:, :, ... - mtpInputs.numPaddingFrames + 1 : end - mtpInputs.numPaddingFrames); -end - -function saveMuscleTendonLengthInitializationResults(... - precalInputs, mtpInputs, resultsDirectory) - - tempValues.optimalFiberLengthScaleFactors = ... - mtpInputs.optimalFiberLength ./ precalInputs.optimalFiberLength; - tempValues.tendonSlackLengthScaleFactors = ... - mtpInputs.tendonSlackLength ./ precalInputs.tendonSlackLength; - precalInputs.maxIsometricForce = mtpInputs.maxIsometricForce; - precalInputs.optimizeIsometricMaxForce = 0; - modeledValues = calcMuscleTendonLengthInitializationModeledValues(tempValues, precalInputs); - savePassiveForceData(permute(modeledValues.passiveForce, [3 1 2]), ... - precalInputs.muscleNames, resultsDirectory); -% if precalInputs.passiveMomentDataExists -% plotPassiveMomentData(permute(modeledValues.passiveModelMoments, [3 1 2]), ... -% permute(precalInputs.passiveData.inverseDynamicsMoments, [3 1 2]), ... -% precalInputs.passivePrefixes) -% end -end - -function savePassiveForceData(modeledValue, muscleNames, resultsDirectory) - - meanModeledValue = squeeze(mean(modeledValue, 2)); - stdModeledValue = squeeze(std(modeledValue, [], 2)); - - data = {meanModeledValue, stdModeledValue}; - dataNames = ["meanModeledValue", "stdModeledValue"]; - - [stoDataArray, stoColumnLabels] = groupDataByMuscle(data, dataNames, muscleNames); - - writeToSto(stoColumnLabels, 1:1:length(stoDataArray), stoDataArray, ... - strcat(resultsDirectory, "/passiveForceData/", "_passiveForceData.sto")) % Maybe needs counter for gait cycles? -end + getValuesToReport(mtpInputs, precalInputs, optimizedParams) % Need to downscale excitation arrays + +finalValues = makeMtpValuesAsStruct([], optimizedParams, zeros(1, 7)); +save('finalvalues.mat', 'finalValues') +resultsSynx = calcMtpSynXModeledValues(finalValues, mtpInputs, struct()); +finalValues.synergyWeights(mtpInputs.numberOfExtrapolationWeights + 1 : end) = 0; +resultsSynxNoResiduals = calcMtpSynXModeledValues(finalValues, mtpInputs, struct()); +results = calcMtpModeledValues(finalValues, mtpInputs, struct()); +results.time = mtpInputs.emgTime(:, mtpInputs.numPaddingFrames + 1 : ... + end - mtpInputs.numPaddingFrames); +results.muscleExcitations = results.muscleExcitations(:, :, ... + mtpInputs.numPaddingFrames + 1 : end - mtpInputs.numPaddingFrames); +resultsSynx.muscleExcitations = resultsSynx.muscleExcitations(:, :, ... + mtpInputs.numPaddingFrames + 1 : end - mtpInputs.numPaddingFrames); +end + +function modeledValues = getMuscleTendonLengthInitializationData(... + precalInputs, mtpInputs) + +tempValues.optimalFiberLengthScaleFactors = ... + mtpInputs.optimalFiberLength ./ precalInputs.optimalFiberLength; + +tempValues.tendonSlackLengthScaleFactors = ... + mtpInputs.tendonSlackLength ./ precalInputs.tendonSlackLength; + +precalInputs.maxIsometricForce = mtpInputs.maxIsometricForce; + +precalInputs.optimizeIsometricMaxForce = 0; + +modeledValues = calcMuscleTendonLengthInitializationModeledValues(tempValues, precalInputs); +end function printJointMomentMatchingError(muscleJointMoments, inverseDynamicsMoments) % Good - for i = 1 : size(muscleJointMoments, 2) - jointMomentsRmse(i) = sqrt(sum((muscleJointMoments(:, i, :) - ... - inverseDynamicsMoments(:, i, :)) .^ 2, 'all') / ... - (numel(inverseDynamicsMoments(:, 1, :)) - 1)); - jointMomentsMae(i) = sum(abs(muscleJointMoments(:, i, :) - ... - inverseDynamicsMoments(:, i, :)) / ... - numel(inverseDynamicsMoments(:, 1, :)), 'all'); - end - fprintf(['The root mean sqrt (RMS) errors between model-predicted ' ... - 'and inverse dynamic moments are: \n' ]); - fprintf(['\n ' num2str(jointMomentsRmse) ' \n']); - fprintf(['The mean absolute errors (MAEs) between model-predicted ' ... - 'and inverse dynamic moments are: \n' ]); - fprintf(['\n ' num2str(jointMomentsMae) ' \n']); -end - -function saveExcitationAndActivationData(results, resultsSynx, ... - experimentalData, synergyParameters, resultsDirectory) - muscleLabels = getSynxMuscleNames(experimentalData.muscleNames, ... - synergyParameters.missingEmgChannelGroups); - for i = 1 : numel(synergyParameters.taskNames) - - [stoDataArray, stoColumnLabels] = formatMuscleExcitationsAndActivations(... - results.muscleExcitations(synergyParameters.trialIndex{i}, :, :), ... - resultsSynx.muscleExcitations(synergyParameters.trialIndex{i}, :, :), ... - results.muscleActivations(synergyParameters.trialIndex{i}, :, :), ... - resultsSynx.muscleActivations(synergyParameters.trialIndex{i}, :, :), ... - muscleLabels); - writeToSto(stoColumnLabels, 1:1:length(stoDataArray), stoDataArray, ... - strcat(resultsDirectory, "/muscleExcitationsAndActivations/", ... - synergyParameters.taskNames{i}, "_muscleExcitationsActivations.sto")) - end +for i = 1 : size(muscleJointMoments, 2) + jointMomentsRmse(i) = sqrt(sum((muscleJointMoments(:, i, :) - ... + inverseDynamicsMoments(:, i, :)) .^ 2, 'all') / ... + (numel(inverseDynamicsMoments(:, 1, :)) - 1)); + jointMomentsMae(i) = sum(abs(muscleJointMoments(:, i, :) - ... + inverseDynamicsMoments(:, i, :)) / ... + numel(inverseDynamicsMoments(:, 1, :)), 'all'); +end +fprintf(['The root mean sqrt (RMS) errors between model-predicted ' ... + 'and inverse dynamic moments are: \n' ]); +fprintf(['\n ' num2str(jointMomentsRmse) ' \n']); +fprintf(['The mean absolute errors (MAEs) between model-predicted ' ... + 'and inverse dynamic moments are: \n' ]); +fprintf(['\n ' num2str(jointMomentsMae) ' \n']); end -function [stoDataArray, stoColumnLabels] = formatMuscleExcitationsAndActivations( ... - muscleExcitations, muscleExcitationsSynx, muscleActivations, ... - muscleActivationsSynx, muscleLabels) - - meanMuscleExcitation = permute(mean(muscleExcitations, 1), [3 2 1]); - stdMuscleExcitation = permute(std(muscleExcitations, [], 1), [3 2 1]); - meanMuscleExcitationSynx = permute(mean(muscleExcitationsSynx, 1), [3 2 1]); - stdMuscleExcitationSynx = permute(std(muscleExcitationsSynx, [], 1), [3 2 1]); - meanMuscleActivation = permute(mean(muscleActivations, 1), [3 2 1]); - stdMuscleActivation = permute(std(muscleActivations, [], 1), [3 2 1]); - meanMuscleActivationSynx = permute(mean(muscleActivationsSynx, 1), [3 2 1]); - stdMuscleActivationSynx = permute(std(muscleActivationsSynx, [], 1), [3 2 1]); - excitationsAndActivations = { - meanMuscleExcitation, stdMuscleExcitation, ... - meanMuscleExcitationSynx, stdMuscleExcitationSynx, ... - meanMuscleActivation, stdMuscleActivation, ... - meanMuscleActivationSynx, stdMuscleActivationSynx}; - dataNames = ["meanMuscleExcitation", "stdMuscleExcitation", ... - "meanMuscleExcitationSynx", "stdMuscleExcitationSynx", ... - "meanMuscleActivation", "stdMuscleActivation", ... - "meanMuscleActivationSynx", "stdMuscleActivationSynx"]; - [stoDataArray, stoColumnLabels] = groupDataByMuscle(excitationsAndActivations, ... - dataNames, muscleLabels); -% for i = 0 : numel(muscleLabels) - 1 -% stoDataArray(:, i*8+1) = meanMuscleExcitation(1:101, i + 1); -% stoDataArray(:, i*8+2) = stdMuscleExcitation(1:101, i+1); -% stoDataArray(:, i*8+3) = meanMuscleExcitationSynx(1:101, i+1); -% stoDataArray(:, i*8+4) = stdMuscleExcitationSynx(1:101, i+1); -% stoDataArray(:, i*8+5) = meanMuscleActivation(1:101, i+1); -% stoDataArray(:, i*8+6) = stdMuscleActivation(1:101, i+1); -% stoDataArray(:, i*8+7) = meanMuscleActivationSynx(1:101, i+1); -% stoDataArray(:, i*8+8) = stdMuscleActivationSynx(1:101, i+1); -% end -% -% stoColumnLabels = strings(8*numel(muscleLabels),1); -% for i=0:numel(muscleLabels)-1 -% stoColumnLabels(8*i+1) = strcat(muscleLabels{i+1}, "_", "meanMuscleExcitation"); -% stoColumnLabels(8*i+2) = strcat(muscleLabels{i+1}, "_", "stdMuscleExcitation"); -% stoColumnLabels(8*i+3) = strcat(muscleLabels{i+1}, "_", "meanMuscleExcitationSynx"); -% stoColumnLabels(8*i+4) = strcat(muscleLabels{i+1}, "_", "stdMuscleExcitationSynx"); -% stoColumnLabels(8*i+5) = strcat(muscleLabels{i+1}, "_", "meanMuscleActivation"); -% stoColumnLabels(8*i+6) = strcat(muscleLabels{i+1}, "_", "stdMuscleActivation"); -% stoColumnLabels(8*i+7) = strcat(muscleLabels{i+1}, "_", "meanMuscleActivationSynx"); -% stoColumnLabels(8*i+8) = strcat(muscleLabels{i+1}, "_", "stdMuscleActivationSynx"); -end - -function [dataArray, columnNames] = groupDataByMuscle(... - data, dataNames, muscleNames) - - lengthMuscleNames = numel(muscleNames); - lengthDataNames = numel(dataNames); - dataArray = zeros(length(data{1}), lengthMuscleNames*lengthDataNames); - columnNames = strings(lengthDataNames*lengthMuscleNames, 1); - for i = 0 : lengthMuscleNames - 1 - for j = 1 : lengthDataNames - dataArray(:, lengthDataNames * i + j) = data{j}(:, i + 1); - columnNames(lengthDataNames * i + j) = strcat(muscleNames(i + 1), "_", dataNames(j)); - end - end +function saveAnalysisData(columnLabels, taskNames, data, directory, fileName) +if ~exist(directory, "dir") + mkdir(directory); +end +for i = 1 : size(data,1) + writeToSto(columnLabels, 1:1:length(data(i,:,:)), ... + permute(data(i,:,:), [3 2 1]), strcat(directory, "\", taskNames(i), fileName)) +end end function muscleLabels = getSynxMuscleNames(muscleNames, ... missingEmgChannelGroups) - for i = 1 : numel(muscleNames) - if ismember(i, [missingEmgChannelGroups{:}]) - muscleLabels{i} = [muscleNames{i} '(*)']; - else - muscleLabels{i} = muscleNames{i}; - end +for i = 1 : numel(muscleNames) + if ismember(i, [missingEmgChannelGroups{:}]) + muscleLabels{i} = [muscleNames{i} '(*)']; + else + muscleLabels{i} = muscleNames{i}; end +end end \ No newline at end of file From a46d22efa5799fa3a59e606851643f5ec6807b39 Mon Sep 17 00:00:00 2001 From: RobSalati Date: Fri, 24 Nov 2023 16:03:59 -0600 Subject: [PATCH 211/365] Creating new folder for analysis --- .../pN9Z5OP9bqj0zUPkEbtV71ffm3Qd.xml | 2 + .../pN9Z5OP9bqj0zUPkEbtV71ffm3Qp.xml | 2 + .../-aBlB53ncmKI2GMHO0FbGFfJ9vsd.xml | 2 + .../-aBlB53ncmKI2GMHO0FbGFfJ9vsp.xml | 2 + .../MuscleTendonPersonalizationTool.m | 17 +- ...m => saveMuscleTendonOptimizationParams.m} | 191 ++++++++---------- 6 files changed, 104 insertions(+), 112 deletions(-) create mode 100644 resources/project/-aBlB53ncmKI2GMHO0FbGFfJ9vs/pN9Z5OP9bqj0zUPkEbtV71ffm3Qd.xml create mode 100644 resources/project/-aBlB53ncmKI2GMHO0FbGFfJ9vs/pN9Z5OP9bqj0zUPkEbtV71ffm3Qp.xml create mode 100644 resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/-aBlB53ncmKI2GMHO0FbGFfJ9vsd.xml create mode 100644 resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/-aBlB53ncmKI2GMHO0FbGFfJ9vsp.xml rename src/MuscleTendonPersonalization/{saveMuscleTendonOptimizationResults.m => saveMuscleTendonOptimizationParams.m} (91%) diff --git a/resources/project/-aBlB53ncmKI2GMHO0FbGFfJ9vs/pN9Z5OP9bqj0zUPkEbtV71ffm3Qd.xml b/resources/project/-aBlB53ncmKI2GMHO0FbGFfJ9vs/pN9Z5OP9bqj0zUPkEbtV71ffm3Qd.xml new file mode 100644 index 000000000..a75f7a81b --- /dev/null +++ b/resources/project/-aBlB53ncmKI2GMHO0FbGFfJ9vs/pN9Z5OP9bqj0zUPkEbtV71ffm3Qd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/-aBlB53ncmKI2GMHO0FbGFfJ9vs/pN9Z5OP9bqj0zUPkEbtV71ffm3Qp.xml b/resources/project/-aBlB53ncmKI2GMHO0FbGFfJ9vs/pN9Z5OP9bqj0zUPkEbtV71ffm3Qp.xml new file mode 100644 index 000000000..842de6ab3 --- /dev/null +++ b/resources/project/-aBlB53ncmKI2GMHO0FbGFfJ9vs/pN9Z5OP9bqj0zUPkEbtV71ffm3Qp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/-aBlB53ncmKI2GMHO0FbGFfJ9vsd.xml b/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/-aBlB53ncmKI2GMHO0FbGFfJ9vsd.xml new file mode 100644 index 000000000..a75f7a81b --- /dev/null +++ b/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/-aBlB53ncmKI2GMHO0FbGFfJ9vsd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/-aBlB53ncmKI2GMHO0FbGFfJ9vsp.xml b/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/-aBlB53ncmKI2GMHO0FbGFfJ9vsp.xml new file mode 100644 index 000000000..f10f1a511 --- /dev/null +++ b/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/-aBlB53ncmKI2GMHO0FbGFfJ9vsp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m b/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m index e74df65f7..849972c91 100644 --- a/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m +++ b/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m @@ -46,10 +46,11 @@ function MuscleTendonPersonalizationTool(settingsFileName) optimizedParams = MuscleTendonPersonalization(inputs, params); if params.performMuscleTendonLengthInitialization - reportMuscleTendonPersonalizationResults(optimizedParams, ... - inputs, precalInputs); + saveMuscleTendonOptimizationParams(".\mtpResults\Analysis", ... + optimizedParams, inputs, precalInputs) else - reportMuscleTendonPersonalizationResults(optimizedParams, inputs); + saveMuscleTendonOptimizationParams(".\mtpResults\Analysis", ... + optimizedParams, inputs) end finalValues = makeMtpValuesAsStruct([], optimizedParams, zeros(1, 7)); @@ -63,8 +64,10 @@ function MuscleTendonPersonalizationTool(settingsFileName) saveMuscleTendonPersonalizationResults(inputs.model, ... inputs.osimxFileName, inputs.prefixes, inputs.coordinateNames, ... finalValues, results, resultsDirectory, inputs.muscleTendonColumnNames); -save("params.mat", "params") -save("optimizedParams.mat", "optimizedParams") -save("precalInputs.mat", "precalInputs") -save("inputs.mat", "inputs") +% saveMuscleTendonOptimizationResults(optimizedParams, inputs, precalInputs, ... +% ".\mtpResults\Analysis") +% save("params.mat", "params") +% save("optimizedParams.mat", "optimizedParams") +% save("precalInputs.mat", "precalInputs") +% save("inputs.mat", "inputs") end diff --git a/src/MuscleTendonPersonalization/saveMuscleTendonOptimizationResults.m b/src/MuscleTendonPersonalization/saveMuscleTendonOptimizationParams.m similarity index 91% rename from src/MuscleTendonPersonalization/saveMuscleTendonOptimizationResults.m rename to src/MuscleTendonPersonalization/saveMuscleTendonOptimizationParams.m index d127b9346..44e3b3719 100644 --- a/src/MuscleTendonPersonalization/saveMuscleTendonOptimizationResults.m +++ b/src/MuscleTendonPersonalization/saveMuscleTendonOptimizationParams.m @@ -6,9 +6,9 @@ % (struct, struct, struct, string) -> (None) % Saves data from optimization to 6 .sto files -function saveMuscleTendonOptimizationResults(optimizedParams, ... - mtpInputs, precalInputs, resultsDirectory) -if nargin < 3 +function saveMuscleTendonOptimizationParams(resultsDirectory, optimizedParams, ... + mtpInputs, precalInputs) +if nargin < 4 precalInputs = []; end [finalValues, results, resultsSynx, resultsSynxNoResiduals] = ... @@ -16,31 +16,57 @@ function saveMuscleTendonOptimizationResults(optimizedParams, ... if ~isempty(precalInputs) modeledValues = getMuscleTendonLengthInitializationData(precalInputs, ... mtpInputs); % Modeled passive force data & params from experimental data. + savePassiveMomentData(precalInputs, modeledValues, resultsDirectory); + savePassiveForceData(mtpInputs, modeledValues, results, resultsSynx, ... + resultsSynxNoResiduals, resultsDirectory); end printJointMomentMatchingError(resultsSynx.muscleJointMoments, ... mtpInputs.inverseDynamicsMoments); % Keep this -%% Save excitations and activations saveActivationAndExcitationData(mtpInputs, results, resultsSynx, resultsDirectory); -%% Save Noramlized Fiber lengths saveAnalysisData(mtpInputs.muscleNames, mtpInputs.prefixes, results.normalizedFiberLength, ... strcat(resultsDirectory, "\normalizedFiberLengths"), "_normalizedFiberLengths.sto") -%% Save Joint moments -saveJointMomentData(mtpInputs, results, resultsSynx, resultsSynxNoResiduals, resultsDirectory); +saveJointMomentData(mtpInputs, results, resultsSynx, resultsSynxNoResiduals, ... + resultsDirectory); -%% Save hill type model params saveMuscleModelParameters(finalValues, fullfile(resultsDirectory, ... "muscleModelParemeters")); +end -%% Save Passive moments -savePassiveMomentData(precalInputs, modeledValues, resultsDirectory); +function [finalValues, results, resultsSynx, resultsSynxNoResiduals] = ... + getValuesToReport(mtpInputs, precalInputs, optimizedParams) % Need to downscale excitation arrays -%% Save passive forces -savePassiveForceData(mtpInputs, modeledValues, results, resultsSynx, ... - resultsSynxNoResiduals, resultsDirectory); +finalValues = makeMtpValuesAsStruct([], optimizedParams, zeros(1, 7)); +save('finalvalues.mat', 'finalValues') +resultsSynx = calcMtpSynXModeledValues(finalValues, mtpInputs, struct()); +finalValues.synergyWeights(mtpInputs.numberOfExtrapolationWeights + 1 : end) = 0; +resultsSynxNoResiduals = calcMtpSynXModeledValues(finalValues, mtpInputs, struct()); +results = calcMtpModeledValues(finalValues, mtpInputs, struct()); +results.time = mtpInputs.emgTime(:, mtpInputs.numPaddingFrames + 1 : ... + end - mtpInputs.numPaddingFrames); +results.muscleExcitations = results.muscleExcitations(:, :, ... + mtpInputs.numPaddingFrames + 1 : end - mtpInputs.numPaddingFrames); +resultsSynx.muscleExcitations = resultsSynx.muscleExcitations(:, :, ... + mtpInputs.numPaddingFrames + 1 : end - mtpInputs.numPaddingFrames); +end + +function modeledValues = getMuscleTendonLengthInitializationData(... + precalInputs, mtpInputs) + +tempValues.optimalFiberLengthScaleFactors = ... + mtpInputs.optimalFiberLength ./ precalInputs.optimalFiberLength; + +tempValues.tendonSlackLengthScaleFactors = ... + mtpInputs.tendonSlackLength ./ precalInputs.tendonSlackLength; + +precalInputs.maxIsometricForce = mtpInputs.maxIsometricForce; + +precalInputs.optimizeIsometricMaxForce = 0; + +modeledValues = calcMuscleTendonLengthInitializationModeledValues(tempValues, precalInputs); end function savePassiveMomentData(precalInputs, modeledValues, resultsDirectory) @@ -73,20 +99,38 @@ function savePassiveMomentData(precalInputs, modeledValues, resultsDirectory) "passiveJointMomentsModeled"), "_passiveJointMomentsModeled.sto") end -function saveJointMomentData(mtpInputs, results, resultsSynx, ... +function savePassiveForceData(mtpInputs, modeledValues, results, resultsSynx, ... resultsSynxNoResiduals, resultsDirectory) -saveAnalysisData(mtpInputs.coordinateNames, mtpInputs.prefixes, ... - results.muscleJointMoments, strcat(resultsDirectory, ... - "\modelJointMomentsNoSynx"), "_modelJointMomentsNoSynx.sto") -saveAnalysisData(mtpInputs.coordinateNames, mtpInputs.prefixes, ... - resultsSynx.muscleJointMoments, strcat(resultsDirectory, ... - "\modelJointMomentsSynx"), "_modelJointMomentsSynx.sto") -saveAnalysisData(mtpInputs.coordinateNames, mtpInputs.prefixes, ... - resultsSynxNoResiduals.muscleJointMoments, strcat(resultsDirectory, ... - "\modelJointMomentsSynxNoResiduals"), "_modelJointMomentsSynxNoResiduals.sto") -saveAnalysisData(mtpInputs.coordinateNames, mtpInputs.prefixes, ... - mtpInputs.inverseDynamicsMoments, strcat(resultsDirectory, ... - "\inverseDynamicsJointMoments"), "_inverseDynamicsJointMoments.sto") +saveAnalysisData(mtpInputs.muscleNames, mtpInputs.prefixes, ... + modeledValues.passiveForce, strcat(resultsDirectory, ... + "\passiveForcesExperimental"), "_passiveForcesExperimental.sto"); +saveAnalysisData(mtpInputs.muscleNames, mtpInputs.prefixes, ... + results.passiveForce, strcat(resultsDirectory, ... + "\passiveForcesModel"), "_passiveForcesModel.sto"); +saveAnalysisData(mtpInputs.muscleNames, mtpInputs.prefixes, ... + resultsSynx.passiveForce, strcat(resultsDirectory, ... + "\passiveForcesModelSynx"), "_passiveForcesModelSynx.sto"); +saveAnalysisData(mtpInputs.muscleNames, mtpInputs.prefixes, ... + resultsSynxNoResiduals.passiveForce, strcat(resultsDirectory, ... + "\passiveForcesModelSynxNoResiduals"), "_passiveForcesModelSynxNoResiduals.sto"); +end + +function printJointMomentMatchingError(muscleJointMoments, inverseDynamicsMoments) % Good + +for i = 1 : size(muscleJointMoments, 2) + jointMomentsRmse(i) = sqrt(sum((muscleJointMoments(:, i, :) - ... + inverseDynamicsMoments(:, i, :)) .^ 2, 'all') / ... + (numel(inverseDynamicsMoments(:, 1, :)) - 1)); + jointMomentsMae(i) = sum(abs(muscleJointMoments(:, i, :) - ... + inverseDynamicsMoments(:, i, :)) / ... + numel(inverseDynamicsMoments(:, 1, :)), 'all'); +end +fprintf(['The root mean sqrt (RMS) errors between model-predicted ' ... + 'and inverse dynamic moments are: \n' ]); +fprintf(['\n ' num2str(jointMomentsRmse) ' \n']); +fprintf(['The mean absolute errors (MAEs) between model-predicted ' ... + 'and inverse dynamic moments are: \n' ]); +fprintf(['\n ' num2str(jointMomentsMae) ' \n']); end function saveActivationAndExcitationData(mtpInputs, results, ... @@ -105,6 +149,22 @@ function saveActivationAndExcitationData(mtpInputs, results, ... "\muscleActivationsSynx"), "_muscleActivationsSynx.sto") end +function saveJointMomentData(mtpInputs, results, resultsSynx, ... + resultsSynxNoResiduals, resultsDirectory) +saveAnalysisData(mtpInputs.coordinateNames, mtpInputs.prefixes, ... + results.muscleJointMoments, strcat(resultsDirectory, ... + "\modelJointMomentsNoSynx"), "_modelJointMomentsNoSynx.sto") +saveAnalysisData(mtpInputs.coordinateNames, mtpInputs.prefixes, ... + resultsSynx.muscleJointMoments, strcat(resultsDirectory, ... + "\modelJointMomentsSynx"), "_modelJointMomentsSynx.sto") +saveAnalysisData(mtpInputs.coordinateNames, mtpInputs.prefixes, ... + resultsSynxNoResiduals.muscleJointMoments, strcat(resultsDirectory, ... + "\modelJointMomentsSynxNoResiduals"), "_modelJointMomentsSynxNoResiduals.sto") +saveAnalysisData(mtpInputs.coordinateNames, mtpInputs.prefixes, ... + mtpInputs.inverseDynamicsMoments, strcat(resultsDirectory, ... + "\inverseDynamicsJointMoments"), "_inverseDynamicsJointMoments.sto") +end + function saveMuscleModelParameters(finalValues, resultsDirectory) if ~exist(resultsDirectory, "dir") mkdir(resultsDirectory); @@ -124,73 +184,6 @@ function saveMuscleModelParameters(finalValues, resultsDirectory) fullfile(resultsDirectory, "muscleModelParameters.sto")); end -function savePassiveForceData(mtpInputs, modeledValues, results, resultsSynx, ... - resultsSynxNoResiduals, resultsDirectory) -saveAnalysisData(mtpInputs.muscleNames, mtpInputs.prefixes, ... - modeledValues.passiveForce, strcat(resultsDirectory, ... - "\passiveForcesExperimental"), "_passiveForcesExperimental.sto"); -saveAnalysisData(mtpInputs.muscleNames, mtpInputs.prefixes, ... - results.passiveForce, strcat(resultsDirectory, ... - "\passiveForcesModel"), "_passiveForcesModel.sto"); -saveAnalysisData(mtpInputs.muscleNames, mtpInputs.prefixes, ... - resultsSynx.passiveForce, strcat(resultsDirectory, ... - "\passiveForcesModelSynx"), "_passiveForcesModelSynx.sto"); -saveAnalysisData(mtpInputs.muscleNames, mtpInputs.prefixes, ... - resultsSynxNoResiduals.passiveForce, strcat(resultsDirectory, ... - "\passiveForcesModelSynxNoResiduals"), "_passiveForcesModelSynxNoResiduals.sto"); -end - -function [finalValues, results, resultsSynx, resultsSynxNoResiduals] = ... - getValuesToReport(mtpInputs, precalInputs, optimizedParams) % Need to downscale excitation arrays - -finalValues = makeMtpValuesAsStruct([], optimizedParams, zeros(1, 7)); -save('finalvalues.mat', 'finalValues') -resultsSynx = calcMtpSynXModeledValues(finalValues, mtpInputs, struct()); -finalValues.synergyWeights(mtpInputs.numberOfExtrapolationWeights + 1 : end) = 0; -resultsSynxNoResiduals = calcMtpSynXModeledValues(finalValues, mtpInputs, struct()); -results = calcMtpModeledValues(finalValues, mtpInputs, struct()); -results.time = mtpInputs.emgTime(:, mtpInputs.numPaddingFrames + 1 : ... - end - mtpInputs.numPaddingFrames); -results.muscleExcitations = results.muscleExcitations(:, :, ... - mtpInputs.numPaddingFrames + 1 : end - mtpInputs.numPaddingFrames); -resultsSynx.muscleExcitations = resultsSynx.muscleExcitations(:, :, ... - mtpInputs.numPaddingFrames + 1 : end - mtpInputs.numPaddingFrames); -end - -function modeledValues = getMuscleTendonLengthInitializationData(... - precalInputs, mtpInputs) - -tempValues.optimalFiberLengthScaleFactors = ... - mtpInputs.optimalFiberLength ./ precalInputs.optimalFiberLength; - -tempValues.tendonSlackLengthScaleFactors = ... - mtpInputs.tendonSlackLength ./ precalInputs.tendonSlackLength; - -precalInputs.maxIsometricForce = mtpInputs.maxIsometricForce; - -precalInputs.optimizeIsometricMaxForce = 0; - -modeledValues = calcMuscleTendonLengthInitializationModeledValues(tempValues, precalInputs); -end - -function printJointMomentMatchingError(muscleJointMoments, inverseDynamicsMoments) % Good - -for i = 1 : size(muscleJointMoments, 2) - jointMomentsRmse(i) = sqrt(sum((muscleJointMoments(:, i, :) - ... - inverseDynamicsMoments(:, i, :)) .^ 2, 'all') / ... - (numel(inverseDynamicsMoments(:, 1, :)) - 1)); - jointMomentsMae(i) = sum(abs(muscleJointMoments(:, i, :) - ... - inverseDynamicsMoments(:, i, :)) / ... - numel(inverseDynamicsMoments(:, 1, :)), 'all'); -end -fprintf(['The root mean sqrt (RMS) errors between model-predicted ' ... - 'and inverse dynamic moments are: \n' ]); -fprintf(['\n ' num2str(jointMomentsRmse) ' \n']); -fprintf(['The mean absolute errors (MAEs) between model-predicted ' ... - 'and inverse dynamic moments are: \n' ]); -fprintf(['\n ' num2str(jointMomentsMae) ' \n']); -end - function saveAnalysisData(columnLabels, taskNames, data, directory, fileName) if ~exist(directory, "dir") mkdir(directory); @@ -199,16 +192,4 @@ function saveAnalysisData(columnLabels, taskNames, data, directory, fileName) writeToSto(columnLabels, 1:1:length(data(i,:,:)), ... permute(data(i,:,:), [3 2 1]), strcat(directory, "\", taskNames(i), fileName)) end -end - -function muscleLabels = getSynxMuscleNames(muscleNames, ... - missingEmgChannelGroups) - -for i = 1 : numel(muscleNames) - if ismember(i, [missingEmgChannelGroups{:}]) - muscleLabels{i} = [muscleNames{i} '(*)']; - else - muscleLabels{i} = muscleNames{i}; - end -end end \ No newline at end of file From 63382963891efde85683539e14636739fdbebe16 Mon Sep 17 00:00:00 2001 From: RobSalati Date: Sat, 25 Nov 2023 18:18:22 -0600 Subject: [PATCH 212/365] wrote extractSavedData & plotExcitationsAndActivations Wrote function to extract saved .sto files to 3D arrays for plotting. Wrote a function to plot muscle excitations and activations with and without synx. --- .../-2H4XpEbDs4LttLPyXfzbPUMCNod.xml} | 0 .../-2H4XpEbDs4LttLPyXfzbPUMCNop.xml} | 0 .../5Nw1t_7cq4qlgUmed80suoxL1bod.xml | 6 +++ .../5Nw1t_7cq4qlgUmed80suoxL1bop.xml | 2 + .../Tijkec3DG5uwce1DYpgDo58XJtEd.xml | 6 +++ .../Tijkec3DG5uwce1DYpgDo58XJtEp.xml | 2 + .../ZGtRn_ciKm4_JaX-GAud3iO0k_Ed.xml | 6 +++ .../ZGtRn_ciKm4_JaX-GAud3iO0k_Ep.xml | 2 + .../diONK4qBYLdv_ys7QKb0WQQIOr0d.xml | 6 +++ .../diONK4qBYLdv_ys7QKb0WQQIOr0p.xml | 2 + .../sdn40Xzdst7Zb2H9tGSrmIM0DwYd.xml | 6 +++ .../sdn40Xzdst7Zb2H9tGSrmIM0DwYp.xml | 2 + .../tMvQ-p2hcTG5TEzg6UjLlP8PoZsd.xml | 6 +++ .../tMvQ-p2hcTG5TEzg6UjLlP8PoZsp.xml | 2 + .../udhwDiQtJoEy0hk-eIHYPaAcY-4d.xml | 6 +++ .../udhwDiQtJoEy0hk-eIHYPaAcY-4p.xml | 2 + .../siPmnMvEOL-G1gFgOVY3MMk5oAcd.xml | 2 + .../siPmnMvEOL-G1gFgOVY3MMk5oAcp.xml | 2 + .../-aBlB53ncmKI2GMHO0FbGFfJ9vsp.xml | 2 - ...d.xml => 3S1lLYGLIjnYoeqS66xjbiTYaGod.xml} | 0 .../3S1lLYGLIjnYoeqS66xjbiTYaGop.xml | 2 + .../wlGOvJScYYqnqtARzyW1cByrXncd.xml | 6 +++ .../wlGOvJScYYqnqtARzyW1cByrXncp.xml | 2 + .../Analysis/extractSavedData.m | 19 +++++++ .../Analysis/plotHillTypeMuscleParams.m | 7 +++ .../Analysis/plotJointMoments.m | 7 +++ .../plotMuscleExcitationsAndActivations.m | 53 +++++++++++++++++++ .../Analysis/plotNormalizedFiberLengths.m | 7 +++ .../Analysis/plotPassiveForceCurves.m | 7 +++ .../Analysis/plotPassiveMomentCurves.m | 7 +++ .../MuscleTendonPersonalizationTool.m | 12 ++--- ...eportMuscleTendonPersonalizationResults2.m | 47 ++++++++++++++++ .../saveMuscleTendonOptimizationParams.m | 24 ++++++++- 33 files changed, 250 insertions(+), 12 deletions(-) rename resources/project/{-aBlB53ncmKI2GMHO0FbGFfJ9vs/pN9Z5OP9bqj0zUPkEbtV71ffm3Qd.xml => 3S1lLYGLIjnYoeqS66xjbiTYaGo/-2H4XpEbDs4LttLPyXfzbPUMCNod.xml} (100%) rename resources/project/{-aBlB53ncmKI2GMHO0FbGFfJ9vs/pN9Z5OP9bqj0zUPkEbtV71ffm3Qp.xml => 3S1lLYGLIjnYoeqS66xjbiTYaGo/-2H4XpEbDs4LttLPyXfzbPUMCNop.xml} (100%) create mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/5Nw1t_7cq4qlgUmed80suoxL1bod.xml create mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/5Nw1t_7cq4qlgUmed80suoxL1bop.xml create mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Tijkec3DG5uwce1DYpgDo58XJtEd.xml create mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Tijkec3DG5uwce1DYpgDo58XJtEp.xml create mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/ZGtRn_ciKm4_JaX-GAud3iO0k_Ed.xml create mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/ZGtRn_ciKm4_JaX-GAud3iO0k_Ep.xml create mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/diONK4qBYLdv_ys7QKb0WQQIOr0d.xml create mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/diONK4qBYLdv_ys7QKb0WQQIOr0p.xml create mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/sdn40Xzdst7Zb2H9tGSrmIM0DwYd.xml create mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/sdn40Xzdst7Zb2H9tGSrmIM0DwYp.xml create mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/tMvQ-p2hcTG5TEzg6UjLlP8PoZsd.xml create mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/tMvQ-p2hcTG5TEzg6UjLlP8PoZsp.xml create mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/udhwDiQtJoEy0hk-eIHYPaAcY-4d.xml create mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/udhwDiQtJoEy0hk-eIHYPaAcY-4p.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/siPmnMvEOL-G1gFgOVY3MMk5oAcd.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/siPmnMvEOL-G1gFgOVY3MMk5oAcp.xml delete mode 100644 resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/-aBlB53ncmKI2GMHO0FbGFfJ9vsp.xml rename resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/{-aBlB53ncmKI2GMHO0FbGFfJ9vsd.xml => 3S1lLYGLIjnYoeqS66xjbiTYaGod.xml} (100%) create mode 100644 resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/3S1lLYGLIjnYoeqS66xjbiTYaGop.xml create mode 100644 resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/wlGOvJScYYqnqtARzyW1cByrXncd.xml create mode 100644 resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/wlGOvJScYYqnqtARzyW1cByrXncp.xml create mode 100644 src/MuscleTendonPersonalization/Analysis/extractSavedData.m create mode 100644 src/MuscleTendonPersonalization/Analysis/plotHillTypeMuscleParams.m create mode 100644 src/MuscleTendonPersonalization/Analysis/plotJointMoments.m create mode 100644 src/MuscleTendonPersonalization/Analysis/plotMuscleExcitationsAndActivations.m create mode 100644 src/MuscleTendonPersonalization/Analysis/plotNormalizedFiberLengths.m create mode 100644 src/MuscleTendonPersonalization/Analysis/plotPassiveForceCurves.m create mode 100644 src/MuscleTendonPersonalization/Analysis/plotPassiveMomentCurves.m create mode 100644 src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResults2.m diff --git a/resources/project/-aBlB53ncmKI2GMHO0FbGFfJ9vs/pN9Z5OP9bqj0zUPkEbtV71ffm3Qd.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/-2H4XpEbDs4LttLPyXfzbPUMCNod.xml similarity index 100% rename from resources/project/-aBlB53ncmKI2GMHO0FbGFfJ9vs/pN9Z5OP9bqj0zUPkEbtV71ffm3Qd.xml rename to resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/-2H4XpEbDs4LttLPyXfzbPUMCNod.xml diff --git a/resources/project/-aBlB53ncmKI2GMHO0FbGFfJ9vs/pN9Z5OP9bqj0zUPkEbtV71ffm3Qp.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/-2H4XpEbDs4LttLPyXfzbPUMCNop.xml similarity index 100% rename from resources/project/-aBlB53ncmKI2GMHO0FbGFfJ9vs/pN9Z5OP9bqj0zUPkEbtV71ffm3Qp.xml rename to resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/-2H4XpEbDs4LttLPyXfzbPUMCNop.xml diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/5Nw1t_7cq4qlgUmed80suoxL1bod.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/5Nw1t_7cq4qlgUmed80suoxL1bod.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/5Nw1t_7cq4qlgUmed80suoxL1bod.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/5Nw1t_7cq4qlgUmed80suoxL1bop.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/5Nw1t_7cq4qlgUmed80suoxL1bop.xml new file mode 100644 index 000000000..213e8a9bf --- /dev/null +++ b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/5Nw1t_7cq4qlgUmed80suoxL1bop.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Tijkec3DG5uwce1DYpgDo58XJtEd.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Tijkec3DG5uwce1DYpgDo58XJtEd.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Tijkec3DG5uwce1DYpgDo58XJtEd.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Tijkec3DG5uwce1DYpgDo58XJtEp.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Tijkec3DG5uwce1DYpgDo58XJtEp.xml new file mode 100644 index 000000000..01277b0e5 --- /dev/null +++ b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Tijkec3DG5uwce1DYpgDo58XJtEp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/ZGtRn_ciKm4_JaX-GAud3iO0k_Ed.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/ZGtRn_ciKm4_JaX-GAud3iO0k_Ed.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/ZGtRn_ciKm4_JaX-GAud3iO0k_Ed.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/ZGtRn_ciKm4_JaX-GAud3iO0k_Ep.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/ZGtRn_ciKm4_JaX-GAud3iO0k_Ep.xml new file mode 100644 index 000000000..833a9d565 --- /dev/null +++ b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/ZGtRn_ciKm4_JaX-GAud3iO0k_Ep.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/diONK4qBYLdv_ys7QKb0WQQIOr0d.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/diONK4qBYLdv_ys7QKb0WQQIOr0d.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/diONK4qBYLdv_ys7QKb0WQQIOr0d.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/diONK4qBYLdv_ys7QKb0WQQIOr0p.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/diONK4qBYLdv_ys7QKb0WQQIOr0p.xml new file mode 100644 index 000000000..37bed85dc --- /dev/null +++ b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/diONK4qBYLdv_ys7QKb0WQQIOr0p.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/sdn40Xzdst7Zb2H9tGSrmIM0DwYd.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/sdn40Xzdst7Zb2H9tGSrmIM0DwYd.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/sdn40Xzdst7Zb2H9tGSrmIM0DwYd.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/sdn40Xzdst7Zb2H9tGSrmIM0DwYp.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/sdn40Xzdst7Zb2H9tGSrmIM0DwYp.xml new file mode 100644 index 000000000..5ae250aa9 --- /dev/null +++ b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/sdn40Xzdst7Zb2H9tGSrmIM0DwYp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/tMvQ-p2hcTG5TEzg6UjLlP8PoZsd.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/tMvQ-p2hcTG5TEzg6UjLlP8PoZsd.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/tMvQ-p2hcTG5TEzg6UjLlP8PoZsd.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/tMvQ-p2hcTG5TEzg6UjLlP8PoZsp.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/tMvQ-p2hcTG5TEzg6UjLlP8PoZsp.xml new file mode 100644 index 000000000..182ca876b --- /dev/null +++ b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/tMvQ-p2hcTG5TEzg6UjLlP8PoZsp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/udhwDiQtJoEy0hk-eIHYPaAcY-4d.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/udhwDiQtJoEy0hk-eIHYPaAcY-4d.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/udhwDiQtJoEy0hk-eIHYPaAcY-4d.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/udhwDiQtJoEy0hk-eIHYPaAcY-4p.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/udhwDiQtJoEy0hk-eIHYPaAcY-4p.xml new file mode 100644 index 000000000..220513d19 --- /dev/null +++ b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/udhwDiQtJoEy0hk-eIHYPaAcY-4p.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/siPmnMvEOL-G1gFgOVY3MMk5oAcd.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/siPmnMvEOL-G1gFgOVY3MMk5oAcd.xml new file mode 100644 index 000000000..761c6b701 --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/siPmnMvEOL-G1gFgOVY3MMk5oAcd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/siPmnMvEOL-G1gFgOVY3MMk5oAcp.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/siPmnMvEOL-G1gFgOVY3MMk5oAcp.xml new file mode 100644 index 000000000..49a349d5e --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/siPmnMvEOL-G1gFgOVY3MMk5oAcp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/-aBlB53ncmKI2GMHO0FbGFfJ9vsp.xml b/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/-aBlB53ncmKI2GMHO0FbGFfJ9vsp.xml deleted file mode 100644 index f10f1a511..000000000 --- a/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/-aBlB53ncmKI2GMHO0FbGFfJ9vsp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/-aBlB53ncmKI2GMHO0FbGFfJ9vsd.xml b/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/3S1lLYGLIjnYoeqS66xjbiTYaGod.xml similarity index 100% rename from resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/-aBlB53ncmKI2GMHO0FbGFfJ9vsd.xml rename to resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/3S1lLYGLIjnYoeqS66xjbiTYaGod.xml diff --git a/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/3S1lLYGLIjnYoeqS66xjbiTYaGop.xml b/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/3S1lLYGLIjnYoeqS66xjbiTYaGop.xml new file mode 100644 index 000000000..7d0d0afa0 --- /dev/null +++ b/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/3S1lLYGLIjnYoeqS66xjbiTYaGop.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/wlGOvJScYYqnqtARzyW1cByrXncd.xml b/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/wlGOvJScYYqnqtARzyW1cByrXncd.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/wlGOvJScYYqnqtARzyW1cByrXncd.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/wlGOvJScYYqnqtARzyW1cByrXncp.xml b/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/wlGOvJScYYqnqtARzyW1cByrXncp.xml new file mode 100644 index 000000000..f2d1d15ad --- /dev/null +++ b/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/wlGOvJScYYqnqtARzyW1cByrXncp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/Analysis/extractSavedData.m b/src/MuscleTendonPersonalization/Analysis/extractSavedData.m new file mode 100644 index 000000000..346af43df --- /dev/null +++ b/src/MuscleTendonPersonalization/Analysis/extractSavedData.m @@ -0,0 +1,19 @@ +function [columnNames, data] = extractSavedData(resultsDirectoryParent, resultsDirectoryChild) + import org.opensim.modeling.Storage + if exist(fullfile(resultsDirectoryParent, resultsDirectoryChild), "dir") + dataDir = dir(fullfile(resultsDirectoryParent, resultsDirectoryChild)); + dataFiles = {dataDir(3:end).name}; + else + fprintf("%s not found", resultsDirectoryChild); + return + end + data = cell(1, numel(dataFiles)); + for i = 1:numel(dataFiles) + dataStorage = Storage(fullfile(fullfile(resultsDirectoryParent, resultsDirectoryChild, dataFiles{i}))); + columnNames = getStorageColumnNames(dataStorage); + data{i} = storageToDoubleMatrix(dataStorage)'; + end + trialSize = size(data{1}); + numTrials = numel(data); + data = reshape(cell2mat(data), trialSize(1), trialSize(2), numTrials); +end \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/Analysis/plotHillTypeMuscleParams.m b/src/MuscleTendonPersonalization/Analysis/plotHillTypeMuscleParams.m new file mode 100644 index 000000000..aa1755229 --- /dev/null +++ b/src/MuscleTendonPersonalization/Analysis/plotHillTypeMuscleParams.m @@ -0,0 +1,7 @@ +function [outputArg1,outputArg2] = plotHillTypeMuscleParams(inputArg1,inputArg2) +%UNTITLED5 Summary of this function goes here +% Detailed explanation goes here +outputArg1 = inputArg1; +outputArg2 = inputArg2; +end + diff --git a/src/MuscleTendonPersonalization/Analysis/plotJointMoments.m b/src/MuscleTendonPersonalization/Analysis/plotJointMoments.m new file mode 100644 index 000000000..f81317946 --- /dev/null +++ b/src/MuscleTendonPersonalization/Analysis/plotJointMoments.m @@ -0,0 +1,7 @@ +function [outputArg1,outputArg2] = plotJointMoments(inputArg1,inputArg2) +%UNTITLED4 Summary of this function goes here +% Detailed explanation goes here +outputArg1 = inputArg1; +outputArg2 = inputArg2; +end + diff --git a/src/MuscleTendonPersonalization/Analysis/plotMuscleExcitationsAndActivations.m b/src/MuscleTendonPersonalization/Analysis/plotMuscleExcitationsAndActivations.m new file mode 100644 index 000000000..1d4428d69 --- /dev/null +++ b/src/MuscleTendonPersonalization/Analysis/plotMuscleExcitationsAndActivations.m @@ -0,0 +1,53 @@ +function plotMuscleExcitationsAndActivations(resultsDirectory) +[muscleNames, excitations] = extractSavedData(resultsDirectory, "muscleExcitations"); +[~, activations] = extractSavedData(resultsDirectory, "muscleActivations"); +[~, excitationsSynx] = extractSavedData(resultsDirectory, "muscleExcitationsSynx"); +[~, activationsSynx] = extractSavedData(resultsDirectory, "muscleActivationsSynx"); + +meanExcitations = mean(excitations, 3); +stdExcitations = std(excitations,[], 3); +meanActivations = mean(activations, 3); +stdActivations = std(activations,[], 3); +meanExcitationsSynx = mean(excitationsSynx, 3); +stdExcitationsSynx = std(excitationsSynx,[], 3); +meanActivationsSynx = mean(activationsSynx, 3); +stdActivationsSynx = std(activationsSynx,[], 3); + +numWindows = ceil(sqrt(numel(muscleNames))); + +figure() + +t = 1:1:size(meanExcitations,1); +for i = 1:numel(muscleNames) + subplot(numWindows, numWindows, i) + hold on + plot(meanExcitations(:,i), 'b-', linewidth=2) + plot(meanActivations(:,i), 'r-', linewidth=2) + plot(meanExcitationsSynx(:,i), 'b--', linewidth=2) + plot(meanActivationsSynx(:,i), 'r--', linewidth=2) + + excitationFillRegion = [(meanExcitations(:,i)+stdExcitations(:,i)); ... + flipud((meanExcitations(:,i)-stdExcitations(:,i)))]; + fill([t, fliplr(t)]', excitationFillRegion, 'b', FaceAlpha=0.2, ... + EdgeColor='none', HandleVisibility='off') + + excitationSynxFillRegion = [(meanExcitationsSynx(:,i)+stdExcitationsSynx(:,i)); ... + flipud((meanExcitationsSynx(:,i)-stdExcitationsSynx(:,i)))]; + fill([t, fliplr(t)]', excitationSynxFillRegion, 'b', FaceAlpha=0.2, ... + EdgeColor='none', HandleVisibility='off') + + activationFillRegion = [(meanActivations(:,i)+stdActivations(:,i)); ... + flipud((meanActivations(:,i)-stdActivations(:,i)))]; + fill([t, fliplr(t)]', activationFillRegion, 'b', FaceAlpha=0.2, ... + EdgeColor='none', HandleVisibility='off') + + activationSynxFillRegion = [(meanActivationsSynx(:,i)+stdActivationsSynx(:,i)); ... + flipud((meanActivationsSynx(:,i)-stdActivationsSynx(:,i)))]; + fill([t, fliplr(t)]', activationSynxFillRegion, 'b', FaceAlpha=0.2, ... + EdgeColor='none', HandleVisibility='off') + + axis([1 size(meanExcitations, 1) 0 1]) + title(muscleNames(i), FontSize=10, interpreter='latex'); + % set(gca, 'FontSize', 10) +end +end \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/Analysis/plotNormalizedFiberLengths.m b/src/MuscleTendonPersonalization/Analysis/plotNormalizedFiberLengths.m new file mode 100644 index 000000000..8231a1c79 --- /dev/null +++ b/src/MuscleTendonPersonalization/Analysis/plotNormalizedFiberLengths.m @@ -0,0 +1,7 @@ +function [outputArg1,outputArg2] = plotNormalizedFiberLengths(inputArg1,inputArg2) +%UNTITLED6 Summary of this function goes here +% Detailed explanation goes here +outputArg1 = inputArg1; +outputArg2 = inputArg2; +end + diff --git a/src/MuscleTendonPersonalization/Analysis/plotPassiveForceCurves.m b/src/MuscleTendonPersonalization/Analysis/plotPassiveForceCurves.m new file mode 100644 index 000000000..d9364ec48 --- /dev/null +++ b/src/MuscleTendonPersonalization/Analysis/plotPassiveForceCurves.m @@ -0,0 +1,7 @@ +function [outputArg1,outputArg2] = plotPassiveForceCurves(inputArg1,inputArg2) +%UNTITLED2 Summary of this function goes here +% Detailed explanation goes here +outputArg1 = inputArg1; +outputArg2 = inputArg2; +end + diff --git a/src/MuscleTendonPersonalization/Analysis/plotPassiveMomentCurves.m b/src/MuscleTendonPersonalization/Analysis/plotPassiveMomentCurves.m new file mode 100644 index 000000000..1b5803d08 --- /dev/null +++ b/src/MuscleTendonPersonalization/Analysis/plotPassiveMomentCurves.m @@ -0,0 +1,7 @@ +function [outputArg1,outputArg2] = plotPassiveMomentCurves(inputArg1,inputArg2) +%UNTITLED3 Summary of this function goes here +% Detailed explanation goes here +outputArg1 = inputArg1; +outputArg2 = inputArg2; +end + diff --git a/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m b/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m index 849972c91..f801ce483 100644 --- a/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m +++ b/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m @@ -35,7 +35,6 @@ function MuscleTendonPersonalizationTool(settingsFileName) [inputs, params, resultsDirectory] = ... parseMuscleTendonPersonalizationSettingsTree(settingsTree); precalInputs = parseMuscleTendonLengthInitializationSettingsTree(settingsTree); - if isstruct(precalInputs) optimizedInitialGuess = MuscleTendonLengthInitialization(precalInputs); inputs = updateMtpInitialGuess(inputs, precalInputs, ... @@ -43,9 +42,10 @@ function MuscleTendonPersonalizationTool(settingsFileName) else precalInputs = struct('optimizeIsometricMaxForce', false); end - optimizedParams = MuscleTendonPersonalization(inputs, params); + if params.performMuscleTendonLengthInitialization + % reportMuscleTendonPersonalizationResults(optimizedParams, inputs, precalInputs); saveMuscleTendonOptimizationParams(".\mtpResults\Analysis", ... optimizedParams, inputs, precalInputs) else @@ -53,6 +53,8 @@ function MuscleTendonPersonalizationTool(settingsFileName) optimizedParams, inputs) end +reportMuscleTendonPersonalizationResults2(".\mtpResults\Analysis"); + finalValues = makeMtpValuesAsStruct([], optimizedParams, zeros(1, 7)); if precalInputs.optimizeIsometricMaxForce finalValues.maxIsometricForce = inputs.maxIsometricForce; @@ -64,10 +66,4 @@ function MuscleTendonPersonalizationTool(settingsFileName) saveMuscleTendonPersonalizationResults(inputs.model, ... inputs.osimxFileName, inputs.prefixes, inputs.coordinateNames, ... finalValues, results, resultsDirectory, inputs.muscleTendonColumnNames); -% saveMuscleTendonOptimizationResults(optimizedParams, inputs, precalInputs, ... -% ".\mtpResults\Analysis") -% save("params.mat", "params") -% save("optimizedParams.mat", "optimizedParams") -% save("precalInputs.mat", "precalInputs") -% save("inputs.mat", "inputs") end diff --git a/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResults2.m b/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResults2.m new file mode 100644 index 000000000..1924adcad --- /dev/null +++ b/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResults2.m @@ -0,0 +1,47 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function writes to console or file the results of the optimization +% for easy evaluation by the user. It may require the input model to +% produce relative results or other information for output. +% +% (struct, struct) -> (None) +% Prints and plots results from muscle tendon personalization + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Robert Salati % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % +function reportMuscleTendonPersonalizationResults2(resultsDirectory) + + % Read from STO files and call individual functions + plotMuscleExcitationsAndActivations(resultsDirectory); + % ... + % plotPassiveForceCurves(); + % % ... + % plotPassiveMomentCurves(); + % % ... + % plotJointMoments(); + % % ... + % plotHillTypeMuscleParams(); + % % ... + % plotNormalizedFiberLengths(); + +end + diff --git a/src/MuscleTendonPersonalization/saveMuscleTendonOptimizationParams.m b/src/MuscleTendonPersonalization/saveMuscleTendonOptimizationParams.m index 44e3b3719..6678d057f 100644 --- a/src/MuscleTendonPersonalization/saveMuscleTendonOptimizationParams.m +++ b/src/MuscleTendonPersonalization/saveMuscleTendonOptimizationParams.m @@ -4,8 +4,28 @@ % the user in either MATLAB or Opensim % % (struct, struct, struct, string) -> (None) -% Saves data from optimization to 6 .sto files - +% Saves MTP optimization parameters to .sto files to be plotted. +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Robert Salati, Di Ao, Marleny Vega % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % function saveMuscleTendonOptimizationParams(resultsDirectory, optimizedParams, ... mtpInputs, precalInputs) if nargin < 4 From f359645b15514bf4aecce804e61ef237be402b19 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 27 Nov 2023 22:23:20 -0600 Subject: [PATCH 213/365] Fix parsing torque controls --- .../OptimalControlSetupFunctions/setupGpopsInitialGuess.m | 6 +++++- src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m index 1b9f7dc76..d2b4f2599 100644 --- a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m +++ b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m @@ -93,7 +93,11 @@ controls = [controls, inputs.initialTorqueControls]; else if ~isempty(valueOrAlternate(inputs, "torqueControllerCoordinateNames", [])) - controls = [controls, inputs.experimentalJointMoments]; + stateTorqueControls = subsetDataByCoordinates( ... + inputs.experimentalJointMoments, ... + inputs.coordinateNames, ... + inputs.torqueControllerCoordinateNames); + controls = [controls, stateTorqueControls]; end end guess.phase.control = scaleToBounds(controls, inputs.maxControl, ... diff --git a/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m b/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m index 6c05d77c8..6f73c4301 100644 --- a/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m +++ b/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m @@ -50,8 +50,8 @@ length(inputs.statesCoordinateNames) + inputs.numSynergies); end values.torqueControls = control(:, ... - inputs.numCoordinates + 1 + inputs.numSynergies : ... - inputs.numCoordinates + inputs.numSynergies + ... + length(inputs.statesCoordinateNames) + 1 + inputs.numSynergies : ... + length(inputs.statesCoordinateNames) + inputs.numSynergies + ... length(inputs.torqueControllerCoordinateNames)); if strcmp(inputs.toolName, "TrackingOptimization") From 1eb93375d8cc2a12381e43a9ab0a751f8f59918f Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 27 Nov 2023 22:57:50 -0600 Subject: [PATCH 214/365] Fix torque control tracking indices --- .../IntegrandTerms/calcTrackingCoordinateIntegrand.m | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m index 507341603..215795c4d 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m @@ -45,8 +45,13 @@ experimentalJointAngles = fnval(auxdata.splineJointAngles, time); end -cost = calcTrackingCostArrayTerm(experimentalJointAngles, ... - statePositions, indx); +assert(all([any(auxdata.statesCoordinateNames == coordinateName), ... + any(string(auxdata.coordinateNames) == coordinateName)])); +stateIndex = auxdata.statesCoordinateNames == coordinateName; +experimentalIndex = string(auxdata.coordinateNames) == coordinateName; + +cost = calcTrackingCostArrayTerm(experimentalJointAngles(:, stateIndex),... + statePositions(:, experimentalIndex), 1); if normalizeByFinalTime cost = cost / time(end); From e41ff2855ead44389a0691749a2077d7825890a0 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 27 Nov 2023 22:59:02 -0600 Subject: [PATCH 215/365] add error message --- .../IntegrandTerms/calcTrackingCoordinateIntegrand.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m index 215795c4d..4d0aa7f33 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m @@ -46,7 +46,9 @@ end assert(all([any(auxdata.statesCoordinateNames == coordinateName), ... - any(string(auxdata.coordinateNames) == coordinateName)])); + any(string(auxdata.coordinateNames) == coordinateName)]), ... + "Tracked coordinate must exist in states and experimental data: " ... + + coordinateName); stateIndex = auxdata.statesCoordinateNames == coordinateName; experimentalIndex = string(auxdata.coordinateNames) == coordinateName; From 030c716f428b2b1c84febfba1cc471c81b6d92c5 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 27 Nov 2023 23:19:52 -0600 Subject: [PATCH 216/365] Revert "add error message" This reverts commit e41ff2855ead44389a0691749a2077d7825890a0. --- .../IntegrandTerms/calcTrackingCoordinateIntegrand.m | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m index 4d0aa7f33..215795c4d 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m @@ -46,9 +46,7 @@ end assert(all([any(auxdata.statesCoordinateNames == coordinateName), ... - any(string(auxdata.coordinateNames) == coordinateName)]), ... - "Tracked coordinate must exist in states and experimental data: " ... - + coordinateName); + any(string(auxdata.coordinateNames) == coordinateName)])); stateIndex = auxdata.statesCoordinateNames == coordinateName; experimentalIndex = string(auxdata.coordinateNames) == coordinateName; From 6bd9788fe5c9385c184f390d1247b2aa7c95798f Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 27 Nov 2023 23:19:57 -0600 Subject: [PATCH 217/365] Revert "Fix torque control tracking indices" This reverts commit 1eb93375d8cc2a12381e43a9ab0a751f8f59918f. --- .../IntegrandTerms/calcTrackingCoordinateIntegrand.m | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m index 215795c4d..507341603 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m @@ -45,13 +45,8 @@ experimentalJointAngles = fnval(auxdata.splineJointAngles, time); end -assert(all([any(auxdata.statesCoordinateNames == coordinateName), ... - any(string(auxdata.coordinateNames) == coordinateName)])); -stateIndex = auxdata.statesCoordinateNames == coordinateName; -experimentalIndex = string(auxdata.coordinateNames) == coordinateName; - -cost = calcTrackingCostArrayTerm(experimentalJointAngles(:, stateIndex),... - statePositions(:, experimentalIndex), 1); +cost = calcTrackingCostArrayTerm(experimentalJointAngles, ... + statePositions, indx); if normalizeByFinalTime cost = cost / time(end); From 0fd8c099ea2fff1ab0a70f8e6bf1a271ec9e5a78 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 27 Nov 2023 23:21:55 -0600 Subject: [PATCH 218/365] Fix index for coordinate tracking --- .../IntegrandTerms/calcTrackingCoordinateIntegrand.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m index 507341603..ac46d4ca7 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m @@ -32,7 +32,7 @@ statePositions, coordinateName) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); -indx = find(strcmp(convertCharsToStrings(auxdata.statesCoordinateNames), ... +indx = find(strcmp(convertCharsToStrings(auxdata.coordinateNames), ... coordinateName)); if isempty(indx) throw(MException('CostTermError:CoordinateNotInState', ... From a5d5a84909db8b256fc14e5fd2c9b66d7f7ff17e Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 27 Nov 2023 23:28:42 -0600 Subject: [PATCH 219/365] Fix appliedLoads matrix --- .../calcTorqueBasedModeledValues.m | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m b/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m index 9599a46a7..a28f3d21a 100644 --- a/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m +++ b/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m @@ -44,6 +44,13 @@ function [appliedLoads, modeledValues] = setupAppliedLoads(values, ... inputs, modeledValues) appliedLoads = []; +numCoordinateLoads = inputs.model.getForceSet().getSize() - ... + inputs.model.getForceSet().getMuscles().getSize() - ... + (12 * length(inputs.contactSurfaces)); +coordinateLoads = zeros(length(values.time), numCoordinateLoads); +appliedLoads = [zeros(length(values.time), ... + inputs.model.getForceSet().getMuscles().getSize()) ... + appliedLoads coordinateLoads]; if ~isempty(inputs.contactSurfaces) clear pointKinematics [springPositions, springVelocities] = getSpringLocations( ... @@ -55,13 +62,7 @@ groundReactionsBody = tranferGroundReactionMoments( ... modeledValues.bodyLocations, groundReactions, inputs); modeledValues.groundReactionsLab = calcGroundReactionsLab(groundReactions); - numCoordinateLoads = inputs.model.getForceSet().getSize() - ... - inputs.model.getForceSet().getMuscles().getSize() - ... - (12 * length(inputs.contactSurfaces)); - coordinateLoads = zeros(length(values.time), numCoordinateLoads); - appliedLoads = [zeros(length(values.time), ... - inputs.model.getForceSet().getMuscles().getSize()) ... - appliedLoads coordinateLoads groundReactionsBody]; + appliedLoads = [appliedLoads groundReactionsBody]; end end From 133756df606aec0ca0625ece734f18eb0367bbe3 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 27 Nov 2023 23:46:43 -0600 Subject: [PATCH 220/365] Fix checking for empty constraint sets --- .../gpops/computeGpopsContinuousFunction.m | 2 +- .../gpops/computeGpopsEndpointFunction.m | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/TreatmentOptimization/gpops/computeGpopsContinuousFunction.m b/src/TreatmentOptimization/gpops/computeGpopsContinuousFunction.m index 29ca7dcfc..3b042a581 100644 --- a/src/TreatmentOptimization/gpops/computeGpopsContinuousFunction.m +++ b/src/TreatmentOptimization/gpops/computeGpopsContinuousFunction.m @@ -37,7 +37,7 @@ modeledValues = calcSynergyBasedModeledValues(values, setup.auxdata, ... modeledValues); modeledValues.dynamics = calcDynamicConstraint(values, setup.auxdata); -if ~isempty(setup.auxdata.path) +if any(cellfun(@(x) x.isEnabled == 1, setup.auxdata.path)) [constraintTermCalculations, allowedTypes] = ... generateConstraintTermStruct("path", ... setup.auxdata.controllerType, setup.auxdata.toolName); diff --git a/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m b/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m index 31fdd03de..c0fae8127 100644 --- a/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m +++ b/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m @@ -29,7 +29,7 @@ % ----------------------------------------------------------------------- % function output = computeGpopsEndpointFunction(setup) -if ~isempty(setup.auxdata.terminal) || strcmp(setup.auxdata.toolName, "DesignOptimization") +if any(cellfun(@(x) x.isEnabled == 1, setup.auxdata.terminal)) || strcmp(setup.auxdata.toolName, "DesignOptimization") setup.phase.state = [setup.phase.initialstate; setup.phase.finalstate]; setup.phase.time = [setup.phase.initialtime; setup.phase.finaltime]; setup.phase.control = ones(size(setup.phase.time,1),length(setup.auxdata.minControl)); @@ -45,7 +45,7 @@ modeledValues); end -if ~isempty(setup.auxdata.terminal) +if any(cellfun(@(x) x.isEnabled == 1, setup.auxdata.terminal)) [constraintTermCalculations, allowedTypes] = ... generateConstraintTermStruct("terminal", ... setup.auxdata.controllerType, setup.auxdata.toolName); From c5464ee6008e4b5ee4258aa98bae6fcebe29981b Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 27 Nov 2023 23:48:22 -0600 Subject: [PATCH 221/365] Preliminary kinematic symmetry cost term --- .../calcKinematicSymmetryDiscrete.m | 40 +++++++++++++++++++ .../generateCostTermStruct.m | 19 ++++++--- 2 files changed, 53 insertions(+), 6 deletions(-) create mode 100644 src/TreatmentOptimization/IntegrandTerms/calcKinematicSymmetryDiscrete.m diff --git a/src/TreatmentOptimization/IntegrandTerms/calcKinematicSymmetryDiscrete.m b/src/TreatmentOptimization/IntegrandTerms/calcKinematicSymmetryDiscrete.m new file mode 100644 index 000000000..70443d6e2 --- /dev/null +++ b/src/TreatmentOptimization/IntegrandTerms/calcKinematicSymmetryDiscrete.m @@ -0,0 +1,40 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function uses frequency analysis to calculate a cost value for +% minimizing gait asymmetry. +% +% (struct, struct, struct) -> (Array of number) +% + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Spencer Williams % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function cost = calcKinematicSymmetryDiscrete(values, inputs, costTerm) +rightCoordinate = costTerm.coordinate + "_r"; +leftCoordinate = costTerm.coordinate + "_l"; +data = values.statePositions(:, inputs.statesCoordinateNames == rightCoordinate); +data = [data values.statePositions(:, inputs.statesCoordinateNames == leftCoordinate)]; + +spectra = pspectrum(data, values.time, 'FrequencyResolution', 0.5/mean(diff(values.time)), 'Leakage', 0); +spectra = sqrt(spectra); +cost = norm((spectra(:, 1) - spectra(:, 2)) ./ mean(spectra, 2)); +end diff --git a/src/TreatmentOptimization/generateCostTermStruct.m b/src/TreatmentOptimization/generateCostTermStruct.m index a142e60ae..d89bd7dd4 100644 --- a/src/TreatmentOptimization/generateCostTermStruct.m +++ b/src/TreatmentOptimization/generateCostTermStruct.m @@ -113,6 +113,7 @@ allowedTypes = [ ... "synergy_vector_tracking" ... "belt_speed_goal" ... + "kinematic_symmetry" ... "user_defined" ... ]; otherwise @@ -341,12 +342,12 @@ values.time, ... auxdata); -costTermCalculations.kinematic_symmetry = @(values, modeledValues, auxdata, costTerm) ... - calcKinematicSymmetryIntegrand( ... - values.positions, ... - values.time, ... - auxdata, ... - costTerm); +% costTermCalculations.kinematic_symmetry = @(values, modeledValues, auxdata, costTerm) ... +% calcKinematicSymmetryIntegrand( ... +% values.positions, ... +% values.time, ... +% auxdata, ... +% costTerm); costTermCalculations.walking_speed_goal = @(values, modeledValues, auxdata, costTerm) ... calcGoalWalkingSpeedIntegrand( ... @@ -376,6 +377,12 @@ auxdata, ... costTerm); +costTermCalculations.kinematic_symmetry = @(values, modeledValues, auxdata, costTerm) ... + calcKinematicSymmetryDiscrete( ... + values, ... + auxdata, ... + costTerm); + costTermCalculations.user_defined = @(values, modeledValues, auxdata, costTerm) ... userDefinedFunction(values, ... modeledValues, ... From f4d830f92d137f739bef4c619e3d956c7d61e9bd Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 27 Nov 2023 23:53:04 -0600 Subject: [PATCH 222/365] Make model_functions, parameter set optional for DO --- .../parseDesignOptimizationSettingsTree.m | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/TreatmentOptimization/DesignOptimization/parseDesignOptimizationSettingsTree.m b/src/TreatmentOptimization/DesignOptimization/parseDesignOptimizationSettingsTree.m index e8f14cbb3..f58a65dc6 100644 --- a/src/TreatmentOptimization/DesignOptimization/parseDesignOptimizationSettingsTree.m +++ b/src/TreatmentOptimization/DesignOptimization/parseDesignOptimizationSettingsTree.m @@ -38,8 +38,10 @@ function inputs = parseUserDefinedFunctions(tree, inputs) import org.opensim.modeling.Storage -inputs.systemFns = parseSpaceSeparatedList(tree, "model_functions"); -parameterTree = getFieldByNameOrError(tree, "RCNLParameterTermSet"); +if getFieldByName(tree, "model_functions") + inputs.systemFns = parseSpaceSeparatedList(tree, "model_functions"); +end +parameterTree = getFieldByName(tree, "RCNLParameterTermSet"); if isstruct(parameterTree) && isfield(parameterTree, "RCNLParameterTerm") inputs.userDefinedVariables = parseRcnlCostTermSet( ... parameterTree.RCNLParameterTerm); From ed7ba21deb1ecc778b3f39b74258b5cfd1077634 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 27 Nov 2023 23:56:21 -0600 Subject: [PATCH 223/365] Skip model functions if unused --- .../updateSystemFromUserDefinedFunctions.m | 8 +++++--- src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m | 3 ++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/TreatmentOptimization/DesignOptimization/updateSystemFromUserDefinedFunctions.m b/src/TreatmentOptimization/DesignOptimization/updateSystemFromUserDefinedFunctions.m index 3649e31d5..0c623f955 100644 --- a/src/TreatmentOptimization/DesignOptimization/updateSystemFromUserDefinedFunctions.m +++ b/src/TreatmentOptimization/DesignOptimization/updateSystemFromUserDefinedFunctions.m @@ -29,8 +29,10 @@ % ----------------------------------------------------------------------- % function inputs = updateSystemFromUserDefinedFunctions(inputs, values) -for i = 1:length(inputs.auxdata.systemFns) - func = str2func(inputs.auxdata.systemFns(i)); - inputs = func(inputs, values); +if getFieldByName(inputs.auxdata, "systemFns") + for i = 1:length(inputs.auxdata.systemFns) + func = str2func(inputs.auxdata.systemFns(i)); + inputs = func(inputs, values); + end end end \ No newline at end of file diff --git a/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m b/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m index 6f73c4301..e5d49d4de 100644 --- a/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m +++ b/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m @@ -96,7 +96,8 @@ end end end - if isfield(inputs, 'userDefinedVariables') + if isfield(inputs, 'userDefinedVariables') && ... + ~isempty(inputs.userDefinedVariables) parameters = scaleToOriginal(phase.parameter(1,:), ... inputs.maxParameter, inputs.minParameter); for i = 1:length(inputs.userDefinedVariables) From 7730a409181562cbf87e56ffe5a7c15be81a2700 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Tue, 28 Nov 2023 00:42:54 -0600 Subject: [PATCH 224/365] Remove symmetry from discrete --- src/TreatmentOptimization/generateCostTermStruct.m | 1 - 1 file changed, 1 deletion(-) diff --git a/src/TreatmentOptimization/generateCostTermStruct.m b/src/TreatmentOptimization/generateCostTermStruct.m index d89bd7dd4..3914519d7 100644 --- a/src/TreatmentOptimization/generateCostTermStruct.m +++ b/src/TreatmentOptimization/generateCostTermStruct.m @@ -113,7 +113,6 @@ allowedTypes = [ ... "synergy_vector_tracking" ... "belt_speed_goal" ... - "kinematic_symmetry" ... "user_defined" ... ]; otherwise From 18434415fb7359fee226c4f75ef4090ef7fcd399 Mon Sep 17 00:00:00 2001 From: RobSalati Date: Thu, 30 Nov 2023 13:49:13 -0600 Subject: [PATCH 225/365] Plotting functions Added functions to read saved data and recreate the existing plots. --- .../xxEkn1cXg5yjGbZKOOkJj6kXUGwd.xml | 6 ++ .../xxEkn1cXg5yjGbZKOOkJj6kXUGwp.xml | 2 + .../Analysis/plotJointMoments.m | 81 +++++++++++++++++-- .../Analysis/plotMtpData.m | 32 ++++++++ .../plotMuscleExcitationsAndActivations.m | 35 ++++++-- .../Analysis/plotNormalizedFiberLengths.m | 23 ++++-- .../Analysis/plotPassiveForceCurves.m | 44 ++++++++-- .../Analysis/plotPassiveMomentCurves.m | 55 +++++++++++-- .../MuscleTendonPersonalizationTool.m | 2 +- ...eportMuscleTendonPersonalizationResults2.m | 10 +-- .../saveMuscleTendonOptimizationParams.m | 1 + 11 files changed, 260 insertions(+), 31 deletions(-) create mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/xxEkn1cXg5yjGbZKOOkJj6kXUGwd.xml create mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/xxEkn1cXg5yjGbZKOOkJj6kXUGwp.xml create mode 100644 src/MuscleTendonPersonalization/Analysis/plotMtpData.m diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/xxEkn1cXg5yjGbZKOOkJj6kXUGwd.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/xxEkn1cXg5yjGbZKOOkJj6kXUGwd.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/xxEkn1cXg5yjGbZKOOkJj6kXUGwd.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/xxEkn1cXg5yjGbZKOOkJj6kXUGwp.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/xxEkn1cXg5yjGbZKOOkJj6kXUGwp.xml new file mode 100644 index 000000000..bfda1cc83 --- /dev/null +++ b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/xxEkn1cXg5yjGbZKOOkJj6kXUGwp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/Analysis/plotJointMoments.m b/src/MuscleTendonPersonalization/Analysis/plotJointMoments.m index f81317946..3188078ac 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotJointMoments.m +++ b/src/MuscleTendonPersonalization/Analysis/plotJointMoments.m @@ -1,7 +1,78 @@ -function [outputArg1,outputArg2] = plotJointMoments(inputArg1,inputArg2) -%UNTITLED4 Summary of this function goes here -% Detailed explanation goes here -outputArg1 = inputArg1; -outputArg2 = inputArg2; +function plotJointMoments(resultsDirectory) + [jointLabels, idMoments] = extractSavedData(resultsDirectory, "inverseDynamicsJointMoments"); + [~, noResidualMoments] = extractSavedData(resultsDirectory, "modelJointMomentsNoSynx"); + [~, withResidualMoments] = extractSavedData(resultsDirectory, "modelJointMomentsSynx"); + + meanIdMoments = mean(idMoments, 3); + stdIdMoments = std(idMoments, [], 3); + meanNoResidualMoments = mean(noResidualMoments, 3); + stdNoResidualMoments = std(noResidualMoments, [], 3); + meanWithResidualMoments = mean(withResidualMoments, 3); + stdWithResidualMoments = std(withResidualMoments, [], 3); + maxMoment = max([max(meanIdMoments, [], "all"), max(meanNoResidualMoments, [], "all") ... + max(meanWithResidualMoments, [], "all")]); + minMoment = min([min(meanIdMoments, [], "all"), min(meanNoResidualMoments, [], "all") ... + min(meanWithResidualMoments, [], "all")]); + + % meanModelMoments = [meanNoResidualMoments, meanWithResidualMoments]; + % stdModelMoments = [stdNoResidualMoments, stdWithResidualMoments]; + % meanIdMoments = [meanIdMoments, meanIdMoments]; %Just reformat for plotting purpose. + % stdIdMoments = [stdIdMoments, stdIdMoments]; + + % data.mean = {meanModelMoments, meanIdMoments}; + % data.std = {stdModelMoments, stdIdMoments}; + % data.labels = [jointLabels jointLabels]; + % plotOptions.axisLimits = [1 size(meanIdMoments, 1) minMoment maxMoment]; + % plotOptions.colors = ["b-", "r-"]; + % plotOptions.xlabel = "Time Points"; + % plotOptions.ylabel = "Joint Moment [Nm]"; + % + % plotMtpData(data, plotOptions) + + figure() + set(gcf,'Position',[750,400,950,700]) + t = 1:1:size(meanIdMoments,1); + numWindows = numel(jointLabels); + for i=1:numel(jointLabels) + subplot(2, numWindows, i); + hold on + plot(meanNoResidualMoments(:,i), 'r-', linewidth=2) + plot(meanIdMoments(:,i), 'b-', linewidth=2) + + noResidualFillRegion = [(meanNoResidualMoments(:,i)+stdNoResidualMoments(:,i)); + flipud(meanNoResidualMoments(:,i)-stdNoResidualMoments(:,i))]; + idFillRegion = [(meanIdMoments(:,i)+stdIdMoments(:,i)); + flipud(meanIdMoments(:,i)-stdIdMoments(:,i))]; + fill([t, fliplr(t)]', noResidualFillRegion, 'r', FaceAlpha=0.2, ... + EdgeColor='none', HandleVisibility='off') + fill([t, fliplr(t)]', idFillRegion, 'r', FaceAlpha=0.2, ... + EdgeColor='none', HandleVisibility='off') + hold off + axis([0 size(meanIdMoments, 1) minMoment, maxMoment]) + if i == 1 + legend("Mean Moment No Residual", "Mean Inverse Dynamics Moment") + ylabel("Joint Moment [Nm]") + end + subplot(2, numWindows, i+3) + hold on + plot(meanWithResidualMoments(:,i), 'r-', linewidth=2) + plot(meanIdMoments(:,i), 'b-', linewidth=2) + + withResidualFillRegion = [(meanWithResidualMoments(:,i)+stdWithResidualMoments(:,i)); + flipud(meanWithResidualMoments(:,i)-stdWithResidualMoments(:,i))]; + idFillRegion = [(meanIdMoments(:,i)+stdIdMoments(:,i)); + flipud(meanIdMoments(:,i)-stdIdMoments(:,i))]; + fill([t, fliplr(t)]', withResidualFillRegion, 'r', FaceAlpha=0.2, ... + EdgeColor='none', HandleVisibility='off') + fill([t, fliplr(t)]', idFillRegion, 'r', FaceAlpha=0.2, ... + EdgeColor='none', HandleVisibility='off') + hold off + if i == 1 + legend("Mean Moment With Residual", "Mean Inverse Dynamics Moment") + ylabel("Joint Moment [Nm]") + end + xlabel("Time Point") + axis([0 size(meanIdMoments, 1) minMoment, maxMoment]) + end end diff --git a/src/MuscleTendonPersonalization/Analysis/plotMtpData.m b/src/MuscleTendonPersonalization/Analysis/plotMtpData.m new file mode 100644 index 000000000..c2f88a47f --- /dev/null +++ b/src/MuscleTendonPersonalization/Analysis/plotMtpData.m @@ -0,0 +1,32 @@ +function plotMtpData(data, options) + figure() + set(gcf,'Position',[750,400,950,700]) + t = 1:1:size(data.mean{1},1); + numWindows = ceil(sqrt(numel(data.labels))); + for i = 1 : numel(data.labels) + subplot(numWindows, numWindows, i); + hold on + for j = 1 : numel(data.mean) + mean = data.mean{j}; + std = data.std{j}; + + plot(mean(:,i), options.colors(j), lineWidth=2) + fillRegion = [(mean(:,i) + std(:,i)); flipud(mean(:,i) - std(:,i))]; + fill([t, fliplr(t)]', fillRegion, options.colors(j), FaceAlpha=0.2, ... + EdgeColor='none', HandleVisibility='off') + end + hold off + title(data.labels(i), FontSize=10, interpreter='latex') + axis(options.axisLimits) + if mod(i,3) == 1 + ylabel("Magnitude") + end + if i>numel(data.labels)-numWindows + xlabel("Time Points") + end + % if i == 1 && isfield(options, legend) + % legend(options.legend) + % end + end +end + diff --git a/src/MuscleTendonPersonalization/Analysis/plotMuscleExcitationsAndActivations.m b/src/MuscleTendonPersonalization/Analysis/plotMuscleExcitationsAndActivations.m index 1d4428d69..206706f33 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotMuscleExcitationsAndActivations.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMuscleExcitationsAndActivations.m @@ -13,17 +13,30 @@ function plotMuscleExcitationsAndActivations(resultsDirectory) meanActivationsSynx = mean(activationsSynx, 3); stdActivationsSynx = std(activationsSynx,[], 3); -numWindows = ceil(sqrt(numel(muscleNames))); +% data.mean = {meanExcitations, meanExcitationsSynx, meanActivations, meanActivationsSynx}; +% data.std = {stdExcitations, stdExcitationsSynx, stdActivations, stdActivationsSynx}; +% data.labels = muscleNames; +% plotOptions.colors = ["b-", "b--", "r-", "r--"]; +% plotOptions.legend = ["Excitation(without residual)", ... +% "Excitation(with residual)", ... +% "Activation(without residual)", ... +% "Activation(with residual)"]; +% plotOptions.axisLimits = [1 size(meanExcitations, 1) 0 1]; +% plotOptions.xlabel = "Time Points"; +% plotOptions.ylabel = "Magnitude"; +% +% plotMtpData(data, plotOptions); figure() - +set(gcf,'Position',[750,400,950,700]) t = 1:1:size(meanExcitations,1); +numWindows = ceil(sqrt(numel(muscleNames))); for i = 1:numel(muscleNames) - subplot(numWindows, numWindows, i) + subplot(numWindows, numWindows, i); hold on plot(meanExcitations(:,i), 'b-', linewidth=2) - plot(meanActivations(:,i), 'r-', linewidth=2) plot(meanExcitationsSynx(:,i), 'b--', linewidth=2) + plot(meanActivations(:,i), 'r-', linewidth=2) plot(meanActivationsSynx(:,i), 'r--', linewidth=2) excitationFillRegion = [(meanExcitations(:,i)+stdExcitations(:,i)); ... @@ -48,6 +61,18 @@ function plotMuscleExcitationsAndActivations(resultsDirectory) axis([1 size(meanExcitations, 1) 0 1]) title(muscleNames(i), FontSize=10, interpreter='latex'); - % set(gca, 'FontSize', 10) + if i == 1 + legend ('Excitation(without residual)', ... + 'Excitation(with residual)', ... + 'Activation(without residual)', ... + 'Activation(with residual)'); + end + if mod(i,3) == 1 + ylabel("Magnitude") + end + if i>numel(muscleNames)-numWindows + xlabel("Time Points") + end end + end \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/Analysis/plotNormalizedFiberLengths.m b/src/MuscleTendonPersonalization/Analysis/plotNormalizedFiberLengths.m index 8231a1c79..b51d3cb18 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotNormalizedFiberLengths.m +++ b/src/MuscleTendonPersonalization/Analysis/plotNormalizedFiberLengths.m @@ -1,7 +1,20 @@ -function [outputArg1,outputArg2] = plotNormalizedFiberLengths(inputArg1,inputArg2) -%UNTITLED6 Summary of this function goes here -% Detailed explanation goes here -outputArg1 = inputArg1; -outputArg2 = inputArg2; +function plotNormalizedFiberLengths(resultsDirectory) + [muscleNames, normalizedFiberLengths] = extractSavedData( ... + resultsDirectory, "normalizedFiberLengths"); + + meanFiberLengths = mean(normalizedFiberLengths, 3); + stdFiberLengths = std(normalizedFiberLengths, [], 3); + + figure() + set(gcf,'Position',[750,400,950,700]) + t = 1:1:size(meanExcitations,1); + numWindows = ceil(sqrt(numel(muscleNames))); + passiveLower = ones(size(t))*0.7; + passiveUpper = ones(size(t)); + + for i=1:numel(muscleNames) + + end + end diff --git a/src/MuscleTendonPersonalization/Analysis/plotPassiveForceCurves.m b/src/MuscleTendonPersonalization/Analysis/plotPassiveForceCurves.m index d9364ec48..341cee755 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotPassiveForceCurves.m +++ b/src/MuscleTendonPersonalization/Analysis/plotPassiveForceCurves.m @@ -1,7 +1,41 @@ -function [outputArg1,outputArg2] = plotPassiveForceCurves(inputArg1,inputArg2) -%UNTITLED2 Summary of this function goes here -% Detailed explanation goes here -outputArg1 = inputArg1; -outputArg2 = inputArg2; +function plotPassiveForceCurves(resultsDirectory) +[muscleNames, passiveForce] = extractSavedData(resultsDirectory, "passiveForcesExperimental"); + +meanPassiveForce = mean(passiveForce, 3); +stdPassiveForce = std(passiveForce, [], 3); +maxForce = max(meanPassiveForce, [], 'all'); +numWindows = ceil(sqrt(numel(muscleNames))); + +% data.mean = {meanPassiveForce}; +% data.std = {stdPassiveForce}; +% data.labels = muscleNames; +% plotOptions.colors = ["b-"]; +% plotOptions.axisLimits = [1 size(meanPassiveForce, 1), 0, maxForce]; +% plotOptions.xlabel = "Time Points"; +% plotOptions.ylabel = "Force [N]"; +% +% plotMtpData(data, plotOptions); + +figure() +set(gcf,'Position',[750,400,950,700]) +t = 1:1:size(meanPassiveForce,1); +for i = 1:numel(muscleNames) + subplot(numWindows, numWindows, i) + hold on + plot(meanPassiveForce(:,i), 'b-', linewidth=2) + + fillRegion = [(meanPassiveForce(:,i)+stdPassiveForce(:,i)); + flipud((meanPassiveForce(:,i)-stdPassiveForce(:,i)))]; + fill([t, fliplr(t)]', fillRegion, 'b', FaceAlpha=0.2, ... + EdgeColor='none', HandleVisibility='off') + hold off + axis([1 size(meanPassiveForce, 1) 0 maxForce]) + title(muscleNames(i), FontSize=10, interpreter='latex'); + if mod(i,3) == 1 + ylabel("Magnitude") + end + if i>numel(muscleNames)-numWindows + xlabel("Time Points") + end end diff --git a/src/MuscleTendonPersonalization/Analysis/plotPassiveMomentCurves.m b/src/MuscleTendonPersonalization/Analysis/plotPassiveMomentCurves.m index 1b5803d08..9992d311a 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotPassiveMomentCurves.m +++ b/src/MuscleTendonPersonalization/Analysis/plotPassiveMomentCurves.m @@ -1,7 +1,52 @@ -function [outputArg1,outputArg2] = plotPassiveMomentCurves(inputArg1,inputArg2) -%UNTITLED3 Summary of this function goes here -% Detailed explanation goes here -outputArg1 = inputArg1; -outputArg2 = inputArg2; +function plotPassiveMomentCurves(resultsDirectory) +[momentNames, passiveMomentsExperimental] = extractSavedData(resultsDirectory, "passiveJointMomentsExperimental"); +[~, passiveMomentsModel] = extractSavedData(resultsDirectory, "passiveJointMomentsModeled"); + +meanPassiveMomentsExperimental = mean(passiveMomentsExperimental, 3); +stdPassiveMomentsExperimental = std(passiveMomentsExperimental, [], 3); +meanPassiveMomentsModel = mean(passiveMomentsModel, 3); +stdPassiveMomentsModel = std(passiveMomentsModel, [], 3); +maxMoment = max([max(meanPassiveMomentsExperimental, [], 'all'), ... + max(meanPassiveMomentsModel, [], 'all')]); +minMoment = min([min(meanPassiveMomentsExperimental, [], 'all'), ... + min(meanPassiveMomentsModel, [], 'all')]); + +% data.mean = {meanPassiveMomentsExperimental, meanPassiveMomentsModel}; +% data.std = {stdPassiveMomentsExperimental, stdPassiveMomentsModel}; +% data.labels = momentNames; +% +% plotOptions.colors = ["b-", "r-"]; +% plotOptions.legend = ["Experimental", "Model"]; +% plotOptions.axisLimits = [1 size(meanPassiveMomentsModel, 1) minMoment maxMoment]; +% plotOptions.xlabel = "Time Points"; +% plotOptions.ylabel = "Moment [Nm]"; +% +% plotMtpData(data, plotOptions); + +numWindows = ceil(sqrt(numel(momentNames))); +t = 1:1:size(meanPassiveMomentsModel,1); +figure() +set(gcf,'Position',[750,400,950,700]) + +for i = 1:numel(momentNames) + subplot(numWindows, numWindows, i) + hold on + + plot(meanPassiveMomentsExperimental(:,i), 'b-', linewidth=2) + plot(meanPassiveMomentsModel(:,i), 'r-', linewidth=2) + + fillRegionExperimental = [(meanPassiveMomentsExperimental(:,i)+stdPassiveMomentsExperimental(:,i)); + flipud((meanPassiveMomentsExperimental(:,i)-stdPassiveMomentsExperimental(:,i)))]; + fill([t, fliplr(t)]', fillRegionExperimental, 'b', FaceAlpha=0.2, ... + EdgeColor='none', HandleVisibility='off') + fillRegionModel = [(meanPassiveMomentsModel(:,i)+stdPassiveMomentsModel(:,i)); + flipud((meanPassiveMomentsModel(:,i)-stdPassiveMomentsModel(:,i)))]; + fill([t, fliplr(t)]', fillRegionModel, 'b', FaceAlpha=0.2, ... + EdgeColor='none', HandleVisibility='off') + hold off + + axis([1 size(meanPassiveMomentsModel, 1) minMoment maxMoment]) +end + end diff --git a/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m b/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m index f801ce483..6a079e149 100644 --- a/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m +++ b/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m @@ -45,7 +45,7 @@ function MuscleTendonPersonalizationTool(settingsFileName) optimizedParams = MuscleTendonPersonalization(inputs, params); if params.performMuscleTendonLengthInitialization - % reportMuscleTendonPersonalizationResults(optimizedParams, inputs, precalInputs); + reportMuscleTendonPersonalizationResults(optimizedParams, inputs, precalInputs); saveMuscleTendonOptimizationParams(".\mtpResults\Analysis", ... optimizedParams, inputs, precalInputs) else diff --git a/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResults2.m b/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResults2.m index 1924adcad..224d13eaf 100644 --- a/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResults2.m +++ b/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResults2.m @@ -33,15 +33,15 @@ function reportMuscleTendonPersonalizationResults2(resultsDirectory) % Read from STO files and call individual functions plotMuscleExcitationsAndActivations(resultsDirectory); % ... - % plotPassiveForceCurves(); + plotPassiveForceCurves(resultsDirectory); % % ... - % plotPassiveMomentCurves(); + plotPassiveMomentCurves(resultsDirectory); % % ... - % plotJointMoments(); + plotJointMoments(resultsDirectory); % % ... - % plotHillTypeMuscleParams(); + % plotHillTypeMuscleParams(resultsDirectory); % % ... - % plotNormalizedFiberLengths(); + plotNormalizedFiberLengths(resultsDirectory); end diff --git a/src/MuscleTendonPersonalization/saveMuscleTendonOptimizationParams.m b/src/MuscleTendonPersonalization/saveMuscleTendonOptimizationParams.m index 6678d057f..b42cee487 100644 --- a/src/MuscleTendonPersonalization/saveMuscleTendonOptimizationParams.m +++ b/src/MuscleTendonPersonalization/saveMuscleTendonOptimizationParams.m @@ -167,6 +167,7 @@ function saveActivationAndExcitationData(mtpInputs, results, ... saveAnalysisData(mtpInputs.muscleNames, mtpInputs.prefixes, ... resultsSynx.muscleActivations, strcat(resultsDirectory, ... "\muscleActivationsSynx"), "_muscleActivationsSynx.sto") + end function saveJointMomentData(mtpInputs, results, resultsSynx, ... From 67a1d90c9d4d79840649cf9b73d1346ea67b0a0c Mon Sep 17 00:00:00 2001 From: RobSalati Date: Fri, 1 Dec 2023 00:33:13 -0600 Subject: [PATCH 226/365] Fiber length plots Wrote function to create plots for normalized fiber length from a .sto file --- .../Analysis/plotHillTypeMuscleParams.m | 10 ++++---- .../Analysis/plotNormalizedFiberLengths.m | 23 ++++++++++++++++++- .../MuscleTendonPersonalizationTool.m | 2 +- ...eportMuscleTendonPersonalizationResults2.m | 10 +------- .../saveMuscleTendonOptimizationParams.m | 2 +- 5 files changed, 30 insertions(+), 17 deletions(-) diff --git a/src/MuscleTendonPersonalization/Analysis/plotHillTypeMuscleParams.m b/src/MuscleTendonPersonalization/Analysis/plotHillTypeMuscleParams.m index aa1755229..0e3f1e8a1 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotHillTypeMuscleParams.m +++ b/src/MuscleTendonPersonalization/Analysis/plotHillTypeMuscleParams.m @@ -1,7 +1,7 @@ -function [outputArg1,outputArg2] = plotHillTypeMuscleParams(inputArg1,inputArg2) -%UNTITLED5 Summary of this function goes here -% Detailed explanation goes here -outputArg1 = inputArg1; -outputArg2 = inputArg2; +function plotHillTypeMuscleParams(resultsDirectory) + [paramLabels, params] = extractSavedData(resultsDirectory, "muscleModelParameters"); + + + end diff --git a/src/MuscleTendonPersonalization/Analysis/plotNormalizedFiberLengths.m b/src/MuscleTendonPersonalization/Analysis/plotNormalizedFiberLengths.m index b51d3cb18..8246414fc 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotNormalizedFiberLengths.m +++ b/src/MuscleTendonPersonalization/Analysis/plotNormalizedFiberLengths.m @@ -7,13 +7,34 @@ function plotNormalizedFiberLengths(resultsDirectory) figure() set(gcf,'Position',[750,400,950,700]) - t = 1:1:size(meanExcitations,1); + t = 1:1:size(meanFiberLengths,1); numWindows = ceil(sqrt(numel(muscleNames))); passiveLower = ones(size(t))*0.7; passiveUpper = ones(size(t)); for i=1:numel(muscleNames) + subplot(numWindows, numWindows, i); + hold on + plot(meanFiberLengths(:,i), 'b-', LineWidth=2); + + fillRegion = [(meanFiberLengths(:,i)+stdFiberLengths(:,i)); + flipud(meanFiberLengths(:,i)-stdFiberLengths(:,i))]; + fill([t, fliplr(t)]', fillRegion, 'b', FaceAlpha=0.2, ... + EdgeColor='none', HandleVisibility='off') + + plot(t, passiveUpper, 'r--', LineWidth=2); + plot(t, passiveLower, 'r--', LineWidth=2); + hold off + + axis([1 size(meanFiberLengths, 1) 0 1.5]) + title(muscleNames(i), FontSize=10, interpreter='latex'); + if mod(i,3) == 1 + ylabel("Normalized Fiber Length") + end + if i>numel(muscleNames)-numWindows + xlabel("Time Points") + end end end diff --git a/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m b/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m index 6a079e149..1d443314f 100644 --- a/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m +++ b/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m @@ -45,7 +45,7 @@ function MuscleTendonPersonalizationTool(settingsFileName) optimizedParams = MuscleTendonPersonalization(inputs, params); if params.performMuscleTendonLengthInitialization - reportMuscleTendonPersonalizationResults(optimizedParams, inputs, precalInputs); +% reportMuscleTendonPersonalizationResults(optimizedParams, inputs, precalInputs); saveMuscleTendonOptimizationParams(".\mtpResults\Analysis", ... optimizedParams, inputs, precalInputs) else diff --git a/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResults2.m b/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResults2.m index 224d13eaf..298ab6d0e 100644 --- a/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResults2.m +++ b/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResults2.m @@ -29,19 +29,11 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % function reportMuscleTendonPersonalizationResults2(resultsDirectory) - - % Read from STO files and call individual functions plotMuscleExcitationsAndActivations(resultsDirectory); - % ... plotPassiveForceCurves(resultsDirectory); - % % ... plotPassiveMomentCurves(resultsDirectory); - % % ... plotJointMoments(resultsDirectory); - % % ... - % plotHillTypeMuscleParams(resultsDirectory); - % % ... + plotHillTypeMuscleParams(resultsDirectory); plotNormalizedFiberLengths(resultsDirectory); - end diff --git a/src/MuscleTendonPersonalization/saveMuscleTendonOptimizationParams.m b/src/MuscleTendonPersonalization/saveMuscleTendonOptimizationParams.m index b42cee487..b86d86297 100644 --- a/src/MuscleTendonPersonalization/saveMuscleTendonOptimizationParams.m +++ b/src/MuscleTendonPersonalization/saveMuscleTendonOptimizationParams.m @@ -53,7 +53,7 @@ function saveMuscleTendonOptimizationParams(resultsDirectory, optimizedParams, . resultsDirectory); saveMuscleModelParameters(finalValues, fullfile(resultsDirectory, ... - "muscleModelParemeters")); + "muscleModelParameters")); end function [finalValues, results, resultsSynx, resultsSynxNoResiduals] = ... From 39ddd8c8a67a1e7c3d4722c500b2b3c062c4b84b Mon Sep 17 00:00:00 2001 From: RobSalati Date: Fri, 1 Dec 2023 22:12:30 -0600 Subject: [PATCH 227/365] All plots created Created and formatted all plots. Just need to integrate with pipeline. --- .../Analysis/plotHillTypeMuscleParams.m | 22 +++++++++++++++++-- .../Analysis/plotJointMoments.m | 15 ++++++++----- .../plotMuscleExcitationsAndActivations.m | 11 +++++----- .../Analysis/plotNormalizedFiberLengths.m | 16 +++++++------- .../Analysis/plotPassiveForceCurves.m | 10 +++++---- .../Analysis/plotPassiveMomentCurves.m | 16 ++++++++++---- .../MuscleTendonPersonalizationTool.m | 1 - ...eportMuscleTendonPersonalizationResults2.m | 2 +- .../saveMuscleTendonOptimizationParams.m | 13 ++++++----- 9 files changed, 70 insertions(+), 36 deletions(-) diff --git a/src/MuscleTendonPersonalization/Analysis/plotHillTypeMuscleParams.m b/src/MuscleTendonPersonalization/Analysis/plotHillTypeMuscleParams.m index 0e3f1e8a1..1a6188a8f 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotHillTypeMuscleParams.m +++ b/src/MuscleTendonPersonalization/Analysis/plotHillTypeMuscleParams.m @@ -1,6 +1,24 @@ function plotHillTypeMuscleParams(resultsDirectory) - [paramLabels, params] = extractSavedData(resultsDirectory, "muscleModelParameters"); - + [muscleNames, params] = extractSavedData(resultsDirectory, "muscleModelParameters"); + muscleNames = strrep(muscleNames, '_', ' '); + figure(Name = "Muscle Model Parameters") + sgtitle("Muscle Model Parameters", Fontsize=14) + set(gcf,'Position',[600,400,1200,800]) + paramLabels = ["Activation Time Constant", "Activation Nonlinearity", ... + "Electromechanical Time Delay", "EMG Scaling Factor", ... + "Optimal Fiber Length Scaling Factor", "Tendon Slack Length Scaling Factor"]; + for i = 1 : numel(paramLabels) + subplot(1, 6, i) + barh(params(i,:)) + + title(textwrap(paramLabels(i), 20), FontSize=12) + if i == 1 + set(gca, yticklabels = muscleNames, fontsize=11); + else + set(gca, yticklabels = [], fontsize=11); + end + + end end diff --git a/src/MuscleTendonPersonalization/Analysis/plotJointMoments.m b/src/MuscleTendonPersonalization/Analysis/plotJointMoments.m index 3188078ac..7920009f8 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotJointMoments.m +++ b/src/MuscleTendonPersonalization/Analysis/plotJointMoments.m @@ -2,7 +2,7 @@ function plotJointMoments(resultsDirectory) [jointLabels, idMoments] = extractSavedData(resultsDirectory, "inverseDynamicsJointMoments"); [~, noResidualMoments] = extractSavedData(resultsDirectory, "modelJointMomentsNoSynx"); [~, withResidualMoments] = extractSavedData(resultsDirectory, "modelJointMomentsSynx"); - + jointLabels = strrep(jointLabels, '_', ' '); meanIdMoments = mean(idMoments, 3); stdIdMoments = std(idMoments, [], 3); meanNoResidualMoments = mean(noResidualMoments, 3); @@ -29,8 +29,9 @@ function plotJointMoments(resultsDirectory) % % plotMtpData(data, plotOptions) - figure() - set(gcf,'Position',[750,400,950,700]) + figure(Name = "Joint Moments") + sgtitle("Joint Moments", Fontsize=14) + set(gcf, Position=[750,400,1050,800]) t = 1:1:size(meanIdMoments,1); numWindows = numel(jointLabels); for i=1:numel(jointLabels) @@ -48,6 +49,7 @@ function plotJointMoments(resultsDirectory) fill([t, fliplr(t)]', idFillRegion, 'r', FaceAlpha=0.2, ... EdgeColor='none', HandleVisibility='off') hold off + title(jointLabels(i), fontsize=12) axis([0 size(meanIdMoments, 1) minMoment, maxMoment]) if i == 1 legend("Mean Moment No Residual", "Mean Inverse Dynamics Moment") @@ -67,12 +69,15 @@ function plotJointMoments(resultsDirectory) fill([t, fliplr(t)]', idFillRegion, 'r', FaceAlpha=0.2, ... EdgeColor='none', HandleVisibility='off') hold off + set(gca, fontsize=11) + title(jointLabels(i), FontSize=12) + xlabel("Time Point") + axis([0 size(meanIdMoments, 1) minMoment, maxMoment]) if i == 1 legend("Mean Moment With Residual", "Mean Inverse Dynamics Moment") ylabel("Joint Moment [Nm]") end - xlabel("Time Point") - axis([0 size(meanIdMoments, 1) minMoment, maxMoment]) + end end diff --git a/src/MuscleTendonPersonalization/Analysis/plotMuscleExcitationsAndActivations.m b/src/MuscleTendonPersonalization/Analysis/plotMuscleExcitationsAndActivations.m index 206706f33..63ff44fb4 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotMuscleExcitationsAndActivations.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMuscleExcitationsAndActivations.m @@ -3,7 +3,7 @@ function plotMuscleExcitationsAndActivations(resultsDirectory) [~, activations] = extractSavedData(resultsDirectory, "muscleActivations"); [~, excitationsSynx] = extractSavedData(resultsDirectory, "muscleExcitationsSynx"); [~, activationsSynx] = extractSavedData(resultsDirectory, "muscleActivationsSynx"); - +muscleNames = strrep(muscleNames, '_', ' '); meanExcitations = mean(excitations, 3); stdExcitations = std(excitations,[], 3); meanActivations = mean(activations, 3); @@ -27,8 +27,9 @@ function plotMuscleExcitationsAndActivations(resultsDirectory) % % plotMtpData(data, plotOptions); -figure() -set(gcf,'Position',[750,400,950,700]) +figure(Name = "Muscle Excitations and Activations") +sgtitle("Muscle Excitations and Activations", Fontsize=14) +set(gcf, Position=[750,400,1050,800]) t = 1:1:size(meanExcitations,1); numWindows = ceil(sqrt(numel(muscleNames))); for i = 1:numel(muscleNames) @@ -58,9 +59,9 @@ function plotMuscleExcitationsAndActivations(resultsDirectory) flipud((meanActivationsSynx(:,i)-stdActivationsSynx(:,i)))]; fill([t, fliplr(t)]', activationSynxFillRegion, 'b', FaceAlpha=0.2, ... EdgeColor='none', HandleVisibility='off') - + set(gca, fontsize=11) axis([1 size(meanExcitations, 1) 0 1]) - title(muscleNames(i), FontSize=10, interpreter='latex'); + title(muscleNames(i), FontSize=12); if i == 1 legend ('Excitation(without residual)', ... 'Excitation(with residual)', ... diff --git a/src/MuscleTendonPersonalization/Analysis/plotNormalizedFiberLengths.m b/src/MuscleTendonPersonalization/Analysis/plotNormalizedFiberLengths.m index 8246414fc..e864ce1b4 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotNormalizedFiberLengths.m +++ b/src/MuscleTendonPersonalization/Analysis/plotNormalizedFiberLengths.m @@ -1,12 +1,13 @@ function plotNormalizedFiberLengths(resultsDirectory) [muscleNames, normalizedFiberLengths] = extractSavedData( ... resultsDirectory, "normalizedFiberLengths"); - + muscleNames = strrep(muscleNames, '_', ' '); meanFiberLengths = mean(normalizedFiberLengths, 3); stdFiberLengths = std(normalizedFiberLengths, [], 3); - figure() - set(gcf,'Position',[750,400,950,700]) + figure(Name = "Normalized Fiber Lengths") + sgtitle("Normalized Fiber Length", Fontsize=14) + set(gcf, Position=[750,400,1050,800]) t = 1:1:size(meanFiberLengths,1); numWindows = ceil(sqrt(numel(muscleNames))); passiveLower = ones(size(t))*0.7; @@ -25,15 +26,14 @@ function plotNormalizedFiberLengths(resultsDirectory) plot(t, passiveUpper, 'r--', LineWidth=2); plot(t, passiveLower, 'r--', LineWidth=2); hold off - + set(gca, fontsize=11) axis([1 size(meanFiberLengths, 1) 0 1.5]) - - title(muscleNames(i), FontSize=10, interpreter='latex'); + title(muscleNames(i), FontSize=12); if mod(i,3) == 1 - ylabel("Normalized Fiber Length") + ylabel('Normalized Fiber Length', FontSize=12) end if i>numel(muscleNames)-numWindows - xlabel("Time Points") + xlabel("Time Points", FontSize=12) end end diff --git a/src/MuscleTendonPersonalization/Analysis/plotPassiveForceCurves.m b/src/MuscleTendonPersonalization/Analysis/plotPassiveForceCurves.m index 341cee755..5d71f0228 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotPassiveForceCurves.m +++ b/src/MuscleTendonPersonalization/Analysis/plotPassiveForceCurves.m @@ -1,6 +1,6 @@ function plotPassiveForceCurves(resultsDirectory) [muscleNames, passiveForce] = extractSavedData(resultsDirectory, "passiveForcesExperimental"); - +muscleNames = strrep(muscleNames, '_', ' '); meanPassiveForce = mean(passiveForce, 3); stdPassiveForce = std(passiveForce, [], 3); maxForce = max(meanPassiveForce, [], 'all'); @@ -16,8 +16,9 @@ function plotPassiveForceCurves(resultsDirectory) % % plotMtpData(data, plotOptions); -figure() -set(gcf,'Position',[750,400,950,700]) +figure(Name = "Passive Force Curves") +sgtitle("Passive Force Curves", Fontsize=14) +set(gcf, Position=[750,400,1050,800]) t = 1:1:size(meanPassiveForce,1); for i = 1:numel(muscleNames) subplot(numWindows, numWindows, i) @@ -29,8 +30,9 @@ function plotPassiveForceCurves(resultsDirectory) fill([t, fliplr(t)]', fillRegion, 'b', FaceAlpha=0.2, ... EdgeColor='none', HandleVisibility='off') hold off + set(gca, fontsize=11) axis([1 size(meanPassiveForce, 1) 0 maxForce]) - title(muscleNames(i), FontSize=10, interpreter='latex'); + title(muscleNames(i), FontSize=12); if mod(i,3) == 1 ylabel("Magnitude") end diff --git a/src/MuscleTendonPersonalization/Analysis/plotPassiveMomentCurves.m b/src/MuscleTendonPersonalization/Analysis/plotPassiveMomentCurves.m index 9992d311a..7b9bba450 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotPassiveMomentCurves.m +++ b/src/MuscleTendonPersonalization/Analysis/plotPassiveMomentCurves.m @@ -1,7 +1,7 @@ function plotPassiveMomentCurves(resultsDirectory) [momentNames, passiveMomentsExperimental] = extractSavedData(resultsDirectory, "passiveJointMomentsExperimental"); [~, passiveMomentsModel] = extractSavedData(resultsDirectory, "passiveJointMomentsModeled"); - +momentNames = strrep(momentNames, '_', ' '); meanPassiveMomentsExperimental = mean(passiveMomentsExperimental, 3); stdPassiveMomentsExperimental = std(passiveMomentsExperimental, [], 3); meanPassiveMomentsModel = mean(passiveMomentsModel, 3); @@ -25,8 +25,9 @@ function plotPassiveMomentCurves(resultsDirectory) numWindows = ceil(sqrt(numel(momentNames))); t = 1:1:size(meanPassiveMomentsModel,1); -figure() -set(gcf,'Position',[750,400,950,700]) +figure(Name = "Passive Moment Curves") +sgtitle("Passive Moment Curves", Fontsize=14) +set(gcf, Position=[750,400,1050,800]) for i = 1:numel(momentNames) subplot(numWindows, numWindows, i) @@ -44,8 +45,15 @@ function plotPassiveMomentCurves(resultsDirectory) fill([t, fliplr(t)]', fillRegionModel, 'b', FaceAlpha=0.2, ... EdgeColor='none', HandleVisibility='off') hold off - + set(gca, fontsize=11) + title(momentNames(i), FontSize=12) axis([1 size(meanPassiveMomentsModel, 1) minMoment maxMoment]) + if mod(i,4) == 1 + ylabel("Moment [Nm]") + end + if i>numel(momentNames)-numWindows + xlabel("Time Points") + end end end diff --git a/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m b/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m index 1d443314f..46c0e44e3 100644 --- a/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m +++ b/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m @@ -45,7 +45,6 @@ function MuscleTendonPersonalizationTool(settingsFileName) optimizedParams = MuscleTendonPersonalization(inputs, params); if params.performMuscleTendonLengthInitialization -% reportMuscleTendonPersonalizationResults(optimizedParams, inputs, precalInputs); saveMuscleTendonOptimizationParams(".\mtpResults\Analysis", ... optimizedParams, inputs, precalInputs) else diff --git a/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResults2.m b/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResults2.m index 298ab6d0e..6f57fb1b0 100644 --- a/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResults2.m +++ b/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResults2.m @@ -33,7 +33,7 @@ function reportMuscleTendonPersonalizationResults2(resultsDirectory) plotPassiveForceCurves(resultsDirectory); plotPassiveMomentCurves(resultsDirectory); plotJointMoments(resultsDirectory); - plotHillTypeMuscleParams(resultsDirectory); plotNormalizedFiberLengths(resultsDirectory); + plotHillTypeMuscleParams(resultsDirectory); end diff --git a/src/MuscleTendonPersonalization/saveMuscleTendonOptimizationParams.m b/src/MuscleTendonPersonalization/saveMuscleTendonOptimizationParams.m index b86d86297..51f01f9d8 100644 --- a/src/MuscleTendonPersonalization/saveMuscleTendonOptimizationParams.m +++ b/src/MuscleTendonPersonalization/saveMuscleTendonOptimizationParams.m @@ -52,7 +52,7 @@ function saveMuscleTendonOptimizationParams(resultsDirectory, optimizedParams, . saveJointMomentData(mtpInputs, results, resultsSynx, resultsSynxNoResiduals, ... resultsDirectory); -saveMuscleModelParameters(finalValues, fullfile(resultsDirectory, ... +saveMuscleModelParameters(mtpInputs, finalValues, fullfile(resultsDirectory, ... "muscleModelParameters")); end @@ -186,20 +186,21 @@ function saveJointMomentData(mtpInputs, results, resultsSynx, ... "\inverseDynamicsJointMoments"), "_inverseDynamicsJointMoments.sto") end -function saveMuscleModelParameters(finalValues, resultsDirectory) +function saveMuscleModelParameters(mtpInputs, finalValues, resultsDirectory) if ~exist(resultsDirectory, "dir") mkdir(resultsDirectory); end -columnLabels = ["Activation Time Constant", "Activation Nonlinearity", ... - "Electromechanical Time Delay", "EMG Scaling Factor", ... - "Optimal Fiber Length Scaling Factor", "Tendon Slack Length Scaling Factor"]; +columnLabels = mtpInputs.muscleNames; +% columnLabels = ["Activation Time Constant", "Activation Nonlinearity", ... +% "Electromechanical Time Delay", "EMG Scaling Factor", ... +% "Optimal Fiber Length Scaling Factor", "Tendon Slack Length Scaling Factor"]; dataPoints = [finalValues.activationTimeConstants; finalValues.activationNonlinearityConstants; finalValues.electromechanicalDelays; finalValues.emgScaleFactors; finalValues.optimalFiberLengthScaleFactors; - finalValues.tendonSlackLengthScaleFactors]'; + finalValues.tendonSlackLengthScaleFactors]; writeToSto(columnLabels, 1:1:size(dataPoints,1), dataPoints, ... fullfile(resultsDirectory, "muscleModelParameters.sto")); From dceefda2ed045f360e9ee0c466a00e432d7b10f8 Mon Sep 17 00:00:00 2001 From: RobSalati Date: Wed, 6 Dec 2023 11:58:49 -0600 Subject: [PATCH 228/365] Moved files around --- ...d.xml => PkiIZsXBNV2p5PmLAZbrlCKNO1wd.xml} | 0 .../PkiIZsXBNV2p5PmLAZbrlCKNO1wp.xml | 2 + .../wlGOvJScYYqnqtARzyW1cByrXncp.xml | 2 - .../MuscleTendonPersonalizationTool.m | 2 +- ...reportMuscleTendonPersonalizationResults.m | 406 +---------------- ...eportMuscleTendonPersonalizationResults2.m | 39 -- ...ortMuscleTendonPersonalizationResultsOld.m | 429 ++++++++++++++++++ 7 files changed, 440 insertions(+), 440 deletions(-) rename resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/{wlGOvJScYYqnqtARzyW1cByrXncd.xml => PkiIZsXBNV2p5PmLAZbrlCKNO1wd.xml} (100%) create mode 100644 resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/PkiIZsXBNV2p5PmLAZbrlCKNO1wp.xml delete mode 100644 resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/wlGOvJScYYqnqtARzyW1cByrXncp.xml delete mode 100644 src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResults2.m create mode 100644 src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResultsOld.m diff --git a/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/wlGOvJScYYqnqtARzyW1cByrXncd.xml b/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/PkiIZsXBNV2p5PmLAZbrlCKNO1wd.xml similarity index 100% rename from resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/wlGOvJScYYqnqtARzyW1cByrXncd.xml rename to resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/PkiIZsXBNV2p5PmLAZbrlCKNO1wd.xml diff --git a/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/PkiIZsXBNV2p5PmLAZbrlCKNO1wp.xml b/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/PkiIZsXBNV2p5PmLAZbrlCKNO1wp.xml new file mode 100644 index 000000000..1d012a5c3 --- /dev/null +++ b/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/PkiIZsXBNV2p5PmLAZbrlCKNO1wp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/wlGOvJScYYqnqtARzyW1cByrXncp.xml b/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/wlGOvJScYYqnqtARzyW1cByrXncp.xml deleted file mode 100644 index f2d1d15ad..000000000 --- a/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/wlGOvJScYYqnqtARzyW1cByrXncp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m b/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m index 46c0e44e3..377032a4d 100644 --- a/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m +++ b/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m @@ -52,7 +52,7 @@ function MuscleTendonPersonalizationTool(settingsFileName) optimizedParams, inputs) end -reportMuscleTendonPersonalizationResults2(".\mtpResults\Analysis"); +reportMuscleTendonPersonalizationResults(".\mtpResults\Analysis"); finalValues = makeMtpValuesAsStruct([], optimizedParams, zeros(1, 7)); if precalInputs.optimizeIsometricMaxForce diff --git a/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResults.m b/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResults.m index 738751875..9c2676ee6 100644 --- a/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResults.m +++ b/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResults.m @@ -15,7 +15,7 @@ % National Institutes of Health (R01 EB030520). % % % % Copyright (c) 2021 Rice University and the Authors % -% Author(s): Di Ao, Marleny Vega % +% Author(s): Robert Salati % % % % Licensed under the Apache License, Version 2.0 (the "License"); % % you may not use this file except in compliance with the License. % @@ -28,402 +28,12 @@ % implied. See the License for the specific language governing % % permissions and limitations under the License. % % ----------------------------------------------------------------------- % - -function reportMuscleTendonPersonalizationResults(optimizedParams, ... - mtpInputs, precalInputs) -if nargin < 3; precalInputs = []; end -[finalValues, results, resultsSynx, resultsSynxNoResiduals] = ... - getValuesToReport(mtpInputs, precalInputs, optimizedParams); -if ~isempty(precalInputs) - plotMuscleTendonLengthInitializationResults(precalInputs, mtpInputs) -end - -printJointMomentMatchingError(resultsSynx.muscleJointMoments, ... - mtpInputs.inverseDynamicsMoments); -makeExcitationAndActivationPlots(results, resultsSynx, mtpInputs, ... - mtpInputs.synergyExtrapolation); -makeModelParameterPlots(finalValues, mtpInputs, ... - mtpInputs.synergyExtrapolation) -makeTaskSpecificMomentMatchingPlots(... - permute(resultsSynxNoResiduals.muscleJointMoments, [3 1 2]), ... - permute(resultsSynx.muscleJointMoments, [3 1 2]), ... - permute(mtpInputs.inverseDynamicsMoments, [3 1 2]), ... - mtpInputs.coordinateNames, mtpInputs.synergyExtrapolation) -makeTaskSpecificNormalizedFiberLengthsPlots( ... - permute(resultsSynx.normalizedFiberLength, [3 1 2]), ... - mtpInputs, mtpInputs.synergyExtrapolation) -end -function [finalValues, results, resultsSynx, resultsSynxNoResiduals] = ... - getValuesToReport(mtpInputs, precalInputs, optimizedParams) - -finalValues = makeMtpValuesAsStruct([], optimizedParams, zeros(1, 7)); -save('finalvalues.mat', 'finalValues') -resultsSynx = calcMtpSynXModeledValues(finalValues, mtpInputs, struct()); -finalValues.synergyWeights(mtpInputs.numberOfExtrapolationWeights + 1 : end) = 0; -resultsSynxNoResiduals = calcMtpSynXModeledValues(finalValues, mtpInputs, struct()); -results = calcMtpModeledValues(finalValues, mtpInputs, struct()); -results.time = mtpInputs.emgTime(:, mtpInputs.numPaddingFrames + 1 : ... - end - mtpInputs.numPaddingFrames); -results.muscleExcitations = results.muscleExcitations(:, :, ... - mtpInputs.numPaddingFrames + 1 : end - mtpInputs.numPaddingFrames); -resultsSynx.muscleExcitations = resultsSynx.muscleExcitations(:, :, ... - mtpInputs.numPaddingFrames + 1 : end - mtpInputs.numPaddingFrames); -if ~isempty(precalInputs) -% finalOptimalFiberLength = ... -% finalValues.optimalFiberLengthScaleFactors .* mtpInputs.optimalFiberLength; -% finalValues.optimalFiberLengthScaleFactors = ... -% finalOptimalFiberLength ./ precalInputs.optimalFiberLength; -% finalTendonSlackLength = ... -% finalValues.tendonSlackLengthScaleFactors .* mtpInputs.tendonSlackLength; -% finalValues.tendonSlackLengthScaleFactors = ... -% finalTendonSlackLength ./ precalInputs.tendonSlackLength; -end -end -function plotMuscleTendonLengthInitializationResults(precalInputs, mtpInputs) - -tempValues.optimalFiberLengthScaleFactors = ... - mtpInputs.optimalFiberLength ./ precalInputs.optimalFiberLength; -mtpInputs.optimalFiberLength -tempValues.tendonSlackLengthScaleFactors = ... - mtpInputs.tendonSlackLength ./ precalInputs.tendonSlackLength; -precalInputs.maxIsometricForce = mtpInputs.maxIsometricForce; -precalInputs.optimizeIsometricMaxForce = 0; -modeledValues = calcMuscleTendonLengthInitializationModeledValues(tempValues, precalInputs); -plotPassiveForceData(permute(modeledValues.passiveForce, [3 1 2]), ... - precalInputs); -if precalInputs.passiveMomentDataExists -plotPassiveMomentData(permute(modeledValues.passiveModelMoments, [3 1 2]), ... - permute(precalInputs.passiveData.inverseDynamicsMoments, [3 1 2]), ... - precalInputs.passivePrefixes) -end -end -function plotPassiveMomentData(modeledValue, experimentalValue, titleName) - -columnsWithAllZeros = all(experimentalValue == 0, 1); -inverseDynamicsMoments = experimentalValue(repmat(~columnsWithAllZeros, ... - size(experimentalValue, 1), 1, 1)); -passiveModelMoments = modeledValue(repmat(~columnsWithAllZeros, ... - size(experimentalValue, 1), 1, 1)); - -inverseDynamicsMoments = ... - reshape(inverseDynamicsMoments, size(modeledValue, 1), []); -passiveModelMoments = ... - reshape(passiveModelMoments, size(modeledValue, 1), []); -figure('name', 'MuscleTendonLengthInitialization Passive Moment Matching'); -nplot = ceil(sqrt(size(passiveModelMoments, 2))); -for i = 1 : size(passiveModelMoments, 2) -subplot(nplot, nplot, i) -plot(passiveModelMoments(:, i), 'r', 'LineWidth', 1.25); hold on; -plot(inverseDynamicsMoments(:, i), 'k', 'LineWidth', 1.75); -if i == 1; ylabel('Passive Moments [Nm]'); -elseif i == size(passiveModelMoments, 2); legend('Model','Experimental') -end -axis([1 size(modeledValue, 1) min([inverseDynamicsMoments; ... - passiveModelMoments], [], 'all') max([inverseDynamicsMoments; ... - passiveModelMoments], [],'all')]) -xlabel('Time Points'); -title(strrep(titleName{i}, '_', ' ')) -end -end -function plotPassiveForceData(modeledValue, experimentalData) - -meanModeledValue = squeeze(mean(modeledValue, 2)); -stdModeledValue = squeeze(std(modeledValue, [], 2)); -figure('name', 'MuscleTendonLengthInitialization Passive Forces'); -nplots = ceil(sqrt(length(experimentalData.muscleNames))); - -t = 1 : size(meanModeledValue, 1); -for i = 1 : size(meanModeledValue, 2) -subplot(nplots, nplots, i); -plot(meanModeledValue(:, i), 'LineWidth', 2); hold on -fill([t'; flipud(t')], ... - [meanModeledValue(:,i) - stdModeledValue(:,i); ... - flipud(meanModeledValue(:, i) + stdModeledValue(:, i))], ... - 'b', 'linestyle', 'None', 'FaceAlpha', 0.5); -axis([1 size(modeledValue, 1) min(modeledValue,[],'all') max(modeledValue,[],'all')]) -title(strrep(experimentalData.muscleNames{i}, '_', ' ')) -if i > length(experimentalData.muscleNames) - nplots; xlabel('Time Points'); -else xticklabels(''); end -if ismember(i, 1 : nplots : length(experimentalData.muscleNames)) - ylabel({'Passive','Force [N]'}); -else yticklabels(''); end -end -end -function printJointMomentMatchingError(muscleJointMoments, inverseDynamicsMoments) - -for i = 1 : size(muscleJointMoments, 2) -jointMomentsRmse(i) = sqrt(sum((muscleJointMoments(:, i, :) - ... - inverseDynamicsMoments(:, i, :)) .^ 2, 'all') / ... - (numel(inverseDynamicsMoments(:, 1, :)) - 1)); -jointMomentsMae(i) = sum(abs(muscleJointMoments(:, i, :) - ... - inverseDynamicsMoments(:, i, :)) / ... - numel(inverseDynamicsMoments(:, 1, :)), 'all'); -end -fprintf(['The root mean sqrt (RMS) errors between model-predicted ' ... - 'and inverse dynamic moments are: \n' ]); -fprintf(['\n ' num2str(jointMomentsRmse) ' \n']); -fprintf(['The mean absolute errors (MAEs) between model-predicted ' ... - 'and inverse dynamic moments are: \n' ]); -fprintf(['\n ' num2str(jointMomentsMae) ' \n']); -end -function makeExcitationAndActivationPlots(results, resultsSynx, ... - experimentalData, synergyParameters) - -muscleLabels = getSynxMuscleNames(experimentalData.muscleNames, ... - synergyParameters.missingEmgChannelGroups); - -for i = 1 : numel(synergyParameters.taskNames) -figure('name', ['Muscle excitations/activations for ', ... - synergyParameters.taskNames{i}]); - -plotMuscleExcitationsAndActivations(... - results.muscleExcitations(synergyParameters.trialIndex{i}, :, :), ... - resultsSynx.muscleExcitations(synergyParameters.trialIndex{i}, :, :), ... - results.muscleActivations(synergyParameters.trialIndex{i}, :, :), ... - resultsSynx.muscleActivations(synergyParameters.trialIndex{i}, :, :), ... - muscleLabels) -end -end -function plotMuscleExcitationsAndActivations(muscleExcitations, ... - muscleExcitationsSynx, muscleActivations, muscleActivationsSynx, ... - muscleLabels) - -meanMuscleExcitation = permute(mean(muscleExcitations, 1), [3 2 1]); -stdMuscleExcitation = permute(std(muscleExcitations, [], 1), [3 2 1]); -meanMuscleExcitationSynx = permute(mean(muscleExcitationsSynx, 1), [3 2 1]); -stdMuscleExcitationSynx = permute(std(muscleExcitationsSynx, [], 1), [3 2 1]); -meanMuscleActivation = permute(mean(muscleActivations, 1), [3 2 1]); -stdMuscleActivation = permute(std(muscleActivations, [], 1), [3 2 1]); -meanMuscleActivationSynx = permute(mean(muscleActivationsSynx, 1), [3 2 1]); -stdMuscleActivationSynx = permute(std(muscleActivationsSynx, [], 1), [3 2 1]); - -t = 1 : size(meanMuscleExcitation, 1); -nplot = ceil(sqrt(numel(muscleLabels))); -for j = 1 : numel(muscleLabels) - subplot(nplot,nplot,j); - plot(meanMuscleExcitation(:, j), 'b-', 'LineWidth', 2); hold on - fill([t'; flipud(t')], ... - [meanMuscleExcitation(:, j) - stdMuscleExcitation(:, j); ... - flipud(meanMuscleExcitation(:, j) + stdMuscleExcitation(:, j))], ... - 'b', 'linestyle', 'None', 'FaceAlpha', 0.5); - hold on - plot(meanMuscleExcitationSynx(:, j), 'b--', 'LineWidth', 2); hold on - fill([t'; flipud(t')], ... - [meanMuscleExcitationSynx(:, j) - stdMuscleExcitationSynx(:, j); ... - flipud(meanMuscleExcitationSynx(:, j) + stdMuscleExcitationSynx(:, j))], ... - 'b', 'linestyle', 'None', 'FaceAlpha', 0.3); - plot(meanMuscleActivation(:, j), 'r-', 'LineWidth', 2); hold on - fill([t'; flipud(t')], ... - [meanMuscleActivation(:, j) - stdMuscleActivation(:, j); ... - flipud(meanMuscleActivation(:, j) + stdMuscleActivation(:, j))], ... - 'r', 'linestyle', 'None', 'FaceAlpha', 0.5); - plot(meanMuscleActivationSynx(:, j), 'r--', 'LineWidth', 2); hold on - fill([t'; flipud(t')], ... - [meanMuscleActivationSynx(:, j) - stdMuscleActivationSynx(:, j); ... - flipud(meanMuscleActivationSynx(:, j) + stdMuscleActivationSynx(:, j))], ... - 'r', 'linestyle', 'None', 'FaceAlpha', 0.3); - axis([1 size(meanMuscleExcitation, 1) 0 1]) - title(muscleLabels{j}); - if j == 1 - legend ('Excitation(without residual)', '', ... - 'Excitation(with residual)', '', ... - 'Activation(without residual)', '', ... - 'Activation(with residual)', ''); - end -end -set(gca, 'FontSize', 12) -end -function makeModelParameterPlots(finalValues, experimentalData, synergyParameters) - -muscleLabels = getSynxMuscleNames(experimentalData.muscleNames, ... - synergyParameters.missingEmgChannelGroups); - -In_width = 0.145; % witdth of each subplot -figure('name', 'Model Parameters', 'units', 'normalized', ... - 'outerposition', [0 0 1 1]); -for subplotElement = 1 : 6 - subplotTight(1, 6, subplotElement, [0.04, 0.001]); - switch subplotElement - case 1 %activationTimeConstant - bar(1 : numel(muscleLabels), finalValues.activationTimeConstants, ... - 'barwidth', 0.8, 'Horizontal', 'on', 'FaceAlpha', 0.6); - hold on; - title('Activation Time Constant (cs)') - pos_in = get(gca, 'Position'); - pos_in(3) = In_width; - pos_in(1) = pos_in(1) + 0.05; - set(gca, 'YTick', 1 : numel(muscleLabels), 'yTickLabel', ... - muscleLabels, 'Fontsize', 11, 'Position', pos_in); - case 2 %activationNonlinearity - bar(finalValues.activationNonlinearityConstants, 'Horizontal', 'on', ... - 'barwidth', 0.8, 'FaceAlpha', 0.6); - title('Activation Nonlinearity') - pos_in(1) = pos_in(1) + pos_in(3) + 0.015; - set(gca, 'YTick', [], 'yTickLabel', [], 'Position', pos_in, ... - 'Fontsize', 11); - case 3 %timeDelay - bar(finalValues.electromechanicalDelays, 'Horizontal', 'on', 'barwidth', 0.8, ... - 'FaceAlpha', 0.6); - title('Electromechanical Time Delay (ds)') - pos_in(1) = pos_in(1) + pos_in(3) + 0.015; - set(gca, 'YTick', [], 'yTickLabel', [],'Position', pos_in, ... - 'Fontsize', 11); - case 4 % emgScalingFactor - bar(finalValues.emgScaleFactors, 'Horizontal', 'on', 'barwidth', 0.8, ... - 'FaceAlpha', 0.6); - title('Emg Scaling Factor') - pos_in(1) = pos_in(1) + pos_in(3) + 0.015; - set(gca, 'YTick', [], 'yTickLabel', [],'Position', pos_in, ... - 'Fontsize', 11); - case 5 %muscleOptimalLength - bar(finalValues.optimalFiberLengthScaleFactors, 'Horizontal', 'on', 'barwidth', ... - 0.8, 'FaceAlpha', 0.6); - title('Optimal Fiber Length Scaling Factor') - pos_in(1) = pos_in(1) + pos_in(3) + 0.015; - set(gca, 'YTick', [], 'yTickLabel', [],'Position', pos_in, ... - 'Fontsize', 11); - case 6 %tendonSlackLength - bar(finalValues.tendonSlackLengthScaleFactors, 'Horizontal', 'on', 'barwidth', ... - 0.8, 'FaceAlpha', 0.6); - title('Tendon Slack Length Scaling Factor') - pos_in(1) = pos_in(1) + pos_in(3) + 0.015; - set(gca, 'YTick', [], 'yTickLabel', [],'Position', pos_in, ... - 'Fontsize', 11); - end -end -end -function vargout=subplotTight(m, n, p, margins, varargin) -%% Default params -isWrapper=false; -if (nargin<4) || isempty(margins) - margins=[0.04, 0.04]; % default margins value- 4% of figure -end -if length(margins)==1 - margins(2)=margins; -end - -%note n and m are switched as Matlab indexing is column-wise, while subplot indexing is row-wise :( -[subplot_col, subplot_row] = ind2sub([n, m], p); - -height = (1 - (m + 1) * margins(1)) / m; % single subplot height -width = (1 - (n + 1) * margins(2)) / n; % single subplot width - -% note subplot suppors vector p inputs- so a merged subplot of higher dimentions will be created -subplotColumns = 1 + max(subplot_col) - min(subplot_col); % number of column elements in merged subplot -subplotRows = 1 + max(subplot_row) - min(subplot_row); % number of row elements in merged subplot - -mergedHeight = subplotRows * (height + margins(1)) - margins(1); % merged subplot height -mergedWidth = subplotColumns * (width + margins(2)) - margins(2); % merged subplot width - -mergedBottom = (m - max(subplot_row)) * (height + margins(1)) + margins(1); % merged subplot bottom position -mergedLeft = min(subplot_col) * (width + margins(2)) - width; % merged subplot left position -pos = [mergedLeft, mergedBottom, mergedWidth, mergedHeight]; - - -if isWrapper - h=subplot(m, n, p, varargin{:}, 'Units', 'Normalized', 'Position', pos); -else - h=axes('Position', pos, varargin{:}); -end - -if nargout == 1;vargout=h; end +function reportMuscleTendonPersonalizationResults(resultsDirectory) + plotMuscleExcitationsAndActivations(resultsDirectory); + plotPassiveForceCurves(resultsDirectory); + plotPassiveMomentCurves(resultsDirectory); + plotJointMoments(resultsDirectory); + plotNormalizedFiberLengths(resultsDirectory); + plotHillTypeMuscleParams(resultsDirectory); end -function makeTaskSpecificMomentMatchingPlots(jointMoments, jointMomentsSynx, ... - inverseDynamicsMoments, coordinates, synergyParameters) -t = 1 : size(jointMoments,1); -for i = 1 : numel(synergyParameters.taskNames) -figure('name', ['Joint moments for ', synergyParameters.taskNames{i}]); -plotJointMoments(jointMoments(:, synergyParameters.trialIndex{i}, :), ... - inverseDynamicsMoments(:, synergyParameters.trialIndex{i}, :), ... - synergyParameters.taskNames{i}, coordinates, 0,... - {'','Predicted (w/o residual excitations)','','Inverse dynamics'}); -plotJointMoments(jointMomentsSynx(:, synergyParameters.trialIndex{i}, :), ... - inverseDynamicsMoments(:, synergyParameters.trialIndex{i}, :), ... - synergyParameters.taskNames{i}, coordinates, numel(coordinates), ... - {'','Predicted (with residual excitations)','','Inverse dynamics'}); -end -end -function plotJointMoments(jointMoments, inverseDynamicsMoments, taskName, ... - coordinates, subplotIndex, legendName) - -meanJointMoments = squeeze(mean(jointMoments, 2)); -stdJointMoments = squeeze(std(jointMoments, [], 2)); -meaninverseDynamicsMoments = squeeze(mean(inverseDynamicsMoments, 2)); -stdinverseDynamicsMoments = squeeze(std(inverseDynamicsMoments, [], 2)); -t = 1 : size(jointMoments,1); -for j = 1:size(jointMoments,3) -subplot(2, size(jointMoments, 3), j + subplotIndex); -plot(-meanJointMoments(:, j), 'r', 'LineWidth', 2); hold on -fill([t'; flipud(t')], ... - [-meanJointMoments(:,j) - stdJointMoments(:,j); ... - flipud(-meanJointMoments(:, j) + stdJointMoments(:, j))], ... - 'r', 'linestyle', 'None', 'FaceAlpha', 0.5); -subplot(2, size(jointMoments, 3), j + subplotIndex); -plot(-meaninverseDynamicsMoments(:, j), 'k', 'LineWidth', 2); -fill([t'; flipud(t')], ... - [-meaninverseDynamicsMoments(:, j) - stdinverseDynamicsMoments(:, j); ... - flipud(-meaninverseDynamicsMoments(:, j) + stdinverseDynamicsMoments(:, j))], ... - 'k', 'linestyle', 'None', 'FaceAlpha', 0.5); -title({taskName, [strrep(coordinates{j}, '_', ' ') ],'Moment (N-m)'}); -xlabel('Time Frames'); axis([0 100 -80 120]); -if j == 1; ylabel('Moment (N-m)'); -elseif j == numel(coordinates) - lgd = legend(legendName); - lgd.Orientation ='horizontal'; - lgd.NumColumns = 2; -end -set(gca, 'FontSize', 12) -end -end -function makeTaskSpecificNormalizedFiberLengthsPlots(... - normalizedFiberLengths, experimentalData, synergyParameters) - -muscleLabels = getSynxMuscleNames(experimentalData.muscleNames, ... - synergyParameters.missingEmgChannelGroups); - -for i = 1 : numel(synergyParameters.taskNames) -figure('name', ['Normalized Fiber Lengths for ', ... - synergyParameters.taskNames{i}]); - -plotNormalizedFiberLength(normalizedFiberLengths(:, ... - synergyParameters.trialIndex{i}, :), muscleLabels) -end -end -function plotNormalizedFiberLength(normalizedFiberLengths, ... - muscleLabels) - -nplots = ceil(sqrt(numel(muscleLabels))); -meanNormalizedFiberLengths = squeeze(mean(normalizedFiberLengths, 2)); -stdNormalizedFiberLengths = squeeze(std(normalizedFiberLengths, [], 2)); -t = 1 : size(normalizedFiberLengths,1); -for i = 1 : numel(muscleLabels) - subplot(nplots,nplots,i) - hold on - plot(meanNormalizedFiberLengths(:, i), 'b', 'LineWidth', 2); hold on - fill([t'; flipud(t')], ... - [meanNormalizedFiberLengths(:, i) - stdNormalizedFiberLengths(:, i); ... - flipud(meanNormalizedFiberLengths(:, i) + stdNormalizedFiberLengths(:, i))], ... - 'b', 'linestyle', 'None', 'FaceAlpha', 0.3); - plot(0.7*ones(1, size(normalizedFiberLengths, 1)),'r--','LineWidth',2) - plot(1*ones(1, size(normalizedFiberLengths, 1)),'r--','LineWidth',2) - title(muscleLabels{i}) - axis([1 size(normalizedFiberLengths, 1) 0 1.5]) - if i > numel(muscleLabels) - nplots; xlabel('Time Points'); - else xticklabels(''); end - if ismember(i, 1 : nplots : numel(muscleLabels)) - ylabel({'Normalized','Fiber Length'}); - else yticklabels(''); end -end -end -function muscleLabels = getSynxMuscleNames(muscleNames, ... - missingEmgChannelGroups) - -for i = 1 : numel(muscleNames) - if ismember(i, [missingEmgChannelGroups{:}]) - muscleLabels{i} = [muscleNames{i} '(*)']; - else - muscleLabels{i} = muscleNames{i}; - end -end -end \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResults2.m b/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResults2.m deleted file mode 100644 index 6f57fb1b0..000000000 --- a/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResults2.m +++ /dev/null @@ -1,39 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function writes to console or file the results of the optimization -% for easy evaluation by the user. It may require the input model to -% produce relative results or other information for output. -% -% (struct, struct) -> (None) -% Prints and plots results from muscle tendon personalization - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Robert Salati % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % -function reportMuscleTendonPersonalizationResults2(resultsDirectory) - plotMuscleExcitationsAndActivations(resultsDirectory); - plotPassiveForceCurves(resultsDirectory); - plotPassiveMomentCurves(resultsDirectory); - plotJointMoments(resultsDirectory); - plotNormalizedFiberLengths(resultsDirectory); - plotHillTypeMuscleParams(resultsDirectory); -end - diff --git a/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResultsOld.m b/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResultsOld.m new file mode 100644 index 000000000..83d3d2b7c --- /dev/null +++ b/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResultsOld.m @@ -0,0 +1,429 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function writes to console or file the results of the optimization +% for easy evaluation by the user. It may require the input model to +% produce relative results or other information for output. +% +% (struct, struct) -> (None) +% Prints and plots results from muscle tendon personalization + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Di Ao, Marleny Vega % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function reportMuscleTendonPersonalizationResultsOld(optimizedParams, ... + mtpInputs, precalInputs) +if nargin < 3; precalInputs = []; end +[finalValues, results, resultsSynx, resultsSynxNoResiduals] = ... + getValuesToReport(mtpInputs, precalInputs, optimizedParams); +if ~isempty(precalInputs) + plotMuscleTendonLengthInitializationResults(precalInputs, mtpInputs) +end + +printJointMomentMatchingError(resultsSynx.muscleJointMoments, ... + mtpInputs.inverseDynamicsMoments); +makeExcitationAndActivationPlots(results, resultsSynx, mtpInputs, ... + mtpInputs.synergyExtrapolation); +makeModelParameterPlots(finalValues, mtpInputs, ... + mtpInputs.synergyExtrapolation) +makeTaskSpecificMomentMatchingPlots(... + permute(resultsSynxNoResiduals.muscleJointMoments, [3 1 2]), ... + permute(resultsSynx.muscleJointMoments, [3 1 2]), ... + permute(mtpInputs.inverseDynamicsMoments, [3 1 2]), ... + mtpInputs.coordinateNames, mtpInputs.synergyExtrapolation) +makeTaskSpecificNormalizedFiberLengthsPlots( ... + permute(resultsSynx.normalizedFiberLength, [3 1 2]), ... + mtpInputs, mtpInputs.synergyExtrapolation) +end +function [finalValues, results, resultsSynx, resultsSynxNoResiduals] = ... + getValuesToReport(mtpInputs, precalInputs, optimizedParams) + +finalValues = makeMtpValuesAsStruct([], optimizedParams, zeros(1, 7)); +save('finalvalues.mat', 'finalValues') +resultsSynx = calcMtpSynXModeledValues(finalValues, mtpInputs, struct()); +finalValues.synergyWeights(mtpInputs.numberOfExtrapolationWeights + 1 : end) = 0; +resultsSynxNoResiduals = calcMtpSynXModeledValues(finalValues, mtpInputs, struct()); +results = calcMtpModeledValues(finalValues, mtpInputs, struct()); +results.time = mtpInputs.emgTime(:, mtpInputs.numPaddingFrames + 1 : ... + end - mtpInputs.numPaddingFrames); +results.muscleExcitations = results.muscleExcitations(:, :, ... + mtpInputs.numPaddingFrames + 1 : end - mtpInputs.numPaddingFrames); +resultsSynx.muscleExcitations = resultsSynx.muscleExcitations(:, :, ... + mtpInputs.numPaddingFrames + 1 : end - mtpInputs.numPaddingFrames); +if ~isempty(precalInputs) +% finalOptimalFiberLength = ... +% finalValues.optimalFiberLengthScaleFactors .* mtpInputs.optimalFiberLength; +% finalValues.optimalFiberLengthScaleFactors = ... +% finalOptimalFiberLength ./ precalInputs.optimalFiberLength; +% finalTendonSlackLength = ... +% finalValues.tendonSlackLengthScaleFactors .* mtpInputs.tendonSlackLength; +% finalValues.tendonSlackLengthScaleFactors = ... +% finalTendonSlackLength ./ precalInputs.tendonSlackLength; +end +end +function plotMuscleTendonLengthInitializationResults(precalInputs, mtpInputs) + +tempValues.optimalFiberLengthScaleFactors = ... + mtpInputs.optimalFiberLength ./ precalInputs.optimalFiberLength; +mtpInputs.optimalFiberLength +tempValues.tendonSlackLengthScaleFactors = ... + mtpInputs.tendonSlackLength ./ precalInputs.tendonSlackLength; +precalInputs.maxIsometricForce = mtpInputs.maxIsometricForce; +precalInputs.optimizeIsometricMaxForce = 0; +modeledValues = calcMuscleTendonLengthInitializationModeledValues(tempValues, precalInputs); +plotPassiveForceData(permute(modeledValues.passiveForce, [3 1 2]), ... + precalInputs); +if precalInputs.passiveMomentDataExists +plotPassiveMomentData(permute(modeledValues.passiveModelMoments, [3 1 2]), ... + permute(precalInputs.passiveData.inverseDynamicsMoments, [3 1 2]), ... + precalInputs.passivePrefixes) +end +end +function plotPassiveMomentData(modeledValue, experimentalValue, titleName) + +columnsWithAllZeros = all(experimentalValue == 0, 1); +inverseDynamicsMoments = experimentalValue(repmat(~columnsWithAllZeros, ... + size(experimentalValue, 1), 1, 1)); +passiveModelMoments = modeledValue(repmat(~columnsWithAllZeros, ... + size(experimentalValue, 1), 1, 1)); + +inverseDynamicsMoments = ... + reshape(inverseDynamicsMoments, size(modeledValue, 1), []); +passiveModelMoments = ... + reshape(passiveModelMoments, size(modeledValue, 1), []); +figure('name', 'MuscleTendonLengthInitialization Passive Moment Matching'); +nplot = ceil(sqrt(size(passiveModelMoments, 2))); +for i = 1 : size(passiveModelMoments, 2) +subplot(nplot, nplot, i) +plot(passiveModelMoments(:, i), 'r', 'LineWidth', 1.25); hold on; +plot(inverseDynamicsMoments(:, i), 'k', 'LineWidth', 1.75); +if i == 1; ylabel('Passive Moments [Nm]'); +elseif i == size(passiveModelMoments, 2); legend('Model','Experimental') +end +axis([1 size(modeledValue, 1) min([inverseDynamicsMoments; ... + passiveModelMoments], [], 'all') max([inverseDynamicsMoments; ... + passiveModelMoments], [],'all')]) +xlabel('Time Points'); +title(strrep(titleName{i}, '_', ' ')) +end +end +function plotPassiveForceData(modeledValue, experimentalData) + +meanModeledValue = squeeze(mean(modeledValue, 2)); +stdModeledValue = squeeze(std(modeledValue, [], 2)); +figure('name', 'MuscleTendonLengthInitialization Passive Forces'); +nplots = ceil(sqrt(length(experimentalData.muscleNames))); + +t = 1 : size(meanModeledValue, 1); +for i = 1 : size(meanModeledValue, 2) +subplot(nplots, nplots, i); +plot(meanModeledValue(:, i), 'LineWidth', 2); hold on +fill([t'; flipud(t')], ... + [meanModeledValue(:,i) - stdModeledValue(:,i); ... + flipud(meanModeledValue(:, i) + stdModeledValue(:, i))], ... + 'b', 'linestyle', 'None', 'FaceAlpha', 0.5); +axis([1 size(modeledValue, 1) min(modeledValue,[],'all') max(modeledValue,[],'all')]) +title(strrep(experimentalData.muscleNames{i}, '_', ' ')) +if i > length(experimentalData.muscleNames) - nplots; xlabel('Time Points'); +else xticklabels(''); end +if ismember(i, 1 : nplots : length(experimentalData.muscleNames)) + ylabel({'Passive','Force [N]'}); +else yticklabels(''); end +end +end +function printJointMomentMatchingError(muscleJointMoments, inverseDynamicsMoments) + +for i = 1 : size(muscleJointMoments, 2) +jointMomentsRmse(i) = sqrt(sum((muscleJointMoments(:, i, :) - ... + inverseDynamicsMoments(:, i, :)) .^ 2, 'all') / ... + (numel(inverseDynamicsMoments(:, 1, :)) - 1)); +jointMomentsMae(i) = sum(abs(muscleJointMoments(:, i, :) - ... + inverseDynamicsMoments(:, i, :)) / ... + numel(inverseDynamicsMoments(:, 1, :)), 'all'); +end +fprintf(['The root mean sqrt (RMS) errors between model-predicted ' ... + 'and inverse dynamic moments are: \n' ]); +fprintf(['\n ' num2str(jointMomentsRmse) ' \n']); +fprintf(['The mean absolute errors (MAEs) between model-predicted ' ... + 'and inverse dynamic moments are: \n' ]); +fprintf(['\n ' num2str(jointMomentsMae) ' \n']); +end +function makeExcitationAndActivationPlots(results, resultsSynx, ... + experimentalData, synergyParameters) + +muscleLabels = getSynxMuscleNames(experimentalData.muscleNames, ... + synergyParameters.missingEmgChannelGroups); + +for i = 1 : numel(synergyParameters.taskNames) +figure('name', ['Muscle excitations/activations for ', ... + synergyParameters.taskNames{i}]); + +plotMuscleExcitationsAndActivations(... + results.muscleExcitations(synergyParameters.trialIndex{i}, :, :), ... + resultsSynx.muscleExcitations(synergyParameters.trialIndex{i}, :, :), ... + results.muscleActivations(synergyParameters.trialIndex{i}, :, :), ... + resultsSynx.muscleActivations(synergyParameters.trialIndex{i}, :, :), ... + muscleLabels) +end +end +function plotMuscleExcitationsAndActivations(muscleExcitations, ... + muscleExcitationsSynx, muscleActivations, muscleActivationsSynx, ... + muscleLabels) + +meanMuscleExcitation = permute(mean(muscleExcitations, 1), [3 2 1]); +stdMuscleExcitation = permute(std(muscleExcitations, [], 1), [3 2 1]); +meanMuscleExcitationSynx = permute(mean(muscleExcitationsSynx, 1), [3 2 1]); +stdMuscleExcitationSynx = permute(std(muscleExcitationsSynx, [], 1), [3 2 1]); +meanMuscleActivation = permute(mean(muscleActivations, 1), [3 2 1]); +stdMuscleActivation = permute(std(muscleActivations, [], 1), [3 2 1]); +meanMuscleActivationSynx = permute(mean(muscleActivationsSynx, 1), [3 2 1]); +stdMuscleActivationSynx = permute(std(muscleActivationsSynx, [], 1), [3 2 1]); + +t = 1 : size(meanMuscleExcitation, 1); +nplot = ceil(sqrt(numel(muscleLabels))); +for j = 1 : numel(muscleLabels) + subplot(nplot,nplot,j); + plot(meanMuscleExcitation(:, j), 'b-', 'LineWidth', 2); hold on + fill([t'; flipud(t')], ... + [meanMuscleExcitation(:, j) - stdMuscleExcitation(:, j); ... + flipud(meanMuscleExcitation(:, j) + stdMuscleExcitation(:, j))], ... + 'b', 'linestyle', 'None', 'FaceAlpha', 0.5); + hold on + plot(meanMuscleExcitationSynx(:, j), 'b--', 'LineWidth', 2); hold on + fill([t'; flipud(t')], ... + [meanMuscleExcitationSynx(:, j) - stdMuscleExcitationSynx(:, j); ... + flipud(meanMuscleExcitationSynx(:, j) + stdMuscleExcitationSynx(:, j))], ... + 'b', 'linestyle', 'None', 'FaceAlpha', 0.3); + plot(meanMuscleActivation(:, j), 'r-', 'LineWidth', 2); hold on + fill([t'; flipud(t')], ... + [meanMuscleActivation(:, j) - stdMuscleActivation(:, j); ... + flipud(meanMuscleActivation(:, j) + stdMuscleActivation(:, j))], ... + 'r', 'linestyle', 'None', 'FaceAlpha', 0.5); + plot(meanMuscleActivationSynx(:, j), 'r--', 'LineWidth', 2); hold on + fill([t'; flipud(t')], ... + [meanMuscleActivationSynx(:, j) - stdMuscleActivationSynx(:, j); ... + flipud(meanMuscleActivationSynx(:, j) + stdMuscleActivationSynx(:, j))], ... + 'r', 'linestyle', 'None', 'FaceAlpha', 0.3); + axis([1 size(meanMuscleExcitation, 1) 0 1]) + title(muscleLabels{j}); + if j == 1 + legend ('Excitation(without residual)', '', ... + 'Excitation(with residual)', '', ... + 'Activation(without residual)', '', ... + 'Activation(with residual)', ''); + end +end +set(gca, 'FontSize', 12) +end +function makeModelParameterPlots(finalValues, experimentalData, synergyParameters) + +muscleLabels = getSynxMuscleNames(experimentalData.muscleNames, ... + synergyParameters.missingEmgChannelGroups); + +In_width = 0.145; % witdth of each subplot +figure('name', 'Model Parameters', 'units', 'normalized', ... + 'outerposition', [0 0 1 1]); +for subplotElement = 1 : 6 + subplotTight(1, 6, subplotElement, [0.04, 0.001]); + switch subplotElement + case 1 %activationTimeConstant + bar(1 : numel(muscleLabels), finalValues.activationTimeConstants, ... + 'barwidth', 0.8, 'Horizontal', 'on', 'FaceAlpha', 0.6); + hold on; + title('Activation Time Constant (cs)') + pos_in = get(gca, 'Position'); + pos_in(3) = In_width; + pos_in(1) = pos_in(1) + 0.05; + set(gca, 'YTick', 1 : numel(muscleLabels), 'yTickLabel', ... + muscleLabels, 'Fontsize', 11, 'Position', pos_in); + case 2 %activationNonlinearity + bar(finalValues.activationNonlinearityConstants, 'Horizontal', 'on', ... + 'barwidth', 0.8, 'FaceAlpha', 0.6); + title('Activation Nonlinearity') + pos_in(1) = pos_in(1) + pos_in(3) + 0.015; + set(gca, 'YTick', [], 'yTickLabel', [], 'Position', pos_in, ... + 'Fontsize', 11); + case 3 %timeDelay + bar(finalValues.electromechanicalDelays, 'Horizontal', 'on', 'barwidth', 0.8, ... + 'FaceAlpha', 0.6); + title('Electromechanical Time Delay (ds)') + pos_in(1) = pos_in(1) + pos_in(3) + 0.015; + set(gca, 'YTick', [], 'yTickLabel', [],'Position', pos_in, ... + 'Fontsize', 11); + case 4 % emgScalingFactor + bar(finalValues.emgScaleFactors, 'Horizontal', 'on', 'barwidth', 0.8, ... + 'FaceAlpha', 0.6); + title('Emg Scaling Factor') + pos_in(1) = pos_in(1) + pos_in(3) + 0.015; + set(gca, 'YTick', [], 'yTickLabel', [],'Position', pos_in, ... + 'Fontsize', 11); + case 5 %muscleOptimalLength + bar(finalValues.optimalFiberLengthScaleFactors, 'Horizontal', 'on', 'barwidth', ... + 0.8, 'FaceAlpha', 0.6); + title('Optimal Fiber Length Scaling Factor') + pos_in(1) = pos_in(1) + pos_in(3) + 0.015; + set(gca, 'YTick', [], 'yTickLabel', [],'Position', pos_in, ... + 'Fontsize', 11); + case 6 %tendonSlackLength + bar(finalValues.tendonSlackLengthScaleFactors, 'Horizontal', 'on', 'barwidth', ... + 0.8, 'FaceAlpha', 0.6); + title('Tendon Slack Length Scaling Factor') + pos_in(1) = pos_in(1) + pos_in(3) + 0.015; + set(gca, 'YTick', [], 'yTickLabel', [],'Position', pos_in, ... + 'Fontsize', 11); + end +end +end +function vargout=subplotTight(m, n, p, margins, varargin) +%% Default params +isWrapper=false; +if (nargin<4) || isempty(margins) + margins=[0.04, 0.04]; % default margins value- 4% of figure +end +if length(margins)==1 + margins(2)=margins; +end + +%note n and m are switched as Matlab indexing is column-wise, while subplot indexing is row-wise :( +[subplot_col, subplot_row] = ind2sub([n, m], p); + +height = (1 - (m + 1) * margins(1)) / m; % single subplot height +width = (1 - (n + 1) * margins(2)) / n; % single subplot width + +% note subplot suppors vector p inputs- so a merged subplot of higher dimentions will be created +subplotColumns = 1 + max(subplot_col) - min(subplot_col); % number of column elements in merged subplot +subplotRows = 1 + max(subplot_row) - min(subplot_row); % number of row elements in merged subplot + +mergedHeight = subplotRows * (height + margins(1)) - margins(1); % merged subplot height +mergedWidth = subplotColumns * (width + margins(2)) - margins(2); % merged subplot width + +mergedBottom = (m - max(subplot_row)) * (height + margins(1)) + margins(1); % merged subplot bottom position +mergedLeft = min(subplot_col) * (width + margins(2)) - width; % merged subplot left position +pos = [mergedLeft, mergedBottom, mergedWidth, mergedHeight]; + + +if isWrapper + h=subplot(m, n, p, varargin{:}, 'Units', 'Normalized', 'Position', pos); +else + h=axes('Position', pos, varargin{:}); +end + +if nargout == 1;vargout=h; end +end +function makeTaskSpecificMomentMatchingPlots(jointMoments, jointMomentsSynx, ... + inverseDynamicsMoments, coordinates, synergyParameters) + +t = 1 : size(jointMoments,1); +for i = 1 : numel(synergyParameters.taskNames) +figure('name', ['Joint moments for ', synergyParameters.taskNames{i}]); +plotJointMoments(jointMoments(:, synergyParameters.trialIndex{i}, :), ... + inverseDynamicsMoments(:, synergyParameters.trialIndex{i}, :), ... + synergyParameters.taskNames{i}, coordinates, 0,... + {'','Predicted (w/o residual excitations)','','Inverse dynamics'}); +plotJointMoments(jointMomentsSynx(:, synergyParameters.trialIndex{i}, :), ... + inverseDynamicsMoments(:, synergyParameters.trialIndex{i}, :), ... + synergyParameters.taskNames{i}, coordinates, numel(coordinates), ... + {'','Predicted (with residual excitations)','','Inverse dynamics'}); +end +end +function plotJointMoments(jointMoments, inverseDynamicsMoments, taskName, ... + coordinates, subplotIndex, legendName) + +meanJointMoments = squeeze(mean(jointMoments, 2)); +stdJointMoments = squeeze(std(jointMoments, [], 2)); +meaninverseDynamicsMoments = squeeze(mean(inverseDynamicsMoments, 2)); +stdinverseDynamicsMoments = squeeze(std(inverseDynamicsMoments, [], 2)); +t = 1 : size(jointMoments,1); +for j = 1:size(jointMoments,3) +subplot(2, size(jointMoments, 3), j + subplotIndex); +plot(-meanJointMoments(:, j), 'r', 'LineWidth', 2); hold on +fill([t'; flipud(t')], ... + [-meanJointMoments(:,j) - stdJointMoments(:,j); ... + flipud(-meanJointMoments(:, j) + stdJointMoments(:, j))], ... + 'r', 'linestyle', 'None', 'FaceAlpha', 0.5); +subplot(2, size(jointMoments, 3), j + subplotIndex); +plot(-meaninverseDynamicsMoments(:, j), 'k', 'LineWidth', 2); +fill([t'; flipud(t')], ... + [-meaninverseDynamicsMoments(:, j) - stdinverseDynamicsMoments(:, j); ... + flipud(-meaninverseDynamicsMoments(:, j) + stdinverseDynamicsMoments(:, j))], ... + 'k', 'linestyle', 'None', 'FaceAlpha', 0.5); +title({taskName, [strrep(coordinates{j}, '_', ' ') ],'Moment (N-m)'}); +xlabel('Time Frames'); axis([0 100 -80 120]); +if j == 1; ylabel('Moment (N-m)'); +elseif j == numel(coordinates) + lgd = legend(legendName); + lgd.Orientation ='horizontal'; + lgd.NumColumns = 2; +end +set(gca, 'FontSize', 12) +end +end +function makeTaskSpecificNormalizedFiberLengthsPlots(... + normalizedFiberLengths, experimentalData, synergyParameters) + +muscleLabels = getSynxMuscleNames(experimentalData.muscleNames, ... + synergyParameters.missingEmgChannelGroups); + +for i = 1 : numel(synergyParameters.taskNames) +figure('name', ['Normalized Fiber Lengths for ', ... + synergyParameters.taskNames{i}]); + +plotNormalizedFiberLength(normalizedFiberLengths(:, ... + synergyParameters.trialIndex{i}, :), muscleLabels) +end +end +function plotNormalizedFiberLength(normalizedFiberLengths, ... + muscleLabels) + +nplots = ceil(sqrt(numel(muscleLabels))); +meanNormalizedFiberLengths = squeeze(mean(normalizedFiberLengths, 2)); +stdNormalizedFiberLengths = squeeze(std(normalizedFiberLengths, [], 2)); +t = 1 : size(normalizedFiberLengths,1); +for i = 1 : numel(muscleLabels) + subplot(nplots,nplots,i) + hold on + plot(meanNormalizedFiberLengths(:, i), 'b', 'LineWidth', 2); hold on + fill([t'; flipud(t')], ... + [meanNormalizedFiberLengths(:, i) - stdNormalizedFiberLengths(:, i); ... + flipud(meanNormalizedFiberLengths(:, i) + stdNormalizedFiberLengths(:, i))], ... + 'b', 'linestyle', 'None', 'FaceAlpha', 0.3); + plot(0.7*ones(1, size(normalizedFiberLengths, 1)),'r--','LineWidth',2) + plot(1*ones(1, size(normalizedFiberLengths, 1)),'r--','LineWidth',2) + title(muscleLabels{i}) + axis([1 size(normalizedFiberLengths, 1) 0 1.5]) + if i > numel(muscleLabels) - nplots; xlabel('Time Points'); + else xticklabels(''); end + if ismember(i, 1 : nplots : numel(muscleLabels)) + ylabel({'Normalized','Fiber Length'}); + else yticklabels(''); end +end +end +function muscleLabels = getSynxMuscleNames(muscleNames, ... + missingEmgChannelGroups) + +for i = 1 : numel(muscleNames) + if ismember(i, [missingEmgChannelGroups{:}]) + muscleLabels{i} = [muscleNames{i} '(*)']; + else + muscleLabels{i} = muscleNames{i}; + end +end +end \ No newline at end of file From 1c40f78bc1c3320dadac2e361f94ff17a8924d1f Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Thu, 7 Dec 2023 22:29:53 -0600 Subject: [PATCH 229/365] Evaluate surrogate model as polynomial --- .../SurrogateModelCreation.m | 12 ++--- .../createSurrogateModel.m | 10 ++-- .../evaluateSurrogate.m | 47 +++++-------------- 3 files changed, 25 insertions(+), 44 deletions(-) diff --git a/src/SurrogateModelCreation/SurrogateModelCreation.m b/src/SurrogateModelCreation/SurrogateModelCreation.m index 64a8fd028..efaf3c3a5 100644 --- a/src/SurrogateModelCreation/SurrogateModelCreation.m +++ b/src/SurrogateModelCreation/SurrogateModelCreation.m @@ -48,9 +48,9 @@ tree.trial_prefixes = inputs.trialName; prefixes = findPrefixes(tree.trial_prefixes, inputs.dataDirectory); -% inputs.muscleNames = getMusclesFromCoordinates(inputs.model, ... -% inputs.surrogateModelCoordinateNames); -% inputs.numMuscles = length(inputs.muscleNames); +inputs.muscleNames = getMusclesFromCoordinates(inputs.model, ... + inputs.surrogateModelCoordinateNames); +inputs.numMuscles = length(inputs.muscleNames); inverseKinematicsFileNames = findFileListFromPrefixList(fullfile( ... inputs.dataDirectory, "IKData"), prefixes); @@ -72,9 +72,9 @@ inputs.dataDirectory, "MAData"), prefixes); [inputs.muscleTendonLengths, inputs.muscleTendonColumnNames] = ... parseFileFromDirectories(directories, "Length.sto", Model(inputs.model)); -inputs.muscleNames = findMusclesFromSynergyGroups(inputs); -inputs.surrogateModelCoordinateNames = findSurrogateModelCoordinates( ... - inputs, directories); +% inputs.muscleNames = findMusclesFromSynergyGroups(inputs); +% inputs.surrogateModelCoordinateNames = findSurrogateModelCoordinates( ... +% inputs, directories); inputs.muscleTendonLengths = findSpecificMusclesInData( ... inputs.muscleTendonLengths, inputs.muscleTendonColumnNames, ... inputs.muscleNames); diff --git a/src/SurrogateModelCreation/createSurrogateModel.m b/src/SurrogateModelCreation/createSurrogateModel.m index ba501acdf..ffb82b194 100644 --- a/src/SurrogateModelCreation/createSurrogateModel.m +++ b/src/SurrogateModelCreation/createSurrogateModel.m @@ -49,10 +49,14 @@ createMuscleSpecificSurrogateModel(jointAngles{i}, ... muscleTendonLengths(:, i), momentArms{i}, polynomialDegree); +polynomialMuscleTendonLengths = matlabFunction(polynomialExpressionMuscleTendonLengths); +polynomialMuscleTendonVelocities = matlabFunction(polynomialExpressionMuscleTendonVelocities); +polynomialMomentArms = matlabFunction(polynomialExpressionMomentArms); + surrogateMuscles{i} = @(jointAngles, jointVelocities)evaluateSurrogate( ... jointAngles, jointVelocities, ... - polynomialExpressionMuscleTendonLengths, ... - polynomialExpressionMuscleTendonVelocities, ... - polynomialExpressionMomentArms, coefficients); + polynomialMuscleTendonLengths, ... + polynomialMuscleTendonVelocities, ... + polynomialMomentArms, coefficients); end end diff --git a/src/SurrogateModelCreation/evaluateSurrogate.m b/src/SurrogateModelCreation/evaluateSurrogate.m index 22bd61e3d..cd2073b96 100644 --- a/src/SurrogateModelCreation/evaluateSurrogate.m +++ b/src/SurrogateModelCreation/evaluateSurrogate.m @@ -20,7 +20,7 @@ % National Institutes of Health (R01 EB030520). % % % % Copyright (c) 2021 Rice University and the Authors % -% Author(s): Spencer Williams % +% Author(s): Spencer Williams % % % % Licensed under the Apache License, Version 2.0 (the "License"); % % you may not use this file except in compliance with the License. % @@ -40,40 +40,17 @@ polynomialExpressionMuscleTendonVelocity, ... polynomialExpressionMomentArms, coefficients) -% Values are set to match symbolic expressions in polynomials -for i = 1 : size(jointAngles, 2) - eval(['theta' num2str(i) ' = jointAngles(:,' num2str(i) ');']); - eval(['thetaDot' num2str(i) ' = jointVelocities(:,' num2str(i) ');']); -end -muscleTendonLengthMatrix = zeros(size(jointAngles, 1), ... - size(polynomialExpressionMomentArms, 2)); -muscleTendonVelocityMatrix = zeros(size(jointVelocities, 1), ... - size(polynomialExpressionMomentArms, 2)); -momentArmsMatrix = zeros(size(jointAngles, 1), size(jointAngles, 2), ... - size(polynomialExpressionMomentArms, 2)); -for j = 1 : size(polynomialExpressionMomentArms, 2) - muscleTendonLengthMatrix(:, j) = ... - eval(polynomialExpressionMuscleTendonLength(1, j)) .* ... - ones(size(jointAngles, 1), 1); - muscleTendonVelocityMatrix(:, j) = ... - eval(polynomialExpressionMuscleTendonVelocity(1, j)) .* ... - ones(size(jointAngles, 1), 1); - for k = 1 : size(jointAngles, 2) - momentArmsMatrix(:, k, j) = ... - eval(polynomialExpressionMomentArms(k, j)) .* ... - ones(size(jointAngles, 1), 1); - end -end -fullMatrix = [muscleTendonLengthMatrix; muscleTendonVelocityMatrix; ... - reshape(momentArmsMatrix, [], ... - size(polynomialExpressionMomentArms, 2))]; +muscleTendonLength = zeros(size(jointAngles, 1), 1); +muscleTendonVelocity = zeros(size(jointAngles, 1), 1); +momentArms = zeros(size(jointAngles)).'; -modeledValues = fullMatrix * coefficients; -muscleTendonLength = modeledValues(1 : size(jointAngles, 1)); -muscleTendonVelocity = modeledValues(1 + size(jointAngles, 1) : ... - size(jointAngles, 1) * 2); -for i = 1 : size(jointAngles, 2) - momentArms(:, i) = modeledValues(1 + size(jointAngles, 1) * (1 + i) ... - : size(jointAngles, 1) * (2 + i)); +for i = 1 : size(jointAngles, 1) + positionArgs = num2cell(jointAngles(i, :)); + velocityArgs = [positionArgs num2cell(jointVelocities(i, :))]; + muscleTendonLength(i) = polynomialExpressionMuscleTendonLength(positionArgs{:}) * coefficients; + muscleTendonVelocity(i) = polynomialExpressionMuscleTendonVelocity(velocityArgs{:}) * coefficients; + momentArms(:, i) = polynomialExpressionMomentArms(positionArgs{:}) * coefficients; end + +momentArms = momentArms.'; end From 34499e7607ee0adb837c8c621de37eef8c510980 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Fri, 8 Dec 2023 15:14:51 -0600 Subject: [PATCH 230/365] Revert "Remove symmetry from discrete" This reverts commit 7730a409181562cbf87e56ffe5a7c15be81a2700. --- src/TreatmentOptimization/generateCostTermStruct.m | 1 + 1 file changed, 1 insertion(+) diff --git a/src/TreatmentOptimization/generateCostTermStruct.m b/src/TreatmentOptimization/generateCostTermStruct.m index 3914519d7..d89bd7dd4 100644 --- a/src/TreatmentOptimization/generateCostTermStruct.m +++ b/src/TreatmentOptimization/generateCostTermStruct.m @@ -113,6 +113,7 @@ allowedTypes = [ ... "synergy_vector_tracking" ... "belt_speed_goal" ... + "kinematic_symmetry" ... "user_defined" ... ]; otherwise From 47ab296896e9a81f120a42f7273254ec34afc080 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Fri, 8 Dec 2023 15:14:57 -0600 Subject: [PATCH 231/365] Revert "Preliminary kinematic symmetry cost term" This reverts commit c5464ee6008e4b5ee4258aa98bae6fcebe29981b. --- .../calcKinematicSymmetryDiscrete.m | 40 ------------------- .../generateCostTermStruct.m | 19 +++------ 2 files changed, 6 insertions(+), 53 deletions(-) delete mode 100644 src/TreatmentOptimization/IntegrandTerms/calcKinematicSymmetryDiscrete.m diff --git a/src/TreatmentOptimization/IntegrandTerms/calcKinematicSymmetryDiscrete.m b/src/TreatmentOptimization/IntegrandTerms/calcKinematicSymmetryDiscrete.m deleted file mode 100644 index 70443d6e2..000000000 --- a/src/TreatmentOptimization/IntegrandTerms/calcKinematicSymmetryDiscrete.m +++ /dev/null @@ -1,40 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function uses frequency analysis to calculate a cost value for -% minimizing gait asymmetry. -% -% (struct, struct, struct) -> (Array of number) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Spencer Williams % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function cost = calcKinematicSymmetryDiscrete(values, inputs, costTerm) -rightCoordinate = costTerm.coordinate + "_r"; -leftCoordinate = costTerm.coordinate + "_l"; -data = values.statePositions(:, inputs.statesCoordinateNames == rightCoordinate); -data = [data values.statePositions(:, inputs.statesCoordinateNames == leftCoordinate)]; - -spectra = pspectrum(data, values.time, 'FrequencyResolution', 0.5/mean(diff(values.time)), 'Leakage', 0); -spectra = sqrt(spectra); -cost = norm((spectra(:, 1) - spectra(:, 2)) ./ mean(spectra, 2)); -end diff --git a/src/TreatmentOptimization/generateCostTermStruct.m b/src/TreatmentOptimization/generateCostTermStruct.m index d89bd7dd4..a142e60ae 100644 --- a/src/TreatmentOptimization/generateCostTermStruct.m +++ b/src/TreatmentOptimization/generateCostTermStruct.m @@ -113,7 +113,6 @@ allowedTypes = [ ... "synergy_vector_tracking" ... "belt_speed_goal" ... - "kinematic_symmetry" ... "user_defined" ... ]; otherwise @@ -342,12 +341,12 @@ values.time, ... auxdata); -% costTermCalculations.kinematic_symmetry = @(values, modeledValues, auxdata, costTerm) ... -% calcKinematicSymmetryIntegrand( ... -% values.positions, ... -% values.time, ... -% auxdata, ... -% costTerm); +costTermCalculations.kinematic_symmetry = @(values, modeledValues, auxdata, costTerm) ... + calcKinematicSymmetryIntegrand( ... + values.positions, ... + values.time, ... + auxdata, ... + costTerm); costTermCalculations.walking_speed_goal = @(values, modeledValues, auxdata, costTerm) ... calcGoalWalkingSpeedIntegrand( ... @@ -377,12 +376,6 @@ auxdata, ... costTerm); -costTermCalculations.kinematic_symmetry = @(values, modeledValues, auxdata, costTerm) ... - calcKinematicSymmetryDiscrete( ... - values, ... - auxdata, ... - costTerm); - costTermCalculations.user_defined = @(values, modeledValues, auxdata, costTerm) ... userDefinedFunction(values, ... modeledValues, ... From 15f26ffdd20256c71bb3684a25863bf7ff8036b5 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Fri, 8 Dec 2023 17:10:52 -0600 Subject: [PATCH 232/365] Angular momentum mex file --- src/core/mex/compileMex.m | 21 +++ ...verseDynamicsAngularMomentumMexWindows.cpp | 169 ++++++++++++++++++ ...seDynamicsAngularMomentumMexWindows.mexw64 | Bin 0 -> 34304 bytes 3 files changed, 190 insertions(+) create mode 100644 src/core/mex/compileMex.m create mode 100644 src/core/mex/inverseDynamicsAngularMomentumMexWindows.cpp create mode 100644 src/core/mex/inverseDynamicsAngularMomentumMexWindows.mexw64 diff --git a/src/core/mex/compileMex.m b/src/core/mex/compileMex.m new file mode 100644 index 000000000..67046f813 --- /dev/null +++ b/src/core/mex/compileMex.m @@ -0,0 +1,21 @@ +mex CXXFLAGS="/$CXXFLAGS -fopenmp" LDFLAGS="/$LDFLAGS -fopenmp"... + COMPFLAGS="/openmp $COMPFLAGS"... + inverseDynamicsAngularMomentumMexWindows.cpp... + -L'C:\OpenSim 4.4\sdk\lib'... + -L'C:\OpenSim 4.4\sdk\Simbody\lib'... + -L'C:\OpenSim 4.4\sdk\spdlog\lib\spdlog'... + -L'C:\OpenSim 4.4\sdk\spdlog\include'... + -L'C:\OpenSim 4.4\sdk\spdlog\include\spdlog'... + -losimCommon -losimSimulation... + -losimAnalyses -losimActuators -losimTools... + -lSimTKcommon -lSimTKmath... + -lSimTKsimbody -lliblapack... + -llibblas -losimJavaJNI -losimLepton... + -lspdlog... + -I'C:\OpenSim 4.4\sdk\include'... + -I'C:\OpenSim 4.4\sdk\include\OpenSim'... + -I'C:\OpenSim 4.4\sdk\Simbody\include'... + -I'C:\OpenSim 4.4\sdk\include\OpenSim\Simulation'... + -I'C:\OpenSim 4.4\sdk\spdlog\include'... + -DWIN32 -D_WINDOWS -DNDEBUG... + ; \ No newline at end of file diff --git a/src/core/mex/inverseDynamicsAngularMomentumMexWindows.cpp b/src/core/mex/inverseDynamicsAngularMomentumMexWindows.cpp new file mode 100644 index 000000000..2d4668cd1 --- /dev/null +++ b/src/core/mex/inverseDynamicsAngularMomentumMexWindows.cpp @@ -0,0 +1,169 @@ +// This function is part of the NMSM Pipeline, see file for full license. +// +// performs inverse dynamics with openMP + +// ----------------------------------------------------------------------- // +// The NMSM Pipeline is a toolkit for model personalization and treatment // +// optimization of neuromusculoskeletal models through OpenSim. See // +// nmsm.rice.edu and the NOTICE file for more information. The // +// NMSM Pipeline is developed at Rice University and supported by the US // +// National Institutes of Health (R01 EB030520). // +// // +// Copyright (c) 2021 Rice University and the Authors // +// Author(s): Marleny Vega // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// http://www.apache.org/licenses/LICENSE-2.0. // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or // +// implied. See the License for the specific language governing // +// permissions and limitations under the License. // +// ----------------------------------------------------------------------- // + +#include "mex.h" +#include +#include +#include +#include +#include +#include +#include // +#include +#include +#include + +using namespace OpenSim; +using namespace SimTK; +using namespace std; +#define numThreads 20 // + +//______________________________________________________________________________ + +static Model *osimModel; +static State *osimState; +static InverseDynamicsSolver *idSolver; +static bool modelIsLoaded = false; + +void ClearMemory(void){ + delete osimModel; + delete idSolver; + modelIsLoaded = false; + mexPrintf("Cleared memory from inverseDynamics mex file.\n"); +} + +vector> mexArrayToVector(const mxArray *input) { + const mwSize *dimension; + dimension = mxGetDimensions(input); + int rows = (int) dimension[0]; + int columns = (int) dimension[1]; + double *data = (double *) mxGetData(input); + vector> output(rows, vector(columns)); + for (int i = 0; i < rows; i++) { + for (int j = 0; j < columns; j++) { + memcpy(&output[i][j], data + i + j * rows, sizeof(double) ); + } + } + return output; +} + +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]){ + mexAtExit(ClearMemory); + if (nrhs == 1) { + if (modelIsLoaded == true){ + ClearMemory(); + } + string modelName = mxArrayToString(prhs[0]); + std::streambuf* oldCoutStreamBuf = std::cout.rdbuf(); + std::ostringstream strCout; + std::cout.rdbuf(strCout.rdbuf()); + osimModel = new Model(modelName); + osimState = &osimModel->initSystem(); + idSolver = new InverseDynamicsSolver(*osimModel); + std::cout.rdbuf(oldCoutStreamBuf); + modelIsLoaded = true; + } + else if (nrhs == 6) { + if (modelIsLoaded == false){ + mexErrMsgTxt("!!!No OpenSim model has been loaded!!!\n"); + } + const int numPts = mxGetM(prhs[0]); + const int numQs = mxGetN(prhs[1]); + const int numControls = mxGetN(prhs[5]); + const int numCoords = osimState[0].getNQ(); + + double *time = mxGetPr(prhs[0]); + vector> q = mexArrayToVector(prhs[1]); + vector> qp = mexArrayToVector(prhs[2]); + vector> qpp = mexArrayToVector(prhs[3]); + vector> u = mexArrayToVector(prhs[5]); + + const mxArray *cellElementPtr; + mwIndex k; + mwSize numLabels, buflen; + numLabels = mxGetNumberOfElements(prhs[4]); + int status; + + plhs[0] = mxCreateDoubleMatrix(numPts,numCoords,mxREAL); + double *idLoads = mxGetPr(plhs[0]); + plhs[1] = mxCreateDoubleMatrix(numPts, 3, mxREAL); + double* angularMomentum = mxGetPr(plhs[1]); + + for (int i = 0; i < numPts; ++i){ + osimState->setTime(time[i]); + + for (int k = 0; k < numLabels; k++){ + cellElementPtr = mxGetCell(prhs[4], k); + buflen = mxGetN(cellElementPtr)*sizeof(mxChar) + 1; + char* c_array; + c_array = (char *) mxCalloc(buflen, sizeof(char)); + status = mxGetString(cellElementPtr, c_array, buflen); + osimModel->getCoordinateSet().get(c_array).setValue(*osimState, q[i][k], false); + osimModel->getCoordinateSet().get(c_array).setSpeedValue(*osimState, qp[i][k]); + mxFree(c_array); + } + osimModel->realizeVelocity(*osimState); + + // Whole body angular momentum + if (nlhs > 1) { + SpatialVec momentum = osimModel->getMatterSubsystem().calcSystemCentralMomentum(*osimState); + Vec3 angularMomentumPoint = momentum.get(0); + for (int j = 0; j <= 2; j++) { + angularMomentum[i + numPts * j] = angularMomentumPoint.get(j); + } + } + + Vector AccelsVec(numCoords, 0.0); + for (int j = 0; j < numCoords; j++){ + double StateQ = osimState->getQ().get(j); + for (int k = 0; k < numQs; k++){ + if (abs(q[i][k] - StateQ) <= 1e-6){ + AccelsVec.set(j, qpp[i][k]); + } + } + } + + Vector newControls(numControls, 0.0); + for (int j = 0; j < numControls; j++){ + newControls.set(j, u[i][j]); + } + osimModel->setControls(*osimState, newControls); + osimModel->markControlsAsValid(*osimState); + osimModel->realizeDynamics(*osimState); + + Vector IDLoadsVec; + IDLoadsVec = idSolver->solve(*osimState, AccelsVec); + for (int j = 0; j < numCoords; j++){ + idLoads[i + numPts * j] = IDLoadsVec[j]; + } + + } + q.clear(); + qp.clear(); + qpp.clear(); + u.clear(); + } +} \ No newline at end of file diff --git a/src/core/mex/inverseDynamicsAngularMomentumMexWindows.mexw64 b/src/core/mex/inverseDynamicsAngularMomentumMexWindows.mexw64 new file mode 100644 index 0000000000000000000000000000000000000000..df6a75b4df2dfddfc17de2fccf94a9329c83c3fc GIT binary patch literal 34304 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 z%0DaiUsALRyI_Bs6m1X>uHEuwxsv=r5gb4nd-E5uAiL#HJh6gAr3MpJb|t1)9zD`V zhRtKw18Tk(*R5B1 zDTa0Mi>*>ehqdzKj(9w5=#)ART6-sPVTk7!G1CM5G1@Cq)NpjKA(Zq478RyELl5-e z&T)bAXAL+>SP=QUA(TPEl*p$*GB;*>WfR zc_hl<+X~Yij>imRS=E#cQdPq49?yh5NJe;pokmHM;aU_@FI2E4oHJr^*8jkCqpbGHW zB{{((rzC-xGyY=Sw50;Cix7kStX3U6dXA(Uw#Pw)DQTeHjPc`gp{E*|j$1#GO-|Hr zYQHfBo-`^uAihkT+aHLUGEkuG#xQcp*dvR6R}H4a=POTA*H@!}Z+0TH?n6F9H!ejp z*@qpszMx!-*wG`Wz8ZPUp!^ota%bN?Dic3INprK%V;m|v(xP6dvSh}I0=^j{ z8wSkzEu<(+b!TM6;wvRD`>jv8V=6+(JaozOp$m(*fH4RTwRMe+LcWYsx`wCVkHJ>W@cZih8wpn8sxlhT=uI} zN}HTDSnVVG9C(gGuZC#PrnG12Zh3&7S-+1-5;iG+gAlMs!1_-LGVEbu@Atqu>hC4{ z5h=4mRZZs*Gg1C>#|%*{B$}_IPUiI1D-4yEHqsTrO zx4j9KhJ}P5h)CRafF}<;F(HxB9JOD8R~ddmc{uEWTl;SA<7gas&B%j29sfA#^>cXs zl~T73I<*HW@^s*En^s}?Rp&d>4qwLfS0F%?7gBcA^y@Me(}DQIDCsgqkweXPnIecm zB*=@2SR2%zN!PT4U`iA6q^?8*wLFwBr<{8w@}r(ayOs&Dq@-z?sF;YQQHhf(ME2!k zP#h~)(@b*U#6NC(Y$9sQ4-bH7cATI36w}7%`MB*RDnVZtNRbNcJ4m2aW$9d)}P$ zm=!8_*JHND2b8BgATlpLJDe?B8z?v zMPuP{aa%na8nWuoLaZ(yD#T|kM*;LamU1rAy{ExcKvfqY-Z=ioS%gc$d6Lryb;(8sW=ks)EE~K1Y zX{k3|4?8MF4#e?j*K{@zpo>`mlq85`9>9K+388^Y9G=|cAi|VS5pX@gQrpp1bU6;S z7MOmCv!if>RG!j<2(01H2qZkSWdXfU8!l~|2m?~Nz?l{<_RQXEdCZwA#qgR&LN}s# z_Lkh}i6uWHMN`vpARkJTmnM7iIs!@ZVsk==C$BvKW4Je??@c`Y#bQUAk{Y=#on23{ zBTOq35j zclP68fGPVh^T2nksNdPv4bqAa*_4di9t1LWbqdgMd~*baSQ}ssuU$Ke1hCY_Dj4x^*+Drl7fJ$Qh4?%Hp;0}{d60yS!&M$wuhZu=6rP`_k| z-W-z>l;3fm;I4F<5z>}@Bs1*wxGedsm15KRc~)#uA&#~BPJ-t~{f7zkz|!IIINmsV zGtjHfRGOdaIB0cGr?arwqO=iptz*>1u=2L8>Z>c5=6Jl8eb91Gq1=H7Tw!u-u=0&>1T*xF#O3+~eHxL3N$a9ZgJkO?(FWkSKpbD|WOvJvr=^ zA}QyuhP-C==f^FJ+Q%7KC}YvuaYh!JvB*#06jSRbpTj@1Z%mgSH(>20@k*9^JA$c& zvZ=ci-YTBs7Di1^Bic6}c~2qI1{==`W721N{hQAuUeS7?Ode38*0$w|wysliRam=+9zyrCQKXxXKS z-4c3bCojW3v-5(^@TIb8HLxQG%v~lIBBbL{QvqVmqn-9pZsdTy%XAKs7-1BA8?iQE zbr+s-q%iMD7`r`CO7B_N6HbYwOhl<8I^6(3Bbz~vbqG7E$UZB7J(Vwg@Ex+-7nnXQ z2LE@q<0!xg((zdzLbcGzDrJISrM}9xZzn97jhtd+IbB5A> zN~aH{t<;}Xksjn%cI8BR3@`?15ns-iuz1vM9NClA!gDA}Xnh=#F4^r{Ibx*43V&%o z(DbFeocKf8VzABA#2?aGNDsDtYmtMK@7;-4S%!poYmr^KonCGcZ4ctB$p87VhK%C@ zyt-zPVQS#7(p>VyQWiq+9@*uWSHZU}TC3y$ohW5uq(F(+$UeA*o~yK5?&y#^kPqIz z(d&jrm7H~p3O^O;Hw4@#;I9PSF5vHOWc*qu(#r+BK)`7NG6CNe_=g2#;(Dh+jodHb zHUTR)srh#an0%d@UM%3R1XKjf_>NkBwSa8`j@zu}djz~qz-I+ay#R6*O zOr&!KJX64!b-KgQNX?#RBXJ)froi?_1CCyr+_yI zC<%CqfUjMn;ytB7QNKi_7Yg{n)hgaY0&WoS0s-Hx7UcroCg5BFe_o}QUnSs70qaD2 z9}0LO9+uK|yMS*Cn1g41-B>_DG zdIc;IaE*X#1*{U#FW_bYw+g7yzfGj?5b!PmTLf$quwB4z0kv+YU8MG7X;~dx;#SsthkRhO6X*;HRy$x@P#iL2pawcq`zDs;?H;i|f8>gqSv*?kS= z;Hcr>SYYOU3nehRE->tLZr+oh{%30UHH=&*>`OsUrU@0c`>{ELY1LPgWtx zW*ctl5;ff__?k6GO$P)nohRSFac8$HNEQqhPeRa^ZvI<`XP?FdN zA0}8}NdIE*{bt9<`&%!>F$P^X@(|F!Qtf~9JQb2&(8cw-2A|xPsgtAmPPIJqhr{s% zok2i#zbn4EPC@)xc;UsqX@$Isj^^JrL-yhDGMpP+O^9Kaf@&_R;D*q3!w}re% zHrcQ7=TiAK3)m>+J`!I`>)ChwfUyh@O6VTr!f-H$uDk^CMqOH~Mzl2AG`N)Sz|HAX zLR>q5r(JrymQiRD9?@*qm5pp$0X^bTyAlzh4c>sRj#fsH4lb-kLphm&uqwOCcLUk8+Pk zPv0NHCAm6qkL-^|kNBfY!yCyT@kQ$gl{bQi9pDRV%$S!6C2hKL1##=ejCEsNGZ6Rc z%0?qXZBScu>HA6T>aQN)kxtO1$IC@qqg_PPt1J7eJZW=-XqsvWWiinm*Ti-s>*IybA<+Y;D zc9g?68uOyDXw{W>BR&=nM1*K+^Iy*gwWGf>$B>u(5CHA>c`M>aaObXotr|gFPlRZXrk~WF{%Qo?XmZegw?vOiaiW=qwzca-yp({x z)@Qw(*Xn3!vCO?f=UKcjpx)QcrS5Hrq;v|m@ z;AoeG_*k?#4rq=hZ)!(>>G{aG6Z(&G_)ueBgx{(wZ`Z}C64B5u{WuU^?Mg$$*l29j z(x0QuN(ltNoCf>5h_Rn*j3F-*Mf`VP(ku9`KNES@Ia*$A9aVLpj$;t7wk|z0lhetB z5Y)Ojm;nMkmzrBmzvYsCF3=hLDV9O?GBe97v8}PX)U|AG!3sQvEpQS}+VH$plxH8F zcRA%{49j!#x|zfCI9~3sJTJ#{4A0|u(y+W@jz`Ze(S4@!SIqI&B5%hyErG_{``Cw)VGO{lg4`Q*!zPpj5VzwWy0D%R27^vG%^WO0wYU5eQNh zFZ4NsWV`TC*2@n+sW9%GhD>ny~c2yV5X44Y`Wup3jOvLC@BW7|T;?ljVO!GAU`CeShA0s?m@aNtb1 zfQUGgHW1=Z?!_G2@l5Mvgjp1CUPtlfEDC#g=uEtDKdUwxOA;>_x7~bsl4Pz(mdsw` zz@*h|uXW~0(^98RNin5eHzmc0;BM12`)RKy*zGg-B&07q^R)DPWKX&wG0!PtQYM2IZkXmFv^`I)*6| z*p!foO{qv^Q;HMVlpG_Q(t|dWO)NQ-$dW4(STfR_Hp*}=!9v_UsU2Fo~R zsTj{J7&A-OM86o{%&8;tX`BW?i_60RlVZGE^m!b!R!m^lV)Q*Lxo1X7B1;L4XC}}i z`@aUa8R*nMeOfPPFUmRH%ZTn%#>rEX(8tN>qm@m$ozJTFWdFpq#KUHwWHcf8Ke&^U zdN2(fS?y4Cr}2Vr0Dsl5NXM)fvX6|$O~%B9<9?O&fian-VSo%q{DxX4;M62GH8g=u z#Y>=5i^sF6;B{)24bnpy?#VVYv!UDux!6!<%bMJyfGCY|A?iPj`?P$*W^BrQ^bNO` zp3G%yW=Si?`Kb<;F?I#Yufu(=sN+TacHGNF`lpEBrz@xQZe96=Q^v7VaxCnWz?7a; z|H*5KF7f#~a5}SaW&%0{`Pi*6dJPi{Y{ELE&56t$Nc1PHHD(%UTyiF`l&o>y2{dPB z!WIiU7v(bcBJS;=Ly*hK3z%wVsgO}BWRwayq-I(D2Ac^od4kytZEPIjkPvVYRCNh@ zlw)F+lP3Ck0R^ycbV+Mes1M1eR3X0^ciSlCgiF3TU7DYgMv2K33|&@4X)i&S9T7*

S=Hui?__x{CUZ!MQk~+jw3M16UHSE9VDhh|`f>z;|u9 z8s~du{%ShYQ_q4~Jb$PAG>p@<3`B2^0 zkaq+=TH4y7ybm`pW_Myv>hi9`bG8P`)6rYnfIQIi>hd-tuLOAwr9wt7c8w0R@WBP*VfVl%jXRF`MDRb zyBd#r9O8+P!?DV>*jc*V=~}!xbH2j?=j>%*6`c^vo|umxiuKi1I4deZ-(O#cMb0r4 z8>C<4aym=AF6Zj`Sw#K}UA^M^m7Bn6t+NvBt_-g$*XRUCC$!jE;sl|ha80OsU46wS zwYCEfwu+ZIIPd3Z)K~b*YO1gEIm^p^SXB8lspHUGyn1D*EacNunlplu_Hga%QJFh} zin}ftDyu8^eLX7kMo=ljZz|UuhlKN(?qrUj;DuU{+i@HsqsSy{1eFy&e|=z#LFZ#N z3XSX$G>WThzos;DMpCHv*Vojq-*lY594ioZ?g%QY>F0v0L&u{sN^kBOmtP%pI!2!@ zUf1G6X#DsI`Cu7Ea zPUuUh4PtfJoLOGF9BlrA*InuN`6|9P0qW&1EcxqwAxD1tN`$LNYkjro2aL%uj>v%N z@UtA0k)OV~To=nm# zDhsLRHOP&va~^IUt>@NmJazU&L5hUxQ7YUQdPQs5JO;`(XlTo}ixA#zV7?UE47}*kr2jTpTv% zmj#2>>+2j}fgqp4LzC%p_WEG$QMz~xHcQGc$BRn76F0TkDOP_-DM_n!OcQ#nHekUO z2#0)J-B*_ds>{~Z_zHc2>J8}X248UGD3TL1`shD8X~<8}{EabcAJ?<^vqlGsR0q8% zu2;KoOh<$rSG}Ent=1A4QF2H`Z7jW_52JWYF7lXYJ`a^~*NG)tL|-&&w4!(e^?qL< zR6UwdKnFr+8CpFz>i7Jj};mICmYLwtWgjVA9IiD)aAx;O)B=`G{F$O$?>XCt0 zdzC#%GvNDXNIEo%-wZicJV?(M8WK0ja8z?l({)s3jL_;8p}@DvJc(%cBQ<6LgV)=Z{V0g6~2 z@P&K@H82^=%ix5a&}OLX#D8f`C2g!6qy1xM(l^vzk3By~hWNxCbUO1%ltX4;EPg|4 zYYrE`c4(*Su)0>L@6S1;Va|jkJPGbkkbm^&|7KpOjlX;YZD>ol+TZLP$9%PynrKuh zIq*DgFqX9(kAyW{(?!MF`PP32SLp5AUxO|;s_K*-9NERIM;lEso0hx5J&pd*{2E0) z2nn40^&92{jh7;FT5j`_oKRl@qbi^HKBd?q>!57xFq-EgaC zEQitZ{QPv4wB)P73Jxz*w5+kmE@0@vaj_(6h(lc$<~tp^5*CzzFn$Jh@MyT}(R#KA}% zJpc-po}Zn`o=|h~W@n=sEAZ9S@RHjFKK(nU18RJEn7*92xKhm8AbU#W<2j)k^9Gm; zu>$Pq;EdFV;a;nw%t^A>Rjif#qXeFHHgdR`ffLPhSxxo2+6LZ{1N0=z6$lgs*B8@2 zk9wQZ{EVWKJxOtA$kk9C;x&9~74}_YerbvN`9mrHCPQ5+FXMww$0lr^s{MmIcwbrV zx^U%Kn|QT4pR~}4DC%Nv7aD`q6{R zwbt^@iTQyF>Z8{132Tz6pxu>L0S6r?5L1?e@Uyu_ChH}9!nQQo&eGSyHh@|@e*d^Z z`+987ckuJ1oAq-1N^NPf%)lsm>dUVgl;w#udU`p zuyiRz#J&KPO<#YW5CJmW>{L^JxU!~xIu5BN52<>~_O{G3d6WjKSC;?T096bE;;{P-2`;ix%- z-=CGz($3h-WAZC}(2V*`cpss(prN6-Dp0@CrT=KHl(LuLA2#}=vbqX99m!#AA*^(5 zIni50v08j&$1bE8bPmto(MCaO(C;g+uEc3-m0FFlSsIsk(o{+x+m(v8OKb21im}xN zD^@Kpb{Dw@`=!q*ov-$F0Ylvlbf#D8FC(_l>C(D-POlD!-Eg|_>Plt_al>l=;aK zg-^-*0~;7ix6szbAQA@v>8sycN5D+aY<2^S7y zJ5AWpj#->#gjZ4!4g??xoPw15@WSRkQ?aI@S{7?ba0f-AKClG-1$gxad_fYRKk<^V zFR;n$3*a#ZtWP<50V&|S&g^5UyDQlFx?E_oudD)@EGYq;sNNg7ea;{{;G-DJG2pa> zhC@a6RIdx*BUJLSg6!4=I{p&J=pHDj1afQ#xGkjbMc4ZR8kfslE0()F*;%T7s?UZ0 zw}h{W?|kv9Fu&d&k$zXC>HRml21FXKJFA!YE?BE?7HRGKpHz`f5aB=%WrSnEZNxp#j)1NK6;M81($eAIDbB_21K(Q0V$pdY%wehO zM5~L=aq#I0mxeS3-NCix1b8DKC62}CD5t`s_JC|HkeoNi;!F3LC{K@&zg5&W;juHf@I<{kNDNZ266VX56W<@YM8l8~l=Qtc%cmC%= zyjOb`?#7bg?{(Ar-uL59gI`YYcepbVr}uEbz`X|e^zQB=+!UwxZttOYZxN^Ww~ctA z`zgF#Ob16F~X>f)PvwoAlY0skuE z{eamv@CG^#z*}$=|IL7{xEl~BXq$p^@N5Ts0e25*b^~U93+)1jpc6OM^#YcOxF7JK zh<5`z(o`IR_fCah1pHROe%#v-CrI!3uR&Y-cm4I>0nqmVowyfXrouSlUc?F7PQ&}d zh~u{y*zZpV4aD{D`P2LVEx5NMpWt)2e}Oo?bNnN`b4=g*IPi_iX51vtt$=Uhegyo` zw=1RdAWz~A@VmH=Adc_#*^?9p{!@U-nQ9*i&YaKKYrshdY{8v&g$iH6yXiHE)Bil5 znyrqP18^;Fl6e*2UAT!R!MYqk;QImpjC&{IZv!@;3;7{V@L}8}{~o~4MErBWbI()r za{+5aoZuD_-wOB(5hvIo;`;#8&qq6;Pp}v_(I;3X;shVWO?*BAn4hc09RzVdg}MYk z7x@HdEl_cC0V5Yc=D^ts*p8cYmEh+h&hqf-%0hf2g7@I@J2PwrZZqQetr@lfw-s^v zNa#J>1D8U708=hh4{&g?a zzcXStg!uxI(RX0zcB4i|EvNrok3NmaC*NcT^6)BpBclA$@bTlqLzyVi=Qkt$R4Zeb zgTUKr0ZV&`v7hPE$!rx{$x7J@<^sGJsTFwLo#L2FcvOiWd#TxEZ>X)Q3oe?8o#6$+ z@+x0#S#S=1`65sstgj5s!Ab80Wx?9H8|KfnV~@vDX+oWQb+Ep!DE8c> zy>{!?e_o%o_wlr)uO9g{?I#WAUQsf0LVilsA9AkWm~dUsL$AD2zrOMs^Ni4@>wNDw zUsyNcj|YA_{+2uLGMnytDJQsgMPzMf#<@Rz;;M)07N<2$E8d-2yX@xg__}`h@yz$H z?Av_POKawzdGh8nyMF$d?Y64PhkyOiIc>_8KMq{`J@eHm9alMiH1EsZ-&wi;jm-w{ zvhn5#H(PUJ;Q0`4>J6l)5m(j0X56M7Jhh#API*vqCLO>s*S(T6#;E%Y6 z6Y?%I;bTbJ5*S$%L7HpK>ny}yp?mp7B_$&Aucny;YcohHJNtJI>}0#lyV7>8-Bq>A zzpG(a`yV`dh*wwx3$gZAU%C7!h1H0IL=KHMorQMftpM7`6 z?#$iZ-6gx%?ylPH-`%jgWq0fDw%zT!5A5#VePnmfZe@4>?t$H`#oS_TNo%pUWVB?q zI9j9@Z%awb+Lo#oe@jD4W6QRd=9cX(J6c*=T3gy$+FK5^bhjL7>1k0~`dbEC*dFs9 z>z=ed_B|PUGWX=}aqN-yc=wd-S-YodkAF|Yp2j`f_B8L=zGugtmOZU|+V(i^m+r5+ z-+#aP0s8|P541jT;DPQ3jy%xw0DYGV+Xka$-C^I6xx=x;yJPK+*1LP|Hs6zZPsu%v i_q5(~ Date: Fri, 8 Dec 2023 17:17:37 -0600 Subject: [PATCH 233/365] Calculate angular momentum when needed --- .../calcTorqueBasedModeledValues.m | 16 +++++++++++---- .../initializeMexOrMatlabParallelFunctions.m | 2 +- src/core/mex/inverseDynamics.m | 20 +++++++++++++------ .../parse/parseTreatmentOptimizationInputs.m | 5 +++++ 4 files changed, 32 insertions(+), 11 deletions(-) diff --git a/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m b/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m index a28f3d21a..0aecc8dd3 100644 --- a/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m +++ b/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m @@ -35,10 +35,18 @@ [appliedLoads, modeledValues] = setupAppliedLoads(values, inputs, ... modeledValues); modeledValues.markerPositions = calcTrackedMarkerPositions(values, inputs); -modeledValues.inverseDynamicsMoments = inverseDynamics(values.time, ... - values.positions, values.velocities, ... - values.accelerations, inputs.coordinateNames, appliedLoads, ... - inputs.mexModel); +if valueOrAlternate(inputs, 'calculateAngularMomentum', false) + [modeledValues.inverseDynamicsMoments, ... + modeledValues.angularMomentum] = inverseDynamics(values.time, ... + values.positions, values.velocities, ... + values.accelerations, inputs.coordinateNames, appliedLoads, ... + inputs.mexModel); +else + modeledValues.inverseDynamicsMoments = inverseDynamics(values.time, ... + values.positions, values.velocities, ... + values.accelerations, inputs.coordinateNames, appliedLoads, ... + inputs.mexModel); +end end function [appliedLoads, modeledValues] = setupAppliedLoads(values, ... diff --git a/src/core/mex/initializeMexOrMatlabParallelFunctions.m b/src/core/mex/initializeMexOrMatlabParallelFunctions.m index 9be9ddd71..d7a990814 100644 --- a/src/core/mex/initializeMexOrMatlabParallelFunctions.m +++ b/src/core/mex/initializeMexOrMatlabParallelFunctions.m @@ -32,7 +32,7 @@ function initializeMexOrMatlabParallelFunctions(modelFile) if isequal(mexext, 'mexw64') pointKinematicsMexWindows(modelFile); - inverseDynamicsMexWindows(modelFile); + inverseDynamicsAngularMomentumMexWindows(modelFile); end clear inverseDynamicsMatlabParallel clear pointKinematicsMatlabParallel diff --git a/src/core/mex/inverseDynamics.m b/src/core/mex/inverseDynamics.m index cb77cf3fe..c3061891e 100644 --- a/src/core/mex/inverseDynamics.m +++ b/src/core/mex/inverseDynamics.m @@ -29,13 +29,21 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function inverseDynamicsMoments = inverseDynamics(time, jointAngles, ... - jointVelocities, jointAccelerations, coordinateLabels, appliedLoads, ... - modelName) +function [inverseDynamicsMoments, angularMomentum] = inverseDynamics( ... + time, jointAngles, jointVelocities, jointAccelerations, ... + coordinateLabels, appliedLoads, modelName) if isequal(mexext, 'mexw64') - inverseDynamicsMoments = inverseDynamicsMexWindows(time, jointAngles, ... - jointVelocities, jointAccelerations, coordinateLabels, ... - appliedLoads); + if nargout == 1 + inverseDynamicsMoments = ... + inverseDynamicsAngularMomentumMexWindows(time, jointAngles, ... + jointVelocities, jointAccelerations, coordinateLabels, ... + appliedLoads); + else + [inverseDynamicsMoments, angularMomentum] = ... + inverseDynamicsAngularMomentumMexWindows(time, jointAngles, ... + jointVelocities, jointAccelerations, coordinateLabels, ... + appliedLoads); + end else inverseDynamicsMoments = inverseDynamicsMatlabParallel(time, ... jointAngles, jointVelocities, jointAccelerations, coordinateLabels, ... diff --git a/src/core/parse/parseTreatmentOptimizationInputs.m b/src/core/parse/parseTreatmentOptimizationInputs.m index f3a8c6380..43a7a41db 100644 --- a/src/core/parse/parseTreatmentOptimizationInputs.m +++ b/src/core/parse/parseTreatmentOptimizationInputs.m @@ -37,6 +37,11 @@ inputs = parseOptimalControlSolverSettings(tree, inputs); inputs.costTerms = parseRcnlCostTermSetHelper( ... getFieldByNameOrError(tree, 'RCNLCostTermSet')); +if isequal(mexext, 'mexw64') + inputs.calculateAngularMomentum = ... + any(strcmp(cellfun(@(term) term.type, inputs.costTerms, ... + 'UniformOutput', false), {'angular_momentum_minimization'})); +end [inputs.path, inputs.terminal] = parseRcnlConstraintTermSetHelper( ... getFieldByNameOrError(tree, 'RCNLConstraintTermSet'), ... inputs.controllerType, inputs.toolName); From 2733f5323b6f48b886b7354dde8dff0349039392 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Fri, 8 Dec 2023 17:37:01 -0600 Subject: [PATCH 234/365] Whole body angular momentum calculation for Windows --- .../calcMinimizingAngularMomentumIntegrand.m | 44 +++++++++++++++++++ .../generateCostTermStruct.m | 7 +++ 2 files changed, 51 insertions(+) create mode 100644 src/TreatmentOptimization/IntegrandTerms/calcMinimizingAngularMomentumIntegrand.m diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingAngularMomentumIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingAngularMomentumIntegrand.m new file mode 100644 index 000000000..e9aa51825 --- /dev/null +++ b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingAngularMomentumIntegrand.m @@ -0,0 +1,44 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function minimizes the whole body angular momentum for the specified +% axis. +% +% (2D matrix, struct, Array of string) -> (Array of number) +% + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Spencer Williams % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function cost = calcMinimizingAngularMomentumIntegrand( ... + modeledValues, time, costTerm) +normalizeByFinalTime = valueOrAlternate(costTerm, ... + "normalize_by_final_time", true); + +index = find(strcmpi(costTerm.axis, ["x" "y" "z"])); +assert(~isempty(index), "Angular momentum axis must be X, Y, or Z, but "... + + costTerm.axis + "was given."); + +cost = modeledValues.angularMomentum(:, index); +if normalizeByFinalTime + cost = cost / time(end); +end +end \ No newline at end of file diff --git a/src/TreatmentOptimization/generateCostTermStruct.m b/src/TreatmentOptimization/generateCostTermStruct.m index a142e60ae..71d508e07 100644 --- a/src/TreatmentOptimization/generateCostTermStruct.m +++ b/src/TreatmentOptimization/generateCostTermStruct.m @@ -94,6 +94,7 @@ "kinematic_symmetry" ... "walking_speed_goal" ... "external_torque_control_minimization" ... + "angular_momentum_minimization" ... "user_defined", ... ]; otherwise @@ -364,6 +365,12 @@ auxdata, ... costTerm.coordinate); +costTermCalculations.angular_momentum_minimization = @(values, modeledValues, auxdata, costTerm) ... + calcMinimizingAngularMomentumIntegrand( ... + modeledValues, ... + values.time, ... + costTerm); + costTermCalculations.synergy_vector_tracking = @(values, modeledValues, auxdata, costTerm) ... calcTrackingSynergyVectorsDiscrete( ... values.synergyWeights, ... From 84cfee770fd9c7b869657ae245b868b9d0e82d05 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Fri, 8 Dec 2023 17:50:14 -0600 Subject: [PATCH 235/365] Support multiple axes in angular momentum cost term --- .../calcMinimizingAngularMomentumIntegrand.m | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingAngularMomentumIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingAngularMomentumIntegrand.m index e9aa51825..ebf7c808e 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingAngularMomentumIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingAngularMomentumIntegrand.m @@ -1,7 +1,7 @@ % This function is part of the NMSM Pipeline, see file for full license. % % This function minimizes the whole body angular momentum for the specified -% axis. +% axes. % % (2D matrix, struct, Array of string) -> (Array of number) % @@ -32,12 +32,19 @@ modeledValues, time, costTerm) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); +axes = ["x" "y" "z"]; -index = find(strcmpi(costTerm.axis, ["x" "y" "z"])); -assert(~isempty(index), "Angular momentum axis must be X, Y, or Z, but "... - + costTerm.axis + "was given."); +indices = []; +for substring = 1:3 + if contains(costTerm.axes, axes(substring), 'IgnoreCase', true) + indices(end+1) = substring; + end +end + +assert(~isempty(indices), "Angular momentum axes must include X, Y " + ... + "or Z, but " + costTerm.axes + "was given."); -cost = modeledValues.angularMomentum(:, index); +cost = modeledValues.angularMomentum(:, indices); if normalizeByFinalTime cost = cost / time(end); end From 6abc2a0b3dbc303c3703537b900d3d077962b3ab Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Fri, 8 Dec 2023 18:10:06 -0600 Subject: [PATCH 236/365] Matlab implementation of angular momentum --- src/core/mex/inverseDynamics.m | 13 +++-- src/core/mex/inverseDynamicsMatlabParallel.m | 51 ++++++++++++++++---- 2 files changed, 52 insertions(+), 12 deletions(-) diff --git a/src/core/mex/inverseDynamics.m b/src/core/mex/inverseDynamics.m index c3061891e..2843870b1 100644 --- a/src/core/mex/inverseDynamics.m +++ b/src/core/mex/inverseDynamics.m @@ -45,8 +45,15 @@ appliedLoads); end else - inverseDynamicsMoments = inverseDynamicsMatlabParallel(time, ... - jointAngles, jointVelocities, jointAccelerations, coordinateLabels, ... - appliedLoads, modelName); + if nargout == 1 + inverseDynamicsMoments = inverseDynamicsMatlabParallel(time, ... + jointAngles, jointVelocities, jointAccelerations, ... + coordinateLabels, appliedLoads, modelName); + else + [inverseDynamicsMoments, angularMomentum] = ... + inverseDynamicsMatlabParallel(time, ... + jointAngles, jointVelocities, jointAccelerations, ... + coordinateLabels, appliedLoads, modelName); + end end end diff --git a/src/core/mex/inverseDynamicsMatlabParallel.m b/src/core/mex/inverseDynamicsMatlabParallel.m index 0fe2dfea4..e72a7b2b9 100644 --- a/src/core/mex/inverseDynamicsMatlabParallel.m +++ b/src/core/mex/inverseDynamicsMatlabParallel.m @@ -30,9 +30,9 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function inverseDynamicsMoments = inverseDynamicsMatlabParallel(time, ... - jointAngles, jointVelocities, jointAccelerations, coordinateLabels, ... - appliedLoads, modelName) +function [inverseDynamicsMoments, angularMomentum] = ... + inverseDynamicsMatlabParallel(time, jointAngles, jointVelocities, ... + jointAccelerations, coordinateLabels, appliedLoads, modelName) % Get the number of coords and markers numPts = size(time,1); @@ -41,19 +41,40 @@ % Split time points into parallel problems numWorkers = gcp().NumWorkers; inverseDynamicsJobs = cell(1, numWorkers); -parfor worker = 1:numWorkers - inverseDynamicsJobs{worker} = idWorkerHelper(modelName, numPts, ... - numControls, numCoords, numWorkers, ... - time, jointAngles, jointVelocities, jointAccelerations, ... - coordinateLabels,appliedLoads,worker); +if nargout > 1 + angularMomentumJobs = cell(1, numWorkers); + parfor worker = 1:numWorkers + [inverseDynamicsJobs{worker}, angularMomentumJobs{worker}] = ... + idWorkerHelper(modelName, numPts, ... + numControls, numCoords, numWorkers, ... + time, jointAngles, jointVelocities, jointAccelerations, ... + coordinateLabels,appliedLoads,worker); + end +else + parfor worker = 1:numWorkers + inverseDynamicsJobs{worker} = idWorkerHelper(modelName, numPts, ... + numControls, numCoords, numWorkers, ... + time, jointAngles, jointVelocities, jointAccelerations, ... + coordinateLabels,appliedLoads,worker); + end end + inverseDynamicsMoments = inverseDynamicsJobs{1}; for job = 2 : numWorkers inverseDynamicsMoments = cat(1, inverseDynamicsMoments, ... inverseDynamicsJobs{job}); end +if nargout > 1 + angularMomentum = angularMomentumJobs{1}; + for job = 2 : numWorkers + angularMomentum = cat(1, angularMomentum, ... + angularMomentumJobs{job}); + end +end end -function inverseDynamicsJobs = idWorkerHelper(modelName, numPts, ... + +function [inverseDynamicsJobs, angularMomentumJobs] = ... + idWorkerHelper(modelName, numPts, ... numControls, numCoords, numWorkers, time, jointAngles, ... jointVelocities, jointAccelerations, coordinateLabels, appliedLoads, ... worker) @@ -68,6 +89,7 @@ inverseDynamicsSolver = InverseDynamicsSolver(osimModel); end inverseDynamicsJobs = []; +angularMomentumJobs = []; start = round((numPts/numWorkers) * (worker-1)) + 1; stop = round((numPts/numWorkers) * (worker)); for j = start : stop @@ -82,6 +104,17 @@ end end osimModel.realizeVelocity(osimState); + + % Whole body angular momentum + if nargout > 1 + momentum = osimModel.getMatterSubsystem() ... + .calcSystemCentralMomentum(osimState); + angularMomentum = momentum.get(0); + for i = 0 : 2 + angularMomentumJobs(j-start + 1, i + 1) = angularMomentum.get(i); + end + end + accelsTempVec = zeros(1, osimState.getNQ()); for i=1:osimState.getNQ StateQ = osimState.getQ.get(i-1); From 20a5377af8b3699f4d9e5f95c96f6c28b1d8a749 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Fri, 8 Dec 2023 18:16:34 -0600 Subject: [PATCH 237/365] Add error message for missing axes field --- .../IntegrandTerms/calcMinimizingAngularMomentumIntegrand.m | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingAngularMomentumIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingAngularMomentumIntegrand.m index ebf7c808e..44cb12043 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingAngularMomentumIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingAngularMomentumIntegrand.m @@ -34,6 +34,9 @@ "normalize_by_final_time", true); axes = ["x" "y" "z"]; +assert(isfield(costTerm, 'axes'), "Angular momentum minimization " + ... + "requires an 'axes' field in the cost term settings.") + indices = []; for substring = 1:3 if contains(costTerm.axes, axes(substring), 'IgnoreCase', true) From 7be6410cf9c8bf348829f98bebed9a999981d564 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Fri, 8 Dec 2023 18:19:23 -0600 Subject: [PATCH 238/365] Revert to one axis to work with allowable error --- .../calcMinimizingAngularMomentumIntegrand.m | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingAngularMomentumIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingAngularMomentumIntegrand.m index 44cb12043..536a7ae84 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingAngularMomentumIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingAngularMomentumIntegrand.m @@ -1,7 +1,7 @@ % This function is part of the NMSM Pipeline, see file for full license. % % This function minimizes the whole body angular momentum for the specified -% axes. +% axis. % % (2D matrix, struct, Array of string) -> (Array of number) % @@ -32,22 +32,15 @@ modeledValues, time, costTerm) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); -axes = ["x" "y" "z"]; -assert(isfield(costTerm, 'axes'), "Angular momentum minimization " + ... - "requires an 'axes' field in the cost term settings.") +assert(isfield(costTerm, 'axis'), "Angular momentum minimization " + ... + "requires an 'axis' field in the cost term settings.") -indices = []; -for substring = 1:3 - if contains(costTerm.axes, axes(substring), 'IgnoreCase', true) - indices(end+1) = substring; - end -end - -assert(~isempty(indices), "Angular momentum axes must include X, Y " + ... - "or Z, but " + costTerm.axes + "was given."); +index = find(strcmpi(costTerm.axis, ["x" "y" "z"])); +assert(~isempty(index), "Angular momentum axis must be X, Y, or Z, but "... + + costTerm.axis + "was given."); -cost = modeledValues.angularMomentum(:, indices); +cost = modeledValues.angularMomentum(:, index); if normalizeByFinalTime cost = cost / time(end); end From e30428bba0d7f3885d1302d3cbac67459b620b75 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Fri, 8 Dec 2023 18:21:41 -0600 Subject: [PATCH 239/365] Update axis mismatch error message --- .../IntegrandTerms/calcMinimizingAngularMomentumIntegrand.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingAngularMomentumIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingAngularMomentumIntegrand.m index 536a7ae84..8558b64f5 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingAngularMomentumIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingAngularMomentumIntegrand.m @@ -37,8 +37,8 @@ "requires an 'axis' field in the cost term settings.") index = find(strcmpi(costTerm.axis, ["x" "y" "z"])); -assert(~isempty(index), "Angular momentum axis must be X, Y, or Z, but "... - + costTerm.axis + "was given."); +assert(~isempty(index), "Angular momentum axis must be X, Y, or Z, " + ... + "but '" + costTerm.axis + "' was given."); cost = modeledValues.angularMomentum(:, index); if normalizeByFinalTime From 10f1a329c5868cb0724f2f818d8127fdb4a4408c Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 11 Dec 2023 17:06:42 -0600 Subject: [PATCH 240/365] Fix redundant muscle names list --- src/SurrogateModelCreation/SurrogateModelCreation.m | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/SurrogateModelCreation/SurrogateModelCreation.m b/src/SurrogateModelCreation/SurrogateModelCreation.m index efaf3c3a5..becc1c4f8 100644 --- a/src/SurrogateModelCreation/SurrogateModelCreation.m +++ b/src/SurrogateModelCreation/SurrogateModelCreation.m @@ -48,8 +48,10 @@ tree.trial_prefixes = inputs.trialName; prefixes = findPrefixes(tree.trial_prefixes, inputs.dataDirectory); -inputs.muscleNames = getMusclesFromCoordinates(inputs.model, ... - inputs.surrogateModelCoordinateNames); +if ~isfield(inputs, 'muscleNames') + inputs.muscleNames = getMusclesFromCoordinates(inputs.model, ... + inputs.surrogateModelCoordinateNames); +end inputs.numMuscles = length(inputs.muscleNames); inverseKinematicsFileNames = findFileListFromPrefixList(fullfile( ... From 819a6d6e1db416b06ff9c32f7719dbe02707d8c8 Mon Sep 17 00:00:00 2001 From: RobSalati Date: Wed, 13 Dec 2023 00:48:06 -0500 Subject: [PATCH 241/365] File organization for plotting MTP Created folders for saving data and analysis. --- ...d.xml => Dj1elW6LFUKwUlOO79e3l_KFnqId.xml} | 0 .../Dj1elW6LFUKwUlOO79e3l_KFnqIp.xml | 2 + .../H2ZBtSUaEqrhzwn-V7gzQTP8xkMd.xml} | 0 .../H2ZBtSUaEqrhzwn-V7gzQTP8xkMp.xml | 2 + .../ZLU9mS9w-LxpUpNVAq7gzeLsGNMd.xml} | 0 .../ZLU9mS9w-LxpUpNVAq7gzeLsGNMp.xml | 2 + .../xxEkn1cXg5yjGbZKOOkJj6kXUGwp.xml | 2 - .../6Grat0VHXlfim03pMMC5jcjAsMsd.xml | 2 + .../6Grat0VHXlfim03pMMC5jcjAsMsp.xml | 2 + .../KZjwdxWx7CTdYd34eqZ_RTkgI7gp.xml | 2 - .../PkiIZsXBNV2p5PmLAZbrlCKNO1wp.xml | 2 - .../SM-7XD5VEsEA2ReW_Gcx0KdVR4Ed.xml | 2 + .../SM-7XD5VEsEA2ReW_Gcx0KdVR4Ep.xml | 2 + .../0QLTlFQJ1pdHZic9CzK-fwvy8JEd.xml} | 0 .../0QLTlFQJ1pdHZic9CzK-fwvy8JEp.xml | 2 + .../9V40WFzIKXJGGp8oEqob15GAlwgd.xml | 6 + .../9V40WFzIKXJGGp8oEqob15GAlwgp.xml | 2 + .../JNQFPhtRfUmBhRG2yjt-nzPe-7od.xml | 6 + .../JNQFPhtRfUmBhRG2yjt-nzPe-7op.xml | 2 + .../UczN0iNj0jtudo2nI2gnIWNiIesd.xml | 6 + .../UczN0iNj0jtudo2nI2gnIWNiIesp.xml | 2 + .../WMyozuhOFw24HLR_4y6r67mZpicd.xml | 2 + .../WMyozuhOFw24HLR_4y6r67mZpicp.xml | 2 + .../bhlRi9i4fJ6RxGucF8nbbN9vdz4d.xml | 6 + .../bhlRi9i4fJ6RxGucF8nbbN9vdz4p.xml | 2 + .../iE5EXX33uQ1jkGTVTBI1zz34bFMd.xml | 6 + .../iE5EXX33uQ1jkGTVTBI1zz34bFMp.xml} | 0 .../mBZmtBM2h2hK_XmPgKPYO7mRtqYd.xml | 6 + .../mBZmtBM2h2hK_XmPgKPYO7mRtqYp.xml | 2 + .../mCKHnf8EjNZ4xKZB_R8pR0AMbNUd.xml | 6 + .../mCKHnf8EjNZ4xKZB_R8pR0AMbNUp.xml | 2 + .../tUk4baD64Ncsu-t2EDryess8gEwd.xml | 6 + .../tUk4baD64Ncsu-t2EDryess8gEwp.xml | 2 + ...eTendonLengthInitializationModeledValues.m | 3 +- .../getMaxIsometricForce.m | 3 +- .../Analysis/plotHillTypeMuscleParams.m | 44 +++--- .../Analysis/plotJointMoments.m | 144 +++++++++--------- .../{plotMtpData.m => plotMtpData_remove.m} | 2 +- .../plotMuscleExcitationsAndActivations.m | 32 ++-- .../Analysis/plotNormalizedFiberLengths.m | 62 ++++---- .../Analysis/plotPassiveForceCurves.m | 21 +-- .../Analysis/plotPassiveMomentCurves.m | 29 ++-- .../Analysis/printJointMomentMatchingError.m | 23 +++ ...scleTendonPersonalizationResults_remove.m} | 2 +- .../MuscleTendonPersonalizationTool.m | 27 +--- .../Saving/getMtpResultsToSave.m | 37 +++++ .../Saving/saveActivationAndExcitationData.m | 15 ++ .../Saving/saveJointMomentData.m | 15 ++ .../Saving/saveMuscleModelParameters.m | 14 ++ .../saveMuscleTendonOptimizationParamsOld.m} | 52 +++---- ...aveMuscleTendonOptimizationParams_remove.m | 83 ++++++++++ .../saveMuscleTendonPersonalizationResults.m | 49 +++--- .../Saving/savePassiveForceData.m | 15 ++ .../Saving/savePassiveMomentData.m | 26 ++++ .../Saving/writeMtpDataToSto.m | 9 ++ ...reportMuscleTendonPersonalizationResults.m | 39 ----- 56 files changed, 528 insertions(+), 306 deletions(-) rename resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/{xxEkn1cXg5yjGbZKOOkJj6kXUGwd.xml => Dj1elW6LFUKwUlOO79e3l_KFnqId.xml} (100%) create mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Dj1elW6LFUKwUlOO79e3l_KFnqIp.xml rename resources/project/{RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/B3qliqlfxQ02trRoxA9bnJZ8sxYd.xml => 3S1lLYGLIjnYoeqS66xjbiTYaGo/H2ZBtSUaEqrhzwn-V7gzQTP8xkMd.xml} (100%) create mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/H2ZBtSUaEqrhzwn-V7gzQTP8xkMp.xml rename resources/project/{RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/KZjwdxWx7CTdYd34eqZ_RTkgI7gd.xml => 3S1lLYGLIjnYoeqS66xjbiTYaGo/ZLU9mS9w-LxpUpNVAq7gzeLsGNMd.xml} (100%) create mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/ZLU9mS9w-LxpUpNVAq7gzeLsGNMp.xml delete mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/xxEkn1cXg5yjGbZKOOkJj6kXUGwp.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/6Grat0VHXlfim03pMMC5jcjAsMsd.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/6Grat0VHXlfim03pMMC5jcjAsMsp.xml delete mode 100644 resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/KZjwdxWx7CTdYd34eqZ_RTkgI7gp.xml delete mode 100644 resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/PkiIZsXBNV2p5PmLAZbrlCKNO1wp.xml create mode 100644 resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/SM-7XD5VEsEA2ReW_Gcx0KdVR4Ed.xml create mode 100644 resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/SM-7XD5VEsEA2ReW_Gcx0KdVR4Ep.xml rename resources/project/{RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/PkiIZsXBNV2p5PmLAZbrlCKNO1wd.xml => SM-7XD5VEsEA2ReW_Gcx0KdVR4E/0QLTlFQJ1pdHZic9CzK-fwvy8JEd.xml} (100%) create mode 100644 resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/0QLTlFQJ1pdHZic9CzK-fwvy8JEp.xml create mode 100644 resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/9V40WFzIKXJGGp8oEqob15GAlwgd.xml create mode 100644 resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/9V40WFzIKXJGGp8oEqob15GAlwgp.xml create mode 100644 resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/JNQFPhtRfUmBhRG2yjt-nzPe-7od.xml create mode 100644 resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/JNQFPhtRfUmBhRG2yjt-nzPe-7op.xml create mode 100644 resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/UczN0iNj0jtudo2nI2gnIWNiIesd.xml create mode 100644 resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/UczN0iNj0jtudo2nI2gnIWNiIesp.xml create mode 100644 resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/WMyozuhOFw24HLR_4y6r67mZpicd.xml create mode 100644 resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/WMyozuhOFw24HLR_4y6r67mZpicp.xml create mode 100644 resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/bhlRi9i4fJ6RxGucF8nbbN9vdz4d.xml create mode 100644 resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/bhlRi9i4fJ6RxGucF8nbbN9vdz4p.xml create mode 100644 resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/iE5EXX33uQ1jkGTVTBI1zz34bFMd.xml rename resources/project/{RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/B3qliqlfxQ02trRoxA9bnJZ8sxYp.xml => SM-7XD5VEsEA2ReW_Gcx0KdVR4E/iE5EXX33uQ1jkGTVTBI1zz34bFMp.xml} (100%) create mode 100644 resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/mBZmtBM2h2hK_XmPgKPYO7mRtqYd.xml create mode 100644 resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/mBZmtBM2h2hK_XmPgKPYO7mRtqYp.xml create mode 100644 resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/mCKHnf8EjNZ4xKZB_R8pR0AMbNUd.xml create mode 100644 resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/mCKHnf8EjNZ4xKZB_R8pR0AMbNUp.xml create mode 100644 resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/tUk4baD64Ncsu-t2EDryess8gEwd.xml create mode 100644 resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/tUk4baD64Ncsu-t2EDryess8gEwp.xml rename src/MuscleTendonPersonalization/Analysis/{plotMtpData.m => plotMtpData_remove.m} (95%) create mode 100644 src/MuscleTendonPersonalization/Analysis/printJointMomentMatchingError.m rename src/MuscleTendonPersonalization/{reportMuscleTendonPersonalizationResultsOld.m => Analysis/reportMuscleTendonPersonalizationResults_remove.m} (99%) create mode 100644 src/MuscleTendonPersonalization/Saving/getMtpResultsToSave.m create mode 100644 src/MuscleTendonPersonalization/Saving/saveActivationAndExcitationData.m create mode 100644 src/MuscleTendonPersonalization/Saving/saveJointMomentData.m create mode 100644 src/MuscleTendonPersonalization/Saving/saveMuscleModelParameters.m rename src/MuscleTendonPersonalization/{saveMuscleTendonOptimizationParams.m => Saving/saveMuscleTendonOptimizationParamsOld.m} (81%) create mode 100644 src/MuscleTendonPersonalization/Saving/saveMuscleTendonOptimizationParams_remove.m rename src/MuscleTendonPersonalization/{ => Saving}/saveMuscleTendonPersonalizationResults.m (62%) create mode 100644 src/MuscleTendonPersonalization/Saving/savePassiveForceData.m create mode 100644 src/MuscleTendonPersonalization/Saving/savePassiveMomentData.m create mode 100644 src/MuscleTendonPersonalization/Saving/writeMtpDataToSto.m delete mode 100644 src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResults.m diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/xxEkn1cXg5yjGbZKOOkJj6kXUGwd.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Dj1elW6LFUKwUlOO79e3l_KFnqId.xml similarity index 100% rename from resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/xxEkn1cXg5yjGbZKOOkJj6kXUGwd.xml rename to resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Dj1elW6LFUKwUlOO79e3l_KFnqId.xml diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Dj1elW6LFUKwUlOO79e3l_KFnqIp.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Dj1elW6LFUKwUlOO79e3l_KFnqIp.xml new file mode 100644 index 000000000..c5cf2ca28 --- /dev/null +++ b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Dj1elW6LFUKwUlOO79e3l_KFnqIp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/B3qliqlfxQ02trRoxA9bnJZ8sxYd.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/H2ZBtSUaEqrhzwn-V7gzQTP8xkMd.xml similarity index 100% rename from resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/B3qliqlfxQ02trRoxA9bnJZ8sxYd.xml rename to resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/H2ZBtSUaEqrhzwn-V7gzQTP8xkMd.xml diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/H2ZBtSUaEqrhzwn-V7gzQTP8xkMp.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/H2ZBtSUaEqrhzwn-V7gzQTP8xkMp.xml new file mode 100644 index 000000000..16bf0bc90 --- /dev/null +++ b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/H2ZBtSUaEqrhzwn-V7gzQTP8xkMp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/KZjwdxWx7CTdYd34eqZ_RTkgI7gd.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/ZLU9mS9w-LxpUpNVAq7gzeLsGNMd.xml similarity index 100% rename from resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/KZjwdxWx7CTdYd34eqZ_RTkgI7gd.xml rename to resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/ZLU9mS9w-LxpUpNVAq7gzeLsGNMd.xml diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/ZLU9mS9w-LxpUpNVAq7gzeLsGNMp.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/ZLU9mS9w-LxpUpNVAq7gzeLsGNMp.xml new file mode 100644 index 000000000..b60432870 --- /dev/null +++ b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/ZLU9mS9w-LxpUpNVAq7gzeLsGNMp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/xxEkn1cXg5yjGbZKOOkJj6kXUGwp.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/xxEkn1cXg5yjGbZKOOkJj6kXUGwp.xml deleted file mode 100644 index bfda1cc83..000000000 --- a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/xxEkn1cXg5yjGbZKOOkJj6kXUGwp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/6Grat0VHXlfim03pMMC5jcjAsMsd.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/6Grat0VHXlfim03pMMC5jcjAsMsd.xml new file mode 100644 index 000000000..ad77bec5f --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/6Grat0VHXlfim03pMMC5jcjAsMsd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/6Grat0VHXlfim03pMMC5jcjAsMsp.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/6Grat0VHXlfim03pMMC5jcjAsMsp.xml new file mode 100644 index 000000000..57f905db0 --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/6Grat0VHXlfim03pMMC5jcjAsMsp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/KZjwdxWx7CTdYd34eqZ_RTkgI7gp.xml b/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/KZjwdxWx7CTdYd34eqZ_RTkgI7gp.xml deleted file mode 100644 index 9a0c6de9d..000000000 --- a/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/KZjwdxWx7CTdYd34eqZ_RTkgI7gp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/PkiIZsXBNV2p5PmLAZbrlCKNO1wp.xml b/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/PkiIZsXBNV2p5PmLAZbrlCKNO1wp.xml deleted file mode 100644 index 1d012a5c3..000000000 --- a/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/PkiIZsXBNV2p5PmLAZbrlCKNO1wp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/SM-7XD5VEsEA2ReW_Gcx0KdVR4Ed.xml b/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/SM-7XD5VEsEA2ReW_Gcx0KdVR4Ed.xml new file mode 100644 index 000000000..a75f7a81b --- /dev/null +++ b/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/SM-7XD5VEsEA2ReW_Gcx0KdVR4Ed.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/SM-7XD5VEsEA2ReW_Gcx0KdVR4Ep.xml b/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/SM-7XD5VEsEA2ReW_Gcx0KdVR4Ep.xml new file mode 100644 index 000000000..290fc4165 --- /dev/null +++ b/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/SM-7XD5VEsEA2ReW_Gcx0KdVR4Ep.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/PkiIZsXBNV2p5PmLAZbrlCKNO1wd.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/0QLTlFQJ1pdHZic9CzK-fwvy8JEd.xml similarity index 100% rename from resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/PkiIZsXBNV2p5PmLAZbrlCKNO1wd.xml rename to resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/0QLTlFQJ1pdHZic9CzK-fwvy8JEd.xml diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/0QLTlFQJ1pdHZic9CzK-fwvy8JEp.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/0QLTlFQJ1pdHZic9CzK-fwvy8JEp.xml new file mode 100644 index 000000000..371fc4abb --- /dev/null +++ b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/0QLTlFQJ1pdHZic9CzK-fwvy8JEp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/9V40WFzIKXJGGp8oEqob15GAlwgd.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/9V40WFzIKXJGGp8oEqob15GAlwgd.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/9V40WFzIKXJGGp8oEqob15GAlwgd.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/9V40WFzIKXJGGp8oEqob15GAlwgp.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/9V40WFzIKXJGGp8oEqob15GAlwgp.xml new file mode 100644 index 000000000..7225b6797 --- /dev/null +++ b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/9V40WFzIKXJGGp8oEqob15GAlwgp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/JNQFPhtRfUmBhRG2yjt-nzPe-7od.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/JNQFPhtRfUmBhRG2yjt-nzPe-7od.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/JNQFPhtRfUmBhRG2yjt-nzPe-7od.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/JNQFPhtRfUmBhRG2yjt-nzPe-7op.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/JNQFPhtRfUmBhRG2yjt-nzPe-7op.xml new file mode 100644 index 000000000..a2b074afc --- /dev/null +++ b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/JNQFPhtRfUmBhRG2yjt-nzPe-7op.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/UczN0iNj0jtudo2nI2gnIWNiIesd.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/UczN0iNj0jtudo2nI2gnIWNiIesd.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/UczN0iNj0jtudo2nI2gnIWNiIesd.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/UczN0iNj0jtudo2nI2gnIWNiIesp.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/UczN0iNj0jtudo2nI2gnIWNiIesp.xml new file mode 100644 index 000000000..c13bfe6f8 --- /dev/null +++ b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/UczN0iNj0jtudo2nI2gnIWNiIesp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/WMyozuhOFw24HLR_4y6r67mZpicd.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/WMyozuhOFw24HLR_4y6r67mZpicd.xml new file mode 100644 index 000000000..a75f7a81b --- /dev/null +++ b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/WMyozuhOFw24HLR_4y6r67mZpicd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/WMyozuhOFw24HLR_4y6r67mZpicp.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/WMyozuhOFw24HLR_4y6r67mZpicp.xml new file mode 100644 index 000000000..842de6ab3 --- /dev/null +++ b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/WMyozuhOFw24HLR_4y6r67mZpicp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/bhlRi9i4fJ6RxGucF8nbbN9vdz4d.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/bhlRi9i4fJ6RxGucF8nbbN9vdz4d.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/bhlRi9i4fJ6RxGucF8nbbN9vdz4d.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/bhlRi9i4fJ6RxGucF8nbbN9vdz4p.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/bhlRi9i4fJ6RxGucF8nbbN9vdz4p.xml new file mode 100644 index 000000000..2f0f10b7d --- /dev/null +++ b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/bhlRi9i4fJ6RxGucF8nbbN9vdz4p.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/iE5EXX33uQ1jkGTVTBI1zz34bFMd.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/iE5EXX33uQ1jkGTVTBI1zz34bFMd.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/iE5EXX33uQ1jkGTVTBI1zz34bFMd.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/B3qliqlfxQ02trRoxA9bnJZ8sxYp.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/iE5EXX33uQ1jkGTVTBI1zz34bFMp.xml similarity index 100% rename from resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/B3qliqlfxQ02trRoxA9bnJZ8sxYp.xml rename to resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/iE5EXX33uQ1jkGTVTBI1zz34bFMp.xml diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/mBZmtBM2h2hK_XmPgKPYO7mRtqYd.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/mBZmtBM2h2hK_XmPgKPYO7mRtqYd.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/mBZmtBM2h2hK_XmPgKPYO7mRtqYd.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/mBZmtBM2h2hK_XmPgKPYO7mRtqYp.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/mBZmtBM2h2hK_XmPgKPYO7mRtqYp.xml new file mode 100644 index 000000000..fd426fbd6 --- /dev/null +++ b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/mBZmtBM2h2hK_XmPgKPYO7mRtqYp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/mCKHnf8EjNZ4xKZB_R8pR0AMbNUd.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/mCKHnf8EjNZ4xKZB_R8pR0AMbNUd.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/mCKHnf8EjNZ4xKZB_R8pR0AMbNUd.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/mCKHnf8EjNZ4xKZB_R8pR0AMbNUp.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/mCKHnf8EjNZ4xKZB_R8pR0AMbNUp.xml new file mode 100644 index 000000000..803427be8 --- /dev/null +++ b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/mCKHnf8EjNZ4xKZB_R8pR0AMbNUp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/tUk4baD64Ncsu-t2EDryess8gEwd.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/tUk4baD64Ncsu-t2EDryess8gEwd.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/tUk4baD64Ncsu-t2EDryess8gEwd.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/tUk4baD64Ncsu-t2EDryess8gEwp.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/tUk4baD64Ncsu-t2EDryess8gEwp.xml new file mode 100644 index 000000000..965c6cb3d --- /dev/null +++ b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/tUk4baD64Ncsu-t2EDryess8gEwp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/MuscleTendonLengthInitialization/calcMuscleTendonLengthInitializationModeledValues.m b/src/MuscleTendonLengthInitialization/calcMuscleTendonLengthInitializationModeledValues.m index 3a77c65c5..d863249f0 100644 --- a/src/MuscleTendonLengthInitialization/calcMuscleTendonLengthInitializationModeledValues.m +++ b/src/MuscleTendonLengthInitialization/calcMuscleTendonLengthInitializationModeledValues.m @@ -51,5 +51,4 @@ experimentalData, modeledValues.maxIsometricForce, ... modeledValues.passiveNormalizedFiberLength); end -end - +end \ No newline at end of file diff --git a/src/MuscleTendonLengthInitialization/getMaxIsometricForce.m b/src/MuscleTendonLengthInitialization/getMaxIsometricForce.m index 7cf05a2bc..86cec8a8d 100644 --- a/src/MuscleTendonLengthInitialization/getMaxIsometricForce.m +++ b/src/MuscleTendonLengthInitialization/getMaxIsometricForce.m @@ -30,7 +30,6 @@ scaledOptimalFiberLength = experimentalData.optimalFiberLength .* ... values.optimalFiberLengthScaleFactors; - if experimentalData.optimizeIsometricMaxForce scaledMaximumMuscleStress = experimentalData.maximumMuscleStress .* ... values.maximumMuscleStressScaleFactor; @@ -40,4 +39,4 @@ else maxIsometricForce = experimentalData.maxIsometricForce; end -end +end \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/Analysis/plotHillTypeMuscleParams.m b/src/MuscleTendonPersonalization/Analysis/plotHillTypeMuscleParams.m index 1a6188a8f..39eb6e558 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotHillTypeMuscleParams.m +++ b/src/MuscleTendonPersonalization/Analysis/plotHillTypeMuscleParams.m @@ -1,25 +1,27 @@ function plotHillTypeMuscleParams(resultsDirectory) - [muscleNames, params] = extractSavedData(resultsDirectory, "muscleModelParameters"); - muscleNames = strrep(muscleNames, '_', ' '); - figure(Name = "Muscle Model Parameters") - sgtitle("Muscle Model Parameters", Fontsize=14) - set(gcf,'Position',[600,400,1200,800]) - paramLabels = ["Activation Time Constant", "Activation Nonlinearity", ... - "Electromechanical Time Delay", "EMG Scaling Factor", ... - "Optimal Fiber Length Scaling Factor", "Tendon Slack Length Scaling Factor"]; - for i = 1 : numel(paramLabels) - subplot(1, 6, i) - barh(params(i,:)) - - title(textwrap(paramLabels(i), 20), FontSize=12) - if i == 1 - set(gca, yticklabels = muscleNames, fontsize=11); - else - set(gca, yticklabels = [], fontsize=11); - end - - end - +[muscleNames, params] = extractSavedData( ... + resultsDirectory, "muscleModelParameters"); +muscleNames = strrep(muscleNames, '_', ' '); +figure(Name = "Muscle Model Parameters", ... + Units='normalized', ... + Position=[0.1 0.1 0.8 0.8]) + +paramLabels = ["Activation Time Constant", ... + "Activation Nonlinearity", ... + "Electromechanical Time Delay", ... + "EMG Scaling Factor", ... + "Optimal Fiber Length Scaling Factor", ... + "Tendon Slack Length Scaling Factor"]; +for i = 1 : numel(paramLabels) + subplot(1, 6, i) + barh(params(i,:)) + title(textwrap(paramLabels(i), 20), FontSize=12) + if i == 1 + set(gca, yticklabels = muscleNames, fontsize=11); + else + set(gca, yticklabels = [], fontsize=11); + end +end end diff --git a/src/MuscleTendonPersonalization/Analysis/plotJointMoments.m b/src/MuscleTendonPersonalization/Analysis/plotJointMoments.m index 7920009f8..e347b57c5 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotJointMoments.m +++ b/src/MuscleTendonPersonalization/Analysis/plotJointMoments.m @@ -1,83 +1,75 @@ function plotJointMoments(resultsDirectory) - [jointLabels, idMoments] = extractSavedData(resultsDirectory, "inverseDynamicsJointMoments"); - [~, noResidualMoments] = extractSavedData(resultsDirectory, "modelJointMomentsNoSynx"); - [~, withResidualMoments] = extractSavedData(resultsDirectory, "modelJointMomentsSynx"); - jointLabels = strrep(jointLabels, '_', ' '); - meanIdMoments = mean(idMoments, 3); - stdIdMoments = std(idMoments, [], 3); - meanNoResidualMoments = mean(noResidualMoments, 3); - stdNoResidualMoments = std(noResidualMoments, [], 3); - meanWithResidualMoments = mean(withResidualMoments, 3); - stdWithResidualMoments = std(withResidualMoments, [], 3); - maxMoment = max([max(meanIdMoments, [], "all"), max(meanNoResidualMoments, [], "all") ... - max(meanWithResidualMoments, [], "all")]); - minMoment = min([min(meanIdMoments, [], "all"), min(meanNoResidualMoments, [], "all") ... - min(meanWithResidualMoments, [], "all")]); +[jointLabels, idMoments] = extractSavedData( ... + resultsDirectory, "inverseDynamicsJointMoments"); +[~, noResidualMoments] = extractSavedData( ... + resultsDirectory, "modelJointMomentsNoSynx"); +[~, withResidualMoments] = extractSavedData( ... + resultsDirectory, "modelJointMomentsSynx"); +jointLabels = strrep(jointLabels, '_', ' '); +meanIdMoments = mean(idMoments, 3); +stdIdMoments = std(idMoments, [], 3); +meanNoResidualMoments = mean(noResidualMoments, 3); +stdNoResidualMoments = std(noResidualMoments, [], 3); +meanWithResidualMoments = mean(withResidualMoments, 3); +stdWithResidualMoments = std(withResidualMoments, [], 3); +maxMoment = max([ ... + max(meanIdMoments, [], "all"), ... + max(meanNoResidualMoments, [], "all") ... + max(meanWithResidualMoments, [], "all")]); +minMoment = min([ ... + min(meanIdMoments, [], "all"), ... + min(meanNoResidualMoments, [], "all") ... + min(meanWithResidualMoments, [], "all")]); - % meanModelMoments = [meanNoResidualMoments, meanWithResidualMoments]; - % stdModelMoments = [stdNoResidualMoments, stdWithResidualMoments]; - % meanIdMoments = [meanIdMoments, meanIdMoments]; %Just reformat for plotting purpose. - % stdIdMoments = [stdIdMoments, stdIdMoments]; - - % data.mean = {meanModelMoments, meanIdMoments}; - % data.std = {stdModelMoments, stdIdMoments}; - % data.labels = [jointLabels jointLabels]; - % plotOptions.axisLimits = [1 size(meanIdMoments, 1) minMoment maxMoment]; - % plotOptions.colors = ["b-", "r-"]; - % plotOptions.xlabel = "Time Points"; - % plotOptions.ylabel = "Joint Moment [Nm]"; - % - % plotMtpData(data, plotOptions) +figure(Name = "Joint Moments", ... + Units='normalized', ... + Position=[0.1 0.1 0.8 0.8]) +t = 1:1:size(meanIdMoments,1); +numWindows = numel(jointLabels); +for i=1:numel(jointLabels) + subplot(2, numWindows, i); + hold on + plot(meanNoResidualMoments(:,i), 'r-', linewidth=2) + plot(meanIdMoments(:,i), 'b-', linewidth=2) - figure(Name = "Joint Moments") - sgtitle("Joint Moments", Fontsize=14) - set(gcf, Position=[750,400,1050,800]) - t = 1:1:size(meanIdMoments,1); - numWindows = numel(jointLabels); - for i=1:numel(jointLabels) - subplot(2, numWindows, i); - hold on - plot(meanNoResidualMoments(:,i), 'r-', linewidth=2) - plot(meanIdMoments(:,i), 'b-', linewidth=2) - - noResidualFillRegion = [(meanNoResidualMoments(:,i)+stdNoResidualMoments(:,i)); - flipud(meanNoResidualMoments(:,i)-stdNoResidualMoments(:,i))]; - idFillRegion = [(meanIdMoments(:,i)+stdIdMoments(:,i)); - flipud(meanIdMoments(:,i)-stdIdMoments(:,i))]; - fill([t, fliplr(t)]', noResidualFillRegion, 'r', FaceAlpha=0.2, ... - EdgeColor='none', HandleVisibility='off') - fill([t, fliplr(t)]', idFillRegion, 'r', FaceAlpha=0.2, ... - EdgeColor='none', HandleVisibility='off') - hold off - title(jointLabels(i), fontsize=12) - axis([0 size(meanIdMoments, 1) minMoment, maxMoment]) - if i == 1 - legend("Mean Moment No Residual", "Mean Inverse Dynamics Moment") - ylabel("Joint Moment [Nm]") - end - subplot(2, numWindows, i+3) - hold on - plot(meanWithResidualMoments(:,i), 'r-', linewidth=2) - plot(meanIdMoments(:,i), 'b-', linewidth=2) + noResidualFillRegion = [(meanNoResidualMoments(:,i)+stdNoResidualMoments(:,i)); + flipud(meanNoResidualMoments(:,i)-stdNoResidualMoments(:,i))]; + idFillRegion = [(meanIdMoments(:,i)+stdIdMoments(:,i)); + flipud(meanIdMoments(:,i)-stdIdMoments(:,i))]; + fill([t, fliplr(t)]', noResidualFillRegion, 'r', FaceAlpha=0.2, ... + EdgeColor='none', HandleVisibility='off') + fill([t, fliplr(t)]', idFillRegion, 'r', FaceAlpha=0.2, ... + EdgeColor='none', HandleVisibility='off') + hold off + title(jointLabels(i), fontsize=12) + axis([0 size(meanIdMoments, 1) minMoment, maxMoment]) + if i == 1 + legend("Mean Moment No Residual", "Mean Inverse Dynamics Moment") + ylabel("Joint Moment [Nm]") + end + subplot(2, numWindows, i+3) + hold on + plot(meanWithResidualMoments(:,i), 'r-', linewidth=2) + plot(meanIdMoments(:,i), 'b-', linewidth=2) - withResidualFillRegion = [(meanWithResidualMoments(:,i)+stdWithResidualMoments(:,i)); - flipud(meanWithResidualMoments(:,i)-stdWithResidualMoments(:,i))]; - idFillRegion = [(meanIdMoments(:,i)+stdIdMoments(:,i)); - flipud(meanIdMoments(:,i)-stdIdMoments(:,i))]; - fill([t, fliplr(t)]', withResidualFillRegion, 'r', FaceAlpha=0.2, ... - EdgeColor='none', HandleVisibility='off') - fill([t, fliplr(t)]', idFillRegion, 'r', FaceAlpha=0.2, ... - EdgeColor='none', HandleVisibility='off') - hold off - set(gca, fontsize=11) - title(jointLabels(i), FontSize=12) - xlabel("Time Point") - axis([0 size(meanIdMoments, 1) minMoment, maxMoment]) - if i == 1 - legend("Mean Moment With Residual", "Mean Inverse Dynamics Moment") - ylabel("Joint Moment [Nm]") - end - + withResidualFillRegion = [(meanWithResidualMoments(:,i)+stdWithResidualMoments(:,i)); + flipud(meanWithResidualMoments(:,i)-stdWithResidualMoments(:,i))]; + idFillRegion = [(meanIdMoments(:,i)+stdIdMoments(:,i)); + flipud(meanIdMoments(:,i)-stdIdMoments(:,i))]; + fill([t, fliplr(t)]', withResidualFillRegion, 'r', FaceAlpha=0.2, ... + EdgeColor='none', HandleVisibility='off') + fill([t, fliplr(t)]', idFillRegion, 'r', FaceAlpha=0.2, ... + EdgeColor='none', HandleVisibility='off') + hold off + set(gca, fontsize=11) + title(jointLabels(i), FontSize=12) + xlabel("Time Point") + axis([0 size(meanIdMoments, 1) minMoment, maxMoment]) + if i == 1 + legend("Mean Moment With Residual", "Mean Inverse Dynamics Moment") + ylabel("Joint Moment [Nm]") end + +end end diff --git a/src/MuscleTendonPersonalization/Analysis/plotMtpData.m b/src/MuscleTendonPersonalization/Analysis/plotMtpData_remove.m similarity index 95% rename from src/MuscleTendonPersonalization/Analysis/plotMtpData.m rename to src/MuscleTendonPersonalization/Analysis/plotMtpData_remove.m index c2f88a47f..0a7ca0f51 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotMtpData.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMtpData_remove.m @@ -1,4 +1,4 @@ -function plotMtpData(data, options) +function plotMtpData_remove(data, options) figure() set(gcf,'Position',[750,400,950,700]) t = 1:1:size(data.mean{1},1); diff --git a/src/MuscleTendonPersonalization/Analysis/plotMuscleExcitationsAndActivations.m b/src/MuscleTendonPersonalization/Analysis/plotMuscleExcitationsAndActivations.m index 63ff44fb4..d806d3165 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotMuscleExcitationsAndActivations.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMuscleExcitationsAndActivations.m @@ -1,8 +1,12 @@ function plotMuscleExcitationsAndActivations(resultsDirectory) -[muscleNames, excitations] = extractSavedData(resultsDirectory, "muscleExcitations"); -[~, activations] = extractSavedData(resultsDirectory, "muscleActivations"); -[~, excitationsSynx] = extractSavedData(resultsDirectory, "muscleExcitationsSynx"); -[~, activationsSynx] = extractSavedData(resultsDirectory, "muscleActivationsSynx"); +[muscleNames, excitations] = extractSavedData( ... + resultsDirectory, "muscleExcitations"); +[~, activations] = extractSavedData( ... + resultsDirectory, "muscleActivations"); +[~, excitationsSynx] = extractSavedData( ... + resultsDirectory, "muscleExcitationsSynx"); +[~, activationsSynx] = extractSavedData( ... + resultsDirectory, "muscleActivationsSynx"); muscleNames = strrep(muscleNames, '_', ' '); meanExcitations = mean(excitations, 3); stdExcitations = std(excitations,[], 3); @@ -13,23 +17,10 @@ function plotMuscleExcitationsAndActivations(resultsDirectory) meanActivationsSynx = mean(activationsSynx, 3); stdActivationsSynx = std(activationsSynx,[], 3); -% data.mean = {meanExcitations, meanExcitationsSynx, meanActivations, meanActivationsSynx}; -% data.std = {stdExcitations, stdExcitationsSynx, stdActivations, stdActivationsSynx}; -% data.labels = muscleNames; -% plotOptions.colors = ["b-", "b--", "r-", "r--"]; -% plotOptions.legend = ["Excitation(without residual)", ... -% "Excitation(with residual)", ... -% "Activation(without residual)", ... -% "Activation(with residual)"]; -% plotOptions.axisLimits = [1 size(meanExcitations, 1) 0 1]; -% plotOptions.xlabel = "Time Points"; -% plotOptions.ylabel = "Magnitude"; -% -% plotMtpData(data, plotOptions); +figure(Name = "Muscle Excitations and Activations", ... + Units='normalized', ... + Position=[0.1 0.1 0.8 0.8]) -figure(Name = "Muscle Excitations and Activations") -sgtitle("Muscle Excitations and Activations", Fontsize=14) -set(gcf, Position=[750,400,1050,800]) t = 1:1:size(meanExcitations,1); numWindows = ceil(sqrt(numel(muscleNames))); for i = 1:numel(muscleNames) @@ -75,5 +66,4 @@ function plotMuscleExcitationsAndActivations(resultsDirectory) xlabel("Time Points") end end - end \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/Analysis/plotNormalizedFiberLengths.m b/src/MuscleTendonPersonalization/Analysis/plotNormalizedFiberLengths.m index e864ce1b4..7055d007a 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotNormalizedFiberLengths.m +++ b/src/MuscleTendonPersonalization/Analysis/plotNormalizedFiberLengths.m @@ -1,41 +1,41 @@ function plotNormalizedFiberLengths(resultsDirectory) - [muscleNames, normalizedFiberLengths] = extractSavedData( ... - resultsDirectory, "normalizedFiberLengths"); - muscleNames = strrep(muscleNames, '_', ' '); - meanFiberLengths = mean(normalizedFiberLengths, 3); - stdFiberLengths = std(normalizedFiberLengths, [], 3); +[muscleNames, normalizedFiberLengths] = extractSavedData( ... + resultsDirectory, "normalizedFiberLengths"); +muscleNames = strrep(muscleNames, '_', ' '); +meanFiberLengths = mean(normalizedFiberLengths, 3); +stdFiberLengths = std(normalizedFiberLengths, [], 3); - figure(Name = "Normalized Fiber Lengths") - sgtitle("Normalized Fiber Length", Fontsize=14) - set(gcf, Position=[750,400,1050,800]) - t = 1:1:size(meanFiberLengths,1); - numWindows = ceil(sqrt(numel(muscleNames))); - passiveLower = ones(size(t))*0.7; - passiveUpper = ones(size(t)); +figure(Name = "Normalized Fiber Lengths", ... + Units='normalized', ... + Position=[0.1 0.1 0.8 0.8]) +t = 1:1:size(meanFiberLengths,1); +numWindows = ceil(sqrt(numel(muscleNames))); +passiveLower = ones(size(t))*0.7; +passiveUpper = ones(size(t)); - for i=1:numel(muscleNames) - subplot(numWindows, numWindows, i); - hold on - plot(meanFiberLengths(:,i), 'b-', LineWidth=2); +for i=1:numel(muscleNames) + subplot(numWindows, numWindows, i); + hold on + plot(meanFiberLengths(:,i), 'b-', LineWidth=2); - fillRegion = [(meanFiberLengths(:,i)+stdFiberLengths(:,i)); - flipud(meanFiberLengths(:,i)-stdFiberLengths(:,i))]; - fill([t, fliplr(t)]', fillRegion, 'b', FaceAlpha=0.2, ... + fillRegion = [(meanFiberLengths(:,i)+stdFiberLengths(:,i)); + flipud(meanFiberLengths(:,i)-stdFiberLengths(:,i))]; + fill([t, fliplr(t)]', fillRegion, 'b', FaceAlpha=0.2, ... EdgeColor='none', HandleVisibility='off') - plot(t, passiveUpper, 'r--', LineWidth=2); - plot(t, passiveLower, 'r--', LineWidth=2); - hold off - set(gca, fontsize=11) - axis([1 size(meanFiberLengths, 1) 0 1.5]) - title(muscleNames(i), FontSize=12); - if mod(i,3) == 1 - ylabel('Normalized Fiber Length', FontSize=12) - end - if i>numel(muscleNames)-numWindows - xlabel("Time Points", FontSize=12) - end + plot(t, passiveUpper, 'r--', LineWidth=2); + plot(t, passiveLower, 'r--', LineWidth=2); + hold off + set(gca, fontsize=11) + axis([1 size(meanFiberLengths, 1) 0 1.5]) + title(muscleNames(i), FontSize=12); + if mod(i,3) == 1 + ylabel('Normalized Fiber Length', FontSize=12) end + if i>numel(muscleNames)-numWindows + xlabel("Time Points", FontSize=12) + end +end end diff --git a/src/MuscleTendonPersonalization/Analysis/plotPassiveForceCurves.m b/src/MuscleTendonPersonalization/Analysis/plotPassiveForceCurves.m index 5d71f0228..5ca6e91e3 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotPassiveForceCurves.m +++ b/src/MuscleTendonPersonalization/Analysis/plotPassiveForceCurves.m @@ -1,31 +1,22 @@ function plotPassiveForceCurves(resultsDirectory) -[muscleNames, passiveForce] = extractSavedData(resultsDirectory, "passiveForcesExperimental"); +[muscleNames, passiveForce] = extractSavedData( ... + resultsDirectory, "passiveForcesExperimental"); muscleNames = strrep(muscleNames, '_', ' '); meanPassiveForce = mean(passiveForce, 3); stdPassiveForce = std(passiveForce, [], 3); maxForce = max(meanPassiveForce, [], 'all'); numWindows = ceil(sqrt(numel(muscleNames))); -% data.mean = {meanPassiveForce}; -% data.std = {stdPassiveForce}; -% data.labels = muscleNames; -% plotOptions.colors = ["b-"]; -% plotOptions.axisLimits = [1 size(meanPassiveForce, 1), 0, maxForce]; -% plotOptions.xlabel = "Time Points"; -% plotOptions.ylabel = "Force [N]"; -% -% plotMtpData(data, plotOptions); - -figure(Name = "Passive Force Curves") -sgtitle("Passive Force Curves", Fontsize=14) -set(gcf, Position=[750,400,1050,800]) +figure(Name = "Passive Force Curves", ... + Units='normalized', ... + Position=[0.1 0.1 0.8 0.8]) t = 1:1:size(meanPassiveForce,1); for i = 1:numel(muscleNames) subplot(numWindows, numWindows, i) hold on plot(meanPassiveForce(:,i), 'b-', linewidth=2) - fillRegion = [(meanPassiveForce(:,i)+stdPassiveForce(:,i)); + fillRegion = [(meanPassiveForce(:,i)+stdPassiveForce(:,i)); flipud((meanPassiveForce(:,i)-stdPassiveForce(:,i)))]; fill([t, fliplr(t)]', fillRegion, 'b', FaceAlpha=0.2, ... EdgeColor='none', HandleVisibility='off') diff --git a/src/MuscleTendonPersonalization/Analysis/plotPassiveMomentCurves.m b/src/MuscleTendonPersonalization/Analysis/plotPassiveMomentCurves.m index 7b9bba450..98dbada77 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotPassiveMomentCurves.m +++ b/src/MuscleTendonPersonalization/Analysis/plotPassiveMomentCurves.m @@ -1,6 +1,8 @@ function plotPassiveMomentCurves(resultsDirectory) -[momentNames, passiveMomentsExperimental] = extractSavedData(resultsDirectory, "passiveJointMomentsExperimental"); -[~, passiveMomentsModel] = extractSavedData(resultsDirectory, "passiveJointMomentsModeled"); +[momentNames, passiveMomentsExperimental] = extractSavedData( ... + resultsDirectory, "passiveJointMomentsExperimental"); +[~, passiveMomentsModel] = extractSavedData( ... + resultsDirectory, "passiveJointMomentsModeled"); momentNames = strrep(momentNames, '_', ' '); meanPassiveMomentsExperimental = mean(passiveMomentsExperimental, 3); stdPassiveMomentsExperimental = std(passiveMomentsExperimental, [], 3); @@ -11,23 +13,11 @@ function plotPassiveMomentCurves(resultsDirectory) minMoment = min([min(meanPassiveMomentsExperimental, [], 'all'), ... min(meanPassiveMomentsModel, [], 'all')]); -% data.mean = {meanPassiveMomentsExperimental, meanPassiveMomentsModel}; -% data.std = {stdPassiveMomentsExperimental, stdPassiveMomentsModel}; -% data.labels = momentNames; -% -% plotOptions.colors = ["b-", "r-"]; -% plotOptions.legend = ["Experimental", "Model"]; -% plotOptions.axisLimits = [1 size(meanPassiveMomentsModel, 1) minMoment maxMoment]; -% plotOptions.xlabel = "Time Points"; -% plotOptions.ylabel = "Moment [Nm]"; -% -% plotMtpData(data, plotOptions); - numWindows = ceil(sqrt(numel(momentNames))); t = 1:1:size(meanPassiveMomentsModel,1); -figure(Name = "Passive Moment Curves") -sgtitle("Passive Moment Curves", Fontsize=14) -set(gcf, Position=[750,400,1050,800]) +figure(Name = "Passive Moment Curves", ... + Units='normalized', ... + Position=[0.1 0.1 0.8 0.8]) for i = 1:numel(momentNames) subplot(numWindows, numWindows, i) @@ -36,11 +26,11 @@ function plotPassiveMomentCurves(resultsDirectory) plot(meanPassiveMomentsExperimental(:,i), 'b-', linewidth=2) plot(meanPassiveMomentsModel(:,i), 'r-', linewidth=2) - fillRegionExperimental = [(meanPassiveMomentsExperimental(:,i)+stdPassiveMomentsExperimental(:,i)); + fillRegionExperimental = [(meanPassiveMomentsExperimental(:,i)+stdPassiveMomentsExperimental(:,i)); flipud((meanPassiveMomentsExperimental(:,i)-stdPassiveMomentsExperimental(:,i)))]; fill([t, fliplr(t)]', fillRegionExperimental, 'b', FaceAlpha=0.2, ... EdgeColor='none', HandleVisibility='off') - fillRegionModel = [(meanPassiveMomentsModel(:,i)+stdPassiveMomentsModel(:,i)); + fillRegionModel = [(meanPassiveMomentsModel(:,i)+stdPassiveMomentsModel(:,i)); flipud((meanPassiveMomentsModel(:,i)-stdPassiveMomentsModel(:,i)))]; fill([t, fliplr(t)]', fillRegionModel, 'b', FaceAlpha=0.2, ... EdgeColor='none', HandleVisibility='off') @@ -55,6 +45,5 @@ function plotPassiveMomentCurves(resultsDirectory) xlabel("Time Points") end end - end diff --git a/src/MuscleTendonPersonalization/Analysis/printJointMomentMatchingError.m b/src/MuscleTendonPersonalization/Analysis/printJointMomentMatchingError.m new file mode 100644 index 000000000..3e09de8ac --- /dev/null +++ b/src/MuscleTendonPersonalization/Analysis/printJointMomentMatchingError.m @@ -0,0 +1,23 @@ +function printJointMomentMatchingError(resultsDirectory) +[jointMomentLabels, muscleJointMoments] = extractSavedData( ... + resultsDirectory, "modelJointMomentsSynx"); +[~, inverseDynamicsMoments] = extractSavedData( ... + resultsDirectory, "inverseDynamicsJointMoments"); +jointMomentsRmse = zeros(size(jointMomentLabels)); +jointMomentsMae = zeros(size(jointMomentLabels)); +for i = 1 : size(muscleJointMoments, 2) + jointMomentsRmse(i) = sqrt(sum((muscleJointMoments(:, i) - ... + inverseDynamicsMoments(:, i)) .^ 2, 'all') / ... + (numel(inverseDynamicsMoments(:, 1)) - 1)); + jointMomentsMae(i) = sum(abs(muscleJointMoments(:, i) - ... + inverseDynamicsMoments(:, i)) / ... + numel(inverseDynamicsMoments(:, 1)), 'all'); +end +fprintf(['The root mean sqrt (RMS) errors between model-predicted ' ... + 'and inverse dynamic moments are: \n' ]); +fprintf(['\n ' num2str(jointMomentsRmse) ' \n']); +fprintf(['The mean absolute errors (MAEs) between model-predicted ' ... + 'and inverse dynamic moments are: \n' ]); +fprintf(['\n ' num2str(jointMomentsMae) ' \n']); +end + diff --git a/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResultsOld.m b/src/MuscleTendonPersonalization/Analysis/reportMuscleTendonPersonalizationResults_remove.m similarity index 99% rename from src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResultsOld.m rename to src/MuscleTendonPersonalization/Analysis/reportMuscleTendonPersonalizationResults_remove.m index 83d3d2b7c..5ef1df320 100644 --- a/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResultsOld.m +++ b/src/MuscleTendonPersonalization/Analysis/reportMuscleTendonPersonalizationResults_remove.m @@ -29,7 +29,7 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function reportMuscleTendonPersonalizationResultsOld(optimizedParams, ... +function reportMuscleTendonPersonalizationResults_remove(optimizedParams, ... mtpInputs, precalInputs) if nargin < 3; precalInputs = []; end [finalValues, results, resultsSynx, resultsSynxNoResiduals] = ... diff --git a/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m b/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m index 377032a4d..869128190 100644 --- a/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m +++ b/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m @@ -43,26 +43,15 @@ function MuscleTendonPersonalizationTool(settingsFileName) precalInputs = struct('optimizeIsometricMaxForce', false); end optimizedParams = MuscleTendonPersonalization(inputs, params); - if params.performMuscleTendonLengthInitialization - saveMuscleTendonOptimizationParams(".\mtpResults\Analysis", ... - optimizedParams, inputs, precalInputs) + [finalValues, resultsStruct, modeledValues] = ... + getMtpResultsToSave(inputs, params, optimizedParams, precalInputs); + saveMuscleTendonPersonalizationResults(inputs, finalValues, modeledValues, ... + resultsStruct, resultsDirectory, precalInputs); else - saveMuscleTendonOptimizationParams(".\mtpResults\Analysis", ... - optimizedParams, inputs) + [finalValues, resultsStruct, modeledValues] = ... + getMtpResultsToSave(inputs, params, optimizedParams); + saveMuscleTendonPersonalizationResults(inputs, finalValues, modeledValues, ... + resultsStruct, resultsDirectory); end - -reportMuscleTendonPersonalizationResults(".\mtpResults\Analysis"); - -finalValues = makeMtpValuesAsStruct([], optimizedParams, zeros(1, 7)); -if precalInputs.optimizeIsometricMaxForce - finalValues.maxIsometricForce = inputs.maxIsometricForce; -end -results = calcMtpSynXModeledValues(finalValues, inputs, params); - -results.time = inputs.emgTime(:, inputs.numPaddingFrames + 1 : ... - end - inputs.numPaddingFrames); -saveMuscleTendonPersonalizationResults(inputs.model, ... - inputs.osimxFileName, inputs.prefixes, inputs.coordinateNames, ... - finalValues, results, resultsDirectory, inputs.muscleTendonColumnNames); end diff --git a/src/MuscleTendonPersonalization/Saving/getMtpResultsToSave.m b/src/MuscleTendonPersonalization/Saving/getMtpResultsToSave.m new file mode 100644 index 000000000..df737a5ff --- /dev/null +++ b/src/MuscleTendonPersonalization/Saving/getMtpResultsToSave.m @@ -0,0 +1,37 @@ +function [finalValues, resultsStruct, modeledValues] = ... + getMtpResultsToSave(mtpInputs, params, optimizedParams, precalInputs) +finalValues = makeMtpValuesAsStruct([], optimizedParams, zeros(1, 7)); +if nargin < 4 + modeledValues = []; +else + tempValues.optimalFiberLengthScaleFactors = ... + mtpInputs.optimalFiberLength ./ precalInputs.optimalFiberLength; + tempValues.tendonSlackLengthScaleFactors = ... + mtpInputs.tendonSlackLength ./ precalInputs.tendonSlackLength; + precalInputs.maxIsometricForce = mtpInputs.maxIsometricForce; + precalInputs.optimizeIsometricMaxForce = 0; + modeledValues = calcMuscleTendonLengthInitializationModeledValues(tempValues, precalInputs); + if precalInputs.optimizeIsometricMaxForce + finalValues.maxIsometricForce = mtpInputs.maxIsometricForce; + end +end + +if precalInputs.optimizeIsometricMaxForce + finalValues.maxIsometricForce = mtpInputs.maxIsometricForce; +end +results = calcMtpModeledValues(finalValues, mtpInputs, struct()); +results.time = mtpInputs.emgTime(:, mtpInputs.numPaddingFrames + 1 : ... + end - mtpInputs.numPaddingFrames); +results.muscleExcitations = results.muscleExcitations(:, :, ... + mtpInputs.numPaddingFrames + 1 : end - mtpInputs.numPaddingFrames); +resultsSynx = calcMtpSynXModeledValues(finalValues, mtpInputs, params); +resultsSynx.time = mtpInputs.emgTime(:, mtpInputs.numPaddingFrames + 1 : ... + end - mtpInputs.numPaddingFrames); +resultsSynx.muscleExcitations = resultsSynx.muscleExcitations(:, :, ... + mtpInputs.numPaddingFrames + 1 : end - mtpInputs.numPaddingFrames); +finalValues.synergyWeights(mtpInputs.numberOfExtrapolationWeights + 1 : end) = 0; +resultsSynxNoResiduals = calcMtpSynXModeledValues(finalValues, mtpInputs, struct()); +resultsStruct = struct("results", results, ... + "resultsSynx", resultsSynx, ... + "resultsSynxNoResiduals", resultsSynxNoResiduals); +end \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/Saving/saveActivationAndExcitationData.m b/src/MuscleTendonPersonalization/Saving/saveActivationAndExcitationData.m new file mode 100644 index 000000000..93e65c0ee --- /dev/null +++ b/src/MuscleTendonPersonalization/Saving/saveActivationAndExcitationData.m @@ -0,0 +1,15 @@ +function saveActivationAndExcitationData(mtpInputs, results, ... + resultsSynx, resultsDirectory) +writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... + results.muscleExcitations, strcat(resultsDirectory, ... + "\muscleExcitations"), "_muscleExcitations.sto") +writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... + resultsSynx.muscleExcitations, strcat(resultsDirectory, ... + "\muscleExcitationsSynx"), "_muscleExcitationsSynx.sto") +writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... + results.muscleActivations, strcat(resultsDirectory, ... + "\muscleActivations"), "_muscleActivations.sto") +writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... + resultsSynx.muscleActivations, strcat(resultsDirectory, ... + "\muscleActivationsSynx"), "_muscleActivationsSynx.sto") +end \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/Saving/saveJointMomentData.m b/src/MuscleTendonPersonalization/Saving/saveJointMomentData.m new file mode 100644 index 000000000..24b6fdb79 --- /dev/null +++ b/src/MuscleTendonPersonalization/Saving/saveJointMomentData.m @@ -0,0 +1,15 @@ +function saveJointMomentData(mtpInputs, results, resultsSynx, ... + resultsSynxNoResiduals, resultsDirectory) +writeMtpDataToSto(mtpInputs.coordinateNames, mtpInputs.prefixes, ... + results.muscleJointMoments, strcat(resultsDirectory, ... + "\modelJointMomentsNoSynx"), "_modelJointMomentsNoSynx.sto") +writeMtpDataToSto(mtpInputs.coordinateNames, mtpInputs.prefixes, ... + resultsSynx.muscleJointMoments, strcat(resultsDirectory, ... + "\modelJointMomentsSynx"), "_modelJointMomentsSynx.sto") +writeMtpDataToSto(mtpInputs.coordinateNames, mtpInputs.prefixes, ... + resultsSynxNoResiduals.muscleJointMoments, strcat(resultsDirectory, ... + "\modelJointMomentsSynxNoResiduals"), "_modelJointMomentsSynxNoResiduals.sto") +writeMtpDataToSto(mtpInputs.coordinateNames, mtpInputs.prefixes, ... + mtpInputs.inverseDynamicsMoments, strcat(resultsDirectory, ... + "\inverseDynamicsJointMoments"), "_inverseDynamicsJointMoments.sto") +end \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/Saving/saveMuscleModelParameters.m b/src/MuscleTendonPersonalization/Saving/saveMuscleModelParameters.m new file mode 100644 index 000000000..a8c03bc02 --- /dev/null +++ b/src/MuscleTendonPersonalization/Saving/saveMuscleModelParameters.m @@ -0,0 +1,14 @@ +function saveMuscleModelParameters(mtpInputs, finalValues, resultsDirectory) +if ~exist(resultsDirectory, "dir") + mkdir(resultsDirectory); +end +columnLabels = mtpInputs.muscleNames; +dataPoints = [finalValues.activationTimeConstants; + finalValues.activationNonlinearityConstants; + finalValues.electromechanicalDelays; + finalValues.emgScaleFactors; + finalValues.optimalFiberLengthScaleFactors; + finalValues.tendonSlackLengthScaleFactors]; +writeToSto(columnLabels, 1:1:size(dataPoints,1), dataPoints, ... + fullfile(resultsDirectory, "muscleModelParameters.sto")); +end \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/saveMuscleTendonOptimizationParams.m b/src/MuscleTendonPersonalization/Saving/saveMuscleTendonOptimizationParamsOld.m similarity index 81% rename from src/MuscleTendonPersonalization/saveMuscleTendonOptimizationParams.m rename to src/MuscleTendonPersonalization/Saving/saveMuscleTendonOptimizationParamsOld.m index 51f01f9d8..598c03238 100644 --- a/src/MuscleTendonPersonalization/saveMuscleTendonOptimizationParams.m +++ b/src/MuscleTendonPersonalization/Saving/saveMuscleTendonOptimizationParamsOld.m @@ -36,23 +36,23 @@ function saveMuscleTendonOptimizationParams(resultsDirectory, optimizedParams, . if ~isempty(precalInputs) modeledValues = getMuscleTendonLengthInitializationData(precalInputs, ... mtpInputs); % Modeled passive force data & params from experimental data. - savePassiveMomentData(precalInputs, modeledValues, resultsDirectory); - savePassiveForceData(mtpInputs, modeledValues, results, resultsSynx, ... + savePassiveMomentDataToSto(precalInputs, modeledValues, resultsDirectory); + savePassiveForceDataToSto(mtpInputs, modeledValues, results, resultsSynx, ... resultsSynxNoResiduals, resultsDirectory); end printJointMomentMatchingError(resultsSynx.muscleJointMoments, ... mtpInputs.inverseDynamicsMoments); % Keep this -saveActivationAndExcitationData(mtpInputs, results, resultsSynx, resultsDirectory); +saveActivationAndExcitationDataToSto(mtpInputs, results, resultsSynx, resultsDirectory); -saveAnalysisData(mtpInputs.muscleNames, mtpInputs.prefixes, results.normalizedFiberLength, ... +saveAnalysisDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, results.normalizedFiberLength, ... strcat(resultsDirectory, "\normalizedFiberLengths"), "_normalizedFiberLengths.sto") -saveJointMomentData(mtpInputs, results, resultsSynx, resultsSynxNoResiduals, ... +saveJointMomentDataToSto(mtpInputs, results, resultsSynx, resultsSynxNoResiduals, ... resultsDirectory); -saveMuscleModelParameters(mtpInputs, finalValues, fullfile(resultsDirectory, ... +saveMuscleModelParametersToSto(mtpInputs, finalValues, fullfile(resultsDirectory, ... "muscleModelParameters")); end @@ -89,7 +89,7 @@ function saveMuscleTendonOptimizationParams(resultsDirectory, optimizedParams, . modeledValues = calcMuscleTendonLengthInitializationModeledValues(tempValues, precalInputs); end -function savePassiveMomentData(precalInputs, modeledValues, resultsDirectory) +function savePassiveMomentDataToSto(precalInputs, modeledValues, resultsDirectory) % Passive moments need processing first. modelPassiveMoments = permute(modeledValues.passiveModelMoments, [3 1 2]); sizeTemp = size(modelPassiveMoments,1); @@ -111,26 +111,26 @@ function savePassiveMomentData(precalInputs, modeledValues, resultsDirectory) reshape(modelPassiveMoments, sizeTemp, []); modelPassiveMoments = ... reshape(modelPassiveMoments', 1, 12, 101); % Want a better way to do this -saveAnalysisData(precalInputs.passivePrefixes, precalInputs.prefixes, ... +saveAnalysisDataToSto(precalInputs.passivePrefixes, precalInputs.prefixes, ... experimentalPassiveMoments, fullfile(resultsDirectory, ... "passiveJointMomentsExperimental"), "_passiveJointMomentsExperimental.sto") -saveAnalysisData(precalInputs.passivePrefixes, precalInputs.prefixes, ... +saveAnalysisDataToSto(precalInputs.passivePrefixes, precalInputs.prefixes, ... modelPassiveMoments, fullfile(resultsDirectory, ... "passiveJointMomentsModeled"), "_passiveJointMomentsModeled.sto") end -function savePassiveForceData(mtpInputs, modeledValues, results, resultsSynx, ... +function savePassiveForceDataToSto(mtpInputs, modeledValues, results, resultsSynx, ... resultsSynxNoResiduals, resultsDirectory) -saveAnalysisData(mtpInputs.muscleNames, mtpInputs.prefixes, ... +saveAnalysisDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... modeledValues.passiveForce, strcat(resultsDirectory, ... "\passiveForcesExperimental"), "_passiveForcesExperimental.sto"); -saveAnalysisData(mtpInputs.muscleNames, mtpInputs.prefixes, ... +saveAnalysisDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... results.passiveForce, strcat(resultsDirectory, ... "\passiveForcesModel"), "_passiveForcesModel.sto"); -saveAnalysisData(mtpInputs.muscleNames, mtpInputs.prefixes, ... +saveAnalysisDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... resultsSynx.passiveForce, strcat(resultsDirectory, ... "\passiveForcesModelSynx"), "_passiveForcesModelSynx.sto"); -saveAnalysisData(mtpInputs.muscleNames, mtpInputs.prefixes, ... +saveAnalysisDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... resultsSynxNoResiduals.passiveForce, strcat(resultsDirectory, ... "\passiveForcesModelSynxNoResiduals"), "_passiveForcesModelSynxNoResiduals.sto"); end @@ -153,40 +153,40 @@ function printJointMomentMatchingError(muscleJointMoments, inverseDynamicsMoment fprintf(['\n ' num2str(jointMomentsMae) ' \n']); end -function saveActivationAndExcitationData(mtpInputs, results, ... +function saveActivationAndExcitationDataToSto(mtpInputs, results, ... resultsSynx, resultsDirectory) -saveAnalysisData(mtpInputs.muscleNames, mtpInputs.prefixes, ... +saveAnalysisDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... results.muscleExcitations, strcat(resultsDirectory, ... "\muscleExcitations"), "_muscleExcitations.sto") -saveAnalysisData(mtpInputs.muscleNames, mtpInputs.prefixes, ... +saveAnalysisDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... resultsSynx.muscleExcitations, strcat(resultsDirectory, ... "\muscleExcitationsSynx"), "_muscleExcitationsSynx.sto") -saveAnalysisData(mtpInputs.muscleNames, mtpInputs.prefixes, ... +saveAnalysisDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... results.muscleActivations, strcat(resultsDirectory, ... "\muscleActivations"), "_muscleActivations.sto") -saveAnalysisData(mtpInputs.muscleNames, mtpInputs.prefixes, ... +saveAnalysisDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... resultsSynx.muscleActivations, strcat(resultsDirectory, ... "\muscleActivationsSynx"), "_muscleActivationsSynx.sto") end -function saveJointMomentData(mtpInputs, results, resultsSynx, ... +function saveJointMomentDataToSto(mtpInputs, results, resultsSynx, ... resultsSynxNoResiduals, resultsDirectory) -saveAnalysisData(mtpInputs.coordinateNames, mtpInputs.prefixes, ... +saveAnalysisDataToSto(mtpInputs.coordinateNames, mtpInputs.prefixes, ... results.muscleJointMoments, strcat(resultsDirectory, ... "\modelJointMomentsNoSynx"), "_modelJointMomentsNoSynx.sto") -saveAnalysisData(mtpInputs.coordinateNames, mtpInputs.prefixes, ... +saveAnalysisDataToSto(mtpInputs.coordinateNames, mtpInputs.prefixes, ... resultsSynx.muscleJointMoments, strcat(resultsDirectory, ... "\modelJointMomentsSynx"), "_modelJointMomentsSynx.sto") -saveAnalysisData(mtpInputs.coordinateNames, mtpInputs.prefixes, ... +saveAnalysisDataToSto(mtpInputs.coordinateNames, mtpInputs.prefixes, ... resultsSynxNoResiduals.muscleJointMoments, strcat(resultsDirectory, ... "\modelJointMomentsSynxNoResiduals"), "_modelJointMomentsSynxNoResiduals.sto") -saveAnalysisData(mtpInputs.coordinateNames, mtpInputs.prefixes, ... +saveAnalysisDataToSto(mtpInputs.coordinateNames, mtpInputs.prefixes, ... mtpInputs.inverseDynamicsMoments, strcat(resultsDirectory, ... "\inverseDynamicsJointMoments"), "_inverseDynamicsJointMoments.sto") end -function saveMuscleModelParameters(mtpInputs, finalValues, resultsDirectory) +function saveMuscleModelParametersToSto(mtpInputs, finalValues, resultsDirectory) if ~exist(resultsDirectory, "dir") mkdir(resultsDirectory); end @@ -206,7 +206,7 @@ function saveMuscleModelParameters(mtpInputs, finalValues, resultsDirectory) fullfile(resultsDirectory, "muscleModelParameters.sto")); end -function saveAnalysisData(columnLabels, taskNames, data, directory, fileName) +function saveAnalysisDataToSto(columnLabels, taskNames, data, directory, fileName) if ~exist(directory, "dir") mkdir(directory); end diff --git a/src/MuscleTendonPersonalization/Saving/saveMuscleTendonOptimizationParams_remove.m b/src/MuscleTendonPersonalization/Saving/saveMuscleTendonOptimizationParams_remove.m new file mode 100644 index 000000000..33ef45f6a --- /dev/null +++ b/src/MuscleTendonPersonalization/Saving/saveMuscleTendonOptimizationParams_remove.m @@ -0,0 +1,83 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function saves optimization results to .sto files to be plotted by +% the user in either MATLAB or Opensim +% +% (struct, struct, struct, string) -> (None) +% Saves MTP optimization parameters to .sto files to be plotted. +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Robert Salati, Di Ao, Marleny Vega % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % +function saveMuscleTendonOptimizationParams_remove(resultsDirectory, optimizedParams, ... + mtpInputs, precalInputs) +if nargin < 4 + precalInputs = []; +end +[finalValues, results, resultsSynx, resultsSynxNoResiduals] = ... + getValuesToSave(mtpInputs, optimizedParams); +if ~isempty(precalInputs) + modeledValues = getMuscleTendonLengthInitializationData(precalInputs, ... + mtpInputs); % Modeled passive force data & params from experimental data. + savePassiveMomentData(precalInputs, modeledValues, resultsDirectory); + savePassiveForceData(mtpInputs, modeledValues, results, resultsSynx, ... + resultsSynxNoResiduals, resultsDirectory); +end + +saveActivationAndExcitationData(mtpInputs, results, resultsSynx, resultsDirectory); +writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, results.normalizedFiberLength, ... + fullfile(resultsDirectory, "normalizedFiberLengths"), "_normalizedFiberLengths.sto") +saveJointMomentData(mtpInputs, results, resultsSynx, resultsSynxNoResiduals, ... + resultsDirectory); +saveMuscleModelParameters(mtpInputs, finalValues, fullfile(resultsDirectory, ... + "muscleModelParameters")); +end + +function [finalValues, results, resultsSynx, resultsSynxNoResiduals] = ... + getValuesToSave(mtpInputs, optimizedParams) + +finalValues = makeMtpValuesAsStruct([], optimizedParams, zeros(1, 7)); +resultsSynx = calcMtpSynXModeledValues(finalValues, mtpInputs, struct()); +finalValues.synergyWeights(mtpInputs.numberOfExtrapolationWeights + 1 : end) = 0; +resultsSynxNoResiduals = calcMtpSynXModeledValues(finalValues, mtpInputs, struct()); +results = calcMtpModeledValues(finalValues, mtpInputs, struct()); +results.time = mtpInputs.emgTime(:, mtpInputs.numPaddingFrames + 1 : ... + end - mtpInputs.numPaddingFrames); +results.muscleExcitations = results.muscleExcitations(:, :, ... + mtpInputs.numPaddingFrames + 1 : end - mtpInputs.numPaddingFrames); +resultsSynx.muscleExcitations = resultsSynx.muscleExcitations(:, :, ... + mtpInputs.numPaddingFrames + 1 : end - mtpInputs.numPaddingFrames); +end + +function modeledValues = getMuscleTendonLengthInitializationData(... + precalInputs, mtpInputs) + +tempValues.optimalFiberLengthScaleFactors = ... + mtpInputs.optimalFiberLength ./ precalInputs.optimalFiberLength; + +tempValues.tendonSlackLengthScaleFactors = ... + mtpInputs.tendonSlackLength ./ precalInputs.tendonSlackLength; + +precalInputs.maxIsometricForce = mtpInputs.maxIsometricForce; + +precalInputs.optimizeIsometricMaxForce = 0; + +modeledValues = calcMuscleTendonLengthInitializationModeledValues(tempValues, precalInputs); +end \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/saveMuscleTendonPersonalizationResults.m b/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m similarity index 62% rename from src/MuscleTendonPersonalization/saveMuscleTendonPersonalizationResults.m rename to src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m index ed9954558..b04a2241d 100644 --- a/src/MuscleTendonPersonalization/saveMuscleTendonPersonalizationResults.m +++ b/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m @@ -30,32 +30,29 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function saveMuscleTendonPersonalizationResults(modelFileName, ... - osimxFileName, prefixes, coordinateNames, finalValues, results, ... - resultsDirectory, muscleColumnNames) - -model = Model(modelFileName); -% muscleColumnNames = getEnabledMusclesInOrder(model); -if ~exist(resultsDirectory, "dir") - mkdir(resultsDirectory); -end -if ~exist(fullfile(resultsDirectory, "muscleActivations"), "dir") - mkdir(fullfile(resultsDirectory, "muscleActivations")); -end -if ~exist(fullfile(resultsDirectory, "modelMoments"), "dir") - mkdir(fullfile(resultsDirectory, "modelMoments")); +function saveMuscleTendonPersonalizationResults(mtpInputs, finalValues, ... + modeledValues, resultsStruct, resultsDirectory, precalInputs) +results = resultsStruct.results; +resultsSynx = resultsStruct.resultsSynx; +resultsSynxNoResiduals = resultsStruct.resultsSynxNoResiduals; +analysisDirectory = fullfile(resultsDirectory, "Analysis"); +if nargin < 6 + precalInputs = []; end -for i = 1:size(results.muscleActivations, 1) - % Need to figure out how to print out individuals file names for each trial - writeToSto(muscleColumnNames, results.time(i, :), ... - squeeze(results.muscleActivations(i, :, :))', fullfile(resultsDirectory, ... - "muscleActivations", strcat(prefixes(i), "_muscleActivations", ".sto"))); - writeToSto(coordinateNames, results.time(i, :), ... - squeeze(results.muscleJointMoments(i, :, :))', fullfile(resultsDirectory, ... - "modelMoments", strcat(prefixes(i), "_modelMoments", ".sto"))); +if ~isempty(precalInputs) + savePassiveMomentData(precalInputs, modeledValues, analysisDirectory); + savePassiveForceData(mtpInputs, modeledValues, results, resultsSynx, ... + resultsSynxNoResiduals, analysisDirectory); end -muscleNames = getMusclesFromCoordinates(model, coordinateNames); -writeMuscleTendonPersonalizationOsimxFile(modelFileName, osimxFileName, ... +saveActivationAndExcitationData(mtpInputs, results, resultsSynx, analysisDirectory); +writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, results.normalizedFiberLength, ... + fullfile(analysisDirectory, "normalizedFiberLengths"), "_normalizedFiberLengths.sto") +saveJointMomentData(mtpInputs, results, resultsSynx, resultsSynxNoResiduals, ... + analysisDirectory); +saveMuscleModelParameters(mtpInputs, finalValues, fullfile(analysisDirectory, ... + "muscleModelParameters")); +model = Model(mtpInputs.model); +muscleNames = getMusclesFromCoordinates(model, mtpInputs.coordinateNames); +writeMuscleTendonPersonalizationOsimxFile(mtpInputs.model, mtpInputs.osimxFileName, ... finalValues, muscleNames, resultsDirectory); -end - +end \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/Saving/savePassiveForceData.m b/src/MuscleTendonPersonalization/Saving/savePassiveForceData.m new file mode 100644 index 000000000..0c9da4dd1 --- /dev/null +++ b/src/MuscleTendonPersonalization/Saving/savePassiveForceData.m @@ -0,0 +1,15 @@ +function savePassiveForceData(mtpInputs, modeledValues, results, resultsSynx, ... + resultsSynxNoResiduals, resultsDirectory) +writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... + modeledValues.passiveForce, strcat(resultsDirectory, ... + "\passiveForcesExperimental"), "_passiveForcesExperimental.sto"); +writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... + results.passiveForce, strcat(resultsDirectory, ... + "\passiveForcesModel"), "_passiveForcesModel.sto"); +writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... + resultsSynx.passiveForce, strcat(resultsDirectory, ... + "\passiveForcesModelSynx"), "_passiveForcesModelSynx.sto"); +writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... + resultsSynxNoResiduals.passiveForce, strcat(resultsDirectory, ... + "\passiveForcesModelSynxNoResiduals"), "_passiveForcesModelSynxNoResiduals.sto"); +end diff --git a/src/MuscleTendonPersonalization/Saving/savePassiveMomentData.m b/src/MuscleTendonPersonalization/Saving/savePassiveMomentData.m new file mode 100644 index 000000000..9acac735b --- /dev/null +++ b/src/MuscleTendonPersonalization/Saving/savePassiveMomentData.m @@ -0,0 +1,26 @@ +function savePassiveMomentData(precalInputs, modeledValues, resultsDirectory) +modelPassiveMoments = permute(modeledValues.passiveModelMoments, [3 1 2]); +sizeTemp = size(modelPassiveMoments,1); +experimentalPassiveMoments = permute(precalInputs.passiveData.inverseDynamicsMoments, [3 1 2]); +numberOfMoments = size(modelPassiveMoments, 2); +dataLength = size(modelPassiveMoments, 1); +columnsWithAllZeros = all(experimentalPassiveMoments == 0, 1); +experimentalPassiveMoments = experimentalPassiveMoments(repmat(~columnsWithAllZeros, ... + size(experimentalPassiveMoments, 1), 1, 1)); +modelPassiveMoments = modelPassiveMoments(repmat(~columnsWithAllZeros, ... + size(modelPassiveMoments, 1), 1, 1)); +experimentalPassiveMoments = ... + reshape(experimentalPassiveMoments, sizeTemp, []); +experimentalPassiveMoments = ... + reshape(experimentalPassiveMoments', 1, numberOfMoments, dataLength); +modelPassiveMoments = ... + reshape(modelPassiveMoments, sizeTemp, []); +modelPassiveMoments = ... + reshape(modelPassiveMoments', 1, numberOfMoments, dataLength); +writeMtpDataToSto(precalInputs.passivePrefixes, precalInputs.prefixes, ... + experimentalPassiveMoments, fullfile(resultsDirectory, ... + "passiveJointMomentsExperimental"), "_passiveJointMomentsExperimental.sto") +writeMtpDataToSto(precalInputs.passivePrefixes, precalInputs.prefixes, ... + modelPassiveMoments, fullfile(resultsDirectory, ... + "passiveJointMomentsModeled"), "_passiveJointMomentsModeled.sto") +end \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/Saving/writeMtpDataToSto.m b/src/MuscleTendonPersonalization/Saving/writeMtpDataToSto.m new file mode 100644 index 000000000..5a217f83a --- /dev/null +++ b/src/MuscleTendonPersonalization/Saving/writeMtpDataToSto.m @@ -0,0 +1,9 @@ +function writeMtpDataToSto(columnLabels, taskNames, data, directory, fileName) +if ~exist(directory, "dir") + mkdir(directory); +end +for i = 1 : size(data,1) + writeToSto(columnLabels, 1:1:length(data(i,:,:)), ... + permute(data(i,:,:), [3 2 1]), strcat(directory, "\", taskNames(i), fileName)) +end +end \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResults.m b/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResults.m deleted file mode 100644 index 9c2676ee6..000000000 --- a/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResults.m +++ /dev/null @@ -1,39 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function writes to console or file the results of the optimization -% for easy evaluation by the user. It may require the input model to -% produce relative results or other information for output. -% -% (struct, struct) -> (None) -% Prints and plots results from muscle tendon personalization - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Robert Salati % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % -function reportMuscleTendonPersonalizationResults(resultsDirectory) - plotMuscleExcitationsAndActivations(resultsDirectory); - plotPassiveForceCurves(resultsDirectory); - plotPassiveMomentCurves(resultsDirectory); - plotJointMoments(resultsDirectory); - plotNormalizedFiberLengths(resultsDirectory); - plotHillTypeMuscleParams(resultsDirectory); -end - From 62a0025d7a8ac5a024ef777ec2fc914fb1297eda Mon Sep 17 00:00:00 2001 From: RobSalati Date: Wed, 13 Dec 2023 22:11:01 -0500 Subject: [PATCH 242/365] Renamed files --- ...gDo58XJtEd.xml => PztFgXV8694vmeaWq77sy6knLfEd.xml} | 0 .../PztFgXV8694vmeaWq77sy6knLfEp.xml | 2 ++ .../Tijkec3DG5uwce1DYpgDo58XJtEp.xml | 2 -- .../{extractSavedData.m => extractMtpDataFromSto.m} | 4 ++-- .../Analysis/plotHillTypeMuscleParams.m | 2 +- .../Analysis/plotJointMoments.m | 6 +++--- .../Analysis/plotMuscleExcitationsAndActivations.m | 8 ++++---- .../Analysis/plotNormalizedFiberLengths.m | 2 +- .../Analysis/plotPassiveForceCurves.m | 2 +- .../Analysis/plotPassiveMomentCurves.m | 4 ++-- .../Analysis/printJointMomentMatchingError.m | 4 ++-- .../Saving/getMtpResultsToSave.m | 10 ++++++++++ .../Saving/saveMuscleTendonPersonalizationResults.m | 1 + 13 files changed, 29 insertions(+), 18 deletions(-) rename resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/{Tijkec3DG5uwce1DYpgDo58XJtEd.xml => PztFgXV8694vmeaWq77sy6knLfEd.xml} (100%) create mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/PztFgXV8694vmeaWq77sy6knLfEp.xml delete mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Tijkec3DG5uwce1DYpgDo58XJtEp.xml rename src/MuscleTendonPersonalization/Analysis/{extractSavedData.m => extractMtpDataFromSto.m} (81%) diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Tijkec3DG5uwce1DYpgDo58XJtEd.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/PztFgXV8694vmeaWq77sy6knLfEd.xml similarity index 100% rename from resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Tijkec3DG5uwce1DYpgDo58XJtEd.xml rename to resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/PztFgXV8694vmeaWq77sy6knLfEd.xml diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/PztFgXV8694vmeaWq77sy6knLfEp.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/PztFgXV8694vmeaWq77sy6knLfEp.xml new file mode 100644 index 000000000..772d3f2f1 --- /dev/null +++ b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/PztFgXV8694vmeaWq77sy6knLfEp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Tijkec3DG5uwce1DYpgDo58XJtEp.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Tijkec3DG5uwce1DYpgDo58XJtEp.xml deleted file mode 100644 index 01277b0e5..000000000 --- a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Tijkec3DG5uwce1DYpgDo58XJtEp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/Analysis/extractSavedData.m b/src/MuscleTendonPersonalization/Analysis/extractMtpDataFromSto.m similarity index 81% rename from src/MuscleTendonPersonalization/Analysis/extractSavedData.m rename to src/MuscleTendonPersonalization/Analysis/extractMtpDataFromSto.m index 346af43df..9e161040c 100644 --- a/src/MuscleTendonPersonalization/Analysis/extractSavedData.m +++ b/src/MuscleTendonPersonalization/Analysis/extractMtpDataFromSto.m @@ -1,10 +1,10 @@ -function [columnNames, data] = extractSavedData(resultsDirectoryParent, resultsDirectoryChild) +function [columnNames, data] = extractMtpDataFromSto(resultsDirectoryParent, resultsDirectoryChild) import org.opensim.modeling.Storage if exist(fullfile(resultsDirectoryParent, resultsDirectoryChild), "dir") dataDir = dir(fullfile(resultsDirectoryParent, resultsDirectoryChild)); dataFiles = {dataDir(3:end).name}; else - fprintf("%s not found", resultsDirectoryChild); + fprintf("%s not found\n", resultsDirectoryChild); return end data = cell(1, numel(dataFiles)); diff --git a/src/MuscleTendonPersonalization/Analysis/plotHillTypeMuscleParams.m b/src/MuscleTendonPersonalization/Analysis/plotHillTypeMuscleParams.m index 39eb6e558..f248de63c 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotHillTypeMuscleParams.m +++ b/src/MuscleTendonPersonalization/Analysis/plotHillTypeMuscleParams.m @@ -1,5 +1,5 @@ function plotHillTypeMuscleParams(resultsDirectory) -[muscleNames, params] = extractSavedData( ... +[muscleNames, params] = extractMtpDataFromSto( ... resultsDirectory, "muscleModelParameters"); muscleNames = strrep(muscleNames, '_', ' '); figure(Name = "Muscle Model Parameters", ... diff --git a/src/MuscleTendonPersonalization/Analysis/plotJointMoments.m b/src/MuscleTendonPersonalization/Analysis/plotJointMoments.m index e347b57c5..bf984e38a 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotJointMoments.m +++ b/src/MuscleTendonPersonalization/Analysis/plotJointMoments.m @@ -1,9 +1,9 @@ function plotJointMoments(resultsDirectory) -[jointLabels, idMoments] = extractSavedData( ... +[jointLabels, idMoments] = extractMtpDataFromSto( ... resultsDirectory, "inverseDynamicsJointMoments"); -[~, noResidualMoments] = extractSavedData( ... +[~, noResidualMoments] = extractMtpDataFromSto( ... resultsDirectory, "modelJointMomentsNoSynx"); -[~, withResidualMoments] = extractSavedData( ... +[~, withResidualMoments] = extractMtpDataFromSto( ... resultsDirectory, "modelJointMomentsSynx"); jointLabels = strrep(jointLabels, '_', ' '); meanIdMoments = mean(idMoments, 3); diff --git a/src/MuscleTendonPersonalization/Analysis/plotMuscleExcitationsAndActivations.m b/src/MuscleTendonPersonalization/Analysis/plotMuscleExcitationsAndActivations.m index d806d3165..834b4987f 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotMuscleExcitationsAndActivations.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMuscleExcitationsAndActivations.m @@ -1,11 +1,11 @@ function plotMuscleExcitationsAndActivations(resultsDirectory) -[muscleNames, excitations] = extractSavedData( ... +[muscleNames, excitations] = extractMtpDataFromSto( ... resultsDirectory, "muscleExcitations"); -[~, activations] = extractSavedData( ... +[~, activations] = extractMtpDataFromSto( ... resultsDirectory, "muscleActivations"); -[~, excitationsSynx] = extractSavedData( ... +[~, excitationsSynx] = extractMtpDataFromSto( ... resultsDirectory, "muscleExcitationsSynx"); -[~, activationsSynx] = extractSavedData( ... +[~, activationsSynx] = extractMtpDataFromSto( ... resultsDirectory, "muscleActivationsSynx"); muscleNames = strrep(muscleNames, '_', ' '); meanExcitations = mean(excitations, 3); diff --git a/src/MuscleTendonPersonalization/Analysis/plotNormalizedFiberLengths.m b/src/MuscleTendonPersonalization/Analysis/plotNormalizedFiberLengths.m index 7055d007a..8cc121430 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotNormalizedFiberLengths.m +++ b/src/MuscleTendonPersonalization/Analysis/plotNormalizedFiberLengths.m @@ -1,5 +1,5 @@ function plotNormalizedFiberLengths(resultsDirectory) -[muscleNames, normalizedFiberLengths] = extractSavedData( ... +[muscleNames, normalizedFiberLengths] = extractMtpDataFromSto( ... resultsDirectory, "normalizedFiberLengths"); muscleNames = strrep(muscleNames, '_', ' '); meanFiberLengths = mean(normalizedFiberLengths, 3); diff --git a/src/MuscleTendonPersonalization/Analysis/plotPassiveForceCurves.m b/src/MuscleTendonPersonalization/Analysis/plotPassiveForceCurves.m index 5ca6e91e3..c3860a397 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotPassiveForceCurves.m +++ b/src/MuscleTendonPersonalization/Analysis/plotPassiveForceCurves.m @@ -1,5 +1,5 @@ function plotPassiveForceCurves(resultsDirectory) -[muscleNames, passiveForce] = extractSavedData( ... +[muscleNames, passiveForce] = extractMtpDataFromSto( ... resultsDirectory, "passiveForcesExperimental"); muscleNames = strrep(muscleNames, '_', ' '); meanPassiveForce = mean(passiveForce, 3); diff --git a/src/MuscleTendonPersonalization/Analysis/plotPassiveMomentCurves.m b/src/MuscleTendonPersonalization/Analysis/plotPassiveMomentCurves.m index 98dbada77..1ca637d12 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotPassiveMomentCurves.m +++ b/src/MuscleTendonPersonalization/Analysis/plotPassiveMomentCurves.m @@ -1,7 +1,7 @@ function plotPassiveMomentCurves(resultsDirectory) -[momentNames, passiveMomentsExperimental] = extractSavedData( ... +[momentNames, passiveMomentsExperimental] = extractMtpDataFromSto( ... resultsDirectory, "passiveJointMomentsExperimental"); -[~, passiveMomentsModel] = extractSavedData( ... +[~, passiveMomentsModel] = extractMtpDataFromSto( ... resultsDirectory, "passiveJointMomentsModeled"); momentNames = strrep(momentNames, '_', ' '); meanPassiveMomentsExperimental = mean(passiveMomentsExperimental, 3); diff --git a/src/MuscleTendonPersonalization/Analysis/printJointMomentMatchingError.m b/src/MuscleTendonPersonalization/Analysis/printJointMomentMatchingError.m index 3e09de8ac..261aa1710 100644 --- a/src/MuscleTendonPersonalization/Analysis/printJointMomentMatchingError.m +++ b/src/MuscleTendonPersonalization/Analysis/printJointMomentMatchingError.m @@ -1,7 +1,7 @@ function printJointMomentMatchingError(resultsDirectory) -[jointMomentLabels, muscleJointMoments] = extractSavedData( ... +[jointMomentLabels, muscleJointMoments] = extractMtpDataFromSto( ... resultsDirectory, "modelJointMomentsSynx"); -[~, inverseDynamicsMoments] = extractSavedData( ... +[~, inverseDynamicsMoments] = extractMtpDataFromSto( ... resultsDirectory, "inverseDynamicsJointMoments"); jointMomentsRmse = zeros(size(jointMomentLabels)); jointMomentsMae = zeros(size(jointMomentLabels)); diff --git a/src/MuscleTendonPersonalization/Saving/getMtpResultsToSave.m b/src/MuscleTendonPersonalization/Saving/getMtpResultsToSave.m index df737a5ff..9bdde999f 100644 --- a/src/MuscleTendonPersonalization/Saving/getMtpResultsToSave.m +++ b/src/MuscleTendonPersonalization/Saving/getMtpResultsToSave.m @@ -34,4 +34,14 @@ resultsStruct = struct("results", results, ... "resultsSynx", resultsSynx, ... "resultsSynxNoResiduals", resultsSynxNoResiduals); +if ~isempty(precalInputs) +finalOptimalFiberLength = ... + finalValues.optimalFiberLengthScaleFactors .* mtpInputs.optimalFiberLength; +finalValues.optimalFiberLengthScaleFactors = ... + finalOptimalFiberLength ./ precalInputs.optimalFiberLength; +finalTendonSlackLength = ... + finalValues.tendonSlackLengthScaleFactors .* mtpInputs.tendonSlackLength; +finalValues.tendonSlackLengthScaleFactors = ... + finalTendonSlackLength ./ precalInputs.tendonSlackLength; +end end \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m b/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m index b04a2241d..c190c85e0 100644 --- a/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m +++ b/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m @@ -36,6 +36,7 @@ function saveMuscleTendonPersonalizationResults(mtpInputs, finalValues, ... resultsSynx = resultsStruct.resultsSynx; resultsSynxNoResiduals = resultsStruct.resultsSynxNoResiduals; analysisDirectory = fullfile(resultsDirectory, "Analysis"); + if nargin < 6 precalInputs = []; end From ae5b1348a9f389142dc6aecdc9f60ef101f7abfa Mon Sep 17 00:00:00 2001 From: RobSalati Date: Thu, 14 Dec 2023 20:01:27 -0500 Subject: [PATCH 243/365] Removed temporary files and added documentation --- .../Dj1elW6LFUKwUlOO79e3l_KFnqIp.xml | 2 - .../H2ZBtSUaEqrhzwn-V7gzQTP8xkMd.xml | 6 -- .../H2ZBtSUaEqrhzwn-V7gzQTP8xkMp.xml | 2 - ...d.xml => chzmG-xRr23AFbYxIHXC11oFG9kd.xml} | 0 .../chzmG-xRr23AFbYxIHXC11oFG9kp.xml | 2 + .../JNQFPhtRfUmBhRG2yjt-nzPe-7od.xml | 6 -- .../JNQFPhtRfUmBhRG2yjt-nzPe-7op.xml | 2 - .../Analysis/extractMtpDataFromSto.m | 43 ++++++++-- .../Analysis/plotHillTypeMuscleParams.m | 35 +++++++- .../Analysis/plotJointMoments.m | 38 ++++++++- .../Analysis/plotMtpData_remove.m | 32 ------- .../plotMuscleExcitationsAndActivations.m | 43 ++++++++-- .../Analysis/plotNormalizedFiberLengths.m | 32 ++++++- .../Analysis/plotPassiveForceCurves.m | 32 ++++++- .../Analysis/plotPassiveMomentCurves.m | 44 ++++++++-- .../Analysis/printJointMomentMatchingError.m | 35 +++++++- ...eportMuscleTendonPersonalizationResults.m} | 2 +- .../Saving/getMtpResultsToSave.m | 31 +++++++ .../Saving/saveActivationAndExcitationData.m | 31 +++++++ .../Saving/saveJointMomentData.m | 32 +++++++ .../Saving/saveMuscleModelParameters.m | 35 ++++++++ ...aveMuscleTendonOptimizationParams_remove.m | 83 ------------------- .../saveMuscleTendonPersonalizationResults.m | 16 ++-- .../Saving/savePassiveForceData.m | 32 +++++++ .../Saving/savePassiveMomentData.m | 31 +++++++ .../Saving/writeMtpDataToSto.m | 31 +++++++ 26 files changed, 510 insertions(+), 168 deletions(-) delete mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Dj1elW6LFUKwUlOO79e3l_KFnqIp.xml delete mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/H2ZBtSUaEqrhzwn-V7gzQTP8xkMd.xml delete mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/H2ZBtSUaEqrhzwn-V7gzQTP8xkMp.xml rename resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/{Dj1elW6LFUKwUlOO79e3l_KFnqId.xml => chzmG-xRr23AFbYxIHXC11oFG9kd.xml} (100%) create mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/chzmG-xRr23AFbYxIHXC11oFG9kp.xml delete mode 100644 resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/JNQFPhtRfUmBhRG2yjt-nzPe-7od.xml delete mode 100644 resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/JNQFPhtRfUmBhRG2yjt-nzPe-7op.xml delete mode 100644 src/MuscleTendonPersonalization/Analysis/plotMtpData_remove.m rename src/MuscleTendonPersonalization/Analysis/{reportMuscleTendonPersonalizationResults_remove.m => reportMuscleTendonPersonalizationResults.m} (99%) delete mode 100644 src/MuscleTendonPersonalization/Saving/saveMuscleTendonOptimizationParams_remove.m diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Dj1elW6LFUKwUlOO79e3l_KFnqIp.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Dj1elW6LFUKwUlOO79e3l_KFnqIp.xml deleted file mode 100644 index c5cf2ca28..000000000 --- a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Dj1elW6LFUKwUlOO79e3l_KFnqIp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/H2ZBtSUaEqrhzwn-V7gzQTP8xkMd.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/H2ZBtSUaEqrhzwn-V7gzQTP8xkMd.xml deleted file mode 100644 index 7a6326b99..000000000 --- a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/H2ZBtSUaEqrhzwn-V7gzQTP8xkMd.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/H2ZBtSUaEqrhzwn-V7gzQTP8xkMp.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/H2ZBtSUaEqrhzwn-V7gzQTP8xkMp.xml deleted file mode 100644 index 16bf0bc90..000000000 --- a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/H2ZBtSUaEqrhzwn-V7gzQTP8xkMp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Dj1elW6LFUKwUlOO79e3l_KFnqId.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/chzmG-xRr23AFbYxIHXC11oFG9kd.xml similarity index 100% rename from resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Dj1elW6LFUKwUlOO79e3l_KFnqId.xml rename to resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/chzmG-xRr23AFbYxIHXC11oFG9kd.xml diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/chzmG-xRr23AFbYxIHXC11oFG9kp.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/chzmG-xRr23AFbYxIHXC11oFG9kp.xml new file mode 100644 index 000000000..9a0c6de9d --- /dev/null +++ b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/chzmG-xRr23AFbYxIHXC11oFG9kp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/JNQFPhtRfUmBhRG2yjt-nzPe-7od.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/JNQFPhtRfUmBhRG2yjt-nzPe-7od.xml deleted file mode 100644 index 7a6326b99..000000000 --- a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/JNQFPhtRfUmBhRG2yjt-nzPe-7od.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/JNQFPhtRfUmBhRG2yjt-nzPe-7op.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/JNQFPhtRfUmBhRG2yjt-nzPe-7op.xml deleted file mode 100644 index a2b074afc..000000000 --- a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/JNQFPhtRfUmBhRG2yjt-nzPe-7op.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/Analysis/extractMtpDataFromSto.m b/src/MuscleTendonPersonalization/Analysis/extractMtpDataFromSto.m index 9e161040c..1fde150df 100644 --- a/src/MuscleTendonPersonalization/Analysis/extractMtpDataFromSto.m +++ b/src/MuscleTendonPersonalization/Analysis/extractMtpDataFromSto.m @@ -1,15 +1,48 @@ -function [columnNames, data] = extractMtpDataFromSto(resultsDirectoryParent, resultsDirectoryChild) +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function reads all .sto files in resultsDirectory and converts them +% to a 3D array. Dimensions 1 & 2 represent rows and columns from a single +% file, and dimension 3 holds different files. Returns a row vector +% containing column headings read from the .sto file, and a 3D array +% containing data from all files in resultsDirectory. +% +% (string) -> (array), (array) +% Extracts data from .sto file. + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Robert Salati % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function [columnNames, data] = extractMtpDataFromSto(resultsDirectory) import org.opensim.modeling.Storage - if exist(fullfile(resultsDirectoryParent, resultsDirectoryChild), "dir") - dataDir = dir(fullfile(resultsDirectoryParent, resultsDirectoryChild)); + if exist(resultsDirectory, "dir") + dataDir = dir(resultsDirectory); dataFiles = {dataDir(3:end).name}; else - fprintf("%s not found\n", resultsDirectoryChild); + fprintf("%s not found\n", resultsDirectory); return end data = cell(1, numel(dataFiles)); for i = 1:numel(dataFiles) - dataStorage = Storage(fullfile(fullfile(resultsDirectoryParent, resultsDirectoryChild, dataFiles{i}))); + dataStorage = Storage(fullfile(fullfile(resultsDirectory, dataFiles{i}))); columnNames = getStorageColumnNames(dataStorage); data{i} = storageToDoubleMatrix(dataStorage)'; end diff --git a/src/MuscleTendonPersonalization/Analysis/plotHillTypeMuscleParams.m b/src/MuscleTendonPersonalization/Analysis/plotHillTypeMuscleParams.m index f248de63c..3caa53701 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotHillTypeMuscleParams.m +++ b/src/MuscleTendonPersonalization/Analysis/plotHillTypeMuscleParams.m @@ -1,6 +1,36 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function reads .sto files created by +% saveMuscleTendonPersonalizationResults.m containing optimized Hill-type +% muscle-tendon model parameters and creates a bar plot of them. +% +% (string) -> (None) +% Plot Hill-Type Muscle-Tendon model parameters from file. + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Di Ao, Marleny Vega, Robert Salati % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % function plotHillTypeMuscleParams(resultsDirectory) [muscleNames, params] = extractMtpDataFromSto( ... - resultsDirectory, "muscleModelParameters"); + fullfile(resultsDirectory, "muscleModelParameters")); muscleNames = strrep(muscleNames, '_', ' '); figure(Name = "Muscle Model Parameters", ... Units='normalized', ... @@ -23,5 +53,4 @@ function plotHillTypeMuscleParams(resultsDirectory) set(gca, yticklabels = [], fontsize=11); end end -end - +end \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/Analysis/plotJointMoments.m b/src/MuscleTendonPersonalization/Analysis/plotJointMoments.m index bf984e38a..803726ac7 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotJointMoments.m +++ b/src/MuscleTendonPersonalization/Analysis/plotJointMoments.m @@ -1,10 +1,40 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function reads .sto files created by +% saveMuscleTendonPersonalizationResults.m containing inverse dynamics +% joint moments and model joint moments and creates plots of them. +% +% (string) -> (None) +% Plot joint moment curves from file. + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Di Ao, Marleny Vega, Robert Salati % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % function plotJointMoments(resultsDirectory) [jointLabels, idMoments] = extractMtpDataFromSto( ... - resultsDirectory, "inverseDynamicsJointMoments"); + fullfile(resultsDirectory, "inverseDynamicsJointMoments")); [~, noResidualMoments] = extractMtpDataFromSto( ... - resultsDirectory, "modelJointMomentsNoSynx"); + fullfile(resultsDirectory, "modelJointMomentsNoSynx")); [~, withResidualMoments] = extractMtpDataFromSto( ... - resultsDirectory, "modelJointMomentsSynx"); + fullfile(resultsDirectory, "modelJointMomentsSynx")); jointLabels = strrep(jointLabels, '_', ' '); meanIdMoments = mean(idMoments, 3); stdIdMoments = std(idMoments, [], 3); @@ -38,7 +68,7 @@ function plotJointMoments(resultsDirectory) flipud(meanIdMoments(:,i)-stdIdMoments(:,i))]; fill([t, fliplr(t)]', noResidualFillRegion, 'r', FaceAlpha=0.2, ... EdgeColor='none', HandleVisibility='off') - fill([t, fliplr(t)]', idFillRegion, 'r', FaceAlpha=0.2, ... + fill([t, fliplr(t)]', idFillRegion, 'b', FaceAlpha=0.2, ... EdgeColor='none', HandleVisibility='off') hold off title(jointLabels(i), fontsize=12) diff --git a/src/MuscleTendonPersonalization/Analysis/plotMtpData_remove.m b/src/MuscleTendonPersonalization/Analysis/plotMtpData_remove.m deleted file mode 100644 index 0a7ca0f51..000000000 --- a/src/MuscleTendonPersonalization/Analysis/plotMtpData_remove.m +++ /dev/null @@ -1,32 +0,0 @@ -function plotMtpData_remove(data, options) - figure() - set(gcf,'Position',[750,400,950,700]) - t = 1:1:size(data.mean{1},1); - numWindows = ceil(sqrt(numel(data.labels))); - for i = 1 : numel(data.labels) - subplot(numWindows, numWindows, i); - hold on - for j = 1 : numel(data.mean) - mean = data.mean{j}; - std = data.std{j}; - - plot(mean(:,i), options.colors(j), lineWidth=2) - fillRegion = [(mean(:,i) + std(:,i)); flipud(mean(:,i) - std(:,i))]; - fill([t, fliplr(t)]', fillRegion, options.colors(j), FaceAlpha=0.2, ... - EdgeColor='none', HandleVisibility='off') - end - hold off - title(data.labels(i), FontSize=10, interpreter='latex') - axis(options.axisLimits) - if mod(i,3) == 1 - ylabel("Magnitude") - end - if i>numel(data.labels)-numWindows - xlabel("Time Points") - end - % if i == 1 && isfield(options, legend) - % legend(options.legend) - % end - end -end - diff --git a/src/MuscleTendonPersonalization/Analysis/plotMuscleExcitationsAndActivations.m b/src/MuscleTendonPersonalization/Analysis/plotMuscleExcitationsAndActivations.m index 834b4987f..2dfdc6328 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotMuscleExcitationsAndActivations.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMuscleExcitationsAndActivations.m @@ -1,12 +1,43 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function reads .sto files created by +% saveMuscleTendonPersonalizationResults.m containing muscle excitations +% and activations and creates plots of them. +% +% (string) -> (None) +% Plot muscle activations and excitations from file. + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Di Ao, Marleny Vega, Robert Salati % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + function plotMuscleExcitationsAndActivations(resultsDirectory) [muscleNames, excitations] = extractMtpDataFromSto( ... - resultsDirectory, "muscleExcitations"); + fullfile(resultsDirectory, "muscleExcitations")); [~, activations] = extractMtpDataFromSto( ... - resultsDirectory, "muscleActivations"); + fullfile(resultsDirectory, "muscleActivations")); [~, excitationsSynx] = extractMtpDataFromSto( ... - resultsDirectory, "muscleExcitationsSynx"); + fullfile(resultsDirectory, "muscleExcitationsSynx")); [~, activationsSynx] = extractMtpDataFromSto( ... - resultsDirectory, "muscleActivationsSynx"); + fullfile(resultsDirectory, "muscleActivationsSynx")); muscleNames = strrep(muscleNames, '_', ' '); meanExcitations = mean(excitations, 3); stdExcitations = std(excitations,[], 3); @@ -43,12 +74,12 @@ function plotMuscleExcitationsAndActivations(resultsDirectory) activationFillRegion = [(meanActivations(:,i)+stdActivations(:,i)); ... flipud((meanActivations(:,i)-stdActivations(:,i)))]; - fill([t, fliplr(t)]', activationFillRegion, 'b', FaceAlpha=0.2, ... + fill([t, fliplr(t)]', activationFillRegion, 'r', FaceAlpha=0.2, ... EdgeColor='none', HandleVisibility='off') activationSynxFillRegion = [(meanActivationsSynx(:,i)+stdActivationsSynx(:,i)); ... flipud((meanActivationsSynx(:,i)-stdActivationsSynx(:,i)))]; - fill([t, fliplr(t)]', activationSynxFillRegion, 'b', FaceAlpha=0.2, ... + fill([t, fliplr(t)]', activationSynxFillRegion, 'r', FaceAlpha=0.2, ... EdgeColor='none', HandleVisibility='off') set(gca, fontsize=11) axis([1 size(meanExcitations, 1) 0 1]) diff --git a/src/MuscleTendonPersonalization/Analysis/plotNormalizedFiberLengths.m b/src/MuscleTendonPersonalization/Analysis/plotNormalizedFiberLengths.m index 8cc121430..26f7e9581 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotNormalizedFiberLengths.m +++ b/src/MuscleTendonPersonalization/Analysis/plotNormalizedFiberLengths.m @@ -1,6 +1,36 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function reads .sto files created by +% saveMuscleTendonPersonalizationResults.m containing normalized fiber +% lengths and creates plots of them. +% +% (string) -> (None) +% Plot normalized fiber lengths from file. + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Di Ao, Marleny Vega, Robert Salati % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % function plotNormalizedFiberLengths(resultsDirectory) [muscleNames, normalizedFiberLengths] = extractMtpDataFromSto( ... - resultsDirectory, "normalizedFiberLengths"); + fullfile(resultsDirectory, "normalizedFiberLengths")); muscleNames = strrep(muscleNames, '_', ' '); meanFiberLengths = mean(normalizedFiberLengths, 3); stdFiberLengths = std(normalizedFiberLengths, [], 3); diff --git a/src/MuscleTendonPersonalization/Analysis/plotPassiveForceCurves.m b/src/MuscleTendonPersonalization/Analysis/plotPassiveForceCurves.m index c3860a397..3aee45c23 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotPassiveForceCurves.m +++ b/src/MuscleTendonPersonalization/Analysis/plotPassiveForceCurves.m @@ -1,6 +1,36 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function reads .sto files created by +% saveMuscleTendonPersonalizationResults.m containing passive force curves +% and creates plots of them. +% +% (string) -> (None) +% Plot passive force curves from file. + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Di Ao, Marleny Vega, Robert Salati % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % function plotPassiveForceCurves(resultsDirectory) [muscleNames, passiveForce] = extractMtpDataFromSto( ... - resultsDirectory, "passiveForcesExperimental"); + fullfile(resultsDirectory, "passiveForcesExperimental")); muscleNames = strrep(muscleNames, '_', ' '); meanPassiveForce = mean(passiveForce, 3); stdPassiveForce = std(passiveForce, [], 3); diff --git a/src/MuscleTendonPersonalization/Analysis/plotPassiveMomentCurves.m b/src/MuscleTendonPersonalization/Analysis/plotPassiveMomentCurves.m index 1ca637d12..693fa5038 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotPassiveMomentCurves.m +++ b/src/MuscleTendonPersonalization/Analysis/plotPassiveMomentCurves.m @@ -1,8 +1,39 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function reads .sto files created by +% saveMuscleTendonPersonalizationResults.m containing passive moment data +% and creates plots of experimental and modeled passive moment curves. +% +% (string) -> (None) +% Plot passive moment curves from file. + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Di Ao, Marleny Vega, Robert Salati % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + function plotPassiveMomentCurves(resultsDirectory) [momentNames, passiveMomentsExperimental] = extractMtpDataFromSto( ... - resultsDirectory, "passiveJointMomentsExperimental"); + fullfile(resultsDirectory, "passiveJointMomentsExperimental")); [~, passiveMomentsModel] = extractMtpDataFromSto( ... - resultsDirectory, "passiveJointMomentsModeled"); + fullfile(resultsDirectory, "passiveJointMomentsModeled")); momentNames = strrep(momentNames, '_', ' '); meanPassiveMomentsExperimental = mean(passiveMomentsExperimental, 3); stdPassiveMomentsExperimental = std(passiveMomentsExperimental, [], 3); @@ -23,21 +54,24 @@ function plotPassiveMomentCurves(resultsDirectory) subplot(numWindows, numWindows, i) hold on - plot(meanPassiveMomentsExperimental(:,i), 'b-', linewidth=2) + plot(meanPassiveMomentsExperimental(:,i), 'k-', linewidth=2) plot(meanPassiveMomentsModel(:,i), 'r-', linewidth=2) fillRegionExperimental = [(meanPassiveMomentsExperimental(:,i)+stdPassiveMomentsExperimental(:,i)); flipud((meanPassiveMomentsExperimental(:,i)-stdPassiveMomentsExperimental(:,i)))]; - fill([t, fliplr(t)]', fillRegionExperimental, 'b', FaceAlpha=0.2, ... + fill([t, fliplr(t)]', fillRegionExperimental, 'k', FaceAlpha=0.2, ... EdgeColor='none', HandleVisibility='off') fillRegionModel = [(meanPassiveMomentsModel(:,i)+stdPassiveMomentsModel(:,i)); flipud((meanPassiveMomentsModel(:,i)-stdPassiveMomentsModel(:,i)))]; - fill([t, fliplr(t)]', fillRegionModel, 'b', FaceAlpha=0.2, ... + fill([t, fliplr(t)]', fillRegionModel, 'r', FaceAlpha=0.2, ... EdgeColor='none', HandleVisibility='off') hold off set(gca, fontsize=11) title(momentNames(i), FontSize=12) axis([1 size(meanPassiveMomentsModel, 1) minMoment maxMoment]) + if i == 1 + legend("Experimental", "Model") + end if mod(i,4) == 1 ylabel("Moment [Nm]") end diff --git a/src/MuscleTendonPersonalization/Analysis/printJointMomentMatchingError.m b/src/MuscleTendonPersonalization/Analysis/printJointMomentMatchingError.m index 261aa1710..882651805 100644 --- a/src/MuscleTendonPersonalization/Analysis/printJointMomentMatchingError.m +++ b/src/MuscleTendonPersonalization/Analysis/printJointMomentMatchingError.m @@ -1,8 +1,39 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function reads .sto files created by +% saveMuscleTendonPersonalizationResults.m containing model and +% experimental joint moments and outputs RMSE error via print command. +% +% (string) -> (None) +% Print joint moment matching error from file. + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Di Ao, Marleny Vega, Robert Salati % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + function printJointMomentMatchingError(resultsDirectory) [jointMomentLabels, muscleJointMoments] = extractMtpDataFromSto( ... - resultsDirectory, "modelJointMomentsSynx"); + fullfile(resultsDirectory, "modelJointMomentsSynx")); [~, inverseDynamicsMoments] = extractMtpDataFromSto( ... - resultsDirectory, "inverseDynamicsJointMoments"); + fullfile(resultsDirectory, "inverseDynamicsJointMoments")); jointMomentsRmse = zeros(size(jointMomentLabels)); jointMomentsMae = zeros(size(jointMomentLabels)); for i = 1 : size(muscleJointMoments, 2) diff --git a/src/MuscleTendonPersonalization/Analysis/reportMuscleTendonPersonalizationResults_remove.m b/src/MuscleTendonPersonalization/Analysis/reportMuscleTendonPersonalizationResults.m similarity index 99% rename from src/MuscleTendonPersonalization/Analysis/reportMuscleTendonPersonalizationResults_remove.m rename to src/MuscleTendonPersonalization/Analysis/reportMuscleTendonPersonalizationResults.m index 5ef1df320..738751875 100644 --- a/src/MuscleTendonPersonalization/Analysis/reportMuscleTendonPersonalizationResults_remove.m +++ b/src/MuscleTendonPersonalization/Analysis/reportMuscleTendonPersonalizationResults.m @@ -29,7 +29,7 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function reportMuscleTendonPersonalizationResults_remove(optimizedParams, ... +function reportMuscleTendonPersonalizationResults(optimizedParams, ... mtpInputs, precalInputs) if nargin < 3; precalInputs = []; end [finalValues, results, resultsSynx, resultsSynxNoResiduals] = ... diff --git a/src/MuscleTendonPersonalization/Saving/getMtpResultsToSave.m b/src/MuscleTendonPersonalization/Saving/getMtpResultsToSave.m index 9bdde999f..01f1495d5 100644 --- a/src/MuscleTendonPersonalization/Saving/getMtpResultsToSave.m +++ b/src/MuscleTendonPersonalization/Saving/getMtpResultsToSave.m @@ -1,3 +1,34 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function takes MTP inputs and optimized muscle parameters and +% calculates normalized fiber length, passive force, passive moment, muscle +% activation, and muscle excitation curves to be later saved. +% +% (struct), (struct), (cell), (struct) -> (struct), (struct), (struct) +% Calculates relevant MTP curves + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Robert Salati % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + function [finalValues, resultsStruct, modeledValues] = ... getMtpResultsToSave(mtpInputs, params, optimizedParams, precalInputs) finalValues = makeMtpValuesAsStruct([], optimizedParams, zeros(1, 7)); diff --git a/src/MuscleTendonPersonalization/Saving/saveActivationAndExcitationData.m b/src/MuscleTendonPersonalization/Saving/saveActivationAndExcitationData.m index 93e65c0ee..bef650cf3 100644 --- a/src/MuscleTendonPersonalization/Saving/saveActivationAndExcitationData.m +++ b/src/MuscleTendonPersonalization/Saving/saveActivationAndExcitationData.m @@ -1,3 +1,34 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function saves muscle exctiations and activations with and without +% SynX to their appropriate .sto files in a directory specified by +% resultsDirectory. +% +% (struct, struct, struct, string) -> (None) +% Saves muscle activation and excitation data to .sto file. + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Robert Salati % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + function saveActivationAndExcitationData(mtpInputs, results, ... resultsSynx, resultsDirectory) writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... diff --git a/src/MuscleTendonPersonalization/Saving/saveJointMomentData.m b/src/MuscleTendonPersonalization/Saving/saveJointMomentData.m index 24b6fdb79..dac13f3af 100644 --- a/src/MuscleTendonPersonalization/Saving/saveJointMomentData.m +++ b/src/MuscleTendonPersonalization/Saving/saveJointMomentData.m @@ -1,3 +1,35 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function saves experimental joint moment data, and modeled joint +% moment data without SynX, with SynX, and with SynX with no residuals to +% their appropriate .sto files in a directory specified by +% resultsDirectory. +% +% (struct, struct, struct, struct, struct, string) -> (None) +% Saves joint moment data to .sto files. + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Robert Salati % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + function saveJointMomentData(mtpInputs, results, resultsSynx, ... resultsSynxNoResiduals, resultsDirectory) writeMtpDataToSto(mtpInputs.coordinateNames, mtpInputs.prefixes, ... diff --git a/src/MuscleTendonPersonalization/Saving/saveMuscleModelParameters.m b/src/MuscleTendonPersonalization/Saving/saveMuscleModelParameters.m index a8c03bc02..4257034ae 100644 --- a/src/MuscleTendonPersonalization/Saving/saveMuscleModelParameters.m +++ b/src/MuscleTendonPersonalization/Saving/saveMuscleModelParameters.m @@ -1,3 +1,38 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function saves optimized Hill-type muscle-tendon model parameters +% output by MTP to a .sto file in a directory specified by +% resultsDirectory. Each row in the .sto file corresponds to a parameter. +% In order from top to bottom, these are: activation time constants, +% activation nonlinearity constants, electromechanical delays, emg scale +% factors, optimal fiber length scale factors, tendon slack length scale +% factors. +% +% (struct, struct, struct, struct, struct, string) -> (None) +% Saves joint moment data to .sto files. + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Robert Salati % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + function saveMuscleModelParameters(mtpInputs, finalValues, resultsDirectory) if ~exist(resultsDirectory, "dir") mkdir(resultsDirectory); diff --git a/src/MuscleTendonPersonalization/Saving/saveMuscleTendonOptimizationParams_remove.m b/src/MuscleTendonPersonalization/Saving/saveMuscleTendonOptimizationParams_remove.m deleted file mode 100644 index 33ef45f6a..000000000 --- a/src/MuscleTendonPersonalization/Saving/saveMuscleTendonOptimizationParams_remove.m +++ /dev/null @@ -1,83 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function saves optimization results to .sto files to be plotted by -% the user in either MATLAB or Opensim -% -% (struct, struct, struct, string) -> (None) -% Saves MTP optimization parameters to .sto files to be plotted. -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Robert Salati, Di Ao, Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % -function saveMuscleTendonOptimizationParams_remove(resultsDirectory, optimizedParams, ... - mtpInputs, precalInputs) -if nargin < 4 - precalInputs = []; -end -[finalValues, results, resultsSynx, resultsSynxNoResiduals] = ... - getValuesToSave(mtpInputs, optimizedParams); -if ~isempty(precalInputs) - modeledValues = getMuscleTendonLengthInitializationData(precalInputs, ... - mtpInputs); % Modeled passive force data & params from experimental data. - savePassiveMomentData(precalInputs, modeledValues, resultsDirectory); - savePassiveForceData(mtpInputs, modeledValues, results, resultsSynx, ... - resultsSynxNoResiduals, resultsDirectory); -end - -saveActivationAndExcitationData(mtpInputs, results, resultsSynx, resultsDirectory); -writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, results.normalizedFiberLength, ... - fullfile(resultsDirectory, "normalizedFiberLengths"), "_normalizedFiberLengths.sto") -saveJointMomentData(mtpInputs, results, resultsSynx, resultsSynxNoResiduals, ... - resultsDirectory); -saveMuscleModelParameters(mtpInputs, finalValues, fullfile(resultsDirectory, ... - "muscleModelParameters")); -end - -function [finalValues, results, resultsSynx, resultsSynxNoResiduals] = ... - getValuesToSave(mtpInputs, optimizedParams) - -finalValues = makeMtpValuesAsStruct([], optimizedParams, zeros(1, 7)); -resultsSynx = calcMtpSynXModeledValues(finalValues, mtpInputs, struct()); -finalValues.synergyWeights(mtpInputs.numberOfExtrapolationWeights + 1 : end) = 0; -resultsSynxNoResiduals = calcMtpSynXModeledValues(finalValues, mtpInputs, struct()); -results = calcMtpModeledValues(finalValues, mtpInputs, struct()); -results.time = mtpInputs.emgTime(:, mtpInputs.numPaddingFrames + 1 : ... - end - mtpInputs.numPaddingFrames); -results.muscleExcitations = results.muscleExcitations(:, :, ... - mtpInputs.numPaddingFrames + 1 : end - mtpInputs.numPaddingFrames); -resultsSynx.muscleExcitations = resultsSynx.muscleExcitations(:, :, ... - mtpInputs.numPaddingFrames + 1 : end - mtpInputs.numPaddingFrames); -end - -function modeledValues = getMuscleTendonLengthInitializationData(... - precalInputs, mtpInputs) - -tempValues.optimalFiberLengthScaleFactors = ... - mtpInputs.optimalFiberLength ./ precalInputs.optimalFiberLength; - -tempValues.tendonSlackLengthScaleFactors = ... - mtpInputs.tendonSlackLength ./ precalInputs.tendonSlackLength; - -precalInputs.maxIsometricForce = mtpInputs.maxIsometricForce; - -precalInputs.optimizeIsometricMaxForce = 0; - -modeledValues = calcMuscleTendonLengthInitializationModeledValues(tempValues, precalInputs); -end \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m b/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m index c190c85e0..19ab9429e 100644 --- a/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m +++ b/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m @@ -1,11 +1,13 @@ % This function is part of the NMSM Pipeline, see file for full license. +% +% This function takes the input, finalValues, result structs and writes to +% the appropriate .osimx muscle model, .sto joint moments, .sto muscle +% activations & excitations, .sto Hill-Type muscle-tendon model parameter, +% .sto passive forces, and .sto passive moment files. The model is included +% in instances where the results are relative to the original model, which +% is used for reference. % -% This function takes the result struct and writes the values to the -% appropriate .osimx muscle model, .mot muscle moment, and .sto muscle -% activation files. The model is included in instances where the results -% are relative to the original model, which is used for reference. -% -% (Model, struct, string, string, string) -> (None) +% (struct, struct, struct, struct, string, struct) -> (None) % Saves results in the struct to the given filenames % ----------------------------------------------------------------------- % @@ -16,7 +18,7 @@ % National Institutes of Health (R01 EB030520). % % % % Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega, Claire V. Hammond % +% Author(s): Marleny Vega, Claire V. Hammond, Robert Salati % % % % Licensed under the Apache License, Version 2.0 (the "License"); % % you may not use this file except in compliance with the License. % diff --git a/src/MuscleTendonPersonalization/Saving/savePassiveForceData.m b/src/MuscleTendonPersonalization/Saving/savePassiveForceData.m index 0c9da4dd1..af0b39881 100644 --- a/src/MuscleTendonPersonalization/Saving/savePassiveForceData.m +++ b/src/MuscleTendonPersonalization/Saving/savePassiveForceData.m @@ -1,3 +1,35 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function saves experimental passive force data, modeled passive +% force data without SynX, with SynX, and with SynX with no residuals to +% their appropriate .sto files in a directory specified by +% resultsDirectory. +% +% (struct, struct, struct, struct, struct, string) -> (None) +% Saves passive force data to .sto files. + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Robert Salati % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + function savePassiveForceData(mtpInputs, modeledValues, results, resultsSynx, ... resultsSynxNoResiduals, resultsDirectory) writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... diff --git a/src/MuscleTendonPersonalization/Saving/savePassiveMomentData.m b/src/MuscleTendonPersonalization/Saving/savePassiveMomentData.m index 9acac735b..773afb7c0 100644 --- a/src/MuscleTendonPersonalization/Saving/savePassiveMomentData.m +++ b/src/MuscleTendonPersonalization/Saving/savePassiveMomentData.m @@ -1,3 +1,34 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function formats experimental and modeled passive moment data and +% saves them to appropriate .sto files in a directory specified by +% resultsDirectory. +% +% (struct, struct, string) -> (None) +% Saves passive moment data to .sto file. + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Robert Salati % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + function savePassiveMomentData(precalInputs, modeledValues, resultsDirectory) modelPassiveMoments = permute(modeledValues.passiveModelMoments, [3 1 2]); sizeTemp = size(modelPassiveMoments,1); diff --git a/src/MuscleTendonPersonalization/Saving/writeMtpDataToSto.m b/src/MuscleTendonPersonalization/Saving/writeMtpDataToSto.m index 5a217f83a..08a843c59 100644 --- a/src/MuscleTendonPersonalization/Saving/writeMtpDataToSto.m +++ b/src/MuscleTendonPersonalization/Saving/writeMtpDataToSto.m @@ -1,3 +1,34 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function formats and saves MTP data to a .sto file. It splits data +% by gait cycle and saves each gait cycle to a separate file with the +% appropriate prefix. If the output directory does not already exist, it is +% created. +% +% (array), (cell), (array), (string), (string) -> (none) +% Saves MTP data to a .sto file. + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Robert Salati % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % function writeMtpDataToSto(columnLabels, taskNames, data, directory, fileName) if ~exist(directory, "dir") mkdir(directory); From 9f2d8324139b9c07c1d2670c8be90482ca0e9135 Mon Sep 17 00:00:00 2001 From: RobSalati Date: Thu, 14 Dec 2023 20:18:48 -0500 Subject: [PATCH 244/365] bug fix with no precal --- src/MuscleTendonPersonalization/Saving/getMtpResultsToSave.m | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/MuscleTendonPersonalization/Saving/getMtpResultsToSave.m b/src/MuscleTendonPersonalization/Saving/getMtpResultsToSave.m index 01f1495d5..f56cc2fd1 100644 --- a/src/MuscleTendonPersonalization/Saving/getMtpResultsToSave.m +++ b/src/MuscleTendonPersonalization/Saving/getMtpResultsToSave.m @@ -34,6 +34,7 @@ finalValues = makeMtpValuesAsStruct([], optimizedParams, zeros(1, 7)); if nargin < 4 modeledValues = []; + precalInputs = []; else tempValues.optimalFiberLengthScaleFactors = ... mtpInputs.optimalFiberLength ./ precalInputs.optimalFiberLength; @@ -47,9 +48,6 @@ end end -if precalInputs.optimizeIsometricMaxForce - finalValues.maxIsometricForce = mtpInputs.maxIsometricForce; -end results = calcMtpModeledValues(finalValues, mtpInputs, struct()); results.time = mtpInputs.emgTime(:, mtpInputs.numPaddingFrames + 1 : ... end - mtpInputs.numPaddingFrames); From 501adec85429c8c5671d1b2f8a7484fa91c101a2 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 17 Dec 2023 16:20:28 -0600 Subject: [PATCH 245/365] changes to mtp to work with uf4 --- ...cNormalizedFiberLengthMeanSimilarityCost.m | 13 +- .../MuscleTendonLengthInitialization.m | 2 +- ...leTendonLengthInitializationSettingsTree.m | 5 +- .../MuscleTendonPersonalization.m | 72 ++++--- .../MuscleTendonPersonalizationTool.m | 10 +- .../calcDifferencesInEmgGroups.m | 3 +- .../calcMuscleTendonNonLinearConstraints.m | 6 +- ...rmalizedFiberLengthGroupedSimilarityCost.m | 13 +- ...SynergyExtrapolationMuscleActivationCost.m | 7 +- .../Optimization/calcMtpCost.m | 65 ++++-- .../computeMuscleTendonCostFunction.m | 18 +- .../computeMuscleTendonRoundOptimization.m | 6 +- .../Optimization/makeMtpValuesAsStruct.m | 7 +- .../Optimization/updateDesignVariables.m | 4 +- .../calcMuscleExcitationsSynX.m | 8 +- .../getLinearInequalityConstraints.m | 2 +- .../SynergyExtrapolation/getSynergyCommands.m | 2 +- ...eMuscleTendonPersonalizationSettingsTree.m | 30 +-- ...reportMuscleTendonPersonalizationResults.m | 185 +++++++++++++++++- ...NeuralControlPersonalizationSettingsTree.m | 4 +- src/Preprocessing/splitIntoTrials.m | 5 +- src/core/parse/parseSpaceSeparatedList.m | 3 +- 22 files changed, 363 insertions(+), 107 deletions(-) diff --git a/src/MuscleTendonLengthInitialization/CostComputations/calcNormalizedFiberLengthMeanSimilarityCost.m b/src/MuscleTendonLengthInitialization/CostComputations/calcNormalizedFiberLengthMeanSimilarityCost.m index a24ecd428..1c93ed5af 100644 --- a/src/MuscleTendonLengthInitialization/CostComputations/calcNormalizedFiberLengthMeanSimilarityCost.m +++ b/src/MuscleTendonLengthInitialization/CostComputations/calcNormalizedFiberLengthMeanSimilarityCost.m @@ -26,20 +26,21 @@ % ----------------------------------------------------------------------- % function cost = calcNormalizedFiberLengthMeanSimilarityCost( ... - modeledValues, experimentalData, costTerm) + modeledValues, inputs, costTerm) errorCenter = valueOrAlternate(costTerm, "errorCenter", 0); maximumAllowableError = valueOrAlternate(costTerm, "maxAllowableError", 0.1); Ind = 1; -for i = 1:length(experimentalData.normalizedFiberLengthGroups) +lowestIndex = min(cell2mat(inputs.normalizedFiberLengthGroups)) - 1; +for i = 1:length(inputs.normalizedFiberLengthGroups) normalizedFiberLengthSimilarity = ... abs(modeledValues.normalizedFiberLength(:, ... - experimentalData.normalizedFiberLengthGroups{i}, :) - ... + inputs.normalizedFiberLengthGroups{i} - lowestIndex, :) - ... mean(modeledValues.normalizedFiberLength(:, ... - experimentalData.normalizedFiberLengthGroups{i}, :), 2)); + inputs.normalizedFiberLengthGroups{i} - lowestIndex, :), 2)); normalizedFiberLengthSimilarityCost(Ind : Ind + ... - size(experimentalData.normalizedFiberLengthGroups{i}, 2) - 1) = ... + size(inputs.normalizedFiberLengthGroups{i}, 2) - 1) = ... log(sum(exp(500 * (normalizedFiberLengthSimilarity)), [1 3])) / 500; -Ind = Ind + size(experimentalData.normalizedFiberLengthGroups{i}, 2); +Ind = Ind + size(inputs.normalizedFiberLengthGroups{i}, 2); end cost = calcDeviationCostArray(... diff --git a/src/MuscleTendonLengthInitialization/MuscleTendonLengthInitialization.m b/src/MuscleTendonLengthInitialization/MuscleTendonLengthInitialization.m index a063cafd7..4f6e6ef3d 100644 --- a/src/MuscleTendonLengthInitialization/MuscleTendonLengthInitialization.m +++ b/src/MuscleTendonLengthInitialization/MuscleTendonLengthInitialization.m @@ -73,7 +73,7 @@ % setup optimizer options struct to pass to fmincon function output = makeOptimizerOptions(params) output = optimset('UseParallel', true); -output.MaxIter = valueOrAlternate(params, 'maxIterations', 10000); +output.MaxIter = valueOrAlternate(params, 'maxIterations', 100); output.MaxFunEvals = valueOrAlternate(params, ... 'maxFunctionEvaluations', 100000000); output.Display = 'iter'; diff --git a/src/MuscleTendonLengthInitialization/parseMuscleTendonLengthInitializationSettingsTree.m b/src/MuscleTendonLengthInitialization/parseMuscleTendonLengthInitializationSettingsTree.m index 778ee2595..b6077ffba 100644 --- a/src/MuscleTendonLengthInitialization/parseMuscleTendonLengthInitializationSettingsTree.m +++ b/src/MuscleTendonLengthInitialization/parseMuscleTendonLengthInitializationSettingsTree.m @@ -35,6 +35,7 @@ inputs = getInputs(settingsTree); inputs = getMtpModelInputs(inputs); inputs = getMuscleVolume(inputs); + inputs = rmfield(inputs, "model"); else inputs = false; end @@ -143,10 +144,12 @@ normalizedFiberLengthGroupNames, inputs.model); inputs.numMuscleGroups = numel(inputs.normalizedFiberLengthGroups); numMuscles = length(inputs.muscleNames); +lowestIndex = min(cell2mat(inputs.normalizedFiberLengthGroups)); for i = 1 : inputs.numMuscleGroups for j = 1 : numel(inputs.normalizedFiberLengthGroups{i}) inputs.groupedMaxNormalizedFiberLength(... - inputs.normalizedFiberLengthGroups{i}(j)) = i; + inputs.normalizedFiberLengthGroups{i}(j) ... + - lowestIndex + 1) = i; end end inputs.numMusclesIndividual = 0; diff --git a/src/MuscleTendonPersonalization/MuscleTendonPersonalization.m b/src/MuscleTendonPersonalization/MuscleTendonPersonalization.m index 31ddaa28a..56b0d33d0 100644 --- a/src/MuscleTendonPersonalization/MuscleTendonPersonalization.m +++ b/src/MuscleTendonPersonalization/MuscleTendonPersonalization.m @@ -49,17 +49,24 @@ for i=1:length(inputs.tasks) [taskValues, taskLowerBounds, taskUpperBounds] = makeTaskValues( ... primaryValues, inputs.tasks{i}, lowerBounds, upperBounds); - taskParams = makeTaskParams( ... - inputs.tasks{i}, ... - params, ... - inputs.synergyExtrapolation); - [A, b] = getLinearInequalityConstraints(inputs.synergyExtrapolation, ... - 6 * length(inputs.muscleNames), inputs.extrapolationCommands, ... - permute(inputs.emgData, [3 1 2])); -% optimizedValues = taskValues; - optimizedValues = computeMuscleTendonRoundOptimization(taskValues, ... - primaryValues, inputs.tasks{i}.isIncluded, taskLowerBounds, ... - taskUpperBounds, inputs, taskParams, optimizerOptions, A, b); + taskParams = makeTaskParams(params); + if isfield(inputs, "synergyExtrapolation") + taskParams.costTerms = ... + [inputs.tasks{i}.costTerms, inputs.synergyExtrapolation.costTerms]; + [A, b] = getLinearInequalityConstraints(inputs.synergyExtrapolation, ... + 6 * length(inputs.muscleNames), inputs.extrapolationCommands, ... + permute(inputs.emgData, [3 1 2])); +% optimizedValues = taskValues; + optimizedValues = computeMuscleTendonRoundOptimization(taskValues, ... + primaryValues, inputs.tasks{i}.isIncluded, taskLowerBounds, ... + taskUpperBounds, inputs, taskParams, optimizerOptions, A, b); + else + taskParams.costTerms = inputs.tasks{i}.costTerms; +% optimizedValues = taskValues; + optimizedValues = computeMuscleTendonRoundOptimization(taskValues, ... + primaryValues, inputs.tasks{i}.isIncluded, taskLowerBounds, ... + taskUpperBounds, inputs, taskParams, optimizerOptions, [], []); + end primaryValues = updateDesignVariables(primaryValues, ... optimizedValues, inputs.tasks{i}.isIncluded); end @@ -75,7 +82,7 @@ function verifyInputs(inputs) for i=1:length(inputs.tasks) try verifyNumeric(inputs.tasks{i}.isIncluded); catch; throw(MException('',strcat('invalid isIncluded boolean', ... - 'array for task ', num2str(i)))); + 'array for task ', num2str(i)))); end end end @@ -103,14 +110,22 @@ function verifyParams(params) values{4} = repmat(0.5, 1, numMuscles); % EMG scale factors values{5} = repmat(1, 1, numMuscles); % optimal fiber length scale factor values{6} = repmat(1, 1, numMuscles); % tendon slack length scale factor -values{7} = repmat(0, 1, inputs.numberOfExtrapolationWeights + ... - inputs.numberOfResidualWeights); % synergy commands +if isfield(inputs, "synergyExtrapolation") + values{7} = repmat(0, 1, inputs.numberOfExtrapolationWeights + ... + inputs.numberOfResidualWeights); % synergy commands +end end function inputs = finalizeInputs(inputs, primaryValues, params) -values = makeMtpValuesAsStruct(struct(), primaryValues, zeros(1, 7)); +values = makeMtpValuesAsStruct(struct(), primaryValues, zeros(1, 7), inputs); modeledValues = calcMtpModeledValues(values, inputs, params); inputs = mergeStructs(inputs, modeledValues); +inputs = rmfield(inputs, "model"); +if ~isfield(inputs, "synergyExtrapolation") + for i = 1:length(inputs.tasks) + inputs.tasks{i}.isIncluded(7) = 0; + end +end end % (struct, struct) -> (6 x numEnabledMuscles matrix of number) @@ -125,8 +140,10 @@ function verifyParams(params) lowerBounds{4} = repmat(0.05, 1, numMuscles); % EMG scale factors lowerBounds{5} = repmat(0.6, 1, numMuscles); % optimal fiber length scale factor lowerBounds{6} = repmat(0.6, 1, numMuscles); % tendon slack length scale factor - lowerBounds{7} = repmat(-100, 1, inputs.numberOfExtrapolationWeights + ... - inputs.numberOfResidualWeights); % synergy commands + if isfield(inputs, "synergyExtrapolation") + lowerBounds{7} = repmat(-100, 1, inputs.numberOfExtrapolationWeights + ... + inputs.numberOfResidualWeights); % synergy commands + end end end @@ -142,8 +159,10 @@ function verifyParams(params) upperBounds{4} = repmat(1, 1, numMuscles); % EMG scale factors upperBounds{5} = repmat(1.4, 1, numMuscles); % optimal fiber length scale factor upperBounds{6} = repmat(1.4, 1, numMuscles); % tendon slack length scale factor - upperBounds{7} = repmat(100, 1, inputs.numberOfExtrapolationWeights + ... - inputs.numberOfResidualWeights); % synergy commands + if isfield(inputs, "synergyExtrapolation") + upperBounds{7} = repmat(100, 1, inputs.numberOfExtrapolationWeights + ... + inputs.numberOfResidualWeights); % synergy commands + end end end @@ -162,6 +181,8 @@ function verifyParams(params) output.Display = 'iter'; output.Hessian = 'lbfgs'; output.GradObj = 'off'; +output.DiffMaxChange = 10; +output.DiffMinChange = 1e-5; end % (struct, struct) -> (Array of number) @@ -172,19 +193,18 @@ function verifyParams(params) taskLowerBounds = []; taskUpperBounds = []; for i = 1:length(taskInputs.isIncluded) - if(taskInputs.isIncluded(i)) - taskValues = [taskValues primaryValues{i}]; - taskLowerBounds = [taskLowerBounds lowerBounds{i}]; - taskUpperBounds = [taskUpperBounds upperBounds{i}]; - end + if(taskInputs.isIncluded(i)) + taskValues = [taskValues primaryValues{i}]; + taskLowerBounds = [taskLowerBounds lowerBounds{i}]; + taskUpperBounds = [taskUpperBounds upperBounds{i}]; + end end end % (struct, struct) -> (struct) % prepare optimizer parameters for the given task -function taskParams = makeTaskParams(taskInputs, params, synergyExtrapolation) +function taskParams = makeTaskParams(params) taskParams = params; -taskParams.costTerms = [taskInputs.costTerms, synergyExtrapolation.costTerms]; if(~isfield(params, 'maxIterations')) taskParams.maxIterations = 2e3; end diff --git a/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m b/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m index 9e843563d..cd3ecba8c 100644 --- a/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m +++ b/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m @@ -50,15 +50,19 @@ function MuscleTendonPersonalizationTool(settingsFileName) else reportMuscleTendonPersonalizationResults(optimizedParams, inputs); end -finalValues = makeMtpValuesAsStruct([], optimizedParams, zeros(1, 7)); +finalValues = makeMtpValuesAsStruct([], optimizedParams, zeros(1, 7), inputs); if precalInputs.optimizeIsometricMaxForce finalValues.maxIsometricForce = inputs.maxIsometricForce; end -results = calcMtpSynXModeledValues(finalValues, inputs, params); +if isfield(inputs, "synergyExtrapolation") + results = calcMtpSynXModeledValues(finalValues, inputs, params); +else + results = calcMtpModeledValues(finalValues, inputs, params); +end results.time = inputs.emgTime(:, inputs.numPaddingFrames + 1 : ... end - inputs.numPaddingFrames); -saveMuscleTendonPersonalizationResults(inputs.model, ... +saveMuscleTendonPersonalizationResults(inputs.modelFileName, ... inputs.osimxFileName, inputs.prefixes, inputs.coordinateNames, ... finalValues, results, resultsDirectory, inputs.muscleTendonColumnNames); end diff --git a/src/MuscleTendonPersonalization/Optimization/CostComputations/calcDifferencesInEmgGroups.m b/src/MuscleTendonPersonalization/Optimization/CostComputations/calcDifferencesInEmgGroups.m index 997660790..475a262f4 100644 --- a/src/MuscleTendonPersonalization/Optimization/CostComputations/calcDifferencesInEmgGroups.m +++ b/src/MuscleTendonPersonalization/Optimization/CostComputations/calcDifferencesInEmgGroups.m @@ -31,10 +31,11 @@ function deviationsEMGScale = calcDifferencesInEmgGroups( ... emgScale, activationGroups) +lowestIndex = min(cell2mat(activationGroups)) - 1; Ind = 1; for i = 1:length(activationGroups) deviationsEMGScale(:, Ind:Ind + size(activationGroups{i}, 2) - 1) = ... - calcMeanDifference2D(emgScale(activationGroups{i})); + calcMeanDifference2D(emgScale(activationGroups{i} - lowestIndex)); Ind = Ind + size(activationGroups{i}, 2); end end \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/Optimization/CostComputations/calcMuscleTendonNonLinearConstraints.m b/src/MuscleTendonPersonalization/Optimization/CostComputations/calcMuscleTendonNonLinearConstraints.m index 6b70d1f98..8c28fef96 100644 --- a/src/MuscleTendonPersonalization/Optimization/CostComputations/calcMuscleTendonNonLinearConstraints.m +++ b/src/MuscleTendonPersonalization/Optimization/CostComputations/calcMuscleTendonNonLinearConstraints.m @@ -26,11 +26,11 @@ % ----------------------------------------------------------------------- % function [c, ceq] = calcMuscleTendonNonLinearConstraints(values, ... - primaryValues, isIncluded, experimentalData, params) + primaryValues, isIncluded, inputs, params) ceq = []; -values = makeMtpValuesAsStruct(values, primaryValues, isIncluded); -modeledValues = calcMtpModeledValues(values, experimentalData, struct()); +values = makeMtpValuesAsStruct(values, primaryValues, isIncluded, inputs); +modeledValues = calcMtpModeledValues(values, inputs, struct()); softmaxlmtildaCon = log(sum(exp(500 * ... (modeledValues.normalizedFiberLength - 1.3)), [1 3])) / 500; % max lmtilda less than 1.5 softminlmtildaCon = log(sum(exp(500 * (0.3 - ... diff --git a/src/MuscleTendonPersonalization/Optimization/CostTerms/calcNormalizedFiberLengthGroupedSimilarityCost.m b/src/MuscleTendonPersonalization/Optimization/CostTerms/calcNormalizedFiberLengthGroupedSimilarityCost.m index 635acdbae..660eb14d1 100644 --- a/src/MuscleTendonPersonalization/Optimization/CostTerms/calcNormalizedFiberLengthGroupedSimilarityCost.m +++ b/src/MuscleTendonPersonalization/Optimization/CostTerms/calcNormalizedFiberLengthGroupedSimilarityCost.m @@ -26,16 +26,19 @@ % ----------------------------------------------------------------------- % function cost = calcNormalizedFiberLengthGroupedSimilarityCost( ... - modeledValues, experimentalData, costTerm) + modeledValues, inputs, costTerm) errorCenter = valueOrAlternate(costTerm, "errorCenter", 0); -maximumAllowableError = valueOrAlternate(costTerm, "maxAllowableError", 0.05); +maximumAllowableError = valueOrAlternate(costTerm, ... + "maxAllowableError", 0.05); +lowestIndex = min(cell2mat(inputs.normalizedFiberLengthGroups)) - 1; index = 1; -for i = 1:length(experimentalData.normalizedFiberLengthGroups) - muscleGroup = experimentalData.normalizedFiberLengthGroups{i}; +for i = 1:length(inputs.normalizedFiberLengthGroups) + muscleGroup = inputs.normalizedFiberLengthGroups{i} ... + - lowestIndex; normalizedFiberLengthMagnitudeDeviation(:, index : index + size(... muscleGroup, 2) - 1) = calcMeanDeviations( ... modeledValues.normalizedFiberLength(:, muscleGroup, :), ... - experimentalData.normalizedFiberLength(:, muscleGroup, :)); + inputs.normalizedFiberLength(:, muscleGroup, :)); normalizedFiberLengthShapeDeviation(:, index : index + size(... muscleGroup, 2) - 1, :) = calcMeanShapeDeviations( ... modeledValues.normalizedFiberLength(:, muscleGroup, :)); diff --git a/src/MuscleTendonPersonalization/Optimization/CostTerms/calcSynergyExtrapolationMuscleActivationCost.m b/src/MuscleTendonPersonalization/Optimization/CostTerms/calcSynergyExtrapolationMuscleActivationCost.m index 94d884fc2..e10ee7c15 100644 --- a/src/MuscleTendonPersonalization/Optimization/CostTerms/calcSynergyExtrapolationMuscleActivationCost.m +++ b/src/MuscleTendonPersonalization/Optimization/CostTerms/calcSynergyExtrapolationMuscleActivationCost.m @@ -26,11 +26,12 @@ % ----------------------------------------------------------------------- % function cost = calcSynergyExtrapolationMuscleActivationCost( ... - modeledValues, experimentalData, costTerm) + modeledValues, inputs, costTerm) errorCenter = valueOrAlternate(costTerm, "errorCenter", 0); maximumAllowableError = valueOrAlternate(costTerm, "maxAllowableError", 0.3); +lowestIndex = min(cell2mat(inputs.normalizedFiberLengthGroups)) - 1; cost = calcDeviationCostTerm(... modeledValues.muscleActivations(:, ... - [experimentalData.synergyExtrapolation.missingEmgChannelGroups{:}], ... - :), errorCenter, maximumAllowableError); + reshape(cell2mat(inputs.synergyExtrapolation.missingEmgChannelGroups), 1, []) ... + - lowestIndex, :), errorCenter, maximumAllowableError); end \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/Optimization/calcMtpCost.m b/src/MuscleTendonPersonalization/Optimization/calcMtpCost.m index 7ec645d8d..bb2442549 100644 --- a/src/MuscleTendonPersonalization/Optimization/calcMtpCost.m +++ b/src/MuscleTendonPersonalization/Optimization/calcMtpCost.m @@ -26,20 +26,23 @@ % ----------------------------------------------------------------------- % function totalCost = calcMtpCost(values, synxModeledValues, modeledValues, ... - experimentalData, params) + inputs, params) totalCost = 0; for i = 1 : length(params.costTerms) costTerm = params.costTerms{i}; if costTerm.isEnabled + cost = 0; switch costTerm.type case "measured_inverse_dynamics_joint_moment" - cost = calcSynergyExtrapolationMomentTrackingCost( ... - synxModeledValues, ... - experimentalData, ... - costTerm); + if isfield(inputs, "synergyExtrapolation") + cost = calcSynergyExtrapolationMomentTrackingCost( ... + synxModeledValues, ... + inputs, ... + costTerm); + end case "inverse_dynamics_joint_moment" cost = calcMomentTrackingCost(modeledValues, ... - experimentalData, costTerm); + inputs, costTerm); case "activation_time_constant" cost = calcActivationTimeConstantDeviationCost(values, ... costTerm); @@ -48,35 +51,55 @@ costTerm); case "optimal_muscle_fiber_length" cost = calcOptimalFiberLengthDeviationCost(values, ... - experimentalData, costTerm); + inputs, costTerm); case "tendon_slack_length" cost = calcTendonSlackLengthDeviationCost(values, ... - experimentalData, costTerm); + inputs, costTerm); case "emg_scale_factor" cost = calcEmgScaleFactorDevationCost(values, costTerm); case "normalized_muscle_fiber_length" - cost = calcNormalizedFiberLengthDeviationCost( ... - synxModeledValues, experimentalData, costTerm); + if isfield(inputs, "synergyExtrapolation") + cost = calcNormalizedFiberLengthDeviationCost( ... + synxModeledValues, inputs, costTerm); + else + cost = calcNormalizedFiberLengthDeviationCost( ... + modeledValues, inputs, costTerm); + end case "passive_muscle_force" - cost = calcPassiveForceCost(synxModeledValues, costTerm); + if isfield(inputs, "synergyExtrapolation") + cost = calcPassiveForceCost(synxModeledValues, costTerm); + else + cost = calcPassiveForceCost(modeledValues, costTerm); + end case "grouped_normalized_muscle_fiber_length" - cost = calcNormalizedFiberLengthGroupedSimilarityCost( ... - synxModeledValues, experimentalData, costTerm); + if isfield(inputs, "synergyExtrapolation") + cost = calcNormalizedFiberLengthGroupedSimilarityCost( ... + synxModeledValues, inputs, costTerm); + else + cost = calcNormalizedFiberLengthGroupedSimilarityCost( ... + modeledValues, inputs, costTerm); + end case "grouped_emg_scale_factor" cost = calcEmgScaleFactorGroupedSimilarityCost(values, ... - experimentalData, costTerm); + inputs, costTerm); case "grouped_electromechanical_delay" cost = calcElectromechanicalDelayGroupedSimilarityCost( ... - values, experimentalData, costTerm); + values, inputs, costTerm); case "extrapolated_muscle_activation" - cost = calcSynergyExtrapolationMuscleActivationCost( ... - synxModeledValues, experimentalData, costTerm); + if isfield(inputs, "synergyExtrapolation") + cost = calcSynergyExtrapolationMuscleActivationCost( ... + synxModeledValues, inputs, costTerm); + end case "residual_muscle_activation" - cost = calcResidualMuscleActivationCost( ... - synxModeledValues, modeledValues, experimentalData, costTerm); + if isfield(inputs, "synergyExtrapolation") + cost = calcResidualMuscleActivationCost( ... + synxModeledValues, modeledValues, inputs, costTerm); + end case "muscle_excitation_penalty" - cost = calcMuscleExcitationPenaltyCost( ... - synxModeledValues, experimentalData, costTerm); + if isfield(inputs, "synergyExtrapolation") + cost = calcMuscleExcitationPenaltyCost( ... + synxModeledValues, inputs, costTerm); + end otherwise throw(MException('', 'Cost term %s is not valid for MTP', ... costTerm.type)) diff --git a/src/MuscleTendonPersonalization/Optimization/computeMuscleTendonCostFunction.m b/src/MuscleTendonPersonalization/Optimization/computeMuscleTendonCostFunction.m index fafd73598..96950f18f 100644 --- a/src/MuscleTendonPersonalization/Optimization/computeMuscleTendonCostFunction.m +++ b/src/MuscleTendonPersonalization/Optimization/computeMuscleTendonCostFunction.m @@ -26,10 +26,16 @@ % ----------------------------------------------------------------------- % function cost = computeMuscleTendonCostFunction(secondaryValues, ... - primaryValues, isIncluded, experimentalData, params) -values = makeMtpValuesAsStruct(secondaryValues, primaryValues, isIncluded); -synxModeledValues = calcMtpSynXModeledValues(values, experimentalData, params); -modeledValues = calcMtpModeledValues(values, experimentalData, params); -cost = calcMtpCost(values, synxModeledValues, modeledValues, ... - experimentalData, params); + primaryValues, isIncluded, inputs, params) +values = makeMtpValuesAsStruct(secondaryValues, primaryValues, ... + isIncluded, inputs); +modeledValues = calcMtpModeledValues(values, inputs, params); +if isfield(inputs, "synergyExtrapolation") + synxModeledValues = calcMtpSynXModeledValues(values, inputs, params); + cost = calcMtpCost(values, synxModeledValues, modeledValues, ... + inputs, params); +else + cost = calcMtpCost(values, struct(), modeledValues, ... + inputs, params); +end end \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/Optimization/computeMuscleTendonRoundOptimization.m b/src/MuscleTendonPersonalization/Optimization/computeMuscleTendonRoundOptimization.m index dc21cec9d..72de0f1b9 100644 --- a/src/MuscleTendonPersonalization/Optimization/computeMuscleTendonRoundOptimization.m +++ b/src/MuscleTendonPersonalization/Optimization/computeMuscleTendonRoundOptimization.m @@ -30,11 +30,11 @@ function optimizedValues = computeMuscleTendonRoundOptimization( ... initialValues, primaryValues, isIncluded, lowerBounds, upperBounds, ... - experimentalData, params, optimizerOptions, A, b) + inputs, params, optimizerOptions, A, b) optimizedValues = fmincon(@(values)computeMuscleTendonCostFunction( ... - values, primaryValues, isIncluded, experimentalData, params), ... + values, primaryValues, isIncluded, inputs, params), ... initialValues, A, b, [], [], lowerBounds, upperBounds, ... @(values)calcMuscleTendonNonLinearConstraints(values, primaryValues, ... - isIncluded, experimentalData, params), optimizerOptions); + isIncluded, inputs, params), optimizerOptions); end \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/Optimization/makeMtpValuesAsStruct.m b/src/MuscleTendonPersonalization/Optimization/makeMtpValuesAsStruct.m index c22ec5ae4..beb43b5e5 100644 --- a/src/MuscleTendonPersonalization/Optimization/makeMtpValuesAsStruct.m +++ b/src/MuscleTendonPersonalization/Optimization/makeMtpValuesAsStruct.m @@ -26,7 +26,8 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function values = makeMtpValuesAsStruct(secondaryValues, primaryValues, isIncluded) +function values = makeMtpValuesAsStruct(secondaryValues, primaryValues, ... + isIncluded, inputs) valuesHelper.secondaryValues = secondaryValues; valuesHelper.primaryValues = primaryValues; valuesHelper.isIncluded = isIncluded; @@ -36,7 +37,9 @@ values.emgScaleFactors = findCorrectMtpValues(4, valuesHelper); values.optimalFiberLengthScaleFactors = findCorrectMtpValues(5, valuesHelper); values.tendonSlackLengthScaleFactors = findCorrectMtpValues(6, valuesHelper); -values.synergyWeights = findCorrectMtpValues(7, valuesHelper); +if isfield(inputs, "synergyExtrapolation") + values.synergyWeights = findCorrectMtpValues(7, valuesHelper); +end end function output = findCorrectMtpValues(index, valuesStruct) diff --git a/src/MuscleTendonPersonalization/Optimization/updateDesignVariables.m b/src/MuscleTendonPersonalization/Optimization/updateDesignVariables.m index a76e4db89..7de0c7c80 100644 --- a/src/MuscleTendonPersonalization/Optimization/updateDesignVariables.m +++ b/src/MuscleTendonPersonalization/Optimization/updateDesignVariables.m @@ -39,7 +39,9 @@ primaryValues, isIncluded, i); newPrimaryValues{i} = secondaryValues(startIndex:endIndex); else - newPrimaryValues{i} = primaryValues{i}; + if i ~= 7 + newPrimaryValues{i} = primaryValues{i}; + end end end end \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/SynergyExtrapolation/calcMuscleExcitationsSynX.m b/src/MuscleTendonPersonalization/SynergyExtrapolation/calcMuscleExcitationsSynX.m index eae6b2404..e6ab86266 100644 --- a/src/MuscleTendonPersonalization/SynergyExtrapolation/calcMuscleExcitationsSynX.m +++ b/src/MuscleTendonPersonalization/SynergyExtrapolation/calcMuscleExcitationsSynX.m @@ -70,7 +70,8 @@ experimentalData.numberOfExtrapolationWeights + 1 : end)); % Insert unmeasured muscle excitations from SynX emgData = updateEmgSignals(params.missingEmgChannelGroups, ... - experimentalData.emgDataExpanded, unmeasuredEmgSignals); + experimentalData.emgDataExpanded, unmeasuredEmgSignals, ... + experimentalData.normalizedFiberLengthGroups); % muscleExcitations are scaled processed Emg signals emgData = emgData .* emgScalingFactor; % Distribute residual excitations @@ -153,11 +154,12 @@ end function emgData = updateEmgSignals(missingEmgChannelGroups, emgData, ... - unmeasuredEmgSignals) + unmeasuredEmgSignals, normalizedFiberLengthGroups) +lowestIndex = min(cell2mat(normalizedFiberLengthGroups)) - 1; for i = 1 : size(missingEmgChannelGroups, 2) for j = 1 : size(missingEmgChannelGroups{i}, 2) - emgData(:, missingEmgChannelGroups{i}(1, j), :) = ... + emgData(:, missingEmgChannelGroups{i}(1, j) - lowestIndex, :) = ... unmeasuredEmgSignals(:, i, :); end end diff --git a/src/MuscleTendonPersonalization/SynergyExtrapolation/getLinearInequalityConstraints.m b/src/MuscleTendonPersonalization/SynergyExtrapolation/getLinearInequalityConstraints.m index 67f50956e..07c3f56f7 100644 --- a/src/MuscleTendonPersonalization/SynergyExtrapolation/getLinearInequalityConstraints.m +++ b/src/MuscleTendonPersonalization/SynergyExtrapolation/getLinearInequalityConstraints.m @@ -121,7 +121,7 @@ function aMatrixSynergy = updateHalfMatrixA(emgData, aMatrix,... numberOfSynergies, missingEmgChannelGroups, thirdMatrixDimension, ... matrixFactorizationFactor) - +aMatrixSynergy = []; for i = 1:size(missingEmgChannelGroups,2) aMatrixSynergy((i - 1) * size(emgData, 1) * size(emgData, 2) + 1 : ... i * size(emgData, 1) * size(emgData, 2), (i - 1) * ( ... diff --git a/src/MuscleTendonPersonalization/SynergyExtrapolation/getSynergyCommands.m b/src/MuscleTendonPersonalization/SynergyExtrapolation/getSynergyCommands.m index 48ecdb469..974950d8a 100644 --- a/src/MuscleTendonPersonalization/SynergyExtrapolation/getSynergyCommands.m +++ b/src/MuscleTendonPersonalization/SynergyExtrapolation/getSynergyCommands.m @@ -98,7 +98,7 @@ function pcaCommands = getPcaCommands(normalizedEMG, numberOfSynergies, ... categorizationOfTrials) - +pcaCommands = {}; for i = 1 : length(categorizationOfTrials) [~, principleComponents] = pca(reshape(permute(normalizedEMG(:, :, ... categorizationOfTrials{i}), [1 3 2]), size(normalizedEMG, 1) * ... diff --git a/src/MuscleTendonPersonalization/parseMuscleTendonPersonalizationSettingsTree.m b/src/MuscleTendonPersonalization/parseMuscleTendonPersonalizationSettingsTree.m index aa91bfa21..7e093bd23 100644 --- a/src/MuscleTendonPersonalization/parseMuscleTendonPersonalizationSettingsTree.m +++ b/src/MuscleTendonPersonalization/parseMuscleTendonPersonalizationSettingsTree.m @@ -33,13 +33,15 @@ inputs = getInputs(settingsTree); params = getParams(settingsTree); inputs = getMtpModelInputs(inputs); -inputs = mergeStructs(getSynergyExtrapolationInputs(... - inputs.model, ... - inputs.synergyExtrapolation, ... - inputs.emgDataColumnNames, ... - inputs.emgData, ... - inputs.muscleNames), ... - inputs); +if isfield(inputs, "synergyExtrapolation") + inputs = mergeStructs(getSynergyExtrapolationInputs(... + inputs.model, ... + inputs.synergyExtrapolation, ... + inputs.emgDataColumnNames, ... + inputs.emgData, ... + inputs.muscleNames), ... + inputs); +end resultsDirectory = getFieldByName(settingsTree, 'results_directory').Text; if(isempty(resultsDirectory)) resultsDirectory = pwd; @@ -51,10 +53,14 @@ dataDirectory = getFieldByNameOrError(tree, 'data_directory').Text; inputs = parseEmgData(tree, inputs, dataDirectory); inputs.tasks = getTasks(tree); -inputs.synergyExtrapolation = getSynergyExtrapolationParameters(tree, ... - inputs.model); -inputs.synergyExtrapolation = getTrialIndexes( ... - inputs.synergyExtrapolation, size(inputs.emgData, 1), inputs.prefixes); +if strcmpi(getTextFromField(getFieldByNameOrAlternate( ... + getFieldByNameOrError(tree, "MTPSynergyExtrapolation"), ... + 'is_enabled', 'false')), 'true') + inputs.synergyExtrapolation = getSynergyExtrapolationParameters(tree, ... + inputs.model); + inputs.synergyExtrapolation = getTrialIndexes( ... + inputs.synergyExtrapolation, size(inputs.emgData, 1), inputs.prefixes); +end inputs = reorderPreprocessedDataByMuscleNames(inputs, inputs.muscleNames); if ~isfield(inputs, "emgSplines") inputs.emgSplines = makeEmgSplines(inputs.emgTime, ... @@ -156,7 +162,7 @@ end function inputs = getSynergyExtrapolationInputs(model, ... - synergyExtrapolation, emgDataColumnNames, emgData, muscleNames) + synergyExtrapolation, emgDataColumnNames, emgData, muscleNames) model = Model(model); groupToName = getMuscleNameByGroupStruct(model, ... emgDataColumnNames); diff --git a/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResults.m b/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResults.m index 48926ecab..954de5bc0 100644 --- a/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResults.m +++ b/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResults.m @@ -32,12 +32,185 @@ function reportMuscleTendonPersonalizationResults(optimizedParams, ... mtpInputs, precalInputs) if nargin < 3; precalInputs = []; end -[finalValues, results, resultsSynx, resultsSynxNoResiduals] = ... - getValuesToReport(mtpInputs, precalInputs, optimizedParams); if ~isempty(precalInputs) plotMuscleTendonLengthInitializationResults(precalInputs, mtpInputs) end +if isfield(mtpInputs, "synergyExtrapolation") + reportSynX(optimizedParams, mtpInputs, precalInputs) +else + reportNoSynX(optimizedParams, mtpInputs, precalInputs) +end +end + +function reportNoSynX(optimizedParams, mtpInputs, precalInputs) +[finalValues, results] = ... + getValuesToReportNoSynX(mtpInputs, precalInputs, optimizedParams); +printJointMomentMatchingError(results.muscleJointMoments, ... + mtpInputs.inverseDynamicsMoments); +makeExcitationAndActivationPlotsNoSynX(results, mtpInputs); +makeModelParameterPlotsNoSynX(finalValues, mtpInputs) +makeTaskSpecificMomentMatchingPlotsNoSynX(... + permute(results.muscleJointMoments, [3 1 2]), ... + permute(mtpInputs.inverseDynamicsMoments, [3 1 2]), ... + mtpInputs.coordinateNames) +% makeTaskSpecificNormalizedFiberLengthsPlots( ... +% permute(resultsSynx.normalizedFiberLength, [3 1 2]), ... +% mtpInputs, mtpInputs.synergyExtrapolation) +end + +function [finalValues, results] = getValuesToReportNoSynX(mtpInputs, ... + precalInputs, optimizedParams) +finalValues = makeMtpValuesAsStruct([], optimizedParams, zeros(1, 7), mtpInputs); +results = calcMtpModeledValues(finalValues, mtpInputs, struct()); +results.time = mtpInputs.emgTime(:, mtpInputs.numPaddingFrames + 1 : ... + end - mtpInputs.numPaddingFrames); +results.muscleExcitations = results.muscleExcitations(:, :, ... + mtpInputs.numPaddingFrames + 1 : end - mtpInputs.numPaddingFrames); +if ~isempty(precalInputs) +finalOptimalFiberLength = ... + finalValues.optimalFiberLengthScaleFactors .* mtpInputs.optimalFiberLength; +finalValues.optimalFiberLengthScaleFactors = ... + finalOptimalFiberLength ./ precalInputs.optimalFiberLength; +finalTendonSlackLength = ... + finalValues.tendonSlackLengthScaleFactors .* mtpInputs.tendonSlackLength; +finalValues.tendonSlackLengthScaleFactors = ... + finalTendonSlackLength ./ precalInputs.tendonSlackLength; +end +end + +function makeExcitationAndActivationPlotsNoSynX(results, ... + experimentalData) +muscleLabels = experimentalData.muscleNames; + +muscleExcitations = results.muscleExcitations(:, :, :); +muscleActivations = results.muscleActivations(:, :, :); +meanMuscleExcitation = permute(mean(muscleExcitations, 1), [3 2 1]); +stdMuscleExcitation = permute(std(muscleExcitations, [], 1), [3 2 1]); +meanMuscleActivation = permute(mean(muscleActivations, 1), [3 2 1]); +stdMuscleActivation = permute(std(muscleActivations, [], 1), [3 2 1]); + +t = 1 : size(meanMuscleExcitation, 1); +nplot = ceil(sqrt(numel(muscleLabels))); +for j = 1 : numel(muscleLabels) + subplot(nplot,nplot,j); + plot(meanMuscleExcitation(:, j), 'b-', 'LineWidth', 2); hold on + fill([t'; flipud(t')], ... + [meanMuscleExcitation(:, j) - stdMuscleExcitation(:, j); ... + flipud(meanMuscleExcitation(:, j) + stdMuscleExcitation(:, j))], ... + 'b', 'linestyle', 'None', 'FaceAlpha', 0.5); + hold on + plot(meanMuscleActivation(:, j), 'r-', 'LineWidth', 2); hold on + fill([t'; flipud(t')], ... + [meanMuscleActivation(:, j) - stdMuscleActivation(:, j); ... + flipud(meanMuscleActivation(:, j) + stdMuscleActivation(:, j))], ... + 'r', 'linestyle', 'None', 'FaceAlpha', 0.5); + axis([1 size(meanMuscleExcitation, 1) 0 1]) + title(muscleLabels{j}); + if j == 1 + legend ('Excitation(without residual)', '', ... + 'Activation(without residual)', ''); + end +end +set(gca, 'FontSize', 12) +end + +function makeModelParameterPlotsNoSynX(finalValues, experimentalData) + +muscleLabels = experimentalData.muscleNames; + +In_width = 0.145; % witdth of each subplot +figure('name', 'Model Parameters', 'units', 'normalized', ... + 'outerposition', [0 0 1 1]); +for subplotElement = 1 : 6 + subplotTight(1, 6, subplotElement, [0.04, 0.001]); + switch subplotElement + case 1 %activationTimeConstant + bar(1 : numel(muscleLabels), finalValues.activationTimeConstants, ... + 'barwidth', 0.8, 'Horizontal', 'on', 'FaceAlpha', 0.6); + hold on; + title('Activation Time Constant (cs)') + pos_in = get(gca, 'Position'); + pos_in(3) = In_width; + pos_in(1) = pos_in(1) + 0.05; + set(gca, 'YTick', 1 : numel(muscleLabels), 'yTickLabel', ... + muscleLabels, 'Fontsize', 11, 'Position', pos_in); + case 2 %activationNonlinearity + bar(finalValues.activationNonlinearityConstants, 'Horizontal', 'on', ... + 'barwidth', 0.8, 'FaceAlpha', 0.6); + title('Activation Nonlinearity') + pos_in(1) = pos_in(1) + pos_in(3) + 0.015; + set(gca, 'YTick', [], 'yTickLabel', [], 'Position', pos_in, ... + 'Fontsize', 11); + case 3 %timeDelay + bar(finalValues.electromechanicalDelays, 'Horizontal', 'on', 'barwidth', 0.8, ... + 'FaceAlpha', 0.6); + title('Electromechanical Time Delay (ds)') + pos_in(1) = pos_in(1) + pos_in(3) + 0.015; + set(gca, 'YTick', [], 'yTickLabel', [],'Position', pos_in, ... + 'Fontsize', 11); + case 4 % emgScalingFactor + bar(finalValues.emgScaleFactors, 'Horizontal', 'on', 'barwidth', 0.8, ... + 'FaceAlpha', 0.6); + title('Emg Scaling Factor') + pos_in(1) = pos_in(1) + pos_in(3) + 0.015; + set(gca, 'YTick', [], 'yTickLabel', [],'Position', pos_in, ... + 'Fontsize', 11); + case 5 %muscleOptimalLength + bar(finalValues.optimalFiberLengthScaleFactors, 'Horizontal', 'on', 'barwidth', ... + 0.8, 'FaceAlpha', 0.6); + title('Optimal Fiber Length Scaling Factor') + pos_in(1) = pos_in(1) + pos_in(3) + 0.015; + set(gca, 'YTick', [], 'yTickLabel', [],'Position', pos_in, ... + 'Fontsize', 11); + case 6 %tendonSlackLength + bar(finalValues.tendonSlackLengthScaleFactors, 'Horizontal', 'on', 'barwidth', ... + 0.8, 'FaceAlpha', 0.6); + title('Tendon Slack Length Scaling Factor') + pos_in(1) = pos_in(1) + pos_in(3) + 0.015; + set(gca, 'YTick', [], 'yTickLabel', [],'Position', pos_in, ... + 'Fontsize', 11); + end +end +end + +function makeTaskSpecificMomentMatchingPlotsNoSynX(jointMoments, ... + inverseDynamicsMoments, coordinates) + +figure('name', ['Joint moments']); +meanJointMoments = squeeze(mean(jointMoments, 2)); +stdJointMoments = squeeze(std(jointMoments, [], 2)); +meaninverseDynamicsMoments = squeeze(mean(inverseDynamicsMoments, 2)); +stdinverseDynamicsMoments = squeeze(std(inverseDynamicsMoments, [], 2)); +t = 1 : size(jointMoments,1); +for j = 1:size(jointMoments,3) +subplot(2, size(jointMoments, 3), j); +plot(-meanJointMoments(:, j), 'r', 'LineWidth', 2); hold on +fill([t'; flipud(t')], ... + [-meanJointMoments(:,j) - stdJointMoments(:,j); ... + flipud(-meanJointMoments(:, j) + stdJointMoments(:, j))], ... + 'r', 'linestyle', 'None', 'FaceAlpha', 0.5); +subplot(2, size(jointMoments, 3), j); +plot(-meaninverseDynamicsMoments(:, j), 'k', 'LineWidth', 2); +fill([t'; flipud(t')], ... + [-meaninverseDynamicsMoments(:, j) - stdinverseDynamicsMoments(:, j); ... + flipud(-meaninverseDynamicsMoments(:, j) + stdinverseDynamicsMoments(:, j))], ... + 'k', 'linestyle', 'None', 'FaceAlpha', 0.5); +title({[strrep(coordinates{j}, '_', ' ') ],'Moment (N-m)'}); +xlabel('Time Frames'); axis([0 100 -80 120]); +if j == 1; ylabel('Moment (N-m)'); +elseif j == numel(coordinates) + lgd = legend(""); + lgd.Orientation ='horizontal'; + lgd.NumColumns = 2; +end +set(gca, 'FontSize', 12) +end +end + +function reportSynX(optimizedParams, mtpInputs, precalInputs) +[finalValues, results, resultsSynx, resultsSynxNoResiduals] = ... + getValuesToReport(mtpInputs, precalInputs, optimizedParams); printJointMomentMatchingError(resultsSynx.muscleJointMoments, ... mtpInputs.inverseDynamicsMoments); makeExcitationAndActivationPlots(results, resultsSynx, mtpInputs, ... @@ -53,10 +226,11 @@ function reportMuscleTendonPersonalizationResults(optimizedParams, ... permute(resultsSynx.normalizedFiberLength, [3 1 2]), ... mtpInputs, mtpInputs.synergyExtrapolation) end + function [finalValues, results, resultsSynx, resultsSynxNoResiduals] = ... getValuesToReport(mtpInputs, precalInputs, optimizedParams) -finalValues = makeMtpValuesAsStruct([], optimizedParams, zeros(1, 7)); +finalValues = makeMtpValuesAsStruct([], optimizedParams, zeros(1, 7), mtpInputs); resultsSynx = calcMtpSynXModeledValues(finalValues, mtpInputs, struct()); finalValues.synergyWeights(mtpInputs.numberOfExtrapolationWeights + 1 : end) = 0; resultsSynxNoResiduals = calcMtpSynXModeledValues(finalValues, mtpInputs, struct()); @@ -164,6 +338,7 @@ function printJointMomentMatchingError(muscleJointMoments, inverseDynamicsMoment 'and inverse dynamic moments are: \n' ]); fprintf(['\n ' num2str(jointMomentsMae) ' \n']); end + function makeExcitationAndActivationPlots(results, resultsSynx, ... experimentalData, synergyParameters) @@ -182,6 +357,7 @@ function makeExcitationAndActivationPlots(results, resultsSynx, ... muscleLabels) end end + function plotMuscleExcitationsAndActivations(muscleExcitations, ... muscleExcitationsSynx, muscleActivations, muscleActivationsSynx, ... muscleLabels) @@ -326,6 +502,7 @@ function makeModelParameterPlots(finalValues, experimentalData, synergyParameter if nargout == 1;vargout=h; end end + function makeTaskSpecificMomentMatchingPlots(jointMoments, jointMomentsSynx, ... inverseDynamicsMoments, coordinates, synergyParameters) @@ -342,6 +519,7 @@ function makeTaskSpecificMomentMatchingPlots(jointMoments, jointMomentsSynx, ... {'','Predicted (with residual excitations)','','Inverse dynamics'}); end end + function plotJointMoments(jointMoments, inverseDynamicsMoments, taskName, ... coordinates, subplotIndex, legendName) @@ -374,6 +552,7 @@ function plotJointMoments(jointMoments, inverseDynamicsMoments, taskName, ... set(gca, 'FontSize', 12) end end + function makeTaskSpecificNormalizedFiberLengthsPlots(... normalizedFiberLengths, experimentalData, synergyParameters) diff --git a/src/NeuralControlPersonalization/parseNeuralControlPersonalizationSettingsTree.m b/src/NeuralControlPersonalization/parseNeuralControlPersonalizationSettingsTree.m index eb7fe35b8..d7833e3a2 100644 --- a/src/NeuralControlPersonalization/parseNeuralControlPersonalizationSettingsTree.m +++ b/src/NeuralControlPersonalization/parseNeuralControlPersonalizationSettingsTree.m @@ -37,11 +37,12 @@ if(isempty(resultsDirectory)) resultsDirectory = pwd; end +inputs = rmfield(inputs, "model"); end function inputs = getInputs(tree) inputs = parseMtpNcpSharedInputs(tree); -inputs.synergyGroups = parseSynergyGroups(tree, Model(inputs.model)); +inputs.synergyGroups = parseSynergyGroups(tree, inputs.model); inputs = matchMuscleNamesFromCoordinatesAndSynergyGroups(inputs); inputs = reorderPreprocessedDataByMuscleNames(inputs, inputs.muscleNames); [inputs.maxIsometricForce, inputs.optimalFiberLength, ... @@ -100,7 +101,6 @@ end function params = getParams(tree, model, inputs) -model = Model(model); params = struct(); params.activationGroupNames = parseSpaceSeparatedList(tree, ... 'activation_muscle_groups'); diff --git a/src/Preprocessing/splitIntoTrials.m b/src/Preprocessing/splitIntoTrials.m index 7b5ecc799..d56512523 100644 --- a/src/Preprocessing/splitIntoTrials.m +++ b/src/Preprocessing/splitIntoTrials.m @@ -98,7 +98,8 @@ function splitIntoTrials(timePairs, inputSettings, outputSettings) sectionDataFiles(filesToSection, timePairs, rowsPerTrial, trialName, ... filesToFilter, cutoffFrequency); -numBufferRows = calcNumPaddingFrames(timePairs, rowsPerTrial); +% numBufferRows = calcNumPaddingFrames(timePairs, rowsPerTrial); +numBufferRows = 19; paddedTimePairs = addBufferToTimePairs(timePairs, numBufferRows, ... rowsPerTrial); sectionDataFiles( ... @@ -175,7 +176,7 @@ function preprocessMuscleAnalysisData(outputDir, muscleAnalysisDirectory, ... function [filesToSection, filesToFilter] = makeFilesToSection( ... outputDir, ikOutputDir, idOutputDir, maOutputDir, grfOutputDir, ... trialName, coordinates, included) -filesToSection = []; +filesToSection = string([]); filesToFilter = []; if included.ma filesToSection = [ ... diff --git a/src/core/parse/parseSpaceSeparatedList.m b/src/core/parse/parseSpaceSeparatedList.m index 7d34e4155..2317a931d 100644 --- a/src/core/parse/parseSpaceSeparatedList.m +++ b/src/core/parse/parseSpaceSeparatedList.m @@ -34,7 +34,8 @@ if ~isfield(prefixField, "Text") throw(MException('', strcat(elementName, " is not in the xml file."))) end -if ~isempty(prefixField.Text) +prefixField.Text = strip(prefixField.Text); +if ~isempty(convertStringsToChars(prefixField.Text)) if(strcmp(prefixField.Text(1), ' ')) prefixField.Text = prefixField.Text(2:end); end From 2326324b14baa9e71fc3921793da1b3a88082c74 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 17 Dec 2023 16:23:33 -0600 Subject: [PATCH 246/365] added a few features to JMP --- .../computeInverseKinematicsSquaredError.m | 12 +++--- .../computeKinematicCalibration.m | 3 ++ ...rseJointModelPersonalizationSettingsTree.m | 37 +++++++++++++++---- 3 files changed, 39 insertions(+), 13 deletions(-) diff --git a/src/JointModelPersonalization/KinematicCalibration/computeInverseKinematicsSquaredError.m b/src/JointModelPersonalization/KinematicCalibration/computeInverseKinematicsSquaredError.m index 16a7b1369..e05c2c058 100644 --- a/src/JointModelPersonalization/KinematicCalibration/computeInverseKinematicsSquaredError.m +++ b/src/JointModelPersonalization/KinematicCalibration/computeInverseKinematicsSquaredError.m @@ -31,14 +31,14 @@ function error = computeInverseKinematicsSquaredError(model, ikSolver, ... markersReference, params) import org.opensim.modeling.* -[state, numFrames, frequency, finishTime] = prepareFrameIterations(... - model, ikSolver, markersReference, params); -markerTable = markersReference.getMarkerTable(); -times = markerTable.getIndependentColumn(); -error = []; -frameCounter = 0; persistent errorSize; try + [state, numFrames, frequency, finishTime] = prepareFrameIterations(... + model, ikSolver, markersReference, params); + markerTable = markersReference.getMarkerTable(); + times = markerTable.getIndependentColumn(); + error = []; + frameCounter = 0; for i=1:numFrames %start time is set so start with recording error ikSolver.track(state); error = [error calculateFrameSquaredError(ikSolver)]; diff --git a/src/JointModelPersonalization/KinematicCalibration/computeKinematicCalibration.m b/src/JointModelPersonalization/KinematicCalibration/computeKinematicCalibration.m index 371574ddc..f284ea1e1 100644 --- a/src/JointModelPersonalization/KinematicCalibration/computeKinematicCalibration.m +++ b/src/JointModelPersonalization/KinematicCalibration/computeKinematicCalibration.m @@ -73,7 +73,10 @@ 'stepTolerance', 1e-6); output.MaxFunctionEvaluations = valueOrAlternate(params, ... 'maxFunctionEvaluations', 3e3); +output.MaxIterations = valueOrAlternate(params, ... + 'maxIterations', 4e6); output.Display = valueOrAlternate(params, ... 'display','iter'); +output.FiniteDifferenceType = 'central'; end diff --git a/src/JointModelPersonalization/parseJointModelPersonalizationSettingsTree.m b/src/JointModelPersonalization/parseJointModelPersonalizationSettingsTree.m index 94137f63a..1182ff936 100644 --- a/src/JointModelPersonalization/parseJointModelPersonalizationSettingsTree.m +++ b/src/JointModelPersonalization/parseJointModelPersonalizationSettingsTree.m @@ -89,21 +89,28 @@ output.startTime = str2double(timeRange{1}); output.finishTime = str2double(timeRange{2}); end + try -markerNames = parseSpaceSeparatedList(tree, "marker_names"); -if ~isempty(markerNames) - output.markerNames = markerNames; -end + markerNames = parseSpaceSeparatedList(tree, "marker_names"); + if ~isempty(markerNames) + output.markerNames = markerNames; + end catch; end +try + freeMarkers = parseSpaceSeparatedList(tree, "free_markers"); +catch + freeMarkers = []; +end + output.parameters = {}; if(isstruct(getFieldByName(tree, "JMPJointSet"))) output.parameters = getJointParameters(tree.JMPJointSet); end output.scaling = []; output.markers = []; -if(isstruct(getFieldByName(tree, "JMPBodySet"))) +if isstruct(getFieldByName(tree, "JMPBodySet")) || ~isempty(freeMarkers) [output.scaling, output.markers] = ... - getBodyParameters(tree.JMPBodySet, model); + getBodyParameters(tree.JMPBodySet, model, freeMarkers); end translationBounds = getFieldByName(tree, 'translation_bounds'); if(isstruct(translationBounds)) @@ -178,7 +185,7 @@ end function [scaling, markers] = getBodyParameters( ... - bodySetTree, model) + bodySetTree, model, freeMarkers) if isfield(bodySetTree, "JMPBody") bodyTree = bodySetTree.JMPBody; scaling = getScalingBodies(bodyTree); @@ -187,6 +194,7 @@ scaling = string([]); markers = {}; end +markers = addFreeMarkers(markers, freeMarkers); end function inputs = getScalingBodies(bodyTree) @@ -232,6 +240,21 @@ end end end + +end + +function markers = addFreeMarkers(markers, freeMarkers) +for i = 1:length(freeMarkers) + axesToBeAdded = ["x", "y", "z"]; + for j = 1:length(markers) + if markers{j}(1) == freeMarkers(i) && any(ismember(axesToBeAdded, markers{j}(2))) + axesToBeAdded = axesToBeAdded(~ismember(axesToBeAdded, markers{j}(2))); + end + end + for j = 1:length(axesToBeAdded) + markers{end+1} = [freeMarkers(i), axesToBeAdded(j)]; + end +end end function output = getInitialValues(model, parameters, scaling, markers) From afa12554482c0b4b92928f95eec9c4edc6ad7b93 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Mon, 18 Dec 2023 18:44:41 -0600 Subject: [PATCH 247/365] update NCP with modelFileName --- .../saveNeuralControlPersonalizationResults.m | 2 +- src/core/osimx/writeNeuralControlPersonalizationOsimxFile.m | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/NeuralControlPersonalization/saveNeuralControlPersonalizationResults.m b/src/NeuralControlPersonalization/saveNeuralControlPersonalizationResults.m index 463c0f34e..af4d47b95 100644 --- a/src/NeuralControlPersonalization/saveNeuralControlPersonalizationResults.m +++ b/src/NeuralControlPersonalization/saveNeuralControlPersonalizationResults.m @@ -43,7 +43,7 @@ function saveNeuralControlPersonalizationResults(synergyWeights, ... ) tempMoments = permute(moments(i, :, :), [3 1 2]); momentColumns = inputs.coordinateNames; - model = Model(inputs.model); + model = Model(inputs.modelFileName); for j = 1:length(momentColumns) if strcmpi(model.getCoordinateSet.get(momentColumns(j)).getMotionType.toString.toCharArray', 'Rotational') momentColumns(j) = momentColumns(j) + "_moment"; diff --git a/src/core/osimx/writeNeuralControlPersonalizationOsimxFile.m b/src/core/osimx/writeNeuralControlPersonalizationOsimxFile.m index 4950091bd..f0ae596f2 100644 --- a/src/core/osimx/writeNeuralControlPersonalizationOsimxFile.m +++ b/src/core/osimx/writeNeuralControlPersonalizationOsimxFile.m @@ -30,8 +30,7 @@ function writeNeuralControlPersonalizationOsimxFile(inputs, ... resultsDirectory, precalInputs) -modelFileName = inputs.model; -model = Model(modelFileName); +model = Model(inputs.modelFileName); buildFromExisting = false; if isfield(inputs, 'osimxFileName') @@ -52,7 +51,7 @@ function writeNeuralControlPersonalizationOsimxFile(inputs, ... outfile = fullfile(resultsDirectory, strcat(name, "_ncp.xml")); end osimx.modelName = name; -osimx.model = modelFileName; +osimx.model = inputs.modelFileName; if ~isfield(osimx, 'muscles') osimx.muscles = []; end From 5eb1160daed52f269162e9daf39e5459b3825149 Mon Sep 17 00:00:00 2001 From: RobSalati <142843461+RobSalati@users.noreply.github.com> Date: Tue, 19 Dec 2023 14:42:55 -0500 Subject: [PATCH 248/365] Merged dev, renamed files, checked for SynX execution Merged dev with current branch. Renamed plotting functions to start with plotMtp... Renamed saving functions to start with saveMtp... Changed saving functions to only save SynX files if SynX is enabled. Changed plotting functions to only plot SynX if the correct files can be found. Added a new function to plot the mean value and shade +/- 1 std. --- .../5Nw1t_7cq4qlgUmed80suoxL1bop.xml | 2 - ...d.xml => AALSxy2_uPME6FBZ9MBCN1FqHigd.xml} | 0 .../AALSxy2_uPME6FBZ9MBCN1FqHigp.xml | 2 + ...d.xml => GhA7yyZ8gWjUTCoIPLShMR-04gQd.xml} | 0 .../GhA7yyZ8gWjUTCoIPLShMR-04gQp.xml | 2 + ...d.xml => Gpp9YsAsQAJWysZyj1cojVK3X6Md.xml} | 0 .../Gpp9YsAsQAJWysZyj1cojVK3X6Mp.xml | 2 + ...d.xml => MEPE9Z1LWKBNy4z5_S8K_-GNMUAd.xml} | 0 .../MEPE9Z1LWKBNy4z5_S8K_-GNMUAp.xml | 2 + ...d.xml => Mf5W7OBG_oZJzTY5xCqxob-FeQYd.xml} | 0 .../Mf5W7OBG_oZJzTY5xCqxob-FeQYp.xml | 2 + ...d.xml => R3gn2Qk5TEw-oczFAwJWS5gCjfAd.xml} | 0 .../R3gn2Qk5TEw-oczFAwJWS5gCjfAp.xml | 2 + ...d.xml => SDs3ORiDaz_ugAcUz3wD_qVQLDgd.xml} | 0 .../SDs3ORiDaz_ugAcUz3wD_qVQLDgp.xml | 2 + .../ZGtRn_ciKm4_JaX-GAud3iO0k_Ep.xml | 2 - .../ZLU9mS9w-LxpUpNVAq7gzeLsGNMp.xml | 2 - .../diONK4qBYLdv_ys7QKb0WQQIOr0p.xml | 2 - .../lJReWdGlE_L2vN0iJHuG9_W5z5kd.xml} | 0 .../lJReWdGlE_L2vN0iJHuG9_W5z5kp.xml | 2 + .../sdn40Xzdst7Zb2H9tGSrmIM0DwYp.xml | 2 - .../tMvQ-p2hcTG5TEzg6UjLlP8PoZsp.xml | 2 - .../udhwDiQtJoEy0hk-eIHYPaAcY-4p.xml | 2 - .../0QLTlFQJ1pdHZic9CzK-fwvy8JEp.xml | 2 - .../9V40WFzIKXJGGp8oEqob15GAlwgp.xml | 2 - ...d.xml => A3BkjYsewYZv3b_c9MKlO0lijG4d.xml} | 0 .../A3BkjYsewYZv3b_c9MKlO0lijG4p.xml | 2 + ...d.xml => AgcUDHWYVpO3utpT0Z6-LmAt6UUd.xml} | 0 .../AgcUDHWYVpO3utpT0Z6-LmAt6UUp.xml | 2 + ...d.xml => E73eq9DGYjhriVhYYpv10_eQeFgd.xml} | 0 .../E73eq9DGYjhriVhYYpv10_eQeFgp.xml | 2 + .../UczN0iNj0jtudo2nI2gnIWNiIesp.xml | 2 - ...d.xml => bhkbou8oMnNGwgUfTIp5aAoS2scd.xml} | 0 .../bhkbou8oMnNGwgUfTIp5aAoS2scp.xml | 2 + .../bhlRi9i4fJ6RxGucF8nbbN9vdz4p.xml | 2 - ...d.xml => gY40mCv5zigjIbgF70Tlefoo_mgd.xml} | 0 .../gY40mCv5zigjIbgF70Tlefoo_mgp.xml | 2 + .../iE5EXX33uQ1jkGTVTBI1zz34bFMp.xml | 2 - .../tUk4baD64Ncsu-t2EDryess8gEwp.xml | 2 - .../upN0pl2OtxHu1LpD1nHN8MyUJjwd.xml | 6 + .../upN0pl2OtxHu1LpD1nHN8MyUJjwp.xml | 2 + .../Analysis/extractMtpDataFromSto.m | 2 + .../Analysis/plotJointMoments.m | 105 -------------- .../Analysis/plotMeanAndStd.m | 7 + ...Params.m => plotMtpHillTypeMuscleParams.m} | 5 +- .../Analysis/plotMtpJointMoments.m | 129 ++++++++++++++++++ ... plotMtpMuscleExcitationsAndActivations.m} | 62 ++++----- ...gths.m => plotMtpNormalizedFiberLengths.m} | 23 ++-- ...ceCurves.m => plotMtpPassiveForceCurves.m} | 5 +- ...tCurves.m => plotMtpPassiveMomentCurves.m} | 7 +- ...r.m => printMtpJointMomentMatchingError.m} | 7 +- .../MuscleTendonPersonalizationTool.m | 22 +-- .../Saving/getMtpResultsToSave.m | 26 ++-- ...m => saveMtpActivationAndExcitationData.m} | 23 ++-- ...tMomentData.m => saveMtpJointMomentData.m} | 21 +-- ...eters.m => saveMtpMuscleModelParameters.m} | 2 +- ...veMtpMuscleTendonPersonalizationResults.m} | 25 ++-- ...eForceData.m => saveMtpPassiveForceData.m} | 20 +-- ...omentData.m => saveMtpPassiveMomentData.m} | 2 +- 59 files changed, 291 insertions(+), 262 deletions(-) delete mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/5Nw1t_7cq4qlgUmed80suoxL1bop.xml rename resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/{5Nw1t_7cq4qlgUmed80suoxL1bod.xml => AALSxy2_uPME6FBZ9MBCN1FqHigd.xml} (100%) create mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/AALSxy2_uPME6FBZ9MBCN1FqHigp.xml rename resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/{ZGtRn_ciKm4_JaX-GAud3iO0k_Ed.xml => GhA7yyZ8gWjUTCoIPLShMR-04gQd.xml} (100%) create mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/GhA7yyZ8gWjUTCoIPLShMR-04gQp.xml rename resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/{ZLU9mS9w-LxpUpNVAq7gzeLsGNMd.xml => Gpp9YsAsQAJWysZyj1cojVK3X6Md.xml} (100%) create mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Gpp9YsAsQAJWysZyj1cojVK3X6Mp.xml rename resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/{diONK4qBYLdv_ys7QKb0WQQIOr0d.xml => MEPE9Z1LWKBNy4z5_S8K_-GNMUAd.xml} (100%) create mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/MEPE9Z1LWKBNy4z5_S8K_-GNMUAp.xml rename resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/{sdn40Xzdst7Zb2H9tGSrmIM0DwYd.xml => Mf5W7OBG_oZJzTY5xCqxob-FeQYd.xml} (100%) create mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Mf5W7OBG_oZJzTY5xCqxob-FeQYp.xml rename resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/{tMvQ-p2hcTG5TEzg6UjLlP8PoZsd.xml => R3gn2Qk5TEw-oczFAwJWS5gCjfAd.xml} (100%) create mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/R3gn2Qk5TEw-oczFAwJWS5gCjfAp.xml rename resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/{udhwDiQtJoEy0hk-eIHYPaAcY-4d.xml => SDs3ORiDaz_ugAcUz3wD_qVQLDgd.xml} (100%) create mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/SDs3ORiDaz_ugAcUz3wD_qVQLDgp.xml delete mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/ZGtRn_ciKm4_JaX-GAud3iO0k_Ep.xml delete mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/ZLU9mS9w-LxpUpNVAq7gzeLsGNMp.xml delete mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/diONK4qBYLdv_ys7QKb0WQQIOr0p.xml rename resources/project/{SM-7XD5VEsEA2ReW_Gcx0KdVR4E/0QLTlFQJ1pdHZic9CzK-fwvy8JEd.xml => 3S1lLYGLIjnYoeqS66xjbiTYaGo/lJReWdGlE_L2vN0iJHuG9_W5z5kd.xml} (100%) create mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/lJReWdGlE_L2vN0iJHuG9_W5z5kp.xml delete mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/sdn40Xzdst7Zb2H9tGSrmIM0DwYp.xml delete mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/tMvQ-p2hcTG5TEzg6UjLlP8PoZsp.xml delete mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/udhwDiQtJoEy0hk-eIHYPaAcY-4p.xml delete mode 100644 resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/0QLTlFQJ1pdHZic9CzK-fwvy8JEp.xml delete mode 100644 resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/9V40WFzIKXJGGp8oEqob15GAlwgp.xml rename resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/{9V40WFzIKXJGGp8oEqob15GAlwgd.xml => A3BkjYsewYZv3b_c9MKlO0lijG4d.xml} (100%) create mode 100644 resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/A3BkjYsewYZv3b_c9MKlO0lijG4p.xml rename resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/{UczN0iNj0jtudo2nI2gnIWNiIesd.xml => AgcUDHWYVpO3utpT0Z6-LmAt6UUd.xml} (100%) create mode 100644 resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/AgcUDHWYVpO3utpT0Z6-LmAt6UUp.xml rename resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/{bhlRi9i4fJ6RxGucF8nbbN9vdz4d.xml => E73eq9DGYjhriVhYYpv10_eQeFgd.xml} (100%) create mode 100644 resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/E73eq9DGYjhriVhYYpv10_eQeFgp.xml delete mode 100644 resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/UczN0iNj0jtudo2nI2gnIWNiIesp.xml rename resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/{iE5EXX33uQ1jkGTVTBI1zz34bFMd.xml => bhkbou8oMnNGwgUfTIp5aAoS2scd.xml} (100%) create mode 100644 resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/bhkbou8oMnNGwgUfTIp5aAoS2scp.xml delete mode 100644 resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/bhlRi9i4fJ6RxGucF8nbbN9vdz4p.xml rename resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/{tUk4baD64Ncsu-t2EDryess8gEwd.xml => gY40mCv5zigjIbgF70Tlefoo_mgd.xml} (100%) create mode 100644 resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/gY40mCv5zigjIbgF70Tlefoo_mgp.xml delete mode 100644 resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/iE5EXX33uQ1jkGTVTBI1zz34bFMp.xml delete mode 100644 resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/tUk4baD64Ncsu-t2EDryess8gEwp.xml create mode 100644 resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/upN0pl2OtxHu1LpD1nHN8MyUJjwd.xml create mode 100644 resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/upN0pl2OtxHu1LpD1nHN8MyUJjwp.xml delete mode 100644 src/MuscleTendonPersonalization/Analysis/plotJointMoments.m create mode 100644 src/MuscleTendonPersonalization/Analysis/plotMeanAndStd.m rename src/MuscleTendonPersonalization/Analysis/{plotHillTypeMuscleParams.m => plotMtpHillTypeMuscleParams.m} (93%) create mode 100644 src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m rename src/MuscleTendonPersonalization/Analysis/{plotMuscleExcitationsAndActivations.m => plotMtpMuscleExcitationsAndActivations.m} (64%) rename src/MuscleTendonPersonalization/Analysis/{plotNormalizedFiberLengths.m => plotMtpNormalizedFiberLengths.m} (81%) rename src/MuscleTendonPersonalization/Analysis/{plotPassiveForceCurves.m => plotMtpPassiveForceCurves.m} (94%) rename src/MuscleTendonPersonalization/Analysis/{plotPassiveMomentCurves.m => plotMtpPassiveMomentCurves.m} (94%) rename src/MuscleTendonPersonalization/Analysis/{printJointMomentMatchingError.m => printMtpJointMomentMatchingError.m} (92%) rename src/MuscleTendonPersonalization/Saving/{saveActivationAndExcitationData.m => saveMtpActivationAndExcitationData.m} (74%) rename src/MuscleTendonPersonalization/Saving/{saveJointMomentData.m => saveMtpJointMomentData.m} (75%) rename src/MuscleTendonPersonalization/Saving/{saveMuscleModelParameters.m => saveMtpMuscleModelParameters.m} (97%) rename src/MuscleTendonPersonalization/Saving/{saveMuscleTendonPersonalizationResults.m => saveMtpMuscleTendonPersonalizationResults.m} (77%) rename src/MuscleTendonPersonalization/Saving/{savePassiveForceData.m => saveMtpPassiveForceData.m} (77%) rename src/MuscleTendonPersonalization/Saving/{savePassiveMomentData.m => saveMtpPassiveMomentData.m} (97%) diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/5Nw1t_7cq4qlgUmed80suoxL1bop.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/5Nw1t_7cq4qlgUmed80suoxL1bop.xml deleted file mode 100644 index 213e8a9bf..000000000 --- a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/5Nw1t_7cq4qlgUmed80suoxL1bop.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/5Nw1t_7cq4qlgUmed80suoxL1bod.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/AALSxy2_uPME6FBZ9MBCN1FqHigd.xml similarity index 100% rename from resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/5Nw1t_7cq4qlgUmed80suoxL1bod.xml rename to resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/AALSxy2_uPME6FBZ9MBCN1FqHigd.xml diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/AALSxy2_uPME6FBZ9MBCN1FqHigp.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/AALSxy2_uPME6FBZ9MBCN1FqHigp.xml new file mode 100644 index 000000000..0f5597a1a --- /dev/null +++ b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/AALSxy2_uPME6FBZ9MBCN1FqHigp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/ZGtRn_ciKm4_JaX-GAud3iO0k_Ed.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/GhA7yyZ8gWjUTCoIPLShMR-04gQd.xml similarity index 100% rename from resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/ZGtRn_ciKm4_JaX-GAud3iO0k_Ed.xml rename to resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/GhA7yyZ8gWjUTCoIPLShMR-04gQd.xml diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/GhA7yyZ8gWjUTCoIPLShMR-04gQp.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/GhA7yyZ8gWjUTCoIPLShMR-04gQp.xml new file mode 100644 index 000000000..4530f36ca --- /dev/null +++ b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/GhA7yyZ8gWjUTCoIPLShMR-04gQp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/ZLU9mS9w-LxpUpNVAq7gzeLsGNMd.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Gpp9YsAsQAJWysZyj1cojVK3X6Md.xml similarity index 100% rename from resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/ZLU9mS9w-LxpUpNVAq7gzeLsGNMd.xml rename to resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Gpp9YsAsQAJWysZyj1cojVK3X6Md.xml diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Gpp9YsAsQAJWysZyj1cojVK3X6Mp.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Gpp9YsAsQAJWysZyj1cojVK3X6Mp.xml new file mode 100644 index 000000000..d00572c1f --- /dev/null +++ b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Gpp9YsAsQAJWysZyj1cojVK3X6Mp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/diONK4qBYLdv_ys7QKb0WQQIOr0d.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/MEPE9Z1LWKBNy4z5_S8K_-GNMUAd.xml similarity index 100% rename from resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/diONK4qBYLdv_ys7QKb0WQQIOr0d.xml rename to resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/MEPE9Z1LWKBNy4z5_S8K_-GNMUAd.xml diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/MEPE9Z1LWKBNy4z5_S8K_-GNMUAp.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/MEPE9Z1LWKBNy4z5_S8K_-GNMUAp.xml new file mode 100644 index 000000000..1e08dc112 --- /dev/null +++ b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/MEPE9Z1LWKBNy4z5_S8K_-GNMUAp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/sdn40Xzdst7Zb2H9tGSrmIM0DwYd.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Mf5W7OBG_oZJzTY5xCqxob-FeQYd.xml similarity index 100% rename from resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/sdn40Xzdst7Zb2H9tGSrmIM0DwYd.xml rename to resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Mf5W7OBG_oZJzTY5xCqxob-FeQYd.xml diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Mf5W7OBG_oZJzTY5xCqxob-FeQYp.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Mf5W7OBG_oZJzTY5xCqxob-FeQYp.xml new file mode 100644 index 000000000..3e5f79722 --- /dev/null +++ b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Mf5W7OBG_oZJzTY5xCqxob-FeQYp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/tMvQ-p2hcTG5TEzg6UjLlP8PoZsd.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/R3gn2Qk5TEw-oczFAwJWS5gCjfAd.xml similarity index 100% rename from resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/tMvQ-p2hcTG5TEzg6UjLlP8PoZsd.xml rename to resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/R3gn2Qk5TEw-oczFAwJWS5gCjfAd.xml diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/R3gn2Qk5TEw-oczFAwJWS5gCjfAp.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/R3gn2Qk5TEw-oczFAwJWS5gCjfAp.xml new file mode 100644 index 000000000..bb8c32fd5 --- /dev/null +++ b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/R3gn2Qk5TEw-oczFAwJWS5gCjfAp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/udhwDiQtJoEy0hk-eIHYPaAcY-4d.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/SDs3ORiDaz_ugAcUz3wD_qVQLDgd.xml similarity index 100% rename from resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/udhwDiQtJoEy0hk-eIHYPaAcY-4d.xml rename to resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/SDs3ORiDaz_ugAcUz3wD_qVQLDgd.xml diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/SDs3ORiDaz_ugAcUz3wD_qVQLDgp.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/SDs3ORiDaz_ugAcUz3wD_qVQLDgp.xml new file mode 100644 index 000000000..a24199a96 --- /dev/null +++ b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/SDs3ORiDaz_ugAcUz3wD_qVQLDgp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/ZGtRn_ciKm4_JaX-GAud3iO0k_Ep.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/ZGtRn_ciKm4_JaX-GAud3iO0k_Ep.xml deleted file mode 100644 index 833a9d565..000000000 --- a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/ZGtRn_ciKm4_JaX-GAud3iO0k_Ep.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/ZLU9mS9w-LxpUpNVAq7gzeLsGNMp.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/ZLU9mS9w-LxpUpNVAq7gzeLsGNMp.xml deleted file mode 100644 index b60432870..000000000 --- a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/ZLU9mS9w-LxpUpNVAq7gzeLsGNMp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/diONK4qBYLdv_ys7QKb0WQQIOr0p.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/diONK4qBYLdv_ys7QKb0WQQIOr0p.xml deleted file mode 100644 index 37bed85dc..000000000 --- a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/diONK4qBYLdv_ys7QKb0WQQIOr0p.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/0QLTlFQJ1pdHZic9CzK-fwvy8JEd.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/lJReWdGlE_L2vN0iJHuG9_W5z5kd.xml similarity index 100% rename from resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/0QLTlFQJ1pdHZic9CzK-fwvy8JEd.xml rename to resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/lJReWdGlE_L2vN0iJHuG9_W5z5kd.xml diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/lJReWdGlE_L2vN0iJHuG9_W5z5kp.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/lJReWdGlE_L2vN0iJHuG9_W5z5kp.xml new file mode 100644 index 000000000..559229e0f --- /dev/null +++ b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/lJReWdGlE_L2vN0iJHuG9_W5z5kp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/sdn40Xzdst7Zb2H9tGSrmIM0DwYp.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/sdn40Xzdst7Zb2H9tGSrmIM0DwYp.xml deleted file mode 100644 index 5ae250aa9..000000000 --- a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/sdn40Xzdst7Zb2H9tGSrmIM0DwYp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/tMvQ-p2hcTG5TEzg6UjLlP8PoZsp.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/tMvQ-p2hcTG5TEzg6UjLlP8PoZsp.xml deleted file mode 100644 index 182ca876b..000000000 --- a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/tMvQ-p2hcTG5TEzg6UjLlP8PoZsp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/udhwDiQtJoEy0hk-eIHYPaAcY-4p.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/udhwDiQtJoEy0hk-eIHYPaAcY-4p.xml deleted file mode 100644 index 220513d19..000000000 --- a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/udhwDiQtJoEy0hk-eIHYPaAcY-4p.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/0QLTlFQJ1pdHZic9CzK-fwvy8JEp.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/0QLTlFQJ1pdHZic9CzK-fwvy8JEp.xml deleted file mode 100644 index 371fc4abb..000000000 --- a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/0QLTlFQJ1pdHZic9CzK-fwvy8JEp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/9V40WFzIKXJGGp8oEqob15GAlwgp.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/9V40WFzIKXJGGp8oEqob15GAlwgp.xml deleted file mode 100644 index 7225b6797..000000000 --- a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/9V40WFzIKXJGGp8oEqob15GAlwgp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/9V40WFzIKXJGGp8oEqob15GAlwgd.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/A3BkjYsewYZv3b_c9MKlO0lijG4d.xml similarity index 100% rename from resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/9V40WFzIKXJGGp8oEqob15GAlwgd.xml rename to resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/A3BkjYsewYZv3b_c9MKlO0lijG4d.xml diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/A3BkjYsewYZv3b_c9MKlO0lijG4p.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/A3BkjYsewYZv3b_c9MKlO0lijG4p.xml new file mode 100644 index 000000000..48f06a319 --- /dev/null +++ b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/A3BkjYsewYZv3b_c9MKlO0lijG4p.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/UczN0iNj0jtudo2nI2gnIWNiIesd.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/AgcUDHWYVpO3utpT0Z6-LmAt6UUd.xml similarity index 100% rename from resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/UczN0iNj0jtudo2nI2gnIWNiIesd.xml rename to resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/AgcUDHWYVpO3utpT0Z6-LmAt6UUd.xml diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/AgcUDHWYVpO3utpT0Z6-LmAt6UUp.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/AgcUDHWYVpO3utpT0Z6-LmAt6UUp.xml new file mode 100644 index 000000000..e6c7b92d7 --- /dev/null +++ b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/AgcUDHWYVpO3utpT0Z6-LmAt6UUp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/bhlRi9i4fJ6RxGucF8nbbN9vdz4d.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/E73eq9DGYjhriVhYYpv10_eQeFgd.xml similarity index 100% rename from resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/bhlRi9i4fJ6RxGucF8nbbN9vdz4d.xml rename to resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/E73eq9DGYjhriVhYYpv10_eQeFgd.xml diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/E73eq9DGYjhriVhYYpv10_eQeFgp.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/E73eq9DGYjhriVhYYpv10_eQeFgp.xml new file mode 100644 index 000000000..523829dca --- /dev/null +++ b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/E73eq9DGYjhriVhYYpv10_eQeFgp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/UczN0iNj0jtudo2nI2gnIWNiIesp.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/UczN0iNj0jtudo2nI2gnIWNiIesp.xml deleted file mode 100644 index c13bfe6f8..000000000 --- a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/UczN0iNj0jtudo2nI2gnIWNiIesp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/iE5EXX33uQ1jkGTVTBI1zz34bFMd.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/bhkbou8oMnNGwgUfTIp5aAoS2scd.xml similarity index 100% rename from resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/iE5EXX33uQ1jkGTVTBI1zz34bFMd.xml rename to resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/bhkbou8oMnNGwgUfTIp5aAoS2scd.xml diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/bhkbou8oMnNGwgUfTIp5aAoS2scp.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/bhkbou8oMnNGwgUfTIp5aAoS2scp.xml new file mode 100644 index 000000000..9e23fa5fd --- /dev/null +++ b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/bhkbou8oMnNGwgUfTIp5aAoS2scp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/bhlRi9i4fJ6RxGucF8nbbN9vdz4p.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/bhlRi9i4fJ6RxGucF8nbbN9vdz4p.xml deleted file mode 100644 index 2f0f10b7d..000000000 --- a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/bhlRi9i4fJ6RxGucF8nbbN9vdz4p.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/tUk4baD64Ncsu-t2EDryess8gEwd.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/gY40mCv5zigjIbgF70Tlefoo_mgd.xml similarity index 100% rename from resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/tUk4baD64Ncsu-t2EDryess8gEwd.xml rename to resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/gY40mCv5zigjIbgF70Tlefoo_mgd.xml diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/gY40mCv5zigjIbgF70Tlefoo_mgp.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/gY40mCv5zigjIbgF70Tlefoo_mgp.xml new file mode 100644 index 000000000..86d4923ca --- /dev/null +++ b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/gY40mCv5zigjIbgF70Tlefoo_mgp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/iE5EXX33uQ1jkGTVTBI1zz34bFMp.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/iE5EXX33uQ1jkGTVTBI1zz34bFMp.xml deleted file mode 100644 index 9c300499c..000000000 --- a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/iE5EXX33uQ1jkGTVTBI1zz34bFMp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/tUk4baD64Ncsu-t2EDryess8gEwp.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/tUk4baD64Ncsu-t2EDryess8gEwp.xml deleted file mode 100644 index 965c6cb3d..000000000 --- a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/tUk4baD64Ncsu-t2EDryess8gEwp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/upN0pl2OtxHu1LpD1nHN8MyUJjwd.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/upN0pl2OtxHu1LpD1nHN8MyUJjwd.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/upN0pl2OtxHu1LpD1nHN8MyUJjwd.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/upN0pl2OtxHu1LpD1nHN8MyUJjwp.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/upN0pl2OtxHu1LpD1nHN8MyUJjwp.xml new file mode 100644 index 000000000..daf0cec15 --- /dev/null +++ b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/upN0pl2OtxHu1LpD1nHN8MyUJjwp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/Analysis/extractMtpDataFromSto.m b/src/MuscleTendonPersonalization/Analysis/extractMtpDataFromSto.m index 1fde150df..5b4a26b7f 100644 --- a/src/MuscleTendonPersonalization/Analysis/extractMtpDataFromSto.m +++ b/src/MuscleTendonPersonalization/Analysis/extractMtpDataFromSto.m @@ -38,6 +38,8 @@ dataFiles = {dataDir(3:end).name}; else fprintf("%s not found\n", resultsDirectory); + columnNames = []; + data = []; return end data = cell(1, numel(dataFiles)); diff --git a/src/MuscleTendonPersonalization/Analysis/plotJointMoments.m b/src/MuscleTendonPersonalization/Analysis/plotJointMoments.m deleted file mode 100644 index 803726ac7..000000000 --- a/src/MuscleTendonPersonalization/Analysis/plotJointMoments.m +++ /dev/null @@ -1,105 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function reads .sto files created by -% saveMuscleTendonPersonalizationResults.m containing inverse dynamics -% joint moments and model joint moments and creates plots of them. -% -% (string) -> (None) -% Plot joint moment curves from file. - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Di Ao, Marleny Vega, Robert Salati % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % -function plotJointMoments(resultsDirectory) -[jointLabels, idMoments] = extractMtpDataFromSto( ... - fullfile(resultsDirectory, "inverseDynamicsJointMoments")); -[~, noResidualMoments] = extractMtpDataFromSto( ... - fullfile(resultsDirectory, "modelJointMomentsNoSynx")); -[~, withResidualMoments] = extractMtpDataFromSto( ... - fullfile(resultsDirectory, "modelJointMomentsSynx")); -jointLabels = strrep(jointLabels, '_', ' '); -meanIdMoments = mean(idMoments, 3); -stdIdMoments = std(idMoments, [], 3); -meanNoResidualMoments = mean(noResidualMoments, 3); -stdNoResidualMoments = std(noResidualMoments, [], 3); -meanWithResidualMoments = mean(withResidualMoments, 3); -stdWithResidualMoments = std(withResidualMoments, [], 3); -maxMoment = max([ ... - max(meanIdMoments, [], "all"), ... - max(meanNoResidualMoments, [], "all") ... - max(meanWithResidualMoments, [], "all")]); -minMoment = min([ ... - min(meanIdMoments, [], "all"), ... - min(meanNoResidualMoments, [], "all") ... - min(meanWithResidualMoments, [], "all")]); - -figure(Name = "Joint Moments", ... - Units='normalized', ... - Position=[0.1 0.1 0.8 0.8]) -t = 1:1:size(meanIdMoments,1); -numWindows = numel(jointLabels); -for i=1:numel(jointLabels) - subplot(2, numWindows, i); - hold on - plot(meanNoResidualMoments(:,i), 'r-', linewidth=2) - plot(meanIdMoments(:,i), 'b-', linewidth=2) - - noResidualFillRegion = [(meanNoResidualMoments(:,i)+stdNoResidualMoments(:,i)); - flipud(meanNoResidualMoments(:,i)-stdNoResidualMoments(:,i))]; - idFillRegion = [(meanIdMoments(:,i)+stdIdMoments(:,i)); - flipud(meanIdMoments(:,i)-stdIdMoments(:,i))]; - fill([t, fliplr(t)]', noResidualFillRegion, 'r', FaceAlpha=0.2, ... - EdgeColor='none', HandleVisibility='off') - fill([t, fliplr(t)]', idFillRegion, 'b', FaceAlpha=0.2, ... - EdgeColor='none', HandleVisibility='off') - hold off - title(jointLabels(i), fontsize=12) - axis([0 size(meanIdMoments, 1) minMoment, maxMoment]) - if i == 1 - legend("Mean Moment No Residual", "Mean Inverse Dynamics Moment") - ylabel("Joint Moment [Nm]") - end - subplot(2, numWindows, i+3) - hold on - plot(meanWithResidualMoments(:,i), 'r-', linewidth=2) - plot(meanIdMoments(:,i), 'b-', linewidth=2) - - withResidualFillRegion = [(meanWithResidualMoments(:,i)+stdWithResidualMoments(:,i)); - flipud(meanWithResidualMoments(:,i)-stdWithResidualMoments(:,i))]; - idFillRegion = [(meanIdMoments(:,i)+stdIdMoments(:,i)); - flipud(meanIdMoments(:,i)-stdIdMoments(:,i))]; - fill([t, fliplr(t)]', withResidualFillRegion, 'r', FaceAlpha=0.2, ... - EdgeColor='none', HandleVisibility='off') - fill([t, fliplr(t)]', idFillRegion, 'r', FaceAlpha=0.2, ... - EdgeColor='none', HandleVisibility='off') - hold off - set(gca, fontsize=11) - title(jointLabels(i), FontSize=12) - xlabel("Time Point") - axis([0 size(meanIdMoments, 1) minMoment, maxMoment]) - if i == 1 - legend("Mean Moment With Residual", "Mean Inverse Dynamics Moment") - ylabel("Joint Moment [Nm]") - end - -end -end - diff --git a/src/MuscleTendonPersonalization/Analysis/plotMeanAndStd.m b/src/MuscleTendonPersonalization/Analysis/plotMeanAndStd.m new file mode 100644 index 000000000..4c89fdc39 --- /dev/null +++ b/src/MuscleTendonPersonalization/Analysis/plotMeanAndStd.m @@ -0,0 +1,7 @@ +function plotMeanAndStd(mean, std, time, color) + plot(time, mean, color, linewidth=2) + FillRegion = [(mean+std); + flipud(mean-std)]; + fill([time, fliplr(time)]', FillRegion, color, FaceAlpha=0.2, ... + EdgeColor='none', HandleVisibility='off') +end \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/Analysis/plotHillTypeMuscleParams.m b/src/MuscleTendonPersonalization/Analysis/plotMtpHillTypeMuscleParams.m similarity index 93% rename from src/MuscleTendonPersonalization/Analysis/plotHillTypeMuscleParams.m rename to src/MuscleTendonPersonalization/Analysis/plotMtpHillTypeMuscleParams.m index 3caa53701..489192cf2 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotHillTypeMuscleParams.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMtpHillTypeMuscleParams.m @@ -28,9 +28,10 @@ % implied. See the License for the specific language governing % % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function plotHillTypeMuscleParams(resultsDirectory) +function plotMtpHillTypeMuscleParams(resultsDirectory) +analysisDirectory = fullfile(resultsDirectory, "Analysis"); [muscleNames, params] = extractMtpDataFromSto( ... - fullfile(resultsDirectory, "muscleModelParameters")); + fullfile(analysisDirectory, "muscleModelParameters")); muscleNames = strrep(muscleNames, '_', ' '); figure(Name = "Muscle Model Parameters", ... Units='normalized', ... diff --git a/src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m b/src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m new file mode 100644 index 000000000..ca40df213 --- /dev/null +++ b/src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m @@ -0,0 +1,129 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function reads .sto files created by +% saveMuscleTendonPersonalizationResults.m containing inverse dynamics +% joint moments and model joint moments and creates plots of them. +% +% (string) -> (None) +% Plot joint moment curves from file. + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Di Ao, Marleny Vega, Robert Salati % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % +function plotMtpJointMoments(resultsDirectory) +analysisDirectory = fullfile(resultsDirectory, "Analysis"); +numRows = 1; + +[jointLabels, idMoments] = extractMtpDataFromSto( ... + fullfile(analysisDirectory, "inverseDynamicsJointMoments")); + +[~, modelMoments] = extractMtpDataFromSto( ... + fullfile(analysisDirectory, "modelJointMoments")); + +if exist(fullfile(analysisDirectory, "modelJointMomentsSynx"), "dir") + [~, modelMomentsSynx] = extractMtpDataFromSto( ... + fullfile(analysisDirectory, "modelJointMomentsSynx")); + numRows = numRows + 1; +else + modelMomentsSynx = []; +end + +if exist(fullfile(analysisDirectory, "modelJointMomentsSynxNoResiduals"), "dir") + [~, modelMomentsSynxNoResiduals] = extractMtpDataFromSto( ... + fullfile(analysisDirectory, "modelJointMomentsSynxNoResiduals")); + numRows = numRows + 1; +else + modelMomentsSynxNoResiduals = []; +end + +jointLabels = strrep(jointLabels, '_', ' '); +meanIdMoments = mean(idMoments, 3); +stdIdMoments = std(idMoments, [], 3); +meanMoments = mean(modelMoments, 3); +stdMoments = std(modelMoments, [], 3); +meanMomentsSynx = mean(modelMomentsSynx, 3); +stdMomentsSynx = std(modelMomentsSynx, [], 3); +meanMomentsSynxNoResidual = mean(modelMomentsSynxNoResiduals, 3); +stdMomentsSynxNoResidual = std(modelMomentsSynxNoResiduals, [], 3); +maxMoment = max([ ... + max(meanIdMoments, [], "all"), ... + max(meanMoments, [], "all"), ... + max(meanMomentsSynx, [], "all"), ... + max(meanMomentsSynxNoResidual, [], "all")]); +minMoment = min([ ... + min(meanIdMoments, [], "all"), ... + min(meanMoments, [], "all"), ... + min(meanMomentsSynx, [], "all"), ... + min(meanMomentsSynxNoResidual, [], "all")]); + +figure(Name = "Joint Moments", ... + Units='normalized', ... + Position=[0.1 0.1 0.8 0.8]) +time = 1:1:size(meanIdMoments,1); +numColumns = numel(jointLabels); +for i=1:numel(jointLabels) + if ~isempty(meanMoments) + subplot(numRows, numColumns, i); + hold on + plotMeanAndStd(meanMoments(:,i), stdMoments(:,i), time, 'r-') + plotMeanAndStd(meanIdMoments(:,i), stdIdMoments(:,i), time, 'b-') + hold off + title(jointLabels(i), fontsize=12) + axis([0 size(meanIdMoments, 1) minMoment, maxMoment]) + if i == 1 + legend("Mean Moment No Synx", "Mean Inverse Dynamics Moment") + ylabel("Joint Moment [Nm]") + end + end + + if ~isempty(meanMomentsSynx) + subplot(numRows, numColumns, i+3) + hold on + plotMeanAndStd(meanMomentsSynx(:,i), stdMomentsSynx(:,i), time, 'r-') + plotMeanAndStd(meanIdMoments(:,i), stdIdMoments(:,i), time, 'b-') + hold off + set(gca, fontsize=11) + title(jointLabels(i), FontSize=12) + xlabel("Time Point") + axis([0 size(meanIdMoments, 1) minMoment, maxMoment]) + if i == 1 + legend("Mean Moment Synx", "Mean Inverse Dynamics Moment") + ylabel("Joint Moment [Nm]") + end + end + + if ~isempty(meanMomentsSynxNoResidual) + subplot(numRows, numColumns, i+6) + hold on + plotMeanAndStd(meanMomentsSynxNoResidual(:,i), stdMomentsSynxNoResidual(:,i), time, 'r-') + plotMeanAndStd(meanIdMoments(:,i), stdIdMoments(:,i), time, 'b-') + hold off + set(gca, fontsize=11) + title(jointLabels(i), FontSize=12) + xlabel("Time Point") + axis([0 size(meanIdMoments, 1) minMoment, maxMoment]) + if i == 1 + legend("Mean Moment Synx No Residual", "Mean Inverse Dynamics Moment") + ylabel("Joint Moment [Nm]") + end + end +end +end \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/Analysis/plotMuscleExcitationsAndActivations.m b/src/MuscleTendonPersonalization/Analysis/plotMtpMuscleExcitationsAndActivations.m similarity index 64% rename from src/MuscleTendonPersonalization/Analysis/plotMuscleExcitationsAndActivations.m rename to src/MuscleTendonPersonalization/Analysis/plotMtpMuscleExcitationsAndActivations.m index 2dfdc6328..6d821f47e 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotMuscleExcitationsAndActivations.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMtpMuscleExcitationsAndActivations.m @@ -29,15 +29,27 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function plotMuscleExcitationsAndActivations(resultsDirectory) +function plotMtpMuscleExcitationsAndActivations(resultsDirectory) +analysisDirectory = fullfile(resultsDirectory, "Analysis"); [muscleNames, excitations] = extractMtpDataFromSto( ... - fullfile(resultsDirectory, "muscleExcitations")); + fullfile(analysisDirectory, "muscleExcitations")); + [~, activations] = extractMtpDataFromSto( ... - fullfile(resultsDirectory, "muscleActivations")); -[~, excitationsSynx] = extractMtpDataFromSto( ... - fullfile(resultsDirectory, "muscleExcitationsSynx")); -[~, activationsSynx] = extractMtpDataFromSto( ... - fullfile(resultsDirectory, "muscleActivationsSynx")); + fullfile(analysisDirectory, "muscleActivations")); + +if exist(fullfile(analysisDirectory, "muscleExcitationsSynx"), "dir") + [~, excitationsSynx] = extractMtpDataFromSto( ... + fullfile(analysisDirectory, "muscleExcitationsSynx")); +else + excitationsSynx = []; +end + +if exist(fullfile(analysisDirectory, "muscleActivationsSynx"), "dir") + [~, activationsSynx] = extractMtpDataFromSto( ... + fullfile(analysisDirectory, "muscleActivationsSynx")); +else + activationsSynx = []; +end muscleNames = strrep(muscleNames, '_', ' '); meanExcitations = mean(excitations, 3); stdExcitations = std(excitations,[], 3); @@ -52,42 +64,26 @@ function plotMuscleExcitationsAndActivations(resultsDirectory) Units='normalized', ... Position=[0.1 0.1 0.8 0.8]) -t = 1:1:size(meanExcitations,1); +time = 1:1:size(meanExcitations,1); numWindows = ceil(sqrt(numel(muscleNames))); for i = 1:numel(muscleNames) subplot(numWindows, numWindows, i); hold on - plot(meanExcitations(:,i), 'b-', linewidth=2) - plot(meanExcitationsSynx(:,i), 'b--', linewidth=2) - plot(meanActivations(:,i), 'r-', linewidth=2) - plot(meanActivationsSynx(:,i), 'r--', linewidth=2) - - excitationFillRegion = [(meanExcitations(:,i)+stdExcitations(:,i)); ... - flipud((meanExcitations(:,i)-stdExcitations(:,i)))]; - fill([t, fliplr(t)]', excitationFillRegion, 'b', FaceAlpha=0.2, ... - EdgeColor='none', HandleVisibility='off') - - excitationSynxFillRegion = [(meanExcitationsSynx(:,i)+stdExcitationsSynx(:,i)); ... - flipud((meanExcitationsSynx(:,i)-stdExcitationsSynx(:,i)))]; - fill([t, fliplr(t)]', excitationSynxFillRegion, 'b', FaceAlpha=0.2, ... - EdgeColor='none', HandleVisibility='off') - - activationFillRegion = [(meanActivations(:,i)+stdActivations(:,i)); ... - flipud((meanActivations(:,i)-stdActivations(:,i)))]; - fill([t, fliplr(t)]', activationFillRegion, 'r', FaceAlpha=0.2, ... - EdgeColor='none', HandleVisibility='off') - - activationSynxFillRegion = [(meanActivationsSynx(:,i)+stdActivationsSynx(:,i)); ... - flipud((meanActivationsSynx(:,i)-stdActivationsSynx(:,i)))]; - fill([t, fliplr(t)]', activationSynxFillRegion, 'r', FaceAlpha=0.2, ... - EdgeColor='none', HandleVisibility='off') + plotMeanAndStd(meanExcitations(:,i), stdExcitations, time, 'b-'); + plotMeanAndStd(meanActivations(:,i), stdActivations(:,i), time, 'r-'); + if ~isempty(meanExcitationsSynx) + plotMeanAndStd(meanExcitationsSynx(:,i), stdExcitationsSynx, time, 'b--'); + end + if ~isempty(meanActivationsSynx) + plotMeanAndStd(meanActivationsSynx(:,i), stdActivationsSynx, time, 'r--'); + end set(gca, fontsize=11) axis([1 size(meanExcitations, 1) 0 1]) title(muscleNames(i), FontSize=12); if i == 1 legend ('Excitation(without residual)', ... - 'Excitation(with residual)', ... 'Activation(without residual)', ... + 'Excitation(with residual)', ... 'Activation(with residual)'); end if mod(i,3) == 1 diff --git a/src/MuscleTendonPersonalization/Analysis/plotNormalizedFiberLengths.m b/src/MuscleTendonPersonalization/Analysis/plotMtpNormalizedFiberLengths.m similarity index 81% rename from src/MuscleTendonPersonalization/Analysis/plotNormalizedFiberLengths.m rename to src/MuscleTendonPersonalization/Analysis/plotMtpNormalizedFiberLengths.m index 26f7e9581..7049f071c 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotNormalizedFiberLengths.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMtpNormalizedFiberLengths.m @@ -28,9 +28,10 @@ % implied. See the License for the specific language governing % % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function plotNormalizedFiberLengths(resultsDirectory) +function plotMtpNormalizedFiberLengths(resultsDirectory) +analysisDirectory = fullfile(resultsDirectory, "Analysis"); [muscleNames, normalizedFiberLengths] = extractMtpDataFromSto( ... - fullfile(resultsDirectory, "normalizedFiberLengths")); + fullfile(analysisDirectory, "normalizedFiberLengths")); muscleNames = strrep(muscleNames, '_', ' '); meanFiberLengths = mean(normalizedFiberLengths, 3); stdFiberLengths = std(normalizedFiberLengths, [], 3); @@ -38,23 +39,17 @@ function plotNormalizedFiberLengths(resultsDirectory) figure(Name = "Normalized Fiber Lengths", ... Units='normalized', ... Position=[0.1 0.1 0.8 0.8]) -t = 1:1:size(meanFiberLengths,1); +time = 1:1:size(meanFiberLengths,1); numWindows = ceil(sqrt(numel(muscleNames))); -passiveLower = ones(size(t))*0.7; -passiveUpper = ones(size(t)); +passiveLower = ones(size(time))*0.7; +passiveUpper = ones(size(time)); for i=1:numel(muscleNames) subplot(numWindows, numWindows, i); hold on - plot(meanFiberLengths(:,i), 'b-', LineWidth=2); - - fillRegion = [(meanFiberLengths(:,i)+stdFiberLengths(:,i)); - flipud(meanFiberLengths(:,i)-stdFiberLengths(:,i))]; - fill([t, fliplr(t)]', fillRegion, 'b', FaceAlpha=0.2, ... - EdgeColor='none', HandleVisibility='off') - - plot(t, passiveUpper, 'r--', LineWidth=2); - plot(t, passiveLower, 'r--', LineWidth=2); + plotMeanAndStd(meanFiberLengths(:,i), stdFiberLengths(:,i), time, 'b-') + plot(time, passiveUpper, 'r--', LineWidth=2); + plot(time, passiveLower, 'r--', LineWidth=2); hold off set(gca, fontsize=11) axis([1 size(meanFiberLengths, 1) 0 1.5]) diff --git a/src/MuscleTendonPersonalization/Analysis/plotPassiveForceCurves.m b/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveForceCurves.m similarity index 94% rename from src/MuscleTendonPersonalization/Analysis/plotPassiveForceCurves.m rename to src/MuscleTendonPersonalization/Analysis/plotMtpPassiveForceCurves.m index 3aee45c23..54404b6a1 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotPassiveForceCurves.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveForceCurves.m @@ -28,9 +28,10 @@ % implied. See the License for the specific language governing % % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function plotPassiveForceCurves(resultsDirectory) +function plotMtpPassiveForceCurves(resultsDirectory) +analysisDirectory = fullfile(resultsDirectory, "Analysis"); [muscleNames, passiveForce] = extractMtpDataFromSto( ... - fullfile(resultsDirectory, "passiveForcesExperimental")); + fullfile(analysisDirectory, "passiveForcesExperimental")); muscleNames = strrep(muscleNames, '_', ' '); meanPassiveForce = mean(passiveForce, 3); stdPassiveForce = std(passiveForce, [], 3); diff --git a/src/MuscleTendonPersonalization/Analysis/plotPassiveMomentCurves.m b/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveMomentCurves.m similarity index 94% rename from src/MuscleTendonPersonalization/Analysis/plotPassiveMomentCurves.m rename to src/MuscleTendonPersonalization/Analysis/plotMtpPassiveMomentCurves.m index 693fa5038..b63790b67 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotPassiveMomentCurves.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveMomentCurves.m @@ -29,11 +29,12 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function plotPassiveMomentCurves(resultsDirectory) +function plotMtpPassiveMomentCurves(resultsDirectory) +analysisDirectory = fullfile(resultsDirectory, "Analysis"); [momentNames, passiveMomentsExperimental] = extractMtpDataFromSto( ... - fullfile(resultsDirectory, "passiveJointMomentsExperimental")); + fullfile(analysisDirectory, "passiveJointMomentsExperimental")); [~, passiveMomentsModel] = extractMtpDataFromSto( ... - fullfile(resultsDirectory, "passiveJointMomentsModeled")); + fullfile(analysisDirectory, "passiveJointMomentsModeled")); momentNames = strrep(momentNames, '_', ' '); meanPassiveMomentsExperimental = mean(passiveMomentsExperimental, 3); stdPassiveMomentsExperimental = std(passiveMomentsExperimental, [], 3); diff --git a/src/MuscleTendonPersonalization/Analysis/printJointMomentMatchingError.m b/src/MuscleTendonPersonalization/Analysis/printMtpJointMomentMatchingError.m similarity index 92% rename from src/MuscleTendonPersonalization/Analysis/printJointMomentMatchingError.m rename to src/MuscleTendonPersonalization/Analysis/printMtpJointMomentMatchingError.m index 882651805..0fdb53149 100644 --- a/src/MuscleTendonPersonalization/Analysis/printJointMomentMatchingError.m +++ b/src/MuscleTendonPersonalization/Analysis/printMtpJointMomentMatchingError.m @@ -29,11 +29,12 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function printJointMomentMatchingError(resultsDirectory) +function printMtpJointMomentMatchingError(resultsDirectory) +analysisDirectory = fullfile(resultsDirectory, "Analysis"); [jointMomentLabels, muscleJointMoments] = extractMtpDataFromSto( ... - fullfile(resultsDirectory, "modelJointMomentsSynx")); + fullfile(analysisDirectory, "modelJointMomentsSynx")); [~, inverseDynamicsMoments] = extractMtpDataFromSto( ... - fullfile(resultsDirectory, "inverseDynamicsJointMoments")); + fullfile(analysisDirectory, "inverseDynamicsJointMoments")); jointMomentsRmse = zeros(size(jointMomentLabels)); jointMomentsMae = zeros(size(jointMomentLabels)); for i = 1 : size(muscleJointMoments, 2) diff --git a/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m b/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m index e01149d42..f0e29a760 100644 --- a/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m +++ b/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m @@ -46,27 +46,13 @@ function MuscleTendonPersonalizationTool(settingsFileName) if params.performMuscleTendonLengthInitialization [finalValues, resultsStruct, modeledValues] = ... getMtpResultsToSave(inputs, params, optimizedParams, precalInputs); - saveMuscleTendonPersonalizationResults(inputs, finalValues, modeledValues, ... + saveMtpMuscleTendonPersonalizationResults(inputs, finalValues, modeledValues, ... resultsStruct, resultsDirectory, precalInputs); else [finalValues, resultsStruct, modeledValues] = ... getMtpResultsToSave(inputs, params, optimizedParams); - saveMuscleTendonPersonalizationResults(inputs, finalValues, modeledValues, ... + saveMtpMuscleTendonPersonalizationResults(inputs, finalValues, modeledValues, ... resultsStruct, resultsDirectory); end -finalValues = makeMtpValuesAsStruct([], optimizedParams, zeros(1, 7), inputs); -if precalInputs.optimizeIsometricMaxForce - finalValues.maxIsometricForce = inputs.maxIsometricForce; -end -if isfield(inputs, "synergyExtrapolation") - results = calcMtpSynXModeledValues(finalValues, inputs, params); -else - results = calcMtpModeledValues(finalValues, inputs, params); -end - -results.time = inputs.emgTime(:, inputs.numPaddingFrames + 1 : ... - end - inputs.numPaddingFrames); -saveMuscleTendonPersonalizationResults(inputs.modelFileName, ... - inputs.osimxFileName, inputs.prefixes, inputs.coordinateNames, ... - finalValues, results, resultsDirectory, inputs.muscleTendonColumnNames); -end +printMtpJointMomentMatchingError(resultsDirectory); +end \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/Saving/getMtpResultsToSave.m b/src/MuscleTendonPersonalization/Saving/getMtpResultsToSave.m index f56cc2fd1..d6c6786b9 100644 --- a/src/MuscleTendonPersonalization/Saving/getMtpResultsToSave.m +++ b/src/MuscleTendonPersonalization/Saving/getMtpResultsToSave.m @@ -31,7 +31,7 @@ function [finalValues, resultsStruct, modeledValues] = ... getMtpResultsToSave(mtpInputs, params, optimizedParams, precalInputs) -finalValues = makeMtpValuesAsStruct([], optimizedParams, zeros(1, 7)); +finalValues = makeMtpValuesAsStruct([], optimizedParams, zeros(1, 7), mtpInputs); if nargin < 4 modeledValues = []; precalInputs = []; @@ -53,16 +53,20 @@ end - mtpInputs.numPaddingFrames); results.muscleExcitations = results.muscleExcitations(:, :, ... mtpInputs.numPaddingFrames + 1 : end - mtpInputs.numPaddingFrames); -resultsSynx = calcMtpSynXModeledValues(finalValues, mtpInputs, params); -resultsSynx.time = mtpInputs.emgTime(:, mtpInputs.numPaddingFrames + 1 : ... - end - mtpInputs.numPaddingFrames); -resultsSynx.muscleExcitations = resultsSynx.muscleExcitations(:, :, ... - mtpInputs.numPaddingFrames + 1 : end - mtpInputs.numPaddingFrames); -finalValues.synergyWeights(mtpInputs.numberOfExtrapolationWeights + 1 : end) = 0; -resultsSynxNoResiduals = calcMtpSynXModeledValues(finalValues, mtpInputs, struct()); -resultsStruct = struct("results", results, ... - "resultsSynx", resultsSynx, ... - "resultsSynxNoResiduals", resultsSynxNoResiduals); +if isfield(mtpInputs, "synergyExtrapolation") + resultsSynx = calcMtpSynXModeledValues(finalValues, mtpInputs, params); + resultsSynx.time = mtpInputs.emgTime(:, mtpInputs.numPaddingFrames + 1 : ... + end - mtpInputs.numPaddingFrames); + resultsSynx.muscleExcitations = resultsSynx.muscleExcitations(:, :, ... + mtpInputs.numPaddingFrames + 1 : end - mtpInputs.numPaddingFrames); + finalValues.synergyWeights(mtpInputs.numberOfExtrapolationWeights + 1 : end) = 0; + resultsSynxNoResiduals = calcMtpSynXModeledValues(finalValues, mtpInputs, struct()); + resultsStruct = struct("results", results, ... + "resultsSynx", resultsSynx, ... + "resultsSynxNoResiduals", resultsSynxNoResiduals); +else + resultsStruct = struct("results", results); +end if ~isempty(precalInputs) finalOptimalFiberLength = ... finalValues.optimalFiberLengthScaleFactors .* mtpInputs.optimalFiberLength; diff --git a/src/MuscleTendonPersonalization/Saving/saveActivationAndExcitationData.m b/src/MuscleTendonPersonalization/Saving/saveMtpActivationAndExcitationData.m similarity index 74% rename from src/MuscleTendonPersonalization/Saving/saveActivationAndExcitationData.m rename to src/MuscleTendonPersonalization/Saving/saveMtpActivationAndExcitationData.m index bef650cf3..741a080c5 100644 --- a/src/MuscleTendonPersonalization/Saving/saveActivationAndExcitationData.m +++ b/src/MuscleTendonPersonalization/Saving/saveMtpActivationAndExcitationData.m @@ -29,18 +29,21 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function saveActivationAndExcitationData(mtpInputs, results, ... - resultsSynx, resultsDirectory) +function saveMtpActivationAndExcitationData(mtpInputs, resultsStruct, ... + resultsDirectory) writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... - results.muscleExcitations, strcat(resultsDirectory, ... + resultsStruct.results.muscleExcitations, strcat(resultsDirectory, ... "\muscleExcitations"), "_muscleExcitations.sto") writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... - resultsSynx.muscleExcitations, strcat(resultsDirectory, ... - "\muscleExcitationsSynx"), "_muscleExcitationsSynx.sto") -writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... - results.muscleActivations, strcat(resultsDirectory, ... + resultsStruct.results.muscleActivations, strcat(resultsDirectory, ... "\muscleActivations"), "_muscleActivations.sto") -writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... - resultsSynx.muscleActivations, strcat(resultsDirectory, ... - "\muscleActivationsSynx"), "_muscleActivationsSynx.sto") +if isfield(mtpInputs, "synergyExtrapolation") + writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... + resultsStruct.resultsSynx.muscleExcitations, strcat(resultsDirectory, ... + "\muscleExcitationsSynx"), "_muscleExcitationsSynx.sto") + + writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... + resultsStruct.resultsSynx.muscleActivations, strcat(resultsDirectory, ... + "\muscleActivationsSynx"), "_muscleActivationsSynx.sto") +end end \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/Saving/saveJointMomentData.m b/src/MuscleTendonPersonalization/Saving/saveMtpJointMomentData.m similarity index 75% rename from src/MuscleTendonPersonalization/Saving/saveJointMomentData.m rename to src/MuscleTendonPersonalization/Saving/saveMtpJointMomentData.m index dac13f3af..91a32dca6 100644 --- a/src/MuscleTendonPersonalization/Saving/saveJointMomentData.m +++ b/src/MuscleTendonPersonalization/Saving/saveMtpJointMomentData.m @@ -30,17 +30,18 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function saveJointMomentData(mtpInputs, results, resultsSynx, ... - resultsSynxNoResiduals, resultsDirectory) +function saveMtpJointMomentData(mtpInputs, resultsStruct, resultsDirectory) writeMtpDataToSto(mtpInputs.coordinateNames, mtpInputs.prefixes, ... - results.muscleJointMoments, strcat(resultsDirectory, ... - "\modelJointMomentsNoSynx"), "_modelJointMomentsNoSynx.sto") -writeMtpDataToSto(mtpInputs.coordinateNames, mtpInputs.prefixes, ... - resultsSynx.muscleJointMoments, strcat(resultsDirectory, ... - "\modelJointMomentsSynx"), "_modelJointMomentsSynx.sto") -writeMtpDataToSto(mtpInputs.coordinateNames, mtpInputs.prefixes, ... - resultsSynxNoResiduals.muscleJointMoments, strcat(resultsDirectory, ... - "\modelJointMomentsSynxNoResiduals"), "_modelJointMomentsSynxNoResiduals.sto") + resultsStruct.results.muscleJointMoments, strcat(resultsDirectory, ... + "\modelJointMoments"), "_modelJointMoments.sto") +if isfield(mtpInputs, "synergyExtrapolation") + writeMtpDataToSto(mtpInputs.coordinateNames, mtpInputs.prefixes, ... + resultsStruct.resultsSynx.muscleJointMoments, strcat(resultsDirectory, ... + "\modelJointMomentsSynx"), "_modelJointMomentsSynx.sto") + writeMtpDataToSto(mtpInputs.coordinateNames, mtpInputs.prefixes, ... + resultsStruct.resultsSynxNoResiduals.muscleJointMoments, strcat(resultsDirectory, ... + "\modelJointMomentsSynxNoResiduals"), "_modelJointMomentsSynxNoResiduals.sto") +end writeMtpDataToSto(mtpInputs.coordinateNames, mtpInputs.prefixes, ... mtpInputs.inverseDynamicsMoments, strcat(resultsDirectory, ... "\inverseDynamicsJointMoments"), "_inverseDynamicsJointMoments.sto") diff --git a/src/MuscleTendonPersonalization/Saving/saveMuscleModelParameters.m b/src/MuscleTendonPersonalization/Saving/saveMtpMuscleModelParameters.m similarity index 97% rename from src/MuscleTendonPersonalization/Saving/saveMuscleModelParameters.m rename to src/MuscleTendonPersonalization/Saving/saveMtpMuscleModelParameters.m index 4257034ae..9a851e162 100644 --- a/src/MuscleTendonPersonalization/Saving/saveMuscleModelParameters.m +++ b/src/MuscleTendonPersonalization/Saving/saveMtpMuscleModelParameters.m @@ -33,7 +33,7 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function saveMuscleModelParameters(mtpInputs, finalValues, resultsDirectory) +function saveMtpMuscleModelParameters(mtpInputs, finalValues, resultsDirectory) if ~exist(resultsDirectory, "dir") mkdir(resultsDirectory); end diff --git a/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m b/src/MuscleTendonPersonalization/Saving/saveMtpMuscleTendonPersonalizationResults.m similarity index 77% rename from src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m rename to src/MuscleTendonPersonalization/Saving/saveMtpMuscleTendonPersonalizationResults.m index 19ab9429e..7f7ab6983 100644 --- a/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m +++ b/src/MuscleTendonPersonalization/Saving/saveMtpMuscleTendonPersonalizationResults.m @@ -32,30 +32,27 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function saveMuscleTendonPersonalizationResults(mtpInputs, finalValues, ... +function saveMtpMuscleTendonPersonalizationResults(mtpInputs, finalValues, ... modeledValues, resultsStruct, resultsDirectory, precalInputs) -results = resultsStruct.results; -resultsSynx = resultsStruct.resultsSynx; -resultsSynxNoResiduals = resultsStruct.resultsSynxNoResiduals; analysisDirectory = fullfile(resultsDirectory, "Analysis"); - +if exist(analysisDirectory, "dir") + rmdir(analysisDirectory, 's') +end if nargin < 6 precalInputs = []; end if ~isempty(precalInputs) - savePassiveMomentData(precalInputs, modeledValues, analysisDirectory); - savePassiveForceData(mtpInputs, modeledValues, results, resultsSynx, ... - resultsSynxNoResiduals, analysisDirectory); + saveMtpPassiveMomentData(precalInputs, modeledValues, analysisDirectory); + saveMtpPassiveForceData(mtpInputs, modeledValues, resultsStruct, analysisDirectory); end -saveActivationAndExcitationData(mtpInputs, results, resultsSynx, analysisDirectory); -writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, results.normalizedFiberLength, ... +saveMtpActivationAndExcitationData(mtpInputs, resultsStruct, analysisDirectory); +writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, resultsStruct.results.normalizedFiberLength, ... fullfile(analysisDirectory, "normalizedFiberLengths"), "_normalizedFiberLengths.sto") -saveJointMomentData(mtpInputs, results, resultsSynx, resultsSynxNoResiduals, ... - analysisDirectory); -saveMuscleModelParameters(mtpInputs, finalValues, fullfile(analysisDirectory, ... +saveMtpJointMomentData(mtpInputs, resultsStruct, analysisDirectory); +saveMtpMuscleModelParameters(mtpInputs, finalValues, fullfile(analysisDirectory, ... "muscleModelParameters")); model = Model(mtpInputs.model); muscleNames = getMusclesFromCoordinates(model, mtpInputs.coordinateNames); -writeMuscleTendonPersonalizationOsimxFile(mtpInputs.model, mtpInputs.osimxFileName, ... +writeMuscleTendonPersonalizationOsimxFile(mtpInputs.modelFileName, mtpInputs.osimxFileName, ... finalValues, muscleNames, resultsDirectory); end \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/Saving/savePassiveForceData.m b/src/MuscleTendonPersonalization/Saving/saveMtpPassiveForceData.m similarity index 77% rename from src/MuscleTendonPersonalization/Saving/savePassiveForceData.m rename to src/MuscleTendonPersonalization/Saving/saveMtpPassiveForceData.m index af0b39881..df616a083 100644 --- a/src/MuscleTendonPersonalization/Saving/savePassiveForceData.m +++ b/src/MuscleTendonPersonalization/Saving/saveMtpPassiveForceData.m @@ -30,18 +30,20 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function savePassiveForceData(mtpInputs, modeledValues, results, resultsSynx, ... - resultsSynxNoResiduals, resultsDirectory) +function saveMtpPassiveForceData(mtpInputs, modeledValues, resultsStruct, ... + resultsDirectory) writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... modeledValues.passiveForce, strcat(resultsDirectory, ... "\passiveForcesExperimental"), "_passiveForcesExperimental.sto"); writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... - results.passiveForce, strcat(resultsDirectory, ... + resultsStruct.results.passiveForce, strcat(resultsDirectory, ... "\passiveForcesModel"), "_passiveForcesModel.sto"); -writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... - resultsSynx.passiveForce, strcat(resultsDirectory, ... - "\passiveForcesModelSynx"), "_passiveForcesModelSynx.sto"); -writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... - resultsSynxNoResiduals.passiveForce, strcat(resultsDirectory, ... - "\passiveForcesModelSynxNoResiduals"), "_passiveForcesModelSynxNoResiduals.sto"); +if isfield(mtpInputs, "synergyExtrapolation") + writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... + resultsStruct.resultsSynx.passiveForce, strcat(resultsDirectory, ... + "\passiveForcesModelSynx"), "_passiveForcesModelSynx.sto"); + writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... + resultsStruct.resultsSynxNoResiduals.passiveForce, strcat(resultsDirectory, ... + "\passiveForcesModelSynxNoResiduals"), "_passiveForcesModelSynxNoResiduals.sto"); +end end diff --git a/src/MuscleTendonPersonalization/Saving/savePassiveMomentData.m b/src/MuscleTendonPersonalization/Saving/saveMtpPassiveMomentData.m similarity index 97% rename from src/MuscleTendonPersonalization/Saving/savePassiveMomentData.m rename to src/MuscleTendonPersonalization/Saving/saveMtpPassiveMomentData.m index 773afb7c0..86d7e01b2 100644 --- a/src/MuscleTendonPersonalization/Saving/savePassiveMomentData.m +++ b/src/MuscleTendonPersonalization/Saving/saveMtpPassiveMomentData.m @@ -29,7 +29,7 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function savePassiveMomentData(precalInputs, modeledValues, resultsDirectory) +function saveMtpPassiveMomentData(precalInputs, modeledValues, resultsDirectory) modelPassiveMoments = permute(modeledValues.passiveModelMoments, [3 1 2]); sizeTemp = size(modelPassiveMoments,1); experimentalPassiveMoments = permute(precalInputs.passiveData.inverseDynamicsMoments, [3 1 2]); From a6c7978cf6c2359053046a5b3443306811c619f8 Mon Sep 17 00:00:00 2001 From: RobSalati <142843461+RobSalati@users.noreply.github.com> Date: Tue, 19 Dec 2023 21:30:13 -0500 Subject: [PATCH 249/365] Added modelMoments and muscleActivations outside of Analysis folder --- .../AgcUDHWYVpO3utpT0Z6-LmAt6UUp.xml | 2 - ...d.xml => iE5EXX33uQ1jkGTVTBI1zz34bFMd.xml} | 0 .../iE5EXX33uQ1jkGTVTBI1zz34bFMp.xml | 2 + .../Analysis/plotMeanAndStd.m | 3 +- .../Analysis/plotMtpPassiveForceCurves.m | 35 ++++++++++------ .../Analysis/plotMtpPassiveMomentCurves.m | 35 ++++++---------- .../printMtpJointMomentMatchingError.m | 2 +- .../MuscleTendonPersonalizationTool.m | 4 +- .../saveMtpActivationAndExcitationData.m | 40 +++++++++++++++---- .../Saving/saveMtpJointMomentData.m | 35 +++++++++++----- ... saveMuscleTendonPersonalizationResults.m} | 16 ++++---- .../Saving/writeMtpDataToSto.m | 3 +- 12 files changed, 111 insertions(+), 66 deletions(-) delete mode 100644 resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/AgcUDHWYVpO3utpT0Z6-LmAt6UUp.xml rename resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/{AgcUDHWYVpO3utpT0Z6-LmAt6UUd.xml => iE5EXX33uQ1jkGTVTBI1zz34bFMd.xml} (100%) create mode 100644 resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/iE5EXX33uQ1jkGTVTBI1zz34bFMp.xml rename src/MuscleTendonPersonalization/Saving/{saveMtpMuscleTendonPersonalizationResults.m => saveMuscleTendonPersonalizationResults.m} (85%) diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/AgcUDHWYVpO3utpT0Z6-LmAt6UUp.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/AgcUDHWYVpO3utpT0Z6-LmAt6UUp.xml deleted file mode 100644 index e6c7b92d7..000000000 --- a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/AgcUDHWYVpO3utpT0Z6-LmAt6UUp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/AgcUDHWYVpO3utpT0Z6-LmAt6UUd.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/iE5EXX33uQ1jkGTVTBI1zz34bFMd.xml similarity index 100% rename from resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/AgcUDHWYVpO3utpT0Z6-LmAt6UUd.xml rename to resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/iE5EXX33uQ1jkGTVTBI1zz34bFMd.xml diff --git a/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/iE5EXX33uQ1jkGTVTBI1zz34bFMp.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/iE5EXX33uQ1jkGTVTBI1zz34bFMp.xml new file mode 100644 index 000000000..9c300499c --- /dev/null +++ b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/iE5EXX33uQ1jkGTVTBI1zz34bFMp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/Analysis/plotMeanAndStd.m b/src/MuscleTendonPersonalization/Analysis/plotMeanAndStd.m index 4c89fdc39..f76f8a1b8 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotMeanAndStd.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMeanAndStd.m @@ -1,7 +1,6 @@ function plotMeanAndStd(mean, std, time, color) plot(time, mean, color, linewidth=2) - FillRegion = [(mean+std); - flipud(mean-std)]; + FillRegion = [(mean+std); flipud(mean-std)]; fill([time, fliplr(time)]', FillRegion, color, FaceAlpha=0.2, ... EdgeColor='none', HandleVisibility='off') end \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveForceCurves.m b/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveForceCurves.m index 54404b6a1..477a0b8dd 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveForceCurves.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveForceCurves.m @@ -30,31 +30,42 @@ % ----------------------------------------------------------------------- % function plotMtpPassiveForceCurves(resultsDirectory) analysisDirectory = fullfile(resultsDirectory, "Analysis"); -[muscleNames, passiveForce] = extractMtpDataFromSto( ... +[muscleNames, experimentalForce] = extractMtpDataFromSto( ... fullfile(analysisDirectory, "passiveForcesExperimental")); +if exist(fullfile(analysisDirectory, "passiveForcesModelSynx"), "dir") + [~, modelForce] = extractMtpDataFromSto(... + fullfile(analysisDirectory, "passiveForcesModelSynx")); +else + [~, modelForce] = extractMtpDataFromSto(... + fullfile(analysisDirectory, "passiveForcesModel")); +end muscleNames = strrep(muscleNames, '_', ' '); -meanPassiveForce = mean(passiveForce, 3); -stdPassiveForce = std(passiveForce, [], 3); -maxForce = max(meanPassiveForce, [], 'all'); +meanExperimentalForce = mean(experimentalForce, 3); +stdExperimentalForce = std(experimentalForce, [], 3); +meanModelForce = mean(modelForce, 3); +stdModelForce = std(modelForce, [], 3); +maxForce = max( ... + [max(meanModelForce, [], 'all'), ... + max(meanExperimentalForce, [], 'all')]); numWindows = ceil(sqrt(numel(muscleNames))); figure(Name = "Passive Force Curves", ... Units='normalized', ... Position=[0.1 0.1 0.8 0.8]) -t = 1:1:size(meanPassiveForce,1); +time = 1:1:size(meanExperimentalForce,1); for i = 1:numel(muscleNames) subplot(numWindows, numWindows, i) hold on - plot(meanPassiveForce(:,i), 'b-', linewidth=2) - - fillRegion = [(meanPassiveForce(:,i)+stdPassiveForce(:,i)); - flipud((meanPassiveForce(:,i)-stdPassiveForce(:,i)))]; - fill([t, fliplr(t)]', fillRegion, 'b', FaceAlpha=0.2, ... - EdgeColor='none', HandleVisibility='off') + plotMeanAndStd(meanExperimentalForce(:,i), stdExperimentalForce(:,i), ... + time, 'k-') + plotMeanAndStd(meanModelForce(:,i), stdModelForce(:,i), time, 'r-'); hold off set(gca, fontsize=11) - axis([1 size(meanPassiveForce, 1) 0 maxForce]) + axis([1 numel(time) 0 maxForce]) title(muscleNames(i), FontSize=12); + if i == 1 + legend("Experimental", "Model") + end if mod(i,3) == 1 ylabel("Magnitude") end diff --git a/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveMomentCurves.m b/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveMomentCurves.m index b63790b67..23ef50d98 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveMomentCurves.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveMomentCurves.m @@ -36,17 +36,17 @@ function plotMtpPassiveMomentCurves(resultsDirectory) [~, passiveMomentsModel] = extractMtpDataFromSto( ... fullfile(analysisDirectory, "passiveJointMomentsModeled")); momentNames = strrep(momentNames, '_', ' '); -meanPassiveMomentsExperimental = mean(passiveMomentsExperimental, 3); -stdPassiveMomentsExperimental = std(passiveMomentsExperimental, [], 3); -meanPassiveMomentsModel = mean(passiveMomentsModel, 3); -stdPassiveMomentsModel = std(passiveMomentsModel, [], 3); -maxMoment = max([max(meanPassiveMomentsExperimental, [], 'all'), ... - max(meanPassiveMomentsModel, [], 'all')]); -minMoment = min([min(meanPassiveMomentsExperimental, [], 'all'), ... - min(meanPassiveMomentsModel, [], 'all')]); +meanMomentsExperimental = mean(passiveMomentsExperimental, 3); +stdMomentsExperimental = std(passiveMomentsExperimental, [], 3); +meanMomentsModel = mean(passiveMomentsModel, 3); +stdMomentsModel = std(passiveMomentsModel, [], 3); +maxMoment = max([max(meanMomentsExperimental, [], 'all'), ... + max(meanMomentsModel, [], 'all')]); +minMoment = min([min(meanMomentsExperimental, [], 'all'), ... + min(meanMomentsModel, [], 'all')]); numWindows = ceil(sqrt(numel(momentNames))); -t = 1:1:size(meanPassiveMomentsModel,1); +time = 1:1:size(meanMomentsModel,1); figure(Name = "Passive Moment Curves", ... Units='normalized', ... Position=[0.1 0.1 0.8 0.8]) @@ -54,22 +54,13 @@ function plotMtpPassiveMomentCurves(resultsDirectory) for i = 1:numel(momentNames) subplot(numWindows, numWindows, i) hold on - - plot(meanPassiveMomentsExperimental(:,i), 'k-', linewidth=2) - plot(meanPassiveMomentsModel(:,i), 'r-', linewidth=2) - - fillRegionExperimental = [(meanPassiveMomentsExperimental(:,i)+stdPassiveMomentsExperimental(:,i)); - flipud((meanPassiveMomentsExperimental(:,i)-stdPassiveMomentsExperimental(:,i)))]; - fill([t, fliplr(t)]', fillRegionExperimental, 'k', FaceAlpha=0.2, ... - EdgeColor='none', HandleVisibility='off') - fillRegionModel = [(meanPassiveMomentsModel(:,i)+stdPassiveMomentsModel(:,i)); - flipud((meanPassiveMomentsModel(:,i)-stdPassiveMomentsModel(:,i)))]; - fill([t, fliplr(t)]', fillRegionModel, 'r', FaceAlpha=0.2, ... - EdgeColor='none', HandleVisibility='off') + plotMeanAndStd(meanMomentsExperimental(:,i), stdMomentsExperimental(:,i), ... + time, 'k-') + plotMeanAndStd(meanMomentsModel(:,i), stdMomentsModel(:,i), time, 'r-') hold off set(gca, fontsize=11) title(momentNames(i), FontSize=12) - axis([1 size(meanPassiveMomentsModel, 1) minMoment maxMoment]) + axis([1 size(meanMomentsModel, 1) minMoment maxMoment]) if i == 1 legend("Experimental", "Model") end diff --git a/src/MuscleTendonPersonalization/Analysis/printMtpJointMomentMatchingError.m b/src/MuscleTendonPersonalization/Analysis/printMtpJointMomentMatchingError.m index 0fdb53149..9c5e198ad 100644 --- a/src/MuscleTendonPersonalization/Analysis/printMtpJointMomentMatchingError.m +++ b/src/MuscleTendonPersonalization/Analysis/printMtpJointMomentMatchingError.m @@ -32,7 +32,7 @@ function printMtpJointMomentMatchingError(resultsDirectory) analysisDirectory = fullfile(resultsDirectory, "Analysis"); [jointMomentLabels, muscleJointMoments] = extractMtpDataFromSto( ... - fullfile(analysisDirectory, "modelJointMomentsSynx")); + fullfile(resultsDirectory, "modelMoments")); [~, inverseDynamicsMoments] = extractMtpDataFromSto( ... fullfile(analysisDirectory, "inverseDynamicsJointMoments")); jointMomentsRmse = zeros(size(jointMomentLabels)); diff --git a/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m b/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m index f0e29a760..9a72cfd22 100644 --- a/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m +++ b/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m @@ -46,12 +46,12 @@ function MuscleTendonPersonalizationTool(settingsFileName) if params.performMuscleTendonLengthInitialization [finalValues, resultsStruct, modeledValues] = ... getMtpResultsToSave(inputs, params, optimizedParams, precalInputs); - saveMtpMuscleTendonPersonalizationResults(inputs, finalValues, modeledValues, ... + saveMuscleTendonPersonalizationResults(inputs, finalValues, modeledValues, ... resultsStruct, resultsDirectory, precalInputs); else [finalValues, resultsStruct, modeledValues] = ... getMtpResultsToSave(inputs, params, optimizedParams); - saveMtpMuscleTendonPersonalizationResults(inputs, finalValues, modeledValues, ... + saveMuscleTendonPersonalizationResults(inputs, finalValues, modeledValues, ... resultsStruct, resultsDirectory); end printMtpJointMomentMatchingError(resultsDirectory); diff --git a/src/MuscleTendonPersonalization/Saving/saveMtpActivationAndExcitationData.m b/src/MuscleTendonPersonalization/Saving/saveMtpActivationAndExcitationData.m index 741a080c5..b0ca01239 100644 --- a/src/MuscleTendonPersonalization/Saving/saveMtpActivationAndExcitationData.m +++ b/src/MuscleTendonPersonalization/Saving/saveMtpActivationAndExcitationData.m @@ -32,18 +32,42 @@ function saveMtpActivationAndExcitationData(mtpInputs, resultsStruct, ... resultsDirectory) writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... - resultsStruct.results.muscleExcitations, strcat(resultsDirectory, ... - "\muscleExcitations"), "_muscleExcitations.sto") + resultsStruct.results.muscleExcitations, ... + fullfile(resultsDirectory, "muscleExcitations"), ... + "_muscleExcitations.sto") writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... - resultsStruct.results.muscleActivations, strcat(resultsDirectory, ... - "\muscleActivations"), "_muscleActivations.sto") + resultsStruct.results.muscleActivations, ... + fullfile(resultsDirectory, "muscleActivations"), ... + "_muscleActivations.sto") if isfield(mtpInputs, "synergyExtrapolation") writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... - resultsStruct.resultsSynx.muscleExcitations, strcat(resultsDirectory, ... - "\muscleExcitationsSynx"), "_muscleExcitationsSynx.sto") + resultsStruct.resultsSynx.muscleExcitations, ... + fullfile(resultsDirectory, "muscleExcitationsSynx"), ... + "_muscleExcitationsSynx.sto") writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... - resultsStruct.resultsSynx.muscleActivations, strcat(resultsDirectory, ... - "\muscleActivationsSynx"), "_muscleActivationsSynx.sto") + resultsStruct.resultsSynx.muscleActivations, ... + fullfile(resultsDirectory, "muscleActivationsSynx"), ... + "_muscleActivationsSynx.sto") + + writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... + resultsStruct.resultsSynxNoResiduals.muscleExcitations, ... + fullfile(resultsDirectory, "muscleExcitationsSynxNoResiduals"), ... + "_muscleExcitationsSynxNoResiduals.sto") + + writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... + resultsStruct.resultsSynxNoResiduals.muscleActivations, ... + fullfile(resultsDirectory, "muscleActivationsSynxNoResiduals"), ... + "_muscleActivationsSynxNoResiduals.sto") + + writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... + resultsStruct.resultsSynx.muscleActivations, ... + fullfile(resultsDirectory, "..", "muscleActivations"), ... + "_muscleActivations.sto") +else + writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... + resultsStruct.results.muscleActivations, ... + fullfile(resultsDirectory, "..", "muscleActivations"), ... + "_muscleActivations.sto") end end \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/Saving/saveMtpJointMomentData.m b/src/MuscleTendonPersonalization/Saving/saveMtpJointMomentData.m index 91a32dca6..e05ed2dfa 100644 --- a/src/MuscleTendonPersonalization/Saving/saveMtpJointMomentData.m +++ b/src/MuscleTendonPersonalization/Saving/saveMtpJointMomentData.m @@ -32,17 +32,34 @@ function saveMtpJointMomentData(mtpInputs, resultsStruct, resultsDirectory) writeMtpDataToSto(mtpInputs.coordinateNames, mtpInputs.prefixes, ... - resultsStruct.results.muscleJointMoments, strcat(resultsDirectory, ... - "\modelJointMoments"), "_modelJointMoments.sto") + resultsStruct.results.muscleJointMoments, ... + fullfile(resultsDirectory, "\modelJointMoments"), ... + "_modelJointMoments.sto") + +writeMtpDataToSto(mtpInputs.coordinateNames, mtpInputs.prefixes, ... + mtpInputs.inverseDynamicsMoments, ... + fullfile(resultsDirectory, "\inverseDynamicsJointMoments"), ... + "_inverseDynamicsJointMoments.sto") + if isfield(mtpInputs, "synergyExtrapolation") writeMtpDataToSto(mtpInputs.coordinateNames, mtpInputs.prefixes, ... - resultsStruct.resultsSynx.muscleJointMoments, strcat(resultsDirectory, ... - "\modelJointMomentsSynx"), "_modelJointMomentsSynx.sto") + resultsStruct.resultsSynx.muscleJointMoments, ... + fullfile(resultsDirectory, "\modelJointMomentsSynx"), ... + "_modelJointMomentsSynx.sto") + + writeMtpDataToSto(mtpInputs.coordinateNames, mtpInputs.prefixes, ... + resultsStruct.resultsSynxNoResiduals.muscleJointMoments, ... + fullfile(resultsDirectory, "\modelJointMomentsSynxNoResiduals"), ... + "_modelJointMomentsSynxNoResiduals.sto") + + writeMtpDataToSto(mtpInputs.coordinateNames, mtpInputs.prefixes, ... + resultsStruct.resultsSynx.muscleJointMoments, ... + fullfile(resultsDirectory, "..", "modelMoments"), ... + "_modelMoments.sto"); +else writeMtpDataToSto(mtpInputs.coordinateNames, mtpInputs.prefixes, ... - resultsStruct.resultsSynxNoResiduals.muscleJointMoments, strcat(resultsDirectory, ... - "\modelJointMomentsSynxNoResiduals"), "_modelJointMomentsSynxNoResiduals.sto") + resultsStruct.results.muscleJointMoments, ... + fullfile(resultsDirectory, "..", "modelMoments"), ... + "_modelMoments.sto"); end -writeMtpDataToSto(mtpInputs.coordinateNames, mtpInputs.prefixes, ... - mtpInputs.inverseDynamicsMoments, strcat(resultsDirectory, ... - "\inverseDynamicsJointMoments"), "_inverseDynamicsJointMoments.sto") end \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/Saving/saveMtpMuscleTendonPersonalizationResults.m b/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m similarity index 85% rename from src/MuscleTendonPersonalization/Saving/saveMtpMuscleTendonPersonalizationResults.m rename to src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m index 7f7ab6983..c04e712e1 100644 --- a/src/MuscleTendonPersonalization/Saving/saveMtpMuscleTendonPersonalizationResults.m +++ b/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m @@ -32,7 +32,7 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function saveMtpMuscleTendonPersonalizationResults(mtpInputs, finalValues, ... +function saveMuscleTendonPersonalizationResults(mtpInputs, finalValues, ... modeledValues, resultsStruct, resultsDirectory, precalInputs) analysisDirectory = fullfile(resultsDirectory, "Analysis"); if exist(analysisDirectory, "dir") @@ -46,13 +46,15 @@ function saveMtpMuscleTendonPersonalizationResults(mtpInputs, finalValues, ... saveMtpPassiveForceData(mtpInputs, modeledValues, resultsStruct, analysisDirectory); end saveMtpActivationAndExcitationData(mtpInputs, resultsStruct, analysisDirectory); -writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, resultsStruct.results.normalizedFiberLength, ... - fullfile(analysisDirectory, "normalizedFiberLengths"), "_normalizedFiberLengths.sto") +writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... + resultsStruct.results.normalizedFiberLength, ... + fullfile(analysisDirectory, "normalizedFiberLengths"), ... + "_normalizedFiberLengths.sto") saveMtpJointMomentData(mtpInputs, resultsStruct, analysisDirectory); -saveMtpMuscleModelParameters(mtpInputs, finalValues, fullfile(analysisDirectory, ... - "muscleModelParameters")); +saveMtpMuscleModelParameters(mtpInputs, finalValues, ... + fullfile(analysisDirectory, "muscleModelParameters")); model = Model(mtpInputs.model); muscleNames = getMusclesFromCoordinates(model, mtpInputs.coordinateNames); -writeMuscleTendonPersonalizationOsimxFile(mtpInputs.modelFileName, mtpInputs.osimxFileName, ... - finalValues, muscleNames, resultsDirectory); +writeMuscleTendonPersonalizationOsimxFile(mtpInputs.modelFileName, ... + mtpInputs.osimxFileName, finalValues, muscleNames, resultsDirectory); end \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/Saving/writeMtpDataToSto.m b/src/MuscleTendonPersonalization/Saving/writeMtpDataToSto.m index 08a843c59..5dde5c2fc 100644 --- a/src/MuscleTendonPersonalization/Saving/writeMtpDataToSto.m +++ b/src/MuscleTendonPersonalization/Saving/writeMtpDataToSto.m @@ -35,6 +35,7 @@ function writeMtpDataToSto(columnLabels, taskNames, data, directory, fileName) end for i = 1 : size(data,1) writeToSto(columnLabels, 1:1:length(data(i,:,:)), ... - permute(data(i,:,:), [3 2 1]), strcat(directory, "\", taskNames(i), fileName)) + permute(data(i,:,:), [3 2 1]), ... + strcat(directory, "\", taskNames(i), fileName)) end end \ No newline at end of file From 6196ae647b572d2b7a7ca77cb972e278a411623a Mon Sep 17 00:00:00 2001 From: RobSalati <142843461+RobSalati@users.noreply.github.com> Date: Tue, 19 Dec 2023 21:33:49 -0500 Subject: [PATCH 250/365] Add documentation --- .../Analysis/plotMeanAndStd.m | 29 +++++++++++++++++++ .../Saving/getMtpResultsToSave.m | 2 +- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/MuscleTendonPersonalization/Analysis/plotMeanAndStd.m b/src/MuscleTendonPersonalization/Analysis/plotMeanAndStd.m index f76f8a1b8..44e8ed751 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotMeanAndStd.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMeanAndStd.m @@ -1,3 +1,32 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function takes a mean and standard deviation and plots the mean with +% a solid line and shades +/- 1 standard deviation from the mean. +% +% (Array), (Array), (Array), (String) -> (None) +% Plot passive moment curves from file. + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Robert Salati % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % function plotMeanAndStd(mean, std, time, color) plot(time, mean, color, linewidth=2) FillRegion = [(mean+std); flipud(mean-std)]; diff --git a/src/MuscleTendonPersonalization/Saving/getMtpResultsToSave.m b/src/MuscleTendonPersonalization/Saving/getMtpResultsToSave.m index d6c6786b9..557c58685 100644 --- a/src/MuscleTendonPersonalization/Saving/getMtpResultsToSave.m +++ b/src/MuscleTendonPersonalization/Saving/getMtpResultsToSave.m @@ -15,7 +15,7 @@ % National Institutes of Health (R01 EB030520). % % % % Copyright (c) 2021 Rice University and the Authors % -% Author(s): Robert Salati % +% Author(s): Di Ao, Marleny Vega, Robert Salati % % % % Licensed under the Apache License, Version 2.0 (the "License"); % % you may not use this file except in compliance with the License. % From 40418dae9f30eb91b619ead136fa1d109e248bcb Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Mon, 1 Jan 2024 20:51:08 -0600 Subject: [PATCH 251/365] fix small bugs for TO --- .../makeExperimentalDataSplines.m | 4 ++-- .../prepareGroundContactSurfaces.m | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/TreatmentOptimization/makeExperimentalDataSplines.m b/src/TreatmentOptimization/makeExperimentalDataSplines.m index 784be860d..49aef1065 100644 --- a/src/TreatmentOptimization/makeExperimentalDataSplines.m +++ b/src/TreatmentOptimization/makeExperimentalDataSplines.m @@ -41,9 +41,9 @@ for i = 1:length(inputs.contactSurfaces) inputs.splineExperimentalGroundReactionForces{i} = ... makeGcvSplineSet(inputs.experimentalTime, ... - inputs.contactSurfaces{i}.experimentalGroundReactionForces, 0:2); + inputs.contactSurfaces{i}.experimentalGroundReactionForces, string(0:2)); inputs.splineExperimentalGroundReactionMoments{i} = ... makeGcvSplineSet(inputs.experimentalTime, ... - inputs.contactSurfaces{i}.experimentalGroundReactionMoments, 0:2); + inputs.contactSurfaces{i}.experimentalGroundReactionMoments, string(0:2)); end end diff --git a/src/TreatmentOptimization/prepareGroundContactSurfaces.m b/src/TreatmentOptimization/prepareGroundContactSurfaces.m index eeb169c58..5f318e7e5 100644 --- a/src/TreatmentOptimization/prepareGroundContactSurfaces.m +++ b/src/TreatmentOptimization/prepareGroundContactSurfaces.m @@ -50,6 +50,16 @@ contactSurface.parentSpringConstants = []; contactSurface.childSpringPointsOnBody = []; contactSurface.childSpringConstants = []; +joints = getBodyJointNames(osimModel, contactSurface.hindfootBodyName); +assert(length(joints) == 2, ... + "Treatment Optimization supports two segment foot models only"); +for i = 1 : length(joints) + [parent, ~] = getJointBodyNames(osimModel, joints(i)); + if strcmp(parent, contactSurface.hindfootBodyName) + contactSurface.toesJointName = joints(i); + break + end +end [contactSurface.parentBodyName, contactSurface.childBodyName] = ... getJointBodyNames(osimModel, contactSurface.toesJointName); for j = 1:length(contactSurface.springs) From 6b24ff27098206e18ea5a7900a16f9380a733eff Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Mon, 1 Jan 2024 23:22:44 -0600 Subject: [PATCH 252/365] initial pre-splining Co-Authored-By: Spencer Williams <54556034+SpencerTWilliams@users.noreply.github.com> --- .../calcTrackingControllerIntegrand.m | 8 +- .../calcTrackingCoordinateIntegrand.m | 3 +- .../calcTrackingExternalForcesIntegrand.m | 11 +- .../calcTrackingExternalMomentsIntegrand.m | 11 +- ...calcTrackingInverseDynamicLoadsIntegrand.m | 3 +- .../calcTrackingMarkerPosition.m | 4 +- .../calcTrackingMuscleActivationIntegrand.m | 3 +- .../calcTrackingSpeedIntegrand.m | 3 +- .../gpops/makeGpopsValuesAsStruct.m | 9 +- .../gpops/preSplineGpopsInputs.m | 139 ++++++++++++++++++ .../makeStateDerivatives.m | 8 +- .../makeTreatmentOptimizationInputs.m | 2 +- .../solveOptimalControlProblem.m | 1 + 13 files changed, 163 insertions(+), 42 deletions(-) create mode 100644 src/TreatmentOptimization/gpops/preSplineGpopsInputs.m diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m index b966a3bf0..f6b6cb1f6 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m @@ -38,9 +38,7 @@ indx = find(strcmp(convertCharsToStrings( ... inputs.synergyLabels), controllerName)); if ~isempty(indx) - synergyActivations = ... - evaluateGcvSplines(inputs.splineSynergyActivations, ... - inputs.synergyLabels, time); + synergyActivations = inputs.splinedSynergyActivations; cost = calcTrackingCostArrayTerm(synergyActivations, ... values.controlSynergyActivations, indx); return @@ -51,9 +49,7 @@ indx2 = find(strcmp(convertCharsToStrings( ... strcat(inputs.torqueControllerCoordinateNames, '_moment')), ... controllerName)); -experimentalJointMoments = ... - evaluateGcvSplines(inputs.splineTorqueControls, ... - inputs.torqueLabels, time); +experimentalJointMoments = inputs.splinedTorqueControls; cost = experimentalJointMoments(:, indx1) - ... values.torqueControls(:, indx2); if normalizeByFinalTime diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m index 440e305c4..37bb5792f 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m @@ -39,8 +39,7 @@ strcat("Coordinate ", coordinateName, " is not in the ", ... ""))) end -experimentalJointAngles = evaluateGcvSplines( ... - inputs.splineJointAngles, inputs.coordinateNames, time); +experimentalJointAngles = inputs.splinedJointAngles; cost = calcTrackingCostArrayTerm(experimentalJointAngles, ... statePositions, indx); diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m index 171edf400..e2c2c8285 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m @@ -28,18 +28,15 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcTrackingExternalForcesIntegrand(costTerm, params, ... +function cost = calcTrackingExternalForcesIntegrand(costTerm, inputs, ... groundReactionsForces, time, forceName) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); -for i = 1:length(params.contactSurfaces) - indx = find(strcmp(convertCharsToStrings(params.contactSurfaces{i}. ... +for i = 1:length(inputs.contactSurfaces) + indx = find(strcmp(convertCharsToStrings(inputs.contactSurfaces{i}. ... forceColumns), forceName)); if ~isempty(indx) - experimentalGroundReactions = ... - evaluateGcvSplines( ... - params.splineExperimentalGroundReactionForces{i}, 0:2, ... - time); + experimentalGroundReactions = inputs.splinedGroundReactionForces{i}; cost = calcTrackingCostArrayTerm(... experimentalGroundReactions, groundReactionsForces{i}, ... indx); diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m index f84979545..f76f9b6aa 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m @@ -28,18 +28,15 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcTrackingExternalMomentsIntegrand(costTerm, params, ... +function cost = calcTrackingExternalMomentsIntegrand(costTerm, inputs, ... groundReactionMoments, time, loadName) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); -for i = 1:length(params.contactSurfaces) - indx = find(strcmp(convertCharsToStrings(params.contactSurfaces{i}. ... +for i = 1:length(inputs.contactSurfaces) + indx = find(strcmp(convertCharsToStrings(inputs.contactSurfaces{i}. ... momentColumns), loadName)); if ~isempty(indx) - experimentalGroundReactions = ... - evaluateGcvSplines( ... - params.splineExperimentalGroundReactionMoments{i}, 0:2, ... - time); + experimentalGroundReactions = inputs.splinedGroundReactionMoments{i}; cost = calcTrackingCostArrayTerm(... experimentalGroundReactions, groundReactionMoments{i}, ... indx); diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m index 5a2c9d5a2..fcf760e57 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m @@ -36,8 +36,7 @@ loadName = erase(loadName, '_force'); indx = find(strcmp(convertCharsToStrings(inputs.coordinateNames), ... loadName)); -experimentalJointMoments = evaluateGcvSplines( ... - inputs.splineJointMoments, inputs.inverseDynamicsMomentLabels, time); +experimentalJointMoments = inputs.splinedJointMoments; momentLabelsNoSuffix = erase(inputs.inverseDynamicsMomentLabels, '_moment'); momentLabelsNoSuffix = erase(momentLabelsNoSuffix, '_force'); includedJointMomentCols = ismember(momentLabelsNoSuffix, convertCharsToStrings(inputs.coordinateNames)); diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m index ec36e0228..d0020462c 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m @@ -38,9 +38,7 @@ strcat("Marker ", costTerm.marker, " is not in the ", ... "list of tracked markers"))) end -experimentalMarkerPositions = ... - evaluateGcvSplines(inputs.splineMarkerPositions{indx}, ... - 0:2, time); +experimentalMarkerPositions = inputs.splinedMarkerPositions{indx}; cost = calcTrackingCostArrayTerm(experimentalMarkerPositions, ... markerPositions, indx); cost = cost + calcTrackingCostArrayTerm(experimentalMarkerPositions, ... diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m index d469726ef..3199f7144 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m @@ -34,8 +34,7 @@ "normalize_by_final_time", true); indx = find(strcmp(convertCharsToStrings(inputs.muscleNames), ... muscleName)); -experimentalMuscleActivations = evaluateGcvSplines( ... - inputs.splineMuscleActivations, inputs.muscleNames, time); +experimentalMuscleActivations = inputs.splinedMuscleActivations; cost = calcTrackingCostArrayTerm(experimentalMuscleActivations, ... muscleActivations, indx); if normalizeByFinalTime diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m index b1f1a9474..f3ba4b210 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m @@ -39,8 +39,7 @@ strcat("Coordinate ", coordinateName, " is not in the ", ... ""))) end -experimentalJointVelocities = evaluateGcvSplines( ... - inputs.splineJointAngles, inputs.coordinateNames, time, 1); +experimentalJointVelocities = inputs.splinedJointSpeeds; cost = calcTrackingCostArrayTerm(experimentalJointVelocities, ... stateVelocities, indx); diff --git a/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m b/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m index 2150b9ac7..2f174f742 100644 --- a/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m +++ b/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m @@ -113,12 +113,9 @@ function [positions, velocities, accelerations] = recombineFullState( ... values, inputs) -positions = evaluateGcvSplines(inputs.splineJointAngles, ... - inputs.coordinateNames, values.time); -velocities = evaluateGcvSplines(inputs.splineJointAngles, ... - inputs.coordinateNames, values.time, 1); -accelerations = evaluateGcvSplines(inputs.splineJointAngles, ... - inputs.coordinateNames, values.time, 2); +positions = inputs.splinedJointAngles; +velocities = inputs.splinedJointSpeeds; +accelerations = inputs.splinedJointAccelerations; for i = 1:length(inputs.coordinateNames) index = find(ismember( ... inputs.statesCoordinateNames, inputs.coordinateNames{i})); diff --git a/src/TreatmentOptimization/gpops/preSplineGpopsInputs.m b/src/TreatmentOptimization/gpops/preSplineGpopsInputs.m new file mode 100644 index 000000000..3d57b1d16 --- /dev/null +++ b/src/TreatmentOptimization/gpops/preSplineGpopsInputs.m @@ -0,0 +1,139 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Claire V. Hammond % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function setup = preSplineGpopsInputs(setup) +tempSetup = makeMinimalSetup(setup); +collocationPointTimes = findCollocationPointsForSetup(tempSetup); +setup = applyPreSpline(setup, collocationPointTimes); +end + +function collocationPointTimes = findCollocationPointsForSetup(setup) +output = gpops2(tempSetup); +collocationPointTimes = output.result.solution.phase.timeRadau; +end + +function setup = applyPreSpline(setup, collocationPointTimes) +splineJointMoments = makeGcvSplineSet(setup.auxdata.experimentalTime, ... + setup.auxdata.experimentalJointMoments, ... + setup.auxdata.inverseDynamicsMomentLabels); +if strcmp(setup.auxdata.controllerType, 'synergy') + splineMuscleActivations = makeGcvSplineSet( ... + setup.auxdata.experimentalTime, ... + setup.auxdata.experimentalMuscleActivations, ... + setup.auxdata.muscleLabels); +end +for i = 1:length(setup.auxdata.contactSurfaces) + splineExperimentalGroundReactionForces{i} = ... + makeGcvSplineSet(setup.auxdata.experimentalTime, ... + setup.auxdata.contactSurfaces{i}.experimentalGroundReactionForces, string(0:2)); + splineExperimentalGroundReactionMoments{i} = ... + makeGcvSplineSet(setup.auxdata.experimentalTime, ... + setup.auxdata.contactSurfaces{i}.experimentalGroundReactionMoments, string(0:2)); +end +splineJointAngles = makeGcvSplineSet(setup.auxdata.experimentalTime, ... + setup.auxdata.experimentalJointAngles', setup.auxdata.coordinateNames); +time = scaleToOriginal(collocationPointTimes, setup.auxdata.maxTime, ... + setup.auxdata.minTime); + +setup.auxdata.splinedJointAngles = evaluateGcvSplines(splineJointAngles, ... + setup.auxdata.coordinateNames, time, 0); +setup.auxdata.splinedJointSpeeds = evaluateGcvSplines(splineJointAngles, ... + setup.auxdata.coordinateNames, time, 1); +setup.auxdata.splinedJointAccelerations = evaluateGcvSplines(splineJointAngles, ... + setup.auxdata.coordinateNames, time, 2); +setup.auxdata.splinedJointMoments = evaluateGcvSplines(splineJointMoments, ... + setup.auxdata.inverseDynamicsMomentLabels, time, 0); +if strcmp(setup.auxdata.controllerType, 'synergy') + setup.auxdata.splinedSynergyActivations = evaluateGcvSplines( ... + setup.auxdata.splineSynergyActivations, ... + setup.auxdata.synergyLabels, time, 0); + setup.auxdata = rmfield(setup.auxdata, 'splineSynergyActivations'); + setup.auxdata.splinedMuscleActivations = evaluateGcvSplines( ... + splineMuscleActivations, setup.auxdata.muscleLabels, time, 0); +end +if isfield(inputs, 'torqueControllerCoordinateNames') && ... + ~isempty(inputs.torqueControllerCoordinateNames) + setup.auxdata.splinedTorqueControls = evaluateGcvSplines( ... + setup.auxdata.splineTorqueControls, setup.auxdata.torqueLabels, time, 0); + setup.auxdata = rmfield(setup.auxdata, 'splineTorqueControls'); +end +if isfield(setup.auxdata, "splineMarkerPositions") + for i = 1:length(setup.auxdata.splineMarkerPositions) + setup.auxdata.splinedMarkerPositions{i} = evaluateGcvSplines( ... + inputs.splineMarkerPositions{i}, 0:2, time, 0); + end + setup.auxdata = rmfield(setup.auxdata, 'splineMarkerPositions'); +end +for i = 1:length(setup.auxdata.contactSurfaces) + setup.auxdata.splinedGroundReactionForces{i} = evaluateGcvSplines( ... + splineExperimentalGroundReactionForces{i}, 0:2, time, 0); + setup.auxdata.splinedGroundReactionMoments{i} = evaluateGcvSplines( ... + splineExperimentalGroundReactionMoments{i}, 0:2, time, 0); +end +end + +function tempSetup = makeMinimalSetup(setup) +tempSetup.name = 'Test'; +tempSetup.functions.continuous = @continuous; +tempSetup.functions.endpoint = @endpoint; + +tempSetup.guess.phase.time = setup.guess.phase.time; + +tempSetup.guess.phase.state = zeros(size(tempSetup.guess.phase.time)); +tempSetup.guess.phase.control = zeros(size(tempSetup.guess.phase.time)); +tempSetup.guess.phase.integral = 0; + +tempSetup.bounds.phase.initialtime.lower = tempSetup.guess.phase.time(1); +tempSetup.bounds.phase.initialtime.upper = tempSetup.guess.phase.time(1); +tempSetup.bounds.phase.finaltime.lower = tempSetup.guess.phase.time(end); +tempSetup.bounds.phase.finaltime.upper = tempSetup.guess.phase.time(end); +tempSetup.bounds.phase.initialstate.lower = -1; +tempSetup.bounds.phase.initialstate.upper = 1; +tempSetup.bounds.phase.state.lower = -1; +tempSetup.bounds.phase.state.upper = 1; +tempSetup.bounds.phase.finalstate.lower = -1; +tempSetup.bounds.phase.finalstate.upper = 1; +tempSetup.bounds.phase.control.lower = -1; +tempSetup.bounds.phase.control.upper = 1; +tempSetup.bounds.phase.integral.lower = -1; +tempSetup.bounds.phase.integral.upper = 1; +tempSetup.derivatives.derivativelevel= 'first'; +tempSetup.nlp.ipoptoptions.maxiterations = 1; +tempSetup.scales.method = 'none'; + +tempSetup.mesh.phase.colpoints = setup.mesh.phase.colpoints; +tempSetup.mesh.phase.fraction = setup.mesh.phase.fraction; +tempSetup.mesh.maxiterations = 0; +end + +function output = continuous(input) +output.dynamics = zeros(size(input.phase.time)); +output.integrand = zeros(size(input.phase.time)); +end + +function output = endpoint(input) +output.objective = input.phase.integral; +end \ No newline at end of file diff --git a/src/TreatmentOptimization/makeStateDerivatives.m b/src/TreatmentOptimization/makeStateDerivatives.m index 03f872989..da00d071e 100644 --- a/src/TreatmentOptimization/makeStateDerivatives.m +++ b/src/TreatmentOptimization/makeStateDerivatives.m @@ -29,15 +29,15 @@ % ----------------------------------------------------------------------- % function inputs = makeStateDerivatives(inputs, params) -inputs.splineJointAngles = makeGcvSplineSet(inputs.experimentalTime, ... +splineJointAngles = makeGcvSplineSet(inputs.experimentalTime, ... inputs.experimentalJointAngles', inputs.coordinateNames); inputs.experimentalJointVelocities = evaluateGcvSplines( ... - inputs.splineJointAngles, inputs.coordinateNames, ... + splineJointAngles, inputs.coordinateNames, ... inputs.experimentalTime, 1); inputs.experimentalJointAccelerations = evaluateGcvSplines( ... - inputs.splineJointAngles, inputs.coordinateNames, ... + splineJointAngles, inputs.coordinateNames, ... inputs.experimentalTime, 2); inputs.experimentalJointJerks = evaluateGcvSplines( ... - inputs.splineJointAngles, inputs.coordinateNames, ... + splineJointAngles, inputs.coordinateNames, ... inputs.experimentalTime, 3); end diff --git a/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m b/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m index fa14c5bb8..c46c0db5d 100644 --- a/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m +++ b/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m @@ -34,7 +34,7 @@ inputs = modifyModelForces(inputs); initializeMexOrMatlabParallelFunctions(inputs.mexModel); inputs = setupGroundContact(inputs); -inputs = makeExperimentalDataSplines(inputs); +% inputs = makeExperimentalDataSplines(inputs); inputs = makeSurrogateModel(inputs); [inputs.continuousMaxAllowableError, inputs.discreteMaxAllowableError] ... = makeMaxAllowableError(inputs.toolName, inputs.costTerms); diff --git a/src/TreatmentOptimization/solveOptimalControlProblem.m b/src/TreatmentOptimization/solveOptimalControlProblem.m index 0f1ddf9aa..6a2655e1c 100644 --- a/src/TreatmentOptimization/solveOptimalControlProblem.m +++ b/src/TreatmentOptimization/solveOptimalControlProblem.m @@ -26,6 +26,7 @@ function output = solveOptimalControlProblem(inputs, params) setup = convertToGpopsInputs(inputs, params); +setup = preSplineGpopsInputs(setup); solution = gpops2(setup); output = convertFromGpopsOutputs(solution, ... inputs, params); From 7a339326af12050a03ac6912aaf8d0fe0803dc5a Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Tue, 2 Jan 2024 02:00:53 -0600 Subject: [PATCH 253/365] fix bugs in pre-spline --- ...calcMaximizingTrailingLimbAngleIntegrand.m | 2 +- .../calcMinimizingMetabolicCost.m | 2 +- .../calcTrackingControllerIntegrand.m | 16 +++++- .../calcTrackingCoordinateIntegrand.m | 11 +++- .../calcTrackingExternalForcesIntegrand.m | 13 ++++- .../calcTrackingExternalMomentsIntegrand.m | 9 ++- ...calcTrackingInverseDynamicLoadsIntegrand.m | 7 ++- .../calcTrackingMarkerPosition.m | 8 ++- .../calcTrackingMuscleActivationIntegrand.m | 11 +++- .../calcTrackingSpeedIntegrand.m | 7 ++- .../TrackingOptimizationTool.m | 2 +- .../gpops/convertToGpopsInputs.m | 3 +- .../gpops/makeGpopsValuesAsStruct.m | 15 ++++- .../gpops/preSplineGpopsInputs.m | 56 +++++++------------ .../makeStateDerivatives.m | 8 +-- .../makeTreatmentOptimizationInputs.m | 2 +- .../solveOptimalControlProblem.m | 4 +- 17 files changed, 111 insertions(+), 65 deletions(-) diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMaximizingTrailingLimbAngleIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMaximizingTrailingLimbAngleIntegrand.m index 3e2a4308b..756e5aa08 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMaximizingTrailingLimbAngleIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMaximizingTrailingLimbAngleIntegrand.m @@ -40,7 +40,7 @@ trailingLimbAngle = calcTrailingLimb(costTerm, values, ... normalForce, params); cost = calcMaximizingCostArrayTerm(trailingLimbAngle * ... - ones(length(values.time), 1)); + ones(length(time), 1)); if normalizeByFinalTime cost = cost / time(end); end diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMetabolicCost.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMetabolicCost.m index 5dcf371a5..0687e9fcf 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMetabolicCost.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMetabolicCost.m @@ -30,7 +30,7 @@ modeledValues, params) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", false); -metabolicCost = calcMetabolicCost(values.time, ... +metabolicCost = calcMetabolicCost(time, ... values.statePositions, modeledValues.muscleActivations, params); cost = calcMinimizingCostArrayTerm(metabolicCost); if normalizeByFinalTime diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m index f6b6cb1f6..13ac8e3cb 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m @@ -38,7 +38,13 @@ indx = find(strcmp(convertCharsToStrings( ... inputs.synergyLabels), controllerName)); if ~isempty(indx) - synergyActivations = inputs.splinedSynergyActivations; + if size(time) == size(inputs.collocationTimeOriginal) + synergyActivations = inputs.splinedSynergyActivations; + else + synergyActivations = ... + evaluateGcvSplines(inputs.splineSynergyActivations, ... + inputs.synergyLabels, time); + end cost = calcTrackingCostArrayTerm(synergyActivations, ... values.controlSynergyActivations, indx); return @@ -49,7 +55,13 @@ indx2 = find(strcmp(convertCharsToStrings( ... strcat(inputs.torqueControllerCoordinateNames, '_moment')), ... controllerName)); -experimentalJointMoments = inputs.splinedTorqueControls; +if size(time) == size(inputs.collocationTimeOriginal) + experimentalJointMoments = inputs.splinedTorqueControls; +else + experimentalJointMoments = ... + evaluateGcvSplines(inputs.splineTorqueControls, ... + inputs.torqueLabels, time); +end cost = experimentalJointMoments(:, indx1) - ... values.torqueControls(:, indx2); if normalizeByFinalTime diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m index 37bb5792f..41787b816 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m @@ -1,10 +1,10 @@ % This function is part of the NMSM Pipeline, see file for full license. % % This function calculates the difference between the experimental and -% predicted joint angles for the specified coordinate. +% predicted joint angles for the specified coordinate. % % (struct, Array of number, 2D matrix, Array of string) -> (Array of number) -% +% % ----------------------------------------------------------------------- % % The NMSM Pipeline is a toolkit for model personalization and treatment % @@ -39,7 +39,12 @@ strcat("Coordinate ", coordinateName, " is not in the ", ... ""))) end -experimentalJointAngles = inputs.splinedJointAngles; +if size(time) == size(inputs.collocationTimeOriginal) + experimentalJointAngles = inputs.splinedJointAngles; +else + experimentalJointAngles = evaluateGcvSplines( ... + inputs.splineJointAngles, inputs.coordinateNames, time); +end cost = calcTrackingCostArrayTerm(experimentalJointAngles, ... statePositions, indx); diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m index e2c2c8285..4983e0f77 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m @@ -1,10 +1,10 @@ % This function is part of the NMSM Pipeline, see file for full license. % % This function calculates the difference between the experimental and -% predicted ground reaction forces for the specified force. +% predicted ground reaction forces for the specified force. % % (struct, 2D matrix, Array of number, Array of string) -> (Array of number) -% +% % ----------------------------------------------------------------------- % % The NMSM Pipeline is a toolkit for model personalization and treatment % @@ -36,7 +36,14 @@ indx = find(strcmp(convertCharsToStrings(inputs.contactSurfaces{i}. ... forceColumns), forceName)); if ~isempty(indx) - experimentalGroundReactions = inputs.splinedGroundReactionForces{i}; + if size(time) == size(inputs.collocationTimeOriginal) + experimentalGroundReactions = inputs.splinedGroundReactionForces{i}; + else + experimentalGroundReactions = ... + evaluateGcvSplines( ... + params.splineExperimentalGroundReactionForces{i}, 0:2, ... + time); + end cost = calcTrackingCostArrayTerm(... experimentalGroundReactions, groundReactionsForces{i}, ... indx); diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m index f76f9b6aa..f63e4d764 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m @@ -36,7 +36,14 @@ indx = find(strcmp(convertCharsToStrings(inputs.contactSurfaces{i}. ... momentColumns), loadName)); if ~isempty(indx) - experimentalGroundReactions = inputs.splinedGroundReactionMoments{i}; + if size(time) == size(inputs.collocationTimeOriginal) + experimentalGroundReactions = inputs.splinedGroundReactionMoments{i}; + else + experimentalGroundReactions = ... + evaluateGcvSplines( ... + params.splineExperimentalGroundReactionMoments{i}, 0:2, ... + time); + end cost = calcTrackingCostArrayTerm(... experimentalGroundReactions, groundReactionMoments{i}, ... indx); diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m index fcf760e57..5e12ca2e4 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m @@ -36,7 +36,12 @@ loadName = erase(loadName, '_force'); indx = find(strcmp(convertCharsToStrings(inputs.coordinateNames), ... loadName)); -experimentalJointMoments = inputs.splinedJointMoments; +if size(time) == size(inputs.collocationTimeOriginal) + experimentalJointMoments = inputs.splinedJointMoments; +else + experimentalJointMoments = evaluateGcvSplines( ... + inputs.splineJointMoments, inputs.inverseDynamicsMomentLabels, time); +end momentLabelsNoSuffix = erase(inputs.inverseDynamicsMomentLabels, '_moment'); momentLabelsNoSuffix = erase(momentLabelsNoSuffix, '_force'); includedJointMomentCols = ismember(momentLabelsNoSuffix, convertCharsToStrings(inputs.coordinateNames)); diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m index d0020462c..ab3fd9ea2 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m @@ -38,7 +38,13 @@ strcat("Marker ", costTerm.marker, " is not in the ", ... "list of tracked markers"))) end -experimentalMarkerPositions = inputs.splinedMarkerPositions{indx}; +if size(time) == size(inputs.collocationTimeOriginal) + experimentalMarkerPositions = inputs.splinedMarkerPositions{indx}; +else + experimentalMarkerPositions = ... + evaluateGcvSplines(inputs.splineMarkerPositions{indx}, ... + 0:2, time); +end cost = calcTrackingCostArrayTerm(experimentalMarkerPositions, ... markerPositions, indx); cost = cost + calcTrackingCostArrayTerm(experimentalMarkerPositions, ... diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m index 3199f7144..634ca29a7 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m @@ -1,10 +1,10 @@ % This function is part of the NMSM Pipeline, see file for full license. % % This function calculates the difference between the experimental and -% predicted muscle activations for the specified muscle. +% predicted muscle activations for the specified muscle. % % (2D matrix, Array of number, struct, Array of string) -> (Array of number) -% +% % ----------------------------------------------------------------------- % % The NMSM Pipeline is a toolkit for model personalization and treatment % @@ -34,7 +34,12 @@ "normalize_by_final_time", true); indx = find(strcmp(convertCharsToStrings(inputs.muscleNames), ... muscleName)); -experimentalMuscleActivations = inputs.splinedMuscleActivations; +if size(time) == size(inputs.collocationTimeOriginal) + experimentalMuscleActivations = inputs.splinedMuscleActivations; +else + experimentalMuscleActivations = evaluateGcvSplines( ... + inputs.splineMuscleActivations, inputs.muscleNames, time); +end cost = calcTrackingCostArrayTerm(experimentalMuscleActivations, ... muscleActivations, indx); if normalizeByFinalTime diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m index f3ba4b210..c2bad0720 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m @@ -39,7 +39,12 @@ strcat("Coordinate ", coordinateName, " is not in the ", ... ""))) end -experimentalJointVelocities = inputs.splinedJointSpeeds; +if size(time) == size(inputs.collocationTimeOriginal) + experimentalJointVelocities = inputs.splinedJointSpeeds; +else + experimentalJointVelocities = evaluateGcvSplines( ... + inputs.splineJointAngles, inputs.coordinateNames, time, 1); +end cost = calcTrackingCostArrayTerm(experimentalJointVelocities, ... stateVelocities, indx); diff --git a/src/TreatmentOptimization/TrackingOptimization/TrackingOptimizationTool.m b/src/TreatmentOptimization/TrackingOptimization/TrackingOptimizationTool.m index b0023be31..1d1dc237c 100644 --- a/src/TreatmentOptimization/TrackingOptimization/TrackingOptimizationTool.m +++ b/src/TreatmentOptimization/TrackingOptimization/TrackingOptimizationTool.m @@ -35,7 +35,7 @@ function TrackingOptimizationTool(settingsFileName) [inputs, params] = parseTrackingOptimizationSettingsTree(settingsTree); inputs = normalizeSynergyData(inputs); inputs = makeTreatmentOptimizationInputs(inputs, params); -outputs = solveOptimalControlProblem(inputs, params); +[inputs, outputs] = solveOptimalControlProblem(inputs, params); reportTreatmentOptimizationResults(outputs, inputs); saveTrackingOptimizationResults(outputs, inputs); end diff --git a/src/TreatmentOptimization/gpops/convertToGpopsInputs.m b/src/TreatmentOptimization/gpops/convertToGpopsInputs.m index 5d5e86058..d552281f6 100644 --- a/src/TreatmentOptimization/gpops/convertToGpopsInputs.m +++ b/src/TreatmentOptimization/gpops/convertToGpopsInputs.m @@ -31,6 +31,7 @@ bounds, guess, params, ... @computeGpopsContinuousFunction, ... @computeGpopsEndpointFunction); -checkInitialGuess(guess, inputs, ... +setup = preSplineGpopsInputs(setup); +checkInitialGuess(guess, setup.auxdata, ... @computeGpopsContinuousFunction); end diff --git a/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m b/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m index 2f174f742..8a0864cb5 100644 --- a/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m +++ b/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m @@ -113,9 +113,18 @@ function [positions, velocities, accelerations] = recombineFullState( ... values, inputs) -positions = inputs.splinedJointAngles; -velocities = inputs.splinedJointSpeeds; -accelerations = inputs.splinedJointAccelerations; +if size(values.time) == size(inputs.collocationTimeOriginal) + positions = inputs.splinedJointAngles; + velocities = inputs.splinedJointSpeeds; + accelerations = inputs.splinedJointAccelerations; +else + positions = evaluateGcvSplines(inputs.splineJointAngles, ... + inputs.coordinateNames, values.time); + velocities = evaluateGcvSplines(inputs.splineJointAngles, ... + inputs.coordinateNames, values.time, 1); + accelerations = evaluateGcvSplines(inputs.splineJointAngles, ... + inputs.coordinateNames, values.time, 2); +end for i = 1:length(inputs.coordinateNames) index = find(ismember( ... inputs.statesCoordinateNames, inputs.coordinateNames{i})); diff --git a/src/TreatmentOptimization/gpops/preSplineGpopsInputs.m b/src/TreatmentOptimization/gpops/preSplineGpopsInputs.m index 3d57b1d16..d0e9d0e36 100644 --- a/src/TreatmentOptimization/gpops/preSplineGpopsInputs.m +++ b/src/TreatmentOptimization/gpops/preSplineGpopsInputs.m @@ -24,74 +24,58 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function setup = preSplineGpopsInputs(setup) +function splinedSetup = preSplineGpopsInputs(setup) tempSetup = makeMinimalSetup(setup); collocationPointTimes = findCollocationPointsForSetup(tempSetup); -setup = applyPreSpline(setup, collocationPointTimes); +splinedSetup = applyPreSpline(setup, collocationPointTimes); end -function collocationPointTimes = findCollocationPointsForSetup(setup) +function collocationPointTimes = findCollocationPointsForSetup(tempSetup) output = gpops2(tempSetup); collocationPointTimes = output.result.solution.phase.timeRadau; end function setup = applyPreSpline(setup, collocationPointTimes) -splineJointMoments = makeGcvSplineSet(setup.auxdata.experimentalTime, ... - setup.auxdata.experimentalJointMoments, ... - setup.auxdata.inverseDynamicsMomentLabels); -if strcmp(setup.auxdata.controllerType, 'synergy') - splineMuscleActivations = makeGcvSplineSet( ... - setup.auxdata.experimentalTime, ... - setup.auxdata.experimentalMuscleActivations, ... - setup.auxdata.muscleLabels); -end -for i = 1:length(setup.auxdata.contactSurfaces) - splineExperimentalGroundReactionForces{i} = ... - makeGcvSplineSet(setup.auxdata.experimentalTime, ... - setup.auxdata.contactSurfaces{i}.experimentalGroundReactionForces, string(0:2)); - splineExperimentalGroundReactionMoments{i} = ... - makeGcvSplineSet(setup.auxdata.experimentalTime, ... - setup.auxdata.contactSurfaces{i}.experimentalGroundReactionMoments, string(0:2)); -end -splineJointAngles = makeGcvSplineSet(setup.auxdata.experimentalTime, ... - setup.auxdata.experimentalJointAngles', setup.auxdata.coordinateNames); +setup.auxdata.collocationTimeBound = collocationPointTimes; time = scaleToOriginal(collocationPointTimes, setup.auxdata.maxTime, ... setup.auxdata.minTime); +setup.auxdata.collocationTimeOriginal = time; -setup.auxdata.splinedJointAngles = evaluateGcvSplines(splineJointAngles, ... +setup.auxdata.splinedJointAngles = evaluateGcvSplines(setup.auxdata.splineJointAngles, ... setup.auxdata.coordinateNames, time, 0); -setup.auxdata.splinedJointSpeeds = evaluateGcvSplines(splineJointAngles, ... +setup.auxdata.splinedJointSpeeds = evaluateGcvSplines(setup.auxdata.splineJointAngles, ... setup.auxdata.coordinateNames, time, 1); -setup.auxdata.splinedJointAccelerations = evaluateGcvSplines(splineJointAngles, ... +setup.auxdata.splinedJointAccelerations = evaluateGcvSplines(setup.auxdata.splineJointAngles, ... setup.auxdata.coordinateNames, time, 2); -setup.auxdata.splinedJointMoments = evaluateGcvSplines(splineJointMoments, ... +setup.auxdata.splinedJointMoments = evaluateGcvSplines(setup.auxdata.splineJointMoments, ... setup.auxdata.inverseDynamicsMomentLabels, time, 0); -if strcmp(setup.auxdata.controllerType, 'synergy') +if strcmp(setup.auxdata.controllerType, 'synergy') && ... + isfield(setup.auxdata, 'splineSynergyActivations') setup.auxdata.splinedSynergyActivations = evaluateGcvSplines( ... setup.auxdata.splineSynergyActivations, ... setup.auxdata.synergyLabels, time, 0); - setup.auxdata = rmfield(setup.auxdata, 'splineSynergyActivations'); +end +if strcmp(setup.auxdata.controllerType, 'synergy') && ... + isfield(setup.auxdata, 'splineMuscleActivations') setup.auxdata.splinedMuscleActivations = evaluateGcvSplines( ... - splineMuscleActivations, setup.auxdata.muscleLabels, time, 0); + setup.auxdata.splineMuscleActivations, setup.auxdata.muscleLabels, time, 0); end -if isfield(inputs, 'torqueControllerCoordinateNames') && ... - ~isempty(inputs.torqueControllerCoordinateNames) +if isfield(setup.auxdata, 'torqueControllerCoordinateNames') && ... + ~isempty(setup.auxdata.torqueControllerCoordinateNames) setup.auxdata.splinedTorqueControls = evaluateGcvSplines( ... setup.auxdata.splineTorqueControls, setup.auxdata.torqueLabels, time, 0); - setup.auxdata = rmfield(setup.auxdata, 'splineTorqueControls'); end if isfield(setup.auxdata, "splineMarkerPositions") for i = 1:length(setup.auxdata.splineMarkerPositions) setup.auxdata.splinedMarkerPositions{i} = evaluateGcvSplines( ... - inputs.splineMarkerPositions{i}, 0:2, time, 0); + setup.auxdata.splineMarkerPositions{i}, 0:2, time, 0); end - setup.auxdata = rmfield(setup.auxdata, 'splineMarkerPositions'); end for i = 1:length(setup.auxdata.contactSurfaces) setup.auxdata.splinedGroundReactionForces{i} = evaluateGcvSplines( ... - splineExperimentalGroundReactionForces{i}, 0:2, time, 0); + setup.auxdata.splineExperimentalGroundReactionForces{i}, 0:2, time, 0); setup.auxdata.splinedGroundReactionMoments{i} = evaluateGcvSplines( ... - splineExperimentalGroundReactionMoments{i}, 0:2, time, 0); + setup.auxdata.splineExperimentalGroundReactionMoments{i}, 0:2, time, 0); end end diff --git a/src/TreatmentOptimization/makeStateDerivatives.m b/src/TreatmentOptimization/makeStateDerivatives.m index da00d071e..03f872989 100644 --- a/src/TreatmentOptimization/makeStateDerivatives.m +++ b/src/TreatmentOptimization/makeStateDerivatives.m @@ -29,15 +29,15 @@ % ----------------------------------------------------------------------- % function inputs = makeStateDerivatives(inputs, params) -splineJointAngles = makeGcvSplineSet(inputs.experimentalTime, ... +inputs.splineJointAngles = makeGcvSplineSet(inputs.experimentalTime, ... inputs.experimentalJointAngles', inputs.coordinateNames); inputs.experimentalJointVelocities = evaluateGcvSplines( ... - splineJointAngles, inputs.coordinateNames, ... + inputs.splineJointAngles, inputs.coordinateNames, ... inputs.experimentalTime, 1); inputs.experimentalJointAccelerations = evaluateGcvSplines( ... - splineJointAngles, inputs.coordinateNames, ... + inputs.splineJointAngles, inputs.coordinateNames, ... inputs.experimentalTime, 2); inputs.experimentalJointJerks = evaluateGcvSplines( ... - splineJointAngles, inputs.coordinateNames, ... + inputs.splineJointAngles, inputs.coordinateNames, ... inputs.experimentalTime, 3); end diff --git a/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m b/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m index c46c0db5d..fa14c5bb8 100644 --- a/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m +++ b/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m @@ -34,7 +34,7 @@ inputs = modifyModelForces(inputs); initializeMexOrMatlabParallelFunctions(inputs.mexModel); inputs = setupGroundContact(inputs); -% inputs = makeExperimentalDataSplines(inputs); +inputs = makeExperimentalDataSplines(inputs); inputs = makeSurrogateModel(inputs); [inputs.continuousMaxAllowableError, inputs.discreteMaxAllowableError] ... = makeMaxAllowableError(inputs.toolName, inputs.costTerms); diff --git a/src/TreatmentOptimization/solveOptimalControlProblem.m b/src/TreatmentOptimization/solveOptimalControlProblem.m index 6a2655e1c..0a0a1e47a 100644 --- a/src/TreatmentOptimization/solveOptimalControlProblem.m +++ b/src/TreatmentOptimization/solveOptimalControlProblem.m @@ -24,10 +24,10 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function output = solveOptimalControlProblem(inputs, params) +function [inputs, output] = solveOptimalControlProblem(inputs, params) setup = convertToGpopsInputs(inputs, params); -setup = preSplineGpopsInputs(setup); solution = gpops2(setup); +inputs = setup.auxdata; output = convertFromGpopsOutputs(solution, ... inputs, params); end From 38ab1e488e1aa760a38e5998d0c2fc3434d295d7 Mon Sep 17 00:00:00 2001 From: RobSalati <142843461+RobSalati@users.noreply.github.com> Date: Tue, 2 Jan 2024 18:31:56 -0500 Subject: [PATCH 254/365] Fixed formatting bugs Fixed formatting bugs which arose with more muscles. --- .../Analysis/plotMtpHillTypeMuscleParams.m | 8 +++++--- .../Analysis/plotMtpJointMoments.m | 4 ++-- .../Analysis/plotMtpMuscleExcitationsAndActivations.m | 8 ++++---- .../Analysis/plotMtpNormalizedFiberLengths.m | 4 ++-- .../Analysis/plotMtpPassiveForceCurves.m | 2 +- .../Analysis/plotMtpPassiveMomentCurves.m | 2 +- 6 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/MuscleTendonPersonalization/Analysis/plotMtpHillTypeMuscleParams.m b/src/MuscleTendonPersonalization/Analysis/plotMtpHillTypeMuscleParams.m index 489192cf2..5cc23f034 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotMtpHillTypeMuscleParams.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMtpHillTypeMuscleParams.m @@ -45,13 +45,15 @@ function plotMtpHillTypeMuscleParams(resultsDirectory) "Tendon Slack Length Scaling Factor"]; for i = 1 : numel(paramLabels) subplot(1, 6, i) - barh(params(i,:)) + barh(1:numel(muscleNames), params(i,:)) title(textwrap(paramLabels(i), 20), FontSize=12) if i == 1 - set(gca, yticklabels = muscleNames, fontsize=11); + yticks(1:numel(muscleNames)) + yticklabels(muscleNames) else - set(gca, yticklabels = [], fontsize=11); + yticks([]) + yticklabels([]) end end end \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m b/src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m index ca40df213..d9f3eb8c2 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m @@ -95,7 +95,7 @@ function plotMtpJointMoments(resultsDirectory) end if ~isempty(meanMomentsSynx) - subplot(numRows, numColumns, i+3) + subplot(numRows, numColumns, i+numColumns) hold on plotMeanAndStd(meanMomentsSynx(:,i), stdMomentsSynx(:,i), time, 'r-') plotMeanAndStd(meanIdMoments(:,i), stdIdMoments(:,i), time, 'b-') @@ -111,7 +111,7 @@ function plotMtpJointMoments(resultsDirectory) end if ~isempty(meanMomentsSynxNoResidual) - subplot(numRows, numColumns, i+6) + subplot(numRows, numColumns, i+2*numColumns) hold on plotMeanAndStd(meanMomentsSynxNoResidual(:,i), stdMomentsSynxNoResidual(:,i), time, 'r-') plotMeanAndStd(meanIdMoments(:,i), stdIdMoments(:,i), time, 'b-') diff --git a/src/MuscleTendonPersonalization/Analysis/plotMtpMuscleExcitationsAndActivations.m b/src/MuscleTendonPersonalization/Analysis/plotMtpMuscleExcitationsAndActivations.m index 6d821f47e..902560ec3 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotMtpMuscleExcitationsAndActivations.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMtpMuscleExcitationsAndActivations.m @@ -69,13 +69,13 @@ function plotMtpMuscleExcitationsAndActivations(resultsDirectory) for i = 1:numel(muscleNames) subplot(numWindows, numWindows, i); hold on - plotMeanAndStd(meanExcitations(:,i), stdExcitations, time, 'b-'); + plotMeanAndStd(meanExcitations(:,i), stdExcitations(:,i), time, 'b-'); plotMeanAndStd(meanActivations(:,i), stdActivations(:,i), time, 'r-'); if ~isempty(meanExcitationsSynx) - plotMeanAndStd(meanExcitationsSynx(:,i), stdExcitationsSynx, time, 'b--'); + plotMeanAndStd(meanExcitationsSynx(:,i), stdExcitationsSynx(:,i), time, 'b--'); end if ~isempty(meanActivationsSynx) - plotMeanAndStd(meanActivationsSynx(:,i), stdActivationsSynx, time, 'r--'); + plotMeanAndStd(meanActivationsSynx(:,i), stdActivationsSynx(:,i), time, 'r--'); end set(gca, fontsize=11) axis([1 size(meanExcitations, 1) 0 1]) @@ -86,7 +86,7 @@ function plotMtpMuscleExcitationsAndActivations(resultsDirectory) 'Excitation(with residual)', ... 'Activation(with residual)'); end - if mod(i,3) == 1 + if mod(i,numWindows) == 1 ylabel("Magnitude") end if i>numel(muscleNames)-numWindows diff --git a/src/MuscleTendonPersonalization/Analysis/plotMtpNormalizedFiberLengths.m b/src/MuscleTendonPersonalization/Analysis/plotMtpNormalizedFiberLengths.m index 7049f071c..5b7657386 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotMtpNormalizedFiberLengths.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMtpNormalizedFiberLengths.m @@ -54,8 +54,8 @@ function plotMtpNormalizedFiberLengths(resultsDirectory) set(gca, fontsize=11) axis([1 size(meanFiberLengths, 1) 0 1.5]) title(muscleNames(i), FontSize=12); - if mod(i,3) == 1 - ylabel('Normalized Fiber Length', FontSize=12) + if mod(i,numWindows) == 1 + ylabel(textwrap("Normalized Fiber Length",10), FontSize=12) end if i>numel(muscleNames)-numWindows xlabel("Time Points", FontSize=12) diff --git a/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveForceCurves.m b/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveForceCurves.m index 477a0b8dd..272c5e9d3 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveForceCurves.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveForceCurves.m @@ -66,7 +66,7 @@ function plotMtpPassiveForceCurves(resultsDirectory) if i == 1 legend("Experimental", "Model") end - if mod(i,3) == 1 + if mod(i,numWindows) == 1 ylabel("Magnitude") end if i>numel(muscleNames)-numWindows diff --git a/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveMomentCurves.m b/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveMomentCurves.m index 23ef50d98..f9d5a73bc 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveMomentCurves.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveMomentCurves.m @@ -64,7 +64,7 @@ function plotMtpPassiveMomentCurves(resultsDirectory) if i == 1 legend("Experimental", "Model") end - if mod(i,4) == 1 + if mod(i,numWindows) == 1 ylabel("Moment [Nm]") end if i>numel(momentNames)-numWindows From c7774a7a7d0109d4ea60e393c3280ba159f1eb57 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Wed, 3 Jan 2024 00:45:08 -0600 Subject: [PATCH 255/365] fix import for surrogate model --- .../SurrogateModelCreation.m | 34 +++++++------------ src/core/parse/findPrefixes.m | 2 +- 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/src/SurrogateModelCreation/SurrogateModelCreation.m b/src/SurrogateModelCreation/SurrogateModelCreation.m index becc1c4f8..99c2499d2 100644 --- a/src/SurrogateModelCreation/SurrogateModelCreation.m +++ b/src/SurrogateModelCreation/SurrogateModelCreation.m @@ -46,7 +46,7 @@ function inputs = getData(inputs) -tree.trial_prefixes = inputs.trialName; +tree.trial_prefixes.Text = inputs.trialName; prefixes = findPrefixes(tree.trial_prefixes, inputs.dataDirectory); if ~isfield(inputs, 'muscleNames') inputs.muscleNames = getMusclesFromCoordinates(inputs.model, ... @@ -54,26 +54,18 @@ end inputs.numMuscles = length(inputs.muscleNames); -inverseKinematicsFileNames = findFileListFromPrefixList(fullfile( ... - inputs.dataDirectory, "IKData"), prefixes); -[inputs.experimentalJointAngles, inputs.coordinateNames] = ... - parseInverseKinematicsFile(inverseKinematicsFileNames, inputs.model); -inputs.experimentalJointAngles = ... - reshape(permute(inputs.experimentalJointAngles, [1 3 2]), [], ... - length(inputs.coordinateNames)); -experimentalTime = parseTimeColumn(findFileListFromPrefixList(... - fullfile(inputs.dataDirectory, "IKData"), prefixes))'; + +[inputs.experimentalJointAngles, inputs.coordinateNames, experimentalTime] = ... + parseTrialData(fullfile(inputs.dataDirectory, "IKData"), inputs.trialName, inputs.model); +% inputs.experimentalJointAngles = ... +% reshape(permute(inputs.experimentalJointAngles, [1 3 2]), [], ... +% length(inputs.coordinateNames)); inputs.experimentalJointVelocities = calcBSplineDerivative( ... - experimentalTime', inputs.experimentalJointAngles', 5, 20)'; -% for i = 1:size(inputs.experimentalJointAngles, 2) -% [~, inputs.experimentalJointVelocities(:, i)] = SplineSmooth( ... -% experimentalTime, inputs.experimentalJointAngles(:, i), 1); -% end - -directories = findFirstLevelSubDirectoriesFromPrefixes(fullfile( ... - inputs.dataDirectory, "MAData"), prefixes); + experimentalTime, inputs.experimentalJointAngles, 5, 20)'; + +directory = fullfile(inputs.dataDirectory, "MAData", inputs.trialName); [inputs.muscleTendonLengths, inputs.muscleTendonColumnNames] = ... - parseFileFromDirectories(directories, "Length.sto", Model(inputs.model)); + parseFileFromDirectories(directory, "Length.sto", inputs.model); % inputs.muscleNames = findMusclesFromSynergyGroups(inputs); % inputs.surrogateModelCoordinateNames = findSurrogateModelCoordinates( ... % inputs, directories); @@ -83,13 +75,13 @@ inputs.muscleTendonLengths = reshape(permute(inputs.muscleTendonLengths, ... [1 3 2]), [], length(inputs.muscleNames)); [inputs.muscleTendonVelocities, muscleTendonColumnNames] = ... - parseFileFromDirectories(directories, "Velocity.sto", Model(inputs.model)); + parseFileFromDirectories(directory, "Velocity.sto", Model(inputs.model)); inputs.muscleTendonVelocities = findSpecificMusclesInData( ... inputs.muscleTendonVelocities, muscleTendonColumnNames, ... inputs.muscleNames); inputs.muscleTendonVelocities = reshape(permute(inputs.muscleTendonVelocities, ... [1 3 2]), [], length(inputs.muscleNames)); -inputs.momentArms = parseSelectMomentArms(directories, ... +inputs.momentArms = parseSelectMomentArms(directory, ... inputs.surrogateModelCoordinateNames, inputs.muscleNames); inputs.momentArms = reshape(permute(inputs.momentArms, [1 4 2 3]), [], ... length(inputs.surrogateModelCoordinateNames), length(inputs.muscleNames)); diff --git a/src/core/parse/findPrefixes.m b/src/core/parse/findPrefixes.m index 14d7d0337..86f655a9a 100644 --- a/src/core/parse/findPrefixes.m +++ b/src/core/parse/findPrefixes.m @@ -32,7 +32,7 @@ function prefixes = findPrefixes(tree, inputDirectory) prefixField = getFieldByName(tree, 'trial_prefixes'); -if(isstruct(prefixField) && length(prefixField.Text) > 0) +if isstruct(prefixField) && length(prefixField.Text) > 0 prefixes = strsplit(prefixField.Text, ' '); else files = dir(fullfile(inputDirectory, "IDData")); From 326b77525d4dc09a025c90d8645a12dec521cf93 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Wed, 3 Jan 2024 00:46:56 -0600 Subject: [PATCH 256/365] update mex with parallel --- ...verseDynamicsAngularMomentumMexWindows.cpp | 63 +++++++++--------- ...seDynamicsAngularMomentumMexWindows.mexw64 | Bin 34304 -> 35840 bytes 2 files changed, 33 insertions(+), 30 deletions(-) diff --git a/src/core/mex/inverseDynamicsAngularMomentumMexWindows.cpp b/src/core/mex/inverseDynamicsAngularMomentumMexWindows.cpp index 2d4668cd1..5a9cb0b4c 100644 --- a/src/core/mex/inverseDynamicsAngularMomentumMexWindows.cpp +++ b/src/core/mex/inverseDynamicsAngularMomentumMexWindows.cpp @@ -43,14 +43,16 @@ using namespace std; //______________________________________________________________________________ -static Model *osimModel; -static State *osimState; -static InverseDynamicsSolver *idSolver; +static Model *osimModel[numThreads]; +static State *osimState[numThreads]; +static InverseDynamicsSolver *idSolver[numThreads]; static bool modelIsLoaded = false; void ClearMemory(void){ - delete osimModel; - delete idSolver; + for (int i = 0; i < numThreads; i++){ + delete osimModel[i]; + delete idSolver[i]; + } modelIsLoaded = false; mexPrintf("Cleared memory from inverseDynamics mex file.\n"); } @@ -80,9 +82,11 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]){ std::streambuf* oldCoutStreamBuf = std::cout.rdbuf(); std::ostringstream strCout; std::cout.rdbuf(strCout.rdbuf()); - osimModel = new Model(modelName); - osimState = &osimModel->initSystem(); - idSolver = new InverseDynamicsSolver(*osimModel); + for (int i = 0; i < numThreads; i++){ + osimModel[i] = new Model(modelName); + osimState[i] = &osimModel[i]->initSystem(); + idSolver[i] = new InverseDynamicsSolver(*osimModel[i]); + } std::cout.rdbuf(oldCoutStreamBuf); modelIsLoaded = true; } @@ -93,7 +97,7 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]){ const int numPts = mxGetM(prhs[0]); const int numQs = mxGetN(prhs[1]); const int numControls = mxGetN(prhs[5]); - const int numCoords = osimState[0].getNQ(); + const int numCoords = osimState[0]->getNQ(); double *time = mxGetPr(prhs[0]); vector> q = mexArrayToVector(prhs[1]); @@ -101,35 +105,35 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]){ vector> qpp = mexArrayToVector(prhs[3]); vector> u = mexArrayToVector(prhs[5]); - const mxArray *cellElementPtr; mwIndex k; mwSize numLabels, buflen; numLabels = mxGetNumberOfElements(prhs[4]); - int status; plhs[0] = mxCreateDoubleMatrix(numPts,numCoords,mxREAL); double *idLoads = mxGetPr(plhs[0]); - plhs[1] = mxCreateDoubleMatrix(numPts, 3, mxREAL); + plhs[1] = mxCreateDoubleMatrix(numPts, 3, mxREAL); double* angularMomentum = mxGetPr(plhs[1]); + #pragma omp parallel for num_threads(numThreads) for (int i = 0; i < numPts; ++i){ - osimState->setTime(time[i]); - + int thread_id = omp_get_thread_num(); + osimState[thread_id]->setTime(time[i]); + for (int k = 0; k < numLabels; k++){ + const mxArray *cellElementPtr; cellElementPtr = mxGetCell(prhs[4], k); buflen = mxGetN(cellElementPtr)*sizeof(mxChar) + 1; char* c_array; c_array = (char *) mxCalloc(buflen, sizeof(char)); - status = mxGetString(cellElementPtr, c_array, buflen); - osimModel->getCoordinateSet().get(c_array).setValue(*osimState, q[i][k], false); - osimModel->getCoordinateSet().get(c_array).setSpeedValue(*osimState, qp[i][k]); + int status = mxGetString(cellElementPtr, c_array, buflen); + osimModel[thread_id]->getCoordinateSet().get(c_array).setValue(*osimState[thread_id], q[i][k], false); + osimModel[thread_id]->getCoordinateSet().get(c_array).setSpeedValue(*osimState[thread_id], qp[i][k]); mxFree(c_array); } - osimModel->realizeVelocity(*osimState); - // Whole body angular momentum + osimModel[thread_id]->realizeVelocity(*osimState[thread_id]); if (nlhs > 1) { - SpatialVec momentum = osimModel->getMatterSubsystem().calcSystemCentralMomentum(*osimState); + SpatialVec momentum = osimModel[thread_id]->getMatterSubsystem().calcSystemCentralMomentum(*osimState[thread_id]); Vec3 angularMomentumPoint = momentum.get(0); for (int j = 0; j <= 2; j++) { angularMomentum[i + numPts * j] = angularMomentumPoint.get(j); @@ -138,7 +142,7 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]){ Vector AccelsVec(numCoords, 0.0); for (int j = 0; j < numCoords; j++){ - double StateQ = osimState->getQ().get(j); + double StateQ = osimState[thread_id]->getQ().get(j); for (int k = 0; k < numQs; k++){ if (abs(q[i][k] - StateQ) <= 1e-6){ AccelsVec.set(j, qpp[i][k]); @@ -150,20 +154,19 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]){ for (int j = 0; j < numControls; j++){ newControls.set(j, u[i][j]); } - osimModel->setControls(*osimState, newControls); - osimModel->markControlsAsValid(*osimState); - osimModel->realizeDynamics(*osimState); + osimModel[thread_id]->setControls(*osimState[thread_id], newControls); + osimModel[thread_id]->markControlsAsValid(*osimState[thread_id]); + osimModel[thread_id]->realizeDynamics(*osimState[thread_id]); Vector IDLoadsVec; - IDLoadsVec = idSolver->solve(*osimState, AccelsVec); + IDLoadsVec = idSolver[thread_id]->solve(*osimState[thread_id], AccelsVec); for (int j = 0; j < numCoords; j++){ idLoads[i + numPts * j] = IDLoadsVec[j]; } - } - q.clear(); - qp.clear(); - qpp.clear(); - u.clear(); + //q.clear(); + //qp.clear(); + //qpp.clear(); + //u.clear(); } } \ No newline at end of file diff --git a/src/core/mex/inverseDynamicsAngularMomentumMexWindows.mexw64 b/src/core/mex/inverseDynamicsAngularMomentumMexWindows.mexw64 index df6a75b4df2dfddfc17de2fccf94a9329c83c3fc..3d48b22a6bd157b9c12cb5e7bd21b66cfee25bae 100644 GIT binary patch literal 35840 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 CX?Yj` literal 34304 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 z%0D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 i_q5(~ Date: Wed, 3 Jan 2024 00:49:12 -0600 Subject: [PATCH 257/365] fix bug in TO parse --- .../IntegrandTerms/calcTrackingExternalForcesIntegrand.m | 2 +- .../IntegrandTerms/calcTrackingExternalMomentsIntegrand.m | 2 +- src/TreatmentOptimization/gpops/preSplineGpopsInputs.m | 1 + src/TreatmentOptimization/saveTreatmentOptimizationResults.m | 3 ++- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m index 4983e0f77..a08dcbc6b 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m @@ -41,7 +41,7 @@ else experimentalGroundReactions = ... evaluateGcvSplines( ... - params.splineExperimentalGroundReactionForces{i}, 0:2, ... + inputs.splineExperimentalGroundReactionForces{i}, 0:2, ... time); end cost = calcTrackingCostArrayTerm(... diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m index f63e4d764..582ab2165 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m @@ -41,7 +41,7 @@ else experimentalGroundReactions = ... evaluateGcvSplines( ... - params.splineExperimentalGroundReactionMoments{i}, 0:2, ... + inputs.splineExperimentalGroundReactionMoments{i}, 0:2, ... time); end cost = calcTrackingCostArrayTerm(... diff --git a/src/TreatmentOptimization/gpops/preSplineGpopsInputs.m b/src/TreatmentOptimization/gpops/preSplineGpopsInputs.m index d0e9d0e36..8c58b4abb 100644 --- a/src/TreatmentOptimization/gpops/preSplineGpopsInputs.m +++ b/src/TreatmentOptimization/gpops/preSplineGpopsInputs.m @@ -111,6 +111,7 @@ tempSetup.mesh.phase.colpoints = setup.mesh.phase.colpoints; tempSetup.mesh.phase.fraction = setup.mesh.phase.fraction; tempSetup.mesh.maxiterations = 0; +tempSetup.displaylevel = 0; end function output = continuous(input) diff --git a/src/TreatmentOptimization/saveTreatmentOptimizationResults.m b/src/TreatmentOptimization/saveTreatmentOptimizationResults.m index 045e86e6b..65825e356 100644 --- a/src/TreatmentOptimization/saveTreatmentOptimizationResults.m +++ b/src/TreatmentOptimization/saveTreatmentOptimizationResults.m @@ -122,7 +122,8 @@ function saveGroundReactionResults(solution, inputs, values, outputDirectory) midfootSuperiorLocation = pointKinematics(values.time, ... values.statePositions, values.stateVelocities, ... inputs.contactSurfaces{i}.midfootSuperiorPointOnBody', ... - inputs.contactSurfaces{i}.midfootSuperiorBody, inputs.coordinateNames); + inputs.contactSurfaces{i}.midfootSuperiorBody, inputs.modelName, ... + inputs.coordinateNames); midfootSuperiorLocation(:, 2) = 0; groundContactData = [groundContactData, ... solution.groundReactionsLab.forces{i}, ... From 7f378e48a74f8c25bb4c71f0498cad0c6cb4a6ab Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Wed, 3 Jan 2024 21:05:20 -0600 Subject: [PATCH 258/365] Update parseOsimxFile.m --- src/core/osimx/parseOsimxFile.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/osimx/parseOsimxFile.m b/src/core/osimx/parseOsimxFile.m index 2c7215502..c12ac255f 100644 --- a/src/core/osimx/parseOsimxFile.m +++ b/src/core/osimx/parseOsimxFile.m @@ -103,11 +103,11 @@ contactSurface.momentColumns = parseSpaceSeparatedList(tree, "moment_columns"); contactSurface.electricalCenterColumns = parseSpaceSeparatedList(tree, "electrical_center_columns"); -hindfootBodyName = getFieldByName(tree, "hindfoot_body").Text; -if ~hindfootBodyName +hindfootBodyName = getFieldByName(tree, "hindfoot_body"); +if ~isstruct(hindfootBodyName) throw(MException('', " is replaced by in the osimx file. Please update your osimx file.")) else -contactSurface.hindfootBodyName = hindfootBodyName; +contactSurface.hindfootBodyName = hindfootBodyName.Text; end contactSurface.toeMarker = getFieldByNameOrError(tree, "toe_marker").Text; From b74ddcc82853a8fa8bffae96db0b51449bfdd78d Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Mon, 8 Jan 2024 20:40:22 -0600 Subject: [PATCH 259/365] Update saveTreatmentOptimizationResults.m --- src/TreatmentOptimization/saveTreatmentOptimizationResults.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TreatmentOptimization/saveTreatmentOptimizationResults.m b/src/TreatmentOptimization/saveTreatmentOptimizationResults.m index 65825e356..12ea48778 100644 --- a/src/TreatmentOptimization/saveTreatmentOptimizationResults.m +++ b/src/TreatmentOptimization/saveTreatmentOptimizationResults.m @@ -122,7 +122,7 @@ function saveGroundReactionResults(solution, inputs, values, outputDirectory) midfootSuperiorLocation = pointKinematics(values.time, ... values.statePositions, values.stateVelocities, ... inputs.contactSurfaces{i}.midfootSuperiorPointOnBody', ... - inputs.contactSurfaces{i}.midfootSuperiorBody, inputs.modelName, ... + inputs.contactSurfaces{i}.midfootSuperiorBody, inputs.modelFileName, ... inputs.coordinateNames); midfootSuperiorLocation(:, 2) = 0; groundContactData = [groundContactData, ... From d9613eba178023ed0868ca62b41a750af47239bf Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Mon, 8 Jan 2024 20:41:02 -0600 Subject: [PATCH 260/365] fix torque driven from pre-spline --- src/TreatmentOptimization/gpops/preSplineGpopsInputs.m | 6 ++++-- src/TreatmentOptimization/makeTreatmentOptimizationInputs.m | 4 +++- src/core/parse/parseTreatmentOptimizationDataDirectory.m | 1 + 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/TreatmentOptimization/gpops/preSplineGpopsInputs.m b/src/TreatmentOptimization/gpops/preSplineGpopsInputs.m index 8c58b4abb..06041359e 100644 --- a/src/TreatmentOptimization/gpops/preSplineGpopsInputs.m +++ b/src/TreatmentOptimization/gpops/preSplineGpopsInputs.m @@ -62,8 +62,10 @@ end if isfield(setup.auxdata, 'torqueControllerCoordinateNames') && ... ~isempty(setup.auxdata.torqueControllerCoordinateNames) - setup.auxdata.splinedTorqueControls = evaluateGcvSplines( ... - setup.auxdata.splineTorqueControls, setup.auxdata.torqueLabels, time, 0); + if isfield(setup.auxdata, "splineTorqueControls") + setup.auxdata.splinedTorqueControls = evaluateGcvSplines( ... + setup.auxdata.splineTorqueControls, setup.auxdata.torqueLabels, time, 0); + end end if isfield(setup.auxdata, "splineMarkerPositions") for i = 1:length(setup.auxdata.splineMarkerPositions) diff --git a/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m b/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m index fa14c5bb8..0b44a9920 100644 --- a/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m +++ b/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m @@ -42,6 +42,8 @@ inputs = makePathConstraintBounds(inputs); inputs = makeTerminalConstraintBounds(inputs); inputs = makeOptimalControlBounds(inputs); -inputs.surrogateMuscles = SurrogateModelCreation(inputs); +if strcmp(inputs.controllerType, "synergy") + inputs.surrogateMuscles = SurrogateModelCreation(inputs); +end end diff --git a/src/core/parse/parseTreatmentOptimizationDataDirectory.m b/src/core/parse/parseTreatmentOptimizationDataDirectory.m index d12fed93a..dc6503a09 100644 --- a/src/core/parse/parseTreatmentOptimizationDataDirectory.m +++ b/src/core/parse/parseTreatmentOptimizationDataDirectory.m @@ -65,6 +65,7 @@ fullfile(dataDirectory, "IKData"), inputs.trialName, inputs.model); inputs.coordinateNames = cellstr(inputs.coordinateNames); inputs.experimentalTime = experimentalTime - experimentalTime(1); +inputs.initialTime = inputs.experimentalTime; if isfield(inputs.osimx, 'groundContact') && ... isfield(inputs.osimx.groundContact, 'contactSurface') inputs.contactSurfaces = inputs.osimx.groundContact.contactSurface; From 2ed45ec06ff4433fee83f603faaba61c120471c8 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Tue, 9 Jan 2024 01:20:28 -0600 Subject: [PATCH 261/365] fix tracking coordinate bug --- .../IntegrandTerms/calcTrackingCoordinateIntegrand.m | 6 +++--- .../IntegrandTerms/calcTrackingSpeedIntegrand.m | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m index 41787b816..7aaf2d2be 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m @@ -29,10 +29,10 @@ % ----------------------------------------------------------------------- % function cost = calcTrackingCoordinateIntegrand(costTerm, inputs, time, ... - statePositions, coordinateName) + positions, coordinateName) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); -indx = find(strcmp(convertCharsToStrings(inputs.statesCoordinateNames), ... +indx = find(strcmp(convertCharsToStrings(inputs.coordinateNames), ... coordinateName)); if isempty(indx) throw(MException('CostTermError:CoordinateNotInState', ... @@ -46,7 +46,7 @@ inputs.splineJointAngles, inputs.coordinateNames, time); end cost = calcTrackingCostArrayTerm(experimentalJointAngles, ... - statePositions, indx); + positions, indx); if normalizeByFinalTime cost = cost / time(end); diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m index c2bad0720..161e9fc8e 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m @@ -29,10 +29,10 @@ % ----------------------------------------------------------------------- % function cost = calcTrackingSpeedIntegrand(costTerm, inputs, time, ... - stateVelocities, coordinateName) + velocities, coordinateName) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); -indx = find(strcmp(convertCharsToStrings(inputs.statesCoordinateNames), ... +indx = find(strcmp(convertCharsToStrings(inputs.coordinateNames), ... coordinateName)); if isempty(indx) throw(MException('CostTermError:CoordinateNotInState', ... @@ -47,7 +47,7 @@ end cost = calcTrackingCostArrayTerm(experimentalJointVelocities, ... - stateVelocities, indx); + velocities, indx); if normalizeByFinalTime cost = cost / time(end); From 9db7eec8b75f1139614139ae19f2c652076fdda5 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Tue, 9 Jan 2024 14:43:18 -0600 Subject: [PATCH 262/365] Fix muscle-tendon velocity orientation --- src/SurrogateModelCreation/SurrogateModelCreation.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SurrogateModelCreation/SurrogateModelCreation.m b/src/SurrogateModelCreation/SurrogateModelCreation.m index 99c2499d2..82f0c8096 100644 --- a/src/SurrogateModelCreation/SurrogateModelCreation.m +++ b/src/SurrogateModelCreation/SurrogateModelCreation.m @@ -61,7 +61,7 @@ % reshape(permute(inputs.experimentalJointAngles, [1 3 2]), [], ... % length(inputs.coordinateNames)); inputs.experimentalJointVelocities = calcBSplineDerivative( ... - experimentalTime, inputs.experimentalJointAngles, 5, 20)'; + experimentalTime, inputs.experimentalJointAngles, 5, 20); directory = fullfile(inputs.dataDirectory, "MAData", inputs.trialName); [inputs.muscleTendonLengths, inputs.muscleTendonColumnNames] = ... From cc4b73984f17b3bfb1e2a684c7ed770a52b818ca Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Tue, 9 Jan 2024 16:08:47 -0600 Subject: [PATCH 263/365] obviously cell arrays are easy to use --- src/SurrogateModelCreation/calcSurrogateModel.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SurrogateModelCreation/calcSurrogateModel.m b/src/SurrogateModelCreation/calcSurrogateModel.m index 11c31a3eb..46ea12847 100644 --- a/src/SurrogateModelCreation/calcSurrogateModel.m +++ b/src/SurrogateModelCreation/calcSurrogateModel.m @@ -50,7 +50,7 @@ index = 1; for j = 1 : length(inputs.coordinateNames) for k = 1 : length(inputs.surrogateModelLabels{i}) - if strcmp(inputs.coordinateNames(j), inputs.surrogateModelLabels{i}(k)) + if strcmp(inputs.coordinateNames(j), inputs.surrogateModelLabels{i}{k}) newMomentArms(:, j, i) = momentArms(:, index); index = index + 1; end From 059e5f35266f91864d04db8ddcba928b18a97935 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Tue, 9 Jan 2024 20:46:03 -0600 Subject: [PATCH 264/365] Update saveTreatmentOptimizationResults.m --- src/TreatmentOptimization/saveTreatmentOptimizationResults.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TreatmentOptimization/saveTreatmentOptimizationResults.m b/src/TreatmentOptimization/saveTreatmentOptimizationResults.m index 12ea48778..728f03faf 100644 --- a/src/TreatmentOptimization/saveTreatmentOptimizationResults.m +++ b/src/TreatmentOptimization/saveTreatmentOptimizationResults.m @@ -121,7 +121,7 @@ function saveGroundReactionResults(solution, inputs, values, outputDirectory) inputs.contactSurfaces{i}.electricalCenterColumns]); midfootSuperiorLocation = pointKinematics(values.time, ... values.statePositions, values.stateVelocities, ... - inputs.contactSurfaces{i}.midfootSuperiorPointOnBody', ... + inputs.contactSurfaces{i}.midfootSuperiorPointOnBody, ... inputs.contactSurfaces{i}.midfootSuperiorBody, inputs.modelFileName, ... inputs.coordinateNames); midfootSuperiorLocation(:, 2) = 0; From 6e9ec789d629a01c0ac44bf5929be528893ba4a8 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Wed, 10 Jan 2024 02:06:39 -0600 Subject: [PATCH 265/365] Update parseTreatmentOptimizationDataDirectory.m --- src/core/parse/parseTreatmentOptimizationDataDirectory.m | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/parse/parseTreatmentOptimizationDataDirectory.m b/src/core/parse/parseTreatmentOptimizationDataDirectory.m index dc6503a09..56d90fbd5 100644 --- a/src/core/parse/parseTreatmentOptimizationDataDirectory.m +++ b/src/core/parse/parseTreatmentOptimizationDataDirectory.m @@ -142,9 +142,12 @@ function [forces, moments, ec] = parseGroundReactionDataWithoutTime( ... inputs, dataDirectory, surfaceIndex) import org.opensim.modeling.Storage -[grfData, grfColumnNames, ~] = parseTrialDataTryDirectories( ... +[grfData, grfColumnNames, grfTime] = parseTrialDataTryDirectories( ... fullfile(inputs.previousResultsDirectory, "GRFData"), ... fullfile(dataDirectory, "GRFData"), inputs.trialName, inputs.model); +forces = NaN(length(grfTime), 3); +moments = NaN(length(grfTime), 3); +ec = NaN(length(grfTime), 3); for i=1:size(grfColumnNames') label = grfColumnNames(i); for j = 1:3 From c8f1a9deee7db2fe554c0b1a7fae7db871efb8b7 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Wed, 10 Jan 2024 22:44:19 -0600 Subject: [PATCH 266/365] add fix for negative height in foot model --- .../calcModeledVerticalGroundReactionForce.m | 11 +++++++---- .../calcTorqueBasedModeledValues.m | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/GroundContactPersonalization/Optimizations/ModelCalculation/calcModeledVerticalGroundReactionForce.m b/src/GroundContactPersonalization/Optimizations/ModelCalculation/calcModeledVerticalGroundReactionForce.m index ed7068b37..dbe04c2a2 100644 --- a/src/GroundContactPersonalization/Optimizations/ModelCalculation/calcModeledVerticalGroundReactionForce.m +++ b/src/GroundContactPersonalization/Optimizations/ModelCalculation/calcModeledVerticalGroundReactionForce.m @@ -59,10 +59,13 @@ freglyVerticalGrf = -s .* (v .* height - c .* ... log(cosh((height + h) ./ c))) - constant; % Account for potential errors in force model - freglyVerticalGrf(isnan(freglyVerticalGrf)) = ... - min(min(freglyVerticalGrf)); - freglyVerticalGrf(isinf(freglyVerticalGrf)) = ... - min(min(freglyVerticalGrf)); + if isnan(freglyVerticalGrf) || isinf(freglyVerticalGrf) || freglyVerticalGrf > 1e8 + freglyVerticalGrf = 1e7; + end + % freglyVerticalGrf(isnan(freglyVerticalGrf)) = ... + % min(min(freglyVerticalGrf)); + % freglyVerticalGrf(isinf(freglyVerticalGrf)) = ... + % min(min(freglyVerticalGrf)); springForces(2, i) = freglyVerticalGrf * (1 + dampingFactor * ... verticalVelocity); modeledVerticalGrf = modeledVerticalGrf + springForces(2, i); diff --git a/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m b/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m index 0aecc8dd3..0140cb18d 100644 --- a/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m +++ b/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m @@ -58,7 +58,7 @@ coordinateLoads = zeros(length(values.time), numCoordinateLoads); appliedLoads = [zeros(length(values.time), ... inputs.model.getForceSet().getMuscles().getSize()) ... - appliedLoads coordinateLoads]; + coordinateLoads]; if ~isempty(inputs.contactSurfaces) clear pointKinematics [springPositions, springVelocities] = getSpringLocations( ... From a6584be25b61b64aea63699a9ee9703f5631b679 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Fri, 12 Jan 2024 02:06:35 -0600 Subject: [PATCH 267/365] fix bug with out of order states_coordinates Co-Authored-By: Spencer Williams <54556034+SpencerTWilliams@users.noreply.github.com> --- src/TreatmentOptimization/subsetDataByCoordinates.m | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/TreatmentOptimization/subsetDataByCoordinates.m b/src/TreatmentOptimization/subsetDataByCoordinates.m index 791cb8a59..29a9e9366 100644 --- a/src/TreatmentOptimization/subsetDataByCoordinates.m +++ b/src/TreatmentOptimization/subsetDataByCoordinates.m @@ -32,7 +32,13 @@ function output = subsetDataByCoordinates(data, coordinateNames, ... subsetOfCoordinateNames) -includedSubset = ismember(coordinateNames, subsetOfCoordinateNames); +includedSubset = []; +for i = 1 : length(subsetOfCoordinateNames) + temp = find(coordinateNames == subsetOfCoordinateNames(i)); + assert(~isempty(temp), subsetOfCoordinateNames(i) + ... + " is not a coordinate in the IK data"); + includedSubset(end + 1) = temp; +end output = data(:, includedSubset); end From d99e770a75385a17ddb63af357bbb07d9ad1754c Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sat, 13 Jan 2024 16:00:03 -0600 Subject: [PATCH 268/365] Update parseSynergyController.m --- src/core/parse/parseSynergyController.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/parse/parseSynergyController.m b/src/core/parse/parseSynergyController.m index a8d9ac3b7..8e088c10a 100644 --- a/src/core/parse/parseSynergyController.m +++ b/src/core/parse/parseSynergyController.m @@ -33,7 +33,7 @@ inputs.synergyGroups = inputs.osimx.synergyGroups; inputs.numSynergies = getNumSynergies(inputs.synergyGroups); inputs.surrogateModelCoordinateNames = parseSpaceSeparatedList(tree, ... - "states_coordinate_list"); + "surrogate_model_coordinate_list"); inputs.muscleNames = getMusclesFromCoordinates(inputs.model, ... inputs.surrogateModelCoordinateNames); inputs.numMuscles = length(inputs.muscleNames); From cf020c952cfd25119c81215d14857e4af5f54b45 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 14 Jan 2024 13:54:01 -0600 Subject: [PATCH 269/365] fix id labels for TO --- .../calcTrackingInverseDynamicLoadsIntegrand.m | 11 ++++------- .../setupGpopsInitialGuess.m | 2 +- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m index 5e12ca2e4..90309686b 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m @@ -32,20 +32,17 @@ inputs, time, inverseDynamicsMoments, loadName) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); -loadName = erase(loadName, '_moment'); -loadName = erase(loadName, '_force'); -indx = find(strcmp(convertCharsToStrings(inputs.coordinateNames), ... - loadName)); +indx = find(strcmp(inputs.inverseDynamicsMomentLabels, loadName)); if size(time) == size(inputs.collocationTimeOriginal) experimentalJointMoments = inputs.splinedJointMoments; else experimentalJointMoments = evaluateGcvSplines( ... inputs.splineJointMoments, inputs.inverseDynamicsMomentLabels, time); end -momentLabelsNoSuffix = erase(inputs.inverseDynamicsMomentLabels, '_moment'); -momentLabelsNoSuffix = erase(momentLabelsNoSuffix, '_force'); -includedJointMomentCols = ismember(momentLabelsNoSuffix, convertCharsToStrings(inputs.coordinateNames)); if size(inverseDynamicsMoments, 2) ~= size(experimentalJointMoments, 2) + momentLabelsNoSuffix = erase(inputs.inverseDynamicsMomentLabels, '_moment'); + momentLabelsNoSuffix = erase(momentLabelsNoSuffix, '_force'); + includedJointMomentCols = ismember(momentLabelsNoSuffix, convertCharsToStrings(inputs.coordinateNames)); experimentalJointMoments = experimentalJointMoments(:, includedJointMomentCols); end cost = calcTrackingCostArrayTerm(experimentalJointMoments, ... diff --git a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m index d2b4f2599..1bd35d7ee 100644 --- a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m +++ b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m @@ -95,7 +95,7 @@ if ~isempty(valueOrAlternate(inputs, "torqueControllerCoordinateNames", [])) stateTorqueControls = subsetDataByCoordinates( ... inputs.experimentalJointMoments, ... - inputs.coordinateNames, ... + erase(erase(inputs.inverseDynamicsMomentLabels, '_moment'), '_force'), ... inputs.torqueControllerCoordinateNames); controls = [controls, stateTorqueControls]; end From 6b567c4301243e6587eb3543dc05c7931ccb2cb4 Mon Sep 17 00:00:00 2001 From: RobSalati Date: Tue, 16 Jan 2024 14:57:31 -0600 Subject: [PATCH 270/365] Fixed x labels on joint moments plots --- .../Analysis/plotMtpJointMoments.m | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m b/src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m index d9f3eb8c2..e93847d17 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m @@ -86,6 +86,7 @@ function plotMtpJointMoments(resultsDirectory) plotMeanAndStd(meanMoments(:,i), stdMoments(:,i), time, 'r-') plotMeanAndStd(meanIdMoments(:,i), stdIdMoments(:,i), time, 'b-') hold off + set(gca, fontsize=11) title(jointLabels(i), fontsize=12) axis([0 size(meanIdMoments, 1) minMoment, maxMoment]) if i == 1 @@ -93,7 +94,6 @@ function plotMtpJointMoments(resultsDirectory) ylabel("Joint Moment [Nm]") end end - if ~isempty(meanMomentsSynx) subplot(numRows, numColumns, i+numColumns) hold on @@ -102,14 +102,12 @@ function plotMtpJointMoments(resultsDirectory) hold off set(gca, fontsize=11) title(jointLabels(i), FontSize=12) - xlabel("Time Point") axis([0 size(meanIdMoments, 1) minMoment, maxMoment]) if i == 1 legend("Mean Moment Synx", "Mean Inverse Dynamics Moment") ylabel("Joint Moment [Nm]") end end - if ~isempty(meanMomentsSynxNoResidual) subplot(numRows, numColumns, i+2*numColumns) hold on @@ -118,12 +116,12 @@ function plotMtpJointMoments(resultsDirectory) hold off set(gca, fontsize=11) title(jointLabels(i), FontSize=12) - xlabel("Time Point") axis([0 size(meanIdMoments, 1) minMoment, maxMoment]) if i == 1 legend("Mean Moment Synx No Residual", "Mean Inverse Dynamics Moment") ylabel("Joint Moment [Nm]") end end + xlabel("Time Point") end end \ No newline at end of file From 548f1a8347c88bb4e7139f35773aded3cdaa7f48 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Tue, 16 Jan 2024 19:02:44 -0600 Subject: [PATCH 271/365] add minimizing id loads cost term --- ...lcMinimizingInverseDynamicLoadsIntegrand.m | 41 +++++++++++++++++++ .../generateCostTermStruct.m | 10 +++++ 2 files changed, 51 insertions(+) create mode 100644 src/TreatmentOptimization/IntegrandTerms/calcMinimizingInverseDynamicLoadsIntegrand.m diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingInverseDynamicLoadsIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingInverseDynamicLoadsIntegrand.m new file mode 100644 index 000000000..fcb44945f --- /dev/null +++ b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingInverseDynamicLoadsIntegrand.m @@ -0,0 +1,41 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function calculates the difference between the experimental and +% predicted inverse dynamic moments for the specified coordinate. +% +% (struct, Array of number, 2D matrix, Array of string) -> (Array of number) +% + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Marleny Vega % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function cost = calcMinimizingInverseDynamicLoadsIntegrand(costTerm, ... + inputs, time, inverseDynamicsMoments, loadName) +normalizeByFinalTime = valueOrAlternate(costTerm, ... + "normalize_by_final_time", true); +indx = find(strcmp(inputs.inverseDynamicsMomentLabels, loadName)); +cost = calcTrackingCostArrayTerm(zeros(size(inverseDynamicsMoments)), ... + inverseDynamicsMoments, indx); +if normalizeByFinalTime + cost = cost / time(end); +end +end diff --git a/src/TreatmentOptimization/generateCostTermStruct.m b/src/TreatmentOptimization/generateCostTermStruct.m index af8044c2d..a11b5806e 100644 --- a/src/TreatmentOptimization/generateCostTermStruct.m +++ b/src/TreatmentOptimization/generateCostTermStruct.m @@ -49,6 +49,7 @@ "generalized_speed_tracking", ... "marker_position_tracking", ... "inverse_dynamics_load_tracking", ... + "inverse_dynamics_load_minimization", ... "external_force_tracking", ... "external_moment_tracking", ... "muscle_activation_tracking", ... @@ -191,6 +192,15 @@ costTerm.load ... ); +costTermCalculations.inverse_dynamics_load_minimization = @(values, modeledValues, auxdata, costTerm) ... + calcMinimizingInverseDynamicLoadsIntegrand( ... + costTerm, ... + auxdata, ... + values.time, ... + modeledValues.inverseDynamicsMoments, ... + costTerm.load ... + ); + costTermCalculations.external_force_tracking = @(values, modeledValues, auxdata, costTerm) ... calcTrackingExternalForcesIntegrand( ... costTerm, ... From 4ec783c67b0e7445293a32995eb021b9f58235bf Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Tue, 16 Jan 2024 19:02:57 -0600 Subject: [PATCH 272/365] add error message for kinetic path constraint --- .../PathTerms/calcKineticPathConstraint.m | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/TreatmentOptimization/PathTerms/calcKineticPathConstraint.m b/src/TreatmentOptimization/PathTerms/calcKineticPathConstraint.m index f8bc042b4..ec12ba959 100644 --- a/src/TreatmentOptimization/PathTerms/calcKineticPathConstraint.m +++ b/src/TreatmentOptimization/PathTerms/calcKineticPathConstraint.m @@ -47,6 +47,11 @@ else torqueLoad = torqueControls(:, torqueIndex); end +if length(synergyLoad) == 1 && length(torqueLoad) == 1 && ... + torqueLoad == 0 && synergyLoad == 0 + throw(MException('', "kinetic path constraint load is not a" + ... + " synergy driven or torque driven coordinate")) +end pathTerm = modeledValues.inverseDynamicsMoments(:, inverseDynamicsIndex) - ... (synergyLoad + torqueLoad); end From d406163113a8e312f10d03513bf422a7ade5475a Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Tue, 16 Jan 2024 19:37:36 -0600 Subject: [PATCH 273/365] small fixes --- .../calcModeledVerticalGroundReactionForce.m | 8 -------- .../plotNeuralControlPersonalizationActivations.m | 2 +- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/GroundContactPersonalization/Optimizations/ModelCalculation/calcModeledVerticalGroundReactionForce.m b/src/GroundContactPersonalization/Optimizations/ModelCalculation/calcModeledVerticalGroundReactionForce.m index dbe04c2a2..40b66a368 100644 --- a/src/GroundContactPersonalization/Optimizations/ModelCalculation/calcModeledVerticalGroundReactionForce.m +++ b/src/GroundContactPersonalization/Optimizations/ModelCalculation/calcModeledVerticalGroundReactionForce.m @@ -58,14 +58,6 @@ constant = -s .* (v .* ymax - c .* log(cosh((ymax + h) ./ c))); freglyVerticalGrf = -s .* (v .* height - c .* ... log(cosh((height + h) ./ c))) - constant; - % Account for potential errors in force model - if isnan(freglyVerticalGrf) || isinf(freglyVerticalGrf) || freglyVerticalGrf > 1e8 - freglyVerticalGrf = 1e7; - end - % freglyVerticalGrf(isnan(freglyVerticalGrf)) = ... - % min(min(freglyVerticalGrf)); - % freglyVerticalGrf(isinf(freglyVerticalGrf)) = ... - % min(min(freglyVerticalGrf)); springForces(2, i) = freglyVerticalGrf * (1 + dampingFactor * ... verticalVelocity); modeledVerticalGrf = modeledVerticalGrf + springForces(2, i); diff --git a/src/NeuralControlPersonalization/Analysis/plotNeuralControlPersonalizationActivations.m b/src/NeuralControlPersonalization/Analysis/plotNeuralControlPersonalizationActivations.m index 4a4b070f0..dc6ff6e96 100644 --- a/src/NeuralControlPersonalization/Analysis/plotNeuralControlPersonalizationActivations.m +++ b/src/NeuralControlPersonalization/Analysis/plotNeuralControlPersonalizationActivations.m @@ -59,7 +59,7 @@ function plotNeuralControlPersonalizationActivations(weightsFile, ... figureNumber = 1; subplotNumber = 1; hasLegend = false; -figure(1) +% figure(1) for i = 1:size(muscleActivations, 1) if i > figureSize * figureNumber figureNumber = figureNumber + 1; From 8f1a9d5582a66fd6f650a8aa14bbc30873b3cd94 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Wed, 17 Jan 2024 17:14:10 -0600 Subject: [PATCH 274/365] add minimizing kinetic inconsistency integrand --- ...cMinimizingKineticInconsistencyIntegrand.m | 40 +++++++++++++++++++ .../generateCostTermStruct.m | 13 +++++- 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 src/TreatmentOptimization/IntegrandTerms/calcMinimizingKineticInconsistencyIntegrand.m diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingKineticInconsistencyIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingKineticInconsistencyIntegrand.m new file mode 100644 index 000000000..b9571ced6 --- /dev/null +++ b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingKineticInconsistencyIntegrand.m @@ -0,0 +1,40 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function calculates the difference between the experimental and +% predicted inverse dynamic moments for the specified coordinate. +% +% (struct, Array of number, 2D matrix, Array of string) -> (Array of number) +% + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Marleny Vega % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function cost = calcMinimizingKineticInconsistencyIntegrand(costTerm, ... + inputs, time, modeledValues, torqueControls, loadName) +normalizeByFinalTime = valueOrAlternate(costTerm, ... + "normalize_by_final_time", true); +cost = calcKineticPathConstraint(inputs, modeledValues, torqueControls, ... + loadName); +if normalizeByFinalTime + cost = cost / time(end); +end +end diff --git a/src/TreatmentOptimization/generateCostTermStruct.m b/src/TreatmentOptimization/generateCostTermStruct.m index a11b5806e..af1c24e3b 100644 --- a/src/TreatmentOptimization/generateCostTermStruct.m +++ b/src/TreatmentOptimization/generateCostTermStruct.m @@ -50,6 +50,7 @@ "marker_position_tracking", ... "inverse_dynamics_load_tracking", ... "inverse_dynamics_load_minimization", ... + "kinetic_inconsistency_minimization", ... "external_force_tracking", ... "external_moment_tracking", ... "muscle_activation_tracking", ... @@ -122,7 +123,7 @@ "user_defined" ... ]; otherwise - throw(MException('', ['Tool name' toolName 'is not valid'])) + throw(MException('', ['Tool name ' toolName ' is not valid'])) end else throw(MException('', ['Cost term type ' costTermType ... @@ -200,6 +201,16 @@ modeledValues.inverseDynamicsMoments, ... costTerm.load ... ); + +costTermCalculations.kinetic_inconsistency_minimization = @(values, modeledValues, auxdata, costTerm) ... + calcMinimizingKineticInconsistencyIntegrand( ... + costTerm, ... + auxdata, ... + values.time, ... + modeledValues, ... + values.torqueControls, ... + costTerm.load ... + ); costTermCalculations.external_force_tracking = @(values, modeledValues, auxdata, costTerm) ... calcTrackingExternalForcesIntegrand( ... From c18d211b3a428514f40ed3b8c378ef2ee18801fc Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Wed, 17 Jan 2024 19:12:26 -0600 Subject: [PATCH 275/365] Plot VAF and muscle RMSE for NCP results --- .../Analysis/plotNcpActivationRmsAndVaf.m | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 src/NeuralControlPersonalization/Analysis/plotNcpActivationRmsAndVaf.m diff --git a/src/NeuralControlPersonalization/Analysis/plotNcpActivationRmsAndVaf.m b/src/NeuralControlPersonalization/Analysis/plotNcpActivationRmsAndVaf.m new file mode 100644 index 000000000..b21c59bb7 --- /dev/null +++ b/src/NeuralControlPersonalization/Analysis/plotNcpActivationRmsAndVaf.m @@ -0,0 +1,75 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% Plot total VAF and RMS error for invdividual NCP muscle activations +% compared to tracked activations. RMS error is used for individual muscles +% instead of VAF because it better describes the fit of smaller +% activations. +% +% (string, string, string) -> (None) +% Plot total VAF and RMS error for NCP muscle activations. + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Spencer Williams % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function plotNcpActivationRmsAndVaf(weightsFile, commandsFile, mtpActivationsFile) +import org.opensim.modeling.Storage +weightsStorage = Storage(weightsFile); +ncpMuscleNames = getStorageColumnNames(weightsStorage); +synergyWeights = storageToDoubleMatrix(weightsStorage); +commandsStorage = Storage(commandsFile); +synergyCommands = storageToDoubleMatrix(commandsStorage); +ncpActivations = synergyWeights * synergyCommands; + +mtpStorage = Storage(mtpActivationsFile); +mtpMuscleNames = getStorageColumnNames(mtpStorage); +mtpActivations = storageToDoubleMatrix(mtpStorage); + +[sharedMuscleNames, ncpIndices, mtpIndices] = ... + intersect(ncpMuscleNames, mtpMuscleNames); +ncpSubset = ncpActivations(ncpIndices, :); +mtpSubset = mtpActivations(mtpIndices, :); + + +rmsError = zeros(1, length(sharedMuscleNames)); +for i = 1 : length(sharedMuscleNames) + rmsError(i) = rms(mtpSubset(i, :) - ncpSubset(i, :)); +end +totalVaf = calcPercentVaf(reshape(mtpSubset, 1, []), ... + reshape(ncpSubset, 1, [])); +[worstError, worstMuscleIndex] = max(rmsError); +worstMuscleName = sharedMuscleNames(worstMuscleIndex); + +boxplot(rmsError) +title("RMS error for muscles with tracked activations", ... + "Total VAF: " + sprintf('\\bf{%.2f}%%\\rm', totalVaf) + newline + ... + "Worst individual muscle: " + strrep(worstMuscleName, '_', '\_') + ... + sprintf(" (RMSE: %.3f)", worstError)) +ylabel("RMSE") +set(gca, 'XTick', []) +end + +function percentVaf = calcPercentVaf(experimental, reconstructed) +sr = sum((experimental - reconstructed) .^ 2); +st = sum(experimental .^ 2); + +percentVaf = (1 - sr/st) * 100; +end From 54136cdbc0104a86c1470995b6807f45cfaf8abe Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Wed, 17 Jan 2024 19:57:25 -0600 Subject: [PATCH 276/365] Fix VAF plot function formatting --- .../Analysis/plotNcpActivationRmsAndVaf.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NeuralControlPersonalization/Analysis/plotNcpActivationRmsAndVaf.m b/src/NeuralControlPersonalization/Analysis/plotNcpActivationRmsAndVaf.m index b21c59bb7..f2e871867 100644 --- a/src/NeuralControlPersonalization/Analysis/plotNcpActivationRmsAndVaf.m +++ b/src/NeuralControlPersonalization/Analysis/plotNcpActivationRmsAndVaf.m @@ -30,7 +30,8 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function plotNcpActivationRmsAndVaf(weightsFile, commandsFile, mtpActivationsFile) +function plotNcpActivationRmsAndVaf(weightsFile, commandsFile, ... + mtpActivationsFile) import org.opensim.modeling.Storage weightsStorage = Storage(weightsFile); ncpMuscleNames = getStorageColumnNames(weightsStorage); @@ -48,7 +49,6 @@ function plotNcpActivationRmsAndVaf(weightsFile, commandsFile, mtpActivationsFil ncpSubset = ncpActivations(ncpIndices, :); mtpSubset = mtpActivations(mtpIndices, :); - rmsError = zeros(1, length(sharedMuscleNames)); for i = 1 : length(sharedMuscleNames) rmsError(i) = rms(mtpSubset(i, :) - ncpSubset(i, :)); From cfe15d3250774e1a977d1acf664ebc28fdd7ecaa Mon Sep 17 00:00:00 2001 From: RobSalati Date: Mon, 22 Jan 2024 14:32:33 -0600 Subject: [PATCH 277/365] Fixes for joint moments & passive force saving. --- .../Analysis/plotMtpJointMoments.m | 30 ++----------------- .../Analysis/plotMtpPassiveForceCurves.m | 28 +++++------------ .../Saving/getMtpResultsToSave.m | 4 +-- .../saveMtpActivationAndExcitationData.m | 10 ------- .../Saving/saveMtpJointMomentData.m | 5 ---- .../Saving/saveMtpPassiveForceData.m | 13 +------- .../saveMuscleTendonPersonalizationResults.m | 2 +- 7 files changed, 12 insertions(+), 80 deletions(-) diff --git a/src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m b/src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m index e93847d17..a5418552e 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m @@ -46,14 +46,6 @@ function plotMtpJointMoments(resultsDirectory) modelMomentsSynx = []; end -if exist(fullfile(analysisDirectory, "modelJointMomentsSynxNoResiduals"), "dir") - [~, modelMomentsSynxNoResiduals] = extractMtpDataFromSto( ... - fullfile(analysisDirectory, "modelJointMomentsSynxNoResiduals")); - numRows = numRows + 1; -else - modelMomentsSynxNoResiduals = []; -end - jointLabels = strrep(jointLabels, '_', ' '); meanIdMoments = mean(idMoments, 3); stdIdMoments = std(idMoments, [], 3); @@ -61,18 +53,14 @@ function plotMtpJointMoments(resultsDirectory) stdMoments = std(modelMoments, [], 3); meanMomentsSynx = mean(modelMomentsSynx, 3); stdMomentsSynx = std(modelMomentsSynx, [], 3); -meanMomentsSynxNoResidual = mean(modelMomentsSynxNoResiduals, 3); -stdMomentsSynxNoResidual = std(modelMomentsSynxNoResiduals, [], 3); maxMoment = max([ ... max(meanIdMoments, [], "all"), ... max(meanMoments, [], "all"), ... - max(meanMomentsSynx, [], "all"), ... - max(meanMomentsSynxNoResidual, [], "all")]); + max(meanMomentsSynx, [], "all")]); minMoment = min([ ... min(meanIdMoments, [], "all"), ... min(meanMoments, [], "all"), ... - min(meanMomentsSynx, [], "all"), ... - min(meanMomentsSynxNoResidual, [], "all")]); + min(meanMomentsSynx, [], "all")]); figure(Name = "Joint Moments", ... Units='normalized', ... @@ -108,20 +96,6 @@ function plotMtpJointMoments(resultsDirectory) ylabel("Joint Moment [Nm]") end end - if ~isempty(meanMomentsSynxNoResidual) - subplot(numRows, numColumns, i+2*numColumns) - hold on - plotMeanAndStd(meanMomentsSynxNoResidual(:,i), stdMomentsSynxNoResidual(:,i), time, 'r-') - plotMeanAndStd(meanIdMoments(:,i), stdIdMoments(:,i), time, 'b-') - hold off - set(gca, fontsize=11) - title(jointLabels(i), FontSize=12) - axis([0 size(meanIdMoments, 1) minMoment, maxMoment]) - if i == 1 - legend("Mean Moment Synx No Residual", "Mean Inverse Dynamics Moment") - ylabel("Joint Moment [Nm]") - end - end xlabel("Time Point") end end \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveForceCurves.m b/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveForceCurves.m index 272c5e9d3..7f7936eb2 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveForceCurves.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveForceCurves.m @@ -30,42 +30,28 @@ % ----------------------------------------------------------------------- % function plotMtpPassiveForceCurves(resultsDirectory) analysisDirectory = fullfile(resultsDirectory, "Analysis"); -[muscleNames, experimentalForce] = extractMtpDataFromSto( ... - fullfile(analysisDirectory, "passiveForcesExperimental")); -if exist(fullfile(analysisDirectory, "passiveForcesModelSynx"), "dir") - [~, modelForce] = extractMtpDataFromSto(... - fullfile(analysisDirectory, "passiveForcesModelSynx")); -else - [~, modelForce] = extractMtpDataFromSto(... - fullfile(analysisDirectory, "passiveForcesModel")); -end + +[muscleNames, modelForce] = extractMtpDataFromSto(... + fullfile(analysisDirectory, "passiveForcesModel")); + muscleNames = strrep(muscleNames, '_', ' '); -meanExperimentalForce = mean(experimentalForce, 3); -stdExperimentalForce = std(experimentalForce, [], 3); meanModelForce = mean(modelForce, 3); stdModelForce = std(modelForce, [], 3); -maxForce = max( ... - [max(meanModelForce, [], 'all'), ... - max(meanExperimentalForce, [], 'all')]); +maxForce = max(meanModelForce,[], 'all'); numWindows = ceil(sqrt(numel(muscleNames))); figure(Name = "Passive Force Curves", ... Units='normalized', ... Position=[0.1 0.1 0.8 0.8]) -time = 1:1:size(meanExperimentalForce,1); +time = 1:1:size(meanModelForce,1); for i = 1:numel(muscleNames) subplot(numWindows, numWindows, i) hold on - plotMeanAndStd(meanExperimentalForce(:,i), stdExperimentalForce(:,i), ... - time, 'k-') - plotMeanAndStd(meanModelForce(:,i), stdModelForce(:,i), time, 'r-'); + plotMeanAndStd(meanModelForce(:,i), stdModelForce(:,i), time, 'b-'); hold off set(gca, fontsize=11) axis([1 numel(time) 0 maxForce]) title(muscleNames(i), FontSize=12); - if i == 1 - legend("Experimental", "Model") - end if mod(i,numWindows) == 1 ylabel("Magnitude") end diff --git a/src/MuscleTendonPersonalization/Saving/getMtpResultsToSave.m b/src/MuscleTendonPersonalization/Saving/getMtpResultsToSave.m index 557c58685..b5c7b1e36 100644 --- a/src/MuscleTendonPersonalization/Saving/getMtpResultsToSave.m +++ b/src/MuscleTendonPersonalization/Saving/getMtpResultsToSave.m @@ -60,10 +60,8 @@ resultsSynx.muscleExcitations = resultsSynx.muscleExcitations(:, :, ... mtpInputs.numPaddingFrames + 1 : end - mtpInputs.numPaddingFrames); finalValues.synergyWeights(mtpInputs.numberOfExtrapolationWeights + 1 : end) = 0; - resultsSynxNoResiduals = calcMtpSynXModeledValues(finalValues, mtpInputs, struct()); resultsStruct = struct("results", results, ... - "resultsSynx", resultsSynx, ... - "resultsSynxNoResiduals", resultsSynxNoResiduals); + "resultsSynx", resultsSynx); else resultsStruct = struct("results", results); end diff --git a/src/MuscleTendonPersonalization/Saving/saveMtpActivationAndExcitationData.m b/src/MuscleTendonPersonalization/Saving/saveMtpActivationAndExcitationData.m index b0ca01239..84f0dbdc0 100644 --- a/src/MuscleTendonPersonalization/Saving/saveMtpActivationAndExcitationData.m +++ b/src/MuscleTendonPersonalization/Saving/saveMtpActivationAndExcitationData.m @@ -50,16 +50,6 @@ function saveMtpActivationAndExcitationData(mtpInputs, resultsStruct, ... fullfile(resultsDirectory, "muscleActivationsSynx"), ... "_muscleActivationsSynx.sto") - writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... - resultsStruct.resultsSynxNoResiduals.muscleExcitations, ... - fullfile(resultsDirectory, "muscleExcitationsSynxNoResiduals"), ... - "_muscleExcitationsSynxNoResiduals.sto") - - writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... - resultsStruct.resultsSynxNoResiduals.muscleActivations, ... - fullfile(resultsDirectory, "muscleActivationsSynxNoResiduals"), ... - "_muscleActivationsSynxNoResiduals.sto") - writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... resultsStruct.resultsSynx.muscleActivations, ... fullfile(resultsDirectory, "..", "muscleActivations"), ... diff --git a/src/MuscleTendonPersonalization/Saving/saveMtpJointMomentData.m b/src/MuscleTendonPersonalization/Saving/saveMtpJointMomentData.m index e05ed2dfa..97e1525b0 100644 --- a/src/MuscleTendonPersonalization/Saving/saveMtpJointMomentData.m +++ b/src/MuscleTendonPersonalization/Saving/saveMtpJointMomentData.m @@ -47,11 +47,6 @@ function saveMtpJointMomentData(mtpInputs, resultsStruct, resultsDirectory) fullfile(resultsDirectory, "\modelJointMomentsSynx"), ... "_modelJointMomentsSynx.sto") - writeMtpDataToSto(mtpInputs.coordinateNames, mtpInputs.prefixes, ... - resultsStruct.resultsSynxNoResiduals.muscleJointMoments, ... - fullfile(resultsDirectory, "\modelJointMomentsSynxNoResiduals"), ... - "_modelJointMomentsSynxNoResiduals.sto") - writeMtpDataToSto(mtpInputs.coordinateNames, mtpInputs.prefixes, ... resultsStruct.resultsSynx.muscleJointMoments, ... fullfile(resultsDirectory, "..", "modelMoments"), ... diff --git a/src/MuscleTendonPersonalization/Saving/saveMtpPassiveForceData.m b/src/MuscleTendonPersonalization/Saving/saveMtpPassiveForceData.m index df616a083..c7b025d4b 100644 --- a/src/MuscleTendonPersonalization/Saving/saveMtpPassiveForceData.m +++ b/src/MuscleTendonPersonalization/Saving/saveMtpPassiveForceData.m @@ -30,20 +30,9 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function saveMtpPassiveForceData(mtpInputs, modeledValues, resultsStruct, ... +function saveMtpPassiveForceData(mtpInputs, resultsStruct, ... resultsDirectory) -writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... - modeledValues.passiveForce, strcat(resultsDirectory, ... - "\passiveForcesExperimental"), "_passiveForcesExperimental.sto"); writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... resultsStruct.results.passiveForce, strcat(resultsDirectory, ... "\passiveForcesModel"), "_passiveForcesModel.sto"); -if isfield(mtpInputs, "synergyExtrapolation") - writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... - resultsStruct.resultsSynx.passiveForce, strcat(resultsDirectory, ... - "\passiveForcesModelSynx"), "_passiveForcesModelSynx.sto"); - writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... - resultsStruct.resultsSynxNoResiduals.passiveForce, strcat(resultsDirectory, ... - "\passiveForcesModelSynxNoResiduals"), "_passiveForcesModelSynxNoResiduals.sto"); -end end diff --git a/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m b/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m index c04e712e1..bf70b0ccf 100644 --- a/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m +++ b/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m @@ -43,7 +43,7 @@ function saveMuscleTendonPersonalizationResults(mtpInputs, finalValues, ... end if ~isempty(precalInputs) saveMtpPassiveMomentData(precalInputs, modeledValues, analysisDirectory); - saveMtpPassiveForceData(mtpInputs, modeledValues, resultsStruct, analysisDirectory); + saveMtpPassiveForceData(mtpInputs, resultsStruct, analysisDirectory); end saveMtpActivationAndExcitationData(mtpInputs, resultsStruct, analysisDirectory); writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... From c8e159f03802f71864e031625cdb2bd9e4329523 Mon Sep 17 00:00:00 2001 From: RobSalati <142843461+RobSalati@users.noreply.github.com> Date: Mon, 22 Jan 2024 19:49:18 -0600 Subject: [PATCH 278/365] Passive moment plotting fixes in progress --- .../8nhMKvP4YlPvbkX7uIqp17R-qc0d.xml | 6 ++++ .../8nhMKvP4YlPvbkX7uIqp17R-qc0p.xml | 2 ++ ...leTendonLengthInitializationSettingsTree.m | 1 + .../Analysis/plotMtpPassiveForceCurves.m | 2 +- .../Analysis/readMomentArmsForPlotting.m | 6 ++++ .../MuscleTendonPersonalizationTool.m | 9 ++++++ .../Saving/saveMtpPassiveMomentData.m | 29 ++++++++----------- .../saveMuscleTendonPersonalizationResults.m | 8 ++--- src/core/muscle/calcPassiveMuscleMoments.m | 2 +- 9 files changed, 42 insertions(+), 23 deletions(-) create mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/8nhMKvP4YlPvbkX7uIqp17R-qc0d.xml create mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/8nhMKvP4YlPvbkX7uIqp17R-qc0p.xml create mode 100644 src/MuscleTendonPersonalization/Analysis/readMomentArmsForPlotting.m diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/8nhMKvP4YlPvbkX7uIqp17R-qc0d.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/8nhMKvP4YlPvbkX7uIqp17R-qc0d.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/8nhMKvP4YlPvbkX7uIqp17R-qc0d.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/8nhMKvP4YlPvbkX7uIqp17R-qc0p.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/8nhMKvP4YlPvbkX7uIqp17R-qc0p.xml new file mode 100644 index 000000000..af6ddb0d1 --- /dev/null +++ b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/8nhMKvP4YlPvbkX7uIqp17R-qc0p.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/MuscleTendonLengthInitialization/parseMuscleTendonLengthInitializationSettingsTree.m b/src/MuscleTendonLengthInitialization/parseMuscleTendonLengthInitializationSettingsTree.m index b6077ffba..f791dcd84 100644 --- a/src/MuscleTendonLengthInitialization/parseMuscleTendonLengthInitializationSettingsTree.m +++ b/src/MuscleTendonLengthInitialization/parseMuscleTendonLengthInitializationSettingsTree.m @@ -53,6 +53,7 @@ function inputs = getPassiveData(tree, inputs) import org.opensim.modeling.Storage passiveInputDirectory = getFieldByName(tree, 'passive_data_input_directory').Text; +inputs.passiveInputDirectory = passiveInputDirectory; inputs.passiveMomentDataExists = 0; if (~isempty(passiveInputDirectory)) if isfolder(passiveInputDirectory) diff --git a/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveForceCurves.m b/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveForceCurves.m index 7f7936eb2..8a90d2552 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveForceCurves.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveForceCurves.m @@ -53,7 +53,7 @@ function plotMtpPassiveForceCurves(resultsDirectory) axis([1 numel(time) 0 maxForce]) title(muscleNames(i), FontSize=12); if mod(i,numWindows) == 1 - ylabel("Magnitude") + ylabel("Force [N]") end if i>numel(muscleNames)-numWindows xlabel("Time Points") diff --git a/src/MuscleTendonPersonalization/Analysis/readMomentArmsForPlotting.m b/src/MuscleTendonPersonalization/Analysis/readMomentArmsForPlotting.m new file mode 100644 index 000000000..c1d9d8d20 --- /dev/null +++ b/src/MuscleTendonPersonalization/Analysis/readMomentArmsForPlotting.m @@ -0,0 +1,6 @@ +function momentArms = readMomentArmsForPlotting(precalInputs) + momentArmDirectory = fullfile(precalInputs.passiveInputDirectory, "MAData"); + momentCoordinates = precalInputs.coordinateNames; + momentArmFolders = dir(momentArmDirectory); +end + diff --git a/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m b/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m index 9a72cfd22..109bcf539 100644 --- a/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m +++ b/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m @@ -43,6 +43,15 @@ function MuscleTendonPersonalizationTool(settingsFileName) precalInputs = struct('optimizeIsometricMaxForce', false); end optimizedParams = MuscleTendonPersonalization(inputs, params); +save("optimizedParams.mat", 'optimizedParams') +save("precalInputs.mat", "precalInputs") +save("inputs.mat", "inputs") +save("params.mat", "params") +resultsDirectory = "mtpResultsRight3"; +load("optimizedParams.mat") +load("inputs.mat") +load("params.mat") +load("precalInputs.mat") if params.performMuscleTendonLengthInitialization [finalValues, resultsStruct, modeledValues] = ... getMtpResultsToSave(inputs, params, optimizedParams, precalInputs); diff --git a/src/MuscleTendonPersonalization/Saving/saveMtpPassiveMomentData.m b/src/MuscleTendonPersonalization/Saving/saveMtpPassiveMomentData.m index 86d7e01b2..a77a6e033 100644 --- a/src/MuscleTendonPersonalization/Saving/saveMtpPassiveMomentData.m +++ b/src/MuscleTendonPersonalization/Saving/saveMtpPassiveMomentData.m @@ -30,24 +30,19 @@ % ----------------------------------------------------------------------- % function saveMtpPassiveMomentData(precalInputs, modeledValues, resultsDirectory) -modelPassiveMoments = permute(modeledValues.passiveModelMoments, [3 1 2]); -sizeTemp = size(modelPassiveMoments,1); -experimentalPassiveMoments = permute(precalInputs.passiveData.inverseDynamicsMoments, [3 1 2]); -numberOfMoments = size(modelPassiveMoments, 2); +modelPassiveMoments = permute(modeledValues.passiveModelMoments, [3 2 1]); +experimentalPassiveMoments = permute(precalInputs.passiveData.inverseDynamicsMoments, [3 2 1]); +numberOfMoments = size(modelPassiveMoments, 3); dataLength = size(modelPassiveMoments, 1); -columnsWithAllZeros = all(experimentalPassiveMoments == 0, 1); -experimentalPassiveMoments = experimentalPassiveMoments(repmat(~columnsWithAllZeros, ... - size(experimentalPassiveMoments, 1), 1, 1)); -modelPassiveMoments = modelPassiveMoments(repmat(~columnsWithAllZeros, ... - size(modelPassiveMoments, 1), 1, 1)); -experimentalPassiveMoments = ... - reshape(experimentalPassiveMoments, sizeTemp, []); -experimentalPassiveMoments = ... - reshape(experimentalPassiveMoments', 1, numberOfMoments, dataLength); -modelPassiveMoments = ... - reshape(modelPassiveMoments, sizeTemp, []); -modelPassiveMoments = ... - reshape(modelPassiveMoments', 1, numberOfMoments, dataLength); +zeroIndices = experimentalPassiveMoments == 0; +modelPassiveMoments = modelPassiveMoments(~zeroIndices); +modelPassiveMoments = permute( ... + reshape(modelPassiveMoments, [dataLength, numberOfMoments, 1]), ... + [3 2 1]); +experimentalPassiveMoments = experimentalPassiveMoments(~zeroIndices); +experimentalPassiveMoments = permute( ... + reshape(experimentalPassiveMoments, [dataLength, numberOfMoments, 1]), ... + [3 2 1]); writeMtpDataToSto(precalInputs.passivePrefixes, precalInputs.prefixes, ... experimentalPassiveMoments, fullfile(resultsDirectory, ... "passiveJointMomentsExperimental"), "_passiveJointMomentsExperimental.sto") diff --git a/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m b/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m index bf70b0ccf..4928f54f9 100644 --- a/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m +++ b/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m @@ -53,8 +53,8 @@ function saveMuscleTendonPersonalizationResults(mtpInputs, finalValues, ... saveMtpJointMomentData(mtpInputs, resultsStruct, analysisDirectory); saveMtpMuscleModelParameters(mtpInputs, finalValues, ... fullfile(analysisDirectory, "muscleModelParameters")); -model = Model(mtpInputs.model); -muscleNames = getMusclesFromCoordinates(model, mtpInputs.coordinateNames); -writeMuscleTendonPersonalizationOsimxFile(mtpInputs.modelFileName, ... - mtpInputs.osimxFileName, finalValues, muscleNames, resultsDirectory); +% model = Model(mtpInputs.model); +% muscleNames = getMusclesFromCoordinates(model, mtpInputs.coordinateNames); +% writeMuscleTendonPersonalizationOsimxFile(mtpInputs.modelFileName, ... +% mtpInputs.osimxFileName, finalValues, muscleNames, resultsDirectory); end \ No newline at end of file diff --git a/src/core/muscle/calcPassiveMuscleMoments.m b/src/core/muscle/calcPassiveMuscleMoments.m index 137d83d57..c73484987 100644 --- a/src/core/muscle/calcPassiveMuscleMoments.m +++ b/src/core/muscle/calcPassiveMuscleMoments.m @@ -54,7 +54,7 @@ parallelComponentOfPennationAngle), 1); expandedParallelComponentOfPennationAngle(1, 1, :, 1) = ... parallelComponentOfPennationAngle; - +readMomentArmsForPlotting(experimentalData); passiveModelMoments = experimentalData.momentArms .* ... expandedMaxIsometricForce .* expandedPassiveForce .* ... expandedParallelComponentOfPennationAngle; From 0b12d3fcef5cdc92a89d6910d569d7a97d456d15 Mon Sep 17 00:00:00 2001 From: RobSalati Date: Mon, 22 Jan 2024 22:57:53 -0600 Subject: [PATCH 279/365] Passive moments preliminary fix --- .../Analysis/readMomentArmsForPlotting.m | 25 ++++++++++++++ .../MuscleTendonPersonalizationTool.m | 34 +++++++++---------- src/core/muscle/calcPassiveMuscleMoments.m | 4 +-- 3 files changed, 44 insertions(+), 19 deletions(-) diff --git a/src/MuscleTendonPersonalization/Analysis/readMomentArmsForPlotting.m b/src/MuscleTendonPersonalization/Analysis/readMomentArmsForPlotting.m index c1d9d8d20..db81f74c8 100644 --- a/src/MuscleTendonPersonalization/Analysis/readMomentArmsForPlotting.m +++ b/src/MuscleTendonPersonalization/Analysis/readMomentArmsForPlotting.m @@ -1,6 +1,31 @@ function momentArms = readMomentArmsForPlotting(precalInputs) momentArmDirectory = fullfile(precalInputs.passiveInputDirectory, "MAData"); momentCoordinates = precalInputs.coordinateNames; + momentArms = zeros(12, 6, 86, 101); momentArmFolders = dir(momentArmDirectory); + momentArmFolders = momentArmFolders(3:end); + coordinateNames = "MomentArm_"+precalInputs.coordinateNames+".sto"; + for i=1:numel(momentArmFolders) + momentArmFiles = dir(fullfile(momentArmFolders(i).folder, momentArmFolders(i).name)); + momentArmFiles = momentArmFiles(3:end); + k = 1; + for j = 1:numel(momentArmFiles) + if contains(momentArmFiles(j).name, coordinateNames) + data = storageToDoubleMatrix(fullfile(momentArmFiles(j).folder, momentArmFiles(j).name)); + momentArms(i, k, :, :) = data; + k = k + 1; + end + end + end + includedIndices = ismember( ... + precalInputs.passiveMuscleTendonLengthColumnNames, precalInputs.muscleNames); + momentArms = momentArms(:, :, includedIndices, :); + temp = momentArms; + momentArms(:,5,:,:) = temp(:, 1, :, :); + momentArms(:,2,:,:) = temp(:, 2, :, :); + momentArms(:,1,:,:) = temp(:, 3, :, :); + momentArms(:,3,:,:) = temp(:, 4, :, :); + momentArms(:,4,:,:) = temp(:, 5, :, :); + momentArms(:,6,:,:) = temp(:, 6, :, :); end diff --git a/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m b/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m index 109bcf539..db02e69bb 100644 --- a/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m +++ b/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m @@ -30,23 +30,23 @@ % ----------------------------------------------------------------------- % function MuscleTendonPersonalizationTool(settingsFileName) -settingsTree = xml2struct(settingsFileName); -verifyVersion(settingsTree, "MuscleTendonPersonalizationTool"); -[inputs, params, resultsDirectory] = ... - parseMuscleTendonPersonalizationSettingsTree(settingsTree); -precalInputs = parseMuscleTendonLengthInitializationSettingsTree(settingsTree); -if isstruct(precalInputs) - optimizedInitialGuess = MuscleTendonLengthInitialization(precalInputs); - inputs = updateMtpInitialGuess(inputs, precalInputs, ... - optimizedInitialGuess); -else - precalInputs = struct('optimizeIsometricMaxForce', false); -end -optimizedParams = MuscleTendonPersonalization(inputs, params); -save("optimizedParams.mat", 'optimizedParams') -save("precalInputs.mat", "precalInputs") -save("inputs.mat", "inputs") -save("params.mat", "params") +% settingsTree = xml2struct(settingsFileName); +% verifyVersion(settingsTree, "MuscleTendonPersonalizationTool"); +% [inputs, params, resultsDirectory] = ... +% parseMuscleTendonPersonalizationSettingsTree(settingsTree); +% precalInputs = parseMuscleTendonLengthInitializationSettingsTree(settingsTree); +% if isstruct(precalInputs) +% optimizedInitialGuess = MuscleTendonLengthInitialization(precalInputs); +% inputs = updateMtpInitialGuess(inputs, precalInputs, ... +% optimizedInitialGuess); +% else +% precalInputs = struct('optimizeIsometricMaxForce', false); +% end +% optimizedParams = MuscleTendonPersonalization(inputs, params); +% save("optimizedParams.mat", 'optimizedParams') +% save("precalInputs.mat", "precalInputs") +% save("inputs.mat", "inputs") +% save("params.mat", "params") resultsDirectory = "mtpResultsRight3"; load("optimizedParams.mat") load("inputs.mat") diff --git a/src/core/muscle/calcPassiveMuscleMoments.m b/src/core/muscle/calcPassiveMuscleMoments.m index c73484987..a0bf7cd2a 100644 --- a/src/core/muscle/calcPassiveMuscleMoments.m +++ b/src/core/muscle/calcPassiveMuscleMoments.m @@ -54,8 +54,8 @@ parallelComponentOfPennationAngle), 1); expandedParallelComponentOfPennationAngle(1, 1, :, 1) = ... parallelComponentOfPennationAngle; -readMomentArmsForPlotting(experimentalData); -passiveModelMoments = experimentalData.momentArms .* ... +momentArms = readMomentArmsForPlotting(experimentalData); +passiveModelMoments = momentArms .* ... expandedMaxIsometricForce .* expandedPassiveForce .* ... expandedParallelComponentOfPennationAngle; From 27d0e69ad4144fa9dfe74e4ddeecdddda83f8696 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Tue, 23 Jan 2024 14:39:59 -0600 Subject: [PATCH 280/365] fix small bugs in Treatment Optimization --- .../DesignOptimization/DesignOptimizationTool.m | 2 +- .../VerificationOptimization/VerificationOptimizationTool.m | 2 +- .../gpops/computeGpopsContinuousFunction.m | 3 +++ .../gpops/computeGpopsEndpointFunction.m | 6 +++++- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/TreatmentOptimization/DesignOptimization/DesignOptimizationTool.m b/src/TreatmentOptimization/DesignOptimization/DesignOptimizationTool.m index dc8e467e8..c842d73e4 100644 --- a/src/TreatmentOptimization/DesignOptimization/DesignOptimizationTool.m +++ b/src/TreatmentOptimization/DesignOptimization/DesignOptimizationTool.m @@ -37,7 +37,7 @@ function DesignOptimizationTool(settingsFileName) inputs = setupMuscleSynergies(inputs); inputs = setupTorqueControls(inputs); inputs = makeTreatmentOptimizationInputs(inputs, params); -outputs = solveOptimalControlProblem(inputs, params); +[inputs, outputs] = solveOptimalControlProblem(inputs, params); reportTreatmentOptimizationResults(outputs, inputs); saveDesignOptimizationResults(outputs, inputs); end diff --git a/src/TreatmentOptimization/VerificationOptimization/VerificationOptimizationTool.m b/src/TreatmentOptimization/VerificationOptimization/VerificationOptimizationTool.m index aa86cc9c9..68ab76b8e 100644 --- a/src/TreatmentOptimization/VerificationOptimization/VerificationOptimizationTool.m +++ b/src/TreatmentOptimization/VerificationOptimization/VerificationOptimizationTool.m @@ -37,7 +37,7 @@ function VerificationOptimizationTool(settingsFileName) inputs = setupMuscleSynergies(inputs); inputs = setupTorqueControls(inputs); inputs = makeTreatmentOptimizationInputs(inputs, params); -outputs = solveOptimalControlProblem(inputs, params); +[inputs, outputs] = solveOptimalControlProblem(inputs, params); reportTreatmentOptimizationResults(outputs, inputs); saveVerificationOptimizationResults(outputs, inputs); end diff --git a/src/TreatmentOptimization/gpops/computeGpopsContinuousFunction.m b/src/TreatmentOptimization/gpops/computeGpopsContinuousFunction.m index 3b042a581..f8b0cf263 100644 --- a/src/TreatmentOptimization/gpops/computeGpopsContinuousFunction.m +++ b/src/TreatmentOptimization/gpops/computeGpopsContinuousFunction.m @@ -48,4 +48,7 @@ modeledValues.path, setup.auxdata.maxPath, setup.auxdata.minPath); end modeledValues.integrand = calcGpopsIntegrand(values, modeledValues, setup.auxdata); +if isempty(modeledValues.integrand) + modeledValues = rmfield(modeledValues, "integrand"); +end end diff --git a/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m b/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m index c0fae8127..0f5f9cfbd 100644 --- a/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m +++ b/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m @@ -70,7 +70,11 @@ discreteObjective = 0; end -continuousObjective = sum(setup.phase.integral) / length(setup.phase.integral); +if isfield(setup.phase, "integral") && any(isnan(setup.phase.integral)) + continuousObjective = sum(setup.phase.integral) / length(setup.phase.integral); +else + continuousObjective = 0; +end output.objective = continuousObjective + discreteObjective; end From 737f12ef4608e2af4f829d9c939f8fd4343bffc8 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Tue, 23 Jan 2024 20:20:38 -0600 Subject: [PATCH 281/365] Fix saving updated max isometric force in MTP --- src/MuscleTendonPersonalization/Saving/getMtpResultsToSave.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/MuscleTendonPersonalization/Saving/getMtpResultsToSave.m b/src/MuscleTendonPersonalization/Saving/getMtpResultsToSave.m index 557c58685..1acddd3ea 100644 --- a/src/MuscleTendonPersonalization/Saving/getMtpResultsToSave.m +++ b/src/MuscleTendonPersonalization/Saving/getMtpResultsToSave.m @@ -36,6 +36,7 @@ modeledValues = []; precalInputs = []; else + updatedMaxIsometricForce = precalInputs.optimizeIsometricMaxForce; tempValues.optimalFiberLengthScaleFactors = ... mtpInputs.optimalFiberLength ./ precalInputs.optimalFiberLength; tempValues.tendonSlackLengthScaleFactors = ... @@ -43,7 +44,7 @@ precalInputs.maxIsometricForce = mtpInputs.maxIsometricForce; precalInputs.optimizeIsometricMaxForce = 0; modeledValues = calcMuscleTendonLengthInitializationModeledValues(tempValues, precalInputs); - if precalInputs.optimizeIsometricMaxForce + if updatedMaxIsometricForce finalValues.maxIsometricForce = mtpInputs.maxIsometricForce; end end From a7c099fad123e6670a4833420c83034be413f4e1 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Wed, 24 Jan 2024 01:37:01 -0600 Subject: [PATCH 282/365] fix bug with endpoint function --- src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m b/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m index 0f5f9cfbd..3e0611f7f 100644 --- a/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m +++ b/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m @@ -70,7 +70,7 @@ discreteObjective = 0; end -if isfield(setup.phase, "integral") && any(isnan(setup.phase.integral)) +if isfield(setup.phase, "integral") && ~any(isnan(setup.phase.integral)) continuousObjective = sum(setup.phase.integral) / length(setup.phase.integral); else continuousObjective = 0; From 5ab5ac6e220dfc457ab2eb83bfc6ec377f43425e Mon Sep 17 00:00:00 2001 From: RobSalati Date: Wed, 24 Jan 2024 22:35:41 -0600 Subject: [PATCH 283/365] Revert "Passive moments preliminary fix" This reverts commit 0b12d3fcef5cdc92a89d6910d569d7a97d456d15. --- .../Analysis/readMomentArmsForPlotting.m | 25 -------------- .../MuscleTendonPersonalizationTool.m | 34 +++++++++---------- src/core/muscle/calcPassiveMuscleMoments.m | 4 +-- 3 files changed, 19 insertions(+), 44 deletions(-) diff --git a/src/MuscleTendonPersonalization/Analysis/readMomentArmsForPlotting.m b/src/MuscleTendonPersonalization/Analysis/readMomentArmsForPlotting.m index db81f74c8..c1d9d8d20 100644 --- a/src/MuscleTendonPersonalization/Analysis/readMomentArmsForPlotting.m +++ b/src/MuscleTendonPersonalization/Analysis/readMomentArmsForPlotting.m @@ -1,31 +1,6 @@ function momentArms = readMomentArmsForPlotting(precalInputs) momentArmDirectory = fullfile(precalInputs.passiveInputDirectory, "MAData"); momentCoordinates = precalInputs.coordinateNames; - momentArms = zeros(12, 6, 86, 101); momentArmFolders = dir(momentArmDirectory); - momentArmFolders = momentArmFolders(3:end); - coordinateNames = "MomentArm_"+precalInputs.coordinateNames+".sto"; - for i=1:numel(momentArmFolders) - momentArmFiles = dir(fullfile(momentArmFolders(i).folder, momentArmFolders(i).name)); - momentArmFiles = momentArmFiles(3:end); - k = 1; - for j = 1:numel(momentArmFiles) - if contains(momentArmFiles(j).name, coordinateNames) - data = storageToDoubleMatrix(fullfile(momentArmFiles(j).folder, momentArmFiles(j).name)); - momentArms(i, k, :, :) = data; - k = k + 1; - end - end - end - includedIndices = ismember( ... - precalInputs.passiveMuscleTendonLengthColumnNames, precalInputs.muscleNames); - momentArms = momentArms(:, :, includedIndices, :); - temp = momentArms; - momentArms(:,5,:,:) = temp(:, 1, :, :); - momentArms(:,2,:,:) = temp(:, 2, :, :); - momentArms(:,1,:,:) = temp(:, 3, :, :); - momentArms(:,3,:,:) = temp(:, 4, :, :); - momentArms(:,4,:,:) = temp(:, 5, :, :); - momentArms(:,6,:,:) = temp(:, 6, :, :); end diff --git a/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m b/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m index db02e69bb..109bcf539 100644 --- a/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m +++ b/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m @@ -30,23 +30,23 @@ % ----------------------------------------------------------------------- % function MuscleTendonPersonalizationTool(settingsFileName) -% settingsTree = xml2struct(settingsFileName); -% verifyVersion(settingsTree, "MuscleTendonPersonalizationTool"); -% [inputs, params, resultsDirectory] = ... -% parseMuscleTendonPersonalizationSettingsTree(settingsTree); -% precalInputs = parseMuscleTendonLengthInitializationSettingsTree(settingsTree); -% if isstruct(precalInputs) -% optimizedInitialGuess = MuscleTendonLengthInitialization(precalInputs); -% inputs = updateMtpInitialGuess(inputs, precalInputs, ... -% optimizedInitialGuess); -% else -% precalInputs = struct('optimizeIsometricMaxForce', false); -% end -% optimizedParams = MuscleTendonPersonalization(inputs, params); -% save("optimizedParams.mat", 'optimizedParams') -% save("precalInputs.mat", "precalInputs") -% save("inputs.mat", "inputs") -% save("params.mat", "params") +settingsTree = xml2struct(settingsFileName); +verifyVersion(settingsTree, "MuscleTendonPersonalizationTool"); +[inputs, params, resultsDirectory] = ... + parseMuscleTendonPersonalizationSettingsTree(settingsTree); +precalInputs = parseMuscleTendonLengthInitializationSettingsTree(settingsTree); +if isstruct(precalInputs) + optimizedInitialGuess = MuscleTendonLengthInitialization(precalInputs); + inputs = updateMtpInitialGuess(inputs, precalInputs, ... + optimizedInitialGuess); +else + precalInputs = struct('optimizeIsometricMaxForce', false); +end +optimizedParams = MuscleTendonPersonalization(inputs, params); +save("optimizedParams.mat", 'optimizedParams') +save("precalInputs.mat", "precalInputs") +save("inputs.mat", "inputs") +save("params.mat", "params") resultsDirectory = "mtpResultsRight3"; load("optimizedParams.mat") load("inputs.mat") diff --git a/src/core/muscle/calcPassiveMuscleMoments.m b/src/core/muscle/calcPassiveMuscleMoments.m index a0bf7cd2a..c73484987 100644 --- a/src/core/muscle/calcPassiveMuscleMoments.m +++ b/src/core/muscle/calcPassiveMuscleMoments.m @@ -54,8 +54,8 @@ parallelComponentOfPennationAngle), 1); expandedParallelComponentOfPennationAngle(1, 1, :, 1) = ... parallelComponentOfPennationAngle; -momentArms = readMomentArmsForPlotting(experimentalData); -passiveModelMoments = momentArms .* ... +readMomentArmsForPlotting(experimentalData); +passiveModelMoments = experimentalData.momentArms .* ... expandedMaxIsometricForce .* expandedPassiveForce .* ... expandedParallelComponentOfPennationAngle; From d4b99f561a97833deb5432ee6dbadd2393c6b0e3 Mon Sep 17 00:00:00 2001 From: RobSalati Date: Wed, 24 Jan 2024 22:35:45 -0600 Subject: [PATCH 284/365] Revert "Passive moment plotting fixes in progress" This reverts commit c8e159f03802f71864e031625cdb2bd9e4329523. --- .../8nhMKvP4YlPvbkX7uIqp17R-qc0d.xml | 6 ---- .../8nhMKvP4YlPvbkX7uIqp17R-qc0p.xml | 2 -- ...leTendonLengthInitializationSettingsTree.m | 1 - .../Analysis/plotMtpPassiveForceCurves.m | 2 +- .../Analysis/readMomentArmsForPlotting.m | 6 ---- .../MuscleTendonPersonalizationTool.m | 9 ------ .../Saving/saveMtpPassiveMomentData.m | 29 +++++++++++-------- .../saveMuscleTendonPersonalizationResults.m | 8 ++--- src/core/muscle/calcPassiveMuscleMoments.m | 2 +- 9 files changed, 23 insertions(+), 42 deletions(-) delete mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/8nhMKvP4YlPvbkX7uIqp17R-qc0d.xml delete mode 100644 resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/8nhMKvP4YlPvbkX7uIqp17R-qc0p.xml delete mode 100644 src/MuscleTendonPersonalization/Analysis/readMomentArmsForPlotting.m diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/8nhMKvP4YlPvbkX7uIqp17R-qc0d.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/8nhMKvP4YlPvbkX7uIqp17R-qc0d.xml deleted file mode 100644 index 7a6326b99..000000000 --- a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/8nhMKvP4YlPvbkX7uIqp17R-qc0d.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/8nhMKvP4YlPvbkX7uIqp17R-qc0p.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/8nhMKvP4YlPvbkX7uIqp17R-qc0p.xml deleted file mode 100644 index af6ddb0d1..000000000 --- a/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/8nhMKvP4YlPvbkX7uIqp17R-qc0p.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/src/MuscleTendonLengthInitialization/parseMuscleTendonLengthInitializationSettingsTree.m b/src/MuscleTendonLengthInitialization/parseMuscleTendonLengthInitializationSettingsTree.m index f791dcd84..b6077ffba 100644 --- a/src/MuscleTendonLengthInitialization/parseMuscleTendonLengthInitializationSettingsTree.m +++ b/src/MuscleTendonLengthInitialization/parseMuscleTendonLengthInitializationSettingsTree.m @@ -53,7 +53,6 @@ function inputs = getPassiveData(tree, inputs) import org.opensim.modeling.Storage passiveInputDirectory = getFieldByName(tree, 'passive_data_input_directory').Text; -inputs.passiveInputDirectory = passiveInputDirectory; inputs.passiveMomentDataExists = 0; if (~isempty(passiveInputDirectory)) if isfolder(passiveInputDirectory) diff --git a/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveForceCurves.m b/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveForceCurves.m index 8a90d2552..7f7936eb2 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveForceCurves.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveForceCurves.m @@ -53,7 +53,7 @@ function plotMtpPassiveForceCurves(resultsDirectory) axis([1 numel(time) 0 maxForce]) title(muscleNames(i), FontSize=12); if mod(i,numWindows) == 1 - ylabel("Force [N]") + ylabel("Magnitude") end if i>numel(muscleNames)-numWindows xlabel("Time Points") diff --git a/src/MuscleTendonPersonalization/Analysis/readMomentArmsForPlotting.m b/src/MuscleTendonPersonalization/Analysis/readMomentArmsForPlotting.m deleted file mode 100644 index c1d9d8d20..000000000 --- a/src/MuscleTendonPersonalization/Analysis/readMomentArmsForPlotting.m +++ /dev/null @@ -1,6 +0,0 @@ -function momentArms = readMomentArmsForPlotting(precalInputs) - momentArmDirectory = fullfile(precalInputs.passiveInputDirectory, "MAData"); - momentCoordinates = precalInputs.coordinateNames; - momentArmFolders = dir(momentArmDirectory); -end - diff --git a/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m b/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m index 109bcf539..9a72cfd22 100644 --- a/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m +++ b/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m @@ -43,15 +43,6 @@ function MuscleTendonPersonalizationTool(settingsFileName) precalInputs = struct('optimizeIsometricMaxForce', false); end optimizedParams = MuscleTendonPersonalization(inputs, params); -save("optimizedParams.mat", 'optimizedParams') -save("precalInputs.mat", "precalInputs") -save("inputs.mat", "inputs") -save("params.mat", "params") -resultsDirectory = "mtpResultsRight3"; -load("optimizedParams.mat") -load("inputs.mat") -load("params.mat") -load("precalInputs.mat") if params.performMuscleTendonLengthInitialization [finalValues, resultsStruct, modeledValues] = ... getMtpResultsToSave(inputs, params, optimizedParams, precalInputs); diff --git a/src/MuscleTendonPersonalization/Saving/saveMtpPassiveMomentData.m b/src/MuscleTendonPersonalization/Saving/saveMtpPassiveMomentData.m index a77a6e033..86d7e01b2 100644 --- a/src/MuscleTendonPersonalization/Saving/saveMtpPassiveMomentData.m +++ b/src/MuscleTendonPersonalization/Saving/saveMtpPassiveMomentData.m @@ -30,19 +30,24 @@ % ----------------------------------------------------------------------- % function saveMtpPassiveMomentData(precalInputs, modeledValues, resultsDirectory) -modelPassiveMoments = permute(modeledValues.passiveModelMoments, [3 2 1]); -experimentalPassiveMoments = permute(precalInputs.passiveData.inverseDynamicsMoments, [3 2 1]); -numberOfMoments = size(modelPassiveMoments, 3); +modelPassiveMoments = permute(modeledValues.passiveModelMoments, [3 1 2]); +sizeTemp = size(modelPassiveMoments,1); +experimentalPassiveMoments = permute(precalInputs.passiveData.inverseDynamicsMoments, [3 1 2]); +numberOfMoments = size(modelPassiveMoments, 2); dataLength = size(modelPassiveMoments, 1); -zeroIndices = experimentalPassiveMoments == 0; -modelPassiveMoments = modelPassiveMoments(~zeroIndices); -modelPassiveMoments = permute( ... - reshape(modelPassiveMoments, [dataLength, numberOfMoments, 1]), ... - [3 2 1]); -experimentalPassiveMoments = experimentalPassiveMoments(~zeroIndices); -experimentalPassiveMoments = permute( ... - reshape(experimentalPassiveMoments, [dataLength, numberOfMoments, 1]), ... - [3 2 1]); +columnsWithAllZeros = all(experimentalPassiveMoments == 0, 1); +experimentalPassiveMoments = experimentalPassiveMoments(repmat(~columnsWithAllZeros, ... + size(experimentalPassiveMoments, 1), 1, 1)); +modelPassiveMoments = modelPassiveMoments(repmat(~columnsWithAllZeros, ... + size(modelPassiveMoments, 1), 1, 1)); +experimentalPassiveMoments = ... + reshape(experimentalPassiveMoments, sizeTemp, []); +experimentalPassiveMoments = ... + reshape(experimentalPassiveMoments', 1, numberOfMoments, dataLength); +modelPassiveMoments = ... + reshape(modelPassiveMoments, sizeTemp, []); +modelPassiveMoments = ... + reshape(modelPassiveMoments', 1, numberOfMoments, dataLength); writeMtpDataToSto(precalInputs.passivePrefixes, precalInputs.prefixes, ... experimentalPassiveMoments, fullfile(resultsDirectory, ... "passiveJointMomentsExperimental"), "_passiveJointMomentsExperimental.sto") diff --git a/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m b/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m index 4928f54f9..bf70b0ccf 100644 --- a/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m +++ b/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m @@ -53,8 +53,8 @@ function saveMuscleTendonPersonalizationResults(mtpInputs, finalValues, ... saveMtpJointMomentData(mtpInputs, resultsStruct, analysisDirectory); saveMtpMuscleModelParameters(mtpInputs, finalValues, ... fullfile(analysisDirectory, "muscleModelParameters")); -% model = Model(mtpInputs.model); -% muscleNames = getMusclesFromCoordinates(model, mtpInputs.coordinateNames); -% writeMuscleTendonPersonalizationOsimxFile(mtpInputs.modelFileName, ... -% mtpInputs.osimxFileName, finalValues, muscleNames, resultsDirectory); +model = Model(mtpInputs.model); +muscleNames = getMusclesFromCoordinates(model, mtpInputs.coordinateNames); +writeMuscleTendonPersonalizationOsimxFile(mtpInputs.modelFileName, ... + mtpInputs.osimxFileName, finalValues, muscleNames, resultsDirectory); end \ No newline at end of file diff --git a/src/core/muscle/calcPassiveMuscleMoments.m b/src/core/muscle/calcPassiveMuscleMoments.m index c73484987..137d83d57 100644 --- a/src/core/muscle/calcPassiveMuscleMoments.m +++ b/src/core/muscle/calcPassiveMuscleMoments.m @@ -54,7 +54,7 @@ parallelComponentOfPennationAngle), 1); expandedParallelComponentOfPennationAngle(1, 1, :, 1) = ... parallelComponentOfPennationAngle; -readMomentArmsForPlotting(experimentalData); + passiveModelMoments = experimentalData.momentArms .* ... expandedMaxIsometricForce .* expandedPassiveForce .* ... expandedParallelComponentOfPennationAngle; From 7497b9bd43d3130d4a58caf16ef2d6c4d616204e Mon Sep 17 00:00:00 2001 From: RobSalati Date: Thu, 25 Jan 2024 21:50:31 -0600 Subject: [PATCH 285/365] Edited parsing to only select relevant passive moment arms Parsing now takes the passive moment arm column names and cross checks them with the thelen column names. It indexes moment arms to only be about the 6 axes in the thelen dataset. --- ...leTendonLengthInitializationSettingsTree.m | 8 +++- .../MuscleTendonPersonalizationTool.m | 38 ++++++++++--------- src/core/muscle/calcPassiveMuscleMoments.m | 14 +++++-- 3 files changed, 37 insertions(+), 23 deletions(-) diff --git a/src/MuscleTendonLengthInitialization/parseMuscleTendonLengthInitializationSettingsTree.m b/src/MuscleTendonLengthInitialization/parseMuscleTendonLengthInitializationSettingsTree.m index f791dcd84..9841642bf 100644 --- a/src/MuscleTendonLengthInitialization/parseMuscleTendonLengthInitializationSettingsTree.m +++ b/src/MuscleTendonLengthInitialization/parseMuscleTendonLengthInitializationSettingsTree.m @@ -78,13 +78,17 @@ length(inputs.muscleNames)) throw(MException('', 'Muscle names in passive data do not match muscle names from coordinates.')) end - inputs.passiveMomentArms = ... + [inputs.passiveMomentArms, inputs.passiveMomentArmCoordinates] = ... parseMomentArms(passiveDirectories, inputs.model); includedIndices = ismember( ... inputs.passiveMuscleTendonLengthColumnNames, inputs.muscleNames); inputs.passiveMuscleTendonLengthColumnNames = inputs.passiveMuscleTendonLengthColumnNames(includedIndices); + [~, ~, momentCoordinateIndices] = intersect(inputs.coordinateNames, ... + inputs.passiveMomentArmCoordinates, 'stable'); inputs.passiveMomentArms = inputs.passiveMomentArms(:, :, includedIndices, :); - + + inputs.passiveMomentArms = inputs.passiveMomentArms(:, momentCoordinateIndices, :, :); + inputs.passiveMuscleTendonLength = inputs.passiveMuscleTendonLength(:, includedIndices, :); inputs.passiveMomentDataExists = 1; end diff --git a/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m b/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m index db02e69bb..87ff26fd7 100644 --- a/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m +++ b/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m @@ -30,28 +30,30 @@ % ----------------------------------------------------------------------- % function MuscleTendonPersonalizationTool(settingsFileName) -% settingsTree = xml2struct(settingsFileName); -% verifyVersion(settingsTree, "MuscleTendonPersonalizationTool"); -% [inputs, params, resultsDirectory] = ... -% parseMuscleTendonPersonalizationSettingsTree(settingsTree); -% precalInputs = parseMuscleTendonLengthInitializationSettingsTree(settingsTree); -% if isstruct(precalInputs) -% optimizedInitialGuess = MuscleTendonLengthInitialization(precalInputs); -% inputs = updateMtpInitialGuess(inputs, precalInputs, ... -% optimizedInitialGuess); -% else -% precalInputs = struct('optimizeIsometricMaxForce', false); -% end -% optimizedParams = MuscleTendonPersonalization(inputs, params); +settingsTree = xml2struct(settingsFileName); +verifyVersion(settingsTree, "MuscleTendonPersonalizationTool"); +[inputs, params, resultsDirectory] = ... + parseMuscleTendonPersonalizationSettingsTree(settingsTree); +precalInputs = parseMuscleTendonLengthInitializationSettingsTree(settingsTree); +precalInputs2 = precalInputs; +save("inputsCopy.mat", "precalInputs2") +if isstruct(precalInputs) + optimizedInitialGuess = MuscleTendonLengthInitialization(precalInputs); + inputs = updateMtpInitialGuess(inputs, precalInputs, ... + optimizedInitialGuess); +else + precalInputs = struct('optimizeIsometricMaxForce', false); +end +optimizedParams = MuscleTendonPersonalization(inputs, params); % save("optimizedParams.mat", 'optimizedParams') % save("precalInputs.mat", "precalInputs") % save("inputs.mat", "inputs") % save("params.mat", "params") -resultsDirectory = "mtpResultsRight3"; -load("optimizedParams.mat") -load("inputs.mat") -load("params.mat") -load("precalInputs.mat") +% resultsDirectory = "mtpResultsRight3"; +% load("optimizedParams.mat") +% load("inputs.mat") +% load("params.mat") +% load("precalInputs.mat") if params.performMuscleTendonLengthInitialization [finalValues, resultsStruct, modeledValues] = ... getMtpResultsToSave(inputs, params, optimizedParams, precalInputs); diff --git a/src/core/muscle/calcPassiveMuscleMoments.m b/src/core/muscle/calcPassiveMuscleMoments.m index a0bf7cd2a..1ea0f7d2b 100644 --- a/src/core/muscle/calcPassiveMuscleMoments.m +++ b/src/core/muscle/calcPassiveMuscleMoments.m @@ -54,10 +54,18 @@ parallelComponentOfPennationAngle), 1); expandedParallelComponentOfPennationAngle(1, 1, :, 1) = ... parallelComponentOfPennationAngle; -momentArms = readMomentArmsForPlotting(experimentalData); -passiveModelMoments = momentArms .* ... + +% momentArms = experimentalData.momentArms(:, indices, :, :); +% temp = momentArms; +% momentArms(:,5,:,:) = temp(:, 1, :, :); +% momentArms(:,2,:,:) = temp(:, 2, :, :); +% momentArms(:,1,:,:) = temp(:, 3, :, :); +% momentArms(:,3,:,:) = temp(:, 4, :, :); +% momentArms(:,4,:,:) = temp(:, 5, :, :); +% momentArms(:,6,:,:) = temp(:, 6, :, :); +passiveModelMoments = experimentalData.momentArms .* ... expandedMaxIsometricForce .* expandedPassiveForce .* ... expandedParallelComponentOfPennationAngle; - passiveModelMoments = permute(sum(passiveModelMoments, 3), [1 2 4 3]); +max(passiveModelMoments, [], 'all') end From 10eb3d98537c2510ddb100062b5cbfae01babd0d Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Fri, 26 Jan 2024 12:48:25 -0600 Subject: [PATCH 286/365] fix load previous results --- .../PathTerms/calcKineticPathConstraint.m | 8 ++++++++ .../gpops/computeGpopsEndpointFunction.m | 2 +- .../saveTreatmentOptimizationResults.m | 14 +++++++------- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/TreatmentOptimization/PathTerms/calcKineticPathConstraint.m b/src/TreatmentOptimization/PathTerms/calcKineticPathConstraint.m index ec12ba959..7e6690776 100644 --- a/src/TreatmentOptimization/PathTerms/calcKineticPathConstraint.m +++ b/src/TreatmentOptimization/PathTerms/calcKineticPathConstraint.m @@ -35,8 +35,16 @@ loadName)); synergyIndex = find(strcmp(strcat(inputs.surrogateModelCoordinateNames, ... '_moment'), loadName)); +if isempty(synergyIndex) + synergyIndex = find(strcmp(strcat(inputs.surrogateModelCoordinateNames, ... + '_force'), loadName)); +end torqueIndex = find(strcmp(strcat(inputs.torqueControllerCoordinateNames, ... '_moment'), loadName)); +if isempty(torqueIndex) + torqueIndex = find(strcmp(strcat(inputs.torqueControllerCoordinateNames, ... + '_force'), loadName)); +end if isempty(synergyIndex) synergyLoad = 0; else diff --git a/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m b/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m index 3e0611f7f..8e4ea260c 100644 --- a/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m +++ b/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m @@ -70,7 +70,7 @@ discreteObjective = 0; end -if isfield(setup.phase, "integral") && ~any(isnan(setup.phase.integral)) +if isfield(setup.phase, "integral") && ~any(isnan(setup.phase.integral)) && ~isempty(setup.phase.integral) continuousObjective = sum(setup.phase.integral) / length(setup.phase.integral); else continuousObjective = 0; diff --git a/src/TreatmentOptimization/saveTreatmentOptimizationResults.m b/src/TreatmentOptimization/saveTreatmentOptimizationResults.m index 728f03faf..7b30d94d7 100644 --- a/src/TreatmentOptimization/saveTreatmentOptimizationResults.m +++ b/src/TreatmentOptimization/saveTreatmentOptimizationResults.m @@ -38,15 +38,15 @@ function saveTreatmentOptimizationResults(solution, inputs, values) saveInverseDynamicsResults(solution, inputs, values, inputs.resultsDirectory); saveGroundReactionResults(solution, inputs, values, inputs.resultsDirectory); -stateLabels = inputs.coordinateNames; -for i = 1 : length(inputs.coordinateNames) - stateLabels{end + 1} = strcat(inputs.coordinateNames{i}, '_u'); +stateLabels = inputs.statesCoordinateNames; +for i = 1 : length(inputs.statesCoordinateNames) + stateLabels{end + 1} = strcat(inputs.statesCoordinateNames{i}, '_u'); end -for i = 1 : length(inputs.coordinateNames) - stateLabels{end + 1} = strcat(inputs.coordinateNames{i}, '_dudt'); +for i = 1 : length(inputs.statesCoordinateNames) + stateLabels{end + 1} = strcat(inputs.statesCoordinateNames{i}, '_dudt'); end -[time, data] = splineToEvenlySpaced(values.time, [values.positions ... - values.velocities values.accelerations]); +[time, data] = splineToEvenlySpaced(values.time, [values.statePositions ... + values.stateVelocities values.stateAccelerations]); writeToSto(stateLabels, time, data, ... fullfile(inputs.resultsDirectory, strcat(inputs.trialName, "_states.sto"))); [time, jerks] = splineToEvenlySpaced(values.time, values.controlJerks); From 0267cc27b8b39d06cf696f42070ce11b64ba1215 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Fri, 26 Jan 2024 18:49:44 -0600 Subject: [PATCH 287/365] fix hindfoot_body error message Co-Authored-By: Spencer Williams <54556034+SpencerTWilliams@users.noreply.github.com> --- .../parseGroundContactPersonalizationSettingsTree.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/GroundContactPersonalization/parseGroundContactPersonalizationSettingsTree.m b/src/GroundContactPersonalization/parseGroundContactPersonalizationSettingsTree.m index 0e446e6bd..9b16483a1 100644 --- a/src/GroundContactPersonalization/parseGroundContactPersonalizationSettingsTree.m +++ b/src/GroundContactPersonalization/parseGroundContactPersonalizationSettingsTree.m @@ -164,11 +164,11 @@ function task = getFootData(tree) task.isLeftFoot = strcmpi('true', ... getFieldByNameOrError(tree, 'is_left_foot').Text); -hindfootBodyName = getFieldByName(tree, "hindfoot_body").Text; +hindfootBodyName = getFieldByName(tree, "hindfoot_body"); if ~hindfootBodyName throw(MException('', " is replaced by in the GCP settings file.")) else - task.hindfootBodyName = hindfootBodyName; + task.hindfootBodyName = hindfootBodyName.Text; end task.markerNames.toe = getFieldByNameOrError(tree, ... 'toe_marker').Text; From 545d18f0b7ba48c0e5809afc501bcd56b4b3dc3b Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Fri, 26 Jan 2024 22:37:09 -0600 Subject: [PATCH 288/365] add output grf and ik to gcp Co-Authored-By: Spencer Williams <54556034+SpencerTWilliams@users.noreply.github.com> Co-Authored-By: Robert Salati <142843461+RobSalati@users.noreply.github.com> --- .../jVYSzuwRibJr8rAuNbPQWyDjlT0d.xml | 2 + .../jVYSzuwRibJr8rAuNbPQWyDjlT0p.xml | 2 + .../m4YW3kyFKhaw2jhfe5pX4koVSF0d.xml | 2 + .../m4YW3kyFKhaw2jhfe5pX4koVSF0p.xml | 2 + .../Ge7ijHD-QOCX1Vh_15TrHFqSGPQd.xml | 6 + .../Ge7ijHD-QOCX1Vh_15TrHFqSGPQp.xml | 2 + .../IGJLBsn5zyrwrJ0Ri0i1Ko7r_xkd.xml | 6 + .../IGJLBsn5zyrwrJ0Ri0i1Ko7r_xkp.xml | 2 + .../IpHzUnQyP59JYR1Z_tIIrX8K2UAd.xml | 6 + .../IpHzUnQyP59JYR1Z_tIIrX8K2UAp.xml | 2 + .../QlTkLLPEJG_45v6SBsylfHJ5nNod.xml | 6 + .../QlTkLLPEJG_45v6SBsylfHJ5nNop.xml | 2 + .../TOFjg06uQu3Zfq7kzjebZhRypikd.xml | 6 + .../TOFjg06uQu3Zfq7kzjebZhRypikp.xml | 2 + .../_SAx-QeBHslskguQ9aXk9RNEjhYd.xml | 2 + .../_SAx-QeBHslskguQ9aXk9RNEjhYp.xml | 2 + .../hH_1Yj64xFVM9hFUXve2TuYwxLcd.xml | 6 + .../hH_1Yj64xFVM9hFUXve2TuYwxLcp.xml | 2 + .../saveGroundContactPersonalizationResults.m | 2 + ...iteCombinedOptimizedGroundReactionsToSto.m | 79 ++++++++++ .../Saving/writeFullBodyKinematicsFromGcp.m | 149 ++++++++++++++++++ ...GroundContactPersonalizationSettingsTree.m | 3 +- src/core/motToTrc/TrcFromMot.m | 53 +++++++ src/core/motToTrc/applyValuesFromStorage.m | 18 +++ src/core/motToTrc/getCoordinateFromName.m | 18 +++ src/core/motToTrc/makeTimeSeriesTableVec3.m | 19 +++ src/core/motToTrc/recordMarkersFromState.m | 15 ++ src/core/motToTrc/setValueFromStorage.m | 30 ++++ 28 files changed, 445 insertions(+), 1 deletion(-) create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/jVYSzuwRibJr8rAuNbPQWyDjlT0d.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/jVYSzuwRibJr8rAuNbPQWyDjlT0p.xml create mode 100644 resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/m4YW3kyFKhaw2jhfe5pX4koVSF0d.xml create mode 100644 resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/m4YW3kyFKhaw2jhfe5pX4koVSF0p.xml create mode 100644 resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/Ge7ijHD-QOCX1Vh_15TrHFqSGPQd.xml create mode 100644 resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/Ge7ijHD-QOCX1Vh_15TrHFqSGPQp.xml create mode 100644 resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/IGJLBsn5zyrwrJ0Ri0i1Ko7r_xkd.xml create mode 100644 resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/IGJLBsn5zyrwrJ0Ri0i1Ko7r_xkp.xml create mode 100644 resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/IpHzUnQyP59JYR1Z_tIIrX8K2UAd.xml create mode 100644 resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/IpHzUnQyP59JYR1Z_tIIrX8K2UAp.xml create mode 100644 resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/QlTkLLPEJG_45v6SBsylfHJ5nNod.xml create mode 100644 resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/QlTkLLPEJG_45v6SBsylfHJ5nNop.xml create mode 100644 resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/TOFjg06uQu3Zfq7kzjebZhRypikd.xml create mode 100644 resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/TOFjg06uQu3Zfq7kzjebZhRypikp.xml create mode 100644 resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/_SAx-QeBHslskguQ9aXk9RNEjhYd.xml create mode 100644 resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/_SAx-QeBHslskguQ9aXk9RNEjhYp.xml create mode 100644 resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/hH_1Yj64xFVM9hFUXve2TuYwxLcd.xml create mode 100644 resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/hH_1Yj64xFVM9hFUXve2TuYwxLcp.xml create mode 100644 src/GroundContactPersonalization/Saving/writeCombinedOptimizedGroundReactionsToSto.m create mode 100644 src/GroundContactPersonalization/Saving/writeFullBodyKinematicsFromGcp.m create mode 100644 src/core/motToTrc/TrcFromMot.m create mode 100644 src/core/motToTrc/applyValuesFromStorage.m create mode 100644 src/core/motToTrc/getCoordinateFromName.m create mode 100644 src/core/motToTrc/makeTimeSeriesTableVec3.m create mode 100644 src/core/motToTrc/recordMarkersFromState.m create mode 100644 src/core/motToTrc/setValueFromStorage.m diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/jVYSzuwRibJr8rAuNbPQWyDjlT0d.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/jVYSzuwRibJr8rAuNbPQWyDjlT0d.xml new file mode 100644 index 000000000..fef16e418 --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/jVYSzuwRibJr8rAuNbPQWyDjlT0d.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/jVYSzuwRibJr8rAuNbPQWyDjlT0p.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/jVYSzuwRibJr8rAuNbPQWyDjlT0p.xml new file mode 100644 index 000000000..59d2600ab --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/jVYSzuwRibJr8rAuNbPQWyDjlT0p.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/m4YW3kyFKhaw2jhfe5pX4koVSF0d.xml b/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/m4YW3kyFKhaw2jhfe5pX4koVSF0d.xml new file mode 100644 index 000000000..a75f7a81b --- /dev/null +++ b/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/m4YW3kyFKhaw2jhfe5pX4koVSF0d.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/m4YW3kyFKhaw2jhfe5pX4koVSF0p.xml b/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/m4YW3kyFKhaw2jhfe5pX4koVSF0p.xml new file mode 100644 index 000000000..65d7325ac --- /dev/null +++ b/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/m4YW3kyFKhaw2jhfe5pX4koVSF0p.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/Ge7ijHD-QOCX1Vh_15TrHFqSGPQd.xml b/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/Ge7ijHD-QOCX1Vh_15TrHFqSGPQd.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/Ge7ijHD-QOCX1Vh_15TrHFqSGPQd.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/Ge7ijHD-QOCX1Vh_15TrHFqSGPQp.xml b/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/Ge7ijHD-QOCX1Vh_15TrHFqSGPQp.xml new file mode 100644 index 000000000..8b56687ad --- /dev/null +++ b/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/Ge7ijHD-QOCX1Vh_15TrHFqSGPQp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/IGJLBsn5zyrwrJ0Ri0i1Ko7r_xkd.xml b/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/IGJLBsn5zyrwrJ0Ri0i1Ko7r_xkd.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/IGJLBsn5zyrwrJ0Ri0i1Ko7r_xkd.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/IGJLBsn5zyrwrJ0Ri0i1Ko7r_xkp.xml b/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/IGJLBsn5zyrwrJ0Ri0i1Ko7r_xkp.xml new file mode 100644 index 000000000..ea1d09fc5 --- /dev/null +++ b/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/IGJLBsn5zyrwrJ0Ri0i1Ko7r_xkp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/IpHzUnQyP59JYR1Z_tIIrX8K2UAd.xml b/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/IpHzUnQyP59JYR1Z_tIIrX8K2UAd.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/IpHzUnQyP59JYR1Z_tIIrX8K2UAd.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/IpHzUnQyP59JYR1Z_tIIrX8K2UAp.xml b/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/IpHzUnQyP59JYR1Z_tIIrX8K2UAp.xml new file mode 100644 index 000000000..f79b9403e --- /dev/null +++ b/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/IpHzUnQyP59JYR1Z_tIIrX8K2UAp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/QlTkLLPEJG_45v6SBsylfHJ5nNod.xml b/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/QlTkLLPEJG_45v6SBsylfHJ5nNod.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/QlTkLLPEJG_45v6SBsylfHJ5nNod.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/QlTkLLPEJG_45v6SBsylfHJ5nNop.xml b/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/QlTkLLPEJG_45v6SBsylfHJ5nNop.xml new file mode 100644 index 000000000..5f1f9e9f6 --- /dev/null +++ b/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/QlTkLLPEJG_45v6SBsylfHJ5nNop.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/TOFjg06uQu3Zfq7kzjebZhRypikd.xml b/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/TOFjg06uQu3Zfq7kzjebZhRypikd.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/TOFjg06uQu3Zfq7kzjebZhRypikd.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/TOFjg06uQu3Zfq7kzjebZhRypikp.xml b/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/TOFjg06uQu3Zfq7kzjebZhRypikp.xml new file mode 100644 index 000000000..7bf2bb044 --- /dev/null +++ b/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/TOFjg06uQu3Zfq7kzjebZhRypikp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/_SAx-QeBHslskguQ9aXk9RNEjhYd.xml b/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/_SAx-QeBHslskguQ9aXk9RNEjhYd.xml new file mode 100644 index 000000000..a75f7a81b --- /dev/null +++ b/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/_SAx-QeBHslskguQ9aXk9RNEjhYd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/_SAx-QeBHslskguQ9aXk9RNEjhYp.xml b/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/_SAx-QeBHslskguQ9aXk9RNEjhYp.xml new file mode 100644 index 000000000..842de6ab3 --- /dev/null +++ b/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/_SAx-QeBHslskguQ9aXk9RNEjhYp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/hH_1Yj64xFVM9hFUXve2TuYwxLcd.xml b/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/hH_1Yj64xFVM9hFUXve2TuYwxLcd.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/hH_1Yj64xFVM9hFUXve2TuYwxLcd.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/hH_1Yj64xFVM9hFUXve2TuYwxLcp.xml b/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/hH_1Yj64xFVM9hFUXve2TuYwxLcp.xml new file mode 100644 index 000000000..bf8a0fe84 --- /dev/null +++ b/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/hH_1Yj64xFVM9hFUXve2TuYwxLcp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/GroundContactPersonalization/Saving/saveGroundContactPersonalizationResults.m b/src/GroundContactPersonalization/Saving/saveGroundContactPersonalizationResults.m index b7007b3a8..2ca0d4b01 100644 --- a/src/GroundContactPersonalization/Saving/saveGroundContactPersonalizationResults.m +++ b/src/GroundContactPersonalization/Saving/saveGroundContactPersonalizationResults.m @@ -39,6 +39,8 @@ function saveGroundContactPersonalizationResults(inputs, params, ... writeReplacedExperimentalGroundReactionsToSto(inputs, ... resultsDirectory, name); writeOptimizedGroundReactionsToSto(inputs, params, resultsDirectory, name); +writeCombinedOptimizedGroundReactionsToSto(inputs, params, resultsDirectory); +writeFullBodyKinematicsFromGcp(inputs, params, resultsDirectory); writeGroundContactPersonalizationOsimxFile(inputs, resultsDirectory, ... osimxFileName); end diff --git a/src/GroundContactPersonalization/Saving/writeCombinedOptimizedGroundReactionsToSto.m b/src/GroundContactPersonalization/Saving/writeCombinedOptimizedGroundReactionsToSto.m new file mode 100644 index 000000000..126cf86c8 --- /dev/null +++ b/src/GroundContactPersonalization/Saving/writeCombinedOptimizedGroundReactionsToSto.m @@ -0,0 +1,79 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% +% +% (struct, string, string) -> (None) +% Write modeled foot kinematics to an OpenSim Storage file. + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Claire V. Hammond, Spencer Williams % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function writeCombinedOptimizedGroundReactionsToSto(inputs, params, ... + resultsDirectory) +for foot = 1:length(inputs.surfaces) + models.("model_" + foot) = Model(inputs.surfaces{foot}.model); +end +[~,name,ext] = fileparts(inputs.grfFileName); +outfile = strcat(name, ext); + +timePoints = inputs.surfaces{1}.time; +data = zeros(length(timePoints), 9 * length(inputs.surfaces)); +columnLabels = string([]); +for foot = 1:length(inputs.surfaces) + if any(size(timePoints) ~= size(inputs.surfaces{foot}.time)) || ... + any(timePoints ~= inputs.surfaces{foot}.time) + return; + end +end +if ~exist(fullfile(resultsDirectory, "GRFData"), "dir") + mkdir(fullfile(resultsDirectory, "GRFData")) +end +for foot = 1:length(inputs.surfaces) + for i = 1:3 + columnLabels(end + 1) = convertCharsToStrings( ... + inputs.surfaces{foot}.forceColumns(i, :)); + end + for i = 1:3 + columnLabels(end + 1) = convertCharsToStrings( ... + inputs.surfaces{foot}.momentColumns(i, :)); + end + for i = 1:3 + columnLabels(end + 1) = convertCharsToStrings( ... + inputs.surfaces{foot}.electricalCenterColumns(i, :)); + end + [modeledJointPositions, modeledJointVelocities] = ... + calcGCPJointKinematics(inputs.surfaces{foot} ... + .experimentalJointPositions, inputs.surfaces{foot} ... + .jointKinematicsBSplines, inputs.surfaces{foot}.bSplineCoefficients); + modeledValues = calcGCPModeledValues(inputs, inputs, ... + modeledJointPositions, modeledJointVelocities, params, ... + length(params.tasks), foot, models); + center = inputs.surfaces{foot}.midfootSuperiorPosition; + center(2, :) = inputs.restingSpringLength; + data(:, (foot - 1) * 9 + 1 : foot * 9) = [modeledValues.anteriorGrf' modeledValues.verticalGrf' ... + modeledValues.lateralGrf' modeledValues.xGrfMoment' ... + modeledValues.yGrfMoment' modeledValues.zGrfMoment' center']; +end +writeToSto(columnLabels, timePoints, data, ... + fullfile(resultsDirectory, "GRFData", outfile)); +end + diff --git a/src/GroundContactPersonalization/Saving/writeFullBodyKinematicsFromGcp.m b/src/GroundContactPersonalization/Saving/writeFullBodyKinematicsFromGcp.m new file mode 100644 index 000000000..65d973e0b --- /dev/null +++ b/src/GroundContactPersonalization/Saving/writeFullBodyKinematicsFromGcp.m @@ -0,0 +1,149 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% Saves a ground contact .osimx model and experimental and modeled +% kinematics and ground reactions for each foot. +% +% (struct, struct, string) -> (None) +% Save Ground Contact Personalization kinematics results. + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Claire V. Hammond, Spencer Williams % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function writeFullBodyKinematicsFromGcp(inputs, params, resultsDirectory) +timePoints = inputs.surfaces{1}.time; +for foot = 1:length(inputs.surfaces) + if any(size(timePoints) ~= size(inputs.surfaces{foot}.time)) || ... + any(timePoints ~= inputs.surfaces{foot}.time) + return; + end +end +for foot = 1:length(inputs.surfaces) + models.("model_" + foot) = Model(inputs.surfaces{foot}.model); +end +footMarkerNames = string([]); +for foot = 1:length(inputs.surfaces) + [modeledJointPositions, modeledJointVelocities] = ... + calcGCPJointKinematics(inputs.surfaces{foot} ... + .experimentalJointPositions, inputs.surfaces{foot} ... + .jointKinematicsBSplines, inputs.surfaces{foot}.bSplineCoefficients); + modeledValues = calcGCPModeledValues(inputs, inputs, ... + modeledJointPositions, modeledJointVelocities, params, ... + length(params.tasks), foot, models); + + % footMarkerPositions = zeros(length(timePoints), 9 * 3 * length(inputs.surfaces)); + footMarkerNames(end + 1) = inputs.surfaces{foot}.markerNames.toe; + footMarkerNames(end + 1) = inputs.surfaces{foot}.markerNames.medial; + footMarkerNames(end + 1) = inputs.surfaces{foot}.markerNames.lateral; + footMarkerNames(end + 1) = inputs.surfaces{foot}.markerNames.heel; + footMarkerNames(end + 1) = inputs.surfaces{foot}.markerNames.midfootSuperior; + + footMarkerData.(footMarkerNames(end - 4)) = modeledValues.markerPositions.toe; + footMarkerData.(footMarkerNames(end - 3)) = modeledValues.markerPositions.medial; + footMarkerData.(footMarkerNames(end - 2)) = modeledValues.markerPositions.lateral; + footMarkerData.(footMarkerNames(end - 1)) = modeledValues.markerPositions.heel; + footMarkerData.(footMarkerNames(end)) = modeledValues.markerPositions.midfootSuperior; + +end + +% make ikdata + +trcFromMotParams.trcFileName = "preGcp.trc"; +trcFromMotParams.dataRate = 1; +TrcFromMot(inputs.bodyModel, inputs.motionFileName, trcFromMotParams) + +% load trc file +timeSeriesTable = TimeSeriesTableVec3(trcFromMotParams.trcFileName); +columnNames = timeSeriesTable.getColumnLabels(); +strings = {}; +for i=0:columnNames.size()-1 + strings{end+1} = columnNames.get(i); +end +markerNames = string(strings); + +% put foot marker data inside trc file +for i = 1:length(footMarkerNames) + column = timeSeriesTable.updDependentColumn(footMarkerNames(i)); + for j = 1:length(inputs.surfaces{1}.time) + temp = footMarkerData.(footMarkerNames(i))(:, j); + column.set(i-1, org.opensim.modeling.Vec3(temp(1), temp(2), temp(3))); + end +end + +for i = 1:length(timePoints) + timeSeriesTable.setIndependentValueAtIndex(i - 1, timePoints(i)); +end + +% save trc file +trcFileName = "postGcp.trc"; +trcFileAdapter = org.opensim.modeling.TRCFileAdapter(); +trcFileAdapter.write(timeSeriesTable, trcFileName); + +import org.opensim.modeling.TimeSeriesTableVec3 +import org.opensim.modeling.SetMarkerWeights +import org.opensim.modeling.MarkerWeight +timeSeriesTable = TimeSeriesTableVec3(trcFileName); +strings = {}; +columnNames = timeSeriesTable.getColumnLabels(); +for i=0:columnNames.size()-1 + strings{end+1} = columnNames.get(i); +end +markerNames = string(strings); +markerWeightSet = SetMarkerWeights(); +for i=1:length(markerNames) + if any(ismember(fieldnames(footMarkerData), markerNames(i))) + markerWeightSet.cloneAndAppend(MarkerWeight(markerNames(i), 100.0)); + else + markerWeightSet.cloneAndAppend(MarkerWeight(markerNames(i), 1.0)); + end +end +try + markersReference = org.opensim.modeling.MarkersReference(trcFileName, markerWeightSet); +catch + markersReference = org.opensim.modeling.MarkersReference(trcFileName); + markersReference.setMarkerWeightSet(markerWeightSet); +end +timeSeriesTable = libpointer; +[model, state] = Model(inputs.bodyModel); +ikSolver = org.opensim.modeling.InverseKinematicsSolver(model, markersReference, ... + org.opensim.modeling.SimTKArrayCoordinateReference()); +ikSolver.setAccuracy(1e-6); + +% make new kinematics *run ik solver* + +kinematicsReporter = org.opensim.modeling.Kinematics(model); +state.setTime(timePoints(1)); +ikSolver.assemble(state); +kinematicsReporter.begin(state); +for i = 1:length(timePoints) + state.setTime(timePoints(i)); + ikSolver.track(state); + kinematicsReporter.step(state, i); +end +[~,name,ext] = fileparts(inputs.motionFileName); +outfile = strcat(name, ext); +if ~exist(fullfile(resultsDirectory, "IKData"), "dir") + mkdir(fullfile(resultsDirectory, "IKData")) +end +kinematicsReporter.getPositionStorage().print( ... + fullfile(resultsDirectory, "IKData", outfile)); +end + diff --git a/src/GroundContactPersonalization/parseGroundContactPersonalizationSettingsTree.m b/src/GroundContactPersonalization/parseGroundContactPersonalizationSettingsTree.m index 9b16483a1..91f1a8763 100644 --- a/src/GroundContactPersonalization/parseGroundContactPersonalizationSettingsTree.m +++ b/src/GroundContactPersonalization/parseGroundContactPersonalizationSettingsTree.m @@ -165,7 +165,7 @@ task.isLeftFoot = strcmpi('true', ... getFieldByNameOrError(tree, 'is_left_foot').Text); hindfootBodyName = getFieldByName(tree, "hindfoot_body"); -if ~hindfootBodyName +if ~isstruct(hindfootBodyName) throw(MException('', " is replaced by in the GCP settings file.")) else task.hindfootBodyName = hindfootBodyName.Text; @@ -248,6 +248,7 @@ function verifyTime(grfTime, ikTime) % Gets cost terms and design variables included in each task. function output = getOptimizationTasks(tree) +output = {}; tasks = getFieldByNameOrError(tree, 'GCPTaskList'); counter = 1; gcpTasks = orderByIndex(tasks.GCPTask); diff --git a/src/core/motToTrc/TrcFromMot.m b/src/core/motToTrc/TrcFromMot.m new file mode 100644 index 000000000..1516311b2 --- /dev/null +++ b/src/core/motToTrc/TrcFromMot.m @@ -0,0 +1,53 @@ +% This function accepts a model and .mot file and writes a .trc file that +% contains the marker data for the given motion. Noise can be added +% as a parameter. +% Parameters include: +% trcFileName - string - output file +% dataRate - number - data rate of mot file +% translationNoise - number - width of gaussian noise for translational +% coordinates +% rotationNoise - number - width of gaussian noise for rotational +% coordinates + +% Copyright RNCL *change later* + +% (Model, string, string) -> (None) +% Writes a .trc file from a Model and .mot file. +function TrcFromMot(inputModel, motFileName, params) +[model, state] = Model(inputModel); +storage = org.opensim.modeling.Storage(motFileName); +dataRate = dataRateOrError(params); +table = makeTimeSeriesTableVec3(model, dataRate); +applyValuesFromStorage(model, state, storage, table, params); +trcFileAdapter = org.opensim.modeling.TRCFileAdapter(); +trcFileAdapter.write(table, trcFileNameOrDefault(params)) +end + +% (struct) -> (number) +% Returns the dataRate if it's a number, otherwise throws an error +function dataRate = dataRateOrError(params) +if(isfield(params, 'dataRate')) + if(isnumeric(params.dataRate)) + dataRate = params.dataRate; + else + throw(MException('dataRate must be a number')) + end +else + throw(MException('dataRate param required')) +end +end + +% (struct) -> (string) +% Returns the trcFileName or default value +function fileName = trcFileNameOrDefault(params) +if(isfield(params, 'trcFileName')) + if(isstring(params.trcFileName) || ischar(params.trcFileName)) + fileName = params.trcFileName; + else + warning('params.trcFileName is not a string, using output.trc') + fileName = 'output.trc'; + end +else + throw(MException('trcFileName param required')) +end +end \ No newline at end of file diff --git a/src/core/motToTrc/applyValuesFromStorage.m b/src/core/motToTrc/applyValuesFromStorage.m new file mode 100644 index 000000000..c17a28ea1 --- /dev/null +++ b/src/core/motToTrc/applyValuesFromStorage.m @@ -0,0 +1,18 @@ +% This function iterates through the storage and applies the coordinate +% values in order. + +% Copyright RCNL *change later* + +% (Model, State, Storage, TimeSeriesTableVec3, number) -> None +function applyValuesFromStorage(model, state, storage, table, params) +import org.opensim.modeling.* +for i=0:storage.getSize()-1 + for j=1:storage.getColumnLabels().size()-1 + setValueFromStorage(model, state, storage, i, j, params) + model.assemble(state); + end + model.assemble(state); + table.appendRow(i/params.dataRate, ... + recordMarkersFromState(model, state)); +end +end \ No newline at end of file diff --git a/src/core/motToTrc/getCoordinateFromName.m b/src/core/motToTrc/getCoordinateFromName.m new file mode 100644 index 000000000..8e45ea156 --- /dev/null +++ b/src/core/motToTrc/getCoordinateFromName.m @@ -0,0 +1,18 @@ +% This function returns a mutable coordinate for changing the position of a +% model. This coordinate can be used in coord.setValue(state, value) + +% Copyright RCNL *change later* + +% (Model, string) -> (Coordinate) +% Returns a coordinate with the given name +function coord = getCoordinateFromName(model, name) + for i=0:model.getJointSet().getSize()-1 + joint = model.getJointSet.get(i); + for j=0:joint.numCoordinates()-1 + if(joint.get_coordinates(j).getName() == name) + coord = joint.upd_coordinates(j); + return + end + end + end +end \ No newline at end of file diff --git a/src/core/motToTrc/makeTimeSeriesTableVec3.m b/src/core/motToTrc/makeTimeSeriesTableVec3.m new file mode 100644 index 000000000..17c0adbb8 --- /dev/null +++ b/src/core/motToTrc/makeTimeSeriesTableVec3.m @@ -0,0 +1,19 @@ +% This function makes a TimeSeriesTableVec3 for a given model and data rate +% for use in a TRCFileAdapter. This function assumes units in meters. + +% Copyright RCNL *change later* + +% (Model, number) -> (TimeSeriesTableVec3) +% Builds a TimeSeriesTableVec3 from a model and dataRate +function table = makeTimeSeriesTableVec3(model, dataRate) +import org.opensim.modeling.* +table = TimeSeriesTableVec3(); +table.addTableMetaDataString("DataRate", num2str(dataRate)) +table.addTableMetaDataString("Units", "m") +labels = StdVectorString(); +for i=0:model.getMarkerSet().getSize()-1 + labels.add(model.getMarkerSet().get(i).getName()) +end +table.setColumnLabels(labels) +end + diff --git a/src/core/motToTrc/recordMarkersFromState.m b/src/core/motToTrc/recordMarkersFromState.m new file mode 100644 index 000000000..63d5b96ea --- /dev/null +++ b/src/core/motToTrc/recordMarkersFromState.m @@ -0,0 +1,15 @@ +% This function returns a RowVectorOfVec3 from a given state including all +% markers in the state. + +% Copyright RCNL *change later* + +% (Model, State) -> (RowVectorOfVec3) +% Return a RowVectorOfVec3 of marker positions for a given state +function row = recordMarkersFromState(model, state) +import org.opensim.modeling.* +row = RowVectorVec3(model.getMarkerSet().getSize()); +for i=0:model.getMarkerSet().getSize()-1 + m = model.getMarkerSet().get(i); + row.set(i,m.getLocationInGround(state)); +end +end \ No newline at end of file diff --git a/src/core/motToTrc/setValueFromStorage.m b/src/core/motToTrc/setValueFromStorage.m new file mode 100644 index 000000000..dec1130e1 --- /dev/null +++ b/src/core/motToTrc/setValueFromStorage.m @@ -0,0 +1,30 @@ +% This function sets the value of the coordinate in the given state from +% the data within the storage object. + +% Copyright RCNL *change later* + +% (Model, State, Storage, number, number) -> None +% Sets value of state coordinates from a Storage object row and column +function setValueFromStorage(model, state, storage, row, column, params) +import org.opensim.modeling.* +dbl = ArrayDouble(); +storage.getDataColumn(column-1, dbl); +coord = getCoordinateFromName(model, storage.getColumnLabels.get(column)); +value = dbl.get(row); +% applyGaussian(value, params, 'rotationNoise')*3.14/180); + +% coord.setValue(state, applyGaussian(value,params, 'translationNoise')); +coord.setValue(state, value) +end + +function output = applyGaussian(value, params, fieldName) +if(isfield(params, fieldName)) + if(isnumeric(params.(fieldName))) + output = value + normrnd(0, params.(fieldName)); + else + output = value; + end +else + output = value; +end +end \ No newline at end of file From 32e530b0c78e19ba4948faabb4830b02ce49895a Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Fri, 26 Jan 2024 22:51:03 -0600 Subject: [PATCH 289/365] fix write to radians Co-Authored-By: Spencer Williams <54556034+SpencerTWilliams@users.noreply.github.com> --- .../Saving/writeFullBodyKinematicsFromGcp.m | 1 + 1 file changed, 1 insertion(+) diff --git a/src/GroundContactPersonalization/Saving/writeFullBodyKinematicsFromGcp.m b/src/GroundContactPersonalization/Saving/writeFullBodyKinematicsFromGcp.m index 65d973e0b..1b1fe4e20 100644 --- a/src/GroundContactPersonalization/Saving/writeFullBodyKinematicsFromGcp.m +++ b/src/GroundContactPersonalization/Saving/writeFullBodyKinematicsFromGcp.m @@ -130,6 +130,7 @@ function writeFullBodyKinematicsFromGcp(inputs, params, resultsDirectory) % make new kinematics *run ik solver* kinematicsReporter = org.opensim.modeling.Kinematics(model); +kinematicsReporter.setInDegrees(false); state.setTime(timePoints(1)); ikSolver.assemble(state); kinematicsReporter.begin(state); From 4cdd0b66c0810f59c3736a0a371baef616a99fcc Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sat, 27 Jan 2024 22:47:54 -0600 Subject: [PATCH 290/365] fix accelerations working --- .../setupGpopsInitialGuess.m | 21 +++++----- .../SetupBounds/makeOptimalControlBounds.m | 40 +++++++++++-------- .../calcDynamicConstraint.m | 3 +- .../gpops/makeGpopsValuesAsStruct.m | 37 ++++++++++++----- .../makeStateDerivatives.m | 6 +-- .../saveTreatmentOptimizationResults.m | 14 +++---- 6 files changed, 75 insertions(+), 46 deletions(-) diff --git a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m index 1bd35d7ee..fae13d943 100644 --- a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m +++ b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m @@ -57,12 +57,15 @@ inputs.experimentalJointVelocities, ... inputs.coordinateNames, ... inputs.statesCoordinateNames); - stateJointAccelerations = subsetDataByCoordinates( ... - inputs.experimentalJointAccelerations, ... - inputs.coordinateNames, ... - inputs.statesCoordinateNames); - guess.phase.state = scaleToBounds([stateJointAngles, ... - stateJointVelocities, stateJointAccelerations], inputs.maxState, ... + % stateJointAccelerations = subsetDataByCoordinates( ... + % inputs.experimentalJointAccelerations, ... + % inputs.coordinateNames, ... + % inputs.statesCoordinateNames); + guess.phase.state = scaleToBounds([ ... + stateJointAngles, ... + stateJointVelocities, ... + % stateJointAccelerations, ... + ], inputs.maxState, ... inputs.minState); guess.phase.time = scaleToBounds(inputs.experimentalTime, inputs.maxTime, ... inputs.minTime); @@ -73,12 +76,12 @@ if isfield(inputs, "initialJerks") controls = inputs.initialJerks; else - stateJointJerks = subsetDataByCoordinates( ... - inputs.experimentalJointJerks, ... + stateJointAccelerations = subsetDataByCoordinates( ... + inputs.experimentalJointAccelerations, ... inputs.coordinateNames, ... inputs.statesCoordinateNames); - controls = stateJointJerks; + controls = stateJointAccelerations; end if strcmp(inputs.controllerType, "synergy") if isfield(inputs, "initialSynergyControls") diff --git a/src/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m b/src/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m index a2f15f40e..24b6100a1 100644 --- a/src/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m +++ b/src/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m @@ -53,10 +53,10 @@ inputs.experimentalJointVelocities, ... inputs.coordinateNames, ... inputs.statesCoordinateNames); -stateJointAccelerations = subsetDataByCoordinates( ... - inputs.experimentalJointAccelerations, ... - inputs.coordinateNames, ... - inputs.statesCoordinateNames); +% stateJointAccelerations = subsetDataByCoordinates( ... +% inputs.experimentalJointAccelerations, ... +% inputs.coordinateNames, ... +% inputs.statesCoordinateNames); maxStatePositions = max(stateJointAngles) + ... inputs.jointPositionsMultiple * range(stateJointAngles); @@ -66,26 +66,34 @@ inputs.jointVelocitiesMultiple * range(stateJointVelocities); minStateVelocities = min(stateJointVelocities) - ... inputs.jointVelocitiesMultiple * range(stateJointVelocities); -maxStateAccelerations = max(stateJointAccelerations) + ... - inputs.jointAccelerationsMultiple * range(stateJointAccelerations); -minStateAccelerations = min(stateJointAccelerations) - ... - inputs.jointAccelerationsMultiple * range(stateJointAccelerations); +% maxStateAccelerations = max(stateJointAccelerations) + ... +% inputs.jointAccelerationsMultiple * range(stateJointAccelerations); +% minStateAccelerations = min(stateJointAccelerations) - ... +% inputs.jointAccelerationsMultiple * range(stateJointAccelerations); -inputs.maxState = [maxStatePositions maxStateVelocities maxStateAccelerations]; -inputs.minState = [minStatePositions minStateVelocities minStateAccelerations]; +inputs.maxState = [ ... + maxStatePositions, ... + maxStateVelocities, ... + % maxStateAccelerations, ... + ]; +inputs.minState = [ ... + minStatePositions, ... + minStateVelocities, ... + % minStateAccelerations, ... + ]; end function inputs = makeControlBounds(inputs) -stateJointJerks = subsetDataByCoordinates( ... - inputs.experimentalJointJerks, ... +stateJointAccelerations = subsetDataByCoordinates( ... + inputs.experimentalJointAccelerations, ... inputs.coordinateNames, ... inputs.statesCoordinateNames); -inputs.maxControl = max(stateJointJerks) + ... - inputs.controlJerksMultiple * range(stateJointJerks); -inputs.minControl = min(stateJointJerks) - ... - inputs.controlJerksMultiple * range(stateJointJerks); +inputs.maxControl = max(stateJointAccelerations) + ... + inputs.controlJerksMultiple * range(stateJointAccelerations); +inputs.minControl = min(stateJointAccelerations) - ... + inputs.controlJerksMultiple * range(stateJointAccelerations); if strcmp(inputs.controllerType, 'synergy') maxControlSynergyActivations = inputs.maxControlSynergyActivations * ... diff --git a/src/TreatmentOptimization/calcDynamicConstraint.m b/src/TreatmentOptimization/calcDynamicConstraint.m index c05dc87ba..f7bcf164b 100644 --- a/src/TreatmentOptimization/calcDynamicConstraint.m +++ b/src/TreatmentOptimization/calcDynamicConstraint.m @@ -32,6 +32,5 @@ params) dynamics = (params.maxTime - params.minTime) * ... - ([values.stateVelocities values.stateAccelerations ... - values.controlJerks]) ./ (params.maxState - params.minState); + ([values.stateVelocities values.controlAccelerations]) ./ (params.maxState - params.minState); end diff --git a/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m b/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m index 8a0864cb5..e7c52f906 100644 --- a/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m +++ b/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m @@ -39,11 +39,12 @@ state, 1, length(inputs.statesCoordinateNames)); values.stateVelocities = getCorrectStates( ... state, 2, length(inputs.statesCoordinateNames)); -values.stateAccelerations = getCorrectStates( ... - state, 3, length(inputs.statesCoordinateNames)); -values.controlJerks = control(:, 1 : length(inputs.statesCoordinateNames)); -[values.positions, values.velocities, ... - values.accelerations] = recombineFullState(values, inputs); +% values.stateAccelerations = getCorrectStates( ... +% state, 3, length(inputs.statesCoordinateNames)); +values.controlAccelerations = control(:, 1 : length(inputs.statesCoordinateNames)); +% [values.positions, values.velocities, values.accelerations] = recombineFullState(values, inputs); +[values.positions, values.velocities] = recombineFullState(values, inputs); +values.accelerations = recombineFullAccelerations(values, inputs); if strcmp(inputs.controllerType, 'synergy') values.controlSynergyActivations = control(:, ... length(inputs.statesCoordinateNames) + 1 : ... @@ -111,7 +112,9 @@ end end -function [positions, velocities, accelerations] = recombineFullState( ... +% function [positions, velocities, accelerations] = recombineFullState( ... +% values, inputs) +function [positions, velocities] = recombineFullState( ... values, inputs) if size(values.time) == size(inputs.collocationTimeOriginal) positions = inputs.splinedJointAngles; @@ -122,8 +125,8 @@ inputs.coordinateNames, values.time); velocities = evaluateGcvSplines(inputs.splineJointAngles, ... inputs.coordinateNames, values.time, 1); - accelerations = evaluateGcvSplines(inputs.splineJointAngles, ... - inputs.coordinateNames, values.time, 2); + % accelerations = evaluateGcvSplines(inputs.splineJointAngles, ... + % inputs.coordinateNames, values.time, 2); end for i = 1:length(inputs.coordinateNames) index = find(ismember( ... @@ -131,7 +134,23 @@ if ~isempty(index) positions(:, i) = values.statePositions(:, index); velocities(:, i) = values.stateVelocities(:, index); - accelerations(:, i) = values.stateAccelerations(:, index); + % accelerations(:, i) = values.stateAccelerations(:, index); end end end + +function accelerations = recombineFullAccelerations(values, inputs) +if size(values.time) == size(inputs.collocationTimeOriginal) + accelerations = inputs.splinedJointAccelerations; +else + accelerations = evaluateGcvSplines(inputs.splineJointAngles, ... + inputs.coordinateNames, values.time, 2); +end +for i = 1:length(inputs.coordinateNames) + index = find(ismember( ... + inputs.statesCoordinateNames, inputs.coordinateNames{i})); + if ~isempty(index) + accelerations(:, i) = values.controlAccelerations(:, index); + end +end +end \ No newline at end of file diff --git a/src/TreatmentOptimization/makeStateDerivatives.m b/src/TreatmentOptimization/makeStateDerivatives.m index 03f872989..aa66906e0 100644 --- a/src/TreatmentOptimization/makeStateDerivatives.m +++ b/src/TreatmentOptimization/makeStateDerivatives.m @@ -37,7 +37,7 @@ inputs.experimentalJointAccelerations = evaluateGcvSplines( ... inputs.splineJointAngles, inputs.coordinateNames, ... inputs.experimentalTime, 2); -inputs.experimentalJointJerks = evaluateGcvSplines( ... - inputs.splineJointAngles, inputs.coordinateNames, ... - inputs.experimentalTime, 3); +% inputs.experimentalJointJerks = evaluateGcvSplines( ... +% inputs.splineJointAngles, inputs.coordinateNames, ... +% inputs.experimentalTime, 3); end diff --git a/src/TreatmentOptimization/saveTreatmentOptimizationResults.m b/src/TreatmentOptimization/saveTreatmentOptimizationResults.m index 7b30d94d7..075bcade1 100644 --- a/src/TreatmentOptimization/saveTreatmentOptimizationResults.m +++ b/src/TreatmentOptimization/saveTreatmentOptimizationResults.m @@ -42,16 +42,16 @@ function saveTreatmentOptimizationResults(solution, inputs, values) for i = 1 : length(inputs.statesCoordinateNames) stateLabels{end + 1} = strcat(inputs.statesCoordinateNames{i}, '_u'); end -for i = 1 : length(inputs.statesCoordinateNames) - stateLabels{end + 1} = strcat(inputs.statesCoordinateNames{i}, '_dudt'); -end +% for i = 1 : length(inputs.statesCoordinateNames) +% stateLabels{end + 1} = strcat(inputs.statesCoordinateNames{i}, '_dudt'); +% end [time, data] = splineToEvenlySpaced(values.time, [values.statePositions ... - values.stateVelocities values.stateAccelerations]); + values.stateVelocities]); writeToSto(stateLabels, time, data, ... fullfile(inputs.resultsDirectory, strcat(inputs.trialName, "_states.sto"))); -[time, jerks] = splineToEvenlySpaced(values.time, values.controlJerks); -writeToSto(inputs.statesCoordinateNames, time, jerks, ... - fullfile(inputs.resultsDirectory, strcat(inputs.trialName, "_jerks.sto"))); +[time, accelerations] = splineToEvenlySpaced(values.time, values.controlAccelerations); +writeToSto(inputs.statesCoordinateNames, time, accelerations, ... + fullfile(inputs.resultsDirectory, strcat(inputs.trialName, "_accelerations.sto"))); if strcmp(inputs.controllerType, 'synergy') controlLabels = {}; for i = 1 : length(inputs.osimx.synergyGroups) From 14b12c8374e1f7f6d37685aa76e56ce363f66f28 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 28 Jan 2024 05:33:50 -0600 Subject: [PATCH 291/365] make data rate really small --- .../Saving/saveGroundContactPersonalizationResults.m | 4 ++-- .../Saving/writeFullBodyKinematicsFromGcp.m | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/GroundContactPersonalization/Saving/saveGroundContactPersonalizationResults.m b/src/GroundContactPersonalization/Saving/saveGroundContactPersonalizationResults.m index 2ca0d4b01..74cf69966 100644 --- a/src/GroundContactPersonalization/Saving/saveGroundContactPersonalizationResults.m +++ b/src/GroundContactPersonalization/Saving/saveGroundContactPersonalizationResults.m @@ -39,9 +39,9 @@ function saveGroundContactPersonalizationResults(inputs, params, ... writeReplacedExperimentalGroundReactionsToSto(inputs, ... resultsDirectory, name); writeOptimizedGroundReactionsToSto(inputs, params, resultsDirectory, name); -writeCombinedOptimizedGroundReactionsToSto(inputs, params, resultsDirectory); -writeFullBodyKinematicsFromGcp(inputs, params, resultsDirectory); writeGroundContactPersonalizationOsimxFile(inputs, resultsDirectory, ... osimxFileName); +writeCombinedOptimizedGroundReactionsToSto(inputs, params, resultsDirectory); +writeFullBodyKinematicsFromGcp(inputs, params, resultsDirectory); end diff --git a/src/GroundContactPersonalization/Saving/writeFullBodyKinematicsFromGcp.m b/src/GroundContactPersonalization/Saving/writeFullBodyKinematicsFromGcp.m index 1b1fe4e20..d2b8b8c28 100644 --- a/src/GroundContactPersonalization/Saving/writeFullBodyKinematicsFromGcp.m +++ b/src/GroundContactPersonalization/Saving/writeFullBodyKinematicsFromGcp.m @@ -67,7 +67,7 @@ function writeFullBodyKinematicsFromGcp(inputs, params, resultsDirectory) % make ikdata trcFromMotParams.trcFileName = "preGcp.trc"; -trcFromMotParams.dataRate = 1; +trcFromMotParams.dataRate = .00001; TrcFromMot(inputs.bodyModel, inputs.motionFileName, trcFromMotParams) % load trc file From dc3ad922f0f2431d682159eeb5741122508427b7 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 28 Jan 2024 23:02:44 -0600 Subject: [PATCH 292/365] added cleanup to trcfrommot --- .../Saving/writeFullBodyKinematicsFromGcp.m | 2 ++ src/core/motToTrc/TrcFromMot.m | 2 +- src/core/motToTrc/applyValuesFromStorage.m | 2 +- src/core/motToTrc/makeTimeSeriesTableVec3.m | 8 ++++---- src/core/motToTrc/setValueFromStorage.m | 2 +- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/GroundContactPersonalization/Saving/writeFullBodyKinematicsFromGcp.m b/src/GroundContactPersonalization/Saving/writeFullBodyKinematicsFromGcp.m index d2b8b8c28..960887250 100644 --- a/src/GroundContactPersonalization/Saving/writeFullBodyKinematicsFromGcp.m +++ b/src/GroundContactPersonalization/Saving/writeFullBodyKinematicsFromGcp.m @@ -146,5 +146,7 @@ function writeFullBodyKinematicsFromGcp(inputs, params, resultsDirectory) end kinematicsReporter.getPositionStorage().print( ... fullfile(resultsDirectory, "IKData", outfile)); +delete "postGcp.trc" +delete "preGcp.trc" end diff --git a/src/core/motToTrc/TrcFromMot.m b/src/core/motToTrc/TrcFromMot.m index 1516311b2..47e673c52 100644 --- a/src/core/motToTrc/TrcFromMot.m +++ b/src/core/motToTrc/TrcFromMot.m @@ -20,7 +20,7 @@ function TrcFromMot(inputModel, motFileName, params) table = makeTimeSeriesTableVec3(model, dataRate); applyValuesFromStorage(model, state, storage, table, params); trcFileAdapter = org.opensim.modeling.TRCFileAdapter(); -trcFileAdapter.write(table, trcFileNameOrDefault(params)) +trcFileAdapter.write(table, trcFileNameOrDefault(params)); end % (struct) -> (number) diff --git a/src/core/motToTrc/applyValuesFromStorage.m b/src/core/motToTrc/applyValuesFromStorage.m index c17a28ea1..163e26476 100644 --- a/src/core/motToTrc/applyValuesFromStorage.m +++ b/src/core/motToTrc/applyValuesFromStorage.m @@ -8,7 +8,7 @@ function applyValuesFromStorage(model, state, storage, table, params) import org.opensim.modeling.* for i=0:storage.getSize()-1 for j=1:storage.getColumnLabels().size()-1 - setValueFromStorage(model, state, storage, i, j, params) + setValueFromStorage(model, state, storage, i, j, params); model.assemble(state); end model.assemble(state); diff --git a/src/core/motToTrc/makeTimeSeriesTableVec3.m b/src/core/motToTrc/makeTimeSeriesTableVec3.m index 17c0adbb8..c5a844a6d 100644 --- a/src/core/motToTrc/makeTimeSeriesTableVec3.m +++ b/src/core/motToTrc/makeTimeSeriesTableVec3.m @@ -8,12 +8,12 @@ function table = makeTimeSeriesTableVec3(model, dataRate) import org.opensim.modeling.* table = TimeSeriesTableVec3(); -table.addTableMetaDataString("DataRate", num2str(dataRate)) -table.addTableMetaDataString("Units", "m") +table.addTableMetaDataString("DataRate", num2str(dataRate)); +table.addTableMetaDataString("Units", "m"); labels = StdVectorString(); for i=0:model.getMarkerSet().getSize()-1 - labels.add(model.getMarkerSet().get(i).getName()) + labels.add(model.getMarkerSet().get(i).getName()); end -table.setColumnLabels(labels) +table.setColumnLabels(labels); end diff --git a/src/core/motToTrc/setValueFromStorage.m b/src/core/motToTrc/setValueFromStorage.m index dec1130e1..1471b81fd 100644 --- a/src/core/motToTrc/setValueFromStorage.m +++ b/src/core/motToTrc/setValueFromStorage.m @@ -14,7 +14,7 @@ function setValueFromStorage(model, state, storage, row, column, params) % applyGaussian(value, params, 'rotationNoise')*3.14/180); % coord.setValue(state, applyGaussian(value,params, 'translationNoise')); -coord.setValue(state, value) +coord.setValue(state, value); end function output = applyGaussian(value, params, fieldName) From f53d4eb330debe0ec9a262c22de3767ad2fea2f6 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Mon, 29 Jan 2024 17:31:51 -0600 Subject: [PATCH 293/365] add filtering to output data --- .../Saving/writeCombinedOptimizedGroundReactionsToSto.m | 1 + .../Saving/writeFullBodyKinematicsFromGcp.m | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/src/GroundContactPersonalization/Saving/writeCombinedOptimizedGroundReactionsToSto.m b/src/GroundContactPersonalization/Saving/writeCombinedOptimizedGroundReactionsToSto.m index 126cf86c8..be56c42e0 100644 --- a/src/GroundContactPersonalization/Saving/writeCombinedOptimizedGroundReactionsToSto.m +++ b/src/GroundContactPersonalization/Saving/writeCombinedOptimizedGroundReactionsToSto.m @@ -72,6 +72,7 @@ function writeCombinedOptimizedGroundReactionsToSto(inputs, params, ... data(:, (foot - 1) * 9 + 1 : foot * 9) = [modeledValues.anteriorGrf' modeledValues.verticalGrf' ... modeledValues.lateralGrf' modeledValues.xGrfMoment' ... modeledValues.yGrfMoment' modeledValues.zGrfMoment' center']; + data = lowpassFilter(time, data, 2, 6, 0); end writeToSto(columnLabels, timePoints, data, ... fullfile(resultsDirectory, "GRFData", outfile)); diff --git a/src/GroundContactPersonalization/Saving/writeFullBodyKinematicsFromGcp.m b/src/GroundContactPersonalization/Saving/writeFullBodyKinematicsFromGcp.m index 960887250..1045fca9e 100644 --- a/src/GroundContactPersonalization/Saving/writeFullBodyKinematicsFromGcp.m +++ b/src/GroundContactPersonalization/Saving/writeFullBodyKinematicsFromGcp.m @@ -146,6 +146,11 @@ function writeFullBodyKinematicsFromGcp(inputs, params, resultsDirectory) end kinematicsReporter.getPositionStorage().print( ... fullfile(resultsDirectory, "IKData", outfile)); +[columnNames, time, data] = parseMotToComponents(model, ... + Storage(fullfile(resultsDirectory, "IKData", outfile))); +data = lowpassFilter(time, data, 4, 6, 0); +writeToSto(columnNames, timePoints, data, ... + fullfile(resultsDirectory, "IKData", outfile)); delete "postGcp.trc" delete "preGcp.trc" end From ba4afd1b33df1d69ac41beb250e338e171a6f29d Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 29 Jan 2024 19:02:13 -0600 Subject: [PATCH 294/365] Update plotMomentMatchingResults.m --- .../Analysis/plotMomentMatchingResults.m | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/NeuralControlPersonalization/Analysis/plotMomentMatchingResults.m b/src/NeuralControlPersonalization/Analysis/plotMomentMatchingResults.m index d301d2b05..10a8e9121 100644 --- a/src/NeuralControlPersonalization/Analysis/plotMomentMatchingResults.m +++ b/src/NeuralControlPersonalization/Analysis/plotMomentMatchingResults.m @@ -49,13 +49,14 @@ function plotMomentMatchingResults(experimentalMomentsFile, ... [modeledColumns, modeledTime, modeledMoments] = parseMotToComponents( ... org.opensim.modeling.Model(), Storage(modeledMomentsFile)); -includedColumns = ismember(experimentalColumns, modeledColumns); +includedColumns = logical(ismember(experimentalColumns, modeledColumns) ... + + ismember(experimentalColumns + "_moment", modeledColumns)); experimentalMoments = experimentalMoments(includedColumns, :); experimentalColumns = experimentalColumns(includedColumns); subplotNumber = 1; hasLegend = false; -figure(figureNumber) +% figure(figureNumber) figureIndex = 1; for i = 1:length(experimentalColumns) if i > figureSize * figureIndex @@ -65,8 +66,11 @@ function plotMomentMatchingResults(experimentalMomentsFile, ... hasLegend = false; end subplot(figureHeight, figureWidth, subplotNumber) - plot(experimentalTime, experimentalMoments(i, :), 'LineWidth', 2) + plot(modeledTime, experimentalMoments(i, :), 'LineWidth', 2) modeledIndex = find(experimentalColumns(i) == modeledColumns); + if isempty(modeledIndex) + modeledIndex = find(experimentalColumns(i) + "_moment" == modeledColumns); + end if ~isempty(modeledIndex) hold on plot(modeledTime, modeledMoments(modeledIndex, :), 'LineWidth', 2); From c9c0c2721b7d1e3ef9c952001756b9c2f324d23b Mon Sep 17 00:00:00 2001 From: RobSalati Date: Tue, 30 Jan 2024 17:00:30 -0600 Subject: [PATCH 295/365] Full bug fix Added underscore to "Length.sto" to parse the correct file. Indexed moment arms to only use the moment arms about axes given in moment files. --- ...arseMuscleTendonLengthInitializationSettingsTree.m | 7 +++---- .../MuscleTendonPersonalizationTool.m | 11 ----------- .../Saving/saveMuscleTendonPersonalizationResults.m | 2 +- src/core/muscle/calcPassiveMuscleMoments.m | 9 --------- 4 files changed, 4 insertions(+), 25 deletions(-) diff --git a/src/MuscleTendonLengthInitialization/parseMuscleTendonLengthInitializationSettingsTree.m b/src/MuscleTendonLengthInitialization/parseMuscleTendonLengthInitializationSettingsTree.m index 9841642bf..fa152ee21 100644 --- a/src/MuscleTendonLengthInitialization/parseMuscleTendonLengthInitializationSettingsTree.m +++ b/src/MuscleTendonLengthInitialization/parseMuscleTendonLengthInitializationSettingsTree.m @@ -71,7 +71,7 @@ passiveInputDirectory, "MAData"), inputs.passivePrefixes); [inputs.passiveMuscleTendonLength, ... inputs.passiveMuscleTendonLengthColumnNames] = ... - parseFileFromDirectories(passiveDirectories, "Length.sto", ... + parseFileFromDirectories(passiveDirectories, "_Length.sto", ... Model(inputs.model)); if ~(sum(ismember(inputs.muscleNames, ... inputs.passiveMuscleTendonLengthColumnNames)) == ... @@ -85,9 +85,8 @@ inputs.passiveMuscleTendonLengthColumnNames = inputs.passiveMuscleTendonLengthColumnNames(includedIndices); [~, ~, momentCoordinateIndices] = intersect(inputs.coordinateNames, ... inputs.passiveMomentArmCoordinates, 'stable'); - inputs.passiveMomentArms = inputs.passiveMomentArms(:, :, includedIndices, :); - - inputs.passiveMomentArms = inputs.passiveMomentArms(:, momentCoordinateIndices, :, :); + + inputs.passiveMomentArms = inputs.passiveMomentArms(:, momentCoordinateIndices, includedIndices, :); inputs.passiveMuscleTendonLength = inputs.passiveMuscleTendonLength(:, includedIndices, :); inputs.passiveMomentDataExists = 1; diff --git a/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m b/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m index 87ff26fd7..9a72cfd22 100644 --- a/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m +++ b/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m @@ -35,8 +35,6 @@ function MuscleTendonPersonalizationTool(settingsFileName) [inputs, params, resultsDirectory] = ... parseMuscleTendonPersonalizationSettingsTree(settingsTree); precalInputs = parseMuscleTendonLengthInitializationSettingsTree(settingsTree); -precalInputs2 = precalInputs; -save("inputsCopy.mat", "precalInputs2") if isstruct(precalInputs) optimizedInitialGuess = MuscleTendonLengthInitialization(precalInputs); inputs = updateMtpInitialGuess(inputs, precalInputs, ... @@ -45,15 +43,6 @@ function MuscleTendonPersonalizationTool(settingsFileName) precalInputs = struct('optimizeIsometricMaxForce', false); end optimizedParams = MuscleTendonPersonalization(inputs, params); -% save("optimizedParams.mat", 'optimizedParams') -% save("precalInputs.mat", "precalInputs") -% save("inputs.mat", "inputs") -% save("params.mat", "params") -% resultsDirectory = "mtpResultsRight3"; -% load("optimizedParams.mat") -% load("inputs.mat") -% load("params.mat") -% load("precalInputs.mat") if params.performMuscleTendonLengthInitialization [finalValues, resultsStruct, modeledValues] = ... getMtpResultsToSave(inputs, params, optimizedParams, precalInputs); diff --git a/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m b/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m index 4928f54f9..cbf24d39f 100644 --- a/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m +++ b/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m @@ -43,8 +43,8 @@ function saveMuscleTendonPersonalizationResults(mtpInputs, finalValues, ... end if ~isempty(precalInputs) saveMtpPassiveMomentData(precalInputs, modeledValues, analysisDirectory); - saveMtpPassiveForceData(mtpInputs, resultsStruct, analysisDirectory); end +saveMtpPassiveForceData(mtpInputs, resultsStruct, analysisDirectory); saveMtpActivationAndExcitationData(mtpInputs, resultsStruct, analysisDirectory); writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... resultsStruct.results.normalizedFiberLength, ... diff --git a/src/core/muscle/calcPassiveMuscleMoments.m b/src/core/muscle/calcPassiveMuscleMoments.m index 1ea0f7d2b..04b0976e2 100644 --- a/src/core/muscle/calcPassiveMuscleMoments.m +++ b/src/core/muscle/calcPassiveMuscleMoments.m @@ -54,15 +54,6 @@ parallelComponentOfPennationAngle), 1); expandedParallelComponentOfPennationAngle(1, 1, :, 1) = ... parallelComponentOfPennationAngle; - -% momentArms = experimentalData.momentArms(:, indices, :, :); -% temp = momentArms; -% momentArms(:,5,:,:) = temp(:, 1, :, :); -% momentArms(:,2,:,:) = temp(:, 2, :, :); -% momentArms(:,1,:,:) = temp(:, 3, :, :); -% momentArms(:,3,:,:) = temp(:, 4, :, :); -% momentArms(:,4,:,:) = temp(:, 5, :, :); -% momentArms(:,6,:,:) = temp(:, 6, :, :); passiveModelMoments = experimentalData.momentArms .* ... expandedMaxIsometricForce .* expandedPassiveForce .* ... expandedParallelComponentOfPennationAngle; From 476adcbde3ed6fc38c6540976b2ba1e608e2e568 Mon Sep 17 00:00:00 2001 From: RobSalati Date: Tue, 30 Jan 2024 17:22:01 -0600 Subject: [PATCH 296/365] Fix indexing error Added check for "_moment" at the end of coordinate axes. --- .../parseMuscleTendonLengthInitializationSettingsTree.m | 5 ++++- src/core/muscle/calcPassiveMuscleMoments.m | 1 - 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/MuscleTendonLengthInitialization/parseMuscleTendonLengthInitializationSettingsTree.m b/src/MuscleTendonLengthInitialization/parseMuscleTendonLengthInitializationSettingsTree.m index fa152ee21..dfac6b826 100644 --- a/src/MuscleTendonLengthInitialization/parseMuscleTendonLengthInitializationSettingsTree.m +++ b/src/MuscleTendonLengthInitialization/parseMuscleTendonLengthInitializationSettingsTree.m @@ -85,7 +85,10 @@ inputs.passiveMuscleTendonLengthColumnNames = inputs.passiveMuscleTendonLengthColumnNames(includedIndices); [~, ~, momentCoordinateIndices] = intersect(inputs.coordinateNames, ... inputs.passiveMomentArmCoordinates, 'stable'); - + if isempty(momentCoordinateIndices) + [~, ~, momentCoordinateIndices] = intersect(inputs.coordinateNames, ... + strcat(inputs.passiveMomentArmCoordinates,"_moment"), 'stable'); + end inputs.passiveMomentArms = inputs.passiveMomentArms(:, momentCoordinateIndices, includedIndices, :); inputs.passiveMuscleTendonLength = inputs.passiveMuscleTendonLength(:, includedIndices, :); diff --git a/src/core/muscle/calcPassiveMuscleMoments.m b/src/core/muscle/calcPassiveMuscleMoments.m index 04b0976e2..0739ae33d 100644 --- a/src/core/muscle/calcPassiveMuscleMoments.m +++ b/src/core/muscle/calcPassiveMuscleMoments.m @@ -58,5 +58,4 @@ expandedMaxIsometricForce .* expandedPassiveForce .* ... expandedParallelComponentOfPennationAngle; passiveModelMoments = permute(sum(passiveModelMoments, 3), [1 2 4 3]); -max(passiveModelMoments, [], 'all') end From 1e9da045a59594b51f60feb9c473d273d76b9043 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Wed, 31 Jan 2024 23:32:54 -0600 Subject: [PATCH 297/365] GCV splines mex --- src/core/mex/compileMex.m | 3 +- src/core/mex/evaluateGcvSplinesMexWindows.cpp | 109 ++++++++++++++++++ 2 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 src/core/mex/evaluateGcvSplinesMexWindows.cpp diff --git a/src/core/mex/compileMex.m b/src/core/mex/compileMex.m index 67046f813..03b405b69 100644 --- a/src/core/mex/compileMex.m +++ b/src/core/mex/compileMex.m @@ -1,6 +1,6 @@ mex CXXFLAGS="/$CXXFLAGS -fopenmp" LDFLAGS="/$LDFLAGS -fopenmp"... COMPFLAGS="/openmp $COMPFLAGS"... - inverseDynamicsAngularMomentumMexWindows.cpp... + evaluateGcvSplinesMexWindows.cpp... -L'C:\OpenSim 4.4\sdk\lib'... -L'C:\OpenSim 4.4\sdk\Simbody\lib'... -L'C:\OpenSim 4.4\sdk\spdlog\lib\spdlog'... @@ -16,6 +16,7 @@ -I'C:\OpenSim 4.4\sdk\include\OpenSim'... -I'C:\OpenSim 4.4\sdk\Simbody\include'... -I'C:\OpenSim 4.4\sdk\include\OpenSim\Simulation'... + -I'C:\OpenSim 4.4\sdk\include\OpenSim\Common'... -I'C:\OpenSim 4.4\sdk\spdlog\include'... -DWIN32 -D_WINDOWS -DNDEBUG... ; \ No newline at end of file diff --git a/src/core/mex/evaluateGcvSplinesMexWindows.cpp b/src/core/mex/evaluateGcvSplinesMexWindows.cpp new file mode 100644 index 000000000..830783bd2 --- /dev/null +++ b/src/core/mex/evaluateGcvSplinesMexWindows.cpp @@ -0,0 +1,109 @@ +// This function is part of the NMSM Pipeline, see file for full license. +// +// performs inverse dynamics with openMP + +// ----------------------------------------------------------------------- // +// The NMSM Pipeline is a toolkit for model personalization and treatment // +// optimization of neuromusculoskeletal models through OpenSim. See // +// nmsm.rice.edu and the NOTICE file for more information. The // +// NMSM Pipeline is developed at Rice University and supported by the US // +// National Institutes of Health (R01 EB030520). // +// // +// Copyright (c) 2021 Rice University and the Authors // +// Author(s): Marleny Vega // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// http://www.apache.org/licenses/LICENSE-2.0. // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or // +// implied. See the License for the specific language governing // +// permissions and limitations under the License. // +// ----------------------------------------------------------------------- // + +#include "mex.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace OpenSim; +using namespace SimTK; +using namespace std; + +//______________________________________________________________________________ + + +void ClearMemory(void){ +} + +vector> mexArrayToVector(const mxArray *input) { + const mwSize *dimension; + dimension = mxGetDimensions(input); + int rows = (int) dimension[0]; + int columns = (int) dimension[1]; + double *data = (double *) mxGetData(input); + vector> output(rows, vector(columns)); + for (int i = 0; i < rows; i++) { + for (int j = 0; j < columns; j++) { + memcpy(&output[i][j], data + i + j * rows, sizeof(double) ); + } + } + return output; +} + +vector mexArrayToVectorInt(const mxArray *input, int numPts) { + int *data = (int *) mxGetData(input); + vector output(numPts); + for (int i = 0; i < numPts; i++) { + memcpy(&output[i], data + i, sizeof(int) ); + } + return output; +} + +vector mexArrayToVectorDouble(const mxArray *input, int numPts) { + double *data = (double *) mxGetData(input); + vector output(numPts); + for (int i = 0; i < numPts; i++) { + memcpy(&output[i], data + i, sizeof(double) ); + } + return output; +} + +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]){ + mexAtExit(ClearMemory); + if (nrhs == 4) { + const int numColumns = mxGetN(prhs[1]); + const int numPts = mxGetN(prhs[2]); + + vector columns = mexArrayToVectorInt(prhs[1], numColumns); + vector time = mexArrayToVectorDouble(prhs[2], numPts); + + GCVSplineSet *splineSet = (GCVSplineSet *) mxGetPr(prhs[0]); + int *derivative = (int *) mxGetPr(prhs[3]); + + plhs[0] = mxCreateDoubleMatrix(numPts,numColumns,mxREAL); + double *values = mxGetPr(plhs[0]); + + for (int i = 0; i < numColumns; ++i) { + for (int j = 0; j < numPts; ++i) { + values[j + numPts * i] = *splineSet.evaluate(columns[i], *derivative, time[j]); + } + } + + columns.clear(); + time.clear(); + } +} \ No newline at end of file From 532b5fce8ce00eea50565ad8b15b77d147616b66 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Wed, 31 Jan 2024 23:33:35 -0600 Subject: [PATCH 298/365] fix gcp output writing --- .../Saving/writeCombinedOptimizedGroundReactionsToSto.m | 2 +- .../Saving/writeFullBodyKinematicsFromGcp.m | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/GroundContactPersonalization/Saving/writeCombinedOptimizedGroundReactionsToSto.m b/src/GroundContactPersonalization/Saving/writeCombinedOptimizedGroundReactionsToSto.m index be56c42e0..c46b97127 100644 --- a/src/GroundContactPersonalization/Saving/writeCombinedOptimizedGroundReactionsToSto.m +++ b/src/GroundContactPersonalization/Saving/writeCombinedOptimizedGroundReactionsToSto.m @@ -72,7 +72,7 @@ function writeCombinedOptimizedGroundReactionsToSto(inputs, params, ... data(:, (foot - 1) * 9 + 1 : foot * 9) = [modeledValues.anteriorGrf' modeledValues.verticalGrf' ... modeledValues.lateralGrf' modeledValues.xGrfMoment' ... modeledValues.yGrfMoment' modeledValues.zGrfMoment' center']; - data = lowpassFilter(time, data, 2, 6, 0); + data = lowpassFilter(inputs.surfaces{1}.time, data, 2, 6, 0); end writeToSto(columnLabels, timePoints, data, ... fullfile(resultsDirectory, "GRFData", outfile)); diff --git a/src/GroundContactPersonalization/Saving/writeFullBodyKinematicsFromGcp.m b/src/GroundContactPersonalization/Saving/writeFullBodyKinematicsFromGcp.m index 1045fca9e..565d9ed71 100644 --- a/src/GroundContactPersonalization/Saving/writeFullBodyKinematicsFromGcp.m +++ b/src/GroundContactPersonalization/Saving/writeFullBodyKinematicsFromGcp.m @@ -147,11 +147,11 @@ function writeFullBodyKinematicsFromGcp(inputs, params, resultsDirectory) kinematicsReporter.getPositionStorage().print( ... fullfile(resultsDirectory, "IKData", outfile)); [columnNames, time, data] = parseMotToComponents(model, ... - Storage(fullfile(resultsDirectory, "IKData", outfile))); -data = lowpassFilter(time, data, 4, 6, 0); + org.opensim.modeling.Storage(fullfile(resultsDirectory, "IKData", outfile))); +data = lowpassFilter(time, data', 4, 6, 0); writeToSto(columnNames, timePoints, data, ... fullfile(resultsDirectory, "IKData", outfile)); -delete "postGcp.trc" -delete "preGcp.trc" +delete postGcp.trc +delete preGcp.trc end From 74a2d0b1e0af417955f7758e64c0184259932535 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Thu, 1 Feb 2024 00:02:23 -0600 Subject: [PATCH 299/365] compile gcv spline mex --- src/core/mex/compileMex.m | 6 ++++-- src/core/mex/evaluateGcvSplinesMexWindows.cpp | 2 +- .../mex/evaluateGcvSplinesMexWindows.mexw64 | Bin 0 -> 16384 bytes 3 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 src/core/mex/evaluateGcvSplinesMexWindows.mexw64 diff --git a/src/core/mex/compileMex.m b/src/core/mex/compileMex.m index 03b405b69..7a0161532 100644 --- a/src/core/mex/compileMex.m +++ b/src/core/mex/compileMex.m @@ -1,6 +1,6 @@ mex CXXFLAGS="/$CXXFLAGS -fopenmp" LDFLAGS="/$LDFLAGS -fopenmp"... COMPFLAGS="/openmp $COMPFLAGS"... - evaluateGcvSplinesMexWindows.cpp... + evaluateGcvSplinesMexWindows.cpp... -L'C:\OpenSim 4.4\sdk\lib'... -L'C:\OpenSim 4.4\sdk\Simbody\lib'... -L'C:\OpenSim 4.4\sdk\spdlog\lib\spdlog'... @@ -16,7 +16,9 @@ -I'C:\OpenSim 4.4\sdk\include\OpenSim'... -I'C:\OpenSim 4.4\sdk\Simbody\include'... -I'C:\OpenSim 4.4\sdk\include\OpenSim\Simulation'... - -I'C:\OpenSim 4.4\sdk\include\OpenSim\Common'... -I'C:\OpenSim 4.4\sdk\spdlog\include'... + -I'C:\OpenSim 4.4\sdk\spdlog\include\spdlog\details'... + -I'C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\ucrt'... + -I'C:\OpenSim 4.4\sdk\include\OpenSim\Common'... -DWIN32 -D_WINDOWS -DNDEBUG... ; \ No newline at end of file diff --git a/src/core/mex/evaluateGcvSplinesMexWindows.cpp b/src/core/mex/evaluateGcvSplinesMexWindows.cpp index 830783bd2..d887e0878 100644 --- a/src/core/mex/evaluateGcvSplinesMexWindows.cpp +++ b/src/core/mex/evaluateGcvSplinesMexWindows.cpp @@ -99,7 +99,7 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]){ for (int i = 0; i < numColumns; ++i) { for (int j = 0; j < numPts; ++i) { - values[j + numPts * i] = *splineSet.evaluate(columns[i], *derivative, time[j]); + values[j + numPts * i] = splineSet->evaluate(columns[i], *derivative, time[j]); } } diff --git a/src/core/mex/evaluateGcvSplinesMexWindows.mexw64 b/src/core/mex/evaluateGcvSplinesMexWindows.mexw64 new file mode 100644 index 0000000000000000000000000000000000000000..aa9adb1b6fdde78b78ca84aeaa3f7f787071b4e1 GIT binary patch literal 16384 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 literal 0 HcmV?d00001 From 33d6de4b9f4c18b66894223ff0d6d54bf7b3161a Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Thu, 1 Feb 2024 04:28:33 -0600 Subject: [PATCH 300/365] change gcv check to include time matching --- .../calcTrackingControllerIntegrand.m | 6 +++-- .../calcTrackingCoordinateIntegrand.m | 3 ++- .../calcTrackingExternalForcesIntegrand.m | 3 ++- .../calcTrackingExternalMomentsIntegrand.m | 3 ++- ...calcTrackingInverseDynamicLoadsIntegrand.m | 3 ++- .../calcTrackingMarkerPosition.m | 3 ++- .../calcTrackingMuscleActivationIntegrand.m | 3 ++- .../calcTrackingSpeedIntegrand.m | 3 ++- src/core/GCVSpline/evaluateGcvSplines.m | 13 ++++++--- src/core/mex/evaluateGcvSplinesMexWindows.cpp | 25 ++++++++++-------- .../mex/evaluateGcvSplinesMexWindows.mexw64 | Bin 16384 -> 16384 bytes 11 files changed, 41 insertions(+), 24 deletions(-) diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m index 13ac8e3cb..9b8f8df89 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m @@ -38,7 +38,8 @@ indx = find(strcmp(convertCharsToStrings( ... inputs.synergyLabels), controllerName)); if ~isempty(indx) - if size(time) == size(inputs.collocationTimeOriginal) + if size(time) == size(inputs.collocationTimeOriginal) && ... + all(time == inputs.collocationTimeOriginal) synergyActivations = inputs.splinedSynergyActivations; else synergyActivations = ... @@ -55,7 +56,8 @@ indx2 = find(strcmp(convertCharsToStrings( ... strcat(inputs.torqueControllerCoordinateNames, '_moment')), ... controllerName)); -if size(time) == size(inputs.collocationTimeOriginal) +if size(time) == size(inputs.collocationTimeOriginal) && ... + all(time == inputs.collocationTimeOriginal) experimentalJointMoments = inputs.splinedTorqueControls; else experimentalJointMoments = ... diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m index 7aaf2d2be..30f38db35 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m @@ -39,7 +39,8 @@ strcat("Coordinate ", coordinateName, " is not in the ", ... ""))) end -if size(time) == size(inputs.collocationTimeOriginal) +if size(time) == size(inputs.collocationTimeOriginal) && ... + all(time == inputs.collocationTimeOriginal) experimentalJointAngles = inputs.splinedJointAngles; else experimentalJointAngles = evaluateGcvSplines( ... diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m index a08dcbc6b..a36dae5a9 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m @@ -36,7 +36,8 @@ indx = find(strcmp(convertCharsToStrings(inputs.contactSurfaces{i}. ... forceColumns), forceName)); if ~isempty(indx) - if size(time) == size(inputs.collocationTimeOriginal) + if size(time) == size(inputs.collocationTimeOriginal) && ... + all(time == inputs.collocationTimeOriginal) experimentalGroundReactions = inputs.splinedGroundReactionForces{i}; else experimentalGroundReactions = ... diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m index 582ab2165..3c767a582 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m @@ -36,7 +36,8 @@ indx = find(strcmp(convertCharsToStrings(inputs.contactSurfaces{i}. ... momentColumns), loadName)); if ~isempty(indx) - if size(time) == size(inputs.collocationTimeOriginal) + if size(time) == size(inputs.collocationTimeOriginal) && ... + all(time == inputs.collocationTimeOriginal) experimentalGroundReactions = inputs.splinedGroundReactionMoments{i}; else experimentalGroundReactions = ... diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m index 90309686b..f8a4c959c 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m @@ -33,7 +33,8 @@ normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); indx = find(strcmp(inputs.inverseDynamicsMomentLabels, loadName)); -if size(time) == size(inputs.collocationTimeOriginal) +if size(time) == size(inputs.collocationTimeOriginal) && ... + all(time == inputs.collocationTimeOriginal) experimentalJointMoments = inputs.splinedJointMoments; else experimentalJointMoments = evaluateGcvSplines( ... diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m index ab3fd9ea2..5f958727b 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m @@ -38,7 +38,8 @@ strcat("Marker ", costTerm.marker, " is not in the ", ... "list of tracked markers"))) end -if size(time) == size(inputs.collocationTimeOriginal) +if size(time) == size(inputs.collocationTimeOriginal) && ... + all(time == inputs.collocationTimeOriginal) experimentalMarkerPositions = inputs.splinedMarkerPositions{indx}; else experimentalMarkerPositions = ... diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m index 634ca29a7..3ccc904ff 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m @@ -34,7 +34,8 @@ "normalize_by_final_time", true); indx = find(strcmp(convertCharsToStrings(inputs.muscleNames), ... muscleName)); -if size(time) == size(inputs.collocationTimeOriginal) +if size(time) == size(inputs.collocationTimeOriginal) && ... + all(time == inputs.collocationTimeOriginal) experimentalMuscleActivations = inputs.splinedMuscleActivations; else experimentalMuscleActivations = evaluateGcvSplines( ... diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m index 161e9fc8e..59d2f6fb7 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m @@ -39,7 +39,8 @@ strcat("Coordinate ", coordinateName, " is not in the ", ... ""))) end -if size(time) == size(inputs.collocationTimeOriginal) +if size(time) == size(inputs.collocationTimeOriginal) && ... + all(time == inputs.collocationTimeOriginal) experimentalJointVelocities = inputs.splinedJointSpeeds; else experimentalJointVelocities = evaluateGcvSplines( ... diff --git a/src/core/GCVSpline/evaluateGcvSplines.m b/src/core/GCVSpline/evaluateGcvSplines.m index 8e6a83372..7a8bfe01e 100644 --- a/src/core/GCVSpline/evaluateGcvSplines.m +++ b/src/core/GCVSpline/evaluateGcvSplines.m @@ -5,7 +5,7 @@ % which can be provided as either an integer (the raw index in the spline % set) or a string defining a column label that should exist in the spline % set. The derivative level is given as an integer (0 for position, 1 for -% velocity, 2 for acceleration, etc.). +% velocity, 2 for acceleration, etc.). % % (GCVSplineSet, string OR int, Array of double, int) -> (Array of double) % Evaluate GCV spline values or derivatives at a set of time points. @@ -52,9 +52,14 @@ derivative = 0; end -for i = 1 : length(columnLabels) - for j = 1 : length(time) - values(j, i) = splineSet.evaluate(columnLabels(i), derivative, time(j)); +if isequal(mexext, 'mexw64') + values = evaluateGcvSplinesMexWindows(splineSet, columnLabels, time, ... + derivative); +else + for i = 1 : length(columnLabels) + for j = 1 : length(time) + values(j, i) = splineSet.evaluate(columnLabels(i), derivative, time(j)); + end end end end diff --git a/src/core/mex/evaluateGcvSplinesMexWindows.cpp b/src/core/mex/evaluateGcvSplinesMexWindows.cpp index d887e0878..23e0a5f9f 100644 --- a/src/core/mex/evaluateGcvSplinesMexWindows.cpp +++ b/src/core/mex/evaluateGcvSplinesMexWindows.cpp @@ -91,19 +91,22 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]){ vector columns = mexArrayToVectorInt(prhs[1], numColumns); vector time = mexArrayToVectorDouble(prhs[2], numPts); - GCVSplineSet *splineSet = (GCVSplineSet *) mxGetPr(prhs[0]); - int *derivative = (int *) mxGetPr(prhs[3]); + mexPrintf(typeid(dynamic_cast(mxGetPr(prhs[0]))).name()); - plhs[0] = mxCreateDoubleMatrix(numPts,numColumns,mxREAL); - double *values = mxGetPr(plhs[0]); + GCVSplineSet *splineSet = dynamic_cast(mxGetPr(prhs[0])); - for (int i = 0; i < numColumns; ++i) { - for (int j = 0; j < numPts; ++i) { - values[j + numPts * i] = splineSet->evaluate(columns[i], *derivative, time[j]); - } - } + // int derivative = mxGetScalar(prhs[3]); + + // plhs[0] = mxCreateDoubleMatrix(numPts,numColumns,mxREAL); + // double *values = mxGetPr(plhs[0]); + + // for (int i = 0; i < numColumns; i++) { + // for (int j = 0; j < numPts; j++) { + // values[j + numPts * i] = splineSet->evaluate(columns[i], 0, time[j]); + // } + // } - columns.clear(); - time.clear(); + // columns.clear(); + // time.clear(); } } \ No newline at end of file diff --git a/src/core/mex/evaluateGcvSplinesMexWindows.mexw64 b/src/core/mex/evaluateGcvSplinesMexWindows.mexw64 index aa9adb1b6fdde78b78ca84aeaa3f7f787071b4e1..d23d58b3299cf5eb1e8cd794d08b5b78f4a082a6 100644 GIT binary patch delta 6148 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 BP6Pk| delta 6406 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 From d15b06a38d596954d1c37380bcf55891c63c51f1 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Thu, 1 Feb 2024 04:33:38 -0600 Subject: [PATCH 301/365] remove gcp mex --- src/core/GCVSpline/evaluateGcvSplines.m | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/core/GCVSpline/evaluateGcvSplines.m b/src/core/GCVSpline/evaluateGcvSplines.m index 7a8bfe01e..9ae498e2e 100644 --- a/src/core/GCVSpline/evaluateGcvSplines.m +++ b/src/core/GCVSpline/evaluateGcvSplines.m @@ -52,14 +52,14 @@ derivative = 0; end -if isequal(mexext, 'mexw64') - values = evaluateGcvSplinesMexWindows(splineSet, columnLabels, time, ... - derivative); -else - for i = 1 : length(columnLabels) - for j = 1 : length(time) - values(j, i) = splineSet.evaluate(columnLabels(i), derivative, time(j)); - end +% if isequal(mexext, 'mexw64') +% values = evaluateGcvSplinesMexWindows(splineSet, columnLabels, time, ... +% derivative); +% else +for i = 1 : length(columnLabels) + for j = 1 : length(time) + values(j, i) = splineSet.evaluate(columnLabels(i), derivative, time(j)); end end +% end end From 38ca02791184a89509be81918220d582981f6bc8 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Thu, 1 Feb 2024 16:38:26 -0600 Subject: [PATCH 302/365] fix gcv spline time to all --- .../calcTrackingControllerIntegrand.m | 4 ++-- .../calcTrackingCoordinateIntegrand.m | 2 +- .../calcTrackingExternalForcesIntegrand.m | 2 +- .../calcTrackingExternalMomentsIntegrand.m | 2 +- .../calcTrackingInverseDynamicLoadsIntegrand.m | 2 +- .../IntegrandTerms/calcTrackingMarkerPosition.m | 2 +- .../calcTrackingMuscleActivationIntegrand.m | 2 +- .../IntegrandTerms/calcTrackingSpeedIntegrand.m | 2 +- src/core/GCVSpline/evaluateGcvSplines.m | 16 ++++++++-------- 9 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m index 9b8f8df89..08298251c 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m @@ -38,7 +38,7 @@ indx = find(strcmp(convertCharsToStrings( ... inputs.synergyLabels), controllerName)); if ~isempty(indx) - if size(time) == size(inputs.collocationTimeOriginal) && ... + if all(size(time) == size(inputs.collocationTimeOriginal)) && ... all(time == inputs.collocationTimeOriginal) synergyActivations = inputs.splinedSynergyActivations; else @@ -56,7 +56,7 @@ indx2 = find(strcmp(convertCharsToStrings( ... strcat(inputs.torqueControllerCoordinateNames, '_moment')), ... controllerName)); -if size(time) == size(inputs.collocationTimeOriginal) && ... +if all(size(time) == size(inputs.collocationTimeOriginal)) && ... all(time == inputs.collocationTimeOriginal) experimentalJointMoments = inputs.splinedTorqueControls; else diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m index 30f38db35..0afb3455e 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m @@ -39,7 +39,7 @@ strcat("Coordinate ", coordinateName, " is not in the ", ... ""))) end -if size(time) == size(inputs.collocationTimeOriginal) && ... +if all(size(time) == size(inputs.collocationTimeOriginal)) && ... all(time == inputs.collocationTimeOriginal) experimentalJointAngles = inputs.splinedJointAngles; else diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m index a36dae5a9..9b5587c4a 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m @@ -36,7 +36,7 @@ indx = find(strcmp(convertCharsToStrings(inputs.contactSurfaces{i}. ... forceColumns), forceName)); if ~isempty(indx) - if size(time) == size(inputs.collocationTimeOriginal) && ... + if all(size(time) == size(inputs.collocationTimeOriginal)) && ... all(time == inputs.collocationTimeOriginal) experimentalGroundReactions = inputs.splinedGroundReactionForces{i}; else diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m index 3c767a582..67efca3a1 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m @@ -36,7 +36,7 @@ indx = find(strcmp(convertCharsToStrings(inputs.contactSurfaces{i}. ... momentColumns), loadName)); if ~isempty(indx) - if size(time) == size(inputs.collocationTimeOriginal) && ... + if all(size(time) == size(inputs.collocationTimeOriginal)) && ... all(time == inputs.collocationTimeOriginal) experimentalGroundReactions = inputs.splinedGroundReactionMoments{i}; else diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m index f8a4c959c..648a98dc2 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m @@ -33,7 +33,7 @@ normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); indx = find(strcmp(inputs.inverseDynamicsMomentLabels, loadName)); -if size(time) == size(inputs.collocationTimeOriginal) && ... +if all(size(time) == size(inputs.collocationTimeOriginal)) && ... all(time == inputs.collocationTimeOriginal) experimentalJointMoments = inputs.splinedJointMoments; else diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m index 5f958727b..4fb425e26 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m @@ -38,7 +38,7 @@ strcat("Marker ", costTerm.marker, " is not in the ", ... "list of tracked markers"))) end -if size(time) == size(inputs.collocationTimeOriginal) && ... +if all(size(time) == size(inputs.collocationTimeOriginal)) && ... all(time == inputs.collocationTimeOriginal) experimentalMarkerPositions = inputs.splinedMarkerPositions{indx}; else diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m index 3ccc904ff..f663d6560 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m @@ -34,7 +34,7 @@ "normalize_by_final_time", true); indx = find(strcmp(convertCharsToStrings(inputs.muscleNames), ... muscleName)); -if size(time) == size(inputs.collocationTimeOriginal) && ... +if all(size(time) == size(inputs.collocationTimeOriginal)) && ... all(time == inputs.collocationTimeOriginal) experimentalMuscleActivations = inputs.splinedMuscleActivations; else diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m index 59d2f6fb7..29fd2ac4a 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m @@ -39,7 +39,7 @@ strcat("Coordinate ", coordinateName, " is not in the ", ... ""))) end -if size(time) == size(inputs.collocationTimeOriginal) && ... +if all(size(time) == size(inputs.collocationTimeOriginal)) && ... all(time == inputs.collocationTimeOriginal) experimentalJointVelocities = inputs.splinedJointSpeeds; else diff --git a/src/core/GCVSpline/evaluateGcvSplines.m b/src/core/GCVSpline/evaluateGcvSplines.m index 7a8bfe01e..9ae498e2e 100644 --- a/src/core/GCVSpline/evaluateGcvSplines.m +++ b/src/core/GCVSpline/evaluateGcvSplines.m @@ -52,14 +52,14 @@ derivative = 0; end -if isequal(mexext, 'mexw64') - values = evaluateGcvSplinesMexWindows(splineSet, columnLabels, time, ... - derivative); -else - for i = 1 : length(columnLabels) - for j = 1 : length(time) - values(j, i) = splineSet.evaluate(columnLabels(i), derivative, time(j)); - end +% if isequal(mexext, 'mexw64') +% values = evaluateGcvSplinesMexWindows(splineSet, columnLabels, time, ... +% derivative); +% else +for i = 1 : length(columnLabels) + for j = 1 : length(time) + values(j, i) = splineSet.evaluate(columnLabels(i), derivative, time(j)); end end +% end end From c42f647bc9c7a522603d564a1c7adfec419332ba Mon Sep 17 00:00:00 2001 From: Spencer Williams <54556034+SpencerTWilliams@users.noreply.github.com> Date: Thu, 1 Feb 2024 18:51:02 -0600 Subject: [PATCH 303/365] Fix midfoot superior calculation --- .../TrackingOptimization/calcTorqueBasedModeledValues.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m b/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m index 0140cb18d..61b8501e9 100644 --- a/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m +++ b/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m @@ -102,7 +102,8 @@ inputs.contactSurfaces{i}.midfootSuperiorPointOnBody, ... inputs.contactSurfaces{i}.midfootSuperiorBody, ... inputs.mexModel, inputs.coordinateNames); - bodyLocations.midfootSuperior{i}(:, 2) = 0; + bodyLocations.midfootSuperior{i}(:, 2) = ... + inputs.contactSurfaces{i}.restingSpringLength; bodyLocations.parent{i} = pointKinematics(time, statePositions, ... stateVelocities, [0 0 0], inputs.contactSurfaces{i}.parentBody, ... inputs.mexModel, inputs.coordinateNames); From aeb03b1b551263453ec666ddf3a9a7be31a897eb Mon Sep 17 00:00:00 2001 From: Spencer Williams <54556034+SpencerTWilliams@users.noreply.github.com> Date: Sat, 3 Feb 2024 12:52:04 -0600 Subject: [PATCH 304/365] Ground reactions on hindfoot --- .../TrackingOptimization/calcTorqueBasedModeledValues.m | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m b/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m index 61b8501e9..b5126b58c 100644 --- a/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m +++ b/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m @@ -67,6 +67,7 @@ values.positions, values.velocities, inputs); groundReactions = calcFootGroundReactions(springPositions, ... springVelocities, inputs, modeledValues.bodyLocations); + modeledValues.bodyLocations.child = modeledValues.bodyLocations.parent; groundReactionsBody = tranferGroundReactionMoments( ... modeledValues.bodyLocations, groundReactions, inputs); modeledValues.groundReactionsLab = calcGroundReactionsLab(groundReactions); @@ -124,8 +125,11 @@ childMoment = transferMoments(bodyLocations.midfootSuperior{i}, ... bodyLocations.child{i}, groundReactions.childMoments{i}, ... groundReactions.childForces{i}); + parentMoment = parentMoment + childMoment; + childMoment(:) = 0; groundReactionsBody = [groundReactionsBody ... - groundReactions.parentForces{i} groundReactions.childForces{i} ... + groundReactions.parentForces{i} + groundReactions.childForces{i}, ... + zeros(size(groundReactions.childForces{i})) ... parentMoment childMoment]; end end From 7797a128cbbce3b7ad317441994c3ace102e6456 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Sat, 3 Feb 2024 15:24:11 -0600 Subject: [PATCH 305/365] ID slope tracking term --- ...calcTrackingInverseDynamicSlopeIntegrand.m | 60 +++++++++++++++++++ .../generateCostTermStruct.m | 11 ++++ 2 files changed, 71 insertions(+) create mode 100644 src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicSlopeIntegrand.m diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicSlopeIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicSlopeIntegrand.m new file mode 100644 index 000000000..a1ef65304 --- /dev/null +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicSlopeIntegrand.m @@ -0,0 +1,60 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function calculates the difference between the experimental and +% predicted inverse dynamic moments for the specified coordinate. +% +% (struct, Array of number, 2D matrix, Array of string) -> (Array of number) +% + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Marleny Vega, Spencer Williams % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function cost = calcTrackingInverseDynamicSlopeIntegrand(costTerm, ... + inputs, time, inverseDynamicsMoments, loadName) +normalizeByFinalTime = valueOrAlternate(costTerm, ... + "normalize_by_final_time", true); +indx = find(strcmp(inputs.inverseDynamicsMomentLabels, loadName)); +if all(size(time) == size(inputs.collocationTimeOriginal)) && ... + all(time == inputs.collocationTimeOriginal) + experimentalJointMoments = inputs.splinedJointMoments; +else + experimentalJointMoments = evaluateGcvSplines( ... + inputs.splineJointMoments, inputs.inverseDynamicsMomentLabels, time); +end +if size(inverseDynamicsMoments, 2) ~= size(experimentalJointMoments, 2) + momentLabelsNoSuffix = erase(inputs.inverseDynamicsMomentLabels, '_moment'); + momentLabelsNoSuffix = erase(momentLabelsNoSuffix, '_force'); + includedJointMomentCols = ismember(momentLabelsNoSuffix, convertCharsToStrings(inputs.coordinateNames)); + experimentalJointMoments = experimentalJointMoments(:, includedJointMomentCols); +end + +timeDiff = diff(time); +experimentalDiff = diff(experimentalJointMoments, 1) ./ timeDiff; +experimentalDiff(end + 1, :) = 0; +modeledDiff = diff(inverseDynamicsMoments, 1) ./ timeDiff; +modeledDiff(end + 1, :) = 0; + +cost = calcTrackingCostArrayTerm(experimentalDiff, modeledDiff, indx); +if normalizeByFinalTime + cost = cost / time(end); +end +end diff --git a/src/TreatmentOptimization/generateCostTermStruct.m b/src/TreatmentOptimization/generateCostTermStruct.m index af1c24e3b..1e3573244 100644 --- a/src/TreatmentOptimization/generateCostTermStruct.m +++ b/src/TreatmentOptimization/generateCostTermStruct.m @@ -50,6 +50,7 @@ "marker_position_tracking", ... "inverse_dynamics_load_tracking", ... "inverse_dynamics_load_minimization", ... + "inverse_dynamics_slope_tracking", ... "kinetic_inconsistency_minimization", ... "external_force_tracking", ... "external_moment_tracking", ... @@ -70,6 +71,7 @@ "generalized_speed_tracking", ... "marker_position_tracking", ... "inverse_dynamics_load_tracking", ... + "inverse_dynamics_slope_tracking", ... "external_force_tracking", ... "external_moment_tracking", ... "muscle_activation_tracking", ... @@ -201,6 +203,15 @@ modeledValues.inverseDynamicsMoments, ... costTerm.load ... ); + +costTermCalculations.inverse_dynamics_slope_tracking = @(values, modeledValues, auxdata, costTerm) ... + calcTrackingInverseDynamicSlopeIntegrand( ... + costTerm, ... + auxdata, ... + values.time, ... + modeledValues.inverseDynamicsMoments, ... + costTerm.load ... + ); costTermCalculations.kinetic_inconsistency_minimization = @(values, modeledValues, auxdata, costTerm) ... calcMinimizingKineticInconsistencyIntegrand( ... From 0ab9df6eaf70948485836dade4412aba41ea3de8 Mon Sep 17 00:00:00 2001 From: Spencer Williams <54556034+SpencerTWilliams@users.noreply.github.com> Date: Sat, 3 Feb 2024 17:40:07 -0600 Subject: [PATCH 306/365] Fix coordinate names in periodicity constraints --- src/TreatmentOptimization/generateConstraintTermStruct.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/TreatmentOptimization/generateConstraintTermStruct.m b/src/TreatmentOptimization/generateConstraintTermStruct.m index eea94aa38..2cddaa05a 100644 --- a/src/TreatmentOptimization/generateConstraintTermStruct.m +++ b/src/TreatmentOptimization/generateConstraintTermStruct.m @@ -229,14 +229,14 @@ constraintTermCalculations.state_position_periodicity = @(values, modeledValues, auxdata, constraintTerm) ... calcStatePositionPeriodicity( ... values.statePositions, ... - auxdata.coordinateNames, ... + auxdata.statesCoordinateNames, ... constraintTerm.coordinate ... ); constraintTermCalculations.state_velocity_periodicity = @(values, modeledValues, auxdata, constraintTerm) ... calcStateVelocityPeriodicity( ... values.stateVelocities, ... - auxdata.coordinateNames, ... + auxdata.statesCoordinateNames, ... constraintTerm.coordinate ... ); From e93e483ae4ecb9c2007132a928cf570fd1fc930c Mon Sep 17 00:00:00 2001 From: Spencer Williams <54556034+SpencerTWilliams@users.noreply.github.com> Date: Mon, 5 Feb 2024 15:30:21 -0600 Subject: [PATCH 307/365] Separate hindfoot and toes ground reactions --- .../calcTorqueBasedModeledValues.m | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m b/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m index b5126b58c..633c53f3d 100644 --- a/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m +++ b/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m @@ -67,7 +67,7 @@ values.positions, values.velocities, inputs); groundReactions = calcFootGroundReactions(springPositions, ... springVelocities, inputs, modeledValues.bodyLocations); - modeledValues.bodyLocations.child = modeledValues.bodyLocations.parent; + % modeledValues.bodyLocations.child = modeledValues.bodyLocations.parent; groundReactionsBody = tranferGroundReactionMoments( ... modeledValues.bodyLocations, groundReactions, inputs); modeledValues.groundReactionsLab = calcGroundReactionsLab(groundReactions); @@ -125,12 +125,12 @@ childMoment = transferMoments(bodyLocations.midfootSuperior{i}, ... bodyLocations.child{i}, groundReactions.childMoments{i}, ... groundReactions.childForces{i}); - parentMoment = parentMoment + childMoment; - childMoment(:) = 0; - groundReactionsBody = [groundReactionsBody ... - groundReactions.parentForces{i} + groundReactions.childForces{i}, ... - zeros(size(groundReactions.childForces{i})) ... - parentMoment childMoment]; + % parentMoment = parentMoment + childMoment; + % childMoment(:) = 0; + groundReactionsBody = [groundReactionsBody, ... + groundReactions.parentForces{i}, groundReactions.childForces{i}, ... + ... zeros(size(groundReactions.childForces{i})) ... + parentMoment, childMoment]; end end From 1479a31cb58fbf2d5f9f25dbbb661349e25ed9eb Mon Sep 17 00:00:00 2001 From: Spencer Williams Date: Wed, 7 Feb 2024 16:39:31 -0600 Subject: [PATCH 308/365] Warn if multiple cost terms track same marker --- .../IntegrandTerms/calcTrackingMarkerPosition.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m index 4fb425e26..a18611ac9 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m @@ -38,6 +38,8 @@ strcat("Marker ", costTerm.marker, " is not in the ", ... "list of tracked markers"))) end +assert(length(indx) == 1, "Marker " + costTerm.marker + ... + " must only have one tracking cost term.") if all(size(time) == size(inputs.collocationTimeOriginal)) && ... all(time == inputs.collocationTimeOriginal) experimentalMarkerPositions = inputs.splinedMarkerPositions{indx}; From 38c11d882bf77850e42d5b80aade325daa48bc59 Mon Sep 17 00:00:00 2001 From: Spencer Williams <54556034+SpencerTWilliams@users.noreply.github.com> Date: Fri, 9 Feb 2024 18:44:34 -0600 Subject: [PATCH 309/365] Save ncp activation moments --- .../NeuralControlPersonalizationTool.m | 16 +++++++++------- .../saveNeuralControlPersonalizationResults.m | 17 +++++++++++++---- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/NeuralControlPersonalization/NeuralControlPersonalizationTool.m b/src/NeuralControlPersonalization/NeuralControlPersonalizationTool.m index 83d807c45..dfecfb3d4 100644 --- a/src/NeuralControlPersonalization/NeuralControlPersonalizationTool.m +++ b/src/NeuralControlPersonalization/NeuralControlPersonalizationTool.m @@ -47,17 +47,19 @@ function NeuralControlPersonalizationTool(settingsFileName) optimizedValues, inputs, params); [synergyWeights, synergyCommands] = normalizeSynergiesByMaximumWeight(... synergyWeights, synergyCommands); -combinedActivations = combineFinalActivations(inputs, synergyWeights, ... - synergyCommands); -muscleJointMoments = calcFinalMuscleJointMoments(inputs, ... +[combinedActivations, ncpActivations] = combineFinalActivations(inputs, ... + synergyWeights, synergyCommands); +combinedMuscleJointMoments = calcFinalMuscleJointMoments(inputs, ... combinedActivations); +ncpMuscleJointMoments = calcFinalMuscleJointMoments(inputs, ... + ncpActivations); saveNeuralControlPersonalizationResults(synergyWeights, ... - synergyCommands, combinedActivations, muscleJointMoments, inputs, ... - resultsDirectory, precalInputs); + synergyCommands, combinedActivations, combinedMuscleJointMoments, ... + ncpMuscleJointMoments, inputs, resultsDirectory, precalInputs); end -function combinedActivations = combineFinalActivations(inputs, ... - synergyWeights, synergyCommands) +function [combinedActivations, synergyActivations] = ... + combineFinalActivations(inputs, synergyWeights, synergyCommands) synergyActivations = zeros(inputs.numTrials, inputs.numMuscles, ... inputs.numPoints); for i = 1:inputs.numTrials diff --git a/src/NeuralControlPersonalization/saveNeuralControlPersonalizationResults.m b/src/NeuralControlPersonalization/saveNeuralControlPersonalizationResults.m index af4d47b95..3a31364ad 100644 --- a/src/NeuralControlPersonalization/saveNeuralControlPersonalizationResults.m +++ b/src/NeuralControlPersonalization/saveNeuralControlPersonalizationResults.m @@ -1,5 +1,5 @@ function saveNeuralControlPersonalizationResults(synergyWeights, ... - synergyCommands, activations, moments, inputs, resultsDirectory, ... + synergyCommands, activations, combinedMoments, ncpMoments, inputs, resultsDirectory, ... precalInputs) if ~exist(resultsDirectory, "dir") mkdir(resultsDirectory); @@ -41,7 +41,6 @@ function saveNeuralControlPersonalizationResults(synergyWeights, ... inputs.prefixes(i) + "_combinedActivations.sto" ... ) ... ) - tempMoments = permute(moments(i, :, :), [3 1 2]); momentColumns = inputs.coordinateNames; model = Model(inputs.modelFileName); for j = 1:length(momentColumns) @@ -54,11 +53,21 @@ function saveNeuralControlPersonalizationResults(synergyWeights, ... writeToSto( ... momentColumns, ... inputs.time(i, :), ... - tempMoments, ... + permute(combinedMoments(i, :, :), [3 1 2]), ... fullfile( ... resultsDirectory, ... "modelMoments", ... - inputs.prefixes(i) + "_modeledMoments.sto" ... + inputs.prefixes(i) + "_modeledMomentsMtpNcpCombined.sto" ... + ) ... + ) + writeToSto( ... + momentColumns, ... + inputs.time(i, :), ... + permute(ncpMoments(i, :, :), [3 1 2]), ... + fullfile( ... + resultsDirectory, ... + "modelMoments", ... + inputs.prefixes(i) + "_modeledMomentsNcp.sto" ... ) ... ) end From e700b07676b4ea298dfe6dc61e4af7a8f0048d9e Mon Sep 17 00:00:00 2001 From: Spencer Williams <54556034+SpencerTWilliams@users.noreply.github.com> Date: Fri, 9 Feb 2024 18:44:51 -0600 Subject: [PATCH 310/365] Fix model used for ID --- src/TreatmentOptimization/modifyModelForces.m | 1 + 1 file changed, 1 insertion(+) diff --git a/src/TreatmentOptimization/modifyModelForces.m b/src/TreatmentOptimization/modifyModelForces.m index c333e73c9..524bc7147 100644 --- a/src/TreatmentOptimization/modifyModelForces.m +++ b/src/TreatmentOptimization/modifyModelForces.m @@ -34,4 +34,5 @@ model = addContactSurfaceActuators(inputs, model); inputs.mexModel = strcat(strrep(inputs.modelFileName,'.osim',''), '_inactiveMuscles.osim'); model.print(inputs.mexModel); +inputs.model = Model(inputs.mexModel); end \ No newline at end of file From bd2de81af10602bf73c363cf0bae7a0696c435db Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Fri, 9 Feb 2024 19:57:59 -0600 Subject: [PATCH 311/365] Restore MTP osimx saving --- .../Saving/saveMuscleTendonPersonalizationResults.m | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m b/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m index cbf24d39f..1c4ec1a48 100644 --- a/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m +++ b/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m @@ -53,8 +53,8 @@ function saveMuscleTendonPersonalizationResults(mtpInputs, finalValues, ... saveMtpJointMomentData(mtpInputs, resultsStruct, analysisDirectory); saveMtpMuscleModelParameters(mtpInputs, finalValues, ... fullfile(analysisDirectory, "muscleModelParameters")); -% model = Model(mtpInputs.model); -% muscleNames = getMusclesFromCoordinates(model, mtpInputs.coordinateNames); -% writeMuscleTendonPersonalizationOsimxFile(mtpInputs.modelFileName, ... -% mtpInputs.osimxFileName, finalValues, muscleNames, resultsDirectory); +model = Model(mtpInputs.model); +muscleNames = getMusclesFromCoordinates(model, mtpInputs.coordinateNames); +writeMuscleTendonPersonalizationOsimxFile(mtpInputs.modelFileName, ... + mtpInputs.osimxFileName, finalValues, muscleNames, resultsDirectory); end \ No newline at end of file From 497dd666ecdae9824498c45e32b03961d35b22ba Mon Sep 17 00:00:00 2001 From: RobSalati Date: Fri, 9 Feb 2024 23:35:35 -0600 Subject: [PATCH 312/365] Dynamics plotting Wrote function to plot treatment optimization dynamics from saved files. --- .../Bvpj7fG3aQHDQZkSkAm5Y7Mm79Id.xml | 2 + .../Bvpj7fG3aQHDQZkSkAm5Y7Mm79Ip.xml | 2 + .../-3b1n-XfyCyTaGBiCjyzBR9pLL8d.xml | 6 ++ .../-3b1n-XfyCyTaGBiCjyzBR9pLL8p.xml | 2 + .../VBzNr-UJnK1VpshGIqoYe2h0C2cd.xml | 6 ++ .../VBzNr-UJnK1VpshGIqoYe2h0C2cp.xml | 2 + .../cGlRp54T3CcqS_R5IVqnmxrQlfAd.xml | 6 ++ .../cGlRp54T3CcqS_R5IVqnmxrQlfAp.xml | 2 + .../eGRpb3Y9fnUsdgOMh_ITJhT6cvQd.xml | 2 + .../eGRpb3Y9fnUsdgOMh_ITJhT6cvQp.xml | 2 + .../j1su1TAZRkcY37B9D8owiFMCF0wd.xml | 6 ++ .../j1su1TAZRkcY37B9D8owiFMCF0wp.xml | 2 + .../cqycJC3N6OF8UO0dBYEaEl86UlQd.xml | 2 + .../cqycJC3N6OF8UO0dBYEaEl86UlQp.xml | 2 + .../plotTreatmentOptimizationActivations.m | 43 +++++++++ .../plotTreatmentOptimizationAngles.m | 42 +++++++++ .../plotTreatmentOptimizationDynamics.m | 87 +++++++++++++++++++ ...plotTreatmentOptimizationGroundReactions.m | 42 +++++++++ 18 files changed, 258 insertions(+) create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/Bvpj7fG3aQHDQZkSkAm5Y7Mm79Id.xml create mode 100644 resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/Bvpj7fG3aQHDQZkSkAm5Y7Mm79Ip.xml create mode 100644 resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/-3b1n-XfyCyTaGBiCjyzBR9pLL8d.xml create mode 100644 resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/-3b1n-XfyCyTaGBiCjyzBR9pLL8p.xml create mode 100644 resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/VBzNr-UJnK1VpshGIqoYe2h0C2cd.xml create mode 100644 resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/VBzNr-UJnK1VpshGIqoYe2h0C2cp.xml create mode 100644 resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/cGlRp54T3CcqS_R5IVqnmxrQlfAd.xml create mode 100644 resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/cGlRp54T3CcqS_R5IVqnmxrQlfAp.xml create mode 100644 resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/eGRpb3Y9fnUsdgOMh_ITJhT6cvQd.xml create mode 100644 resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/eGRpb3Y9fnUsdgOMh_ITJhT6cvQp.xml create mode 100644 resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/j1su1TAZRkcY37B9D8owiFMCF0wd.xml create mode 100644 resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/j1su1TAZRkcY37B9D8owiFMCF0wp.xml create mode 100644 resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/cqycJC3N6OF8UO0dBYEaEl86UlQd.xml create mode 100644 resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/cqycJC3N6OF8UO0dBYEaEl86UlQp.xml create mode 100644 src/TreatmentOptimization/Analysis/plotTreatmentOptimizationActivations.m create mode 100644 src/TreatmentOptimization/Analysis/plotTreatmentOptimizationAngles.m create mode 100644 src/TreatmentOptimization/Analysis/plotTreatmentOptimizationDynamics.m create mode 100644 src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/Bvpj7fG3aQHDQZkSkAm5Y7Mm79Id.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/Bvpj7fG3aQHDQZkSkAm5Y7Mm79Id.xml new file mode 100644 index 000000000..7860bdaee --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/Bvpj7fG3aQHDQZkSkAm5Y7Mm79Id.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/Bvpj7fG3aQHDQZkSkAm5Y7Mm79Ip.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/Bvpj7fG3aQHDQZkSkAm5Y7Mm79Ip.xml new file mode 100644 index 000000000..2b49b5d5b --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/Bvpj7fG3aQHDQZkSkAm5Y7Mm79Ip.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/-3b1n-XfyCyTaGBiCjyzBR9pLL8d.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/-3b1n-XfyCyTaGBiCjyzBR9pLL8d.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/-3b1n-XfyCyTaGBiCjyzBR9pLL8d.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/-3b1n-XfyCyTaGBiCjyzBR9pLL8p.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/-3b1n-XfyCyTaGBiCjyzBR9pLL8p.xml new file mode 100644 index 000000000..428e957a9 --- /dev/null +++ b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/-3b1n-XfyCyTaGBiCjyzBR9pLL8p.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/VBzNr-UJnK1VpshGIqoYe2h0C2cd.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/VBzNr-UJnK1VpshGIqoYe2h0C2cd.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/VBzNr-UJnK1VpshGIqoYe2h0C2cd.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/VBzNr-UJnK1VpshGIqoYe2h0C2cp.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/VBzNr-UJnK1VpshGIqoYe2h0C2cp.xml new file mode 100644 index 000000000..37c2e5093 --- /dev/null +++ b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/VBzNr-UJnK1VpshGIqoYe2h0C2cp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/cGlRp54T3CcqS_R5IVqnmxrQlfAd.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/cGlRp54T3CcqS_R5IVqnmxrQlfAd.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/cGlRp54T3CcqS_R5IVqnmxrQlfAd.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/cGlRp54T3CcqS_R5IVqnmxrQlfAp.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/cGlRp54T3CcqS_R5IVqnmxrQlfAp.xml new file mode 100644 index 000000000..6f27fc2ff --- /dev/null +++ b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/cGlRp54T3CcqS_R5IVqnmxrQlfAp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/eGRpb3Y9fnUsdgOMh_ITJhT6cvQd.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/eGRpb3Y9fnUsdgOMh_ITJhT6cvQd.xml new file mode 100644 index 000000000..a75f7a81b --- /dev/null +++ b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/eGRpb3Y9fnUsdgOMh_ITJhT6cvQd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/eGRpb3Y9fnUsdgOMh_ITJhT6cvQp.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/eGRpb3Y9fnUsdgOMh_ITJhT6cvQp.xml new file mode 100644 index 000000000..842de6ab3 --- /dev/null +++ b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/eGRpb3Y9fnUsdgOMh_ITJhT6cvQp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/j1su1TAZRkcY37B9D8owiFMCF0wd.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/j1su1TAZRkcY37B9D8owiFMCF0wd.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/j1su1TAZRkcY37B9D8owiFMCF0wd.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/j1su1TAZRkcY37B9D8owiFMCF0wp.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/j1su1TAZRkcY37B9D8owiFMCF0wp.xml new file mode 100644 index 000000000..5f29da99a --- /dev/null +++ b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/j1su1TAZRkcY37B9D8owiFMCF0wp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/cqycJC3N6OF8UO0dBYEaEl86UlQd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/cqycJC3N6OF8UO0dBYEaEl86UlQd.xml new file mode 100644 index 000000000..a75f7a81b --- /dev/null +++ b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/cqycJC3N6OF8UO0dBYEaEl86UlQd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/cqycJC3N6OF8UO0dBYEaEl86UlQp.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/cqycJC3N6OF8UO0dBYEaEl86UlQp.xml new file mode 100644 index 000000000..7d0d0afa0 --- /dev/null +++ b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/cqycJC3N6OF8UO0dBYEaEl86UlQp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationActivations.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationActivations.m new file mode 100644 index 000000000..669b40c31 --- /dev/null +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationActivations.m @@ -0,0 +1,43 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function reads .sto files created by +% saveMuscleTendonPersonalizationResults.m containing inverse dynamics +% joint moments and model joint moments and creates plots of them. +% +% (string) -> (None) +% Plot joint moment curves from file. + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Robert Salati % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % +function plotTreatmentOptimizationActivations(activationsFile, ... + figureWidth, figureHeight) + if nargin < 3 + figureWidth = 8; + end + if nargin < 4 + figureHeight = 8; + end + figureSize = figureWidth * figureHeight; + import org.opensim.modeling.Storage + +end + diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationAngles.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationAngles.m new file mode 100644 index 000000000..c351aa779 --- /dev/null +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationAngles.m @@ -0,0 +1,42 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function reads .sto files created by +% saveMuscleTendonPersonalizationResults.m containing inverse dynamics +% joint moments and model joint moments and creates plots of them. +% +% (string) -> (None) +% Plot joint moment curves from file. + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Robert Salati % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % +function plotTreatmentOptimizationAngles(anglesFile, ... + figureWidth, figureHeight) + if nargin < 3 + figureWidth = 8; + end + if nargin < 4 + figureHeight = 8; + end + figureSize = figureWidth * figureHeight; + import org.opensim.modeling.Storage +end + diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationDynamics.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationDynamics.m new file mode 100644 index 000000000..ad4f7092e --- /dev/null +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationDynamics.m @@ -0,0 +1,87 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function reads .sto files created by +% saveMuscleTendonPersonalizationResults.m containing inverse dynamics +% joint moments and model joint moments and creates plots of them. +% +% (string) -> (None) +% Plot joint moment curves from file. + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Robert Salati % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % +function plotTreatmentOptimizationMoments(experimentalMomentsFile, ... + solutionMomentsFile, figureWidth, figureHeight) + if nargin < 3 + figureWidth = 8; + end + if nargin < 4 + figureHeight = 8; + end + figureSize = figureWidth * figureHeight; + import org.opensim.modeling.Storage + experimentalMomentsStorage = Storage(experimentalMomentsFile); + momentAxes = getStorageColumnNames(experimentalMomentsStorage); + experimentalMoments = storageToDoubleMatrix(experimentalMomentsStorage)'; + experimentalTime = findTimeColumn(experimentalMomentsStorage); + if experimentalTime(1) ~= 0 + experimentalTime = experimentalTime - experimentalTime(1); + end + solutionMomentsStorage = Storage(solutionMomentsFile); + solutionMoments = storageToDoubleMatrix(solutionMomentsStorage)'; + solutionTime = findTimeColumn(solutionMomentsStorage); + if solutionTime(1) ~= 0 + solutionTime = solutionTime - solutionTime(1); + end + + figure(Name="Treatment Optimization Moments") + subplotNumber = 1; + figureNumber = 1; + for i=1:numel(momentAxes) + if i > figureSize * figureNumber + figureNumber = figureNumber + 1; + figure(Name="Treatment Optimization Moments") + subplotNumber = 1; + end + subplot(figureWidth, figureHeight, subplotNumber) + hold on + plot(experimentalTime, experimentalMoments(:, i)); + plot(solutionTime, solutionMoments(:, i)); + if contains(momentAxes(i), "moment") + title(strcat(strrep(momentAxes(i), "_", " "), " [Nm]")) + elseif contains(momentAxes(i), "force") + title(strcat(strrep(momentAxes(i), "_", " "), " [N]")) + else + title(strrep(momentAxes(i), "_", " ")) + end + + hold off + xlim([0, experimentalTime(end)]) + if subplotNumber==1 + legend("Experimental Moments", "Model Moments") + end + % if subplotNumber > figureSize-figureHeight + % xlabel("Time [s]") + % end + subplotNumber = subplotNumber + 1; + end +end + diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m new file mode 100644 index 000000000..22a6b7538 --- /dev/null +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m @@ -0,0 +1,42 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function reads .sto files created by +% saveMuscleTendonPersonalizationResults.m containing inverse dynamics +% joint moments and model joint moments and creates plots of them. +% +% (string) -> (None) +% Plot joint moment curves from file. + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Robert Salati % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % +function plotTreatmentOptimizationGroundReactions(groundReactionFile, ... + figureWidth, figureHeight) + if nargin < 3 + figureWidth = 8; + end + if nargin < 4 + figureHeight = 8; + end + figureSize = figureWidth * figureHeight; + import org.opensim.modeling.Storage +end + From 888fb6ecec16ffd4a4de5cee3e71fe86d770f26c Mon Sep 17 00:00:00 2001 From: RobSalati Date: Sun, 11 Feb 2024 23:11:45 -0600 Subject: [PATCH 313/365] Plots for activations and controls --- .../WnlbACgZ449p_KqRN0_m4q3dnjMd.xml | 6 + .../WnlbACgZ449p_KqRN0_m4q3dnjMp.xml | 2 + .../sh2eKEgJT0hPZzSs5gVDE9Y3x38d.xml | 6 + .../sh2eKEgJT0hPZzSs5gVDE9Y3x38p.xml | 2 + .../plotTreatmentOptimizationActivations.m | 54 +++++++-- .../plotTreatmentOptimizationDynamics.m | 107 +++++++++--------- ...plotTreatmentOptimizationSynergyControls.m | 70 ++++++++++++ .../plotTreatmentOptimizationTorqueControls.m | 70 ++++++++++++ 8 files changed, 257 insertions(+), 60 deletions(-) create mode 100644 resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/WnlbACgZ449p_KqRN0_m4q3dnjMd.xml create mode 100644 resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/WnlbACgZ449p_KqRN0_m4q3dnjMp.xml create mode 100644 resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/sh2eKEgJT0hPZzSs5gVDE9Y3x38d.xml create mode 100644 resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/sh2eKEgJT0hPZzSs5gVDE9Y3x38p.xml create mode 100644 src/TreatmentOptimization/Analysis/plotTreatmentOptimizationSynergyControls.m create mode 100644 src/TreatmentOptimization/Analysis/plotTreatmentOptimizationTorqueControls.m diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/WnlbACgZ449p_KqRN0_m4q3dnjMd.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/WnlbACgZ449p_KqRN0_m4q3dnjMd.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/WnlbACgZ449p_KqRN0_m4q3dnjMd.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/WnlbACgZ449p_KqRN0_m4q3dnjMp.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/WnlbACgZ449p_KqRN0_m4q3dnjMp.xml new file mode 100644 index 000000000..82197d247 --- /dev/null +++ b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/WnlbACgZ449p_KqRN0_m4q3dnjMp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/sh2eKEgJT0hPZzSs5gVDE9Y3x38d.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/sh2eKEgJT0hPZzSs5gVDE9Y3x38d.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/sh2eKEgJT0hPZzSs5gVDE9Y3x38d.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/sh2eKEgJT0hPZzSs5gVDE9Y3x38p.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/sh2eKEgJT0hPZzSs5gVDE9Y3x38p.xml new file mode 100644 index 000000000..fb6062f7d --- /dev/null +++ b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/sh2eKEgJT0hPZzSs5gVDE9Y3x38p.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationActivations.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationActivations.m index 669b40c31..07fb263d4 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationActivations.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationActivations.m @@ -28,16 +28,52 @@ % implied. See the License for the specific language governing % % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function plotTreatmentOptimizationActivations(activationsFile, ... - figureWidth, figureHeight) - if nargin < 3 - figureWidth = 8; +function plotTreatmentOptimizationActivations(experimentalActivationsFile, ... + modelActivationsFile, figureWidth, figureHeight) +if nargin < 3 + figureWidth = 8; +end +if nargin < 4 + figureHeight = 8; +end +figureSize = figureWidth * figureHeight; +import org.opensim.modeling.Storage +experimentalActivationsStorage = Storage(experimentalActivationsFile); +muscleLabels = getStorageColumnNames(experimentalActivationsStorage); +experimentalActivations = storageToDoubleMatrix(experimentalActivationsStorage)'; +experimentalTime = findTimeColumn(experimentalActivationsStorage); +if experimentalTime(1) ~= 0 + experimentalTime = experimentalTime - experimentalTime(1); +end +modelActivationsStorage = Storage(modelActivationsFile); +modelActivations = storageToDoubleMatrix(modelActivationsStorage)'; +modelTime = findTimeColumn(modelActivationsStorage); +if modelTime(1) ~= 0 + modelTime = modelTime - modelTime(1); +end +figure(Name="Treatment Optimization Activations", ... + Units='normalized', ... + Position=[0 0 1 1]) +subplotNumber = 1; +figureNumber = 1; +for i=1:numel(muscleLabels) + if i > figureSize * figureNumber + figureNumber = figureNumber + 1; + figure(Name="Treatment Optimization Activations", ... + Units='normalized', ... + Position=[0 0 1 1]) + subplotNumber = 1; end - if nargin < 4 - figureHeight = 8; + subplot(figureWidth, figureHeight, subplotNumber) + hold on + plot(experimentalTime, experimentalActivations(:, i)) + plot(modelTime, modelActivations(:, i)) + hold off + title(strrep(muscleLabels(i), "_", " ")); + if subplotNumber == 1 + legend("Experimental Activations", "Model Activations") end - figureSize = figureWidth * figureHeight; - import org.opensim.modeling.Storage - + subplotNumber = subplotNumber + 1; +end end diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationDynamics.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationDynamics.m index ad4f7092e..98405ff9a 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationDynamics.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationDynamics.m @@ -28,60 +28,65 @@ % implied. See the License for the specific language governing % % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function plotTreatmentOptimizationMoments(experimentalMomentsFile, ... - solutionMomentsFile, figureWidth, figureHeight) - if nargin < 3 - figureWidth = 8; - end - if nargin < 4 - figureHeight = 8; - end - figureSize = figureWidth * figureHeight; - import org.opensim.modeling.Storage - experimentalMomentsStorage = Storage(experimentalMomentsFile); - momentAxes = getStorageColumnNames(experimentalMomentsStorage); - experimentalMoments = storageToDoubleMatrix(experimentalMomentsStorage)'; - experimentalTime = findTimeColumn(experimentalMomentsStorage); - if experimentalTime(1) ~= 0 - experimentalTime = experimentalTime - experimentalTime(1); - end - solutionMomentsStorage = Storage(solutionMomentsFile); - solutionMoments = storageToDoubleMatrix(solutionMomentsStorage)'; - solutionTime = findTimeColumn(solutionMomentsStorage); - if solutionTime(1) ~= 0 - solutionTime = solutionTime - solutionTime(1); +function plotTreatmentOptimizationDynamics(experimentalMomentsFile, ... + modelMomentsFile, figureWidth, figureHeight) +if nargin < 3 + figureWidth = 8; +end +if nargin < 4 + figureHeight = 8; +end +figureSize = figureWidth * figureHeight; +import org.opensim.modeling.Storage +experimentalMomentsStorage = Storage(experimentalMomentsFile); +momentAxes = getStorageColumnNames(experimentalMomentsStorage); +experimentalMoments = storageToDoubleMatrix(experimentalMomentsStorage)'; +experimentalTime = findTimeColumn(experimentalMomentsStorage); +if experimentalTime(1) ~= 0 + experimentalTime = experimentalTime - experimentalTime(1); +end +modelMomentsStorage = Storage(modelMomentsFile); +modelMoments = storageToDoubleMatrix(modelMomentsStorage)'; +modelTime = findTimeColumn(modelMomentsStorage); +if modelTime(1) ~= 0 + modelTime = modelTime - modelTime(1); +end + +figure(Name="Treatment Optimization Moments", ... + Units='normalized', ... + Position=[0 0 1 1]) +subplotNumber = 1; +figureNumber = 1; +for i=1:numel(momentAxes) + if i > figureSize * figureNumber + figureNumber = figureNumber + 1; + figure(Name="Treatment Optimization Moments", ... + Units='normalized', ... + Position=[0 0 1 1]) + subplotNumber = 1; end + subplot(figureWidth, figureHeight, subplotNumber) + hold on + plot(experimentalTime, experimentalMoments(:, i)); + plot(modelTime, modelMoments(:, i)); + hold off - figure(Name="Treatment Optimization Moments") - subplotNumber = 1; - figureNumber = 1; - for i=1:numel(momentAxes) - if i > figureSize * figureNumber - figureNumber = figureNumber + 1; - figure(Name="Treatment Optimization Moments") - subplotNumber = 1; - end - subplot(figureWidth, figureHeight, subplotNumber) - hold on - plot(experimentalTime, experimentalMoments(:, i)); - plot(solutionTime, solutionMoments(:, i)); - if contains(momentAxes(i), "moment") - title(strcat(strrep(momentAxes(i), "_", " "), " [Nm]")) - elseif contains(momentAxes(i), "force") - title(strcat(strrep(momentAxes(i), "_", " "), " [N]")) - else - title(strrep(momentAxes(i), "_", " ")) - end + if contains(momentAxes(i), "moment") + title(strcat(strrep(momentAxes(i), "_", " "), " [Nm]")) + elseif contains(momentAxes(i), "force") + title(strcat(strrep(momentAxes(i), "_", " "), " [N]")) + else + title(strrep(momentAxes(i), "_", " ")) + end - hold off - xlim([0, experimentalTime(end)]) - if subplotNumber==1 - legend("Experimental Moments", "Model Moments") - end - % if subplotNumber > figureSize-figureHeight - % xlabel("Time [s]") - % end - subplotNumber = subplotNumber + 1; + xlim([0, experimentalTime(end)]) + if subplotNumber==1 + legend("Experimental Moments", "Model Moments") end + % if subplotNumber > figureSize-figureHeight + % xlabel("Time [s]") + % end + subplotNumber = subplotNumber + 1; +end end diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationSynergyControls.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationSynergyControls.m new file mode 100644 index 000000000..f3abdf8a6 --- /dev/null +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationSynergyControls.m @@ -0,0 +1,70 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function reads .sto files created by +% saveMuscleTendonPersonalizationResults.m containing inverse dynamics +% joint moments and model joint moments and creates plots of them. +% +% (string) -> (None) +% Plot joint moment curves from file. + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Robert Salati % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % +function plotTreatmentOptimizationSynergyControls(controlsFile, ... + figureWidth, figureHeight) +if nargin < 2 + figureWidth = 4; +end +if nargin < 3 + figureHeight = 4; +end +figureSize = figureWidth * figureHeight; +import org.opensim.modeling.Storage +controlsStorage = Storage(controlsFile); +controlLabels = getStorageColumnNames(controlsStorage); +controls = storageToDoubleMatrix(controlsStorage)'; +time = findTimeColumn(controlsStorage); +if time(1) ~= 0 + time = time - time(1); +end +figure(Name="Treatment Optimization Controls", ... + Units='normalized', ... + Position=[0 0 1 1]) +subplotNumber = 1; +figureNumber = 1; +for i=1:numel(controlLabels) + if i > figureSize * figureNumber + figureNumber = figureNumber + 1; + figure(Name="Treatment Optimization Controls", ... + Units='normalized', ... + Position=[0 0 1 1]) + subplotNumber = 1; + end + subplot(figureWidth, figureHeight, subplotNumber) + hold on + plot(time, controls(:, i)); + hold off + title(strrep(controlLabels(i), "_", " ")); + xlim([0, time(end)]) + subplotNumber = subplotNumber + 1; +end +end + diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationTorqueControls.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationTorqueControls.m new file mode 100644 index 000000000..570791bfa --- /dev/null +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationTorqueControls.m @@ -0,0 +1,70 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function reads .sto files created by +% saveMuscleTendonPersonalizationResults.m containing inverse dynamics +% joint moments and model joint moments and creates plots of them. +% +% (string) -> (None) +% Plot joint moment curves from file. + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Robert Salati % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % +function plotTreatmentOptimizationTorqueControls(controlsFile, ... + figureWidth, figureHeight) +if nargin < 2 + figureWidth = 4; +end +if nargin < 3 + figureHeight = 4; +end +figureSize = figureWidth * figureHeight; +import org.opensim.modeling.Storage +controlsStorage = Storage(controlsFile); +controlLabels = getStorageColumnNames(controlsStorage); +controls = storageToDoubleMatrix(controlsStorage)'; +time = findTimeColumn(controlsStorage); +if time(1) ~= 0 + time = time - time(1); +end +figure(Name="Treatment Optimization Controls", ... + Units='normalized', ... + Position=[0 0 1 1]) +subplotNumber = 1; +figureNumber = 1; +for i=1:numel(controlLabels) + if i > figureSize * figureNumber + figureNumber = figureNumber + 1; + figure(Name="Treatment Optimization Controls", ... + Units='normalized', ... + Position=[0 0 1 1]) + subplotNumber = 1; + end + subplot(figureWidth, figureHeight, subplotNumber) + hold on + plot(time, controls(:, i)); + hold off + title(strrep(controlLabels(i), "_", " ")); + xlim([0, time(end)]) + subplotNumber = subplotNumber + 1; +end +end + From 05f945ac96a8c22403624ce0dc3eee23db5c0315 Mon Sep 17 00:00:00 2001 From: RobSalati <142843461+RobSalati@users.noreply.github.com> Date: Mon, 12 Feb 2024 10:20:23 -0600 Subject: [PATCH 314/365] Angle plotting --- .../plotTreatmentOptimizationAngles.m | 64 ++++++++++++++++--- .../plotTreatmentOptimizationDynamics.m | 6 +- 2 files changed, 60 insertions(+), 10 deletions(-) diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationAngles.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationAngles.m index c351aa779..9d9b398ef 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationAngles.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationAngles.m @@ -28,15 +28,63 @@ % implied. See the License for the specific language governing % % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function plotTreatmentOptimizationAngles(anglesFile, ... - figureWidth, figureHeight) - if nargin < 3 - figureWidth = 8; +function plotTreatmentOptimizationAngles(experimentalAnglesFile, ... + modelAnglesFile, figureWidth, figureHeight) +% Get these to degrees instead of radians +if nargin < 3 + figureWidth = 8; +end +if nargin < 4 + figureHeight = 8; +end +figureSize = figureWidth * figureHeight; +import org.opensim.modeling.Storage +experimentalAnglesStorage = Storage(experimentalAnglesFile); +angleLabels = getStorageColumnNames(experimentalAnglesStorage); +experimentalAngles = storageToDoubleMatrix(experimentalAnglesStorage)'; +experimentalAngles = experimentalAngles .* 180/pi; +experimentalTime = findTimeColumn(experimentalAnglesStorage); +if experimentalTime(1) ~= 0 + experimentalTime = experimentalTime - experimentalTime(1); +end +modelAnglesStorage = Storage(modelAnglesFile); +modelAngles = storageToDoubleMatrix(modelAnglesStorage)'; +modelAngles = modelAngles .* 180/pi; +modelTime = findTimeColumn(modelAnglesStorage); +if modelTime(1) ~= 0 + modelTime = modelTime - modelTime(1); +end + +figure(Name = "Treatment Optimization Angles", ... + Units='normalized', ... + Position=[0 0 1 1]) +subplotNumber = 1; +figureNumber = 1; +for i=1:numel(angleLabels) + if i > figureSize * figureNumber + figureNumber = figureNumber + 1; + figure(Name="Treatment Optimization Angles", ... + Units='normalized', ... + Position=[0 0 1 1]) + subplotNumber = 1; + end + subplot(figureWidth, figureHeight, subplotNumber) + hold on + plot(experimentalTime, experimentalAngles(:, i)); + plot(modelTime, modelAngles(:, i)); + hold off + title(strrep(angleLabels(i), "_", " ")); + + if subplotNumber==1 + legend("Experimental Angles", "Model Angles") end - if nargin < 4 - figureHeight = 8; + + xlim([0, experimentalTime(end)]) + maxAngle = max([experimentalAngles(:, i); modelAngles(:, i)],[], "all"); + minAngle = min([experimentalAngles(:, i); modelAngles(:, i)],[], "all"); + if maxAngle-minAngle < 10 + ylim([(maxAngle+minAngle)/2-5, (maxAngle+minAngle)/2+5]) end - figureSize = figureWidth * figureHeight; - import org.opensim.modeling.Storage + subplotNumber = subplotNumber + 1; end diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationDynamics.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationDynamics.m index 98405ff9a..48e87259a 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationDynamics.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationDynamics.m @@ -78,11 +78,13 @@ function plotTreatmentOptimizationDynamics(experimentalMomentsFile, ... else title(strrep(momentAxes(i), "_", " ")) end - - xlim([0, experimentalTime(end)]) + + if subplotNumber==1 legend("Experimental Moments", "Model Moments") end + + xlim([0, experimentalTime(end)]) % if subplotNumber > figureSize-figureHeight % xlabel("Time [s]") % end From 62fcc1e2ba1dc57fbcb8bf02b7876f0b3a9f84bc Mon Sep 17 00:00:00 2001 From: Spencer Williams <54556034+SpencerTWilliams@users.noreply.github.com> Date: Mon, 12 Feb 2024 21:35:28 -0600 Subject: [PATCH 315/365] Fix moment indices for NCP --- .../CostComputations/calcMomentTrackingCost.m | 11 ----- .../reorderPreprocessedDataByMuscleNames.m | 47 ++++++++++++------- 2 files changed, 29 insertions(+), 29 deletions(-) delete mode 100644 src/NeuralControlPersonalization/Optimization/CostComputations/calcMomentTrackingCost.m diff --git a/src/NeuralControlPersonalization/Optimization/CostComputations/calcMomentTrackingCost.m b/src/NeuralControlPersonalization/Optimization/CostComputations/calcMomentTrackingCost.m deleted file mode 100644 index c02c9aff6..000000000 --- a/src/NeuralControlPersonalization/Optimization/CostComputations/calcMomentTrackingCost.m +++ /dev/null @@ -1,11 +0,0 @@ -function cost = calcMomentTrackingCost(modeledValues, ... - experimentalData, params) -costWeight = valueOrAlternate(params, "momentTrackingCostWeight", 1); -errorCenter = valueOrAlternate(params, "momentTrackingErrorCenter", 0); -maximumAllowableError = valueOrAlternate(params, ... - "momentTrackingMaximumAllowableError", 2); -cost = costWeight * calcTrackingCostTerm( ... - modeledValues.muscleJointMoments, ... - experimentalData.muscleJointMoments, errorCenter, ... - maximumAllowableError); -end \ No newline at end of file diff --git a/src/core/parse/reorderPreprocessedDataByMuscleNames.m b/src/core/parse/reorderPreprocessedDataByMuscleNames.m index 9a75d8b0d..25af98255 100644 --- a/src/core/parse/reorderPreprocessedDataByMuscleNames.m +++ b/src/core/parse/reorderPreprocessedDataByMuscleNames.m @@ -1,7 +1,10 @@ function inputs = reorderPreprocessedDataByMuscleNames(inputs, muscleNames) -indexesInParsedMuscleData = ... - ismember(inputs.muscleTendonColumnNames, muscleNames); +% indexesInParsedMuscleData = ... +% ismember(inputs.muscleTendonColumnNames, muscleNames); + +[~, ~, indexesInParsedMuscleData] = intersect( ... + muscleNames, inputs.muscleTendonColumnNames, 'stable'); inputs.muscleTendonColumnNames = ... inputs.muscleTendonColumnNames(indexesInParsedMuscleData); @@ -10,16 +13,21 @@ inputs.muscleTendonVelocity = inputs.muscleTendonVelocity(:, ... indexesInParsedMuscleData, :); -indexesInParsedCoordinateData = ... - ismember( ... - inputs.inverseDynamicsMomentsColumnNames, ... - inputs.coordinateNames ... - ); -indexesInParsedMuscleAnalysisData = ... - ismember( ... - inputs.momentArmsCoordinateNames, ... - inputs.coordinateNames ... - ); +% indexesInParsedCoordinateData = ... +% ismember( ... +% inputs.inverseDynamicsMomentsColumnNames, ... +% inputs.coordinateNames ... +% ); +% indexesInParsedMuscleAnalysisData = ... +% ismember( ... +% inputs.momentArmsCoordinateNames, ... +% inputs.coordinateNames ... +% ); + +[~, ~, indexesInParsedCoordinateData] = intersect( ... + inputs.coordinateNames, inputs.inverseDynamicsMomentsColumnNames, 'stable'); +[~, ~, indexesInParsedMuscleAnalysisData] = intersect( ... + inputs.coordinateNames, inputs.momentArmsCoordinateNames, 'stable'); inputs.inverseDynamicsMoments = ... inputs.inverseDynamicsMoments(:, indexesInParsedCoordinateData, :); @@ -27,13 +35,16 @@ indexesInParsedMuscleData, :); if (isfield(inputs, "mtpActivationsColumnNames")) -indexesInParsedActivationData = ... - ismember(inputs.mtpActivationsColumnNames, muscleNames); + % indexesInParsedActivationData = ... + % ismember(inputs.mtpActivationsColumnNames, muscleNames); + + [~, ~, indexesInParsedActivationData] = ... + intersect(muscleNames, inputs.mtpActivationsColumnNames, 'stable'); -inputs.mtpActivationsColumnNames = ... - inputs.mtpActivationsColumnNames(indexesInParsedActivationData); -inputs.mtpActivations = ... - inputs.mtpActivations(:, indexesInParsedActivationData, :); + inputs.mtpActivationsColumnNames = ... + inputs.mtpActivationsColumnNames(indexesInParsedActivationData); + inputs.mtpActivations = ... + inputs.mtpActivations(:, indexesInParsedActivationData, :); end end From 94b35032043c877a7f7cd925b24c59544e5f715e Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 12 Feb 2024 21:40:56 -0600 Subject: [PATCH 316/365] Remove unused ismember() --- .../reorderPreprocessedDataByMuscleNames.m | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/core/parse/reorderPreprocessedDataByMuscleNames.m b/src/core/parse/reorderPreprocessedDataByMuscleNames.m index 25af98255..12e41b93a 100644 --- a/src/core/parse/reorderPreprocessedDataByMuscleNames.m +++ b/src/core/parse/reorderPreprocessedDataByMuscleNames.m @@ -1,8 +1,5 @@ function inputs = reorderPreprocessedDataByMuscleNames(inputs, muscleNames) -% indexesInParsedMuscleData = ... -% ismember(inputs.muscleTendonColumnNames, muscleNames); - [~, ~, indexesInParsedMuscleData] = intersect( ... muscleNames, inputs.muscleTendonColumnNames, 'stable'); @@ -13,17 +10,6 @@ inputs.muscleTendonVelocity = inputs.muscleTendonVelocity(:, ... indexesInParsedMuscleData, :); -% indexesInParsedCoordinateData = ... -% ismember( ... -% inputs.inverseDynamicsMomentsColumnNames, ... -% inputs.coordinateNames ... -% ); -% indexesInParsedMuscleAnalysisData = ... -% ismember( ... -% inputs.momentArmsCoordinateNames, ... -% inputs.coordinateNames ... -% ); - [~, ~, indexesInParsedCoordinateData] = intersect( ... inputs.coordinateNames, inputs.inverseDynamicsMomentsColumnNames, 'stable'); [~, ~, indexesInParsedMuscleAnalysisData] = intersect( ... @@ -35,9 +21,6 @@ indexesInParsedMuscleData, :); if (isfield(inputs, "mtpActivationsColumnNames")) - % indexesInParsedActivationData = ... - % ismember(inputs.mtpActivationsColumnNames, muscleNames); - [~, ~, indexesInParsedActivationData] = ... intersect(muscleNames, inputs.mtpActivationsColumnNames, 'stable'); From d5443bc1e495e6d2e54ff280671ce71ae489c435 Mon Sep 17 00:00:00 2001 From: RobSalati <142843461+RobSalati@users.noreply.github.com> Date: Tue, 13 Feb 2024 14:54:59 -0600 Subject: [PATCH 317/365] Basic plotting functionality done Can now create plots for joint angles, joint loads, synergy controls, torque controls, muscle activations, and ground reactions. Ground reaction moments are not about the midfoot superior marker in this commit. --- .../-3b1n-XfyCyTaGBiCjyzBR9pLL8p.xml | 2 - ...d.xml => ThOxiimtCadipN0DSzt0WSfyhQgd.xml} | 0 .../ThOxiimtCadipN0DSzt0WSfyhQgp.xml | 2 + .../VBzNr-UJnK1VpshGIqoYe2h0C2cp.xml | 2 - ...d.xml => gq-la7hESjuBnPU9DovNTn10Z7Ud.xml} | 0 .../gq-la7hESjuBnPU9DovNTn10Z7Up.xml | 2 + .../j1su1TAZRkcY37B9D8owiFMCF0wp.xml | 2 - ...d.xml => lzBPtPM9pIFaKjtc82DDciac-YYd.xml} | 0 .../lzBPtPM9pIFaKjtc82DDciac-YYp.xml | 2 + ...plotTreatmentOptimizationGroundReactions.m | 71 ++++++++++++++++--- ...=> plotTreatmentOptimizationJointAngles.m} | 29 ++++---- ... => plotTreatmentOptimizationJointLoads.m} | 46 ++++++------ ...tTreatmentOptimizationMuscleActivations.m} | 34 ++++----- ...plotTreatmentOptimizationSynergyControls.m | 23 +++--- .../plotTreatmentOptimizationTorqueControls.m | 23 +++--- 15 files changed, 154 insertions(+), 84 deletions(-) delete mode 100644 resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/-3b1n-XfyCyTaGBiCjyzBR9pLL8p.xml rename resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/{-3b1n-XfyCyTaGBiCjyzBR9pLL8d.xml => ThOxiimtCadipN0DSzt0WSfyhQgd.xml} (100%) create mode 100644 resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/ThOxiimtCadipN0DSzt0WSfyhQgp.xml delete mode 100644 resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/VBzNr-UJnK1VpshGIqoYe2h0C2cp.xml rename resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/{VBzNr-UJnK1VpshGIqoYe2h0C2cd.xml => gq-la7hESjuBnPU9DovNTn10Z7Ud.xml} (100%) create mode 100644 resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/gq-la7hESjuBnPU9DovNTn10Z7Up.xml delete mode 100644 resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/j1su1TAZRkcY37B9D8owiFMCF0wp.xml rename resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/{j1su1TAZRkcY37B9D8owiFMCF0wd.xml => lzBPtPM9pIFaKjtc82DDciac-YYd.xml} (100%) create mode 100644 resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/lzBPtPM9pIFaKjtc82DDciac-YYp.xml rename src/TreatmentOptimization/Analysis/{plotTreatmentOptimizationAngles.m => plotTreatmentOptimizationJointAngles.m} (88%) rename src/TreatmentOptimization/Analysis/{plotTreatmentOptimizationDynamics.m => plotTreatmentOptimizationJointLoads.m} (76%) rename src/TreatmentOptimization/Analysis/{plotTreatmentOptimizationActivations.m => plotTreatmentOptimizationMuscleActivations.m} (87%) diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/-3b1n-XfyCyTaGBiCjyzBR9pLL8p.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/-3b1n-XfyCyTaGBiCjyzBR9pLL8p.xml deleted file mode 100644 index 428e957a9..000000000 --- a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/-3b1n-XfyCyTaGBiCjyzBR9pLL8p.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/-3b1n-XfyCyTaGBiCjyzBR9pLL8d.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/ThOxiimtCadipN0DSzt0WSfyhQgd.xml similarity index 100% rename from resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/-3b1n-XfyCyTaGBiCjyzBR9pLL8d.xml rename to resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/ThOxiimtCadipN0DSzt0WSfyhQgd.xml diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/ThOxiimtCadipN0DSzt0WSfyhQgp.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/ThOxiimtCadipN0DSzt0WSfyhQgp.xml new file mode 100644 index 000000000..744496e40 --- /dev/null +++ b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/ThOxiimtCadipN0DSzt0WSfyhQgp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/VBzNr-UJnK1VpshGIqoYe2h0C2cp.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/VBzNr-UJnK1VpshGIqoYe2h0C2cp.xml deleted file mode 100644 index 37c2e5093..000000000 --- a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/VBzNr-UJnK1VpshGIqoYe2h0C2cp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/VBzNr-UJnK1VpshGIqoYe2h0C2cd.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/gq-la7hESjuBnPU9DovNTn10Z7Ud.xml similarity index 100% rename from resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/VBzNr-UJnK1VpshGIqoYe2h0C2cd.xml rename to resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/gq-la7hESjuBnPU9DovNTn10Z7Ud.xml diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/gq-la7hESjuBnPU9DovNTn10Z7Up.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/gq-la7hESjuBnPU9DovNTn10Z7Up.xml new file mode 100644 index 000000000..9fb2e0409 --- /dev/null +++ b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/gq-la7hESjuBnPU9DovNTn10Z7Up.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/j1su1TAZRkcY37B9D8owiFMCF0wp.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/j1su1TAZRkcY37B9D8owiFMCF0wp.xml deleted file mode 100644 index 5f29da99a..000000000 --- a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/j1su1TAZRkcY37B9D8owiFMCF0wp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/j1su1TAZRkcY37B9D8owiFMCF0wd.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/lzBPtPM9pIFaKjtc82DDciac-YYd.xml similarity index 100% rename from resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/j1su1TAZRkcY37B9D8owiFMCF0wd.xml rename to resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/lzBPtPM9pIFaKjtc82DDciac-YYd.xml diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/lzBPtPM9pIFaKjtc82DDciac-YYp.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/lzBPtPM9pIFaKjtc82DDciac-YYp.xml new file mode 100644 index 000000000..5fce7f00b --- /dev/null +++ b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/lzBPtPM9pIFaKjtc82DDciac-YYp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m index 22a6b7538..e9d0a512e 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m @@ -28,15 +28,70 @@ % implied. See the License for the specific language governing % % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function plotTreatmentOptimizationGroundReactions(groundReactionFile, ... - figureWidth, figureHeight) - if nargin < 3 - figureWidth = 8; +function plotTreatmentOptimizationGroundReactions(experimentalGroundReactionFile, ... + modelGroundReactionFile, figureWidth, figureHeight) + +import org.opensim.modeling.Storage +experimentalGRStorage = Storage(experimentalGroundReactionFile); +experimentalGRLabels = getStorageColumnNames(experimentalGRStorage); +experimentalGR = storageToDoubleMatrix(experimentalGRStorage)'; +experimentalTime = findTimeColumn(experimentalGRStorage); +if experimentalTime(1) ~= 0 + experimentalTime = experimentalTime - experimentalTime(1); +end +modelGRStorage = Storage(modelGroundReactionFile); +modelGRLabels = getStorageColumnNames(modelGRStorage); +modelGR = storageToDoubleMatrix(modelGRStorage)'; +modelTime = findTimeColumn(modelGRStorage); +if modelTime(1) ~= 0 + modelTime = modelTime - modelTime(1); +end + +% Check both GR files are in the same order. +experimentalForcePlate1 = contains(experimentalGRLabels, "1"); +modelForcePlate1 = contains(modelGRLabels, "1"); +if experimentalForcePlate1 ~= modelForcePlate1 + temp = modelGR; + modelGR(:, ~experimentalForcePlate1) = modelGR(:, experimentalForcePlate1); + modelGR(:, experimentalForcePlate1) = temp(:, ~experimentalForcePlate1); +end + +momentIndices = contains(experimentalGRLabels, "_m"); +forceIndices = contains(experimentalGRLabels, "_v"); +includedIndices = momentIndices | forceIndices; + +experimentalGRLabels = experimentalGRLabels(includedIndices); +modelGR = modelGR(:, includedIndices); +experimentalGR = experimentalGR(:, includedIndices); + +if nargin < 3 + figureWidth = 3; +end +if nargin < 4 + figureHeight = 2; +end +figureSize = figureWidth * figureHeight; + +figure(Name = "Treatment Optimization Ground Reactions") +subplotNumber = 1; +figureNumber = 1; +for i=1:numel(experimentalGRLabels) + if i > figureSize * figureNumber + figureNumber = figureNumber + 1; + figure(Name="Treatment Optimization Ground Reactions") + subplotNumber = 1; end - if nargin < 4 - figureHeight = 8; + subplot(figureHeight, figureWidth, subplotNumber) + hold on + plot(experimentalTime, experimentalGR(:, i)); + plot(modelTime, modelGR(:, i)) + hold off + title(strrep(experimentalGRLabels(i), "_", " ")) + if subplotNumber==1 + legend("Experimental", "Model") end - figureSize = figureWidth * figureHeight; - import org.opensim.modeling.Storage + xlim([0, experimentalTime(end)]) + subplotNumber = subplotNumber + 1; +end end diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationAngles.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m similarity index 88% rename from src/TreatmentOptimization/Analysis/plotTreatmentOptimizationAngles.m rename to src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m index 9d9b398ef..77be8386f 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationAngles.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m @@ -28,16 +28,9 @@ % implied. See the License for the specific language governing % % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function plotTreatmentOptimizationAngles(experimentalAnglesFile, ... +function plotTreatmentOptimizationJointAngles(experimentalAnglesFile, ... modelAnglesFile, figureWidth, figureHeight) -% Get these to degrees instead of radians -if nargin < 3 - figureWidth = 8; -end -if nargin < 4 - figureHeight = 8; -end -figureSize = figureWidth * figureHeight; + import org.opensim.modeling.Storage experimentalAnglesStorage = Storage(experimentalAnglesFile); angleLabels = getStorageColumnNames(experimentalAnglesStorage); @@ -55,7 +48,15 @@ function plotTreatmentOptimizationAngles(experimentalAnglesFile, ... modelTime = modelTime - modelTime(1); end -figure(Name = "Treatment Optimization Angles", ... +if nargin < 3 + figureWidth = ceil(sqrt(numel(angleLabels))); +end +if nargin < 4 + figureHeight = ceil(sqrt(numel(angleLabels))); +end +figureSize = figureWidth * figureHeight; + +figure(Name = "Treatment Optimization Joint Angles", ... Units='normalized', ... Position=[0 0 1 1]) subplotNumber = 1; @@ -63,12 +64,12 @@ function plotTreatmentOptimizationAngles(experimentalAnglesFile, ... for i=1:numel(angleLabels) if i > figureSize * figureNumber figureNumber = figureNumber + 1; - figure(Name="Treatment Optimization Angles", ... + figure(Name="Treatment Optimization Joint Angles", ... Units='normalized', ... Position=[0 0 1 1]) subplotNumber = 1; end - subplot(figureWidth, figureHeight, subplotNumber) + subplot(figureHeight, figureWidth, subplotNumber) hold on plot(experimentalTime, experimentalAngles(:, i)); plot(modelTime, modelAngles(:, i)); @@ -76,14 +77,14 @@ function plotTreatmentOptimizationAngles(experimentalAnglesFile, ... title(strrep(angleLabels(i), "_", " ")); if subplotNumber==1 - legend("Experimental Angles", "Model Angles") + legend("Experimental", "Model") end xlim([0, experimentalTime(end)]) maxAngle = max([experimentalAngles(:, i); modelAngles(:, i)],[], "all"); minAngle = min([experimentalAngles(:, i); modelAngles(:, i)],[], "all"); if maxAngle-minAngle < 10 - ylim([(maxAngle+minAngle)/2-5, (maxAngle+minAngle)/2+5]) + ylim([(maxAngle+minAngle)/2-10, (maxAngle+minAngle)/2+10]) end subplotNumber = subplotNumber + 1; end diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationDynamics.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoads.m similarity index 76% rename from src/TreatmentOptimization/Analysis/plotTreatmentOptimizationDynamics.m rename to src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoads.m index 48e87259a..f3cb22aba 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationDynamics.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoads.m @@ -28,18 +28,12 @@ % implied. See the License for the specific language governing % % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function plotTreatmentOptimizationDynamics(experimentalMomentsFile, ... +function plotTreatmentOptimizationJointLoads(experimentalMomentsFile, ... modelMomentsFile, figureWidth, figureHeight) -if nargin < 3 - figureWidth = 8; -end -if nargin < 4 - figureHeight = 8; -end -figureSize = figureWidth * figureHeight; + import org.opensim.modeling.Storage experimentalMomentsStorage = Storage(experimentalMomentsFile); -momentAxes = getStorageColumnNames(experimentalMomentsStorage); +momentLabels = getStorageColumnNames(experimentalMomentsStorage); experimentalMoments = storageToDoubleMatrix(experimentalMomentsStorage)'; experimentalTime = findTimeColumn(experimentalMomentsStorage); if experimentalTime(1) ~= 0 @@ -52,42 +46,54 @@ function plotTreatmentOptimizationDynamics(experimentalMomentsFile, ... modelTime = modelTime - modelTime(1); end -figure(Name="Treatment Optimization Moments", ... +if nargin < 3 + figureWidth = ceil(sqrt(numel(momentLabels))); +end +if nargin < 4 + figureHeight = ceil(sqrt(numel(momentLabels))); +end +figureSize = figureWidth * figureHeight; + +figure(Name="Treatment Optimization Joint Loads", ... Units='normalized', ... Position=[0 0 1 1]) subplotNumber = 1; figureNumber = 1; -for i=1:numel(momentAxes) +for i=1:numel(momentLabels) if i > figureSize * figureNumber figureNumber = figureNumber + 1; - figure(Name="Treatment Optimization Moments", ... + figure(Name="Treatment Optimization Joint Loads", ... Units='normalized', ... Position=[0 0 1 1]) subplotNumber = 1; end - subplot(figureWidth, figureHeight, subplotNumber) + subplot(figureHeight, figureWidth, subplotNumber) hold on plot(experimentalTime, experimentalMoments(:, i)); plot(modelTime, modelMoments(:, i)); hold off - if contains(momentAxes(i), "moment") - title(strcat(strrep(momentAxes(i), "_", " "), " [Nm]")) - elseif contains(momentAxes(i), "force") - title(strcat(strrep(momentAxes(i), "_", " "), " [N]")) + if contains(momentLabels(i), "moment") + title(strcat(strrep(momentLabels(i), "_", " "), " [Nm]")) + elseif contains(momentLabels(i), "force") + title(strcat(strrep(momentLabels(i), "_", " "), " [N]")) else - title(strrep(momentAxes(i), "_", " ")) + title(strrep(momentLabels(i), "_", " ")) end - if subplotNumber==1 - legend("Experimental Moments", "Model Moments") + legend("Experimental", "Model") end xlim([0, experimentalTime(end)]) % if subplotNumber > figureSize-figureHeight % xlabel("Time [s]") % end + maxLoad = max([experimentalMoments(:, i); modelMoments(:, i)],[], "all"); + minLoad = min([experimentalMoments(:, i); modelMoments(:, i)],[], "all"); + if maxLoad-minLoad < 10 + ylim([(maxLoad+minLoad)/2-10, (maxLoad+minLoad)/2+10]) + end subplotNumber = subplotNumber + 1; end end diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationActivations.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivations.m similarity index 87% rename from src/TreatmentOptimization/Analysis/plotTreatmentOptimizationActivations.m rename to src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivations.m index 07fb263d4..47dff87f3 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationActivations.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivations.m @@ -28,15 +28,9 @@ % implied. See the License for the specific language governing % % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function plotTreatmentOptimizationActivations(experimentalActivationsFile, ... +function plotTreatmentOptimizationMuscleActivations(experimentalActivationsFile, ... modelActivationsFile, figureWidth, figureHeight) -if nargin < 3 - figureWidth = 8; -end -if nargin < 4 - figureHeight = 8; -end -figureSize = figureWidth * figureHeight; + import org.opensim.modeling.Storage experimentalActivationsStorage = Storage(experimentalActivationsFile); muscleLabels = getStorageColumnNames(experimentalActivationsStorage); @@ -51,7 +45,16 @@ function plotTreatmentOptimizationActivations(experimentalActivationsFile, ... if modelTime(1) ~= 0 modelTime = modelTime - modelTime(1); end -figure(Name="Treatment Optimization Activations", ... + +if nargin < 3 + figureWidth = ceil(sqrt(numel(muscleLabels))); +end +if nargin < 4 + figureHeight = ceil(sqrt(numel(muscleLabels))); +end +figureSize = figureWidth * figureHeight; + +figure(Name="Treatment Optimization Muscle Activations", ... Units='normalized', ... Position=[0 0 1 1]) subplotNumber = 1; @@ -59,21 +62,20 @@ function plotTreatmentOptimizationActivations(experimentalActivationsFile, ... for i=1:numel(muscleLabels) if i > figureSize * figureNumber figureNumber = figureNumber + 1; - figure(Name="Treatment Optimization Activations", ... - Units='normalized', ... - Position=[0 0 1 1]) + figure(Name="Treatment Optimization Muscle Activations") subplotNumber = 1; end - subplot(figureWidth, figureHeight, subplotNumber) + subplot(figureHeight, figureWidth, subplotNumber) hold on plot(experimentalTime, experimentalActivations(:, i)) plot(modelTime, modelActivations(:, i)) hold off title(strrep(muscleLabels(i), "_", " ")); if subplotNumber == 1 - legend("Experimental Activations", "Model Activations") + legend("Experimental", "Model") end + xlim([0, experimentalTime(end)]); + ylim([0 1]) subplotNumber = subplotNumber + 1; end -end - +end \ No newline at end of file diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationSynergyControls.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationSynergyControls.m index f3abdf8a6..cb4f5ff03 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationSynergyControls.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationSynergyControls.m @@ -30,13 +30,7 @@ % ----------------------------------------------------------------------- % function plotTreatmentOptimizationSynergyControls(controlsFile, ... figureWidth, figureHeight) -if nargin < 2 - figureWidth = 4; -end -if nargin < 3 - figureHeight = 4; -end -figureSize = figureWidth * figureHeight; + import org.opensim.modeling.Storage controlsStorage = Storage(controlsFile); controlLabels = getStorageColumnNames(controlsStorage); @@ -45,7 +39,15 @@ function plotTreatmentOptimizationSynergyControls(controlsFile, ... if time(1) ~= 0 time = time - time(1); end -figure(Name="Treatment Optimization Controls", ... + +if nargin < 3 + figureWidth = ceil(sqrt(numel(controlLabels))); +end +if nargin < 4 + figureHeight = ceil(sqrt(numel(controlLabels))); +end +figureSize = figureWidth * figureHeight; +figure(Name="Treatment Optimization Synergy Controls", ... Units='normalized', ... Position=[0 0 1 1]) subplotNumber = 1; @@ -53,17 +55,18 @@ function plotTreatmentOptimizationSynergyControls(controlsFile, ... for i=1:numel(controlLabels) if i > figureSize * figureNumber figureNumber = figureNumber + 1; - figure(Name="Treatment Optimization Controls", ... + figure(Name="Treatment Optimization Synergy Controls", ... Units='normalized', ... Position=[0 0 1 1]) subplotNumber = 1; end - subplot(figureWidth, figureHeight, subplotNumber) + subplot(figureHeight, figureWidth, subplotNumber) hold on plot(time, controls(:, i)); hold off title(strrep(controlLabels(i), "_", " ")); xlim([0, time(end)]) + ylim([0, 1]) subplotNumber = subplotNumber + 1; end end diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationTorqueControls.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationTorqueControls.m index 570791bfa..7e8015b97 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationTorqueControls.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationTorqueControls.m @@ -30,13 +30,7 @@ % ----------------------------------------------------------------------- % function plotTreatmentOptimizationTorqueControls(controlsFile, ... figureWidth, figureHeight) -if nargin < 2 - figureWidth = 4; -end -if nargin < 3 - figureHeight = 4; -end -figureSize = figureWidth * figureHeight; + import org.opensim.modeling.Storage controlsStorage = Storage(controlsFile); controlLabels = getStorageColumnNames(controlsStorage); @@ -45,7 +39,16 @@ function plotTreatmentOptimizationTorqueControls(controlsFile, ... if time(1) ~= 0 time = time - time(1); end -figure(Name="Treatment Optimization Controls", ... + +if nargin < 3 + figureWidth = ceil(sqrt(numel(controlLabels))); +end +if nargin < 4 + figureHeight = ceil(sqrt(numel(controlLabels))); +end +figureSize = figureWidth * figureHeight; + +figure(Name="Treatment Optimization Torque Controls", ... Units='normalized', ... Position=[0 0 1 1]) subplotNumber = 1; @@ -53,12 +56,12 @@ function plotTreatmentOptimizationTorqueControls(controlsFile, ... for i=1:numel(controlLabels) if i > figureSize * figureNumber figureNumber = figureNumber + 1; - figure(Name="Treatment Optimization Controls", ... + figure(Name="Treatment Optimization Torque Controls", ... Units='normalized', ... Position=[0 0 1 1]) subplotNumber = 1; end - subplot(figureWidth, figureHeight, subplotNumber) + subplot(figureHeight, figureWidth, subplotNumber) hold on plot(time, controls(:, i)); hold off From ea36936919bc85a8ba01f1c2f2eaf78a6a151799 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Tue, 13 Feb 2024 16:10:40 -0600 Subject: [PATCH 318/365] improve foot marker tracking --- .../Saving/writeFullBodyKinematicsFromGcp.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GroundContactPersonalization/Saving/writeFullBodyKinematicsFromGcp.m b/src/GroundContactPersonalization/Saving/writeFullBodyKinematicsFromGcp.m index 565d9ed71..77e093e55 100644 --- a/src/GroundContactPersonalization/Saving/writeFullBodyKinematicsFromGcp.m +++ b/src/GroundContactPersonalization/Saving/writeFullBodyKinematicsFromGcp.m @@ -110,7 +110,7 @@ function writeFullBodyKinematicsFromGcp(inputs, params, resultsDirectory) markerWeightSet = SetMarkerWeights(); for i=1:length(markerNames) if any(ismember(fieldnames(footMarkerData), markerNames(i))) - markerWeightSet.cloneAndAppend(MarkerWeight(markerNames(i), 100.0)); + markerWeightSet.cloneAndAppend(MarkerWeight(markerNames(i), 1000.0)); else markerWeightSet.cloneAndAppend(MarkerWeight(markerNames(i), 1.0)); end From c828d71f448362d71128a7f92c775ded69e96949 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sat, 17 Feb 2024 23:43:53 -0600 Subject: [PATCH 319/365] fix bug with multiple tasks --- .../JointModelPersonalization.m | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/JointModelPersonalization/JointModelPersonalization.m b/src/JointModelPersonalization/JointModelPersonalization.m index 42953d4b9..53c55b1c3 100644 --- a/src/JointModelPersonalization/JointModelPersonalization.m +++ b/src/JointModelPersonalization/JointModelPersonalization.m @@ -39,12 +39,15 @@ params.markerNames = getMarkersInTask(outputModel, ... inputs.tasks{i}); taskParams = mergeStructs(inputs.tasks{i}, params); - optimizedValues = computeKinematicCalibration(inputs.modelFileName, ... + outputModelFileName = saveTempOutputModel(inputs.modelFileName, ... + outputModel); + optimizedValues = computeKinematicCalibration(outputModelFileName, ... inputs.tasks{i}.markerFile, functions, inputs.desiredError, ... taskParams); - outputModel = adjustModelFromOptimizerOutput(outputModel, ... - functions, optimizedValues); + outputModel = adjustModelFromOptimizerOutput( ... + Model(outputModelFileName), functions, optimizedValues); end +delete(outputModelFileName); end % (struct) -> (None) @@ -98,6 +101,13 @@ function verifyParam(params, fieldName, fn, message) end end +function outputModelFileName = saveTempOutputModel(modelFileName, ... + outputModel) +[~,name,ext] = fileparts(modelFileName); +outputModelFileName = name + "_jmp_temp" + ext; +outputModel.print(outputModelFileName); +end + function functions = makeFunctions(... parameters, scaling, markers, anatomicalMarkers) functions = {}; From a1b8dc75e1145438fedc0cffdb6d6a0918c32540 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 18 Feb 2024 18:31:49 -0600 Subject: [PATCH 320/365] fix contact surface floor height --- .../OptimalControlSetupFunctions/setupGroundContact.m | 2 +- .../saveTreatmentOptimizationResults.m | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGroundContact.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGroundContact.m index 5b3522121..dcaaf4123 100644 --- a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGroundContact.m +++ b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGroundContact.m @@ -36,7 +36,7 @@ inputs.contactSurfaces{i}.midfootSuperiorPointOnBody, ... inputs.contactSurfaces{i}.midfootSuperiorBody, ... inputs.modelFileName, inputs.coordinateNames); - midfootSuperiorLocation(:, 2) = 0; + midfootSuperiorLocation(:, 2) = inputs.contactSurfaces{i}.restingSpringLength; inputs.contactSurfaces{i}.experimentalGroundReactionMoments = ... transferMoments(inputs.contactSurfaces{i}.electricalCenter, ... midfootSuperiorLocation, ... diff --git a/src/TreatmentOptimization/saveTreatmentOptimizationResults.m b/src/TreatmentOptimization/saveTreatmentOptimizationResults.m index 075bcade1..e9297ae59 100644 --- a/src/TreatmentOptimization/saveTreatmentOptimizationResults.m +++ b/src/TreatmentOptimization/saveTreatmentOptimizationResults.m @@ -124,7 +124,7 @@ function saveGroundReactionResults(solution, inputs, values, outputDirectory) inputs.contactSurfaces{i}.midfootSuperiorPointOnBody, ... inputs.contactSurfaces{i}.midfootSuperiorBody, inputs.modelFileName, ... inputs.coordinateNames); - midfootSuperiorLocation(:, 2) = 0; + midfootSuperiorLocation(:, 2) = inputs.contactSurfaces{i}.restingSpringLength; groundContactData = [groundContactData, ... solution.groundReactionsLab.forces{i}, ... solution.groundReactionsLab.moments{i}, ... @@ -134,8 +134,10 @@ function saveGroundReactionResults(solution, inputs, values, outputDirectory) if ~exist(fullfile(outputDirectory, "GRFData"), "dir") mkdir(fullfile(outputDirectory, "GRFData")) end - writeToSto(groundContactLabels, values.time, ... - groundContactData, fullfile(inputs.resultsDirectory, "GRFData", ... + [time, splinedGroundContactData] = splineToEvenlySpaced(values.time, ... + groundContactData); + writeToSto(groundContactLabels, time, splinedGroundContactData, ... + fullfile(inputs.resultsDirectory, "GRFData", ... strcat(inputs.trialName, ".sto"))); end end From 6bd2c1cd628bde18908de585ea98f11686073fa9 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 18 Feb 2024 18:32:17 -0600 Subject: [PATCH 321/365] fix acceleration controls state parsing --- .../OptimalControlSetupFunctions/setupGpopsInitialGuess.m | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m index fae13d943..35ca33774 100644 --- a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m +++ b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m @@ -136,11 +136,10 @@ function output = subsetInitialStatesDataByCoordinates(data, ... coordinateNames, subsetOfCoordinateNames) includedSubset = ismember(coordinateNames, subsetOfCoordinateNames); -numCoordinates = length(includedSubset) / 3; +numCoordinates = length(includedSubset) / 2; for i = 1:numCoordinates if includedSubset(i) includedSubset(i + numCoordinates) = true; - includedSubset(i + (2 * numCoordinates)) = true; end end output = data(:, includedSubset); From 197db19be99ff28bfe4f5ce62f0e3cd41e5eee83 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Mon, 19 Feb 2024 20:14:25 -0600 Subject: [PATCH 322/365] update point kinematics arguments Co-Authored-By: Spencer Williams <54556034+SpencerTWilliams@users.noreply.github.com> --- .../calcTorqueBasedModeledValues.m | 20 +++++++++---------- .../saveTreatmentOptimizationResults.m | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m b/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m index 633c53f3d..e95c74336 100644 --- a/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m +++ b/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m @@ -76,17 +76,17 @@ end function [springPositions, springVelocities] = getSpringLocations(time, .... - statePositions, stateVelocities, inputs) + positions, velocities, inputs) for i = 1:length(inputs.contactSurfaces) [springPositions.parent{i}, springVelocities.parent{i}] = ... - pointKinematics(time, statePositions, stateVelocities, ... + pointKinematics(time, positions, velocities, ... inputs.contactSurfaces{i}.parentSpringPointsOnBody, ... inputs.contactSurfaces{i}.parentBody * ones(1, ... size(inputs.contactSurfaces{i}.parentSpringPointsOnBody, 1)), ... inputs.mexModel, inputs.coordinateNames); [springPositions.child{i}, springVelocities.child{i}] = ... - pointKinematics(time, statePositions, stateVelocities, ... + pointKinematics(time, positions, velocities, ... inputs.contactSurfaces{i}.childSpringPointsOnBody, ... inputs.contactSurfaces{i}.childBody * ones(1, ... size(inputs.contactSurfaces{i}.childSpringPointsOnBody, 1)), ... @@ -94,22 +94,22 @@ end end -function bodyLocations = getBodyLocations(time, statePositions, ... - stateVelocities, inputs) +function bodyLocations = getBodyLocations(time, positions, ... + velocities, inputs) for i = 1:length(inputs.contactSurfaces) bodyLocations.midfootSuperior{i} = pointKinematics(time, ... - statePositions, stateVelocities, ... + positions, velocities, ... inputs.contactSurfaces{i}.midfootSuperiorPointOnBody, ... inputs.contactSurfaces{i}.midfootSuperiorBody, ... inputs.mexModel, inputs.coordinateNames); bodyLocations.midfootSuperior{i}(:, 2) = ... inputs.contactSurfaces{i}.restingSpringLength; - bodyLocations.parent{i} = pointKinematics(time, statePositions, ... - stateVelocities, [0 0 0], inputs.contactSurfaces{i}.parentBody, ... + bodyLocations.parent{i} = pointKinematics(time, positions, ... + velocities, [0 0 0], inputs.contactSurfaces{i}.parentBody, ... inputs.mexModel, inputs.coordinateNames); - bodyLocations.child{i} = pointKinematics(time, statePositions, ... - stateVelocities, [0 0 0], inputs.contactSurfaces{i}.childBody, ... + bodyLocations.child{i} = pointKinematics(time, positions, ... + velocities, [0 0 0], inputs.contactSurfaces{i}.childBody, ... inputs.mexModel, inputs.coordinateNames); end end diff --git a/src/TreatmentOptimization/saveTreatmentOptimizationResults.m b/src/TreatmentOptimization/saveTreatmentOptimizationResults.m index e9297ae59..9db703d1d 100644 --- a/src/TreatmentOptimization/saveTreatmentOptimizationResults.m +++ b/src/TreatmentOptimization/saveTreatmentOptimizationResults.m @@ -120,7 +120,7 @@ function saveGroundReactionResults(solution, inputs, values, outputDirectory) inputs.contactSurfaces{i}.momentColumns, ... inputs.contactSurfaces{i}.electricalCenterColumns]); midfootSuperiorLocation = pointKinematics(values.time, ... - values.statePositions, values.stateVelocities, ... + values.positions, values.velocities, ... inputs.contactSurfaces{i}.midfootSuperiorPointOnBody, ... inputs.contactSurfaces{i}.midfootSuperiorBody, inputs.modelFileName, ... inputs.coordinateNames); From e218cf26dd4772841851e4dfbe6ae2903e3e700b Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Mon, 19 Feb 2024 20:55:38 -0600 Subject: [PATCH 323/365] fix results saving splining --- .../setupGpopsInitialGuess.m | 4 +- .../saveTreatmentOptimizationResults.m | 46 ++++++++++--------- ...nlySpaced.m => splineToExperimentalTime.m} | 10 ++-- .../parseTreatmentOptimizationDataDirectory.m | 4 +- 4 files changed, 34 insertions(+), 30 deletions(-) rename src/TreatmentOptimization/{splineToEvenlySpaced.m => splineToExperimentalTime.m} (87%) diff --git a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m index 35ca33774..6b16e24b9 100644 --- a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m +++ b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m @@ -73,8 +73,8 @@ end function guess = setupInitialControlsGuess(inputs, guess) -if isfield(inputs, "initialJerks") - controls = inputs.initialJerks; +if isfield(inputs, "initialAccelerations") + controls = inputs.initialAccelerations; else stateJointAccelerations = subsetDataByCoordinates( ... inputs.experimentalJointAccelerations, ... diff --git a/src/TreatmentOptimization/saveTreatmentOptimizationResults.m b/src/TreatmentOptimization/saveTreatmentOptimizationResults.m index 9db703d1d..f63b08010 100644 --- a/src/TreatmentOptimization/saveTreatmentOptimizationResults.m +++ b/src/TreatmentOptimization/saveTreatmentOptimizationResults.m @@ -45,12 +45,13 @@ function saveTreatmentOptimizationResults(solution, inputs, values) % for i = 1 : length(inputs.statesCoordinateNames) % stateLabels{end + 1} = strcat(inputs.statesCoordinateNames{i}, '_dudt'); % end -[time, data] = splineToEvenlySpaced(values.time, [values.statePositions ... - values.stateVelocities]); -writeToSto(stateLabels, time, data, ... +data = splineToExperimentalTime(values.time, [values.statePositions ... + values.stateVelocities], inputs.experimentalTime); +writeToSto(stateLabels, inputs.experimentalTime, data, ... fullfile(inputs.resultsDirectory, strcat(inputs.trialName, "_states.sto"))); -[time, accelerations] = splineToEvenlySpaced(values.time, values.controlAccelerations); -writeToSto(inputs.statesCoordinateNames, time, accelerations, ... +accelerations = splineToExperimentalTime(values.time, ... + values.controlAccelerations, inputs.experimentalTime); +writeToSto(inputs.statesCoordinateNames, inputs.experimentalTime, accelerations, ... fullfile(inputs.resultsDirectory, strcat(inputs.trialName, "_accelerations.sto"))); if strcmp(inputs.controllerType, 'synergy') controlLabels = {}; @@ -61,9 +62,9 @@ function saveTreatmentOptimizationResults(solution, inputs, values) "_", num2str(j))); end end - [time, controls] = splineToEvenlySpaced( ... - values.time, values.controlSynergyActivations); - writeToSto(controlLabels, time, controls, ... + controls = splineToExperimentalTime( ... + values.time, values.controlSynergyActivations, inputs.experimentalTime); + writeToSto(controlLabels, inputs.experimentalTime, controls, ... fullfile(inputs.resultsDirectory, ... strcat(inputs.trialName, "_synergyCommands.sto"))); end @@ -72,9 +73,9 @@ function saveTreatmentOptimizationResults(solution, inputs, values) for i = 1 : length(inputs.torqueControllerCoordinateNames) controlLabels{end + 1} = inputs.torqueControllerCoordinateNames{i}; end - [time, controls] = splineToEvenlySpaced( ... - values.time, values.torqueControls); - writeToSto(controlLabels, time, controls, ... + controls = splineToExperimentalTime( ... + values.time, values.torqueControls, inputs.experimentalTime); + writeToSto(controlLabels, inputs.experimentalTime, controls, ... fullfile(inputs.resultsDirectory, ... strcat(inputs.trialName, "_torqueControls.sto"))); end @@ -82,9 +83,9 @@ function saveTreatmentOptimizationResults(solution, inputs, values) writeToSto(inputs.muscleLabels, linspace(1, inputs.numSynergies, ... inputs.numSynergies), [values.synergyWeights], ... fullfile(inputs.resultsDirectory, "synergyWeights.sto")); - [time, activations] = splineToEvenlySpaced(values.time, ... - solution.muscleActivations); - writeToSto(inputs.muscleLabels, time, ... + activations = splineToExperimentalTime(values.time, ... + solution.muscleActivations, inputs.experimentalTime); + writeToSto(inputs.muscleLabels, inputs.experimentalTime, ... activations, fullfile(inputs.resultsDirectory, ... strcat(inputs.trialName, "_combinedActivations.sto"))); end @@ -95,8 +96,9 @@ function saveInverseKinematicsResults(inputs, values, outputDirectory) if ~exist(fullfile(outputDirectory, "IKData"), "dir") mkdir(fullfile(outputDirectory, "IKData")) end -[time, positions] = splineToEvenlySpaced(values.time, values.positions); -writeToSto(inputs.coordinateNames, time, positions, ... +positions = splineToExperimentalTime(values.time, ... + values.positions, inputs.experimentalTime); +writeToSto(inputs.coordinateNames, inputs.experimentalTime, positions, ... fullfile(outputDirectory, "IKData", strcat(inputs.trialName, ".sto"))); end @@ -104,9 +106,9 @@ function saveInverseDynamicsResults(solution, inputs, values, outputDirectory) if ~exist(fullfile(outputDirectory, "IDData"), "dir") mkdir(fullfile(outputDirectory, "IDData")) end -[time, moments] = splineToEvenlySpaced(values.time, ... - solution.inverseDynamicsMoments); -writeToSto(inputs.inverseDynamicsMomentLabels, time, ... +moments = splineToExperimentalTime(values.time, ... + solution.inverseDynamicsMoments, inputs.experimentalTime); +writeToSto(inputs.inverseDynamicsMomentLabels, inputs.experimentalTime, ... moments, fullfile(outputDirectory, "IDData", ... strcat(inputs.trialName, ".sto"))); end @@ -134,9 +136,9 @@ function saveGroundReactionResults(solution, inputs, values, outputDirectory) if ~exist(fullfile(outputDirectory, "GRFData"), "dir") mkdir(fullfile(outputDirectory, "GRFData")) end - [time, splinedGroundContactData] = splineToEvenlySpaced(values.time, ... - groundContactData); - writeToSto(groundContactLabels, time, splinedGroundContactData, ... + splinedGroundContactData = splineToExperimentalTime(values.time, ... + groundContactData, inputs.experimentalTime); + writeToSto(groundContactLabels, inputs.experimentalTime, splinedGroundContactData, ... fullfile(inputs.resultsDirectory, "GRFData", ... strcat(inputs.trialName, ".sto"))); end diff --git a/src/TreatmentOptimization/splineToEvenlySpaced.m b/src/TreatmentOptimization/splineToExperimentalTime.m similarity index 87% rename from src/TreatmentOptimization/splineToEvenlySpaced.m rename to src/TreatmentOptimization/splineToExperimentalTime.m index 433d0af8e..8d8c401c8 100644 --- a/src/TreatmentOptimization/splineToEvenlySpaced.m +++ b/src/TreatmentOptimization/splineToExperimentalTime.m @@ -29,9 +29,11 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function [newTime, evenlySpacedData] = splineToEvenlySpaced(time, data) -newTime = linspace(time(1), time(end), length(time)); -spline = spaps(time, data', 1e-7); -evenlySpacedData = fnval(spline, newTime)'; +function newData = splineToExperimentalTime(time, data, ... + newTime) +gcvSplineSet = makeGcvSplineSet(time(1 : end - 1), ... + data(1 : end - 1, :), string([1:size(data, 2)])); +newData = evaluateGcvSplines( gcvSplineSet, string([1:size(data, 2)]), ... + newTime-newTime(1), 0); end diff --git a/src/core/parse/parseTreatmentOptimizationDataDirectory.m b/src/core/parse/parseTreatmentOptimizationDataDirectory.m index 56d90fbd5..29ed26762 100644 --- a/src/core/parse/parseTreatmentOptimizationDataDirectory.m +++ b/src/core/parse/parseTreatmentOptimizationDataDirectory.m @@ -108,9 +108,9 @@ strcat(inputs.trialName, "_states"), inputs.model); catch; end try - [inputs.initialJerks, inputs.initialJerksLabels] = ... + [inputs.initialAccelerations, inputs.initialAccelerationsLabels] = ... parseTrialData(inputs.previousResultsDirectory, ... - strcat(inputs.trialName, "_jerks"), inputs.model); + strcat(inputs.trialName, "_accelerations"), inputs.model); catch; end try [inputs.initialTorqueControls, inputs.initialTorqueControlsLabels] = ... From 0b1a21d448ebf8111b43886b203a65af1b0d0289 Mon Sep 17 00:00:00 2001 From: RobSalati <142843461+RobSalati@users.noreply.github.com> Date: Tue, 20 Feb 2024 17:48:34 -0600 Subject: [PATCH 324/365] Treatment optimization and MTP plot restructure Changed plots to use tiledlayout instead of subplot. This gives easier customization of properties such as spacing. Bolded lines for treatment optimization plots Changed titles to include RMSE (RMSE is not calculated yet, but titles have a placeholder) Switched MTP activations & excitations line styles. Include max allowable error in mtp joint moment plots. Added a line to save the mtp settings in the results directory. This is used for reporting the max allowable error on joint moments. --- .../Analysis/plotMtpHillTypeMuscleParams.m | 6 ++- .../Analysis/plotMtpJointMoments.m | 28 ++++++++--- .../plotMtpMuscleExcitationsAndActivations.m | 36 ++++++++------ .../Analysis/plotMtpNormalizedFiberLengths.m | 19 ++++--- .../Analysis/plotMtpPassiveForceCurves.m | 16 +++--- .../Analysis/plotMtpPassiveMomentCurves.m | 20 +++++--- .../saveMuscleTendonPersonalizationResults.m | 2 + ...plotTreatmentOptimizationGroundReactions.m | 33 ++++++++----- .../plotTreatmentOptimizationJointAngles.m | 42 ++++++++++------ .../plotTreatmentOptimizationJointLoads.m | 49 ++++++++++++------- ...otTreatmentOptimizationMuscleActivations.m | 35 ++++++++----- ...plotTreatmentOptimizationSynergyControls.m | 33 ++++++++----- .../plotTreatmentOptimizationTorqueControls.m | 33 ++++++++----- 13 files changed, 225 insertions(+), 127 deletions(-) diff --git a/src/MuscleTendonPersonalization/Analysis/plotMtpHillTypeMuscleParams.m b/src/MuscleTendonPersonalization/Analysis/plotMtpHillTypeMuscleParams.m index 5cc23f034..e699edf9b 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotMtpHillTypeMuscleParams.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMtpHillTypeMuscleParams.m @@ -35,7 +35,7 @@ function plotMtpHillTypeMuscleParams(resultsDirectory) muscleNames = strrep(muscleNames, '_', ' '); figure(Name = "Muscle Model Parameters", ... Units='normalized', ... - Position=[0.1 0.1 0.8 0.8]) + Position=[0.05 0.05 0.9 0.85]) paramLabels = ["Activation Time Constant", ... "Activation Nonlinearity", ... @@ -43,8 +43,10 @@ function plotMtpHillTypeMuscleParams(resultsDirectory) "EMG Scaling Factor", ... "Optimal Fiber Length Scaling Factor", ... "Tendon Slack Length Scaling Factor"]; +t = tiledlayout(1, 6, ... + TileSpacing='Compact', Padding='Compact'); for i = 1 : numel(paramLabels) - subplot(1, 6, i) + nexttile(i) barh(1:numel(muscleNames), params(i,:)) title(textwrap(paramLabels(i), 20), FontSize=12) diff --git a/src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m b/src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m index a5418552e..751107fba 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m @@ -30,7 +30,8 @@ % ----------------------------------------------------------------------- % function plotMtpJointMoments(resultsDirectory) analysisDirectory = fullfile(resultsDirectory, "Analysis"); -numRows = 1; +% Include max allowable error, and rmse +figureHeight = 1; [jointLabels, idMoments] = extractMtpDataFromSto( ... fullfile(analysisDirectory, "inverseDynamicsJointMoments")); @@ -38,10 +39,12 @@ function plotMtpJointMoments(resultsDirectory) [~, modelMoments] = extractMtpDataFromSto( ... fullfile(analysisDirectory, "modelJointMoments")); +load(fullfile(analysisDirectory, "mtpInputs.mat"), "mtpInputs"); + if exist(fullfile(analysisDirectory, "modelJointMomentsSynx"), "dir") [~, modelMomentsSynx] = extractMtpDataFromSto( ... fullfile(analysisDirectory, "modelJointMomentsSynx")); - numRows = numRows + 1; + figureHeight = figureHeight + 1; else modelMomentsSynx = []; end @@ -64,18 +67,24 @@ function plotMtpJointMoments(resultsDirectory) figure(Name = "Joint Moments", ... Units='normalized', ... - Position=[0.1 0.1 0.8 0.8]) + Position=[0.05 0.05 0.9 0.85]) time = 1:1:size(meanIdMoments,1); -numColumns = numel(jointLabels); +figureWidth = numel(jointLabels); +t = tiledlayout(figureHeight, figureWidth, ... + TileSpacing='Compact', Padding='Compact'); for i=1:numel(jointLabels) if ~isempty(meanMoments) - subplot(numRows, numColumns, i); + nexttile(i); hold on plotMeanAndStd(meanMoments(:,i), stdMoments(:,i), time, 'r-') plotMeanAndStd(meanIdMoments(:,i), stdIdMoments(:,i), time, 'b-') hold off set(gca, fontsize=11) - title(jointLabels(i), fontsize=12) + rmse = rms(meanMoments(:,i) - meanIdMoments(:,i)); + mae = mtpInputs.tasks{1}.costTerms{1}.maxAllowableError; + + title(sprintf("%s \n RMSE: %.4f, MAE: %f", ... + jointLabels(i), rmse, mae), fontsize=12) axis([0 size(meanIdMoments, 1) minMoment, maxMoment]) if i == 1 legend("Mean Moment No Synx", "Mean Inverse Dynamics Moment") @@ -83,13 +92,16 @@ function plotMtpJointMoments(resultsDirectory) end end if ~isempty(meanMomentsSynx) - subplot(numRows, numColumns, i+numColumns) + nexttile(i+figureWidth); hold on plotMeanAndStd(meanMomentsSynx(:,i), stdMomentsSynx(:,i), time, 'r-') plotMeanAndStd(meanIdMoments(:,i), stdIdMoments(:,i), time, 'b-') hold off set(gca, fontsize=11) - title(jointLabels(i), FontSize=12) + rmse = rms(meanMomentsSynx(:,i) - meanIdMoments(:,i)); + mae = mtpInputs.synergyExtrapolation.costTerms{1}.maxAllowableError; + title(sprintf("%s \n RMSE: %.4f, MAE: %f", ... + jointLabels(i), rmse, mae), fontsize=12) axis([0 size(meanIdMoments, 1) minMoment, maxMoment]) if i == 1 legend("Mean Moment Synx", "Mean Inverse Dynamics Moment") diff --git a/src/MuscleTendonPersonalization/Analysis/plotMtpMuscleExcitationsAndActivations.m b/src/MuscleTendonPersonalization/Analysis/plotMtpMuscleExcitationsAndActivations.m index 902560ec3..acdae3bca 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotMtpMuscleExcitationsAndActivations.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMtpMuscleExcitationsAndActivations.m @@ -59,37 +59,41 @@ function plotMtpMuscleExcitationsAndActivations(resultsDirectory) stdExcitationsSynx = std(excitationsSynx,[], 3); meanActivationsSynx = mean(activationsSynx, 3); stdActivationsSynx = std(activationsSynx,[], 3); +time = 1:1:size(meanExcitations,1); +figureWidth = ceil(sqrt(numel(muscleNames))); +figureHeight = ceil(numel(muscleNames)/figureWidth); figure(Name = "Muscle Excitations and Activations", ... Units='normalized', ... - Position=[0.1 0.1 0.8 0.8]) + Position=[0.05 0.05 0.9 0.85]) +t = tiledlayout(figureHeight, figureWidth, ... + TileSpacing='Compact', Padding='Compact'); -time = 1:1:size(meanExcitations,1); -numWindows = ceil(sqrt(numel(muscleNames))); for i = 1:numel(muscleNames) - subplot(numWindows, numWindows, i); + nexttile(i); hold on - plotMeanAndStd(meanExcitations(:,i), stdExcitations(:,i), time, 'b-'); - plotMeanAndStd(meanActivations(:,i), stdActivations(:,i), time, 'r-'); if ~isempty(meanExcitationsSynx) - plotMeanAndStd(meanExcitationsSynx(:,i), stdExcitationsSynx(:,i), time, 'b--'); - end - if ~isempty(meanActivationsSynx) - plotMeanAndStd(meanActivationsSynx(:,i), stdActivationsSynx(:,i), time, 'r--'); + plotMeanAndStd(meanExcitations(:,i), stdExcitations(:,i), time, 'b--'); + plotMeanAndStd(meanActivations(:,i), stdActivations(:,i), time, 'r--'); + plotMeanAndStd(meanExcitationsSynx(:,i), stdExcitationsSynx(:,i), time, 'b-'); + plotMeanAndStd(meanActivationsSynx(:,i), stdActivationsSynx(:,i), time, 'r-'); + else + plotMeanAndStd(meanExcitations(:,i), stdExcitations(:,i), time, 'b-'); + plotMeanAndStd(meanActivations(:,i), stdActivations(:,i), time, 'r-'); end set(gca, fontsize=11) axis([1 size(meanExcitations, 1) 0 1]) title(muscleNames(i), FontSize=12); if i == 1 - legend ('Excitation(without residual)', ... - 'Activation(without residual)', ... - 'Excitation(with residual)', ... - 'Activation(with residual)'); + legend ('Excitation (No SynX)', ... + 'Activation (No SynX)', ... + 'Excitation (With SynX)', ... + 'Activation (With SynX)'); end - if mod(i,numWindows) == 1 + if mod(i,figureWidth) == 1 ylabel("Magnitude") end - if i>numel(muscleNames)-numWindows + if i>numel(muscleNames)-figureWidth xlabel("Time Points") end end diff --git a/src/MuscleTendonPersonalization/Analysis/plotMtpNormalizedFiberLengths.m b/src/MuscleTendonPersonalization/Analysis/plotMtpNormalizedFiberLengths.m index 5b7657386..22a35ec20 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotMtpNormalizedFiberLengths.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMtpNormalizedFiberLengths.m @@ -35,17 +35,20 @@ function plotMtpNormalizedFiberLengths(resultsDirectory) muscleNames = strrep(muscleNames, '_', ' '); meanFiberLengths = mean(normalizedFiberLengths, 3); stdFiberLengths = std(normalizedFiberLengths, [], 3); - -figure(Name = "Normalized Fiber Lengths", ... - Units='normalized', ... - Position=[0.1 0.1 0.8 0.8]) time = 1:1:size(meanFiberLengths,1); -numWindows = ceil(sqrt(numel(muscleNames))); passiveLower = ones(size(time))*0.7; passiveUpper = ones(size(time)); +figureWidth = ceil(sqrt(numel(muscleNames))); +figureHeight = ceil(numel(muscleNames)/figureWidth); +figure(Name = "Normalized Fiber Lengths", ... + Units='normalized', ... + Position=[0.05 0.05 0.9 0.85]) +t = tiledlayout(figureHeight, figureWidth, ... + TileSpacing='Compact', Padding='Compact'); + for i=1:numel(muscleNames) - subplot(numWindows, numWindows, i); + nexttile(i); hold on plotMeanAndStd(meanFiberLengths(:,i), stdFiberLengths(:,i), time, 'b-') plot(time, passiveUpper, 'r--', LineWidth=2); @@ -54,10 +57,10 @@ function plotMtpNormalizedFiberLengths(resultsDirectory) set(gca, fontsize=11) axis([1 size(meanFiberLengths, 1) 0 1.5]) title(muscleNames(i), FontSize=12); - if mod(i,numWindows) == 1 + if mod(i,figureWidth) == 1 ylabel(textwrap("Normalized Fiber Length",10), FontSize=12) end - if i>numel(muscleNames)-numWindows + if i>numel(muscleNames)-figureWidth xlabel("Time Points", FontSize=12) end end diff --git a/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveForceCurves.m b/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveForceCurves.m index 7f7936eb2..ab74090ec 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveForceCurves.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveForceCurves.m @@ -38,24 +38,28 @@ function plotMtpPassiveForceCurves(resultsDirectory) meanModelForce = mean(modelForce, 3); stdModelForce = std(modelForce, [], 3); maxForce = max(meanModelForce,[], 'all'); -numWindows = ceil(sqrt(numel(muscleNames))); +time = 1:1:size(meanModelForce,1); +figureWidth = ceil(sqrt(numel(muscleNames))); +figureHeight = ceil(numel(muscleNames)/figureWidth); figure(Name = "Passive Force Curves", ... Units='normalized', ... - Position=[0.1 0.1 0.8 0.8]) -time = 1:1:size(meanModelForce,1); + Position=[0.05 0.05 0.9 0.85]) +t = tiledlayout(figureHeight, figureWidth, ... + TileSpacing='Compact', Padding='Compact'); + for i = 1:numel(muscleNames) - subplot(numWindows, numWindows, i) + nexttile(i); hold on plotMeanAndStd(meanModelForce(:,i), stdModelForce(:,i), time, 'b-'); hold off set(gca, fontsize=11) axis([1 numel(time) 0 maxForce]) title(muscleNames(i), FontSize=12); - if mod(i,numWindows) == 1 + if mod(i,figureWidth) == 1 ylabel("Magnitude") end - if i>numel(muscleNames)-numWindows + if i>numel(muscleNames)-figureWidth xlabel("Time Points") end end diff --git a/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveMomentCurves.m b/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveMomentCurves.m index f9d5a73bc..f29c75dfe 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveMomentCurves.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveMomentCurves.m @@ -44,30 +44,36 @@ function plotMtpPassiveMomentCurves(resultsDirectory) max(meanMomentsModel, [], 'all')]); minMoment = min([min(meanMomentsExperimental, [], 'all'), ... min(meanMomentsModel, [], 'all')]); - -numWindows = ceil(sqrt(numel(momentNames))); time = 1:1:size(meanMomentsModel,1); + +figureWidth = ceil(sqrt(numel(momentNames))); +figureHeight = ceil(numel(momentNames)/figureWidth); + figure(Name = "Passive Moment Curves", ... Units='normalized', ... - Position=[0.1 0.1 0.8 0.8]) + Position=[0.05 0.05 0.9 0.85]) +t = tiledlayout(figureHeight, figureWidth, ... + TileSpacing='Compact', Padding='Compact'); for i = 1:numel(momentNames) - subplot(numWindows, numWindows, i) + nexttile(i); hold on plotMeanAndStd(meanMomentsExperimental(:,i), stdMomentsExperimental(:,i), ... time, 'k-') plotMeanAndStd(meanMomentsModel(:,i), stdMomentsModel(:,i), time, 'r-') hold off set(gca, fontsize=11) - title(momentNames(i), FontSize=12) + rmse = rms(passiveMomentsExperimental(:,i) - passiveMomentsModel(:,i)); + title(sprintf("%s \n RMSE: %.4f", ... + momentNames(i), rmse), FontSize=12) axis([1 size(meanMomentsModel, 1) minMoment maxMoment]) if i == 1 legend("Experimental", "Model") end - if mod(i,numWindows) == 1 + if mod(i,figureWidth) == 1 ylabel("Moment [Nm]") end - if i>numel(momentNames)-numWindows + if i>numel(momentNames)-figureWidth xlabel("Time Points") end end diff --git a/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m b/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m index bf70b0ccf..fb3e5458c 100644 --- a/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m +++ b/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m @@ -41,6 +41,7 @@ function saveMuscleTendonPersonalizationResults(mtpInputs, finalValues, ... if nargin < 6 precalInputs = []; end + if ~isempty(precalInputs) saveMtpPassiveMomentData(precalInputs, modeledValues, analysisDirectory); saveMtpPassiveForceData(mtpInputs, resultsStruct, analysisDirectory); @@ -53,6 +54,7 @@ function saveMuscleTendonPersonalizationResults(mtpInputs, finalValues, ... saveMtpJointMomentData(mtpInputs, resultsStruct, analysisDirectory); saveMtpMuscleModelParameters(mtpInputs, finalValues, ... fullfile(analysisDirectory, "muscleModelParameters")); +save(fullfile(analysisDirectory, "mtpInputs.mat"), "mtpInputs"); model = Model(mtpInputs.model); muscleNames = getMusclesFromCoordinates(model, mtpInputs.coordinateNames); writeMuscleTendonPersonalizationOsimxFile(mtpInputs.modelFileName, ... diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m index e9d0a512e..0d9acb784 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m @@ -33,14 +33,14 @@ function plotTreatmentOptimizationGroundReactions(experimentalGroundReactionFile import org.opensim.modeling.Storage experimentalGRStorage = Storage(experimentalGroundReactionFile); -experimentalGRLabels = getStorageColumnNames(experimentalGRStorage); +experimentalLabels = getStorageColumnNames(experimentalGRStorage); experimentalGR = storageToDoubleMatrix(experimentalGRStorage)'; experimentalTime = findTimeColumn(experimentalGRStorage); if experimentalTime(1) ~= 0 experimentalTime = experimentalTime - experimentalTime(1); end modelGRStorage = Storage(modelGroundReactionFile); -modelGRLabels = getStorageColumnNames(modelGRStorage); +modelLabels = getStorageColumnNames(modelGRStorage); modelGR = storageToDoubleMatrix(modelGRStorage)'; modelTime = findTimeColumn(modelGRStorage); if modelTime(1) ~= 0 @@ -48,19 +48,19 @@ function plotTreatmentOptimizationGroundReactions(experimentalGroundReactionFile end % Check both GR files are in the same order. -experimentalForcePlate1 = contains(experimentalGRLabels, "1"); -modelForcePlate1 = contains(modelGRLabels, "1"); +experimentalForcePlate1 = contains(experimentalLabels, "1"); +modelForcePlate1 = contains(modelLabels, "1"); if experimentalForcePlate1 ~= modelForcePlate1 temp = modelGR; modelGR(:, ~experimentalForcePlate1) = modelGR(:, experimentalForcePlate1); modelGR(:, experimentalForcePlate1) = temp(:, ~experimentalForcePlate1); end -momentIndices = contains(experimentalGRLabels, "_m"); -forceIndices = contains(experimentalGRLabels, "_v"); +momentIndices = contains(experimentalLabels, "_m"); +forceIndices = contains(experimentalLabels, "_v"); includedIndices = momentIndices | forceIndices; -experimentalGRLabels = experimentalGRLabels(includedIndices); +experimentalLabels = experimentalLabels(includedIndices); modelGR = modelGR(:, includedIndices); experimentalGR = experimentalGR(:, includedIndices); @@ -75,18 +75,27 @@ function plotTreatmentOptimizationGroundReactions(experimentalGroundReactionFile figure(Name = "Treatment Optimization Ground Reactions") subplotNumber = 1; figureNumber = 1; -for i=1:numel(experimentalGRLabels) +t = tiledlayout(figureHeight, figureWidth, ... + TileSpacing='Compact', Padding='Compact'); +xlabel(t, "Time [s]") +ylabel(t, "Ground Reactions") +for i=1:numel(experimentalLabels) if i > figureSize * figureNumber figureNumber = figureNumber + 1; figure(Name="Treatment Optimization Ground Reactions") + t = tiledlayout(figureHeight, figureWidth, ... + TileSpacing='Compact', Padding='Compact'); + xlabel(t, "Time [s]") + ylabel(t, "Ground Reactions") subplotNumber = 1; end - subplot(figureHeight, figureWidth, subplotNumber) + nexttile(subplotNumber); hold on - plot(experimentalTime, experimentalGR(:, i)); - plot(modelTime, modelGR(:, i)) + plot(experimentalTime, experimentalGR(:, i), LineWidth=2); + plot(modelTime, modelGR(:, i), LineWidth=2); hold off - title(strrep(experimentalGRLabels(i), "_", " ")) + title(sprintf("%s \n RMSE: %d", ... + strrep(experimentalLabels(i), "_", " "), 0)) if subplotNumber==1 legend("Experimental", "Model") end diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m index 77be8386f..0507494de 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m @@ -33,7 +33,7 @@ function plotTreatmentOptimizationJointAngles(experimentalAnglesFile, ... import org.opensim.modeling.Storage experimentalAnglesStorage = Storage(experimentalAnglesFile); -angleLabels = getStorageColumnNames(experimentalAnglesStorage); +labels = getStorageColumnNames(experimentalAnglesStorage); experimentalAngles = storageToDoubleMatrix(experimentalAnglesStorage)'; experimentalAngles = experimentalAngles .* 180/pi; experimentalTime = findTimeColumn(experimentalAnglesStorage); @@ -49,33 +49,42 @@ function plotTreatmentOptimizationJointAngles(experimentalAnglesFile, ... end if nargin < 3 - figureWidth = ceil(sqrt(numel(angleLabels))); -end -if nargin < 4 - figureHeight = ceil(sqrt(numel(angleLabels))); + figureWidth = ceil(sqrt(numel(labels))); + figureHeight = ceil(numel(labels)/figureWidth); +elseif nargin < 4 + figureHeight = ceil(numel(labels)/figureWidth); end figureSize = figureWidth * figureHeight; figure(Name = "Treatment Optimization Joint Angles", ... Units='normalized', ... - Position=[0 0 1 1]) + Position=[0.05 0.05 0.9 0.85]) subplotNumber = 1; figureNumber = 1; -for i=1:numel(angleLabels) +t = tiledlayout(figureHeight, figureWidth, ... + TileSpacing='Compact', Padding='Compact'); +xlabel(t, "Time [s]") +ylabel(t, "Joint Angle [deg]") +for i=1:numel(labels) if i > figureSize * figureNumber figureNumber = figureNumber + 1; figure(Name="Treatment Optimization Joint Angles", ... Units='normalized', ... - Position=[0 0 1 1]) + Position=[0.05 0.05 0.9 0.85]) + t = tiledlayout(figureHeight, figureWidth, ... + TileSpacing='Compact', Padding='Compact'); + xlabel(t, "Time [s]") + ylabel(t, "Joint Angle [deg]") subplotNumber = 1; end - subplot(figureHeight, figureWidth, subplotNumber) + nexttile(subplotNumber); hold on - plot(experimentalTime, experimentalAngles(:, i)); - plot(modelTime, modelAngles(:, i)); + plot(experimentalTime, experimentalAngles(:, i), LineWidth=2); + plot(modelTime, modelAngles(:, i), LineWidth=2); hold off - title(strrep(angleLabels(i), "_", " ")); - + title(sprintf("%s \n RMSE: %d", ... + strrep(labels(i), "_", " "), 0)); + if subplotNumber==1 legend("Experimental", "Model") end @@ -86,6 +95,9 @@ function plotTreatmentOptimizationJointAngles(experimentalAnglesFile, ... if maxAngle-minAngle < 10 ylim([(maxAngle+minAngle)/2-10, (maxAngle+minAngle)/2+10]) end - subplotNumber = subplotNumber + 1; -end + % if subplotNumber > figureSize-figureHeight + % xlabel("Time [s]") + % end + subplotNumber = subplotNumber + 1; +end \ No newline at end of file diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoads.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoads.m index f3cb22aba..cf82fd27e 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoads.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoads.m @@ -33,7 +33,7 @@ function plotTreatmentOptimizationJointLoads(experimentalMomentsFile, ... import org.opensim.modeling.Storage experimentalMomentsStorage = Storage(experimentalMomentsFile); -momentLabels = getStorageColumnNames(experimentalMomentsStorage); +labels = getStorageColumnNames(experimentalMomentsStorage); experimentalMoments = storageToDoubleMatrix(experimentalMomentsStorage)'; experimentalTime = findTimeColumn(experimentalMomentsStorage); if experimentalTime(1) ~= 0 @@ -47,38 +47,49 @@ function plotTreatmentOptimizationJointLoads(experimentalMomentsFile, ... end if nargin < 3 - figureWidth = ceil(sqrt(numel(momentLabels))); -end -if nargin < 4 - figureHeight = ceil(sqrt(numel(momentLabels))); + figureWidth = ceil(sqrt(numel(labels))); + figureHeight = ceil(numel(labels)/figureWidth); +elseif nargin < 4 + figureHeight = ceil(numel(labels)/figureWidth); end figureSize = figureWidth * figureHeight; figure(Name="Treatment Optimization Joint Loads", ... Units='normalized', ... - Position=[0 0 1 1]) + Position=[0.05 0.05 0.9 0.85]) subplotNumber = 1; figureNumber = 1; -for i=1:numel(momentLabels) +t = tiledlayout(figureHeight, figureWidth, ... + TileSpacing='Compact', Padding='Compact'); +xlabel(t, "Time [s]") +ylabel(t, "Joint Load") +for i=1:numel(labels) if i > figureSize * figureNumber figureNumber = figureNumber + 1; figure(Name="Treatment Optimization Joint Loads", ... Units='normalized', ... - Position=[0 0 1 1]) + Position=[0.05 0.05 0.9 0.85]) + t = tiledlayout(figureHeight, figureWidth, ... + TileSpacing='Compact', Padding='Compact'); + xlabel(t, "Time [s]") + ylabel(t, "Joint Load") subplotNumber = 1; end - subplot(figureHeight, figureWidth, subplotNumber) + nexttile(subplotNumber); hold on - plot(experimentalTime, experimentalMoments(:, i)); - plot(modelTime, modelMoments(:, i)); + plot(experimentalTime, experimentalMoments(:, i), LineWidth=2); + plot(modelTime, modelMoments(:, i), LineWidth=2); hold off - if contains(momentLabels(i), "moment") - title(strcat(strrep(momentLabels(i), "_", " "), " [Nm]")) - elseif contains(momentLabels(i), "force") - title(strcat(strrep(momentLabels(i), "_", " "), " [N]")) + if contains(labels(i), "moment") + title(sprintf("%s \n RMSE: %d", ... + strcat(strrep(labels(i), "_", " "), " [Nm]"), 0)) + elseif contains(labels(i), "force") + title(sprintf("%s \n RMSE: %d", ... + strcat(strrep(labels(i), "_", " "), " [N]"), 0)) else - title(strrep(momentLabels(i), "_", " ")) + title(sprintf("%s \n RMSE: %d", ... + strrep(labels(i), "_", " "), 0)) end if subplotNumber==1 @@ -86,14 +97,14 @@ function plotTreatmentOptimizationJointLoads(experimentalMomentsFile, ... end xlim([0, experimentalTime(end)]) - % if subplotNumber > figureSize-figureHeight - % xlabel("Time [s]") - % end maxLoad = max([experimentalMoments(:, i); modelMoments(:, i)],[], "all"); minLoad = min([experimentalMoments(:, i); modelMoments(:, i)],[], "all"); if maxLoad-minLoad < 10 ylim([(maxLoad+minLoad)/2-10, (maxLoad+minLoad)/2+10]) end + % if subplotNumber > figureSize-figureHeight + % xlabel("Time [s]") + % end subplotNumber = subplotNumber + 1; end end diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivations.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivations.m index 47dff87f3..2f30cdc9b 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivations.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivations.m @@ -33,7 +33,7 @@ function plotTreatmentOptimizationMuscleActivations(experimentalActivationsFile, import org.opensim.modeling.Storage experimentalActivationsStorage = Storage(experimentalActivationsFile); -muscleLabels = getStorageColumnNames(experimentalActivationsStorage); +labels = getStorageColumnNames(experimentalActivationsStorage); experimentalActivations = storageToDoubleMatrix(experimentalActivationsStorage)'; experimentalTime = findTimeColumn(experimentalActivationsStorage); if experimentalTime(1) ~= 0 @@ -47,30 +47,41 @@ function plotTreatmentOptimizationMuscleActivations(experimentalActivationsFile, end if nargin < 3 - figureWidth = ceil(sqrt(numel(muscleLabels))); -end -if nargin < 4 - figureHeight = ceil(sqrt(numel(muscleLabels))); + figureWidth = ceil(sqrt(numel(labels))); + figureHeight = ceil(numel(labels)/figureWidth); +elseif nargin < 4 + figureHeight = ceil(numel(labels)/figureWidth); end figureSize = figureWidth * figureHeight; figure(Name="Treatment Optimization Muscle Activations", ... Units='normalized', ... - Position=[0 0 1 1]) + Position=[0.05 0.05 0.9 0.85]) subplotNumber = 1; figureNumber = 1; -for i=1:numel(muscleLabels) +t = tiledlayout(figureHeight, figureWidth, ... + TileSpacing='Compact', Padding='Compact'); +xlabel(t, "Time [s]") +ylabel(t, "Muscle Activations") +for i=1:numel(labels) if i > figureSize * figureNumber figureNumber = figureNumber + 1; - figure(Name="Treatment Optimization Muscle Activations") + figure(Name="Treatment Optimization Muscle Activations", ... + Units='normalized', ... + Position=[0.05 0.05 0.9 0.85]) + t = tiledlayout(figureHeight, figureWidth, ... + TileSpacing='Compact', Padding='Compact'); + xlabel(t, "Time [s]") + ylabel(t, "Muscle Activations") subplotNumber = 1; end - subplot(figureHeight, figureWidth, subplotNumber) + nexttile(subplotNumber); hold on - plot(experimentalTime, experimentalActivations(:, i)) - plot(modelTime, modelActivations(:, i)) + plot(experimentalTime, experimentalActivations(:, i), LineWidth=2) + plot(modelTime, modelActivations(:, i), LineWidth=2) hold off - title(strrep(muscleLabels(i), "_", " ")); + title(sprintf("%s \n RMSE: %d", ... + strrep(labels(i), "_", " "), 0)); if subplotNumber == 1 legend("Experimental", "Model") end diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationSynergyControls.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationSynergyControls.m index cb4f5ff03..f3c3dc4a2 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationSynergyControls.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationSynergyControls.m @@ -33,7 +33,7 @@ function plotTreatmentOptimizationSynergyControls(controlsFile, ... import org.opensim.modeling.Storage controlsStorage = Storage(controlsFile); -controlLabels = getStorageColumnNames(controlsStorage); +labels = getStorageColumnNames(controlsStorage); controls = storageToDoubleMatrix(controlsStorage)'; time = findTimeColumn(controlsStorage); if time(1) ~= 0 @@ -41,32 +41,43 @@ function plotTreatmentOptimizationSynergyControls(controlsFile, ... end if nargin < 3 - figureWidth = ceil(sqrt(numel(controlLabels))); -end -if nargin < 4 - figureHeight = ceil(sqrt(numel(controlLabels))); + figureWidth = ceil(sqrt(numel(labels))); + figureHeight = ceil(numel(labels)/figureWidth); +elseif nargin < 4 + figureHeight = ceil(numel(labels)/figureWidth); end figureSize = figureWidth * figureHeight; figure(Name="Treatment Optimization Synergy Controls", ... Units='normalized', ... - Position=[0 0 1 1]) + Position=[0.05 0.05 0.9 0.85]) +t = tiledlayout(figureHeight, figureWidth, ... + TileSpacing='Compact', Padding='Compact'); +xlabel(t, "Time [s]") +ylabel(t, "Synergy Controls") subplotNumber = 1; figureNumber = 1; -for i=1:numel(controlLabels) +for i=1:numel(labels) if i > figureSize * figureNumber figureNumber = figureNumber + 1; figure(Name="Treatment Optimization Synergy Controls", ... Units='normalized', ... - Position=[0 0 1 1]) + Position=[0.05 0.05 0.9 0.85]) + t = tiledlayout(figureHeight, figureWidth, ... + TileSpacing='Compact', Padding='Compact'); + xlabel(t, "Time [s]") + ylabel(t, "Synergy Controls") subplotNumber = 1; end - subplot(figureHeight, figureWidth, subplotNumber) + nexttile(subplotNumber); hold on - plot(time, controls(:, i)); + plot(time, controls(:, i), LineWidth=2); hold off - title(strrep(controlLabels(i), "_", " ")); + title(strrep(labels(i), "_", " ")); xlim([0, time(end)]) ylim([0, 1]) + % if subplotNumber > figureSize-figureHeight + % xlabel("Time [s]") + % end subplotNumber = subplotNumber + 1; end end diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationTorqueControls.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationTorqueControls.m index 7e8015b97..6ca0845b5 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationTorqueControls.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationTorqueControls.m @@ -33,7 +33,7 @@ function plotTreatmentOptimizationTorqueControls(controlsFile, ... import org.opensim.modeling.Storage controlsStorage = Storage(controlsFile); -controlLabels = getStorageColumnNames(controlsStorage); +labels = getStorageColumnNames(controlsStorage); controls = storageToDoubleMatrix(controlsStorage)'; time = findTimeColumn(controlsStorage); if time(1) ~= 0 @@ -41,32 +41,43 @@ function plotTreatmentOptimizationTorqueControls(controlsFile, ... end if nargin < 3 - figureWidth = ceil(sqrt(numel(controlLabels))); -end -if nargin < 4 - figureHeight = ceil(sqrt(numel(controlLabels))); + figureWidth = ceil(sqrt(numel(labels))); + figureHeight = ceil(numel(labels)/figureWidth); +elseif nargin < 4 + figureHeight = ceil(numel(labels)/figureWidth); end figureSize = figureWidth * figureHeight; figure(Name="Treatment Optimization Torque Controls", ... Units='normalized', ... - Position=[0 0 1 1]) + Position=[0.05 0.05 0.9 0.85]) subplotNumber = 1; figureNumber = 1; -for i=1:numel(controlLabels) +t = tiledlayout(figureHeight, figureWidth, ... + TileSpacing='Compact', Padding='Compact'); +xlabel(t, "Time [s]") +ylabel(t, "Torque Controls [Nm]") +for i=1:numel(labels) if i > figureSize * figureNumber figureNumber = figureNumber + 1; figure(Name="Treatment Optimization Torque Controls", ... Units='normalized', ... - Position=[0 0 1 1]) + Position=[0.05 0.05 0.9 0.85]) + t = tiledlayout(figureHeight, figureWidth, ... + TileSpacing='Compact', Padding='Compact'); + xlabel(t, "Time [s]") + ylabel(t, "Torque Controls [Nm]") subplotNumber = 1; end - subplot(figureHeight, figureWidth, subplotNumber) + nexttile(subplotNumber); hold on - plot(time, controls(:, i)); + plot(time, controls(:, i), LineWidth=2); hold off - title(strrep(controlLabels(i), "_", " ")); + title(strrep(labels(i), "_", " ")); xlim([0, time(end)]) + % if subplotNumber > figureSize-figureHeight + % xlabel("Time [s]") + % end subplotNumber = subplotNumber + 1; end end From a9856b9dbdbb4303323cfa7b9d6bf8dd583a76ee Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Tue, 20 Feb 2024 22:52:14 -0600 Subject: [PATCH 325/365] add surrogate model saving --- .../makeTreatmentOptimizationInputs.m | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m b/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m index 0b44a9920..026136937 100644 --- a/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m +++ b/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m @@ -43,7 +43,14 @@ inputs = makeTerminalConstraintBounds(inputs); inputs = makeOptimalControlBounds(inputs); if strcmp(inputs.controllerType, "synergy") - inputs.surrogateMuscles = SurrogateModelCreation(inputs); + if isfile("surrogateMuscles.mat") + inputs.surrogateMuscles = load("surrogateMuscles.mat").surrogateMuscles; + else + inputs.surrogateMuscles = SurrogateModelCreation(inputs); + surrogateMuscles = inputs.surrogateMuscles; + save("surrogateMuscles.mat", "surrogateMuscles"); + end + end end From cb85d9816b818566f3a0635dd7d1966c497f4bb9 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Tue, 20 Feb 2024 22:52:31 -0600 Subject: [PATCH 326/365] initial change to kinetic consistency --- .../PathTerms/calcKineticPathConstraint.m | 7 +------ .../TrackingOptimization/TrackingOptimizationTool.m | 2 +- .../TrackingOptimization/calcSynergyBasedModeledValues.m | 2 -- src/TreatmentOptimization/makeSurrogateModel.m | 2 +- 4 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/TreatmentOptimization/PathTerms/calcKineticPathConstraint.m b/src/TreatmentOptimization/PathTerms/calcKineticPathConstraint.m index 7e6690776..08a5e091e 100644 --- a/src/TreatmentOptimization/PathTerms/calcKineticPathConstraint.m +++ b/src/TreatmentOptimization/PathTerms/calcKineticPathConstraint.m @@ -33,12 +33,7 @@ modeledValues, torqueControls, loadName) inverseDynamicsIndex = find(strcmp(convertCharsToStrings(inputs.inverseDynamicsMomentLabels), ... loadName)); -synergyIndex = find(strcmp(strcat(inputs.surrogateModelCoordinateNames, ... - '_moment'), loadName)); -if isempty(synergyIndex) - synergyIndex = find(strcmp(strcat(inputs.surrogateModelCoordinateNames, ... - '_force'), loadName)); -end +synergyIndex = find(inputs.dofsActuatedIndex == inverseDynamicsIndex); torqueIndex = find(strcmp(strcat(inputs.torqueControllerCoordinateNames, ... '_moment'), loadName)); if isempty(torqueIndex) diff --git a/src/TreatmentOptimization/TrackingOptimization/TrackingOptimizationTool.m b/src/TreatmentOptimization/TrackingOptimization/TrackingOptimizationTool.m index 1d1dc237c..2312d8a24 100644 --- a/src/TreatmentOptimization/TrackingOptimization/TrackingOptimizationTool.m +++ b/src/TreatmentOptimization/TrackingOptimization/TrackingOptimizationTool.m @@ -33,7 +33,7 @@ function TrackingOptimizationTool(settingsFileName) settingsTree = xml2struct(settingsFileName); verifyVersion(settingsTree, "TrackingOptimizationTool"); [inputs, params] = parseTrackingOptimizationSettingsTree(settingsTree); -inputs = normalizeSynergyData(inputs); +%inputs = normalizeSynergyData(inputs); inputs = makeTreatmentOptimizationInputs(inputs, params); [inputs, outputs] = solveOptimalControlProblem(inputs, params); reportTreatmentOptimizationResults(outputs, inputs); diff --git a/src/TreatmentOptimization/TrackingOptimization/calcSynergyBasedModeledValues.m b/src/TreatmentOptimization/TrackingOptimization/calcSynergyBasedModeledValues.m index f572a5e33..91f7a9dbd 100644 --- a/src/TreatmentOptimization/TrackingOptimization/calcSynergyBasedModeledValues.m +++ b/src/TreatmentOptimization/TrackingOptimization/calcSynergyBasedModeledValues.m @@ -52,8 +52,6 @@ modeledValues.muscleJointMoments = permute(muscleJointMoments, [3 2 1]); modeledValues.muscleJointMoments = modeledValues.muscleJointMoments(:, ... inputs.surrogateModelIndex); - modeledValues.muscleJointMoments = modeledValues.muscleJointMoments(:, ... - inputs.dofsActuatedIndex); modeledValues.muscleActivations = permute(modeledValues.muscleActivations, [3 2 1]); end end diff --git a/src/TreatmentOptimization/makeSurrogateModel.m b/src/TreatmentOptimization/makeSurrogateModel.m index 411c4f22f..92d3902aa 100644 --- a/src/TreatmentOptimization/makeSurrogateModel.m +++ b/src/TreatmentOptimization/makeSurrogateModel.m @@ -41,7 +41,7 @@ for j = 1 : length(inputs.surrogateModelCoordinateNames) if strcmp(inputs.inverseDynamicsMomentLabels(i), ... strcat(inputs.surrogateModelCoordinateNames(j), '_moment')) - inputs.dofsActuatedIndex(end+1) = j; + inputs.dofsActuatedIndex(end+1) = i; end end end From ae4cc37bd6b5f213501a51a6e364860b9fadb43c Mon Sep 17 00:00:00 2001 From: RobSalati Date: Tue, 20 Feb 2024 23:36:31 -0600 Subject: [PATCH 327/365] Added RMSE to treatment optimization plots --- .../Analysis/plotMtpJointMoments.m | 18 +++++++++++------- .../plotMtpMuscleExcitationsAndActivations.m | 6 +++++- ...plotTreatmentOptimizationGroundReactions.m | 8 ++++++-- .../plotTreatmentOptimizationJointAngles.m | 8 ++++++-- .../plotTreatmentOptimizationJointLoads.m | 19 ++++++++++++------- ...otTreatmentOptimizationMuscleActivations.m | 8 ++++++-- 6 files changed, 46 insertions(+), 21 deletions(-) diff --git a/src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m b/src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m index 751107fba..19178b1d4 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m @@ -39,7 +39,7 @@ function plotMtpJointMoments(resultsDirectory) [~, modelMoments] = extractMtpDataFromSto( ... fullfile(analysisDirectory, "modelJointMoments")); -load(fullfile(analysisDirectory, "mtpInputs.mat"), "mtpInputs"); +% load(fullfile(analysisDirectory, "mtpInputs.mat"), "mtpInputs"); if exist(fullfile(analysisDirectory, "modelJointMomentsSynx"), "dir") [~, modelMomentsSynx] = extractMtpDataFromSto( ... @@ -81,10 +81,12 @@ function plotMtpJointMoments(resultsDirectory) hold off set(gca, fontsize=11) rmse = rms(meanMoments(:,i) - meanIdMoments(:,i)); - mae = mtpInputs.tasks{1}.costTerms{1}.maxAllowableError; + % mae = mtpInputs.tasks{1}.costTerms{1}.maxAllowableError; - title(sprintf("%s \n RMSE: %.4f, MAE: %f", ... - jointLabels(i), rmse, mae), fontsize=12) + % title(sprintf("%s \n RMSE: %.4f, MAE: %f", ... + % jointLabels(i), rmse, mae), fontsize=12) + title(sprintf("%s \n RMSE: %.4f", ... + jointLabels(i), rmse), fontsize=12) axis([0 size(meanIdMoments, 1) minMoment, maxMoment]) if i == 1 legend("Mean Moment No Synx", "Mean Inverse Dynamics Moment") @@ -99,9 +101,11 @@ function plotMtpJointMoments(resultsDirectory) hold off set(gca, fontsize=11) rmse = rms(meanMomentsSynx(:,i) - meanIdMoments(:,i)); - mae = mtpInputs.synergyExtrapolation.costTerms{1}.maxAllowableError; - title(sprintf("%s \n RMSE: %.4f, MAE: %f", ... - jointLabels(i), rmse, mae), fontsize=12) + % mae = mtpInputs.synergyExtrapolation.costTerms{1}.maxAllowableError; + % title(sprintf("%s \n RMSE: %.4f, MAE: %f", ... + % jointLabels(i), rmse, mae), fontsize=12) + title(sprintf("%s \n RMSE: %.4f", ... + jointLabels(i), rmse), fontsize=12) axis([0 size(meanIdMoments, 1) minMoment, maxMoment]) if i == 1 legend("Mean Moment Synx", "Mean Inverse Dynamics Moment") diff --git a/src/MuscleTendonPersonalization/Analysis/plotMtpMuscleExcitationsAndActivations.m b/src/MuscleTendonPersonalization/Analysis/plotMtpMuscleExcitationsAndActivations.m index acdae3bca..11f6c4f0b 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotMtpMuscleExcitationsAndActivations.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMtpMuscleExcitationsAndActivations.m @@ -83,7 +83,11 @@ function plotMtpMuscleExcitationsAndActivations(resultsDirectory) end set(gca, fontsize=11) axis([1 size(meanExcitations, 1) 0 1]) - title(muscleNames(i), FontSize=12); + if (max(meanExcitations(:, i)) == 0) + title(strcat(muscleNames(i), " *"), FontSize=12); + else + title(muscleNames(i), FontSize=12); + end if i == 1 legend ('Excitation (No SynX)', ... 'Activation (No SynX)', ... diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m index 0d9acb784..ff735a899 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m @@ -94,8 +94,12 @@ function plotTreatmentOptimizationGroundReactions(experimentalGroundReactionFile plot(experimentalTime, experimentalGR(:, i), LineWidth=2); plot(modelTime, modelGR(:, i), LineWidth=2); hold off - title(sprintf("%s \n RMSE: %d", ... - strrep(experimentalLabels(i), "_", " "), 0)) + resampledExperimental = downsample( ... + interp(experimentalGR(:, i), length(modelGR(:, i))), ... + length(experimentalGR(:, i))); + rmse = rms(resampledExperimental - modelGR(:, i)); + title(sprintf("%s \n RMSE: %.4f", ... + strrep(experimentalLabels(i), "_", " "), rmse)) if subplotNumber==1 legend("Experimental", "Model") end diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m index 0507494de..7fda2ed56 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m @@ -82,8 +82,12 @@ function plotTreatmentOptimizationJointAngles(experimentalAnglesFile, ... plot(experimentalTime, experimentalAngles(:, i), LineWidth=2); plot(modelTime, modelAngles(:, i), LineWidth=2); hold off - title(sprintf("%s \n RMSE: %d", ... - strrep(labels(i), "_", " "), 0)); + resampledExperimental = downsample( ... + interp(experimentalAngles(:, i), length(modelAngles(:, i))), ... + length(experimentalAngles(:, i))); + rmse = rms(resampledExperimental - modelAngles(:, i)); + title(sprintf("%s \n RMSE: %.4f", ... + strrep(labels(i), "_", " "), rmse)); if subplotNumber==1 legend("Experimental", "Model") diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoads.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoads.m index cf82fd27e..9c5296301 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoads.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoads.m @@ -80,16 +80,21 @@ function plotTreatmentOptimizationJointLoads(experimentalMomentsFile, ... plot(experimentalTime, experimentalMoments(:, i), LineWidth=2); plot(modelTime, modelMoments(:, i), LineWidth=2); hold off - + + resampledExperimental = downsample( ... + interp(experimentalMoments(:, i), length(modelMoments(:, i))), ... + length(experimentalMoments(:, i))); + rmse = rms(resampledExperimental - modelMoments(:, i)); + if contains(labels(i), "moment") - title(sprintf("%s \n RMSE: %d", ... - strcat(strrep(labels(i), "_", " "), " [Nm]"), 0)) + title(sprintf("%s \n RMSE: %.4f", ... + strcat(strrep(labels(i), "_", " "), " [Nm]"), rmse)) elseif contains(labels(i), "force") - title(sprintf("%s \n RMSE: %d", ... - strcat(strrep(labels(i), "_", " "), " [N]"), 0)) + title(sprintf("%s \n RMSE: %.4f", ... + strcat(strrep(labels(i), "_", " "), " [N]"), rmse)) else - title(sprintf("%s \n RMSE: %d", ... - strrep(labels(i), "_", " "), 0)) + title(sprintf("%s \n RMSE: %.4f", ... + strrep(labels(i), "_", " "), rmse)) end if subplotNumber==1 diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivations.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivations.m index 2f30cdc9b..9721a4e11 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivations.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivations.m @@ -80,8 +80,12 @@ function plotTreatmentOptimizationMuscleActivations(experimentalActivationsFile, plot(experimentalTime, experimentalActivations(:, i), LineWidth=2) plot(modelTime, modelActivations(:, i), LineWidth=2) hold off - title(sprintf("%s \n RMSE: %d", ... - strrep(labels(i), "_", " "), 0)); + resampledExperimental = downsample( ... + interp(experimentalActivations(:, i), length(modelActivations(:, i))), ... + length(experimentalActivations(:, i))); + rmse = rms(resampledExperimental - modelActivations(:, i)); + title(sprintf("%s \n RMSE: %.4f", ... + strrep(labels(i), "_", " "), rmse)); if subplotNumber == 1 legend("Experimental", "Model") end From a309a271f4377cc0face7acd8fc7f25464ec0960 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Wed, 21 Feb 2024 13:53:15 -0600 Subject: [PATCH 328/365] Only check dofsActuatedIndex if synergy-driven --- .../PathTerms/calcKineticPathConstraint.m | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/TreatmentOptimization/PathTerms/calcKineticPathConstraint.m b/src/TreatmentOptimization/PathTerms/calcKineticPathConstraint.m index 08a5e091e..c5aa07556 100644 --- a/src/TreatmentOptimization/PathTerms/calcKineticPathConstraint.m +++ b/src/TreatmentOptimization/PathTerms/calcKineticPathConstraint.m @@ -33,7 +33,11 @@ modeledValues, torqueControls, loadName) inverseDynamicsIndex = find(strcmp(convertCharsToStrings(inputs.inverseDynamicsMomentLabels), ... loadName)); -synergyIndex = find(inputs.dofsActuatedIndex == inverseDynamicsIndex); +if strcmpi(inputs.controllerType, "synergy") + synergyIndex = find(inputs.dofsActuatedIndex == inverseDynamicsIndex); +else + synergyIndex = []; +end torqueIndex = find(strcmp(strcat(inputs.torqueControllerCoordinateNames, ... '_moment'), loadName)); if isempty(torqueIndex) From 9a567b09b2b34c9d481dfeb4d3fd38904289e2ac Mon Sep 17 00:00:00 2001 From: RobSalati <142843461+RobSalati@users.noreply.github.com> Date: Wed, 21 Feb 2024 14:23:10 -0600 Subject: [PATCH 329/365] GCV splines for rmse, and changed NCP & GCP plots to use tiltedlayout --- .../Analysis/plotGcpFootKinematicsFromFiles.m | 4 +- .../plotGcpGroundReactionsFromFiles.m | 4 +- .../Analysis/plotMtpJointMoments.m | 9 ----- .../saveMuscleTendonPersonalizationResults.m | 1 - .../Analysis/plotMomentMatchingResults.m | 6 ++- ...tNeuralControlPersonalizationActivations.m | 6 ++- ...plotTreatmentOptimizationGroundReactions.m | 11 ++++-- .../plotTreatmentOptimizationJointAngles.m | 13 ++++--- .../plotTreatmentOptimizationJointLoads.m | 39 +++++++++++-------- ...otTreatmentOptimizationMuscleActivations.m | 11 ++++-- 10 files changed, 61 insertions(+), 43 deletions(-) diff --git a/src/GroundContactPersonalization/Analysis/plotGcpFootKinematicsFromFiles.m b/src/GroundContactPersonalization/Analysis/plotGcpFootKinematicsFromFiles.m index ea26c61cc..570f9c616 100644 --- a/src/GroundContactPersonalization/Analysis/plotGcpFootKinematicsFromFiles.m +++ b/src/GroundContactPersonalization/Analysis/plotGcpFootKinematicsFromFiles.m @@ -45,8 +45,10 @@ function plotGcpFootKinematicsFromFiles(experimentalKinematicsFileName, ... time = findTimeColumn(Storage(experimentalKinematicsFileName)); figure(plotNumber) +t = tiledlayout(2, 4, ... + TileSpacing='compact', Padding='compact'); for i = 1:7 - subplot(2, 4, i) + nexttile(i) % Rotational coordinate data are converted to degrees. if i <= 4 experimental = rad2deg(experimentalKinematics(i, :)); diff --git a/src/GroundContactPersonalization/Analysis/plotGcpGroundReactionsFromFiles.m b/src/GroundContactPersonalization/Analysis/plotGcpGroundReactionsFromFiles.m index 964c8e768..581437505 100644 --- a/src/GroundContactPersonalization/Analysis/plotGcpGroundReactionsFromFiles.m +++ b/src/GroundContactPersonalization/Analysis/plotGcpGroundReactionsFromFiles.m @@ -47,8 +47,10 @@ function plotGcpGroundReactionsFromFiles( ... time = findTimeColumn(Storage(experimentalGroundReactionsFileName)); figure(plotNumber) +t = tiledlayout(2, 3, ... + TileSpacing='compact', Padding='compact'); for i = 1:6 - subplot(2, 3, i); + nexttile(i) experimental = experimentalGroundReactions(i, :); model = modeledGroundReactions(i, :); plot(time, experimental, "red", "LineWidth", 2) diff --git a/src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m b/src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m index 19178b1d4..f0dfff5ed 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m @@ -39,8 +39,6 @@ function plotMtpJointMoments(resultsDirectory) [~, modelMoments] = extractMtpDataFromSto( ... fullfile(analysisDirectory, "modelJointMoments")); -% load(fullfile(analysisDirectory, "mtpInputs.mat"), "mtpInputs"); - if exist(fullfile(analysisDirectory, "modelJointMomentsSynx"), "dir") [~, modelMomentsSynx] = extractMtpDataFromSto( ... fullfile(analysisDirectory, "modelJointMomentsSynx")); @@ -81,10 +79,6 @@ function plotMtpJointMoments(resultsDirectory) hold off set(gca, fontsize=11) rmse = rms(meanMoments(:,i) - meanIdMoments(:,i)); - % mae = mtpInputs.tasks{1}.costTerms{1}.maxAllowableError; - - % title(sprintf("%s \n RMSE: %.4f, MAE: %f", ... - % jointLabels(i), rmse, mae), fontsize=12) title(sprintf("%s \n RMSE: %.4f", ... jointLabels(i), rmse), fontsize=12) axis([0 size(meanIdMoments, 1) minMoment, maxMoment]) @@ -101,9 +95,6 @@ function plotMtpJointMoments(resultsDirectory) hold off set(gca, fontsize=11) rmse = rms(meanMomentsSynx(:,i) - meanIdMoments(:,i)); - % mae = mtpInputs.synergyExtrapolation.costTerms{1}.maxAllowableError; - % title(sprintf("%s \n RMSE: %.4f, MAE: %f", ... - % jointLabels(i), rmse, mae), fontsize=12) title(sprintf("%s \n RMSE: %.4f", ... jointLabels(i), rmse), fontsize=12) axis([0 size(meanIdMoments, 1) minMoment, maxMoment]) diff --git a/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m b/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m index fb3e5458c..f63868216 100644 --- a/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m +++ b/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m @@ -54,7 +54,6 @@ function saveMuscleTendonPersonalizationResults(mtpInputs, finalValues, ... saveMtpJointMomentData(mtpInputs, resultsStruct, analysisDirectory); saveMtpMuscleModelParameters(mtpInputs, finalValues, ... fullfile(analysisDirectory, "muscleModelParameters")); -save(fullfile(analysisDirectory, "mtpInputs.mat"), "mtpInputs"); model = Model(mtpInputs.model); muscleNames = getMusclesFromCoordinates(model, mtpInputs.coordinateNames); writeMuscleTendonPersonalizationOsimxFile(mtpInputs.modelFileName, ... diff --git a/src/NeuralControlPersonalization/Analysis/plotMomentMatchingResults.m b/src/NeuralControlPersonalization/Analysis/plotMomentMatchingResults.m index 10a8e9121..05dfbdb30 100644 --- a/src/NeuralControlPersonalization/Analysis/plotMomentMatchingResults.m +++ b/src/NeuralControlPersonalization/Analysis/plotMomentMatchingResults.m @@ -58,14 +58,18 @@ function plotMomentMatchingResults(experimentalMomentsFile, ... hasLegend = false; % figure(figureNumber) figureIndex = 1; +t = tiledlayout(figureHeight, figureWidth, ... + TileSpacing='compact', Padding='compact'); for i = 1:length(experimentalColumns) if i > figureSize * figureIndex figureIndex = figureIndex + 1; figure(figureNumber + figureIndex - 1) + t = tiledlayout(figureHeight, figureWidth, ... + TileSpacing='compact', Padding='compact'); subplotNumber = 1; hasLegend = false; end - subplot(figureHeight, figureWidth, subplotNumber) + nexttile(subplotNumber) plot(modeledTime, experimentalMoments(i, :), 'LineWidth', 2) modeledIndex = find(experimentalColumns(i) == modeledColumns); if isempty(modeledIndex) diff --git a/src/NeuralControlPersonalization/Analysis/plotNeuralControlPersonalizationActivations.m b/src/NeuralControlPersonalization/Analysis/plotNeuralControlPersonalizationActivations.m index dc6ff6e96..63adafcf3 100644 --- a/src/NeuralControlPersonalization/Analysis/plotNeuralControlPersonalizationActivations.m +++ b/src/NeuralControlPersonalization/Analysis/plotNeuralControlPersonalizationActivations.m @@ -59,15 +59,19 @@ function plotNeuralControlPersonalizationActivations(weightsFile, ... figureNumber = 1; subplotNumber = 1; hasLegend = false; +t = tiledlayout(figureHeight, figureWidth, ... + TileSpacing='Compact', Padding='Compact'); % figure(1) for i = 1:size(muscleActivations, 1) if i > figureSize * figureNumber figureNumber = figureNumber + 1; figure(figureNumber) + t = tiledlayout(figureHeight, figureWidth, ... + TileSpacing='Compact', Padding='Compact'); subplotNumber = 1; hasLegend = false; end - subplot(figureHeight, figureWidth, subplotNumber) + nexttile(subplotNumber) plot(time, muscleActivations(i, :), 'LineWidth', 2) mtpIndex = find(muscleNames(i) == mtpMuscleNames); if ~isempty(mtpIndex) diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m index ff735a899..7c7e4c68a 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m @@ -64,6 +64,12 @@ function plotTreatmentOptimizationGroundReactions(experimentalGroundReactionFile modelGR = modelGR(:, includedIndices); experimentalGR = experimentalGR(:, includedIndices); +% Spline experimental time to the same time points as the model. +experimentalSpline = makeGcvSplineSet(experimentalTime, ... + experimentalGR, experimentalLabels); +resampledExperimental = evaluateGcvSplines(experimentalSpline, ... + experimentalLabels, modelTime); + if nargin < 3 figureWidth = 3; end @@ -94,10 +100,7 @@ function plotTreatmentOptimizationGroundReactions(experimentalGroundReactionFile plot(experimentalTime, experimentalGR(:, i), LineWidth=2); plot(modelTime, modelGR(:, i), LineWidth=2); hold off - resampledExperimental = downsample( ... - interp(experimentalGR(:, i), length(modelGR(:, i))), ... - length(experimentalGR(:, i))); - rmse = rms(resampledExperimental - modelGR(:, i)); + rmse = rms(resampledExperimental(:, i) - modelGR(:, i)); title(sprintf("%s \n RMSE: %.4f", ... strrep(experimentalLabels(i), "_", " "), rmse)) if subplotNumber==1 diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m index 7fda2ed56..a96590910 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m @@ -48,6 +48,12 @@ function plotTreatmentOptimizationJointAngles(experimentalAnglesFile, ... modelTime = modelTime - modelTime(1); end +% Spline experimental time to the same time points as the model. +experimentalSpline = makeGcvSplineSet(experimentalTime, ... + experimentalAngles, labels); +resampledExperimental = evaluateGcvSplines(experimentalSpline, ... + labels, modelTime); + if nargin < 3 figureWidth = ceil(sqrt(numel(labels))); figureHeight = ceil(numel(labels)/figureWidth); @@ -62,7 +68,7 @@ function plotTreatmentOptimizationJointAngles(experimentalAnglesFile, ... subplotNumber = 1; figureNumber = 1; t = tiledlayout(figureHeight, figureWidth, ... - TileSpacing='Compact', Padding='Compact'); + TileSpacing='compact', Padding='compact'); xlabel(t, "Time [s]") ylabel(t, "Joint Angle [deg]") for i=1:numel(labels) @@ -82,10 +88,7 @@ function plotTreatmentOptimizationJointAngles(experimentalAnglesFile, ... plot(experimentalTime, experimentalAngles(:, i), LineWidth=2); plot(modelTime, modelAngles(:, i), LineWidth=2); hold off - resampledExperimental = downsample( ... - interp(experimentalAngles(:, i), length(modelAngles(:, i))), ... - length(experimentalAngles(:, i))); - rmse = rms(resampledExperimental - modelAngles(:, i)); + rmse = rms(resampledExperimental(:, i) - modelAngles(:, i)); title(sprintf("%s \n RMSE: %.4f", ... strrep(labels(i), "_", " "), rmse)); diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoads.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoads.m index 9c5296301..af6d31907 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoads.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoads.m @@ -32,20 +32,26 @@ function plotTreatmentOptimizationJointLoads(experimentalMomentsFile, ... modelMomentsFile, figureWidth, figureHeight) import org.opensim.modeling.Storage -experimentalMomentsStorage = Storage(experimentalMomentsFile); -labels = getStorageColumnNames(experimentalMomentsStorage); -experimentalMoments = storageToDoubleMatrix(experimentalMomentsStorage)'; -experimentalTime = findTimeColumn(experimentalMomentsStorage); +experimentalLoadsStorage = Storage(experimentalMomentsFile); +labels = getStorageColumnNames(experimentalLoadsStorage); +experimentalLoads = storageToDoubleMatrix(experimentalLoadsStorage)'; +experimentalTime = findTimeColumn(experimentalLoadsStorage); if experimentalTime(1) ~= 0 experimentalTime = experimentalTime - experimentalTime(1); end -modelMomentsStorage = Storage(modelMomentsFile); -modelMoments = storageToDoubleMatrix(modelMomentsStorage)'; -modelTime = findTimeColumn(modelMomentsStorage); +modelLoadsStorage = Storage(modelMomentsFile); +modelLoads = storageToDoubleMatrix(modelLoadsStorage)'; +modelTime = findTimeColumn(modelLoadsStorage); if modelTime(1) ~= 0 modelTime = modelTime - modelTime(1); end +% Spline experimental time to the same time points as the model. +experimentalSpline = makeGcvSplineSet(experimentalTime, ... + experimentalLoads, labels); +resampledExperimental = evaluateGcvSplines(experimentalSpline, ... + labels, modelTime); + if nargin < 3 figureWidth = ceil(sqrt(numel(labels))); figureHeight = ceil(numel(labels)/figureWidth); @@ -77,14 +83,10 @@ function plotTreatmentOptimizationJointLoads(experimentalMomentsFile, ... end nexttile(subplotNumber); hold on - plot(experimentalTime, experimentalMoments(:, i), LineWidth=2); - plot(modelTime, modelMoments(:, i), LineWidth=2); + plot(experimentalTime, experimentalLoads(:, i), LineWidth=2); + plot(modelTime, modelLoads(:, i), LineWidth=2); hold off - - resampledExperimental = downsample( ... - interp(experimentalMoments(:, i), length(modelMoments(:, i))), ... - length(experimentalMoments(:, i))); - rmse = rms(resampledExperimental - modelMoments(:, i)); + rmse = rms(resampledExperimental(:, i) - modelLoads(:, i)); if contains(labels(i), "moment") title(sprintf("%s \n RMSE: %.4f", ... @@ -102,11 +104,16 @@ function plotTreatmentOptimizationJointLoads(experimentalMomentsFile, ... end xlim([0, experimentalTime(end)]) - maxLoad = max([experimentalMoments(:, i); modelMoments(:, i)],[], "all"); - minLoad = min([experimentalMoments(:, i); modelMoments(:, i)],[], "all"); + maxLoad = max([experimentalLoads(:, i); modelLoads(:, i)],[], "all"); + minLoad = min([experimentalLoads(:, i); modelLoads(:, i)],[], "all"); if maxLoad-minLoad < 10 ylim([(maxLoad+minLoad)/2-10, (maxLoad+minLoad)/2+10]) + else + ylim([ ... + min([modelLoads(1:end-1, i); experimentalLoads(:, i)])-5, ... + max([modelLoads(1:end-1, i); experimentalLoads(:, i)])+5]) end + % if subplotNumber > figureSize-figureHeight % xlabel("Time [s]") % end diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivations.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivations.m index 9721a4e11..73f53329f 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivations.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivations.m @@ -46,6 +46,12 @@ function plotTreatmentOptimizationMuscleActivations(experimentalActivationsFile, modelTime = modelTime - modelTime(1); end +% Spline experimental time to the same time points as the model. +experimentalSpline = makeGcvSplineSet(experimentalTime, ... + experimentalActivations, labels); +resampledExperimental = evaluateGcvSplines(experimentalSpline, ... + labels, modelTime); + if nargin < 3 figureWidth = ceil(sqrt(numel(labels))); figureHeight = ceil(numel(labels)/figureWidth); @@ -80,10 +86,7 @@ function plotTreatmentOptimizationMuscleActivations(experimentalActivationsFile, plot(experimentalTime, experimentalActivations(:, i), LineWidth=2) plot(modelTime, modelActivations(:, i), LineWidth=2) hold off - resampledExperimental = downsample( ... - interp(experimentalActivations(:, i), length(modelActivations(:, i))), ... - length(experimentalActivations(:, i))); - rmse = rms(resampledExperimental - modelActivations(:, i)); + rmse = rms(resampledExperimental(:, i) - modelActivations(:, i)); title(sprintf("%s \n RMSE: %.4f", ... strrep(labels(i), "_", " "), rmse)); if subplotNumber == 1 From 067a55079bcc4d090bba2d5d1cd41439f6af6149 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Wed, 21 Feb 2024 14:31:13 -0600 Subject: [PATCH 330/365] Safely parse empty model functions field --- .../DesignOptimization/parseDesignOptimizationSettingsTree.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TreatmentOptimization/DesignOptimization/parseDesignOptimizationSettingsTree.m b/src/TreatmentOptimization/DesignOptimization/parseDesignOptimizationSettingsTree.m index f58a65dc6..880e220e4 100644 --- a/src/TreatmentOptimization/DesignOptimization/parseDesignOptimizationSettingsTree.m +++ b/src/TreatmentOptimization/DesignOptimization/parseDesignOptimizationSettingsTree.m @@ -38,7 +38,7 @@ function inputs = parseUserDefinedFunctions(tree, inputs) import org.opensim.modeling.Storage -if getFieldByName(tree, "model_functions") +if ~(getFieldByNameOrAlternate(tree, "model_functions", "none") == "none") inputs.systemFns = parseSpaceSeparatedList(tree, "model_functions"); end parameterTree = getFieldByName(tree, "RCNLParameterTermSet"); From 83d2ba0009f708f1902162fee5a326ae75d7c3bd Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Wed, 21 Feb 2024 14:36:53 -0600 Subject: [PATCH 331/365] Fix parsing empty model functions --- .../parseDesignOptimizationSettingsTree.m | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/TreatmentOptimization/DesignOptimization/parseDesignOptimizationSettingsTree.m b/src/TreatmentOptimization/DesignOptimization/parseDesignOptimizationSettingsTree.m index 880e220e4..b88b5a3ef 100644 --- a/src/TreatmentOptimization/DesignOptimization/parseDesignOptimizationSettingsTree.m +++ b/src/TreatmentOptimization/DesignOptimization/parseDesignOptimizationSettingsTree.m @@ -38,8 +38,11 @@ function inputs = parseUserDefinedFunctions(tree, inputs) import org.opensim.modeling.Storage -if ~(getFieldByNameOrAlternate(tree, "model_functions", "none") == "none") - inputs.systemFns = parseSpaceSeparatedList(tree, "model_functions"); +if isstruct(getFieldByName(tree, "model_functions")) + systemFns = parseSpaceSeparatedList(tree, "model_functions"); + if ~isempty(systemFns) + inputs.systemFns = systemFns; + end end parameterTree = getFieldByName(tree, "RCNLParameterTermSet"); if isstruct(parameterTree) && isfield(parameterTree, "RCNLParameterTerm") From 26dfe00f53e6c2ca9d3d88549457c742a6f4b7c7 Mon Sep 17 00:00:00 2001 From: RobSalati <142843461+RobSalati@users.noreply.github.com> Date: Wed, 21 Feb 2024 14:46:16 -0600 Subject: [PATCH 332/365] Documentation Filled out documentation in the header comments. --- .../plotTreatmentOptimizationGroundReactions.m | 10 +++++++--- .../Analysis/plotTreatmentOptimizationJointAngles.m | 12 ++++++++---- .../Analysis/plotTreatmentOptimizationJointLoads.m | 10 +++++++--- .../plotTreatmentOptimizationMuscleActivations.m | 10 +++++++--- .../plotTreatmentOptimizationSynergyControls.m | 10 +++++++--- .../plotTreatmentOptimizationTorqueControls.m | 10 +++++++--- 6 files changed, 43 insertions(+), 19 deletions(-) diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m index 7c7e4c68a..f8d87b48b 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m @@ -1,8 +1,12 @@ % This function is part of the NMSM Pipeline, see file for full license. % -% This function reads .sto files created by -% saveMuscleTendonPersonalizationResults.m containing inverse dynamics -% joint moments and model joint moments and creates plots of them. +% This function reads two .sto files: 1) experimental ground reactions, and +% 2) model ground reactions, and plots them. There are 2 optional arguments +% for figure width and figure height. If no optional arguments are given, +% the figure size is automatically adjusted to fit all data on one plot. +% Giving just figure width imposes the width and fits the height to fit on +% one plot. Giving both arguments will impose both figure height and width, +% and create multiple plots as needed. % % (string) -> (None) % Plot joint moment curves from file. diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m index a96590910..fef3357ed 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m @@ -1,8 +1,12 @@ % This function is part of the NMSM Pipeline, see file for full license. -% -% This function reads .sto files created by -% saveMuscleTendonPersonalizationResults.m containing inverse dynamics -% joint moments and model joint moments and creates plots of them. +% +% This function reads two .sto files: 1) experimental angles, and 2) model +% angles, and plots them. There are 2 optional arguments for figure width +% and figure height. If no optional arguments are given, the figure size is +% automatically adjusted to fit all data on one plot. Giving just figure +% width imposes the width and fits the height to fit on one plot. Giving +% both arguments will impose both figure height and width, and create +% multiple plots as needed. % % (string) -> (None) % Plot joint moment curves from file. diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoads.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoads.m index af6d31907..1921b0757 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoads.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoads.m @@ -1,8 +1,12 @@ % This function is part of the NMSM Pipeline, see file for full license. % -% This function reads .sto files created by -% saveMuscleTendonPersonalizationResults.m containing inverse dynamics -% joint moments and model joint moments and creates plots of them. +% This function reads two .sto files: 1) experimental loads, and 2) model +% loads, and plots them. There are 2 optional arguments for figure width +% and figure height. If no optional arguments are given, the figure size is +% automatically adjusted to fit all data on one plot. Giving just figure +% width imposes the width and fits the height to fit on one plot. Giving +% both arguments will impose both figure height and width, and create +% multiple plots as needed. % % (string) -> (None) % Plot joint moment curves from file. diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivations.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivations.m index 73f53329f..0bae8564c 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivations.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivations.m @@ -1,8 +1,12 @@ % This function is part of the NMSM Pipeline, see file for full license. % -% This function reads .sto files created by -% saveMuscleTendonPersonalizationResults.m containing inverse dynamics -% joint moments and model joint moments and creates plots of them. +% This function reads two .sto files: 1) experimental activations, and 2) +% model activations, and plots them. There are 2 optional arguments for +% figure width and figure height. If no optional arguments are given, the +% figure size is automatically adjusted to fit all data on one plot. Giving +% just figure width imposes the width and fits the height to fit on one +% plot. Giving both arguments will impose both figure height and width, and +% create multiple plots as needed. % % (string) -> (None) % Plot joint moment curves from file. diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationSynergyControls.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationSynergyControls.m index f3c3dc4a2..761deeef3 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationSynergyControls.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationSynergyControls.m @@ -1,8 +1,12 @@ % This function is part of the NMSM Pipeline, see file for full license. % -% This function reads .sto files created by -% saveMuscleTendonPersonalizationResults.m containing inverse dynamics -% joint moments and model joint moments and creates plots of them. +% This function reads one .sto file for treatment optimization synergy +% controls and plots it. There are 2 optional arguments for figure width +% and figure height. If no optional arguments are given, the figure size is +% automatically adjusted to fit all data on one plot. Giving just figure +% width imposes the width and fits the height to fit on one plot. Giving +% both arguments will impose both figure height and width, and create +% multiple plots as needed. % % (string) -> (None) % Plot joint moment curves from file. diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationTorqueControls.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationTorqueControls.m index 6ca0845b5..7c389456f 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationTorqueControls.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationTorqueControls.m @@ -1,8 +1,12 @@ % This function is part of the NMSM Pipeline, see file for full license. % -% This function reads .sto files created by -% saveMuscleTendonPersonalizationResults.m containing inverse dynamics -% joint moments and model joint moments and creates plots of them. +% This function reads one .sto file for treatment optimization torque +% controls and plots it. There are 2 optional arguments for figure width +% and figure height. If no optional arguments are given, the figure size is +% automatically adjusted to fit all data on one plot. Giving just figure +% width imposes the width and fits the height to fit on one plot. Giving +% both arguments will impose both figure height and width, and create +% multiple plots as needed. % % (string) -> (None) % Plot joint moment curves from file. From b0216467ff29a40c1c1ccb72e842756a7892bad7 Mon Sep 17 00:00:00 2001 From: Spencer Williams <54556034+SpencerTWilliams@users.noreply.github.com> Date: Fri, 23 Feb 2024 14:59:13 -0600 Subject: [PATCH 333/365] Remove unused DO cost terms --- .../calcGoalSingleSupportTimeIntegrand.m | 56 ------ .../calcGoalWalkingSpeedIntegrand.m | 45 ----- .../calcKinematicSymmetryIntegrand.m | 53 ------ .../calcMaximizingBreakingForceIntegrand.m | 44 ----- .../calcMaximizingMuscleActivationIntegrand.m | 40 ----- .../calcMaximizingPropulsiveForceIntegrand.m | 44 ----- ...calcMaximizingSingleSupportTimeIntegrand.m | 52 ------ .../calcMaximizingStepLengthIntegrand.m | 54 ------ ...calcMaximizingTrailingLimbAngleIntegrand.m | 47 ----- .../calcMinimizingBreakingForceIntegrand.m | 44 ----- ...cMinimizingKineticInconsistencyIntegrand.m | 40 ----- ...lcMinimizingMassCenterVelocityXIntegrand.m | 40 ----- ...lcMinimizingMassCenterVelocityYIntegrand.m | 40 ----- ...lcMinimizingMassCenterVelocityZIntegrand.m | 40 ----- .../calcMinimizingMetabolicCost.m | 39 ----- .../calcMinimizingPropulsiveForceIntegrand.m | 44 ----- ...calcMinimizingTrailingLimbAngleIntegrand.m | 47 ----- .../IntegrandTerms/calcSingleSupportTime.m | 56 ------ .../IntegrandTerms/calcStepLength.m | 49 ------ .../IntegrandTerms/calcStepLengthAsymmetry.m | 49 ------ .../calcStepLengthAsymmetryIntegrand.m | 44 ----- .../IntegrandTerms/calcStepTimeAsymmetry.m | 47 ----- .../calcStepTimeAsymmetryIntegrand.m | 45 ----- .../IntegrandTerms/calcStrideLength.m | 46 ----- ...calcTrackingInverseDynamicSlopeIntegrand.m | 4 +- .../IntegrandTerms/calcTrailingLimb.m | 55 ------ .../IntegrandTerms/getBreakingForce.m | 36 ---- .../IntegrandTerms/getHeelStrikeEvent.m | 42 ----- .../IntegrandTerms/getPropulsiveForce.m | 36 ---- .../IntegrandTerms/getToeOffEvent.m | 42 ----- .../generateCostTermStruct.m | 163 ------------------ 31 files changed, 2 insertions(+), 1481 deletions(-) delete mode 100644 src/TreatmentOptimization/IntegrandTerms/calcGoalSingleSupportTimeIntegrand.m delete mode 100644 src/TreatmentOptimization/IntegrandTerms/calcGoalWalkingSpeedIntegrand.m delete mode 100644 src/TreatmentOptimization/IntegrandTerms/calcKinematicSymmetryIntegrand.m delete mode 100644 src/TreatmentOptimization/IntegrandTerms/calcMaximizingBreakingForceIntegrand.m delete mode 100644 src/TreatmentOptimization/IntegrandTerms/calcMaximizingMuscleActivationIntegrand.m delete mode 100644 src/TreatmentOptimization/IntegrandTerms/calcMaximizingPropulsiveForceIntegrand.m delete mode 100644 src/TreatmentOptimization/IntegrandTerms/calcMaximizingSingleSupportTimeIntegrand.m delete mode 100644 src/TreatmentOptimization/IntegrandTerms/calcMaximizingStepLengthIntegrand.m delete mode 100644 src/TreatmentOptimization/IntegrandTerms/calcMaximizingTrailingLimbAngleIntegrand.m delete mode 100644 src/TreatmentOptimization/IntegrandTerms/calcMinimizingBreakingForceIntegrand.m delete mode 100644 src/TreatmentOptimization/IntegrandTerms/calcMinimizingKineticInconsistencyIntegrand.m delete mode 100644 src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityXIntegrand.m delete mode 100644 src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityYIntegrand.m delete mode 100644 src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityZIntegrand.m delete mode 100644 src/TreatmentOptimization/IntegrandTerms/calcMinimizingMetabolicCost.m delete mode 100644 src/TreatmentOptimization/IntegrandTerms/calcMinimizingPropulsiveForceIntegrand.m delete mode 100644 src/TreatmentOptimization/IntegrandTerms/calcMinimizingTrailingLimbAngleIntegrand.m delete mode 100644 src/TreatmentOptimization/IntegrandTerms/calcSingleSupportTime.m delete mode 100644 src/TreatmentOptimization/IntegrandTerms/calcStepLength.m delete mode 100644 src/TreatmentOptimization/IntegrandTerms/calcStepLengthAsymmetry.m delete mode 100644 src/TreatmentOptimization/IntegrandTerms/calcStepLengthAsymmetryIntegrand.m delete mode 100644 src/TreatmentOptimization/IntegrandTerms/calcStepTimeAsymmetry.m delete mode 100644 src/TreatmentOptimization/IntegrandTerms/calcStepTimeAsymmetryIntegrand.m delete mode 100644 src/TreatmentOptimization/IntegrandTerms/calcStrideLength.m delete mode 100644 src/TreatmentOptimization/IntegrandTerms/calcTrailingLimb.m delete mode 100644 src/TreatmentOptimization/IntegrandTerms/getBreakingForce.m delete mode 100644 src/TreatmentOptimization/IntegrandTerms/getHeelStrikeEvent.m delete mode 100644 src/TreatmentOptimization/IntegrandTerms/getPropulsiveForce.m delete mode 100644 src/TreatmentOptimization/IntegrandTerms/getToeOffEvent.m diff --git a/src/TreatmentOptimization/IntegrandTerms/calcGoalSingleSupportTimeIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcGoalSingleSupportTimeIntegrand.m deleted file mode 100644 index 47a3ff8d0..000000000 --- a/src/TreatmentOptimization/IntegrandTerms/calcGoalSingleSupportTimeIntegrand.m +++ /dev/null @@ -1,56 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function tracks the difference between the predicted single spport -% phase and the error center for the specified foot. On average, single -% support phase is around 0.37 to 0.39 of the gait cycle. -% -% (struct, struct, struct, struct) -> (Array of number) - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function cost = calcGoalSingleSupportTimeIntegrand(time, ... - modeledValues, params, costTerm) -normalizeByFinalTime = valueOrAlternate(costTerm, ... - "normalize_by_final_time", false); -errorCenter = valueOrAlternate(costTerm, "error_center", 0.38); -for i = 1:length(params.contactSurfaces) - if params.contactSurfaces{i}.isLeftFoot == costTerm.is_left_foot - if i == 1 - singleSupportTime = calcSingleSupportTime( ... - modeledValues.groundReactionsLab.forces{i + 1}(:, 2), ... - time); - else - singleSupportTime = calcSingleSupportTime( ... - modeledValues.groundReactionsLab.forces{i - 1}(:, 2), ... - time); - end - end -end -percentSingleSupportTime = singleSupportTime / time(end); -cost = calcTrackingCostArrayTerm(percentSingleSupportTime * ... - ones(length(time), 1), errorCenter * ... - ones(length(time), 1), 1); -if normalizeByFinalTime - cost = cost / time(end); -end -end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcGoalWalkingSpeedIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcGoalWalkingSpeedIntegrand.m deleted file mode 100644 index fb4479245..000000000 --- a/src/TreatmentOptimization/IntegrandTerms/calcGoalWalkingSpeedIntegrand.m +++ /dev/null @@ -1,45 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function calculates the difference between the walking speed and the -% error center specified by the user. On average, normal walking speed is -% around 1.2 to 1.4 meters per second. -% -% (struct, struct, struct, struct) -> (Array of number) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function cost = calcGoalWalkingSpeedIntegrand(modeledValues, values, ... - time, params, costTerm) -normalizeByFinalTime = valueOrAlternate(costTerm, ... - "normalize_by_final_time", true); -errorCenter = valueOrAlternate(costTerm, "errorCenter", 1.3); -strideLength = calcStrideLength(values, modeledValues,... - params); -speed = strideLength / values.time(end); -cost = calcTrackingCostArrayTerm(speed * ones(length(values.time), 1), ... - errorCenter * ones(length(values.time), 1), 1); -if normalizeByFinalTime - cost = cost / time(end); -end -end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcKinematicSymmetryIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcKinematicSymmetryIntegrand.m deleted file mode 100644 index 1af48ba31..000000000 --- a/src/TreatmentOptimization/IntegrandTerms/calcKinematicSymmetryIntegrand.m +++ /dev/null @@ -1,53 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function calculates kinematic symmetry, or the difference between -% two joint angles. Kinematic symmetry is calculated as the difference -% between one joint angle and the second joint angle with a 50% phase -% shift. To use this term, an even number is encouraged for the -% tag in the optimal control settings. -% Additionally, two coordinate names are required to calculate the -% difference. -% -% (2D matrix, struct, struct) -> (Array of number) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function cost = calcKinematicSymmetryIntegrand(statePositions, time, ... - auxdata, costTerm) -normalizeByFinalTime = valueOrAlternate(costTerm, ... - "normalize_by_final_time", true); -halfWayFrame = size(statePositions, 1)/2; - -indx1 = find(strcmp(convertCharsToStrings(auxdata.coordinateNames), ... - costTerm.coordinate1)); -indx2 = find(strcmp(convertCharsToStrings(auxdata.coordinateNames), ... - costTerm.coordinate2)); - -cost = calcTrackingCostArrayTerm(statePositions(:,indx1), ... - [statePositions(1 + round(halfWayFrame):end, indx2); ... - statePositions(1:round(halfWayFrame), indx2)], 1); -if normalizeByFinalTime - cost = cost / time(end); -end -end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMaximizingBreakingForceIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMaximizingBreakingForceIntegrand.m deleted file mode 100644 index 2dc529e92..000000000 --- a/src/TreatmentOptimization/IntegrandTerms/calcMaximizingBreakingForceIntegrand.m +++ /dev/null @@ -1,44 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function maximizes the breaking force for the specified foot. -% -% (struct, struct, struct) -> (Array of number) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function cost = calcMaximizingBreakingForceIntegrand(modeledValues, ... - time, params, costTerm) -normalizeByFinalTime = valueOrAlternate(costTerm, ... - "normalize_by_final_time", false); -for i = 1:length(params.contactSurfaces) - if params.contactSurfaces{i}.isLeftFoot == costTerm.is_left_foot - breakingForce = getBreakingForce( ... - modeledValues.groundReactionsLab.forces{i}(:,1)); - end -end -cost = calcMaximizingCostArrayTerm(breakingForce); -if normalizeByFinalTime - cost = cost / time(end); -end -end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMaximizingMuscleActivationIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMaximizingMuscleActivationIntegrand.m deleted file mode 100644 index a1e14c183..000000000 --- a/src/TreatmentOptimization/IntegrandTerms/calcMaximizingMuscleActivationIntegrand.m +++ /dev/null @@ -1,40 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function maximizes the muscle activation for the specified muscle. -% -% (2D matrix, struct, Array of string) -> (Array of number) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function cost = calcMaximizingMuscleActivationIntegrand(costTerm, time, ... - muscleActivations, params, muscleName) -normalizeByFinalTime = valueOrAlternate(costTerm, ... - "normalize_by_final_time", true); -indx = find(strcmp(convertCharsToStrings(params.muscleNames), ... - muscleName)); -cost = calcMaximizingCostArrayTerm(muscleActivations(:, indx)); -if normalizeByFinalTime - cost = cost / time(end); -end -end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMaximizingPropulsiveForceIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMaximizingPropulsiveForceIntegrand.m deleted file mode 100644 index e93ee0879..000000000 --- a/src/TreatmentOptimization/IntegrandTerms/calcMaximizingPropulsiveForceIntegrand.m +++ /dev/null @@ -1,44 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function maximizes the propulsive force for the specified foot. -% -% (struct, struct, struct) -> (Array of number) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function cost = calcMaximizingPropulsiveForceIntegrand(modeledValues, ... - time, params, costTerm) -normalizeByFinalTime = valueOrAlternate(costTerm, ... - "normalize_by_final_time", false); -for i = 1:length(params.contactSurfaces) - if params.contactSurfaces{i}.isLeftFoot == costTerm.is_left_foot - propulsiveForce = getPropulsiveForce( ... - modeledValues.groundReactionsLab.forces{i}(:,1)); - end -end -cost = calcMaximizingCostArrayTerm(propulsiveForce); -if normalizeByFinalTime - cost = cost / time(end); -end -end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMaximizingSingleSupportTimeIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMaximizingSingleSupportTimeIntegrand.m deleted file mode 100644 index 945af2596..000000000 --- a/src/TreatmentOptimization/IntegrandTerms/calcMaximizingSingleSupportTimeIntegrand.m +++ /dev/null @@ -1,52 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function maximizes the single support time for the specified foot. -% -% (struct, struct, struct, struct) -> (Array of number) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function cost = calcMaximizingSingleSupportTimeIntegrand(time, ... - modeledValues, params, costTerm) -normalizeByFinalTime = valueOrAlternate(costTerm, ... - "normalize_by_final_time", false); -for i = 1:length(params.contactSurfaces) - if params.contactSurfaces{i}.isLeftFoot == costTerm.is_left_foot - if i == 1 - singleSupportTime = calcSingleSupportTime( ... - modeledValues.groundReactionsLab.forces{i + 1}(:, 2), ... - time); - else - singleSupportTime = calcSingleSupportTime( ... - modeledValues.groundReactionsLab.forces{i - 1}(:, 2), ... - time); - end - end -end -cost = calcMaximizingCostArrayTerm(singleSupportTime * ... - ones(length(time), 1)); -if normalizeByFinalTime - cost = cost / time(end); -end -end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMaximizingStepLengthIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMaximizingStepLengthIntegrand.m deleted file mode 100644 index a633f90b0..000000000 --- a/src/TreatmentOptimization/IntegrandTerms/calcMaximizingStepLengthIntegrand.m +++ /dev/null @@ -1,54 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function maximizes the step length for the specified foot. -% -% (struct, struct, struct, struct) -> (Array of number) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function cost = calcMaximizingStepLengthIntegrand(time, modeledValues,... - params, costTerm) -normalizeByFinalTime = valueOrAlternate(costTerm, ... - "normalize_by_final_time", true); -for i = 1:length(params.contactSurfaces) - if params.contactSurfaces{i}.isLeftFoot == costTerm.is_left_foot - if i == 1 - stepLength = calcStepLength(modeledValues. ... - groundReactionsLab.forces{i}(:, 2), ... - [modeledValues.bodyLocations.parent{i}(:, 1) ... - modeledValues.bodyLocations.parent{i+1}(:, 1)]); - else - stepLength = calcStepLength(modeledValues. ... - groundReactionsLab.forces{i}(:,2), ... - [modeledValues.bodyLocations.parent{i}(:, 1) ... - modeledValues.bodyLocations.parent{i-1}(:, 1)]); - end - end -end -cost = calcMaximizingCostArrayTerm(stepLength * ... - ones(length(time), 1)); -if normalizeByFinalTime - cost = cost / time(end); -end -end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMaximizingTrailingLimbAngleIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMaximizingTrailingLimbAngleIntegrand.m deleted file mode 100644 index 756e5aa08..000000000 --- a/src/TreatmentOptimization/IntegrandTerms/calcMaximizingTrailingLimbAngleIntegrand.m +++ /dev/null @@ -1,47 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function maximizes the trailing limb for the specified foot. -% -% (struct, struct, struct, struct) -> (Array of number) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function cost = calcMaximizingTrailingLimbAngleIntegrand(values, time, ... - modeledValues, params, costTerm) -normalizeByFinalTime = valueOrAlternate(costTerm, ... - "normalize_by_final_time", true); -for i = 1:length(params.contactSurfaces) - if params.contactSurfaces{i}.isLeftFoot == costTerm.is_left_foot - normalForce = ... - modeledValues.groundReactionsLab.forces{i}(:, 2) - 20; - end -end -trailingLimbAngle = calcTrailingLimb(costTerm, values, ... - normalForce, params); -cost = calcMaximizingCostArrayTerm(trailingLimbAngle * ... - ones(length(time), 1)); -if normalizeByFinalTime - cost = cost / time(end); -end -end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingBreakingForceIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingBreakingForceIntegrand.m deleted file mode 100644 index 5a7842312..000000000 --- a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingBreakingForceIntegrand.m +++ /dev/null @@ -1,44 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function minimizes the breaking force for the specified foot. -% -% (struct, struct, struct) -> (Array of number) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function cost = calcMinimizingBreakingForceIntegrand(modeledValues, ... - time, params, costTerm) -normalizeByFinalTime = valueOrAlternate(costTerm, ... - "normalize_by_final_time", false); -for i = 1:length(params.contactSurfaces) - if params.contactSurfaces{i}.isLeftFoot == costTerm.is_left_foot - breakingForce = getBreakingForce( ... - modeledValues.groundReactionsLab.forces{i}(:,1)); - end -end -cost = calcMinimizingCostArrayTerm(breakingForce); -if normalizeByFinalTime - cost = cost / time(end); -end -end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingKineticInconsistencyIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingKineticInconsistencyIntegrand.m deleted file mode 100644 index b9571ced6..000000000 --- a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingKineticInconsistencyIntegrand.m +++ /dev/null @@ -1,40 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function calculates the difference between the experimental and -% predicted inverse dynamic moments for the specified coordinate. -% -% (struct, Array of number, 2D matrix, Array of string) -> (Array of number) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function cost = calcMinimizingKineticInconsistencyIntegrand(costTerm, ... - inputs, time, modeledValues, torqueControls, loadName) -normalizeByFinalTime = valueOrAlternate(costTerm, ... - "normalize_by_final_time", true); -cost = calcKineticPathConstraint(inputs, modeledValues, torqueControls, ... - loadName); -if normalizeByFinalTime - cost = cost / time(end); -end -end diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityXIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityXIntegrand.m deleted file mode 100644 index 5e9cb0455..000000000 --- a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityXIntegrand.m +++ /dev/null @@ -1,40 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function minimizes the center of mass velocity in the x direction. -% -% (struct, struct) -> (Array of number) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function cost = calcMinimizingMassCenterVelocityXIntegrand(costTerm, ... - values, time, params) -normalizeByFinalTime = valueOrAlternate(costTerm, ... - "normalize_by_final_time", true); -massCenterVelocity = calcMassCenterVelocity(values, params.model, ... - params.coordinateNames); -cost = calcMinimizingCostArrayTerm(massCenterVelocity(:, 1)); -if normalizeByFinalTime - cost = cost / time(end); -end -end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityYIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityYIntegrand.m deleted file mode 100644 index 2dda31c63..000000000 --- a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityYIntegrand.m +++ /dev/null @@ -1,40 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function minimizes the center of mass velocity in the y direction. -% -% (struct, struct) -> (Array of number) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function cost = calcMinimizingMassCenterVelocityYIntegrand(costTerm, ... - values, time, params) -normalizeByFinalTime = valueOrAlternate(costTerm, ... - "normalize_by_final_time", true); -massCenterVelocity = calcMassCenterVelocity(values, params.model, ... - params.coordinateNames); -cost = calcMinimizingCostArrayTerm(massCenterVelocity(:, 2)); -if normalizeByFinalTime - cost = cost / time(end); -end -end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityZIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityZIntegrand.m deleted file mode 100644 index 6d0a8b190..000000000 --- a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityZIntegrand.m +++ /dev/null @@ -1,40 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function minimizes the center of mass velocity in the z direction. -% -% (struct, struct) -> (Array of number) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function cost = calcMinimizingMassCenterVelocityZIntegrand(costTerm, ... - values, time, params) -normalizeByFinalTime = valueOrAlternate(costTerm, ... - "normalize_by_final_time", true); -massCenterVelocity = calcMassCenterVelocity(values, params.model, ... - params.coordinateNames); -cost = calcMinimizingCostArrayTerm(massCenterVelocity(:, 3)); -if normalizeByFinalTime - cost = cost / time(end); -end -end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMetabolicCost.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMetabolicCost.m deleted file mode 100644 index 0687e9fcf..000000000 --- a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMetabolicCost.m +++ /dev/null @@ -1,39 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function minimizes the metabolic cost. -% -% (struct, struct, struct) -> (Array of number) -% -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function cost = calcMinimizingMetabolicCost(costTerm, values, time, ... - modeledValues, params) -normalizeByFinalTime = valueOrAlternate(costTerm, ... - "normalize_by_final_time", false); -metabolicCost = calcMetabolicCost(time, ... - values.statePositions, modeledValues.muscleActivations, params); -cost = calcMinimizingCostArrayTerm(metabolicCost); -if normalizeByFinalTime - cost = cost / time(end); -end -end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingPropulsiveForceIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingPropulsiveForceIntegrand.m deleted file mode 100644 index 454995931..000000000 --- a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingPropulsiveForceIntegrand.m +++ /dev/null @@ -1,44 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function minimizes the propulsive force for the specified foot. -% -% (struct, struct, struct) -> (Array of number) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function cost = calcMinimizingPropulsiveForceIntegrand(modeledValues, ... - time, params, costTerm) -normalizeByFinalTime = valueOrAlternate(costTerm, ... - "normalize_by_final_time", false); -for i = 1:length(params.contactSurfaces) - if params.contactSurfaces{i}.isLeftFoot == costTerm.is_left_foot - propulsiveForce = getPropulsiveForce(... - modeledValues.groundReactionsLab.forces{i}(:,1)); - end -end -cost = calcMinimizingCostArrayTerm(propulsiveForce); -if normalizeByFinalTime - cost = cost / time(end); -end -end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingTrailingLimbAngleIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingTrailingLimbAngleIntegrand.m deleted file mode 100644 index 067a53244..000000000 --- a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingTrailingLimbAngleIntegrand.m +++ /dev/null @@ -1,47 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function minimizes the trailing limb for the specified foot. -% -% (struct, struct, struct, struct) -> (Array of number) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function cost = calcMinimizingTrailingLimbAngleIntegrand(values, time, ... - modeledValues, params, costTerm) -normalizeByFinalTime = valueOrAlternate(costTerm, ... - "normalize_by_final_time", true); -for i = 1:length(params.contactSurfaces) - if params.contactSurfaces{i}.isLeftFoot == costTerm.is_left_foot - normalForce = ... - modeledValues.groundReactionsLab.forces{i}(:, 2) - 20; - end -end -trailingLimbAngle = calcTrailingLimb(costTerm, values, ... - normalForce, params); -cost = calcMinimizingCostArrayTerm(trailingLimbAngle * ... - ones(length(time), 1)); -if normalizeByFinalTime - cost = cost / time(end); -end -end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcSingleSupportTime.m b/src/TreatmentOptimization/IntegrandTerms/calcSingleSupportTime.m deleted file mode 100644 index d0a98b91c..000000000 --- a/src/TreatmentOptimization/IntegrandTerms/calcSingleSupportTime.m +++ /dev/null @@ -1,56 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function calculates the single support time for the specified foot -% for one gait cycle. -% -% (Array of number, Array of number) -> (Number) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function singleSupportTime = calcSingleSupportTime(normalForce, time) - -normalForce = normalForce - 30; % Noise buffer -normalForce(normalForce<0) = 0; -slope = diff(normalForce); - -heelStrikeEvent = getHeelStrikeEvent(slope); -toeOffEvent = getToeOffEvent(slope); - -if isempty(heelStrikeEvent) - if normalForce(1) == 0 - heelStrikeEvent = 1; - end -end - -singleSupportTime = []; -if toeOffEvent < heelStrikeEvent - singleSupportTime = time(heelStrikeEvent) - time(toeOffEvent); -end -if toeOffEvent > heelStrikeEvent - singleSupportTime = (time(end) - time(toeOffEvent)) + time(heelStrikeEvent); -end -if isempty(singleSupportTime) - singleSupportTime = 0; -end -end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcStepLength.m b/src/TreatmentOptimization/IntegrandTerms/calcStepLength.m deleted file mode 100644 index b70e0fe0b..000000000 --- a/src/TreatmentOptimization/IntegrandTerms/calcStepLength.m +++ /dev/null @@ -1,49 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function calculates the step length for the specified foot for one -% gait cycle. -% -% (Array of number, Array of number) -> (Number) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function stepLength = calcStepLength(normalForce, heelPosition) - -normalForce = normalForce - 30; % Noise buffer -normalForce(normalForce<0) = 0; -slope = diff(normalForce); -heelStrikeEvent = getHeelStrikeEvent(slope); -if isempty(heelStrikeEvent) - if normalForce(1) == 0 - heelStrikeEvent = 1; - stepLength = heelPosition(heelStrikeEvent, 1) - ... - heelPosition(heelStrikeEvent, 2); - else - stepLength = 0; - end -else - stepLength = heelPosition(heelStrikeEvent, 1) - ... - heelPosition(heelStrikeEvent, 2); -end -end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcStepLengthAsymmetry.m b/src/TreatmentOptimization/IntegrandTerms/calcStepLengthAsymmetry.m deleted file mode 100644 index 50bbb3328..000000000 --- a/src/TreatmentOptimization/IntegrandTerms/calcStepLengthAsymmetry.m +++ /dev/null @@ -1,49 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function calculates step length asymmetry. Step length asymmetry is -% calculated as the ratio between step lengths, therefore a step length -% asymmetry of 1 represent symmetry. -% -% (struct, struct) -> (Number) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function stepLengthAsymmetry = calcStepLengthAsymmetry(modeledValues, ... - params) - -for i = 1:length(params.contactSurfaces) - if i == 1 - stepLength(i) = calcStepLength(modeledValues. ... - groundReactionsLab.forces{i}(:,2), ... - [modeledValues.bodyLocations.parent{i}(:, 1) ... - modeledValues.bodyLocations.parent{i+1}(:, 1)]); - else - stepLength(i) = calcStepLength(modeledValues. ... - groundReactionsLab.forces{i}(:,2), ... - [modeledValues.bodyLocations.parent{i}(:, 1) ... - modeledValues.bodyLocations.parent{i-1}(:, 1)]); - end -end -stepLengthAsymmetry = stepLength(1) / stepLength(2); -end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcStepLengthAsymmetryIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcStepLengthAsymmetryIntegrand.m deleted file mode 100644 index fa4e4da4b..000000000 --- a/src/TreatmentOptimization/IntegrandTerms/calcStepLengthAsymmetryIntegrand.m +++ /dev/null @@ -1,44 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function tracks the difference between the predicted step length -% asymmetry and the error center specified. An error center of "1" would -% encourage step legth symmetry. -% -% (struct, struct, struct, struct) -> (Array of number) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function cost = calcStepLengthAsymmetryIntegrand(time, modeledValues,... - params, costTerm) - -errorCenter = valueOrAlternate(costTerm, "errorCenter", 1); -stepLengthAsymmetry = calcStepLengthAsymmetry(modeledValues,... - params, costTerm.reference_body); -cost = calcTrackingCostArrayTerm(stepLengthAsymmetry * ... - ones(length(time), 1), errorCenter * ... - ones(length(time), 1), 1); -if normalizeByFinalTime - cost = cost / time(end); -end -end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcStepTimeAsymmetry.m b/src/TreatmentOptimization/IntegrandTerms/calcStepTimeAsymmetry.m deleted file mode 100644 index 5172e4e39..000000000 --- a/src/TreatmentOptimization/IntegrandTerms/calcStepTimeAsymmetry.m +++ /dev/null @@ -1,47 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function calculates step time asymmetry. Step time asymmetry is -% calculated as the ratio between single support times, therefore a step -% time asymmetry of 1 represent symmetry. -% -% (struct, struct, struct) -> (Number) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function stepTimeAsymmetry = calcStepTimeAsymmetry(values, ... - modeledValues, params) - -for i = 1:length(params.contactSurfaces) - if i == 1 - singleSupportTime(i) = calcSingleSupportTime( ... - modeledValues.groundReactionsLab.forces{i + 1}(:, 2), ... - values.time); - else - singleSupportTime(i) = calcSingleSupportTime( ... - modeledValues.groundReactionsLab.forces{i - 1}(:, 2), ... - values.time); - end -end -stepTimeAsymmetry = singleSupportTime(1) / singleSupportTime(2); -end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcStepTimeAsymmetryIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcStepTimeAsymmetryIntegrand.m deleted file mode 100644 index 822c374dd..000000000 --- a/src/TreatmentOptimization/IntegrandTerms/calcStepTimeAsymmetryIntegrand.m +++ /dev/null @@ -1,45 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function tracks the difference between the predicted step time -% asymmetry and the error center specified. An error center of "1" would -% encourage step time symmetry. -% -% (struct, struct, struct, struct) -> (Array of number) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function cost = calcStepTimeAsymmetryIntegrand(values, time, ... - modeledValues, params, costTerm) -normalizeByFinalTime = valueOrAlternate(costTerm, ... - "normalize_by_final_time", false); -errorCenter = valueOrAlternate(costTerm, "errorCenter", 1); -stepTimeAsymmetry = calcStepTimeAsymmetry(values, ... - modeledValues, params); -cost = calcTrackingCostArrayTerm(stepTimeAsymmetry * ... - ones(length(time), 1), errorCenter * ... - ones(length(time), 1), 1); -if normalizeByFinalTime - cost = cost / time(end); -end -end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcStrideLength.m b/src/TreatmentOptimization/IntegrandTerms/calcStrideLength.m deleted file mode 100644 index c40aa054c..000000000 --- a/src/TreatmentOptimization/IntegrandTerms/calcStrideLength.m +++ /dev/null @@ -1,46 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function calculates stride length. -% -% (struct, struct) -> (number) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function strideLength = calcStrideLength(modeledValues, params) - -for i = 1:length(params.contactSurfaces) - if i == 1 - stepLength(i) = calcStepLength(modeledValues. ... - groundReactionsLab.forces{i}(:,2), ... - [modeledValues.bodyLocations.parent{i}(:, 1) ... - modeledValues.bodyLocations.parent{i+1}(:, 1)]); - else - stepLength(i) = calcStepLength(modeledValues. ... - groundReactionsLab.forces{i}(:,2), ... - [modeledValues.bodyLocations.parent{i}(:, 1) ... - modeledValues.bodyLocations.parent{i-1}(:, 1)]); - end -end -strideLength = sum(stepLength); -end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicSlopeIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicSlopeIntegrand.m index a1ef65304..3988734c9 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicSlopeIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicSlopeIntegrand.m @@ -1,7 +1,7 @@ % This function is part of the NMSM Pipeline, see file for full license. % -% This function calculates the difference between the experimental and -% predicted inverse dynamic moments for the specified coordinate. +% This function calculates the difference between the slope of experimental +% and predicted inverse dynamic moments for the specified coordinate. % % (struct, Array of number, 2D matrix, Array of string) -> (Array of number) % diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrailingLimb.m b/src/TreatmentOptimization/IntegrandTerms/calcTrailingLimb.m deleted file mode 100644 index 4d8074ba4..000000000 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrailingLimb.m +++ /dev/null @@ -1,55 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function calculates the trailing limb angle. The trailing limb angle -% is the angle between OpenSim's vertical axis and a vector created between -% the greater trochanter and the fifth metatarsal head. The location of the -% greater trochanter and the fifth metatarsal is required. The name of the -% bodies that each location is in reference to must also be assigned. -% -% (struct, struct, 2D matrix, struct) -> (Array of number) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function trailingLimbAngle = calcTrailingLimb(costTerm, values, ... - normalForce, params) - -fifthMetarsalLocation = calcBodyLocation(values, str2num( ... - costTerm.toe_point), costTerm.toe_body, params); -greaterTrochanterLocation = calcBodyLocation(values, str2num( ... - costTerm.femur_point), costTerm.femur_body, params); - -normalForce(normalForce<0) = 0; -slope = diff(normalForce); -toeOffEvent = getToeOffEvent(slope); - -if ~isempty(toeOffEvent) - trailingLimbVector = fifthMetarsalLocation(toeOffEvent, :) - ... - greaterTrochanterLocation(toeOffEvent, :); - trailingLimbUnitVector = trailingLimbVector/norm(trailingLimbVector); - dotProduct = dot(trailingLimbUnitVector, [0 -1 0]); - trailingLimbAngle = acosd(dotProduct); -else - trailingLimbAngle = 0; -end -end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/getBreakingForce.m b/src/TreatmentOptimization/IntegrandTerms/getBreakingForce.m deleted file mode 100644 index 9c90c97be..000000000 --- a/src/TreatmentOptimization/IntegrandTerms/getBreakingForce.m +++ /dev/null @@ -1,36 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function zeros out the positive portions of the anterior-posterior -% force to obtain the breaking force. -% -% (struct, struct, struct) -> (Array of number) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function breakingForce = getBreakingForce(anteroPosteriorForce) - -anteroPosteriorForce = anteroPosteriorForce + 10; % Noise buffer -anteroPosteriorForce(anteroPosteriorForce>=0) = 0; -breakingForce = anteroPosteriorForce; -end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/getHeelStrikeEvent.m b/src/TreatmentOptimization/IntegrandTerms/getHeelStrikeEvent.m deleted file mode 100644 index 315916c31..000000000 --- a/src/TreatmentOptimization/IntegrandTerms/getHeelStrikeEvent.m +++ /dev/null @@ -1,42 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function identifies the heel strike event by detecting changes in -% slope of the normal force. To avoid erroneous detection of a heel strike -% event, the slope of the normal force must be positive for at least 10% of -% the time. Detects only one heel strike (use for only one gait cycle). -% -% (Array of number) -> (Number) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function heelStrikeEvent = getHeelStrikeEvent(slope) - -timePadding = round(length(slope) * 0.10); -heelStrikeEvent = []; -for i = 2 : length(slope) - timePadding - if slope(i - 1) == 0 && all(slope(i : i + timePadding) > 0) - heelStrikeEvent = i; - end -end -end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/getPropulsiveForce.m b/src/TreatmentOptimization/IntegrandTerms/getPropulsiveForce.m deleted file mode 100644 index 2c6dae23f..000000000 --- a/src/TreatmentOptimization/IntegrandTerms/getPropulsiveForce.m +++ /dev/null @@ -1,36 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function zeros out the negative portions of the anterior-posterior -% force to obtain the propulive force. -% -% (struct, struct, struct) -> (Array of number) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function propulsiveForce = getPropulsiveForce(anteroPosteriorForce) - -anteroPosteriorForce = anteroPosteriorForce - 10; % Noise buffer -anteroPosteriorForce(anteroPosteriorForce<=0) = 0; -propulsiveForce = anteroPosteriorForce; -end \ No newline at end of file diff --git a/src/TreatmentOptimization/IntegrandTerms/getToeOffEvent.m b/src/TreatmentOptimization/IntegrandTerms/getToeOffEvent.m deleted file mode 100644 index 6d61940fb..000000000 --- a/src/TreatmentOptimization/IntegrandTerms/getToeOffEvent.m +++ /dev/null @@ -1,42 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function identifies the toe off event by detecting changes in -% slope of the normal force. To avoid erroneous detection of a toe off -% event, the slope of the normal force must be negative for at least 10% of -% the time. Detects only one toe off event (use for only one gait cycle). -% -% (Array of number) -> (Number) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function toeOffEvent = getToeOffEvent(slope) - -timePadding = round(length(slope) * 0.10) + 1; -toeOffEvent = []; -for i = 1 + timePadding : length(slope) - if slope(i) == 0 && all(slope(i - timePadding : i - 1) < 0) - toeOffEvent = i; - end -end -end \ No newline at end of file diff --git a/src/TreatmentOptimization/generateCostTermStruct.m b/src/TreatmentOptimization/generateCostTermStruct.m index 1e3573244..54c8f6cee 100644 --- a/src/TreatmentOptimization/generateCostTermStruct.m +++ b/src/TreatmentOptimization/generateCostTermStruct.m @@ -77,30 +77,12 @@ "muscle_activation_tracking", ... "controller_tracking", ... "joint_jerk_minimization", ... - "metabolic_cost_minimization" ... - "propulsive_force_maximization" ... - "propulsive_force_minimization" ... - "breaking_force_maximization" ... - "breaking_force_minimization" ... - "step_length_maximization" ... - "step_length_asymmetry_minimization" ... - "single_support_time_maximization" ... - "single_support_time_goal" ... - "step_time_asymmetry_minimization" ... "joint_power_minimization" ... "joint_energy_generation_goal" ... "joint_energy_absorption_goal" ... "propulsive_impulse_goal" ... "braking_impulse_goal" ... - "trailing_limb_angle_minimization" ... - "trailing_limb_angle_maximization" ... "muscle_activation_minimization" ... - "muscle_activation_maximization" ... - "center_mass_velocity_x_minimization" ... - "center_mass_velocity_y_minimization" ... - "center_mass_velocity_z_minimization" ... - "kinematic_symmetry" ... - "walking_speed_goal" ... "external_torque_control_minimization" ... "angular_momentum_minimization" ... "user_defined", ... @@ -178,14 +160,6 @@ costTerm ... ); -costTermCalculations.metabolic_cost_minimization = @(values, modeledValues, auxdata, costTerm) ... - calcMinimizingMetabolicCost( ... - costTerm, ... - values, ... - values.time, ... - modeledValues, ... - auxdata); - costTermCalculations.inverse_dynamics_load_tracking = @(values, modeledValues, auxdata, costTerm) ... calcTrackingInverseDynamicLoadsIntegrand( ... costTerm, ... @@ -212,16 +186,6 @@ modeledValues.inverseDynamicsMoments, ... costTerm.load ... ); - -costTermCalculations.kinetic_inconsistency_minimization = @(values, modeledValues, auxdata, costTerm) ... - calcMinimizingKineticInconsistencyIntegrand( ... - costTerm, ... - auxdata, ... - values.time, ... - modeledValues, ... - values.torqueControls, ... - costTerm.load ... - ); costTermCalculations.external_force_tracking = @(values, modeledValues, auxdata, costTerm) ... calcTrackingExternalForcesIntegrand( ... @@ -247,70 +211,6 @@ auxdata, ... costTerm.muscle); -costTermCalculations.propulsive_force_maximization = @(values, modeledValues, auxdata, costTerm) ... - calcMaximizingPropulsiveForceIntegrand( ... - modeledValues, ... - values.time, ... - auxdata, ... - costTerm); - -costTermCalculations.propulsive_force_minimization = @(values, modeledValues, auxdata, costTerm) ... - calcMinimizingPropulsiveForceIntegrand( ... - modeledValues, ... - values.time, ... - auxdata, ... - costTerm); - -costTermCalculations.breaking_force_maximization = @(values, modeledValues, auxdata, costTerm) ... - calcMaximizingBreakingForceIntegrand( ... - modeledValues, ... - values.time, ... - auxdata, ... - costTerm); - -costTermCalculations.breaking_force_minimization = @(values, modeledValues, auxdata, costTerm) ... - calcMinimizingBreakingForceIntegrand( ... - modeledValues, ... - values.time, ... - auxdata, ... - costTerm); - -costTermCalculations.step_length_maximization = @(values, modeledValues, auxdata, costTerm) ... - calcMaximizingStepLengthIntegrand( ... - values.time, ... - modeledValues, ... - auxdata, ... - costTerm); - -costTermCalculations.step_length_asymmetry_minimization = @(values, modeledValues, auxdata, costTerm) ... - calcStepLengthAsymmetryIntegrand( ... - values.time, ... - modeledValues, ... - auxdata, ... - costTerm); - -costTermCalculations.single_support_time_maximization = @(values, modeledValues, auxdata, costTerm) ... - calcMaximizingSingleSupportTimeIntegrand( ... - values.time, ... - modeledValues, ... - auxdata, ... - costTerm); - -costTermCalculations.single_support_time_goal = @(values, modeledValues, auxdata, costTerm) ... - calcGoalSingleSupportTimeIntegrand( ... - values.time, ... - modeledValues, ... - auxdata, ... - costTerm); - -costTermCalculations.step_time_asymmetry_minimization = @(values, modeledValues, auxdata, costTerm) ... - calcStepTimeAsymmetryIntegrand( ... - values, ... - values.time, ... - modeledValues, ... - auxdata, ... - costTerm); - costTermCalculations.joint_power_minimization = @(values, modeledValues, auxdata, costTerm) ... calcMinimizingJointPowerIntegrand( ... costTerm, ... @@ -357,24 +257,6 @@ costTerm ... ); -costTermCalculations.trailing_limb_angle_minimization = @(values, modeledValues, auxdata, costTerm) ... - calcMinimizingTrailingLimbAngleIntegrand( ... - values, ... - values.time, ... - modeledValues, ... - auxdata, ... - costTerm ... - ); - -costTermCalculations.trailing_limb_angle_maximization = @(values, modeledValues, auxdata, costTerm) ... - calcMaximizingTrailingLimbAngleIntegrand( ... - values, ... - values.time, ... - modeledValues, ... - auxdata, ... - costTerm ... - ); - costTermCalculations.muscle_activation_minimization = @(values, modeledValues, auxdata, costTerm) ... calcMinimizingMuscleActivationIntegrand( ... costTerm, ... @@ -384,51 +266,6 @@ costTerm.muscle ... ); -costTermCalculations.muscle_activation_maximization = @(values, modeledValues, auxdata, costTerm) ... - calcMaximizingMuscleActivationIntegrand( ... - costTerm, ... - values.time, ... - modeledValues.muscleActivations, ... - auxdata, ... - costTerm.muscle ... - ); - -costTermCalculations.center_mass_velocity_x_minimization = @(values, modeledValues, auxdata, costTerm) ... - calcMinimizingMassCenterVelocityXIntegrand( ... - costTerm, ... - values, ... - values.time, ... - auxdata); - -costTermCalculations.center_mass_velocity_y_minimization = @(values, modeledValues, auxdata, costTerm) ... - calcMinimizingMassCenterVelocityYIntegrand( ... - costTerm, ... - values, ... - values.time, ... - auxdata); - -costTermCalculations.center_mass_velocity_z_minimization = @(values, modeledValues, auxdata, costTerm) ... - calcMinimizingMassCenterVelocityZIntegrand( ... - costTerm, ... - values, ... - values.time, ... - auxdata); - -costTermCalculations.kinematic_symmetry = @(values, modeledValues, auxdata, costTerm) ... - calcKinematicSymmetryIntegrand( ... - values.positions, ... - values.time, ... - auxdata, ... - costTerm); - -costTermCalculations.walking_speed_goal = @(values, modeledValues, auxdata, costTerm) ... - calcGoalWalkingSpeedIntegrand( ... - modeledValues, ... - values, ... - values.time, ... - auxdata, ... - costTerm); - costTermCalculations.external_torque_control_minimization = @(values, modeledValues, auxdata, costTerm) ... calcMinimizingExternalTorqueControl( ... costTerm, ... From 6f19e0f39d99596fec8077f102192be7327baf51 Mon Sep 17 00:00:00 2001 From: Spencer Williams <54556034+SpencerTWilliams@users.noreply.github.com> Date: Fri, 23 Feb 2024 15:27:31 -0600 Subject: [PATCH 334/365] Metabolic cost per time and distance --- ...alcMetabolicCostPerDistanceGoalIntegrand.m | 40 +++++++++++++++++++ .../calcMetabolicCostPerTimeGoalIntegrand.m | 38 ++++++++++++++++++ .../generateCostTermStruct.m | 16 ++++++++ 3 files changed, 94 insertions(+) create mode 100644 src/TreatmentOptimization/IntegrandTerms/calcMetabolicCostPerDistanceGoalIntegrand.m create mode 100644 src/TreatmentOptimization/IntegrandTerms/calcMetabolicCostPerTimeGoalIntegrand.m diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMetabolicCostPerDistanceGoalIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMetabolicCostPerDistanceGoalIntegrand.m new file mode 100644 index 000000000..9a23ccc3e --- /dev/null +++ b/src/TreatmentOptimization/IntegrandTerms/calcMetabolicCostPerDistanceGoalIntegrand.m @@ -0,0 +1,40 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function returns an integrand cost for metabolic cost normalized by +% time. +% +% (struct, struct, struct, struct) -> (Array of double) + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Spencer Williams % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function cost = calcMetabolicCostPerDistanceGoalIntegrand( ... + modeledValues, values, inputs, costTerm) +assert(isfield(modeledValues, 'muscleActivations'), "Muscle " + ... + "activations are required for metabolic cost calculations.") +rawCost = calcMetabolicCost(values.time, values.positions, ... + modeledValues.muscleActivations, inputs); +speed = calcMassCenterVelocity(values, inputs.model, ... + inputs.coordinateNames); +cost = (rawCost - costTerm.errorCenter) / costTerm.maxAllowableError ... + / values.time(end) / speed; +end diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMetabolicCostPerTimeGoalIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMetabolicCostPerTimeGoalIntegrand.m new file mode 100644 index 000000000..7b7e33afe --- /dev/null +++ b/src/TreatmentOptimization/IntegrandTerms/calcMetabolicCostPerTimeGoalIntegrand.m @@ -0,0 +1,38 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function returns an integrand cost for metabolic cost normalized by +% time. +% +% (struct, struct, struct, struct) -> (Array of double) + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Spencer Williams % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function cost = calcMetabolicCostPerTimeGoalIntegrand( ... + modeledValues, values, inputs, costTerm) +assert(isfield(modeledValues, 'muscleActivations'), "Muscle " + ... + "activations are required for metabolic cost calculations.") +rawCost = calcMetabolicCost(values.time, values.positions, ... + modeledValues.muscleActivations, inputs); +cost = (rawCost - costTerm.errorCenter) / costTerm.maxAllowableError ... + / values.time(end); +end diff --git a/src/TreatmentOptimization/generateCostTermStruct.m b/src/TreatmentOptimization/generateCostTermStruct.m index 54c8f6cee..4b29ccb4e 100644 --- a/src/TreatmentOptimization/generateCostTermStruct.m +++ b/src/TreatmentOptimization/generateCostTermStruct.m @@ -85,6 +85,8 @@ "muscle_activation_minimization" ... "external_torque_control_minimization" ... "angular_momentum_minimization" ... + "metabolic_cost_per_time" ... + "metabolic_cost_per_distance" ... "user_defined", ... ]; otherwise @@ -280,6 +282,20 @@ values.time, ... costTerm); +costTermCalculations.metabolic_cost_per_time = @(values, modeledValues, auxdata, costTerm) ... + calcMetabolicCostPerTimeGoalIntegrand( ... + modeledValues, ... + values, ... + auxdata, ... + costTerm); + +costTermCalculations.metabolic_cost_per_distance = @(values, modeledValues, auxdata, costTerm) ... + calcMetabolicCostPerDistanceGoalIntegrand( ... + modeledValues, ... + values, ... + auxdata, ... + costTerm); + costTermCalculations.synergy_vector_tracking = @(values, modeledValues, auxdata, costTerm) ... calcTrackingSynergyVectorsDiscrete( ... values.synergyWeights, ... From 64f47d9b9dbac3fc3b72be0b57998c37522d3245 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sat, 24 Feb 2024 21:14:10 -0600 Subject: [PATCH 335/365] fix synergy driven kinetic constraint Co-Authored-By: Spencer Williams <54556034+SpencerTWilliams@users.noreply.github.com> --- .../PathTerms/calcKineticPathConstraint.m | 8 +++++--- src/TreatmentOptimization/makeSurrogateModel.m | 9 --------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/TreatmentOptimization/PathTerms/calcKineticPathConstraint.m b/src/TreatmentOptimization/PathTerms/calcKineticPathConstraint.m index c5aa07556..f5eca5d1d 100644 --- a/src/TreatmentOptimization/PathTerms/calcKineticPathConstraint.m +++ b/src/TreatmentOptimization/PathTerms/calcKineticPathConstraint.m @@ -34,15 +34,17 @@ inverseDynamicsIndex = find(strcmp(convertCharsToStrings(inputs.inverseDynamicsMomentLabels), ... loadName)); if strcmpi(inputs.controllerType, "synergy") - synergyIndex = find(inputs.dofsActuatedIndex == inverseDynamicsIndex); + coordinateNameIndex = find(strcmp(convertCharsToStrings(inputs.coordinateNames), ... + replace(replace(loadName, '_moment', ''), '_force', ''))); + synergyIndex = find(inputs.surrogateModelIndex == coordinateNameIndex); else synergyIndex = []; end torqueIndex = find(strcmp(strcat(inputs.torqueControllerCoordinateNames, ... '_moment'), loadName)); if isempty(torqueIndex) - torqueIndex = find(strcmp(strcat(inputs.torqueControllerCoordinateNames, ... - '_force'), loadName)); + torqueIndex = find(strcmp(strcat(inputs.torqueControllerCoordinateNames, ... + '_force'), loadName)); end if isempty(synergyIndex) synergyLoad = 0; diff --git a/src/TreatmentOptimization/makeSurrogateModel.m b/src/TreatmentOptimization/makeSurrogateModel.m index 92d3902aa..49cc3bf55 100644 --- a/src/TreatmentOptimization/makeSurrogateModel.m +++ b/src/TreatmentOptimization/makeSurrogateModel.m @@ -36,14 +36,5 @@ end end end - inputs.dofsActuatedIndex = []; - for i = 1 : length(inputs.inverseDynamicsMomentLabels) - for j = 1 : length(inputs.surrogateModelCoordinateNames) - if strcmp(inputs.inverseDynamicsMomentLabels(i), ... - strcat(inputs.surrogateModelCoordinateNames(j), '_moment')) - inputs.dofsActuatedIndex(end+1) = i; - end - end - end end end From d730c4052b39d1e853402f33184a7e83b570b0b8 Mon Sep 17 00:00:00 2001 From: RobSalati <142843461+RobSalati@users.noreply.github.com> Date: Mon, 26 Feb 2024 19:12:14 -0600 Subject: [PATCH 336/365] Plotting multiple model files at once & added MAE for MTP joint moments --- .../2vSkMDCswQeoYhO-OpwhdjQdDVod.xml | 6 + .../2vSkMDCswQeoYhO-OpwhdjQdDVop.xml | 2 + .../RhzEFSy2P8YbTqJISC7SXKsqTK8d.xml | 6 + .../RhzEFSy2P8YbTqJISC7SXKsqTK8p.xml | 2 + .../YTLZFTjx9_oUnvXPIvdx4ROS1eEd.xml | 6 + .../YTLZFTjx9_oUnvXPIvdx4ROS1eEp.xml | 2 + .../_j9Okdc3TksJWGa7ieAdzJtI0Ucd.xml | 6 + .../_j9Okdc3TksJWGa7ieAdzJtI0Ucp.xml | 2 + .../Analysis/plotMtpJointMoments.m | 10 +- ...reatmentOptimizationGroundReactionsMulti.m | 111 +++++++++++++++ .../plotTreatmentOptimizationJointAngles.m | 4 +- ...lotTreatmentOptimizationJointAnglesMulti.m | 125 +++++++++++++++++ .../plotTreatmentOptimizationJointLoads.m | 1 + ...plotTreatmentOptimizationJointLoadsMulti.m | 132 ++++++++++++++++++ ...atmentOptimizationMuscleActivationsMulti.m | 112 +++++++++++++++ 15 files changed, 521 insertions(+), 6 deletions(-) create mode 100644 resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/2vSkMDCswQeoYhO-OpwhdjQdDVod.xml create mode 100644 resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/2vSkMDCswQeoYhO-OpwhdjQdDVop.xml create mode 100644 resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/RhzEFSy2P8YbTqJISC7SXKsqTK8d.xml create mode 100644 resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/RhzEFSy2P8YbTqJISC7SXKsqTK8p.xml create mode 100644 resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/YTLZFTjx9_oUnvXPIvdx4ROS1eEd.xml create mode 100644 resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/YTLZFTjx9_oUnvXPIvdx4ROS1eEp.xml create mode 100644 resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/_j9Okdc3TksJWGa7ieAdzJtI0Ucd.xml create mode 100644 resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/_j9Okdc3TksJWGa7ieAdzJtI0Ucp.xml create mode 100644 src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactionsMulti.m create mode 100644 src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAnglesMulti.m create mode 100644 src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoadsMulti.m create mode 100644 src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivationsMulti.m diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/2vSkMDCswQeoYhO-OpwhdjQdDVod.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/2vSkMDCswQeoYhO-OpwhdjQdDVod.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/2vSkMDCswQeoYhO-OpwhdjQdDVod.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/2vSkMDCswQeoYhO-OpwhdjQdDVop.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/2vSkMDCswQeoYhO-OpwhdjQdDVop.xml new file mode 100644 index 000000000..c1ca5bacf --- /dev/null +++ b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/2vSkMDCswQeoYhO-OpwhdjQdDVop.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/RhzEFSy2P8YbTqJISC7SXKsqTK8d.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/RhzEFSy2P8YbTqJISC7SXKsqTK8d.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/RhzEFSy2P8YbTqJISC7SXKsqTK8d.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/RhzEFSy2P8YbTqJISC7SXKsqTK8p.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/RhzEFSy2P8YbTqJISC7SXKsqTK8p.xml new file mode 100644 index 000000000..16ec9eddc --- /dev/null +++ b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/RhzEFSy2P8YbTqJISC7SXKsqTK8p.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/YTLZFTjx9_oUnvXPIvdx4ROS1eEd.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/YTLZFTjx9_oUnvXPIvdx4ROS1eEd.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/YTLZFTjx9_oUnvXPIvdx4ROS1eEd.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/YTLZFTjx9_oUnvXPIvdx4ROS1eEp.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/YTLZFTjx9_oUnvXPIvdx4ROS1eEp.xml new file mode 100644 index 000000000..7b837791f --- /dev/null +++ b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/YTLZFTjx9_oUnvXPIvdx4ROS1eEp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/_j9Okdc3TksJWGa7ieAdzJtI0Ucd.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/_j9Okdc3TksJWGa7ieAdzJtI0Ucd.xml new file mode 100644 index 000000000..7a6326b99 --- /dev/null +++ b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/_j9Okdc3TksJWGa7ieAdzJtI0Ucd.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/_j9Okdc3TksJWGa7ieAdzJtI0Ucp.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/_j9Okdc3TksJWGa7ieAdzJtI0Ucp.xml new file mode 100644 index 000000000..627533055 --- /dev/null +++ b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/_j9Okdc3TksJWGa7ieAdzJtI0Ucp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m b/src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m index f0dfff5ed..8a5d2d616 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m @@ -79,8 +79,9 @@ function plotMtpJointMoments(resultsDirectory) hold off set(gca, fontsize=11) rmse = rms(meanMoments(:,i) - meanIdMoments(:,i)); - title(sprintf("%s \n RMSE: %.4f", ... - jointLabels(i), rmse), fontsize=12) + mae = mean(abs(meanMoments(:,i) - meanIdMoments(:,i))); + title(sprintf("%s \n RMSE: %.4f, MAE: %.4f", ... + jointLabels(i), rmse, mae), fontsize=12) axis([0 size(meanIdMoments, 1) minMoment, maxMoment]) if i == 1 legend("Mean Moment No Synx", "Mean Inverse Dynamics Moment") @@ -95,8 +96,9 @@ function plotMtpJointMoments(resultsDirectory) hold off set(gca, fontsize=11) rmse = rms(meanMomentsSynx(:,i) - meanIdMoments(:,i)); - title(sprintf("%s \n RMSE: %.4f", ... - jointLabels(i), rmse), fontsize=12) + mae = mean(abs(meanMomentsSynx(:,i) - meanIdMoments(:,i))); + title(sprintf("%s \n RMSE: %.4f, MAE: %.4f", ... + jointLabels(i), rmse, mae), fontsize=12) axis([0 size(meanIdMoments, 1) minMoment, maxMoment]) if i == 1 legend("Mean Moment Synx", "Mean Inverse Dynamics Moment") diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactionsMulti.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactionsMulti.m new file mode 100644 index 000000000..24ca4ba32 --- /dev/null +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactionsMulti.m @@ -0,0 +1,111 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function reads two .sto files: 1) experimental angles, and 2) model +% angles, and plots them. There are 2 optional arguments for figure width +% and figure height. If no optional arguments are given, the figure size is +% automatically adjusted to fit all data on one plot. Giving just figure +% width imposes the width and fits the height to fit on one plot. Giving +% both arguments will impose both figure height and width, and create +% multiple plots as needed. +% +% (string) -> (None) +% Plot joint moment curves from file. + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Robert Salati % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % +function plotTreatmentOptimizationGroundReactionsMulti(experimentalFile, ... + modelFiles, figureWidth, figureHeight) + +import org.opensim.modeling.Storage +experimentalStorage = Storage(experimentalFile); +labels = getStorageColumnNames(experimentalStorage); +experimentalData = storageToDoubleMatrix(experimentalStorage)'; +experimentalTime = findTimeColumn(experimentalStorage); +if experimentalTime(1) ~= 0 + experimentalTime = experimentalTime - experimentalTime(1); +end +modelData = {}; +for i=1:numel(modelFiles) + modelStorage = Storage(modelFiles(i)); + modelData{i} = storageToDoubleMatrix(modelStorage)'; + modelTime{i} = findTimeColumn(modelStorage); +end + +% Spline experimental time to the same time points as the model. +experimentalSpline = makeGcvSplineSet(experimentalTime, ... + experimentalData, labels); +resampledExperimental = {}; +for i = 1 : numel(modelFiles) + resampledExperimental{i} = evaluateGcvSplines(experimentalSpline, ... + labels, modelTime{i}); +end +if nargin < 3 + figureWidth = 3; +end +if nargin < 4 + figureHeight = 2; +end +figureSize = figureWidth * figureHeight; +figure(Name = "Treatment Optimization Ground Reactions", ... + Units='normalized', ... + Position=[0.05 0.05 0.9 0.85]) +subplotNumber = 1; +figureNumber = 1; +t = tiledlayout(figureHeight, figureWidth, ... + TileSpacing='compact', Padding='compact'); +xlabel(t, "Time [s]") +ylabel(t, "Ground Reaction") +for i=1:numel(labels) + if i > figureSize * figureNumber + figureNumber = figureNumber + 1; + figure(Name="Treatment Optimization Ground Reactions", ... + Units='normalized', ... + Position=[0.05 0.05 0.9 0.85]) + t = tiledlayout(figureHeight, figureWidth, ... + TileSpacing='Compact', Padding='Compact'); + xlabel(t, "Time [s]") + ylabel(t, "Ground Reaction") + subplotNumber = 1; + end + nexttile(subplotNumber); + hold on + plot(experimentalTime, experimentalData(:, i), LineWidth=2); + for j = 1 : numel(modelFiles) + plot(modelTime{j}, modelData{j}(:, i), LineWidth=2); + end + hold off + titleString = [sprintf("%s", strrep(labels(i), "_", " "))]; + for j = 1 : numel(modelFiles) + rmse = rms(resampledExperimental{j}(:, i) - modelData{j}(:, i)); + titleString(j+1) = sprintf("RMSE %d: %.4f", j, rmse); + end + title(titleString) + if subplotNumber==1 + legendValues = "Experimental"; + for j = 1 : numel(modelFiles) + legendValues(j+1) = strcat("Model ", num2str(j)); + end + legend(legendValues) + end + xlim([0, experimentalTime(end)]) + subplotNumber = subplotNumber + 1; +end \ No newline at end of file diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m index fef3357ed..534175304 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m @@ -33,7 +33,7 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % function plotTreatmentOptimizationJointAngles(experimentalAnglesFile, ... - modelAnglesFile, figureWidth, figureHeight) + modelAnglesFiles, figureWidth, figureHeight) import org.opensim.modeling.Storage experimentalAnglesStorage = Storage(experimentalAnglesFile); @@ -44,7 +44,7 @@ function plotTreatmentOptimizationJointAngles(experimentalAnglesFile, ... if experimentalTime(1) ~= 0 experimentalTime = experimentalTime - experimentalTime(1); end -modelAnglesStorage = Storage(modelAnglesFile); +modelAnglesStorage = Storage(modelAnglesFiles); modelAngles = storageToDoubleMatrix(modelAnglesStorage)'; modelAngles = modelAngles .* 180/pi; modelTime = findTimeColumn(modelAnglesStorage); diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAnglesMulti.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAnglesMulti.m new file mode 100644 index 000000000..22d627feb --- /dev/null +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAnglesMulti.m @@ -0,0 +1,125 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function reads two .sto files: 1) experimental angles, and 2) model +% angles, and plots them. There are 2 optional arguments for figure width +% and figure height. If no optional arguments are given, the figure size is +% automatically adjusted to fit all data on one plot. Giving just figure +% width imposes the width and fits the height to fit on one plot. Giving +% both arguments will impose both figure height and width, and create +% multiple plots as needed. +% +% (string) -> (None) +% Plot joint moment curves from file. + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Robert Salati % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % +function plotTreatmentOptimizationJointAnglesMulti(experimentalFile, ... + modelFiles, figureWidth, figureHeight) + +import org.opensim.modeling.Storage +experimentalStorage = Storage(experimentalFile); +labels = getStorageColumnNames(experimentalStorage); +experimentalData = storageToDoubleMatrix(experimentalStorage)'; +experimentalData = experimentalData .* 180/pi; +experimentalTime = findTimeColumn(experimentalStorage); +if experimentalTime(1) ~= 0 + experimentalTime = experimentalTime - experimentalTime(1); +end +modelData = {}; +for i=1:numel(modelFiles) + modelStorage = Storage(modelFiles(i)); + modelData{i} = storageToDoubleMatrix(modelStorage)' .* 180/pi; + modelTime{i} = findTimeColumn(modelStorage); +end + +% Spline experimental time to the same time points as the model. +experimentalSpline = makeGcvSplineSet(experimentalTime, ... + experimentalData, labels); +resampledExperimental = {}; +for i = 1 : numel(modelFiles) + resampledExperimental{i}= evaluateGcvSplines(experimentalSpline, ... + labels, modelTime{i}); +end +if nargin < 3 + figureWidth = ceil(sqrt(numel(labels))); + figureHeight = ceil(numel(labels)/figureWidth); +elseif nargin < 4 + figureHeight = ceil(numel(labels)/figureWidth); +end +figureSize = figureWidth * figureHeight; +figure(Name = "Treatment Optimization Joint Angles", ... + Units='normalized', ... + Position=[0.05 0.05 0.9 0.85]) +subplotNumber = 1; +figureNumber = 1; +t = tiledlayout(figureHeight, figureWidth, ... + TileSpacing='compact', Padding='compact'); +xlabel(t, "Time [s]") +ylabel(t, "Joint Angle [deg]") +for i=1:numel(labels) + if i > figureSize * figureNumber + figureNumber = figureNumber + 1; + figure(Name="Treatment Optimization Joint Angles", ... + Units='normalized', ... + Position=[0.05 0.05 0.9 0.85]) + t = tiledlayout(figureHeight, figureWidth, ... + TileSpacing='Compact', Padding='Compact'); + xlabel(t, "Time [s]") + ylabel(t, "Joint Angle [deg]") + subplotNumber = 1; + end + nexttile(subplotNumber); + hold on + plot(experimentalTime, experimentalData(:, i), LineWidth=2); + for j = 1 : numel(modelFiles) + plot(modelTime{j}, modelData{j}(:, i), LineWidth=2); + end + hold off + titleString = [sprintf("%s", strrep(labels(i), "_", " "))]; + for j = 1 : numel(modelFiles) + rmse = rms(resampledExperimental{j}(:, i) - modelData{j}(:, i)); + titleString(j+1) = sprintf("RMSE %d: %.4f", j, rmse); + end + title(titleString) + if subplotNumber==1 + legendValues = "Experimental"; + for j = 1 : numel(modelFiles) + legendValues(j+1) = strcat("Model ", num2str(j)); + end + legend(legendValues) + end + xlim([0, experimentalTime(end)]) + maxData = []; + minData = []; + for j = 1 : numel(modelFiles) + maxData(j) = max(modelData{j}(:, i), [], "all"); + minData(j) = min(modelData{j}(:, i), [], "all"); + end + maxData(j+1) = max(experimentalData(:, i), [], "all"); + minData(j+1) = min(experimentalData(:, i), [], "all"); + yLimitUpper = max(maxData); + yLimitLower = min(minData); + if yLimitUpper - yLimitLower < 10 + ylim([(yLimitUpper+yLimitLower)/2-10, (yLimitUpper+yLimitLower)/2+10]) + end + subplotNumber = subplotNumber + 1; +end \ No newline at end of file diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoads.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoads.m index 1921b0757..ad852dec3 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoads.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoads.m @@ -91,6 +91,7 @@ function plotTreatmentOptimizationJointLoads(experimentalMomentsFile, ... plot(modelTime, modelLoads(:, i), LineWidth=2); hold off rmse = rms(resampledExperimental(:, i) - modelLoads(:, i)); + mae = mean(abs(resampledExperimental(:, i) - modelLoads(:, i))); if contains(labels(i), "moment") title(sprintf("%s \n RMSE: %.4f", ... diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoadsMulti.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoadsMulti.m new file mode 100644 index 000000000..dc99e601b --- /dev/null +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoadsMulti.m @@ -0,0 +1,132 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function reads two .sto files: 1) experimental angles, and 2) model +% angles, and plots them. There are 2 optional arguments for figure width +% and figure height. If no optional arguments are given, the figure size is +% automatically adjusted to fit all data on one plot. Giving just figure +% width imposes the width and fits the height to fit on one plot. Giving +% both arguments will impose both figure height and width, and create +% multiple plots as needed. +% +% (string) -> (None) +% Plot joint moment curves from file. + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Robert Salati % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % +function plotTreatmentOptimizationJointLoadsMulti(experimentalFile, ... + modelFiles, figureWidth, figureHeight) + +import org.opensim.modeling.Storage +experimentalStorage = Storage(experimentalFile); +labels = getStorageColumnNames(experimentalStorage); +experimentalData = storageToDoubleMatrix(experimentalStorage)'; +experimentalTime = findTimeColumn(experimentalStorage); +if experimentalTime(1) ~= 0 + experimentalTime = experimentalTime - experimentalTime(1); +end +modelData = {}; +for i=1:numel(modelFiles) + modelStorage = Storage(modelFiles(i)); + modelData{i} = storageToDoubleMatrix(modelStorage)'; + modelTime{i} = findTimeColumn(modelStorage); +end + +% Spline experimental time to the same time points as the model. +experimentalSpline = makeGcvSplineSet(experimentalTime, ... + experimentalData, labels); +resampledExperimental = {}; +for i = 1 : numel(modelFiles) + resampledExperimental{i}= evaluateGcvSplines(experimentalSpline, ... + labels, modelTime{i}); +end +if nargin < 3 + figureWidth = ceil(sqrt(numel(labels))); + figureHeight = ceil(numel(labels)/figureWidth); +elseif nargin < 4 + figureHeight = ceil(numel(labels)/figureWidth); +end +figureSize = figureWidth * figureHeight; +figure(Name = "Treatment Optimization Joint Loads", ... + Units='normalized', ... + Position=[0.05 0.05 0.9 0.85]) +subplotNumber = 1; +figureNumber = 1; +t = tiledlayout(figureHeight, figureWidth, ... + TileSpacing='compact', Padding='compact'); +xlabel(t, "Time [s]") +ylabel(t, "Joint Angle [deg]") +for i=1:numel(labels) + if i > figureSize * figureNumber + figureNumber = figureNumber + 1; + figure(Name="Treatment Optimization Joint Loads", ... + Units='normalized', ... + Position=[0.05 0.05 0.9 0.85]) + t = tiledlayout(figureHeight, figureWidth, ... + TileSpacing='Compact', Padding='Compact'); + xlabel(t, "Time [s]") + ylabel(t, "Joint Loads") + subplotNumber = 1; + end + nexttile(subplotNumber); + hold on + plot(experimentalTime, experimentalData(:, i), LineWidth=2); + for j = 1 : numel(modelFiles) + plot(modelTime{j}, modelData{j}(:, i), LineWidth=2); + end + hold off + if contains(labels(i), "moment") + titleString = [sprintf("%s [Nm]", strrep(labels(i), "_", " "))]; + elseif contains(labels(i), "force") + titleString = [sprintf("%s [N]", strrep(labels(i), "_", " "))]; + else + titleString = [sprintf("%s", strrep(labels(i), "_", " "))]; + end + for j = 1 : numel(modelFiles) + rmse = rms(resampledExperimental{j}(:, i) - modelData{j}(:, i)); + titleString(j+1) = sprintf("RMSE %d: %.4f", j, rmse); + end + title(titleString) + if subplotNumber==1 + legendValues = "Experimental"; + for j = 1 : numel(modelFiles) + legendValues(j+1) = strcat("Model ", num2str(j)); + end + legend(legendValues) + end + xlim([0, experimentalTime(end)]) + maxData = []; + minData = []; + for j = 1 : numel(modelFiles) + maxData(j) = max(modelData{j}(1:end-1, i), [], "all"); + minData(j) = min(modelData{j}(1:end-1, i), [], "all"); + end + maxData(j+1) = max(experimentalData(1:end-1, i), [], "all"); + minData(j+1) = min(experimentalData(1:end-1, i), [], "all"); + yLimitUpper = max(maxData); + yLimitLower = min(minData); + if yLimitUpper - yLimitLower < 10 + ylim([(yLimitUpper+yLimitLower)/2-10, (yLimitUpper+yLimitLower)/2+10]) + else + ylim([yLimitLower, yLimitUpper]); + end + subplotNumber = subplotNumber + 1; +end \ No newline at end of file diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivationsMulti.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivationsMulti.m new file mode 100644 index 000000000..b7b6370ed --- /dev/null +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivationsMulti.m @@ -0,0 +1,112 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function reads two .sto files: 1) experimental angles, and 2) model +% angles, and plots them. There are 2 optional arguments for figure width +% and figure height. If no optional arguments are given, the figure size is +% automatically adjusted to fit all data on one plot. Giving just figure +% width imposes the width and fits the height to fit on one plot. Giving +% both arguments will impose both figure height and width, and create +% multiple plots as needed. +% +% (string) -> (None) +% Plot joint moment curves from file. + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Robert Salati % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % +function plotTreatmentOptimizationMuscleActivationsMulti(experimentalFile, ... + modelFiles, figureWidth, figureHeight) + +import org.opensim.modeling.Storage +experimentalStorage = Storage(experimentalFile); +labels = getStorageColumnNames(experimentalStorage); +experimentalData = storageToDoubleMatrix(experimentalStorage)'; +experimentalTime = findTimeColumn(experimentalStorage); +if experimentalTime(1) ~= 0 + experimentalTime = experimentalTime - experimentalTime(1); +end +modelData = {}; +for i=1:numel(modelFiles) + modelStorage = Storage(modelFiles(i)); + modelData{i} = storageToDoubleMatrix(modelStorage)'; + modelTime{i} = findTimeColumn(modelStorage); +end + +% Spline experimental time to the same time points as the model. +experimentalSpline = makeGcvSplineSet(experimentalTime, ... + experimentalData, labels); +resampledExperimental = {}; +for i = 1 : numel(modelFiles) + resampledExperimental{i}= evaluateGcvSplines(experimentalSpline, ... + labels, modelTime{i}); +end +if nargin < 3 + figureWidth = ceil(sqrt(numel(labels))); + figureHeight = ceil(numel(labels)/figureWidth); +elseif nargin < 4 + figureHeight = ceil(numel(labels)/figureWidth); +end +figureSize = figureWidth * figureHeight; +figure(Name = "Treatment Optimization Muscle Activations", ... + Units='normalized', ... + Position=[0.05 0.05 0.9 0.85]) +subplotNumber = 1; +figureNumber = 1; +t = tiledlayout(figureHeight, figureWidth, ... + TileSpacing='compact', Padding='compact'); +xlabel(t, "Time [s]") +ylabel(t, "Muscle Activations") +for i=1:numel(labels) + if i > figureSize * figureNumber + figureNumber = figureNumber + 1; + figure(Name="Treatment Optimization Muscle Activations", ... + Units='normalized', ... + Position=[0.05 0.05 0.9 0.85]) + t = tiledlayout(figureHeight, figureWidth, ... + TileSpacing='Compact', Padding='Compact'); + xlabel(t, "Time [s]") + ylabel(t, "Muscle Activations") + subplotNumber = 1; + end + nexttile(subplotNumber); + hold on + plot(experimentalTime, experimentalData(:, i), LineWidth=2); + for j = 1 : numel(modelFiles) + plot(modelTime{j}, modelData{j}(:, i), LineWidth=2); + end + hold off + titleString = [sprintf("%s", strrep(labels(i), "_", " "))]; + for j = 1 : numel(modelFiles) + rmse = rms(resampledExperimental{j}(:, i) - modelData{j}(:, i)); + titleString(j+1) = sprintf("RMSE %d: %.4f", j, rmse); + end + title(titleString) + if subplotNumber==1 + legendValues = "Experimental"; + for j = 1 : numel(modelFiles) + legendValues(j+1) = strcat("Model ", num2str(j)); + end + legend(legendValues) + end + xlim([0, experimentalTime(end)]) + ylim([0, 1]) + subplotNumber = subplotNumber + 1; +end \ No newline at end of file From a5c5a46567a06bebfc7b81cd1244eb11d633e972 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 26 Feb 2024 19:37:40 -0600 Subject: [PATCH 337/365] Optionally save/load surrogate model --- .../makeTreatmentOptimizationInputs.m | 11 +++++++---- src/core/parse/parseSynergyController.m | 4 ++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m b/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m index 026136937..ab9bb71da 100644 --- a/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m +++ b/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m @@ -42,15 +42,18 @@ inputs = makePathConstraintBounds(inputs); inputs = makeTerminalConstraintBounds(inputs); inputs = makeOptimalControlBounds(inputs); -if strcmp(inputs.controllerType, "synergy") - if isfile("surrogateMuscles.mat") - inputs.surrogateMuscles = load("surrogateMuscles.mat").surrogateMuscles; + +if strcmpi(inputs.controllerType, "synergy") + if inputs.loadSurrogate && isfile("surrogateMuscles.mat") + inputs.surrogateMuscles = load("surrogateMuscles.mat") ... + .surrogateMuscles; else inputs.surrogateMuscles = SurrogateModelCreation(inputs); + end + if inputs.saveSurrogate surrogateMuscles = inputs.surrogateMuscles; save("surrogateMuscles.mat", "surrogateMuscles"); end - end end diff --git a/src/core/parse/parseSynergyController.m b/src/core/parse/parseSynergyController.m index 8e088c10a..eb2c38634 100644 --- a/src/core/parse/parseSynergyController.m +++ b/src/core/parse/parseSynergyController.m @@ -61,5 +61,9 @@ inputs.torqueControllerCoordinateNames = []; end inputs = getModelOrOsimxInputs(inputs); +inputs.saveSurrogate = getBooleanLogicFromField( ... + getFieldByNameOrAlternate(tree, 'save_surrogate_model', false)); +inputs.loadSurrogate = getBooleanLogicFromField( ... + getFieldByNameOrAlternate(tree, 'load_surrogate_model', false)); end From 772c4f2cd9c8bf6430b64b5fe1a9ea935cc9a4dc Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Mon, 26 Feb 2024 21:21:51 -0600 Subject: [PATCH 338/365] Fix surrogate model coordinate order bug --- src/core/parse/parseSynergyController.m | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/parse/parseSynergyController.m b/src/core/parse/parseSynergyController.m index eb2c38634..463064c45 100644 --- a/src/core/parse/parseSynergyController.m +++ b/src/core/parse/parseSynergyController.m @@ -34,6 +34,10 @@ inputs.numSynergies = getNumSynergies(inputs.synergyGroups); inputs.surrogateModelCoordinateNames = parseSpaceSeparatedList(tree, ... "surrogate_model_coordinate_list"); +[~, ~, statesOrder] = intersect(inputs.statesCoordinateNames, ... + inputs.surrogateModelCoordinateNames, 'stable'); +inputs.surrogateModelCoordinateNames = ... + inputs.surrogateModelCoordinateNames(statesOrder); inputs.muscleNames = getMusclesFromCoordinates(inputs.model, ... inputs.surrogateModelCoordinateNames); inputs.numMuscles = length(inputs.muscleNames); From 4287de98c5e9b5d3b818d3e5c0d398d28689ec2f Mon Sep 17 00:00:00 2001 From: RobSalati Date: Mon, 26 Feb 2024 22:56:59 -0600 Subject: [PATCH 339/365] Ground reaction plotting function --- ...reatmentOptimizationGroundReactionsMulti.m | 47 ++++++++++++++----- ...lotTreatmentOptimizationJointAnglesMulti.m | 5 +- ...plotTreatmentOptimizationJointLoadsMulti.m | 9 ++-- ...atmentOptimizationMuscleActivationsMulti.m | 7 +-- 4 files changed, 47 insertions(+), 21 deletions(-) diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactionsMulti.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactionsMulti.m index 24ca4ba32..9aaec4019 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactionsMulti.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactionsMulti.m @@ -37,26 +37,49 @@ function plotTreatmentOptimizationGroundReactionsMulti(experimentalFile, ... import org.opensim.modeling.Storage experimentalStorage = Storage(experimentalFile); -labels = getStorageColumnNames(experimentalStorage); +experimentalLabels = getStorageColumnNames(experimentalStorage); experimentalData = storageToDoubleMatrix(experimentalStorage)'; experimentalTime = findTimeColumn(experimentalStorage); if experimentalTime(1) ~= 0 experimentalTime = experimentalTime - experimentalTime(1); end -modelData = {}; -for i=1:numel(modelFiles) - modelStorage = Storage(modelFiles(i)); - modelData{i} = storageToDoubleMatrix(modelStorage)'; - modelTime{i} = findTimeColumn(modelStorage); +for j = 1 : numel(modelFiles) + modelStorage = Storage(modelFiles(j)); + modelData{j} = storageToDoubleMatrix(modelStorage)'; + modelLabels{j} = getStorageColumnNames(modelStorage); + modelTime{j} = findTimeColumn(modelStorage); +end + +experimentalMomentIndices = contains(experimentalLabels, ["_m", "M"]); +experimentalForceIndices = contains(experimentalLabels, ["_v", "F"]); +experimentalIncludedIndices = experimentalMomentIndices | experimentalForceIndices; +experimentalData = experimentalData(:, experimentalIncludedIndices); +experimentalLabels = experimentalLabels(experimentalIncludedIndices); +experimentalForcePlate1 = contains(experimentalLabels, "1"); +for j = 1 : numel(modelFiles) + modelMomentIndices = contains(modelLabels{j}, "_m"); + modelForceIndices = contains(modelLabels{j}, "_v"); + modelIncludedIndices = modelMomentIndices | modelForceIndices; + modelData{j} = modelData{j}(:, modelIncludedIndices); + modelLabels{j} = modelLabels{j}(modelIncludedIndices); + modelForcePlate1 = contains(modelLabels{j}, "1"); + % modelData{j} = modelData{j}(:, ~modelForcePlate1); + if experimentalForcePlate1 ~= modelForcePlate1 + temp = modelData{j}; + modelData{j}(:, ~experimentalForcePlate1) = ... + modelData{j}(:, experimentalForcePlate1); + modelData{j}(:, experimentalForcePlate1) = ... + temp(:, ~experimentalForcePlate1); + end end % Spline experimental time to the same time points as the model. experimentalSpline = makeGcvSplineSet(experimentalTime, ... - experimentalData, labels); + experimentalData, experimentalLabels); resampledExperimental = {}; for i = 1 : numel(modelFiles) resampledExperimental{i} = evaluateGcvSplines(experimentalSpline, ... - labels, modelTime{i}); + experimentalLabels, modelTime{i}); end if nargin < 3 figureWidth = 3; @@ -74,7 +97,8 @@ function plotTreatmentOptimizationGroundReactionsMulti(experimentalFile, ... TileSpacing='compact', Padding='compact'); xlabel(t, "Time [s]") ylabel(t, "Ground Reaction") -for i=1:numel(labels) +title(t, "Force Plate 2") +for i=1:numel(experimentalLabels) if i > figureSize * figureNumber figureNumber = figureNumber + 1; figure(Name="Treatment Optimization Ground Reactions", ... @@ -93,7 +117,7 @@ function plotTreatmentOptimizationGroundReactionsMulti(experimentalFile, ... plot(modelTime{j}, modelData{j}(:, i), LineWidth=2); end hold off - titleString = [sprintf("%s", strrep(labels(i), "_", " "))]; + titleString = [sprintf("%s", strrep(experimentalLabels(i), "_", " "))]; for j = 1 : numel(modelFiles) rmse = rms(resampledExperimental{j}(:, i) - modelData{j}(:, i)); titleString(j+1) = sprintf("RMSE %d: %.4f", j, rmse); @@ -102,7 +126,8 @@ function plotTreatmentOptimizationGroundReactionsMulti(experimentalFile, ... if subplotNumber==1 legendValues = "Experimental"; for j = 1 : numel(modelFiles) - legendValues(j+1) = strcat("Model ", num2str(j)); + splitFileName = split(modelFiles(j), ["/", "\"]); + legendValues(j+1) = sprintf("%s (%d)", splitFileName(1), j); end legend(legendValues) end diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAnglesMulti.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAnglesMulti.m index 22d627feb..c0d23fe53 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAnglesMulti.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAnglesMulti.m @@ -63,7 +63,7 @@ function plotTreatmentOptimizationJointAnglesMulti(experimentalFile, ... figureWidth = ceil(sqrt(numel(labels))); figureHeight = ceil(numel(labels)/figureWidth); elseif nargin < 4 - figureHeight = ceil(numel(labels)/figureWidth); + figureHeight = ceil(sqrt(numel(labels))); end figureSize = figureWidth * figureHeight; figure(Name = "Treatment Optimization Joint Angles", ... @@ -103,7 +103,8 @@ function plotTreatmentOptimizationJointAnglesMulti(experimentalFile, ... if subplotNumber==1 legendValues = "Experimental"; for j = 1 : numel(modelFiles) - legendValues(j+1) = strcat("Model ", num2str(j)); + splitFileName = split(modelFiles(j), ["/", "\"]); + legendValues(j+1) = sprintf("%s (%d)", splitFileName(1), j); end legend(legendValues) end diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoadsMulti.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoadsMulti.m index dc99e601b..a53fcbb3b 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoadsMulti.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoadsMulti.m @@ -43,7 +43,6 @@ function plotTreatmentOptimizationJointLoadsMulti(experimentalFile, ... if experimentalTime(1) ~= 0 experimentalTime = experimentalTime - experimentalTime(1); end -modelData = {}; for i=1:numel(modelFiles) modelStorage = Storage(modelFiles(i)); modelData{i} = storageToDoubleMatrix(modelStorage)'; @@ -53,7 +52,6 @@ function plotTreatmentOptimizationJointLoadsMulti(experimentalFile, ... % Spline experimental time to the same time points as the model. experimentalSpline = makeGcvSplineSet(experimentalTime, ... experimentalData, labels); -resampledExperimental = {}; for i = 1 : numel(modelFiles) resampledExperimental{i}= evaluateGcvSplines(experimentalSpline, ... labels, modelTime{i}); @@ -62,7 +60,7 @@ function plotTreatmentOptimizationJointLoadsMulti(experimentalFile, ... figureWidth = ceil(sqrt(numel(labels))); figureHeight = ceil(numel(labels)/figureWidth); elseif nargin < 4 - figureHeight = ceil(numel(labels)/figureWidth); + figureHeight = ceil(sqrt(numel(labels))); end figureSize = figureWidth * figureHeight; figure(Name = "Treatment Optimization Joint Loads", ... @@ -73,7 +71,7 @@ function plotTreatmentOptimizationJointLoadsMulti(experimentalFile, ... t = tiledlayout(figureHeight, figureWidth, ... TileSpacing='compact', Padding='compact'); xlabel(t, "Time [s]") -ylabel(t, "Joint Angle [deg]") +ylabel(t, "Joint Loads") for i=1:numel(labels) if i > figureSize * figureNumber figureNumber = figureNumber + 1; @@ -108,7 +106,8 @@ function plotTreatmentOptimizationJointLoadsMulti(experimentalFile, ... if subplotNumber==1 legendValues = "Experimental"; for j = 1 : numel(modelFiles) - legendValues(j+1) = strcat("Model ", num2str(j)); + splitFileName = split(modelFiles(j), ["/", "\"]); + legendValues(j+1) = sprintf("%s (%d)", splitFileName(1), j); end legend(legendValues) end diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivationsMulti.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivationsMulti.m index b7b6370ed..20cd1a5fd 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivationsMulti.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivationsMulti.m @@ -43,10 +43,10 @@ function plotTreatmentOptimizationMuscleActivationsMulti(experimentalFile, ... if experimentalTime(1) ~= 0 experimentalTime = experimentalTime - experimentalTime(1); end -modelData = {}; for i=1:numel(modelFiles) modelStorage = Storage(modelFiles(i)); modelData{i} = storageToDoubleMatrix(modelStorage)'; + modelLabels{i} = getStorageColumnNames(modelStorage); modelTime{i} = findTimeColumn(modelStorage); end @@ -62,7 +62,7 @@ function plotTreatmentOptimizationMuscleActivationsMulti(experimentalFile, ... figureWidth = ceil(sqrt(numel(labels))); figureHeight = ceil(numel(labels)/figureWidth); elseif nargin < 4 - figureHeight = ceil(numel(labels)/figureWidth); + figureHeight = ceil(sqrt(numel(labels))); end figureSize = figureWidth * figureHeight; figure(Name = "Treatment Optimization Muscle Activations", ... @@ -102,7 +102,8 @@ function plotTreatmentOptimizationMuscleActivationsMulti(experimentalFile, ... if subplotNumber==1 legendValues = "Experimental"; for j = 1 : numel(modelFiles) - legendValues(j+1) = strcat("Model ", num2str(j)); + splitFileName = split(modelFiles(j), ["/", "\"]); + legendValues(j+1) = sprintf("%s (%d)", splitFileName(1), j); end legend(legendValues) end From fa2b15bd20142d3e63df5e913e2de65056c6b48a Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Tue, 27 Feb 2024 17:08:10 -0600 Subject: [PATCH 340/365] Fix saving experimental midfoot superior position --- .../GroundContactPersonalization.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/GroundContactPersonalization/GroundContactPersonalization.m b/src/GroundContactPersonalization/GroundContactPersonalization.m index a3478b9dd..15ab377a6 100644 --- a/src/GroundContactPersonalization/GroundContactPersonalization.m +++ b/src/GroundContactPersonalization/GroundContactPersonalization.m @@ -59,6 +59,7 @@ replaceMomentsAboutMidfootSuperior(surface, inputs) replacedMoments = ... zeros(size(surface.experimentalGroundReactionMoments)); + momentCenter = zeros(size(surface.midfootSuperiorPosition)); for i = 1:size(replacedMoments, 2) newCenter = surface.midfootSuperiorPosition(:, i); newCenter(2) = inputs.restingSpringLength; @@ -66,6 +67,6 @@ surface.experimentalGroundReactionMoments(:, i) + ... cross((surface.electricalCenter(:, i) - newCenter), ... surface.experimentalGroundReactionForces(:, i)); - momentCenter = repmat(newCenter, 1, 101); + momentCenter(:, i) = newCenter; end end From 7c53575cf49030eca78997cc38c2fa92000482b7 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Tue, 27 Feb 2024 17:08:38 -0600 Subject: [PATCH 341/365] Update metabolic cost terms --- .../calcMetabolicCostPerDistanceGoalIntegrand.m | 17 +++++++++++++---- .../calcMetabolicCostPerTimeGoalIntegrand.m | 3 +-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMetabolicCostPerDistanceGoalIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMetabolicCostPerDistanceGoalIntegrand.m index 9a23ccc3e..a890024c6 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMetabolicCostPerDistanceGoalIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMetabolicCostPerDistanceGoalIntegrand.m @@ -1,7 +1,7 @@ % This function is part of the NMSM Pipeline, see file for full license. % % This function returns an integrand cost for metabolic cost normalized by -% time. +% distance. % % (struct, struct, struct, struct) -> (Array of double) @@ -33,8 +33,17 @@ "activations are required for metabolic cost calculations.") rawCost = calcMetabolicCost(values.time, values.positions, ... modeledValues.muscleActivations, inputs); -speed = calcMassCenterVelocity(values, inputs.model, ... +massCenterVelocity = calcMassCenterVelocity(values, inputs.model, ... inputs.coordinateNames); -cost = (rawCost - costTerm.errorCenter) / costTerm.maxAllowableError ... - / values.time(end) / speed; +massCenterVelocity = massCenterVelocity(1); +beltSpeed = 0; +if ~isempty(inputs.contactSurfaces) + for i = 1 : length(inputs.contactSurfaces) + beltSpeed = beltSpeed + inputs.contactSurfaces{i}.beltSpeed; + end + beltSpeed = beltSpeed / length(inputs.contactSurfaces); +end + +cost = (rawCost - costTerm.errorCenter) / values.time(end) ... + / (massCenterVelocity + beltSpeed); end diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMetabolicCostPerTimeGoalIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMetabolicCostPerTimeGoalIntegrand.m index 7b7e33afe..b2134fbc6 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMetabolicCostPerTimeGoalIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMetabolicCostPerTimeGoalIntegrand.m @@ -33,6 +33,5 @@ "activations are required for metabolic cost calculations.") rawCost = calcMetabolicCost(values.time, values.positions, ... modeledValues.muscleActivations, inputs); -cost = (rawCost - costTerm.errorCenter) / costTerm.maxAllowableError ... - / values.time(end); +cost = (rawCost - costTerm.errorCenter) / values.time(end); end From 9847a1b14ee54ad8ea4d9204e85eabcbb90f35b4 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Tue, 27 Feb 2024 21:14:01 -0600 Subject: [PATCH 342/365] Relative walking speed goal --- .../calcGoalRelativeWalkingSpeedDiscrete.m | 37 +++++++++++++++++++ ...alcMetabolicCostPerDistanceGoalIntegrand.m | 4 +- .../generateCostTermStruct.m | 7 ++++ 3 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 src/TreatmentOptimization/DiscreteTerms/calcGoalRelativeWalkingSpeedDiscrete.m diff --git a/src/TreatmentOptimization/DiscreteTerms/calcGoalRelativeWalkingSpeedDiscrete.m b/src/TreatmentOptimization/DiscreteTerms/calcGoalRelativeWalkingSpeedDiscrete.m new file mode 100644 index 000000000..6fa361888 --- /dev/null +++ b/src/TreatmentOptimization/DiscreteTerms/calcGoalRelativeWalkingSpeedDiscrete.m @@ -0,0 +1,37 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function returns an integrand cost for metabolic cost normalized by +% distance. +% +% (struct, struct, struct, struct) -> (Array of double) + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Spencer Williams % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function cost = calcGoalRelativeWalkingSpeedDiscrete(values, inputs, ... + costTerm) +massCenterVelocity = mean(calcMassCenterVelocity(values, inputs.model, ... + inputs.coordinateNames), 1); +rawCost = massCenterVelocity(1); + +cost = rawCost - costTerm.errorCenter; +end diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMetabolicCostPerDistanceGoalIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMetabolicCostPerDistanceGoalIntegrand.m index a890024c6..2b21b8778 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMetabolicCostPerDistanceGoalIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcMetabolicCostPerDistanceGoalIntegrand.m @@ -33,8 +33,8 @@ "activations are required for metabolic cost calculations.") rawCost = calcMetabolicCost(values.time, values.positions, ... modeledValues.muscleActivations, inputs); -massCenterVelocity = calcMassCenterVelocity(values, inputs.model, ... - inputs.coordinateNames); +massCenterVelocity = mean(calcMassCenterVelocity(values, inputs.model, ... + inputs.coordinateNames), 1); massCenterVelocity = massCenterVelocity(1); beltSpeed = 0; if ~isempty(inputs.contactSurfaces) diff --git a/src/TreatmentOptimization/generateCostTermStruct.m b/src/TreatmentOptimization/generateCostTermStruct.m index 4b29ccb4e..2a9805769 100644 --- a/src/TreatmentOptimization/generateCostTermStruct.m +++ b/src/TreatmentOptimization/generateCostTermStruct.m @@ -106,6 +106,7 @@ allowedTypes = [ ... "synergy_vector_tracking" ... "belt_speed_goal" ... + "relative_walking_speed_goal" ... "user_defined" ... ]; otherwise @@ -308,6 +309,12 @@ auxdata, ... costTerm); +costTermCalculations.relative_walking_speed_goal = @(values, modeledValues, auxdata, costTerm) ... + calcGoalRelativeWalkingSpeedDiscrete( ... + values, ... + auxdata, ... + costTerm); + costTermCalculations.user_defined = @(values, modeledValues, auxdata, costTerm) ... userDefinedFunction(values, ... modeledValues, ... From e5f76b704e343e33a8a224017d6ec9f4a1f3e793 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Tue, 27 Feb 2024 21:27:34 -0600 Subject: [PATCH 343/365] Update walking speed documentation --- .../DiscreteTerms/calcGoalRelativeWalkingSpeedDiscrete.m | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/TreatmentOptimization/DiscreteTerms/calcGoalRelativeWalkingSpeedDiscrete.m b/src/TreatmentOptimization/DiscreteTerms/calcGoalRelativeWalkingSpeedDiscrete.m index 6fa361888..1b82628fe 100644 --- a/src/TreatmentOptimization/DiscreteTerms/calcGoalRelativeWalkingSpeedDiscrete.m +++ b/src/TreatmentOptimization/DiscreteTerms/calcGoalRelativeWalkingSpeedDiscrete.m @@ -1,9 +1,11 @@ % This function is part of the NMSM Pipeline, see file for full license. % -% This function returns an integrand cost for metabolic cost normalized by -% distance. +% This function returns a discrete cost for matching a goal walking speed. +% This speed is defined relative to the ground. For example, with a subject +% walking at 0.6 m/s on a treadmill, a goal of 0.2 m/s will seek a solution +% with the subject walking at 0.8 m/s. % -% (struct, struct, struct, struct) -> (Array of double) +% (struct, struct, struct) -> (double) % ----------------------------------------------------------------------- % % The NMSM Pipeline is a toolkit for model personalization and treatment % From 5c4989e429300b57c9cef265de151cdc3beb955b Mon Sep 17 00:00:00 2001 From: RobSalati Date: Tue, 27 Feb 2024 23:33:48 -0600 Subject: [PATCH 344/365] Saving experimental moments about midfoot superior & organization Save files about midfoot superior. The moments were already found, they are just saved along with the rest of the data now. Organized plotting files and updated documentation. --- .../2vSkMDCswQeoYhO-OpwhdjQdDVod.xml | 6 - .../2vSkMDCswQeoYhO-OpwhdjQdDVop.xml | 2 - .../RhzEFSy2P8YbTqJISC7SXKsqTK8d.xml | 6 - .../RhzEFSy2P8YbTqJISC7SXKsqTK8p.xml | 2 - .../YTLZFTjx9_oUnvXPIvdx4ROS1eEd.xml | 6 - .../YTLZFTjx9_oUnvXPIvdx4ROS1eEp.xml | 2 - .../_j9Okdc3TksJWGa7ieAdzJtI0Ucd.xml | 6 - .../_j9Okdc3TksJWGa7ieAdzJtI0Ucp.xml | 2 - ...plotTreatmentOptimizationGroundReactions.m | 133 +++++++++-------- ...reatmentOptimizationGroundReactionsMulti.m | 136 ------------------ .../plotTreatmentOptimizationJointAngles.m | 110 ++++++++------ ...lotTreatmentOptimizationJointAnglesMulti.m | 126 ---------------- .../plotTreatmentOptimizationJointLoads.m | 127 ++++++++-------- ...plotTreatmentOptimizationJointLoadsMulti.m | 131 ----------------- ...otTreatmentOptimizationMuscleActivations.m | 94 ++++++------ ...atmentOptimizationMuscleActivationsMulti.m | 113 --------------- ...plotTreatmentOptimizationSynergyControls.m | 28 ++-- .../plotTreatmentOptimizationTorqueControls.m | 28 ++-- .../saveTreatmentOptimizationResults.m | 23 +++ 19 files changed, 310 insertions(+), 771 deletions(-) delete mode 100644 resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/2vSkMDCswQeoYhO-OpwhdjQdDVod.xml delete mode 100644 resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/2vSkMDCswQeoYhO-OpwhdjQdDVop.xml delete mode 100644 resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/RhzEFSy2P8YbTqJISC7SXKsqTK8d.xml delete mode 100644 resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/RhzEFSy2P8YbTqJISC7SXKsqTK8p.xml delete mode 100644 resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/YTLZFTjx9_oUnvXPIvdx4ROS1eEd.xml delete mode 100644 resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/YTLZFTjx9_oUnvXPIvdx4ROS1eEp.xml delete mode 100644 resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/_j9Okdc3TksJWGa7ieAdzJtI0Ucd.xml delete mode 100644 resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/_j9Okdc3TksJWGa7ieAdzJtI0Ucp.xml delete mode 100644 src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactionsMulti.m delete mode 100644 src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAnglesMulti.m delete mode 100644 src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoadsMulti.m delete mode 100644 src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivationsMulti.m diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/2vSkMDCswQeoYhO-OpwhdjQdDVod.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/2vSkMDCswQeoYhO-OpwhdjQdDVod.xml deleted file mode 100644 index 7a6326b99..000000000 --- a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/2vSkMDCswQeoYhO-OpwhdjQdDVod.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/2vSkMDCswQeoYhO-OpwhdjQdDVop.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/2vSkMDCswQeoYhO-OpwhdjQdDVop.xml deleted file mode 100644 index c1ca5bacf..000000000 --- a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/2vSkMDCswQeoYhO-OpwhdjQdDVop.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/RhzEFSy2P8YbTqJISC7SXKsqTK8d.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/RhzEFSy2P8YbTqJISC7SXKsqTK8d.xml deleted file mode 100644 index 7a6326b99..000000000 --- a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/RhzEFSy2P8YbTqJISC7SXKsqTK8d.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/RhzEFSy2P8YbTqJISC7SXKsqTK8p.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/RhzEFSy2P8YbTqJISC7SXKsqTK8p.xml deleted file mode 100644 index 16ec9eddc..000000000 --- a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/RhzEFSy2P8YbTqJISC7SXKsqTK8p.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/YTLZFTjx9_oUnvXPIvdx4ROS1eEd.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/YTLZFTjx9_oUnvXPIvdx4ROS1eEd.xml deleted file mode 100644 index 7a6326b99..000000000 --- a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/YTLZFTjx9_oUnvXPIvdx4ROS1eEd.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/YTLZFTjx9_oUnvXPIvdx4ROS1eEp.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/YTLZFTjx9_oUnvXPIvdx4ROS1eEp.xml deleted file mode 100644 index 7b837791f..000000000 --- a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/YTLZFTjx9_oUnvXPIvdx4ROS1eEp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/_j9Okdc3TksJWGa7ieAdzJtI0Ucd.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/_j9Okdc3TksJWGa7ieAdzJtI0Ucd.xml deleted file mode 100644 index 7a6326b99..000000000 --- a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/_j9Okdc3TksJWGa7ieAdzJtI0Ucd.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/_j9Okdc3TksJWGa7ieAdzJtI0Ucp.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/_j9Okdc3TksJWGa7ieAdzJtI0Ucp.xml deleted file mode 100644 index 627533055..000000000 --- a/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/_j9Okdc3TksJWGa7ieAdzJtI0Ucp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m index f8d87b48b..90e353359 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m @@ -1,15 +1,19 @@ % This function is part of the NMSM Pipeline, see file for full license. % -% This function reads two .sto files: 1) experimental ground reactions, and -% 2) model ground reactions, and plots them. There are 2 optional arguments -% for figure width and figure height. If no optional arguments are given, -% the figure size is automatically adjusted to fit all data on one plot. -% Giving just figure width imposes the width and fits the height to fit on -% one plot. Giving both arguments will impose both figure height and width, -% and create multiple plots as needed. +% This function reads .sto files for experimental and model ground +% reactions and plots them. There is an option to plot multiple model files +% by passing in a list of model file names. % -% (string) -> (None) -% Plot joint moment curves from file. +% There are 2 optional arguments for figure width and figure height. If no +% optional arguments are given, the figure size is automatically adjusted +% to fit all data on one plot. Giving just figure width and no figure +% height will set figure height to a default value and extra figures will +% be created as needed. If both figure width and figure height are given, +% the figure size will be fixed and extra figures will be created as +% needed. +% +% (string) (List of strings) (int), (int) -> (None) +% Plot experimental and model ground reactions from file % ----------------------------------------------------------------------- % % The NMSM Pipeline is a toolkit for model personalization and treatment % @@ -32,48 +36,54 @@ % implied. See the License for the specific language governing % % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function plotTreatmentOptimizationGroundReactions(experimentalGroundReactionFile, ... - modelGroundReactionFile, figureWidth, figureHeight) +function plotTreatmentOptimizationGroundReactions(experimentalFile, ... + modelFiles, figureWidth, figureHeight) import org.opensim.modeling.Storage -experimentalGRStorage = Storage(experimentalGroundReactionFile); -experimentalLabels = getStorageColumnNames(experimentalGRStorage); -experimentalGR = storageToDoubleMatrix(experimentalGRStorage)'; -experimentalTime = findTimeColumn(experimentalGRStorage); +experimentalStorage = Storage(experimentalFile); +experimentalLabels = getStorageColumnNames(experimentalStorage); +experimentalData = storageToDoubleMatrix(experimentalStorage)'; +experimentalTime = findTimeColumn(experimentalStorage); if experimentalTime(1) ~= 0 experimentalTime = experimentalTime - experimentalTime(1); end -modelGRStorage = Storage(modelGroundReactionFile); -modelLabels = getStorageColumnNames(modelGRStorage); -modelGR = storageToDoubleMatrix(modelGRStorage)'; -modelTime = findTimeColumn(modelGRStorage); -if modelTime(1) ~= 0 - modelTime = modelTime - modelTime(1); +for j = 1 : numel(modelFiles) + modelStorage = Storage(modelFiles(j)); + modelData{j} = storageToDoubleMatrix(modelStorage)'; + modelLabels{j} = getStorageColumnNames(modelStorage); + modelTime{j} = findTimeColumn(modelStorage); end -% Check both GR files are in the same order. +experimentalMomentIndices = contains(experimentalLabels, ["_m", "M"]); +experimentalForceIndices = contains(experimentalLabels, ["_v", "F"]); +experimentalIncludedIndices = experimentalMomentIndices | experimentalForceIndices; +experimentalData = experimentalData(:, experimentalIncludedIndices); +experimentalLabels = experimentalLabels(experimentalIncludedIndices); experimentalForcePlate1 = contains(experimentalLabels, "1"); -modelForcePlate1 = contains(modelLabels, "1"); -if experimentalForcePlate1 ~= modelForcePlate1 - temp = modelGR; - modelGR(:, ~experimentalForcePlate1) = modelGR(:, experimentalForcePlate1); - modelGR(:, experimentalForcePlate1) = temp(:, ~experimentalForcePlate1); +for j = 1 : numel(modelFiles) + modelMomentIndices = contains(modelLabels{j}, "_m"); + modelForceIndices = contains(modelLabels{j}, "_v"); + modelIncludedIndices = modelMomentIndices | modelForceIndices; + modelData{j} = modelData{j}(:, modelIncludedIndices); + modelLabels{j} = modelLabels{j}(modelIncludedIndices); + modelForcePlate1 = contains(modelLabels{j}, "1"); + if experimentalForcePlate1 ~= modelForcePlate1 + temp = modelData{j}; + modelData{j}(:, ~experimentalForcePlate1) = ... + modelData{j}(:, experimentalForcePlate1); + modelData{j}(:, experimentalForcePlate1) = ... + temp(:, ~experimentalForcePlate1); + end end -momentIndices = contains(experimentalLabels, "_m"); -forceIndices = contains(experimentalLabels, "_v"); -includedIndices = momentIndices | forceIndices; - -experimentalLabels = experimentalLabels(includedIndices); -modelGR = modelGR(:, includedIndices); -experimentalGR = experimentalGR(:, includedIndices); - -% Spline experimental time to the same time points as the model. -experimentalSpline = makeGcvSplineSet(experimentalTime, ... - experimentalGR, experimentalLabels); -resampledExperimental = evaluateGcvSplines(experimentalSpline, ... - experimentalLabels, modelTime); - +% Spline experimental time to the same time points as the model. +experimentalSpline = makeGcvSplineSet(experimentalTime, ... + experimentalData, experimentalLabels); +resampledExperimental = {}; +for i = 1 : numel(modelFiles) + resampledExperimental{i} = evaluateGcvSplines(experimentalSpline, ... + experimentalLabels, modelTime{i}); +end if nargin < 3 figureWidth = 3; end @@ -81,37 +91,48 @@ function plotTreatmentOptimizationGroundReactions(experimentalGroundReactionFile figureHeight = 2; end figureSize = figureWidth * figureHeight; - -figure(Name = "Treatment Optimization Ground Reactions") +figure(Name = "Treatment Optimization Ground Reactions", ... + Units='normalized', ... + Position=[0.05 0.05 0.9 0.85]) subplotNumber = 1; figureNumber = 1; t = tiledlayout(figureHeight, figureWidth, ... - TileSpacing='Compact', Padding='Compact'); + TileSpacing='compact', Padding='compact'); xlabel(t, "Time [s]") -ylabel(t, "Ground Reactions") +ylabel(t, "Ground Reaction") for i=1:numel(experimentalLabels) if i > figureSize * figureNumber figureNumber = figureNumber + 1; - figure(Name="Treatment Optimization Ground Reactions") + figure(Name="Treatment Optimization Ground Reactions", ... + Units='normalized', ... + Position=[0.05 0.05 0.9 0.85]) t = tiledlayout(figureHeight, figureWidth, ... TileSpacing='Compact', Padding='Compact'); xlabel(t, "Time [s]") - ylabel(t, "Ground Reactions") + ylabel(t, "Ground Reaction") subplotNumber = 1; end nexttile(subplotNumber); hold on - plot(experimentalTime, experimentalGR(:, i), LineWidth=2); - plot(modelTime, modelGR(:, i), LineWidth=2); + plot(experimentalTime, experimentalData(:, i), LineWidth=2); + for j = 1 : numel(modelFiles) + plot(modelTime{j}, modelData{j}(:, i), LineWidth=2); + end hold off - rmse = rms(resampledExperimental(:, i) - modelGR(:, i)); - title(sprintf("%s \n RMSE: %.4f", ... - strrep(experimentalLabels(i), "_", " "), rmse)) + titleString = [sprintf("%s", strrep(experimentalLabels(i), "_", " "))]; + for j = 1 : numel(modelFiles) + rmse = rms(resampledExperimental{j}(:, i) - modelData{j}(:, i)); + titleString(j+1) = sprintf("RMSE %d: %.4f", j, rmse); + end + title(titleString) if subplotNumber==1 - legend("Experimental", "Model") + legendValues = "Experimental"; + for j = 1 : numel(modelFiles) + splitFileName = split(modelFiles(j), ["/", "\"]); + legendValues(j+1) = sprintf("%s (%d)", splitFileName(1), j); + end + legend(legendValues) end xlim([0, experimentalTime(end)]) subplotNumber = subplotNumber + 1; -end -end - +end \ No newline at end of file diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactionsMulti.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactionsMulti.m deleted file mode 100644 index 9aaec4019..000000000 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactionsMulti.m +++ /dev/null @@ -1,136 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function reads two .sto files: 1) experimental angles, and 2) model -% angles, and plots them. There are 2 optional arguments for figure width -% and figure height. If no optional arguments are given, the figure size is -% automatically adjusted to fit all data on one plot. Giving just figure -% width imposes the width and fits the height to fit on one plot. Giving -% both arguments will impose both figure height and width, and create -% multiple plots as needed. -% -% (string) -> (None) -% Plot joint moment curves from file. - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Robert Salati % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % -function plotTreatmentOptimizationGroundReactionsMulti(experimentalFile, ... - modelFiles, figureWidth, figureHeight) - -import org.opensim.modeling.Storage -experimentalStorage = Storage(experimentalFile); -experimentalLabels = getStorageColumnNames(experimentalStorage); -experimentalData = storageToDoubleMatrix(experimentalStorage)'; -experimentalTime = findTimeColumn(experimentalStorage); -if experimentalTime(1) ~= 0 - experimentalTime = experimentalTime - experimentalTime(1); -end -for j = 1 : numel(modelFiles) - modelStorage = Storage(modelFiles(j)); - modelData{j} = storageToDoubleMatrix(modelStorage)'; - modelLabels{j} = getStorageColumnNames(modelStorage); - modelTime{j} = findTimeColumn(modelStorage); -end - -experimentalMomentIndices = contains(experimentalLabels, ["_m", "M"]); -experimentalForceIndices = contains(experimentalLabels, ["_v", "F"]); -experimentalIncludedIndices = experimentalMomentIndices | experimentalForceIndices; -experimentalData = experimentalData(:, experimentalIncludedIndices); -experimentalLabels = experimentalLabels(experimentalIncludedIndices); -experimentalForcePlate1 = contains(experimentalLabels, "1"); -for j = 1 : numel(modelFiles) - modelMomentIndices = contains(modelLabels{j}, "_m"); - modelForceIndices = contains(modelLabels{j}, "_v"); - modelIncludedIndices = modelMomentIndices | modelForceIndices; - modelData{j} = modelData{j}(:, modelIncludedIndices); - modelLabels{j} = modelLabels{j}(modelIncludedIndices); - modelForcePlate1 = contains(modelLabels{j}, "1"); - % modelData{j} = modelData{j}(:, ~modelForcePlate1); - if experimentalForcePlate1 ~= modelForcePlate1 - temp = modelData{j}; - modelData{j}(:, ~experimentalForcePlate1) = ... - modelData{j}(:, experimentalForcePlate1); - modelData{j}(:, experimentalForcePlate1) = ... - temp(:, ~experimentalForcePlate1); - end -end - -% Spline experimental time to the same time points as the model. -experimentalSpline = makeGcvSplineSet(experimentalTime, ... - experimentalData, experimentalLabels); -resampledExperimental = {}; -for i = 1 : numel(modelFiles) - resampledExperimental{i} = evaluateGcvSplines(experimentalSpline, ... - experimentalLabels, modelTime{i}); -end -if nargin < 3 - figureWidth = 3; -end -if nargin < 4 - figureHeight = 2; -end -figureSize = figureWidth * figureHeight; -figure(Name = "Treatment Optimization Ground Reactions", ... - Units='normalized', ... - Position=[0.05 0.05 0.9 0.85]) -subplotNumber = 1; -figureNumber = 1; -t = tiledlayout(figureHeight, figureWidth, ... - TileSpacing='compact', Padding='compact'); -xlabel(t, "Time [s]") -ylabel(t, "Ground Reaction") -title(t, "Force Plate 2") -for i=1:numel(experimentalLabels) - if i > figureSize * figureNumber - figureNumber = figureNumber + 1; - figure(Name="Treatment Optimization Ground Reactions", ... - Units='normalized', ... - Position=[0.05 0.05 0.9 0.85]) - t = tiledlayout(figureHeight, figureWidth, ... - TileSpacing='Compact', Padding='Compact'); - xlabel(t, "Time [s]") - ylabel(t, "Ground Reaction") - subplotNumber = 1; - end - nexttile(subplotNumber); - hold on - plot(experimentalTime, experimentalData(:, i), LineWidth=2); - for j = 1 : numel(modelFiles) - plot(modelTime{j}, modelData{j}(:, i), LineWidth=2); - end - hold off - titleString = [sprintf("%s", strrep(experimentalLabels(i), "_", " "))]; - for j = 1 : numel(modelFiles) - rmse = rms(resampledExperimental{j}(:, i) - modelData{j}(:, i)); - titleString(j+1) = sprintf("RMSE %d: %.4f", j, rmse); - end - title(titleString) - if subplotNumber==1 - legendValues = "Experimental"; - for j = 1 : numel(modelFiles) - splitFileName = split(modelFiles(j), ["/", "\"]); - legendValues(j+1) = sprintf("%s (%d)", splitFileName(1), j); - end - legend(legendValues) - end - xlim([0, experimentalTime(end)]) - subplotNumber = subplotNumber + 1; -end \ No newline at end of file diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m index 534175304..6ec632376 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m @@ -1,15 +1,19 @@ % This function is part of the NMSM Pipeline, see file for full license. -% -% This function reads two .sto files: 1) experimental angles, and 2) model -% angles, and plots them. There are 2 optional arguments for figure width -% and figure height. If no optional arguments are given, the figure size is -% automatically adjusted to fit all data on one plot. Giving just figure -% width imposes the width and fits the height to fit on one plot. Giving -% both arguments will impose both figure height and width, and create -% multiple plots as needed. % -% (string) -> (None) -% Plot joint moment curves from file. +% This function reads .sto files for experimental and model joint angles +% and plots them. There is an option to plot multiple model files by +% passing in a list of model file names. +% +% There are 2 optional arguments for figure width and figure height. If no +% optional arguments are given, the figure size is automatically adjusted +% to fit all data on one plot. Giving just figure width and no figure +% height will set figure height to a default value and extra figures will +% be created as needed. If both figure width and figure height are given, +% the figure size will be fixed and extra figures will be created as +% needed. +% +% (string) (List of strings) (int), (int) -> (None) +% Plot experimental and model joint angles from file % ----------------------------------------------------------------------- % % The NMSM Pipeline is a toolkit for model personalization and treatment % @@ -32,40 +36,40 @@ % implied. See the License for the specific language governing % % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function plotTreatmentOptimizationJointAngles(experimentalAnglesFile, ... - modelAnglesFiles, figureWidth, figureHeight) +function plotTreatmentOptimizationJointAngles(experimentalFile, ... + modelFiles, figureWidth, figureHeight) import org.opensim.modeling.Storage -experimentalAnglesStorage = Storage(experimentalAnglesFile); -labels = getStorageColumnNames(experimentalAnglesStorage); -experimentalAngles = storageToDoubleMatrix(experimentalAnglesStorage)'; -experimentalAngles = experimentalAngles .* 180/pi; -experimentalTime = findTimeColumn(experimentalAnglesStorage); +experimentalStorage = Storage(experimentalFile); +labels = getStorageColumnNames(experimentalStorage); +experimentalData = storageToDoubleMatrix(experimentalStorage)'; +experimentalData = experimentalData .* 180/pi; +experimentalTime = findTimeColumn(experimentalStorage); if experimentalTime(1) ~= 0 experimentalTime = experimentalTime - experimentalTime(1); end -modelAnglesStorage = Storage(modelAnglesFiles); -modelAngles = storageToDoubleMatrix(modelAnglesStorage)'; -modelAngles = modelAngles .* 180/pi; -modelTime = findTimeColumn(modelAnglesStorage); -if modelTime(1) ~= 0 - modelTime = modelTime - modelTime(1); +modelData = {}; +for i=1:numel(modelFiles) + modelStorage = Storage(modelFiles(i)); + modelData{i} = storageToDoubleMatrix(modelStorage)' .* 180/pi; + modelTime{i} = findTimeColumn(modelStorage); end -% Spline experimental time to the same time points as the model. -experimentalSpline = makeGcvSplineSet(experimentalTime, ... - experimentalAngles, labels); -resampledExperimental = evaluateGcvSplines(experimentalSpline, ... - labels, modelTime); - +% Spline experimental time to the same time points as the model. +experimentalSpline = makeGcvSplineSet(experimentalTime, ... + experimentalData, labels); +resampledExperimental = {}; +for i = 1 : numel(modelFiles) + resampledExperimental{i}= evaluateGcvSplines(experimentalSpline, ... + labels, modelTime{i}); +end if nargin < 3 figureWidth = ceil(sqrt(numel(labels))); figureHeight = ceil(numel(labels)/figureWidth); elseif nargin < 4 - figureHeight = ceil(numel(labels)/figureWidth); + figureHeight = ceil(sqrt(numel(labels))); end figureSize = figureWidth * figureHeight; - figure(Name = "Treatment Optimization Joint Angles", ... Units='normalized', ... Position=[0.05 0.05 0.9 0.85]) @@ -89,26 +93,38 @@ function plotTreatmentOptimizationJointAngles(experimentalAnglesFile, ... end nexttile(subplotNumber); hold on - plot(experimentalTime, experimentalAngles(:, i), LineWidth=2); - plot(modelTime, modelAngles(:, i), LineWidth=2); + plot(experimentalTime, experimentalData(:, i), LineWidth=2); + for j = 1 : numel(modelFiles) + plot(modelTime{j}, modelData{j}(:, i), LineWidth=2); + end hold off - rmse = rms(resampledExperimental(:, i) - modelAngles(:, i)); - title(sprintf("%s \n RMSE: %.4f", ... - strrep(labels(i), "_", " "), rmse)); - + titleString = [sprintf("%s", strrep(labels(i), "_", " "))]; + for j = 1 : numel(modelFiles) + rmse = rms(resampledExperimental{j}(:, i) - modelData{j}(:, i)); + titleString(j+1) = sprintf("RMSE %d: %.4f", j, rmse); + end + title(titleString) if subplotNumber==1 - legend("Experimental", "Model") + legendValues = "Experimental"; + for j = 1 : numel(modelFiles) + splitFileName = split(modelFiles(j), ["/", "\"]); + legendValues(j+1) = sprintf("%s (%d)", splitFileName(1), j); + end + legend(legendValues) end - xlim([0, experimentalTime(end)]) - maxAngle = max([experimentalAngles(:, i); modelAngles(:, i)],[], "all"); - minAngle = min([experimentalAngles(:, i); modelAngles(:, i)],[], "all"); - if maxAngle-minAngle < 10 - ylim([(maxAngle+minAngle)/2-10, (maxAngle+minAngle)/2+10]) + maxData = []; + minData = []; + for j = 1 : numel(modelFiles) + maxData(j) = max(modelData{j}(:, i), [], "all"); + minData(j) = min(modelData{j}(:, i), [], "all"); + end + maxData(j+1) = max(experimentalData(:, i), [], "all"); + minData(j+1) = min(experimentalData(:, i), [], "all"); + yLimitUpper = max(maxData); + yLimitLower = min(minData); + if yLimitUpper - yLimitLower < 10 + ylim([(yLimitUpper+yLimitLower)/2-10, (yLimitUpper+yLimitLower)/2+10]) end - - % if subplotNumber > figureSize-figureHeight - % xlabel("Time [s]") - % end subplotNumber = subplotNumber + 1; end \ No newline at end of file diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAnglesMulti.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAnglesMulti.m deleted file mode 100644 index c0d23fe53..000000000 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAnglesMulti.m +++ /dev/null @@ -1,126 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function reads two .sto files: 1) experimental angles, and 2) model -% angles, and plots them. There are 2 optional arguments for figure width -% and figure height. If no optional arguments are given, the figure size is -% automatically adjusted to fit all data on one plot. Giving just figure -% width imposes the width and fits the height to fit on one plot. Giving -% both arguments will impose both figure height and width, and create -% multiple plots as needed. -% -% (string) -> (None) -% Plot joint moment curves from file. - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Robert Salati % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % -function plotTreatmentOptimizationJointAnglesMulti(experimentalFile, ... - modelFiles, figureWidth, figureHeight) - -import org.opensim.modeling.Storage -experimentalStorage = Storage(experimentalFile); -labels = getStorageColumnNames(experimentalStorage); -experimentalData = storageToDoubleMatrix(experimentalStorage)'; -experimentalData = experimentalData .* 180/pi; -experimentalTime = findTimeColumn(experimentalStorage); -if experimentalTime(1) ~= 0 - experimentalTime = experimentalTime - experimentalTime(1); -end -modelData = {}; -for i=1:numel(modelFiles) - modelStorage = Storage(modelFiles(i)); - modelData{i} = storageToDoubleMatrix(modelStorage)' .* 180/pi; - modelTime{i} = findTimeColumn(modelStorage); -end - -% Spline experimental time to the same time points as the model. -experimentalSpline = makeGcvSplineSet(experimentalTime, ... - experimentalData, labels); -resampledExperimental = {}; -for i = 1 : numel(modelFiles) - resampledExperimental{i}= evaluateGcvSplines(experimentalSpline, ... - labels, modelTime{i}); -end -if nargin < 3 - figureWidth = ceil(sqrt(numel(labels))); - figureHeight = ceil(numel(labels)/figureWidth); -elseif nargin < 4 - figureHeight = ceil(sqrt(numel(labels))); -end -figureSize = figureWidth * figureHeight; -figure(Name = "Treatment Optimization Joint Angles", ... - Units='normalized', ... - Position=[0.05 0.05 0.9 0.85]) -subplotNumber = 1; -figureNumber = 1; -t = tiledlayout(figureHeight, figureWidth, ... - TileSpacing='compact', Padding='compact'); -xlabel(t, "Time [s]") -ylabel(t, "Joint Angle [deg]") -for i=1:numel(labels) - if i > figureSize * figureNumber - figureNumber = figureNumber + 1; - figure(Name="Treatment Optimization Joint Angles", ... - Units='normalized', ... - Position=[0.05 0.05 0.9 0.85]) - t = tiledlayout(figureHeight, figureWidth, ... - TileSpacing='Compact', Padding='Compact'); - xlabel(t, "Time [s]") - ylabel(t, "Joint Angle [deg]") - subplotNumber = 1; - end - nexttile(subplotNumber); - hold on - plot(experimentalTime, experimentalData(:, i), LineWidth=2); - for j = 1 : numel(modelFiles) - plot(modelTime{j}, modelData{j}(:, i), LineWidth=2); - end - hold off - titleString = [sprintf("%s", strrep(labels(i), "_", " "))]; - for j = 1 : numel(modelFiles) - rmse = rms(resampledExperimental{j}(:, i) - modelData{j}(:, i)); - titleString(j+1) = sprintf("RMSE %d: %.4f", j, rmse); - end - title(titleString) - if subplotNumber==1 - legendValues = "Experimental"; - for j = 1 : numel(modelFiles) - splitFileName = split(modelFiles(j), ["/", "\"]); - legendValues(j+1) = sprintf("%s (%d)", splitFileName(1), j); - end - legend(legendValues) - end - xlim([0, experimentalTime(end)]) - maxData = []; - minData = []; - for j = 1 : numel(modelFiles) - maxData(j) = max(modelData{j}(:, i), [], "all"); - minData(j) = min(modelData{j}(:, i), [], "all"); - end - maxData(j+1) = max(experimentalData(:, i), [], "all"); - minData(j+1) = min(experimentalData(:, i), [], "all"); - yLimitUpper = max(maxData); - yLimitLower = min(minData); - if yLimitUpper - yLimitLower < 10 - ylim([(yLimitUpper+yLimitLower)/2-10, (yLimitUpper+yLimitLower)/2+10]) - end - subplotNumber = subplotNumber + 1; -end \ No newline at end of file diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoads.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoads.m index ad852dec3..cc245ecec 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoads.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoads.m @@ -1,15 +1,19 @@ % This function is part of the NMSM Pipeline, see file for full license. % -% This function reads two .sto files: 1) experimental loads, and 2) model -% loads, and plots them. There are 2 optional arguments for figure width -% and figure height. If no optional arguments are given, the figure size is -% automatically adjusted to fit all data on one plot. Giving just figure -% width imposes the width and fits the height to fit on one plot. Giving -% both arguments will impose both figure height and width, and create -% multiple plots as needed. +% This function reads .sto files for experimental and model joint loads +% and plots them. There is an option to plot multiple model files by +% passing in a list of model file names. % -% (string) -> (None) -% Plot joint moment curves from file. +% There are 2 optional arguments for figure width and figure height. If no +% optional arguments are given, the figure size is automatically adjusted +% to fit all data on one plot. Giving just figure width and no figure +% height will set figure height to a default value and extra figures will +% be created as needed. If both figure width and figure height are given, +% the figure size will be fixed and extra figures will be created as +% needed. +% +% (string) (List of strings) (int), (int) -> (None) +% Plot experimental and model joint loads from file % ----------------------------------------------------------------------- % % The NMSM Pipeline is a toolkit for model personalization and treatment % @@ -32,47 +36,46 @@ % implied. See the License for the specific language governing % % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function plotTreatmentOptimizationJointLoads(experimentalMomentsFile, ... - modelMomentsFile, figureWidth, figureHeight) +function plotTreatmentOptimizationJointLoads(experimentalFile, ... + modelFiles, figureWidth, figureHeight) import org.opensim.modeling.Storage -experimentalLoadsStorage = Storage(experimentalMomentsFile); -labels = getStorageColumnNames(experimentalLoadsStorage); -experimentalLoads = storageToDoubleMatrix(experimentalLoadsStorage)'; -experimentalTime = findTimeColumn(experimentalLoadsStorage); +experimentalStorage = Storage(experimentalFile); +labels = getStorageColumnNames(experimentalStorage); +experimentalData = storageToDoubleMatrix(experimentalStorage)'; +experimentalTime = findTimeColumn(experimentalStorage); if experimentalTime(1) ~= 0 experimentalTime = experimentalTime - experimentalTime(1); end -modelLoadsStorage = Storage(modelMomentsFile); -modelLoads = storageToDoubleMatrix(modelLoadsStorage)'; -modelTime = findTimeColumn(modelLoadsStorage); -if modelTime(1) ~= 0 - modelTime = modelTime - modelTime(1); +for i=1:numel(modelFiles) + modelStorage = Storage(modelFiles(i)); + modelData{i} = storageToDoubleMatrix(modelStorage)'; + modelTime{i} = findTimeColumn(modelStorage); end -% Spline experimental time to the same time points as the model. -experimentalSpline = makeGcvSplineSet(experimentalTime, ... - experimentalLoads, labels); -resampledExperimental = evaluateGcvSplines(experimentalSpline, ... - labels, modelTime); - +% Spline experimental time to the same time points as the model. +experimentalSpline = makeGcvSplineSet(experimentalTime, ... + experimentalData, labels); +for i = 1 : numel(modelFiles) + resampledExperimental{i}= evaluateGcvSplines(experimentalSpline, ... + labels, modelTime{i}); +end if nargin < 3 figureWidth = ceil(sqrt(numel(labels))); figureHeight = ceil(numel(labels)/figureWidth); elseif nargin < 4 - figureHeight = ceil(numel(labels)/figureWidth); + figureHeight = ceil(sqrt(numel(labels))); end figureSize = figureWidth * figureHeight; - -figure(Name="Treatment Optimization Joint Loads", ... +figure(Name = "Treatment Optimization Joint Loads", ... Units='normalized', ... Position=[0.05 0.05 0.9 0.85]) subplotNumber = 1; figureNumber = 1; t = tiledlayout(figureHeight, figureWidth, ... - TileSpacing='Compact', Padding='Compact'); + TileSpacing='compact', Padding='compact'); xlabel(t, "Time [s]") -ylabel(t, "Joint Load") +ylabel(t, "Joint Loads") for i=1:numel(labels) if i > figureSize * figureNumber figureNumber = figureNumber + 1; @@ -82,47 +85,51 @@ function plotTreatmentOptimizationJointLoads(experimentalMomentsFile, ... t = tiledlayout(figureHeight, figureWidth, ... TileSpacing='Compact', Padding='Compact'); xlabel(t, "Time [s]") - ylabel(t, "Joint Load") + ylabel(t, "Joint Loads") subplotNumber = 1; end nexttile(subplotNumber); hold on - plot(experimentalTime, experimentalLoads(:, i), LineWidth=2); - plot(modelTime, modelLoads(:, i), LineWidth=2); + plot(experimentalTime, experimentalData(:, i), LineWidth=2); + for j = 1 : numel(modelFiles) + plot(modelTime{j}, modelData{j}(:, i), LineWidth=2); + end hold off - rmse = rms(resampledExperimental(:, i) - modelLoads(:, i)); - mae = mean(abs(resampledExperimental(:, i) - modelLoads(:, i))); - if contains(labels(i), "moment") - title(sprintf("%s \n RMSE: %.4f", ... - strcat(strrep(labels(i), "_", " "), " [Nm]"), rmse)) + titleString = [sprintf("%s [Nm]", strrep(labels(i), "_", " "))]; elseif contains(labels(i), "force") - title(sprintf("%s \n RMSE: %.4f", ... - strcat(strrep(labels(i), "_", " "), " [N]"), rmse)) + titleString = [sprintf("%s [N]", strrep(labels(i), "_", " "))]; else - title(sprintf("%s \n RMSE: %.4f", ... - strrep(labels(i), "_", " "), rmse)) + titleString = [sprintf("%s", strrep(labels(i), "_", " "))]; end - + for j = 1 : numel(modelFiles) + rmse = rms(resampledExperimental{j}(:, i) - modelData{j}(:, i)); + titleString(j+1) = sprintf("RMSE %d: %.4f", j, rmse); + end + title(titleString) if subplotNumber==1 - legend("Experimental", "Model") + legendValues = "Experimental"; + for j = 1 : numel(modelFiles) + splitFileName = split(modelFiles(j), ["/", "\"]); + legendValues(j+1) = sprintf("%s (%d)", splitFileName(1), j); + end + legend(legendValues) end - xlim([0, experimentalTime(end)]) - maxLoad = max([experimentalLoads(:, i); modelLoads(:, i)],[], "all"); - minLoad = min([experimentalLoads(:, i); modelLoads(:, i)],[], "all"); - if maxLoad-minLoad < 10 - ylim([(maxLoad+minLoad)/2-10, (maxLoad+minLoad)/2+10]) + maxData = []; + minData = []; + for j = 1 : numel(modelFiles) + maxData(j) = max(modelData{j}(1:end-1, i), [], "all"); + minData(j) = min(modelData{j}(1:end-1, i), [], "all"); + end + maxData(j+1) = max(experimentalData(1:end-1, i), [], "all"); + minData(j+1) = min(experimentalData(1:end-1, i), [], "all"); + yLimitUpper = max(maxData); + yLimitLower = min(minData); + if yLimitUpper - yLimitLower < 10 + ylim([(yLimitUpper+yLimitLower)/2-10, (yLimitUpper+yLimitLower)/2+10]) else - ylim([ ... - min([modelLoads(1:end-1, i); experimentalLoads(:, i)])-5, ... - max([modelLoads(1:end-1, i); experimentalLoads(:, i)])+5]) + ylim([yLimitLower, yLimitUpper]); end - - % if subplotNumber > figureSize-figureHeight - % xlabel("Time [s]") - % end subplotNumber = subplotNumber + 1; -end -end - +end \ No newline at end of file diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoadsMulti.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoadsMulti.m deleted file mode 100644 index a53fcbb3b..000000000 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoadsMulti.m +++ /dev/null @@ -1,131 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function reads two .sto files: 1) experimental angles, and 2) model -% angles, and plots them. There are 2 optional arguments for figure width -% and figure height. If no optional arguments are given, the figure size is -% automatically adjusted to fit all data on one plot. Giving just figure -% width imposes the width and fits the height to fit on one plot. Giving -% both arguments will impose both figure height and width, and create -% multiple plots as needed. -% -% (string) -> (None) -% Plot joint moment curves from file. - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Robert Salati % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % -function plotTreatmentOptimizationJointLoadsMulti(experimentalFile, ... - modelFiles, figureWidth, figureHeight) - -import org.opensim.modeling.Storage -experimentalStorage = Storage(experimentalFile); -labels = getStorageColumnNames(experimentalStorage); -experimentalData = storageToDoubleMatrix(experimentalStorage)'; -experimentalTime = findTimeColumn(experimentalStorage); -if experimentalTime(1) ~= 0 - experimentalTime = experimentalTime - experimentalTime(1); -end -for i=1:numel(modelFiles) - modelStorage = Storage(modelFiles(i)); - modelData{i} = storageToDoubleMatrix(modelStorage)'; - modelTime{i} = findTimeColumn(modelStorage); -end - -% Spline experimental time to the same time points as the model. -experimentalSpline = makeGcvSplineSet(experimentalTime, ... - experimentalData, labels); -for i = 1 : numel(modelFiles) - resampledExperimental{i}= evaluateGcvSplines(experimentalSpline, ... - labels, modelTime{i}); -end -if nargin < 3 - figureWidth = ceil(sqrt(numel(labels))); - figureHeight = ceil(numel(labels)/figureWidth); -elseif nargin < 4 - figureHeight = ceil(sqrt(numel(labels))); -end -figureSize = figureWidth * figureHeight; -figure(Name = "Treatment Optimization Joint Loads", ... - Units='normalized', ... - Position=[0.05 0.05 0.9 0.85]) -subplotNumber = 1; -figureNumber = 1; -t = tiledlayout(figureHeight, figureWidth, ... - TileSpacing='compact', Padding='compact'); -xlabel(t, "Time [s]") -ylabel(t, "Joint Loads") -for i=1:numel(labels) - if i > figureSize * figureNumber - figureNumber = figureNumber + 1; - figure(Name="Treatment Optimization Joint Loads", ... - Units='normalized', ... - Position=[0.05 0.05 0.9 0.85]) - t = tiledlayout(figureHeight, figureWidth, ... - TileSpacing='Compact', Padding='Compact'); - xlabel(t, "Time [s]") - ylabel(t, "Joint Loads") - subplotNumber = 1; - end - nexttile(subplotNumber); - hold on - plot(experimentalTime, experimentalData(:, i), LineWidth=2); - for j = 1 : numel(modelFiles) - plot(modelTime{j}, modelData{j}(:, i), LineWidth=2); - end - hold off - if contains(labels(i), "moment") - titleString = [sprintf("%s [Nm]", strrep(labels(i), "_", " "))]; - elseif contains(labels(i), "force") - titleString = [sprintf("%s [N]", strrep(labels(i), "_", " "))]; - else - titleString = [sprintf("%s", strrep(labels(i), "_", " "))]; - end - for j = 1 : numel(modelFiles) - rmse = rms(resampledExperimental{j}(:, i) - modelData{j}(:, i)); - titleString(j+1) = sprintf("RMSE %d: %.4f", j, rmse); - end - title(titleString) - if subplotNumber==1 - legendValues = "Experimental"; - for j = 1 : numel(modelFiles) - splitFileName = split(modelFiles(j), ["/", "\"]); - legendValues(j+1) = sprintf("%s (%d)", splitFileName(1), j); - end - legend(legendValues) - end - xlim([0, experimentalTime(end)]) - maxData = []; - minData = []; - for j = 1 : numel(modelFiles) - maxData(j) = max(modelData{j}(1:end-1, i), [], "all"); - minData(j) = min(modelData{j}(1:end-1, i), [], "all"); - end - maxData(j+1) = max(experimentalData(1:end-1, i), [], "all"); - minData(j+1) = min(experimentalData(1:end-1, i), [], "all"); - yLimitUpper = max(maxData); - yLimitLower = min(minData); - if yLimitUpper - yLimitLower < 10 - ylim([(yLimitUpper+yLimitLower)/2-10, (yLimitUpper+yLimitLower)/2+10]) - else - ylim([yLimitLower, yLimitUpper]); - end - subplotNumber = subplotNumber + 1; -end \ No newline at end of file diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivations.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivations.m index 0bae8564c..9ac2ee70f 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivations.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivations.m @@ -1,15 +1,19 @@ % This function is part of the NMSM Pipeline, see file for full license. % -% This function reads two .sto files: 1) experimental activations, and 2) -% model activations, and plots them. There are 2 optional arguments for -% figure width and figure height. If no optional arguments are given, the -% figure size is automatically adjusted to fit all data on one plot. Giving -% just figure width imposes the width and fits the height to fit on one -% plot. Giving both arguments will impose both figure height and width, and -% create multiple plots as needed. +% This function reads .sto files for experimental and model muscle +% activations and plots them. There is an option to plot multiple model +% files by passing in a list of model file names. % -% (string) -> (None) -% Plot joint moment curves from file. +% There are 2 optional arguments for figure width and figure height. If no +% optional arguments are given, the figure size is automatically adjusted +% to fit all data on one plot. Giving just figure width and no figure +% height will set figure height to a default value and extra figures will +% be created as needed. If both figure width and figure height are given, +% the figure size will be fixed and extra figures will be created as +% needed. +% +% (string) (List of strings) (int), (int) -> (None) +% Plot experimental and model activations from file % ----------------------------------------------------------------------- % % The NMSM Pipeline is a toolkit for model personalization and treatment % @@ -32,45 +36,46 @@ % implied. See the License for the specific language governing % % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function plotTreatmentOptimizationMuscleActivations(experimentalActivationsFile, ... - modelActivationsFile, figureWidth, figureHeight) +function plotTreatmentOptimizationMuscleActivations(experimentalFile, ... + modelFiles, figureWidth, figureHeight) import org.opensim.modeling.Storage -experimentalActivationsStorage = Storage(experimentalActivationsFile); -labels = getStorageColumnNames(experimentalActivationsStorage); -experimentalActivations = storageToDoubleMatrix(experimentalActivationsStorage)'; -experimentalTime = findTimeColumn(experimentalActivationsStorage); +experimentalStorage = Storage(experimentalFile); +labels = getStorageColumnNames(experimentalStorage); +experimentalData = storageToDoubleMatrix(experimentalStorage)'; +experimentalTime = findTimeColumn(experimentalStorage); if experimentalTime(1) ~= 0 experimentalTime = experimentalTime - experimentalTime(1); end -modelActivationsStorage = Storage(modelActivationsFile); -modelActivations = storageToDoubleMatrix(modelActivationsStorage)'; -modelTime = findTimeColumn(modelActivationsStorage); -if modelTime(1) ~= 0 - modelTime = modelTime - modelTime(1); +for i=1:numel(modelFiles) + modelStorage = Storage(modelFiles(i)); + modelData{i} = storageToDoubleMatrix(modelStorage)'; + modelLabels{i} = getStorageColumnNames(modelStorage); + modelTime{i} = findTimeColumn(modelStorage); end -% Spline experimental time to the same time points as the model. -experimentalSpline = makeGcvSplineSet(experimentalTime, ... - experimentalActivations, labels); -resampledExperimental = evaluateGcvSplines(experimentalSpline, ... - labels, modelTime); - +% Spline experimental time to the same time points as the model. +experimentalSpline = makeGcvSplineSet(experimentalTime, ... + experimentalData, labels); +resampledExperimental = {}; +for i = 1 : numel(modelFiles) + resampledExperimental{i}= evaluateGcvSplines(experimentalSpline, ... + labels, modelTime{i}); +end if nargin < 3 figureWidth = ceil(sqrt(numel(labels))); figureHeight = ceil(numel(labels)/figureWidth); elseif nargin < 4 - figureHeight = ceil(numel(labels)/figureWidth); + figureHeight = ceil(sqrt(numel(labels))); end figureSize = figureWidth * figureHeight; - -figure(Name="Treatment Optimization Muscle Activations", ... +figure(Name = "Treatment Optimization Muscle Activations", ... Units='normalized', ... Position=[0.05 0.05 0.9 0.85]) subplotNumber = 1; figureNumber = 1; t = tiledlayout(figureHeight, figureWidth, ... - TileSpacing='Compact', Padding='Compact'); + TileSpacing='compact', Padding='compact'); xlabel(t, "Time [s]") ylabel(t, "Muscle Activations") for i=1:numel(labels) @@ -87,17 +92,26 @@ function plotTreatmentOptimizationMuscleActivations(experimentalActivationsFile, end nexttile(subplotNumber); hold on - plot(experimentalTime, experimentalActivations(:, i), LineWidth=2) - plot(modelTime, modelActivations(:, i), LineWidth=2) + plot(experimentalTime, experimentalData(:, i), LineWidth=2); + for j = 1 : numel(modelFiles) + plot(modelTime{j}, modelData{j}(:, i), LineWidth=2); + end hold off - rmse = rms(resampledExperimental(:, i) - modelActivations(:, i)); - title(sprintf("%s \n RMSE: %.4f", ... - strrep(labels(i), "_", " "), rmse)); - if subplotNumber == 1 - legend("Experimental", "Model") + titleString = [sprintf("%s", strrep(labels(i), "_", " "))]; + for j = 1 : numel(modelFiles) + rmse = rms(resampledExperimental{j}(:, i) - modelData{j}(:, i)); + titleString(j+1) = sprintf("RMSE %d: %.4f", j, rmse); end - xlim([0, experimentalTime(end)]); - ylim([0 1]) + title(titleString) + if subplotNumber==1 + legendValues = "Experimental"; + for j = 1 : numel(modelFiles) + splitFileName = split(modelFiles(j), ["/", "\"]); + legendValues(j+1) = sprintf("%s (%d)", splitFileName(1), j); + end + legend(legendValues) + end + xlim([0, experimentalTime(end)]) + ylim([0, 1]) subplotNumber = subplotNumber + 1; -end end \ No newline at end of file diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivationsMulti.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivationsMulti.m deleted file mode 100644 index 20cd1a5fd..000000000 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivationsMulti.m +++ /dev/null @@ -1,113 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function reads two .sto files: 1) experimental angles, and 2) model -% angles, and plots them. There are 2 optional arguments for figure width -% and figure height. If no optional arguments are given, the figure size is -% automatically adjusted to fit all data on one plot. Giving just figure -% width imposes the width and fits the height to fit on one plot. Giving -% both arguments will impose both figure height and width, and create -% multiple plots as needed. -% -% (string) -> (None) -% Plot joint moment curves from file. - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Robert Salati % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % -function plotTreatmentOptimizationMuscleActivationsMulti(experimentalFile, ... - modelFiles, figureWidth, figureHeight) - -import org.opensim.modeling.Storage -experimentalStorage = Storage(experimentalFile); -labels = getStorageColumnNames(experimentalStorage); -experimentalData = storageToDoubleMatrix(experimentalStorage)'; -experimentalTime = findTimeColumn(experimentalStorage); -if experimentalTime(1) ~= 0 - experimentalTime = experimentalTime - experimentalTime(1); -end -for i=1:numel(modelFiles) - modelStorage = Storage(modelFiles(i)); - modelData{i} = storageToDoubleMatrix(modelStorage)'; - modelLabels{i} = getStorageColumnNames(modelStorage); - modelTime{i} = findTimeColumn(modelStorage); -end - -% Spline experimental time to the same time points as the model. -experimentalSpline = makeGcvSplineSet(experimentalTime, ... - experimentalData, labels); -resampledExperimental = {}; -for i = 1 : numel(modelFiles) - resampledExperimental{i}= evaluateGcvSplines(experimentalSpline, ... - labels, modelTime{i}); -end -if nargin < 3 - figureWidth = ceil(sqrt(numel(labels))); - figureHeight = ceil(numel(labels)/figureWidth); -elseif nargin < 4 - figureHeight = ceil(sqrt(numel(labels))); -end -figureSize = figureWidth * figureHeight; -figure(Name = "Treatment Optimization Muscle Activations", ... - Units='normalized', ... - Position=[0.05 0.05 0.9 0.85]) -subplotNumber = 1; -figureNumber = 1; -t = tiledlayout(figureHeight, figureWidth, ... - TileSpacing='compact', Padding='compact'); -xlabel(t, "Time [s]") -ylabel(t, "Muscle Activations") -for i=1:numel(labels) - if i > figureSize * figureNumber - figureNumber = figureNumber + 1; - figure(Name="Treatment Optimization Muscle Activations", ... - Units='normalized', ... - Position=[0.05 0.05 0.9 0.85]) - t = tiledlayout(figureHeight, figureWidth, ... - TileSpacing='Compact', Padding='Compact'); - xlabel(t, "Time [s]") - ylabel(t, "Muscle Activations") - subplotNumber = 1; - end - nexttile(subplotNumber); - hold on - plot(experimentalTime, experimentalData(:, i), LineWidth=2); - for j = 1 : numel(modelFiles) - plot(modelTime{j}, modelData{j}(:, i), LineWidth=2); - end - hold off - titleString = [sprintf("%s", strrep(labels(i), "_", " "))]; - for j = 1 : numel(modelFiles) - rmse = rms(resampledExperimental{j}(:, i) - modelData{j}(:, i)); - titleString(j+1) = sprintf("RMSE %d: %.4f", j, rmse); - end - title(titleString) - if subplotNumber==1 - legendValues = "Experimental"; - for j = 1 : numel(modelFiles) - splitFileName = split(modelFiles(j), ["/", "\"]); - legendValues(j+1) = sprintf("%s (%d)", splitFileName(1), j); - end - legend(legendValues) - end - xlim([0, experimentalTime(end)]) - ylim([0, 1]) - subplotNumber = subplotNumber + 1; -end \ No newline at end of file diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationSynergyControls.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationSynergyControls.m index 761deeef3..56d981250 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationSynergyControls.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationSynergyControls.m @@ -1,12 +1,15 @@ % This function is part of the NMSM Pipeline, see file for full license. % -% This function reads one .sto file for treatment optimization synergy -% controls and plots it. There are 2 optional arguments for figure width -% and figure height. If no optional arguments are given, the figure size is -% automatically adjusted to fit all data on one plot. Giving just figure -% width imposes the width and fits the height to fit on one plot. Giving -% both arguments will impose both figure height and width, and create -% multiple plots as needed. +% This function reads one .sto file for treatment optimization synergy +% controls and plots it. +% +% There are 2 optional arguments for figure width and figure height. If no +% optional arguments are given, the figure size is automatically adjusted +% to fit all data on one plot. Giving just figure width and no figure +% height will set figure height to a default value and extra figures will +% be created as needed. If both figure width and figure height are given, +% the figure size will be fixed and extra figures will be created as +% needed. % % (string) -> (None) % Plot joint moment curves from file. @@ -43,12 +46,11 @@ function plotTreatmentOptimizationSynergyControls(controlsFile, ... if time(1) ~= 0 time = time - time(1); end - -if nargin < 3 +if nargin < 2 figureWidth = ceil(sqrt(numel(labels))); figureHeight = ceil(numel(labels)/figureWidth); -elseif nargin < 4 - figureHeight = ceil(numel(labels)/figureWidth); +elseif nargin < 3 + figureHeight = ceil(sqrt(numel(labels))); end figureSize = figureWidth * figureHeight; figure(Name="Treatment Optimization Synergy Controls", ... @@ -78,10 +80,6 @@ function plotTreatmentOptimizationSynergyControls(controlsFile, ... hold off title(strrep(labels(i), "_", " ")); xlim([0, time(end)]) - ylim([0, 1]) - % if subplotNumber > figureSize-figureHeight - % xlabel("Time [s]") - % end subplotNumber = subplotNumber + 1; end end diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationTorqueControls.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationTorqueControls.m index 7c389456f..783b676d1 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationTorqueControls.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationTorqueControls.m @@ -1,12 +1,15 @@ % This function is part of the NMSM Pipeline, see file for full license. % -% This function reads one .sto file for treatment optimization torque -% controls and plots it. There are 2 optional arguments for figure width -% and figure height. If no optional arguments are given, the figure size is -% automatically adjusted to fit all data on one plot. Giving just figure -% width imposes the width and fits the height to fit on one plot. Giving -% both arguments will impose both figure height and width, and create -% multiple plots as needed. +% This function reads one .sto file for treatment optimization torque +% controls and plots it. +% +% There are 2 optional arguments for figure width and figure height. If no +% optional arguments are given, the figure size is automatically adjusted +% to fit all data on one plot. Giving just figure width and no figure +% height will set figure height to a default value and extra figures will +% be created as needed. If both figure width and figure height are given, +% the figure size will be fixed and extra figures will be created as +% needed. % % (string) -> (None) % Plot joint moment curves from file. @@ -43,15 +46,13 @@ function plotTreatmentOptimizationTorqueControls(controlsFile, ... if time(1) ~= 0 time = time - time(1); end - -if nargin < 3 +if nargin < 2 figureWidth = ceil(sqrt(numel(labels))); figureHeight = ceil(numel(labels)/figureWidth); -elseif nargin < 4 - figureHeight = ceil(numel(labels)/figureWidth); +elseif nargin < 3 + figureHeight = ceil(sqrt(numel(labels))); end figureSize = figureWidth * figureHeight; - figure(Name="Treatment Optimization Torque Controls", ... Units='normalized', ... Position=[0.05 0.05 0.9 0.85]) @@ -79,9 +80,6 @@ function plotTreatmentOptimizationTorqueControls(controlsFile, ... hold off title(strrep(labels(i), "_", " ")); xlim([0, time(end)]) - % if subplotNumber > figureSize-figureHeight - % xlabel("Time [s]") - % end subplotNumber = subplotNumber + 1; end end diff --git a/src/TreatmentOptimization/saveTreatmentOptimizationResults.m b/src/TreatmentOptimization/saveTreatmentOptimizationResults.m index f63b08010..07dca52a1 100644 --- a/src/TreatmentOptimization/saveTreatmentOptimizationResults.m +++ b/src/TreatmentOptimization/saveTreatmentOptimizationResults.m @@ -37,6 +37,7 @@ function saveTreatmentOptimizationResults(solution, inputs, values) saveInverseKinematicsResults(inputs, values, inputs.resultsDirectory); saveInverseDynamicsResults(solution, inputs, values, inputs.resultsDirectory); saveGroundReactionResults(solution, inputs, values, inputs.resultsDirectory); +saveExperimentalGroundReactions(inputs, inputs.resultsDirectory); stateLabels = inputs.statesCoordinateNames; for i = 1 : length(inputs.statesCoordinateNames) @@ -143,3 +144,25 @@ function saveGroundReactionResults(solution, inputs, values, outputDirectory) strcat(inputs.trialName, ".sto"))); end end + +function saveExperimentalGroundReactions(inputs, resultsDirectory) +if isempty(inputs.contactSurfaces) + return +end +columnLabels = strings(1, 9*numel(inputs.contactSurfaces)); +dataToSave = zeros(size(inputs.experimentalTime, 1), numel(columnLabels)); +for surface = 1 : numel(inputs.contactSurfaces) + contactSurface = inputs.contactSurfaces{surface}; + columnLabels((surface-1)*9+1:surface*9) = ... + [contactSurface.forceColumns', ... + contactSurface.momentColumns', ... + contactSurface.electricalCenterColumns']; + dataToSave(:, (surface-1)*9+1:surface*9) = ... + [contactSurface.experimentalGroundReactionForces, ... + contactSurface.experimentalGroundReactionMoments, ... + contactSurface.electricalCenter]; +end +writeToSto(columnLabels, inputs.experimentalTime, dataToSave, ... + fullfile(resultsDirectory, ... + strcat(inputs.trialName, "_replacedExperimentalGroundReactions.sto"))); +end \ No newline at end of file From 091784de99e710204c6bb259cd5f6100748afa1e Mon Sep 17 00:00:00 2001 From: RobSalati <142843461+RobSalati@users.noreply.github.com> Date: Wed, 28 Feb 2024 14:19:22 -0600 Subject: [PATCH 345/365] Changed legends to specify tracked data directory. --- .../Analysis/plotTreatmentOptimizationGroundReactions.m | 9 ++++++++- .../Analysis/plotTreatmentOptimizationJointAngles.m | 9 ++++++++- .../Analysis/plotTreatmentOptimizationJointLoads.m | 9 ++++++++- .../plotTreatmentOptimizationMuscleActivations.m | 9 ++++++++- 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m index 90e353359..bf59d4b96 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m @@ -126,7 +126,14 @@ function plotTreatmentOptimizationGroundReactions(experimentalFile, ... end title(titleString) if subplotNumber==1 - legendValues = "Experimental"; + splitFileName = split(experimentalFile, ["/", "\"]); + for k = 1 : numel(splitFileName) + if ~strcmp(splitFileName(k), "..") + legendValues = sprintf("%s (T)", ... + strrep(splitFileName(k), "_", " ")); + break + end + end for j = 1 : numel(modelFiles) splitFileName = split(modelFiles(j), ["/", "\"]); legendValues(j+1) = sprintf("%s (%d)", splitFileName(1), j); diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m index 6ec632376..51ce7b7c2 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m @@ -105,7 +105,14 @@ function plotTreatmentOptimizationJointAngles(experimentalFile, ... end title(titleString) if subplotNumber==1 - legendValues = "Experimental"; + splitFileName = split(experimentalFile, ["/", "\"]); + for k = 1 : numel(splitFileName) + if ~strcmp(splitFileName(k), "..") + legendValues = sprintf("%s (T)", ... + strrep(splitFileName(k), "_", " ")); + break + end + end for j = 1 : numel(modelFiles) splitFileName = split(modelFiles(j), ["/", "\"]); legendValues(j+1) = sprintf("%s (%d)", splitFileName(1), j); diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoads.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoads.m index cc245ecec..7ae1ec845 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoads.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoads.m @@ -108,7 +108,14 @@ function plotTreatmentOptimizationJointLoads(experimentalFile, ... end title(titleString) if subplotNumber==1 - legendValues = "Experimental"; + splitFileName = split(experimentalFile, ["/", "\"]); + for k = 1 : numel(splitFileName) + if ~strcmp(splitFileName(k), "..") + legendValues = sprintf("%s (T)", ... + strrep(splitFileName(k), "_", " ")); + break + end + end for j = 1 : numel(modelFiles) splitFileName = split(modelFiles(j), ["/", "\"]); legendValues(j+1) = sprintf("%s (%d)", splitFileName(1), j); diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivations.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivations.m index 9ac2ee70f..df776d5f5 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivations.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivations.m @@ -104,7 +104,14 @@ function plotTreatmentOptimizationMuscleActivations(experimentalFile, ... end title(titleString) if subplotNumber==1 - legendValues = "Experimental"; + splitFileName = split(experimentalFile, ["/", "\"]); + for k = 1 : numel(splitFileName) + if ~strcmp(splitFileName(k), "..") + legendValues = sprintf("%s (T)", ... + strrep(splitFileName(k), "_", " ")); + break + end + end for j = 1 : numel(modelFiles) splitFileName = split(modelFiles(j), ["/", "\"]); legendValues(j+1) = sprintf("%s (%d)", splitFileName(1), j); From 87fd2c2e286c6fdb02e863847d289db678fdfb88 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Fri, 1 Mar 2024 13:27:41 -0600 Subject: [PATCH 346/365] small fix relative walking speed goal --- .../DiscreteTerms/calcGoalRelativeWalkingSpeedDiscrete.m | 2 +- src/core/mex/setOsimStateBySingleFrame.m | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/TreatmentOptimization/DiscreteTerms/calcGoalRelativeWalkingSpeedDiscrete.m b/src/TreatmentOptimization/DiscreteTerms/calcGoalRelativeWalkingSpeedDiscrete.m index 1b82628fe..afafea6a9 100644 --- a/src/TreatmentOptimization/DiscreteTerms/calcGoalRelativeWalkingSpeedDiscrete.m +++ b/src/TreatmentOptimization/DiscreteTerms/calcGoalRelativeWalkingSpeedDiscrete.m @@ -31,7 +31,7 @@ function cost = calcGoalRelativeWalkingSpeedDiscrete(values, inputs, ... costTerm) -massCenterVelocity = mean(calcMassCenterVelocity(values, inputs.model, ... +massCenterVelocity = mean(calcMassCenterVelocity(values, inputs.mexModel, ... inputs.coordinateNames), 1); rawCost = massCenterVelocity(1); diff --git a/src/core/mex/setOsimStateBySingleFrame.m b/src/core/mex/setOsimStateBySingleFrame.m index 8b542af5e..17a7dc9b2 100644 --- a/src/core/mex/setOsimStateBySingleFrame.m +++ b/src/core/mex/setOsimStateBySingleFrame.m @@ -35,9 +35,9 @@ for k=1:size(coordinateNames,2) if ~osimModel.getCoordinateSet.get(coordinateNames{k}).get_locked osimModel.getCoordinateSet.get(coordinateNames{k}). ... - setValue(osimState, values.statePositions(indx, k)); + setValue(osimState, values.positions(indx, k)); osimModel.getCoordinateSet.get(coordinateNames{k}). ... - setSpeedValue(osimState, values.stateVelocities(indx, k)); + setSpeedValue(osimState, values.velocities(indx, k)); end end osimModel.realizeVelocity(osimState); From ecc15b8ca6be30833fba2cfa334778c500fe433d Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Sun, 3 Mar 2024 22:37:37 -0600 Subject: [PATCH 347/365] Remove old TO plots Co-Authored-By: Claire V. Hammond <61138239+cvhammond@users.noreply.github.com> --- .../DesignOptimization/DesignOptimizationTool.m | 1 - .../TrackingOptimization/TrackingOptimizationTool.m | 1 - .../VerificationOptimization/VerificationOptimizationTool.m | 1 - 3 files changed, 3 deletions(-) diff --git a/src/TreatmentOptimization/DesignOptimization/DesignOptimizationTool.m b/src/TreatmentOptimization/DesignOptimization/DesignOptimizationTool.m index c842d73e4..744dab4ab 100644 --- a/src/TreatmentOptimization/DesignOptimization/DesignOptimizationTool.m +++ b/src/TreatmentOptimization/DesignOptimization/DesignOptimizationTool.m @@ -38,6 +38,5 @@ function DesignOptimizationTool(settingsFileName) inputs = setupTorqueControls(inputs); inputs = makeTreatmentOptimizationInputs(inputs, params); [inputs, outputs] = solveOptimalControlProblem(inputs, params); -reportTreatmentOptimizationResults(outputs, inputs); saveDesignOptimizationResults(outputs, inputs); end diff --git a/src/TreatmentOptimization/TrackingOptimization/TrackingOptimizationTool.m b/src/TreatmentOptimization/TrackingOptimization/TrackingOptimizationTool.m index 2312d8a24..862ab3c89 100644 --- a/src/TreatmentOptimization/TrackingOptimization/TrackingOptimizationTool.m +++ b/src/TreatmentOptimization/TrackingOptimization/TrackingOptimizationTool.m @@ -36,6 +36,5 @@ function TrackingOptimizationTool(settingsFileName) %inputs = normalizeSynergyData(inputs); inputs = makeTreatmentOptimizationInputs(inputs, params); [inputs, outputs] = solveOptimalControlProblem(inputs, params); -reportTreatmentOptimizationResults(outputs, inputs); saveTrackingOptimizationResults(outputs, inputs); end diff --git a/src/TreatmentOptimization/VerificationOptimization/VerificationOptimizationTool.m b/src/TreatmentOptimization/VerificationOptimization/VerificationOptimizationTool.m index 68ab76b8e..c43e67280 100644 --- a/src/TreatmentOptimization/VerificationOptimization/VerificationOptimizationTool.m +++ b/src/TreatmentOptimization/VerificationOptimization/VerificationOptimizationTool.m @@ -38,6 +38,5 @@ function VerificationOptimizationTool(settingsFileName) inputs = setupTorqueControls(inputs); inputs = makeTreatmentOptimizationInputs(inputs, params); [inputs, outputs] = solveOptimalControlProblem(inputs, params); -reportTreatmentOptimizationResults(outputs, inputs); saveVerificationOptimizationResults(outputs, inputs); end From dec41c37a2407d3867d1a332f466490efc55df22 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Sun, 3 Mar 2024 22:55:44 -0600 Subject: [PATCH 348/365] Update saving time Co-Authored-By: Claire V. Hammond <61138239+cvhammond@users.noreply.github.com> --- .../saveTreatmentOptimizationResults.m | 48 +++++++++---------- ...erimentalTime.m => splineToEvenlySpaced.m} | 11 +++-- 2 files changed, 30 insertions(+), 29 deletions(-) rename src/TreatmentOptimization/{splineToExperimentalTime.m => splineToEvenlySpaced.m} (87%) diff --git a/src/TreatmentOptimization/saveTreatmentOptimizationResults.m b/src/TreatmentOptimization/saveTreatmentOptimizationResults.m index 07dca52a1..d6e5222af 100644 --- a/src/TreatmentOptimization/saveTreatmentOptimizationResults.m +++ b/src/TreatmentOptimization/saveTreatmentOptimizationResults.m @@ -46,13 +46,13 @@ function saveTreatmentOptimizationResults(solution, inputs, values) % for i = 1 : length(inputs.statesCoordinateNames) % stateLabels{end + 1} = strcat(inputs.statesCoordinateNames{i}, '_dudt'); % end -data = splineToExperimentalTime(values.time, [values.statePositions ... - values.stateVelocities], inputs.experimentalTime); -writeToSto(stateLabels, inputs.experimentalTime, data, ... +[time, data] = splineToEvenlySpaced(values.time, [values.statePositions ... + values.stateVelocities], length(inputs.experimentalTime)); +writeToSto(stateLabels, time, data, ... fullfile(inputs.resultsDirectory, strcat(inputs.trialName, "_states.sto"))); -accelerations = splineToExperimentalTime(values.time, ... - values.controlAccelerations, inputs.experimentalTime); -writeToSto(inputs.statesCoordinateNames, inputs.experimentalTime, accelerations, ... +[time, accelerations] = splineToEvenlySpaced(values.time, ... + values.controlAccelerations, length(inputs.experimentalTime)); +writeToSto(inputs.statesCoordinateNames, time, accelerations, ... fullfile(inputs.resultsDirectory, strcat(inputs.trialName, "_accelerations.sto"))); if strcmp(inputs.controllerType, 'synergy') controlLabels = {}; @@ -63,9 +63,9 @@ function saveTreatmentOptimizationResults(solution, inputs, values) "_", num2str(j))); end end - controls = splineToExperimentalTime( ... - values.time, values.controlSynergyActivations, inputs.experimentalTime); - writeToSto(controlLabels, inputs.experimentalTime, controls, ... + [time, controls] = splineToEvenlySpaced(values.time, ... + values.controlSynergyActivations, length(inputs.experimentalTime)); + writeToSto(controlLabels, time, controls, ... fullfile(inputs.resultsDirectory, ... strcat(inputs.trialName, "_synergyCommands.sto"))); end @@ -74,9 +74,9 @@ function saveTreatmentOptimizationResults(solution, inputs, values) for i = 1 : length(inputs.torqueControllerCoordinateNames) controlLabels{end + 1} = inputs.torqueControllerCoordinateNames{i}; end - controls = splineToExperimentalTime( ... - values.time, values.torqueControls, inputs.experimentalTime); - writeToSto(controlLabels, inputs.experimentalTime, controls, ... + [time, controls] = splineToEvenlySpaced(values.time, values.torqueControls, ... + length(inputs.experimentalTime)); + writeToSto(controlLabels, time, controls, ... fullfile(inputs.resultsDirectory, ... strcat(inputs.trialName, "_torqueControls.sto"))); end @@ -84,9 +84,9 @@ function saveTreatmentOptimizationResults(solution, inputs, values) writeToSto(inputs.muscleLabels, linspace(1, inputs.numSynergies, ... inputs.numSynergies), [values.synergyWeights], ... fullfile(inputs.resultsDirectory, "synergyWeights.sto")); - activations = splineToExperimentalTime(values.time, ... - solution.muscleActivations, inputs.experimentalTime); - writeToSto(inputs.muscleLabels, inputs.experimentalTime, ... + [time, activations] = splineToEvenlySpaced(values.time, ... + solution.muscleActivations, length(inputs.experimentalTime)); + writeToSto(inputs.muscleLabels, time, ... activations, fullfile(inputs.resultsDirectory, ... strcat(inputs.trialName, "_combinedActivations.sto"))); end @@ -97,9 +97,9 @@ function saveInverseKinematicsResults(inputs, values, outputDirectory) if ~exist(fullfile(outputDirectory, "IKData"), "dir") mkdir(fullfile(outputDirectory, "IKData")) end -positions = splineToExperimentalTime(values.time, ... - values.positions, inputs.experimentalTime); -writeToSto(inputs.coordinateNames, inputs.experimentalTime, positions, ... +[time, positions] = splineToEvenlySpaced(values.time, ... + values.positions, length(inputs.experimentalTime)); +writeToSto(inputs.coordinateNames, time, positions, ... fullfile(outputDirectory, "IKData", strcat(inputs.trialName, ".sto"))); end @@ -107,9 +107,9 @@ function saveInverseDynamicsResults(solution, inputs, values, outputDirectory) if ~exist(fullfile(outputDirectory, "IDData"), "dir") mkdir(fullfile(outputDirectory, "IDData")) end -moments = splineToExperimentalTime(values.time, ... - solution.inverseDynamicsMoments, inputs.experimentalTime); -writeToSto(inputs.inverseDynamicsMomentLabels, inputs.experimentalTime, ... +[time, moments] = splineToEvenlySpaced(values.time, ... + solution.inverseDynamicsMoments, length(inputs.experimentalTime)); +writeToSto(inputs.inverseDynamicsMomentLabels, time, ... moments, fullfile(outputDirectory, "IDData", ... strcat(inputs.trialName, ".sto"))); end @@ -137,9 +137,9 @@ function saveGroundReactionResults(solution, inputs, values, outputDirectory) if ~exist(fullfile(outputDirectory, "GRFData"), "dir") mkdir(fullfile(outputDirectory, "GRFData")) end - splinedGroundContactData = splineToExperimentalTime(values.time, ... - groundContactData, inputs.experimentalTime); - writeToSto(groundContactLabels, inputs.experimentalTime, splinedGroundContactData, ... + [time, splinedGroundContactData] = splineToEvenlySpaced(values.time, ... + groundContactData, length(inputs.experimentalTime)); + writeToSto(groundContactLabels, time, splinedGroundContactData, ... fullfile(inputs.resultsDirectory, "GRFData", ... strcat(inputs.trialName, ".sto"))); end diff --git a/src/TreatmentOptimization/splineToExperimentalTime.m b/src/TreatmentOptimization/splineToEvenlySpaced.m similarity index 87% rename from src/TreatmentOptimization/splineToExperimentalTime.m rename to src/TreatmentOptimization/splineToEvenlySpaced.m index 8d8c401c8..e904415af 100644 --- a/src/TreatmentOptimization/splineToExperimentalTime.m +++ b/src/TreatmentOptimization/splineToEvenlySpaced.m @@ -29,11 +29,12 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function newData = splineToExperimentalTime(time, data, ... - newTime) +function [newTime, newData] = splineToEvenlySpaced(time, data, ... + numPoints) gcvSplineSet = makeGcvSplineSet(time(1 : end - 1), ... - data(1 : end - 1, :), string([1:size(data, 2)])); -newData = evaluateGcvSplines( gcvSplineSet, string([1:size(data, 2)]), ... - newTime-newTime(1), 0); + data(1 : end - 1, :), string(1:size(data, 2))); +newTime = linspace(time(1), time(end), numPoints); +newData = evaluateGcvSplines( gcvSplineSet, string(1:size(data, 2)), ... + newTime, 0); end From a3ecfd168d2476393143a0138075e1eadfff1e88 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 3 Mar 2024 23:10:20 -0600 Subject: [PATCH 349/365] fix plots with different time arrays Co-Authored-By: Spencer Williams <54556034+SpencerTWilliams@users.noreply.github.com> --- .../Analysis/plotTreatmentOptimizationGroundReactions.m | 2 +- .../Analysis/plotTreatmentOptimizationJointAngles.m | 2 +- .../Analysis/plotTreatmentOptimizationJointLoads.m | 2 +- .../Analysis/plotTreatmentOptimizationMuscleActivations.m | 2 +- .../Analysis/plotTreatmentOptimizationSynergyControls.m | 2 +- .../Analysis/plotTreatmentOptimizationTorqueControls.m | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m index bf59d4b96..671c1c748 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m @@ -140,6 +140,6 @@ function plotTreatmentOptimizationGroundReactions(experimentalFile, ... end legend(legendValues) end - xlim([0, experimentalTime(end)]) + xlim("tight") subplotNumber = subplotNumber + 1; end \ No newline at end of file diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m index 51ce7b7c2..a68d66fc8 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m @@ -119,7 +119,7 @@ function plotTreatmentOptimizationJointAngles(experimentalFile, ... end legend(legendValues) end - xlim([0, experimentalTime(end)]) + xlim("tight") maxData = []; minData = []; for j = 1 : numel(modelFiles) diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoads.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoads.m index 7ae1ec845..20601b24a 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoads.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoads.m @@ -122,7 +122,7 @@ function plotTreatmentOptimizationJointLoads(experimentalFile, ... end legend(legendValues) end - xlim([0, experimentalTime(end)]) + xlim("tight") maxData = []; minData = []; for j = 1 : numel(modelFiles) diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivations.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivations.m index df776d5f5..1187b41fd 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivations.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivations.m @@ -118,7 +118,7 @@ function plotTreatmentOptimizationMuscleActivations(experimentalFile, ... end legend(legendValues) end - xlim([0, experimentalTime(end)]) + xlim("tight") ylim([0, 1]) subplotNumber = subplotNumber + 1; end \ No newline at end of file diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationSynergyControls.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationSynergyControls.m index 56d981250..4589fc6ca 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationSynergyControls.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationSynergyControls.m @@ -79,7 +79,7 @@ function plotTreatmentOptimizationSynergyControls(controlsFile, ... plot(time, controls(:, i), LineWidth=2); hold off title(strrep(labels(i), "_", " ")); - xlim([0, time(end)]) + xlim("tight") subplotNumber = subplotNumber + 1; end end diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationTorqueControls.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationTorqueControls.m index 783b676d1..264c8ca9c 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationTorqueControls.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationTorqueControls.m @@ -79,7 +79,7 @@ function plotTreatmentOptimizationTorqueControls(controlsFile, ... plot(time, controls(:, i), LineWidth=2); hold off title(strrep(labels(i), "_", " ")); - xlim([0, time(end)]) + xlim("tight") subplotNumber = subplotNumber + 1; end end From 144c9bc457d6e376065e3a13c7f6c599818ef87e Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Mon, 4 Mar 2024 21:22:23 -0600 Subject: [PATCH 350/365] Update SurrogateModelCreation.m Co-Authored-By: Spencer Williams <54556034+SpencerTWilliams@users.noreply.github.com> --- src/SurrogateModelCreation/SurrogateModelCreation.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SurrogateModelCreation/SurrogateModelCreation.m b/src/SurrogateModelCreation/SurrogateModelCreation.m index 82f0c8096..171c2a0f3 100644 --- a/src/SurrogateModelCreation/SurrogateModelCreation.m +++ b/src/SurrogateModelCreation/SurrogateModelCreation.m @@ -86,7 +86,7 @@ inputs.momentArms = reshape(permute(inputs.momentArms, [1 4 2 3]), [], ... length(inputs.surrogateModelCoordinateNames), length(inputs.muscleNames)); -if(isempty(inputs.resultsDirectory)) +if(~isfield(inputs, "resultsDirectory") || isempty(inputs.resultsDirectory)) inputs.resultsDirectory = pwd; end end From a9830ff627c8ff60555d83d58f173998dae22394 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Wed, 6 Mar 2024 19:19:23 -0600 Subject: [PATCH 351/365] Update to getGeometryPath() for 4.5 --- src/core/model/getMusclesFromCoordinates.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/model/getMusclesFromCoordinates.m b/src/core/model/getMusclesFromCoordinates.m index 5ef691104..29f46b2b6 100644 --- a/src/core/model/getMusclesFromCoordinates.m +++ b/src/core/model/getMusclesFromCoordinates.m @@ -45,7 +45,7 @@ function output = isMuscleInCoordinates(model, coordinates, muscle) if muscle.get_appliesForce() - path = muscle.get_GeometryPath().getPathPointSet(); + path = muscle.getGeometryPath().getPathPointSet(); for j = 0 : path.getSize() - 1 bodyCoordinates = getCoordinatesFromBodies(model, ... path.get(j).getBodyName().toCharArray()'); From 01db96b6b5834e98d4817b15493c22ac6af63215 Mon Sep 17 00:00:00 2001 From: RobSalati <142843461+RobSalati@users.noreply.github.com> Date: Wed, 6 Mar 2024 21:03:34 -0600 Subject: [PATCH 352/365] Small updates for MTP plots and mtp saving Changed MTP plots to include results directory in the figure name. Updated passive moment plotting to match other plots. Changed MTP saving to save actual time points instead of 1-101. --- .../Analysis/plotMtpHillTypeMuscleParams.m | 2 +- .../Analysis/plotMtpJointMoments.m | 8 ++++---- .../plotMtpMuscleExcitationsAndActivations.m | 4 ++-- .../Analysis/plotMtpNormalizedFiberLengths.m | 6 +++--- .../Analysis/plotMtpPassiveForceCurves.m | 16 ++++++++++------ .../Analysis/plotMtpPassiveMomentCurves.m | 7 ++----- .../Saving/saveMtpActivationAndExcitationData.m | 6 ++++++ .../Saving/saveMtpJointMomentData.m | 5 +++++ .../Saving/saveMtpPassiveForceData.m | 5 +++-- .../Saving/saveMtpPassiveMomentData.m | 10 ++++++---- .../saveMuscleTendonPersonalizationResults.m | 1 + .../Saving/writeMtpDataToSto.m | 5 +++-- 12 files changed, 46 insertions(+), 29 deletions(-) diff --git a/src/MuscleTendonPersonalization/Analysis/plotMtpHillTypeMuscleParams.m b/src/MuscleTendonPersonalization/Analysis/plotMtpHillTypeMuscleParams.m index e699edf9b..3d423b813 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotMtpHillTypeMuscleParams.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMtpHillTypeMuscleParams.m @@ -33,7 +33,7 @@ function plotMtpHillTypeMuscleParams(resultsDirectory) [muscleNames, params] = extractMtpDataFromSto( ... fullfile(analysisDirectory, "muscleModelParameters")); muscleNames = strrep(muscleNames, '_', ' '); -figure(Name = "Muscle Model Parameters", ... +figure(Name = strcat(resultsDirectory, " Muscle Model Parameters"), ... Units='normalized', ... Position=[0.05 0.05 0.9 0.85]) diff --git a/src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m b/src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m index 8a5d2d616..07ae559d0 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m @@ -63,7 +63,7 @@ function plotMtpJointMoments(resultsDirectory) min(meanMoments, [], "all"), ... min(meanMomentsSynx, [], "all")]); -figure(Name = "Joint Moments", ... +figure(Name = strcat(resultsDirectory, " Joint Moments"), ... Units='normalized', ... Position=[0.05 0.05 0.9 0.85]) time = 1:1:size(meanIdMoments,1); @@ -82,7 +82,7 @@ function plotMtpJointMoments(resultsDirectory) mae = mean(abs(meanMoments(:,i) - meanIdMoments(:,i))); title(sprintf("%s \n RMSE: %.4f, MAE: %.4f", ... jointLabels(i), rmse, mae), fontsize=12) - axis([0 size(meanIdMoments, 1) minMoment, maxMoment]) + axis([time(1) time(end) minMoment, maxMoment]) if i == 1 legend("Mean Moment No Synx", "Mean Inverse Dynamics Moment") ylabel("Joint Moment [Nm]") @@ -99,12 +99,12 @@ function plotMtpJointMoments(resultsDirectory) mae = mean(abs(meanMomentsSynx(:,i) - meanIdMoments(:,i))); title(sprintf("%s \n RMSE: %.4f, MAE: %.4f", ... jointLabels(i), rmse, mae), fontsize=12) - axis([0 size(meanIdMoments, 1) minMoment, maxMoment]) + axis([time(1) time(end) minMoment, maxMoment]) if i == 1 legend("Mean Moment Synx", "Mean Inverse Dynamics Moment") ylabel("Joint Moment [Nm]") end end - xlabel("Time Point") + xlabel("Time Points") end end \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/Analysis/plotMtpMuscleExcitationsAndActivations.m b/src/MuscleTendonPersonalization/Analysis/plotMtpMuscleExcitationsAndActivations.m index 11f6c4f0b..3b26717bc 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotMtpMuscleExcitationsAndActivations.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMtpMuscleExcitationsAndActivations.m @@ -63,7 +63,7 @@ function plotMtpMuscleExcitationsAndActivations(resultsDirectory) figureWidth = ceil(sqrt(numel(muscleNames))); figureHeight = ceil(numel(muscleNames)/figureWidth); -figure(Name = "Muscle Excitations and Activations", ... +figure(Name = strcat(resultsDirectory, " Muscle Excitations and Activations"), ... Units='normalized', ... Position=[0.05 0.05 0.9 0.85]) t = tiledlayout(figureHeight, figureWidth, ... @@ -82,7 +82,7 @@ function plotMtpMuscleExcitationsAndActivations(resultsDirectory) plotMeanAndStd(meanActivations(:,i), stdActivations(:,i), time, 'r-'); end set(gca, fontsize=11) - axis([1 size(meanExcitations, 1) 0 1]) + axis([time(1) time(end) 0 1]) if (max(meanExcitations(:, i)) == 0) title(strcat(muscleNames(i), " *"), FontSize=12); else diff --git a/src/MuscleTendonPersonalization/Analysis/plotMtpNormalizedFiberLengths.m b/src/MuscleTendonPersonalization/Analysis/plotMtpNormalizedFiberLengths.m index 22a35ec20..b89c10d94 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotMtpNormalizedFiberLengths.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMtpNormalizedFiberLengths.m @@ -41,7 +41,7 @@ function plotMtpNormalizedFiberLengths(resultsDirectory) figureWidth = ceil(sqrt(numel(muscleNames))); figureHeight = ceil(numel(muscleNames)/figureWidth); -figure(Name = "Normalized Fiber Lengths", ... +figure(Name = strcat(resultsDirectory, " Normalized Fiber Lengths"), ... Units='normalized', ... Position=[0.05 0.05 0.9 0.85]) t = tiledlayout(figureHeight, figureWidth, ... @@ -55,13 +55,13 @@ function plotMtpNormalizedFiberLengths(resultsDirectory) plot(time, passiveLower, 'r--', LineWidth=2); hold off set(gca, fontsize=11) - axis([1 size(meanFiberLengths, 1) 0 1.5]) + axis([time(1) time(end) 0 1.5]) title(muscleNames(i), FontSize=12); if mod(i,figureWidth) == 1 ylabel(textwrap("Normalized Fiber Length",10), FontSize=12) end if i>numel(muscleNames)-figureWidth - xlabel("Time Points", FontSize=12) + xlabel("Time Points") end end diff --git a/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveForceCurves.m b/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveForceCurves.m index 8a90d2552..43b2f62b8 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveForceCurves.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveForceCurves.m @@ -40,22 +40,26 @@ function plotMtpPassiveForceCurves(resultsDirectory) maxForce = max(meanModelForce,[], 'all'); numWindows = ceil(sqrt(numel(muscleNames))); -figure(Name = "Passive Force Curves", ... +figureWidth = ceil(sqrt(numel(muscleNames))); +figureHeight = ceil(numel(muscleNames)/figureWidth); +figure(Name = strcat(resultsDirectory, " Passive Force Curves"), ... Units='normalized', ... - Position=[0.1 0.1 0.8 0.8]) + Position=[0.05 0.05 0.9 0.85]) +t = tiledlayout(figureHeight, figureWidth, ... + TileSpacing='Compact', Padding='Compact'); time = 1:1:size(meanModelForce,1); for i = 1:numel(muscleNames) - subplot(numWindows, numWindows, i) + nexttile(i) hold on plotMeanAndStd(meanModelForce(:,i), stdModelForce(:,i), time, 'b-'); hold off set(gca, fontsize=11) - axis([1 numel(time) 0 maxForce]) + axis([time(1) time(end) 0 maxForce]) title(muscleNames(i), FontSize=12); - if mod(i,numWindows) == 1 + if mod(i,figureWidth) == 1 ylabel("Force [N]") end - if i>numel(muscleNames)-numWindows + if i>numel(muscleNames)-figureWidth xlabel("Time Points") end end diff --git a/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveMomentCurves.m b/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveMomentCurves.m index f29c75dfe..fa58eea62 100644 --- a/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveMomentCurves.m +++ b/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveMomentCurves.m @@ -49,7 +49,7 @@ function plotMtpPassiveMomentCurves(resultsDirectory) figureWidth = ceil(sqrt(numel(momentNames))); figureHeight = ceil(numel(momentNames)/figureWidth); -figure(Name = "Passive Moment Curves", ... +figure(Name = strcat(resultsDirectory, " Passive Moment Curves"), ... Units='normalized', ... Position=[0.05 0.05 0.9 0.85]) t = tiledlayout(figureHeight, figureWidth, ... @@ -66,16 +66,13 @@ function plotMtpPassiveMomentCurves(resultsDirectory) rmse = rms(passiveMomentsExperimental(:,i) - passiveMomentsModel(:,i)); title(sprintf("%s \n RMSE: %.4f", ... momentNames(i), rmse), FontSize=12) - axis([1 size(meanMomentsModel, 1) minMoment maxMoment]) + axis([time(1) time(end) minMoment maxMoment]) if i == 1 legend("Experimental", "Model") end if mod(i,figureWidth) == 1 ylabel("Moment [Nm]") end - if i>numel(momentNames)-figureWidth - xlabel("Time Points") - end end end diff --git a/src/MuscleTendonPersonalization/Saving/saveMtpActivationAndExcitationData.m b/src/MuscleTendonPersonalization/Saving/saveMtpActivationAndExcitationData.m index 84f0dbdc0..4bb6a4288 100644 --- a/src/MuscleTendonPersonalization/Saving/saveMtpActivationAndExcitationData.m +++ b/src/MuscleTendonPersonalization/Saving/saveMtpActivationAndExcitationData.m @@ -33,30 +33,36 @@ function saveMtpActivationAndExcitationData(mtpInputs, resultsStruct, ... resultsDirectory) writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... resultsStruct.results.muscleExcitations, ... + resultsStruct.results.time, ... fullfile(resultsDirectory, "muscleExcitations"), ... "_muscleExcitations.sto") writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... resultsStruct.results.muscleActivations, ... + resultsStruct.results.time, ... fullfile(resultsDirectory, "muscleActivations"), ... "_muscleActivations.sto") if isfield(mtpInputs, "synergyExtrapolation") writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... resultsStruct.resultsSynx.muscleExcitations, ... + resultsStruct.resultsSynx.time, ... fullfile(resultsDirectory, "muscleExcitationsSynx"), ... "_muscleExcitationsSynx.sto") writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... resultsStruct.resultsSynx.muscleActivations, ... + resultsStruct.resultsSynx.time, ... fullfile(resultsDirectory, "muscleActivationsSynx"), ... "_muscleActivationsSynx.sto") writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... resultsStruct.resultsSynx.muscleActivations, ... + resultsStruct.resultsSynx.time, ... fullfile(resultsDirectory, "..", "muscleActivations"), ... "_muscleActivations.sto") else writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... resultsStruct.results.muscleActivations, ... + resultsStruct.results.time, ... fullfile(resultsDirectory, "..", "muscleActivations"), ... "_muscleActivations.sto") end diff --git a/src/MuscleTendonPersonalization/Saving/saveMtpJointMomentData.m b/src/MuscleTendonPersonalization/Saving/saveMtpJointMomentData.m index 97e1525b0..166518b78 100644 --- a/src/MuscleTendonPersonalization/Saving/saveMtpJointMomentData.m +++ b/src/MuscleTendonPersonalization/Saving/saveMtpJointMomentData.m @@ -33,27 +33,32 @@ function saveMtpJointMomentData(mtpInputs, resultsStruct, resultsDirectory) writeMtpDataToSto(mtpInputs.coordinateNames, mtpInputs.prefixes, ... resultsStruct.results.muscleJointMoments, ... + resultsStruct.results.time, ... fullfile(resultsDirectory, "\modelJointMoments"), ... "_modelJointMoments.sto") writeMtpDataToSto(mtpInputs.coordinateNames, mtpInputs.prefixes, ... mtpInputs.inverseDynamicsMoments, ... + mtpInputs.time, ... fullfile(resultsDirectory, "\inverseDynamicsJointMoments"), ... "_inverseDynamicsJointMoments.sto") if isfield(mtpInputs, "synergyExtrapolation") writeMtpDataToSto(mtpInputs.coordinateNames, mtpInputs.prefixes, ... resultsStruct.resultsSynx.muscleJointMoments, ... + resultsStruct.resultsSynx.time, ... fullfile(resultsDirectory, "\modelJointMomentsSynx"), ... "_modelJointMomentsSynx.sto") writeMtpDataToSto(mtpInputs.coordinateNames, mtpInputs.prefixes, ... resultsStruct.resultsSynx.muscleJointMoments, ... + resultsStruct.resultsSynx.time, ... fullfile(resultsDirectory, "..", "modelMoments"), ... "_modelMoments.sto"); else writeMtpDataToSto(mtpInputs.coordinateNames, mtpInputs.prefixes, ... resultsStruct.results.muscleJointMoments, ... + resultsStruct.results.time, ... fullfile(resultsDirectory, "..", "modelMoments"), ... "_modelMoments.sto"); end diff --git a/src/MuscleTendonPersonalization/Saving/saveMtpPassiveForceData.m b/src/MuscleTendonPersonalization/Saving/saveMtpPassiveForceData.m index c7b025d4b..f7d97c6a1 100644 --- a/src/MuscleTendonPersonalization/Saving/saveMtpPassiveForceData.m +++ b/src/MuscleTendonPersonalization/Saving/saveMtpPassiveForceData.m @@ -33,6 +33,7 @@ function saveMtpPassiveForceData(mtpInputs, resultsStruct, ... resultsDirectory) writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... - resultsStruct.results.passiveForce, strcat(resultsDirectory, ... - "\passiveForcesModel"), "_passiveForcesModel.sto"); + resultsStruct.results.passiveForce, resultsStruct.results.time, ... + strcat(resultsDirectory, "\passiveForcesModel"), ... + "_passiveForcesModel.sto"); end diff --git a/src/MuscleTendonPersonalization/Saving/saveMtpPassiveMomentData.m b/src/MuscleTendonPersonalization/Saving/saveMtpPassiveMomentData.m index a77a6e033..afaa65c52 100644 --- a/src/MuscleTendonPersonalization/Saving/saveMtpPassiveMomentData.m +++ b/src/MuscleTendonPersonalization/Saving/saveMtpPassiveMomentData.m @@ -44,9 +44,11 @@ function saveMtpPassiveMomentData(precalInputs, modeledValues, resultsDirectory) reshape(experimentalPassiveMoments, [dataLength, numberOfMoments, 1]), ... [3 2 1]); writeMtpDataToSto(precalInputs.passivePrefixes, precalInputs.prefixes, ... - experimentalPassiveMoments, fullfile(resultsDirectory, ... - "passiveJointMomentsExperimental"), "_passiveJointMomentsExperimental.sto") + experimentalPassiveMoments, 1:1:size(experimentalPassiveMoments, 3), ... + fullfile(resultsDirectory, "passiveJointMomentsExperimental"), ... + "_passiveJointMomentsExperimental.sto") writeMtpDataToSto(precalInputs.passivePrefixes, precalInputs.prefixes, ... - modelPassiveMoments, fullfile(resultsDirectory, ... - "passiveJointMomentsModeled"), "_passiveJointMomentsModeled.sto") + modelPassiveMoments, 1:1:size(modelPassiveMoments, 3), ... + fullfile(resultsDirectory, "passiveJointMomentsModeled"), ... + "_passiveJointMomentsModeled.sto") end \ No newline at end of file diff --git a/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m b/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m index 12e4b9efd..8a7ef12f3 100644 --- a/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m +++ b/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m @@ -49,6 +49,7 @@ function saveMuscleTendonPersonalizationResults(mtpInputs, finalValues, ... saveMtpActivationAndExcitationData(mtpInputs, resultsStruct, analysisDirectory); writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ... resultsStruct.results.normalizedFiberLength, ... + resultsStruct.results.time, ... fullfile(analysisDirectory, "normalizedFiberLengths"), ... "_normalizedFiberLengths.sto") saveMtpJointMomentData(mtpInputs, resultsStruct, analysisDirectory); diff --git a/src/MuscleTendonPersonalization/Saving/writeMtpDataToSto.m b/src/MuscleTendonPersonalization/Saving/writeMtpDataToSto.m index 5dde5c2fc..513c8fd84 100644 --- a/src/MuscleTendonPersonalization/Saving/writeMtpDataToSto.m +++ b/src/MuscleTendonPersonalization/Saving/writeMtpDataToSto.m @@ -29,12 +29,13 @@ % implied. See the License for the specific language governing % % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function writeMtpDataToSto(columnLabels, taskNames, data, directory, fileName) +function writeMtpDataToSto(columnLabels, taskNames, data, time, ... + directory, fileName) if ~exist(directory, "dir") mkdir(directory); end for i = 1 : size(data,1) - writeToSto(columnLabels, 1:1:length(data(i,:,:)), ... + writeToSto(columnLabels, time(i, :), ... permute(data(i,:,:), [3 2 1]), ... strcat(directory, "\", taskNames(i), fileName)) end From 60598a9dadc57f1c02338e03afdb62c7e0d0af63 Mon Sep 17 00:00:00 2001 From: stwilliams333 Date: Wed, 6 Mar 2024 21:48:18 -0600 Subject: [PATCH 353/365] Allow surrogate.m script to work --- src/SurrogateModelCreation/SurrogateModelCreation.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SurrogateModelCreation/SurrogateModelCreation.m b/src/SurrogateModelCreation/SurrogateModelCreation.m index 82f0c8096..095022ada 100644 --- a/src/SurrogateModelCreation/SurrogateModelCreation.m +++ b/src/SurrogateModelCreation/SurrogateModelCreation.m @@ -86,7 +86,7 @@ inputs.momentArms = reshape(permute(inputs.momentArms, [1 4 2 3]), [], ... length(inputs.surrogateModelCoordinateNames), length(inputs.muscleNames)); -if(isempty(inputs.resultsDirectory)) +if(~isfield(inputs, 'resultsDirectory') || isempty(inputs.resultsDirectory)) inputs.resultsDirectory = pwd; end end From 6c7209ae9947ea43b14dce0e699523b5d3270bc0 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Thu, 7 Mar 2024 21:26:32 -0600 Subject: [PATCH 354/365] add metabolic cost and relative speed goal --- .../calcGoalRelativeWalkingSpeedDiscrete.m | 11 +- ...iveMetabolicCostPerDistanceGoalDiscrete.m} | 23 ++- ...elativeMetabolicCostPerTimeGoalDiscrete.m} | 16 +- .../setupGpopsInitialGuess.m | 3 + .../calcSynergyBasedModeledValues.m | 5 +- .../calcTorqueBasedModeledValues.m | 26 ++- src/TreatmentOptimization/checkInitialGuess.m | 13 +- .../disableModelMuscles.m | 7 +- .../generateCostTermStruct.m | 13 +- .../gpops/computeGpopsContinuousFunction.m | 7 +- .../gpops/computeGpopsEndpointFunction.m | 18 +- .../gpops/convertToGpopsInputs.m | 10 +- src/TreatmentOptimization/modifyModelForces.m | 11 + .../solveOptimalControlProblem.m | 3 +- src/core/mex/compileMex.m | 2 +- .../initializeMexOrMatlabParallelFunctions.m | 2 +- src/core/mex/inverseDynamics.m | 27 ++- ...nverseDynamicsWithExtraCalcsMexWindows.cpp | 195 ++++++++++++++++++ ...rseDynamicsWithExtraCalcsMexWindows.mexw64 | Bin 0 -> 37888 bytes .../parse/parseTreatmentOptimizationInputs.m | 21 +- 20 files changed, 335 insertions(+), 78 deletions(-) rename src/TreatmentOptimization/IntegrandTerms/{calcMetabolicCostPerDistanceGoalIntegrand.m => calcRelativeMetabolicCostPerDistanceGoalDiscrete.m} (74%) rename src/TreatmentOptimization/IntegrandTerms/{calcMetabolicCostPerTimeGoalIntegrand.m => calcRelativeMetabolicCostPerTimeGoalDiscrete.m} (75%) create mode 100644 src/core/mex/inverseDynamicsWithExtraCalcsMexWindows.cpp create mode 100644 src/core/mex/inverseDynamicsWithExtraCalcsMexWindows.mexw64 diff --git a/src/TreatmentOptimization/DiscreteTerms/calcGoalRelativeWalkingSpeedDiscrete.m b/src/TreatmentOptimization/DiscreteTerms/calcGoalRelativeWalkingSpeedDiscrete.m index afafea6a9..c85033cc7 100644 --- a/src/TreatmentOptimization/DiscreteTerms/calcGoalRelativeWalkingSpeedDiscrete.m +++ b/src/TreatmentOptimization/DiscreteTerms/calcGoalRelativeWalkingSpeedDiscrete.m @@ -29,11 +29,10 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcGoalRelativeWalkingSpeedDiscrete(values, inputs, ... - costTerm) -massCenterVelocity = mean(calcMassCenterVelocity(values, inputs.mexModel, ... - inputs.coordinateNames), 1); -rawCost = massCenterVelocity(1); +function cost = calcGoalRelativeWalkingSpeedDiscrete(values, ... + modeledValues, inputs, costTerm) -cost = rawCost - costTerm.errorCenter; +rawCost = modeledValues.massCenterVelocity; + +cost = ((rawCost - costTerm.errorCenter) ./ costTerm.maxAllowableError) .^ 2; end diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMetabolicCostPerDistanceGoalIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcRelativeMetabolicCostPerDistanceGoalDiscrete.m similarity index 74% rename from src/TreatmentOptimization/IntegrandTerms/calcMetabolicCostPerDistanceGoalIntegrand.m rename to src/TreatmentOptimization/IntegrandTerms/calcRelativeMetabolicCostPerDistanceGoalDiscrete.m index 2b21b8778..c669f8c3d 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMetabolicCostPerDistanceGoalIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcRelativeMetabolicCostPerDistanceGoalDiscrete.m @@ -13,7 +13,7 @@ % National Institutes of Health (R01 EB030520). % % % % Copyright (c) 2021 Rice University and the Authors % -% Author(s): Spencer Williams % +% Author(s): Spencer Williams, Claire V. Hammond % % % % Licensed under the Apache License, Version 2.0 (the "License"); % % you may not use this file except in compliance with the License. % @@ -27,15 +27,9 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcMetabolicCostPerDistanceGoalIntegrand( ... +function cost = calcRelativeMetabolicCostPerDistanceGoalDiscrete( ... modeledValues, values, inputs, costTerm) -assert(isfield(modeledValues, 'muscleActivations'), "Muscle " + ... - "activations are required for metabolic cost calculations.") -rawCost = calcMetabolicCost(values.time, values.positions, ... - modeledValues.muscleActivations, inputs); -massCenterVelocity = mean(calcMassCenterVelocity(values, inputs.model, ... - inputs.coordinateNames), 1); -massCenterVelocity = massCenterVelocity(1); + beltSpeed = 0; if ~isempty(inputs.contactSurfaces) for i = 1 : length(inputs.contactSurfaces) @@ -44,6 +38,13 @@ beltSpeed = beltSpeed / length(inputs.contactSurfaces); end -cost = (rawCost - costTerm.errorCenter) / values.time(end) ... - / (massCenterVelocity + beltSpeed); +currentMetabolicCost = modeledValues.metabolicCost / values.time(end) ... + / (modeledValues.massCenterVelocity + beltSpeed); +normalizedInitialMetabolicCost = inputs.initialMetabolicCost / ... + inputs.experimentalTime(end) / (inputs.initialMassCenterVelocity + beltSpeed); + +rawCost = (currentMetabolicCost - normalizedInitialMetabolicCost) / ... + normalizedInitialMetabolicCost; +assert(~any(isnan(rawCost)), "Relative metabolic cost is infinity, is the initial metabolic cost 0?") +cost = ((rawCost - costTerm.errorCenter) ./ costTerm.maxAllowableError) .^ 2; end diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMetabolicCostPerTimeGoalIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcRelativeMetabolicCostPerTimeGoalDiscrete.m similarity index 75% rename from src/TreatmentOptimization/IntegrandTerms/calcMetabolicCostPerTimeGoalIntegrand.m rename to src/TreatmentOptimization/IntegrandTerms/calcRelativeMetabolicCostPerTimeGoalDiscrete.m index b2134fbc6..5f0b91dfe 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcMetabolicCostPerTimeGoalIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcRelativeMetabolicCostPerTimeGoalDiscrete.m @@ -13,7 +13,7 @@ % National Institutes of Health (R01 EB030520). % % % % Copyright (c) 2021 Rice University and the Authors % -% Author(s): Spencer Williams % +% Author(s): Spencer Williams, Claire V. Hammond % % % % Licensed under the Apache License, Version 2.0 (the "License"); % % you may not use this file except in compliance with the License. % @@ -27,11 +27,13 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function cost = calcMetabolicCostPerTimeGoalIntegrand( ... +function cost = calcRelativeMetabolicCostPerTimeGoalDiscrete( ... modeledValues, values, inputs, costTerm) -assert(isfield(modeledValues, 'muscleActivations'), "Muscle " + ... - "activations are required for metabolic cost calculations.") -rawCost = calcMetabolicCost(values.time, values.positions, ... - modeledValues.muscleActivations, inputs); -cost = (rawCost - costTerm.errorCenter) / values.time(end); +currentMetabolicCost = modeledValues.metabolicCost / values.time(end); +normalizedInitialMetabolicCost = inputs.initialMetabolicCost / ... + inputs.experimentalTime(end); +rawCost = (currentMetabolicCost - normalizedInitialMetabolicCost) / ... + normalizedInitialMetabolicCost; +assert(~any(isnan(rawCost)), "Relative metabolic cost is infinity, is the initial metabolic cost 0?") +cost = ((rawCost - costTerm.errorCenter) ./ costTerm.maxAllowableError) .^ 2; end diff --git a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m index 6b16e24b9..1aacd3298 100644 --- a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m +++ b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m @@ -36,6 +36,9 @@ guess = setupInitialParametersGuess(inputs, guess); guess.phase.integral = scaleToBounds(1e1, inputs.continuousMaxAllowableError, ... zeros(size(inputs.continuousMaxAllowableError))); +if valueOrAlternate(inputs, 'calculateMetabolicCost', false) + guess.phase.integral(:, end + 1) = 1; +end end function guess = setupInitialStatesGuess(inputs, guess) diff --git a/src/TreatmentOptimization/TrackingOptimization/calcSynergyBasedModeledValues.m b/src/TreatmentOptimization/TrackingOptimization/calcSynergyBasedModeledValues.m index 91f7a9dbd..c77095150 100644 --- a/src/TreatmentOptimization/TrackingOptimization/calcSynergyBasedModeledValues.m +++ b/src/TreatmentOptimization/TrackingOptimization/calcSynergyBasedModeledValues.m @@ -32,8 +32,7 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function modeledValues = calcSynergyBasedModeledValues(values, inputs, ... - modeledValues) +function modeledValues = calcSynergyBasedModeledValues(values, inputs) if strcmp(inputs.controllerType, 'synergy') [jointAngles, jointVelocities] = getMuscleActuatedDOFs(values, inputs); [inputs.muscleTendonLength, inputs.momentArms, ... @@ -53,6 +52,8 @@ modeledValues.muscleJointMoments = modeledValues.muscleJointMoments(:, ... inputs.surrogateModelIndex); modeledValues.muscleActivations = permute(modeledValues.muscleActivations, [3 2 1]); +else + modeledValues.muscleActivations = []; end end diff --git a/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m b/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m index e95c74336..32faa975c 100644 --- a/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m +++ b/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m @@ -30,23 +30,21 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function modeledValues = calcTorqueBasedModeledValues(values, inputs) -modeledValues = struct(); +function modeledValues = calcTorqueBasedModeledValues(values, inputs, ... + modeledValues) [appliedLoads, modeledValues] = setupAppliedLoads(values, inputs, ... modeledValues); modeledValues.markerPositions = calcTrackedMarkerPositions(values, inputs); -if valueOrAlternate(inputs, 'calculateAngularMomentum', false) - [modeledValues.inverseDynamicsMoments, ... - modeledValues.angularMomentum] = inverseDynamics(values.time, ... - values.positions, values.velocities, ... - values.accelerations, inputs.coordinateNames, appliedLoads, ... - inputs.mexModel); -else - modeledValues.inverseDynamicsMoments = inverseDynamics(values.time, ... - values.positions, values.velocities, ... - values.accelerations, inputs.coordinateNames, appliedLoads, ... - inputs.mexModel); -end +[modeledValues.inverseDynamicsMoments, modeledValues.angularMomentum, .... + modeledValues.metabolicCost, massCenterPositons] = ... + inverseDynamics(values.time, ... + values.positions, values.velocities, ... + values.accelerations, inputs.coordinateNames, appliedLoads, ... + inputs.mexModel, modeledValues.muscleActivations, ... + sum(valueOrAlternate(inputs, 'calculateAngularMomentum', false)), ... + sum(valueOrAlternate(inputs, 'calculateMetabolicCost', false))); +modeledValues.massCenterVelocity = (massCenterPositons(2) - ... + massCenterPositons(1)) / values.time(end); end function [appliedLoads, modeledValues] = setupAppliedLoads(values, ... diff --git a/src/TreatmentOptimization/checkInitialGuess.m b/src/TreatmentOptimization/checkInitialGuess.m index 89b714ab1..daba557de 100644 --- a/src/TreatmentOptimization/checkInitialGuess.m +++ b/src/TreatmentOptimization/checkInitialGuess.m @@ -30,7 +30,7 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function checkInitialGuess(guess, inputs, continuousFunction) +function inputs = checkInitialGuess(guess, inputs, continuousFunction) initialGuess = guess; initialGuess.auxdata = inputs; if isfield(initialGuess,'parameter') @@ -38,4 +38,15 @@ function checkInitialGuess(guess, inputs, continuousFunction) end output = continuousFunction(initialGuess); output.solution = initialGuess; +if length(output.metabolicCost) == length(inputs.experimentalTime) +cumulativeMetabolicCost = trapz(inputs.experimentalTime, ... + output.metabolicCost); +inputs.initialMetabolicCost = cumulativeMetabolicCost; +inputs.initialMassCenterVelocity = output.massCenterVelocity; +end +end + +function value = simpsons(time, data) +h=time(2) - time(1); +value = h/3*(data(1)+2*sum(data(3:2:end-2))+4*sum(data(2:2:end))+data(end)); end \ No newline at end of file diff --git a/src/TreatmentOptimization/disableModelMuscles.m b/src/TreatmentOptimization/disableModelMuscles.m index 74916e911..a03de3844 100644 --- a/src/TreatmentOptimization/disableModelMuscles.m +++ b/src/TreatmentOptimization/disableModelMuscles.m @@ -28,8 +28,9 @@ % ----------------------------------------------------------------------- % function model = disableModelMuscles(model) -for i = 0:model.getForceSet().getMuscles().getSize()-1 - if model.getForceSet().getMuscles().get(i).get_appliesForce() - model.getForceSet().getMuscles().get(i).set_appliesForce(0); + for i = 0:model.getForceSet().getMuscles().getSize()-1 + if model.getForceSet().getMuscles().get(i).get_appliesForce() + model.getForceSet().getMuscles().get(i).set_appliesForce(0); + end end end \ No newline at end of file diff --git a/src/TreatmentOptimization/generateCostTermStruct.m b/src/TreatmentOptimization/generateCostTermStruct.m index 2a9805769..c044406f7 100644 --- a/src/TreatmentOptimization/generateCostTermStruct.m +++ b/src/TreatmentOptimization/generateCostTermStruct.m @@ -85,8 +85,6 @@ "muscle_activation_minimization" ... "external_torque_control_minimization" ... "angular_momentum_minimization" ... - "metabolic_cost_per_time" ... - "metabolic_cost_per_distance" ... "user_defined", ... ]; otherwise @@ -107,6 +105,8 @@ "synergy_vector_tracking" ... "belt_speed_goal" ... "relative_walking_speed_goal" ... + "relative_metabolic_cost_per_time" ... + "relative_metabolic_cost_per_distance" ... "user_defined" ... ]; otherwise @@ -283,15 +283,15 @@ values.time, ... costTerm); -costTermCalculations.metabolic_cost_per_time = @(values, modeledValues, auxdata, costTerm) ... - calcMetabolicCostPerTimeGoalIntegrand( ... +costTermCalculations.relative_metabolic_cost_per_time = @(values, modeledValues, auxdata, costTerm) ... + calcRelativeMetabolicCostPerTimeGoalDiscrete( ... modeledValues, ... values, ... auxdata, ... costTerm); -costTermCalculations.metabolic_cost_per_distance = @(values, modeledValues, auxdata, costTerm) ... - calcMetabolicCostPerDistanceGoalIntegrand( ... +costTermCalculations.relative_metabolic_cost_per_distance = @(values, modeledValues, auxdata, costTerm) ... + calcRelativeMetabolicCostPerDistanceGoalDiscrete( ... modeledValues, ... values, ... auxdata, ... @@ -312,6 +312,7 @@ costTermCalculations.relative_walking_speed_goal = @(values, modeledValues, auxdata, costTerm) ... calcGoalRelativeWalkingSpeedDiscrete( ... values, ... + modeledValues, ... auxdata, ... costTerm); diff --git a/src/TreatmentOptimization/gpops/computeGpopsContinuousFunction.m b/src/TreatmentOptimization/gpops/computeGpopsContinuousFunction.m index f8b0cf263..cf13f25a8 100644 --- a/src/TreatmentOptimization/gpops/computeGpopsContinuousFunction.m +++ b/src/TreatmentOptimization/gpops/computeGpopsContinuousFunction.m @@ -33,8 +33,8 @@ if strcmp(setup.auxdata.toolName, "DesignOptimization") setup = updateSystemFromUserDefinedFunctions(setup, values); end -modeledValues = calcTorqueBasedModeledValues(values, setup.auxdata); -modeledValues = calcSynergyBasedModeledValues(values, setup.auxdata, ... +modeledValues = calcSynergyBasedModeledValues(values, setup.auxdata); +modeledValues = calcTorqueBasedModeledValues(values, setup.auxdata, ... modeledValues); modeledValues.dynamics = calcDynamicConstraint(values, setup.auxdata); if any(cellfun(@(x) x.isEnabled == 1, setup.auxdata.path)) @@ -48,6 +48,9 @@ modeledValues.path, setup.auxdata.maxPath, setup.auxdata.minPath); end modeledValues.integrand = calcGpopsIntegrand(values, modeledValues, setup.auxdata); +if valueOrAlternate(setup.auxdata, 'calculateMetabolicCost', false) + modeledValues.integrand(:, end+1) = modeledValues.metabolicCost; +end if isempty(modeledValues.integrand) modeledValues = rmfield(modeledValues, "integrand"); end diff --git a/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m b/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m index 8e4ea260c..745676e3a 100644 --- a/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m +++ b/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m @@ -40,9 +40,15 @@ if strcmp(setup.auxdata.toolName, "DesignOptimization") setup = updateSystemFromUserDefinedFunctions(setup, values); end - modeledValues = calcTorqueBasedModeledValues(values, setup.auxdata); - modeledValues = calcSynergyBasedModeledValues(values, setup.auxdata, ... + modeledValues = calcSynergyBasedModeledValues(values, setup.auxdata); + modeledValues = calcTorqueBasedModeledValues(values, setup.auxdata, ... modeledValues); + if valueOrAlternate(setup.auxdata, 'calculateMetabolicCost', false) + modeledValues.metabolicCost = setup.phase.integral(end); + end + if valueOrAlternate(setup.auxdata, 'calculateRelativeSpeed', false) + + end end if any(cellfun(@(x) x.isEnabled == 1, setup.auxdata.terminal)) @@ -63,7 +69,6 @@ generateCostTermStruct("discrete", "DesignOptimization"); discrete = calcTreatmentOptimizationCost( ... costTermCalculations, allowedTypes, values, modeledValues, setup.auxdata); - discrete = discrete ./ setup.auxdata.discreteMaxAllowableError; discreteObjective = sum(discrete) / length(discrete); if isnan(discreteObjective); discreteObjective = 0; end else @@ -71,7 +76,12 @@ end if isfield(setup.phase, "integral") && ~any(isnan(setup.phase.integral)) && ~isempty(setup.phase.integral) - continuousObjective = sum(setup.phase.integral) / length(setup.phase.integral); + if valueOrAlternate(setup.auxdata, 'calculateMetabolicCost', false) + integral = setup.phase.integral(1:end-1); + else + integral = setup.phase.integral; + end + continuousObjective = sum(integral) / length(integral); else continuousObjective = 0; end diff --git a/src/TreatmentOptimization/gpops/convertToGpopsInputs.m b/src/TreatmentOptimization/gpops/convertToGpopsInputs.m index d552281f6..4c747aa15 100644 --- a/src/TreatmentOptimization/gpops/convertToGpopsInputs.m +++ b/src/TreatmentOptimization/gpops/convertToGpopsInputs.m @@ -24,7 +24,7 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function setup = convertToGpopsInputs(inputs, params) +function [setup, inputs] = convertToGpopsInputs(inputs, params) bounds = setupTreatmentOptimizationBounds(inputs, params); guess = setupGpopsInitialGuess(inputs); setup = setupGpopsSettings(inputs, ... @@ -32,6 +32,12 @@ @computeGpopsContinuousFunction, ... @computeGpopsEndpointFunction); setup = preSplineGpopsInputs(setup); -checkInitialGuess(guess, setup.auxdata, ... +inputs = checkInitialGuess(guess, setup.auxdata, ... @computeGpopsContinuousFunction); +if valueOrAlternate(inputs, 'calculateMetabolicCost', false) +setup.bounds.phase.integral.lower(end + 1) = 0; +setup.bounds.phase.integral.upper(end + 1) = (inputs.gpops.integralBound + 1) * ... + max(inputs.initialMetabolicCost); +setup.guess.phase.integral(end) = inputs.initialMetabolicCost; +end end diff --git a/src/TreatmentOptimization/modifyModelForces.m b/src/TreatmentOptimization/modifyModelForces.m index 524bc7147..602d87f42 100644 --- a/src/TreatmentOptimization/modifyModelForces.m +++ b/src/TreatmentOptimization/modifyModelForces.m @@ -31,6 +31,17 @@ function inputs = modifyModelForces(inputs) model = disableModelMuscles(inputs.model); +if valueOrAlternate(inputs, 'calculateMetabolicCost', false) + probe = org.opensim.modeling.Bhargava2004MuscleMetabolicsProbe( ... + true, true, true, false, true); + model.addProbe(probe); + probe.setOperation("value"); + probe.set_report_total_metabolics_only(true); + probe.setName("metabolics"); + for i = 1:length(inputs.muscleNames) + probe.addMuscle(inputs.muscleNames(i), 0.5, 40, 133, 74, 111); + end +end model = addContactSurfaceActuators(inputs, model); inputs.mexModel = strcat(strrep(inputs.modelFileName,'.osim',''), '_inactiveMuscles.osim'); model.print(inputs.mexModel); diff --git a/src/TreatmentOptimization/solveOptimalControlProblem.m b/src/TreatmentOptimization/solveOptimalControlProblem.m index 0a0a1e47a..e04561826 100644 --- a/src/TreatmentOptimization/solveOptimalControlProblem.m +++ b/src/TreatmentOptimization/solveOptimalControlProblem.m @@ -25,7 +25,8 @@ % ----------------------------------------------------------------------- % function [inputs, output] = solveOptimalControlProblem(inputs, params) -setup = convertToGpopsInputs(inputs, params); +[setup, inputs] = convertToGpopsInputs(inputs, params); +setup.auxdata = inputs; solution = gpops2(setup); inputs = setup.auxdata; output = convertFromGpopsOutputs(solution, ... diff --git a/src/core/mex/compileMex.m b/src/core/mex/compileMex.m index 7a0161532..ffef55e49 100644 --- a/src/core/mex/compileMex.m +++ b/src/core/mex/compileMex.m @@ -1,6 +1,6 @@ mex CXXFLAGS="/$CXXFLAGS -fopenmp" LDFLAGS="/$LDFLAGS -fopenmp"... COMPFLAGS="/openmp $COMPFLAGS"... - evaluateGcvSplinesMexWindows.cpp... + inverseDynamicsWithExtraCalcsMexWindows.cpp... -L'C:\OpenSim 4.4\sdk\lib'... -L'C:\OpenSim 4.4\sdk\Simbody\lib'... -L'C:\OpenSim 4.4\sdk\spdlog\lib\spdlog'... diff --git a/src/core/mex/initializeMexOrMatlabParallelFunctions.m b/src/core/mex/initializeMexOrMatlabParallelFunctions.m index d7a990814..b088f68c3 100644 --- a/src/core/mex/initializeMexOrMatlabParallelFunctions.m +++ b/src/core/mex/initializeMexOrMatlabParallelFunctions.m @@ -32,7 +32,7 @@ function initializeMexOrMatlabParallelFunctions(modelFile) if isequal(mexext, 'mexw64') pointKinematicsMexWindows(modelFile); - inverseDynamicsAngularMomentumMexWindows(modelFile); + inverseDynamicsWithExtraCalcsMexWindows(modelFile); end clear inverseDynamicsMatlabParallel clear pointKinematicsMatlabParallel diff --git a/src/core/mex/inverseDynamics.m b/src/core/mex/inverseDynamics.m index 2843870b1..b4f5eeaa8 100644 --- a/src/core/mex/inverseDynamics.m +++ b/src/core/mex/inverseDynamics.m @@ -3,6 +3,8 @@ % This function uses a mex file or a matlab function with parallel workers % to calculate inverse dynamics moments. % +% This assumes muscleActivations matrix is in model muscle order +% % (Array of number, 2D matrix, 2D matrix, 2D matrix, Cell, 2D matrix, % Array of string) -> (2D matrix) % Returns inverse dynamic moments @@ -29,21 +31,18 @@ % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function [inverseDynamicsMoments, angularMomentum] = inverseDynamics( ... - time, jointAngles, jointVelocities, jointAccelerations, ... - coordinateLabels, appliedLoads, modelName) +function [inverseDynamicsMoments, angularMomentum, metabolicCost, ... + massCenterVelocity] = ... + inverseDynamics( time, jointAngles, jointVelocities, ... + jointAccelerations, coordinateLabels, appliedLoads, modelName, ... + muscleActivations, computeAngularMomentum, computeMetabolicCost) if isequal(mexext, 'mexw64') - if nargout == 1 - inverseDynamicsMoments = ... - inverseDynamicsAngularMomentumMexWindows(time, jointAngles, ... - jointVelocities, jointAccelerations, coordinateLabels, ... - appliedLoads); - else - [inverseDynamicsMoments, angularMomentum] = ... - inverseDynamicsAngularMomentumMexWindows(time, jointAngles, ... - jointVelocities, jointAccelerations, coordinateLabels, ... - appliedLoads); - end + [inverseDynamicsMoments, angularMomentum, metabolicCost, ... + massCenterVelocity] = ... + inverseDynamicsWithExtraCalcsMexWindows(time, jointAngles, ... + jointVelocities, jointAccelerations, coordinateLabels, ... + appliedLoads, muscleActivations, computeAngularMomentum, ... + computeMetabolicCost); else if nargout == 1 inverseDynamicsMoments = inverseDynamicsMatlabParallel(time, ... diff --git a/src/core/mex/inverseDynamicsWithExtraCalcsMexWindows.cpp b/src/core/mex/inverseDynamicsWithExtraCalcsMexWindows.cpp new file mode 100644 index 000000000..6e464a692 --- /dev/null +++ b/src/core/mex/inverseDynamicsWithExtraCalcsMexWindows.cpp @@ -0,0 +1,195 @@ +// This function is part of the NMSM Pipeline, see file for full license. +// +// performs inverse dynamics with openMP + +// ----------------------------------------------------------------------- // +// The NMSM Pipeline is a toolkit for model personalization and treatment // +// optimization of neuromusculoskeletal models through OpenSim. See // +// nmsm.rice.edu and the NOTICE file for more information. The // +// NMSM Pipeline is developed at Rice University and supported by the US // +// National Institutes of Health (R01 EB030520). // +// // +// Copyright (c) 2021 Rice University and the Authors // +// Author(s): Marleny Vega // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// http://www.apache.org/licenses/LICENSE-2.0. // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or // +// implied. See the License for the specific language governing // +// permissions and limitations under the License. // +// ----------------------------------------------------------------------- // + +#include "mex.h" +#include +#include +#include +#include +#include +#include +#include // +#include +#include +#include + +using namespace OpenSim; +using namespace SimTK; +using namespace std; +#define numThreads 20 // + +//______________________________________________________________________________ + +static Model *osimModel[numThreads]; +static State *osimState[numThreads]; +static InverseDynamicsSolver *idSolver[numThreads]; +static bool modelIsLoaded = false; + +void ClearMemory(void){ + for (int i = 0; i < numThreads; i++){ + delete osimModel[i]; + delete idSolver[i]; + } + modelIsLoaded = false; + mexPrintf("Cleared memory from inverseDynamics mex file.\n"); +} + +vector> mexArrayToVector(const mxArray *input) { + const mwSize *dimension; + dimension = mxGetDimensions(input); + int rows = (int) dimension[0]; + int columns = (int) dimension[1]; + double *data = (double *) mxGetData(input); + vector> output(rows, vector(columns)); + for (int i = 0; i < rows; i++) { + for (int j = 0; j < columns; j++) { + memcpy(&output[i][j], data + i + j * rows, sizeof(double) ); + } + } + return output; +} + +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]){ + mexAtExit(ClearMemory); + if (nrhs == 1) { + if (modelIsLoaded == true){ + ClearMemory(); + } + string modelName = mxArrayToString(prhs[0]); + std::streambuf* oldCoutStreamBuf = std::cout.rdbuf(); + std::ostringstream strCout; + std::cout.rdbuf(strCout.rdbuf()); + for (int i = 0; i < numThreads; i++){ + osimModel[i] = new Model(modelName); + osimState[i] = &osimModel[i]->initSystem(); + idSolver[i] = new InverseDynamicsSolver(*osimModel[i]); + } + std::cout.rdbuf(oldCoutStreamBuf); + modelIsLoaded = true; + } + else if (nrhs > 1) { + if (modelIsLoaded == false){ + mexErrMsgTxt("!!!No OpenSim model has been loaded!!!\n"); + } + const int numPts = mxGetM(prhs[0]); + const int numQs = mxGetN(prhs[1]); + const int numControls = mxGetN(prhs[5]); + const int numMuscles = mxGetN(prhs[6]); + const int numCoords = osimState[0]->getNQ(); + + double *time = mxGetPr(prhs[0]); + vector> q = mexArrayToVector(prhs[1]); + vector> qp = mexArrayToVector(prhs[2]); + vector> qpp = mexArrayToVector(prhs[3]); + vector> u = mexArrayToVector(prhs[5]); + vector> muscleActivations = mexArrayToVector(prhs[6]); + double* computeAngularMomentum = (double *) mxGetData(prhs[7]); + double* computeMetabolicCost = (double *) mxGetData(prhs[8]); + + mwIndex k; + mwSize numLabels, buflen; + numLabels = mxGetNumberOfElements(prhs[4]); + + plhs[0] = mxCreateDoubleMatrix(numPts,numCoords,mxREAL); + double *idLoads = mxGetPr(plhs[0]); + plhs[1] = mxCreateDoubleMatrix(numPts, 3, mxREAL); + double* angularMomentum = mxGetPr(plhs[1]); + plhs[2] = mxCreateDoubleMatrix(numPts, 1, mxREAL); + double* metabolicCost = mxGetPr(plhs[2]); + plhs[3] = mxCreateDoubleMatrix(2, 1, mxREAL); + double* massCenterPositions = mxGetPr(plhs[3]); + + #pragma omp parallel for num_threads(numThreads) + for (int i = 0; i < numPts; ++i){ + int thread_id = omp_get_thread_num(); + osimState[thread_id]->setTime(time[i]); + + for (int k = 0; k < numLabels; k++){ + const mxArray *cellElementPtr; + cellElementPtr = mxGetCell(prhs[4], k); + buflen = mxGetN(cellElementPtr)*sizeof(mxChar) + 1; + char* c_array; + c_array = (char *) mxCalloc(buflen, sizeof(char)); + int status = mxGetString(cellElementPtr, c_array, buflen); + osimModel[thread_id]->getCoordinateSet().get(c_array).setValue(*osimState[thread_id], q[i][k], false); + osimModel[thread_id]->getCoordinateSet().get(c_array).setSpeedValue(*osimState[thread_id], qp[i][k]); + mxFree(c_array); + } + + osimModel[thread_id]->realizeVelocity(*osimState[thread_id]); + if (*computeAngularMomentum > 0.5) { + SpatialVec momentum = osimModel[thread_id]->getMatterSubsystem().calcSystemCentralMomentum(*osimState[thread_id]); + Vec3 angularMomentumPoint = momentum.get(0); + for (int j = 0; j <= 2; j++) { + angularMomentum[i + numPts * j] = angularMomentumPoint.get(j); + } + } + + if (i == 0) { + massCenterPositions[0] = osimModel[thread_id]->calcMassCenterVelocity(*osimState[thread_id]).get(0); + } else if (i == numPts - 1) { + massCenterPositions[1] = osimModel[thread_id]->calcMassCenterVelocity(*osimState[thread_id]).get(0); + } + + Vector AccelsVec(numCoords, 0.0); + for (int j = 0; j < numCoords; j++){ + double StateQ = osimState[thread_id]->getQ().get(j); + for (int k = 0; k < numQs; k++){ + if (abs(q[i][k] - StateQ) <= 1e-6){ + AccelsVec.set(j, qpp[i][k]); + } + } + } + + Vector newControls(numControls, 0.0); + for (int j = 0; j < numControls; j++){ + newControls.set(j, u[i][j]); + } + osimModel[thread_id]->setControls(*osimState[thread_id], newControls); + osimModel[thread_id]->markControlsAsValid(*osimState[thread_id]); + osimModel[thread_id]->realizeDynamics(*osimState[thread_id]); + + Vector IDLoadsVec; + IDLoadsVec = idSolver[thread_id]->solve(*osimState[thread_id], AccelsVec); + for (int j = 0; j < numCoords; j++){ + idLoads[i + numPts * j] = IDLoadsVec[j]; + } + + if (*computeMetabolicCost > 0.5) { + for (int j = 0; j < numMuscles; j++) { + osimModel[thread_id]->getForceSet().getMuscles().get(j).setActivation(*osimState[thread_id], muscleActivations[i][j]); + } + osimModel[thread_id]->realizeDynamics(*osimState[thread_id]); + osimModel[thread_id]->equilibrateMuscles(*osimState[thread_id]); + metabolicCost[i] = osimModel[thread_id]->getProbeSet().get(0).getProbeOutputs(*osimState[thread_id]).get(0); + } + } + //q.clear(); + //qp.clear(); + //qpp.clear(); + //u.clear(); + } +} \ No newline at end of file diff --git a/src/core/mex/inverseDynamicsWithExtraCalcsMexWindows.mexw64 b/src/core/mex/inverseDynamicsWithExtraCalcsMexWindows.mexw64 new file mode 100644 index 0000000000000000000000000000000000000000..68bca8b8f8c1004cbd9a71e2866518a16ffd3f50 GIT binary patch literal 37888 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 Date: Sat, 9 Mar 2024 21:20:08 -0600 Subject: [PATCH 355/365] fix normalize by final time for tracking terms --- .../IntegrandTerms/calcTrackingControllerIntegrand.m | 3 +++ .../IntegrandTerms/calcTrackingCoordinateIntegrand.m | 3 +++ .../IntegrandTerms/calcTrackingExternalForcesIntegrand.m | 3 +++ .../IntegrandTerms/calcTrackingExternalMomentsIntegrand.m | 3 +++ .../IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m | 3 +++ .../IntegrandTerms/calcTrackingInverseDynamicSlopeIntegrand.m | 3 +++ .../IntegrandTerms/calcTrackingMarkerPosition.m | 3 +++ .../IntegrandTerms/calcTrackingMuscleActivationIntegrand.m | 3 +++ .../IntegrandTerms/calcTrackingSpeedIntegrand.m | 3 +++ 9 files changed, 27 insertions(+) diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m index 08298251c..bf5fb2121 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m @@ -34,6 +34,9 @@ values, time, controllerName) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); +if normalizeByFinalTime + time = time * inputs.experimentalTime(end) / time(end); +end if strcmp(inputs.controllerType, 'synergy') indx = find(strcmp(convertCharsToStrings( ... inputs.synergyLabels), controllerName)); diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m index 0afb3455e..db9da0e9f 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m @@ -32,6 +32,9 @@ positions, coordinateName) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); +if normalizeByFinalTime + time = time * inputs.experimentalTime(end) / time(end); +end indx = find(strcmp(convertCharsToStrings(inputs.coordinateNames), ... coordinateName)); if isempty(indx) diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m index 9b5587c4a..882ad9ad6 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m @@ -32,6 +32,9 @@ groundReactionsForces, time, forceName) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); +if normalizeByFinalTime + time = time * inputs.experimentalTime(end) / time(end); +end for i = 1:length(inputs.contactSurfaces) indx = find(strcmp(convertCharsToStrings(inputs.contactSurfaces{i}. ... forceColumns), forceName)); diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m index 67efca3a1..ea158baa0 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m @@ -32,6 +32,9 @@ groundReactionMoments, time, loadName) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); +if normalizeByFinalTime + time = time * inputs.experimentalTime(end) / time(end); +end for i = 1:length(inputs.contactSurfaces) indx = find(strcmp(convertCharsToStrings(inputs.contactSurfaces{i}. ... momentColumns), loadName)); diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m index 648a98dc2..a0e86945f 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m @@ -32,6 +32,9 @@ inputs, time, inverseDynamicsMoments, loadName) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); +if normalizeByFinalTime + time = time * inputs.experimentalTime(end) / time(end); +end indx = find(strcmp(inputs.inverseDynamicsMomentLabels, loadName)); if all(size(time) == size(inputs.collocationTimeOriginal)) && ... all(time == inputs.collocationTimeOriginal) diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicSlopeIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicSlopeIntegrand.m index 3988734c9..1453a3d57 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicSlopeIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicSlopeIntegrand.m @@ -32,6 +32,9 @@ inputs, time, inverseDynamicsMoments, loadName) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); +if normalizeByFinalTime + time = time * inputs.experimentalTime(end) / time(end); +end indx = find(strcmp(inputs.inverseDynamicsMomentLabels, loadName)); if all(size(time) == size(inputs.collocationTimeOriginal)) && ... all(time == inputs.collocationTimeOriginal) diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m index a18611ac9..b8586277d 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m @@ -31,6 +31,9 @@ markerPositions, inputs) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); +if normalizeByFinalTime + time = time * inputs.experimentalTime(end) / time(end); +end indx = find(strcmp(convertCharsToStrings(inputs.trackedMarkerNames), ... costTerm.marker)); if isempty(indx) diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m index f663d6560..4009c7c09 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m @@ -32,6 +32,9 @@ muscleActivations, time, inputs, muscleName) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); +if normalizeByFinalTime + time = time * inputs.experimentalTime(end) / time(end); +end indx = find(strcmp(convertCharsToStrings(inputs.muscleNames), ... muscleName)); if all(size(time) == size(inputs.collocationTimeOriginal)) && ... diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m index 29fd2ac4a..5c6442e3b 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m @@ -32,6 +32,9 @@ velocities, coordinateName) normalizeByFinalTime = valueOrAlternate(costTerm, ... "normalize_by_final_time", true); +if normalizeByFinalTime + time = time * inputs.experimentalTime(end) / time(end); +end indx = find(strcmp(convertCharsToStrings(inputs.coordinateNames), ... coordinateName)); if isempty(indx) From 567060aa0f6ce11fdabc867ca3b98df7bfac8eb3 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sat, 9 Mar 2024 21:21:03 -0600 Subject: [PATCH 356/365] add initial state position cost term --- .../TerminalTerms/calcInitialStatePosition.m | 42 +++++++++++++++++++ src/TreatmentOptimization/checkInitialGuess.m | 7 +--- .../generateConstraintTermStruct.m | 9 ++++ 3 files changed, 53 insertions(+), 5 deletions(-) create mode 100644 src/TreatmentOptimization/TerminalTerms/calcInitialStatePosition.m diff --git a/src/TreatmentOptimization/TerminalTerms/calcInitialStatePosition.m b/src/TreatmentOptimization/TerminalTerms/calcInitialStatePosition.m new file mode 100644 index 000000000..41efc4048 --- /dev/null +++ b/src/TreatmentOptimization/TerminalTerms/calcInitialStatePosition.m @@ -0,0 +1,42 @@ +% This function is part of the NMSM Pipeline, see file for full license. +% +% This function calculates the difference between the initial state +% position and current state position for the specified coordinate. +% +% (2D matrix, Cell, struct) -> (Number) +% + +% ----------------------------------------------------------------------- % +% The NMSM Pipeline is a toolkit for model personalization and treatment % +% optimization of neuromusculoskeletal models through OpenSim. See % +% nmsm.rice.edu and the NOTICE file for more information. The % +% NMSM Pipeline is developed at Rice University and supported by the US % +% National Institutes of Health (R01 EB030520). % +% % +% Copyright (c) 2021 Rice University and the Authors % +% Author(s): Marleny Vega % +% % +% Licensed under the Apache License, Version 2.0 (the "License"); % +% you may not use this file except in compliance with the License. % +% You may obtain a copy of the License at % +% http://www.apache.org/licenses/LICENSE-2.0. % +% % +% Unless required by applicable law or agreed to in writing, software % +% distributed under the License is distributed on an "AS IS" BASIS, % +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % +% implied. See the License for the specific language governing % +% permissions and limitations under the License. % +% ----------------------------------------------------------------------- % + +function initialStatePosition = calcInitialStatePosition( ... + statePositions, coordinateNames, auxdata, constraintTerm) +indx = find(strcmp(convertCharsToStrings(coordinateNames), ... + constraintTerm.coordinate)); +if isempty(indx) + throw(MException('ConstraintTermError:CoordinateNotInState', ... + strcat("Coordinate ", constraintTerm.coordinate, " is not in the ", ... + ""))) +end +initialStatePosition = statePositions(1, indx) - ... + auxdata.initialStatePositions(1, indx); +end \ No newline at end of file diff --git a/src/TreatmentOptimization/checkInitialGuess.m b/src/TreatmentOptimization/checkInitialGuess.m index daba557de..a2c2c18f8 100644 --- a/src/TreatmentOptimization/checkInitialGuess.m +++ b/src/TreatmentOptimization/checkInitialGuess.m @@ -33,6 +33,8 @@ function inputs = checkInitialGuess(guess, inputs, continuousFunction) initialGuess = guess; initialGuess.auxdata = inputs; +values = makeGpopsValuesAsStruct(guess.phase, inputs); +inputs.initialStatePositions = values.statePositions; if isfield(initialGuess,'parameter') initialGuess.phase.parameter = initialGuess.parameter; end @@ -45,8 +47,3 @@ inputs.initialMassCenterVelocity = output.massCenterVelocity; end end - -function value = simpsons(time, data) -h=time(2) - time(1); -value = h/3*(data(1)+2*sum(data(3:2:end-2))+4*sum(data(2:2:end))+data(end)); -end \ No newline at end of file diff --git a/src/TreatmentOptimization/generateConstraintTermStruct.m b/src/TreatmentOptimization/generateConstraintTermStruct.m index 2cddaa05a..056ba228b 100644 --- a/src/TreatmentOptimization/generateConstraintTermStruct.m +++ b/src/TreatmentOptimization/generateConstraintTermStruct.m @@ -99,6 +99,7 @@ "root_segment_residual_load_periodicity", ... "external_force_periodicity", ... "external_moment_periodicity", ... + "initial_state_position_tracking", ... "final_state_position", ... "final_state_velocity", ... "final_point_position", ... @@ -163,6 +164,7 @@ "external_force_periodicity", ... "external_moment_periodicity", ... "synergy_weight_sum", ... + "initial_state_position_tracking", ... "final_state_position", ... "final_state_velocity", ... "final_point_position", ... @@ -258,6 +260,13 @@ auxdata.contactSurfaces, ... constraintTerm.moment); +constraintTermCalculations.initial_state_position_tracking = @(values, modeledValues, auxdata, constraintTerm) ... + calcInitialStatePosition( ... + values.statePositions, ... + auxdata.statesCoordinateNames, ... + auxdata, ... + constraintTerm); + constraintTermCalculations.final_state_position = @(values, modeledValues, auxdata, constraintTerm) ... calcFinalStatePosition( ... values.statePositions, ... From 0613f9bf3c443e3c159c2ca087dc53067a20dead Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 10 Mar 2024 15:04:33 -0500 Subject: [PATCH 357/365] make tracking time tolerance not absolute --- .../IntegrandTerms/calcTrackingControllerIntegrand.m | 4 ++-- .../IntegrandTerms/calcTrackingCoordinateIntegrand.m | 2 +- .../IntegrandTerms/calcTrackingExternalForcesIntegrand.m | 2 +- .../IntegrandTerms/calcTrackingExternalMomentsIntegrand.m | 2 +- .../IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m | 2 +- .../IntegrandTerms/calcTrackingInverseDynamicSlopeIntegrand.m | 2 +- .../IntegrandTerms/calcTrackingMarkerPosition.m | 2 +- .../IntegrandTerms/calcTrackingMuscleActivationIntegrand.m | 2 +- .../IntegrandTerms/calcTrackingSpeedIntegrand.m | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m index bf5fb2121..7bcad1929 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m @@ -42,7 +42,7 @@ inputs.synergyLabels), controllerName)); if ~isempty(indx) if all(size(time) == size(inputs.collocationTimeOriginal)) && ... - all(time == inputs.collocationTimeOriginal) + max(abs(time - inputs.collocationTimeOriginal)) < 1e-6 synergyActivations = inputs.splinedSynergyActivations; else synergyActivations = ... @@ -60,7 +60,7 @@ strcat(inputs.torqueControllerCoordinateNames, '_moment')), ... controllerName)); if all(size(time) == size(inputs.collocationTimeOriginal)) && ... - all(time == inputs.collocationTimeOriginal) + max(abs(time - inputs.collocationTimeOriginal)) < 1e-6 experimentalJointMoments = inputs.splinedTorqueControls; else experimentalJointMoments = ... diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m index db9da0e9f..a9b00eca6 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m @@ -43,7 +43,7 @@ ""))) end if all(size(time) == size(inputs.collocationTimeOriginal)) && ... - all(time == inputs.collocationTimeOriginal) + max(abs(time - inputs.collocationTimeOriginal)) < 1e-6 experimentalJointAngles = inputs.splinedJointAngles; else experimentalJointAngles = evaluateGcvSplines( ... diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m index 882ad9ad6..7dfabfb47 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m @@ -40,7 +40,7 @@ forceColumns), forceName)); if ~isempty(indx) if all(size(time) == size(inputs.collocationTimeOriginal)) && ... - all(time == inputs.collocationTimeOriginal) + max(abs(time - inputs.collocationTimeOriginal)) < 1e-6 experimentalGroundReactions = inputs.splinedGroundReactionForces{i}; else experimentalGroundReactions = ... diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m index ea158baa0..dbae7bc38 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m @@ -40,7 +40,7 @@ momentColumns), loadName)); if ~isempty(indx) if all(size(time) == size(inputs.collocationTimeOriginal)) && ... - all(time == inputs.collocationTimeOriginal) + max(abs(time - inputs.collocationTimeOriginal)) < 1e-6 experimentalGroundReactions = inputs.splinedGroundReactionMoments{i}; else experimentalGroundReactions = ... diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m index a0e86945f..9d0e9a675 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m @@ -37,7 +37,7 @@ end indx = find(strcmp(inputs.inverseDynamicsMomentLabels, loadName)); if all(size(time) == size(inputs.collocationTimeOriginal)) && ... - all(time == inputs.collocationTimeOriginal) + max(abs(time - inputs.collocationTimeOriginal)) < 1e-6 experimentalJointMoments = inputs.splinedJointMoments; else experimentalJointMoments = evaluateGcvSplines( ... diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicSlopeIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicSlopeIntegrand.m index 1453a3d57..a3c3c7dda 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicSlopeIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicSlopeIntegrand.m @@ -37,7 +37,7 @@ end indx = find(strcmp(inputs.inverseDynamicsMomentLabels, loadName)); if all(size(time) == size(inputs.collocationTimeOriginal)) && ... - all(time == inputs.collocationTimeOriginal) + max(abs(time - inputs.collocationTimeOriginal)) < 1e-6 experimentalJointMoments = inputs.splinedJointMoments; else experimentalJointMoments = evaluateGcvSplines( ... diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m index b8586277d..61ec0bb63 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m @@ -44,7 +44,7 @@ assert(length(indx) == 1, "Marker " + costTerm.marker + ... " must only have one tracking cost term.") if all(size(time) == size(inputs.collocationTimeOriginal)) && ... - all(time == inputs.collocationTimeOriginal) + max(abs(time - inputs.collocationTimeOriginal)) < 1e-6 experimentalMarkerPositions = inputs.splinedMarkerPositions{indx}; else experimentalMarkerPositions = ... diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m index 4009c7c09..e041a7448 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m @@ -38,7 +38,7 @@ indx = find(strcmp(convertCharsToStrings(inputs.muscleNames), ... muscleName)); if all(size(time) == size(inputs.collocationTimeOriginal)) && ... - all(time == inputs.collocationTimeOriginal) + max(abs(time - inputs.collocationTimeOriginal)) < 1e-6 experimentalMuscleActivations = inputs.splinedMuscleActivations; else experimentalMuscleActivations = evaluateGcvSplines( ... diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m index 5c6442e3b..cad63cb18 100644 --- a/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m +++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m @@ -43,7 +43,7 @@ ""))) end if all(size(time) == size(inputs.collocationTimeOriginal)) && ... - all(time == inputs.collocationTimeOriginal) + max(abs(time - inputs.collocationTimeOriginal)) < 1e-6 experimentalJointVelocities = inputs.splinedJointSpeeds; else experimentalJointVelocities = evaluateGcvSplines( ... From 8d827cbf3b386849d32460fff6c6d86beabf9abd Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 10 Mar 2024 15:17:47 -0500 Subject: [PATCH 358/365] temp remove mass center calculation --- ...nverseDynamicsWithExtraCalcsMexWindows.cpp | 6 ++++-- ...rseDynamicsWithExtraCalcsMexWindows.mexw64 | Bin 37888 -> 37376 bytes 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/mex/inverseDynamicsWithExtraCalcsMexWindows.cpp b/src/core/mex/inverseDynamicsWithExtraCalcsMexWindows.cpp index 6e464a692..9d610f657 100644 --- a/src/core/mex/inverseDynamicsWithExtraCalcsMexWindows.cpp +++ b/src/core/mex/inverseDynamicsWithExtraCalcsMexWindows.cpp @@ -149,9 +149,11 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]){ } if (i == 0) { - massCenterPositions[0] = osimModel[thread_id]->calcMassCenterVelocity(*osimState[thread_id]).get(0); + massCenterPositions[0] = 0; + //massCenterPositions[0] = osimModel[thread_id]->calcMassCenterVelocity(*osimState[thread_id]).get(0); } else if (i == numPts - 1) { - massCenterPositions[1] = osimModel[thread_id]->calcMassCenterVelocity(*osimState[thread_id]).get(0); + massCenterPositions[1] = 0; + //massCenterPositions[1] = osimModel[thread_id]->calcMassCenterVelocity(*osimState[thread_id]).get(0); } Vector AccelsVec(numCoords, 0.0); diff --git a/src/core/mex/inverseDynamicsWithExtraCalcsMexWindows.mexw64 b/src/core/mex/inverseDynamicsWithExtraCalcsMexWindows.mexw64 index 68bca8b8f8c1004cbd9a71e2866518a16ffd3f50..2cb74a8d587eede7f23f74e759388dd43818b142 100644 GIT binary patch delta 10261 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 Ci#bvN delta 10435 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 From d97a0df705e8dac3d0cd4b8c3ca222d64a7b0ff5 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 10 Mar 2024 15:27:27 -0500 Subject: [PATCH 359/365] Revert "temp remove mass center calculation" This reverts commit 8d827cbf3b386849d32460fff6c6d86beabf9abd. --- ...nverseDynamicsWithExtraCalcsMexWindows.cpp | 6 ++---- ...rseDynamicsWithExtraCalcsMexWindows.mexw64 | Bin 37376 -> 37888 bytes 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/core/mex/inverseDynamicsWithExtraCalcsMexWindows.cpp b/src/core/mex/inverseDynamicsWithExtraCalcsMexWindows.cpp index 9d610f657..6e464a692 100644 --- a/src/core/mex/inverseDynamicsWithExtraCalcsMexWindows.cpp +++ b/src/core/mex/inverseDynamicsWithExtraCalcsMexWindows.cpp @@ -149,11 +149,9 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]){ } if (i == 0) { - massCenterPositions[0] = 0; - //massCenterPositions[0] = osimModel[thread_id]->calcMassCenterVelocity(*osimState[thread_id]).get(0); + massCenterPositions[0] = osimModel[thread_id]->calcMassCenterVelocity(*osimState[thread_id]).get(0); } else if (i == numPts - 1) { - massCenterPositions[1] = 0; - //massCenterPositions[1] = osimModel[thread_id]->calcMassCenterVelocity(*osimState[thread_id]).get(0); + massCenterPositions[1] = osimModel[thread_id]->calcMassCenterVelocity(*osimState[thread_id]).get(0); } Vector AccelsVec(numCoords, 0.0); diff --git a/src/core/mex/inverseDynamicsWithExtraCalcsMexWindows.mexw64 b/src/core/mex/inverseDynamicsWithExtraCalcsMexWindows.mexw64 index 2cb74a8d587eede7f23f74e759388dd43818b142..68bca8b8f8c1004cbd9a71e2866518a16ffd3f50 100644 GIT binary patch delta 10435 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 delta 10261 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 Ci#bvN From ffe442a8896653e99b01a1baa815307833702262 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Sun, 10 Mar 2024 23:26:17 -0500 Subject: [PATCH 360/365] change free final time formulation --- .../parseDesignOptimizationSettingsTree.m | 9 ++++++--- .../setupTreatmentOptimizationBounds.m | 6 ++---- .../SetupBounds/makeOptimalControlBounds.m | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/TreatmentOptimization/DesignOptimization/parseDesignOptimizationSettingsTree.m b/src/TreatmentOptimization/DesignOptimization/parseDesignOptimizationSettingsTree.m index b88b5a3ef..00a2592c4 100644 --- a/src/TreatmentOptimization/DesignOptimization/parseDesignOptimizationSettingsTree.m +++ b/src/TreatmentOptimization/DesignOptimization/parseDesignOptimizationSettingsTree.m @@ -72,10 +72,13 @@ end function inputs = parseDesignSettings(tree, inputs) -finalTimeRange = getFieldByName(tree, ... +try +finalTimeRange = parseSpaceSeparatedList(tree, ... 'final_time_range'); -if(isstruct(finalTimeRange)) - inputs.finalTimeRange = getDoubleFromField(finalTimeRange); +if(~isempty(finalTimeRange)) + inputs.finalTimeRange = str2double(finalTimeRange); +end +catch end end diff --git a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupTreatmentOptimizationBounds.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupTreatmentOptimizationBounds.m index 436eaba90..7fbefdfe6 100644 --- a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupTreatmentOptimizationBounds.m +++ b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupTreatmentOptimizationBounds.m @@ -36,10 +36,8 @@ bounds.phase.finaltime.lower = 0.5; bounds.phase.finaltime.upper = 0.5; if isfield(inputs, "finalTimeRange") - scaledTime = scaleToBounds(inputs.initialTime, inputs.maxTime, ... - inputs.minTime); - bounds.phase.finaltime.lower = ... - scaledTime(end) - (0.5 - scaledTime(end)); + bounds.phase.finaltime.lower = scaleToBounds( ... + inputs.finalTimeRange(1), inputs.maxTime, inputs.minTime); end % setup state bounds bounds.phase.initialstate.lower = -0.5 * ones(1, length(inputs.minState)); diff --git a/src/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m b/src/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m index 24b6100a1..8bbc36807 100644 --- a/src/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m +++ b/src/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m @@ -39,7 +39,7 @@ function inputs = makeStateBounds(inputs) if isfield(inputs, "finalTimeRange") - inputs.maxTime = max(inputs.experimentalTime) + inputs.finalTimeRange; + inputs.maxTime = inputs.finalTimeRange(2); else inputs.maxTime = max(inputs.experimentalTime); end From bd1cf039a61e4b82e13f7d996e0507c2c61502fd Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Mon, 11 Mar 2024 02:02:24 -0500 Subject: [PATCH 361/365] fix translations having degrees coordinates --- .../plotTreatmentOptimizationJointAngles.m | 34 +++++++++++++------ 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m index a68d66fc8..04c833481 100644 --- a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m +++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m @@ -36,10 +36,11 @@ % implied. See the License for the specific language governing % % permissions and limitations under the License. % % ----------------------------------------------------------------------- % -function plotTreatmentOptimizationJointAngles(experimentalFile, ... - modelFiles, figureWidth, figureHeight) +function plotTreatmentOptimizationJointAngles(modelFileName, ... + experimentalFile, modelFiles, figureWidth, figureHeight) import org.opensim.modeling.Storage +model = Model(modelFileName); experimentalStorage = Storage(experimentalFile); labels = getStorageColumnNames(experimentalStorage); experimentalData = storageToDoubleMatrix(experimentalStorage)'; @@ -63,10 +64,10 @@ function plotTreatmentOptimizationJointAngles(experimentalFile, ... resampledExperimental{i}= evaluateGcvSplines(experimentalSpline, ... labels, modelTime{i}); end -if nargin < 3 +if nargin < 4 figureWidth = ceil(sqrt(numel(labels))); figureHeight = ceil(numel(labels)/figureWidth); -elseif nargin < 4 +elseif nargin < 5 figureHeight = ceil(sqrt(numel(labels))); end figureSize = figureWidth * figureHeight; @@ -80,6 +81,13 @@ function plotTreatmentOptimizationJointAngles(experimentalFile, ... xlabel(t, "Time [s]") ylabel(t, "Joint Angle [deg]") for i=1:numel(labels) + if model.getCoordinateSet().get(labels(i)).getMotionType() ... + .toString().toCharArray()' ~= "Rotational" + experimentalData(:, i) = experimentalData(:, i) * pi/180; + for j = 1 : numel(modelFiles) + modelData{j}(:, i) = modelData{j}(:, i) * pi/180; + end + end if i > figureSize * figureNumber figureNumber = figureNumber + 1; figure(Name="Treatment Optimization Joint Angles", ... @@ -93,10 +101,10 @@ function plotTreatmentOptimizationJointAngles(experimentalFile, ... end nexttile(subplotNumber); hold on - plot(experimentalTime, experimentalData(:, i), LineWidth=2); - for j = 1 : numel(modelFiles) - plot(modelTime{j}, modelData{j}(:, i), LineWidth=2); - end + plot(experimentalTime, experimentalData(:, i), LineWidth=2); + for j = 1 : numel(modelFiles) + plot(modelTime{j}, modelData{j}(:, i), LineWidth=2); + end hold off titleString = [sprintf("%s", strrep(labels(i), "_", " "))]; for j = 1 : numel(modelFiles) @@ -130,8 +138,14 @@ function plotTreatmentOptimizationJointAngles(experimentalFile, ... minData(j+1) = min(experimentalData(:, i), [], "all"); yLimitUpper = max(maxData); yLimitLower = min(minData); - if yLimitUpper - yLimitLower < 10 - ylim([(yLimitUpper+yLimitLower)/2-10, (yLimitUpper+yLimitLower)/2+10]) + if model.getCoordinateSet().get(labels(i)).getMotionType() ... + .toString().toCharArray()' == "Rotational" + minimum = 10; + else + minimum = 0.1; + end + if yLimitUpper - yLimitLower < minimum + ylim([(yLimitUpper+yLimitLower)/2-minimum, (yLimitUpper+yLimitLower)/2+minimum]) end subplotNumber = subplotNumber + 1; end \ No newline at end of file From 75a965a28d20a246f50ba39b01dce33e852f614b Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Mon, 11 Mar 2024 03:46:20 -0500 Subject: [PATCH 362/365] remove jerk references --- .../calcMinimizingJointJerkIntegrand.m | 46 ------------------- .../SetupBounds/makeOptimalControlBounds.m | 8 +--- .../generateCostTermStruct.m | 11 ----- .../makeStateDerivatives.m | 3 -- ...reatmentOptimizationDesignVariableBounds.m | 2 - 5 files changed, 2 insertions(+), 68 deletions(-) delete mode 100644 src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointJerkIntegrand.m diff --git a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointJerkIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointJerkIntegrand.m deleted file mode 100644 index 13eb021d5..000000000 --- a/src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointJerkIntegrand.m +++ /dev/null @@ -1,46 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% This function minimizes the joint jerk for the specified coordinate. -% -% (2D matrix, struct, Array of string) -> (Array of number) -% - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -function cost = calcMinimizingJointJerkIntegrand(jointJerks, time, ... - inputs, costTerm) -normalizeByFinalTime = valueOrAlternate(costTerm, ... - "normalize_by_final_time", true); -indx = find(strcmp(convertCharsToStrings(inputs.statesCoordinateNames), ... - costTerm.coordinate)); -if isempty(indx) - throw(MException('CostTermError:CoordinateNotInState', ... - strcat("Coordinate ", costTerm.coordinate, " is not in the ", ... - ""))) -end -% cost = calcMinimizingCostArrayTerm(jointJerks(:, indx)); -cost = jointJerks(:, indx); -if normalizeByFinalTime - cost = cost / time(end); -end -end \ No newline at end of file diff --git a/src/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m b/src/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m index 8bbc36807..4e6769ff2 100644 --- a/src/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m +++ b/src/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m @@ -66,10 +66,6 @@ inputs.jointVelocitiesMultiple * range(stateJointVelocities); minStateVelocities = min(stateJointVelocities) - ... inputs.jointVelocitiesMultiple * range(stateJointVelocities); -% maxStateAccelerations = max(stateJointAccelerations) + ... -% inputs.jointAccelerationsMultiple * range(stateJointAccelerations); -% minStateAccelerations = min(stateJointAccelerations) - ... -% inputs.jointAccelerationsMultiple * range(stateJointAccelerations); inputs.maxState = [ ... maxStatePositions, ... @@ -91,9 +87,9 @@ inputs.statesCoordinateNames); inputs.maxControl = max(stateJointAccelerations) + ... - inputs.controlJerksMultiple * range(stateJointAccelerations); + inputs.jointAccelerationsMultiple * range(stateJointAccelerations); inputs.minControl = min(stateJointAccelerations) - ... - inputs.controlJerksMultiple * range(stateJointAccelerations); + inputs.jointAccelerationsMultiple * range(stateJointAccelerations); if strcmp(inputs.controllerType, 'synergy') maxControlSynergyActivations = inputs.maxControlSynergyActivations * ... diff --git a/src/TreatmentOptimization/generateCostTermStruct.m b/src/TreatmentOptimization/generateCostTermStruct.m index c044406f7..0c40de2c8 100644 --- a/src/TreatmentOptimization/generateCostTermStruct.m +++ b/src/TreatmentOptimization/generateCostTermStruct.m @@ -55,7 +55,6 @@ "external_force_tracking", ... "external_moment_tracking", ... "muscle_activation_tracking", ... - "joint_jerk_minimization", ... ]; case "VerificationOptimization" allowedTypes = [ ... @@ -63,7 +62,6 @@ "generalized_speed_tracking", ... "marker_position_tracking", ... "controller_tracking", ... - "joint_jerk_minimization", ... ]; case "DesignOptimization" allowedTypes = [ ... @@ -76,7 +74,6 @@ "external_moment_tracking", ... "muscle_activation_tracking", ... "controller_tracking", ... - "joint_jerk_minimization", ... "joint_power_minimization" ... "joint_energy_generation_goal" ... "joint_energy_absorption_goal" ... @@ -155,14 +152,6 @@ costTerm.controller ... ); -costTermCalculations.joint_jerk_minimization = @(values, modeledValues, auxdata, costTerm) ... - calcMinimizingJointJerkIntegrand( ... - values.controlJerks, ... - values.time, ... - auxdata, ... - costTerm ... - ); - costTermCalculations.inverse_dynamics_load_tracking = @(values, modeledValues, auxdata, costTerm) ... calcTrackingInverseDynamicLoadsIntegrand( ... costTerm, ... diff --git a/src/TreatmentOptimization/makeStateDerivatives.m b/src/TreatmentOptimization/makeStateDerivatives.m index aa66906e0..689655b84 100644 --- a/src/TreatmentOptimization/makeStateDerivatives.m +++ b/src/TreatmentOptimization/makeStateDerivatives.m @@ -37,7 +37,4 @@ inputs.experimentalJointAccelerations = evaluateGcvSplines( ... inputs.splineJointAngles, inputs.coordinateNames, ... inputs.experimentalTime, 2); -% inputs.experimentalJointJerks = evaluateGcvSplines( ... -% inputs.splineJointAngles, inputs.coordinateNames, ... -% inputs.experimentalTime, 3); end diff --git a/src/core/parse/parseTreatmentOptimizationDesignVariableBounds.m b/src/core/parse/parseTreatmentOptimizationDesignVariableBounds.m index 89585c45f..3ba4b29e8 100644 --- a/src/core/parse/parseTreatmentOptimizationDesignVariableBounds.m +++ b/src/core/parse/parseTreatmentOptimizationDesignVariableBounds.m @@ -32,6 +32,4 @@ 'joint_velocity_range_scale_factor', 1.5); inputs.jointAccelerationsMultiple = parseDoubleOrAlternate(tree, ... 'joint_acceleration_range_scale_factor', 1); -inputs.controlJerksMultiple = parseDoubleOrAlternate(tree, ... - 'joint_jerk_range_scale_factor', 1); end From 532195224ebbbd416681bcedc0b1e9cabe9ca316 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Mon, 11 Mar 2024 03:46:43 -0500 Subject: [PATCH 363/365] fix initial guess check issue with synergy vectors --- .../OptimalControlSetupFunctions/setupGpopsInitialGuess.m | 1 + 1 file changed, 1 insertion(+) diff --git a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m index 1aacd3298..1a6638e38 100644 --- a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m +++ b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m @@ -133,6 +133,7 @@ if isfield(guess, "parameter") guess.parameter = scaleToBounds(guess.parameter, inputs.maxParameter, ... inputs.minParameter); + guess.phase.parameter = guess.parameter; end end From c0720743f3115d4d0656e1522f36059c49a8a6f0 Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Mon, 11 Mar 2024 03:47:00 -0500 Subject: [PATCH 364/365] remove surrogate.m --- src/SurrogateModelCreation/surrogate.m | 55 -------------------------- 1 file changed, 55 deletions(-) delete mode 100644 src/SurrogateModelCreation/surrogate.m diff --git a/src/SurrogateModelCreation/surrogate.m b/src/SurrogateModelCreation/surrogate.m deleted file mode 100644 index ea6e0c1bf..000000000 --- a/src/SurrogateModelCreation/surrogate.m +++ /dev/null @@ -1,55 +0,0 @@ -% This function is part of the NMSM Pipeline, see file for full license. -% -% The following script can be used to create a surrogate model of the -% muscle tendon lengths, moment arms, and muscle tendon velocities - -% ----------------------------------------------------------------------- % -% The NMSM Pipeline is a toolkit for model personalization and treatment % -% optimization of neuromusculoskeletal models through OpenSim. See % -% nmsm.rice.edu and the NOTICE file for more information. The % -% NMSM Pipeline is developed at Rice University and supported by the US % -% National Institutes of Health (R01 EB030520). % -% % -% Copyright (c) 2021 Rice University and the Authors % -% Author(s): Marleny Vega % -% % -% Licensed under the Apache License, Version 2.0 (the "License"); % -% you may not use this file except in compliance with the License. % -% You may obtain a copy of the License at % -% http://www.apache.org/licenses/LICENSE-2.0. % -% % -% Unless required by applicable law or agreed to in writing, software % -% distributed under the License is distributed on an "AS IS" BASIS, % -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or % -% implied. See the License for the specific language governing % -% permissions and limitations under the License. % -% ----------------------------------------------------------------------- % - -%% Required Values -inputSettings.model = 'preprocessed\exampleModel.osim'; -inputSettings.dataDirectory = 'preprocessed'; -inputSettings.epsilon = 1e-4; -inputSettings.polynomialDegree = 5; -% Indicate 'true' or 'false' for performing latin hyper cube sampling -inputSettings.performLatinHyperCubeSampling = 'false'; -% Indicate the coordinates that should be used in the surrogate model -inputSettings.surrogateModelCoordinateNames = ["hip_adduction_l", ... - "hip_flexion_l", "hip_rotation_l", "knee_angle_l", "ankle_angle_l", ... - "subtalar_angle_l", "hip_adduction_r", "hip_flexion_r", ... - "hip_rotation_r", "knee_angle_r", "ankle_angle_r", "subtalar_angle_r"]; - -% If performLatinHyperCubeSampling is set to true, set the range multiplier -% and the number of points used for latin hyper cube sampling, otherwise, -% leave blank -inputSettings.lhsRangeMultiplier = []; -inputSettings.lhsNumPoints = []; - -%% *Optional* Values -% The trial prefix is the prefix of each output file, identifying the -% motion such as 'gait' or 'squat' or 'step_up'. -inputSettings.trialPrefixes = ["gait_1", "gait_2", "gait_3"]; -% Results directory, if blank, results are printed to current directory -inputSettings.resultsDirectory = []; - -%% Create surrogate model -SurrogateModelCreation(inputSettings); \ No newline at end of file From 54766fb501895b3c04f53b4716208bd57e36186e Mon Sep 17 00:00:00 2001 From: "Claire V. Hammond" <61138239+cvhammond@users.noreply.github.com> Date: Mon, 11 Mar 2024 04:53:42 -0500 Subject: [PATCH 365/365] Update getPipelineVersion.m --- src/core/verify/getPipelineVersion.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/verify/getPipelineVersion.m b/src/core/verify/getPipelineVersion.m index 1fd50c700..6c4b59065 100644 --- a/src/core/verify/getPipelineVersion.m +++ b/src/core/verify/getPipelineVersion.m @@ -38,5 +38,5 @@ % ----------------------------------------------------------------------- % function version = getPipelineVersion() -version = "1.0.0"; +version = "1.1.0"; end