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