opensim-org / opensim-core

SimTK OpenSim C++ libraries and command-line applications, and Java/Python wrapping.
https://opensim.stanford.edu
Apache License 2.0
783 stars 316 forks source link

Calling setOverrideActuation invalidates cache variables at Stage::Position #2323

Open clnsmith opened 5 years ago

clnsmith commented 5 years ago

I am trying to compute the accelerations induced by a unit change in muscle force (similar to what is done in static optimization) at a fixed model posture (ie coordinates and speeds dont change). I have a custom component plugin in the model that implements an elastic foundation force. The EF force is cached at Stage::Position to avoid going through collision detection repeatedly if the position doesn't change.

However, calling setOverrideActuation is invalidating the EF cached variable, so in the following loop my collision detection is performed (in realize Acceleration) every iteration even though the position doesn't change.

If I comment out setOverrideActuation, then the EF cache variable isn't recalculated.

//Compute Accelerations due to unit change in activation
j = 0;
for (ScalarActuator &actuator : _model->updComponentList<ScalarActuator>()) {
    actuator.setOverrideActuation(s, parameters[j] * _optimalForce[j] * _parameter_scale[j] + 1.0);

    _model->realizeAcceleration(s);

    int i = 0;
    for (const Coordinate& coord : _model->updComponentList<Coordinate>()) {
            unit_udot(i, j) = coord.getAccelerationValue(s) - current_udot(i);
            i++;
        }

    actuator.setOverrideActuation(s, _optimalForce[j] * parameters[j] * _parameter_scale[j]);
    j++;
}
chrisdembia commented 5 years ago

How do you create your EF force cache variable(s)?

"override_actuation" is a discrete state variable that invalidates Stage::Time. Could you change the stage that it invalidates to Stage::Velocity (in Actuator.cpp).

Are you trying to compute the partial derivative of generalized accelerations with respect to muscle forces? If so, shouldn't your perturbation be small (like 1e-3)?

You can try using PathActuators instead of muscles (for the specific muscle whose sensitivity you're evaluating). Then you could avoid using override actuation.

clnsmith commented 5 years ago

Cache variable gets created inside the EF force component:

void WISCO_ElasticFoundationForce::extendAddToSystem(MultibodySystem& system) const { Super::extendAddToSystem(system); ... addCacheVariable("target_mesh.tri.pressure", target_mesh_def_vec, Stage::Position); ... }

Ok, I thought that must be the case for the "override_actuation" discrete variable. Are there any bad implications for changing the invalidation stage? Seems like it doesn't make sense for it to be set to Stage::Time?

Would I add a controller to use the Path Actuator without override actuation? Or is there a simpler way to do this?

Generalized accelerations vary linearly with muscle force at a fixed model pose, so in theory you can use whatever step size you want. I haven't actually checked this in practice though. See eq 3 in the CMC paper:

Thelen, D. G., Anderson, F. C., & Delp, S. L. (2003). Generating dynamic simulations of movement using computed muscle control. Journal of biomechanics, 36(3), 321-328.

chrisdembia commented 5 years ago

I cannot think of any negative side effects of changing the invalidation stage.

Generalized accelerations vary linearly with muscle force at a fixed model pose, so in theory you can use whatever step size you want.

Ah right. Well then I wonder if there is a more efficient way to compute the derivatives. I would suggest M^-1 * B, if B is the moment arm matrix, but I don't think this actually makes sense in OpenSim (because of how moment arms are computed).

By the way, I think _optimalForce for muscles is usually 1 (not max isometric force).

chrisdembia commented 5 years ago

It does seem that if controls are computed at the velocity stage, then override actuation need not invalidate all the way to the time stage.