diff --git a/resources/project/5Sv8sFw_LYuwxJg20iZ-lSPNe9g/AzXr7-nnbKkLY4Ocyr-4XyD7ZbEd.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/2C8HNJtYzHbxZ89zTLYPH0eACjkd.xml
similarity index 100%
rename from resources/project/5Sv8sFw_LYuwxJg20iZ-lSPNe9g/AzXr7-nnbKkLY4Ocyr-4XyD7ZbEd.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/5Sv8sFw_LYuwxJg20iZ-lSPNe9g/Ke0uWSdo7yBLrhSQYRC9LeEAY_Md.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/53VFX8re2xrBZK_I6s6coIkEp2od.xml
similarity index 100%
rename from resources/project/5Sv8sFw_LYuwxJg20iZ-lSPNe9g/Ke0uWSdo7yBLrhSQYRC9LeEAY_Md.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/7AZXpb090n98KOSsx01-zOUfRQg/3oidn4LRL5GwwDzzeMZiSp7A2O0d.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/APKTMrxvw_tsjjYaM1RWvPNltuId.xml
similarity index 100%
rename from resources/project/7AZXpb090n98KOSsx01-zOUfRQg/3oidn4LRL5GwwDzzeMZiSp7A2O0d.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/7AZXpb090n98KOSsx01-zOUfRQg/LzzgRGTWkydILioLs0JPaa-Nu40d.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/FxSy9vUXUClSoIRvySSkb9FJ14Yd.xml
similarity index 100%
rename from resources/project/7AZXpb090n98KOSsx01-zOUfRQg/LzzgRGTWkydILioLs0JPaa-Nu40d.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/7AZXpb090n98KOSsx01-zOUfRQg/gDdlibUiOY4zsCI36J4PKMPypIId.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/U8NcbPxNm5bF-j3mpONn6IRUmCwd.xml
similarity index 100%
rename from resources/project/7AZXpb090n98KOSsx01-zOUfRQg/gDdlibUiOY4zsCI36J4PKMPypIId.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/7AZXpb090n98KOSsx01-zOUfRQg/tfTJ4kPklZp2baElxH8WVDN7PYkd.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/aOglYJlxQ7H_dGgajqfDY-e5vqUd.xml
similarity index 100%
rename from resources/project/7AZXpb090n98KOSsx01-zOUfRQg/tfTJ4kPklZp2baElxH8WVDN7PYkd.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/BXX9SqJTGGGO_CDj2AJCCAtV84k/BWtL89f91xbulioiaYLBB_yHOgMd.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/eIP3YXp23lrMI5ZyMWc-IP4Hqn4d.xml
similarity index 100%
rename from resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/BWtL89f91xbulioiaYLBB_yHOgMd.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/7AZXpb090n98KOSsx01-zOUfRQg/DvZOqACkqxwPDMpa6RdAuXS_l1sd.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/fQNuH3kIzHIrkOWnRMvBRrzXrjcd.xml
similarity index 100%
rename from resources/project/7AZXpb090n98KOSsx01-zOUfRQg/DvZOqACkqxwPDMpa6RdAuXS_l1sd.xml
rename to resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/fQNuH3kIzHIrkOWnRMvBRrzXrjcd.xml
diff --git a/resources/project/7AZXpb090n98KOSsx01-zOUfRQg/DvZOqACkqxwPDMpa6RdAuXS_l1sp.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/fQNuH3kIzHIrkOWnRMvBRrzXrjcp.xml
similarity index 100%
rename from resources/project/7AZXpb090n98KOSsx01-zOUfRQg/DvZOqACkqxwPDMpa6RdAuXS_l1sp.xml
rename to resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/fQNuH3kIzHIrkOWnRMvBRrzXrjcp.xml
diff --git a/resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/G0w81rMIRi8PzlrGVbGmoa0FV9gd.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/ldXR-gx8Eq1a-lR8dwU1YKhDE0Yd.xml
similarity index 100%
rename from resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/G0w81rMIRi8PzlrGVbGmoa0FV9gd.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/BXX9SqJTGGGO_CDj2AJCCAtV84k/HdiZGhv_rKHUyETdzYHpE0xs7AUd.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/pISJKup1j7W2w1EMvS_ZPVr8UK0d.xml
similarity index 100%
rename from resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/HdiZGhv_rKHUyETdzYHpE0xs7AUd.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/BXX9SqJTGGGO_CDj2AJCCAtV84k/Onj0w7QprQSOKpr22MXMZdoROBAd.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/tcrKjx9o59mYBXfda-WZP3oIMO8d.xml
similarity index 100%
rename from resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/Onj0w7QprQSOKpr22MXMZdoROBAd.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/BXX9SqJTGGGO_CDj2AJCCAtV84k/Qc2wVp_95I6ELlZy_on0j8jGrt4d.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/xkVEl8F2a3LBgQKu1b0gRTUSWF8d.xml
similarity index 100%
rename from resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/Qc2wVp_95I6ELlZy_on0j8jGrt4d.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/BXX9SqJTGGGO_CDj2AJCCAtV84k/cUlln2uasX69yptYpRT0FIXMmN4d.xml b/resources/project/1D-aA_lDjJlRxIHKl8PXRF67F-k/yXpziHQzwjQvtO_d-DRwjgpNZkMd.xml
similarity index 100%
rename from resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/cUlln2uasX69yptYpRT0FIXMmN4d.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/BXX9SqJTGGGO_CDj2AJCCAtV84k/Zoc8C9B4lYxsGTJUXBoNY2847rsd.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/-2H4XpEbDs4LttLPyXfzbPUMCNod.xml
similarity index 100%
rename from resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/Zoc8C9B4lYxsGTJUXBoNY2847rsd.xml
rename to resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/-2H4XpEbDs4LttLPyXfzbPUMCNod.xml
diff --git a/resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/Zoc8C9B4lYxsGTJUXBoNY2847rsp.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/-2H4XpEbDs4LttLPyXfzbPUMCNop.xml
similarity index 100%
rename from resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/Zoc8C9B4lYxsGTJUXBoNY2847rsp.xml
rename to resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/-2H4XpEbDs4LttLPyXfzbPUMCNop.xml
diff --git a/resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/fbFT1H1Yjhkv8Mn3gVUZJX6bG_od.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/8nhMKvP4YlPvbkX7uIqp17R-qc0d.xml
similarity index 100%
rename from resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/fbFT1H1Yjhkv8Mn3gVUZJX6bG_od.xml
rename to resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/8nhMKvP4YlPvbkX7uIqp17R-qc0d.xml
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/resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/mpAb6JLvq2630l0ar-o34Ybxq50d.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/AALSxy2_uPME6FBZ9MBCN1FqHigd.xml
similarity index 100%
rename from resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/mpAb6JLvq2630l0ar-o34Ybxq50d.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/BXX9SqJTGGGO_CDj2AJCCAtV84k/qXqP2bORsPzSqGsTBWvUFdPwO04d.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/GhA7yyZ8gWjUTCoIPLShMR-04gQd.xml
similarity index 100%
rename from resources/project/BXX9SqJTGGGO_CDj2AJCCAtV84k/qXqP2bORsPzSqGsTBWvUFdPwO04d.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/D7EGYpvIiO_nGXQuTOYjsSP_JQw/2EnTnZEx-4v1jC3AJZADMDpEO1gd.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Gpp9YsAsQAJWysZyj1cojVK3X6Md.xml
similarity index 100%
rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/2EnTnZEx-4v1jC3AJZADMDpEO1gd.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/D7EGYpvIiO_nGXQuTOYjsSP_JQw/5LEawZsxA1aBTeqG0NlpCtR6q6kd.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/MEPE9Z1LWKBNy4z5_S8K_-GNMUAd.xml
similarity index 100%
rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/5LEawZsxA1aBTeqG0NlpCtR6q6kd.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/D7EGYpvIiO_nGXQuTOYjsSP_JQw/5TVbg-DxiEwvaH5Bj9CH0ewcKu8d.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/Mf5W7OBG_oZJzTY5xCqxob-FeQYd.xml
similarity index 100%
rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/5TVbg-DxiEwvaH5Bj9CH0ewcKu8d.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/D7EGYpvIiO_nGXQuTOYjsSP_JQw/8uH7KtfLIR_hLVyUlkUrouETOeod.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/PztFgXV8694vmeaWq77sy6knLfEd.xml
similarity index 100%
rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/8uH7KtfLIR_hLVyUlkUrouETOeod.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/D7EGYpvIiO_nGXQuTOYjsSP_JQw/DpnGV8GaSUAjtUgrpQ5nUXOT5wYd.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/R3gn2Qk5TEw-oczFAwJWS5gCjfAd.xml
similarity index 100%
rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/DpnGV8GaSUAjtUgrpQ5nUXOT5wYd.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/D7EGYpvIiO_nGXQuTOYjsSP_JQw/J8XPAkZbsecOIp7IVKNIsrQZCHEd.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/SDs3ORiDaz_ugAcUz3wD_qVQLDgd.xml
similarity index 100%
rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/J8XPAkZbsecOIp7IVKNIsrQZCHEd.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/D7EGYpvIiO_nGXQuTOYjsSP_JQw/JlNlt7b52D5lndUS8QIFBXuyn8sd.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/chzmG-xRr23AFbYxIHXC11oFG9kd.xml
similarity index 100%
rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/JlNlt7b52D5lndUS8QIFBXuyn8sd.xml
rename to resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/chzmG-xRr23AFbYxIHXC11oFG9kd.xml
diff --git a/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/KZjwdxWx7CTdYd34eqZ_RTkgI7gp.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/chzmG-xRr23AFbYxIHXC11oFG9kp.xml
similarity index 100%
rename from resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/KZjwdxWx7CTdYd34eqZ_RTkgI7gp.xml
rename to resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/chzmG-xRr23AFbYxIHXC11oFG9kp.xml
diff --git a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/Nh99zm6VyLbCagFVKH0kx0irGckd.xml b/resources/project/3S1lLYGLIjnYoeqS66xjbiTYaGo/lJReWdGlE_L2vN0iJHuG9_W5z5kd.xml
similarity index 100%
rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/Nh99zm6VyLbCagFVKH0kx0irGckd.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/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_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/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/nW4p5Z6gDt0MyfrwKAnI3MioETUd.xml b/resources/project/6AGol90mQGOIatxNSyMhdaH59gA/3OaAbed4F4CnFYNp5AkGzyUZHUsd.xml
similarity index 100%
rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/nW4p5Z6gDt0MyfrwKAnI3MioETUd.xml
rename to resources/project/6AGol90mQGOIatxNSyMhdaH59gA/3OaAbed4F4CnFYNp5AkGzyUZHUsd.xml
diff --git a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/nW4p5Z6gDt0MyfrwKAnI3MioETUp.xml b/resources/project/6AGol90mQGOIatxNSyMhdaH59gA/3OaAbed4F4CnFYNp5AkGzyUZHUsp.xml
similarity index 100%
rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/nW4p5Z6gDt0MyfrwKAnI3MioETUp.xml
rename to resources/project/6AGol90mQGOIatxNSyMhdaH59gA/3OaAbed4F4CnFYNp5AkGzyUZHUsp.xml
diff --git a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/PUXt85kSoxl2GhAVUeaGOA1ui0sd.xml b/resources/project/6AGol90mQGOIatxNSyMhdaH59gA/KjY44G7WJ_jinQPfkCzkmqHUBu8d.xml
similarity index 100%
rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/PUXt85kSoxl2GhAVUeaGOA1ui0sd.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/D7EGYpvIiO_nGXQuTOYjsSP_JQw/SLN8kBxZYH73a64wQLgTpFlpH-Ud.xml b/resources/project/6AGol90mQGOIatxNSyMhdaH59gA/NFORnXmhiVQSgHA5Autj49jPftYd.xml
similarity index 100%
rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/SLN8kBxZYH73a64wQLgTpFlpH-Ud.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/D7EGYpvIiO_nGXQuTOYjsSP_JQw/V7dcj86ZsVvV3wlDWv_EePrkW7wd.xml b/resources/project/6AGol90mQGOIatxNSyMhdaH59gA/oHpXnrNixlZjK9qYnwuV6eX9gfcd.xml
similarity index 100%
rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/V7dcj86ZsVvV3wlDWv_EePrkW7wd.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/D7EGYpvIiO_nGXQuTOYjsSP_JQw/XDDpmW-ahmfRGLbHVnihtWagQDEd.xml b/resources/project/6AGol90mQGOIatxNSyMhdaH59gA/sgdsvhsCbARvZJ55oWpjfMFPi3Qd.xml
similarity index 100%
rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/XDDpmW-ahmfRGLbHVnihtWagQDEd.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/D7EGYpvIiO_nGXQuTOYjsSP_JQw/rzCzLkJQsijCRt28FvlpiZrjfZ4d.xml b/resources/project/6AGol90mQGOIatxNSyMhdaH59gA/tkgt0Y0wPwlAY1DVmERxblQgvVod.xml
similarity index 100%
rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/rzCzLkJQsijCRt28FvlpiZrjfZ4d.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/D7EGYpvIiO_nGXQuTOYjsSP_JQw/vyBBHUVgKzM20Od7DNtH6-JNEiAd.xml b/resources/project/6AGol90mQGOIatxNSyMhdaH59gA/zf1841WvvYarScoTMS3MPj4JW9cd.xml
similarity index 100%
rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/vyBBHUVgKzM20Od7DNtH6-JNEiAd.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/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/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/QWMJ0IlUTrzTjwye6g1ffgLjfGYd.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/BMJUj3XqFRkbFNxbqjFRrHNTbQUd.xml
similarity index 100%
rename from resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/QWMJ0IlUTrzTjwye6g1ffgLjfGYd.xml
rename to resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/BMJUj3XqFRkbFNxbqjFRrHNTbQUd.xml
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/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/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_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/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/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/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/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/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/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/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/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/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/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/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/_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/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/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/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/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/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/EEtUlUb-dLAdf0KpMVivaUlztwA/IjYbiW-Ya6w-T2Z7_m6dd_k13wwd.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/yrfuaX-0wpNxMk8pjXQImwzryuAd.xml
similarity index 100%
rename from resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/IjYbiW-Ya6w-T2Z7_m6dd_k13wwd.xml
rename to resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/yrfuaX-0wpNxMk8pjXQImwzryuAd.xml
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
diff --git a/resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/z8PCXMWInzrlFrKj4KQg_4fVTZYd.xml b/resources/project/H72L659GuP9YjS6Dt9kIH5c6HnQ/7SrjXNQDv9BGhC0GEOKQspr9Ocgd.xml
similarity index 100%
rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/z8PCXMWInzrlFrKj4KQg_4fVTZYd.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/D7EGYpvIiO_nGXQuTOYjsSP_JQw/zWhjsX1e0jX9kixjwjfDU_xQo9Qd.xml b/resources/project/H72L659GuP9YjS6Dt9kIH5c6HnQ/82flsbuQG_B85H3tl7ti_QR4mXId.xml
similarity index 100%
rename from resources/project/D7EGYpvIiO_nGXQuTOYjsSP_JQw/zWhjsX1e0jX9kixjwjfDU_xQo9Qd.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/Drp6kay-WW1Z2AQWI8KW60j9HlQ/6Fwf5cB_poUbMFePFS_ylV5Ftusd.xml b/resources/project/H72L659GuP9YjS6Dt9kIH5c6HnQ/a0lBcQv35pXv1BECWbcE4pI6X88d.xml
similarity index 100%
rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/6Fwf5cB_poUbMFePFS_ylV5Ftusd.xml
rename to resources/project/H72L659GuP9YjS6Dt9kIH5c6HnQ/a0lBcQv35pXv1BECWbcE4pI6X88d.xml
diff --git a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/6Fwf5cB_poUbMFePFS_ylV5Ftusp.xml b/resources/project/H72L659GuP9YjS6Dt9kIH5c6HnQ/a0lBcQv35pXv1BECWbcE4pI6X88p.xml
similarity index 100%
rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/6Fwf5cB_poUbMFePFS_ylV5Ftusp.xml
rename to resources/project/H72L659GuP9YjS6Dt9kIH5c6HnQ/a0lBcQv35pXv1BECWbcE4pI6X88p.xml
diff --git a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/0amoX63LRAKETLDqV7_F13CuX74d.xml b/resources/project/H72L659GuP9YjS6Dt9kIH5c6HnQ/bV1SapNLHXOJuqxs4eM5UgXt1DMd.xml
similarity index 100%
rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/0amoX63LRAKETLDqV7_F13CuX74d.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/Drp6kay-WW1Z2AQWI8KW60j9HlQ/6GJfsBaU_5DGhbimCNcp3Txx-v8d.xml b/resources/project/H72L659GuP9YjS6Dt9kIH5c6HnQ/wfGjJ216NLwm4I2ndZI3_86uhgQd.xml
similarity index 100%
rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/6GJfsBaU_5DGhbimCNcp3Txx-v8d.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/Drp6kay-WW1Z2AQWI8KW60j9HlQ/888MRa7OWq3oIOyCKPp0I2UIiKAd.xml b/resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/Eb3URaLnOadzaECH9JhrMNGK0cYd.xml
similarity index 100%
rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/888MRa7OWq3oIOyCKPp0I2UIiKAd.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/Drp6kay-WW1Z2AQWI8KW60j9HlQ/BEHll27af-LsZgmvfXGuLyYJyU0d.xml b/resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/GUD97Kgby-Nbe7a4937yGMYWqoQd.xml
similarity index 100%
rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/BEHll27af-LsZgmvfXGuLyYJyU0d.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/Drp6kay-WW1Z2AQWI8KW60j9HlQ/CgrwWh_FTjN531XPRoLVTVqLbZEd.xml b/resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/IXrhsh0KA0Hd3dEHjxbDlSwV_9wd.xml
similarity index 100%
rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/CgrwWh_FTjN531XPRoLVTVqLbZEd.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/Drp6kay-WW1Z2AQWI8KW60j9HlQ/GSxmGWKoE8yVg8Dt0_WIVqZwfq8d.xml b/resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/_cZck6op8VccwtbtBtqU08jEoNgd.xml
similarity index 100%
rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/GSxmGWKoE8yVg8Dt0_WIVqZwfq8d.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/Drp6kay-WW1Z2AQWI8KW60j9HlQ/GmURLyn29gRlewEo8pphTKMkvfcd.xml b/resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/poWgzYIOpdVLERL5CAxxonZKagod.xml
similarity index 100%
rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/GmURLyn29gRlewEo8pphTKMkvfcd.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/Drp6kay-WW1Z2AQWI8KW60j9HlQ/HinGYaWsjaNlqTK_j5cQYUlmn_4d.xml b/resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/veTEyGEjchushQj-PUakRWJ1Rfwd.xml
similarity index 100%
rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/HinGYaWsjaNlqTK_j5cQYUlmn_4d.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/Drp6kay-WW1Z2AQWI8KW60j9HlQ/K7gRh-Ets4DP9XdM-8WQS6MYiTAd.xml b/resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/wXFztAEfZBGlWkniv1yDOZUPMLkd.xml
similarity index 100%
rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/K7gRh-Ets4DP9XdM-8WQS6MYiTAd.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/HV6_iJQOFZrKU26X_hF7trjvkeM/MMRr3Fv-bYGGSvC6xcJnwOYvzGwd.xml b/resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/yG6FN90UXfIzKIRJPeCgEK9wsiAd.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/MMRr3Fv-bYGGSvC6xcJnwOYvzGwd.xml
rename to resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/yG6FN90UXfIzKIRJPeCgEK9wsiAd.xml
diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/MMRr3Fv-bYGGSvC6xcJnwOYvzGwp.xml b/resources/project/MxOdJHAcr56wMe-tpEEz-LoiQlY/yG6FN90UXfIzKIRJPeCgEK9wsiAp.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/MMRr3Fv-bYGGSvC6xcJnwOYvzGwp.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/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/Drp6kay-WW1Z2AQWI8KW60j9HlQ/QQxpEF0mWnNdoQybWJnETf5i2zgd.xml b/resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/-Lw-S9LvX_FYDRFafe-L_l8cTMsd.xml
similarity index 100%
rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/QQxpEF0mWnNdoQybWJnETf5i2zgd.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/Drp6kay-WW1Z2AQWI8KW60j9HlQ/UlAa7CZxUr955fLFiY8150xe-0Yd.xml b/resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/4JNkAFleLVxY3nQGnz12ugS4ZTEd.xml
similarity index 100%
rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/UlAa7CZxUr955fLFiY8150xe-0Yd.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/Drp6kay-WW1Z2AQWI8KW60j9HlQ/iPx7LMwL8D8SvprpBzhO3qxwy0Id.xml b/resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/4hmmxZ0XovlFSmc-Rsau_8EpzNkd.xml
similarity index 100%
rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/iPx7LMwL8D8SvprpBzhO3qxwy0Id.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/Drp6kay-WW1Z2AQWI8KW60j9HlQ/kyvnzrdUBXgtJ7QWloYIMdgg3f0d.xml b/resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/5lEWuAPjsUhI7_hCP31KFq7iDlUd.xml
similarity index 100%
rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/kyvnzrdUBXgtJ7QWloYIMdgg3f0d.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/Drp6kay-WW1Z2AQWI8KW60j9HlQ/mIYcND4GVDO0WgnXvKhJVZw4Qdsd.xml b/resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/DK5W7ZmXIHOHuwnXHtwNGL22p4Md.xml
similarity index 100%
rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/mIYcND4GVDO0WgnXvKhJVZw4Qdsd.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/Hk1Vip6VmTG9gpBtNOXKEU56oUc/H7mS2g0N2I6TXTkSevNbSUWfbRgd.xml b/resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/GPtTjnSsEKTMO1S-sg_FP4dxbbEd.xml
similarity index 100%
rename from resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/H7mS2g0N2I6TXTkSevNbSUWfbRgd.xml
rename to resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/GPtTjnSsEKTMO1S-sg_FP4dxbbEd.xml
diff --git a/resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/H7mS2g0N2I6TXTkSevNbSUWfbRgp.xml b/resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/GPtTjnSsEKTMO1S-sg_FP4dxbbEp.xml
similarity index 100%
rename from resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/H7mS2g0N2I6TXTkSevNbSUWfbRgp.xml
rename to resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/GPtTjnSsEKTMO1S-sg_FP4dxbbEp.xml
diff --git a/resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/wOcCxJrf1WxhN3mYz0ASwydFXsMd.xml b/resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/J4NSMqZEAmFZH35SWLSatqliZmkd.xml
similarity index 100%
rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/wOcCxJrf1WxhN3mYz0ASwydFXsMd.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/Drp6kay-WW1Z2AQWI8KW60j9HlQ/zRzAsvX6Os0bPYij1y_5pG-94Dgd.xml b/resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/eOoHRDjyUSBj8-WLEX9mxUx6ePUd.xml
similarity index 100%
rename from resources/project/Drp6kay-WW1Z2AQWI8KW60j9HlQ/zRzAsvX6Os0bPYij1y_5pG-94Dgd.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/HV6_iJQOFZrKU26X_hF7trjvkeM/-3QAULuhtItSD7_pHpYyutizqKcd.xml b/resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/mjVTgOO6IWMthsneycvZqWzxD_gd.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/-3QAULuhtItSD7_pHpYyutizqKcd.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/HV6_iJQOFZrKU26X_hF7trjvkeM/-44zf-4QbDVnfxvdMOlS-62neokd.xml b/resources/project/PL5Ga2JyphJjO2f0V1lt1v9mOfQ/nVExW6oxyLwVGXPnnA3XaUC50Jcd.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/-44zf-4QbDVnfxvdMOlS-62neokd.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/HV6_iJQOFZrKU26X_hF7trjvkeM/-pKErZtYRVQsLNI-s6dhlLE9rN0d.xml b/resources/project/PMTiY8ii3Ycb5X733RMaGCNVItA/6tuQeWy9960NGsG8vq2g6XEbfs8d.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/-pKErZtYRVQsLNI-s6dhlLE9rN0d.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/HV6_iJQOFZrKU26X_hF7trjvkeM/00XLiFvyqFehoPigl9ogalyjfbAd.xml b/resources/project/PMTiY8ii3Ycb5X733RMaGCNVItA/A8ncUuxWwwZj8vBXXThduDRmo-Id.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/00XLiFvyqFehoPigl9ogalyjfbAd.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/HV6_iJQOFZrKU26X_hF7trjvkeM/0EOTP3mEDOzkS1q64hgbqKxftLId.xml b/resources/project/PMTiY8ii3Ycb5X733RMaGCNVItA/J95nrDPGFnMMRe-j9IrlrD3MJIwd.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/0EOTP3mEDOzkS1q64hgbqKxftLId.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/NAFZMRUtFQYPd30MjCtFUUPa5w4/rfOlYVfPM98QhiJbvbuozkKdZQkd.xml b/resources/project/PMTiY8ii3Ycb5X733RMaGCNVItA/cmZk236s6Qn7CSYmgTSLERo6-Fod.xml
similarity index 100%
rename from resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/rfOlYVfPM98QhiJbvbuozkKdZQkd.xml
rename to resources/project/PMTiY8ii3Ycb5X733RMaGCNVItA/cmZk236s6Qn7CSYmgTSLERo6-Fod.xml
diff --git a/resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/rfOlYVfPM98QhiJbvbuozkKdZQkp.xml b/resources/project/PMTiY8ii3Ycb5X733RMaGCNVItA/cmZk236s6Qn7CSYmgTSLERo6-Fop.xml
similarity index 100%
rename from resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/rfOlYVfPM98QhiJbvbuozkKdZQkp.xml
rename to resources/project/PMTiY8ii3Ycb5X733RMaGCNVItA/cmZk236s6Qn7CSYmgTSLERo6-Fop.xml
diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/1AUPZjF-DkxHBgstLlITifCjfHAd.xml b/resources/project/PMTiY8ii3Ycb5X733RMaGCNVItA/gdgc-JZm4OTNPgAUaiKWok7sS8Qd.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/1AUPZjF-DkxHBgstLlITifCjfHAd.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/OLuX-8c-UOqeJnrM2amm241SKTs/NVEzjmOMA7-YV0Y8D0XmF7N5Qe4d.xml b/resources/project/R6m7AxFfSX7StHtVJ9WG44h96GM/tkAoR0wDeR9bDqF10NY7YqXFvL4d.xml
similarity index 100%
rename from resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/NVEzjmOMA7-YV0Y8D0XmF7N5Qe4d.xml
rename to resources/project/R6m7AxFfSX7StHtVJ9WG44h96GM/tkAoR0wDeR9bDqF10NY7YqXFvL4d.xml
diff --git a/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4p.xml b/resources/project/R6m7AxFfSX7StHtVJ9WG44h96GM/tkAoR0wDeR9bDqF10NY7YqXFvL4p.xml
similarity index 100%
rename from resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4p.xml
rename to resources/project/R6m7AxFfSX7StHtVJ9WG44h96GM/tkAoR0wDeR9bDqF10NY7YqXFvL4p.xml
diff --git a/resources/project/R6m7AxFfSX7StHtVJ9WG44h96GM/D7EGYpvIiO_nGXQuTOYjsSP_JQwd.xml b/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/3S1lLYGLIjnYoeqS66xjbiTYaGod.xml
similarity index 100%
rename from resources/project/R6m7AxFfSX7StHtVJ9WG44h96GM/D7EGYpvIiO_nGXQuTOYjsSP_JQwd.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/R6m7AxFfSX7StHtVJ9WG44h96GM/Drp6kay-WW1Z2AQWI8KW60j9HlQd.xml b/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/SM-7XD5VEsEA2ReW_Gcx0KdVR4Ed.xml
similarity index 100%
rename from resources/project/R6m7AxFfSX7StHtVJ9WG44h96GM/Drp6kay-WW1Z2AQWI8KW60j9HlQd.xml
rename to resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/SM-7XD5VEsEA2ReW_Gcx0KdVR4Ed.xml
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/HV6_iJQOFZrKU26X_hF7trjvkeM/2FIZqjX_-rJNGXzNKJW_vC24H0Ud.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/-K_tTgFkd3KuOQq4OW0mOmsQS44d.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/2FIZqjX_-rJNGXzNKJW_vC24H0Ud.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/HV6_iJQOFZrKU26X_hF7trjvkeM/3vPrjfHdpkKcTeJZLymuPjPeJLod.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/-ZOcmw4c1B4bINk-xUWH7ahRKJod.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/3vPrjfHdpkKcTeJZLymuPjPeJLod.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/HV6_iJQOFZrKU26X_hF7trjvkeM/6zc8a3V6P8-0WJxogD3UrSBw7mEd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/00BmM5mTpuxcj5K8jG604UO7-Ewd.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/6zc8a3V6P8-0WJxogD3UrSBw7mEd.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/HV6_iJQOFZrKU26X_hF7trjvkeM/9K4DX640tmrkmoBLflTkiNfPiZId.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/0TH8tA8-wfGMXu-ho-n1gCC4wJsd.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/9K4DX640tmrkmoBLflTkiNfPiZId.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/HV6_iJQOFZrKU26X_hF7trjvkeM/AFQGXDiIsf-NM9eQ8n4eWMrVd1Yd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/2lZWGayY8sTSf6C_9MNt1ciblccd.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/AFQGXDiIsf-NM9eQ8n4eWMrVd1Yd.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/HV6_iJQOFZrKU26X_hF7trjvkeM/BNAgggSUW1I0e_gemsww4ojmprsd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/4NaiUnhsCfsLNNAYb_2Nre3n59Ud.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/BNAgggSUW1I0e_gemsww4ojmprsd.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/HV6_iJQOFZrKU26X_hF7trjvkeM/Dg1e3Am5YrqqMqqkIYDk9ZjR1RYd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/4edCiafrq1BIvyyh61g6Z-ot9R4d.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/Dg1e3Am5YrqqMqqkIYDk9ZjR1RYd.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/HV6_iJQOFZrKU26X_hF7trjvkeM/G6IjDtcuN86KCffyBg9hhLwxfM8d.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/4x4IMS_bCxBIpOSTkeIwMAKuTawd.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/G6IjDtcuN86KCffyBg9hhLwxfM8d.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/HV6_iJQOFZrKU26X_hF7trjvkeM/K2LUhs-8CtBjbprCCg-2KajWW0Qd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/5G1NQ5WxNVqz_u56bBC5V8s_cnMd.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/K2LUhs-8CtBjbprCCg-2KajWW0Qd.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/HV6_iJQOFZrKU26X_hF7trjvkeM/LK5LTNCFyhwaQZUlosrnunT2xZId.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/5OtYPmi9zdVJFCfszj3XmwBOicQd.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/LK5LTNCFyhwaQZUlosrnunT2xZId.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/HV6_iJQOFZrKU26X_hF7trjvkeM/LN9DpJcyR6CE_I14ywn3ncJAeXId.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/AiCl4Nv-4TcAuyccq2bcsaO4hGId.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/LN9DpJcyR6CE_I14ywn3ncJAeXId.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/HV6_iJQOFZrKU26X_hF7trjvkeM/MgsQiSKNdqjh2tb2GSg9xpGhlMgd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/BzVxW2mFaL8w6fW-2i_Ks4AtAywd.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/MgsQiSKNdqjh2tb2GSg9xpGhlMgd.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/HV6_iJQOFZrKU26X_hF7trjvkeM/P6lyIvtEaIp3hBgXmHLrvNf9nhod.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/CwijU_1EfQOTRpwgKbRX29DCRqod.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/P6lyIvtEaIp3hBgXmHLrvNf9nhod.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/HV6_iJQOFZrKU26X_hF7trjvkeM/R9uo8TWlIFmDZ_U2L3brdMMNpmUd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/D6bNMzhGpxvqGj045koQvCPRgSgd.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/R9uo8TWlIFmDZ_U2L3brdMMNpmUd.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/HV6_iJQOFZrKU26X_hF7trjvkeM/Rgu_Xpn3p1FJCz3744RsTXGnSX4d.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/FhhflOTx83dyAZA7C5LZFylsG3Qd.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/Rgu_Xpn3p1FJCz3744RsTXGnSX4d.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/SS1oWs8uWRjwpAsBEkjHKIdli3od.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/G8qY3zLR3J2KmeRH1uVSXh4Feewd.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/SS1oWs8uWRjwpAsBEkjHKIdli3od.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/T2fYvrqhpvDyuKc8Y7wsu2vaPcUd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/L2wh56GmocdhTwvqoEVyLSPjn8Id.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/T2fYvrqhpvDyuKc8Y7wsu2vaPcUd.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/TcR3rZOrVrHqDGA6Ls0qBpAOHD0d.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/NgzKbmuSLrYttzedhNh5jGcAk2od.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/TcR3rZOrVrHqDGA6Ls0qBpAOHD0d.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/R6m7AxFfSX7StHtVJ9WG44h96GM/dYvx6wp33Ts8hX1S_rzdiQ3NP4Ad.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/NrbvTPDfIXgykDJkfPdIZyDvtVgd.xml
similarity index 100%
rename from resources/project/R6m7AxFfSX7StHtVJ9WG44h96GM/dYvx6wp33Ts8hX1S_rzdiQ3NP4Ad.xml
rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/NrbvTPDfIXgykDJkfPdIZyDvtVgd.xml
diff --git a/resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/NVEzjmOMA7-YV0Y8D0XmF7N5Qe4p.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/NrbvTPDfIXgykDJkfPdIZyDvtVgp.xml
similarity index 100%
rename from resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/NVEzjmOMA7-YV0Y8D0XmF7N5Qe4p.xml
rename to resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/NrbvTPDfIXgykDJkfPdIZyDvtVgp.xml
diff --git a/resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/Vfp2GuJhjHgccgR8Joly_nEH3uUd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/P0FXd6AwKsPzBiYwIZLsEUD5VrAd.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/Vfp2GuJhjHgccgR8Joly_nEH3uUd.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/XeBJu8m_oaoL52pMHUpwH6lM5Fod.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/TAq2STpvx67m5-UfPyxz4oWjUz8d.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/XeBJu8m_oaoL52pMHUpwH6lM5Fod.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/YAj-Sg2BTfUPlNkFYhjGuYYGyWId.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/Tl-XR5iwVw4PrAcBNW_SjCclo-kd.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/YAj-Sg2BTfUPlNkFYhjGuYYGyWId.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/YhkhMqMTT1rlsumwiMqdjRVcg2Qd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/V_NWUez2zLHkRy0FgbfpoMa561Ed.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/YhkhMqMTT1rlsumwiMqdjRVcg2Qd.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/ZqK39Xv3A0I-0ohwRkWV6jbAW9Md.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/X1p2mbSsnW7dQJV2bKNOREnOyY4d.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/ZqK39Xv3A0I-0ohwRkWV6jbAW9Md.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/aSbr7z_N-_xgJQSr5hrZLyKPz4cd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/YkEXBoi2NKObNUB8qZYLCaOkxj4d.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/aSbr7z_N-_xgJQSr5hrZLyKPz4cd.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/aj3y-ODStp8yUzt7MUXbgCsT1OYd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/aukBLYh0oaCR-vLwva9Y2OCkQzUd.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/aj3y-ODStp8yUzt7MUXbgCsT1OYd.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/eZwFGli01gSYZ44Ebb44wZJEZbMd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/b14wfAQdUmLf7zWEQ4kujKfjTQYd.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/eZwFGli01gSYZ44Ebb44wZJEZbMd.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/j0ffgGF4ix7R_1weXjtRLJ2m60od.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/dPt59MZaEXeu_JPlWh7RDKtusxUd.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/j0ffgGF4ix7R_1weXjtRLJ2m60od.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/jCqmmsqwCvwa4Nzg0x8RYjbjld8d.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/eCuRoIFEDG25vAewgv-nf3GqNI0d.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/jCqmmsqwCvwa4Nzg0x8RYjbjld8d.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/l0P1VTKdxzsxQxR0_jCuzcfb2UEd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/iYmVif7LRRnGYE-Zeun_xHMvrVUd.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/l0P1VTKdxzsxQxR0_jCuzcfb2UEd.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/lrtTrV3gL6zhRL5HQl_mNRj1jiId.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/iqU8I5FyZGvsOpUbA1lqu65aWYsd.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/lrtTrV3gL6zhRL5HQl_mNRj1jiId.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/mZYgr5isIaGLkE3fNy5zsSlr_38d.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/j6pwo65gaiPoea-wYOGH4DewgEcd.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/mZYgr5isIaGLkE3fNy5zsSlr_38d.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/n6yybQJW-SCbwLJB8CxlywM5r38d.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/lsw_ecgYyohO6CkqAqCk2bJqNuMd.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/n6yybQJW-SCbwLJB8CxlywM5r38d.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/oaOhkesibNoJGQ_fNXCCFrNYNekd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/ny2aRTbOyjYb8JDFd4dItADtN4Id.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/oaOhkesibNoJGQ_fNXCCFrNYNekd.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/smVcGh3dB4hFNj3JUwzxfAKA5NQd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/oLERgIPUw52TmzFNtvUaq1v3Hykd.xml
similarity index 100%
rename from resources/project/HV6_iJQOFZrKU26X_hF7trjvkeM/smVcGh3dB4hFNj3JUwzxfAKA5NQd.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/Hk1Vip6VmTG9gpBtNOXKEU56oUc/2ReW6jdy5OFPurN6zA_O_5XHgZ4d.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/pegGFSZoGR96d4YxbbK-T9-uicsd.xml
similarity index 100%
rename from resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/2ReW6jdy5OFPurN6zA_O_5XHgZ4d.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/Hk1Vip6VmTG9gpBtNOXKEU56oUc/418XLOPub0IQmO32_fwbHEZ9aWQd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/qMOfCxA6oAYXN36hOE_emzY7-K4d.xml
similarity index 100%
rename from resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/418XLOPub0IQmO32_fwbHEZ9aWQd.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/Hk1Vip6VmTG9gpBtNOXKEU56oUc/7YGtSs8Fx6KVMJ6VnXcbLgutukUd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/sgs-V8op5u7eBxQMg_dpYIMx_r0d.xml
similarity index 100%
rename from resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/7YGtSs8Fx6KVMJ6VnXcbLgutukUd.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/Hk1Vip6VmTG9gpBtNOXKEU56oUc/AikK6lr_xPm04OOxDRyD3uEU6LYd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/wlzHFGaUXvZky7Iz4g5QWWPjTZod.xml
similarity index 100%
rename from resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/AikK6lr_xPm04OOxDRyD3uEU6LYd.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/Hk1Vip6VmTG9gpBtNOXKEU56oUc/YDXlBTZHLxkb4xHzi3Nh_OCFRwYd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/xjfA1VBG4pGbE5V9ER3DoEdSXfcd.xml
similarity index 100%
rename from resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/YDXlBTZHLxkb4xHzi3Nh_OCFRwYd.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/Hk1Vip6VmTG9gpBtNOXKEU56oUc/anHfLHy8dClzx6n_jtHyG6OnAKgd.xml b/resources/project/SJuj0lQEL2kv8DN93wpDxprjL2Q/zE7jRze1WGVc9GxZNzvBJKkpyN0d.xml
similarity index 100%
rename from resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/anHfLHy8dClzx6n_jtHyG6OnAKgd.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/Hk1Vip6VmTG9gpBtNOXKEU56oUc/fEvuKgmkueTDlYz7GM2hOxML--sd.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/A3BkjYsewYZv3b_c9MKlO0lijG4d.xml
similarity index 100%
rename from resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/fEvuKgmkueTDlYz7GM2hOxML--sd.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/Hk1Vip6VmTG9gpBtNOXKEU56oUc/jL11JwpwvA44_BZZe0gbxo4n_7cd.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/E73eq9DGYjhriVhYYpv10_eQeFgd.xml
similarity index 100%
rename from resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/jL11JwpwvA44_BZZe0gbxo4n_7cd.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/WBRnwihdNLBXSPBzOwR6sb9ZOfU/yghkeR-GbtXqJCQypYRbzGf6WhMd.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/WMyozuhOFw24HLR_4y6r67mZpicd.xml
similarity index 100%
rename from resources/project/WBRnwihdNLBXSPBzOwR6sb9ZOfU/yghkeR-GbtXqJCQypYRbzGf6WhMd.xml
rename to resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/WMyozuhOFw24HLR_4y6r67mZpicd.xml
diff --git a/resources/project/WBRnwihdNLBXSPBzOwR6sb9ZOfU/yghkeR-GbtXqJCQypYRbzGf6WhMp.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/WMyozuhOFw24HLR_4y6r67mZpicp.xml
similarity index 100%
rename from resources/project/WBRnwihdNLBXSPBzOwR6sb9ZOfU/yghkeR-GbtXqJCQypYRbzGf6WhMp.xml
rename to resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/WMyozuhOFw24HLR_4y6r67mZpicp.xml
diff --git a/resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/upiRJjWx9gByawb3bF6KtCjux-sd.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/bhkbou8oMnNGwgUfTIp5aAoS2scd.xml
similarity index 100%
rename from resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/upiRJjWx9gByawb3bF6KtCjux-sd.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/Hk1Vip6VmTG9gpBtNOXKEU56oUc/zAMhJ-uBIliAXbSkNK3rdwXDaHod.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/gY40mCv5zigjIbgF70Tlefoo_mgd.xml
similarity index 100%
rename from resources/project/Hk1Vip6VmTG9gpBtNOXKEU56oUc/zAMhJ-uBIliAXbSkNK3rdwXDaHod.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/NAFZMRUtFQYPd30MjCtFUUPa5w4/1Fsij9kn0jCc5z8BpveJ5Pixotwd.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/iE5EXX33uQ1jkGTVTBI1zz34bFMd.xml
similarity index 100%
rename from resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/1Fsij9kn0jCc5z8BpveJ5Pixotwd.xml
rename to resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/iE5EXX33uQ1jkGTVTBI1zz34bFMd.xml
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/NAFZMRUtFQYPd30MjCtFUUPa5w4/29H25KnJzx-eH6hkyDOSNHx0NX0d.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/mBZmtBM2h2hK_XmPgKPYO7mRtqYd.xml
similarity index 100%
rename from resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/29H25KnJzx-eH6hkyDOSNHx0NX0d.xml
rename to resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/mBZmtBM2h2hK_XmPgKPYO7mRtqYd.xml
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/NAFZMRUtFQYPd30MjCtFUUPa5w4/2NE3JJ7Cwpfj1Twd1MZdwGK46R8d.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/mCKHnf8EjNZ4xKZB_R8pR0AMbNUd.xml
similarity index 100%
rename from resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/2NE3JJ7Cwpfj1Twd1MZdwGK46R8d.xml
rename to resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/mCKHnf8EjNZ4xKZB_R8pR0AMbNUd.xml
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/NAFZMRUtFQYPd30MjCtFUUPa5w4/3L_f_vjNe-AFLP06A5juF8kMGLEd.xml b/resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/upN0pl2OtxHu1LpD1nHN8MyUJjwd.xml
similarity index 100%
rename from resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/3L_f_vjNe-AFLP06A5juF8kMGLEd.xml
rename to resources/project/SM-7XD5VEsEA2ReW_Gcx0KdVR4E/upN0pl2OtxHu1LpD1nHN8MyUJjwd.xml
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/resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/3zKgrWYZ4jPOoOXQS_i9ShO4XZcd.xml b/resources/project/TMR2PLz8-jf_t72xY3w-eiAkMwc/3HnVzJ8uifpbOyg21-U4RCJN4GQd.xml
similarity index 100%
rename from resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/3zKgrWYZ4jPOoOXQS_i9ShO4XZcd.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/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/7AZXpb090n98KOSsx01-zOUfRQgd.xml b/resources/project/TMR2PLz8-jf_t72xY3w-eiAkMwc/7zdx9TfzV1uKVmlJRWzGdJyAcSYd.xml
similarity index 100%
rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/7AZXpb090n98KOSsx01-zOUfRQgd.xml
rename to resources/project/TMR2PLz8-jf_t72xY3w-eiAkMwc/7zdx9TfzV1uKVmlJRWzGdJyAcSYd.xml
diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/REL0fj6PT2tNiayKyTrZEt9--acp.xml b/resources/project/TMR2PLz8-jf_t72xY3w-eiAkMwc/7zdx9TfzV1uKVmlJRWzGdJyAcSYp.xml
similarity index 100%
rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/REL0fj6PT2tNiayKyTrZEt9--acp.xml
rename to resources/project/TMR2PLz8-jf_t72xY3w-eiAkMwc/7zdx9TfzV1uKVmlJRWzGdJyAcSYp.xml
diff --git a/resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/a95L4gsH7xba014Rtg5I-RtazfUd.xml b/resources/project/TMR2PLz8-jf_t72xY3w-eiAkMwc/DAP2fOCUoOsvQKZryp7QTWtvdegd.xml
similarity index 100%
rename from resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/a95L4gsH7xba014Rtg5I-RtazfUd.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/NAFZMRUtFQYPd30MjCtFUUPa5w4/cfs6_1u5uuKLLIe6KzyVp8pn_mUd.xml b/resources/project/TMR2PLz8-jf_t72xY3w-eiAkMwc/ccZjrHRR1lFL21N1ZWdseSM37NQd.xml
similarity index 100%
rename from resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/cfs6_1u5uuKLLIe6KzyVp8pn_mUd.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/NAFZMRUtFQYPd30MjCtFUUPa5w4/dnwHH5dkTAu93LmDUIIdJRW11zUd.xml b/resources/project/TMR2PLz8-jf_t72xY3w-eiAkMwc/omg-Qmyr2Hpg5ztIiWxX0q9_f_sd.xml
similarity index 100%
rename from resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/dnwHH5dkTAu93LmDUIIdJRW11zUd.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/NAFZMRUtFQYPd30MjCtFUUPa5w4/qkgsXBKP6tNxSqIGkUXqoIOmfAYd.xml b/resources/project/TMR2PLz8-jf_t72xY3w-eiAkMwc/rNLU-8Md7l99hBksJZlE3as2Y98d.xml
similarity index 100%
rename from resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/qkgsXBKP6tNxSqIGkUXqoIOmfAYd.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/NAFZMRUtFQYPd30MjCtFUUPa5w4/rnnM4E9PpZDSGfoRKAwKIIqTIq0d.xml b/resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/8FB9zoKe0-Lw7d_EeJv8WYCX-50d.xml
similarity index 100%
rename from resources/project/NAFZMRUtFQYPd30MjCtFUUPa5w4/rnnM4E9PpZDSGfoRKAwKIIqTIq0d.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/OLuX-8c-UOqeJnrM2amm241SKTs/-xMsha79hmE0qOhn3z2Y4wwJaNkd.xml b/resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/90YW2GOol29Apu9oHzv3q7eI8ZMd.xml
similarity index 100%
rename from resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/-xMsha79hmE0qOhn3z2Y4wwJaNkd.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/OLuX-8c-UOqeJnrM2amm241SKTs/0oyoesBN2_oayH0i7gtHJp890Esd.xml b/resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/F1UMXHKWKyyeAiNCKU0ON-Vy2ugd.xml
similarity index 100%
rename from resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/0oyoesBN2_oayH0i7gtHJp890Esd.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/OLuX-8c-UOqeJnrM2amm241SKTs/G2x_hSFRzJNj2RwsZ4N6TWYsHmYd.xml b/resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/SkcnLeu8zuRRgbMYy4nnaoFftigd.xml
similarity index 100%
rename from resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/G2x_hSFRzJNj2RwsZ4N6TWYsHmYd.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/OLuX-8c-UOqeJnrM2amm241SKTs/T9wikiXK6MjHu7XyyDA0kuCTP_kd.xml b/resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/X3U50T46GsNoWWkV12vFPTU5IkAd.xml
similarity index 100%
rename from resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/T9wikiXK6MjHu7XyyDA0kuCTP_kd.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/OLuX-8c-UOqeJnrM2amm241SKTs/jupI72lBOnUNFR-WjgobwXS0zMod.xml b/resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/_p4C4Xaqbc2CySSpIr_XPlz39_8d.xml
similarity index 100%
rename from resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/jupI72lBOnUNFR-WjgobwXS0zMod.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/OLuX-8c-UOqeJnrM2amm241SKTs/kJV53JKRAFPnFBgXx06VMpdr0ssd.xml b/resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/bApZCS4bpdgHa0xKhfxkSKy3kIcd.xml
similarity index 100%
rename from resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/kJV53JKRAFPnFBgXx06VMpdr0ssd.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/OLuX-8c-UOqeJnrM2amm241SKTs/rx1MnRhyaD-dcP1I9WcseP2PGiwd.xml b/resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/dC8vrHmGxRVrn1Vw7pYBaCb25tUd.xml
similarity index 100%
rename from resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/rx1MnRhyaD-dcP1I9WcseP2PGiwd.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/OLuX-8c-UOqeJnrM2amm241SKTs/wSDXMLBIRWsCGVgqLP9ER---lO4d.xml b/resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/slGRI8GHw3J7mTWp953EhtGCuB8d.xml
similarity index 100%
rename from resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/wSDXMLBIRWsCGVgqLP9ER---lO4d.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/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/BXX9SqJTGGGO_CDj2AJCCAtV84kd.xml b/resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/uIWV6l4leGqpetubaJSYVYzic4Ad.xml
similarity index 100%
rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/BXX9SqJTGGGO_CDj2AJCCAtV84kd.xml
rename to resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/uIWV6l4leGqpetubaJSYVYzic4Ad.xml
diff --git a/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/kRJYdCzyq5zNeYLPJeKdZmiLSJEp.xml b/resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/uIWV6l4leGqpetubaJSYVYzic4Ap.xml
similarity index 100%
rename from resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/kRJYdCzyq5zNeYLPJeKdZmiLSJEp.xml
rename to resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/uIWV6l4leGqpetubaJSYVYzic4Ap.xml
diff --git a/resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/yrvDAxHTmqAbl2v6-rWp7tgRl4Ed.xml b/resources/project/UeukJVHJUwLZ1oSw-yBfMdSLcNI/wbJvniNn9pHSMYYk4-SDjMEBinAd.xml
similarity index 100%
rename from resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/yrvDAxHTmqAbl2v6-rWp7tgRl4Ed.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/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/B3qliqlfxQ02trRoxA9bnJZ8sxYd.xml b/resources/project/Zyy4GNpD2r3q5z6rVbyG2A5CwKE/EeuwlRNjbNsLmr2_PZkiFY_uHO0d.xml
similarity index 100%
rename from resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/B3qliqlfxQ02trRoxA9bnJZ8sxYd.xml
rename to resources/project/Zyy4GNpD2r3q5z6rVbyG2A5CwKE/EeuwlRNjbNsLmr2_PZkiFY_uHO0d.xml
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/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/HV6_iJQOFZrKU26X_hF7trjvkeMd.xml b/resources/project/Zyy4GNpD2r3q5z6rVbyG2A5CwKE/SsTOiEKMfihmt3ZlKtT9xs8reekd.xml
similarity index 100%
rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/HV6_iJQOFZrKU26X_hF7trjvkeMd.xml
rename to resources/project/Zyy4GNpD2r3q5z6rVbyG2A5CwKE/SsTOiEKMfihmt3ZlKtT9xs8reekd.xml
diff --git a/resources/project/ieR1AbFuIuiTNyC8z83kLz-KjxQ/QgeUCp8CiwXPaszmjk6YesnqDGkp.xml b/resources/project/Zyy4GNpD2r3q5z6rVbyG2A5CwKE/SsTOiEKMfihmt3ZlKtT9xs8reekp.xml
similarity index 100%
rename from resources/project/ieR1AbFuIuiTNyC8z83kLz-KjxQ/QgeUCp8CiwXPaszmjk6YesnqDGkp.xml
rename to resources/project/Zyy4GNpD2r3q5z6rVbyG2A5CwKE/SsTOiEKMfihmt3ZlKtT9xs8reekp.xml
diff --git a/resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/KZjwdxWx7CTdYd34eqZ_RTkgI7gd.xml b/resources/project/Zyy4GNpD2r3q5z6rVbyG2A5CwKE/narb6tgAWqrDr4i4dAFlCdqFUpEd.xml
similarity index 100%
rename from resources/project/RhAYf9Kq8VJ4m0d6ToLiYOi5PlI/KZjwdxWx7CTdYd34eqZ_RTkgI7gd.xml
rename to resources/project/Zyy4GNpD2r3q5z6rVbyG2A5CwKE/narb6tgAWqrDr4i4dAFlCdqFUpEd.xml
diff --git a/resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/wSDXMLBIRWsCGVgqLP9ER---lO4p.xml b/resources/project/Zyy4GNpD2r3q5z6rVbyG2A5CwKE/narb6tgAWqrDr4i4dAFlCdqFUpEp.xml
similarity index 100%
rename from resources/project/OLuX-8c-UOqeJnrM2amm241SKTs/wSDXMLBIRWsCGVgqLP9ER---lO4p.xml
rename to resources/project/Zyy4GNpD2r3q5z6rVbyG2A5CwKE/narb6tgAWqrDr4i4dAFlCdqFUpEp.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/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/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/Hk1Vip6VmTG9gpBtNOXKEU56oUcd.xml b/resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/BlsnL99CKaby8ck6n_pFKfx8TOEd.xml
similarity index 100%
rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/Hk1Vip6VmTG9gpBtNOXKEU56oUcd.xml
rename to resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/BlsnL99CKaby8ck6n_pFKfx8TOEd.xml
diff --git a/resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/wTRZ9nfLywZT2m22SOEZoykCg4Qp.xml b/resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/BlsnL99CKaby8ck6n_pFKfx8TOEp.xml
similarity index 100%
rename from resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/wTRZ9nfLywZT2m22SOEZoykCg4Qp.xml
rename to resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/BlsnL99CKaby8ck6n_pFKfx8TOEp.xml
diff --git a/resources/project/WBRnwihdNLBXSPBzOwR6sb9ZOfU/4512JjanZ08cqBB0PKUSob2CvAId.xml b/resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/F3Ymj-J16s_9PdjyJ8NmuydN3yId.xml
similarity index 100%
rename from resources/project/WBRnwihdNLBXSPBzOwR6sb9ZOfU/4512JjanZ08cqBB0PKUSob2CvAId.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/WBRnwihdNLBXSPBzOwR6sb9ZOfU/fuDD9sOAbHGOuAW1_c2uQ5HVxkId.xml b/resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/GcqEp1PedF_qduXiylZH_t2MP1Qd.xml
similarity index 100%
rename from resources/project/WBRnwihdNLBXSPBzOwR6sb9ZOfU/fuDD9sOAbHGOuAW1_c2uQ5HVxkId.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/WBRnwihdNLBXSPBzOwR6sb9ZOfU/r1gJ47xS1SI_6x_hhStdKc8rHAAd.xml b/resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/NiQW_7jWHSEL9pyWwCHpUX03D48d.xml
similarity index 100%
rename from resources/project/WBRnwihdNLBXSPBzOwR6sb9ZOfU/r1gJ47xS1SI_6x_hhStdKc8rHAAd.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/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/1uG3tiqfOuVrZXVhllRAYYH-wtod.xml b/resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/c9tjuQNdCrSrZPk-bi5TqtaIYCkd.xml
similarity index 100%
rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/1uG3tiqfOuVrZXVhllRAYYH-wtod.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/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/4lF9gcxRF1S305BdGqOMAHZcYgod.xml b/resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/iCK3aCczRLHuNzxcr2qrQVK9PZ8d.xml
similarity index 100%
rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/4lF9gcxRF1S305BdGqOMAHZcYgod.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/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/8N6cWgm0bCQbFtUjwVpJKMr3DlEd.xml b/resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/rt7-0zPCi_bp1caPhGK26hxMFlgd.xml
similarity index 100%
rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/8N6cWgm0bCQbFtUjwVpJKMr3DlEd.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/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/BkckfovEtaH9G6qBgDPgKm8zKxgd.xml b/resources/project/alAG1z_nIzafOha6mXt5pH8WAmg/ur9Ezr2ljVCKuJkzloOS3DSlTPQd.xml
similarity index 100%
rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/BkckfovEtaH9G6qBgDPgKm8zKxgd.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/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/D2MuV2keQQWbNaEvWpyyDv8QkMsd.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/ThOxiimtCadipN0DSzt0WSfyhQgd.xml
similarity index 100%
rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/D2MuV2keQQWbNaEvWpyyDv8QkMsd.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/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/EIE_vdm0VClM0NZZV517-DBaczEd.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/WnlbACgZ449p_KqRN0_m4q3dnjMd.xml
similarity index 100%
rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/EIE_vdm0VClM0NZZV517-DBaczEd.xml
rename to resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/WnlbACgZ449p_KqRN0_m4q3dnjMd.xml
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/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/Hp4Wu5EduwyItwUIb1uZUV98wGEd.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/cGlRp54T3CcqS_R5IVqnmxrQlfAd.xml
similarity index 100%
rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/Hp4Wu5EduwyItwUIb1uZUV98wGEd.xml
rename to resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/cGlRp54T3CcqS_R5IVqnmxrQlfAd.xml
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/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/NAFZMRUtFQYPd30MjCtFUUPa5w4d.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/eGRpb3Y9fnUsdgOMh_ITJhT6cvQd.xml
similarity index 100%
rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/NAFZMRUtFQYPd30MjCtFUUPa5w4d.xml
rename to resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/eGRpb3Y9fnUsdgOMh_ITJhT6cvQd.xml
diff --git a/resources/project/pDfDXpP-F2Cd6B91DGM44psBFJw/zxz-H_H66S_yiImwKMo7C6xbqr0p.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/eGRpb3Y9fnUsdgOMh_ITJhT6cvQp.xml
similarity index 100%
rename from resources/project/pDfDXpP-F2Cd6B91DGM44psBFJw/zxz-H_H66S_yiImwKMo7C6xbqr0p.xml
rename to resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/eGRpb3Y9fnUsdgOMh_ITJhT6cvQp.xml
diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/IWnqwSbZEiavwf7_s2jeWFKRUyMd.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/gq-la7hESjuBnPU9DovNTn10Z7Ud.xml
similarity index 100%
rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/IWnqwSbZEiavwf7_s2jeWFKRUyMd.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/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/Q55VV6m6MizdVBAaXHDg8O7if2gd.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/lzBPtPM9pIFaKjtc82DDciac-YYd.xml
similarity index 100%
rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/Q55VV6m6MizdVBAaXHDg8O7if2gd.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/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/QNBeGKx711B7_RF8ns22o2sra6od.xml b/resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/sh2eKEgJT0hPZzSs5gVDE9Y3x38d.xml
similarity index 100%
rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/QNBeGKx711B7_RF8ns22o2sra6od.xml
rename to resources/project/cqycJC3N6OF8UO0dBYEaEl86UlQ/sh2eKEgJT0hPZzSs5gVDE9Y3x38d.xml
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/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/S0FtAOjO_bt3aSHMltKOmsdvzZkd.xml b/resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/1U6jA8VW8gquO5tfdlwgKz-0Kjsd.xml
similarity index 100%
rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/S0FtAOjO_bt3aSHMltKOmsdvzZkd.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/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/UPM_TyetjaXWJKkGaorucXMQrKAd.xml b/resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/4tnQjmzGbGyuuipQczDui-LkM4Yd.xml
similarity index 100%
rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/UPM_TyetjaXWJKkGaorucXMQrKAd.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/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/WeWWFR_CW_fJH3v0Spn0rCmP6ccd.xml b/resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/GuR32HWNIsfL9QJxccN3h7ueHWUd.xml
similarity index 100%
rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/WeWWFR_CW_fJH3v0Spn0rCmP6ccd.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/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/OLuX-8c-UOqeJnrM2amm241SKTsd.xml b/resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/N0H1Yk2c0hrEVlplassPmyyQIkgd.xml
similarity index 100%
rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/OLuX-8c-UOqeJnrM2amm241SKTsd.xml
rename to resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/N0H1Yk2c0hrEVlplassPmyyQIkgd.xml
diff --git a/resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/Tc7b1ya7BXcwLEcurYQ2pA061MEp.xml b/resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/N0H1Yk2c0hrEVlplassPmyyQIkgp.xml
similarity index 100%
rename from resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/Tc7b1ya7BXcwLEcurYQ2pA061MEp.xml
rename to resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/N0H1Yk2c0hrEVlplassPmyyQIkgp.xml
diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/Z_VaFjtshHcOoAprv8kGzuWPan8d.xml b/resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/ZhQd5_-iD72EYvyetRmslkcILwgd.xml
similarity index 100%
rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/Z_VaFjtshHcOoAprv8kGzuWPan8d.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/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/gBXVGBJ76fvQ_X7J0ZQGLmXEcqEd.xml b/resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/_DTS8noz4eF6eofLBJF_kJI36Rsd.xml
similarity index 100%
rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/gBXVGBJ76fvQ_X7J0ZQGLmXEcqEd.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/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/gneWb5IhW594iwk9mWJJwtxdpyMd.xml b/resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/ctkpfB9aQsWsqTtrG0AiSWOSsMgd.xml
similarity index 100%
rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/gneWb5IhW594iwk9mWJJwtxdpyMd.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/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/lKVscSUfLpUJ7DMGKxZpl2cj8Cwd.xml b/resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/jaWSShTlT3hy9m_6EXad-HfUEqEd.xml
similarity index 100%
rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/lKVscSUfLpUJ7DMGKxZpl2cj8Cwd.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/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/ojkhqvbsn_quEDp4WaQJ3vMFy2Yd.xml b/resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/se_u97Hhf2JBUhc4KXrIUB6oWjUd.xml
similarity index 100%
rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/ojkhqvbsn_quEDp4WaQJ3vMFy2Yd.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/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/sbvxc69GTsaxWR2ghAIgyxsRoK0d.xml b/resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/vScIuMZR1UoxCROtefKx0SSqHksd.xml
similarity index 100%
rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/sbvxc69GTsaxWR2ghAIgyxsRoK0d.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/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/u6YyGgN6Hd7S4WFttol0-zYdc8cd.xml b/resources/project/d52ncQukW_9fnTuQsihy5s3Rp9k/xBDRN0xkCxYYkWS1cMcY9ajEQ5od.xml
similarity index 100%
rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/u6YyGgN6Hd7S4WFttol0-zYdc8cd.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/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/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/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/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/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-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/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/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/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/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/REL0fj6PT2tNiayKyTrZEt9--acd.xml b/resources/project/fkKGPyMFLRd9q8sWiOz4qApnVgk/1BSDFN_bp_8Wq1u5OO4MK7xHWFEd.xml
similarity index 100%
rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/REL0fj6PT2tNiayKyTrZEt9--acd.xml
rename to resources/project/fkKGPyMFLRd9q8sWiOz4qApnVgk/1BSDFN_bp_8Wq1u5OO4MK7xHWFEd.xml
diff --git a/resources/project/fkKGPyMFLRd9q8sWiOz4qApnVgk/1BSDFN_bp_8Wq1u5OO4MK7xHWFEp.xml b/resources/project/fkKGPyMFLRd9q8sWiOz4qApnVgk/1BSDFN_bp_8Wq1u5OO4MK7xHWFEp.xml
new file mode 100644
index 000000000..842de6ab3
--- /dev/null
+++ b/resources/project/fkKGPyMFLRd9q8sWiOz4qApnVgk/1BSDFN_bp_8Wq1u5OO4MK7xHWFEp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/u9fylSJ1j_4ckhUg5rn809_XS8cd.xml b/resources/project/fkKGPyMFLRd9q8sWiOz4qApnVgk/5mzsc4L1QMwcGyCp10S_0jXV0UYd.xml
similarity index 100%
rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/u9fylSJ1j_4ckhUg5rn809_XS8cd.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/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/znnoG0GBXl0gWrZCcSzmlPlsYO4d.xml b/resources/project/fkKGPyMFLRd9q8sWiOz4qApnVgk/nYxqFWWUsPUVCBz8zZlw7QYCOvod.xml
similarity index 100%
rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/znnoG0GBXl0gWrZCcSzmlPlsYO4d.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/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/7U58VHMhrmTgIuozFzLR1XBJo8Id.xml b/resources/project/fkKGPyMFLRd9q8sWiOz4qApnVgk/uR7UAO4rsdHCU9T8lZjoYW7a32Ud.xml
similarity index 100%
rename from resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/7U58VHMhrmTgIuozFzLR1XBJo8Id.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/ieR1AbFuIuiTNyC8z83kLz-KjxQd.xml b/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/1D-aA_lDjJlRxIHKl8PXRF67F-kd.xml
similarity index 100%
rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/ieR1AbFuIuiTNyC8z83kLz-KjxQd.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/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/oeLLBB_jdP6pKF_DZjKp0QJVZ5Qd.xml b/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/PL5Ga2JyphJjO2f0V1lt1v9mOfQd.xml
similarity index 100%
rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/oeLLBB_jdP6pKF_DZjKp0QJVZ5Qd.xml
rename to resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/PL5Ga2JyphJjO2f0V1lt1v9mOfQd.xml
diff --git a/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/PL5Ga2JyphJjO2f0V1lt1v9mOfQp.xml b/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/PL5Ga2JyphJjO2f0V1lt1v9mOfQp.xml
new file mode 100644
index 000000000..17b3aa625
--- /dev/null
+++ b/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/PL5Ga2JyphJjO2f0V1lt1v9mOfQp.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/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/pDfDXpP-F2Cd6B91DGM44psBFJwd.xml b/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/Zyy4GNpD2r3q5z6rVbyG2A5CwKEd.xml
similarity index 100%
rename from resources/project/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4/pDfDXpP-F2Cd6B91DGM44psBFJwd.xml
rename to resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/Zyy4GNpD2r3q5z6rVbyG2A5CwKEd.xml
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/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/kRJYdCzyq5zNeYLPJeKdZmiLSJEd.xml b/resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/m4YW3kyFKhaw2jhfe5pX4koVSF0d.xml
similarity index 100%
rename from resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/kRJYdCzyq5zNeYLPJeKdZmiLSJEd.xml
rename to resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/m4YW3kyFKhaw2jhfe5pX4koVSF0d.xml
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/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/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/9ULi2Alw-JmXjkCxz2ppBf5D2nUd.xml b/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/Ge7ijHD-QOCX1Vh_15TrHFqSGPQd.xml
similarity index 100%
rename from resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/9ULi2Alw-JmXjkCxz2ppBf5D2nUd.xml
rename to resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/Ge7ijHD-QOCX1Vh_15TrHFqSGPQd.xml
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/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/9orjn84dnpAKkkuchjb6ZXb4lYod.xml b/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/IGJLBsn5zyrwrJ0Ri0i1Ko7r_xkd.xml
similarity index 100%
rename from resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/9orjn84dnpAKkkuchjb6ZXb4lYod.xml
rename to resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/IGJLBsn5zyrwrJ0Ri0i1Ko7r_xkd.xml
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/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/Ckzn7bzQCXjaNEGiflifGGEk0-Ad.xml b/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/IpHzUnQyP59JYR1Z_tIIrX8K2UAd.xml
similarity index 100%
rename from resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/Ckzn7bzQCXjaNEGiflifGGEk0-Ad.xml
rename to resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/IpHzUnQyP59JYR1Z_tIIrX8K2UAd.xml
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/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/Fuv4y78db8mLAjl6Fcl6HGZDRo0d.xml b/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/QlTkLLPEJG_45v6SBsylfHJ5nNod.xml
similarity index 100%
rename from resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/Fuv4y78db8mLAjl6Fcl6HGZDRo0d.xml
rename to resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/QlTkLLPEJG_45v6SBsylfHJ5nNod.xml
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/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/Q_eYts4Jx8lwh_76CdRDIxbbdlkd.xml b/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/TOFjg06uQu3Zfq7kzjebZhRypikd.xml
similarity index 100%
rename from resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/Q_eYts4Jx8lwh_76CdRDIxbbdlkd.xml
rename to resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/TOFjg06uQu3Zfq7kzjebZhRypikd.xml
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/gK_nb01P4_6XSSogizZJn5zmzQQ/WBRnwihdNLBXSPBzOwR6sb9ZOfUd.xml b/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/_SAx-QeBHslskguQ9aXk9RNEjhYd.xml
similarity index 100%
rename from resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/WBRnwihdNLBXSPBzOwR6sb9ZOfUd.xml
rename to resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/_SAx-QeBHslskguQ9aXk9RNEjhYd.xml
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/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/T3r09B-NxK1oaHW6j-rPgd2Qj6Ud.xml b/resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/hH_1Yj64xFVM9hFUXve2TuYwxLcd.xml
similarity index 100%
rename from resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/T3r09B-NxK1oaHW6j-rPgd2Qj6Ud.xml
rename to resources/project/m4YW3kyFKhaw2jhfe5pX4koVSF0/hH_1Yj64xFVM9hFUXve2TuYwxLcd.xml
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/resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/WtpDLoJHlDx008KY8cwkBcD2AT8d.xml b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/-IEsxSr0pUR0RE2Q2b5wpQX0KkEd.xml
similarity index 100%
rename from resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/WtpDLoJHlDx008KY8cwkBcD2AT8d.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/gK_nb01P4_6XSSogizZJn5zmzQQ/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4d.xml b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/2XppXtEHR0PCZWWNfUwQdRTKlDcd.xml
similarity index 100%
rename from resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/_2w7KV6EHc1SMHPJ5sv5BJ_TE_4d.xml
rename to resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/2XppXtEHR0PCZWWNfUwQdRTKlDcd.xml
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/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/XbzuDmDFV-LdTDKCBgtbIoNIeDAd.xml b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/3DhOOe0Tl7hipLJtC66oxocIr9cd.xml
similarity index 100%
rename from resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/XbzuDmDFV-LdTDKCBgtbIoNIeDAd.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/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/aN6B9nVEXMPwk5nipy2pReqfe9kd.xml b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/AxKLxQn_Q5ExtyTFauEpYntfsssd.xml
similarity index 100%
rename from resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/aN6B9nVEXMPwk5nipy2pReqfe9kd.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/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/dKRagt73aqMStWRIRrbSi8CTiWEd.xml b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/IDetplA4BCvExeTDnnCPfg2MhBwd.xml
similarity index 100%
rename from resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/dKRagt73aqMStWRIRrbSi8CTiWEd.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/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/jjYwKTXjk1ClTK05aLas9VzF5YAd.xml b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/QqOZDkdIGwDJT1oKEd1m5cJ1l88d.xml
similarity index 100%
rename from resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/jjYwKTXjk1ClTK05aLas9VzF5YAd.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/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/vok99kMS3Be0cBX4FPi-Zspy6Y0d.xml b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/SJJLTV1fLQ-ZA32hIB2BTOYk5TYd.xml
similarity index 100%
rename from resources/project/dYvx6wp33Ts8hX1S_rzdiQ3NP4A/vok99kMS3Be0cBX4FPi-Zspy6Y0d.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/ieR1AbFuIuiTNyC8z83kLz-KjxQ/gU6nQ9al6ASHLbEF0HmBERcYd4Md.xml b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/gK1b_iZQx1Km0kq0Da4qWwP0JpUd.xml
similarity index 100%
rename from resources/project/ieR1AbFuIuiTNyC8z83kLz-KjxQ/gU6nQ9al6ASHLbEF0HmBERcYd4Md.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/ieR1AbFuIuiTNyC8z83kLz-KjxQ/ogNzaRYtwam4lR-ZN7eI6m_SXeYd.xml b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/oq4yCbIVISuOld7-sgX9uJKIS1wd.xml
similarity index 100%
rename from resources/project/ieR1AbFuIuiTNyC8z83kLz-KjxQ/ogNzaRYtwam4lR-ZN7eI6m_SXeYd.xml
rename to resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/oq4yCbIVISuOld7-sgX9uJKIS1wd.xml
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/ieR1AbFuIuiTNyC8z83kLz-KjxQ/xmaLmb7wr2-EmOlPBo1ae7Q1eEwd.xml b/resources/project/o2cKHtbeLCI5dGNR0Lwpzdptr4s/zMWI0fKvYbz3vT7BQxp-7H6ez4wd.xml
similarity index 100%
rename from resources/project/ieR1AbFuIuiTNyC8z83kLz-KjxQ/xmaLmb7wr2-EmOlPBo1ae7Q1eEwd.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/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/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/FcqvamgK2GFBncjelWFYWpbic6sd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/2BNsbxotz8yS1dkQ-k4TtTtfElkd.xml
similarity index 100%
rename from resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/FcqvamgK2GFBncjelWFYWpbic6sd.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/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/Q9BY7Fa1-dMNQpWUMw61LYZlnpkd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/41tDM4fkWcly5KHLFGBD7TnUF1kd.xml
similarity index 100%
rename from resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/Q9BY7Fa1-dMNQpWUMw61LYZlnpkd.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/gK_nb01P4_6XSSogizZJn5zmzQQ/rjqiBcq-Lf6PHOzurJu8wmXPRLcd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/6AGol90mQGOIatxNSyMhdaH59gAd.xml
similarity index 100%
rename from resources/project/gK_nb01P4_6XSSogizZJn5zmzQQ/rjqiBcq-Lf6PHOzurJu8wmXPRLcd.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/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/TMwS0Ehc1hOj1QS3kXBWqYH2cIYd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/BYuUEuuSEpTxFNYwIIct-_focKkd.xml
similarity index 100%
rename from resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/TMwS0Ehc1hOj1QS3kXBWqYH2cIYd.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/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/XA-33X-Ml7fASA-h03h0NoRtubAd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/FDdGHhZvwjpYxttUpuTOcZx3JmMd.xml
similarity index 100%
rename from resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/XA-33X-Ml7fASA-h03h0NoRtubAd.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/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/ccgGxetCHn-i1g49iWGxy2eRT9gd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/FjEfBzqcYgSPB2oA26wfyEdBqo0d.xml
similarity index 100%
rename from resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/ccgGxetCHn-i1g49iWGxy2eRT9gd.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/ieR1AbFuIuiTNyC8z83kLz-KjxQ/QgeUCp8CiwXPaszmjk6YesnqDGkd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/H72L659GuP9YjS6Dt9kIH5c6HnQd.xml
similarity index 100%
rename from resources/project/ieR1AbFuIuiTNyC8z83kLz-KjxQ/QgeUCp8CiwXPaszmjk6YesnqDGkd.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/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/wv-PgmFd28kiUsYZVe0yPM4yMXEd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/HvUMVTzlhZFbsfNl34oO8xkwaekd.xml
similarity index 100%
rename from resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/wv-PgmFd28kiUsYZVe0yPM4yMXEd.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/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/wTRZ9nfLywZT2m22SOEZoykCg4Qd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/MxOdJHAcr56wMe-tpEEz-LoiQlYd.xml
similarity index 100%
rename from resources/project/oeLLBB_jdP6pKF_DZjKp0QJVZ5Q/wTRZ9nfLywZT2m22SOEZoykCg4Qd.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/pDfDXpP-F2Cd6B91DGM44psBFJw/l8FwG4TnKj8gyMwHPAtYvu1WQGUd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/NEI1fXIv8KRUL_rDHprZREmiW34d.xml
similarity index 100%
rename from resources/project/pDfDXpP-F2Cd6B91DGM44psBFJw/l8FwG4TnKj8gyMwHPAtYvu1WQGUd.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/rjqiBcq-Lf6PHOzurJu8wmXPRLc/5WwusQudPnq6BB-GF8FRrf89dhAd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/NFReMHJQuHGlCss_fLSqFSIT1ZYd.xml
similarity index 100%
rename from resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/5WwusQudPnq6BB-GF8FRrf89dhAd.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/pDfDXpP-F2Cd6B91DGM44psBFJw/zxz-H_H66S_yiImwKMo7C6xbqr0d.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/PMTiY8ii3Ycb5X733RMaGCNVItAd.xml
similarity index 100%
rename from resources/project/pDfDXpP-F2Cd6B91DGM44psBFJw/zxz-H_H66S_yiImwKMo7C6xbqr0d.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/rjqiBcq-Lf6PHOzurJu8wmXPRLc/8fBGPWUvqT-8fICRX_EaE099qkMd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/PQTYqrLotgfQlXwRwcTgmkIRabcd.xml
similarity index 100%
rename from resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/8fBGPWUvqT-8fICRX_EaE099qkMd.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/rjqiBcq-Lf6PHOzurJu8wmXPRLc/GKhQt7L9QdetWF4sO002MIZCU7Id.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/Q-DZerJncmEPNpWIO9dYINnubhAd.xml
similarity index 100%
rename from resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/GKhQt7L9QdetWF4sO002MIZCU7Id.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/rjqiBcq-Lf6PHOzurJu8wmXPRLc/SSxthfNhlTxzkvbLy5rdNwNoPDcd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/R08oPdcWNyOGmE1Nvdwwvs71M1kd.xml
similarity index 100%
rename from resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/SSxthfNhlTxzkvbLy5rdNwNoPDcd.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/rjqiBcq-Lf6PHOzurJu8wmXPRLc/XgkWU6kVr3uGt-m8Ww6oQbheT5sd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/Rx9X9wAt2wpjFi2e7541ezE-nWwd.xml
similarity index 100%
rename from resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/XgkWU6kVr3uGt-m8Ww6oQbheT5sd.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/rjqiBcq-Lf6PHOzurJu8wmXPRLc/Tc7b1ya7BXcwLEcurYQ2pA061MEd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/SJuj0lQEL2kv8DN93wpDxprjL2Qd.xml
similarity index 100%
rename from resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/Tc7b1ya7BXcwLEcurYQ2pA061MEd.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/tkAoR0wDeR9bDqF10NY7YqXFvL4/TMR2PLz8-jf_t72xY3w-eiAkMwcd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/TMR2PLz8-jf_t72xY3w-eiAkMwcd.xml
new file mode 100644
index 000000000..a75f7a81b
--- /dev/null
+++ b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/TMR2PLz8-jf_t72xY3w-eiAkMwcd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
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/tkAoR0wDeR9bDqF10NY7YqXFvL4/UeukJVHJUwLZ1oSw-yBfMdSLcNId.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/UeukJVHJUwLZ1oSw-yBfMdSLcNId.xml
new file mode 100644
index 000000000..a75f7a81b
--- /dev/null
+++ b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/UeukJVHJUwLZ1oSw-yBfMdSLcNId.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
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/rjqiBcq-Lf6PHOzurJu8wmXPRLc/iP5nksVSyCBzfFDmXV4iSEbpBXcd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/V4lqQmJ8gZjfEk0BSGK5rhmhQ7Qd.xml
similarity index 100%
rename from resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/iP5nksVSyCBzfFDmXV4iSEbpBXcd.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/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/rjqiBcq-Lf6PHOzurJu8wmXPRLc/ntOpiq0wcrzRF_WbpUd_GXHqXiMd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/W3r3wIq1F_aH5Bg_6WiksWHWjnsd.xml
similarity index 100%
rename from resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/ntOpiq0wcrzRF_WbpUd_GXHqXiMd.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/rjqiBcq-Lf6PHOzurJu8wmXPRLc/rsypI2SliEIaygpLi080nfFXTFQd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/Yzm_5cRIP1TNTAYJFwi1gFJMvrYd.xml
similarity index 100%
rename from resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/rsypI2SliEIaygpLi080nfFXTFQd.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/rjqiBcq-Lf6PHOzurJu8wmXPRLc/yQ7ukYOEE_Z48S5Kyd3otzmDgHMd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/aQn5VXjSyIOSf20IuNhzQTcJKLEd.xml
similarity index 100%
rename from resources/project/rjqiBcq-Lf6PHOzurJu8wmXPRLc/yQ7ukYOEE_Z48S5Kyd3otzmDgHMd.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/tkAoR0wDeR9bDqF10NY7YqXFvL4/alAG1z_nIzafOha6mXt5pH8WAmgd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/alAG1z_nIzafOha6mXt5pH8WAmgd.xml
new file mode 100644
index 000000000..a75f7a81b
--- /dev/null
+++ b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/alAG1z_nIzafOha6mXt5pH8WAmgd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
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/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/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/d52ncQukW_9fnTuQsihy5s3Rp9kd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/d52ncQukW_9fnTuQsihy5s3Rp9kd.xml
new file mode 100644
index 000000000..a75f7a81b
--- /dev/null
+++ b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/d52ncQukW_9fnTuQsihy5s3Rp9kd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
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/tkAoR0wDeR9bDqF10NY7YqXFvL4/fkKGPyMFLRd9q8sWiOz4qApnVgkd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/fkKGPyMFLRd9q8sWiOz4qApnVgkd.xml
new file mode 100644
index 000000000..a75f7a81b
--- /dev/null
+++ b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/fkKGPyMFLRd9q8sWiOz4qApnVgkd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
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/tkAoR0wDeR9bDqF10NY7YqXFvL4/jcRKP1GWD0s2vbmJjnRVU0UNQuEd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/jcRKP1GWD0s2vbmJjnRVU0UNQuEd.xml
new file mode 100644
index 000000000..7a6326b99
--- /dev/null
+++ b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/jcRKP1GWD0s2vbmJjnRVU0UNQuEd.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
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/tkAoR0wDeR9bDqF10NY7YqXFvL4/lIsawu5M1S3Y4h7oJkYpw3jue7Ed.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/lIsawu5M1S3Y4h7oJkYpw3jue7Ed.xml
new file mode 100644
index 000000000..7a6326b99
--- /dev/null
+++ b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/lIsawu5M1S3Y4h7oJkYpw3jue7Ed.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
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/tkAoR0wDeR9bDqF10NY7YqXFvL4/ljel_A4AHLbljnmRTR2O3qqpy8Ed.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/ljel_A4AHLbljnmRTR2O3qqpy8Ed.xml
new file mode 100644
index 000000000..7a6326b99
--- /dev/null
+++ b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/ljel_A4AHLbljnmRTR2O3qqpy8Ed.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
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/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/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/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/scEHrBcR_4DaGlInftNFgaoYFTMd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/scEHrBcR_4DaGlInftNFgaoYFTMd.xml
new file mode 100644
index 000000000..7a6326b99
--- /dev/null
+++ b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/scEHrBcR_4DaGlInftNFgaoYFTMd.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
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/tkAoR0wDeR9bDqF10NY7YqXFvL4/ux2HJV4G7O8gtSZERsaoTt6GWQkd.xml b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/ux2HJV4G7O8gtSZERsaoTt6GWQkd.xml
new file mode 100644
index 000000000..a75f7a81b
--- /dev/null
+++ b/resources/project/tkAoR0wDeR9bDqF10NY7YqXFvL4/ux2HJV4G7O8gtSZERsaoTt6GWQkd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
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/ux2HJV4G7O8gtSZERsaoTt6GWQk/GNyWdCWXwy_FnEw-XS9bcAQnRkId.xml b/resources/project/ux2HJV4G7O8gtSZERsaoTt6GWQk/GNyWdCWXwy_FnEw-XS9bcAQnRkId.xml
new file mode 100644
index 000000000..7a6326b99
--- /dev/null
+++ b/resources/project/ux2HJV4G7O8gtSZERsaoTt6GWQk/GNyWdCWXwy_FnEw-XS9bcAQnRkId.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
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/ux2HJV4G7O8gtSZERsaoTt6GWQk/STP0mSEMvKb0C99CAKfVG-FjFdUd.xml b/resources/project/ux2HJV4G7O8gtSZERsaoTt6GWQk/STP0mSEMvKb0C99CAKfVG-FjFdUd.xml
new file mode 100644
index 000000000..a75f7a81b
--- /dev/null
+++ b/resources/project/ux2HJV4G7O8gtSZERsaoTt6GWQk/STP0mSEMvKb0C99CAKfVG-FjFdUd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/ux2HJV4G7O8gtSZERsaoTt6GWQk/STP0mSEMvKb0C99CAKfVG-FjFdUp.xml b/resources/project/ux2HJV4G7O8gtSZERsaoTt6GWQk/STP0mSEMvKb0C99CAKfVG-FjFdUp.xml
new file mode 100644
index 000000000..842de6ab3
--- /dev/null
+++ b/resources/project/ux2HJV4G7O8gtSZERsaoTt6GWQk/STP0mSEMvKb0C99CAKfVG-FjFdUp.xml
@@ -0,0 +1,2 @@
+
+
\ 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/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/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/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/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/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 e4366c65c..000000000
--- a/src/DesignOptimization/computeDesignOptimizationMainFunction.m
+++ /dev/null
@@ -1,89 +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_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
-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
-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
\ No newline at end of file
diff --git a/src/DesignOptimization/getDesignOptimizationValueStruct.m b/src/DesignOptimization/getDesignOptimizationValueStruct.m
deleted file mode 100644
index dfab7f79a..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_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
\ No newline at end of file
diff --git a/src/DesignOptimization/parseDesignOptimizationSettingsTree.m b/src/DesignOptimization/parseDesignOptimizationSettingsTree.m
deleted file mode 100644
index 5fccf93a2..000000000
--- a/src/DesignOptimization/parseDesignOptimizationSettingsTree.m
+++ /dev/null
@@ -1,127 +0,0 @@
-% This function is part of the NMSM Pipeline, see file for full license.
-%
-% This function parses the settings tree resulting from xml2struct of the
-% Design Optimizatoin settings XML file.
-%
-% (struct) -> (struct, struct)
-% returns the input values for 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 [inputs, params] = ...
- parseDesignOptimizationSettingsTree(settingsTree)
-inputs = getInputs(settingsTree);
-params = getParams(settingsTree);
-inputs = modifyModelForces(inputs);
-inputs = updateMuscleModelProperties(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
-inputs.systemFns = parseSpaceSeparatedList(tree, "model_functions");
-parameterTree = getFieldByNameOrError(tree, "RCNLParameterTermSet");
-if isstruct(parameterTree) && isfield(parameterTree, "RCNLParameterTerm")
- inputs.userDefinedVariables = parseRcnlCostTermSet( ...
- parameterTree.RCNLParameterTerm);
-else
- inputs.userDefinedVariables = {};
-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, ...
- 'final_time_range');
-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(designVariableTree, ...
- '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
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/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
diff --git a/src/GroundContactPersonalization/Optimizations/ModelCalculation/calcModeledVerticalGroundReactionForce.m b/src/GroundContactPersonalization/Optimizations/ModelCalculation/calcModeledVerticalGroundReactionForce.m
index 53993f83a..40b66a368 100644
--- a/src/GroundContactPersonalization/Optimizations/ModelCalculation/calcModeledVerticalGroundReactionForce.m
+++ b/src/GroundContactPersonalization/Optimizations/ModelCalculation/calcModeledVerticalGroundReactionForce.m
@@ -49,17 +49,15 @@
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);
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
- 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/GroundContactPersonalization/Saving/saveGroundContactPersonalizationResults.m b/src/GroundContactPersonalization/Saving/saveGroundContactPersonalizationResults.m
index b7007b3a8..74cf69966 100644
--- a/src/GroundContactPersonalization/Saving/saveGroundContactPersonalizationResults.m
+++ b/src/GroundContactPersonalization/Saving/saveGroundContactPersonalizationResults.m
@@ -41,5 +41,7 @@ function saveGroundContactPersonalizationResults(inputs, params, ...
writeOptimizedGroundReactionsToSto(inputs, params, resultsDirectory, name);
writeGroundContactPersonalizationOsimxFile(inputs, resultsDirectory, ...
osimxFileName);
+writeCombinedOptimizedGroundReactionsToSto(inputs, params, resultsDirectory);
+writeFullBodyKinematicsFromGcp(inputs, params, resultsDirectory);
end
diff --git a/src/GroundContactPersonalization/Saving/writeCombinedOptimizedGroundReactionsToSto.m b/src/GroundContactPersonalization/Saving/writeCombinedOptimizedGroundReactionsToSto.m
new file mode 100644
index 000000000..c46b97127
--- /dev/null
+++ b/src/GroundContactPersonalization/Saving/writeCombinedOptimizedGroundReactionsToSto.m
@@ -0,0 +1,80 @@
+% 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'];
+ data = lowpassFilter(inputs.surfaces{1}.time, data, 2, 6, 0);
+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..77e093e55
--- /dev/null
+++ b/src/GroundContactPersonalization/Saving/writeFullBodyKinematicsFromGcp.m
@@ -0,0 +1,157 @@
+% 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 = .00001;
+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), 1000.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);
+kinematicsReporter.setInDegrees(false);
+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));
+[columnNames, time, data] = parseMotToComponents(model, ...
+ 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
+end
+
diff --git a/src/GroundContactPersonalization/Saving/writeGroundContactPersonalizationOsimxFile.m b/src/GroundContactPersonalization/Saving/writeGroundContactPersonalizationOsimxFile.m
index 86c9db2b3..f5d8277d0 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
@@ -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..91f1a8763 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");
+if ~isstruct(hindfootBodyName)
+ throw(MException('', " is replaced by in the GCP settings file."))
+else
+ task.hindfootBodyName = hindfootBodyName.Text;
+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.
@@ -244,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/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/JointModelPersonalization/JointModelPersonalization.m b/src/JointModelPersonalization/JointModelPersonalization.m
index 90e10b173..53c55b1c3 100644
--- a/src/JointModelPersonalization/JointModelPersonalization.m
+++ b/src/JointModelPersonalization/JointModelPersonalization.m
@@ -34,16 +34,20 @@
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, ...
+ 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)
@@ -97,14 +101,23 @@ function verifyParam(params, fieldName, fn, message)
end
end
-function functions = makeFunctions(parameters, scaling, markers)
+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 = {};
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 +135,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/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/computeInverseKinematicsSquaredError.m b/src/JointModelPersonalization/KinematicCalibration/computeInverseKinematicsSquaredError.m
index 8d49b7b12..e05c2c058 100644
--- a/src/JointModelPersonalization/KinematicCalibration/computeInverseKinematicsSquaredError.m
+++ b/src/JointModelPersonalization/KinematicCalibration/computeInverseKinematicsSquaredError.m
@@ -31,29 +31,35 @@
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;
-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
+ [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)];
+ 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)));
diff --git a/src/JointModelPersonalization/KinematicCalibration/computeKinematicCalibration.m b/src/JointModelPersonalization/KinematicCalibration/computeKinematicCalibration.m
index e8ac341ab..f284ea1e1 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)
@@ -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/KinematicCalibration/functions/adjustBodyScaling.m b/src/JointModelPersonalization/KinematicCalibration/functions/adjustBodyScaling.m
index 5d7beef86..3b00230bf 100644
--- a/src/JointModelPersonalization/KinematicCalibration/functions/adjustBodyScaling.m
+++ b/src/JointModelPersonalization/KinematicCalibration/functions/adjustBodyScaling.m
@@ -29,12 +29,17 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function adjustBodyScaling(model, bodyName, value)
-
-markers = getMarkersFromBody(model, bodyName);
-markerLocations = {};
-for i = 1:length(markers)
- markerLocations{i} = org.opensim.modeling.Vec3(model.getMarkerSet().get(markers{i}).get_location());
+function [body, joint, coordinate] = adjustBodyScaling(model, bodyName, value, anatomicalMarkers)
+body = bodyName;
+joint = "";
+coordinate = 0;
+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 +53,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/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/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/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/JointModelPersonalization/parseJointModelPersonalizationSettingsTree.m b/src/JointModelPersonalization/parseJointModelPersonalizationSettingsTree.m
index f0c9199f2..1182ff936 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 = ...
@@ -80,22 +80,37 @@
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
+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))
@@ -170,7 +185,7 @@
end
function [scaling, markers] = getBodyParameters( ...
- bodySetTree, model)
+ bodySetTree, model, freeMarkers)
if isfield(bodySetTree, "JMPBody")
bodyTree = bodySetTree.JMPBody;
scaling = getScalingBodies(bodyTree);
@@ -179,6 +194,7 @@
scaling = string([]);
markers = {};
end
+markers = addFreeMarkers(markers, freeMarkers);
end
function inputs = getScalingBodies(bodyTree)
@@ -224,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)
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/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/MuscleTendonLengthInitialization/parseMuscleTendonLengthInitializationSettingsTree.m b/src/MuscleTendonLengthInitialization/parseMuscleTendonLengthInitializationSettingsTree.m
index 778ee2595..dfac6b826 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
@@ -52,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)
@@ -69,20 +71,26 @@
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)) == ...
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);
- inputs.passiveMomentArms = inputs.passiveMomentArms(:, :, 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, :);
inputs.passiveMomentDataExists = 1;
end
@@ -143,10 +151,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/core/TreatmentOptimization/IntegrandTerms/calcStepLengthAsymmetry.m b/src/MuscleTendonPersonalization/Analysis/extractMtpDataFromSto.m
similarity index 57%
rename from src/core/TreatmentOptimization/IntegrandTerms/calcStepLengthAsymmetry.m
rename to src/MuscleTendonPersonalization/Analysis/extractMtpDataFromSto.m
index 50bbb3328..5b4a26b7f 100644
--- a/src/core/TreatmentOptimization/IntegrandTerms/calcStepLengthAsymmetry.m
+++ b/src/MuscleTendonPersonalization/Analysis/extractMtpDataFromSto.m
@@ -1,11 +1,13 @@
% 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)
+% 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 %
@@ -15,7 +17,7 @@
% National Institutes of Health (R01 EB030520). %
% %
% Copyright (c) 2021 Rice University and the Authors %
-% Author(s): 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. %
@@ -29,21 +31,24 @@
% 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)]);
+function [columnNames, data] = extractMtpDataFromSto(resultsDirectory)
+ import org.opensim.modeling.Storage
+ if exist(resultsDirectory, "dir")
+ dataDir = dir(resultsDirectory);
+ dataFiles = {dataDir(3:end).name};
else
- stepLength(i) = calcStepLength(modeledValues. ...
- groundReactionsLab.forces{i}(:,2), ...
- [modeledValues.bodyLocations.parent{i}(:, 1) ...
- modeledValues.bodyLocations.parent{i-1}(:, 1)]);
+ fprintf("%s not found\n", resultsDirectory);
+ columnNames = [];
+ data = [];
+ return
+ end
+ data = cell(1, numel(dataFiles));
+ for i = 1:numel(dataFiles)
+ dataStorage = Storage(fullfile(fullfile(resultsDirectory, dataFiles{i})));
+ columnNames = getStorageColumnNames(dataStorage);
+ data{i} = storageToDoubleMatrix(dataStorage)';
end
-end
-stepLengthAsymmetry = stepLength(1) / stepLength(2);
+ 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/core/TreatmentOptimization/IntegrandTerms/calcMinimizingJointJerkIntegrand.m b/src/MuscleTendonPersonalization/Analysis/plotMeanAndStd.m
similarity index 74%
rename from src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingJointJerkIntegrand.m
rename to src/MuscleTendonPersonalization/Analysis/plotMeanAndStd.m
index 0fc708907..44e8ed751 100644
--- a/src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingJointJerkIntegrand.m
+++ b/src/MuscleTendonPersonalization/Analysis/plotMeanAndStd.m
@@ -1,9 +1,10 @@
% This function is part of the NMSM Pipeline, see file for full license.
%
-% This function minimizes the joint jerk for the specified coordinate.
+% This function takes a mean and standard deviation and plots the mean with
+% a solid line and shades +/- 1 standard deviation from the mean.
%
-% (2D matrix, struct, Array of string) -> (Array of number)
-%
+% (Array), (Array), (Array), (String) -> (None)
+% Plot passive moment curves from file.
% ----------------------------------------------------------------------- %
% The NMSM Pipeline is a toolkit for model personalization and treatment %
@@ -13,7 +14,7 @@
% National Institutes of Health (R01 EB030520). %
% %
% Copyright (c) 2021 Rice University and the Authors %
-% Author(s): 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. %
@@ -26,11 +27,9 @@
% implied. See the License for the specific language governing %
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-
-function cost = calcMinimizingJointJerkIntegrand(jointJerks, params, ...
- coordinateName)
-
-indx = find(strcmp(convertCharsToStrings(params.coordinateNames), ...
- coordinateName));
-cost = calcMinimizingCostArrayTerm(jointJerks(:, indx));
+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/core/TreatmentOptimization/IntegrandTerms/calcStepTimeAsymmetry.m b/src/MuscleTendonPersonalization/Analysis/plotMtpHillTypeMuscleParams.m
similarity index 55%
rename from src/core/TreatmentOptimization/IntegrandTerms/calcStepTimeAsymmetry.m
rename to src/MuscleTendonPersonalization/Analysis/plotMtpHillTypeMuscleParams.m
index 5172e4e39..3d423b813 100644
--- a/src/core/TreatmentOptimization/IntegrandTerms/calcStepTimeAsymmetry.m
+++ b/src/MuscleTendonPersonalization/Analysis/plotMtpHillTypeMuscleParams.m
@@ -1,11 +1,11 @@
% 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)
+% 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 %
@@ -15,7 +15,7 @@
% National Institutes of Health (R01 EB030520). %
% %
% Copyright (c) 2021 Rice University and the Authors %
-% Author(s): Marleny Vega %
+% 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. %
@@ -28,20 +28,34 @@
% implied. See the License for the specific language governing %
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
+function plotMtpHillTypeMuscleParams(resultsDirectory)
+analysisDirectory = fullfile(resultsDirectory, "Analysis");
+[muscleNames, params] = extractMtpDataFromSto( ...
+ fullfile(analysisDirectory, "muscleModelParameters"));
+muscleNames = strrep(muscleNames, '_', ' ');
+figure(Name = strcat(resultsDirectory, " Muscle Model Parameters"), ...
+ Units='normalized', ...
+ Position=[0.05 0.05 0.9 0.85])
-function stepTimeAsymmetry = calcStepTimeAsymmetry(values, ...
- modeledValues, params)
+paramLabels = ["Activation Time Constant", ...
+ "Activation Nonlinearity", ...
+ "Electromechanical Time Delay", ...
+ "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)
+ nexttile(i)
+ barh(1:numel(muscleNames), params(i,:))
-for i = 1:length(params.contactSurfaces)
+ title(textwrap(paramLabels(i), 20), FontSize=12)
if i == 1
- singleSupportTime(i) = calcSingleSupportTime( ...
- modeledValues.groundReactionsLab.forces{i + 1}(:, 2), ...
- values.time);
+ yticks(1:numel(muscleNames))
+ yticklabels(muscleNames)
else
- singleSupportTime(i) = calcSingleSupportTime( ...
- modeledValues.groundReactionsLab.forces{i - 1}(:, 2), ...
- values.time);
+ yticks([])
+ yticklabels([])
end
end
-stepTimeAsymmetry = singleSupportTime(1) / singleSupportTime(2);
end
\ No newline at end of file
diff --git a/src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m b/src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m
new file mode 100644
index 000000000..07ae559d0
--- /dev/null
+++ b/src/MuscleTendonPersonalization/Analysis/plotMtpJointMoments.m
@@ -0,0 +1,110 @@
+% 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");
+% Include max allowable error, and rmse
+figureHeight = 1;
+
+[jointLabels, idMoments] = extractMtpDataFromSto( ...
+ fullfile(analysisDirectory, "inverseDynamicsJointMoments"));
+
+[~, modelMoments] = extractMtpDataFromSto( ...
+ fullfile(analysisDirectory, "modelJointMoments"));
+
+if exist(fullfile(analysisDirectory, "modelJointMomentsSynx"), "dir")
+ [~, modelMomentsSynx] = extractMtpDataFromSto( ...
+ fullfile(analysisDirectory, "modelJointMomentsSynx"));
+ figureHeight = figureHeight + 1;
+else
+ modelMomentsSynx = [];
+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);
+maxMoment = max([ ...
+ max(meanIdMoments, [], "all"), ...
+ max(meanMoments, [], "all"), ...
+ max(meanMomentsSynx, [], "all")]);
+minMoment = min([ ...
+ min(meanIdMoments, [], "all"), ...
+ min(meanMoments, [], "all"), ...
+ min(meanMomentsSynx, [], "all")]);
+
+figure(Name = strcat(resultsDirectory, " Joint Moments"), ...
+ Units='normalized', ...
+ Position=[0.05 0.05 0.9 0.85])
+time = 1:1:size(meanIdMoments,1);
+figureWidth = numel(jointLabels);
+t = tiledlayout(figureHeight, figureWidth, ...
+ TileSpacing='Compact', Padding='Compact');
+for i=1:numel(jointLabels)
+ if ~isempty(meanMoments)
+ nexttile(i);
+ hold on
+ plotMeanAndStd(meanMoments(:,i), stdMoments(:,i), time, 'r-')
+ plotMeanAndStd(meanIdMoments(:,i), stdIdMoments(:,i), time, 'b-')
+ hold off
+ set(gca, fontsize=11)
+ rmse = rms(meanMoments(:,i) - meanIdMoments(:,i));
+ mae = mean(abs(meanMoments(:,i) - meanIdMoments(:,i)));
+ title(sprintf("%s \n RMSE: %.4f, MAE: %.4f", ...
+ jointLabels(i), rmse, mae), fontsize=12)
+ axis([time(1) time(end) minMoment, maxMoment])
+ if i == 1
+ legend("Mean Moment No Synx", "Mean Inverse Dynamics Moment")
+ ylabel("Joint Moment [Nm]")
+ end
+ end
+ if ~isempty(meanMomentsSynx)
+ 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)
+ rmse = rms(meanMomentsSynx(:,i) - meanIdMoments(:,i));
+ mae = mean(abs(meanMomentsSynx(:,i) - meanIdMoments(:,i)));
+ title(sprintf("%s \n RMSE: %.4f, MAE: %.4f", ...
+ jointLabels(i), rmse, mae), fontsize=12)
+ 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 Points")
+end
+end
\ No newline at end of file
diff --git a/src/MuscleTendonPersonalization/Analysis/plotMtpMuscleExcitationsAndActivations.m b/src/MuscleTendonPersonalization/Analysis/plotMtpMuscleExcitationsAndActivations.m
new file mode 100644
index 000000000..3b26717bc
--- /dev/null
+++ b/src/MuscleTendonPersonalization/Analysis/plotMtpMuscleExcitationsAndActivations.m
@@ -0,0 +1,104 @@
+% 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 plotMtpMuscleExcitationsAndActivations(resultsDirectory)
+analysisDirectory = fullfile(resultsDirectory, "Analysis");
+[muscleNames, excitations] = extractMtpDataFromSto( ...
+ fullfile(analysisDirectory, "muscleExcitations"));
+
+[~, activations] = extractMtpDataFromSto( ...
+ 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);
+meanActivations = mean(activations, 3);
+stdActivations = std(activations,[], 3);
+meanExcitationsSynx = mean(excitationsSynx, 3);
+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 = strcat(resultsDirectory, " Muscle Excitations and Activations"), ...
+ Units='normalized', ...
+ Position=[0.05 0.05 0.9 0.85])
+t = tiledlayout(figureHeight, figureWidth, ...
+ TileSpacing='Compact', Padding='Compact');
+
+for i = 1:numel(muscleNames)
+ nexttile(i);
+ hold on
+ if ~isempty(meanExcitationsSynx)
+ 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([time(1) time(end) 0 1])
+ 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)', ...
+ 'Excitation (With SynX)', ...
+ 'Activation (With SynX)');
+ end
+ if mod(i,figureWidth) == 1
+ ylabel("Magnitude")
+ end
+ if i>numel(muscleNames)-figureWidth
+ xlabel("Time Points")
+ end
+end
+end
\ No newline at end of file
diff --git a/src/MuscleTendonPersonalization/Analysis/plotMtpNormalizedFiberLengths.m b/src/MuscleTendonPersonalization/Analysis/plotMtpNormalizedFiberLengths.m
new file mode 100644
index 000000000..b89c10d94
--- /dev/null
+++ b/src/MuscleTendonPersonalization/Analysis/plotMtpNormalizedFiberLengths.m
@@ -0,0 +1,69 @@
+% 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 plotMtpNormalizedFiberLengths(resultsDirectory)
+analysisDirectory = fullfile(resultsDirectory, "Analysis");
+[muscleNames, normalizedFiberLengths] = extractMtpDataFromSto( ...
+ fullfile(analysisDirectory, "normalizedFiberLengths"));
+muscleNames = strrep(muscleNames, '_', ' ');
+meanFiberLengths = mean(normalizedFiberLengths, 3);
+stdFiberLengths = std(normalizedFiberLengths, [], 3);
+time = 1:1:size(meanFiberLengths,1);
+passiveLower = ones(size(time))*0.7;
+passiveUpper = ones(size(time));
+
+figureWidth = ceil(sqrt(numel(muscleNames)));
+figureHeight = ceil(numel(muscleNames)/figureWidth);
+figure(Name = strcat(resultsDirectory, " 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)
+ nexttile(i);
+ hold on
+ 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([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")
+ end
+end
+
+end
+
diff --git a/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveForceCurves.m b/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveForceCurves.m
new file mode 100644
index 000000000..43b2f62b8
--- /dev/null
+++ b/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveForceCurves.m
@@ -0,0 +1,66 @@
+% 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 plotMtpPassiveForceCurves(resultsDirectory)
+analysisDirectory = fullfile(resultsDirectory, "Analysis");
+
+[muscleNames, modelForce] = extractMtpDataFromSto(...
+ fullfile(analysisDirectory, "passiveForcesModel"));
+
+muscleNames = strrep(muscleNames, '_', ' ');
+meanModelForce = mean(modelForce, 3);
+stdModelForce = std(modelForce, [], 3);
+maxForce = max(meanModelForce,[], 'all');
+numWindows = ceil(sqrt(numel(muscleNames)));
+
+figureWidth = ceil(sqrt(numel(muscleNames)));
+figureHeight = ceil(numel(muscleNames)/figureWidth);
+figure(Name = strcat(resultsDirectory, " Passive Force Curves"), ...
+ Units='normalized', ...
+ 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)
+ nexttile(i)
+ hold on
+ plotMeanAndStd(meanModelForce(:,i), stdModelForce(:,i), time, 'b-');
+ hold off
+ set(gca, fontsize=11)
+ axis([time(1) time(end) 0 maxForce])
+ title(muscleNames(i), FontSize=12);
+ if mod(i,figureWidth) == 1
+ ylabel("Force [N]")
+ end
+ if i>numel(muscleNames)-figureWidth
+ xlabel("Time Points")
+ end
+end
+
diff --git a/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveMomentCurves.m b/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveMomentCurves.m
new file mode 100644
index 000000000..fa58eea62
--- /dev/null
+++ b/src/MuscleTendonPersonalization/Analysis/plotMtpPassiveMomentCurves.m
@@ -0,0 +1,78 @@
+% 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 plotMtpPassiveMomentCurves(resultsDirectory)
+analysisDirectory = fullfile(resultsDirectory, "Analysis");
+[momentNames, passiveMomentsExperimental] = extractMtpDataFromSto( ...
+ fullfile(analysisDirectory, "passiveJointMomentsExperimental"));
+[~, passiveMomentsModel] = extractMtpDataFromSto( ...
+ fullfile(analysisDirectory, "passiveJointMomentsModeled"));
+momentNames = strrep(momentNames, '_', ' ');
+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')]);
+time = 1:1:size(meanMomentsModel,1);
+
+figureWidth = ceil(sqrt(numel(momentNames)));
+figureHeight = ceil(numel(momentNames)/figureWidth);
+
+figure(Name = strcat(resultsDirectory, " Passive Moment Curves"), ...
+ Units='normalized', ...
+ Position=[0.05 0.05 0.9 0.85])
+t = tiledlayout(figureHeight, figureWidth, ...
+ TileSpacing='Compact', Padding='Compact');
+
+for i = 1:numel(momentNames)
+ nexttile(i);
+ hold on
+ plotMeanAndStd(meanMomentsExperimental(:,i), stdMomentsExperimental(:,i), ...
+ time, 'k-')
+ plotMeanAndStd(meanMomentsModel(:,i), stdMomentsModel(:,i), time, 'r-')
+ hold off
+ set(gca, fontsize=11)
+ rmse = rms(passiveMomentsExperimental(:,i) - passiveMomentsModel(:,i));
+ title(sprintf("%s \n RMSE: %.4f", ...
+ momentNames(i), rmse), FontSize=12)
+ axis([time(1) time(end) minMoment maxMoment])
+ if i == 1
+ legend("Experimental", "Model")
+ end
+ if mod(i,figureWidth) == 1
+ ylabel("Moment [Nm]")
+ end
+end
+end
+
diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcTrailingLimb.m b/src/MuscleTendonPersonalization/Analysis/printMtpJointMomentMatchingError.m
similarity index 52%
rename from src/core/TreatmentOptimization/IntegrandTerms/calcTrailingLimb.m
rename to src/MuscleTendonPersonalization/Analysis/printMtpJointMomentMatchingError.m
index 4d8074ba4..9c5e198ad 100644
--- a/src/core/TreatmentOptimization/IntegrandTerms/calcTrailingLimb.m
+++ b/src/MuscleTendonPersonalization/Analysis/printMtpJointMomentMatchingError.m
@@ -1,13 +1,11 @@
% 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)
+% 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 %
@@ -17,7 +15,7 @@
% National Institutes of Health (R01 EB030520). %
% %
% Copyright (c) 2021 Rice University and the Authors %
-% Author(s): Marleny Vega %
+% 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. %
@@ -31,25 +29,27 @@
% 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;
+function printMtpJointMomentMatchingError(resultsDirectory)
+analysisDirectory = fullfile(resultsDirectory, "Analysis");
+[jointMomentLabels, muscleJointMoments] = extractMtpDataFromSto( ...
+ fullfile(resultsDirectory, "modelMoments"));
+[~, inverseDynamicsMoments] = extractMtpDataFromSto( ...
+ fullfile(analysisDirectory, "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
-end
\ No newline at end of file
+
diff --git a/src/MuscleTendonPersonalization/Analysis/readMomentArmsForPlotting.m b/src/MuscleTendonPersonalization/Analysis/readMomentArmsForPlotting.m
new file mode 100644
index 000000000..db81f74c8
--- /dev/null
+++ b/src/MuscleTendonPersonalization/Analysis/readMomentArmsForPlotting.m
@@ -0,0 +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/reportMuscleTendonPersonalizationResults.m b/src/MuscleTendonPersonalization/Analysis/reportMuscleTendonPersonalizationResults.m
similarity index 72%
rename from src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResults.m
rename to src/MuscleTendonPersonalization/Analysis/reportMuscleTendonPersonalizationResults.m
index 48926ecab..a38cba0bf 100644
--- a/src/MuscleTendonPersonalization/reportMuscleTendonPersonalizationResults.m
+++ b/src/MuscleTendonPersonalization/Analysis/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());
@@ -68,20 +242,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;
@@ -164,6 +339,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 +358,7 @@ function makeExcitationAndActivationPlots(results, resultsSynx, ...
muscleLabels)
end
end
+
function plotMuscleExcitationsAndActivations(muscleExcitations, ...
muscleExcitationsSynx, muscleActivations, muscleActivationsSynx, ...
muscleLabels)
@@ -326,6 +503,7 @@ function makeModelParameterPlots(finalValues, experimentalData, synergyParameter
if nargout == 1;vargout=h; end
end
+
function makeTaskSpecificMomentMatchingPlots(jointMoments, jointMomentsSynx, ...
inverseDynamicsMoments, coordinates, synergyParameters)
@@ -342,6 +520,7 @@ function makeTaskSpecificMomentMatchingPlots(jointMoments, jointMomentsSynx, ...
{'','Predicted (with residual excitations)','','Inverse dynamics'});
end
end
+
function plotJointMoments(jointMoments, inverseDynamicsMoments, taskName, ...
coordinates, subplotIndex, legendName)
@@ -374,6 +553,7 @@ function plotJointMoments(jointMoments, inverseDynamicsMoments, taskName, ...
set(gca, 'FontSize', 12)
end
end
+
function makeTaskSpecificNormalizedFiberLengthsPlots(...
normalizedFiberLengths, experimentalData, synergyParameters)
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..9a72cfd22 100644
--- a/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m
+++ b/src/MuscleTendonPersonalization/MuscleTendonPersonalizationTool.m
@@ -42,23 +42,17 @@ function MuscleTendonPersonalizationTool(settingsFileName)
else
precalInputs = struct('optimizeIsometricMaxForce', false);
end
-
optimizedParams = MuscleTendonPersonalization(inputs, params);
if params.performMuscleTendonLengthInitialization
- reportMuscleTendonPersonalizationResults(optimizedParams, ...
- inputs, precalInputs);
+ [finalValues, resultsStruct, modeledValues] = ...
+ getMtpResultsToSave(inputs, params, optimizedParams, precalInputs);
+ saveMuscleTendonPersonalizationResults(inputs, finalValues, modeledValues, ...
+ resultsStruct, resultsDirectory, precalInputs);
else
- reportMuscleTendonPersonalizationResults(optimizedParams, inputs);
-end
-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);
+ [finalValues, resultsStruct, modeledValues] = ...
+ getMtpResultsToSave(inputs, params, optimizedParams);
+ saveMuscleTendonPersonalizationResults(inputs, finalValues, modeledValues, ...
+ resultsStruct, resultsDirectory);
end
+printMtpJointMomentMatchingError(resultsDirectory);
+end
\ No newline at end of file
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 f364e7474..8c28fef96 100644
--- a/src/MuscleTendonPersonalization/Optimization/CostComputations/calcMuscleTendonNonLinearConstraints.m
+++ b/src/MuscleTendonPersonalization/Optimization/CostComputations/calcMuscleTendonNonLinearConstraints.m
@@ -25,12 +25,12 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function [c,ceq] = calcMuscleTendonNonLinearConstraints(values, ...
- primaryValues, isIncluded, experimentalData, params)
+function [c, ceq] = calcMuscleTendonNonLinearConstraints(values, ...
+ 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/Saving/getMtpResultsToSave.m b/src/MuscleTendonPersonalization/Saving/getMtpResultsToSave.m
new file mode 100644
index 000000000..49e8a7eb4
--- /dev/null
+++ b/src/MuscleTendonPersonalization/Saving/getMtpResultsToSave.m
@@ -0,0 +1,79 @@
+% 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): 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 [finalValues, resultsStruct, modeledValues] = ...
+ getMtpResultsToSave(mtpInputs, params, optimizedParams, precalInputs)
+finalValues = makeMtpValuesAsStruct([], optimizedParams, zeros(1, 7), mtpInputs);
+if nargin < 4
+ modeledValues = [];
+ precalInputs = [];
+else
+ updatedMaxIsometricForce = precalInputs.optimizeIsometricMaxForce;
+ tempValues.optimalFiberLengthScaleFactors = ...
+ mtpInputs.optimalFiberLength ./ precalInputs.optimalFiberLength;
+ tempValues.tendonSlackLengthScaleFactors = ...
+ mtpInputs.tendonSlackLength ./ precalInputs.tendonSlackLength;
+ precalInputs.maxIsometricForce = mtpInputs.maxIsometricForce;
+ precalInputs.optimizeIsometricMaxForce = 0;
+ modeledValues = calcMuscleTendonLengthInitializationModeledValues(tempValues, precalInputs);
+ if updatedMaxIsometricForce
+ finalValues.maxIsometricForce = mtpInputs.maxIsometricForce;
+ end
+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);
+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;
+ resultsStruct = struct("results", results, ...
+ "resultsSynx", resultsSynx);
+else
+ resultsStruct = struct("results", results);
+end
+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/saveMtpActivationAndExcitationData.m b/src/MuscleTendonPersonalization/Saving/saveMtpActivationAndExcitationData.m
new file mode 100644
index 000000000..4bb6a4288
--- /dev/null
+++ b/src/MuscleTendonPersonalization/Saving/saveMtpActivationAndExcitationData.m
@@ -0,0 +1,69 @@
+% 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 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
+end
\ No newline at end of file
diff --git a/src/MuscleTendonPersonalization/Saving/saveMtpJointMomentData.m b/src/MuscleTendonPersonalization/Saving/saveMtpJointMomentData.m
new file mode 100644
index 000000000..166518b78
--- /dev/null
+++ b/src/MuscleTendonPersonalization/Saving/saveMtpJointMomentData.m
@@ -0,0 +1,65 @@
+% 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 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
+end
\ No newline at end of file
diff --git a/src/VerificationOptimization/computeVerificationOptimizationContinuousFunction.m b/src/MuscleTendonPersonalization/Saving/saveMtpMuscleModelParameters.m
similarity index 57%
rename from src/VerificationOptimization/computeVerificationOptimizationContinuousFunction.m
rename to src/MuscleTendonPersonalization/Saving/saveMtpMuscleModelParameters.m
index 614800536..9a851e162 100644
--- a/src/VerificationOptimization/computeVerificationOptimizationContinuousFunction.m
+++ b/src/MuscleTendonPersonalization/Saving/saveMtpMuscleModelParameters.m
@@ -1,10 +1,15 @@
% 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)
+% 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 %
@@ -14,7 +19,7 @@
% National Institutes of Health (R01 EB030520). %
% %
% Copyright (c) 2021 Rice University and the Authors %
-% Author(s): 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,16 +33,17 @@
% 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 = calcVerificationOptimizationDynamicsConstraint(values, inputs.auxdata);
-path = calcVerificationOptimizationPathConstraint(values, modeledValues, inputs.auxdata);
-if ~isempty(path)
- modeledValues.path = path;
+function saveMtpMuscleModelParameters(mtpInputs, finalValues, resultsDirectory)
+if ~exist(resultsDirectory, "dir")
+ mkdir(resultsDirectory);
end
-modeledValues.integrand = calcVerificationOptimizationIntegrand(values, ...
- modeledValues, inputs.auxdata);
+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/Saving/saveMtpPassiveForceData.m b/src/MuscleTendonPersonalization/Saving/saveMtpPassiveForceData.m
new file mode 100644
index 000000000..f7d97c6a1
--- /dev/null
+++ b/src/MuscleTendonPersonalization/Saving/saveMtpPassiveForceData.m
@@ -0,0 +1,39 @@
+% 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 saveMtpPassiveForceData(mtpInputs, resultsStruct, ...
+ resultsDirectory)
+writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ...
+ resultsStruct.results.passiveForce, resultsStruct.results.time, ...
+ strcat(resultsDirectory, "\passiveForcesModel"), ...
+ "_passiveForcesModel.sto");
+end
diff --git a/src/VerificationOptimization/calcVerificationOptimizationPathConstraint.m b/src/MuscleTendonPersonalization/Saving/saveMtpPassiveMomentData.m
similarity index 50%
rename from src/VerificationOptimization/calcVerificationOptimizationPathConstraint.m
rename to src/MuscleTendonPersonalization/Saving/saveMtpPassiveMomentData.m
index fe37276f0..afaa65c52 100644
--- a/src/VerificationOptimization/calcVerificationOptimizationPathConstraint.m
+++ b/src/MuscleTendonPersonalization/Saving/saveMtpPassiveMomentData.m
@@ -1,10 +1,11 @@
% This function is part of the NMSM Pipeline, see file for full license.
%
-% This function calculates the path constraint for verification
-% optimization.
+% This function formats experimental and modeled passive moment data and
+% saves them to appropriate .sto files in a directory specified by
+% resultsDirectory.
%
-% (struct, struct, struct) -> (2D matrix)
-% Returns path constraint
+% (struct, struct, string) -> (None)
+% Saves passive moment data to .sto file.
% ----------------------------------------------------------------------- %
% The NMSM Pipeline is a toolkit for model personalization and treatment %
@@ -14,7 +15,7 @@
% National Institutes of Health (R01 EB030520). %
% %
% Copyright (c) 2021 Rice University and the Authors %
-% Author(s): 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,32 +29,26 @@
% 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);
+function saveMtpPassiveMomentData(precalInputs, modeledValues, resultsDirectory)
+modelPassiveMoments = permute(modeledValues.passiveModelMoments, [3 2 1]);
+experimentalPassiveMoments = permute(precalInputs.passiveData.inverseDynamicsMoments, [3 2 1]);
+numberOfMoments = size(modelPassiveMoments, 3);
+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]);
+writeMtpDataToSto(precalInputs.passivePrefixes, precalInputs.prefixes, ...
+ experimentalPassiveMoments, 1:1:size(experimentalPassiveMoments, 3), ...
+ fullfile(resultsDirectory, "passiveJointMomentsExperimental"), ...
+ "_passiveJointMomentsExperimental.sto")
+writeMtpDataToSto(precalInputs.passivePrefixes, precalInputs.prefixes, ...
+ modelPassiveMoments, 1:1:size(modelPassiveMoments, 3), ...
+ fullfile(resultsDirectory, "passiveJointMomentsModeled"), ...
+ "_passiveJointMomentsModeled.sto")
end
\ No newline at end of file
diff --git a/src/MuscleTendonPersonalization/Saving/saveMuscleTendonOptimizationParamsOld.m b/src/MuscleTendonPersonalization/Saving/saveMuscleTendonOptimizationParamsOld.m
new file mode 100644
index 000000000..598c03238
--- /dev/null
+++ b/src/MuscleTendonPersonalization/Saving/saveMuscleTendonOptimizationParamsOld.m
@@ -0,0 +1,217 @@
+% 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(resultsDirectory, optimizedParams, ...
+ mtpInputs, precalInputs)
+if nargin < 4
+ 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.
+ savePassiveMomentDataToSto(precalInputs, modeledValues, resultsDirectory);
+ savePassiveForceDataToSto(mtpInputs, modeledValues, results, resultsSynx, ...
+ resultsSynxNoResiduals, resultsDirectory);
+end
+
+printJointMomentMatchingError(resultsSynx.muscleJointMoments, ...
+ mtpInputs.inverseDynamicsMoments); % Keep this
+
+saveActivationAndExcitationDataToSto(mtpInputs, results, resultsSynx, resultsDirectory);
+
+saveAnalysisDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, results.normalizedFiberLength, ...
+ strcat(resultsDirectory, "\normalizedFiberLengths"), "_normalizedFiberLengths.sto")
+
+saveJointMomentDataToSto(mtpInputs, results, resultsSynx, resultsSynxNoResiduals, ...
+ resultsDirectory);
+
+saveMuscleModelParametersToSto(mtpInputs, finalValues, fullfile(resultsDirectory, ...
+ "muscleModelParameters"));
+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 savePassiveMomentDataToSto(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
+saveAnalysisDataToSto(precalInputs.passivePrefixes, precalInputs.prefixes, ...
+ experimentalPassiveMoments, fullfile(resultsDirectory, ...
+ "passiveJointMomentsExperimental"), "_passiveJointMomentsExperimental.sto")
+saveAnalysisDataToSto(precalInputs.passivePrefixes, precalInputs.prefixes, ...
+ modelPassiveMoments, fullfile(resultsDirectory, ...
+ "passiveJointMomentsModeled"), "_passiveJointMomentsModeled.sto")
+end
+
+function savePassiveForceDataToSto(mtpInputs, modeledValues, results, resultsSynx, ...
+ resultsSynxNoResiduals, resultsDirectory)
+saveAnalysisDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ...
+ modeledValues.passiveForce, strcat(resultsDirectory, ...
+ "\passiveForcesExperimental"), "_passiveForcesExperimental.sto");
+saveAnalysisDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ...
+ results.passiveForce, strcat(resultsDirectory, ...
+ "\passiveForcesModel"), "_passiveForcesModel.sto");
+saveAnalysisDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ...
+ resultsSynx.passiveForce, strcat(resultsDirectory, ...
+ "\passiveForcesModelSynx"), "_passiveForcesModelSynx.sto");
+saveAnalysisDataToSto(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 saveActivationAndExcitationDataToSto(mtpInputs, results, ...
+ resultsSynx, resultsDirectory)
+saveAnalysisDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ...
+ results.muscleExcitations, strcat(resultsDirectory, ...
+ "\muscleExcitations"), "_muscleExcitations.sto")
+saveAnalysisDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ...
+ resultsSynx.muscleExcitations, strcat(resultsDirectory, ...
+ "\muscleExcitationsSynx"), "_muscleExcitationsSynx.sto")
+saveAnalysisDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ...
+ results.muscleActivations, strcat(resultsDirectory, ...
+ "\muscleActivations"), "_muscleActivations.sto")
+saveAnalysisDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ...
+ resultsSynx.muscleActivations, strcat(resultsDirectory, ...
+ "\muscleActivationsSynx"), "_muscleActivationsSynx.sto")
+
+end
+
+function saveJointMomentDataToSto(mtpInputs, results, resultsSynx, ...
+ resultsSynxNoResiduals, resultsDirectory)
+saveAnalysisDataToSto(mtpInputs.coordinateNames, mtpInputs.prefixes, ...
+ results.muscleJointMoments, strcat(resultsDirectory, ...
+ "\modelJointMomentsNoSynx"), "_modelJointMomentsNoSynx.sto")
+saveAnalysisDataToSto(mtpInputs.coordinateNames, mtpInputs.prefixes, ...
+ resultsSynx.muscleJointMoments, strcat(resultsDirectory, ...
+ "\modelJointMomentsSynx"), "_modelJointMomentsSynx.sto")
+saveAnalysisDataToSto(mtpInputs.coordinateNames, mtpInputs.prefixes, ...
+ resultsSynxNoResiduals.muscleJointMoments, strcat(resultsDirectory, ...
+ "\modelJointMomentsSynxNoResiduals"), "_modelJointMomentsSynxNoResiduals.sto")
+saveAnalysisDataToSto(mtpInputs.coordinateNames, mtpInputs.prefixes, ...
+ mtpInputs.inverseDynamicsMoments, strcat(resultsDirectory, ...
+ "\inverseDynamicsJointMoments"), "_inverseDynamicsJointMoments.sto")
+end
+
+function saveMuscleModelParametersToSto(mtpInputs, finalValues, resultsDirectory)
+if ~exist(resultsDirectory, "dir")
+ mkdir(resultsDirectory);
+end
+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];
+
+writeToSto(columnLabels, 1:1:size(dataPoints,1), dataPoints, ...
+ fullfile(resultsDirectory, "muscleModelParameters.sto"));
+end
+
+function saveAnalysisDataToSto(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/Saving/saveMuscleTendonPersonalizationResults.m b/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m
new file mode 100644
index 000000000..8a7ef12f3
--- /dev/null
+++ b/src/MuscleTendonPersonalization/Saving/saveMuscleTendonPersonalizationResults.m
@@ -0,0 +1,62 @@
+% 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.
+%
+% (struct, struct, struct, struct, string, struct) -> (None)
+% Saves results in the struct to the given filenames
+
+% ----------------------------------------------------------------------- %
+% 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, 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 saveMuscleTendonPersonalizationResults(mtpInputs, finalValues, ...
+ modeledValues, resultsStruct, resultsDirectory, precalInputs)
+analysisDirectory = fullfile(resultsDirectory, "Analysis");
+if exist(analysisDirectory, "dir")
+ rmdir(analysisDirectory, 's')
+end
+if nargin < 6
+ precalInputs = [];
+end
+
+if ~isempty(precalInputs)
+ saveMtpPassiveMomentData(precalInputs, modeledValues, analysisDirectory);
+end
+saveMtpPassiveForceData(mtpInputs, resultsStruct, analysisDirectory);
+saveMtpActivationAndExcitationData(mtpInputs, resultsStruct, analysisDirectory);
+writeMtpDataToSto(mtpInputs.muscleNames, mtpInputs.prefixes, ...
+ resultsStruct.results.normalizedFiberLength, ...
+ resultsStruct.results.time, ...
+ fullfile(analysisDirectory, "normalizedFiberLengths"), ...
+ "_normalizedFiberLengths.sto")
+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);
+end
\ No newline at end of file
diff --git a/src/DesignOptimization/calcDesignOptimizationObjective.m b/src/MuscleTendonPersonalization/Saving/writeMtpDataToSto.m
similarity index 69%
rename from src/DesignOptimization/calcDesignOptimizationObjective.m
rename to src/MuscleTendonPersonalization/Saving/writeMtpDataToSto.m
index 977e809cf..513c8fd84 100644
--- a/src/DesignOptimization/calcDesignOptimizationObjective.m
+++ b/src/MuscleTendonPersonalization/Saving/writeMtpDataToSto.m
@@ -1,10 +1,12 @@
% This function is part of the NMSM Pipeline, see file for full license.
%
-% This function calculates the cost function objective for design
-% optimization.
+% 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.
%
-% (Number, Array of number, Number, struct) -> (Number)
-% Returns objective
+% (array), (cell), (array), (string), (string) -> (none)
+% Saves MTP data to a .sto file.
% ----------------------------------------------------------------------- %
% The NMSM Pipeline is a toolkit for model personalization and treatment %
@@ -14,7 +16,7 @@
% National Institutes of Health (R01 EB030520). %
% %
% Copyright (c) 2021 Rice University and the Authors %
-% Author(s): 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. %
@@ -27,14 +29,14 @@
% 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);
-if isfield(inputs, "finalTimeRange")
- continuousObjective = continuousObjective / finalTime;
+function writeMtpDataToSto(columnLabels, taskNames, data, time, ...
+ directory, fileName)
+if ~exist(directory, "dir")
+ mkdir(directory);
+end
+for i = 1 : size(data,1)
+ writeToSto(columnLabels, time(i, :), ...
+ permute(data(i,:,:), [3 2 1]), ...
+ strcat(directory, "\", taskNames(i), fileName))
end
-discreteObjective = sum(discrete) / length(discrete);
-if isnan(discreteObjective); discreteObjective = 0; end
-objective = continuousObjective + discreteObjective;
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/saveMuscleTendonPersonalizationResults.m b/src/MuscleTendonPersonalization/saveMuscleTendonPersonalizationResults.m
deleted file mode 100644
index ed9954558..000000000
--- a/src/MuscleTendonPersonalization/saveMuscleTendonPersonalizationResults.m
+++ /dev/null
@@ -1,61 +0,0 @@
-% This function is part of the NMSM Pipeline, see file for full license.
-%
-% 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)
-% Saves results in the struct to the given filenames
-
-% ----------------------------------------------------------------------- %
-% 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 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"));
-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")));
-end
-muscleNames = getMusclesFromCoordinates(model, coordinateNames);
-writeMuscleTendonPersonalizationOsimxFile(modelFileName, osimxFileName, ...
- finalValues, muscleNames, resultsDirectory);
-end
-
diff --git a/src/NeuralControlPersonalization/Analysis/plotMomentMatchingResults.m b/src/NeuralControlPersonalization/Analysis/plotMomentMatchingResults.m
index d301d2b05..05dfbdb30 100644
--- a/src/NeuralControlPersonalization/Analysis/plotMomentMatchingResults.m
+++ b/src/NeuralControlPersonalization/Analysis/plotMomentMatchingResults.m
@@ -49,24 +49,32 @@ 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;
+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)
- plot(experimentalTime, experimentalMoments(i, :), 'LineWidth', 2)
+ nexttile(subplotNumber)
+ 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);
diff --git a/src/NeuralControlPersonalization/Analysis/plotNcpActivationRmsAndVaf.m b/src/NeuralControlPersonalization/Analysis/plotNcpActivationRmsAndVaf.m
new file mode 100644
index 000000000..f2e871867
--- /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
diff --git a/src/NeuralControlPersonalization/Analysis/plotNeuralControlPersonalizationActivations.m b/src/NeuralControlPersonalization/Analysis/plotNeuralControlPersonalizationActivations.m
index 4a4b070f0..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;
-figure(1)
+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/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/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/NeuralControlPersonalization/parseNeuralControlPersonalizationSettingsTree.m b/src/NeuralControlPersonalization/parseNeuralControlPersonalizationSettingsTree.m
index 62337e9d9..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 = getSynergyGroups(tree, Model(inputs.model));
+inputs.synergyGroups = parseSynergyGroups(tree, inputs.model);
inputs = matchMuscleNamesFromCoordinatesAndSynergyGroups(inputs);
inputs = reorderPreprocessedDataByMuscleNames(inputs, inputs.muscleNames);
[inputs.maxIsometricForce, inputs.optimalFiberLength, ...
@@ -71,7 +72,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);
@@ -100,7 +101,6 @@
end
function params = getParams(tree, model, inputs)
-model = Model(model);
params = struct();
params.activationGroupNames = parseSpaceSeparatedList(tree, ...
'activation_muscle_groups');
@@ -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)
diff --git a/src/NeuralControlPersonalization/saveNeuralControlPersonalizationResults.m b/src/NeuralControlPersonalization/saveNeuralControlPersonalizationResults.m
index bd3f33f26..3a31364ad 100644
--- a/src/NeuralControlPersonalization/saveNeuralControlPersonalizationResults.m
+++ b/src/NeuralControlPersonalization/saveNeuralControlPersonalizationResults.m
@@ -1,12 +1,9 @@
function saveNeuralControlPersonalizationResults(synergyWeights, ...
- synergyCommands, activations, moments, inputs, resultsDirectory, ...
+ synergyCommands, activations, combinedMoments, ncpMoments, inputs, resultsDirectory, ...
precalInputs)
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
@@ -16,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( ...
@@ -36,13 +38,11 @@ function saveNeuralControlPersonalizationResults(synergyWeights, ...
tempActivations, ...
fullfile( ...
resultsDirectory, ...
- "combinedMuscleActivations", ...
inputs.prefixes(i) + "_combinedActivations.sto" ...
) ...
)
- 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";
@@ -53,16 +53,25 @@ 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
-if isstruct(precalInputs)
- writeNeuralControlPersonalizationOsimxFile(inputs, ...
- resultsDirectory, precalInputs)
-end
+writeNeuralControlPersonalizationOsimxFile(inputs, ...
+ resultsDirectory, precalInputs)
+
end
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 505efb316..8dcedc983 100644
--- a/src/Preprocessing/sectionDataFiles.m
+++ b/src/Preprocessing/sectionDataFiles.m
@@ -33,17 +33,21 @@
% 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));
[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 +56,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
diff --git a/src/Preprocessing/splitIntoTrials.m b/src/Preprocessing/splitIntoTrials.m
index c54f3d7ea..d56512523 100644
--- a/src/Preprocessing/splitIntoTrials.m
+++ b/src/Preprocessing/splitIntoTrials.m
@@ -54,6 +54,8 @@ function splitIntoTrials(timePairs, inputSettings, outputSettings)
'trial');
outputDir = getFieldByNameOrAlternate(outputSettings, ...
'resultsDirectory', 'preprocessed');
+cutoffFrequency = valueOrAlternate(inputSettings, 'cutoffFrequency', 6);
+rowsPerTrial = valueOrAlternate(inputSettings, 'rowsPerTrial', 101);
model = Model(model);
ikOutputDir = 'IKData';
@@ -90,16 +92,20 @@ 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);
+numBufferRows = 19;
+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 +173,40 @@ function preprocessMuscleAnalysisData(outputDir, muscleAnalysisDirectory, ...
end
end
-function filesToSection = makeFilesToSection(outputDir, ikOutputDir, ...
- idOutputDir, maOutputDir, grfOutputDir, trialName, coordinates, ...
- included)
-filesToSection = [];
+function [filesToSection, filesToFilter] = makeFilesToSection( ...
+ outputDir, ikOutputDir, idOutputDir, maOutputDir, grfOutputDir, ...
+ trialName, coordinates, included)
+filesToSection = string([]);
+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 +244,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 +256,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 / ...
diff --git a/src/SurrogateModelCreation/SurrogateModelCreation.m b/src/SurrogateModelCreation/SurrogateModelCreation.m
index 48034c28a..171c2a0f3 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. %
@@ -25,7 +25,7 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function SurrogateModelCreation(inputs)
+function surrogateMuscles = SurrogateModelCreation(inputs)
inputs = getData(inputs);
@@ -34,55 +34,59 @@ 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);
-
-saveSurrogateModel(inputs);
-reportSurrogateModel(inputs);
+surrogateMuscles = createSurrogateModel( ...
+ inputs.muscleSpecificJointAngles, inputs.muscleTendonLengths, ...
+ inputs.muscleSpecificMomentArms, inputs.polynomialDegree);
+
+if valueOrAlternate(inputs, 'plotResults', false)
+ inputs.surrogateMuscles = surrogateMuscles;
+ reportSurrogateModel(inputs);
+end
end
function inputs = getData(inputs)
-tree.trial_prefixes = inputs.trialPrefixes;
+tree.trial_prefixes.Text = 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( ...
- inputs.dataDirectory, "IKData"), prefixes);
-[inputs.experimentalJointAngles, inputs.coordinateNames] = ...
- parseInverseKinematicsFile(inverseKinematicsFileNames, inputs.model);
-inputs.experimentalJointAngles = ...
- reshape(permute(inputs.experimentalJointAngles, [1 3 2]), [], ...
- length(inputs.coordinateNames));
-directories = findFirstLevelSubDirectoriesFromPrefixes(fullfile( ...
- inputs.dataDirectory, "MAData"), 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);
+
+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);
inputs.muscleTendonLengths = findSpecificMusclesInData( ...
inputs.muscleTendonLengths, inputs.muscleTendonColumnNames, ...
inputs.muscleNames);
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));
-if(isempty(inputs.resultsDirectory))
+if(~isfield(inputs, "resultsDirectory") || isempty(inputs.resultsDirectory))
inputs.resultsDirectory = pwd;
end
end
@@ -102,6 +106,33 @@ function SurrogateModelCreation(inputs)
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)
diff --git a/src/SurrogateModelCreation/SurrogateModelPreviewTool.m b/src/SurrogateModelCreation/SurrogateModelPreviewTool.m
new file mode 100644
index 000000000..44e3e0cdc
--- /dev/null
+++ b/src/SurrogateModelCreation/SurrogateModelPreviewTool.m
@@ -0,0 +1,52 @@
+% 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.")
+end
+inputs.plotResults = true;
+SurrogateModelCreation(inputs);
+end
diff --git a/src/SurrogateModelCreation/calcSurrogateModel.m b/src/SurrogateModelCreation/calcSurrogateModel.m
index 3b639573d..46ea12847 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. %
@@ -38,25 +38,20 @@
% -----------------------------------------------------------------------
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};
- 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))
- newMomentArms(:, j, i) = vector(size(jointAngles{i}, 1) * ...
- index + 1 : size(jointAngles{i}, 1) * (index + 1));
+ [newMuscleTendonLengths(:, i), newMuscleTendonVelocities(:, i), ...
+ momentArms] = inputs.surrogateMuscles{i}(jointAngles{i}, ...
+ jointVelocities{i});
+ index = 1;
+ 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
end
diff --git a/src/SurrogateModelCreation/createSurrogateModel.m b/src/SurrogateModelCreation/createSurrogateModel.m
index 7715d4d8c..ffb82b194 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. %
@@ -36,20 +36,27 @@
% 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 [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);
+
+polynomialMuscleTendonLengths = matlabFunction(polynomialExpressionMuscleTendonLengths);
+polynomialMuscleTendonVelocities = matlabFunction(polynomialExpressionMuscleTendonVelocities);
+polynomialMomentArms = matlabFunction(polynomialExpressionMomentArms);
+
+surrogateMuscles{i} = @(jointAngles, jointVelocities)evaluateSurrogate( ...
+ jointAngles, jointVelocities, ...
+ polynomialMuscleTendonLengths, ...
+ polynomialMuscleTendonVelocities, ...
+ polynomialMomentArms, coefficients);
+end
end
-end
\ No newline at end of file
diff --git a/src/SurrogateModelCreation/evaluateSurrogate.m b/src/SurrogateModelCreation/evaluateSurrogate.m
new file mode 100644
index 000000000..cd2073b96
--- /dev/null
+++ b/src/SurrogateModelCreation/evaluateSurrogate.m
@@ -0,0 +1,56 @@
+% 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)
+
+muscleTendonLength = zeros(size(jointAngles, 1), 1);
+muscleTendonVelocity = zeros(size(jointAngles, 1), 1);
+momentArms = zeros(size(jointAngles)).';
+
+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
diff --git a/src/SurrogateModelCreation/getMuscleSpecificSurrogateModelData.m b/src/SurrogateModelCreation/getMuscleSpecificSurrogateModelData.m
index b98f76d9d..215d67e7f 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. %
@@ -32,9 +32,9 @@
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);
+ {convertStringsToChars(inputs.coordinateNames(j))};
inputs.muscleSpecificJointAngles{i}(:,counter) = ...
inputs.experimentalJointAngles(:,j);
inputs.muscleSpecificMomentArms{i}(:,counter) = ...
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/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/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')
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
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
diff --git a/src/TrackingOptimization/calcTorqueBasedModeledValues.m b/src/TrackingOptimization/calcTorqueBasedModeledValues.m
deleted file mode 100644
index 2976ee9de..000000000
--- a/src/TrackingOptimization/calcTorqueBasedModeledValues.m
+++ /dev/null
@@ -1,117 +0,0 @@
-% This function is part of the NMSM Pipeline, see file for full license.
-%
-% This function calculates the position and velocities of the spring
-% locations and corresponding ground reaction forces and moments if
-% contact surfaces are present. This function also calculates inverse
-% dynamic moments.
-%
-% (struct, struct) -> (struct)
-% returns body locations, ground reactions, and inverse dynamic moments
-
-% ----------------------------------------------------------------------- %
-% 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 = calcTorqueBasedModeledValues(values, params)
-appliedLoads = [zeros(length(values.time), params.numTotalMuscles)];
-if ~isempty(params.contactSurfaces)
- clear pointKinematics
- [springPositions, springVelocities] = getSpringLocations( ...
- values.time, values.statePositions, values.stateVelocities, params);
- modeledValues.bodyLocations = getBodyLocations(values.time, ....
- values.statePositions, values.stateVelocities, params);
- groundReactions = calcFootGroundReactions(springPositions, ...
- springVelocities, params, modeledValues.bodyLocations);
- groundReactionsBody = tranferGroundReactionMoments( ...
- modeledValues.bodyLocations, groundReactions, params);
- modeledValues.groundReactionsLab = calcGroundReactionsLab(groundReactions);
- appliedLoads = [appliedLoads groundReactionsBody];
-end
-modeledValues.inverseDynamicMoments = inverseDynamics(values.time, ...
- values.statePositions, values.stateVelocities, ...
- values.stateAccelerations, params.coordinateNames, appliedLoads, ...
- params.mexModel);
-end
-
-function [springPositions, springVelocities] = getSpringLocations(time, ....
- statePositions, stateVelocities, params)
-
-for i = 1:length(params.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);
- [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);
-end
-end
-
-function bodyLocations = getBodyLocations(time, statePositions, ...
- stateVelocities, params)
-
-for i = 1:length(params.contactSurfaces)
- bodyLocations.midfootSuperior{i} = pointKinematics(time, ...
- statePositions, stateVelocities, ...
- params.contactSurfaces{i}.midfootSuperiorPointOnBody, ...
- params.contactSurfaces{i}.midfootSuperiorBody, ...
- params.mexModel, params.coordinateNames);
- bodyLocations.midfootSuperior{i}(:, 2) = 0;
- bodyLocations.parent{i} = pointKinematics(time, statePositions, ...
- stateVelocities, [0 0 0], params.contactSurfaces{i}.parentBody, ...
- params.mexModel, params.coordinateNames);
- bodyLocations.child{i} = pointKinematics(time, statePositions, ...
- stateVelocities, [0 0 0], params.contactSurfaces{i}.childBody, ...
- params.mexModel, params.coordinateNames);
-end
-end
-
-function groundReactionsBody = tranferGroundReactionMoments( ...
- bodyLocations, groundReactions, params)
-
-groundReactionsBody = [];
-for i = 1:length(params.contactSurfaces)
- parentMoment = transferMoments(bodyLocations.midfootSuperior{i}, ...
- bodyLocations.parent{i}, groundReactions.parentMoments{i}, ...
- groundReactions.parentForces{i});
- childMoment = transferMoments(bodyLocations.midfootSuperior{i}, ...
- bodyLocations.child{i}, groundReactions.childMoments{i}, ...
- groundReactions.childForces{i});
- groundReactionsBody = [groundReactionsBody ...
- groundReactions.parentForces{i} groundReactions.childForces{i} ...
- parentMoment childMoment];
-end
-end
-
-function groundReactionsInLab = calcGroundReactionsLab(groundReactions)
-
-for i = 1:length(groundReactions.parentForces)
- groundReactionsInLab.forces{i} = ...
- groundReactions.parentForces{i} + groundReactions.childForces{i};
- groundReactionsInLab.moments{i} = ...
- groundReactions.parentMoments{i} + groundReactions.childMoments{i};
-end
-end
\ No newline at end of file
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/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/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/parseTrackingOptimizationSettingsTree.m b/src/TrackingOptimization/parseTrackingOptimizationSettingsTree.m
deleted file mode 100644
index 69de76a0b..000000000
--- a/src/TrackingOptimization/parseTrackingOptimizationSettingsTree.m
+++ /dev/null
@@ -1,89 +0,0 @@
-% This function is part of the NMSM Pipeline, see file for full license.
-%
-% This function parses the settings tree resulting from xml2struct of the
-% Tracking Optimizatoin settings XML file.
-%
-% (struct) -> (struct, struct)
-% returns the input values for 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 [inputs, params, resultsDirectory] = ...
- parseTrackingOptimizationSettingsTree(settingsTree)
-inputs = getTreatmentOptimizationInputs(settingsTree);
-inputs = getDesignVariableBounds(settingsTree, inputs);
-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
diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m
new file mode 100644
index 000000000..671c1c748
--- /dev/null
+++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationGroundReactions.m
@@ -0,0 +1,145 @@
+% This function is part of the NMSM Pipeline, see file for full license.
+%
+% 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.
+%
+% 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 %
+% 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(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");
+ 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")
+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
+ 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);
+ end
+ legend(legendValues)
+ 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
new file mode 100644
index 000000000..04c833481
--- /dev/null
+++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointAngles.m
@@ -0,0 +1,151 @@
+% This function is part of the NMSM Pipeline, see file for full license.
+%
+% 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 %
+% 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 plotTreatmentOptimizationJointAngles(modelFileName, ...
+ experimentalFile, modelFiles, figureWidth, figureHeight)
+
+import org.opensim.modeling.Storage
+model = Model(modelFileName);
+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 < 4
+ figureWidth = ceil(sqrt(numel(labels)));
+ figureHeight = ceil(numel(labels)/figureWidth);
+elseif nargin < 5
+ 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 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", ...
+ 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
+ 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);
+ end
+ legend(legendValues)
+ end
+ xlim("tight")
+ 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 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
diff --git a/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoads.m b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoads.m
new file mode 100644
index 000000000..20601b24a
--- /dev/null
+++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationJointLoads.m
@@ -0,0 +1,142 @@
+% This function is part of the NMSM Pipeline, see file for full license.
+%
+% 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.
+%
+% 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 %
+% 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 plotTreatmentOptimizationJointLoads(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
+ 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);
+ end
+ legend(legendValues)
+ end
+ xlim("tight")
+ 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
new file mode 100644
index 000000000..1187b41fd
--- /dev/null
+++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationMuscleActivations.m
@@ -0,0 +1,124 @@
+% This function is part of the NMSM Pipeline, see file for full license.
+%
+% 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.
+%
+% 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 %
+% 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 plotTreatmentOptimizationMuscleActivations(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
+ 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);
+ end
+ legend(legendValues)
+ 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
new file mode 100644
index 000000000..4589fc6ca
--- /dev/null
+++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationSynergyControls.m
@@ -0,0 +1,86 @@
+% 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 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.
+
+% ----------------------------------------------------------------------- %
+% 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)
+
+import org.opensim.modeling.Storage
+controlsStorage = Storage(controlsFile);
+labels = getStorageColumnNames(controlsStorage);
+controls = storageToDoubleMatrix(controlsStorage)';
+time = findTimeColumn(controlsStorage);
+if time(1) ~= 0
+ time = time - time(1);
+end
+if nargin < 2
+ figureWidth = ceil(sqrt(numel(labels)));
+ figureHeight = ceil(numel(labels)/figureWidth);
+elseif nargin < 3
+ figureHeight = ceil(sqrt(numel(labels)));
+end
+figureSize = figureWidth * figureHeight;
+figure(Name="Treatment Optimization Synergy Controls", ...
+ 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, "Synergy Controls")
+subplotNumber = 1;
+figureNumber = 1;
+for i=1:numel(labels)
+ if i > figureSize * figureNumber
+ figureNumber = figureNumber + 1;
+ figure(Name="Treatment Optimization Synergy Controls", ...
+ 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, "Synergy Controls")
+ subplotNumber = 1;
+ end
+ nexttile(subplotNumber);
+ hold on
+ plot(time, controls(:, i), LineWidth=2);
+ hold off
+ title(strrep(labels(i), "_", " "));
+ xlim("tight")
+ 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..264c8ca9c
--- /dev/null
+++ b/src/TreatmentOptimization/Analysis/plotTreatmentOptimizationTorqueControls.m
@@ -0,0 +1,86 @@
+% 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 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.
+
+% ----------------------------------------------------------------------- %
+% 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)
+
+import org.opensim.modeling.Storage
+controlsStorage = Storage(controlsFile);
+labels = getStorageColumnNames(controlsStorage);
+controls = storageToDoubleMatrix(controlsStorage)';
+time = findTimeColumn(controlsStorage);
+if time(1) ~= 0
+ time = time - time(1);
+end
+if nargin < 2
+ figureWidth = ceil(sqrt(numel(labels)));
+ 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])
+subplotNumber = 1;
+figureNumber = 1;
+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.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
+ nexttile(subplotNumber);
+ hold on
+ plot(time, controls(:, i), LineWidth=2);
+ hold off
+ title(strrep(labels(i), "_", " "));
+ xlim("tight")
+ subplotNumber = subplotNumber + 1;
+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 90%
rename from src/DesignOptimization/DesignOptimizationTool.m
rename to src/TreatmentOptimization/DesignOptimization/DesignOptimizationTool.m
index a9f20347e..744dab4ab 100644
--- a/src/DesignOptimization/DesignOptimizationTool.m
+++ b/src/TreatmentOptimization/DesignOptimization/DesignOptimizationTool.m
@@ -33,7 +33,10 @@ function DesignOptimizationTool(settingsFileName)
settingsTree = xml2struct(settingsFileName);
verifyVersion(settingsTree, "DesignOptimizationTool");
[inputs, params] = parseDesignOptimizationSettingsTree(settingsTree);
-[outputs, inputs] = DesignOptimization(inputs, params);
-reportTreatmentOptimizationResults(outputs, inputs);
+inputs = normalizeSynergyData(inputs);
+inputs = setupMuscleSynergies(inputs);
+inputs = setupTorqueControls(inputs);
+inputs = makeTreatmentOptimizationInputs(inputs, params);
+[inputs, outputs] = solveOptimalControlProblem(inputs, params);
saveDesignOptimizationResults(outputs, inputs);
end
diff --git a/src/TreatmentOptimization/DesignOptimization/parseDesignOptimizationSettingsTree.m b/src/TreatmentOptimization/DesignOptimization/parseDesignOptimizationSettingsTree.m
new file mode 100644
index 000000000..00a2592c4
--- /dev/null
+++ b/src/TreatmentOptimization/DesignOptimization/parseDesignOptimizationSettingsTree.m
@@ -0,0 +1,84 @@
+% This function is part of the NMSM Pipeline, see file for full license.
+%
+% This function parses the settings tree resulting from xml2struct of the
+% Design Optimizatoin settings XML file.
+%
+% (struct) -> (struct, struct)
+% returns the input values for 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, 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, params] = ...
+ parseDesignOptimizationSettingsTree(settingsTree)
+inputs = parseTreatmentOptimizationInputs(settingsTree);
+inputs = parseDesignSettings(settingsTree, inputs);
+inputs = parseUserDefinedFunctions(settingsTree, inputs);
+params = parseTreatmentOptimizationParams(settingsTree);
+end
+
+function inputs = parseUserDefinedFunctions(tree, inputs)
+import org.opensim.modeling.Storage
+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")
+ 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)
+try
+finalTimeRange = parseSpaceSeparatedList(tree, ...
+ 'final_time_range');
+if(~isempty(finalTimeRange))
+ inputs.finalTimeRange = str2double(finalTimeRange);
+end
+catch
+end
+end
+
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 61%
rename from src/DesignOptimization/saveDesignOptimizationResults.m
rename to src/TreatmentOptimization/DesignOptimization/saveDesignOptimizationResults.m
index 2cc6b6bf1..ac16976be 100644
--- a/src/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
%
@@ -30,13 +30,36 @@
% ----------------------------------------------------------------------- %
function saveDesignOptimizationResults(solution, inputs)
-values = getDesignOptimizationValueStruct(solution.solution.phase, inputs);
-saveCommonOptimalControlResults(solution, inputs, values)
+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);
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)
+ valuesStr = strcat(valuesStr, " ", num2str(parameterResults(j)));
+ end
+ parameters.NMSMPipelineDocument.RCNLParameters{i}.RCNLParameterSet.type = inputs.userDefinedVariables{i}.type;
+ parameters.NMSMPipelineDocument.RCNLParameters{i}.RCNLParameterSet.values = convertStringsToChars(valuesStr);
+ struct2xml(parameters, fullfile(inputs.resultsDirectory, ...
+ strcat(inputs.trialName, "_parameterSolution.xml")));
+ end
+end
end
\ No newline at end of file
diff --git a/src/DesignOptimization/updateSystemFromUserDefinedFunctions.m b/src/TreatmentOptimization/DesignOptimization/updateSystemFromUserDefinedFunctions.m
similarity index 90%
rename from src/DesignOptimization/updateSystemFromUserDefinedFunctions.m
rename to src/TreatmentOptimization/DesignOptimization/updateSystemFromUserDefinedFunctions.m
index 3649e31d5..0c623f955 100644
--- a/src/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/core/TreatmentOptimization/IntegrandTerms/calcMaximizingPropulsiveForceIntegrand.m b/src/TreatmentOptimization/DiscreteTerms/calcGoalRelativeWalkingSpeedDiscrete.m
similarity index 73%
rename from src/core/TreatmentOptimization/IntegrandTerms/calcMaximizingPropulsiveForceIntegrand.m
rename to src/TreatmentOptimization/DiscreteTerms/calcGoalRelativeWalkingSpeedDiscrete.m
index 54a6f8e74..c85033cc7 100644
--- a/src/core/TreatmentOptimization/IntegrandTerms/calcMaximizingPropulsiveForceIntegrand.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 maximizes the propulsive force for the specified foot.
-%
-% (struct, struct, struct) -> (Array of number)
+% 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) -> (double)
% ----------------------------------------------------------------------- %
% The NMSM Pipeline is a toolkit for model personalization and treatment %
@@ -13,7 +15,7 @@
% National Institutes of Health (R01 EB030520). %
% %
% Copyright (c) 2021 Rice University and the Authors %
-% Author(s): Marleny Vega %
+% 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. %
@@ -27,14 +29,10 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function cost = calcMaximizingPropulsiveForceIntegrand(modeledValues, ...
- params, costTerm)
+function cost = calcGoalRelativeWalkingSpeedDiscrete(values, ...
+ modeledValues, inputs, costTerm)
+
+rawCost = modeledValues.massCenterVelocity;
-for i = 1:length(params.contactSurfaces)
- if params.contactSurfaces{i}.isLeftFoot == costTerm.is_left_foot
- propulsiveForce = getPropulsiveForce( ...
- modeledValues.groundReactionsLab.forces{i}(:,1));
- end
+cost = ((rawCost - costTerm.errorCenter) ./ costTerm.maxAllowableError) .^ 2;
end
-cost = calcMaximizingCostArrayTerm(propulsiveForce);
-end
\ No newline at end of file
diff --git a/src/core/TreatmentOptimization/DiscreteTerms/calcTrackingSynergyVectorsDiscrete.m b/src/TreatmentOptimization/DiscreteTerms/calcTrackingSynergyVectorsDiscrete.m
similarity index 93%
rename from src/core/TreatmentOptimization/DiscreteTerms/calcTrackingSynergyVectorsDiscrete.m
rename to src/TreatmentOptimization/DiscreteTerms/calcTrackingSynergyVectorsDiscrete.m
index c2abeb7c2..4f3929673 100644
--- a/src/core/TreatmentOptimization/DiscreteTerms/calcTrackingSynergyVectorsDiscrete.m
+++ b/src/TreatmentOptimization/DiscreteTerms/calcTrackingSynergyVectorsDiscrete.m
@@ -31,11 +31,10 @@
function cost = calcTrackingSynergyVectorsDiscrete(synergyWeights, ...
params, costTerm)
-origSynergyWeights = getSynergyWeightsFromGroups(...
- params.synergyWeightsGuess, params);
+origSynergyWeights = params.initialSynergyWeights;
origSynergyWeights(origSynergyWeights==0) = [];
synergyWeights(synergyWeights==0) = [];
cost = calcTrackingCostArrayTerm(synergyWeights, ...
origSynergyWeights, 1:size(synergyWeights, 2));
-cost = cost(:) / costTerm.maxAllowableError;
+cost = cost(:);
end
\ No newline at end of file
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/calcSingleSupportTime.m b/src/TreatmentOptimization/IntegrandTerms/calcBrakingImpulseGoalIntegrand.m
similarity index 64%
rename from src/core/TreatmentOptimization/IntegrandTerms/calcSingleSupportTime.m
rename to src/TreatmentOptimization/IntegrandTerms/calcBrakingImpulseGoalIntegrand.m
index d0a98b91c..1de0b1f36 100644
--- a/src/core/TreatmentOptimization/IntegrandTerms/calcSingleSupportTime.m
+++ b/src/TreatmentOptimization/IntegrandTerms/calcBrakingImpulseGoalIntegrand.m
@@ -1,10 +1,10 @@
% 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)
+% 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 %
@@ -14,7 +14,7 @@
% National Institutes of Health (R01 EB030520). %
% %
% Copyright (c) 2021 Rice University and the Authors %
-% Author(s): Marleny Vega %
+% 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. %
@@ -28,29 +28,20 @@
% 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;
+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
-
-singleSupportTime = [];
-if toeOffEvent < heelStrikeEvent
- singleSupportTime = time(heelStrikeEvent) - time(toeOffEvent);
-end
-if toeOffEvent > heelStrikeEvent
- singleSupportTime = (time(end) - time(toeOffEvent)) + time(heelStrikeEvent);
+assert(~isempty(braking), "Unable to find braking force.")
+cost = real(sqrt((braking - costTerm.errorCenter) / ...
+ costTerm.maxAllowableError));
+if normalizeByFinalTime
+ cost = cost / time(end);
end
-if isempty(singleSupportTime)
- singleSupportTime = 0;
end
-end
\ No newline at end of file
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/TreatmentOptimization/IntegrandTerms/calcJointEnergyAbsorptionGoalIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcJointEnergyAbsorptionGoalIntegrand.m
new file mode 100644
index 000000000..f3113c385
--- /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 = calcJointEnergyAbsorptionGoalIntegrand(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
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
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/calcStrideLength.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingAngularMomentumIntegrand.m
similarity index 68%
rename from src/core/TreatmentOptimization/IntegrandTerms/calcStrideLength.m
rename to src/TreatmentOptimization/IntegrandTerms/calcMinimizingAngularMomentumIntegrand.m
index c40aa054c..8558b64f5 100644
--- a/src/core/TreatmentOptimization/IntegrandTerms/calcStrideLength.m
+++ b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingAngularMomentumIntegrand.m
@@ -1,9 +1,10 @@
% This function is part of the NMSM Pipeline, see file for full license.
%
-% This function calculates stride length.
-%
-% (struct, struct) -> (number)
+% 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 %
@@ -13,7 +14,7 @@
% National Institutes of Health (R01 EB030520). %
% %
% Copyright (c) 2021 Rice University and the Authors %
-% Author(s): Marleny Vega %
+% 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. %
@@ -27,20 +28,20 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function strideLength = calcStrideLength(modeledValues, params)
+function cost = calcMinimizingAngularMomentumIntegrand( ...
+ modeledValues, time, costTerm)
+normalizeByFinalTime = valueOrAlternate(costTerm, ...
+ "normalize_by_final_time", true);
+
+assert(isfield(costTerm, 'axis'), "Angular momentum minimization " + ...
+ "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.");
-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
+cost = modeledValues.angularMomentum(:, index);
+if normalizeByFinalTime
+ cost = cost / time(end);
end
-strideLength = sum(stepLength);
end
\ No newline at end of file
diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingExternalTorqueControl.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingExternalTorqueControl.m
similarity index 88%
rename from src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingExternalTorqueControl.m
rename to src/TreatmentOptimization/IntegrandTerms/calcMinimizingExternalTorqueControl.m
index 2b5ccb068..db74dc3b8 100644
--- a/src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingExternalTorqueControl.m
+++ b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingExternalTorqueControl.m
@@ -28,10 +28,14 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function cost = calcMinimizingExternalTorqueControl(...
- externalTorqueControl, params, coordinate)
-
+function cost = calcMinimizingExternalTorqueControl(costTerm, ...
+ 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/core/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingInverseDynamicLoadsIntegrand.m
similarity index 77%
rename from src/core/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m
rename to src/TreatmentOptimization/IntegrandTerms/calcMinimizingInverseDynamicLoadsIntegrand.m
index 7c3049d32..fcb44945f 100644
--- a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m
+++ b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingInverseDynamicLoadsIntegrand.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 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 %
@@ -28,17 +28,14 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function cost = calcTrackingCoordinateIntegrand(auxdata, time, ...
- statePositions, coordinateName)
-
-indx = find(strcmp(convertCharsToStrings(auxdata.coordinateNames), ...
- coordinateName));
-if auxdata.splineJointAngles.dim > 1
- experimentalJointAngles = fnval(auxdata.splineJointAngles, time)';
-else
- experimentalJointAngles = fnval(auxdata.splineJointAngles, time);
+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
-
-cost = calcTrackingCostArrayTerm(experimentalJointAngles, ...
- statePositions, indx);
-end
\ No newline at end of file
diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingJointPowerIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointPowerIntegrand.m
similarity index 86%
rename from src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingJointPowerIntegrand.m
rename to src/TreatmentOptimization/IntegrandTerms/calcMinimizingJointPowerIntegrand.m
index 39673ae16..4feb0fed3 100644
--- a/src/core/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)
@@ -27,14 +27,15 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function cost = calcMinimizingJointPowerIntegrand(jointVelocity, ...
- jointMoment, params, loadName)
-
+function cost = calcMinimizingJointPowerIntegrand(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.inverseDynamicMomentLabels, '_moment');
+momentLabelsNoSuffix = erase(params.inverseDynamicsMomentLabels, '_moment');
momentLabelsNoSuffix = erase(momentLabelsNoSuffix, '_force');
includedJointMomentCols = ismember(momentLabelsNoSuffix, ...
convertCharsToStrings(params.coordinateNames));
@@ -43,4 +44,7 @@
end
jointPower = jointMoment(:, indx) .* jointVelocity(:, indx);
cost = calcMinimizingCostArrayTerm(jointPower);
-end
\ No newline at end of file
+if normalizeByFinalTime
+ cost = cost / time(end);
+end
+end
diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingMuscleActivationIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcMinimizingMuscleActivationIntegrand.m
similarity index 89%
rename from src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingMuscleActivationIntegrand.m
rename to src/TreatmentOptimization/IntegrandTerms/calcMinimizingMuscleActivationIntegrand.m
index 0cccf99a1..aa8b74e5c 100644
--- a/src/core/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(costTerm, 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/core/TreatmentOptimization/getTerminalConstraintTerms.m b/src/TreatmentOptimization/IntegrandTerms/calcPropulsiveImpulseGoalIntegrand.m
similarity index 65%
rename from src/core/TreatmentOptimization/getTerminalConstraintTerms.m
rename to src/TreatmentOptimization/IntegrandTerms/calcPropulsiveImpulseGoalIntegrand.m
index 8f532ae19..8f6cc4b9b 100644
--- a/src/core/TreatmentOptimization/getTerminalConstraintTerms.m
+++ b/src/TreatmentOptimization/IntegrandTerms/calcPropulsiveImpulseGoalIntegrand.m
@@ -1,10 +1,10 @@
% 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.
+% 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) -> (struct)
-% Parses settings from a RCNLTerminalConstraintTerms.
+% (struct, Array of double, struct, struct) -> (Array of double)
% ----------------------------------------------------------------------- %
% The NMSM Pipeline is a toolkit for model personalization and treatment %
@@ -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): 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,17 +28,20 @@
% 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 = {};
+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
-else
- terminal = {};
-end
\ No newline at end of file
+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/DesignOptimization/computeDesignOptimizationContinuousFunction.m b/src/TreatmentOptimization/IntegrandTerms/calcRelativeMetabolicCostPerDistanceGoalDiscrete.m
similarity index 59%
rename from src/DesignOptimization/computeDesignOptimizationContinuousFunction.m
rename to src/TreatmentOptimization/IntegrandTerms/calcRelativeMetabolicCostPerDistanceGoalDiscrete.m
index f1588cf52..c669f8c3d 100644
--- a/src/DesignOptimization/computeDesignOptimizationContinuousFunction.m
+++ b/src/TreatmentOptimization/IntegrandTerms/calcRelativeMetabolicCostPerDistanceGoalDiscrete.m
@@ -1,10 +1,9 @@
% 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)
+% 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 %
@@ -14,7 +13,7 @@
% National Institutes of Health (R01 EB030520). %
% %
% Copyright (c) 2021 Rice University and the Authors %
-% Author(s): Marleny Vega %
+% 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. %
@@ -28,19 +27,24 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function modeledValues = computeDesignOptimizationContinuousFunction(inputs)
+function cost = calcRelativeMetabolicCostPerDistanceGoalDiscrete( ...
+ modeledValues, values, inputs, costTerm)
-values = getDesignOptimizationValueStruct(inputs.phase, inputs.auxdata);
-inputs = updateSystemFromUserDefinedFunctions(inputs, values);
-modeledValues = calcTorqueBasedModeledValues(values, inputs.auxdata);
-modeledValues = calcSynergyBasedModeledValues(values, inputs.auxdata, ...
- modeledValues);
-modeledValues.dynamics = calcDesignOptimizationDynamicsConstraint(values, ...
- inputs.auxdata);
-if ~isempty(inputs.auxdata.path)
- modeledValues.path = calcDesignOptimizationPathConstraint(values, ...
- modeledValues, inputs.auxdata);
+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
+
+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
-modeledValues.integrand = calcDesignOptimizationIntegrand(values, ...
- modeledValues, inputs.auxdata);
-end
\ No newline at end of file
diff --git a/src/TreatmentOptimization/IntegrandTerms/calcRelativeMetabolicCostPerTimeGoalDiscrete.m b/src/TreatmentOptimization/IntegrandTerms/calcRelativeMetabolicCostPerTimeGoalDiscrete.m
new file mode 100644
index 000000000..5f0b91dfe
--- /dev/null
+++ b/src/TreatmentOptimization/IntegrandTerms/calcRelativeMetabolicCostPerTimeGoalDiscrete.m
@@ -0,0 +1,39 @@
+% 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, 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 = calcRelativeMetabolicCostPerTimeGoalDiscrete( ...
+ modeledValues, values, inputs, costTerm)
+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/core/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m
similarity index 58%
rename from src/core/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m
rename to src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m
index 8f22e433a..7bcad1929 100644
--- a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m
+++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingControllerIntegrand.m
@@ -30,30 +30,46 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function cost = calcTrackingControllerIntegrand(auxdata, values, time, ...
- controllerName)
-
-switch auxdata.controllerType
- case 'synergy_driven'
- indx = find(strcmp(convertCharsToStrings( ...
- auxdata.synergyLabels), controllerName));
- synergyActivations = ...
- fnval(auxdata.splineSynergyActivations, time)';
- cost = calcTrackingCostArrayTerm(synergyActivations, ...
- values.controlSynergyActivations, indx);
- case 'torque_driven'
- indx1 = find(strcmp(convertCharsToStrings( ...
- auxdata.inverseDynamicMomentLabels), controllerName));
- indx2 = find(strcmp(convertCharsToStrings( ...
- strcat(auxdata.controlTorqueNames, '_moment')), ...
- controllerName));
- if auxdata.splineJointMoments.dim > 1
- experimentalJointMoments = ...
- fnval(auxdata.splineJointMoments, time)';
+function cost = calcTrackingControllerIntegrand(costTerm, inputs, ...
+ 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));
+ if ~isempty(indx)
+ if all(size(time) == size(inputs.collocationTimeOriginal)) && ...
+ max(abs(time - inputs.collocationTimeOriginal)) < 1e-6
+ synergyActivations = inputs.splinedSynergyActivations;
else
- experimentalJointMoments = ...
- fnval(auxdata.splineJointMoments, time);
+ synergyActivations = ...
+ evaluateGcvSplines(inputs.splineSynergyActivations, ...
+ inputs.synergyLabels, time);
end
- cost = experimentalJointMoments(:, indx1) - ...
- values.controlTorques(:, indx2);
-end
\ No newline at end of file
+ cost = calcTrackingCostArrayTerm(synergyActivations, ...
+ values.controlSynergyActivations, indx);
+ return
+ end
+end
+indx1 = find(strcmp(convertCharsToStrings( ...
+ strcat(inputs.torqueLabels, '_moment')), controllerName));
+indx2 = find(strcmp(convertCharsToStrings( ...
+ strcat(inputs.torqueControllerCoordinateNames, '_moment')), ...
+ controllerName));
+if all(size(time) == size(inputs.collocationTimeOriginal)) && ...
+ max(abs(time - inputs.collocationTimeOriginal)) < 1e-6
+ experimentalJointMoments = inputs.splinedTorqueControls;
+else
+ experimentalJointMoments = ...
+ evaluateGcvSplines(inputs.splineTorqueControls, ...
+ inputs.torqueLabels, time);
+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
new file mode 100644
index 000000000..a9b00eca6
--- /dev/null
+++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingCoordinateIntegrand.m
@@ -0,0 +1,58 @@
+% 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 = calcTrackingCoordinateIntegrand(costTerm, inputs, time, ...
+ 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)
+ throw(MException('CostTermError:CoordinateNotInState', ...
+ strcat("Coordinate ", coordinateName, " is not in the ", ...
+ "")))
+end
+if all(size(time) == size(inputs.collocationTimeOriginal)) && ...
+ max(abs(time - inputs.collocationTimeOriginal)) < 1e-6
+ experimentalJointAngles = inputs.splinedJointAngles;
+else
+ experimentalJointAngles = evaluateGcvSplines( ...
+ inputs.splineJointAngles, inputs.coordinateNames, time);
+end
+cost = calcTrackingCostArrayTerm(experimentalJointAngles, ...
+ positions, indx);
+
+if normalizeByFinalTime
+ cost = cost / time(end);
+end
+end
\ No newline at end of file
diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m
similarity index 72%
rename from src/core/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m
rename to src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalForcesIntegrand.m
index 377a5d0ae..7dfabfb47 100644
--- a/src/core/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 %
@@ -28,20 +28,24 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function cost = calcTrackingExternalForcesIntegrand(params, ...
+function cost = calcTrackingExternalForcesIntegrand(costTerm, inputs, ...
groundReactionsForces, time, forceName)
-
-for i = 1:length(params.contactSurfaces)
- indx = find(strcmp(convertCharsToStrings(params.contactSurfaces{i}. ...
+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));
if ~isempty(indx)
- if params.splineExperimentalGroundReactionForces{i}.dim > 1
- experimentalGroundReactions = ...
- fnval(params.splineExperimentalGroundReactionForces{i}, ...
- time)';
+ if all(size(time) == size(inputs.collocationTimeOriginal)) && ...
+ max(abs(time - inputs.collocationTimeOriginal)) < 1e-6
+ experimentalGroundReactions = inputs.splinedGroundReactionForces{i};
else
experimentalGroundReactions = ...
- fnval(params.splineExperimentalGroundReactionForces{i}, ...
+ evaluateGcvSplines( ...
+ inputs.splineExperimentalGroundReactionForces{i}, 0:2, ...
time);
end
cost = calcTrackingCostArrayTerm(...
@@ -49,4 +53,7 @@
indx);
end
end
+if normalizeByFinalTime
+ cost = cost / time(end);
+end
end
\ No newline at end of file
diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m
similarity index 72%
rename from src/core/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m
rename to src/TreatmentOptimization/IntegrandTerms/calcTrackingExternalMomentsIntegrand.m
index b903723a4..dbae7bc38 100644
--- a/src/core/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 %
@@ -28,20 +28,24 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function cost = calcTrackingExternalMomentsIntegrand(params, ...
+function cost = calcTrackingExternalMomentsIntegrand(costTerm, inputs, ...
groundReactionMoments, time, loadName)
-
-for i = 1:length(params.contactSurfaces)
- indx = find(strcmp(convertCharsToStrings(params.contactSurfaces{i}. ...
+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));
if ~isempty(indx)
- if params.splineExperimentalGroundReactionMoments{i}.dim > 1
- experimentalGroundReactions = ...
- fnval(params.splineExperimentalGroundReactionMoments{i}, ...
- time)';
+ if all(size(time) == size(inputs.collocationTimeOriginal)) && ...
+ max(abs(time - inputs.collocationTimeOriginal)) < 1e-6
+ experimentalGroundReactions = inputs.splinedGroundReactionMoments{i};
else
experimentalGroundReactions = ...
- fnval(params.splineExperimentalGroundReactionMoments{i}, ...
+ evaluateGcvSplines( ...
+ inputs.splineExperimentalGroundReactionMoments{i}, 0:2, ...
time);
end
cost = calcTrackingCostArrayTerm(...
@@ -49,4 +53,7 @@
indx);
end
end
+if normalizeByFinalTime
+ cost = cost / time(end);
+end
end
\ No newline at end of file
diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m
similarity index 64%
rename from src/core/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m
rename to src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m
index 9fb687f49..9d0e9a675 100644
--- a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m
+++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicLoadsIntegrand.m
@@ -28,26 +28,30 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function cost = calcTrackingInverseDynamicLoadsIntegrand(params, time, ...
- inverseDynamicMoments, loadName)
-
-loadName = erase(loadName, '_moment');
-loadName = erase(loadName, '_force');
-indx = find(strcmp(convertCharsToStrings(params.coordinateNames), ...
- loadName));
-
-if params.splineJointMoments.dim > 1
- experimentalJointMoments = fnval(params.splineJointMoments, time)';
+function cost = calcTrackingInverseDynamicLoadsIntegrand(costTerm, ...
+ 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)) && ...
+ max(abs(time - inputs.collocationTimeOriginal)) < 1e-6
+ experimentalJointMoments = inputs.splinedJointMoments;
else
- experimentalJointMoments = fnval(params.splineJointMoments, time);
+ experimentalJointMoments = evaluateGcvSplines( ...
+ inputs.splineJointMoments, inputs.inverseDynamicsMomentLabels, time);
end
-
-momentLabelsNoSuffix = erase(params.inverseDynamicMomentLabels, '_moment');
-momentLabelsNoSuffix = erase(momentLabelsNoSuffix, '_force');
-includedJointMomentCols = ismember(momentLabelsNoSuffix, convertCharsToStrings(params.coordinateNames));
-if size(inverseDynamicMoments, 2) ~= size(experimentalJointMoments, 2)
+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, ...
- inverseDynamicMoments, indx);
-end
\ No newline at end of file
+ inverseDynamicsMoments, indx);
+if normalizeByFinalTime
+ cost = cost / time(end);
+end
+end
diff --git a/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicSlopeIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicSlopeIntegrand.m
new file mode 100644
index 000000000..a3c3c7dda
--- /dev/null
+++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingInverseDynamicSlopeIntegrand.m
@@ -0,0 +1,63 @@
+% This function is part of the NMSM Pipeline, see file for full license.
+%
+% 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)
+%
+
+% ----------------------------------------------------------------------- %
+% 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);
+if normalizeByFinalTime
+ time = time * inputs.experimentalTime(end) / time(end);
+end
+indx = find(strcmp(inputs.inverseDynamicsMomentLabels, loadName));
+if all(size(time) == size(inputs.collocationTimeOriginal)) && ...
+ max(abs(time - inputs.collocationTimeOriginal)) < 1e-6
+ 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/IntegrandTerms/calcTrackingMarkerPosition.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m
new file mode 100644
index 000000000..61ec0bb63
--- /dev/null
+++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMarkerPosition.m
@@ -0,0 +1,64 @@
+% 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, 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)
+ throw(MException('CostTermError:MarkerDoesNotExist', ...
+ 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)) && ...
+ max(abs(time - inputs.collocationTimeOriginal)) < 1e-6
+ experimentalMarkerPositions = inputs.splinedMarkerPositions{indx};
+else
+ experimentalMarkerPositions = ...
+ evaluateGcvSplines(inputs.splineMarkerPositions{indx}, ...
+ 0:2, time);
+end
+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
+
diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m b/src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m
similarity index 70%
rename from src/core/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m
rename to src/TreatmentOptimization/IntegrandTerms/calcTrackingMuscleActivationIntegrand.m
index 392789b9c..e041a7448 100644
--- a/src/core/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 %
@@ -28,21 +28,25 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function cost = calcTrackingMuscleActivationIntegrand(muscleActivations, ...
- time, params, muscleName)
-
-indx = find(strcmp(convertCharsToStrings(params.muscleNames), ...
+function cost = calcTrackingMuscleActivationIntegrand(costTerm, ...
+ 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 params.splineMuscleActivations.dim > 1
- experimentalMuscleActivations = ...
- fnval(params.splineMuscleActivations, time)';
+if all(size(time) == size(inputs.collocationTimeOriginal)) && ...
+ max(abs(time - inputs.collocationTimeOriginal)) < 1e-6
+ experimentalMuscleActivations = inputs.splinedMuscleActivations;
else
- experimentalMuscleActivations = ...
- fnval(params.splineMuscleActivations, time);
+ experimentalMuscleActivations = evaluateGcvSplines( ...
+ inputs.splineMuscleActivations, inputs.muscleNames, time);
end
-
-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
new file mode 100644
index 000000000..cad63cb18
--- /dev/null
+++ b/src/TreatmentOptimization/IntegrandTerms/calcTrackingSpeedIntegrand.m
@@ -0,0 +1,59 @@
+% 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(costTerm, inputs, time, ...
+ 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)
+ throw(MException('CostTermError:CoordinateNotInState', ...
+ strcat("Coordinate ", coordinateName, " is not in the ", ...
+ "")))
+end
+if all(size(time) == size(inputs.collocationTimeOriginal)) && ...
+ max(abs(time - inputs.collocationTimeOriginal)) < 1e-6
+ experimentalJointVelocities = inputs.splinedJointSpeeds;
+else
+ experimentalJointVelocities = evaluateGcvSplines( ...
+ inputs.splineJointAngles, inputs.coordinateNames, time, 1);
+end
+
+cost = calcTrackingCostArrayTerm(experimentalJointVelocities, ...
+ velocities, indx);
+
+if normalizeByFinalTime
+ cost = cost / time(end);
+end
+end
\ No newline at end of file
diff --git a/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m
new file mode 100644
index 000000000..1a6638e38
--- /dev/null
+++ b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsInitialGuess.m
@@ -0,0 +1,150 @@
+% This function is part of the NMSM Pipeline, see file for full license.
+%
+% This function sets up the common initial guess for an optimal control
+% problem and is used by Tracking Optimization, Verification Optimization,
+% and Design Optimization
+%
+% (struct) -> (struct)
+% 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 guess = setupGpopsInitialGuess(inputs)
+guess = struct();
+guess = setupInitialStatesGuess(inputs, guess);
+guess = setupInitialControlsGuess(inputs, guess);
+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)
+if isfield(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);
+else
+ 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);
+end
+end
+
+function guess = setupInitialControlsGuess(inputs, guess)
+if isfield(inputs, "initialAccelerations")
+ controls = inputs.initialAccelerations;
+else
+ stateJointAccelerations = subsetDataByCoordinates( ...
+ inputs.experimentalJointAccelerations, ...
+ inputs.coordinateNames, ...
+ inputs.statesCoordinateNames);
+
+ controls = stateJointAccelerations;
+end
+if strcmp(inputs.controllerType, "synergy")
+ if isfield(inputs, "initialSynergyControls")
+ controls = [controls, inputs.initialSynergyControls];
+ else
+ throw(MException("NoInitialSynergyControls", ...
+ strcat("initial synergy controls required for synergy", ...
+ " driven, have you run NCP?")));
+ end
+end
+if isfield(inputs, "initialTorqueControls")
+ controls = [controls, inputs.initialTorqueControls];
+else
+ if ~isempty(valueOrAlternate(inputs, "torqueControllerCoordinateNames", []))
+ stateTorqueControls = subsetDataByCoordinates( ...
+ inputs.experimentalJointMoments, ...
+ erase(erase(inputs.inverseDynamicsMomentLabels, '_moment'), '_force'), ...
+ inputs.torqueControllerCoordinateNames);
+ controls = [controls, stateTorqueControls];
+ end
+end
+guess.phase.control = scaleToBounds(controls, inputs.maxControl, ...
+ inputs.minControl);
+end
+
+function guess = setupInitialParametersGuess(inputs, guess)
+if valueOrAlternate(inputs, "optimizeSynergyVectors", false)
+ 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")
+ for i = 1:length(inputs.userDefinedVariables)
+ 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);
+ guess.phase.parameter = guess.parameter;
+end
+end
+
+function output = subsetInitialStatesDataByCoordinates(data, ...
+ coordinateNames, subsetOfCoordinateNames)
+includedSubset = ismember(coordinateNames, subsetOfCoordinateNames);
+numCoordinates = length(includedSubset) / 2;
+for i = 1:numCoordinates
+ if includedSubset(i)
+ includedSubset(i + numCoordinates) = true;
+ end
+end
+output = data(:, includedSubset);
+end
\ No newline at end of file
diff --git a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlSolverSettings.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsSettings.m
similarity index 56%
rename from src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlSolverSettings.m
rename to src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsSettings.m
index 6f2798ed3..4445aaf64 100644
--- a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlSolverSettings.m
+++ b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGpopsSettings.m
@@ -30,40 +30,39 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function setup = setupCommonOptimalControlSolverSettings(inputs, ...
+function setup = setupGpopsSettings(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/OptimalControlSetupFunctions/setupGroundContact.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGroundContact.m
similarity index 92%
rename from src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupGroundContact.m
rename to src/TreatmentOptimization/OptimalControlSetupFunctions/setupGroundContact.m
index 098aefc94..dcaaf4123 100644
--- a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupGroundContact.m
+++ b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupGroundContact.m
@@ -34,13 +34,13 @@
midfootSuperiorLocation = pointKinematics(inputs.experimentalTime, ...
inputs.experimentalJointAngles, inputs.experimentalJointVelocities, ...
inputs.contactSurfaces{i}.midfootSuperiorPointOnBody, ...
- inputs.contactSurfaces{i}.midfootSuperiorBody, inputs.mexModel, ...
- inputs.coordinateNames);
- midfootSuperiorLocation(:, 2) = 0;
+ inputs.contactSurfaces{i}.midfootSuperiorBody, ...
+ inputs.modelFileName, inputs.coordinateNames);
+ midfootSuperiorLocation(:, 2) = inputs.contactSurfaces{i}.restingSpringLength;
inputs.contactSurfaces{i}.experimentalGroundReactionMoments = ...
transferMoments(inputs.contactSurfaces{i}.electricalCenter, ...
midfootSuperiorLocation, ...
inputs.contactSurfaces{i}.experimentalGroundReactionMoments, ...
inputs.contactSurfaces{i}.experimentalGroundReactionForces);
end
-end
\ No newline at end of file
+end
diff --git a/src/VerificationOptimization/computeVerificationOptimizationEndpointFunction.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupMuscleSynergies.m
similarity index 74%
rename from src/VerificationOptimization/computeVerificationOptimizationEndpointFunction.m
rename to src/TreatmentOptimization/OptimalControlSetupFunctions/setupMuscleSynergies.m
index 7820b6e96..677f1c11b 100644
--- a/src/VerificationOptimization/computeVerificationOptimizationEndpointFunction.m
+++ b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupMuscleSynergies.m
@@ -1,10 +1,10 @@
% 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)
+% 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 %
@@ -14,7 +14,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. %
@@ -28,12 +28,11 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function output = computeVerificationOptimizationEndpointFunction(inputs)
-if ~isempty(inputs.auxdata.terminal)
- output.eventgroup.event = ...
- calcVerificationOptimizationTerminalConstraint(inputs, ...
- inputs.auxdata);
+function inputs = setupMuscleSynergies(inputs)
+if strcmp(inputs.controllerType, 'synergy')
+ inputs.splineSynergyActivations = makeGcvSplineSet( ...
+ inputs.initialTime, inputs.initialSynergyControls', ...
+ inputs.initialSynergyControlsLabels);
+ inputs.synergyLabels = inputs.initialSynergyControlsLabels;
end
-output.objective = ...
- calcVerificationOptimizationObjective(inputs.phase.integral);
end
\ No newline at end of file
diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingBreakingForceIntegrand.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupTorqueControls.m
similarity index 73%
rename from src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingBreakingForceIntegrand.m
rename to src/TreatmentOptimization/OptimalControlSetupFunctions/setupTorqueControls.m
index a77b5524c..265298d4f 100644
--- a/src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingBreakingForceIntegrand.m
+++ b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupTorqueControls.m
@@ -1,9 +1,10 @@
% 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)
+% 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 %
@@ -13,7 +14,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. %
@@ -27,14 +28,11 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function cost = calcMinimizingBreakingForceIntegrand(modeledValues, ...
- params, costTerm)
-
-for i = 1:length(params.contactSurfaces)
- if params.contactSurfaces{i}.isLeftFoot == costTerm.is_left_foot
- breakingForce = getBreakingForce( ...
- modeledValues.groundReactionsLab.forces{i}(:,1));
- end
+function inputs = setupTorqueControls(inputs)
+if isfield(inputs, "torqueControllerCoordinateNames") && ...
+ ~isempty(inputs.torqueControllerCoordinateNames)
+inputs.splineTorqueControls = makeGcvSplineSet(inputs.initialTime, ...
+ inputs.initialTorqueControls', inputs.initialTorqueControlsLabels);
+inputs.torqueLabels = inputs.initialTorqueControlsLabels;
end
-cost = calcMinimizingCostArrayTerm(breakingForce);
end
\ No newline at end of file
diff --git a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlBounds.m b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupTreatmentOptimizationBounds.m
similarity index 69%
rename from src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlBounds.m
rename to src/TreatmentOptimization/OptimalControlSetupFunctions/setupTreatmentOptimizationBounds.m
index c24af6d92..7fbefdfe6 100644
--- a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlBounds.m
+++ b/src/TreatmentOptimization/OptimalControlSetupFunctions/setupTreatmentOptimizationBounds.m
@@ -29,12 +29,16 @@
% 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;
bounds.phase.finaltime.lower = 0.5;
bounds.phase.finaltime.upper = 0.5;
+if isfield(inputs, "finalTimeRange")
+ 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));
bounds.phase.initialstate.upper = 0.5 * ones(1, length(inputs.minState));
@@ -49,8 +53,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.upper = params.solverSettings.integralBound * ones(1, length(inputs.minIntegral));
+bounds.phase.integral.lower = zeros(1, length(inputs.continuousMaxAllowableError));
+bounds.phase.integral.upper = inputs.gpops.integralBound * ...
+ ones(1, length(inputs.continuousMaxAllowableError));
% setup terminal constraint bounds
if ~isempty(inputs.minTerminal)
bounds.eventgroup.lower = inputs.minTerminal;
@@ -58,5 +63,27 @@
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));
+ 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/PathTerms/calcKineticPathConstraint.m b/src/TreatmentOptimization/PathTerms/calcKineticPathConstraint.m
new file mode 100644
index 000000000..f5eca5d1d
--- /dev/null
+++ b/src/TreatmentOptimization/PathTerms/calcKineticPathConstraint.m
@@ -0,0 +1,66 @@
+% 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 = calcKineticPathConstraint(inputs, ...
+ modeledValues, torqueControls, loadName)
+inverseDynamicsIndex = find(strcmp(convertCharsToStrings(inputs.inverseDynamicsMomentLabels), ...
+ loadName));
+if strcmpi(inputs.controllerType, "synergy")
+ 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));
+end
+if isempty(synergyIndex)
+ synergyLoad = 0;
+else
+ synergyLoad = modeledValues.muscleJointMoments(:, synergyIndex);
+end
+if isempty(torqueIndex)
+ torqueLoad = 0;
+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
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 87%
rename from src/core/TreatmentOptimization/PathTerms/calcMuscleActuatedMomentsPathConstraints.m
rename to src/TreatmentOptimization/PathTerms/calcMuscleActuatedMomentsPathConstraints.m
index 396b5ec15..b12e66b84 100644
--- a/src/core/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)
%
@@ -29,12 +29,12 @@
% 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.inverseDynamicsMomentLabels), ...
loadName));
-indx2 = find(strcmp(strcat(params.surrogateModelCoordinateNames, ...
+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/core/TreatmentOptimization/PathTerms/calcMuscleActuatedMomentsWithExternalAidPathConstraints.m b/src/TreatmentOptimization/PathTerms/calcMuscleActuatedMomentsWithExternalAidPathConstraints.m
similarity index 90%
rename from src/core/TreatmentOptimization/PathTerms/calcMuscleActuatedMomentsWithExternalAidPathConstraints.m
rename to src/TreatmentOptimization/PathTerms/calcMuscleActuatedMomentsWithExternalAidPathConstraints.m
index 438b7962d..2ffcd8562 100644
--- a/src/core/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 %
@@ -32,16 +32,16 @@
function pathTerm = ...
calcMuscleActuatedMomentsWithExternalAidPathConstraints(params, ...
- modeledValues, externalControlTorques, coordinate)
+ modeledValues, externalTorqueControls, 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
+ externalTorqueControls(:, indx3));
+end
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 90%
rename from src/core/TreatmentOptimization/PathTerms/calcRootSegmentResidualsPathConstraints.m
rename to src/TreatmentOptimization/PathTerms/calcRootSegmentResidualsPathConstraints.m
index 084180cfe..01b5840e9 100644
--- a/src/core/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,9 +29,7 @@
% ----------------------------------------------------------------------- %
function pathTerm = calcRootSegmentResidualsPathConstraints(loadName, ...
- inverseDynamicMomentLabels, inverseDynamicMoments)
-
-indx = find(strcmp(convertCharsToStrings(inverseDynamicMomentLabels), ...
- loadName));
-pathTerm = inverseDynamicMoments(:, indx);
-end
\ No newline at end of file
+ inverseDynamicsMomentLabels, inverseDynamicsMoments)
+pathTerm = inverseDynamicsMoments(:, ...
+ find(strcmp(inverseDynamicsMomentLabels, loadName)));
+end
diff --git a/src/core/TreatmentOptimization/PathTerms/calcTorqueActuatedMomentsPathConstraints.m b/src/TreatmentOptimization/PathTerms/calcTorqueActuatedMomentsPathConstraints.m
similarity index 83%
rename from src/core/TreatmentOptimization/PathTerms/calcTorqueActuatedMomentsPathConstraints.m
rename to src/TreatmentOptimization/PathTerms/calcTorqueActuatedMomentsPathConstraints.m
index 8f05a4972..2a66bc3a2 100644
--- a/src/core/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 %
@@ -29,14 +29,14 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function pathTerm = calcTorqueActuatedMomentsPathConstraints(params, ...
- modeledValues, controlTorques, loadName)
+function pathTerm = calcTorqueActuatedMomentsPathConstraints(inputs, ...
+ modeledValues, torqueControls, 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));
-pathTerm = modeledValues.inverseDynamicMoments(:, indx1) - ...
- controlTorques(:, indx2);
-end
\ No newline at end of file
+indx1 = find(cellfun(@isequal, inputs.coordinateNames, ...
+ repmat({loadName}, 1, length(inputs.coordinateNames))));
+indx2 = find(strcmp(inputs.torqueControllerCoordinateNames, loadName));
+pathTerm = modeledValues.inverseDynamicsMoments(:, indx1) - ...
+ torqueControls(:, indx2);
+end
diff --git a/src/core/TreatmentOptimization/SetupBounds/getIntegralBounds.m b/src/TreatmentOptimization/SetupBounds/makeMaxAllowableError.m
similarity index 63%
rename from src/core/TreatmentOptimization/SetupBounds/getIntegralBounds.m
rename to src/TreatmentOptimization/SetupBounds/makeMaxAllowableError.m
index a7d046336..e0d4ac2e2 100644
--- a/src/core/TreatmentOptimization/SetupBounds/getIntegralBounds.m
+++ b/src/TreatmentOptimization/SetupBounds/makeMaxAllowableError.m
@@ -28,30 +28,42 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function inputs = getIntegralBounds(inputs)
-[~, continuousAllowedTypes] = generateCostTermStruct("continuous", inputs.toolName);
-[~, discreteAllowedTypes] = generateCostTermStruct("discrete", inputs.toolName);
+function [continuousMaxAllowableError, discreteMaxAllowableError] = ...
+ makeMaxAllowableError(toolName, costTerms)
+[~, continuousAllowedTypes] = generateCostTermStruct("continuous", toolName);
+[~, discreteAllowedTypes] = generateCostTermStruct("discrete", toolName);
-inputs.maxIntegral = [];
-inputs.minIntegral = [];
-for i = 1:length(inputs.costTerms)
- costTerm = inputs.costTerms{i};
+continuousMaxAllowableError = [];
+discreteMaxAllowableError = [];
+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, ...
- costTerm.maxAllowableError);
+ continuousMaxAllowableError = cat(2, ...
+ continuousMaxAllowableError, costTerm.maxAllowableError);
elseif strcmp(costTerm.type, "user_defined")
if strcmp(costTerm.cost_term_type, "continuous")
- inputs.maxIntegral = cat(2, inputs.maxIntegral, ...
+ 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, continuousAllowedTypes)) && ...
- any(ismember(costTerm.type, discreteAllowedTypes))
+ 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 ...
' 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/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m b/src/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m
new file mode 100644
index 000000000..4e6769ff2
--- /dev/null
+++ b/src/TreatmentOptimization/SetupBounds/makeOptimalControlBounds.m
@@ -0,0 +1,145 @@
+% This function is part of the NMSM Pipeline, see file for full license.
+%
+% This functions computes the maximum and minimum values for all design
+% variables. The maximum and minimum values for most design variables are
+% 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.
+%
+% (struct) -> (struct)
+% Computes max and min design variable bounds
+
+% ----------------------------------------------------------------------- %
+% 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 = makeOptimalControlBounds(inputs)
+inputs = makeStateBounds(inputs);
+inputs = makeControlBounds(inputs);
+end
+
+function inputs = makeStateBounds(inputs)
+if isfield(inputs, "finalTimeRange")
+ inputs.maxTime = inputs.finalTimeRange(2);
+else
+ inputs.maxTime = max(inputs.experimentalTime);
+end
+inputs.minTime = min(inputs.experimentalTime);
+
+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);
+
+inputs.maxState = [ ...
+ maxStatePositions, ...
+ maxStateVelocities, ...
+ % maxStateAccelerations, ...
+ ];
+inputs.minState = [ ...
+ minStatePositions, ...
+ minStateVelocities, ...
+ % minStateAccelerations, ...
+ ];
+end
+
+function inputs = makeControlBounds(inputs)
+
+stateJointAccelerations = subsetDataByCoordinates( ...
+ inputs.experimentalJointAccelerations, ...
+ inputs.coordinateNames, ...
+ inputs.statesCoordinateNames);
+
+inputs.maxControl = max(stateJointAccelerations) + ...
+ inputs.jointAccelerationsMultiple * range(stateJointAccelerations);
+inputs.minControl = min(stateJointAccelerations) - ...
+ inputs.jointAccelerationsMultiple * range(stateJointAccelerations);
+
+if strcmp(inputs.controllerType, 'synergy')
+ maxControlSynergyActivations = inputs.maxControlSynergyActivations * ...
+ ones(1, inputs.numSynergies);
+ inputs.maxControl = [inputs.maxControl maxControlSynergyActivations];
+ inputs.minControl = [inputs.minControl zeros(1, inputs.numSynergies)];
+
+ if inputs.optimizeSynergyVectors
+ 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")
+ maxTorqueControls = [];
+ minTorqueControls = [];
+ for i = 1:length(inputs.torqueControllerCoordinateNames)
+ indx = find(strcmp(convertCharsToStrings( ...
+ inputs.inverseDynamicsMomentLabels), ...
+ strcat(inputs.torqueControllerCoordinateNames(i), '_moment')));
+ if isempty(indx)
+ indx = find(strcmp(convertCharsToStrings( ...
+ inputs.inverseDynamicsMomentLabels), ...
+ strcat(inputs.torqueControllerCoordinateNames(i), '_force')));
+ end
+ maxTorqueControls(i) = max(inputs.experimentalJointMoments(:, ...
+ indx)) + inputs.maxTorqueControlsMultiple * ...
+ range(inputs.experimentalJointMoments(:, indx));
+ minTorqueControls(i) = min(inputs.experimentalJointMoments(:, ...
+ indx)) - inputs.maxTorqueControlsMultiple * ...
+ range(inputs.experimentalJointMoments(:, indx));
+ end
+ inputs.maxControl = [inputs.maxControl maxTorqueControls];
+ inputs.minControl = [inputs.minControl minTorqueControls];
+end
+end
diff --git a/src/core/TreatmentOptimization/SetupBounds/getPathConstraintBounds.m b/src/TreatmentOptimization/SetupBounds/makePathConstraintBounds.m
similarity index 96%
rename from src/core/TreatmentOptimization/SetupBounds/getPathConstraintBounds.m
rename to src/TreatmentOptimization/SetupBounds/makePathConstraintBounds.m
index cda10fc8f..3a0d966b7 100644
--- a/src/core/TreatmentOptimization/SetupBounds/getPathConstraintBounds.m
+++ b/src/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/TreatmentOptimization/SetupBounds/makeTerminalConstraintBounds.m
similarity index 97%
rename from src/core/TreatmentOptimization/SetupBounds/getTerminalConstraintBounds.m
rename to src/TreatmentOptimization/SetupBounds/makeTerminalConstraintBounds.m
index 4317d4fa3..cd4dad8c8 100644
--- a/src/core/TreatmentOptimization/SetupBounds/getTerminalConstraintBounds.m
+++ b/src/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/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 91%
rename from src/core/TreatmentOptimization/TerminalTerms/calcFinalStatePosition.m
rename to src/TreatmentOptimization/TerminalTerms/calcFinalStatePosition.m
index d212e50d9..78be6acdb 100644
--- a/src/core/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/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/IntegrandTerms/calcMaximizingTrailingLimbAngleIntegrand.m b/src/TreatmentOptimization/TerminalTerms/calcInitialStatePosition.m
similarity index 70%
rename from src/core/TreatmentOptimization/IntegrandTerms/calcMaximizingTrailingLimbAngleIntegrand.m
rename to src/TreatmentOptimization/TerminalTerms/calcInitialStatePosition.m
index f4c7ac140..41efc4048 100644
--- a/src/core/TreatmentOptimization/IntegrandTerms/calcMaximizingTrailingLimbAngleIntegrand.m
+++ b/src/TreatmentOptimization/TerminalTerms/calcInitialStatePosition.m
@@ -1,9 +1,10 @@
% 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)
+% 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 %
@@ -27,17 +28,15 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function cost = calcMaximizingTrailingLimbAngleIntegrand(values, ...
- modeledValues, params, costTerm)
-
-for i = 1:length(params.contactSurfaces)
- if params.contactSurfaces{i}.isLeftFoot == costTerm.is_left_foot
- normalForce = ...
- modeledValues.groundReactionsLab.forces{i}(:, 2) - 20;
- end
+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
-trailingLimbAngle = calcTrailingLimb(costTerm, values, ...
- normalForce, params);
-cost = calcMaximizingCostArrayTerm(trailingLimbAngle * ...
- ones(length(values.time), 1));
+initialStatePosition = statePositions(1, indx) - ...
+ auxdata.initialStatePositions(1, indx);
end
\ No newline at end of file
diff --git a/src/core/TreatmentOptimization/TerminalTerms/calcRootSegmentResidualsPeriodicity.m b/src/TreatmentOptimization/TerminalTerms/calcRootSegmentResidualsPeriodicity.m
similarity index 87%
rename from src/core/TreatmentOptimization/TerminalTerms/calcRootSegmentResidualsPeriodicity.m
rename to src/TreatmentOptimization/TerminalTerms/calcRootSegmentResidualsPeriodicity.m
index 27dbc3d4e..6eb2140ae 100644
--- a/src/core/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/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 74%
rename from src/core/TreatmentOptimization/TerminalTerms/calcSynergyWeightsSum.m
rename to src/TreatmentOptimization/TerminalTerms/calcSynergyWeightsSum.m
index ffba690bc..780da5f39 100644
--- a/src/core/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
diff --git a/src/TrackingOptimization/TrackingOptimizationTool.m b/src/TreatmentOptimization/TrackingOptimization/TrackingOptimizationTool.m
similarity index 90%
rename from src/TrackingOptimization/TrackingOptimizationTool.m
rename to src/TreatmentOptimization/TrackingOptimization/TrackingOptimizationTool.m
index 885dcd756..862ab3c89 100644
--- a/src/TrackingOptimization/TrackingOptimizationTool.m
+++ b/src/TreatmentOptimization/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);
-reportTreatmentOptimizationResults(outputs, inputs);
+%inputs = normalizeSynergyData(inputs);
+inputs = makeTreatmentOptimizationInputs(inputs, params);
+[inputs, outputs] = solveOptimalControlProblem(inputs, params);
saveTrackingOptimizationResults(outputs, inputs);
end
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 67%
rename from src/TrackingOptimization/calcSynergyBasedModeledValues.m
rename to src/TreatmentOptimization/TrackingOptimization/calcSynergyBasedModeledValues.m
index d5b33778e..c77095150 100644
--- a/src/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. %
@@ -32,39 +32,44 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function modeledValues = calcSynergyBasedModeledValues(values, params, ...
- modeledValues)
-if strcmp(params.controllerType, 'synergy_driven')
- [jointAngles, jointVelocities] = getMuscleActuatedDOFs(values, params);
- [params.muscleTendonLength, params.momentArms, ...
- params.muscleTendonVelocity] = calcSurrogateModel(params, ...
+function modeledValues = calcSynergyBasedModeledValues(values, inputs)
+if strcmp(inputs.controllerType, 'synergy')
+ [jointAngles, jointVelocities] = getMuscleActuatedDOFs(values, inputs);
+ [inputs.muscleTendonLength, inputs.momentArms, ...
+ inputs.muscleTendonVelocity] = calcSurrogateModel(inputs, ...
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]);
[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 = muscleJointMoments(:, ...
- params.surrogateModelIndex);
+ modeledValues.muscleJointMoments = permute(muscleJointMoments, [3 2 1]);
modeledValues.muscleJointMoments = modeledValues.muscleJointMoments(:, ...
- params.dofsActuatedIndex);
+ inputs.surrogateModelIndex);
+ modeledValues.muscleActivations = permute(modeledValues.muscleActivations, [3 2 1]);
+else
+ modeledValues.muscleActivations = [];
end
end
function muscleActivations = calcMuscleActivationFromSynergies(values)
muscleActivations = values.controlSynergyActivations * values.synergyWeights;
+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
new file mode 100644
index 000000000..32faa975c
--- /dev/null
+++ b/src/TreatmentOptimization/TrackingOptimization/calcTorqueBasedModeledValues.m
@@ -0,0 +1,155 @@
+% This function is part of the NMSM Pipeline, see file for full license.
+%
+% This function calculates the position and velocities of the spring
+% locations and corresponding ground reaction forces and moments if
+% contact surfaces are present. This function also calculates inverse
+% dynamic moments.
+%
+% (struct, struct) -> (struct)
+% returns body locations, ground reactions, and inverse dynamic moments
+
+% ----------------------------------------------------------------------- %
+% 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 modeledValues = calcTorqueBasedModeledValues(values, inputs, ...
+ modeledValues)
+[appliedLoads, modeledValues] = setupAppliedLoads(values, inputs, ...
+ modeledValues);
+modeledValues.markerPositions = calcTrackedMarkerPositions(values, inputs);
+[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, ...
+ 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()) ...
+ coordinateLoads];
+if ~isempty(inputs.contactSurfaces)
+ clear pointKinematics
+ [springPositions, springVelocities] = getSpringLocations( ...
+ values.time, values.positions, values.velocities, inputs);
+ modeledValues.bodyLocations = getBodyLocations(values.time, ....
+ 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);
+ appliedLoads = [appliedLoads groundReactionsBody];
+end
+end
+
+function [springPositions, springVelocities] = getSpringLocations(time, ....
+ positions, velocities, inputs)
+
+for i = 1:length(inputs.contactSurfaces)
+ [springPositions.parent{i}, springVelocities.parent{i}] = ...
+ 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, positions, velocities, ...
+ 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, positions, ...
+ velocities, inputs)
+
+for i = 1:length(inputs.contactSurfaces)
+ bodyLocations.midfootSuperior{i} = pointKinematics(time, ...
+ 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, positions, ...
+ velocities, [0 0 0], inputs.contactSurfaces{i}.parentBody, ...
+ inputs.mexModel, inputs.coordinateNames);
+ bodyLocations.child{i} = pointKinematics(time, positions, ...
+ velocities, [0 0 0], inputs.contactSurfaces{i}.childBody, ...
+ inputs.mexModel, inputs.coordinateNames);
+end
+end
+
+function groundReactionsBody = tranferGroundReactionMoments( ...
+ bodyLocations, groundReactions, params)
+
+groundReactionsBody = [];
+for i = 1:length(params.contactSurfaces)
+ parentMoment = transferMoments(bodyLocations.midfootSuperior{i}, ...
+ bodyLocations.parent{i}, groundReactions.parentMoments{i}, ...
+ groundReactions.parentForces{i});
+ 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];
+end
+end
+
+function groundReactionsInLab = calcGroundReactionsLab(groundReactions)
+
+for i = 1:length(groundReactions.parentForces)
+ groundReactionsInLab.forces{i} = ...
+ groundReactions.parentForces{i} + groundReactions.childForces{i};
+ groundReactionsInLab.moments{i} = ...
+ 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.trackedMarkerLocations, ...
+ inputs.trackedMarkerBodyIndices, inputs.mexModel, ...
+ inputs.coordinateNames);
+end
+end
diff --git a/src/TrackingOptimization/TrackingOptimization.m b/src/TreatmentOptimization/TrackingOptimization/parseTrackingOptimizationSettingsTree.m
similarity index 78%
rename from src/TrackingOptimization/TrackingOptimization.m
rename to src/TreatmentOptimization/TrackingOptimization/parseTrackingOptimizationSettingsTree.m
index 3521a8da6..71fb268f8 100644
--- a/src/TrackingOptimization/TrackingOptimization.m
+++ b/src/TreatmentOptimization/TrackingOptimization/parseTrackingOptimizationSettingsTree.m
@@ -1,10 +1,10 @@
% 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.
+% This function parses the settings tree resulting from xml2struct of the
+% Tracking Optimizatoin settings XML file.
%
-% (struct, struct) -> (struct, struct)
-% Inputs for the main function are setup
+% (struct) -> (struct, struct)
+% returns the input values for Tracking Optimization
% ----------------------------------------------------------------------- %
% The NMSM Pipeline is a toolkit for model personalization and treatment %
@@ -28,8 +28,9 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function [output, inputs] = TrackingOptimization(inputs, params)
-inputs = makeTreatmentOptimizationInputs(inputs, params);
-initializeMexOrMatlabParallelFunctions(inputs.mexModel);
-output = computeTrackingOptimizationMainFunction(inputs, params);
+function [inputs, params] = ...
+ parseTrackingOptimizationSettingsTree(settingsTree)
+inputs = parseTreatmentOptimizationInputs(settingsTree);
+params = parseTreatmentOptimizationParams(settingsTree);
end
+
diff --git a/src/TrackingOptimization/saveTrackingOptimizationResults.m b/src/TreatmentOptimization/TrackingOptimization/saveTrackingOptimizationResults.m
similarity index 76%
rename from src/TrackingOptimization/saveTrackingOptimizationResults.m
rename to src/TreatmentOptimization/TrackingOptimization/saveTrackingOptimizationResults.m
index 5850ee81c..b64192e6b 100644
--- a/src/TrackingOptimization/saveTrackingOptimizationResults.m
+++ b/src/TreatmentOptimization/TrackingOptimization/saveTrackingOptimizationResults.m
@@ -29,14 +29,6 @@
% ----------------------------------------------------------------------- %
function saveTrackingOptimizationResults(solution, inputs)
-values = getTrackingOptimizationValueStruct(solution.solution.phase, inputs);
-saveCommonOptimalControlResults(solution, inputs, values);
-if strcmp(inputs.controllerType, 'synergy_driven')
- writeToSto(inputs.muscleLabels, linspace(1, inputs.numSynergies, ...
- inputs.numSynergies), [values.synergyWeights], ...
- fullfile(inputs.resultsDirectory, "parameterSolution.sto"));
- writeToSto(inputs.muscleLabels, values.time, ...
- solution.muscleActivations, ...
- fullfile(inputs.resultsDirectory, "optimal", "muscleActivations.sto"));
+values = makeGpopsValuesAsStruct(solution.solution.phase, inputs);
+saveTreatmentOptimizationResults(solution, inputs, values);
end
-end
\ No newline at end of file
diff --git a/src/VerificationOptimization/VerificationOptimizationTool.m b/src/TreatmentOptimization/VerificationOptimization/VerificationOptimizationTool.m
similarity index 90%
rename from src/VerificationOptimization/VerificationOptimizationTool.m
rename to src/TreatmentOptimization/VerificationOptimization/VerificationOptimizationTool.m
index 55fe31f62..c43e67280 100644
--- a/src/VerificationOptimization/VerificationOptimizationTool.m
+++ b/src/TreatmentOptimization/VerificationOptimization/VerificationOptimizationTool.m
@@ -33,7 +33,10 @@ function VerificationOptimizationTool(settingsFileName)
settingsTree = xml2struct(settingsFileName);
verifyVersion(settingsTree, "VerificationOptimizationTool");
[inputs, params] = parseVerificationOptimizationSettingsTree(settingsTree);
-[outputs, inputs] = VerificationOptimization(inputs, params);
-reportTreatmentOptimizationResults(outputs, inputs);
+inputs = normalizeSynergyData(inputs);
+inputs = setupMuscleSynergies(inputs);
+inputs = setupTorqueControls(inputs);
+inputs = makeTreatmentOptimizationInputs(inputs, params);
+[inputs, outputs] = solveOptimalControlProblem(inputs, params);
saveVerificationOptimizationResults(outputs, inputs);
end
diff --git a/src/VerificationOptimization/getVerificationOptimizationValueStruct.m b/src/TreatmentOptimization/VerificationOptimization/parseVerificationOptimizationSettingsTree.m
similarity index 70%
rename from src/VerificationOptimization/getVerificationOptimizationValueStruct.m
rename to src/TreatmentOptimization/VerificationOptimization/parseVerificationOptimizationSettingsTree.m
index f3709571a..82dad5a83 100644
--- a/src/VerificationOptimization/getVerificationOptimizationValueStruct.m
+++ b/src/TreatmentOptimization/VerificationOptimization/parseVerificationOptimizationSettingsTree.m
@@ -1,11 +1,10 @@
% 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.
+% This function parses the settings tree resulting from xml2struct of the
+% Verification Optimizatoin settings XML file.
%
-% (struct, struct) -> (struct)
-% Design variables specific to Verification Optimization are parsed and scaled
+% (struct) -> (struct, struct)
+% returns the input values for Verification Optimization
% ----------------------------------------------------------------------- %
% The NMSM Pipeline is a toolkit for model personalization and treatment %
@@ -29,10 +28,15 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function values = getVerificationOptimizationValueStruct(inputs, params)
-values = getTreatmentOptimizationValueStruct(inputs, params);
-if strcmp(params.controllerType, 'synergy_driven')
- values.synergyWeights = getSynergyWeightsFromGroups(...
- params.synergyWeightsGuess, params);
+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);
end
+
+
diff --git a/src/VerificationOptimization/saveVerificationOptimizationResults.m b/src/TreatmentOptimization/VerificationOptimization/saveVerificationOptimizationResults.m
similarity index 94%
rename from src/VerificationOptimization/saveVerificationOptimizationResults.m
rename to src/TreatmentOptimization/VerificationOptimization/saveVerificationOptimizationResults.m
index eb8daf0be..147a911ca 100644
--- a/src/VerificationOptimization/saveVerificationOptimizationResults.m
+++ b/src/TreatmentOptimization/VerificationOptimization/saveVerificationOptimizationResults.m
@@ -28,7 +28,7 @@
% ----------------------------------------------------------------------- %
function saveVerificationOptimizationResults(solution, inputs)
-values = getVerificationOptimizationValueStruct( ...
+values = makeGpopsValuesAsStruct( ...
solution.solution.phase, inputs);
-saveCommonOptimalControlResults(solution, inputs, values)
+saveTreatmentOptimizationResults(solution, inputs, values)
end
\ No newline at end of file
diff --git a/src/core/TreatmentOptimization/addContactSurfaceActuators.m b/src/TreatmentOptimization/addContactSurfaceActuators.m
similarity index 72%
rename from src/core/TreatmentOptimization/addContactSurfaceActuators.m
rename to src/TreatmentOptimization/addContactSurfaceActuators.m
index fab90bec1..17b9df83e 100644
--- a/src/core/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/VerificationOptimization/calcVerificationOptimizationDynamicsConstraint.m b/src/TreatmentOptimization/calcDynamicConstraint.m
similarity index 86%
rename from src/VerificationOptimization/calcVerificationOptimizationDynamicsConstraint.m
rename to src/TreatmentOptimization/calcDynamicConstraint.m
index 6427cf5f5..f7bcf164b 100644
--- a/src/VerificationOptimization/calcVerificationOptimizationDynamicsConstraint.m
+++ b/src/TreatmentOptimization/calcDynamicConstraint.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,9 @@
% 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
+ ([values.stateVelocities values.controlAccelerations]) ./ (params.maxState - params.minState);
+end
diff --git a/src/core/TreatmentOptimization/calcTreatmentOptimizationCost.m b/src/TreatmentOptimization/calcTreatmentOptimizationCost.m
similarity index 94%
rename from src/core/TreatmentOptimization/calcTreatmentOptimizationCost.m
rename to src/TreatmentOptimization/calcTreatmentOptimizationCost.m
index 6463a0b56..00d291069 100644
--- a/src/core/TreatmentOptimization/calcTreatmentOptimizationCost.m
+++ b/src/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/TreatmentOptimization/checkInitialGuess.m b/src/TreatmentOptimization/checkInitialGuess.m
similarity index 82%
rename from src/core/TreatmentOptimization/checkInitialGuess.m
rename to src/TreatmentOptimization/checkInitialGuess.m
index 89b714ab1..a2c2c18f8 100644
--- a/src/core/TreatmentOptimization/checkInitialGuess.m
+++ b/src/TreatmentOptimization/checkInitialGuess.m
@@ -30,12 +30,20 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function checkInitialGuess(guess, inputs, continuousFunction)
+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
output = continuousFunction(initialGuess);
output.solution = initialGuess;
-end
\ No newline at end of file
+if length(output.metabolicCost) == length(inputs.experimentalTime)
+cumulativeMetabolicCost = trapz(inputs.experimentalTime, ...
+ output.metabolicCost);
+inputs.initialMetabolicCost = cumulativeMetabolicCost;
+inputs.initialMassCenterVelocity = output.massCenterVelocity;
+end
+end
diff --git a/src/core/TreatmentOptimization/disableModelMuscles.m b/src/TreatmentOptimization/disableModelMuscles.m
similarity index 83%
rename from src/core/TreatmentOptimization/disableModelMuscles.m
rename to src/TreatmentOptimization/disableModelMuscles.m
index df39b7064..a03de3844 100644
--- a/src/core/TreatmentOptimization/disableModelMuscles.m
+++ b/src/TreatmentOptimization/disableModelMuscles.m
@@ -27,11 +27,10 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-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);
+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);
+ end
end
end
\ No newline at end of file
diff --git a/src/TreatmentOptimization/generateConstraintTermStruct.m b/src/TreatmentOptimization/generateConstraintTermStruct.m
new file mode 100644
index 000000000..056ba228b
--- /dev/null
+++ b/src/TreatmentOptimization/generateConstraintTermStruct.m
@@ -0,0 +1,295 @@
+% 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", ...
+ "kinetic_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", ...
+ "kinetic_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", ...
+ "kinetic_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", ...
+ "initial_state_position_tracking", ...
+ "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", ...
+ "kinetic_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", ...
+ "kinetic_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", ...
+ "kinetic_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", ...
+ "initial_state_position_tracking", ...
+ "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.inverseDynamicsMomentLabels, ...
+ modeledValues.inverseDynamicsMoments ...
+ );
+
+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.torqueControls, ...
+ constraintTerm.load ...
+ );
+
+constraintTermCalculations.kinetic_consistency = @(values, modeledValues, auxdata, constraintTerm) ...
+ calcKineticPathConstraint( ...
+ auxdata, ...
+ modeledValues, ...
+ values.torqueControls, ...
+ 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.statesCoordinateNames, ...
+ constraintTerm.coordinate ...
+ );
+
+constraintTermCalculations.state_velocity_periodicity = @(values, modeledValues, auxdata, constraintTerm) ...
+ calcStateVelocityPeriodicity( ...
+ values.stateVelocities, ...
+ auxdata.statesCoordinateNames, ...
+ constraintTerm.coordinate ...
+ );
+
+constraintTermCalculations.root_segment_residual_load_periodicity = @(values, modeledValues, auxdata, constraintTerm) ...
+ calcRootSegmentResidualsPeriodicity(...
+ modeledValues.inverseDynamicsMoments, ...
+ auxdata.inverseDynamicsMomentLabels, ...
+ 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.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, ...
+ auxdata.statesCoordinateNames, ...
+ 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, ...
+ auxdata.synergyGroups, ...
+ constraintTerm.synergy_group);
+
+end
diff --git a/src/core/TreatmentOptimization/generateCostTermStruct.m b/src/TreatmentOptimization/generateCostTermStruct.m
similarity index 61%
rename from src/core/TreatmentOptimization/generateCostTermStruct.m
rename to src/TreatmentOptimization/generateCostTermStruct.m
index 8e00962c7..0c40de2c8 100644
--- a/src/core/TreatmentOptimization/generateCostTermStruct.m
+++ b/src/TreatmentOptimization/generateCostTermStruct.m
@@ -45,45 +45,43 @@
switch toolName
case "TrackingOptimization"
allowedTypes = [ ...
- "coordinate_tracking", ...
+ "generalized_coordinate_tracking", ...
+ "generalized_speed_tracking", ...
+ "marker_position_tracking", ...
"inverse_dynamics_load_tracking", ...
+ "inverse_dynamics_load_minimization", ...
+ "inverse_dynamics_slope_tracking", ...
+ "kinetic_inconsistency_minimization", ...
"external_force_tracking", ...
"external_moment_tracking", ...
"muscle_activation_tracking", ...
- "joint_jerk_minimization", ...
];
case "VerificationOptimization"
allowedTypes = [ ...
- "coordinate_tracking", ...
+ "generalized_coordinate_tracking", ...
+ "generalized_speed_tracking", ...
+ "marker_position_tracking", ...
"controller_tracking", ...
- "joint_jerk_minimization", ...
];
case "DesignOptimization"
allowedTypes = [ ...
- "coordinate_tracking", ...
+ "generalized_coordinate_tracking", ...
+ "generalized_speed_tracking", ...
+ "marker_position_tracking", ...
+ "inverse_dynamics_load_tracking", ...
+ "inverse_dynamics_slope_tracking", ...
+ "external_force_tracking", ...
+ "external_moment_tracking", ...
+ "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" ...
- "trailing_limb_angle_minimization" ...
- "trailing_limb_angle_maximization" ...
+ "joint_energy_generation_goal" ...
+ "joint_energy_absorption_goal" ...
+ "propulsive_impulse_goal" ...
+ "braking_impulse_goal" ...
"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", ...
];
otherwise
@@ -103,10 +101,13 @@
allowedTypes = [ ...
"synergy_vector_tracking" ...
"belt_speed_goal" ...
+ "relative_walking_speed_goal" ...
+ "relative_metabolic_cost_per_time" ...
+ "relative_metabolic_cost_per_distance" ...
"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 ...
@@ -116,45 +117,71 @@
function costTermCalculations = getCostTermCalculations(costTermType)
-costTermCalculations.coordinate_tracking = @(values, modeledValues, auxdata, costTerm) ...
+costTermCalculations.generalized_coordinate_tracking = @(values, modeledValues, auxdata, costTerm) ...
calcTrackingCoordinateIntegrand( ...
+ costTerm, ...
+ auxdata, ...
+ values.time, ...
+ values.positions, ...
+ costTerm.coordinate ...
+ );
+
+costTermCalculations.generalized_speed_tracking = @(values, modeledValues, auxdata, costTerm) ...
+ calcTrackingSpeedIntegrand( ...
+ costTerm, ...
auxdata, ...
values.time, ...
- values.statePositions, ...
+ 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, ...
costTerm.controller ...
);
-costTermCalculations.joint_jerk_minimization = @(values, modeledValues, auxdata, costTerm) ...
- calcMinimizingJointJerkIntegrand( ...
- values.controlJerks, ...
+costTermCalculations.inverse_dynamics_load_tracking = @(values, modeledValues, auxdata, costTerm) ...
+ calcTrackingInverseDynamicLoadsIntegrand( ...
+ costTerm, ...
auxdata, ...
- costTerm.coordinate ...
+ values.time, ...
+ modeledValues.inverseDynamicsMoments, ...
+ costTerm.load ...
);
-costTermCalculations.metabolic_cost_minimization = @(values, modeledValues, auxdata, costTerm) ...
- calcMinimizingMetabolicCost( ...
- values, ...
- modeledValues, ...
- auxdata);
+costTermCalculations.inverse_dynamics_load_minimization = @(values, modeledValues, auxdata, costTerm) ...
+ calcMinimizingInverseDynamicLoadsIntegrand( ...
+ costTerm, ...
+ auxdata, ...
+ values.time, ...
+ modeledValues.inverseDynamicsMoments, ...
+ costTerm.load ...
+ );
-costTermCalculations.inverse_dynamics_load_tracking = @(values, modeledValues, auxdata, costTerm) ...
- calcTrackingInverseDynamicLoadsIntegrand( ...
+costTermCalculations.inverse_dynamics_slope_tracking = @(values, modeledValues, auxdata, costTerm) ...
+ calcTrackingInverseDynamicSlopeIntegrand( ...
+ costTerm, ...
auxdata, ...
values.time, ...
- modeledValues.inverseDynamicMoments, ...
+ modeledValues.inverseDynamicsMoments, ...
costTerm.load ...
);
costTermCalculations.external_force_tracking = @(values, modeledValues, auxdata, costTerm) ...
calcTrackingExternalForcesIntegrand( ...
+ costTerm, ...
auxdata, ...
modeledValues.groundReactionsLab.forces, ...
values.time, ...
@@ -162,6 +189,7 @@
costTermCalculations.external_moment_tracking = @(values, modeledValues, auxdata, costTerm) ...
calcTrackingExternalMomentsIntegrand( ...
+ costTerm, ...
auxdata, ...
modeledValues.groundReactionsLab.moments, ...
values.time, ...
@@ -169,143 +197,95 @@
costTermCalculations.muscle_activation_tracking = @(values, modeledValues, auxdata, costTerm) ...
calcTrackingMuscleActivationIntegrand( ...
+ costTerm, ...
modeledValues.muscleActivations, ...
values.time, ...
auxdata, ...
costTerm.muscle);
-costTermCalculations.propulsive_force_maximization = @(values, modeledValues, auxdata, costTerm) ...
- calcMaximizingPropulsiveForceIntegrand( ...
- modeledValues, ...
- auxdata, ...
- costTerm);
-
-costTermCalculations.propulsive_force_minimization = @(values, modeledValues, auxdata, costTerm) ...
- calcMinimizingPropulsiveForceIntegrand( ...
- modeledValues, ...
- auxdata, ...
- costTerm);
-
-costTermCalculations.breaking_force_maximization = @(values, modeledValues, auxdata, costTerm) ...
- calcMaximizingBreakingForceIntegrand( ...
- modeledValues, ...
- auxdata, ...
- costTerm);
-
-costTermCalculations.breaking_force_minimization = @(values, modeledValues, auxdata, costTerm) ...
- calcMinimizingBreakingForceIntegrand( ...
- modeledValues, ...
- auxdata, ...
- costTerm);
-
-costTermCalculations.step_length_maximization = @(values, modeledValues, auxdata, costTerm) ...
- calcMaximizingStepLengthIntegrand( ...
- values, ...
- modeledValues, ...
- auxdata, ...
- costTerm);
-
-costTermCalculations.step_length_asymmetry_minimization = @(values, modeledValues, auxdata, costTerm) ...
- calcStepLengthAsymmetryIntegrand( ...
- values, ...
- modeledValues, ...
- auxdata, ...
- costTerm);
-
-costTermCalculations.single_support_time_maximization = @(values, modeledValues, auxdata, costTerm) ...
- calcMaximizingSingleSupportTimeIntegrand( ...
- values, ...
- modeledValues, ...
- auxdata, ...
- costTerm);
-
-
-costTermCalculations.single_support_time_goal = @(values, modeledValues, auxdata, costTerm) ...
- calcGoalSingleSupportTimeIntegrand( ...
- values, ...
- modeledValues, ...
+costTermCalculations.joint_power_minimization = @(values, modeledValues, auxdata, costTerm) ...
+ calcMinimizingJointPowerIntegrand( ...
+ costTerm, ...
+ values.velocities, ...
+ values.time, ...
+ modeledValues.inverseDynamicsMoments, ...
auxdata, ...
- costTerm);
+ costTerm.load ...
+ );
-costTermCalculations.step_time_asymmetry_minimization = @(values, modeledValues, auxdata, costTerm) ...
- calcStepTimeAsymmetryIntegrand( ...
- values, ...
- modeledValues, ...
+costTermCalculations.joint_energy_generation_goal = @(values, modeledValues, auxdata, costTerm) ...
+ calcJointEnergyGenerationGoalIntegrand( ...
+ costTerm, ...
+ values.velocities, ...
+ values.time, ...
+ modeledValues.inverseDynamicsMoments, ...
auxdata, ...
- costTerm);
+ costTerm.load ...
+ );
-costTermCalculations.joint_power_minimization = @(values, modeledValues, auxdata, costTerm) ...
- calcMinimizingJointPowerIntegrand( ...
- values.stateVelocities, ...
- modeledValues.inverseDynamicMoments, ...
+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, ...
+costTermCalculations.propulsive_impulse_goal = @(values, modeledValues, auxdata, costTerm) ...
+ calcJointEnergyAbsorptionGoalIntegrand( ...
modeledValues, ...
+ values.time, ...
auxdata, ...
costTerm ...
);
-costTermCalculations.trailing_limb_angle_maximization = @(values, modeledValues, auxdata, costTerm) ...
- calcMaximizingTrailingLimbAngleIntegrand( ...
- values, ...
+costTermCalculations.braking_impulse_goal = @(values, modeledValues, auxdata, costTerm) ...
+ calcJointEnergyAbsorptionGoalIntegrand( ...
modeledValues, ...
+ values.time, ...
auxdata, ...
costTerm ...
);
costTermCalculations.muscle_activation_minimization = @(values, modeledValues, auxdata, costTerm) ...
calcMinimizingMuscleActivationIntegrand( ...
+ costTerm, ...
+ values.time, ...
modeledValues.muscleActivations, ...
auxdata, ...
costTerm.muscle ...
);
-costTermCalculations.muscle_activation_maximization = @(values, modeledValues, auxdata, costTerm) ...
- calcMaximizingMuscleActivationIntegrand( ...
- modeledValues.muscleActivations, ...
+costTermCalculations.external_torque_control_minimization = @(values, modeledValues, auxdata, costTerm) ...
+ calcMinimizingExternalTorqueControl( ...
+ costTerm, ...
+ values.externalTorqueControls, ...
+ values.time, ...
auxdata, ...
- costTerm.muscle ...
- );
-
-costTermCalculations.center_mass_velocity_x_minimization = @(values, modeledValues, auxdata, costTerm) ...
- calcMinimizingMassCenterVelocityXIntegrand( ...
- values, ...
- auxdata);
+ costTerm.coordinate);
-costTermCalculations.center_mass_velocity_y_minimization = @(values, modeledValues, auxdata, costTerm) ...
- calcMinimizingMassCenterVelocityYIntegrand( ...
- values, ...
- auxdata);
+costTermCalculations.angular_momentum_minimization = @(values, modeledValues, auxdata, costTerm) ...
+ calcMinimizingAngularMomentumIntegrand( ...
+ modeledValues, ...
+ values.time, ...
+ costTerm);
-costTermCalculations.center_mass_velocity_z_minimization = @(values, modeledValues, auxdata, costTerm) ...
- calcMinimizingMassCenterVelocityZIntegrand( ...
+costTermCalculations.relative_metabolic_cost_per_time = @(values, modeledValues, auxdata, costTerm) ...
+ calcRelativeMetabolicCostPerTimeGoalDiscrete( ...
+ modeledValues, ...
values, ...
- auxdata);
-
-costTermCalculations.kinematic_symmetry = @(values, modeledValues, auxdata, costTerm) ...
- calcKinematicSymmetryIntegrand( ...
- values.statePositions, ...
auxdata, ...
costTerm);
-costTermCalculations.walking_speed_goal = @(values, modeledValues, auxdata, costTerm) ...
- calcGoalWalkingSpeedIntegrand( ...
- values, ...
+costTermCalculations.relative_metabolic_cost_per_distance = @(values, modeledValues, auxdata, costTerm) ...
+ calcRelativeMetabolicCostPerDistanceGoalDiscrete( ...
modeledValues, ...
+ values, ...
auxdata, ...
costTerm);
-costTermCalculations.external_torque_control_minimization = @(values, modeledValues, auxdata, costTerm) ...
- calcMinimizingExternalTorqueControl( ...
- values.externalTorqueControls, ...
- auxdata, ...
- costTerm.coordinate);
-
costTermCalculations.synergy_vector_tracking = @(values, modeledValues, auxdata, costTerm) ...
calcTrackingSynergyVectorsDiscrete( ...
values.synergyWeights, ...
@@ -318,6 +298,13 @@
auxdata, ...
costTerm);
+costTermCalculations.relative_walking_speed_goal = @(values, modeledValues, auxdata, costTerm) ...
+ calcGoalRelativeWalkingSpeedDiscrete( ...
+ values, ...
+ modeledValues, ...
+ auxdata, ...
+ costTerm);
+
costTermCalculations.user_defined = @(values, modeledValues, auxdata, costTerm) ...
userDefinedFunction(values, ...
modeledValues, ...
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/IntegrandTerms/calcMaximizingSingleSupportTimeIntegrand.m b/src/TreatmentOptimization/gpops/calcGpopsConstraint.m
similarity index 63%
rename from src/core/TreatmentOptimization/IntegrandTerms/calcMaximizingSingleSupportTimeIntegrand.m
rename to src/TreatmentOptimization/gpops/calcGpopsConstraint.m
index 9d8089cff..217ba5a34 100644
--- a/src/core/TreatmentOptimization/IntegrandTerms/calcMaximizingSingleSupportTimeIntegrand.m
+++ b/src/TreatmentOptimization/gpops/calcGpopsConstraint.m
@@ -1,8 +1,8 @@
% This function is part of the NMSM Pipeline, see file for full license.
%
-% This function maximizes the single support time for the specified foot.
+% This function computes path constraints (if any) for a gpops2 problem
%
-% (struct, struct, struct, struct) -> (Array of number)
+% (struct) -> (struct)
%
% ----------------------------------------------------------------------- %
@@ -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. %
@@ -27,22 +27,24 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function cost = calcMaximizingSingleSupportTimeIntegrand(values, ...
- modeledValues, params, costTerm)
-
-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);
+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);
+ constraint = cat(2, constraint, ...
+ fn(values, modeledValues, inputs, constraintTerm));
else
- singleSupportTime = calcSingleSupportTime( ...
- modeledValues.groundReactionsLab.forces{i - 1}(:, 2), ...
- values.time);
+% throw(MException('ConstraintTerms:IllegalTerm', ...
+% strcat("Constraint term ", constraintTerm.type, ...
+% " is not allowed for this tool")))
end
end
end
-cost = calcMaximizingCostArrayTerm(singleSupportTime * ...
- ones(length(values.time), 1));
-end
\ No newline at end of file
+end
+
diff --git a/src/TrackingOptimization/calcTrackingOptimizationIntegrand.m b/src/TreatmentOptimization/gpops/calcGpopsIntegrand.m
similarity index 83%
rename from src/TrackingOptimization/calcTrackingOptimizationIntegrand.m
rename to src/TreatmentOptimization/gpops/calcGpopsIntegrand.m
index a25d721c4..6798d4bf1 100644
--- a/src/TrackingOptimization/calcTrackingOptimizationIntegrand.m
+++ b/src/TreatmentOptimization/gpops/calcGpopsIntegrand.m
@@ -1,6 +1,6 @@
% This function is part of the NMSM Pipeline, see file for full license.
%
-% This function calculates the integrand for tracking optimization.
+% This function calculates the integrand for gpops.
%
% (struct, struct, struct) -> (2D matrix)
% Returns scaled integrand
@@ -27,12 +27,12 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function integrand = calcTrackingOptimizationIntegrand(values, ...
- modeledValues, auxdata)
+function integrand = calcGpopsIntegrand(values, modeledValues, inputs)
[costTermCalculations, allowedTypes] = ...
- generateCostTermStruct("continuous", "TrackingOptimization");
+ 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.continuousMaxAllowableError;
integrand = integrand .^ 2;
-end
\ No newline at end of file
+end
+
diff --git a/src/TrackingOptimization/computeTrackingOptimizationContinuousFunction.m b/src/TreatmentOptimization/gpops/computeGpopsContinuousFunction.m
similarity index 54%
rename from src/TrackingOptimization/computeTrackingOptimizationContinuousFunction.m
rename to src/TreatmentOptimization/gpops/computeGpopsContinuousFunction.m
index b6bd7c732..cf13f25a8 100644
--- a/src/TrackingOptimization/computeTrackingOptimizationContinuousFunction.m
+++ b/src/TreatmentOptimization/gpops/computeGpopsContinuousFunction.m
@@ -1,8 +1,8 @@
% 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.
-%
+% and cost function terms (if any) for gpops2.
+%
% (struct) -> (struct)
%
@@ -14,7 +14,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. %
@@ -28,14 +28,30 @@
% 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 = calcTrackingOptimizationDynamicsConstraint(values, inputs.auxdata);
-path = calcTrackingOptimizationPathConstraint(values, modeledValues, inputs.auxdata);
-if ~isempty(path)
- modeledValues.path = path;
+function modeledValues = computeGpopsContinuousFunction(setup)
+values = makeGpopsValuesAsStruct(setup.phase, setup.auxdata);
+if strcmp(setup.auxdata.toolName, "DesignOptimization")
+ setup = updateSystemFromUserDefinedFunctions(setup, values);
+end
+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))
+ [constraintTermCalculations, allowedTypes] = ...
+ generateConstraintTermStruct("path", ...
+ setup.auxdata.controllerType, setup.auxdata.toolName);
+ modeledValues.path = calcGpopsConstraint( ...
+ 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);
+if valueOrAlternate(setup.auxdata, 'calculateMetabolicCost', false)
+ modeledValues.integrand(:, end+1) = modeledValues.metabolicCost;
+end
+if isempty(modeledValues.integrand)
+ modeledValues = rmfield(modeledValues, "integrand");
+end
end
-modeledValues.integrand = calcTrackingOptimizationIntegrand(values, modeledValues, inputs.auxdata);
-end
\ No newline at end of file
diff --git a/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m b/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m
new file mode 100644
index 000000000..745676e3a
--- /dev/null
+++ b/src/TreatmentOptimization/gpops/computeGpopsEndpointFunction.m
@@ -0,0 +1,90 @@
+% 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 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));
+ if isfield(setup, "parameter")
+ setup.phase.parameter = setup.parameter;
+ end
+ values = makeGpopsValuesAsStruct(setup.phase, setup.auxdata);
+ if strcmp(setup.auxdata.toolName, "DesignOptimization")
+ setup = updateSystemFromUserDefinedFunctions(setup, values);
+ end
+ 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))
+ [constraintTermCalculations, allowedTypes] = ...
+ generateConstraintTermStruct("terminal", ...
+ setup.auxdata.controllerType, setup.auxdata.toolName);
+ 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")
+ [costTermCalculations, allowedTypes] = ...
+ generateCostTermStruct("discrete", "DesignOptimization");
+ discrete = calcTreatmentOptimizationCost( ...
+ costTermCalculations, allowedTypes, values, modeledValues, setup.auxdata);
+ discreteObjective = sum(discrete) / length(discrete);
+ if isnan(discreteObjective); discreteObjective = 0; end
+else
+ discreteObjective = 0;
+end
+
+if isfield(setup.phase, "integral") && ~any(isnan(setup.phase.integral)) && ~isempty(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
+
+output.objective = continuousObjective + discreteObjective;
+end
diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcMaximizingBreakingForceIntegrand.m b/src/TreatmentOptimization/gpops/convertFromGpopsOutputs.m
similarity index 72%
rename from src/core/TreatmentOptimization/IntegrandTerms/calcMaximizingBreakingForceIntegrand.m
rename to src/TreatmentOptimization/gpops/convertFromGpopsOutputs.m
index 561829a9c..6886b6356 100644
--- a/src/core/TreatmentOptimization/IntegrandTerms/calcMaximizingBreakingForceIntegrand.m
+++ b/src/TreatmentOptimization/gpops/convertFromGpopsOutputs.m
@@ -1,8 +1,5 @@
% 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)
%
% ----------------------------------------------------------------------- %
@@ -13,7 +10,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. %
@@ -27,14 +24,21 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function cost = calcMaximizingBreakingForceIntegrand(modeledValues, ...
- params, costTerm)
-
-for i = 1:length(params.contactSurfaces)
- if params.contactSurfaces{i}.isLeftFoot == costTerm.is_left_foot
- breakingForce = getBreakingForce( ...
- modeledValues.groundReactionsLab.forces{i}(:,1));
+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
-cost = calcMaximizingCostArrayTerm(breakingForce);
-end
\ No newline at end of file
+output = computeGpopsContinuousFunction(solution);
+output.solution = solution;
+end
+
diff --git a/src/TreatmentOptimization/gpops/convertToGpopsInputs.m b/src/TreatmentOptimization/gpops/convertToGpopsInputs.m
new file mode 100644
index 000000000..4c747aa15
--- /dev/null
+++ b/src/TreatmentOptimization/gpops/convertToGpopsInputs.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 [setup, inputs] = convertToGpopsInputs(inputs, params)
+bounds = setupTreatmentOptimizationBounds(inputs, params);
+guess = setupGpopsInitialGuess(inputs);
+setup = setupGpopsSettings(inputs, ...
+ bounds, guess, params, ...
+ @computeGpopsContinuousFunction, ...
+ @computeGpopsEndpointFunction);
+setup = preSplineGpopsInputs(setup);
+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/gpops/makeGpopsValuesAsStruct.m b/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m
new file mode 100644
index 000000000..e7c52f906
--- /dev/null
+++ b/src/TreatmentOptimization/gpops/makeGpopsValuesAsStruct.m
@@ -0,0 +1,156 @@
+% 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, length(inputs.statesCoordinateNames));
+values.stateVelocities = getCorrectStates( ...
+ state, 2, length(inputs.statesCoordinateNames));
+% 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 : ...
+ length(inputs.statesCoordinateNames) + inputs.numSynergies);
+end
+values.torqueControls = control(:, ...
+ length(inputs.statesCoordinateNames) + 1 + inputs.numSynergies : ...
+ length(inputs.statesCoordinateNames) + inputs.numSynergies + ...
+ length(inputs.torqueControllerCoordinateNames));
+
+if strcmp(inputs.toolName, "TrackingOptimization")
+ if strcmp(inputs.controllerType, 'synergy')
+ values.synergyWeights = inputs.synergyWeights;
+ if inputs.optimizeSynergyVectors
+ parameters = scaleToOriginal(phase.parameter(1,:), ...
+ inputs.maxParameter, inputs.minParameter);
+ 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
+if strcmp(inputs.toolName, "VerificationOptimization")
+ if strcmp(inputs.controllerType, 'synergy')
+ values.synergyWeights = inputs.synergyWeights;
+ end
+end
+if strcmp(inputs.toolName, "DesignOptimization")
+ counter = 1;
+ if strcmp(inputs.controllerType, 'synergy')
+ values.synergyWeights = inputs.synergyWeights;
+ if inputs.optimizeSynergyVectors
+ parameters = scaleToOriginal(phase.parameter(1,:), ...
+ inputs.maxParameter, inputs.minParameter);
+ 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
+ if isfield(inputs, 'userDefinedVariables') && ...
+ ~isempty(inputs.userDefinedVariables)
+ parameters = scaleToOriginal(phase.parameter(1,:), ...
+ inputs.maxParameter, inputs.minParameter);
+ for i = 1:length(inputs.userDefinedVariables)
+ values.parameters.(inputs.userDefinedVariables{i}.type) ...
+ = parameters(counter : counter + ...
+ length(inputs.userDefinedVariables{i}.initial_values) - 1);
+ counter = counter + ...
+ length(inputs.userDefinedVariables{i}.initial_values);
+ end
+ end
+end
+end
+
+% function [positions, velocities, accelerations] = recombineFullState( ...
+% values, inputs)
+function [positions, velocities] = recombineFullState( ...
+ values, inputs)
+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}));
+ if ~isempty(index)
+ positions(:, i) = values.statePositions(:, index);
+ velocities(:, i) = values.stateVelocities(:, 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/gpops/preSplineGpopsInputs.m b/src/TreatmentOptimization/gpops/preSplineGpopsInputs.m
new file mode 100644
index 000000000..06041359e
--- /dev/null
+++ b/src/TreatmentOptimization/gpops/preSplineGpopsInputs.m
@@ -0,0 +1,126 @@
+% 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 splinedSetup = preSplineGpopsInputs(setup)
+tempSetup = makeMinimalSetup(setup);
+collocationPointTimes = findCollocationPointsForSetup(tempSetup);
+splinedSetup = applyPreSpline(setup, collocationPointTimes);
+end
+
+function collocationPointTimes = findCollocationPointsForSetup(tempSetup)
+output = gpops2(tempSetup);
+collocationPointTimes = output.result.solution.phase.timeRadau;
+end
+
+function setup = applyPreSpline(setup, collocationPointTimes)
+setup.auxdata.collocationTimeBound = collocationPointTimes;
+time = scaleToOriginal(collocationPointTimes, setup.auxdata.maxTime, ...
+ setup.auxdata.minTime);
+setup.auxdata.collocationTimeOriginal = time;
+
+setup.auxdata.splinedJointAngles = evaluateGcvSplines(setup.auxdata.splineJointAngles, ...
+ setup.auxdata.coordinateNames, time, 0);
+setup.auxdata.splinedJointSpeeds = evaluateGcvSplines(setup.auxdata.splineJointAngles, ...
+ setup.auxdata.coordinateNames, time, 1);
+setup.auxdata.splinedJointAccelerations = evaluateGcvSplines(setup.auxdata.splineJointAngles, ...
+ setup.auxdata.coordinateNames, time, 2);
+setup.auxdata.splinedJointMoments = evaluateGcvSplines(setup.auxdata.splineJointMoments, ...
+ setup.auxdata.inverseDynamicsMomentLabels, time, 0);
+if strcmp(setup.auxdata.controllerType, 'synergy') && ...
+ isfield(setup.auxdata, 'splineSynergyActivations')
+ setup.auxdata.splinedSynergyActivations = evaluateGcvSplines( ...
+ setup.auxdata.splineSynergyActivations, ...
+ setup.auxdata.synergyLabels, time, 0);
+end
+if strcmp(setup.auxdata.controllerType, 'synergy') && ...
+ isfield(setup.auxdata, 'splineMuscleActivations')
+ setup.auxdata.splinedMuscleActivations = evaluateGcvSplines( ...
+ setup.auxdata.splineMuscleActivations, setup.auxdata.muscleLabels, time, 0);
+end
+if isfield(setup.auxdata, 'torqueControllerCoordinateNames') && ...
+ ~isempty(setup.auxdata.torqueControllerCoordinateNames)
+ 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)
+ setup.auxdata.splinedMarkerPositions{i} = evaluateGcvSplines( ...
+ setup.auxdata.splineMarkerPositions{i}, 0:2, time, 0);
+ end
+end
+for i = 1:length(setup.auxdata.contactSurfaces)
+ setup.auxdata.splinedGroundReactionForces{i} = evaluateGcvSplines( ...
+ setup.auxdata.splineExperimentalGroundReactionForces{i}, 0:2, time, 0);
+ setup.auxdata.splinedGroundReactionMoments{i} = evaluateGcvSplines( ...
+ setup.auxdata.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;
+tempSetup.displaylevel = 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/core/TreatmentOptimization/getSplines.m b/src/TreatmentOptimization/makeExperimentalDataSplines.m
similarity index 77%
rename from src/core/TreatmentOptimization/getSplines.m
rename to src/TreatmentOptimization/makeExperimentalDataSplines.m
index 26200073c..49aef1065 100644
--- a/src/core/TreatmentOptimization/getSplines.m
+++ b/src/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,21 +29,21 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function inputs = getSplines(inputs)
-inputs.splineJointAngles = spaps(inputs.experimentalTime, ...
- inputs.experimentalJointAngles', 0.0000001);
-inputs.splineJointMoments = spaps(inputs.experimentalTime, ...
- inputs.experimentalJointMoments', 0.0000001);
-if strcmp(inputs.controllerType, 'synergy_driven')
-inputs.splineMuscleActivations = spaps(inputs.experimentalTime, ...
- inputs.experimentalMuscleActivations', 0.0000001);
+function inputs = makeExperimentalDataSplines(inputs)
+inputs.splineJointMoments = makeGcvSplineSet(inputs.experimentalTime, ...
+ inputs.experimentalJointMoments, ...
+ inputs.inverseDynamicsMomentLabels);
+if strcmp(inputs.controllerType, 'synergy')
+ 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, string(0:2));
inputs.splineExperimentalGroundReactionMoments{i} = ...
- spaps(inputs.experimentalTime, ...
- inputs.contactSurfaces{i}.experimentalGroundReactionMoments', 0.0000001);
+ makeGcvSplineSet(inputs.experimentalTime, ...
+ inputs.contactSurfaces{i}.experimentalGroundReactionMoments, string(0:2));
+end
end
-end
\ No newline at end of file
diff --git a/src/TreatmentOptimization/makeMarkerTracking.m b/src/TreatmentOptimization/makeMarkerTracking.m
new file mode 100644
index 000000000..89cc013c4
--- /dev/null
+++ b/src/TreatmentOptimization/makeMarkerTracking.m
@@ -0,0 +1,66 @@
+% 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;
+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} = makeGcvSplineSet(inputs.experimentalTime, ...
+ experimentalMarkerPositions, ["x", "y", "z"]);
+ end
+end
+end
+
diff --git a/src/core/TreatmentOptimization/getStateDerivatives.m b/src/TreatmentOptimization/makeStateDerivatives.m
similarity index 78%
rename from src/core/TreatmentOptimization/getStateDerivatives.m
rename to src/TreatmentOptimization/makeStateDerivatives.m
index 697897800..689655b84 100644
--- a/src/core/TreatmentOptimization/getStateDerivatives.m
+++ b/src/TreatmentOptimization/makeStateDerivatives.m
@@ -28,14 +28,13 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function inputs = getStateDerivatives(inputs)
-points = length(inputs.experimentalTime);
-interval = inputs.experimentalTime(2) - inputs.experimentalTime(1);
-[N, Np, Npp] = BSplineMatrices(5, 10, points, interval);
-Nodes = N\inputs.experimentalJointAngles;
-inputs.experimentalJointVelocities = Np * Nodes;
-inputs.experimentalJointAccelerations = Npp * Nodes;
-inputs.experimentalJointJerks = calcBSplineDerivative( ...
- inputs.experimentalTime, inputs.experimentalJointAccelerations, ...
- 2, 10);
+function inputs = makeStateDerivatives(inputs, params)
+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);
end
diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingPropulsiveForceIntegrand.m b/src/TreatmentOptimization/makeSurrogateModel.m
similarity index 73%
rename from src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingPropulsiveForceIntegrand.m
rename to src/TreatmentOptimization/makeSurrogateModel.m
index 65659a815..49cc3bf55 100644
--- a/src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingPropulsiveForceIntegrand.m
+++ b/src/TreatmentOptimization/makeSurrogateModel.m
@@ -1,9 +1,9 @@
% 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)
+% 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 %
@@ -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. %
@@ -27,14 +27,14 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function cost = calcMinimizingPropulsiveForceIntegrand(modeledValues, ...
- params, costTerm)
-
-for i = 1:length(params.contactSurfaces)
- if params.contactSurfaces{i}.isLeftFoot == costTerm.is_left_foot
- propulsiveForce = getPropulsiveForce(...
- modeledValues.groundReactionsLab.forces{i}(:,1));
+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
end
-cost = calcMinimizingCostArrayTerm(propulsiveForce);
-end
\ No newline at end of file
+end
diff --git a/src/DesignOptimization/DesignOptimization.m b/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m
similarity index 57%
rename from src/DesignOptimization/DesignOptimization.m
rename to src/TreatmentOptimization/makeTreatmentOptimizationInputs.m
index df507bcaa..ab9bb71da 100644
--- a/src/DesignOptimization/DesignOptimization.m
+++ b/src/TreatmentOptimization/makeTreatmentOptimizationInputs.m
@@ -1,10 +1,9 @@
% 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
+% This function prepares the inputs for the all treatment optimization
+% modules (tracking, verification, and design optimization.
%
-% (struct, struct) -> (struct, struct)
-% Inputs for the main function are setup
+% (struct, struct) -> (struct)
% ----------------------------------------------------------------------- %
% The NMSM Pipeline is a toolkit for model personalization and treatment %
@@ -28,28 +27,33 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function [output, inputs] = DesignOptimization(inputs, params)
-inputs = makeTreatmentOptimizationInputs(inputs, params);
+function inputs = makeTreatmentOptimizationInputs(inputs, params)
+inputs = makeStateDerivatives(inputs, params);
+inputs.contactSurfaces = prepareGroundContactSurfaces( ...
+ inputs.modelFileName, inputs.contactSurfaces);
+inputs = modifyModelForces(inputs);
initializeMexOrMatlabParallelFunctions(inputs.mexModel);
-if strcmp(inputs.controllerType, 'synergy_driven')
- 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);
+inputs = setupGroundContact(inputs);
+inputs = makeExperimentalDataSplines(inputs);
+inputs = makeSurrogateModel(inputs);
+[inputs.continuousMaxAllowableError, inputs.discreteMaxAllowableError] ...
+ = makeMaxAllowableError(inputs.toolName, inputs.costTerms);
+inputs = makeMarkerTracking(inputs);
+inputs = makePathConstraintBounds(inputs);
+inputs = makeTerminalConstraintBounds(inputs);
+inputs = makeOptimalControlBounds(inputs);
+
+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
-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/core/TreatmentOptimization/modifyModelForces.m b/src/TreatmentOptimization/modifyModelForces.m
similarity index 74%
rename from src/core/TreatmentOptimization/modifyModelForces.m
rename to src/TreatmentOptimization/modifyModelForces.m
index 01082b6a6..602d87f42 100644
--- a/src/core/TreatmentOptimization/modifyModelForces.m
+++ b/src/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,20 @@
% ----------------------------------------------------------------------- %
function inputs = modifyModelForces(inputs)
-if ~isa(inputs.model, 'org.opensim.modeling.Model')
- model = Model(inputs.model);
+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, inputs] = disableModelMuscles(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);
+inputs.model = Model(inputs.mexModel);
end
\ No newline at end of file
diff --git a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/checkStateGuess.m b/src/TreatmentOptimization/normalizeSynergyData.m
similarity index 71%
rename from src/core/TreatmentOptimization/OptimalControlSetupFunctions/checkStateGuess.m
rename to src/TreatmentOptimization/normalizeSynergyData.m
index 987783638..e3ad6d933 100644
--- a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/checkStateGuess.m
+++ b/src/TreatmentOptimization/normalizeSynergyData.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 states file is in the correct
-% order
+% 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 %
@@ -14,7 +14,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. %
@@ -28,16 +28,15 @@
% 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
+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
- inputs.initialGuess.state = inputs.initialGuess.state(:, [stateIndex ...
- stateIndex + inputs.numCoordinates stateIndex + inputs.numCoordinates * 2]);
end
-end
\ No newline at end of file
+end
+
diff --git a/src/core/TreatmentOptimization/prepareGroundContactSurfaces.m b/src/TreatmentOptimization/parseGroundContactSurfaces.m
similarity index 65%
rename from src/core/TreatmentOptimization/prepareGroundContactSurfaces.m
rename to src/TreatmentOptimization/parseGroundContactSurfaces.m
index be84b8ca3..495238d73 100644
--- a/src/core/TreatmentOptimization/prepareGroundContactSurfaces.m
+++ b/src/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,28 +60,39 @@
grfFileName, output{i});
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
+
function output = parseGroundReactionDataWithoutTime(model, grfFile, output)
import org.opensim.modeling.Storage
[grfColumnNames, ~, grfData] = parseMotToComponents(model, Storage(grfFile));
diff --git a/src/TreatmentOptimization/prepareGroundContactSurfaces.m b/src/TreatmentOptimization/prepareGroundContactSurfaces.m
new file mode 100644
index 000000000..5f318e7e5
--- /dev/null
+++ b/src/TreatmentOptimization/prepareGroundContactSurfaces.m
@@ -0,0 +1,78 @@
+% 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 = [];
+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
\ No newline at end of file
diff --git a/src/core/TreatmentOptimization/reportTreatmentOptimizationResults.m b/src/TreatmentOptimization/reportTreatmentOptimizationResults.m
similarity index 55%
rename from src/core/TreatmentOptimization/reportTreatmentOptimizationResults.m
rename to src/TreatmentOptimization/reportTreatmentOptimizationResults.m
index f51169e41..91ef81a37 100644
--- a/src/core/TreatmentOptimization/reportTreatmentOptimizationResults.m
+++ b/src/TreatmentOptimization/reportTreatmentOptimizationResults.m
@@ -28,55 +28,44 @@
% ----------------------------------------------------------------------- %
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)
+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"]);
end
-values = getTreatmentOptimizationValueStruct(solution.solution.phase, inputs);
-if strcmp(inputs.controllerType, 'synergy_driven')
-% 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"]);
-else
% plot torque controls
-plotResultsWithOutComparison(values.controlTorques, values.time, ...
- inputs.controlTorqueNames, ["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);
% 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, ...
+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, ...
- 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')
@@ -92,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 1 0 experimentalTime(end)])
-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, ...
@@ -107,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, ...
@@ -120,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
new file mode 100644
index 000000000..d6e5222af
--- /dev/null
+++ b/src/TreatmentOptimization/saveTreatmentOptimizationResults.m
@@ -0,0 +1,168 @@
+% 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.
+%
+% (struct, struct) -> (None)
+% Prints treatment optimization 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, 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 saveTreatmentOptimizationResults(solution, inputs, values)
+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);
+saveExperimentalGroundReactions(inputs, inputs.resultsDirectory);
+
+stateLabels = inputs.statesCoordinateNames;
+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
+[time, data] = splineToEvenlySpaced(values.time, [values.statePositions ...
+ values.stateVelocities], length(inputs.experimentalTime));
+writeToSto(stateLabels, time, data, ...
+ fullfile(inputs.resultsDirectory, strcat(inputs.trialName, "_states.sto")));
+[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 = {};
+ 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
+ [time, controls] = splineToEvenlySpaced(values.time, ...
+ values.controlSynergyActivations, length(inputs.experimentalTime));
+ writeToSto(controlLabels, time, controls, ...
+ fullfile(inputs.resultsDirectory, ...
+ strcat(inputs.trialName, "_synergyCommands.sto")));
+end
+if ~isempty(valueOrAlternate(inputs, "torqueControllerCoordinateNames", []))
+ controlLabels = {};
+ for i = 1 : length(inputs.torqueControllerCoordinateNames)
+ controlLabels{end + 1} = inputs.torqueControllerCoordinateNames{i};
+ end
+ [time, controls] = splineToEvenlySpaced(values.time, values.torqueControls, ...
+ length(inputs.experimentalTime));
+ writeToSto(controlLabels, time, controls, ...
+ 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"));
+ [time, activations] = splineToEvenlySpaced(values.time, ...
+ solution.muscleActivations, length(inputs.experimentalTime));
+ writeToSto(inputs.muscleLabels, time, ...
+ activations, fullfile(inputs.resultsDirectory, ...
+ strcat(inputs.trialName, "_combinedActivations.sto")));
+end
+delete(inputs.mexModel);
+end
+
+function saveInverseKinematicsResults(inputs, values, outputDirectory)
+if ~exist(fullfile(outputDirectory, "IKData"), "dir")
+ mkdir(fullfile(outputDirectory, "IKData"))
+end
+[time, positions] = splineToEvenlySpaced(values.time, ...
+ values.positions, length(inputs.experimentalTime));
+writeToSto(inputs.coordinateNames, time, positions, ...
+ 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
+[time, moments] = splineToEvenlySpaced(values.time, ...
+ solution.inverseDynamicsMoments, length(inputs.experimentalTime));
+writeToSto(inputs.inverseDynamicsMomentLabels, time, ...
+ moments, 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.positions, values.velocities, ...
+ inputs.contactSurfaces{i}.midfootSuperiorPointOnBody, ...
+ inputs.contactSurfaces{i}.midfootSuperiorBody, inputs.modelFileName, ...
+ inputs.coordinateNames);
+ midfootSuperiorLocation(:, 2) = inputs.contactSurfaces{i}.restingSpringLength;
+ 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
+ [time, splinedGroundContactData] = splineToEvenlySpaced(values.time, ...
+ groundContactData, length(inputs.experimentalTime));
+ writeToSto(groundContactLabels, time, splinedGroundContactData, ...
+ fullfile(inputs.resultsDirectory, "GRFData", ...
+ 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
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/VerificationOptimization/calcVerificationOptimizationObjective.m b/src/TreatmentOptimization/solveOptimalControlProblem.m
similarity index 80%
rename from src/VerificationOptimization/calcVerificationOptimizationObjective.m
rename to src/TreatmentOptimization/solveOptimalControlProblem.m
index f12b2aa69..e04561826 100644
--- a/src/VerificationOptimization/calcVerificationOptimizationObjective.m
+++ b/src/TreatmentOptimization/solveOptimalControlProblem.m
@@ -1,10 +1,6 @@
% 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 %
@@ -14,7 +10,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. %
@@ -28,7 +24,11 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function objective = calcVerificationOptimizationObjective(integral)
-continuousObjective = sum(integral) / length(integral);
-objective = continuousObjective;
-end
\ No newline at end of file
+function [inputs, output] = solveOptimalControlProblem(inputs, params)
+[setup, inputs] = convertToGpopsInputs(inputs, params);
+setup.auxdata = inputs;
+solution = gpops2(setup);
+inputs = setup.auxdata;
+output = convertFromGpopsOutputs(solution, ...
+ inputs, params);
+end
diff --git a/src/TreatmentOptimization/splineToEvenlySpaced.m b/src/TreatmentOptimization/splineToEvenlySpaced.m
new file mode 100644
index 000000000..e904415af
--- /dev/null
+++ b/src/TreatmentOptimization/splineToEvenlySpaced.m
@@ -0,0 +1,40 @@
+% This function is part of the NMSM Pipeline, see file for full license.
+%
+% makeStateDerivatives() 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, newData] = splineToEvenlySpaced(time, data, ...
+ numPoints)
+gcvSplineSet = makeGcvSplineSet(time(1 : end - 1), ...
+ 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
+
diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingTrailingLimbAngleIntegrand.m b/src/TreatmentOptimization/subsetDataByCoordinates.m
similarity index 66%
rename from src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingTrailingLimbAngleIntegrand.m
rename to src/TreatmentOptimization/subsetDataByCoordinates.m
index ea2b804f8..29a9e9366 100644
--- a/src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingTrailingLimbAngleIntegrand.m
+++ b/src/TreatmentOptimization/subsetDataByCoordinates.m
@@ -1,9 +1,12 @@
% 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)
+% 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 %
@@ -13,7 +16,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. %
@@ -27,17 +30,15 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function cost = calcMinimizingTrailingLimbAngleIntegrand(values, ...
- modeledValues, params, costTerm)
-
-for i = 1:length(params.contactSurfaces)
- if params.contactSurfaces{i}.isLeftFoot == costTerm.is_left_foot
- normalForce = ...
- modeledValues.groundReactionsLab.forces{i}(:, 2) - 20;
- end
+function output = subsetDataByCoordinates(data, 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
-trailingLimbAngle = calcTrailingLimb(costTerm, values, ...
- normalForce, params);
-cost = calcMinimizingCostArrayTerm(trailingLimbAngle * ...
- ones(length(values.time), 1));
-end
\ No newline at end of file
+output = data(:, includedSubset);
+end
+
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/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/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/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/VerificationOptimization/parseVerificationOptimizationSettingsTree.m b/src/VerificationOptimization/parseVerificationOptimizationSettingsTree.m
deleted file mode 100644
index df0bbbd49..000000000
--- a/src/VerificationOptimization/parseVerificationOptimizationSettingsTree.m
+++ /dev/null
@@ -1,92 +0,0 @@
-% This function is part of the NMSM Pipeline, see file for full license.
-%
-% This function parses the settings tree resulting from xml2struct of the
-% Verification Optimizatoin settings XML file.
-%
-% (struct) -> (struct, struct)
-% returns the input values for 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 [inputs, params] = ...
- parseVerificationOptimizationSettingsTree(settingsTree)
-inputs = getInputs(settingsTree);
-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
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
diff --git a/src/TrackingOptimization/computeTrackingOptimizationMainFunction.m b/src/core/GCVSpline/evaluateGcvSplines.m
similarity index 52%
rename from src/TrackingOptimization/computeTrackingOptimizationMainFunction.m
rename to src/core/GCVSpline/evaluateGcvSplines.m
index ba06489af..9ae498e2e 100644
--- a/src/TrackingOptimization/computeTrackingOptimizationMainFunction.m
+++ b/src/core/GCVSpline/evaluateGcvSplines.m
@@ -1,9 +1,14 @@
% This function is part of the NMSM Pipeline, see file for full license.
%
-% This function sets up GPOPS-II to run Tracking Optimization.
+% 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.).
%
-% (struct) -> (struct, struct)
-% Assigns optimal control settings and runs Tracking Optimization
+% (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 %
@@ -13,7 +18,7 @@
% National Institutes of Health (R01 EB030520). %
% %
% Copyright (c) 2021 Rice University and the Authors %
-% Author(s): Marleny Vega %
+% 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. %
@@ -27,32 +32,34 @@
% 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;
+function values = evaluateGcvSplines(splineSet, columnLabels, time, ...
+ derivative)
+values = zeros(length(time), length(columnLabels));
+
+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
-output = computeTrackingOptimizationContinuousFunction(solution);
-output.solution = solution;
+
+if nargin < 4
+ derivative = 0;
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));
+% 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/VerificationOptimization/VerificationOptimization.m b/src/core/GCVSpline/makeGcvSplineSet.m
similarity index 57%
rename from src/VerificationOptimization/VerificationOptimization.m
rename to src/core/GCVSpline/makeGcvSplineSet.m
index cbfbd3989..1cc9643c3 100644
--- a/src/VerificationOptimization/VerificationOptimization.m
+++ b/src/core/GCVSpline/makeGcvSplineSet.m
@@ -1,10 +1,15 @@
% 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.
+% 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.
%
-% (struct, struct) -> (struct, struct)
-% Inputs for the main function are setup
+% (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 %
@@ -14,7 +19,7 @@
% National Institutes of Health (R01 EB030520). %
% %
% Copyright (c) 2021 Rice University and the Authors %
-% Author(s): Marleny Vega, Claire V. Hammond %
+% 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. %
@@ -28,17 +33,27 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function [output, inputs] = VerificationOptimization(inputs, params)
-inputs = makeTreatmentOptimizationInputs(inputs, params);
-initializeMexOrMatlabParallelFunctions(inputs.mexModel);
-if strcmp(inputs.controllerType, 'synergy_driven')
- inputs = setupMuscleSynergies(inputs);
+function splineSet = makeGcvSplineSet(time, data, columnLabels, degree)
+import org.opensim.modeling.*
+
+if length(time) ~= size(data, 1)
+ data = data';
end
-output = computeVerificationOptimizationMainFunction(inputs, params);
+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
-function inputs = setupMuscleSynergies(inputs)
-inputs.splineSynergyActivations = spaps(inputs.initialGuess.time, ...
- inputs.initialGuess.control(:, inputs.numCoordinates + 1:end)', 0.0000001);
-inputs.synergyLabels = inputs.initialGuess.controlLabels(:, inputs.numCoordinates + 1:end);
+splineSet = GCVSplineSet(table, labels, degree);
end
diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcGoalSingleSupportTimeIntegrand.m b/src/core/TreatmentOptimization/IntegrandTerms/calcGoalSingleSupportTimeIntegrand.m
deleted file mode 100644
index 6c7ed2674..000000000
--- a/src/core/TreatmentOptimization/IntegrandTerms/calcGoalSingleSupportTimeIntegrand.m
+++ /dev/null
@@ -1,52 +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(values, ...
- modeledValues, params, costTerm)
-
-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);
- else
- singleSupportTime = calcSingleSupportTime( ...
- modeledValues.groundReactionsLab.forces{i - 1}(:, 2), ...
- values.time);
- end
- end
-end
-percentSingleSupportTime = singleSupportTime / values.time(end);
-cost = calcTrackingCostArrayTerm(percentSingleSupportTime * ...
- ones(length(values.time), 1), errorCenter * ...
- ones(length(values.time), 1), 1);
-end
\ No newline at end of file
diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcGoalWalkingSpeedIntegrand.m b/src/core/TreatmentOptimization/IntegrandTerms/calcGoalWalkingSpeedIntegrand.m
deleted file mode 100644
index b46467729..000000000
--- a/src/core/TreatmentOptimization/IntegrandTerms/calcGoalWalkingSpeedIntegrand.m
+++ /dev/null
@@ -1,41 +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,...
- params, costTerm)
-
-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);
-end
\ No newline at end of file
diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcKinematicSymmetryIntegrand.m b/src/core/TreatmentOptimization/IntegrandTerms/calcKinematicSymmetryIntegrand.m
deleted file mode 100644
index 9da220a19..000000000
--- a/src/core/TreatmentOptimization/IntegrandTerms/calcKinematicSymmetryIntegrand.m
+++ /dev/null
@@ -1,49 +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, auxdata, ...
- costTerm)
-
-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);
-end
\ No newline at end of file
diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcMaximizingStepLengthIntegrand.m b/src/core/TreatmentOptimization/IntegrandTerms/calcMaximizingStepLengthIntegrand.m
deleted file mode 100644
index a3b5ef3d7..000000000
--- a/src/core/TreatmentOptimization/IntegrandTerms/calcMaximizingStepLengthIntegrand.m
+++ /dev/null
@@ -1,50 +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(values, modeledValues,...
- params, costTerm)
-
-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(values.time), 1));
-end
\ No newline at end of file
diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityYIntegrand.m b/src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityYIntegrand.m
deleted file mode 100644
index 3438a0a38..000000000
--- a/src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityYIntegrand.m
+++ /dev/null
@@ -1,35 +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(values, params)
-
-massCenterVelocity = calcMassCenterVelocity(values, params.model, ...
- params.coordinateNames);
-cost = calcMinimizingCostArrayTerm(massCenterVelocity(:, 2));
-end
\ No newline at end of file
diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityZIntegrand.m b/src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityZIntegrand.m
deleted file mode 100644
index 25afe9742..000000000
--- a/src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityZIntegrand.m
+++ /dev/null
@@ -1,35 +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(values, params)
-
-massCenterVelocity = calcMassCenterVelocity(values, params.model, ...
- params.coordinateNames);
-cost = calcMinimizingCostArrayTerm(massCenterVelocity(:, 3));
-end
\ No newline at end of file
diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcStepLengthAsymmetryIntegrand.m b/src/core/TreatmentOptimization/IntegrandTerms/calcStepLengthAsymmetryIntegrand.m
deleted file mode 100644
index 95eb8f2f5..000000000
--- a/src/core/TreatmentOptimization/IntegrandTerms/calcStepLengthAsymmetryIntegrand.m
+++ /dev/null
@@ -1,41 +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(values, 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);
-end
\ No newline at end of file
diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcStepTimeAsymmetryIntegrand.m b/src/core/TreatmentOptimization/IntegrandTerms/calcStepTimeAsymmetryIntegrand.m
deleted file mode 100644
index eac8181e9..000000000
--- a/src/core/TreatmentOptimization/IntegrandTerms/calcStepTimeAsymmetryIntegrand.m
+++ /dev/null
@@ -1,41 +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, ...
- modeledValues, params, costTerm)
-
-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);
-end
\ No newline at end of file
diff --git a/src/core/TreatmentOptimization/IntegrandTerms/getBreakingForce.m b/src/core/TreatmentOptimization/IntegrandTerms/getBreakingForce.m
deleted file mode 100644
index 9c90c97be..000000000
--- a/src/core/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/core/TreatmentOptimization/IntegrandTerms/getHeelStrikeEvent.m b/src/core/TreatmentOptimization/IntegrandTerms/getHeelStrikeEvent.m
deleted file mode 100644
index 315916c31..000000000
--- a/src/core/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/core/TreatmentOptimization/IntegrandTerms/getPropulsiveForce.m b/src/core/TreatmentOptimization/IntegrandTerms/getPropulsiveForce.m
deleted file mode 100644
index 2c6dae23f..000000000
--- a/src/core/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/core/TreatmentOptimization/IntegrandTerms/getToeOffEvent.m b/src/core/TreatmentOptimization/IntegrandTerms/getToeOffEvent.m
deleted file mode 100644
index 6d61940fb..000000000
--- a/src/core/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/core/TreatmentOptimization/OpenSimMex/inverseDynamicsMexWindows.mexw64 b/src/core/TreatmentOptimization/OpenSimMex/inverseDynamicsMexWindows.mexw64
deleted file mode 100644
index 61228f3ae..000000000
Binary files a/src/core/TreatmentOptimization/OpenSimMex/inverseDynamicsMexWindows.mexw64 and /dev/null differ
diff --git a/src/core/TreatmentOptimization/OpenSimMex/pointKinematicsMexWindows.mexw64 b/src/core/TreatmentOptimization/OpenSimMex/pointKinematicsMexWindows.mexw64
deleted file mode 100644
index b8ca6b73f..000000000
Binary files a/src/core/TreatmentOptimization/OpenSimMex/pointKinematicsMexWindows.mexw64 and /dev/null differ
diff --git a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/checkParameterGuess.m b/src/core/TreatmentOptimization/OptimalControlSetupFunctions/checkParameterGuess.m
deleted file mode 100644
index f7e38d727..000000000
--- a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/checkParameterGuess.m
+++ /dev/null
@@ -1,95 +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_driven')
- 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.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
-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
\ No newline at end of file
diff --git a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/getGpopsInitialGuess.m b/src/core/TreatmentOptimization/OptimalControlSetupFunctions/getGpopsInitialGuess.m
deleted file mode 100644
index fd804d2b3..000000000
--- a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/getGpopsInitialGuess.m
+++ /dev/null
@@ -1,53 +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 = getGpopsInitialGuess(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});
-end
-controlsFileName = getTextFromField(getFieldByNameOrAlternate(tree, ...
- 'initial_controls_file', ''));
-if ~isempty(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});
-end
-end
\ No newline at end of file
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/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlInitialGuess.m b/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlInitialGuess.m
deleted file mode 100644
index c3fd7153f..000000000
--- a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/setupCommonOptimalControlInitialGuess.m
+++ /dev/null
@@ -1,81 +0,0 @@
-% This function is part of the NMSM Pipeline, see file for full license.
-%
-% This function sets up the common initial guess for an optimal control
-% problem and is used by Tracking Optimization, Verification Optimization,
-% and Design Optimization
-%
-% (struct) -> (struct)
-% 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, 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 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, ...
- inputs.maxState, inputs.minState);
-else
- guess.phase.state = scaleToBounds([inputs.experimentalJointAngles ...
- inputs.experimentalJointVelocities ...
- inputs.experimentalJointAccelerations], inputs.maxState, ...
- inputs.minState);
- guess.phase.time = scaleToBounds(inputs.experimentalTime, inputs.maxTime, ...
- inputs.minTime);
-end
-if strcmp(inputs.controllerType, 'synergy_driven')
- 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 isfield(inputs, "optimizeSynergyVectors") && ...
- inputs.optimizeSynergyVectors
- guess.parameter = scaleToBounds(inputs.synergyWeightsGuess, ...
- inputs.maxParameter, inputs.minParameter);
- end
-elseif strcmp(inputs.controllerType, 'torque_driven')
- if isfield(inputs.initialGuess, 'control')
- guess.phase.control = scaleToBounds(inputs.initialGuess.control, ...
- inputs.maxControl, inputs.minControl);
- 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);
- end
-end
-guess.phase.integral = scaleToBounds(1e1, inputs.maxIntegral, ...
- inputs.minIntegral);
-end
\ No newline at end of file
diff --git a/src/core/TreatmentOptimization/SetupBounds/getDesignVariableInputBounds.m b/src/core/TreatmentOptimization/SetupBounds/getDesignVariableInputBounds.m
deleted file mode 100644
index 8f4f2c143..000000000
--- a/src/core/TreatmentOptimization/SetupBounds/getDesignVariableInputBounds.m
+++ /dev/null
@@ -1,112 +0,0 @@
-% This function is part of the NMSM Pipeline, see file for full license.
-%
-% This functions computes the maximum and minimum values for all design
-% variables. The maximum and minimum values for most design variables are
-% 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.
-%
-% (struct) -> (struct)
-% Computes max and min design variable bounds
-
-% ----------------------------------------------------------------------- %
-% 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 = getDesignVariableInputBounds(inputs)
-if isfield(inputs, "finalTimeRange")
- inputs.maxTime = max(inputs.experimentalTime) + inputs.finalTimeRange;
-else
- inputs.maxTime = max(inputs.experimentalTime);
-end
-inputs.minTime = min(inputs.experimentalTime);
-
-maxStatePositions = max(inputs.experimentalJointAngles) + ...
- inputs.statePositionsMultiple * range(inputs.experimentalJointAngles);
-minStatePositions = min(inputs.experimentalJointAngles) - ...
- inputs.statePositionsMultiple * range(inputs.experimentalJointAngles);
-maxStateVelocities = max(inputs.experimentalJointVelocities) + ...
- inputs.stateVelocitiesMultiple * range(inputs.experimentalJointVelocities);
-minStateVelocities = min(inputs.experimentalJointVelocities) - ...
- inputs.stateVelocitiesMultiple * range(inputs.experimentalJointVelocities);
-maxStateAccelerations = max(inputs.experimentalJointAccelerations) + ...
- inputs.stateAccelerationsMultiple * range(inputs.experimentalJointAccelerations);
-minStateAccelerations = min(inputs.experimentalJointAccelerations) - ...
- inputs.stateAccelerationsMultiple * range(inputs.experimentalJointAccelerations);
-
-inputs.maxState = [maxStatePositions maxStateVelocities maxStateAccelerations];
-inputs.minState = [minStatePositions minStateVelocities minStateAccelerations];
-
-maxControlJerks = max(inputs.experimentalJointJerks) + ...
- inputs.controlJerksMultiple * range(inputs.experimentalJointJerks);
-minControlJerks = min(inputs.experimentalJointJerks) - ...
- inputs.controlJerksMultiple * range(inputs.experimentalJointJerks);
-
-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')
- 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
- maxControlTorques(i) = max(inputs.experimentalJointMoments(:, ...
- indx)) + inputs.maxControlTorquesMultiple * ...
- range(inputs.experimentalJointMoments(:, indx));
- minControlTorques(i) = min(inputs.experimentalJointMoments(:, ...
- indx)) - inputs.maxControlTorquesMultiple * ...
- range(inputs.experimentalJointMoments(:, indx));
- end
- 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
\ No newline at end of file
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
diff --git a/src/core/TreatmentOptimization/getTreatmentOptimizationInputs.m b/src/core/TreatmentOptimization/getTreatmentOptimizationInputs.m
deleted file mode 100644
index 6adee21f7..000000000
--- a/src/core/TreatmentOptimization/getTreatmentOptimizationInputs.m
+++ /dev/null
@@ -1,81 +0,0 @@
-% 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): 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 = getTreatmentOptimizationInputs(tree)
-inputs.resultsDirectory = getTextFromField(getFieldByName(tree, ...
- 'results_directory'));
-if(isempty(inputs.resultsDirectory)); inputs.resultsDirectory = pwd; end
-inputs.controllerType = getTextFromField(getFieldByNameOrError(tree, ...
- 'type_of_controller'));
-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);
-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);
-inputs.terminal = getTerminalConstraintTerms(tree);
-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
-end
diff --git a/src/core/TreatmentOptimization/getTreatmentOptimizationValueStruct.m b/src/core/TreatmentOptimization/getTreatmentOptimizationValueStruct.m
deleted file mode 100644
index 5d526ba2d..000000000
--- a/src/core/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(inputs, params)
-
-values.time = scaleToOriginal(inputs.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);
-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);
-
-if ~strcmp(params.controllerType, 'synergy_driven')
- values.controlTorques = control(:, params.numCoordinates + 1 : ...
- params.numCoordinates + params.numTorqueControls);
-else
- values.controlSynergyActivations = control(:, ...
- params.numCoordinates + 1 : params.numCoordinates + params.numSynergies);
-end
-end
\ No newline at end of file
diff --git a/src/core/TreatmentOptimization/saveCommonOptimalControlResults.m b/src/core/TreatmentOptimization/saveCommonOptimalControlResults.m
deleted file mode 100644
index 6ca194c34..000000000
--- a/src/core/TreatmentOptimization/saveCommonOptimalControlResults.m
+++ /dev/null
@@ -1,97 +0,0 @@
-% 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.
-%
-% (struct, struct) -> (None)
-% Prints treatment optimization 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, 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 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"));
-end
-stateLabels = inputs.coordinateNames;
-for i = 1 : length(inputs.coordinateNames)
- stateLabels{end + 1} = strcat(inputs.coordinateNames{i}, '_u');
-end
-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], ...
- fullfile(inputs.resultsDirectory, "statesSolution.sto"));
-if strcmp(inputs.controllerType, 'synergy_driven')
- 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
- writeToSto(controlLabels, values.time, [values.controlJerks ...
- commands], fullfile(inputs.resultsDirectory, ...
- "controlSolution.sto"));
-elseif strcmp(inputs.controllerType, 'torque_driven')
- controlLabels = inputs.coordinateNames;
- for i = 1 : inputs.numTorqueControls
- controlLabels{end + 1} = strcat('torqueControl', num2str(i));
- end
- writeToSto(controlLabels, values.time, [values.controlJerks ...
- values.controlTorques], fullfile(inputs.resultsDirectory, ...
- "controlSolution.sto"));
-end
-delete(inputs.mexModel);
-end
\ No newline at end of file
diff --git a/src/core/TreatmentOptimization/updateMuscleModelProperties.m b/src/core/TreatmentOptimization/updateMuscleModelProperties.m
deleted file mode 100644
index 42d9fa3ca..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_driven')
-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/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
diff --git a/src/core/mex/PointKinematicsMatlab.m b/src/core/mex/PointKinematicsMatlab.m
new file mode 100644
index 000000000..1b977216f
--- /dev/null
+++ b/src/core/mex/PointKinematicsMatlab.m
@@ -0,0 +1,47 @@
+function [SpringPos, SpringVel] = pointKinematicsMatlab(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
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/mex/compileMex.m b/src/core/mex/compileMex.m
new file mode 100644
index 000000000..ffef55e49
--- /dev/null
+++ b/src/core/mex/compileMex.m
@@ -0,0 +1,24 @@
+mex CXXFLAGS="/$CXXFLAGS -fopenmp" LDFLAGS="/$LDFLAGS -fopenmp"...
+ COMPFLAGS="/openmp $COMPFLAGS"...
+ 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'...
+ -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'...
+ -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
new file mode 100644
index 000000000..23e0a5f9f
--- /dev/null
+++ b/src/core/mex/evaluateGcvSplinesMexWindows.cpp
@@ -0,0 +1,112 @@
+// 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);
+
+ mexPrintf(typeid(dynamic_cast(mxGetPr(prhs[0]))).name());
+
+ GCVSplineSet *splineSet = dynamic_cast(mxGetPr(prhs[0]));
+
+ // 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();
+ }
+}
\ No newline at end of file
diff --git a/src/core/mex/evaluateGcvSplinesMexWindows.mexw64 b/src/core/mex/evaluateGcvSplinesMexWindows.mexw64
new file mode 100644
index 000000000..d23d58b32
Binary files /dev/null and b/src/core/mex/evaluateGcvSplinesMexWindows.mexw64 differ
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/TreatmentOptimization/OpenSimMex/initializeMexOrMatlabParallelFunctions.m b/src/core/mex/initializeMexOrMatlabParallelFunctions.m
similarity index 97%
rename from src/core/TreatmentOptimization/OpenSimMex/initializeMexOrMatlabParallelFunctions.m
rename to src/core/mex/initializeMexOrMatlabParallelFunctions.m
index 9be9ddd71..b088f68c3 100644
--- a/src/core/TreatmentOptimization/OpenSimMex/initializeMexOrMatlabParallelFunctions.m
+++ b/src/core/mex/initializeMexOrMatlabParallelFunctions.m
@@ -32,7 +32,7 @@
function initializeMexOrMatlabParallelFunctions(modelFile)
if isequal(mexext, 'mexw64')
pointKinematicsMexWindows(modelFile);
- inverseDynamicsMexWindows(modelFile);
+ inverseDynamicsWithExtraCalcsMexWindows(modelFile);
end
clear inverseDynamicsMatlabParallel
clear pointKinematicsMatlabParallel
diff --git a/src/core/TreatmentOptimization/OpenSimMex/inverseDynamics.m b/src/core/mex/inverseDynamics.m
similarity index 61%
rename from src/core/TreatmentOptimization/OpenSimMex/inverseDynamics.m
rename to src/core/mex/inverseDynamics.m
index f42c0a267..b4f5eeaa8 100644
--- a/src/core/TreatmentOptimization/OpenSimMex/inverseDynamics.m
+++ b/src/core/mex/inverseDynamics.m
@@ -2,8 +2,10 @@
%
% 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,
+%
+% 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,16 +31,28 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function inverseDynamicMoments = inverseDynamics(time, jointAngles, ...
- jointVelocities, jointAccelerations, coordinateLabels, appliedLoads, ...
- modelName)
-% if isequal(mexext, 'mexw64')
-% inverseDynamicMoments = inverseDynamicsMexWindows(time, jointAngles, ...
-% jointVelocities, jointAccelerations, coordinateLabels, ...
-% appliedLoads);
-% else
- inverseDynamicMoments = inverseDynamicsMatlabParallel(time, ...
- jointAngles, jointVelocities, jointAccelerations, coordinateLabels, ...
- appliedLoads, modelName);
-% end
-end
\ No newline at end of file
+function [inverseDynamicsMoments, angularMomentum, metabolicCost, ...
+ massCenterVelocity] = ...
+ inverseDynamics( time, jointAngles, jointVelocities, ...
+ jointAccelerations, coordinateLabels, appliedLoads, modelName, ...
+ muscleActivations, computeAngularMomentum, computeMetabolicCost)
+if isequal(mexext, 'mexw64')
+ [inverseDynamicsMoments, angularMomentum, metabolicCost, ...
+ massCenterVelocity] = ...
+ inverseDynamicsWithExtraCalcsMexWindows(time, jointAngles, ...
+ jointVelocities, jointAccelerations, coordinateLabels, ...
+ appliedLoads, muscleActivations, computeAngularMomentum, ...
+ computeMetabolicCost);
+else
+ 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/inverseDynamicsAngularMomentumMexWindows.cpp b/src/core/mex/inverseDynamicsAngularMomentumMexWindows.cpp
new file mode 100644
index 000000000..5a9cb0b4c
--- /dev/null
+++ b/src/core/mex/inverseDynamicsAngularMomentumMexWindows.cpp
@@ -0,0 +1,172 @@
+// 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]);
+
+ 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]);
+
+ #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 (nlhs > 1) {
+ 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);
+ }
+ }
+
+ 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];
+ }
+ }
+ //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 000000000..3d48b22a6
Binary files /dev/null and b/src/core/mex/inverseDynamicsAngularMomentumMexWindows.mexw64 differ
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 70%
rename from src/core/TreatmentOptimization/OpenSimMex/inverseDynamicsMatlabParallel.m
rename to src/core/mex/inverseDynamicsMatlabParallel.m
index 5a090dfef..e72a7b2b9 100644
--- a/src/core/TreatmentOptimization/OpenSimMex/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,9 +30,9 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function inverseDynamicMoments = 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);
@@ -40,24 +40,45 @@
numCoords = length(coordinateLabels);
% Split time points into parallel problems
numWorkers = gcp().NumWorkers;
-inverseDynamicJobs = cell(1, numWorkers);
-parfor worker = 1:numWorkers
- inverseDynamicJobs{worker} = idWorkerHelper(modelName, numPts, ...
- numControls, numCoords, numWorkers, ...
- time, jointAngles, jointVelocities, jointAccelerations, ...
- coordinateLabels,appliedLoads,worker);
+inverseDynamicsJobs = cell(1, numWorkers);
+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
-inverseDynamicMoments = inverseDynamicJobs{1};
+
+inverseDynamicsMoments = inverseDynamicsJobs{1};
for job = 2 : numWorkers
- inverseDynamicMoments = cat(1, inverseDynamicMoments, ...
- inverseDynamicJobs{job});
+ 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
-function inverseDynamicJobs = idWorkerHelper(modelName, numPts, ...
+end
+
+function [inverseDynamicsJobs, angularMomentumJobs] = ...
+ idWorkerHelper(modelName, numPts, ...
numControls, numCoords, numWorkers, time, jointAngles, ...
jointVelocities, jointAccelerations, coordinateLabels, appliedLoads, ...
worker)
-
+
import org.opensim.modeling.*;
persistent osimModel;
persistent osimState;
@@ -67,7 +88,8 @@
osimState = osimModel.initSystem();
inverseDynamicsSolver = InverseDynamicsSolver(osimModel);
end
-inverseDynamicJobs = [];
+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);
@@ -98,7 +131,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 +140,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/mex/inverseDynamicsMexWindows.cpp b/src/core/mex/inverseDynamicsMexWindows.cpp
new file mode 100644
index 000000000..3f81ef92b
--- /dev/null
+++ b/src/core/mex/inverseDynamicsMexWindows.cpp
@@ -0,0 +1,158 @@
+// 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]);
+
+ 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);
+
+ 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/inverseDynamicsMexWindows.mexw64 b/src/core/mex/inverseDynamicsMexWindows.mexw64
new file mode 100644
index 000000000..9937d9413
Binary files /dev/null and b/src/core/mex/inverseDynamicsMexWindows.mexw64 differ
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 000000000..68bca8b8f
Binary files /dev/null and b/src/core/mex/inverseDynamicsWithExtraCalcsMexWindows.mexw64 differ
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/mex/pointKinematicsMexWindows.mexw64 b/src/core/mex/pointKinematicsMexWindows.mexw64
new file mode 100644
index 000000000..11c9a1511
Binary files /dev/null and b/src/core/mex/pointKinematicsMexWindows.mexw64 differ
diff --git a/src/core/TreatmentOptimization/OpenSimMex/setOsimStateBySingleFrame.m b/src/core/mex/setOsimStateBySingleFrame.m
similarity index 94%
rename from src/core/TreatmentOptimization/OpenSimMex/setOsimStateBySingleFrame.m
rename to src/core/mex/setOsimStateBySingleFrame.m
index 8b542af5e..17a7dc9b2 100644
--- a/src/core/TreatmentOptimization/OpenSimMex/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);
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()');
diff --git a/src/core/motToTrc/TrcFromMot.m b/src/core/motToTrc/TrcFromMot.m
new file mode 100644
index 000000000..47e673c52
--- /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..163e26476
--- /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..c5a844a6d
--- /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..1471b81fd
--- /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
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 99%
rename from src/core/MuscleCalculations/calcPassiveMuscleMoments.m
rename to src/core/muscle/calcPassiveMuscleMoments.m
index 137d83d57..0739ae33d 100644
--- a/src/core/MuscleCalculations/calcPassiveMuscleMoments.m
+++ b/src/core/muscle/calcPassiveMuscleMoments.m
@@ -54,10 +54,8 @@
parallelComponentOfPennationAngle), 1);
expandedParallelComponentOfPennationAngle(1, 1, :, 1) = ...
parallelComponentOfPennationAngle;
-
passiveModelMoments = experimentalData.momentArms .* ...
expandedMaxIsometricForce .* expandedPassiveForce .* ...
expandedParallelComponentOfPennationAngle;
-
passiveModelMoments = permute(sum(passiveModelMoments, 3), [1 2 4 3]);
end
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
diff --git a/src/core/optimal_control/parseGpopsSolverSettings.m b/src/core/optimal_control/parseGpopsSolverSettings.m
new file mode 100644
index 000000000..5777a4af5
--- /dev/null
+++ b/src/core/optimal_control/parseGpopsSolverSettings.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 = parseGpopsSolverSettings(settingsTree)
+solverSettings.optimizationFileName = 'TreatmentOptimization';
+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/TreatmentOptimization/IntegrandTerms/calcStepLength.m b/src/core/optimal_control/parseOptimalControlSolverSettings.m
similarity index 67%
rename from src/core/TreatmentOptimization/IntegrandTerms/calcStepLength.m
rename to src/core/optimal_control/parseOptimalControlSolverSettings.m
index b70e0fe0b..fea19b709 100644
--- a/src/core/TreatmentOptimization/IntegrandTerms/calcStepLength.m
+++ b/src/core/optimal_control/parseOptimalControlSolverSettings.m
@@ -1,9 +1,5 @@
% 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)
%
% ----------------------------------------------------------------------- %
@@ -14,7 +10,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. %
@@ -28,22 +24,20 @@
% 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
+function inputs = parseOptimalControlSolverSettings( ...
+ tree, inputs)
+settingsFileName = getTextFromField(getFieldByNameOrError(tree, ...
+ 'optimal_control_solver_settings_file'));
+solverSettingsTree = xml2struct(settingsFileName);
+verifyVersion(solverSettingsTree, "OptimalControlSolverSettings");
+tree = ...
+ solverSettingsTree.NMSMPipelineDocument.OptimalControlSolverSettings;
+if isfield(tree, 'GpopsSettings')
+ inputs.gpops = parseGpopsSolverSettings(tree);
+elseif isfield(tree, 'MocoSettings')
+ inputs.moco = parseMocoSolverSettings(tree);
else
- stepLength = heelPosition(heelStrikeEvent, 1) - ...
- heelPosition(heelStrikeEvent, 2);
+ throw(MException("OptimalControlSolverSettings:UnsupportedSolver", ...
+ "Only and are supported"))
+end
end
-end
\ No newline at end of file
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/buildOsimxFromOsimxStruct.m b/src/core/osimx/buildOsimxFromOsimxStruct.m
index ff1f838c0..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.
@@ -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/TrackingOptimization/getTrackingOptimizationValueStruct.m b/src/core/osimx/buildSynergyGroupOsimx.m
similarity index 59%
rename from src/TrackingOptimization/getTrackingOptimizationValueStruct.m
rename to src/core/osimx/buildSynergyGroupOsimx.m
index 8b6a857c4..8d5eb8ba9 100644
--- a/src/TrackingOptimization/getTrackingOptimizationValueStruct.m
+++ b/src/core/osimx/buildSynergyGroupOsimx.m
@@ -1,11 +1,14 @@
% 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.
+% 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 parseSynergyGroups() as
+% used in parseNeuralControlPersonalization()
%
% (struct, struct) -> (struct)
-% Design variables specific to Tracking Optimization are parsed and scaled
+% Adds synergyGroups to .osimxStruct
% ----------------------------------------------------------------------- %
% The NMSM Pipeline is a toolkit for model personalization and treatment %
@@ -15,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): 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,17 +32,17 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function values = getTrackingOptimizationValueStruct(inputs, params)
-values = getTreatmentOptimizationValueStruct(inputs, params);
-if strcmp(params.controllerType, 'synergy_driven')
- if params.optimizeSynergyVectors
- synergyWeights = scaleToOriginal(inputs.parameter(1,:), ...
- params.maxParameter, params.minParameter);
- values.synergyWeights = getSynergyWeightsFromGroups(...
- synergyWeights, params);
- else
- values.synergyWeights = getSynergyWeightsFromGroups(...
- params.synergyWeightsGuess, params);
- end
+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/parseOsimxFile.m b/src/core/osimx/parseOsimxFile.m
index 442c8fd4c..c12ac255f 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,11 @@
end
end
end
+
+rcnlSynergySetTree = getFieldByName(tree, "RCNLSynergySet");
+if isstruct(rcnlSynergySetTree)
+ osimx.synergyGroups = parseSynergyGroups(tree, model);
+end
end
function contactSurface = parseContactSurface(tree)
@@ -98,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");
+if ~isstruct(hindfootBodyName)
+ throw(MException('', " is replaced by in the osimx file. Please update your osimx file."))
+else
+contactSurface.hindfootBodyName = hindfootBodyName.Text;
+end
contactSurface.toeMarker = getFieldByNameOrError(tree, "toe_marker").Text;
contactSurface.medialMarker = getFieldByNameOrError(tree, "medial_marker").Text;
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 80fb34f7f..f0ae596f2 100644
--- a/src/core/osimx/writeNeuralControlPersonalizationOsimxFile.m
+++ b/src/core/osimx/writeNeuralControlPersonalizationOsimxFile.m
@@ -30,13 +30,12 @@
function writeNeuralControlPersonalizationOsimxFile(inputs, ...
resultsDirectory, precalInputs)
-modelFileName = inputs.model;
-model = Model(modelFileName);
+model = Model(inputs.modelFileName);
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;
@@ -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
@@ -68,12 +67,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
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"));
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 (struct)
-% Parses settings from a RCNLPathConstraintTerms.
+% parses shared controller settings from XML tree
% ----------------------------------------------------------------------- %
% The NMSM Pipeline is a toolkit for model personalization and treatment %
@@ -14,7 +15,7 @@
% National Institutes of Health (R01 EB030520). %
% %
% Copyright (c) 2021 Rice University and the Authors %
-% Author(s): Marleny Vega, Claire V. Hammond %
+% 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. %
@@ -28,18 +29,18 @@
% 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 = {};
+function inputs = parseController(tree, inputs)
+inputs = parseTreatmentOptimizationDesignVariableBounds(tree, ...
+ inputs);
+inputs.statesCoordinateNames = 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(tree, inputs);
+end
end
-end
\ No newline at end of file
diff --git a/src/core/TreatmentOptimization/makeTreatmentOptimizationInputs.m b/src/core/parse/parseControllerType.m
similarity index 66%
rename from src/core/TreatmentOptimization/makeTreatmentOptimizationInputs.m
rename to src/core/parse/parseControllerType.m
index d646dadbd..9299847ec 100644
--- a/src/core/TreatmentOptimization/makeTreatmentOptimizationInputs.m
+++ b/src/core/parse/parseControllerType.m
@@ -1,9 +1,12 @@
% 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.
+% 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, struct) -> (struct)
+% (struct) -> (string)
+% returns "synergy" or "torque" depending on the settings file
% ----------------------------------------------------------------------- %
% The NMSM Pipeline is a toolkit for model personalization and treatment %
@@ -13,7 +16,7 @@
% National Institutes of Health (R01 EB030520). %
% %
% Copyright (c) 2021 Rice University and the Authors %
-% Author(s): Marleny Vega, Claire V. Hammond %
+% 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. %
@@ -27,20 +30,17 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function inputs = makeTreatmentOptimizationInputs(inputs, params)
-if isequal(mexext, 'mexw64')
- pointKinematicsMexWindows(inputs.mexModel);
- inverseDynamicsMexWindows(inputs.mexModel);
+function controllerType = parseControllerType(tree)
+synergy = getFieldByName(tree, "RCNLSynergyController");
+if isstruct(synergy)
+ controllerType = "synergy";
+ return
end
-inputs = getStateDerivatives(inputs);
-inputs = setupGroundContact(inputs);
-inputs = getSplines(inputs);
-inputs = checkStateGuess(inputs);
-inputs = checkControlGuess(inputs);
-inputs = checkParameterGuess(inputs);
-inputs = getIntegralBounds(inputs);
-inputs = getPathConstraintBounds(inputs);
-inputs = getTerminalConstraintBounds(inputs);
-inputs = getDesignVariableInputBounds(inputs);
+torque = getFieldByName(tree, "RCNLTorqueController");
+if isstruct(torque)
+ controllerType = "torque";
+ return
end
-
+throw(MException("ParseTreatmentOptimization:NoController", ...
+ "Could not find or "))
+end
\ No newline at end of file
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/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
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/parseRcnlConstraintTermSet.m b/src/core/parse/parseRcnlConstraintTermSet.m
index 208a78f72..cd606fbd1 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, toolName, ...
+ 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, toolName, controllerType);
+ if ~isValid
+ throw(MException("ConstraintTermSet:InvalidType", ...
+ strcat(tempTerm.type, " is not a valid constraint", ...
+ " term for tool ", toolName)));
+ 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,28 @@
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, 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
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/parseSpaceSeparatedList.m b/src/core/parse/parseSpaceSeparatedList.m
index 3178a147e..2317a931d 100644
--- a/src/core/parse/parseSpaceSeparatedList.m
+++ b/src/core/parse/parseSpaceSeparatedList.m
@@ -32,9 +32,10 @@
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)
+prefixField.Text = strip(prefixField.Text);
+if ~isempty(convertStringsToChars(prefixField.Text))
if(strcmp(prefixField.Text(1), ' '))
prefixField.Text = prefixField.Text(2:end);
end
diff --git a/src/core/parse/parseSynergyController.m b/src/core/parse/parseSynergyController.m
new file mode 100644
index 000000000..463064c45
--- /dev/null
+++ b/src/core/parse/parseSynergyController.m
@@ -0,0 +1,73 @@
+% 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 = inputs.osimx.synergyGroups;
+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);
+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"));
+inputs.optimizeSynergyVectors = getBooleanLogic(...
+ parseElementTextByNameOrAlternate(tree, "optimize_synergy_vectors", 0));
+inputs.maxControlSynergyActivations = parseDoubleOrAlternate(tree, ...
+ 'maximum_allowable_synergy_activation', 10);
+if ~isfield(inputs, "torqueControllerCoordinateNames")
+ 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
+
diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingMetabolicCost.m b/src/core/parse/parseTextOrAlternate.m
similarity index 79%
rename from src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingMetabolicCost.m
rename to src/core/parse/parseTextOrAlternate.m
index 0e9c64d9b..4d76cdeac 100644
--- a/src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingMetabolicCost.m
+++ b/src/core/parse/parseTextOrAlternate.m
@@ -1,9 +1,7 @@
% 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 %
@@ -12,7 +10,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. %
@@ -26,10 +24,7 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function cost = calcMinimizingMetabolicCost(values, modeledValues, ...
- params)
-
-metabolicCost = calcMetabolicCost(values.time, ...
- values.statePositions, modeledValues.muscleActivations, params);
-cost = calcMinimizingCostArrayTerm(metabolicCost);
-end
\ No newline at end of file
+function text = parseTextOrAlternate(tree, field, alternate)
+text = getTextFromField( ...
+ getFieldByNameOrAlternate(tree, field, alternate));
+end
diff --git a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/checkControlGuess.m b/src/core/parse/parseTorqueController.m
similarity index 72%
rename from src/core/TreatmentOptimization/OptimalControlSetupFunctions/checkControlGuess.m
rename to src/core/parse/parseTorqueController.m
index d91d6e5b2..cc41fe910 100644
--- a/src/core/TreatmentOptimization/OptimalControlSetupFunctions/checkControlGuess.m
+++ b/src/core/parse/parseTorqueController.m
@@ -1,10 +1,11 @@
% 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
+% 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 %
@@ -14,7 +15,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. %
@@ -28,16 +29,11 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function inputs = checkControlGuess(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;
- end
- end
- end
- inputs.initialGuess.control(:, 1:inputs.numCoordinates) = ...
- inputs.initialGuess.control(:, controlIndex);
+function inputs = parseTorqueController(tree, inputs)
+inputs.torqueControllerCoordinateNames = parseSpaceSeparatedList(tree, ...
+ "coordinate_list");
+inputs.maxTorqueControlsMultiple = parseDoubleOrAlternate(tree, ...
+ 'torque_controls_range_scale_factor', 1);
+inputs.numSynergies = 0;
+inputs.surrogateModelCoordinateNames = [];
end
-end
\ No newline at end of file
diff --git a/src/core/parse/parseTreatmentOptimizationDataDirectory.m b/src/core/parse/parseTreatmentOptimizationDataDirectory.m
index 1862754b4..29ed26762 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. %
@@ -31,74 +31,142 @@
% ----------------------------------------------------------------------- %
function inputs = parseTreatmentOptimizationDataDirectory(tree, inputs)
+[dataDirectory, inputs.previousResultsDirectory] = findDataDirectory(tree, inputs);
+inputs.trialName = parseTrialName(tree);
+inputs = parseExperimentalData(tree, inputs, dataDirectory);
+inputs = parseSynergyExperimentalData(tree, inputs, dataDirectory);
+inputs = parseInitialValues(tree, inputs);
+inputs.numCoordinates = size(inputs.experimentalJointAngles, 2);
+end
+
+function [dataDirectory, previousResultsDirectory] = ...
+ findDataDirectory(tree, inputs)
dataDirectory = parseDataDirectory(tree);
-previousResultsDirectoryElement = getFieldByName(tree, 'previous_results_directory');
-if isstruct(previousResultsDirectoryElement)
- previousResultsDirectory = previousResultsDirectoryElement.Text;
-else
- previousResultsDirectory = [];
+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
-if strcmp(previousResultsDirectory, "")
- previousResultsDirectory = [];
end
-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_driven')
- [inputs.experimentalMuscleActivations, inputs.muscleLabels] = ...
- parseTreatmentOptimizationData(directory, 'muscleActivations', model);
- end
+function inputs = parseExperimentalData(tree, inputs, dataDirectory)
+[inputs.experimentalJointMoments, ...
+ inputs.inverseDynamicsMomentLabels] = ...
+ parseTrialDataTryDirectories( ...
+ fullfile(inputs.previousResultsDirectory, "IDData"), ...
+ fullfile(dataDirectory, "IDData"), inputs.trialName, inputs.model);
+[inputs.experimentalJointAngles, inputs.coordinateNames, ...
+ experimentalTime] = parseTrialDataTryDirectories( ...
+ fullfile(inputs.previousResultsDirectory, "IKData"), ...
+ 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;
+ for surfaceIndex = 1:length(inputs.contactSurfaces)
+ [inputs.contactSurfaces{surfaceIndex} ...
+ .experimentalGroundReactionForces, ...
+ inputs.contactSurfaces{surfaceIndex} ...
+ .experimentalGroundReactionMoments, ...
+ inputs.contactSurfaces{surfaceIndex} ...
+ .electricalCenter] = parseGroundReactionDataWithoutTime( ...
+ inputs, dataDirectory, surfaceIndex);
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_driven')
- directory = findFirstLevelSubDirectoriesFromPrefixes(dataDirectory, "ActData");
- [inputs.experimentalMuscleActivations, inputs.muscleLabels] = ...
- parseTreatmentOptimizationData(directory, prefix, model);
- end
+inputs.contactSurfaces = {};
+end
end
-if strcmp(inputs.controllerType, 'synergy_driven')
- directories = findFirstLevelSubDirectoriesFromPrefixes(fullfile( ...
- dataDirectory, "MAData"), prefix);
- inputs.momentArms = parseSelectMomentArms(directories, ...
+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, inputs.synergyWeightsLabels] = ...
+ parseTrialData(inputs.previousResultsDirectory, ...
+ "synergyWeights", inputs.model);
+ 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));
inputs = getMuscleSpecificSurrogateModelData(inputs);
end
-inputs.numCoordinates = size(inputs.experimentalJointAngles, 2);
-end
\ No newline at end of file
+end
+
+function inputs = parseInitialValues(tree, inputs)
+initialGuess = [];
+try
+ [inputs.initialStates, inputs.initialStatesLabels, inputs.initialTime] = ...
+ parseTrialData(inputs.previousResultsDirectory, ...
+ strcat(inputs.trialName, "_states"), inputs.model);
+catch; end
+try
+ [inputs.initialAccelerations, inputs.initialAccelerationsLabels] = ...
+ parseTrialData(inputs.previousResultsDirectory, ...
+ strcat(inputs.trialName, "_accelerations"), inputs.model);
+catch; end
+try
+ [inputs.initialTorqueControls, inputs.initialTorqueControlsLabels] = ...
+ parseTrialData(inputs.previousResultsDirectory, ...
+ strcat(inputs.trialName, "_torqueControls"), inputs.model);
+catch;end
+if strcmp(inputs.controllerType, "synergy")
+ [inputs.initialSynergyControls, inputs.initialSynergyControlsLabels] = ...
+ parseTrialData(inputs.previousResultsDirectory, ...
+ strcat(inputs.trialName, "_synergyCommands"), inputs.model);
+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
+
+function [forces, moments, ec] = parseGroundReactionDataWithoutTime( ...
+ inputs, dataDirectory, surfaceIndex)
+import org.opensim.modeling.Storage
+[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
+ 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/TreatmentOptimization/IntegrandTerms/calcMaximizingMuscleActivationIntegrand.m b/src/core/parse/parseTreatmentOptimizationDesignVariableBounds.m
similarity index 75%
rename from src/core/TreatmentOptimization/IntegrandTerms/calcMaximizingMuscleActivationIntegrand.m
rename to src/core/parse/parseTreatmentOptimizationDesignVariableBounds.m
index 8ec37297d..3ba4b29e8 100644
--- a/src/core/TreatmentOptimization/IntegrandTerms/calcMaximizingMuscleActivationIntegrand.m
+++ b/src/core/parse/parseTreatmentOptimizationDesignVariableBounds.m
@@ -1,9 +1,6 @@
% 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 %
@@ -13,7 +10,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. %
@@ -27,10 +24,12 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function cost = calcMaximizingMuscleActivationIntegrand(...
- muscleActivations, params, muscleName)
-
-indx = find(strcmp(convertCharsToStrings(params.muscleNames), ...
- muscleName));
-cost = calcMaximizingCostArrayTerm(muscleActivations(:, indx));
-end
\ No newline at end of file
+function inputs = parseTreatmentOptimizationDesignVariableBounds( ...
+ tree, inputs)
+inputs.jointPositionsMultiple = parseDoubleOrAlternate(tree, ...
+ 'joint_position_range_scale_factor', 2);
+inputs.jointVelocitiesMultiple = parseDoubleOrAlternate(tree, ...
+ 'joint_velocity_range_scale_factor', 1.5);
+inputs.jointAccelerationsMultiple = parseDoubleOrAlternate(tree, ...
+ 'joint_acceleration_range_scale_factor', 1);
+end
diff --git a/src/core/parse/parseTreatmentOptimizationInputs.m b/src/core/parse/parseTreatmentOptimizationInputs.m
new file mode 100644
index 000000000..a92a1c0b2
--- /dev/null
+++ b/src/core/parse/parseTreatmentOptimizationInputs.m
@@ -0,0 +1,109 @@
+% 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): 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 = parseTreatmentOptimizationInputs(tree)
+inputs = parseBasicInputs(tree);
+inputs.osimx = parseOsimxFileWithCondition(tree, inputs);
+inputs = parseController(tree, inputs);
+inputs = parseTreatmentOptimizationDataDirectory(tree, inputs);
+inputs = parseOptimalControlSolverSettings(tree, inputs);
+inputs.costTerms = parseRcnlCostTermSetHelper( ...
+ getFieldByNameOrError(tree, 'RCNLCostTermSet'));
+if isequal(mexext, 'mexw64')
+ inputs.calculateAngularMomentum = any(all([ ...
+ strcmp(cellfun(@(term) term.type, inputs.costTerms, ...
+ 'UniformOutput', false), {'angular_momentum_minimization'}) ...
+ ; ...
+ cell2mat(cellfun(@(term) term.isEnabled, inputs.costTerms, ...
+ 'UniformOutput', false)) ...
+ ], 1));
+ inputs.calculateMetabolicCost = any(all([ ...
+ strcmp(cellfun(@(term) term.type, inputs.costTerms, ...
+ 'UniformOutput', false), {'relative_metabolic_cost_per_time'}), ...
+ strcmp(cellfun(@(term) term.type, inputs.costTerms, ...
+ 'UniformOutput', false), {'relative_metabolic_cost_per_distance'}) ...
+ ; ...
+ cell2mat(cellfun(@(term) term.isEnabled, inputs.costTerms, ...
+ 'UniformOutput', false)), ...
+ cell2mat(cellfun(@(term) term.isEnabled, inputs.costTerms, ...
+ 'UniformOutput', false)) ...
+ ], 1));
+end
+[inputs.path, inputs.terminal] = parseRcnlConstraintTermSetHelper( ...
+ getFieldByNameOrError(tree, 'RCNLConstraintTermSet'), ...
+ inputs.controllerType, inputs.toolName);
+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);
+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
+
+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
+
diff --git a/src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityXIntegrand.m b/src/core/parse/parseTreatmentOptimizationParams.m
similarity index 79%
rename from src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityXIntegrand.m
rename to src/core/parse/parseTreatmentOptimizationParams.m
index 831fa0b34..e45215032 100644
--- a/src/core/TreatmentOptimization/IntegrandTerms/calcMinimizingMassCenterVelocityXIntegrand.m
+++ b/src/core/parse/parseTreatmentOptimizationParams.m
@@ -1,9 +1,6 @@
% 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 %
@@ -13,7 +10,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. %
@@ -27,9 +24,7 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function cost = calcMinimizingMassCenterVelocityXIntegrand(values, params)
-
-massCenterVelocity = calcMassCenterVelocity(values, params.model, ...
- params.coordinateNames);
-cost = calcMinimizingCostArrayTerm(massCenterVelocity(:, 1));
-end
\ No newline at end of file
+function params = parseTreatmentOptimizationParams(tree)
+params.experimentalBSplineCutoffFrequency = ...
+ parseDoubleOrAlternate(tree, "experimental_bspline_cutoff_frequency", 6);
+end
diff --git a/src/GroundContactPersonalization/Modeling/prepareModel.m b/src/core/parse/parseTrialData.m
similarity index 59%
rename from src/GroundContactPersonalization/Modeling/prepareModel.m
rename to src/core/parse/parseTrialData.m
index d145d254a..66485b30d 100644
--- a/src/GroundContactPersonalization/Modeling/prepareModel.m
+++ b/src/core/parse/parseTrialData.m
@@ -1,12 +1,10 @@
% 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).
+% 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, string, string, string, string, struct, double, double,
-% logical) -> (Model, Array of double, Array of double)
-% Create foot model and kinematics in seven coordinates.
+% (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 %
@@ -30,23 +28,29 @@
% 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);
+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
diff --git a/src/TrackingOptimization/calcTrackingOptimizationObjective.m b/src/core/parse/parseTrialName.m
similarity index 80%
rename from src/TrackingOptimization/calcTrackingOptimizationObjective.m
rename to src/core/parse/parseTrialName.m
index 6b8eef71d..485baa353 100644
--- a/src/TrackingOptimization/calcTrackingOptimizationObjective.m
+++ b/src/core/parse/parseTrialName.m
@@ -1,10 +1,10 @@
% This function is part of the NMSM Pipeline, see file for full license.
%
-% This function calculates the cost function objective for tracking
-% optimization.
+% trialName indicates which trial is used for Treatment Optimization and
+% this indicates which file should be parsed from the various data sources.
%
-% (Number, Array of number, Number, struct) -> (Number)
-% Returns objective
+% (struct) -> (string)
+% parses the element
% ----------------------------------------------------------------------- %
% The NMSM Pipeline is a toolkit for model personalization and treatment %
@@ -14,7 +14,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. %
@@ -28,7 +28,7 @@
% permissions and limitations under the License. %
% ----------------------------------------------------------------------- %
-function objective = calcTrackingOptimizationObjective(integral)
-continuousObjective = sum(integral) / length(integral);
-objective = continuousObjective;
-end
\ No newline at end of file
+function trialName = parseTrialName(tree)
+trialName = getFieldByNameOrError(tree, "trial_name").Text;
+end
+
diff --git a/src/core/parse/reorderPreprocessedDataByMuscleNames.m b/src/core/parse/reorderPreprocessedDataByMuscleNames.m
index 9a75d8b0d..12e41b93a 100644
--- a/src/core/parse/reorderPreprocessedDataByMuscleNames.m
+++ b/src/core/parse/reorderPreprocessedDataByMuscleNames.m
@@ -1,7 +1,7 @@
function inputs = reorderPreprocessedDataByMuscleNames(inputs, muscleNames)
-indexesInParsedMuscleData = ...
- ismember(inputs.muscleTendonColumnNames, muscleNames);
+[~, ~, indexesInParsedMuscleData] = intersect( ...
+ muscleNames, inputs.muscleTendonColumnNames, 'stable');
inputs.muscleTendonColumnNames = ...
inputs.muscleTendonColumnNames(indexesInParsedMuscleData);
@@ -10,16 +10,10 @@
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( ...
+ inputs.coordinateNames, inputs.momentArmsCoordinateNames, 'stable');
inputs.inverseDynamicsMoments = ...
inputs.inverseDynamicsMoments(:, indexesInParsedCoordinateData, :);
@@ -27,13 +21,13 @@
indexesInParsedMuscleData, :);
if (isfield(inputs, "mtpActivationsColumnNames"))
-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
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
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
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 = {};
diff --git a/src/core/verify/getPipelineVersion.m b/src/core/verify/getPipelineVersion.m
index 678e184cd..6c4b59065 100644
--- a/src/core/verify/getPipelineVersion.m
+++ b/src/core/verify/getPipelineVersion.m
@@ -38,5 +38,5 @@
% ----------------------------------------------------------------------- %
function version = getPipelineVersion()
-version = "1.0.1";
+version = "1.1.0";
end