1 /* -------------------------------------------------------------------------- *
2 * OpenSim: PointKinematics.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 //=============================================================================
26 // INCLUDES
27 //=============================================================================
28 #include <string>
29 #include <OpenSim/Simulation/Model/Model.h>
30 #include "PointKinematics.h"
31
32
33 using namespace OpenSim;
34 using namespace std;
35 using SimTK::Vec3;
36
37 //=============================================================================
38 // CONSTANTS
39 //=============================================================================
40 const int PointKinematics::NAME_LENGTH = PointKinematicsNAME_LENGTH;
41 const int PointKinematics::BUFFER_LENGTH = PointKinematicsBUFFER_LENGTH;
42
43
44 //=============================================================================
45 // CONSTRUCTOR(S) AND DESTRUCTOR
46 //=============================================================================
47 //_____________________________________________________________________________
48 /**
49 * Destructor.
50 */
~PointKinematics()51 PointKinematics::~PointKinematics()
52 {
53 deleteStorage();
54 }
55 //_____________________________________________________________________________
56 /**
57 * Construct an PointKinematics instance for recording the kinematics of
58 * the bodies of a model during a simulation. Also serves as a default constructor
59 *
60 * @param aModel Model for which the analyses are to be recorded.
61 */
PointKinematics(Model * aModel)62 PointKinematics::PointKinematics(Model *aModel) :
63 Analysis(aModel),
64 _body(NULL),
65 _relativeToBody(NULL),
66 _bodyName(_bodyNameProp.getValueStr()),
67 _point(_pointProp.getValueDblVec()),
68 _pointName(_pointNameProp.getValueStr()),
69 _relativeToBodyName(_relativeToBodyNameProp.getValueStr())
70 {
71 // NULL
72 setNull();
73
74 // STORAGE
75 allocateStorage();
76
77 if (aModel==0)
78 return;
79
80 }
81
82
83 //=============================================================================
84 // CONSTRUCTION METHODS
85 //=============================================================================
86 //=============================================================================
87 // Object Overrides
88 //=============================================================================
89 //_____________________________________________________________________________
90 /**
91 * Construct an object from file.
92 *
93 * The object is constructed from the root element of the XML document.
94 * The type of object is the tag name of the XML root element.
95 *
96 * @param aFileName File name of the document.
97 */
PointKinematics(const std::string & aFileName)98 PointKinematics::PointKinematics(const std::string &aFileName):
99 Analysis(aFileName, false),
100 _body(NULL),
101 _relativeToBody(NULL),
102 _bodyName(_bodyNameProp.getValueStr()),
103 _point(_pointProp.getValueDblVec()),
104 _pointName(_pointNameProp.getValueStr()),
105 _relativeToBodyName(_relativeToBodyNameProp.getValueStr())
106 {
107 setNull();
108
109 // Serialize from XML
110 updateFromXMLDocument();
111
112 /* The rest will be done by setModel().
113 // CONSTRUCT DESCRIPTION AND LABELS
114 constructDescription();
115 constructColumnLabels();
116
117 // STORAGE
118 allocateStorage();
119 */
120 }
121
122 // Copy constructor and virtual copy
123 //_____________________________________________________________________________
124 /**
125 * Copy constructor.
126 *
127 */
PointKinematics(const PointKinematics & aPointKinematics)128 PointKinematics::PointKinematics(const PointKinematics &aPointKinematics):
129 Analysis(aPointKinematics),
130 _body(aPointKinematics._body),
131 _relativeToBody(aPointKinematics._relativeToBody),
132 _bodyName(_bodyNameProp.getValueStr()),
133 _point(_pointProp.getValueDblVec()),
134 _pointName(_pointNameProp.getValueStr()),
135 _relativeToBodyName(_relativeToBodyNameProp.getValueStr())
136 {
137 setNull();
138
139 // COPY TYPE AND NAME
140 *this = aPointKinematics;
141 }
142
143 //_____________________________________________________________________________
144 /**
145 * SetNull().
146 */
147 void PointKinematics::
setNull()148 setNull()
149 {
150 // POINTERS
151 _kin = NULL;
152 _pStore = NULL;
153 _vStore = NULL;
154 _aStore = NULL;
155
156 // OTHER VARIABLES
157
158 //?_body
159 setName("PointKinematics");
160
161 // POINT INFORMATION
162 //_point.setSize(3);
163 Vec3 zero3(0.0);
164 _bodyNameProp.setName("body_name");
165 _bodyNameProp.setValue("ground");
166 _propertySet.append( &_bodyNameProp );
167
168 _relativeToBodyNameProp.setName("relative_to_body_name");
169 _relativeToBodyNameProp.setValue("none");
170 _propertySet.append( &_relativeToBodyNameProp );
171
172 _pointNameProp.setName("point_name");
173 _pointNameProp.setValue("NONAME");
174 _propertySet.append( &_pointNameProp );
175
176 _pointProp.setName("point");
177 _pointProp.setValue(zero3);
178 _propertySet.append( &_pointProp );
179 }
180
181
182 //=============================================================================
183 // OPERATORS
184 //=============================================================================
185 //-----------------------------------------------------------------------------
186 // ASSIGNMENT
187 //-----------------------------------------------------------------------------
188 //_____________________________________________________________________________
189 /**
190 * Assign this object to the values of another.
191 *
192 * @return Reference to this object.
193 */
194 PointKinematics& PointKinematics::
operator =(const PointKinematics & aPointKinematics)195 operator=(const PointKinematics &aPointKinematics)
196 {
197 // BASE CLASS
198 Analysis::operator=(aPointKinematics);
199 _body = aPointKinematics._body;
200 _relativeToBody = aPointKinematics._relativeToBody;
201 _point = aPointKinematics._point;
202 _pointName = aPointKinematics._pointName;
203 _bodyName = aPointKinematics._bodyName;
204 _relativeToBodyName = aPointKinematics._relativeToBodyName;
205
206 // STORAGE
207 deleteStorage();
208 allocateStorage();
209
210 return(*this);
211 }
212 //_____________________________________________________________________________
213 /**
214 * Construct a description for the body kinematics files. (needs a model)
215 */
216 void PointKinematics::
constructDescription()217 constructDescription()
218 {
219 char descrip[BUFFER_LENGTH];
220 char tmp[BUFFER_LENGTH];
221
222 strcpy(descrip,"\nThis file contains the kinematics ");
223 strcat(descrip,"(position, velocity, or acceleration) of\n");
224
225 if(_relativeToBody){
226 sprintf(tmp,"point (%lf, %lf, %lf) on body %s relative to body %s of model %s.\n",
227 _point[0],_point[1],_point[2],_body->getName().c_str(),
228 _relativeToBody->getName().c_str(), _model->getName().c_str());
229 }
230 else{
231 sprintf(tmp,"point (%lf, %lf, %lf) on the %s of model %s.\n",
232 _point[0],_point[1],_point[2],_body->getName().c_str(),
233 _model->getName().c_str());
234 }
235 strcat(descrip,tmp);
236 strcat(descrip,"\nUnits are S.I. units (seconds, meters, Newtons,...)\n\n");
237
238 setDescription(descrip);
239 }
240
241 //_____________________________________________________________________________
242 /**
243 * Construct column labels for the body kinematics files.
244 */
245 void PointKinematics::
constructColumnLabels()246 constructColumnLabels()
247 {
248 Array<string> labels;
249 labels.append("time");
250 labels.append(getPointName() + "_X");
251 labels.append(getPointName() + "_Y");
252 labels.append(getPointName() + "_Z");
253 setColumnLabels(labels);
254 }
255
256 //_____________________________________________________________________________
257 /**
258 * Allocate storage for the kinematics.
259 */
260 void PointKinematics::
allocateStorage()261 allocateStorage()
262 {
263 // ACCELERATIONS
264 _aStore = new Storage(1000,"PointAcceleration");
265 _aStore->setDescription(getDescription());
266 _aStore->setColumnLabels(getColumnLabels());
267
268 // VELOCITIES
269 _vStore = new Storage(1000,"PointVelocity");
270 _vStore->setDescription(getDescription());
271 _vStore->setColumnLabels(getColumnLabels());
272
273 // POSITIONS
274 _pStore = new Storage(1000,"PointPosition");
275 _pStore->setDescription(getDescription());
276 _pStore->setColumnLabels(getColumnLabels());
277 }
278
279
280 //=============================================================================
281 // DESTRUCTION METHODS
282 //=============================================================================
283 //_____________________________________________________________________________
284 /**
285 * Delete storage objects.
286 */
287 void PointKinematics::
deleteStorage()288 deleteStorage()
289 {
290 if(_aStore!=NULL) { delete _aStore; _aStore=NULL; }
291 if(_vStore!=NULL) { delete _vStore; _vStore=NULL; }
292 if(_pStore!=NULL) { delete _pStore; _pStore=NULL; }
293 }
294
295
296 //=============================================================================
297 // GET AND SET
298 //=============================================================================
299 //_____________________________________________________________________________
300 /* Set the model for which the point kinematics are to be computed. */
setModel(Model & model)301 void PointKinematics::setModel(Model& model)
302 {
303 Analysis::setModel(model);
304
305 _body = nullptr;
306 _relativeToBody = nullptr;
307
308 // Map name to index
309 if (model.hasComponent<PhysicalFrame>(_bodyName))
310 _body = &model.getComponent<PhysicalFrame>(_bodyName);
311 else if (model.hasComponent<PhysicalFrame>("./bodyset/" + _bodyName))
312 _body = &model.getComponent<PhysicalFrame>("./bodyset/"+_bodyName);
313
314 if (model.hasComponent<PhysicalFrame>(_relativeToBodyName))
315 _relativeToBody = &model.getComponent<PhysicalFrame>(
316 _relativeToBodyName);
317 else if(model.hasComponent<PhysicalFrame>("./bodyset/" + _relativeToBodyName))
318 _relativeToBody = &model.getComponent<PhysicalFrame>(
319 "./bodyset/" + _relativeToBodyName);
320
321 // DESCRIPTION AND LABELS
322 constructDescription();
323 constructColumnLabels();
324 }
325 //-----------------------------------------------------------------------------
326 // BODY
327 //-----------------------------------------------------------------------------
328 //_____________________________________________________________________________
329 /**
330 * Set the body for which the point kinematics are to be computed and the point on the body
331 * represented in local frame. Both params are required to avoid the limbo state where either body
332 * or point are undefined..
333 *
334 * @param aBody Body name
335 * @double[3] aPoint point coordinates
336 */
337 void PointKinematics::
setBodyPoint(const std::string & aBody,const SimTK::Vec3 & aPoint)338 setBodyPoint(const std::string& aBody, const SimTK::Vec3& aPoint)
339 {
340 if (_model == 0)
341 return;
342 setBody(&_model->updBodySet().get(aBody));
343 setPoint(aPoint);
344
345 }
346 //_____________________________________________________________________________
347 /**
348 * Set the body for which the induced accelerations are to be computed.
349 *
350 * @param aBody Body ID
351 */
setBody(const PhysicalFrame * aBody)352 void PointKinematics::setBody(const PhysicalFrame* aBody)
353 {
354 // CHECK
355 if (aBody==NULL) {
356 printf("PointKinematics.setBody: WARN- invalid body pointer %p.\n", aBody);
357 _body = NULL;
358 return;
359 }
360
361 // SET
362 _body = aBody;
363 _bodyName = _body->getName();
364 cout<<"PointKinematics.setBody: set body to "<<_bodyName<<endl;
365 }
setRelativeToBody(const PhysicalFrame * aBody)366 void PointKinematics::setRelativeToBody(const PhysicalFrame* aBody)
367 {
368 // CHECK
369 if (aBody==NULL) {
370 printf("PointKinematics.setRelativeToBody: WARN- invalid body pointer %p.\n", aBody);
371 _body = NULL;
372 return;
373 }
374
375 // SET
376 _relativeToBody = aBody;
377 _relativeToBodyName = aBody->getName();
378 cout<<"PointKinematics.setRelativeToBody: set relative-to body to "<<_bodyName<<endl;
379 }
380
381 //_____________________________________________________________________________
382 /**
383 * Get the body for which the induced accelerations are to be computed.
384 *
385 * @return Body pointer
386 */
getBody() const387 const PhysicalFrame* PointKinematics::getBody() const
388 {
389 return(_body);
390 }
391
getRelativeToBody() const392 const PhysicalFrame* PointKinematics::getRelativeToBody() const
393 {
394 return(_relativeToBody);
395 }
396
397 //-----------------------------------------------------------------------------
398 // POINT
399 //-----------------------------------------------------------------------------
400 //_____________________________________________________________________________
401 /**
402 * Set the point for which the induced accelerations are to be computed.
403 *
404 * @param aPoint X-Y-Z Point
405 */
406 void PointKinematics::
setPoint(const SimTK::Vec3 & aPoint)407 setPoint(const SimTK::Vec3& aPoint)
408 {
409 _point = aPoint;
410 }
411 //_____________________________________________________________________________
412 /**
413 * Get the point for which the induced accelerations are to be computed.
414 *
415 * @param rPoint X-Y-Z Point
416 */
417 void PointKinematics::
getPoint(SimTK::Vec3 & rPoint)418 getPoint(SimTK::Vec3& rPoint)
419 {
420 rPoint = _point;
421 }
422
423 //-----------------------------------------------------------------------------
424 // POINT NAME
425 //-----------------------------------------------------------------------------
426 //_____________________________________________________________________________
427 /**
428 * Set a name for the point.
429 *
430 * @param aName Name for the point.
431 */
432 void PointKinematics::
setPointName(const string & aName)433 setPointName(const string &aName)
434 {
435 _pointName = aName;
436 constructColumnLabels();
437 if(_aStore!=NULL) _aStore->setColumnLabels(getColumnLabels());
438 if(_vStore!=NULL) _vStore->setColumnLabels(getColumnLabels());
439 if(_pStore!=NULL) _pStore->setColumnLabels(getColumnLabels());
440 }
441 //_____________________________________________________________________________
442 /**
443 * Get the point name.
444 *
445 * @param aName Name for the point.
446 */
447 const std::string &PointKinematics::
getPointName()448 getPointName()
449 {
450 return(_pointName);
451 }
452
453 //-----------------------------------------------------------------------------
454 // STORAGE
455 //-----------------------------------------------------------------------------
456 //_____________________________________________________________________________
457 /**
458 * Get the acceleration storage.
459 *
460 * @return Acceleration storage.
461 */
462 Storage* PointKinematics::
getAccelerationStorage()463 getAccelerationStorage()
464 {
465 return(_aStore);
466 }
467 //_____________________________________________________________________________
468 /**
469 * Get the velocity storage.
470 *
471 * @return Velocity storage.
472 */
473 Storage* PointKinematics::
getVelocityStorage()474 getVelocityStorage()
475 {
476 return(_vStore);
477 }
478 //_____________________________________________________________________________
479 /**
480 * Get the position storage.
481 *
482 * @return Position storage.
483 */
484 Storage* PointKinematics::
getPositionStorage()485 getPositionStorage()
486 {
487 return(_pStore);
488 }
489
490 //-----------------------------------------------------------------------------
491 // STORAGE CAPACITY
492 //-----------------------------------------------------------------------------
493 //_____________________________________________________________________________
494 /**
495 * Set the capacity increments of all storage instances.
496 *
497 * @param aIncrement Increment by which storage capacities will be increased
498 * when storage capacities run out.
499 */
500 void PointKinematics::
setStorageCapacityIncrements(int aIncrement)501 setStorageCapacityIncrements(int aIncrement)
502 {
503 _aStore->setCapacityIncrement(aIncrement);
504 _vStore->setCapacityIncrement(aIncrement);
505 _pStore->setCapacityIncrement(aIncrement);
506 }
507
508
509
510 //=============================================================================
511 // ANALYSIS
512 //=============================================================================
513 //_____________________________________________________________________________
514 /**
515 * Record the kinematics.
516 */
517 int PointKinematics::
record(const SimTK::State & s)518 record(const SimTK::State& s)
519 {
520 // VARIABLES
521 SimTK::Vec3 vec;
522
523 const double& time = s.getTime();
524 const Ground& ground = _model->getGround();
525
526 // POSITION
527 vec = _body->findStationLocationInGround(s, _point);
528 if(_relativeToBody){
529 vec = ground.findStationLocationInAnotherFrame(s, vec, *_relativeToBody);
530 }
531
532 _pStore->append(time, vec);
533
534 // VELOCITY
535 vec = _body->findStationVelocityInGround(s, _point);
536 if(_relativeToBody){
537 vec = ground.expressVectorInAnotherFrame(s, vec, *_relativeToBody);
538 }
539
540 _vStore->append(time, vec);
541
542 // ACCELERATIONS
543 _model->getMultibodySystem().realize(s, SimTK::Stage::Acceleration);
544 vec = _body->findStationAccelerationInGround(s, _point);
545 if(_relativeToBody){
546 vec = ground.expressVectorInAnotherFrame(s, vec, *_relativeToBody);
547 }
548
549 _aStore->append(time, vec);
550
551 return(0);
552 }
553 //_____________________________________________________________________________
554 /**
555 * This method is called at the beginning of an analysis so that any
556 * necessary initializations may be performed.
557 *
558 * This method is meant to be called at the beginning of an integration
559 *
560 * This method should be overridden in the child class. It is
561 * included here so that the child class will not have to implement it if it
562 * is not necessary.
563 *
564 * @param s current system state
565 * @param aClientData General use pointer for sending in client data.
566 *
567 * @return -1 on error, 0 otherwise.
568 */
569 int PointKinematics::
begin(const SimTK::State & s)570 begin( const SimTK::State& s)
571 {
572 if(!proceed()) return(0);
573
574 // RESET STORAGE
575 _pStore->reset(s.getTime());
576 _vStore->reset(s.getTime());
577 _aStore->reset(s.getTime());
578
579 int status = record(s);
580
581 return(status);
582 }
583 //_____________________________________________________________________________
584 /**
585 * This method is called to perform the analysis. It can be called during
586 * the execution of a forward integrations or after the integration by
587 * feeding it the necessary data.
588 *
589 * When called during an integration, this method is meant to be called
590 *
591 * This method should be overridden in derived classes. It is
592 * included here so that the derived class will not have to implement it if
593 * it is not necessary.
594 *
595 * @param s current state of system
596 * @param aClientData General use pointer for sending in client data.
597 *
598 * @return -1 on error, 0 otherwise.
599 */
600 int PointKinematics::
step(const SimTK::State & s,int stepNumber)601 step(const SimTK::State& s, int stepNumber)
602 {
603 if(!proceed(stepNumber)) return(0);
604
605 record(s);
606
607 return(0);
608 }
609 //_____________________________________________________________________________
610 /**
611 * This method is called at the end of an analysis so that any
612 * necessary finalizations may be performed.
613 *
614 * This method is meant to be called at the end of an integration
615 *
616 * This method should be overridden in the child class. It is
617 * included here so that the child class will not have to implement it if it
618 * is not necessary.
619 *
620 * @param s current state of system
621 * @param aClientData General use pointer for sending in client data.
622 *
623 * @return -1 on error, 0 otherwise.
624 */
625 int PointKinematics::
end(const SimTK::State & s)626 end( const SimTK::State& s)
627 {
628 if(!proceed()) return(0);
629 record(s);
630 cout<<"PointKinematics.end: Finalizing analysis "<<getName()<<".\n";
631 return(0);
632 }
633
634
635
636
637 //=============================================================================
638 // IO
639 //=============================================================================
640 //_____________________________________________________________________________
641 /**
642 * Print results.
643 *
644 * The file names are constructed as
645 * aDir + "/" + aBaseName + "_" + ComponentName + aExtension
646 *
647 * @param aDir Directory in which the results reside.
648 * @param aBaseName Base file name.
649 * @param aDT Desired time interval between adjacent storage vectors. Linear
650 * interpolation is used to print the data out at the desired interval.
651 * @param aExtension File extension.
652 *
653 * @return 0 on success, -1 on error.
654 */
655 int PointKinematics::
printResults(const string & aBaseName,const string & aDir,double aDT,const string & aExtension)656 printResults(const string &aBaseName,const string &aDir,double aDT,
657 const string &aExtension)
658 {
659 // ACCELERATIONS
660 Storage::printResult(_aStore,aBaseName+"_"+getName()+"_"+getPointName()+"_acc",aDir,aDT,aExtension);
661
662 // VELOCITIES
663 Storage::printResult(_vStore,aBaseName+"_"+getName()+"_"+getPointName()+"_vel",aDir,aDT,aExtension);
664
665 // POSITIONS
666 Storage::printResult(_pStore,aBaseName+"_"+getName()+"_"+getPointName()+"_pos",aDir,aDT,aExtension);
667
668 return(0);
669 }
670
671
672