diff --git a/Bindings/Java/Matlab/examples/Wrapping/exampleScholz2015GeometryPath.m b/Bindings/Java/Matlab/examples/Wrapping/exampleScholz2015GeometryPath.m index b8c902d06a..8401dc5609 100644 --- a/Bindings/Java/Matlab/examples/Wrapping/exampleScholz2015GeometryPath.m +++ b/Bindings/Java/Matlab/examples/Wrapping/exampleScholz2015GeometryPath.m @@ -78,6 +78,11 @@ function exampleScholz2015GeometryPath() % adding, it defines the insertion of the path. path.appendPathPoint(model.getBodySet().get('b1'), Vec3(-0.5, 0.1, 0.)); +% Enable warm starts in the wrapping solver. At each time step, the +% wrapping solver will use the solution from the previous time step as an +% initial guess for the current time step. +path.setUseWarmStart(true); + % Initialize the system. state = model.initSystem(); model.updVisualizer().updSimbodyVisualizer().setBackgroundTypeByInt(2); diff --git a/Bindings/Python/examples/Wrapping/exampleScholz2015GeometryPath.py b/Bindings/Python/examples/Wrapping/exampleScholz2015GeometryPath.py index 3ce684c558..e043f2e354 100644 --- a/Bindings/Python/examples/Wrapping/exampleScholz2015GeometryPath.py +++ b/Bindings/Python/examples/Wrapping/exampleScholz2015GeometryPath.py @@ -76,6 +76,11 @@ # adding, it defines the insertion of the path. path.appendPathPoint(model.getBodySet().get('b1'), osim.Vec3(-0.5, 0.1, 0.)) +# Enable warm starts in the wrapping solver. At each time step, the +# wrapping solver will use the solution from the previous time step as an +# initial guess for the current time step. +path.setUseWarmStart(True) + # Initialize the system. state = model.initSystem() model.updVisualizer().updSimbodyVisualizer().setBackgroundTypeByInt(2) diff --git a/CHANGELOG.md b/CHANGELOG.md index c411641ae1..5983d1f050 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -67,6 +67,7 @@ performance and stability in wrapping solutions. - Implemented `extendPreScale` and `extendPostScale` for `Scholz2015GeometryPath`. Now, scaling a model using `Scholz2015GeometryPath` paths will update `Muscle` tendon slack lengths and optimal fiber lengths. (#4325) - Fix crash deserializing xml files that are missing OpenSimDocument tags. (#4336) - Added `exampleExponentialContactForce`, including C++, Matlab, and Python variants. (#4318) +- Added the property `use_warm_start` and accessors `set/getWarmStart()` in `Scholz2015GeometryPath` to enable toggling on and off warm starts, where the wrapper solver from the previous time step as an initial guess for the path at the next time step. By default, `Scholz2015GeometryPath` now has warm starts disabled, meaning that paths are always computed from wrap obstacle contact hints (previously, warm starts were always enabled). (#4342) v4.5.2 diff --git a/OpenSim/Examples/Wrapping/exampleScholz2015GeometryPath.cpp b/OpenSim/Examples/Wrapping/exampleScholz2015GeometryPath.cpp index d62ae40fd2..60b05aefb7 100644 --- a/OpenSim/Examples/Wrapping/exampleScholz2015GeometryPath.cpp +++ b/OpenSim/Examples/Wrapping/exampleScholz2015GeometryPath.cpp @@ -82,6 +82,11 @@ int main() { path.appendPathPoint(model.getComponent
("/bodyset/b1"), SimTK::Vec3(-0.5, 0.1, 0.)); + // Enable warm starts in the wrapping solver. At each time step, the + // wrapping solver will use the solution from the previous time step as an + // initial guess for the current time step. + path.setUseWarmStart(true); + // Initialize the system. SimTK::State state = model.initSystem(); model.updVisualizer().updSimbodyVisualizer().setBackgroundType( diff --git a/OpenSim/Simulation/Model/Scholz2015GeometryPath.cpp b/OpenSim/Simulation/Model/Scholz2015GeometryPath.cpp index 8c292b7d9a..a0ce07a5f0 100644 --- a/OpenSim/Simulation/Model/Scholz2015GeometryPath.cpp +++ b/OpenSim/Simulation/Model/Scholz2015GeometryPath.cpp @@ -189,6 +189,17 @@ int Scholz2015GeometryPath::getNumPathElements() const { return getProperty_path_elements().size(); } +void Scholz2015GeometryPath::setUseWarmStart(bool useWarmStart) { + if (_index.isValid()) { + updCableSpan().setUseWarmStart(useWarmStart); + } + set_use_warm_start(useWarmStart); +} + +bool Scholz2015GeometryPath::getUseWarmStart() const { + return get_use_warm_start(); +} + //============================================================================= // ABSTRACT PATH INTERFACE //============================================================================= @@ -363,6 +374,7 @@ void Scholz2015GeometryPath::extendAddToSystem( cable.setCurveSegmentAccuracy(1e-10); cable.setSolverMaxIterations(50); cable.setAlgorithm(SimTK::CableSpanAlgorithm::Scholz2015); + cable.setUseWarmStart(get_use_warm_start()); _index = cable.getIndex(); } @@ -419,6 +431,7 @@ void Scholz2015GeometryPath::extendPostScale(const SimTK::State& s, //============================================================================= void Scholz2015GeometryPath::constructProperties() { constructProperty_path_elements(); + constructProperty_use_warm_start(false); } const Scholz2015GeometryPathObstacle* Scholz2015GeometryPath::getObstacle( diff --git a/OpenSim/Simulation/Model/Scholz2015GeometryPath.h b/OpenSim/Simulation/Model/Scholz2015GeometryPath.h index 162510a86a..b1a6f18957 100644 --- a/OpenSim/Simulation/Model/Scholz2015GeometryPath.h +++ b/OpenSim/Simulation/Model/Scholz2015GeometryPath.h @@ -162,8 +162,7 @@ OpenSim_DECLARE_CONCRETE_OBJECT(Scholz2015GeometryPathObstacle, * can touchdown on the obstacle if the surface obstructs the straight line * segment again. * - * The path is computed as an optimization problem using the previous optimal - * path as the warm start. This is done by computing natural geodesic + * The path is computed as an optimization problem by computing natural geodesic * corrections for each curve segment to compute the locally shortest path, * as described in the following publication: * @@ -172,7 +171,10 @@ OpenSim_DECLARE_CONCRETE_OBJECT(Scholz2015GeometryPathObstacle, * System Dynamics 36, 195–219. * * The overall path is locally the shortest, allowing winding over an obstacle - * multiple times, without flipping to the other side. + * multiple times, without flipping to the other side. The wrapping solver can + * optionally use the solution from the previous time step as a warm start to + * improve performance during forward dynamics simulations (see method + * `getUseWarmStart()` for more information). * * This class encapsulates `SimTK::CableSpan`, the Simbody implementation of * this algorithm. For the full details concerning this class, see the Simbody @@ -425,6 +427,28 @@ OpenSim_DECLARE_CONCRETE_OBJECT(Scholz2015GeometryPath, AbstractGeometryPath); */ int getNumPathElements() const; + /** + * Set whether the path uses the solution from the previous time step as a + * warm start for the wrapping solver. + * + * Enable this setting when performing forward dyanmics simulations or any + * simulation where path solutions will be computed sequentially in time + * reasonable small time steps. In other scenarios where the configuration + * of the model, and therefore path solutions, may change rapidly, it is + * recommended to disable this setting to avoid inconsistent path solutions. + * If false, the path will always be computed from the curve contact hints + * when realizing to SimTK::Stage::Position. Default: false. + */ + void setUseWarmStart(bool useWarmStart); + + /** + * Get whether the path uses the solution from the previous time step as a + * warm start for the wrapping solver. + * + * @see setUseWarmStart() + */ + bool getUseWarmStart() const; + // @} //** @name `AbstractGeometryPath` interface */ @@ -446,6 +470,11 @@ OpenSim_DECLARE_CONCRETE_OBJECT(Scholz2015GeometryPath, AbstractGeometryPath); // PROPERTIES OpenSim_DECLARE_LIST_PROPERTY(path_elements, Scholz2015GeometryPathElement, "The list of elements (path points or obstacles) defining the path."); + OpenSim_DECLARE_PROPERTY(use_warm_start, bool, + "Whether to use the path solution from the previous time step as a " + "warm start for the wrapping solver. If false, the path will always be " + "computed from the curve contact hints when realizing to " + "SimTK::Stage::Position. Default: false."); // MODEL COMPONENT INTERFACE void extendConnectToModel(Model& model) override; diff --git a/OpenSim/Tests/Wrapping/testScholz2015GeometryPath.cpp b/OpenSim/Tests/Wrapping/testScholz2015GeometryPath.cpp index 45a098d919..067f571112 100644 --- a/OpenSim/Tests/Wrapping/testScholz2015GeometryPath.cpp +++ b/OpenSim/Tests/Wrapping/testScholz2015GeometryPath.cpp @@ -23,6 +23,7 @@ #include