Closed adamkewley closed 2 months ago
The rollout if now almost complete, and it's compiling + passing on mac + ubuntu. The Windows issue is external (CI images were updated and now there's warnings/errors - see #3895)
testMocoInverse
seems to be broken on Windows (but is otherwise fine on Linux+Mac O_o) - I'm assuming a slight numeric difference has been introduced by the rewrite of GeometryPath::implProduceForces
(it was cleaned up from addInEquivalentForces
).
After reverting the GeometryPath
implementation to basically be exactly the same as the existing addInEquivalentForces
, but now using the ForceConsumer
API, I can get testMocoInverse
to pass - it's probably a numeric instability that needs to be carefully handled in a separate PR (cleaning up that part of the code would be useful, in general, for point-force visualization).
The build is working, test passing etc. with the reversion - I'll tackle it in a separate PR.
I have also added a test that compares the Force
API mutations to the ForceConsumer
API mutations to ensure that they are like-for-like - and actually edit the force vectors.
I'll un-WIP this and try to wrangle someone for a review.
When self-reviewing the diff I found a few things that need to be re-thought:
appliesForce
member from Force
should only be checked by ForceApplier
. ForceProducer
s should unconditionally produce their forces when directly requested for them. If there's a desire to disable the ability to even ask a ForceProducer
to produce its forces (side-effect free), then a new property called producesForces
should be added. This is to discriminate between "produces forces into a consumer" from "applies forces to a multibody system"Force
API changes (protected
--> public
) aren't strictly necessary because ForceApplier
directly uses the SimbodyMattterSubsystem
API.Ok, now it's ready for review :wink:
Thanks for reviewing, @pepbos
@nickbianco - this PR also impacts the AbstractGeometryPath
API slightly (it not "produces" forces rather than addInEquivalentForces
). Is that an acceptable change (I've ported FunctionBasedPath
etc. to make it compatible).
If so, and if there's no further review comments, I'll go ahead and merge this
I think I'm going to go ahead and merge this as-is: the use of raw pointer members is limited to private variables and can be refactored later if there's long term maintenance concerns (which there might be - I'm still undecided on the best approach), and the pointer-based constructor, which is more explicit at the cost of having a more C/C++ey feel, can always be overridden with an additional reference-based constructor if it becomes a pain point later on.
Just FYI, I plan on integrating the API immediately into the dev (main
) branch of OSC so that I can start visualizing force vectors etc. using the API - I imagine that doing so will probably kick up some circumstances that require additional PRs to fix. For example, GeometryPath
is producing two tension force vectors per path point - I anticipate that it's nicer to visualize the resultant vector, which would require rewriting that function a little bit. I also anticipate similar changes might be nice in other force components (e.g. contact forces would be lovely to visualize), but I don't know which/where until I start attaching a visualizer to the API.
Cheers to @nickbianco and @pepbos for reviewing this.
This PR adds
ForceProducer
,ForceConsumer
, andForceApplier
and rolls them out to as manyOpenSim::Force
implementations as feasible.Overview
ForceProducer
is an abstractOpenSim::Force
that downstream classes can use, rather thanForce
, to emit their forces one-by-one to aForceConsumer
.ForceConsumer
is an abstract class with consumption methods (e.g.consumePointForce
) that directly mirror the ofOpenSim::Force
equivalents (e.g.Force::applyForceToPoint
).ForceApplier
is aForceConsumer
that applies each consumed force to aSimTK::Vector_<SimTK::SpatialForce>
of body forces and aSimTK::Vector
of mobility (generalized) forces.Key features:
ForceProducer
's virtual one-by-one API is in contrast toOpenSim::Force
, where forces are added by directly modifying the physics engine'sbodyForces
andgeneralizedForces
vectors in-place. The benefit of this is that it decouples "force production/consumption" from "force application", which lets not-simbody (e.g. UIs) use the force API more easily.ForceConsumer
's API is designed with the similar naming, the same arguments, and the same spatial (e.g. torques in ground) requirements as the equivalentOpenSim::Force
API methods. This makes it easier to portOpenSim::Force
code to theForceConsumer
API. TheForceConsumer
API also separates "body forces" from "point forces", so that concreteForceProducers
are able to emit point-force information to other systems (e.g. debuggers that would like to print point forces).ForceProducer
's API provides a default implementation ofOpenSim::Force::computeForce
, which internally usesForceApplier
apply the one-at-a-time forces to the vectors thatOpenSim::Force::computeForce
uses. This makesForceProducer
backwards-compatible with any existing code that handlesOpenSim::Force
s.AbstractGeometryPath
.The
ForceConsumer
APIThe
ForceConsumer
API is an abstract class that has a public API that consumes four types of forces:Concrete implementations of
ForceConsumer
may override each of the correspondingvirtual void implConsume*
methods in order to consume that type of force. The default implementation of eachimplConsume*
function does nothing (e.g. it ignores the force). A concrete implementation does not have to do anything with a force, which enables using the API to (e.g.) implement force debuggers, force visualizers, point-force reporters, and so on.The
ForceProducer
APIThe
ForceProducer
API is an abstract class that exposesproduceForces
:Concrete implementations can then inherit from
ForceProducer
and supply animplProduceForces
in which the concrete implementation calls (e.g.)ForceConsumer::consumePointForce
. The defaultForceProducer::computeForce
implementation usesForceApplier
to satisfy theOpenSim::Force
API. It'soverride
d, rather thanfinal
d, because there is legacy code in OpenSim that does extra stuff aftercomputeForce
is called (e.g. resetting state variables). An ideal implementation wouldfinal
computeForce
.Motivation (why add this?)
The
OpenSim::Force
API isn't easy for downstream code, such as OpenSim Creator's, to hook into. Because theOpenSim::Force
API couples force generation/production with application. Because of this, downstream code has to:ForceAdaptor
to mutate those vectors.The
OpenSim::Force
API also erases point-force information. At theOpenSim::Force
layer of the API, all forces are already resolved into body forces and body torques. However, downstream code might want to visualize point-forces (e.g. when visualizingExternalForce
,PrescribedForce
, andGeometryPath
).Brief Summary of Changes
ForceProducer
ForceConsumer
ForceApplier
ForceProducer
ForceConsumer
andForceProducer
APIs to all existing OpenSim classes that were previously using theForce
API.opensim-core
can be queried using theForceProducer
APIForce
implementationsvirtual
AbstractGeometryPath::addInEquivalentForces
with avirtual
AbstractGeometryPath::produceForces
API:ForceProducer
s withAbstractGeometryPath
(e.g. necessary forPathActuator
and similar).AbstractGeometryPath::addInEquivalentForces
function that uses the newproduceForces
API internally.AbstractGeometryPath
. I consider this a minor breakage, because the API is very new, it's unlikely 3rd-party code is implementing it, and porting the implementation to useForceConsumer
is an easy change.ForceProducer
andForceConsumer
APIs to the SWIG bindingsNo changes were made to any existing test suites, binding tests, etc. This is to say, these changes should not have broken the existing functionality of the OpenSim API - apart from the virtual API change in
AbstractGeometryPath
.Testing I've completed
ForceProducer
to ensure it behaves as expectedForceApplier
(viaForceProducer::computeForce
) to the mutations caused by a "typical"Force
usage.Force
s were ported to this API. Any existing test suite that tests force components will also be indirectly testing this API (e.g. any tests testing muscle implementations are now indirectly exercising the new API).Looking for feedback on...
CHANGELOG.md
Updated.
This change is