1 /* -------------------------------------------------------------------------- *
2  *                        OpenSim:  SimbodyEngine.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  * Author(s): Frank C. Anderson, Ajay Seth                                    *
12  *                                                                            *
13  * Licensed under the Apache License, Version 2.0 (the "License"); you may    *
14  * not use this file except in compliance with the License. You may obtain a  *
15  * copy of the License at http://www.apache.org/licenses/LICENSE-2.0.         *
16  *                                                                            *
17  * Unless required by applicable law or agreed to in writing, software        *
18  * distributed under the License is distributed on an "AS IS" BASIS,          *
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
20  * See the License for the specific language governing permissions and        *
21  * limitations under the License.                                             *
22  * -------------------------------------------------------------------------- */
23 
24 //=============================================================================
25 // INCLUDES
26 //=============================================================================
27 #include <OpenSim/Common/Exception.h>
28 #include <OpenSim/Common/GCVSplineSet.h>
29 #include <OpenSim/Common/Storage.h>
30 #include <OpenSim/Common/LoadOpenSimLibrary.h>
31 #include <OpenSim/Simulation/Model/Model.h>
32 #include <OpenSim/Simulation/Model/MarkerSet.h>
33 #include <OpenSim/Simulation/Model/BodySet.h>
34 #include <OpenSim/Simulation/Model/PhysicalOffsetFrame.h>
35 
36 #include "SimbodyEngine.h"
37 #include "Coordinate.h"
38 
39 //=============================================================================
40 // STATICS
41 //=============================================================================
42 using namespace std;
43 using namespace OpenSim;
44 using namespace SimTK;
45 
46 //=============================================================================
47 // EXCEPTIONS
48 //=============================================================================
49 class PhysicalOffsetFrameIsInvalidArgument : public OpenSim::Exception {
50 public:
PhysicalOffsetFrameIsInvalidArgument(const std::string & file,size_t line,const std::string & func,const Object & obj)51     PhysicalOffsetFrameIsInvalidArgument(const std::string& file,
52         size_t line,
53         const std::string& func,
54         const Object& obj) :
55         Exception(file, line, func, obj) {
56         std::string msg = "Cannot use PhysicalOffsetFrame with ";
57         msg += "SimbodyEngine. Use methods from the Frame class instead.";
58         addMessage(msg);
59     }
60 };
61 
62 //=============================================================================
63 // CONSTRUCTOR(S) AND DESTRUCTOR
64 //=============================================================================
65 //_____________________________________________________________________________
66 /**
67  * Destructor.
68  */
~SimbodyEngine()69 SimbodyEngine::~SimbodyEngine()
70 {
71 }
72 //_____________________________________________________________________________
73 /**
74  * Default constructor.
75  */
SimbodyEngine()76 SimbodyEngine::SimbodyEngine() :
77     Object()
78 {
79     setNull();
80 }
81 
82 //_____________________________________________________________________________
83 /**
84  * Constructor from an XML Document
85  */
SimbodyEngine(const string & aFileName)86 SimbodyEngine::SimbodyEngine(const string &aFileName) :
87     Object(aFileName, false)
88 {
89     setNull();
90     connectSimbodyEngineToModel(*_model);
91 }
92 
93 
94 //_____________________________________________________________________________
95 /**
96  * Copy constructor.
97  */
SimbodyEngine(const SimbodyEngine & aEngine)98 SimbodyEngine::SimbodyEngine(const SimbodyEngine& aEngine) :
99     Object(aEngine)
100 {
101     setNull();
102     copyData(aEngine);
103     connectSimbodyEngineToModel(*_model);
104 }
105 
106 
107 //=============================================================================
108 // CONSTRUCTION METHODS
109 //=============================================================================
110 
111 
112 //_____________________________________________________________________________
113 /**
114  * Copy data members from one SimbodyEngine to another.
115  *
116  * @param aEngine SimbodyEngine to be copied.
117  */
copyData(const SimbodyEngine & aEngine)118 void SimbodyEngine::copyData(const SimbodyEngine &aEngine)
119 {
120     _model = aEngine._model;
121 }
122 
123 //_____________________________________________________________________________
124 /**
125  * Set NULL values for all the variable members of this class.
126  */
setNull()127 void SimbodyEngine::setNull()
128 {
129     setAuthors("Frank C. Anderson, Ajay Seth");
130     _model = NULL;
131 }
132 
133 
134 //_____________________________________________________________________________
135 /**
136  * Perform some set up functions that happen after the
137  * object has been deserialized or copied.
138  *
139  * @param aModel model containing this SimbodyEngine.
140  */
connectSimbodyEngineToModel(Model & aModel)141 void SimbodyEngine::connectSimbodyEngineToModel(Model& aModel)
142 {
143     _model = &aModel;
144 }
145 
146 
147 //=============================================================================
148 // OPERATORS
149 //=============================================================================
150 //_____________________________________________________________________________
151 /**
152  * Assignment operator.
153  *
154  * @return Reference to this object.
155  */
operator =(const SimbodyEngine & aEngine)156 SimbodyEngine& SimbodyEngine::operator=(const SimbodyEngine &aEngine)
157 {
158     // Base class
159     Object::operator=(aEngine);
160     copyData(aEngine);
161     connectSimbodyEngineToModel(*aEngine._model);
162     return(*this);
163 }
164 
165 
166 
167 //--------------------------------------------------------------------------
168 // COORDINATES
169 //--------------------------------------------------------------------------
170 
171 //_____________________________________________________________________________
172 /**
173  * Get the set of coordinates that are not locked
174  *
175  * @param rUnlockedCoordinates set of unlocked coordinates is returned here
176  */
getUnlockedCoordinates(const SimTK::State & s,CoordinateSet & rUnlockedCoordinates) const177 void SimbodyEngine::getUnlockedCoordinates(const SimTK::State &s, CoordinateSet& rUnlockedCoordinates) const
178 {
179     rUnlockedCoordinates.setSize(0);
180     rUnlockedCoordinates.setMemoryOwner(false);
181 
182     for (int i = 0; i < _model->getCoordinateSet().getSize(); i++)
183         if (!_model->getCoordinateSet().get(i).getLocked(s))
184             rUnlockedCoordinates.adoptAndAppend(&_model->getCoordinateSet().get(i));
185 }
186 
187 
188 
189 //--------------------------------------------------------------------------
190 // KINEMATICS
191 //--------------------------------------------------------------------------
192 //_____________________________________________________________________________
193 /**
194  * Get the inertial position of a point on a body.
195  *
196  * Note that the configuration of the model must be set before calling this
197  * method.
198  *
199  * @param aBody Pointer to body.
200  * @param aPoint Point on the body expressed in the body-local frame.
201  * @param rPos Position of the point in the inertial frame.
202  */
getPosition(const SimTK::State & s,const PhysicalFrame & aBody,const Vec3 & aPoint,Vec3 & rPos) const203 void SimbodyEngine::getPosition(const SimTK::State& s,
204         const PhysicalFrame& aBody, const Vec3& aPoint, Vec3& rPos) const
205 {
206     OPENSIM_THROW_IF_FRMOBJ(
207         dynamic_cast<const PhysicalOffsetFrame*>(&aBody),
208         PhysicalOffsetFrameIsInvalidArgument);
209 
210     rPos = aBody.getMobilizedBody().findStationLocationInGround(s, aPoint);
211 }
212 
213 //_____________________________________________________________________________
214 /**
215  * Get the inertial velocity of a point on a body.
216  *
217  * Note that the configuration of the model must be set before calling this
218  * method.
219  *
220  * @param aBody Pointer to body.
221  * @param aPoint Point on the body expressed in the body-local frame.
222  * @param rVel Velocity of the point in the inertial frame.
223  */
getVelocity(const SimTK::State & s,const PhysicalFrame & aBody,const Vec3 & aPoint,Vec3 & rVel) const224 void SimbodyEngine::getVelocity(const SimTK::State& s,
225         const PhysicalFrame& aBody, const Vec3& aPoint, Vec3& rVel) const
226 {
227     OPENSIM_THROW_IF_FRMOBJ(
228         dynamic_cast<const PhysicalOffsetFrame*>(&aBody),
229         PhysicalOffsetFrameIsInvalidArgument);
230 
231     rVel = aBody.getMobilizedBody().findStationVelocityInGround(s, aPoint);
232 }
233 
234 //_____________________________________________________________________________
235 /**
236  * Get the inertial acceleration of a point on a body.
237  *
238  * Note that the configuration of the model must be set and accelerations of
239  * the generalized coordinates must be computed before calling this method.
240  *
241  * @param aBody Pointer to body.
242  * @param aPoint Point on the body expressed in the body-local frame.
243  * @param rAcc Acceleration of the point in the inertial frame.
244  *
245  * @see set()
246  * @see computeAccelerations()
247  */
getAcceleration(const SimTK::State & s,const PhysicalFrame & aBody,const Vec3 & aPoint,Vec3 & rAcc) const248 void SimbodyEngine::getAcceleration(const SimTK::State& s,
249         const PhysicalFrame& aBody, const Vec3& aPoint, Vec3& rAcc) const
250 {
251     OPENSIM_THROW_IF_FRMOBJ(
252         dynamic_cast<const PhysicalOffsetFrame*>(&aBody),
253         PhysicalOffsetFrameIsInvalidArgument);
254 
255     rAcc = aBody.getMobilizedBody().findStationAccelerationInGround(s, aPoint);
256 }
257 
258 //_____________________________________________________________________________
259 /**
260  * Get the body orientation with respect to the ground.
261  *
262  * @param aBody Pointer to body.
263  * @param rDirCos Orientation of the body with respect to the ground frame.
264  */
getDirectionCosines(const SimTK::State & s,const PhysicalFrame & aBody,double rDirCos[3][3]) const265 void SimbodyEngine::getDirectionCosines(const SimTK::State& s,
266         const PhysicalFrame& aBody, double rDirCos[3][3]) const
267 {
268     OPENSIM_THROW_IF_FRMOBJ(
269         dynamic_cast<const PhysicalOffsetFrame*>(&aBody),
270         PhysicalOffsetFrameIsInvalidArgument);
271 
272     Mat33::updAs(&rDirCos[0][0]) =
273         aBody.getMobilizedBody().getBodyRotation(s).asMat33();
274 }
275 
276 //_____________________________________________________________________________
277 /**
278  * Get the body orientation with respect to the ground.
279  *
280  * @param aBody Pointer to body.
281  * @param rDirCos Orientation of the body with respect to the ground frame.
282  */
getDirectionCosines(const SimTK::State & s,const PhysicalFrame & aBody,double * rDirCos) const283 void SimbodyEngine::getDirectionCosines(const SimTK::State& s,
284         const PhysicalFrame& aBody, double *rDirCos) const
285 {
286     OPENSIM_THROW_IF_FRMOBJ(
287         dynamic_cast<const PhysicalOffsetFrame*>(&aBody),
288         PhysicalOffsetFrameIsInvalidArgument);
289 
290     Mat33::updAs(rDirCos) =
291         aBody.getMobilizedBody().getBodyRotation(s).asMat33();
292 }
293 
294 //_____________________________________________________________________________
295 /**
296  * Get the inertial angular velocity of a body in the ground reference frame.
297  *
298  * @param aBody Pointer to body.
299  * @param rAngVel Angular velocity of the body.
300  */
getAngularVelocity(const SimTK::State & s,const PhysicalFrame & aBody,Vec3 & rAngVel) const301 void SimbodyEngine::getAngularVelocity(const SimTK::State& s,
302         const PhysicalFrame& aBody, Vec3& rAngVel) const
303 {
304     OPENSIM_THROW_IF_FRMOBJ(
305         dynamic_cast<const PhysicalOffsetFrame*>(&aBody),
306         PhysicalOffsetFrameIsInvalidArgument);
307 
308     rAngVel = aBody.getMobilizedBody().getBodyAngularVelocity(s);
309 }
310 
311 //_____________________________________________________________________________
312 /**
313  * Get the inertial angular velocity in the local body reference frame.
314  *
315  * @param aBody Pointer to body.
316  * @param rAngVel Angular velocity of the body.
317  */
getAngularVelocityBodyLocal(const SimTK::State & s,const PhysicalFrame & aBody,Vec3 & rAngVel) const318 void SimbodyEngine::getAngularVelocityBodyLocal(const SimTK::State& s,
319         const PhysicalFrame& aBody, Vec3& rAngVel) const
320 {
321     OPENSIM_THROW_IF_FRMOBJ(
322         dynamic_cast<const PhysicalOffsetFrame*>(&aBody),
323         PhysicalOffsetFrameIsInvalidArgument);
324 
325     rAngVel = aBody.getMobilizedBody().getBodyAngularVelocity(s);
326 }
327 
328 //_____________________________________________________________________________
329 /**
330  * Get the inertial angular acceleration of a body in the ground reference
331  * frame.
332  *
333  * @param aBody Pointer to body.
334  * @param rAngAcc Angular acceleration of the body.
335  */
getAngularAcceleration(const SimTK::State & s,const PhysicalFrame & aBody,Vec3 & rAngAcc) const336 void SimbodyEngine::getAngularAcceleration(const SimTK::State& s,
337         const PhysicalFrame& aBody, Vec3& rAngAcc) const
338 {
339     OPENSIM_THROW_IF_FRMOBJ(
340         dynamic_cast<const PhysicalOffsetFrame*>(&aBody),
341         PhysicalOffsetFrameIsInvalidArgument);
342 
343     rAngAcc = aBody.getMobilizedBody().getBodyAngularAcceleration(s);
344 }
345 
346 //_____________________________________________________________________________
347 /**
348  * Get the inertial angular acceleration in the local body reference frame.
349  *
350  * @param aBody Pointer to body.
351  * @param rAngAcc Angular acceleration of the body.
352  */
getAngularAccelerationBodyLocal(const SimTK::State & s,const PhysicalFrame & aBody,Vec3 & rAngAcc) const353 void SimbodyEngine::getAngularAccelerationBodyLocal(const SimTK::State& s,
354         const PhysicalFrame &aBody, Vec3& rAngAcc) const
355 {
356     OPENSIM_THROW_IF_FRMOBJ(
357         dynamic_cast<const PhysicalOffsetFrame*>(&aBody),
358         PhysicalOffsetFrameIsInvalidArgument);
359 
360     rAngAcc = aBody.getMobilizedBody().getBodyAngularAcceleration(s);
361 }
362 
363 //_____________________________________________________________________________
364 /**
365  * get a copy of the transform from the inertial frame to a body
366  *
367  * @param aBody
368  * @return Transform from inertial frame to body
369  */
getTransform(const SimTK::State & s,const PhysicalFrame & aBody) const370 SimTK::Transform SimbodyEngine::getTransform(const SimTK::State& s,
371         const PhysicalFrame& aBody) const
372 {
373     OPENSIM_THROW_IF_FRMOBJ(
374         dynamic_cast<const PhysicalOffsetFrame*>(&aBody),
375         PhysicalOffsetFrameIsInvalidArgument);
376 
377     return aBody.getMobilizedBody().getBodyTransform(s);
378 }
379 
380 //--------------------------------------------------------------------------
381 // LOAD ACCESS AND COMPUTATION
382 //--------------------------------------------------------------------------
383 //_____________________________________________________________________________
384 /**
385  * Compute the reaction forces and torques at all the joints in the model.
386  * For each joint, what's reported is the force and torque the joint structure
387  * applies on the child body at the joint frame attached to the child body.
388  * Both vectors are expressed in the ground reference frame.
389  *
390  * It is necessary to call computeAccelerations() before this method
391  * to get valid results.  The cost to calculate joint forces and moments is 114
392  * flops/body.
393  *
394  * @param rForces Matrix of reaction forces.  The size should be
395  * at least NumberOfJoints x 3.
396  * @param rTorques Matrix of reaction torques.  The size should be
397  * at least NumberOfJoints x 3.
398  */
computeReactions(const SimTK::State & s,Vector_<Vec3> & rForces,Vector_<Vec3> & rTorques) const399 void SimbodyEngine::computeReactions(const SimTK::State& s, Vector_<Vec3>& rForces, Vector_<Vec3>& rTorques) const
400 {
401     // get the number of mobilized bodies in the underlying SimbodyMatterSubsystem
402     //int nmb = _model->getMatterSubsystem().getNumBodies();
403 
404     // get the number of bodies in the OpenSim model
405     int nj = _model->getNumJoints();
406 
407     //int nf = rForces.size();
408     //int ntorq = rTorques.size();
409 
410     // there may be more mobilized bodies than joint exposed in the OpenSim model
411     // since joints and other components may use (massless) bodies internally
412     assert(_model->getMatterSubsystem().getNumBodies() >= nj);
413     assert(nj == rForces.size());
414     assert(rForces.size() == rTorques.size());
415 
416     SimTK::Vector_<SpatialVec> reactionForces(nj);
417 
418     // Systems must be realized to acceleration stage
419     _model->getMultibodySystem().realize(s, Stage::Acceleration);
420     _model->getMatterSubsystem().calcMobilizerReactionForces(s, reactionForces);
421 
422 
423     const JointSet &joints = _model->getJointSet();
424 
425     //Separate SimTK SpatialVecs to Forces and Torques
426     // SpatialVec = Vec2<Vec3 torque, Vec3 force>
427     for(int i=0; i<nj; i++){
428         const SimTK::MobilizedBodyIndex& ix =
429             joints[i].getChildFrame().getMobilizedBodyIndex();
430 
431         rTorques[i] = reactionForces[ix](0);
432         rForces[i] = reactionForces[ix](1);
433     }
434 }
435 
436 //--------------------------------------------------------------------------
437 // UTILITY
438 //--------------------------------------------------------------------------
439 //_____________________________________________________________________________
440 /**
441  * Transform a vector from one body to another
442  *
443  * @param aBodyFrom the body in which the vector is currently expressed
444  * @param aPos the vector to be transformed
445  * @param aBodyTo the body the vector will be transformed into
446  * @param rPos the vector in the aBodyTo frame is returned here
447  */
transform(const SimTK::State & s,const PhysicalFrame & aBodyFrom,const double aVec[3],const PhysicalFrame & aBodyTo,double rVec[3]) const448 void SimbodyEngine::transform(const SimTK::State& s, const PhysicalFrame &aBodyFrom, const double aVec[3], const PhysicalFrame &aBodyTo, double rVec[3]) const
449 {
450     OPENSIM_THROW_IF_FRMOBJ(
451         dynamic_cast<const PhysicalOffsetFrame*>(&aBodyFrom),
452         PhysicalOffsetFrameIsInvalidArgument);
453 
454     OPENSIM_THROW_IF_FRMOBJ(
455         dynamic_cast<const PhysicalOffsetFrame*>(&aBodyTo),
456         PhysicalOffsetFrameIsInvalidArgument);
457 
458     if(&aBodyFrom == &aBodyTo) { for(int i=0; i<3; i++) { rVec[i]=aVec[i]; } return; }
459     const Body* bFrom = (const Body*)&aBodyFrom;
460     const Body* bTo = (const Body*)&aBodyTo;
461 
462     //Get input vector as a Vec3 to make the call down to Simbody and update the output vector
463     Vec3::updAs(rVec) = _model->getMatterSubsystem().getMobilizedBody(bFrom->getMobilizedBodyIndex()).expressVectorInAnotherBodyFrame(s, Vec3::getAs(aVec), _model->getMatterSubsystem().getMobilizedBody(bTo->getMobilizedBodyIndex()));
464 }
465 
466 //_____________________________________________________________________________
467 /**
468  * Transform a vector from one body to another
469  *
470  * @param aBodyFrom the body in which the vector is currently expressed
471  * @param aPos the vector to be transformed
472  * @param aBodyTo the body the vector will be transformed into
473  * @param rPos the vector in the aBodyTo frame is returned here
474  */
transform(const SimTK::State & s,const PhysicalFrame & aBodyFrom,const Vec3 & aVec,const PhysicalFrame & aBodyTo,Vec3 & rVec) const475 void SimbodyEngine::transform(const SimTK::State& s, const PhysicalFrame &aBodyFrom, const Vec3& aVec, const PhysicalFrame &aBodyTo, Vec3& rVec) const
476 {
477     OPENSIM_THROW_IF_FRMOBJ(
478         dynamic_cast<const PhysicalOffsetFrame*>(&aBodyFrom),
479         PhysicalOffsetFrameIsInvalidArgument);
480 
481     OPENSIM_THROW_IF_FRMOBJ(
482         dynamic_cast<const PhysicalOffsetFrame*>(&aBodyTo),
483         PhysicalOffsetFrameIsInvalidArgument);
484 
485     if(&aBodyFrom == &aBodyTo) { rVec=aVec; return; }
486 
487     // Get input vector as a Vec3 to make the call down to Simbody and update
488     // the output vector
489     rVec = aBodyFrom.getMobilizedBody().expressVectorInAnotherBodyFrame(s, aVec,
490             aBodyTo.getMobilizedBody());
491 }
492 
493 //_____________________________________________________________________________
494 /**
495  * Transform a point from one body to another
496  *
497  * @param aBodyFrom the body in which the point is currently expressed
498  * @param aPos the XYZ coordinates of the point
499  * @param aBodyTo the body the point will be transformed into
500  * @param rPos the XYZ coordinates of the point in the aBodyTo frame are returned here
501  */
502 void SimbodyEngine::
transformPosition(const SimTK::State & s,const PhysicalFrame & aBodyFrom,const double aPos[3],const PhysicalFrame & aBodyTo,double rPos[3]) const503 transformPosition(const SimTK::State& s, const PhysicalFrame &aBodyFrom, const
504         double aPos[3], const PhysicalFrame &aBodyTo, double rPos[3]) const
505 {
506     OPENSIM_THROW_IF_FRMOBJ(
507         dynamic_cast<const PhysicalOffsetFrame*>(&aBodyFrom),
508         PhysicalOffsetFrameIsInvalidArgument);
509 
510     OPENSIM_THROW_IF_FRMOBJ(
511         dynamic_cast<const PhysicalOffsetFrame*>(&aBodyTo),
512         PhysicalOffsetFrameIsInvalidArgument);
513 
514     if(&aBodyFrom == &aBodyTo) {
515        for (int i=0; i<3; i++) rPos[i] = aPos[i];
516         return;
517     }
518 
519     // Get input vector as a Vec3 to make the call down to Simbody and update
520     // the output vector.
521     Vec3::updAs(rPos) =
522         aBodyFrom.getMobilizedBody().findStationLocationInAnotherBody(s,
523                 Vec3::getAs(aPos), aBodyTo.getMobilizedBody());
524 }
525 
526 //_____________________________________________________________________________
527 /**
528  * Transform a point from one body to another
529  *
530  * @param aBodyFrom the body in which the point is currently expressed
531  * @param aPos the XYZ coordinates of the point
532  * @param aBodyTo the body the point will be transformed into
533  * @param rPos the XYZ coordinates of the point in the aBodyTo frame are returned here
534  */
535 void SimbodyEngine::
transformPosition(const SimTK::State & s,const PhysicalFrame & aBodyFrom,const Vec3 & aPos,const PhysicalFrame & aBodyTo,Vec3 & rPos) const536 transformPosition(const SimTK::State& s, const PhysicalFrame &aBodyFrom, const
537         Vec3& aPos, const PhysicalFrame &aBodyTo, Vec3& rPos) const
538 {
539     OPENSIM_THROW_IF_FRMOBJ(
540         dynamic_cast<const PhysicalOffsetFrame*>(&aBodyFrom),
541         PhysicalOffsetFrameIsInvalidArgument);
542 
543     OPENSIM_THROW_IF_FRMOBJ(
544         dynamic_cast<const PhysicalOffsetFrame*>(&aBodyTo),
545         PhysicalOffsetFrameIsInvalidArgument);
546 
547     if(&aBodyFrom == &aBodyTo) {
548        for (int i=0; i<3; i++) rPos[i] = aPos[i];
549         return;
550     }
551 
552     // Get input vector as a Vec3 to make the call down to Simbody and update
553     // the output vector.
554     rPos = aBodyFrom.getMobilizedBody().findStationLocationInAnotherBody(s,
555             aPos, aBodyTo.getMobilizedBody());
556 }
557 
558 //_____________________________________________________________________________
559 /**
560  * Transform a point from one body to the ground body
561  *
562  * @param aBodyFrom the body in which the point is currently expressed
563  * @param aPos the XYZ coordinates of the point
564  * @param rPos the XYZ coordinates of the point in the ground frame are returned here
565  */
transformPosition(const SimTK::State & s,const PhysicalFrame & aBodyFrom,const double aPos[3],double rPos[3]) const566 void SimbodyEngine::transformPosition(const SimTK::State& s, const PhysicalFrame &aBodyFrom, const double aPos[3], double rPos[3]) const
567 {
568     OPENSIM_THROW_IF_FRMOBJ(
569         dynamic_cast<const PhysicalOffsetFrame*>(&aBodyFrom),
570         PhysicalOffsetFrameIsInvalidArgument);
571 
572     //const Body* bFrom = (const Body*)&aBodyFrom;
573 
574     // Get input vector as a Vec3 to make the call down to Simbody and update
575     // the output vector.
576     Vec3::updAs(rPos) =
577         aBodyFrom.getMobilizedBody().findStationLocationInGround(s,
578                 Vec3::getAs(aPos));
579 }
580 
581 //_____________________________________________________________________________
582 /**
583  * Transform a point from one body to the ground body
584  *
585  * @param aBodyFrom the body in which the point is currently expressed
586  * @param aPos the XYZ coordinates of the point
587  * @param rPos the XYZ coordinates of the point in the ground frame are returned here
588  */
589 void SimbodyEngine::
transformPosition(const SimTK::State & s,const PhysicalFrame & aBodyFrom,const Vec3 & aPos,Vec3 & rPos) const590 transformPosition(const SimTK::State& s, const PhysicalFrame& aBodyFrom,
591         const Vec3& aPos, Vec3& rPos) const
592 {
593     OPENSIM_THROW_IF_FRMOBJ(
594         dynamic_cast<const PhysicalOffsetFrame*>(&aBodyFrom),
595         PhysicalOffsetFrameIsInvalidArgument);
596 
597     _model->getMultibodySystem().realize(s, SimTK::Stage::Position);
598     rPos = aBodyFrom.getMobilizedBody().findStationLocationInGround(s, aPos);
599 }
600 
601 //_____________________________________________________________________________
602 /**
603  * Calculate the distance between a point on one body and a point on another body
604  *
605  * @param aBody1 the body that the first point is expressed in
606  * @param aPoint1 the XYZ coordinates of the first point
607  * @param aBody2 the body that the second point is expressed in
608  * @param aPoint2 the XYZ coordinates of the second point
609  * @return the distance between aPoint1 and aPoint2
610  */
611 double SimbodyEngine::
calcDistance(const SimTK::State & s,const PhysicalFrame & aBody1,const Vec3 & aPoint1,const PhysicalFrame & aBody2,const Vec3 & aPoint2) const612 calcDistance(const SimTK::State& s, const PhysicalFrame& aBody1,
613 const Vec3& aPoint1, const PhysicalFrame& aBody2, const Vec3& aPoint2)
614     const
615 {
616     OPENSIM_THROW_IF_FRMOBJ(
617         dynamic_cast<const PhysicalOffsetFrame*>(&aBody1),
618         PhysicalOffsetFrameIsInvalidArgument);
619 
620     OPENSIM_THROW_IF_FRMOBJ(
621         dynamic_cast<const PhysicalOffsetFrame*>(&aBody2),
622         PhysicalOffsetFrameIsInvalidArgument);
623 
624     return aBody1.getMobilizedBody().calcStationToStationDistance(s, aPoint1,
625             aBody2.getMobilizedBody(), aPoint2);
626 }
627 
628 //_____________________________________________________________________________
629 /**
630  * Calculate the distance between a point on one body and a point on another body
631  *
632  * @param aBody1 the body that the first point is expressed in
633  * @param aPoint1 the XYZ coordinates of the first point
634  * @param aBody2 the body that the second point is expressed in
635  * @param aPoint2 the XYZ coordinates of the second point
636  * @return the distance between aPoint1 and aPoint2
637  */
calcDistance(const SimTK::State & s,const PhysicalFrame & aBody1,const double aPoint1[3],const PhysicalFrame & aBody2,const double aPoint2[3]) const638 double SimbodyEngine::calcDistance(const SimTK::State& s, const PhysicalFrame&
639         aBody1, const double aPoint1[3], const PhysicalFrame& aBody2, const
640         double aPoint2[3]) const
641 {
642     OPENSIM_THROW_IF_FRMOBJ(
643         dynamic_cast<const PhysicalOffsetFrame*>(&aBody1),
644         PhysicalOffsetFrameIsInvalidArgument);
645 
646     OPENSIM_THROW_IF_FRMOBJ(
647         dynamic_cast<const PhysicalOffsetFrame*>(&aBody2),
648         PhysicalOffsetFrameIsInvalidArgument);
649 
650     return aBody1.getMobilizedBody().calcStationToStationDistance(s,
651             Vec3::getAs(aPoint1), aBody2.getMobilizedBody(),
652             Vec3::getAs(aPoint2));
653 }
654 
655 //_____________________________________________________________________________
656 /**
657  * Convert angles to direction cosines.
658  * @param aE1 1st Euler angle.
659  * @param aE2 2nd Euler angle.
660  * @param aE3 3rd Euler angle.
661  * @param rDirCos Vector of direction cosines.
662  */
convertAnglesToDirectionCosines(double aE1,double aE2,double aE3,double rDirCos[3][3]) const663 void SimbodyEngine::convertAnglesToDirectionCosines(double aE1, double aE2, double aE3, double rDirCos[3][3]) const
664 {
665     Vec3 angs(aE1, aE2, aE3);
666     Rotation aRot;
667     aRot.setRotationToBodyFixedXYZ(angs);
668     Mat33::updAs(&rDirCos[0][0]) = aRot.asMat33();
669 }
670 
671 //_____________________________________________________________________________
672 /**
673  * Convert angles to direction cosines.
674  * @param aE1 1st Euler angle.
675  * @param aE2 2nd Euler angle.
676  * @param aE3 3rd Euler angle.
677  * @param rDirCos Vector of direction cosines.
678  */
convertAnglesToDirectionCosines(double aE1,double aE2,double aE3,double * rDirCos) const679 void SimbodyEngine::convertAnglesToDirectionCosines(double aE1, double aE2, double aE3, double *rDirCos) const
680 {
681     if(rDirCos==NULL) return;
682 
683     Vec3 angs(aE1, aE2, aE3);
684     Rotation aRot;
685     aRot.setRotationToBodyFixedXYZ(angs);
686     Mat33::updAs(&rDirCos[0]) = aRot.asMat33();
687 }
688 
689 //_____________________________________________________________________________
690 /**
691  * Convert direction cosines to angles.
692  * @param aDirCos Vector of direction cosines.
693  * @param rE1 1st Euler angle.
694  * @param rE2 2nd Euler angle.
695  * @param rE3 3rd Euler angle.
696  */
convertDirectionCosinesToAngles(double aDirCos[3][3],double * rE1,double * rE2,double * rE3) const697 void SimbodyEngine::convertDirectionCosinesToAngles(double aDirCos[3][3], double *rE1, double *rE2, double *rE3) const
698 {
699     Vec3 ang = Rotation(Rotation::getAs(&aDirCos[0][0])).convertRotationToBodyFixedXYZ();
700     *rE1 = ang[0];
701     *rE2 = ang[1];
702     *rE3 = ang[2];
703 }
704 
705 //_____________________________________________________________________________
706 /**
707  * Convert direction cosines to angles.
708  * @param aDirCos Vector of direction cosines.
709  * @param rE1 1st Euler angle.
710  * @param rE2 2nd Euler angle.
711  * @param rE3 3rd Euler angle.
712  */
convertDirectionCosinesToAngles(double * aDirCos,double * rE1,double * rE2,double * rE3) const713 void SimbodyEngine::convertDirectionCosinesToAngles(double *aDirCos, double *rE1, double *rE2, double *rE3) const
714 {
715     if(!aDirCos) return;
716     Vec3 ang = Rotation(Rotation::getAs(aDirCos)).convertRotationToBodyFixedXYZ();
717     *rE1 = ang[0];
718     *rE2 = ang[1];
719     *rE3 = ang[2];
720 }
721 
722 //_____________________________________________________________________________
723 /**
724  * Convert direction cosines to quaternions.
725  * @param aDirCos Vector of direction cosines.
726  * @param rQ1 1st Quaternion.
727  * @param rQ2 2nd Quaternion.
728  * @param rQ3 3rd Quaternion.
729  * @param rQ4 4th Quaternion.
730  */
convertDirectionCosinesToQuaternions(double aDirCos[3][3],double * rQ1,double * rQ2,double * rQ3,double * rQ4) const731 void SimbodyEngine::convertDirectionCosinesToQuaternions(double aDirCos[3][3], double *rQ1, double *rQ2, double *rQ3, double *rQ4) const
732 {
733     Quaternion quat = Rotation(Rotation::getAs(&aDirCos[0][0])).convertRotationToQuaternion();
734     *rQ1 = quat[0];
735     *rQ2 = quat[1];
736     *rQ3 = quat[2];
737     *rQ4 = quat[3];
738 }
739 
740 //_____________________________________________________________________________
741 /**
742  * Convert direction cosines to quaternions.
743  * @param aDirCos Vector of direction cosines.
744  * @param rQ1 1st Quaternion.
745  * @param rQ2 2nd Quaternion.
746  * @param rQ3 3rd Quaternion.
747  * @param rQ4 4th Quaternion.
748  */
convertDirectionCosinesToQuaternions(double * aDirCos,double * rQ1,double * rQ2,double * rQ3,double * rQ4) const749 void SimbodyEngine::convertDirectionCosinesToQuaternions(double *aDirCos, double *rQ1, double *rQ2, double *rQ3, double *rQ4) const
750 {
751     if(aDirCos==NULL) return;
752     Quaternion quat = Rotation(Rotation::getAs(aDirCos)).convertRotationToQuaternion();
753     *rQ1 = quat[0];
754     *rQ2 = quat[1];
755     *rQ3 = quat[2];
756     *rQ4 = quat[3];
757 }
758 
759 //_____________________________________________________________________________
760 /**
761  * Convert quaternions to direction cosines.
762  * @param aQ1 1st Quaternion.
763  * @param aQ2 2nd Quaternion.
764  * @param aQ3 3rd Quaternion.
765  * @param aQ4 4th Quaternion.
766  * @param rDirCos Vector of direction cosines.
767  */
convertQuaternionsToDirectionCosines(double aQ1,double aQ2,double aQ3,double aQ4,double rDirCos[3][3]) const768 void SimbodyEngine::convertQuaternionsToDirectionCosines(double aQ1, double aQ2, double aQ3, double aQ4, double rDirCos[3][3]) const
769 {
770     Rotation R;
771     R.setRotationFromQuaternion(Quaternion(Vec4(aQ1, aQ2, aQ3, aQ4)));
772 
773     Mat33::updAs(&rDirCos[0][0]) = R.asMat33();
774 }
775 
776 //_____________________________________________________________________________
777 /**
778  * Convert quaternions to direction cosines.
779  * @param aQ1 1st Quaternion.
780  * @param aQ2 2nd Quaternion.
781  * @param aQ3 3rd Quaternion.
782  * @param aQ4 4th Quaternion.
783  * @param rDirCos Vector of direction cosines.
784  */
convertQuaternionsToDirectionCosines(double aQ1,double aQ2,double aQ3,double aQ4,double * rDirCos) const785 void SimbodyEngine::convertQuaternionsToDirectionCosines(double aQ1, double aQ2, double aQ3, double aQ4, double *rDirCos) const
786 {
787     if(rDirCos==NULL) return;
788     Rotation R;
789     R.setRotationFromQuaternion(Quaternion(Vec4(aQ1, aQ2, aQ3, aQ4)));
790 
791     Mat33::updAs(rDirCos) = R.asMat33();
792 }
793 
794 
795 //--- Private Utility Methods Below Here ---
796 
797 
798 //=============================================================================
799 // CONFIGURATION
800 //=============================================================================
801 void SimbodyEngine::
formCompleteStorages(const SimTK::State & s,const OpenSim::Storage & aQIn,OpenSim::Storage * & rQComplete,OpenSim::Storage * & rUComplete) const802 formCompleteStorages( const SimTK::State& s, const OpenSim::Storage &aQIn,
803     OpenSim::Storage *&rQComplete,OpenSim::Storage *&rUComplete) const
804 {
805     int i;
806     int nq = _model->getNumCoordinates();
807     int nu = _model->getNumSpeeds();
808 
809     // Get coordinate file indices
810     Array<string> columnLabels, speedLabels, coordStateNames;
811     columnLabels.append("time");
812     speedLabels = columnLabels;
813 
814     Array<int> index(-1,nq);
815     const CoordinateSet& coordinateSet = _model->getCoordinateSet();
816     int sizeCoordSet = coordinateSet.getSize();
817     for(i=0;i<sizeCoordSet;i++) {
818         Coordinate& coord = coordinateSet.get(i);
819         coordStateNames = coord.getStateVariableNames();
820         columnLabels.append(coordStateNames[0]);
821         speedLabels.append(coordStateNames[1]);
822         int fix = aQIn.getStateIndex(coord.getName());
823         if (fix < 0) {
824             fix = aQIn.getStateIndex(columnLabels[i+1]);
825         }
826 
827         index[i] = fix;
828         if(index[i]<0) {
829             string msg = "Model::formCompleteStorages(): WARNING- Did not find column ";
830             msg += coordStateNames[0];
831             msg += " in storage object.\n";
832             cout << msg << endl;
833         }
834     }
835 
836     // Extract Coordinates
837     double time;
838     Array<double> data(0.0);
839     Array<double> q(0.0,nq);
840     Storage *qStore = new Storage();
841     qStore->setInDegrees(aQIn.isInDegrees());
842     qStore->setName("GeneralizedCoordinates");
843     qStore->setColumnLabels(columnLabels);
844     int size = aQIn.getSize();
845     StateVector *vector;
846     int j;
847     for(i=0;i<size;i++) {
848         vector = aQIn.getStateVector(i);
849         data = vector->getData();
850         time = vector->getTime();
851 
852         for(j=0;j<nq;j++) {
853             q[j] = 0.0;
854             if(index[j]<0) continue;
855             q[j] = data[index[j]];
856         }
857 
858         qStore->append(time,nq,&q[0]);
859     }
860 
861     // Convert to radians
862     if (aQIn.isInDegrees())
863         convertDegreesToRadians(*qStore);
864 
865     // Compute generalized speeds
866     GCVSplineSet tempQset(5,qStore);
867     std::unique_ptr<Storage> uStore{tempQset.constructStorage(1)};
868 
869     // Compute constraints
870     Array<double> qu(0.0,nq+nu);
871     rQComplete = new Storage();
872     rUComplete = new Storage();
873     State constrainedState = s;
874      _model->getMultibodySystem().realize(constrainedState, s.getSystemStage());
875     for(i=0;i<size;i++) {
876         qStore->getTime(i,time);
877         qStore->getData(i,nq,&qu[0]);
878         uStore->getData(i,nu,&qu[nq]);
879         for (int j = 0; j < nq; j++) {
880             Coordinate& coord = coordinateSet.get(j);
881             coord.setValue(constrainedState, qu[j], false);
882             coord.setSpeedValue(constrainedState, qu[nq+j]);
883         }
884         _model->assemble(constrainedState);
885         for (int j = 0; j < nq; j++) {
886             Coordinate& coord = coordinateSet.get(j);
887             qu[j] = coord.getValue(constrainedState);
888             qu[nq+j] = coord.getSpeedValue(constrainedState);
889         }
890         rQComplete->append(time,nq,&qu[0]);
891         rUComplete->append(time,nu,&qu[nq]);
892     }
893 
894     delete qStore;
895 
896     // Set column labels before returning
897     rQComplete->setColumnLabels(columnLabels);
898     rUComplete->setColumnLabels(speedLabels);
899 }
900 
901 //=============================================================================
902 // UTILITY
903 //=============================================================================
904 //_____________________________________________________________________________
905 /**
906  */
scaleRotationalDofColumns(Storage & rStorage,double factor) const907 void SimbodyEngine::scaleRotationalDofColumns(Storage &rStorage, double factor) const
908 {
909     const Array<std::string>& columnLabels = rStorage.getColumnLabels();
910     int ncols = columnLabels.getSize();
911     if(ncols == 0)
912         throw Exception("SimbodyEngine.scaleRotationalDofColumns: ERROR- storage has no labels, can't determine coordinate types for deg<->rad conversion",
913                              __FILE__,__LINE__);
914 
915     // Loop through the coordinates in the model. For each one that is rotational,
916     // see if it has a corresponding column of data. If it does, multiply that
917     // column by the given scaling factor.
918     std::string shortName = "";
919     std::string prefix = "";
920     int index = -1;
921     const CoordinateSet& coordinateSet = _model->getCoordinateSet();
922 
923     // first column is time, so skip
924     for (int i = 1; i < ncols; i++) {
925         const std::string& name = columnLabels[i];
926         index = coordinateSet.getIndex(name);
927         if (index < 0){
928             std::string::size_type back = name.rfind("/");
929             prefix = name.substr(0, back);
930             shortName = name.substr(back+1, name.length()-back);
931             index = coordinateSet.getIndex(shortName);
932             // This is a necessary hack to use new component naming,
933             // but SimbodyEngine will be deprecated and so will this code- aseth
934             if (index < 0){ // could be a speed then trim off _u
935                 back = prefix.rfind("/");
936                 shortName = prefix.substr(back+1, prefix.length()-back);
937                 index = coordinateSet.getIndex(shortName);
938             }
939         }
940         if (index >= 0){
941             const Coordinate& coord = coordinateSet.get(index);
942             if (coord.getMotionType() == Coordinate::Rotational) {
943                 // assumes first data column is 0 whereas labels has time as 0
944                 rStorage.multiplyColumn(i-1, factor);
945             }
946         }
947     }
948 }
949 
scaleRotationalDofColumns(TimeSeriesTable & table,double factor) const950 void SimbodyEngine::scaleRotationalDofColumns(TimeSeriesTable& table,
951                                               double factor) const {
952     size_t ncols = table.getNumColumns();
953     if(ncols == 0)
954         throw Exception("SimbodyEngine.scaleRotationalDofColumns: ERROR- storage has no labels, can't determine coordinate types for deg<->rad conversion",
955                              __FILE__,__LINE__);
956 
957     // Loop through the coordinates in the model. For each one that is rotational,
958     // see if it has a corresponding column of data. If it does, multiply that
959     // column by the given scaling factor.
960     std::string shortName = "";
961     std::string prefix = "";
962     int index = -1;
963     const CoordinateSet& coordinateSet = _model->getCoordinateSet();
964 
965     // first column is time, so skip
966     for (size_t i = 0; i < ncols; i++) {
967         const std::string& name = table.getColumnLabel(i);
968         index = coordinateSet.getIndex(name);
969         if (index < 0){
970             std::string::size_type back = name.rfind("/");
971             prefix = name.substr(0, back);
972             shortName = name.substr(back+1, name.length()-back);
973             index = coordinateSet.getIndex(shortName);
974             // This is a necessary hack to use new component naming,
975             // but SimbodyEngine will be deprecated and so will this code- aseth
976             if (index < 0){ // could be a speed then trim off _u
977                 back = prefix.rfind("/");
978                 shortName = prefix.substr(back+1, prefix.length()-back);
979                 index = coordinateSet.getIndex(shortName);
980             }
981         }
982         if (index >= 0){
983             const Coordinate& coord = coordinateSet.get(index);
984             if (coord.getMotionType() == Coordinate::Rotational) {
985                 // assumes first data column is 0 whereas labels has time as 0
986                 table.updDependentColumnAtIndex(i) *= factor;
987             }
988         }
989     }
990 }
991 //_____________________________________________________________________________
992 /**
993  * Convert the rotational generalized coordinates or speeds from units of
994  * degrees to units of radians for all the state-vectors in an Storage
995  * object.  Coordinates/speeds are identified by column names.
996  *
997  * @param rStorage Storage object.
998  */
convertDegreesToRadians(Storage & rStorage) const999 void SimbodyEngine::convertDegreesToRadians(Storage &rStorage) const
1000 {
1001     assert(rStorage.isInDegrees());
1002     scaleRotationalDofColumns(rStorage, SimTK_DEGREE_TO_RADIAN);
1003     rStorage.setInDegrees(false);
1004 }
1005 //_____________________________________________________________________________
1006 /**
1007  * Convert the rotational generalized coordinates or speeds from units of
1008  * radians to units of degrees for all the state-vectors in an Storage
1009  * object.  Coordinates/speeds are identified by column names.
1010  *
1011  * @param rStorage Storage object.
1012  */
convertRadiansToDegrees(Storage & rStorage) const1013 void SimbodyEngine::convertRadiansToDegrees(Storage &rStorage) const
1014 {
1015     assert(!rStorage.isInDegrees());
1016     scaleRotationalDofColumns(rStorage, SimTK_RADIAN_TO_DEGREE);
1017     rStorage.setInDegrees(true);
1018 }
1019 
convertRadiansToDegrees(TimeSeriesTable & table) const1020 void SimbodyEngine::convertRadiansToDegrees(TimeSeriesTable& table) const {
1021     if (table.hasTableMetaDataKey("inDegrees")) {
1022         OPENSIM_THROW_IF(
1023             table.getTableMetaData<std::string>("inDegrees") == "yes",
1024             Exception,
1025             "Columns of the table provided are already in degrees.");
1026         table.removeTableMetaDataKey("inDegrees");
1027     }
1028 
1029     scaleRotationalDofColumns(table, SimTK_RADIAN_TO_DEGREE);
1030     table.addTableMetaData("inDegrees", std::string{"yes"});
1031 }
1032 
convertDegreesToRadians(TimeSeriesTable & table) const1033 void SimbodyEngine::convertDegreesToRadians(TimeSeriesTable& table) const {
1034     if (table.hasTableMetaDataKey("inDegrees")) {
1035         OPENSIM_THROW_IF(
1036             table.getTableMetaData<std::string>("inDegrees") == "no",
1037             Exception,
1038             "Columns of the table provided are already in radians.");
1039         table.removeTableMetaDataKey("inDegrees");
1040         scaleRotationalDofColumns(table, SimTK_DEGREE_TO_RADIAN);
1041         table.addTableMetaData("inDegrees", std::string{ "no" });
1042     }
1043     else {
1044         OPENSIM_THROW(Exception,
1045             "Table provided does not specify rotations to be in degrees.\n"
1046             "No conversion can be applied.");
1047     }
1048 }
1049 
1050 //_____________________________________________________________________________
1051 /**
1052  * Convert an array of Q/U values from degrees to radians. The sizes of the
1053  * arrays are assumed to be equal to the number of Us.
1054  *
1055  * @param aQDeg Array of values, in degrees
1056  * @param rQRad Array of values, in radians
1057  */
convertDegreesToRadians(double * aQDeg,double * rQRad) const1058 void SimbodyEngine::convertDegreesToRadians(double *aQDeg, double *rQRad) const
1059 {
1060     const CoordinateSet& coordinateSet = _model->getCoordinateSet();
1061 
1062     // The arrays aQDeg and rQRad are assumed to be of size getNumSpeeds() or greater.
1063     // It is also assumed that aQDeg[N] corresponds to the first N coordinates
1064     // in the model, whether those N values are coordinates or speeds.
1065     for (int i = 0; i < _model->getNumSpeeds(); i++)
1066     {
1067         if (coordinateSet.get(i).getMotionType() == Coordinate::Rotational)
1068             rQRad[i] = aQDeg[i] * SimTK_DEGREE_TO_RADIAN;
1069         else
1070             rQRad[i] = aQDeg[i];
1071     }
1072 }
1073 //_____________________________________________________________________________
1074 /**
1075  * Convert an array of Q/U values from radians to degrees. The sizes of the
1076  * arrays are assumed to be equal to the number of Us.
1077  *
1078  * @param aQRad Array of values, in radians
1079  * @param rQDeg Array of values, in degrees
1080  */
convertRadiansToDegrees(double * aQRad,double * rQDeg) const1081 void SimbodyEngine::convertRadiansToDegrees(double *aQRad, double *rQDeg) const
1082 {
1083     const CoordinateSet& coordinateSet = _model->getCoordinateSet();
1084 
1085     // The arrays aQRad and rQDeg are assumed to be of size getNumSpeeds() or greater.
1086     // It is also assumed that aQRad[N] corresponds to the first N coordinates
1087     // in the model, whether those N values are coordinates or speeds.
1088     for (int i = 0; i < _model->getNumSpeeds(); i++)
1089     {
1090         if (coordinateSet.get(i).getMotionType() == Coordinate::Rotational)
1091             rQDeg[i] = aQRad[i] * SimTK_RADIAN_TO_DEGREE;
1092         else
1093             rQDeg[i] = aQRad[i];
1094     }
1095 }
1096