@@ -749,6 +749,9 @@ Thelen2003Muscle::initMuscleState(const SimTK::State& s,
749
749
750
750
double phi = 0.0 ;
751
751
double cosphi = 1.0 ;
752
+ double sinphi = 0.0 ;
753
+ double tanphi = 0 .;
754
+
752
755
753
756
// Normalized quantities
754
757
double tlN = tl/tsl;
@@ -782,6 +785,15 @@ Thelen2003Muscle::initMuscleState(const SimTK::State& s,
782
785
783
786
double Ke = 0 ; // Linearized local stiffness of the muscle
784
787
788
+ // MM 4 March 2020: additional numerical bounds to prevent
789
+ // cover a few additional nasty cases
790
+ // eTMin > 0: if the tendon goes slack the tendons force-length gradient
791
+ // goes to zero. The Newton method will fail.
792
+ // fvMin > 0: if the fv multiplier hits zero then the gradient of fiber
793
+ // force w.r.t. length goes to zero and the Newton method will
794
+ // fail.
795
+ double eTMin = aSolTolerance*10 ;
796
+ double fvMin = aSolTolerance*10 ;
785
797
// *******************************
786
798
// Helper functions
787
799
// Update position level quantities, only if they won't go singular
@@ -858,8 +870,124 @@ Thelen2003Muscle::initMuscleState(const SimTK::State& s,
858
870
dlceN = dlce / (vmax*ofl);
859
871
// Update the force-velocity multiplier
860
872
fv = calcfvInv (ma, fal, dlceN, aSolTolerance, 100 );
873
+
874
+ // Ensure that fv is in the allowed range
875
+ if (fv < fvMin){
876
+ fv = fvMin;
877
+ dlceN = calcdlceN (ma,fal, ma*fal*fv);
878
+ dlce = dlceN*(vmax*ofl);
879
+ sinphi = sin (phi);
880
+ tanphi = sin (phi)/cos (phi);
881
+ dphi= getPennationModel ().calcPennationAngularVelocity (
882
+ tanphi,lce,dlce);
883
+ dtl = getPennationModel ().calcTendonVelocity (cosphi,sinphi,dphi,
884
+ lce,dlce,dml);
885
+ }
861
886
};
862
887
888
+
889
+
890
+ // MM 4 March 2020
891
+ // Run the biesection method over tendon strain to get a good initial
892
+ // condition. To get some appropriately large bounds here we evaluate
893
+ // the fiber lengths that correspond to a big tendon strain (4!) and zero
894
+ // tendon strain.
895
+ double eTDelta = 1.0 *get_FmaxTendonStrain ();
896
+ double eTStart = eTDelta*2 ;
897
+ double eTShortest = eTStart - 2 *eTDelta; // shortest eT in the limit
898
+ double eTLongest = eTStart + 2 *eTDelta; // longest eT in the limit
899
+
900
+ // Shorter than this is a problem: the tendon's force-length gradient
901
+ // goes to zero which means the Newton iteration will do nothing useful.
902
+
903
+
904
+ eTShortest = std::max (eTMin,eTShortest);
905
+
906
+ // Map the tendon lengths over to fiber lengths for the bisection method
907
+ // Make sure the bounds we are testing within the allowable range. Adjust
908
+ // the bounds if need be
909
+ double lceLongest =
910
+ getPennationModel ().calcFiberLength (ml, tsl*(1.0 +eTShortest));
911
+ double lceShortest =
912
+ getPennationModel ().calcFiberLength (ml, tsl*(1.0 +eTLongest));
913
+
914
+ if (lceShortest > getMinimumFiberLength () ){
915
+ lceShortest = getMinimumFiberLength ();
916
+ }
917
+
918
+ double lceDelta = (lceLongest-lceShortest)/4.0 ;
919
+ double lceBest = 0.5 *(lceLongest+lceShortest);
920
+
921
+ double lceLeft = 0 .;
922
+ double lceRight = 0 .;
923
+
924
+ double ferrLeft = 0 ;
925
+ double ferrRight = 0 ;
926
+ double ferrBest = 1e100 ;
927
+
928
+ // Evaluate the initial solution
929
+ lce = lceBest;
930
+ positionFunc ();
931
+ multipliersFunc ();
932
+ fv = 1.0 ;
933
+ partialsFunc ();
934
+ velocityFunc ();
935
+ ferrFunc ();
936
+ ferrBest = ferr;
937
+ // 10 Bisection iterations will bring us to a tendon length that is within
938
+ // 4 / 2^10 = 0.0039 of the true normalized strain. This translates directly
939
+ // into 0.0039 of the true force at equilibrium
940
+ unsigned int iterBisection =
941
+ std::max ((unsigned int )(std::ceil (0.5 *aMaxIterations)) ,
942
+ (unsigned int )5 );
943
+
944
+ for (unsigned int i = 0 ; i<iterBisection; ++i) {
945
+ // Evaluate the left hand candidate
946
+ lceLeft = lceBest-lceDelta;
947
+ lce = lceLeft;
948
+ positionFunc ();
949
+ multipliersFunc ();
950
+ fv = 1.0 ;
951
+ partialsFunc ();
952
+ velocityFunc ();
953
+ ferrFunc ();
954
+ ferrLeft = ferr;
955
+
956
+ // Evalute the right hand candidate
957
+ lceRight = lceBest+lceDelta;
958
+ lce = lceRight;
959
+ positionFunc ();
960
+ multipliersFunc ();
961
+ fv = 1.0 ;
962
+ partialsFunc ();
963
+ velocityFunc ();
964
+ ferrFunc ();
965
+ ferrRight = ferr;
966
+
967
+ // Update the current best solution if one of the candidates is suitable
968
+ if ( std::abs (ferrLeft) < std::abs (ferrBest)
969
+ && std::abs (ferrLeft) <= std::abs (ferrRight)){
970
+ lceBest = lceLeft;
971
+ ferrBest = ferrLeft;
972
+ }
973
+ if ( std::abs (ferrRight) < std::abs (ferrBest)
974
+ && std::abs (ferrRight) < std::abs (ferrLeft)){
975
+ lceBest = lceRight;
976
+ ferrBest = ferrRight;
977
+ }
978
+
979
+ // Reduce the step size
980
+ lceDelta = lceDelta*0.5 ;
981
+ }
982
+
983
+ double delta_lce_max = lceDelta*2.0 ;
984
+
985
+ // Polish up the root to tolerance using a text-book Newton's method
986
+ lce = lceBest;
987
+
988
+ double ferrPrev = ferrBest;
989
+ double lcePrev = lceBest;
990
+
863
991
// *******************************
864
992
// Initialize the loop
865
993
int iter = 0 ;
@@ -886,57 +1014,45 @@ Thelen2003Muscle::initMuscleState(const SimTK::State& s,
886
1014
// newly estimated fv
887
1015
partialsFunc ();
888
1016
889
- double ferrPrev = ferr;
890
- double lcePrev = lce;
1017
+ // MM 4 March 2020
1018
+ // Removing the line search capability of the (original) Newton method
1019
+ // implementation: this is less reliable then simply giving the Newton
1020
+ // method a very good initial condition by using the bisection method.
1021
+ // This will be a bit slower, but probably not noticeably so.
891
1022
892
- double h = 1.0 ;
893
- while ( (abs (ferr) > aSolTolerance) && (iter < aMaxIterations)) {
894
- // Compute the search direction
895
- dferr_d_lce = dFmAT_dlce - dFt_d_lce;
896
- h = 1.0 ;
897
-
898
- while (abs (ferr) >= abs (ferrPrev)) {
899
- // Compute the Newton step
900
- delta_lce = -h*ferrPrev / dferr_d_lce;
901
- // Take a Newton Step if the step is nonzero
902
- if (abs (delta_lce) > SimTK::SignificantReal)
903
- lce = lcePrev + delta_lce;
904
- else {
905
- // We've stagnated or hit a limit; assume we are hitting local
906
- // minimum and attempt to approach from the other direction.
907
- lce = lcePrev - sign (delta_lce)*SimTK::SqrtEps;
908
- // Force a break, which will update the derivatives of
909
- // the muscle force and estimate of the fiber-velocity
910
- h = 0 ;
911
- }
1023
+ // double h = 1.0;
912
1024
913
- if (lce < getMinimumFiberLength ()) {
914
- lce = getMinimumFiberLength ();
915
- }
916
1025
917
- // Update the muscles's position level quantities (lengths, angles)
918
- positionFunc ();
1026
+ while ( (abs (ferr) > aSolTolerance) && (iter < aMaxIterations)) {
919
1027
920
- // Update the muscle force multipliers
921
- multipliersFunc () ;
1028
+ dferr_d_lce = dFmAT_dlce - dFt_d_lce;
1029
+ delta_lce = - ferrPrev / dferr_d_lce ;
922
1030
923
- // Compute the force error assuming fiber-velocity is unchanged
924
- ferrFunc ();
1031
+ lce = lcePrev + delta_lce;
925
1032
926
- if (h <= SimTK::SqrtEps ) {
927
- break ;
928
- }
929
- else
930
- h = 0.5 *h;
1033
+ if (lce < getMinimumFiberLength ()) {
1034
+ lce = getMinimumFiberLength ();
931
1035
}
932
1036
933
- ferrPrev = ferr;
934
- lcePrev = lce;
935
1037
936
- // Update the partial derivative of the force error w.r.t. lce
1038
+ // Evaluate the current solution assuming the fiber velocity
1039
+ // does not change from the previous iteration.
1040
+ positionFunc ();
1041
+ multipliersFunc ();
937
1042
partialsFunc ();
938
- // Update velocity estimate and velocity multiplier
939
1043
velocityFunc ();
1044
+ ferrFunc ();
1045
+
1046
+
1047
+ ferrPrev = ferr;
1048
+ lcePrev = lce;
1049
+
1050
+ // If the tendon goes slack the tendon's gradient goes to zero
1051
+ // and the Newton method has no hope of recovering.
1052
+ if ( tlN < 1.0 ){
1053
+ lce = lceLongest;
1054
+ }
1055
+
940
1056
941
1057
iter++;
942
1058
}
0 commit comments