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 ¨i = 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