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