1 /* -------------------------------------------------------------------------- *
2  *                          OpenSim:  Kinematics.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                                               *
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 <OpenSim/Simulation/Model/Model.h>
29 #include "Kinematics.h"
30 
31 
32 using namespace OpenSim;
33 using namespace std;
34 
35 
36 //=============================================================================
37 // CONSTANTS
38 //=============================================================================
39 
40 
41 
42 //=============================================================================
43 // CONSTRUCTOR(S) AND DESTRUCTOR
44 //=============================================================================
45 //_____________________________________________________________________________
46 /**
47  * Destructor.
48  */
~Kinematics()49 Kinematics::~Kinematics()
50 {
51 }
52 //_____________________________________________________________________________
53 /**
54  * Construct an Kinematics object for recording the kinematics of
55  * a model's generalized coordinates during a simulation.
56  *
57  * @param aModel Model for which the kinematics are to be recorded.
58  */
Kinematics(Model * aModel)59 Kinematics::Kinematics(Model *aModel) :
60     Analysis(aModel)
61 {
62     // NULL
63     setNull();
64 
65     // DESCRIPTION
66     constructDescription();
67 
68     // CHECK MODEL
69     if(aModel==NULL) return;
70     setModel(*aModel);
71 
72 }
73 //=============================================================================
74 // Object Overrides
75 //=============================================================================
76 //_____________________________________________________________________________
77 /**
78  * Construct an object from file.
79  *
80  * The object is constructed from the root element of the XML document.
81  * The type of object is the tag name of the XML root element.
82  *
83  * @param aFileName File name of the document.
84  */
Kinematics(const std::string & aFileName)85 Kinematics::Kinematics(const std::string &aFileName):
86     Analysis(aFileName, false)
87 {
88     setNull();
89 
90     // Serialize from XML
91     updateFromXMLDocument();
92 
93     // CONSTRUCT DESCRIPTION AND LABELS
94     constructDescription();
95 
96 }
97 
98 //_____________________________________________________________________________
99 /**
100  * Set all member variables to their null or default values.
101  */
102 void Kinematics::
setNull()103 setNull()
104 {
105     constructProperties();
106 
107     setName("Kinematics");
108     _pStore=_vStore=_aStore=NULL;
109 
110     // Let the list own the storages so we don't have to manually delete them
111     _storageList.setMemoryOwner(true);
112 
113     _recordAccelerations = true;
114 }
115 //_____________________________________________________________________________
116 /**
117  * Connect properties to local pointers.
118  */
119 void Kinematics::
constructProperties()120 constructProperties()
121 {
122     Array<std::string> coordArray;
123     coordArray.setSize(1);
124     coordArray[0] = "all";
125     constructProperty_coordinates(coordArray);
126 }
127 
128 //=============================================================================
129 // CONSTRUCTION METHODS
130 //=============================================================================
131 //_____________________________________________________________________________
132 /**
133  * Allocate storage for the kinematics.
134  */
allocateStorage()135 void Kinematics::allocateStorage()
136 {
137     //Free-up any past storages by resizing which will delete pointers
138     //if memory owner is set to true, as set in setNull() above
139     _storageList.setSize(0);
140 
141     // ACCELERATIONS
142     if(_recordAccelerations) {
143         _aStore = new Storage(1000,"Accelerations");
144         _aStore->setDescription(getDescription());
145         _storageList.append(_aStore);
146     }
147 
148     // VELOCITIES
149     _vStore = new Storage(1000,"Speeds");
150     _vStore->setDescription(getDescription());
151     _storageList.append(_vStore);
152 
153     // POSITIONS
154     _pStore = new Storage(1000,"Coordinates");
155     _pStore->setDescription(getDescription());
156     _storageList.append(_pStore);
157 }
158 
159 
160 //=============================================================================
161 // DESTRUCTION METHODS
162 //=============================================================================
163 //_____________________________________________________________________________
164 /**
165  * Delete storage objects.
166  */
167 void Kinematics::
deleteStorage()168 deleteStorage()
169 {
170     if(_aStore!=NULL) { delete _aStore;  _aStore=NULL; }
171     if(_vStore!=NULL) { delete _vStore;  _vStore=NULL; }
172     if(_pStore!=NULL) { delete _pStore;  _pStore=NULL; }
173 }
174 
175 
176 //_____________________________________________________________________________
177 /**
178  * Update coordinates to record
179  */
180 void Kinematics::
updateCoordinatesToRecord()181 updateCoordinatesToRecord()
182 {
183     if(!_model) {
184         _coordinateIndices.setSize(0);
185         _values.setSize(0);
186         return;
187     }
188 
189     const CoordinateSet& coordSet = _model->getCoordinateSet();
190     _coordinateIndices.setSize(getProperty_coordinates().size());
191     for(int i=0; i<getProperty_coordinates().size(); i++) {
192         if(get_coordinates(i) == "all") {
193             _coordinateIndices.setSize(coordSet.getSize());
194             for(int j=0;j<coordSet.getSize();j++) _coordinateIndices[j]=j;
195             break;
196         }
197 
198         int index = coordSet.getIndex(get_coordinates(i));
199         if(index<0)
200             throw Exception("Kinematics: ERR- Could not find coordinate named '"+get_coordinates(i)+"'",__FILE__,__LINE__);
201         _coordinateIndices[i] = index;
202     }
203     _values.setSize(_coordinateIndices.getSize());
204 
205     if(_values.getSize()==0) {
206          cout << "WARNING: Kinematics analysis has no coordinates to record values for" << endl;
207     }
208 }
209 
210 //=============================================================================
211 // GET AND SET
212 //=============================================================================
213 //-----------------------------------------------------------------------------
214 // DESCRIPTION
215 //-----------------------------------------------------------------------------
216 //_____________________________________________________________________________
217 /**
218  * Construct the description for the kinematics files.
219  */
220 void Kinematics::
constructDescription()221 constructDescription()
222 {
223     char descrip[1024];
224 
225     strcpy(descrip,"\nUnits are S.I. units (second, meters, Newtons, ...)");
226     if(getInDegrees()) {
227         strcat(descrip,"\nAngles are in degrees.");
228     } else {
229         strcat(descrip,"\nAngles are in radians.");
230     }
231     strcat(descrip,"\n\n");
232 
233     setDescription(descrip);
234 }
235 
236 //-----------------------------------------------------------------------------
237 // COLUMN LABELS
238 //-----------------------------------------------------------------------------
239 //_____________________________________________________________________________
240 /**
241  * Construct the column labels for the kinematics files.
242  */
243 void Kinematics::
constructColumnLabels()244 constructColumnLabels()
245 {
246     // CHECK FOR NULL
247     if (!_model || _model->getNumSpeeds() == 0)
248     {
249         setColumnLabels(Array<string>());
250         return;
251     }
252 
253     Array<string> labels;
254     labels.append("time");
255     const CoordinateSet& cs = _model->getCoordinateSet();
256     for(int i=0; i<_coordinateIndices.getSize(); i++) {
257         labels.append(cs.get(_coordinateIndices[i]).getName());
258     }
259 
260     setColumnLabels(labels);
261     if (_pStore){
262         _pStore->setColumnLabels(getColumnLabels());
263         if (getInDegrees()) _pStore->setInDegrees(true);
264     }
265     if (_vStore) {
266         _vStore->setColumnLabels(getColumnLabels());
267         if (getInDegrees()) _vStore->setInDegrees(true);
268     }
269     if (_aStore) {
270         _aStore->setColumnLabels(getColumnLabels());
271         if (getInDegrees()) _aStore->setInDegrees(true);
272     }
273 }
274 
275 
276 //-----------------------------------------------------------------------------
277 // STORAGE
278 //-----------------------------------------------------------------------------
279 //_____________________________________________________________________________
280 /**
281  * Get the acceleration storage.
282  *
283  * @return Acceleration storage.
284  */
285 Storage* Kinematics::
getAccelerationStorage()286 getAccelerationStorage()
287 {
288     return(_aStore);
289 }
290 //_____________________________________________________________________________
291 /**
292  * Get the velocity storage.
293  *
294  * @return Velocity storage.
295  */
296 Storage* Kinematics::
getVelocityStorage()297 getVelocityStorage()
298 {
299     return(_vStore);
300 }
301 //_____________________________________________________________________________
302 /**
303  * Get the position storage.
304  *
305  * @return Position storage.
306  */
307 Storage* Kinematics::
getPositionStorage()308 getPositionStorage()
309 {
310     return(_pStore);
311 }
312 //_____________________________________________________________________________
313 /**
314  * Set the model pointer for analyzing kinematics.
315  */
setModel(Model & aModel)316 void Kinematics::setModel(Model& aModel)
317 {
318     // BASE CLASS
319     Analysis::setModel(aModel);
320 
321     // Allocate storages to contain the results of the analysis
322     allocateStorage();
323 }
324 
325 //-----------------------------------------------------------------------------
326 // STORAGE CAPACITY
327 //-----------------------------------------------------------------------------
328 //_____________________________________________________________________________
329 /**
330  * Set the capacity increments of all storage instances.
331  *
332  * @param aIncrement Increment by which storage capacities will be increased
333  * when storage capacities run out.
334  */
335 void Kinematics::
setStorageCapacityIncrements(int aIncrement)336 setStorageCapacityIncrements(int aIncrement)
337 {
338     if (_aStore) _aStore->setCapacityIncrement(aIncrement);
339     _vStore->setCapacityIncrement(aIncrement);
340     _pStore->setCapacityIncrement(aIncrement);
341 }
342 
343 
344 //=============================================================================
345 // ANALYSIS
346 //=============================================================================
347 //_____________________________________________________________________________
348 /**
349  * Record the kinematics.
350  *
351  * @return 0 of success, -1 on error.
352  */
record(const SimTK::State & s)353 int Kinematics::record(const SimTK::State& s)
354 {
355     if(_recordAccelerations){
356         _model->getMultibodySystem().realize(s, SimTK::Stage::Acceleration);
357     }
358     else{
359         _model->getMultibodySystem().realize(s, SimTK::Stage::Velocity);
360     }
361     // RECORD RESULTS
362     const CoordinateSet& cs = _model->getCoordinateSet();
363     int nvalues = _coordinateIndices.getSize();
364     for(int i=0;i<nvalues;i++){
365         _values[i] = cs[_coordinateIndices[i]].getValue(s);
366         if(getInDegrees() && (cs[_coordinateIndices[i]].getMotionType() == Coordinate::Rotational))
367             _values[i] *= SimTK_RADIAN_TO_DEGREE;
368     }
369     _pStore->append(s.getTime(),nvalues,&_values[0]);
370 
371     for(int i=0;i<nvalues;i++){
372         _values[i] = cs[_coordinateIndices[i]].getSpeedValue(s);
373         if(getInDegrees() && (cs[_coordinateIndices[i]].getMotionType() == Coordinate::Rotational))
374             _values[i] *= SimTK_RADIAN_TO_DEGREE;
375     }
376 
377     _vStore->append(s.getTime(),nvalues,&_values[0]);
378 
379     if(_recordAccelerations) {
380         for(int i=0;i<nvalues;i++){
381             _values[i] = cs[_coordinateIndices[i]].getAccelerationValue(s);
382             if(getInDegrees() && (cs[_coordinateIndices[i]].getMotionType() == Coordinate::Rotational))
383                 _values[i] *= SimTK_RADIAN_TO_DEGREE;
384         }
385         _aStore->append(s.getTime(),nvalues,&_values[0]);
386     }
387 
388 
389     return(0);
390 }
391 //_____________________________________________________________________________
392 /**
393  * This method is called at the beginning of an analysis so that any
394  * necessary initializations may be performed.
395  *
396  * This method is meant to be called at the beginning of an integration
397  *
398  * @param s current state of System
399  *
400  * @return -1 on error, 0 otherwise.
401  */
402 int Kinematics::
begin(const SimTK::State & s)403 begin( const SimTK::State& s )
404 {
405     if(!proceed()) return(0);
406 
407     double time = s.getTime();
408 
409     // UPDATE LABELS
410     updateCoordinatesToRecord();
411     constructColumnLabels();
412 
413     // RESET STORAGE
414     _pStore->reset(time);
415     _vStore->reset(time);
416     if (_aStore) _aStore->reset(time);
417 
418     // RECORD
419     int status = 0;
420     if(_pStore->getSize()<=0) {
421         if(_recordAccelerations && s.getSystemStage() < SimTK::Stage::Acceleration )
422             _model->getMultibodySystem().realize(s, SimTK::Stage::Acceleration);
423         else
424             _model->getMultibodySystem().realize(s, SimTK::Stage::Velocity);
425 
426         status = record(s);
427     }
428 
429     return(status);
430 }
431 //_____________________________________________________________________________
432 /**
433  * This method is called to perform the analysis.  It can be called during
434  * the execution of a forward integrations or after the integration by
435  * feeding it the necessary data.
436  *
437  * When called during an integration, this method is meant to be called
438  *
439  * This method should be overridden in derived classes.  It is
440  * included here so that the derived class will not have to implement it if
441  * it is not necessary.
442  *
443  * @param s current state of system
444  *
445  * @return -1 on error, 0 otherwise.
446  */
447 int Kinematics::
step(const SimTK::State & s,int stepNumber)448 step(const SimTK::State& s, int stepNumber )
449 {
450     if(proceed(stepNumber) && getOn()) record(s);
451 
452     return(0);
453 }
454 //_____________________________________________________________________________
455 /**
456  * This method is called at the end of an analysis so that any
457  * necessary finalizations may be performed.
458  *
459  * This method is meant to be called at the end of an integration
460  *
461  * This method should be overridden in the child class.  It is
462  * included here so that the child class will not have to implement it if it
463  * is not necessary.
464  *
465  * @param s current state of System
466  *
467  * @return -1 on error, 0 otherwise.
468  */
469 int Kinematics::
end(const SimTK::State & s)470 end( const SimTK::State& s )
471 {
472     if (!proceed()) return 0;
473 
474     record(s);
475 
476     return(0);
477 }
478 
479 
480 
481 
482 //=============================================================================
483 // IO
484 //=============================================================================
485 //_____________________________________________________________________________
486 /**
487  * Print results.
488  *
489  * The file names are constructed as
490  * aDir + "/" + aBaseName + "_" + ComponentName + aExtension
491  *
492  * @param aDir Directory in which the results reside.
493  * @param aBaseName Base file name.
494  * @param aDT Desired time interval between adjacent storage vectors.  Linear
495  * interpolation is used to print the data out at the desired interval.
496  * @param aExtension File extension.
497  *
498  * @return 0 on success, -1 on error.
499  */
500 int Kinematics::
printResults(const string & aBaseName,const string & aDir,double aDT,const string & aExtension)501 printResults(const string &aBaseName,const string &aDir,double aDT,
502                  const string &aExtension)
503 {
504     if(!getOn()) {
505         printf("Kinematics.printResults: Off- not printing.\n");
506         return(0);
507     }
508 
509     // ACCELERATIONS
510     if(_recordAccelerations) {
511         Storage::printResult(_aStore,aBaseName+"_"+getName()+"_dudt",aDir,aDT,aExtension);
512     }
513 
514     // VELOCITIES
515     Storage::printResult(_vStore,aBaseName+"_"+getName()+"_u",aDir,aDT,aExtension);
516 
517     // POSITIONS
518     Storage::printResult(_pStore,aBaseName+"_"+getName()+"_q",aDir,aDT,aExtension);
519 
520     /*
521     // POSITIONS (.mot file)
522     // For now (until we resolve the .sto vs .mot issue) also write
523     // an .mot file so the motion can be viewed in SIMM -- Eran.
524     bool writeSIMMHeader=_pStore->getWriteSIMMHeader();
525     _pStore->setWriteSIMMHeader(true);
526     Storage::printResult(_pStore,aBaseName,aDir,aDT,".mot");
527     _pStore->setWriteSIMMHeader(writeSIMMHeader);
528     */
529     return(0);
530 }
531