1 /* -------------------------------------------------------------------------- *
2  *                            OpenSim:  Muscle.cpp                            *
3  * -------------------------------------------------------------------------- *
4  * The OpenSim API is a toolkit for musculoskeletal modeling and simulation.  *
5  * See http://opensim.stanford.edu and the NOTICE file for more information.  *
6  * OpenSim is developed at Stanford University and supported by the US        *
7  * National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA    *
8  * through the Warrior Web program.                                           *
9  *                                                                            *
10  * Copyright (c) 2005-2017 Stanford University and the Authors                *
11  * Author(s): Peter Loan, Ajay Seth                                           *
12  *                                                                            *
13  * Licensed under the Apache License, Version 2.0 (the "License"); you may    *
14  * not use this file except in compliance with the License. You may obtain a  *
15  * copy of the License at http://www.apache.org/licenses/LICENSE-2.0.         *
16  *                                                                            *
17  * Unless required by applicable law or agreed to in writing, software        *
18  * distributed under the License is distributed on an "AS IS" BASIS,          *
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
20  * See the License for the specific language governing permissions and        *
21  * limitations under the License.                                             *
22  * -------------------------------------------------------------------------- */
23 
24 //=============================================================================
25 // INCLUDES
26 //=============================================================================
27 #include "Muscle.h"
28 
29 #include "GeometryPath.h"
30 #include "Model.h"
31 #include <OpenSim/Common/XMLDocument.h>
32 
33 //=============================================================================
34 // STATICS
35 //=============================================================================
36 using namespace std;
37 using namespace OpenSim;
38 using SimTK::Vec3;
39 
40 static const Vec3 DefaultMuscleColor(.8, .1, .1); // Red for backward compatibility
41 
42 //=============================================================================
43 // CONSTRUCTOR
44 //=============================================================================
45 //_____________________________________________________________________________
46 // Default constructor.
Muscle()47 Muscle::Muscle()
48 {
49     constructProperties();
50 }
51 
52 //_____________________________________________________________________________
53 // Override default implementation by object to intercept and fix the XML node
54 // underneath the model to match current version.
updateFromXMLNode(SimTK::Xml::Element & aNode,int versionNumber)55 void Muscle::updateFromXMLNode(SimTK::Xml::Element& aNode, int versionNumber)
56 {
57     if ( versionNumber < XMLDocument::getLatestVersion()) {
58         if (Object::getDebugLevel()>=1)
59             cout << "Updating Muscle object to latest format..." << endl;
60 
61         if (versionNumber <= 20301){
62             SimTK::Xml::element_iterator pathIter =
63                                             aNode.element_begin("GeometryPath");
64             if (pathIter != aNode.element_end()) {
65                 XMLDocument::renameChildNode(*pathIter, "MusclePointSet", "PathPointSet");
66                 XMLDocument::renameChildNode(*pathIter, "MuscleWrapSet", "PathWrapSet");
67             } else { // There was no GeometryPath, just MusclePointSet
68                 SimTK::Xml::element_iterator musclePointSetIter = aNode.element_begin("MusclePointSet");
69                 bool pathPointSetFound=false;
70                 if (musclePointSetIter != aNode.element_end()){
71                     XMLDocument::renameChildNode(aNode, "MusclePointSet", "PathPointSet");
72                     pathPointSetFound=true;
73                 }
74                 bool pathWrapSetFound=false;
75                 SimTK::Xml::element_iterator muscleWrapSetIter = aNode.element_begin("MuscleWrapSet");
76                 if (muscleWrapSetIter != aNode.element_end()){
77                     XMLDocument::renameChildNode(aNode, "MuscleWrapSet", "PathWrapSet");
78                     pathWrapSetFound=true;
79                 }
80                 // Now create a "GeometryPath" node and move MusclePointSet & MuscleWrapSet under it
81                 SimTK::Xml::Element myPathElement("GeometryPath");
82                 SimTK::Xml::Node moveNode;
83                 if (pathPointSetFound) {
84                     SimTK::Xml::element_iterator  pathPointSetIter = aNode.element_begin("PathPointSet");
85                     moveNode = aNode.removeNode(pathPointSetIter);
86                     myPathElement.insertNodeAfter(myPathElement.element_end(),moveNode);
87                 }
88                 if (pathWrapSetFound) {
89                     SimTK::Xml::element_iterator  pathWrapSetIter = aNode.element_begin("PathWrapSet");
90                     moveNode = aNode.removeNode(pathWrapSetIter);
91                     myPathElement.insertNodeAfter(myPathElement.element_end(),moveNode);
92                 }
93                 aNode.insertNodeAfter(aNode.element_end(), myPathElement);
94             }
95             XMLDocument::renameChildNode(aNode, "pennation_angle", "pennation_angle_at_optimal");
96         }
97         if (versionNumber < 30513) {
98             SimTK::Xml::element_iterator minControlElt =
99                 aNode.element_begin("min_control");
100             double minControl = 0;
101             if (minControlElt != aNode.element_end()) {
102                 minControlElt->getValueAs<double>(minControl);
103                 // If the min_control value is 0, then remove the min_control
104                 // property in XML since it is likely a result of a mistake. In
105                 // previous versions, the min_control property was no updated
106                 // to reflect the Muscle's min_activation. Removing it allows
107                 // the Muscle to use the appropriate default specified by the
108                 // derived concrete Muscle.
109                 if (SimTK::isNumericallyEqual(minControl, 0.0)) {
110                     aNode.removeNode(minControlElt);
111                 }
112             }
113             SimTK::Xml::element_iterator maxControlElt =
114                 aNode.element_begin("max_control");
115             double maxControl = 0;
116             if (maxControlElt != aNode.element_end()) {
117                 maxControlElt->getValueAs<double>(maxControl);
118                 // allow Muscle to use its default
119                 if (SimTK::isNumericallyEqual(maxControl, 1.0)) {
120                     aNode.removeNode(maxControlElt);
121                 }
122             }
123         }
124         if (versionNumber < 30516) {
125             // Find GeometryPath node and insert <default_color>
126             SimTK::Xml::element_iterator  geomPathIter = aNode.element_begin("GeometryPath");
127             if (geomPathIter != aNode.element_end()) {
128                 SimTK::Xml::element_iterator  defaultColorIter = geomPathIter->element_begin("default_color");
129                 if (defaultColorIter == geomPathIter->element_end()) {
130                     SimTK::Xml::Element myDefaultColorEement("default_color");
131                     myDefaultColorEement.setValue(".8 .1 .1"); // DefaultMuscleColor
132                     geomPathIter->appendNode(myDefaultColorEement);
133                 }
134             }
135         }
136 
137     }
138     // Call base class now assuming aNode has been corrected for current version
139     Super::updateFromXMLNode(aNode, versionNumber);
140 }
141 
142 
143 //_____________________________________________________________________________
144 /**
145  * Connect properties to local pointers.
146  */
constructProperties()147 void Muscle::constructProperties()
148 {
149     constructProperty_max_isometric_force(1000.0);
150     constructProperty_optimal_fiber_length(0.1);
151     constructProperty_tendon_slack_length(0.2);
152     constructProperty_pennation_angle_at_optimal(0.0);
153     constructProperty_max_contraction_velocity(10.0);
154     constructProperty_ignore_tendon_compliance(false);
155     constructProperty_ignore_activation_dynamics(false);
156 
157     // By default the min and max controls on muscle are 0.0 and 1.0
158     setMinControl(0.0);
159     setMaxControl(1.0);
160     upd_GeometryPath().setDefaultColor(DefaultMuscleColor);
161 }
162 
163 
164 //--------------------------------------------------------------------------
165 // MUSCLE PARAMETERS GETTERS AND SETTERS
166 //--------------------------------------------------------------------------
getMaxIsometricForce() const167 double Muscle::getMaxIsometricForce() const
168 {   return get_max_isometric_force(); }
169 
getOptimalFiberLength() const170 double Muscle::getOptimalFiberLength() const
171 {   return get_optimal_fiber_length(); }
172 
getTendonSlackLength() const173 double Muscle::getTendonSlackLength() const
174 {   return get_tendon_slack_length(); }
175 
getPennationAngleAtOptimalFiberLength() const176 double Muscle::getPennationAngleAtOptimalFiberLength() const
177 {   return get_pennation_angle_at_optimal(); }
178 
getMaxContractionVelocity() const179 double Muscle::getMaxContractionVelocity() const
180 {   return get_max_contraction_velocity(); }
181 
setMaxIsometricForce(double aMaxIsometricForce)182 void Muscle::setMaxIsometricForce(double aMaxIsometricForce)
183 {   set_max_isometric_force(aMaxIsometricForce); }
184 
setOptimalFiberLength(double aOptimalFiberLength)185 void Muscle::setOptimalFiberLength(double aOptimalFiberLength)
186 {   set_optimal_fiber_length(aOptimalFiberLength); }
187 
setTendonSlackLength(double aTendonSlackLength)188 void Muscle::setTendonSlackLength(double aTendonSlackLength)
189 {   set_tendon_slack_length(aTendonSlackLength); }
190 
setPennationAngleAtOptimalFiberLength(double aPennationAngle)191 void Muscle::setPennationAngleAtOptimalFiberLength(double aPennationAngle)
192 {   set_pennation_angle_at_optimal(aPennationAngle); }
193 
setMaxContractionVelocity(double aMaxContractionVelocity)194 void Muscle::setMaxContractionVelocity(double aMaxContractionVelocity)
195 {   set_max_contraction_velocity(aMaxContractionVelocity); }
196 
197 
198 //=============================================================================
199 // ModelComponent Interface Implementation
200 //=============================================================================
extendConnectToModel(Model & aModel)201 void Muscle::extendConnectToModel(Model& aModel)
202 {
203     Super::extendConnectToModel(aModel);
204 
205     _muscleWidth = getOptimalFiberLength()
206                     * sin(getPennationAngleAtOptimalFiberLength());
207 
208     _maxIsometricForce = getMaxIsometricForce();
209     _optimalFiberLength = getOptimalFiberLength();
210     _pennationAngleAtOptimal = getPennationAngleAtOptimalFiberLength();
211     _tendonSlackLength = getTendonSlackLength();
212 }
213 
214 // Add Muscle's contributions to the underlying system
extendAddToSystem(SimTK::MultibodySystem & system) const215  void Muscle::extendAddToSystem(SimTK::MultibodySystem& system) const
216 {
217     Super::extendAddToSystem(system);
218 
219     addModelingOption("ignore_tendon_compliance", 1);
220     addModelingOption("ignore_activation_dynamics", 1);
221 
222     // Cache the calculated values for this muscle categorized by their realization stage
223 
224     //Matt.Millard: changed length info to the velocity stage, as I need to know
225     //              both the position and velocity of the multibody system and
226     //              the muscles path before solving for the fiber length and
227     //              velocity in the reduced model.
228     addCacheVariable<Muscle::MuscleLengthInfo>
229        ("lengthInfo", MuscleLengthInfo(), SimTK::Stage::Velocity);
230     addCacheVariable<Muscle::FiberVelocityInfo>
231        ("velInfo", FiberVelocityInfo(), SimTK::Stage::Velocity);
232     addCacheVariable<Muscle::MuscleDynamicsInfo>
233        ("dynamicsInfo", MuscleDynamicsInfo(), SimTK::Stage::Dynamics);
234     addCacheVariable<Muscle::MusclePotentialEnergyInfo>
235        ("potentialEnergyInfo", MusclePotentialEnergyInfo(), SimTK::Stage::Velocity);
236  }
237 
extendSetPropertiesFromState(const SimTK::State & state)238 void Muscle::extendSetPropertiesFromState(const SimTK::State& state)
239 {
240     Super::extendSetPropertiesFromState(state);
241 
242     set_ignore_tendon_compliance(getIgnoreTendonCompliance(state));
243     set_ignore_activation_dynamics(getIgnoreActivationDynamics(state));
244 }
245 
extendInitStateFromProperties(SimTK::State & state) const246 void  Muscle::extendInitStateFromProperties(SimTK::State& state) const {
247     Super::extendInitStateFromProperties(state);
248 
249     setIgnoreTendonCompliance(state,
250         get_ignore_tendon_compliance());
251     setIgnoreActivationDynamics(state,
252         get_ignore_activation_dynamics());
253 }
254 
255 // Get/set runtime flag to ignore tendon compliance when computing muscle
256 // dynamics.
getIgnoreTendonCompliance(const SimTK::State & s) const257 bool Muscle::getIgnoreTendonCompliance(const SimTK::State& s) const
258 {
259     return (getModelingOption(s, "ignore_tendon_compliance") > 0);
260 }
261 
setIgnoreTendonCompliance(SimTK::State & s,bool ignore) const262 void Muscle::setIgnoreTendonCompliance(SimTK::State& s, bool ignore) const
263 {
264     setModelingOption(s, "ignore_tendon_compliance", int(ignore));
265 }
266 
267 
268 /* get/set flag to activation dynamics when computing muscle dynamics  */
getIgnoreActivationDynamics(const SimTK::State & s) const269 bool Muscle::getIgnoreActivationDynamics(const SimTK::State& s) const
270 {
271     return (getModelingOption(s, "ignore_activation_dynamics") > 0);
272 }
273 
setIgnoreActivationDynamics(SimTK::State & s,bool ignore) const274 void Muscle::setIgnoreActivationDynamics(SimTK::State& s, bool ignore) const
275 {
276     setModelingOption(s, "ignore_activation_dynamics", int(ignore));
277 }
278 
279 
280 
281 //=============================================================================
282 // GET values of interest from calculations
283 //=============================================================================
284 //_____________________________________________________________________________
285 //**
286 // * get the excitation value for this Muscle
287 // */
getExcitation(const SimTK::State & s) const288 double Muscle::getExcitation( const SimTK::State& s) const {
289     return( getControl(s) );
290 }
291 
292 
293 /* get the activation level of the muscle, which modulates the active force of the muscle
294     and has a normalized (0 to 1) value */
getActivation(const SimTK::State & s) const295 double Muscle::getActivation(const SimTK::State& s) const
296 {
297     return getMuscleDynamicsInfo(s).activation;
298 }
299 
300 /* get the current working fiber length (m) for the muscle */
getFiberLength(const SimTK::State & s) const301 double Muscle::getFiberLength(const SimTK::State& s) const
302 {
303     return getMuscleLengthInfo(s).fiberLength;
304 }
305 
306 /* get the current pennation angle (radians) between the fiber and tendon at the current fiber length  */
getPennationAngle(const SimTK::State & s) const307 double Muscle::getPennationAngle(const SimTK::State& s) const
308 {
309     return getMuscleLengthInfo(s).pennationAngle;
310 }
311 
312 /* get the cosine of the current pennation angle (radians) between the fiber and tendon at the current fiber length  */
getCosPennationAngle(const SimTK::State & s) const313 double Muscle::getCosPennationAngle(const SimTK::State& s) const
314 {
315     return getMuscleLengthInfo(s).cosPennationAngle;
316 }
317 
318 /* get the current tendon length (m)  given the current joint angles and fiber length */
getTendonLength(const SimTK::State & s) const319 double Muscle::getTendonLength(const SimTK::State& s) const
320 {
321     return getMuscleLengthInfo(s).tendonLength;
322 }
323 
324 /* get the current normalized fiber length (fiber_length/optimal_fiber_length) */
getNormalizedFiberLength(const SimTK::State & s) const325 double Muscle::getNormalizedFiberLength(const SimTK::State& s) const
326 {
327     return getMuscleLengthInfo(s).normFiberLength;
328 }
329 
330 /* get the current fiber length (m) projected (*cos(pennationAngle)) onto the tendon direction */
getFiberLengthAlongTendon(const SimTK::State & s) const331 double Muscle::getFiberLengthAlongTendon(const SimTK::State& s) const
332 {
333     return getMuscleLengthInfo(s).fiberLength * getMuscleLengthInfo(s).cosPennationAngle;
334 }
335 
336 /* get the current tendon strain (delta_l/lo is dimensionless)  */
getTendonStrain(const SimTK::State & s) const337 double Muscle::getTendonStrain(const SimTK::State& s) const
338 {
339     return getMuscleLengthInfo(s).tendonStrain;
340 }
341 
342 /* the potential energy (J) stored in the fiber due to its parallel elastic element */
getFiberPotentialEnergy(const SimTK::State & s) const343 double Muscle::getFiberPotentialEnergy(const SimTK::State& s) const
344 {
345     return getMusclePotentialEnergyInfo(s).fiberPotentialEnergy;
346 }
347 
348 /* the potential energy (J) stored in the tendon */
getTendonPotentialEnergy(const SimTK::State & s) const349 double Muscle::getTendonPotentialEnergy(const SimTK::State& s) const
350 {
351     return getMusclePotentialEnergyInfo(s).tendonPotentialEnergy;
352 }
353 
354 /* the total potential energy (J) stored in the muscle */
getMusclePotentialEnergy(const SimTK::State & s) const355 double Muscle::getMusclePotentialEnergy(const SimTK::State& s) const
356 {
357     return getMusclePotentialEnergyInfo(s).musclePotentialEnergy;
358 }
359 
360 /* get the passive fiber (parallel elastic element) force multiplier */
getPassiveForceMultiplier(const SimTK::State & s) const361 double Muscle::getPassiveForceMultiplier(const SimTK::State& s) const
362 {
363     return getMuscleLengthInfo(s).fiberPassiveForceLengthMultiplier;
364 }
365 
366 /* get the active fiber (contractile element) force multiplier due to current fiber length */
getActiveForceLengthMultiplier(const SimTK::State & s) const367 double Muscle::getActiveForceLengthMultiplier(const SimTK::State& s) const
368 {
369     return getMuscleLengthInfo(s).fiberActiveForceLengthMultiplier;
370 }
371 
372 /* get current fiber velocity (m/s) positive is lengthening */
getFiberVelocity(const SimTK::State & s) const373 double Muscle::getFiberVelocity(const SimTK::State& s) const
374 {
375     return getFiberVelocityInfo(s).fiberVelocity;
376 }
377 
378 /* get normalized fiber velocity (fiber_length/s / max_contraction_velocity) */
getNormalizedFiberVelocity(const SimTK::State & s) const379 double Muscle::getNormalizedFiberVelocity(const SimTK::State& s) const
380 {
381     return getFiberVelocityInfo(s).normFiberVelocity;
382 }
383 
384 /* get the current fiber velocity (m/s) projected onto the tendon direction */
getFiberVelocityAlongTendon(const SimTK::State & s) const385 double Muscle::getFiberVelocityAlongTendon(const SimTK::State& s) const
386 {
387     return getFiberVelocityInfo(s).fiberVelocityAlongTendon;
388 }
389 
390 /* get the tendon velocity (m/s) */
getTendonVelocity(const SimTK::State & s) const391 double Muscle::getTendonVelocity(const SimTK::State& s) const
392 {
393     return getFiberVelocityInfo(s).tendonVelocity;
394 }
395 
396 /* get the dimensionless factor resulting from the fiber's force-velocity curve */
getForceVelocityMultiplier(const SimTK::State & s) const397 double Muscle::getForceVelocityMultiplier(const SimTK::State& s) const
398 {
399     return getFiberVelocityInfo(s).fiberForceVelocityMultiplier;
400 }
401 
402 /* get pennation angular velocity (radians/s) */
getPennationAngularVelocity(const SimTK::State & s) const403 double Muscle::getPennationAngularVelocity(const SimTK::State& s) const
404 {
405     return getFiberVelocityInfo(s).pennationAngularVelocity;
406 }
407 
408 /* get the current fiber force (N)*/
getFiberForce(const SimTK::State & s) const409 double Muscle::getFiberForce(const SimTK::State& s) const
410 {
411     return getMuscleDynamicsInfo(s).fiberForce;
412 }
413 
414 /* get the current fiber force (N) applied to the tendon */
getFiberForceAlongTendon(const SimTK::State & s) const415 double Muscle::getFiberForceAlongTendon(const SimTK::State& s) const
416 {
417     return getMuscleDynamicsInfo(s).fiberForceAlongTendon;
418 }
419 
420 
421 /* get the current active fiber force (N) due to activation*force_length*force_velocity relationships */
getActiveFiberForce(const SimTK::State & s) const422 double Muscle::getActiveFiberForce(const SimTK::State& s) const
423 {
424     return getMuscleDynamicsInfo(s).activeFiberForce;
425 }
426 
427 /* get the total force applied by all passive elements in the fiber (N) */
getPassiveFiberForce(const SimTK::State & s) const428 double Muscle::getPassiveFiberForce(const SimTK::State& s) const
429 {
430     return getMuscleDynamicsInfo(s).passiveFiberForce;
431 }
432 
433 /* get the current active fiber force (N) projected onto the tendon direction */
getActiveFiberForceAlongTendon(const SimTK::State & s) const434 double Muscle::getActiveFiberForceAlongTendon(const SimTK::State& s) const
435 {
436     return getMuscleDynamicsInfo(s).activeFiberForce * getMuscleLengthInfo(s).cosPennationAngle;
437 }
438 
439 /* get the total force applied by all passive elements in the fiber (N)
440    projected onto the tendon direction */
getPassiveFiberForceAlongTendon(const SimTK::State & s) const441 double Muscle::getPassiveFiberForceAlongTendon(const SimTK::State& s) const
442 {
443     return getMuscleDynamicsInfo(s).passiveFiberForce * getMuscleLengthInfo(s).cosPennationAngle;
444 }
445 
446 /* get the current tendon force (N) applied to bones */
getTendonForce(const SimTK::State & s) const447 double Muscle::getTendonForce(const SimTK::State& s) const
448 {
449     return getMaxIsometricForce() * getMuscleDynamicsInfo(s).normTendonForce;
450 }
451 
452 /* get the current fiber stiffness (N/m) defined as the partial derivative
453     of fiber force w.r.t. fiber length */
getFiberStiffness(const SimTK::State & s) const454 double Muscle::getFiberStiffness(const SimTK::State& s) const
455 {
456     return getMuscleDynamicsInfo(s).fiberStiffness;
457 }
458 
459 /* get the current fiber stiffness (N/m) defined as the partial derivative
460     of fiber force along the tendon w.r.t. small changes in fiber length
461     along the tendon*/
getFiberStiffnessAlongTendon(const SimTK::State & s) const462 double Muscle::getFiberStiffnessAlongTendon(const SimTK::State& s) const
463 {
464     return getMuscleDynamicsInfo(s).fiberStiffnessAlongTendon;
465 }
466 
467 
468 /* get the current tendon stiffness (N/m) defined as the partial derivative
469     of tendon force w.r.t. tendon length */
getTendonStiffness(const SimTK::State & s) const470 double Muscle::getTendonStiffness(const SimTK::State& s) const
471 {
472     return getMuscleDynamicsInfo(s).tendonStiffness;
473 }
474 
475 /* get the current muscle stiffness (N/m) defined as the partial derivative
476     of muscle force w.r.t. muscle length */
getMuscleStiffness(const SimTK::State & s) const477 double Muscle::getMuscleStiffness(const SimTK::State& s) const
478 {
479     return getMuscleDynamicsInfo(s).muscleStiffness;
480 }
481 
482 /* get the current fiber power (W) */
getFiberActivePower(const SimTK::State & s) const483 double Muscle::getFiberActivePower(const SimTK::State& s) const
484 {
485     return getMuscleDynamicsInfo(s).fiberActivePower;
486 }
487 
488 /* get the current fiber active power (W) */
getFiberPassivePower(const SimTK::State & s) const489 double Muscle::getFiberPassivePower(const SimTK::State& s) const
490 {
491     return getMuscleDynamicsInfo(s).fiberPassivePower;
492 }
493 
494 /* get the current tendon power (W) */
getTendonPower(const SimTK::State & s) const495 double Muscle::getTendonPower(const SimTK::State& s) const
496 {
497     return getMuscleDynamicsInfo(s).tendonPower;
498 }
499 
500 /* get the current muscle power (W) */
getMusclePower(const SimTK::State & s) const501 double Muscle::getMusclePower(const SimTK::State& s) const
502 {
503     return getMuscleDynamicsInfo(s).musclePower;
504 }
505 
506 
setExcitation(SimTK::State & s,double excitation) const507 void Muscle::setExcitation(SimTK::State& s, double excitation) const
508 {
509     setControls(SimTK::Vector(1, excitation), _model->updControls(s));
510 }
511 
512 /* Access to muscle calculation data structures */
getMuscleLengthInfo(const SimTK::State & s) const513 const Muscle::MuscleLengthInfo& Muscle::getMuscleLengthInfo(const SimTK::State& s) const
514 {
515     if(!isCacheVariableValid(s,"lengthInfo")){
516         MuscleLengthInfo &umli = updMuscleLengthInfo(s);
517         calcMuscleLengthInfo(s, umli);
518         markCacheVariableValid(s,"lengthInfo");
519         // don't bother fishing it out of the cache since
520         // we just calculated it and still have a handle on it
521         return umli;
522     }
523     return getCacheVariableValue<MuscleLengthInfo>(s, "lengthInfo");
524 }
525 
updMuscleLengthInfo(const SimTK::State & s) const526 Muscle::MuscleLengthInfo& Muscle::updMuscleLengthInfo(const SimTK::State& s) const
527 {
528     return updCacheVariableValue<MuscleLengthInfo>(s, "lengthInfo");
529 }
530 
531 const Muscle::FiberVelocityInfo& Muscle::
getFiberVelocityInfo(const SimTK::State & s) const532 getFiberVelocityInfo(const SimTK::State& s) const
533 {
534     if(!isCacheVariableValid(s,"velInfo")){
535         FiberVelocityInfo& ufvi = updFiberVelocityInfo(s);
536         calcFiberVelocityInfo(s, ufvi);
537         markCacheVariableValid(s,"velInfo");
538         // don't bother fishing it out of the cache since
539         // we just calculated it and still have a handle on it
540         return ufvi;
541     }
542     return getCacheVariableValue<FiberVelocityInfo>(s, "velInfo");
543 }
544 
545 Muscle::FiberVelocityInfo& Muscle::
updFiberVelocityInfo(const SimTK::State & s) const546 updFiberVelocityInfo(const SimTK::State& s) const
547 {
548     return updCacheVariableValue<FiberVelocityInfo>(s, "velInfo");
549 }
550 
551 const Muscle::MuscleDynamicsInfo& Muscle::
getMuscleDynamicsInfo(const SimTK::State & s) const552 getMuscleDynamicsInfo(const SimTK::State& s) const
553 {
554     if(!isCacheVariableValid(s,"dynamicsInfo")){
555         MuscleDynamicsInfo& umdi = updMuscleDynamicsInfo(s);
556         calcMuscleDynamicsInfo(s, umdi);
557         markCacheVariableValid(s,"dynamicsInfo");
558         // don't bother fishing it out of the cache since
559         // we just calculated it and still have a handle on it
560         return umdi;
561     }
562     return getCacheVariableValue<MuscleDynamicsInfo>(s, "dynamicsInfo");
563 }
564 Muscle::MuscleDynamicsInfo& Muscle::
updMuscleDynamicsInfo(const SimTK::State & s) const565 updMuscleDynamicsInfo(const SimTK::State& s) const
566 {
567     return updCacheVariableValue<MuscleDynamicsInfo>(s, "dynamicsInfo");
568 }
569 
570 const Muscle::MusclePotentialEnergyInfo& Muscle::
getMusclePotentialEnergyInfo(const SimTK::State & s) const571 getMusclePotentialEnergyInfo(const SimTK::State& s) const
572 {
573     if(!isCacheVariableValid(s,"potentialEnergyInfo")){
574         MusclePotentialEnergyInfo& umpei = updMusclePotentialEnergyInfo(s);
575         calcMusclePotentialEnergyInfo(s, umpei);
576         markCacheVariableValid(s,"potentialEnergyInfo");
577         // don't bother fishing it out of the cache since
578         // we just calculated it and still have a handle on it
579         return umpei;
580     }
581     return getCacheVariableValue<MusclePotentialEnergyInfo>(s, "potentialEnergyInfo");
582 }
583 
584 Muscle::MusclePotentialEnergyInfo& Muscle::
updMusclePotentialEnergyInfo(const SimTK::State & s) const585 updMusclePotentialEnergyInfo(const SimTK::State& s) const
586 {
587     return updCacheVariableValue<MusclePotentialEnergyInfo>(s, "potentialEnergyInfo");
588 }
589 
590 
591 
592 //_____________________________________________________________________________
593 /**
594  * Get the stress in this muscle actuator.  It is calculated as the force
595  * divided by the maximum isometric force (which is proportional to its area).
596  */
getStress(const SimTK::State & s) const597 double Muscle::getStress(const SimTK::State& s) const
598 {
599     return getActuation(s) / getMaxIsometricForce();
600 }
601 
602 
603 //=============================================================================
604 // CALCULATIONS
605 //=============================================================================
606 /* calculate muscle's position related values such fiber and tendon lengths,
607     normalized lengths, pennation angle, etc... */
calcMuscleLengthInfo(const SimTK::State & s,MuscleLengthInfo & mli) const608 void Muscle::calcMuscleLengthInfo(const SimTK::State& s, MuscleLengthInfo& mli) const
609 {
610     throw Exception("ERROR- "+getConcreteClassName()
611         + "::calcMuscleLengthInfo() NOT IMPLEMENTED.");
612 }
613 
614 /* calculate muscle's velocity related values such fiber and tendon velocities,
615     normalized velocities, pennation angular velocity, etc... */
calcFiberVelocityInfo(const SimTK::State & s,FiberVelocityInfo & fvi) const616 void Muscle::calcFiberVelocityInfo(const SimTK::State& s, FiberVelocityInfo& fvi) const
617 {
618     throw Exception("ERROR- "+getConcreteClassName()
619         + "::calcFiberVelocityInfo() NOT IMPLEMENTED.");
620 }
621 
622 /* calculate muscle's active and passive force-length, force-velocity,
623     tendon force, relationships and their related values */
calcMuscleDynamicsInfo(const SimTK::State & s,MuscleDynamicsInfo & mdi) const624 void Muscle::calcMuscleDynamicsInfo(const SimTK::State& s, MuscleDynamicsInfo& mdi) const
625 {
626     throw Exception("ERROR- "+getConcreteClassName()
627         + "::calcMuscleDynamicsInfo() NOT IMPLEMENTED.");
628 }
629 
630 /* calculate muscle's fiber and tendon potential energy */
calcMusclePotentialEnergyInfo(const SimTK::State & s,MusclePotentialEnergyInfo & mpei) const631 void Muscle::calcMusclePotentialEnergyInfo(const SimTK::State& s,
632     MusclePotentialEnergyInfo& mpei) const
633 {
634     throw Exception("ERROR- "+getConcreteClassName()
635         + "::calcMusclePotentialEnergyInfo() NOT IMPLEMENTED.");
636 }
637 
638 
639 //=============================================================================
640 // Required by CMC and Static Optimization
641 //=============================================================================
calcInextensibleTendonActiveFiberForce(SimTK::State & s,double activation) const642 double Muscle::calcInextensibleTendonActiveFiberForce(SimTK::State& s,
643                                                   double activation) const
644 {
645     const MuscleLengthInfo& mli = getMuscleLengthInfo(s);
646     const FiberVelocityInfo& fvi = getFiberVelocityInfo(s);
647     return getMaxIsometricForce()*activation*
648         mli.fiberActiveForceLengthMultiplier*fvi.fiberForceVelocityMultiplier
649         *mli.cosPennationAngle;
650 }
651 
652 //=============================================================================
653 // FORCE APPLICATION
654 //=============================================================================
655 //_____________________________________________________________________________
656 /**
657  * Apply the muscle's force at its points of attachment to the bodies.
658  */
computeForce(const SimTK::State & s,SimTK::Vector_<SimTK::SpatialVec> & bodyForces,SimTK::Vector & generalizedForces) const659 void Muscle::computeForce(const SimTK::State& s,
660                           SimTK::Vector_<SimTK::SpatialVec>& bodyForces,
661                           SimTK::Vector& generalizedForces) const
662 {
663     // This calls compute actuation.
664     Super::computeForce(s, bodyForces, generalizedForces);
665 
666     if (getDebugLevel() < 0) return;
667     // NOTE: Actuation could be negative, in particular during CMC, when the
668     // optimizer is computing gradients, but in those cases the actuation will
669     // be overridden and will not be computed by the muscle.
670     if (!isActuationOverridden(s) && (getActuation(s) < -SimTK::SqrtEps)) {
671         string msg = getConcreteClassName()
672             + "::computeForce, muscle "+ getName() + " force < 0";
673         cout << msg << " at time = " << s.getTime() << endl;
674         //throw Exception(msg);
675     }
676 }
677 
computePotentialEnergy(const SimTK::State & s) const678 double Muscle::computePotentialEnergy(const SimTK::State& s) const
679 {
680     const MusclePotentialEnergyInfo& mpei = getMusclePotentialEnergyInfo(s);
681     return mpei.musclePotentialEnergy;
682 }
683 
computePathColor(const SimTK::State & state) const684 SimTK::Vec3 Muscle::computePathColor(const SimTK::State& state) const {
685     const double activation =
686         SimTK::clamp(0., getActivation(state), 1.);
687     const SimTK::Vec3 color(activation, 0, 1-activation); // blue to red
688     return color;
689 }
690 
691 
updateGeometry(const SimTK::State & s)692 void Muscle::updateGeometry(const SimTK::State& s)
693 {
694     updGeometryPath().updateGeometry(s);
695 }
696 
697 
698 //_____________________________________________________________________________
699 /**
700  * Utility function to calculate the current pennation angle in a
701  * muscle. Pennation angle increases as muscle fibers shorten. The implicit
702  * modeling assumption is that muscles have constant width.
703  *
704  * @param s State
705  * @return Current pennation angle (in radians).
706  */
707 /*
708  double Muscle::calcPennationAngle(const SimTK::State &s) const
709 {
710     double aFiberLength = getFiberLength(s);
711     if (aFiberLength < SimTK::Eps){
712         cout << "Muscle::calcPennationAngle() ERROR- fiber length is zero." << endl;
713         return SimTK::NaN;
714     }
715 
716     double value = _muscleWidth/aFiberLength;
717 
718     if(value >= 1.0){
719         cout << "Muscle::calcPennationAngle() ERROR- pennation at 90 degrees." << endl;
720         return SimTK_PI/2.0;
721     }
722    else
723       return asin(value);
724 }
725 */
726