1 /* -------------------------------------------------------------------------- *
2  *                        OpenSim:  OpenSimContext.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  *                                                                            *
12  * Licensed under the Apache License, Version 2.0 (the "License"); you may    *
13  * not use this file except in compliance with the License. You may obtain a  *
14  * copy of the License at http://www.apache.org/licenses/LICENSE-2.0.         *
15  *                                                                            *
16  * Unless required by applicable law or agreed to in writing, software        *
17  * distributed under the License is distributed on an "AS IS" BASIS,          *
18  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
19  * See the License for the specific language governing permissions and        *
20  * limitations under the License.                                             *
21  * -------------------------------------------------------------------------- */
22 #include <OpenSim/Common/Exception.h>
23 #include <OpenSim/Common/Function.h>
24 #include <OpenSim/Common/MarkerData.h>
25 #include <OpenSim/Simulation/SimbodyEngine/Body.h>
26 #include <OpenSim/Simulation/SimbodyEngine/Coordinate.h>
27 #include <OpenSim/Simulation/SimbodyEngine/TransformAxis.h>
28 #include <OpenSim/Simulation/Model/Marker.h>
29 #include <OpenSim/Simulation/Model/Model.h>
30 #include <OpenSim/Simulation/Model/MovingPathPoint.h>
31 #include <OpenSim/Simulation/Model/Muscle.h>
32 #include <OpenSim/Simulation/Model/PathPoint.h>
33 #include <OpenSim/Simulation/Wrap/PathWrap.h>
34 #include <OpenSim/Simulation/Model/ConditionalPathPoint.h>
35 #include <OpenSim/Simulation/SimbodyEngine/SimbodyEngine.h>
36 #include <OpenSim/Simulation/SimbodyEngine/TransformAxis.h>
37 #include <OpenSim/Simulation/Wrap/WrapObject.h>
38 #include <OpenSim/Simulation/Model/Analysis.h>
39 #include <OpenSim/Simulation/Model/MarkerSet.h>
40 #include <OpenSim/Tools/InverseKinematicsTool.h>
41 #include <OpenSim/Tools/AnalyzeTool.h>
42 #include <OpenSim/Tools/ModelScaler.h>
43 #include <OpenSim/Tools/Measurement.h>
44 #include <OpenSim/Tools/MarkerPlacer.h>
45 #include <OpenSim/Simulation/Model/ForceSet.h>
46 #include "OpenSimContext.h"
47 
48 namespace OpenSim {
49 
OpenSimContext(SimTK::State * s,Model * model)50 OpenSimContext::OpenSimContext( SimTK::State* s, Model* model ) {
51     _configState.reset(s);
52     _model.reset(model);
53 }
54 
55 
56 // Transforms
transformPosition(const PhysicalFrame & body,double * offset,double * gOffset)57 void OpenSimContext::transformPosition(const PhysicalFrame& body, double* offset, double* gOffset) {
58     _model->getMultibodySystem().realize(*_configState, SimTK::Stage::Position);
59     SimTK::Vec3::updAs(gOffset) =
60         body.findStationLocationInGround(*_configState, SimTK::Vec3(offset));
61 }
62 
getTransform(const PhysicalFrame & body)63 SimTK::Transform OpenSimContext::getTransform(const PhysicalFrame& body) { // Body Should be made const
64      _model->getMultibodySystem().realize(*_configState, SimTK::Stage::Position);
65      return body.getTransformInGround(*_configState);
66 }
67 
transform(const PhysicalFrame & ground,double * d,PhysicalFrame & body,double * dragVectorBody)68 void OpenSimContext::transform(const PhysicalFrame& ground, double* d, PhysicalFrame& body, double* dragVectorBody) {
69     _model->getMultibodySystem().realize(*_configState, SimTK::Stage::Position);
70     SimTK::Vec3::updAs(dragVectorBody) =
71         ground.expressVectorInAnotherFrame(*_configState, SimTK::Vec3(d), body);
72     return;
73 }
74 
75 
76 // Coordinates
getValue(const Coordinate & coord)77 double OpenSimContext::getValue(const Coordinate& coord) {
78         return( coord.getValue( *_configState));
79 }
80 
getLocked(const Coordinate & coord)81 bool OpenSimContext::getLocked(const Coordinate& coord) {
82     return coord.getLocked(*_configState);
83 }
setValue(const Coordinate & coord,double d,bool enforceConstraints)84 void OpenSimContext::setValue(const Coordinate& coord, double d, bool enforceConstraints) {
85   coord.setValue(*_configState, d, enforceConstraints);
86     return;
87 }
88 
setClamped(Coordinate & coord,bool newValue)89 void OpenSimContext::setClamped(Coordinate&  coord, bool newValue) {
90    coord.setDefaultClamped(newValue);
91    recreateSystemKeepStage();
92    return;
93 }
94 
getClamped(const Coordinate & coord)95 bool OpenSimContext::getClamped(const Coordinate& coord) {
96   return coord.getClamped(*_configState);
97 }
98 
setLocked(Coordinate & coord,bool newValue)99 void OpenSimContext::setLocked(Coordinate& coord, bool newValue) {
100    coord.setDefaultValue(getValue(coord));
101    coord.setDefaultLocked(newValue);
102    recreateSystemKeepStage();
103    return;
104 }
105 
isPrescribed(const Coordinate & coord) const106 bool OpenSimContext::isPrescribed(const Coordinate& coord) const {
107   return (coord.isPrescribed(*_configState));
108 }
109 
isConstrained(const Coordinate & coord) const110 bool OpenSimContext::isConstrained(const Coordinate& coord) const {
111   return (coord.isDependent(*_configState));
112 }
113 
114 // Muscles
getActivation(Muscle & m)115 double OpenSimContext::getActivation(Muscle& m) {
116   // realize to dynamics as required by new muscle models before asking for activation
117   _model->getMultibodySystem().realize(*_configState, SimTK::Stage::Dynamics);
118   return m.getActivation(*_configState);
119 }
120 
getMuscleLength(Muscle & m)121 double OpenSimContext::getMuscleLength(Muscle& m) {
122   return m.getLength(*_configState);
123 }
124 
getCurrentPath(Muscle & m)125 const Array<AbstractPathPoint*>& OpenSimContext::getCurrentPath(Muscle& m) {
126   return m.getGeometryPath().getCurrentPath(*_configState);
127 }
128 
copyMuscle(Muscle & from,Muscle & to)129 void OpenSimContext::copyMuscle(Muscle& from, Muscle& to) {
130   to = from;
131   recreateSystemKeepStage();
132   to.updGeometryPath().updateGeometry(*_configState);
133 }
134 
135 // Muscle Points
setXFunction(MovingPathPoint & mmp,Function & newFunction)136 void OpenSimContext::setXFunction(MovingPathPoint& mmp, Function& newFunction) {
137     mmp.set_x_location(newFunction );
138     recreateSystemKeepStage();
139    return;
140 }
141 
setYFunction(MovingPathPoint & mmp,Function & newFunction)142 void OpenSimContext::setYFunction(MovingPathPoint& mmp, Function& newFunction) {
143     mmp.set_y_location(newFunction );
144     recreateSystemKeepStage();
145     return;
146 }
147 
setZFunction(MovingPathPoint & mmp,Function & newFunction)148 void OpenSimContext::setZFunction(MovingPathPoint& mmp, Function& newFunction) {
149     mmp.set_z_location(newFunction );
150     recreateSystemKeepStage();
151     return;
152 }
153 
setXCoordinate(MovingPathPoint & mmp,Coordinate & newCoord)154 void OpenSimContext::setXCoordinate(MovingPathPoint& mmp, Coordinate&  newCoord) {
155     mmp.setXCoordinate(newCoord);
156     recreateSystemKeepStage();
157     return;
158 }
159 
setYCoordinate(MovingPathPoint & mmp,Coordinate & newCoord)160 void OpenSimContext::setYCoordinate(MovingPathPoint& mmp, Coordinate&  newCoord) {
161     mmp.setYCoordinate( newCoord );
162     recreateSystemKeepStage();
163     return;
164 }
165 
setZCoordinate(MovingPathPoint & mmp,Coordinate & newCoord)166 void OpenSimContext::setZCoordinate(MovingPathPoint& mmp, Coordinate&  newCoord) {
167     mmp.setZCoordinate( newCoord );
168     recreateSystemKeepStage();
169     return;
170 }
171 
setBody(AbstractPathPoint & pathPoint,PhysicalFrame & newBody)172 void OpenSimContext::setBody(AbstractPathPoint& pathPoint, PhysicalFrame&  newBody)
173 {
174     PathPoint* spp = dynamic_cast<PathPoint*>(&pathPoint);
175     if (spp) {
176         spp->changeBodyPreserveLocation(*_configState, newBody);
177         this->recreateSystemAfterSystemExists();
178         realizeVelocity();
179         return;
180     }
181     MovingPathPoint* mpp = dynamic_cast<MovingPathPoint*>(&pathPoint);
182     if (mpp) {
183         mpp->setParentFrame(newBody);
184         this->recreateSystemAfterSystemExists();
185         realizeVelocity();
186     }
187 }
188 
189 
190 //-------------------------------------------------------------------
recreateSystemAfterSystemExistsKeepStage()191 void OpenSimContext::recreateSystemAfterSystemExistsKeepStage( )
192 {
193    SimTK::Stage stageBeforeRecreatingSystem = _configState->getSystemStage();
194    this->recreateSystemAfterSystemExists();
195   _model->getMultibodySystem().realize( *_configState, stageBeforeRecreatingSystem );
196 }
197 
198 //-------------------------------------------------------------------
recreateSystemAfterSystemExists()199 void OpenSimContext::recreateSystemAfterSystemExists( )
200 {
201   SimTK::Vector y1 = _configState->getY();
202   SimTK::State* newState = &_model->initSystem();
203   newState->updY() = y1;
204   this->setState( newState );
205 }
206 
207 
setCoordinate(ConditionalPathPoint & via,Coordinate & newCoord)208 void OpenSimContext::setCoordinate(ConditionalPathPoint&  via, Coordinate&  newCoord) {
209     via.setCoordinate( newCoord );
210     recreateSystemKeepStage();
211     return;
212 }
213 
setRangeMin(ConditionalPathPoint & via,double d)214 void OpenSimContext::setRangeMin(ConditionalPathPoint&  via, double d) {
215      via.setRangeMin( d );
216      recreateSystemKeepStage();
217      return;
218 }
219 
setRangeMax(ConditionalPathPoint & via,double d)220 void OpenSimContext::setRangeMax(ConditionalPathPoint&  via, double d) {
221     via.setRangeMax( d );
222     recreateSystemKeepStage();
223     return;
224 }
225 
replacePathPoint(GeometryPath & p,AbstractPathPoint & mp,AbstractPathPoint & newPoint)226 bool OpenSimContext::replacePathPoint(GeometryPath& p, AbstractPathPoint& mp, AbstractPathPoint& newPoint) {
227    bool ret= p.replacePathPoint(*_configState, &mp, &newPoint );
228    recreateSystemAfterSystemExists();
229    realizeVelocity();
230    p.updateGeometry(*_configState);
231    return ret;
232 }
233 
setLocation(PathPoint & mp,int i,double d)234 void OpenSimContext::setLocation(PathPoint& mp, int i, double d) {
235     SimTK::Vec3 loc = mp.getLocation(*_configState);
236     loc[i] = d;
237     mp.setLocation(loc);
238     recreateSystemKeepStage();
239 }
240 
setLocation(PathPoint & mp,const SimTK::Vec3 & newLocation)241 void OpenSimContext::setLocation(PathPoint& mp, const SimTK::Vec3& newLocation) {
242     mp.setLocation(newLocation);
243     recreateSystemKeepStage();
244 }
245 
setEndPoint(PathWrap & mw,int newEndPt)246 void OpenSimContext::setEndPoint(PathWrap& mw, int newEndPt) {
247     mw.setEndPoint(*_configState, newEndPt );
248     recreateSystemKeepStage();
249     return;
250 }
251 
addPathPoint(GeometryPath & p,int menuChoice,PhysicalFrame & body)252 void OpenSimContext::addPathPoint(GeometryPath& p, int menuChoice, PhysicalFrame&  body) {
253     p.addPathPoint(*_configState, menuChoice, body );
254     recreateSystemKeepStage();
255     p.updateGeometry(*_configState);
256     return;
257 }
258 
deletePathPoint(GeometryPath & p,int menuChoice)259 bool OpenSimContext::deletePathPoint(GeometryPath& p, int menuChoice) {
260     bool deletedSuccessfully = p.deletePathPoint(*_configState, menuChoice );
261     if (deletedSuccessfully)
262         recreateSystemKeepStage();
263     return deletedSuccessfully;
264 }
265 
isActivePathPoint(AbstractPathPoint & mp)266 bool OpenSimContext::isActivePathPoint(AbstractPathPoint& mp) {
267   return mp.isActive(*_configState);
268 };
269 
270 //_____________________________________________________________________________
271 /**
272  * Replace one of the actuator's functions in the property array.
273  *
274  * @param aOldFunction the function being replaced.
275  * @param aNewFunction the new function.
276  */
replacePropertyFunction(OpenSim::Object & obj,OpenSim::Function * aOldFunction,OpenSim::Function * aNewFunction)277 void OpenSimContext::replacePropertyFunction(OpenSim::Object& obj, OpenSim::Function* aOldFunction, OpenSim::Function* aNewFunction)
278 {
279   if (aOldFunction && aNewFunction) {
280     PropertySet& propSet = obj.getPropertySet();
281 
282     for (int i=0; i <propSet.getSize(); i++) {
283       Property_Deprecated* prop = propSet.get(i);
284       if (prop->getType() == Property_Deprecated::ObjPtr) {
285         if (prop->getValueObjPtr() == aOldFunction) {
286           prop->setValue(aNewFunction);
287         }
288       }
289     }
290   }
291 }
292 
293 // Muscle Wrapping
setStartPoint(PathWrap & mw,int newStartPt)294 void OpenSimContext::setStartPoint(PathWrap& mw, int newStartPt) {
295      mw.setStartPoint(*_configState, newStartPt );
296      return;
297 }
298 
addPathWrap(GeometryPath & p,WrapObject & awo)299 void OpenSimContext::addPathWrap(GeometryPath& p, WrapObject& awo) {
300     p.addPathWrap( awo );
301     // Adding WrapObject to a GeometryPath requires initSystem similar to other Path edit operations
302     recreateSystemKeepStage();
303     p.updateGeometry(*_configState);
304     return;
305 }
306 
moveUpPathWrap(GeometryPath & p,int num)307 void OpenSimContext::moveUpPathWrap(GeometryPath& p, int num) {
308     p.moveUpPathWrap( *_configState, num );
309     recreateSystemKeepStage();
310     p.updateGeometry(*_configState);
311     return;
312 }
313 
moveDownPathWrap(GeometryPath & p,int num)314 void OpenSimContext::moveDownPathWrap(GeometryPath& p, int num) {
315     p.moveDownPathWrap( *_configState, num );
316     recreateSystemKeepStage();
317     p.updateGeometry(*_configState);
318     return;
319 }
320 
deletePathWrap(GeometryPath & p,int num)321 void OpenSimContext::deletePathWrap(GeometryPath& p, int num) {
322     p.deletePathWrap( *_configState, num );
323     recreateSystemKeepStage();
324     p.updateGeometry(*_configState);
325     return;
326 }
327 
328 // Markers
setBody(Marker & currentMarker,PhysicalFrame & newBody,bool b)329 void OpenSimContext::setBody(Marker& currentMarker, PhysicalFrame&  newBody, bool b) {
330     if( b ) {
331          currentMarker.changeFramePreserveLocation( *_configState, newBody );
332     } else {
333          currentMarker.changeFrame( newBody );
334     }
335     return;
336 }
337 
updateMarkerSet(Model & model,MarkerSet & aMarkerSet)338 void OpenSimContext::updateMarkerSet(Model& model, MarkerSet& aMarkerSet) {
339    model.updateMarkerSet(aMarkerSet);
340    recreateSystemKeepStage();
341 }
342 
343 // Analyses
step(Analysis & analysis)344 int OpenSimContext::step(Analysis& analysis)
345 {
346   return analysis.step( *_configState, 0);
347 }
348 
349 // Tools
350 /*
351 bool OpenSimContext::initializeTrial(IKTool& ikTool, int i) {
352   return ikTool.initializeTrial(*_configState, i);
353 }
354 */
solveInverseKinematics(InverseKinematicsTool & ikTool)355 bool OpenSimContext::solveInverseKinematics( InverseKinematicsTool& ikTool) {
356   return ikTool.run();
357 }
358 
setStatesFromMotion(AnalyzeTool & analyzeTool,const Storage & aMotion,bool aInDegrees)359 void OpenSimContext::setStatesFromMotion(AnalyzeTool& analyzeTool, const Storage &aMotion, bool aInDegrees) {
360   analyzeTool.setStatesFromMotion(*_configState, aMotion, aInDegrees);
361 }
362 
loadStatesFromFile(AnalyzeTool & analyzeTool)363 void OpenSimContext::loadStatesFromFile(AnalyzeTool& analyzeTool) {
364   analyzeTool.loadStatesFromFile(*_configState);
365 }
366 
processModelScale(ModelScaler & modelScaler,Model * aModel,const std::string & aPathToSubject,double aFinalMass)367 bool OpenSimContext::processModelScale(ModelScaler& modelScaler,
368                      Model* aModel,
369                      const std::string& aPathToSubject,
370                      double aFinalMass) {
371   aModel->getMultibodySystem().realizeTopology();
372     _configState.reset(&aModel->updWorkingState());
373   bool retValue= modelScaler.processModel(aModel, aPathToSubject, aFinalMass);
374   // Model has changed need to recreate a valid state
375   aModel->getMultibodySystem().realizeTopology();
376     _configState.reset(&aModel->updWorkingState());
377   aModel->getMultibodySystem().realize(*_configState, SimTK::Stage::Position);
378   return retValue;
379 }
380 
processModelMarkerPlacer(MarkerPlacer & markerPlacer,Model * aModel,const std::string & aPathToSubject)381 bool OpenSimContext::processModelMarkerPlacer( MarkerPlacer& markerPlacer,
382                         Model* aModel,
383                         const std::string& aPathToSubject) {
384   return markerPlacer.processModel(aModel, aPathToSubject);
385 }
386 
computeMeasurementScaleFactor(ModelScaler & modelScaler,const Model & aModel,const MarkerData & aMarkerData,const Measurement & aMeasurement) const387 double OpenSimContext::computeMeasurementScaleFactor(ModelScaler& modelScaler,
388                            const Model& aModel,
389                            const MarkerData& aMarkerData,
390                            const Measurement& aMeasurement) const {
391   return modelScaler.computeMeasurementScaleFactor(*_configState, aModel, aMarkerData, aMeasurement);
392 }
393 
replaceTransformAxisFunction(TransformAxis & aDof,OpenSim::Function & aFunction)394 void OpenSimContext::replaceTransformAxisFunction(TransformAxis& aDof, OpenSim::Function& aFunction) {
395    aDof.setFunction(&aFunction);
396    this->recreateSystemAfterSystemExists();
397    realizeVelocity();
398 }
399 
400 // Force re-realization
realizePosition()401 void OpenSimContext::realizePosition() {
402   _model->getMultibodySystem().realize(*_configState, SimTK::Stage::Position);
403 
404 }
realizeVelocity()405 void OpenSimContext::realizeVelocity() {
406   _model->getMultibodySystem().realize(*_configState, SimTK::Stage::Velocity);
407 
408 }
409 
cacheModelAndState()410 void OpenSimContext::cacheModelAndState()
411 {
412     clonedModel.reset(_model->clone());
413     clonedState = this->getCurrentStateCopy();
414 }
415 
restoreStateFromCachedModel()416 void OpenSimContext::restoreStateFromCachedModel()
417 {
418     _model->initSystem();
419     clonedModel->initSystem();
420 
421     Array<std::string> modelVariableNames = _model->getStateVariableNames();
422     Array<std::string> clonedModelVariableNames = clonedModel->getStateVariableNames();
423 
424     for(int i = 0; i < modelVariableNames.getSize(); i++)
425     {
426         std::string name = modelVariableNames.get(i);
427         if(clonedModelVariableNames.findIndex(name) >= 0)
428         {
429             double value = clonedModel->getStateVariableValue(clonedState, name);
430             _model->setStateVariableValue(_model->updWorkingState(), name, value);
431         }
432     }
433     this->setState(&(_model->updWorkingState()));
434     this->realizePosition();
435 }
436 
setSocketConnecteePath(AbstractSocket & socket,const std::string & componentPathName)437 void OpenSimContext::setSocketConnecteePath(AbstractSocket& socket,
438                                    const std::string& componentPathName) {
439     // Since some socket changes can form an invalid system
440     // we will make the change in a more conservative manner, by:
441     // 1. Making clone of this OpenSimContext's _model and State
442     // 2. Find the socket in the cloned model and apply the change to it
443     // 3. If successful, change is safe, continue with edit on socket
444     // 4. If failure, then exception is thrown and we return and model is
445     //    unchanged.
446     cacheModelAndState();
447 
448     const Component& comp = socket.getOwner();
449     Component& componentInClone = clonedModel->updComponent(comp.getAbsolutePath());
450     auto& clonesocket = componentInClone.updSocket(socket.getName());
451     clonesocket.setConnecteePath(componentPathName);
452     // The following line either succeeds or throws, if the latter happens then
453     // neither model or socket are changed and the message will be caught by GUI
454     try {
455         clonedModel->initSystem();
456     }
457     catch(const std::exception& ex) {
458         std::string message = "Unable to connect Socket<"
459             + socket.getConnecteeTypeName() + "> '" + socket.getName() +
460             "' to Component '" + componentPathName + "'.\n Reason: " +
461             ex.what();
462         throw OpenSim::Exception(message);
463     }
464     // if we made it to this line then the change is safe, redo in actual model/comp/socket
465     socket.disconnect();
466     socket.setConnecteePath(componentPathName);
467     restoreStateFromCachedModel();
468 }
469 } // namespace
470