diff --git a/OpenSim/Actuators/DeGrooteFregly2016Muscle.cpp b/OpenSim/Actuators/DeGrooteFregly2016Muscle.cpp index df86b36a73..0164417526 100644 --- a/OpenSim/Actuators/DeGrooteFregly2016Muscle.cpp +++ b/OpenSim/Actuators/DeGrooteFregly2016Muscle.cpp @@ -408,8 +408,6 @@ void DeGrooteFregly2016Muscle::calcMuscleDynamicsInfoHelper( mli.sinPennationAngle, mli.cosPennationAngle, partialPennationAnglePartialFiberLength); mdi.tendonStiffness = calcTendonStiffness(mli.normTendonLength); - mdi.muscleStiffness = calcMuscleStiffness( - mdi.tendonStiffness, mdi.fiberStiffnessAlongTendon); const auto& partialTendonForcePartialFiberLength = calcPartialTendonForcePartialFiberLength(mdi.tendonStiffness, diff --git a/OpenSim/Actuators/DeGrooteFregly2016Muscle.h b/OpenSim/Actuators/DeGrooteFregly2016Muscle.h index 78bfa73333..e0248a6df3 100644 --- a/OpenSim/Actuators/DeGrooteFregly2016Muscle.h +++ b/OpenSim/Actuators/DeGrooteFregly2016Muscle.h @@ -608,10 +608,20 @@ class OSIMACTUATORS_API DeGrooteFregly2016Muscle : public Muscle { if (get_ignore_tendon_compliance()) return fiberStiffnessAlongTendon; // TODO Millard2012EquilibriumMuscle includes additional checks that // the stiffness is non-negative and that the denominator is non-zero. + // Checks are omitted here to preserve continuity and smoothness for + // optimization (see #3685). return (fiberStiffnessAlongTendon * tendonStiffness) / (fiberStiffnessAlongTendon + tendonStiffness); } + virtual double calcMuscleStiffness(const SimTK::State& s) const override + { + const MuscleDynamicsInfo& mdi = getMuscleDynamicsInfo(s); + return calcMuscleStiffness( + mdi.tendonStiffness, + mdi.fiberStiffnessAlongTendon); + } + /// The derivative of pennation angle with respect to fiber length. /// @note based on /// MuscleFixedWidthPennationModel::calc_DPennationAngle_DFiberLength(). diff --git a/OpenSim/Actuators/Millard2012AccelerationMuscle.cpp b/OpenSim/Actuators/Millard2012AccelerationMuscle.cpp index b4c98a57e7..38ec497d78 100644 --- a/OpenSim/Actuators/Millard2012AccelerationMuscle.cpp +++ b/OpenSim/Actuators/Millard2012AccelerationMuscle.cpp @@ -1054,11 +1054,6 @@ void Millard2012AccelerationMuscle:: //Compute the stiffness of the tendon double dFt_dtl = calcTendonStiffness(ami); - //Compute the stiffness of the whole muscle/tendon complex - double Ke = 0; - if (abs(dFceAT_dlceAT*dFt_dtl)>0) - Ke = (dFceAT_dlceAT*dFt_dtl)/(dFceAT_dlceAT+dFt_dtl); - //Populate the output vector mdi.activation = a; @@ -1077,7 +1072,6 @@ void Millard2012AccelerationMuscle:: mdi.fiberStiffness = dFce_dlce; mdi.fiberStiffnessAlongTendon = dFceAT_dlceAT; mdi.tendonStiffness = dFt_dtl; - mdi.muscleStiffness = Ke; //Check that the derivative of system energy less work is zero within //a reasonable numerical tolerance. diff --git a/OpenSim/Actuators/Millard2012EquilibriumMuscle.cpp b/OpenSim/Actuators/Millard2012EquilibriumMuscle.cpp index 67d246396c..7a351a5b66 100644 --- a/OpenSim/Actuators/Millard2012EquilibriumMuscle.cpp +++ b/OpenSim/Actuators/Millard2012EquilibriumMuscle.cpp @@ -1217,15 +1217,6 @@ calcMuscleDynamicsInfo(const SimTK::State& s, MuscleDynamicsInfo& mdi) const // Compute the stiffness of the muscle. dFt_dtl = mvi.userDefinedVelocityExtras[MVITendonStiffness]; - if(!get_ignore_tendon_compliance()) { - // Compute the stiffness of the whole musculotendon actuator. - if (abs(dFmAT_dlceAT*dFt_dtl) > 0.0 - && abs(dFmAT_dlceAT+dFt_dtl) > SimTK::SignificantReal) { - Ke = (dFmAT_dlceAT*dFt_dtl)/(dFmAT_dlceAT+dFt_dtl); - } - } else { - Ke = dFmAT_dlceAT; - } } const double fse = get_ignore_tendon_compliance() @@ -1243,7 +1234,6 @@ calcMuscleDynamicsInfo(const SimTK::State& s, MuscleDynamicsInfo& mdi) const mdi.fiberStiffness = dFm_dlce; mdi.fiberStiffnessAlongTendon = dFmAT_dlceAT; mdi.tendonStiffness = dFt_dtl; - mdi.muscleStiffness = Ke; // Verify that the derivative of system energy minus work is zero within // a reasonable numerical tolerance. diff --git a/OpenSim/Actuators/Thelen2003Muscle.cpp b/OpenSim/Actuators/Thelen2003Muscle.cpp index 863d839927..fa80326d5b 100644 --- a/OpenSim/Actuators/Thelen2003Muscle.cpp +++ b/OpenSim/Actuators/Thelen2003Muscle.cpp @@ -623,9 +623,6 @@ void Thelen2003Muscle::calcMuscleDynamicsInfo(const SimTK::State& s, dFmAT_dlceAT= dFmAT_dlce*cosphi; dFt_dtl = calcDFseDtl(tl, fiso, tendonSlackLen); - - //Compute the stiffness of the whole muscle/tendon complex - Ke = (dFmAT_dlceAT*dFt_dtl)/(dFmAT_dlceAT+dFt_dtl); } mdi.activation = a; @@ -641,7 +638,6 @@ void Thelen2003Muscle::calcMuscleDynamicsInfo(const SimTK::State& s, mdi.fiberStiffness = dFm_dlce; mdi.fiberStiffnessAlongTendon = dFmAT_dlceAT; mdi.tendonStiffness = dFt_dtl; - mdi.muscleStiffness = Ke; diff --git a/OpenSim/Simulation/Model/Muscle.cpp b/OpenSim/Simulation/Model/Muscle.cpp index 19fa80cf54..3fb1d5170c 100644 --- a/OpenSim/Simulation/Model/Muscle.cpp +++ b/OpenSim/Simulation/Model/Muscle.cpp @@ -473,7 +473,7 @@ double Muscle::getTendonStiffness(const SimTK::State& s) const of muscle force w.r.t. muscle length */ double Muscle::getMuscleStiffness(const SimTK::State& s) const { - return getMuscleDynamicsInfo(s).muscleStiffness; + return calcMuscleStiffness(s); } /* get the current fiber power (W) */ @@ -633,6 +633,26 @@ double Muscle::calcMusclePower(const SimTK::State& s) const return -getLengtheningSpeed(s) * getMuscleDynamicsInfo(s).tendonForce; } +double Muscle::calcMuscleStiffness(const SimTK::State& s) const +{ + const MuscleDynamicsInfo& mdi = getMuscleDynamicsInfo(s); + + const double dFmAT_dlceAT = mdi.fiberStiffnessAlongTendon; + const double dFt_dtl = mdi.tendonStiffness; + + double Ke = 0.; + if (!get_ignore_tendon_compliance()) { + // Compute the stiffness of the whole musculotendon actuator. + if (abs(dFmAT_dlceAT * dFt_dtl) > 0.0 && + abs(dFmAT_dlceAT + dFt_dtl) > SimTK::SignificantReal) { + Ke = (dFmAT_dlceAT * dFt_dtl) / (dFmAT_dlceAT + dFt_dtl); + } + } else { + Ke = dFmAT_dlceAT; + } + return Ke; +} + //============================================================================= // Required by CMC and Static Optimization //============================================================================= diff --git a/OpenSim/Simulation/Model/Muscle.h b/OpenSim/Simulation/Model/Muscle.h index f2f45e387b..348ebb3cff 100644 --- a/OpenSim/Simulation/Model/Muscle.h +++ b/OpenSim/Simulation/Model/Muscle.h @@ -461,10 +461,21 @@ OpenSim_DECLARE_ABSTRACT_OBJECT(Muscle, PathActuator); /** Calculate muscle's power (W). */ double calcMusclePower(const SimTK::State& s) const; +protected: + /** Calculate muscle's stiffness. + + Muscle stiffness is defined as the partial derivative of muscle force + with respect to changes in muscle length. This quantity can be computed + by noting that the tendon and the fiber are in series, with the fiber + at a pennation angle. Thus + + Kmuscle = (Kfiber_along_tendon * Ktendon) + /(Kfiber_along_tendon + Ktendon) */ + virtual double calcMuscleStiffness(const SimTK::State& s) const; + //============================================================================= // DATA //============================================================================= -protected: /** The assumed fixed muscle-width from which the fiber pennation angle can be calculated. */ @@ -710,13 +721,12 @@ OpenSim_DECLARE_ABSTRACT_OBJECT(Muscle, PathActuator); fiberStiffness force/length N/m [7] fiberStiffnessAlongTendon force/length N/m [8] tendonStiffness force/length N/m [9] - muscleStiffness force/length N/m [10] fiberActivePower force*velocity W (N*m/s) fiberPassivePower force*velocity W (N*m/s) tendonPower force*velocity W (N*m/s) - userDefinedDynamicsData NA NA [11] + userDefinedDynamicsData NA NA [10] [1] This is a quantity that ranges between 0 and 1 that dictates how on or activated a muscle is. This term may or may not have its own @@ -752,15 +762,7 @@ OpenSim_DECLARE_ABSTRACT_OBJECT(Muscle, PathActuator); [9] tendonStiffness is defined as the partial derivative of tendon force with respect to tendon length - [10] muscleStiffness is defined as the partial derivative of muscle force - with respect to changes in muscle length. This quantity can usually - be computed by noting that the tendon and the fiber are in series, - with the fiber at a pennation angle. Thus - - Kmuscle = (Kfiber_along_tendon * Ktendon) - /(Kfiber_along_tendon + Ktendon) - - [11] This vector is left for the muscle modeler to populate with any + [10] This vector is left for the muscle modeler to populate with any computationally expensive quantities that might be of interest after dynamics calculations are completed but maybe of use in computing muscle derivatives or reporting values of interest. @@ -781,7 +783,6 @@ OpenSim_DECLARE_ABSTRACT_OBJECT(Muscle, PathActuator); double fiberStiffness; // force/length N/m double fiberStiffnessAlongTendon;//force/length N/m double tendonStiffness; // force/length N/m - double muscleStiffness; // force/length N/m // double fiberActivePower; // force*velocity W double fiberPassivePower; // force*velocity W @@ -801,7 +802,6 @@ OpenSim_DECLARE_ABSTRACT_OBJECT(Muscle, PathActuator); fiberStiffness(SimTK::NaN), fiberStiffnessAlongTendon(SimTK::NaN), tendonStiffness(SimTK::NaN), - muscleStiffness(SimTK::NaN), fiberActivePower(SimTK::NaN), fiberPassivePower(SimTK::NaN), tendonPower(SimTK::NaN), diff --git a/OpenSim/Simulation/Test/testMuscleMetabolicsProbes.cpp b/OpenSim/Simulation/Test/testMuscleMetabolicsProbes.cpp index e40ea35dbe..9da77d6506 100644 --- a/OpenSim/Simulation/Test/testMuscleMetabolicsProbes.cpp +++ b/OpenSim/Simulation/Test/testMuscleMetabolicsProbes.cpp @@ -302,7 +302,6 @@ OpenSim_DECLARE_CONCRETE_OBJECT(UmbergerMuscle, Muscle); mdi.fiberStiffness = 0; mdi.fiberStiffnessAlongTendon = 0; mdi.tendonStiffness = 0; - mdi.muscleStiffness = 0; mdi.fiberActivePower = 0; mdi.fiberPassivePower = 0; mdi.tendonPower = 0;