1 /* -------------------------------------------------------------------------- *
2  *                           OpenSim:  Storage.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 /* Note: This code was originally developed by Realistic Dynamics Inc.
25  * Author: Frank C. Anderson
26  */
27 
28 
29 // INCLUDES
30 #include <iostream>
31 #include "IO.h"
32 #include "Signal.h"
33 #include "Storage.h"
34 #include "GCVSplineSet.h"
35 #include "SimmMacros.h"
36 #include "SimTKcommon.h"
37 #include "GCVSpline.h"
38 #include "StateVector.h"
39 #include "STOFileAdapter.h"
40 #include "TimeSeriesTable.h"
41 
42 using namespace OpenSim;
43 using namespace std;
44 
convertTableToStorage(const AbstractDataTable * table,Storage & sto)45 void convertTableToStorage(const AbstractDataTable* table, Storage& sto)
46 {
47     sto.purge();
48     TimeSeriesTable out;
49 
50     if (auto td = dynamic_cast<const TimeSeriesTable*>(table))
51         // Table is already flattened, so clone for further processing
52         out = TimeSeriesTable{ *td };
53     else if (auto tst = dynamic_cast<const TimeSeriesTable_<SimTK::Vec2>*>(table))
54         out = tst->flatten();
55     else if (auto tst = dynamic_cast<const TimeSeriesTable_<SimTK::Vec3>*>(table))
56         out = tst->flatten({ "_x", "_y", "_z" });
57     else if (auto tst = dynamic_cast<const TimeSeriesTable_<SimTK::Vec4>*>(table))
58         out = tst->flatten();
59     else if (auto tst = dynamic_cast<const TimeSeriesTable_<SimTK::Vec5>*>(table))
60         out = tst->flatten();
61     else if (auto tst = dynamic_cast<const TimeSeriesTable_<SimTK::Vec6>*>(table))
62         out = tst->flatten();
63     else if (auto tst = dynamic_cast<const TimeSeriesTable_<SimTK::Vec7>*>(table))
64         out = tst->flatten();
65     else if (auto tst = dynamic_cast<const TimeSeriesTable_<SimTK::Vec8>*>(table))
66         out = tst->flatten();
67     else if (auto tst = dynamic_cast<const TimeSeriesTable_<SimTK::Vec9>*>(table))
68         out = tst->flatten();
69     else if (auto tst = dynamic_cast<const TimeSeriesTable_<SimTK::Vec<10>>*>(table))
70         out = tst->flatten();
71     else if (auto tst = dynamic_cast<const TimeSeriesTable_<SimTK::Vec<11>>*>(table))
72         out = tst->flatten();
73     else if (auto tst = dynamic_cast<const TimeSeriesTable_<SimTK::Vec<12>>*>(table))
74         out = tst->flatten();
75     else if (auto tst = dynamic_cast<const TimeSeriesTable_<SimTK::UnitVec3>*>(table))
76         out = tst->flatten({ "_x", "_y", "_z" });
77     else if (auto tst = dynamic_cast<const TimeSeriesTable_<SimTK::Quaternion>*>(table))
78         out = tst->flatten();
79     else if (auto tst = dynamic_cast<const TimeSeriesTable_<SimTK::SpatialVec>*>(table))
80         out = tst->flatten({ "_rx", "_ry", "_rz", "_tx", "_ty", "_tz" });
81     else {
82         OPENSIM_THROW( STODataTypeNotSupported, typeid(table).name());
83     }
84 
85     OpenSim::Array<std::string> labels("", (int)out.getNumColumns() + 1);
86     labels[0] = "time";
87     for (int i = 0; i < (int)out.getNumColumns(); ++i) {
88         labels[i + 1] = out.getColumnLabel(i);
89     }
90     sto.setColumnLabels(labels);
91 
92     const auto& times = out.getIndependentColumn();
93     for (unsigned i_time = 0; i_time < out.getNumRows(); ++i_time) {
94         const SimTK::Vector rowVector =
95             out.getRowAtIndex(i_time).transpose().getAsVector();
96         sto.append(times[i_time], rowVector);
97     }
98 }
99 
100 
101 //============================================================================
102 // DEFINES
103 //============================================================================
104 
105 
106 //============================================================================
107 // CONSTANTS
108 //============================================================================
109 //const double Storage::LARGE_NEGATIVE = -1.0e-30;
110 //const double Storage::LARGE_POSITIVE =  1.0e-30;
111 const char* Storage::DEFAULT_HEADER_TOKEN = "endheader";
112 const char* Storage::DEFAULT_HEADER_SEPARATOR = " \t\r\n";
113 const int Storage::MAX_RESAMPLE_SIZE = 100000;
114 //============================================================================
115 // STATICS
116 //============================================================================
117 string Storage::simmReservedKeys[] = {
118                                   "#",
119                                   "range", "Range",
120                                   "wrap", "Wrap", "WRAP",
121                                   "event",
122                                   "enforce_loops",
123                                   "enforce_constraints",
124                                   "calc_derivatives"};
125 int numSimmReservedKeys=10; // Keep this number in sync with above array size
126 
127 // up version to 20301 for separation of RRATool, CMCTool
128 const int Storage::LatestVersion = 1;
129 
130 //=============================================================================
131 // DESTRUCTOR
132 //=============================================================================
133 //_____________________________________________________________________________
134 /**
135  * Destructor.
136  *
137  * The stored StateVectors are deleted during destruction.
138  */
~Storage()139 Storage::~Storage()
140 {
141 }
142 
143 //=============================================================================
144 // CONSTRUCTOR(S)
145 //=============================================================================
146 //_____________________________________________________________________________
147 /**
148  * Default constructor.
149  */
Storage(int aCapacity,const string & aName)150 Storage::Storage(int aCapacity,const string &aName) :
151     StorageInterface(aName),
152     _storage(StateVector())
153 {
154     // SET NULL STATES
155     setNull();
156 
157     // CAPACITY
158     _storage.ensureCapacity(aCapacity);
159     _storage.setCapacityIncrement(-1);
160 
161     _fileVersion = Storage::LatestVersion;
162     // SET THE STATES
163     setName(aName);
164 }
165 //_____________________________________________________________________________
166 /*
167  * Construct an Storage instance from file.
168  *
169  */
Storage(const string & fileName,bool readHeadersOnly)170 Storage::Storage(const string &fileName, bool readHeadersOnly) :
171     StorageInterface(fileName),
172     _storage(StateVector())
173 {
174     // SET NULL STATES
175     setNull();
176 
177     // OPEN FILE
178     std::unique_ptr<ifstream> fp{IO::OpenInputFile(fileName)};
179     OPENSIM_THROW_IF(fp == nullptr, Exception,
180             "Storage: Failed to open file '" + fileName +
181             "'. Verify that the file exists at the specified location." );
182 
183     bool isMotFile = SimTK::String::toLower(fileName).rfind(".mot") != string::npos;
184     bool isStoFile = SimTK::String::toLower(fileName).rfind(".sto") != string::npos;
185     bool useFileAdpater = true;
186 
187     int nr = 0, nc = 0;
188 
189     if (isMotFile || isStoFile) {
190         parseHeaders(*fp, nr, nc);
191         if (_fileVersion <= 1) { // If an old .sto or .mot format
192             // Must have valid number of rows and columns
193             OPENSIM_THROW_IF(nr < 1 && nc < 1, Exception,
194                 "Storage: Failed to parse headers of file "
195                 + fileName);
196             // Checks out as a valid old format, so use legacy code to process
197             useFileAdpater = false;
198         }
199     }
200 
201     if (useFileAdpater) { // For new .sto files and others that are not .mot
202         try {
203             // Try using FileAdpater to read all file types
204             OPENSIM_THROW_IF(readHeadersOnly, Exception,
205                 "Cannot read headers only if not a STO file or its "
206                 "version is greater than 1.");
207             auto dataAdapter = FileAdapter::createAdapterFromExtension(fileName);
208             FileAdapter::OutputTables tables = dataAdapter->read(fileName);
209             if (tables.size() > 1) {
210                 cout << "Storage: cannot read data files with multiple tables. "
211                     << "Only the first table '" << tables.begin()->first << "' will "
212                     << "be loaded as Storage." << endl;
213             }
214             convertTableToStorage(tables.begin()->second.get(), *this);
215             return;
216         }
217         catch (const std::exception& x) {
218             cout << "Storage: FileAdpater failed to read data file.\n"
219                 << x.what() << endl;
220             if (isStoFile)
221                 cout << "Reverting to use conventional Storage reader." << endl;
222             else
223                 throw x;
224         }
225     }
226 
227     // Process file as if it were a .mot file
228     if (getDebugLevel() >= 0) {
229         cout << "Storage: read data file =" << fileName
230             << " (nr=" << nr << " nc=" << nc << ")" << endl;
231     }
232     // Motion files from SIMM are in degrees
233     if (_fileVersion < 1 && isMotFile) {
234         _inDegrees = true;
235     }
236     if (_fileVersion < 1) {
237         cout << ".. assuming rotations in "
238             << (_inDegrees?"Degrees.":"Radians.") << endl;
239     }
240 
241     // IGNORE blank lines after header -- treat \r and \n as end of line chars
242     while(fp->good()) {
243         int c = fp->peek();
244         if(c!='\n' && c!='\r' && c!='\t' && c!=' ') break;
245         fp->get();
246     }
247     // Ayman: Support for oldStyleDescription dropped. 04/11/07
248 
249     // COLUMN LABELS
250     string line;
251     getline(*fp, line);
252     parseColumnLabels(line.c_str());
253 
254     if (_columnLabels.getSize()!= nc){
255         std::cout << "Storage: Warning- inconsistent headers in file " <<
256             fileName << ". nColumns=" << nc << " but "
257             << _columnLabels.getSize() << " were found" << std::endl;
258     }
259     // CAPACITY
260     _storage.ensureCapacity(nr);
261     _storage.setCapacityIncrement(-1);
262 
263     // There are situations where we don't want to read the whole file in advance just header
264     if (readHeadersOnly) return;
265 
266     //MM using the occurrence of time and range in the column labels to distinguish between
267     //SIMM and non SIMMOtion files.
268     Array<std::string> currentLabels = getColumnLabels();
269     int indexTime = currentLabels.findIndex("time");
270     int indexRange = currentLabels.findIndex("range");
271 
272 
273     // DATA
274     if(indexTime != -1 || indexRange != -1){ //MM edit
275         int ny = nc-1;
276         double time;
277         double *y = new double[ny];
278         for(int r=0;r<nr;r++) {
279                 (*fp)>>time;
280                 for(int i=0;i<ny;i++)
281                     (*fp)>>y[i];
282                 append(time,ny,y);
283         }
284         delete[] y;
285     }else{  //MM the modifications below are to make the Storage class
286             //well behaved when it is given data that does not contain a
287             //time or a range column
288         int ny = nc;
289         double time;
290         double *y = new double[ny];
291         for(int r=0;r<nr;r++) {
292                 time=(double)r;
293                 for(int i=0;i<ny;i++)
294                     (*fp)>>y[i];
295                 append(time,ny,y);
296         }
297         delete[] y;
298     }
299     // If what we read was really a sIMM motion file, adjust the data
300     // to account for different assumptions between SIMM.mot OpenSim.sto
301 
302     //MM if this is a SIMM Motion file, post process it as one. Else don't touch the data
303     if(indexTime == -1){
304         postProcessSIMMMotion();
305     }
306 }
307 //_____________________________________________________________________________
308 /**
309  * Copy constructor.
310  */
Storage(const Storage & aStorage,bool aCopyData)311 Storage::Storage(const Storage &aStorage,bool aCopyData) :
312     StorageInterface(aStorage),
313     _storage(StateVector())
314 {
315     // NULL THE DATA
316     setNull();
317 
318     // CAPACITY
319     _storage.ensureCapacity(aStorage._storage.getCapacity());
320     _storage.setCapacityIncrement(aStorage._storage.getCapacityIncrement());
321 
322     // SET STATES
323     setName(aStorage.getName());
324     setDescription(aStorage.getDescription());
325     setHeaderToken(aStorage.getHeaderToken());
326     setColumnLabels(aStorage.getColumnLabels());
327     setStepInterval(aStorage.getStepInterval());
328     setInDegrees(aStorage.isInDegrees());
329     _fileVersion = aStorage._fileVersion;
330     // COPY STORED DATA
331     if(aCopyData) copyData(aStorage);
332 }
333 //_____________________________________________________________________________
334 /**
335  * Construct a copy of a specified storage taking only a subset of the states.
336  *
337  * @param aStorage Storage to be copied.
338  * @param aStateIndex Index of the state (column) at which to start the copy.
339  * @param aN Number of states to copy.
340  * @param aDelimiter Delimiter used to separate state labels (i.e., column
341  * labels).  The delimiter is assumed to be a tab by default.
342  */
343 Storage::
Storage(const Storage & aStorage,int aStateIndex,int aN,const char * aDelimiter)344 Storage(const Storage &aStorage,int aStateIndex,int aN,
345              const char *aDelimiter) :
346      StorageInterface(aStorage),
347     _storage(StateVector())
348 {
349     // NULL THE DATA
350     setNull();
351 
352     // CAPACITY
353     _storage.ensureCapacity(aStorage._storage.getCapacity());
354     _storage.setCapacityIncrement(aStorage._storage.getCapacityIncrement());
355 
356     // SET STATES
357     setName(aStorage.getName());
358     setDescription(aStorage.getDescription());
359     setHeaderToken(aStorage.getHeaderToken());
360     setStepInterval(aStorage.getStepInterval());
361     setInDegrees(aStorage.isInDegrees());
362     _fileVersion = aStorage._fileVersion;
363     // ERROR CHECK
364     if(aStateIndex<0) return;
365     if(aN<=0) return;
366 
367     // SET THE DATA
368     int i,n;
369     double time,*data = new double[aN];
370     for(i=0;i<aStorage._storage.getSize();i++) {
371         aStorage.getTime(i,time);
372         n = aStorage.getData(i,aStateIndex,aN,data);
373         append(time,n,data);
374     }
375     delete[] data;
376 
377     // COPY THE FIRST COLUMN AND COLUMNS aStateIndex+1 through aStateIndex+aN (corresponding to states aStateIndex - aStateIndex+aN-1)
378     int originalNumCol = aStorage.getColumnLabels().getSize();
379     _columnLabels.setSize(0);
380     if(originalNumCol) {
381         _columnLabels.append(aStorage.getColumnLabels()[0]);
382         for(int i=0;i<aN && aStateIndex+1+i<originalNumCol;i++)
383             _columnLabels.append(aStorage.getColumnLabels()[aStateIndex+1+i]);
384     }
385 }
386 
387 
388 /**
389  * Assignment operator.
390  *
391  * @return Reference to this object.
392  */
operator =(const Storage & aStorage)393 Storage& Storage::operator=(const Storage &aStorage)
394 {
395     // BASE CLASS
396     StorageInterface::operator=(aStorage);
397 
398     // Copy Members
399     copyData(aStorage);
400     return(*this);
401 }
402 
403 
404 //=============================================================================
405 // CONSTRUCTION METHODS
406 //=============================================================================
407 //_____________________________________________________________________________
408 /**
409  * Set all states to their null or default values.
410  */
411 void Storage::
setNull()412 setNull()
413 {
414     _writeSIMMHeader = false;
415     setHeaderToken(DEFAULT_HEADER_TOKEN);
416     _stepInterval = 1;
417     _lastI = 0;
418     _fp = 0;
419     _inDegrees = false;
420 }
421 //_____________________________________________________________________________
422 /**
423  * Copy the data stored by another storage instance.
424  *
425  * Note that this method only copies the stored data.  It does not copy
426  * other members of aStorage such as the name and the description.  To get
427  * a complete copy, the copy constructor should be used.
428  *
429  * If this instance does not have enough capacity to hold the states
430  * of the specified storage (aStorage), the capacity is increased.
431  */
432 void Storage::
copyData(const Storage & aStorage)433 copyData(const Storage &aStorage)
434 {
435     _units = aStorage._units;
436     setInDegrees(aStorage.isInDegrees());
437 
438     // ENSURE CAPACITY
439     _storage.ensureCapacity(aStorage._storage.getCapacity());
440 
441     // COPY
442     //rdPtrArray::reset();
443     _storage.setSize(0);
444     for(int i=0;i<aStorage._storage.getSize();i++) {
445         _storage.append(aStorage._storage[i]);
446     }
447 }
448 
449 
450 
451 //=============================================================================
452 // GET AND SET
453 //=============================================================================
454 //-----------------------------------------------------------------------------
455 // SIMM HEADER
456 //-----------------------------------------------------------------------------
457 //_____________________________________________________________________________
458 /**
459  * Set the whether or not to write a header appropriate for a SIMM motion
460  * file.
461  *
462  * @param aTrueFalse Whether (true) or not (false) to write a SIMM header.
463  */
464 void Storage::
setWriteSIMMHeader(bool aTrueFalse)465 setWriteSIMMHeader(bool aTrueFalse)
466 {
467     _writeSIMMHeader = aTrueFalse;
468     if (_writeSIMMHeader) setInDegrees(true);
469 }
470 //_____________________________________________________________________________
471 /**
472  * Get the whether or not to write a header appropriate for a SIMM motion
473  * file.
474  *
475  * @param aTrueFalse Whether (true) or not (false) to write a SIMM header.
476  */
477 bool Storage::
getWriteSIMMHeader() const478 getWriteSIMMHeader() const
479 {
480     return(_writeSIMMHeader);
481 }
482 
483 //-----------------------------------------------------------------------------
484 // HEADER TOKEN
485 //-----------------------------------------------------------------------------
486 //_____________________________________________________________________________
487 /**
488  * Set the header token.
489  * The header token is used to mark the end of the header
490  * portion of an Storage when an Storage is saved in a file.
491  *
492  * If the header token is NULL, a default header token is used.
493  *
494  * @param aToken Header token.
495  */
496 void Storage::
setHeaderToken(const std::string & aToken)497 setHeaderToken(const std::string &aToken)
498 {
499     if(aToken.empty()) _headerToken = DEFAULT_HEADER_TOKEN;
500     else _headerToken = aToken;
501 }
502 //_____________________________________________________________________________
503 /**
504  * Get the header token of this storage.
505  *
506  * @return Header token.
507  * @see setHeaderToken()
508  */
509 const string &Storage::
getHeaderToken() const510 getHeaderToken() const
511 {
512     return(_headerToken);
513 }
514 
515 //-----------------------------------------------------------------------------
516 // COLUMN LABELS
517 //-----------------------------------------------------------------------------
518 //_____________________________________________________________________________
519 // added a default Parameter for startIndex. -Ayman
520 // TODO startIndex is being ignored.
521 const int Storage::
getStateIndex(const std::string & aColumnName,int startIndex) const522 getStateIndex(const std::string &aColumnName, int startIndex) const
523 {
524     int thisColumnIndex = -1;
525 
526     // This uses the `do while(false)` idiom to run common code if one of a
527     // number of conditions succeeds (much like what a goto would be used for).
528     do {
529         thisColumnIndex = _columnLabels.findIndex(aColumnName);
530         if (thisColumnIndex != -1) break;
531 
532         // 4.0 and its beta versions differ slightly in the absolute path but
533         // the <joint>/<coordinate>/value (or speed) will be common to both.
534         // Likewise, for muscle states <muscle>/activation (or fiber_length)
535         // must be common to the state variable (path) name and column label.
536         std::string shortPath = aColumnName;
537         std::string::size_type front = shortPath.find("/");
538         while (thisColumnIndex < 0 && front < std::string::npos) {
539             shortPath = shortPath.substr(front + 1, aColumnName.length());
540             thisColumnIndex = _columnLabels.findIndex(shortPath);
541             front = shortPath.find("/");
542         }
543         if (thisColumnIndex != -1) break;
544 
545         // Assume column labels follow pre-v4.0 state variable labeling.
546         // Redo search with what the pre-v4.0 label might have been.
547 
548         // First, try just the last element of the path.
549         std::string::size_type back = aColumnName.rfind("/");
550         std::string prefix = aColumnName.substr(0, back);
551         std::string shortName = aColumnName.substr(back + 1,
552             aColumnName.length() - back);
553         thisColumnIndex = _columnLabels.findIndex(shortName);
554         if (thisColumnIndex != -1) break;
555 
556         // If that didn't work, specifically check for coordinate state names
557         // (<coord_name>/value and <coord_name>/speed) and muscle state names
558         // (<muscle_name>/activation <muscle_name>/fiber_length).
559         if (shortName == "value") {
560             // pre-v4.0 did not have "/value" so remove it if here
561             back = prefix.rfind("/");
562             shortName = prefix.substr(back + 1, prefix.length());
563             thisColumnIndex = _columnLabels.findIndex(shortName);
564         }
565         else if (shortName == "speed") {
566             // replace "/speed" (the v4.0 labeling for speeds) with "_u"
567             back = prefix.rfind("/");
568             shortName =
569                 prefix.substr(back + 1, prefix.length() - back) + "_u";
570             thisColumnIndex = _columnLabels.findIndex(shortName);
571         }
572         else if (back < aColumnName.length()) {
573             // try replacing the '/' with '.' in the last segment
574             shortName = aColumnName;
575             shortName.replace(back, 1, ".");
576             back = shortName.rfind("/");
577             shortName = shortName.substr(back + 1,
578                 shortName.length() - back);
579             thisColumnIndex = _columnLabels.findIndex(shortName);
580         }
581         if (thisColumnIndex != -1) break;
582 
583         // If all of the above checks failed, return -1.
584         return -1;
585 
586     } while (false);
587 
588     // If we get here, we successfully found a column.
589     // Subtract 1 because time is included in the labels but not
590     // in the "state vector".
591     return thisColumnIndex - 1;
592 }
593 
594 
595 //_____________________________________________________________________________
596 /**
597  * Set a labels string for the columns in this Storage instance.
598  *
599  * A character string is used to label the columns.  Each separate column
600  * label is usually delimited by a tab ("\t"), but any delimiter may
601  * be used.
602  *
603  * The first column is almost always "Time."  The other columns
604  * correspond to the separate elements of a state vector (StateVector).
605  *
606  * If the labels string is set to NULL, the following default labels
607  * will be used when the Storage instance is saved to file:
608  *
609  * time state_0 state_1 state_2 ...
610  *
611  * @param aLabels Character string containing labels for the columns.
612  */
613 void Storage::
parseColumnLabels(const char * aLabels)614 parseColumnLabels(const char *aLabels)
615 {
616     _columnLabels.setSize(0);
617 
618     // HANDLE NULL POINTER
619     if(aLabels==NULL) return;
620 
621     // HANDLE ZERO LENGTH STRING
622     int len = (int)strlen(aLabels);
623     if(len==0) return;
624 
625     // SET NEW
626     char *labelsCopy = new char[len+1];
627     if(aLabels[len-1]=='\n') {
628         // strip carriage return
629         strncpy(labelsCopy,aLabels,len-1);
630         labelsCopy[len-1] = 0;
631     } else {
632         strcpy(labelsCopy,aLabels);
633     }
634 
635     // Parse
636     char *token = strtok(labelsCopy,DEFAULT_HEADER_SEPARATOR );
637     while(token!=NULL)
638     {
639         // Append column label
640         _columnLabels.append(token);
641 
642         // Get next label
643         token = strtok( NULL, DEFAULT_HEADER_SEPARATOR );
644     }
645 
646     delete[] labelsCopy;
647 }
648 
649 //_____________________________________________________________________________
650 /**
651  * Set column labels array.
652  */
653 void Storage::
setColumnLabels(const Array<std::string> & aColumnLabels)654 setColumnLabels(const Array<std::string> &aColumnLabels)
655 {
656     _columnLabels = aColumnLabels;
657 }
658 
659 //_____________________________________________________________________________
660 /**
661  * Get column labels array.
662  *
663  * @return Character string of column labels.
664  */
665 const Array<string> &Storage::
getColumnLabels() const666 getColumnLabels() const
667 {
668     return(_columnLabels);
669 }
670 
671 //-----------------------------------------------------------------------------
672 // STEP INTERVAL
673 //-----------------------------------------------------------------------------
674 //_____________________________________________________________________________
675 /**
676  * Set the step interval.
677  */
678 void Storage::
setStepInterval(int aStepInterval)679 setStepInterval(int aStepInterval)
680 {
681     _stepInterval = aStepInterval;
682     if(_stepInterval<0) _stepInterval = 0;
683 }
684 //_____________________________________________________________________________
685 /**
686  * Get the step interval.
687  */
688 int Storage::
getStepInterval() const689 getStepInterval() const
690 {
691     return(_stepInterval);
692 }
693 
694 //-----------------------------------------------------------------------------
695 // CAPACITY INCREMENT
696 //-----------------------------------------------------------------------------
697 //_____________________________________________________________________________
698 /**
699  * Set the capacity increment of this storage object.  For details on what
700  * the capacity increment does see Array::setCapacityIncrement().
701  *
702  * @param aIncrement Capacity increment.
703  * @see Array
704  */
705 void Storage::
setCapacityIncrement(int aIncrement)706 setCapacityIncrement(int aIncrement)
707 {
708     _storage.setCapacityIncrement(aIncrement);
709 }
710 //_____________________________________________________________________________
711 /**
712  * Get the capacity increment of this storage object.
713  *
714  * @return Capacity increment of this storage.  For details on what
715  * the capacity increment does see Array::setCapacityIncrement().
716  */
717 int Storage::
getCapacityIncrement() const718 getCapacityIncrement() const
719 {
720     return(_storage.getCapacityIncrement());
721 }
722 
723 //-----------------------------------------------------------------------------
724 // STATEVECTORS
725 //-----------------------------------------------------------------------------
726 //_____________________________________________________________________________
727 /**
728  * Get the smallest number of states.
729  *
730  * Ordinarily, the number of states is the same in each state vector;
731  * however, this is not required.
732  * @return Smallest number of states.
733  */
734 int Storage::
getSmallestNumberOfStates() const735 getSmallestNumberOfStates() const
736 {
737     int n,nmin=0;
738     for(int i=0;i<_storage.getSize();i++) {
739         n = _storage[i].getSize();
740         if(i==0) {
741             nmin = n;
742         } else if(n<nmin) {
743             nmin = n;
744         }
745     }
746 
747     return(nmin);
748 }
749 //_____________________________________________________________________________
750 /**
751  * Get the last states stored.
752  *
753  * @return Statevector.  If no state vector is stored, NULL is returned.
754  */
755 StateVector* Storage::
getLastStateVector() const756 getLastStateVector() const
757 {
758     StateVector *vec = NULL;
759     try {
760         vec = &_storage.updLast();
761     } catch(const Exception&) {
762         //x.print(cout);
763     }
764     return(vec);
765 }
766 //_____________________________________________________________________________
767 /**
768  * Get the StateVector at a specified time index.
769  *
770  * @param aTimeIndex Time index at which to get the state vector:
771  * 0 <= aTimeIndex < _storage.getSize().
772  * @return Statevector. If no valid statevector exists at aTimeIndex, NULL
773  * is returned.
774  */
775 StateVector* Storage::
getStateVector(int aTimeIndex) const776 getStateVector(int aTimeIndex) const
777 {
778     return(&_storage.updElt(aTimeIndex));
779 }
780 
781 //-----------------------------------------------------------------------------
782 // TIME
783 //-----------------------------------------------------------------------------
784 //_____________________________________________________________________________
785 /**
786  * Get the time of the first stored states.
787  *
788  * @return Time of the first stored states.  If there is no stored state,
789  * the constant SimTK::NaN (not a number) is returned.
790  */
791 double Storage::
getFirstTime() const792 getFirstTime() const
793 {
794     if(_storage.getSize()<=0) {
795         return(SimTK::NaN);
796     }
797     return(_storage[0].getTime());
798 }
799 //_____________________________________________________________________________
800 /**
801  * Get the time of the last states.
802  *
803  * @return Time of the first stored states.  If there is no stored state,
804  * the constant SimTK::NaN (not a number) is returned.
805  */
806 double Storage::
getLastTime() const807 getLastTime() const
808 {
809     if(_storage.getSize()<=0) {
810         return(SimTK::NaN);
811     }
812     return(_storage.getLast().getTime());
813 }
814 //_____________________________________________________________________________
815 /**
816  * Get the smallest time step.
817  *
818  * @return Smallest time step.  If there are less than 2 state vectors,  SimTK::Infinity  is returned.
819  */
820 double Storage::
getMinTimeStep() const821 getMinTimeStep() const
822 {
823     double *time=NULL;
824     int n = getTimeColumn(time);
825     double dtmin =  SimTK::Infinity;
826     for(int i=1; i<n; i++) {
827         double dt = time[i] - time[i-1];
828         if(dt<dtmin) dtmin = dt;
829     }
830     delete[] time;
831     return dtmin;
832 }
833 //_____________________________________________________________________________
834 /**
835  * Get the time at a specified time index for a specified state.
836  *
837  * @param aTimeIndex Time index (row) for which to get the time.
838  * @param rTime Time value.
839  * @param aStateIndex Index of the state for which to get the time.
840  * By default, aStateIndex has a value of -1, which means disregard whether
841  * or not there is a valid state- just get the time at aTimeIndex.  If
842  * aStateIndex is non-negative, the time is returned only if there is a valid
843  * state at aStateIndex.
844  * @return true when the time was set, false when there was no valid state.
845  */
846 bool Storage::
getTime(int aTimeIndex,double & rTime,int aStateIndex) const847 getTime(int aTimeIndex,double &rTime,int aStateIndex) const
848 {
849     if(aTimeIndex<0) return false;
850     if(aTimeIndex>_storage.getSize()) return false;
851 
852     // GET STATEVECTOR
853     StateVector &vec = _storage[aTimeIndex];
854 
855     // CHECK FOR VALID STATE
856     if(aStateIndex >= vec.getSize()) return false;
857 
858     // ASSIGN TIME
859     rTime = vec.getTime();
860     return true;
861 }
862 //_____________________________________________________________________________
863 /**
864  * Get the times for a specified state.
865  *
866  * @param rTime Array where times are set.  If rTime is sent in as NULL,
867  * memory is allocated.  If rTime is setn in as non-NULL, it is assumed that
868  * enough memory has been allocated at rTime to hold _storage.getSize() doubles.
869  * @param aStateIndex Index of the state for which to get the times.
870  * By default, aStateIndex has a value of -1, which means disregard whether
871  * or not there is a valid state- just get the times.  If aStateIndex is
872  * non-negative, the time is set only if there is a valid state at aStateIndex.
873  * @return Number of times set.  This can be less than _storage.getSize() if
874  * a state does not exist for all or a subset of the stored statevectors.
875  */
876 int Storage::
getTimeColumn(double * & rTimes,int aStateIndex) const877 getTimeColumn(double *&rTimes,int aStateIndex) const
878 {
879     if(_storage.getSize()<=0) return(0);
880 
881     // ALLOCATE MEMORY
882     if(rTimes==NULL) {
883         rTimes = new double[_storage.getSize()];
884     }
885 
886     // LOOP THROUGH STATEVECTORS
887     int i,nTimes;
888     StateVector *vec;
889     for(i=nTimes=0;i<_storage.getSize();i++) {
890         vec = getStateVector(i);
891         if(vec==NULL) continue;
892         if(aStateIndex >= vec->getSize()) continue;
893         rTimes[nTimes] = vec->getTime();
894         nTimes++;
895     }
896 
897     return(nTimes);
898 }
899 int Storage::
getTimeColumn(Array<double> & rTimes,int aStateIndex) const900 getTimeColumn(Array<double> &rTimes,int aStateIndex) const
901 {
902     if(_storage.getSize()<=0) return(0);
903 
904     rTimes.setSize(_storage.getSize());
905 
906     // LOOP THROUGH STATEVECTORS
907     int i,nTimes;
908     for(i=nTimes=0;i<_storage.getSize();i++) {
909         StateVector *vec = getStateVector(i);
910         if(vec==NULL) continue;
911         if(aStateIndex >= vec->getSize()) continue;
912         rTimes[nTimes] = vec->getTime();
913         nTimes++;
914     }
915 
916     rTimes.setSize(nTimes);
917 
918     return(nTimes);
919 }
920 /**
921  * Get the time column starting at aTime. Return it in rTimes
922  * rTimes is preallocated by the caller.
923  */
924 void Storage::
getTimeColumnWithStartTime(Array<double> & rTimes,double aStartTime) const925 getTimeColumnWithStartTime(Array<double>& rTimes,double aStartTime) const
926 {
927     if(_storage.getSize()<=0) return;
928 
929     int startIndex = findIndex(aStartTime);
930 
931     double *timesVec=0;
932     int size = getTimeColumn(timesVec);
933 
934     for(int i=startIndex; i<size; i++)
935         rTimes.append(timesVec[i]);
936     delete[] timesVec;
937 }
938 //-----------------------------------------------------------------------------
939 // DATA
940 //-----------------------------------------------------------------------------
941 //_____________________________________________________________________________
942 /**
943  * Get a data value of a specified state at a specified time index.
944  *
945  * @param aTimeIndex Index that identifies the time (row) at which to get the
946  * data value:  0 <= aTimeIndex < _storage.getSize().
947  * @param aStateIndex Index of the state (column) for which to get the value.
948  * @param rValue Value of the state.
949  * @return 1 on success, 0 on failure.
950  */
951 int Storage::
getData(int aTimeIndex,int aStateIndex,double & rValue) const952 getData(int aTimeIndex,int aStateIndex,double &rValue) const
953 {
954     if(aTimeIndex<0) return(0);
955     if(aTimeIndex>=_storage.getSize()) return(0);
956 
957     // ASSIGNMENT
958     StateVector *vec = getStateVector(aTimeIndex);
959     if(vec==NULL) return(0);
960     return( vec->getDataValue(aStateIndex,rValue) );
961 }
962 //_____________________________________________________________________________
963 /**
964  * Helper function for getData
965  */
966 int Storage::
getData(int aTimeIndex,int aStateIndex,int aN,double ** rData) const967 getData(int aTimeIndex,int aStateIndex,int aN,double **rData) const
968 {
969     if(aN<=0) return(0);
970     if(aStateIndex<0) return(0);
971     if(aTimeIndex<0) return(0);
972     if(aTimeIndex>=_storage.getSize()) return(0);
973 
974     // GET STATEVECTOR
975     StateVector *vec = getStateVector(aTimeIndex);
976     if(vec==NULL) return(0);
977     if(vec->getSize()<=0) return(0);
978 
979     // NUMBER OF STATES TO GET
980     int size = vec->getSize();
981     if(aStateIndex>=size) return(0);
982     int n = aStateIndex + aN;
983     if(n>size) n = size;
984     int N = n - aStateIndex;
985 
986     // ALLOCATE MEMORY
987     if(*rData==NULL) *rData = new double[N];
988 
989     // ASSIGN DATA
990     int i,j;
991     Array<double> &data = vec->getData();
992     double *pData = *rData;
993     for(i=0,j=aStateIndex;j<n;i++,j++) pData[i] = data[j];
994 
995     return(N);
996 }
997 //_____________________________________________________________________________
998 /**
999  * At a specified time index, get a number of state values starting at
1000  * a specified state.  The method simply gets part of a row of data
1001  * from adjacent columns in the storage object.
1002  *
1003  * @param aTimeIndex Index that identifies the time (row) at which to get the
1004  * data value:  0 <= aTimeIndex < _storage.getSize().
1005  * @param aStateIndex Index of the state (column) at which to start getting
1006  * the data.
1007  * @param aN Number of states (columns) to get.
1008  * @param rData Data values. rData should be able to hold at least N values.
1009  * @return Number of states that were gotten.
1010  */
1011 int Storage::
getData(int aTimeIndex,int aStateIndex,int aN,double * rData) const1012 getData(int aTimeIndex,int aStateIndex,int aN,double *rData) const
1013 {
1014     if(rData==NULL) return(0);
1015     else return getData(aTimeIndex,aStateIndex,aN,&rData);
1016 }
1017 //_____________________________________________________________________________
1018 /**
1019  * At a specified time index, get a number of state values starting at
1020  * a specified state.  The method simply gets part of a row of data
1021  * from adjacent columns in the storage object.
1022  *
1023  *  @param aTimeIndex Time index at which to get the states.
1024  * @param aStateIndex Index of the state (column) at which to start getting
1025  * the data.
1026  * @param aN Number of states to get.
1027  * @param rData Pointer to an array where the returned data will be set.  The
1028  * size of *rData is assumed to be at least aN.  If rData comes in as NULL,
1029  * memory is allocated.
1030  * @return Number of states.
1031  */
1032 int Storage::
getData(int aTimeIndex,int aN,double ** rData) const1033 getData(int aTimeIndex,int aN,double **rData) const
1034 {
1035     return getData(aTimeIndex,0,aN,rData);
1036 }
1037 //_____________________________________________________________________________
1038 /**
1039  * Get the first aN states at a specified time index.
1040  *
1041  *  @param aTimeIndex Time index at which to get the states.
1042  * @param aN Number of states to get.
1043  * @param rData Array where the returned data will be set.  The
1044  * size of rData is assumed to be at least aN.
1045  * @return Number of states that were set.
1046  */
1047 int Storage::
getData(int aTimeIndex,int aN,double * rData) const1048 getData(int aTimeIndex,int aN,double *rData) const
1049 {
1050     if(rData==NULL) return(0);
1051     else return getData(aTimeIndex,0,aN,&rData);
1052 }
1053 //_____________________________________________________________________________
1054 /**
1055  * Get the first aN states at a specified time index.
1056  *
1057  *  @param aTimeIndex Time index at which to get the states.
1058  * @param aN Number of states to get.
1059  * @param rData Array where the returned data will be set.  The
1060  * size of rData is assumed to be at least aN.
1061  * @return Number of states that were set.
1062  */
1063 int Storage::
getData(int aTimeIndex,int aN,Array<double> & rData) const1064 getData(int aTimeIndex,int aN,Array<double> &rData) const
1065 {
1066     if(0 == rData.size()) return(0);
1067     else return getData(aTimeIndex,0,aN,&rData[0]);
1068 }
1069 //_____________________________________________________________________________
1070 /**
1071  * Get the first aN states at a specified time index.
1072  *
1073  *  @param aTimeIndex Time index at which to get the states.
1074  * @param aN Number of states to get.
1075  * @param rData Array where the returned data will be set.  The
1076  * size of rData is assumed to be at least aN.
1077  * @return Number of states that were set.
1078  */
1079 int Storage::
getData(int aTimeIndex,int aN,SimTK::Vector & v) const1080 getData(int aTimeIndex,int aN,SimTK::Vector& v) const
1081 {
1082     Array<double> rData;
1083     rData.setSize(aN);
1084     int r = getData(aTimeIndex,0,aN,&rData[0]);
1085     for (int i=0; i<aN; ++i)
1086         v[i] = rData[i];
1087     return r;
1088 }
1089 //_____________________________________________________________________________
1090 /**
1091  * Get the first aN states at a specified time.
1092  * The values of the states are determined by linear interpolation.
1093  *
1094  *  @param aT Time at which to get the states.
1095  * @param aN Number of states to get.
1096  * @param rData Pointer to an array where the returned data will be set.  The
1097  * size of *rData is assumed to be at least aN.  If rData comes in as NULL,
1098  * memory is allocated.
1099  * @return Number of states that were set.
1100  */
1101 int Storage::
getDataAtTime(double aT,int aN,double ** rData) const1102 getDataAtTime(double aT,int aN,double **rData) const
1103 {
1104 
1105     // FIND THE CORRECT INTERVAL FOR aT
1106     int i = findIndex(_lastI,aT);
1107     if((i<0)||(_storage.getSize()<=0)) {
1108         *rData = NULL;
1109         return(0);
1110     }
1111 
1112     // CHECK FOR i AT END POINTS
1113     int i1=i,i2=i+1;
1114 
1115     if(i2==_storage.getSize()) {
1116         i1--;  if(i1<0) i1=0;
1117         i2--;  if(i2<0) i2=0;
1118     }
1119 
1120     // STATES AT FIRST INDEX
1121     int n1 = getStateVector(i1)->getSize();
1122     double t1 = getStateVector(i1)->getTime();
1123     Array<double> &y1 = getStateVector(i1)->getData();
1124 
1125     // STATES AT NEXT INDEX
1126     int n2 = getStateVector(i2)->getSize();
1127     double t2 = getStateVector(i2)->getTime();
1128     Array<double> &y2 = getStateVector(i2)->getData();
1129 
1130     // GET THE SMALLEST N TO PREVENT MEMORY OVER-RUNS
1131     int ns = (n1<n2) ? n1 : n2;
1132 
1133     // ALLOCATE MEMORY?
1134     double *y;
1135     if(*rData==NULL) {
1136         y = new double[ns];
1137     } else {
1138         y = *rData;
1139         if(aN<ns)  ns = aN;
1140     }
1141 
1142     // ASSIGN VALUES
1143     double pct;
1144     double num = aT-t1;
1145     double den = t2-t1;
1146     if(den<SimTK::Eps) {
1147         pct = 0.0;
1148     } else {
1149         pct = num/den;
1150     }
1151 
1152 
1153     for(i=0;i<ns;i++) {
1154         if(pct==0.0) {
1155             y[i] = y1[i];
1156         } else {
1157             y[i] = y1[i] + pct*(y2[i]-y1[i]);
1158         }
1159     }
1160 
1161     // ASSIGN FOR RETURN
1162     *rData = y;
1163 
1164     return(ns);
1165 }
1166 //_____________________________________________________________________________
1167 /**
1168  * Get the first aN states at a specified time.
1169  * The values of the states are determined by linear interpolation.
1170  *
1171  *  @param aT Time at which to get the states.
1172  * @param aN Number of states to get.
1173  * @param rData Array where the returned data will be set.  The
1174  * size of rData is assumed to be at least aN.
1175  * @return Number of states that were set.
1176  */
1177 int Storage::
getDataAtTime(double aT,int aN,double * rData) const1178 getDataAtTime(double aT,int aN,double *rData) const
1179 {
1180     if(rData==NULL) return(0);
1181     else return getDataAtTime(aT,aN,&rData);
1182 }
1183 int Storage::
getDataAtTime(double aT,int aN,Array<double> & rData) const1184 getDataAtTime(double aT,int aN,Array<double> &rData) const
1185 {
1186     double *data=&rData[0];
1187     return getDataAtTime(aT,aN,&data);
1188 }
1189 int Storage::
getDataAtTime(double aT,int aN,SimTK::Vector & v) const1190 getDataAtTime(double aT,int aN,SimTK::Vector& v) const
1191 {
1192     Array<double> rData;
1193     rData.setSize(aN);
1194     int r = getDataAtTime(aT,aN,rData);
1195     for (int i=0; i<aN; ++i)
1196         v[i] = rData[i];
1197     return r;
1198 }
1199 //_____________________________________________________________________________
1200 /**
1201  * Get the data corresponding to a specified state.  This call is equivalent
1202  * to getting a column of data from the storage file.
1203  *
1204  * @param aStateIndex Index of the state (column) for which to get the data.
1205  * @param rData Array containing the desired data.  If rData is sent in as
1206  * NULL, memory is allocated.  However, if rData is sent in as a non-NULL, it
1207  * is assumed that rData points to a memory block that is large enough to
1208  * hold getSize() doubles.
1209  * @return Number of values set in rData.  The number of values set may be
1210  * less than getSize() because not all stored state vectors are
1211  * required to have the same number of states.
1212  */
1213 int Storage::
getDataColumn(int aStateIndex,double * & rData) const1214 getDataColumn(int aStateIndex,double *&rData) const
1215 {
1216     int n = _storage.getSize();
1217     if(n<=0) return(0);
1218 
1219     // ALLOCATION
1220     if(rData==NULL) {
1221         rData = new double[n];
1222     }
1223 
1224     // ASSIGNMENT
1225     int i,nData;
1226     for(i=nData=0;i<n;i++) {
1227         StateVector *vec = getStateVector(i);
1228         if(vec==NULL) continue;
1229         if(vec->getDataValue(aStateIndex,rData[nData])) nData++;
1230     }
1231 
1232     return(nData);
1233 }
1234 int Storage::
getDataColumn(int aStateIndex,Array<double> & rData) const1235 getDataColumn(int aStateIndex,Array<double> &rData) const
1236 {
1237     int n = _storage.getSize();
1238     if(n<=0) return(0);
1239 
1240     rData.setSize(n);
1241 
1242     // ASSIGNMENT
1243     int i,nData;
1244     for(i=nData=0;i<n;i++) {
1245         StateVector *vec = getStateVector(i);
1246         if(vec==NULL) continue;
1247         if(vec->getDataValue(aStateIndex,rData[nData])) nData++;
1248     }
1249 
1250     rData.setSize(nData);
1251 
1252     return(nData);
1253 }
1254 
1255 /**
1256  * Get the data column starting at aTime. Return it in rData
1257  * rData is preallocated by the caller.
1258  */
1259 void Storage::
getDataColumn(const std::string & columnName,Array<double> & rData,double aStartTime)1260 getDataColumn(const std::string& columnName, Array<double>& rData, double aStartTime)
1261 {
1262     if(_storage.getSize()<=0) return;
1263 
1264     int startIndex = findIndex(aStartTime);
1265     int colIndex = getStateIndex(columnName);
1266     double *dataVec=0;
1267     getDataColumn(colIndex, dataVec);
1268     for(int i=startIndex; i<_storage.getSize(); i++)
1269         rData.append(dataVec[i]);
1270     delete[] dataVec;
1271 }
1272 
1273 //_____________________________________________________________________________
1274 /**
1275  * Set the data corresponding to a specified state.  This call is equivalent
1276  * to setting a column of data from the storage file.
1277  *
1278  * @param aStateIndex Index of the state (column) for which to get the data.
1279  * @param aData Array containing the new data.
1280  * @return Number of values set in rData.  The number of values set may be
1281  * less than getSize() because not all stored state vectors are
1282  * required to have the same number of states.
1283  */
1284 void Storage::
setDataColumn(int aStateIndex,const Array<double> & aData)1285 setDataColumn(int aStateIndex,const Array<double> &aData)
1286 {
1287     int n = _storage.getSize();
1288     if(n!=aData.getSize()) {
1289         cout<<"Storage.setDataColumn: ERR- sizes don't match." << endl;
1290         return;
1291     }
1292 
1293     // ASSIGNMENT
1294     for(int i=0;i<n;i++) {
1295         StateVector *vec = getStateVector(i);
1296         if(vec==NULL) continue;
1297         vec->setDataValue(aStateIndex,aData[i]);
1298     }
1299 }
1300 /**
1301  * set values in the column specified by columnName to newValue
1302  */
setDataColumnToFixedValue(const std::string & columnName,double newValue)1303 void Storage::setDataColumnToFixedValue(const std::string& columnName, double newValue) {
1304     int n = _storage.getSize();
1305     int aStateIndex = getStateIndex(columnName);
1306     if(aStateIndex==-1) {
1307         cout<<"Storage.setDataColumnToFixedValue: ERR- column not found." << endl;
1308         return;
1309     }
1310 
1311     // ASSIGNMENT
1312     for(int i=0;i<n;i++) {
1313         StateVector *vec = getStateVector(i);
1314         if(vec==NULL) continue;
1315         vec->setDataValue(aStateIndex,newValue);
1316     }
1317 
1318 }
1319 
1320 //_____________________________________________________________________________
1321 /**
1322  * Get the data corresponding to a state specified by name.  This call is equivalent
1323  * to getting a column of data from the storage file.
1324  *
1325  * @param aColumnName name in header of column for which to get the data.
1326  * @param rData Array containing the desired data.  If rData is sent in as
1327  * NULL, memory is allocated.  However, if rData is sent in as a non-NULL, it
1328  * is assumed that rData points to a memory block that is large enough to
1329  * hold getSize() doubles.
1330  * @return Number of values set in rData.  The number of values set may be
1331  * less than getSize() because not all stored state vectors are
1332  * required to have the same number of states.
1333  */
1334 int Storage::
getDataColumn(const std::string & aColumnName,double * & rData) const1335 getDataColumn(const std::string& aColumnName,double *&rData) const
1336 {
1337     if (aColumnName.c_str()[0]=='#'){
1338         int columnNumber=-1;
1339         sscanf(aColumnName.c_str(), "#%d", &columnNumber);
1340         return getDataColumn(columnNumber-1, rData);
1341     }
1342     else
1343         return getDataColumn(getStateIndex(aColumnName), rData);
1344 }
1345 
1346 /** It is desirable to access the block as a single entity provided an identifier that is common
1347     to all components (such as prefix in the column label).
1348      @param identifier  string identifying a single block of data
1349      @param rData       Array<Array<double>> of data belonging to the identifier */
getDataForIdentifier(const std::string & identifier,Array<Array<double>> & rData,double startTime) const1350 void Storage::getDataForIdentifier(const std::string& identifier, Array<Array<double> >& rData, double startTime) const
1351 {
1352 
1353     Array<int> found = getColumnIndicesForIdentifier(identifier);
1354 
1355     if(found.getSize() == 0){
1356         cout << "WARNING: Storage "+getName()+" could not locate data for identifier "+identifier+"." << endl;
1357         return;
1358     }
1359     /* a row of "data" can be shorter than number of columns if time is the first column, since
1360        that is not considered a state by storage. Need to fix this! -aseth */
1361     int nd = getLastStateVector()->getSize();
1362     int off = _columnLabels.getSize()-nd;
1363 
1364 
1365     for(int i=0; i<found.getSize(); ++i){
1366         Array<double> data{};
1367         getDataColumn(found[i]-off, data);
1368         rData.append(data);
1369     }
1370 }
1371 
1372 OpenSim::Array<int>
getColumnIndicesForIdentifier(const std::string & identifier) const1373 Storage::getColumnIndicesForIdentifier(const std::string& identifier) const
1374 {
1375     Array<int> found;
1376     const size_t lid = identifier.length();
1377 
1378     if (lid < 1)  // Identifier is empty; return empty Array.
1379         return found;
1380 
1381     for (int i = 0; i < _columnLabels.getSize(); ++i) {
1382         if (_columnLabels[i].compare(0,lid, identifier) == 0)
1383             found.append(i);
1384     }
1385     return found;
1386 }
1387 
exportToTable() const1388 TimeSeriesTable Storage::exportToTable() const {
1389     TimeSeriesTable table{};
1390 
1391     table.addTableMetaData("header", getName());
1392     table.addTableMetaData("inDegrees", std::string{_inDegrees ? "yes" : "no"});
1393     table.addTableMetaData("nRows", std::to_string(_storage.getSize()));
1394     table.addTableMetaData("nColumns", std::to_string(_columnLabels.getSize()));
1395     if(!getDescription().empty())
1396         table.addTableMetaData("description", getDescription());
1397 
1398     // Exclude the first column label. It is 'time'. Time is a separate column
1399     // in TimeSeriesTable and column label is optional.
1400     table.setColumnLabels(_columnLabels.get() + 1,
1401                           _columnLabels.get() + _columnLabels.getSize());
1402 
1403     for(int i = 0; i < _storage.getSize(); ++i) {
1404         const auto& row = getStateVector(i)->getData();
1405         const auto time = getStateVector(i)->getTime();
1406         // Exclude the first column. It is 'time'. Time is a separate column in
1407         // TimeSeriesTable.
1408         table.appendRow(time, row.get(), row.get() + row.getSize());
1409     }
1410 
1411     return table;
1412 }
1413 
1414 
1415 //=============================================================================
1416 // RESET
1417 //=============================================================================
1418 //_____________________________________________________________________________
1419 /**
1420  * Reset the storage to start storing at a specified index.  All
1421  * statevectors at and following the specified index are discarded.
1422  *
1423  * If aIndex is less than or equal to zero, the storage object is
1424  * emptied (i.e., its size is set to 0).
1425  *
1426  * If aIndex is larger than the current size of the storage object, no
1427  * action is taken.
1428  *
1429  * @param aIndex Index at which to start storing new statevectors.
1430  * @return Index at which the next appended statevector will be stored.
1431  */
1432 int Storage::
reset(int aIndex)1433 reset(int aIndex)
1434 {
1435     if(aIndex>=_storage.getSize()) return(_storage.getSize());
1436     if(aIndex<0) aIndex = 0;
1437     _storage.setSize(aIndex);
1438 
1439     return(_storage.getSize());
1440 }
1441 //_____________________________________________________________________________
1442 /**
1443  * Reset the storage to start storing after a specified time.  If no valid
1444  * statevector exists at that specified time, the storage is set to occur after
1445  * the first valid statevector that immediately precedes the specified
1446  * time.
1447  *
1448  * @param aT Time after which to start storing states.  If aT doesn't exist
1449  * exactly in the storage, the time is rounded down to the first valid time.
1450  * @return Index at which the next appended statevector will be stored.
1451  */
1452 int Storage::
reset(double aTime)1453 reset(double aTime)
1454 {
1455     // 1 is added so that the states at or just prior to aT are kept.
1456     int index = findIndex(aTime) + 1;
1457 
1458     return( reset(index) );
1459 }
1460 
1461 
1462 //_____________________________________________________________________________
1463 /**
1464  * Crop the storage object to the specified start and final time
1465  */
1466 void Storage::
crop(const double newStartTime,const double newFinalTime)1467 crop(const double newStartTime, const double newFinalTime)
1468 {
1469     int startindex = findIndex(newStartTime);
1470     int finalindex = findIndex(newFinalTime);
1471     // Since underlying Array is packed we'll just move what we need up then
1472     // delete remaining rows in reverse order.
1473     int numRowsToKeep=finalindex-startindex+1;
1474     if (numRowsToKeep <=0){
1475         cout<<"Storage.crop: WARNING: No rows will be left." << endl;
1476         numRowsToKeep=0;
1477     }
1478     if (startindex!=0){
1479         for(int i=0; i<finalindex-startindex+1; i++)
1480             _storage[i]=_storage[startindex+i];
1481     }
1482     _storage.setSize(numRowsToKeep);
1483 }
1484 
1485 //=============================================================================
1486 // STORAGE
1487 //=============================================================================
1488 //_____________________________________________________________________________
1489 /**
1490  * Append an StateVector.
1491  *
1492  * @param aStateVector Statevector to be appended.
1493  * @return Size of the storage after the append.
1494  */
1495 int Storage::
append(const StateVector & aStateVector,bool aCheckForDuplicateTime)1496 append(const StateVector &aStateVector,bool aCheckForDuplicateTime)
1497 {
1498     // TODO: use some tolerance when checking for duplicate time?
1499     if(aCheckForDuplicateTime && _storage.getSize() && _storage.getLast().getTime()==aStateVector.getTime())
1500         _storage.updLast() = aStateVector;
1501     else
1502         _storage.append(aStateVector);
1503 
1504     if (_fp!=0){
1505         aStateVector.print(_fp);
1506         fflush(_fp);
1507     }
1508     return(_storage.getSize());
1509 }
1510 //_____________________________________________________________________________
1511 /**
1512  * Append copies of all state vectors in an Storage object.
1513  *
1514  * This method overrides Array::append(Array).
1515  * Currently, there is no difference.  The override is done for completeness.
1516  *
1517  * @param aStorage Storage to be appended.
1518  * @return The index of the first empty storage element.
1519  */
1520 int Storage::
append(const Array<StateVector> & aStorage)1521 append(const Array<StateVector> &aStorage)
1522 {
1523     for(int i=0; i<aStorage.getSize(); i++)
1524         _storage.append(aStorage[i]);
1525     return(_storage.getSize());
1526 }
1527 //_____________________________________________________________________________
1528 /**
1529  * Append an array of data that occurred at a specified time.
1530  *
1531  * @param aT Time stamp of the data.
1532  * @param aN Length of the array.
1533  * @param aY Array.
1534  * @return Index of the first empty storage element.
1535  */
1536 int Storage::
append(double aT,int aN,const double * aY,bool aCheckForDuplicateTime)1537 append(double aT,int aN,const double *aY,bool aCheckForDuplicateTime)
1538 {
1539     if(aY==NULL) return(_storage.getSize());
1540     if(aN<0) return(_storage.getSize());
1541 
1542     // APPEND
1543     StateVector vec(aT, SimTK::Vector_<double>(aN, aY));
1544     append(vec,aCheckForDuplicateTime);
1545     // TODO: use some tolerance when checking for duplicate time?
1546     /*
1547     if(aCheckForDuplicateTime && _storage.getSize() && _storage.getLast().getTime()==vec.getTime())
1548         _storage.getLast() = vec;
1549     else
1550         _storage.append(vec);
1551     */
1552     return(_storage.getSize());
1553 }
1554 //_____________________________________________________________________________
1555 /**
1556  * Append an array of data that occurred at a specified time.
1557  *
1558  * @param aT Time stamp of the data.
1559  * @param aY Vector.
1560  * @return Index of the first empty storage element.
1561  */
1562 int Storage::
append(double aT,const SimTK::Vector & aY,bool aCheckForDuplicateTime)1563 append(double aT,const SimTK::Vector& aY,bool aCheckForDuplicateTime)
1564 {
1565     // APPEND
1566     return( append ( aT, aY.size(), &aY[0], aCheckForDuplicateTime ));
1567 }
1568 //_____________________________________________________________________________
1569 /**
1570  * Append an array of data that occurred at a specified time.
1571  *
1572  * @param aT Time stamp of the data.
1573  * @param aY Array<double>.
1574  * @return Index of the first empty storage element.
1575  */
1576 int Storage::
append(double aT,const Array<double> & aY,bool aCheckForDuplicateTime)1577 append(double aT,const Array<double>& aY,bool aCheckForDuplicateTime)
1578 {
1579     // APPEND
1580     return( append ( aT, aY.getSize(), &aY[0], aCheckForDuplicateTime ));
1581 }
1582 
1583 //_____________________________________________________________________________
1584 //_____________________________________________________________________________
1585 /**
1586  * Store a simulation vector.
1587  *
1588  * This method differs from append in that, if the integration step of
1589  * a simulation is not a multiple of the step interval of this
1590  * Storage class, the state is not appended.
1591  * Note that if the step storage interval is 0, storage is turned off.
1592  *
1593  * The first empty storage location is returned.
1594  */
1595 int Storage::
store(int aStep,double aT,int aN,const double * aY)1596 store(int aStep,double aT,int aN,const double *aY)
1597 {
1598     if(_stepInterval==0) return(_storage.getSize());
1599     if((aStep%_stepInterval) == 0) {
1600         append(aT,aN,aY);
1601     }
1602 
1603     return(_storage.getSize());
1604 }
1605 
1606 
1607 //=============================================================================
1608 // OPERATIONS
1609 //=============================================================================
1610 //-----------------------------------------------------------------------------
1611 // TIME
1612 //-----------------------------------------------------------------------------
1613 //_____________________________________________________________________________
1614 /**
1615  * Shift the times of all state vectors.
1616  *
1617  * @param aValue Value by which to shift the times.
1618  */
1619 void Storage::
shiftTime(double aValue)1620 shiftTime(double aValue)
1621 {
1622     for(int i=0;i<_storage.getSize();i++) {
1623         _storage[i].shiftTime(aValue);
1624     }
1625 }
1626 //_____________________________________________________________________________
1627 /**
1628  * Scale the times of all state vectors.
1629  *
1630  * @param aValue Value by which to scale the times.
1631  */
1632 void Storage::
scaleTime(double aValue)1633 scaleTime(double aValue)
1634 {
1635     for(int i=0;i<_storage.getSize();i++) {
1636         _storage[i].scaleTime(aValue);
1637     }
1638 }
1639 
1640 //-----------------------------------------------------------------------------
1641 // ADD
1642 //-----------------------------------------------------------------------------
1643 //_____________________________________________________________________________
1644 /**
1645  * Add a value to all state vectors in this storage instance.
1646  *
1647  * @param aValue Value to add to the state vectors.
1648  * @see StateVector::add(double)
1649  */
1650 void Storage::
add(double aValue)1651 add(double aValue)
1652 {
1653     for(int i=0;i<_storage.getSize();i++) {
1654         _storage[i].add(aValue);
1655     }
1656 }
1657 //_____________________________________________________________________________
1658 /**
1659  * Add a value to all entries in one column of the storage.
1660  *
1661  * @param aN Index of the column that the value is to be added to.
1662  * @param aValue Value to add to the column.
1663  * @see StateVector::add(int,double)
1664  */
1665 void Storage::
add(int aN,double aValue)1666 add(int aN, double aValue)
1667 {
1668     for(int i=0;i<_storage.getSize();i++) {
1669         _storage[i].add(aN,aValue);
1670     }
1671 }
1672 //_____________________________________________________________________________
1673 /**
1674  * Add an array to all state vectors in this storage instance.
1675  *
1676  * Only the first aN states of each state vector are altered.
1677  *
1678  * @param values Array of values to add to the state vectors.
1679  * @see StateVector::add(int,double[])
1680  */
add(const SimTK::Vector_<double> & values)1681 void Storage::add(const SimTK::Vector_<double>& values) {
1682     for(int i = 0; i < _storage.getSize(); ++i) {
1683         _storage[i].add(values);
1684     }
1685 }
1686 //_____________________________________________________________________________
1687 /**
1688  * Add a state vector to the all state vectors in this storage instance.
1689  *
1690  * @param aStateVector State vector to add to the state vectors.
1691  * @see StateVector::add(int,double[])
1692  */
1693 void Storage::
add(StateVector * aStateVector)1694 add(StateVector *aStateVector)
1695 {
1696     for(int i=0;i<_storage.getSize();i++) {
1697         _storage[i].add(aStateVector);
1698     }
1699 }
1700 //_____________________________________________________________________________
1701 /**
1702  * Add a storage instance to this storage instance.
1703  *
1704  * Linear interpolation or extrapolation is used to get the values of the
1705  * states that correspond in time to the states held in this storage
1706  * instance.
1707  *
1708  * @param aStorage Storage to add to this storage.
1709 s */
1710 void Storage::
add(Storage * aStorage)1711 add(Storage *aStorage)
1712 {
1713     if(aStorage==NULL) return;
1714 
1715     int n,N=0,nN;
1716     double t,*Y=NULL;
1717     for(int i=0;i<_storage.getSize();i++) {
1718 
1719         // GET INFO ON THIS STORAGE INSTANCE
1720         n = getStateVector(i)->getSize();
1721         t = getStateVector(i)->getTime();
1722 
1723         // GET DATA FROM ARGUMENT
1724         N = aStorage->getDataAtTime(t,N,&Y);
1725 
1726         // SET SIZE TO SMALLER
1727         nN = (n<N) ? n : N;
1728 
1729         // ADD
1730         _storage[i].add(SimTK::Vector_<double>(nN, Y));
1731     }
1732 
1733     // CLEANUP
1734     if(Y!=NULL) delete[] Y;
1735 }
1736 
1737 //-----------------------------------------------------------------------------
1738 // SUBTRACT
1739 //-----------------------------------------------------------------------------
1740 //_____________________________________________________________________________
1741 /**
1742  * Subtract a value from all state vectors in this storage instance.
1743  *
1744  * @param aValue Value to subtract from the state vectors.
1745  * @see StateVector::subtract(double)
1746  */
1747 void Storage::
subtract(double aValue)1748 subtract(double aValue)
1749 {
1750     for(int i=0;i<_storage.getSize();i++) {
1751         _storage[i].subtract(aValue);
1752     }
1753 }
1754 //_____________________________________________________________________________
1755 /**
1756  * Subtract an array from all state vectors in this storage instance.
1757  *
1758  * Only the first aN states of each state vector are altered.
1759  *
1760  * @param values Array of values to subtract from the state vectors.
1761  * @see StateVector::subtract(int,double[])
1762  */
subtract(const SimTK::Vector_<double> & values)1763 void Storage::subtract(const SimTK::Vector_<double>& values) {
1764     for(int i = 0; i < _storage.getSize(); ++i) {
1765         _storage[i].subtract(values);
1766     }
1767 }
1768 //_____________________________________________________________________________
1769 /**
1770  * Subtract a state vector from all state vectors in this storage instance.
1771  *
1772  * @param aStateVector State vector to subtract from the state vectors.
1773  * @see StateVector::subtract(int,double[])
1774  */
1775 void Storage::
subtract(StateVector * aStateVector)1776 subtract(StateVector *aStateVector)
1777 {
1778     for(int i=0;i<_storage.getSize();i++) {
1779         _storage[i].subtract(aStateVector);
1780     }
1781 }
1782 //_____________________________________________________________________________
1783 /**
1784  * Subtract a storage instance to this storage instance.
1785  *
1786  * Linear interpolation or extrapolation is used to get the values of the
1787  * states that correspond in time to the states held in this storage
1788  * instance.
1789  *
1790  * @param aStorage Storage to subtract from this storage.
1791 s */
1792 void Storage::
subtract(Storage * aStorage)1793 subtract(Storage *aStorage)
1794 {
1795     if(aStorage==NULL) return;
1796 
1797     int n,N=0,nN;
1798     double t,*Y=NULL;
1799     for(int i=0;i<_storage.getSize();i++) {
1800 
1801         // GET INFO ON THIS STORAGE INSTANCE
1802         n = getStateVector(i)->getSize();
1803         t = getStateVector(i)->getTime();
1804 
1805         // GET DATA FROM ARGUMENT
1806         N = aStorage->getDataAtTime(t,N,&Y);
1807 
1808         // SET SIZE TO SMALLER
1809         nN = (n<N) ? n : N;
1810 
1811         // SUBTRACT
1812         _storage[i].subtract(SimTK::Vector_<double>(nN, Y));
1813     }
1814 
1815     // CLEANUP
1816     if(Y!=NULL) delete[] Y;
1817 }
1818 
1819 //-----------------------------------------------------------------------------
1820 // MULTIPLY
1821 //-----------------------------------------------------------------------------
1822 //_____________________________________________________________________________
1823 /**
1824  * Multiply all state vectors in this storage instance by a value.
1825  *
1826  * @param aValue Value by which to multiply the state vectors.
1827  * @see StateVector::multiply(double)
1828  */
1829 void Storage::
multiply(double aValue)1830 multiply(double aValue)
1831 {
1832     for(int i=0;i<_storage.getSize();i++) {
1833         _storage[i].multiply(aValue);
1834     }
1835 }
1836 //_____________________________________________________________________________
1837 /**
1838  * Multiply all state vectors in this storage instance by an array.
1839  *
1840  * Only the first aN states of each state vector are altered.
1841  *
1842  * @param values Array of values the states are to be multiplied by.
1843  * @see StateVector::multiply(int,double[])
1844  */
multiply(const SimTK::Vector_<double> & values)1845 void Storage::multiply(const SimTK::Vector_<double>& values) {
1846     for(int i = 0; i < _storage.getSize(); ++i) {
1847         _storage[i].multiply(values);
1848     }
1849 }
1850 
1851 //_____________________________________________________________________________
1852 /**
1853  * Multiply all state vectors in this storage instance by a state vector.
1854  *
1855  * @param aStateVector State vector by which to multiply the state vectors.
1856  * @see StateVector::multiply(StateVector)
1857  */
1858 void Storage::
multiply(StateVector * aStateVector)1859 multiply(StateVector *aStateVector)
1860 {
1861     for(int i=0;i<_storage.getSize();i++) {
1862         _storage[i].multiply(aStateVector);
1863     }
1864 }
1865 //_____________________________________________________________________________
1866 /**
1867  * Multiply this storage instance by a storage instance.
1868  *
1869  * Linear interpolation or extrapolation is used to get the values of the
1870  * states that correspond in time to the states held in this storage
1871  * instance.
1872  *
1873  * @param aStorage Storage instance by which to multiply.
1874 s */
1875 void Storage::
multiply(Storage * aStorage)1876 multiply(Storage *aStorage)
1877 {
1878     if(aStorage==NULL) return;
1879 
1880     int n,N=0,nN;
1881     double t,*Y=NULL;
1882     for(int i=0;i<_storage.getSize();i++) {
1883 
1884         // GET INFO ON THIS STORAGE INSTANCE
1885         n = getStateVector(i)->getSize();
1886         t = getStateVector(i)->getTime();
1887 
1888         // GET DATA FROM ARGUMENT
1889         N = aStorage->getDataAtTime(t,N,&Y);
1890 
1891         // SET SIZE TO SMALLER
1892         nN = (n<N) ? n : N;
1893 
1894         // MULTIPLY
1895         _storage[i].multiply(SimTK::Vector_<double>(nN, Y));
1896     }
1897 
1898     // CLEANUP
1899     if(Y!=NULL) delete[] Y;
1900 }
1901 //_____________________________________________________________________________
1902 /**
1903  * Multiply entries at column aIndex by a value.
1904  *
1905  * @param aIndex is the index of the column to multiply
1906  * @param aValue Value by which to multiply the column.
1907  */
1908 void Storage::
multiplyColumn(int aIndex,double aValue)1909 multiplyColumn(int aIndex, double aValue)
1910 {
1911     double newValue;
1912     for(int i=0;i<_storage.getSize();i++) {
1913         _storage[i].getDataValue(aIndex, newValue);
1914         newValue *= aValue;
1915         _storage[i].setDataValue(aIndex, newValue);
1916     }
1917 }
1918 
1919 //-----------------------------------------------------------------------------
1920 // DIVIDE
1921 //-----------------------------------------------------------------------------
1922 //_____________________________________________________________________________
1923 /**
1924  * Divide all state vectors in this storage instance by a value.
1925  *
1926  * @param aValue Value by which to divide the state vectors.
1927  */
1928 void Storage::
divide(double aValue)1929 divide(double aValue)
1930 {
1931     for(int i=0;i<_storage.getSize();i++) {
1932         _storage[i].divide(aValue);
1933     }
1934 }
1935 //_____________________________________________________________________________
1936 /**
1937  * Divide all state vectors in this storage instance by an array.
1938  *
1939  * Only the first aN states of each state vector are altered.
1940  *
1941  * @param values Array of values the states are to be divided by.
1942  */
divide(const SimTK::Vector_<double> & values)1943 void Storage::divide(const SimTK::Vector_<double>& values) {
1944     for(int i = 0; i < _storage.getSize(); ++i) {
1945         _storage[i].divide(values);
1946     }
1947 }
1948 //_____________________________________________________________________________
1949 /**
1950  * Divide all state vectors in this storage instance by a state vector.
1951  *
1952  * @param aStateVector State vector by which to divide the state vectors.
1953  */
1954 void Storage::
divide(StateVector * aStateVector)1955 divide(StateVector *aStateVector)
1956 {
1957     for(int i=0;i<_storage.getSize();i++) {
1958         _storage[i].divide(aStateVector);
1959     }
1960 }
1961 //_____________________________________________________________________________
1962 /**
1963  * Divide this storage instance by a storage instance.
1964  *
1965  * Linear interpolation or extrapolation is used to get the values of the
1966  * states that correspond in time to the states held in this storage
1967  * instance.
1968  *
1969  * @param aStorage Storage instance by which to divide.
1970 s */
1971 void Storage::
divide(Storage * aStorage)1972 divide(Storage *aStorage)
1973 {
1974     if(aStorage==NULL) return;
1975 
1976     int i;
1977     int n,N=0,nN;
1978     double t,*Y=NULL;
1979     for(i=0;i<_storage.getSize();i++) {
1980 
1981         // GET INFO ON THIS STORAGE INSTANCE
1982         n = getStateVector(i)->getSize();
1983         t = getStateVector(i)->getTime();
1984 
1985         // GET DATA FROM ARGUMENT
1986         N = aStorage->getDataAtTime(t,N,&Y);
1987 
1988         // SET SIZE TO SMALLER
1989         nN = (n<N) ? n : N;
1990 
1991         // DIVIDE
1992         _storage[i].divide(SimTK::Vector_<double>(nN, Y));
1993     }
1994 
1995     // CLEANUP
1996     if(Y!=NULL) delete[] Y;
1997 }
1998 
1999 //=============================================================================
2000 // COMPUTATIONS
2001 //=============================================================================
2002 //_____________________________________________________________________________
2003 /**
2004  * Compute the average value of the first aN states stored for all state
2005  * vectors stored in this storage instance.
2006  *
2007  * This method uses computeArea() to compute the area (integral) and then
2008  * simply divides by the time interval (tf-ti).
2009  *
2010  * It is assumed that there is enough memory at aAve to hold aN states.
2011  * If aN exceeds the number of states held in storage, aN is disregarded.
2012  *
2013  * The number of valid states in aAve is returned.
2014  */
2015 int Storage::
computeAverage(int aN,double * aAve) const2016 computeAverage(int aN,double *aAve) const
2017 {
2018     int n = computeArea(aN,aAve);
2019     if(n==0) return(0);
2020 
2021     // DIVIDE BY THE TIME RANGE
2022     double ti = getFirstTime();
2023     double tf = getLastTime();
2024     if(tf<=ti) {
2025         cout << "Storage.computeAverage: ERROR- time interval invalid." << endl
2026               << "\tfirstTime=" << ti << "  lastTime=" << tf << endl;
2027         return(0);
2028     }
2029     double dt_recip = 1.0 / (tf-ti);
2030     for(int i=0;i<n;i++) {
2031         aAve[i] *= dt_recip;
2032     }
2033 
2034     return(n);
2035 }
2036 //_____________________________________________________________________________
2037 /**
2038  * Compute the average value of the first aN states stored between the
2039  * times aTI and aTF.
2040  *
2041  * This method uses computeArea() to compute the area (integral) of each
2042  * state on the interval [aTI,aTF] and then simply divides by the (aTF-aTI).
2043  *
2044  * It is assumed that there is enough memory at aAve to hold aN states.
2045  * If aN exceeds the number of states held in storage, aN is disregarded.
2046  *
2047  * The number of valid states in aAve is returned.
2048  *
2049  * Note that if aTI and aTF do not fall exactly on the time stamp
2050  * of a stored state, the states are linearly interpolated to provide
2051  * an estimate of the state at aTI or at aTF.
2052  */
2053 int Storage::
computeAverage(double aTI,double aTF,int aN,double * aAve) const2054 computeAverage(double aTI,double aTF,int aN,double *aAve) const
2055 {
2056     int n = computeArea(aTI,aTF,aN,aAve);
2057     if(n==0) return(0);
2058 
2059     // DIVIDE BY THE TIME RANGE
2060     double dt_recip = 1.0 / (aTF-aTI);
2061     for(int i=0;i<n;i++) {
2062         aAve[i] *= dt_recip;
2063     }
2064 
2065     return(n);
2066 }
2067 
2068 //_____________________________________________________________________________
2069 /**
2070  * Helper function for integrate/computeArea
2071  */
2072 int Storage::
integrate(int aI1,int aI2,int aN,double * rArea,Storage * rStorage) const2073 integrate(int aI1,int aI2,int aN,double *rArea,Storage *rStorage) const
2074 {
2075     // CHECK THAT THERE ARE STATES STORED
2076     if(_storage.getSize()<=0) {
2077         cout << "Storage.integrate: ERROR- no stored states." << endl;
2078         return(0);
2079     }
2080 
2081     // CHECK INDICES
2082     if(aI1>=aI2) {
2083         cout << "Storage.integrate:  ERROR- aI1 >= aI2." << endl;
2084         return(0);
2085     }
2086 
2087     // GET THE SMALLEST NUMBER OF STATES
2088     int n = getSmallestNumberOfStates();
2089     if(n>aN) n = aN;
2090     if(n<=0) {
2091         cout << "Storage.computeArea: ERROR- no stored states" << endl;
2092         return(0);
2093     }
2094 
2095     // SET THE INDICES
2096     if(aI1<0) aI1 = 0;
2097     if(aI2<0) aI2 = _storage.getSize()-1;
2098 
2099     // WORKING MEMORY
2100     double ti,tf;
2101     double *yi=NULL,*yf=NULL;
2102 
2103     bool functionAllocatedArea = false;
2104     if(!rArea) {
2105         rArea = new double [n];
2106         functionAllocatedArea = true;
2107     }
2108 
2109     // INITIALIZE AREA
2110     for(int i=0;i<n;i++) rArea[i]=0.0;
2111 
2112     // RECORD FIRST STATE
2113     if(rStorage) {
2114         ti = getStateVector(aI1)->getTime();
2115         rStorage->append(ti,n,rArea);
2116     }
2117 
2118     // INTEGRATE
2119     for(int I=aI1;I<aI2;I++) {
2120 
2121         // INITIAL
2122         ti = getStateVector(I)->getTime();
2123         yi = getStateVector(I)->getData().get();
2124 
2125         // FINAL
2126         tf = getStateVector(I+1)->getTime();
2127         yf = getStateVector(I+1)->getData().get();
2128 
2129         // AREA
2130         for(int i=0;i<n;i++) {
2131             rArea[i] += 0.5*(yf[i]+yi[i])*(tf-ti);
2132         }
2133 
2134         // APPEND
2135         if(rStorage) rStorage->append(tf,n,rArea);
2136     }
2137 
2138     // CLEANUP
2139     if(functionAllocatedArea) delete[] rArea;
2140 
2141     return(n);
2142 }
2143 //_____________________________________________________________________________
2144 /**
2145  * Helper function for integrate/computeArea
2146  */
2147 int Storage::
integrate(double aTI,double aTF,int aN,double * rArea,Storage * rStorage) const2148 integrate(double aTI,double aTF,int aN,double *rArea,Storage *rStorage) const
2149 {
2150     // CHECK THAT THERE ARE STATES STORED
2151     if(_storage.getSize()<=0) {
2152         cout << "Storage.integrate: ERROR- no stored states." << endl;
2153         return(0);
2154     }
2155 
2156     // CHECK INITIAL AND FINAL TIMES
2157     if(aTI>=aTF) {
2158         cout << "Storage.integrate:  ERROR- bad time range." << endl
2159               << "\tInitial time (" << aTI << ") is not smaller than final time (" << aTF << ")" << endl;
2160         return(0);
2161     }
2162 
2163     // CHECK TIME RANGE
2164     double fstT = getFirstTime();
2165     double lstT = getLastTime();
2166     if((aTI<fstT)||(aTI>lstT)||(aTF<fstT)||(aTF>lstT)) {
2167         cout << "Storage.integrate: ERROR- bad time range." << endl
2168               << "\tThe specified range (" << aTI << " to " << aTF << ") is not covered by" << endl
2169               << "\ttime range of the stored states (" << fstT << " to " << lstT << ")." << endl;
2170         return(0);
2171     }
2172 
2173     // CHECK FOR VALID OUTPUT ARRAYS
2174     if(aN<=0) return(0);
2175 
2176     // GET THE SMALLEST NUMBER OF STATES
2177     int n = getSmallestNumberOfStates();
2178     if(n>aN) n = aN;
2179     if(n<=0) {
2180         cout << "Storage.integrate: ERROR- no stored states" << endl;
2181         return(0);
2182     }
2183 
2184     // WORKING MEMORY
2185     double ti,tf;
2186     double *yI = new double[n];
2187     double *yF = new double[n];
2188 
2189     bool functionAllocatedArea = false;
2190     if(!rArea) {
2191         rArea = new double [n];
2192         functionAllocatedArea = true;
2193     }
2194 
2195     // INITIALIZE AREA
2196     for(int i=0;i<n;i++) rArea[i]=0.0;
2197 
2198     // RECORD FIRST STATE
2199     if(rStorage) rStorage->append(aTI,n,rArea);
2200 
2201     // GET RELEVANT STATE INDICES
2202     int II = findIndex(aTI)+1;
2203     int FF = findIndex(aTF);
2204 
2205     // SAME INTERVAL
2206     if(II>FF) {
2207         getDataAtTime(aTI,n,&yI);
2208         getDataAtTime(aTF,n,&yF);
2209         for(int i=0;i<n;i++) {
2210             rArea[i] += 0.5*(yF[i]+yI[i])*(aTF-aTI);
2211         }
2212         if(rStorage) rStorage->append(aTF,n,rArea);
2213 
2214     // SPANS MULTIPLE INTERVALS
2215     } else {
2216         double *yi=NULL,*yf=NULL;
2217 
2218         // FIRST SLICE
2219         getDataAtTime(aTI,n,&yI);
2220         tf = getStateVector(II)->getTime();
2221         yf = getStateVector(II)->getData().get();
2222         for(int i=0;i<n;i++) {
2223             rArea[i] += 0.5*(yf[i]+yI[i])*(tf-aTI);
2224         }
2225         if(rStorage) rStorage->append(tf,n,rArea);
2226 
2227         // INTERVALS
2228         for(int I=II;I<FF;I++) {
2229             ti = getStateVector(I)->getTime();
2230             yi = getStateVector(I)->getData().get();
2231             tf = getStateVector(I+1)->getTime();
2232             yf = getStateVector(I+1)->getData().get();
2233             for(int i=0;i<n;i++) {
2234                 rArea[i] += 0.5*(yf[i]+yi[i])*(tf-ti);
2235             }
2236             if(rStorage) rStorage->append(tf,n,rArea);
2237         }
2238 
2239         // LAST SLICE
2240         ti = getStateVector(FF)->getTime();
2241         yi = getStateVector(FF)->getData().get();
2242         getDataAtTime(aTF,n,&yF);
2243         for(int i=0;i<n;i++) {
2244             rArea[i] += 0.5*(yF[i]+yi[i])*(aTF-ti);
2245         }
2246         if(rStorage) rStorage->append(aTF,n,rArea);
2247     }
2248 
2249     // CLEANUP
2250     delete[] yI;
2251     delete[] yF;
2252     if(functionAllocatedArea) delete[] rArea;
2253 
2254     return(n);
2255 }
2256 //_____________________________________________________________________________
2257 /**
2258  * Compute the area of the first aN states stored for all state vectors
2259  * stored in this storage instance.
2260  *
2261  * It is assumed that there is enough memory at aArea to hold aN states.
2262  * If aN exceeds the number of states held in storage, aN is disregarded.
2263  *
2264  * The number of valid states in aArea is returned.
2265  */
2266 int Storage::
computeArea(int aN,double * aArea) const2267 computeArea(int aN,double *aArea) const
2268 {
2269     // CHECK FOR VALID OUTPUT ARRAYS
2270     if(aN<=0) return(0);
2271     else if(aArea==NULL) return(0);
2272     else return integrate(0,_storage.getSize()-1,aN,aArea,NULL);
2273 }
2274 //_____________________________________________________________________________
2275 /**
2276  * Compute the area of the first aN states stored between the
2277  * times aTI and aTF.
2278  *
2279  * It is assumed that there is enough memory at aArea to hold aN states.
2280  * If aN exceeds the number of states held in storage, aN is disregarded.
2281  *
2282  * The number of valid states in aArea is returned.
2283  *
2284  * Note that if aTI and aTF do not fall exactly on the time stamp
2285  * of a stored state, the states are linearly interpolated to provide
2286  * an estimate of the state at aTI or at aTF.
2287  */
2288 int Storage::
computeArea(double aTI,double aTF,int aN,double * aArea) const2289 computeArea(double aTI,double aTF,int aN,double *aArea) const
2290 {
2291     // CHECK FOR VALID OUTPUT ARRAYS
2292     if(aN<=0) return(0);
2293     else if(aArea==NULL) return(0);
2294     else return integrate(aTI,aTF,aN,aArea,NULL);
2295 }
2296 //_____________________________________________________________________________
2297 /**
2298  * Integrate the state vectors between aI1 and aI2.
2299  *
2300  * The integration results are returned in an Storage instance that is a
2301  * copy of this instance except the name has been appended with "_integrated"
2302  * and, of course, the state vectors have been integrated.  The caller is
2303  * responsible for deleting the returned storage instance.
2304  *
2305  * If aI1 is negative, integrations starts at the first StateVector
2306  * held by this Storage instance.
2307  * If aI2 is negative, integration starts at the last StateVector held by
2308  * this Storage instance.
2309  *
2310  * Note that aI1 and aI2 have negative default values, so that this method
2311  * may be called without arguments to integrate all StateVectors held by
2312  * this Storage instance.
2313  *
2314  * @param aI1 Index of state vector at which to start integration.
2315  * @param aI2 Index of state vector at which to stop integration.
2316  * @return Storage instance of integrated results.  NULL is returned if an
2317  * error is encountered.
2318  */
2319 Storage* Storage::
integrate(int aI1,int aI2) const2320 integrate(int aI1,int aI2) const
2321 {
2322     // CREATE COPY
2323     Storage *integStore = new Storage(*this,false);
2324     integStore->setName(getName()+"_integrated");
2325 
2326     int n = getSmallestNumberOfStates();
2327     int result = integrate(aI1,aI2,n,NULL,integStore);
2328     if(result<=0) {
2329         delete integStore;
2330         return NULL;
2331     } else return integStore;
2332 }
2333 //_____________________________________________________________________________
2334 /**
2335  * Integrate the state vectors between times aTI and aTF.
2336  *
2337  * The integration results are returned in an Storage instance that is a
2338  * copy of this instance except the name has been appended with "_integrated"
2339  * and, of course, the state vectors have been integrated.  The caller is
2340  * responsible for deleting the returned storage instance.
2341  *
2342  * Note that if aTI and aTF do not fall exactly on the time stamp
2343  * of a stored state, the states are linearly interpolated or extrapolated
2344  * to provide an estimate of the state at aTI or at aTF.
2345  *
2346  * @param aTI Time at which to start the integration.
2347  * @param aTF Time at which to stop the integration.
2348  * @return Storage instance of integrated results.  NULL is returned if an
2349  * error is encountered.
2350  */
2351 Storage* Storage::
integrate(double aTI,double aTF) const2352 integrate(double aTI,double aTF) const
2353 {
2354     // CREATE COPY
2355     Storage *integStore = new Storage(*this,false);
2356     integStore->setName(getName()+"_integrated");
2357 
2358     int n = getSmallestNumberOfStates();
2359     int result = integrate(aTI,aTF,n,NULL,integStore);
2360     if(result<=0) {
2361         delete integStore;
2362         return NULL;
2363     } else return integStore;
2364 }
2365 
2366 //_____________________________________________________________________________
2367 /**
2368  * Pad each of the columns in a statevector by a specified amount.
2369  * Data is both prepended and appended by reflecting and negating.
2370  *
2371  * @param aPadSize Number of data points to prepend and append.
2372  */
2373 void Storage::
pad(int aPadSize)2374 pad(int aPadSize)
2375 {
2376     if (aPadSize==0) return; //Nothing to do
2377     // PAD THE TIME COLUMN
2378     Array<double> paddedTime;
2379     int size = getTimeColumn(paddedTime);
2380     Signal::Pad(aPadSize,paddedTime);
2381     int newSize = paddedTime.getSize();
2382 
2383     // PAD EACH COLUMN
2384     int nc = getSmallestNumberOfStates();
2385     Array<double> paddedSignal(0.0,size);
2386     StateVector *vecs = new StateVector[newSize];
2387     for(int j=0;j<newSize;j++) {
2388         vecs[j].getData().setSize(nc);
2389         vecs[j].setTime(paddedTime[j]);
2390     }
2391     for(int i=0;i<nc;i++) {
2392         getDataColumn(i,paddedSignal);
2393         Signal::Pad(aPadSize,paddedSignal);
2394         for(int j=0;j<newSize;j++)
2395             vecs[j].setDataValue(i,paddedSignal[j]);
2396     }
2397 
2398     // APPEND THE STATEVECTORS
2399     _storage.setSize(0);
2400     for(int i=0;i<newSize;i++) _storage.append(vecs[i]);
2401 
2402     // CLEANUP
2403     delete[] vecs;
2404 }
2405 
2406 void Storage::
smoothSpline(int aOrder,double aCutoffFrequency)2407 smoothSpline(int aOrder,double aCutoffFrequency)
2408 {
2409     int size = getSize();
2410     double dtmin = getMinTimeStep();
2411     double avgDt = (_storage[size-1].getTime() - _storage[0].getTime()) / (size-1);
2412 
2413     if(dtmin<SimTK::Eps) {
2414         cout<<"Storage.SmoothSpline: storage cannot be resampled."<<endl;
2415         return;
2416     }
2417 
2418     // RESAMPLE if the sampling interval is not uniform
2419     if ((avgDt - dtmin) > SimTK::Eps) {
2420         dtmin = resample(dtmin, aOrder);
2421         size = getSize();
2422     }
2423 
2424     if(size<(2*aOrder)) {
2425         cout<<"Storage.SmoothSpline: too few data points to filter."<<endl;
2426         return;
2427     }
2428 
2429     // LOOP OVER COLUMNS
2430     double *times=NULL;
2431     int nc = getSmallestNumberOfStates();
2432     double *signal=NULL;
2433     Array<double> filt(0.0,size);
2434     getTimeColumn(times,0);
2435     for(int i=0;i<nc;i++) {
2436         getDataColumn(i,signal);
2437         Signal::SmoothSpline(aOrder,dtmin,aCutoffFrequency,size,times,signal,&filt[0]);
2438         setDataColumn(i,filt);
2439     }
2440 
2441     // CLEANUP
2442     delete[] times;
2443     delete[] signal;
2444 }
2445 
2446 void Storage::
lowpassIIR(double aCutoffFrequency)2447 lowpassIIR(double aCutoffFrequency)
2448 {
2449     int size = getSize();
2450     double dtmin = getMinTimeStep();
2451     double avgDt = (_storage[size-1].getTime() - _storage[0].getTime()) / (size-1);
2452 
2453     if(dtmin<SimTK::Eps) {
2454         cout<<"Storage.lowpassIIR: storage cannot be resampled."<<endl;
2455         return;
2456     }
2457 
2458     // RESAMPLE if the sampling interval is not uniform
2459     if ((avgDt - dtmin) > SimTK::Eps) {
2460         dtmin = resample(dtmin, 5);
2461         size = getSize();
2462     }
2463 
2464     if(size<(4)) {
2465         cout<<"Storage.lowpassIIR: too few data points to filter."<<endl;
2466         return;
2467     }
2468 
2469     // LOOP OVER COLUMNS
2470     int nc = getSmallestNumberOfStates();
2471     double *signal=NULL;
2472     Array<double> filt(0.0,size);
2473     for(int i=0;i<nc;i++) {
2474         getDataColumn(i,signal);
2475         Signal::LowpassIIR(dtmin,aCutoffFrequency,size,signal,&filt[0]);
2476         setDataColumn(i,filt);
2477     }
2478 
2479     // CLEANUP
2480     delete[] signal;
2481 }
2482 
2483 void Storage::
lowpassFIR(int aOrder,double aCutoffFrequency)2484 lowpassFIR(int aOrder,double aCutoffFrequency)
2485 {
2486     int size = getSize();
2487     double dtmin = getMinTimeStep();
2488     double avgDt = (_storage[size-1].getTime() - _storage[0].getTime()) / (size-1);
2489 
2490     if (dtmin<SimTK::Eps) {
2491         cout<<"Storage.lowpassFIR: storage cannot be resampled."<<endl;
2492         return;
2493     }
2494 
2495     // RESAMPLE if the sampling interval is not uniform
2496     if ((avgDt - dtmin) > SimTK::Eps) {
2497         dtmin = resample(dtmin, 5);
2498         size = getSize();
2499     }
2500 
2501     if(size<(2*aOrder)) {
2502         cout<<"Storage.lowpassFIR: too few data points to filter."<<endl;
2503         return;
2504     }
2505 
2506     // LOOP OVER COLUMNS
2507     int nc = getSmallestNumberOfStates();
2508     double *signal=NULL;
2509     Array<double> filt(0.0,size);
2510     for(int i=0;i<nc;i++) {
2511         getDataColumn(i,signal);
2512         Signal::LowpassFIR(aOrder,dtmin,aCutoffFrequency,size,signal,&filt[0]);
2513         setDataColumn(i,filt);
2514     }
2515 
2516     // CLEANUP
2517     delete[] signal;
2518 }
2519 
2520 
2521 //=============================================================================
2522 // UTILITY
2523 //=============================================================================
2524 //_____________________________________________________________________________
2525 /**
2526  * Find the index of the storage element that occurred immediately before
2527  * or at time aT ( aT <= getTime(index) ).
2528  *
2529  * This method can be much more efficient than findIndex(aT) if a good guess
2530  * is made for aI.
2531  * If aI corresponds to a state which occurred later than aT, an exhaustive
2532  * search is performed by calling findIndex(aT).
2533  *
2534  * @param aI Index at which to start searching.
2535  * @param aT Time.
2536  * @return Index preceding or at time aT.  If aT is less than the earliest
2537  * time, 0 is returned.
2538  */
2539 int Storage::
findIndex(int aI,double aT) const2540 findIndex(int aI,double aT) const
2541 {
2542     // MAKE SURE aI IS VALID
2543     if(_storage.getSize()<=0) return(-1);
2544     if((aI>=_storage.getSize())||(aI<0)) aI=0;
2545     if(getStateVector(aI)->getTime()>aT) aI=0;
2546 
2547     // SEARCH
2548     //cout << "Storage.findIndex: starting at " << aI << endl;
2549     int i;
2550     for(i=aI;i<_storage.getSize();i++) {
2551         if(aT<getStateVector(i)->getTime()) break;
2552     }
2553     _lastI = i-1;
2554     if(_lastI<0) _lastI=0;
2555     return(_lastI);
2556 }
2557 //_____________________________________________________________________________
2558 /**
2559  * Find the index of the storage element that occurred immediately before
2560  * or at a specified time ( getTime(index) <= aT ).
2561  *
2562  * This method is not very efficient because it always starts its search
2563  * with the first stored state.
2564  *
2565  * @param aT Time.
2566  * @return Index preceding or at time aT.  If aT is less than the earliest
2567  * time, 0 is returned.
2568  */
2569 int Storage::
findIndex(double aT) const2570 findIndex(double aT) const
2571 {
2572     if(_storage.getSize()<=0) return(-1);
2573     int i;
2574     for(i=0;i<_storage.getSize();i++) {
2575         if(aT<getStateVector(i)->getTime()) break;
2576     }
2577     _lastI = i-1;
2578     if(_lastI<0) _lastI=0;
2579     return(_lastI);
2580 }
2581 //_____________________________________________________________________________
2582 /**
2583  * Find the range of frames that is between start time and end time
2584  * (inclusive). Return the indices of the bounding frames.
2585  */
findFrameRange(double aStartTime,double aEndTime,int & oStartFrame,int & oEndFrame) const2586 void Storage::findFrameRange(double aStartTime, double aEndTime, int& oStartFrame, int& oEndFrame) const
2587 {
2588     SimTK_ASSERT_ALWAYS(aStartTime <= aEndTime, "Start time must be <= end time");
2589 
2590     oStartFrame = findIndex(0, aStartTime);
2591     oEndFrame = findIndex(getSize()-1, aEndTime);
2592 }
2593 //_____________________________________________________________________________
2594 /**
2595  * Resample Storage columns to specified rate. This's done by fitting splines
2596  * to Storage columns and resampling
2597  *
2598  * @param aDT Time interval between adjacent statevectors.
2599  * @return Actual sampling time step (may be clamped)
2600  */
2601 double Storage::
resample(double aDT,int aDegree)2602 resample(double aDT, int aDegree)
2603 {
2604     int numDataRows = _storage.getSize();
2605 
2606     if(numDataRows<=1) return aDT;
2607 
2608     // Limit aDT based on expected number of samples
2609     int maxSamples = MAX_RESAMPLE_SIZE;
2610     if((getLastTime()-getFirstTime())/aDT > maxSamples) {
2611         double newDT = (getLastTime()-getFirstTime())/maxSamples;
2612         cout<<"Storage.resample: WARNING: resampling at time step "<<newDT<<" (but minimum time step is "<<aDT<<")"<<endl;
2613         aDT = newDT;
2614     }
2615 
2616     GCVSplineSet *splineSet = new GCVSplineSet(aDegree,this);
2617 
2618     Array<std::string> saveLabels = getColumnLabels();
2619     // Free up memory used by Storage
2620     _storage.setSize(0);
2621     // For every column, collect data and fit spline to originalTimes, dataColumn.
2622     Storage *newStorage = splineSet->constructStorage(0,aDT);
2623     newStorage->setInDegrees(isInDegrees());
2624     copyData(*newStorage);
2625 
2626     setColumnLabels(saveLabels);
2627 
2628     delete newStorage;
2629     delete splineSet;
2630 
2631     return aDT;
2632 }
2633 //_____________________________________________________________________________
2634 /**
2635  * Resample using linear interpolation
2636  */
2637 double Storage::
resampleLinear(double aDT)2638 resampleLinear(double aDT)
2639 {
2640     int numDataRows = _storage.getSize();
2641 
2642     if(numDataRows<=1) return aDT;
2643 
2644     // Limit aDT based on expected number of samples
2645     int maxSamples = MAX_RESAMPLE_SIZE;
2646     if((getLastTime()-getFirstTime())/aDT > maxSamples) {
2647         double newDT = (getLastTime()-getFirstTime())/maxSamples;
2648         cout<<"Storage.resampleLinear: WARNING: resampling at time step "<<newDT<<" (but minimum time step is "<<aDT<<")"<<endl;
2649         aDT = newDT;
2650     }
2651 
2652     Array<std::string> saveLabels = getColumnLabels();
2653 
2654     // HOW MANY TIME STEPS?
2655     double ti = getFirstTime();
2656     double tf = getLastTime();
2657     int nr = IO::ComputeNumberOfSteps(ti,tf,aDT);
2658 
2659     Storage *newStorage = new Storage(nr);
2660 
2661     // LOOP THROUGH THE DATA
2662     int ny=0;
2663     double *y=NULL;
2664     StateVector vec;
2665     for(int i=0; i<nr; i++) {
2666         double t = ti+aDT*(double)i;
2667         // INTERPOLATE THE STATES
2668         ny = getDataAtTime(t,ny,&y);
2669         newStorage->append(t,ny,y);
2670     }
2671 
2672     copyData(*newStorage);
2673 
2674     delete newStorage;
2675     delete[] y;
2676 
2677     return aDT;
2678 }
2679 
2680 //_____________________________________________________________________________
2681 /**
2682  * interpolateAt passed in list of time values. Tries to check if there is a data
2683  * row at the specified times to avoid introducing duplicates.
2684  */
interpolateAt(const Array<double> & targetTimes)2685 void Storage::interpolateAt(const Array<double> &targetTimes)
2686 {
2687     for(int i=0; i<targetTimes.getSize();i++){
2688         double t = targetTimes[i];
2689         // get index for t
2690         int tIndex = findIndex(t);
2691         // If within small number from t then pass
2692         double actualTime=0.0;
2693         if (tIndex < getSize()-1){
2694             getTime(tIndex+1, actualTime);
2695             if (fabs(actualTime - t)<1e-6)
2696                     continue;
2697         }
2698         // or could be the following one too
2699         getTime(tIndex, actualTime);
2700         if (fabs(actualTime - t)<1e-6)
2701                 continue;
2702         // create a StateVector and add it
2703         double *y=NULL;
2704         int ny=0;
2705         StateVector vec;
2706         // INTERPOLATE THE STATES
2707         ny = getDataAtTime(t,ny,&y);
2708         vec.setStates(t, SimTK::Vector_<double>(ny, y));
2709 
2710         _storage.insert(tIndex+1, vec);
2711     }
2712 }
2713 //=============================================================================
2714 // IO
2715 //=============================================================================
2716 //_____________________________________________________________________________
2717 /**
2718  * Set name of output file to be written into.
2719  * This has the side effect of opening the file for writing. The header will not have the correct
2720  * number of rows but this may not be an issue for version 2 of the Storage class
2721  */
2722 void Storage::
setOutputFileName(const std::string & aFileName)2723 setOutputFileName(const std::string& aFileName)
2724 {
2725     assert(_fileName=="");
2726     _fileName = aFileName;
2727 
2728     // OPEN THE FILE
2729     _fp = IO::OpenFile(aFileName,"w");
2730     if(_fp==NULL) throw(Exception("Could not open file "+aFileName));
2731     // WRITE THE HEADER
2732     writeHeader(_fp);
2733     writeDescription(_fp);
2734     // WRITE THE COLUMN LABELS
2735     writeColumnLabels(_fp);
2736 }
2737 //_____________________________________________________________________________
2738 /**
2739  * Print the contents of this storage instance to a file.
2740  *
2741  * The argument aMode specifies whether the file is opened for writing, "w",
2742  * or appending, "a".  If a bad value for aMode is sent in, the file is opened
2743  * for writing.
2744  *
2745  * The total number of characters written is returned.  If an error occurred,
2746  * a negative number is returned.
2747  *
2748  * @param aFileName Name of file to which to save.
2749  * @param aMode Writing mode: "w" means write and "a" means append.  The
2750  * default is "w".
2751  * @param aComment string to be written to the file header (preceded by # per SIMM)
2752  * @return true on success
2753  */
2754 bool Storage::
print(const string & aFileName,const string & aMode,const string & aComment) const2755 print(const string &aFileName,const string &aMode, const string& aComment) const
2756 {
2757     // OPEN THE FILE
2758     FILE *fp = IO::OpenFile(aFileName,aMode);
2759     if(fp==NULL) return(false);
2760 
2761     // WRITE THE HEADER
2762     int n=0,nTotal=0;
2763     n = writeHeader(fp);
2764     if(n<0) {
2765         cout << "Storage.print(const string&,const string&): failed to" << endl
2766               << " write header to file " << aFileName << endl;
2767         return(false);
2768     }
2769 
2770     // WRITE SIMM HEADER
2771     if(_writeSIMMHeader) {
2772         n = writeSIMMHeader(fp, -1, aComment.c_str());
2773         if(n<0) {
2774             cout << "Storage.print(const string&,const string&): failed to" << endl
2775                   << " write SIMM header to file " << aFileName << endl;
2776             return(false);
2777         }
2778     }
2779 
2780     // WRITE THE DESCRIPTION
2781     n = writeDescription(fp);
2782     if(n<0) {
2783         cout << "Storage.print(const string&,const string&): failed to" << endl
2784               << " write description to file " << aFileName << endl;
2785         return(false);
2786     }
2787 
2788     // WRITE THE COLUMN LABELS
2789     n = writeColumnLabels(fp);
2790     if(n<0) {
2791         cout << "Storage.print(const string&,const string&): failed to" << endl
2792               << " write column labels to file " << aFileName << endl;
2793         return(false);
2794     }
2795 
2796 //printf("Storage.cpp:print storage=%x  n=%d ",&_storage, _storage.getSize());
2797 //std::cout << aFileName << endl;
2798 
2799     // VECTORS
2800     for(int i=0;i<_storage.getSize();i++) {
2801         n = getStateVector(i)->print(fp);
2802         if(n<0) {
2803             cout << "Storage.print(const string&,const string&): error printing to " << aFileName;
2804             return(false);
2805         }
2806         nTotal += n;
2807     }
2808 
2809     // CLOSE
2810     fclose(fp);
2811 
2812     return(nTotal!=0);
2813 }
2814 //_____________________________________________________________________________
2815 /**
2816  * Print the contents of this storage instance to a file named by the argument
2817  * aFileaName using uniform time spacing.
2818  *
2819  * The argument aMode specifies whether the file is opened for writing, "w",
2820  * or appending, "a".  If a bad value for aMode is sent in, the file is opened
2821  * for writing.
2822  *
2823  * The argument aDT specifies the time spacing.
2824  *
2825  * The total number of characters written is returned.  If an error occurred,
2826  * a negative number is returned.
2827  */
2828 int Storage::
print(const string & aFileName,double aDT,const string & aMode) const2829 print(const string &aFileName,double aDT,const string &aMode) const
2830 {
2831     // CHECK FOR VALID DT
2832     if(aDT<=0) return(0);
2833 
2834     if (_fp!= NULL) fclose(_fp);
2835     // OPEN THE FILE
2836     FILE *fp = IO::OpenFile(aFileName,aMode);
2837     if(fp==NULL) return(-1);
2838 
2839     // HOW MANY TIME STEPS?
2840     double ti = getFirstTime();
2841     double tf = getLastTime();
2842     int nr = IO::ComputeNumberOfSteps(ti,tf,aDT);
2843 //printf("Storage.cpp:print ti=%f tf=%f dt=%f nr=%d ", ti,tf,aDT,nr );
2844 //std::cout << aFileName << endl;
2845 
2846     // WRITE THE HEADER
2847     int n,nTotal=0;
2848     n = writeHeader(fp,aDT);
2849     if(n<0) {
2850         cout << "Storage.print(const string&,const string&,double): failed to" << endl
2851               << " write header of file " << aFileName << endl;
2852         return(n);
2853     }
2854 
2855     // WRITE SIMM HEADER
2856     if(_writeSIMMHeader) {
2857         n = writeSIMMHeader(fp,aDT);
2858         if(n<0) {
2859             cout << "Storage.print(const string&,const string&): failed to" << endl
2860                   << " write SIMM header to file " << aFileName << endl;
2861             return(n);
2862         }
2863     }
2864 
2865     // WRITE THE DESCRIPTION
2866     n = writeDescription(fp);
2867     if(n<0) {
2868         cout << "Storage.print(const string&,const string&): failed to" << endl
2869               << " write description to file " << aFileName << endl;
2870         return(n);
2871     }
2872 
2873     // WRITE THE COLUMN LABELS
2874     n = writeColumnLabels(fp);
2875     if(n<0) {
2876         cout << "Storage.print(const string&,const string&): failed to" << endl
2877               << " write column labels to file " << aFileName << endl;
2878         return(n);
2879     }
2880 
2881     // LOOP THROUGH THE DATA
2882     int i,ny=0;
2883     double t,*y=NULL;
2884     StateVector vec;
2885     for(t=ti,i=0;i<nr;i++,t=ti+aDT*(double)i) {
2886 
2887         // INTERPOLATE THE STATES
2888         ny = getDataAtTime(t,ny,&y);
2889         vec.setStates(t, SimTK::Vector_<double>(ny, y));
2890 
2891         // PRINT
2892         n = vec.print(fp);
2893         if(n<0) {
2894             cout << "Storage.print(const string&,const string&): error printing to " << aFileName;
2895             return(n);
2896         }
2897         nTotal += n;
2898     }
2899 
2900     // CLEANUP
2901     fclose(fp);
2902     if(y!=NULL) { delete[] y;  y=NULL; }
2903 
2904     return(nTotal);
2905 }
2906 
2907 void Storage::
printResult(const Storage * aStorage,const std::string & aName,const std::string & aDir,double aDT,const std::string & aExtension)2908 printResult(const Storage *aStorage,const std::string &aName,
2909                 const std::string &aDir,double aDT,const std::string &aExtension)
2910 {
2911     if(!aStorage) return;
2912     std::string path = (aDir=="") ? "." : aDir;
2913     std::string name = (aName.rfind(aExtension)==string::npos)? (path + "/" + aName + aExtension) :  (path + "/" + aName);
2914     if(aDT<=0.0) aStorage->print(name);
2915     else aStorage->print(name,aDT);
2916 }
2917 
2918 //_____________________________________________________________________________
2919 /**
2920  * Write the header.
2921  */
2922 int Storage::
writeHeader(FILE * rFP,double aDT) const2923 writeHeader(FILE *rFP,double aDT) const
2924 {
2925     if(rFP==NULL) return(-1);
2926 
2927     // COMPUTE ATTRIBUTES
2928     int nr,nc;
2929     if(aDT<=0) {
2930         nr = _storage.getSize();
2931     } else {
2932         double ti = getFirstTime();
2933         double tf = getLastTime();
2934         nr = IO::ComputeNumberOfSteps(ti,tf,aDT);
2935     }
2936     nc = getSmallestNumberOfStates()+1;
2937 
2938     // ATTRIBUTES
2939     fprintf(rFP,"%s\n",getName().c_str());
2940     fprintf(rFP,"version=%d\n",LatestVersion);
2941     fprintf(rFP,"nRows=%d\n",nr);
2942     fprintf(rFP,"nColumns=%d\n",nc);
2943     fprintf(rFP,"inDegrees=%s\n",(_inDegrees?"yes":"no"));
2944 
2945     return(0);
2946 }
2947 //_____________________________________________________________________________
2948 /**
2949  * Write a header appropriate for SIMM motion files.
2950  *
2951  * @return SIMM header.
2952  */
2953 int Storage::
writeSIMMHeader(FILE * rFP,double aDT,const char * aComment) const2954 writeSIMMHeader(FILE *rFP,double aDT, const char *aComment) const
2955 {
2956     if(rFP==NULL) return(-1);
2957 
2958     // COMMENT
2959     // NOTE: avoid writing empty comment because SIMM seems to screw up parsing
2960     // of a line with only a #
2961     if (aComment && aComment[0])
2962         fprintf(rFP,"\n# %s\n", aComment);
2963     else
2964         fprintf(rFP,"\n# SIMM Motion File Header:\n");
2965 
2966     // NAME
2967     fprintf(rFP,"name %s\n",getName().c_str());
2968 
2969     // COLUMNS
2970     fprintf(rFP,"datacolumns %d\n",getSmallestNumberOfStates()+1);
2971 
2972     // ROWS
2973     int nRows;
2974     if(aDT<=0) {
2975         nRows = _storage.getSize();
2976     } else {
2977         nRows = IO::ComputeNumberOfSteps(getFirstTime(),getLastTime(),aDT);
2978     }
2979     fprintf(rFP,"datarows %d\n",nRows);
2980 
2981     // OTHER DATA
2982     fprintf(rFP,"otherdata 1\n");
2983 
2984     // RANGE
2985     fprintf(rFP,"range %lf %lf\n",getFirstTime(),getLastTime());
2986 
2987     // Other data from the map
2988     MapKeysToValues::const_iterator iter;
2989 
2990     for(iter = _keyValueMap.begin(); iter != _keyValueMap.end(); iter++){
2991         fprintf(rFP,"%s %s\n",iter->first.c_str(), iter->second.c_str());
2992     }
2993     return(0);
2994 }
2995 //_____________________________________________________________________________
2996 /**
2997  * Write the description.
2998  */
2999 int Storage::
writeDescription(FILE * rFP) const3000 writeDescription(FILE *rFP) const
3001 {
3002     if(rFP==NULL) return(-1);
3003 
3004     // DESCRIPTION
3005     string descrip = getDescription();
3006     size_t len = descrip.size();
3007     if((len>0)&&(descrip[len-1]!='\n')) {
3008         fprintf(rFP,"%s\n",descrip.c_str());
3009     } else {
3010         fprintf(rFP,"%s",descrip.c_str());
3011     }
3012 
3013     // DESCRIPTION TOKEN
3014     fprintf(rFP,"%s\n",_headerToken.c_str());
3015 
3016     return(0);
3017 }
3018 //_____________________________________________________________________________
3019 /**
3020  * Write the column labels.
3021  *
3022  * @param rFP File pointer.
3023  */
3024 int Storage::
writeColumnLabels(FILE * rFP) const3025 writeColumnLabels(FILE *rFP) const
3026 {
3027     if(rFP==NULL) return(-1);
3028 
3029     if(_columnLabels.getSize()) {
3030         fprintf(rFP,"%s",_columnLabels[0].c_str());
3031         for(int i=1;i<_columnLabels.getSize();i++) fprintf(rFP,"\t%s",_columnLabels[i].c_str());
3032         fprintf(rFP,"\n");
3033     } else {
3034         // Write default column labels
3035         fprintf(rFP,"time");
3036         int n=getSmallestNumberOfStates();
3037         for(int i=0;i<n;i++) fprintf(rFP,"\tstate_%d",i);
3038         fprintf(rFP,"\n");
3039     }
3040 
3041     return(0);
3042 }
addToRdStorage(Storage & rStorage,double aStartTime,double aEndTime)3043 void Storage::addToRdStorage(Storage& rStorage, double aStartTime, double aEndTime)
3044 {
3045     bool addedData = false;
3046     double time, stateTime;
3047 
3048     /* Loop through the rows in rStorage from aStartTime to aEndTime,
3049      * looking for a match (by time) in the rows of Storage.
3050      * If you find a match, add the columns in the Storage
3051      * to the end of the state vector in the rStorage. If you
3052      * don't find one, it's a fatal error so throw an exception.
3053      * Don't add a column if its name is 'unassigned'.
3054      */
3055     int i, j, startIndex, endIndex;
3056     rStorage.findFrameRange(aStartTime, aEndTime, startIndex, endIndex);
3057     int numColumns=getColumnLabels().getSize();
3058     for (i = startIndex; i <= endIndex; i++)
3059     {
3060         rStorage.getTime(i, stateTime);
3061         for (j = 0; j < getSize(); j++) {
3062             /* Assume that the first column is 'time'. */
3063             time = getStateVector(j)->getTime();
3064             // The following tolerance is a hack. Previously, it used 0.0001
3065             // which caused values to be duplicated in cases where time
3066             // steps were within the tolerance. This method should only be
3067             // used to concatenate data columns from the same simulation
3068             // or analysis results.
3069             if (EQUAL_WITHIN_TOLERANCE(time, stateTime, SimTK::SignificantReal)) {
3070                 Array<double>& states = rStorage.getStateVector(i)->getData();
3071                 // Start at 1 to avoid duplicate time column
3072                 for (int k = 1; k < numColumns; k++)
3073                 {
3074                     if (_columnLabels[k] != "Unassigned")
3075                     {
3076                         states.append(getStateVector(j)->getData().get(k-1));
3077                         addedData = true;
3078                     }
3079                 }
3080                 break;
3081             }
3082         }
3083         if (j == getSize()) {
3084             stringstream errorMessage;
3085             errorMessage << "Error: no data found at time " << stateTime
3086                 << " in " << _fileName;
3087             throw (Exception(errorMessage.str()));
3088         }
3089     }
3090 
3091     /* Add the coordinate names to the Storage (if at least
3092      * one row of data was added to the object).
3093      */
3094     if (addedData)
3095     {
3096         const Array<std::string>& oldColumnLabels =rStorage.getColumnLabels();
3097         Array<std::string> newColumnLabels(oldColumnLabels);
3098         for (int i = 1; i < _columnLabels.getSize(); i++) // Start at 1 to avoid duplicate time label
3099         {
3100             if (!(_columnLabels[i] == "Unassigned"))
3101                 newColumnLabels.append( _columnLabels[i]);
3102         }
3103         rStorage.setColumnLabels(newColumnLabels);
3104     }
3105 }
3106 //_____________________________________________________________________________
3107 /**
3108  * Processing of special reserved words used by SIMM and corresponding values
3109  * The keys and their corresponding values are maintained in _keyValueMap
3110  */
3111 // Add a new Pair
3112 void Storage::
addKeyValuePair(const std::string & aKey,const std::string & aValue)3113 addKeyValuePair(const std::string& aKey, const std::string& aValue)
3114 {
3115     if (_keyValueMap.find(aKey)!=_keyValueMap.end()){
3116         // Should we warn here? or append in case of comment?
3117     }
3118     _keyValueMap[aKey]=aValue;
3119     //cout << "key, value " << aKey << ", " << aValue << endl;
3120 
3121 }
3122 // Lookup the value for a key
3123 void Storage::
getValueForKey(const std::string & aKey,std::string & rValue) const3124 getValueForKey(const std::string& aKey, std::string& rValue) const
3125 {
3126     MapKeysToValues::const_iterator iter =_keyValueMap.find(aKey);
3127     if (iter!=_keyValueMap.end()){
3128         rValue=iter->second;
3129     }
3130     else
3131         rValue="";
3132 
3133 }
3134 // Check if the key exists
hasKey(const std::string & aKey) const3135 bool Storage::hasKey(const std::string& aKey) const
3136 {
3137     return (_keyValueMap.find(aKey)!=_keyValueMap.end());
3138 }
3139 
3140 //_____________________________________________________________________________
3141 /**
3142  * Check that a Token belongs to a list of reserved keys
3143  *
3144  * @returns true on success (meaningful values of rNumRows, rNumColumns)
3145  */
isSimmReservedToken(const std::string & aToken)3146 bool Storage::isSimmReservedToken(const std::string& aToken)
3147 {
3148     for(int i=0; i<numSimmReservedKeys; i++){
3149         if (simmReservedKeys[i]==aToken)
3150             return true;
3151     }
3152     return false;
3153 }
3154 //_____________________________________________________________________________
3155 /**
3156  * parse headers of OpenSim::Storage file or SIMM motion file into a Storage object
3157  * and populate rNumRows, rNumColumns
3158  * a Map between some keywords and their values for SIMM compatibility
3159  *
3160  * @returns true on success (meaningful values of rNumRows, rNumColumns)
3161  */
parseHeaders(std::ifstream & aStream,int & rNumRows,int & rNumColumns)3162 bool Storage::parseHeaders(std::ifstream& aStream, int& rNumRows, int& rNumColumns)
3163 {
3164     bool done=false;
3165     bool firstLine=true;
3166     _fileVersion = 0;
3167     // Parse until the end of header
3168     while(!done){
3169         // NAME
3170         string line = IO::ReadLine(aStream);
3171         // Always Strip leading and trailing spaces and tabs
3172         IO::TrimLeadingWhitespace(line);
3173         IO::TrimTrailingWhitespace(line);
3174         if(line.empty() && !aStream.good()) {
3175             cout << "Storage: ERROR- no more lines in storage file." << endl;
3176             return false;
3177         }
3178         if (line.length()==0)
3179             continue;
3180 
3181         // Here we have a line. Should be one of:
3182         // name
3183         // nRows, nr, datarows
3184         // nColumns, nc, datacolumns
3185         // nd, nDescrip
3186         // # Comment
3187         size_t delim = line.find_first_of(" \t=");
3188         string key = line.substr(0, delim);
3189         size_t restidx = line.find_first_not_of(" \t=", delim);
3190         string rest = (restidx==string::npos) ? "" : line.substr(restidx);
3191 
3192         if (key== "name"){
3193             setName(rest);
3194         }
3195         else if (key== "nr" || key== "nRows" || key== "datarows"){
3196             rNumRows = atoi(rest.c_str());
3197         }
3198         else if (key== "nc" || key== "nColumns" || key== "datacolumns"){
3199             rNumColumns = atoi(rest.c_str());
3200         }
3201         else if (isSimmReservedToken(key)) {
3202                 _keyValueMap[key]= rest;
3203         }
3204         else if (key== "units") {
3205                 _units = Units(rest);
3206         }
3207         else if (key=="version") {
3208             _fileVersion = atoi(rest.c_str());
3209         }
3210         else if (key=="inDegrees") {
3211             string lower = IO::Lowercase(rest);
3212             bool inDegrees = (lower=="yes" || lower=="y");
3213             setInDegrees(inDegrees);
3214         }
3215         else if (key=="Angles" && _fileVersion==0){
3216             if (line == "Angles are in degrees.")
3217                 setInDegrees(true);
3218             else if (line == "Angles are in radians.")
3219                 setInDegrees(false);
3220         }
3221         else if(key== DEFAULT_HEADER_TOKEN){
3222             break;
3223         }
3224         else if (firstLine){    // Storage file have their names without "name prefix on first line"
3225             setName(line);
3226         }
3227         firstLine=false;
3228     }
3229 
3230     if (_fileVersion < 1) {
3231         cout << "Old version storage/motion file encountered" << endl;
3232     }
3233 
3234     if(rNumColumns==0 || rNumRows==0) {
3235         return false;
3236     }
3237 
3238     return true;
3239 }
3240 //_____________________________________________________________________________
3241 /**
3242  * This function exchanges the time column (including the label) with the column
3243  * at the passed in aColumnIndex. The index is zero based relative to the Data
3244  */
3245 void Storage::
exchangeTimeColumnWith(int aColumnIndex)3246 exchangeTimeColumnWith(int aColumnIndex)
3247 {
3248     StateVector* vec;
3249     for(int i=0; i< _storage.getSize(); i++){
3250         vec = getStateVector(i);
3251         double swap = vec->getData().get(aColumnIndex);
3252         double time=vec->getTime();
3253         vec->setDataValue(aColumnIndex, time);
3254         vec->setTime(swap);
3255     }
3256     // Now column labels
3257     string swap = _columnLabels.get(0);
3258     _columnLabels.set(aColumnIndex+1, swap);
3259     _columnLabels.set(0, "time");
3260 
3261 }
3262 //_____________________________________________________________________________
3263 /**
3264  * If that was a SIMM motion file post-process it to account for
3265  * lack of time column, assumption of uniform time
3266  * other kinds of processing can be added here to account for calc_derivatives, ...
3267  *
3268  * This is all untested since all motion files we deal with so far do not exhibit this behavior.
3269  */
postProcessSIMMMotion()3270 void Storage::postProcessSIMMMotion()
3271 {
3272     Array<std::string> currentLabels = getColumnLabels();
3273     // If time is not first column check if it exists somewhere else and exchange
3274     if (!(currentLabels.get(0)=="time")){
3275         int timeColumnIndx = currentLabels.findIndex("time");
3276         if (timeColumnIndx!=-1){ // Exchange column timeColumnIndx with time
3277             exchangeTimeColumnWith(timeColumnIndx);
3278         }
3279         else {
3280             // There was no time column altogether. make one based on
3281             // range (if specified) and number of entries
3282             MapKeysToValues::iterator iter;
3283             iter = _keyValueMap.find("range");  // Should we check "Range", "RANGE" too?
3284             if (iter !=_keyValueMap.end()){
3285                 string rangeValue = iter->second;
3286                 double start, end;
3287                 sscanf(rangeValue.c_str(), "%lf %lf", &start, &end);
3288                 if (_storage.getSize()<2){  // Something wrong throw exception unless start==end
3289                     if (start !=end){
3290                         stringstream errorMessage;
3291                         errorMessage << "Error: Motion file has inconsistent headers";
3292                         throw (Exception(errorMessage.str()));
3293                     }
3294                     else if (_storage.getSize()==1){
3295                         // Prepend a Time column
3296                         StateVector vec = _storage.get(0);
3297                         vec.getData().append(0.0);
3298                         _columnLabels.append("time");
3299                         exchangeTimeColumnWith(_columnLabels.findIndex("time"));
3300                     }
3301                     else
3302                         throw (Exception("File has no data"));
3303                 }
3304                 else {  // time  column from range, size
3305                     double timeStep = (end - start)/(_storage.getSize()-1);
3306                     _columnLabels.append("time");
3307                     for(int i=0; i<_storage.getSize(); i++){
3308                         Array<double>& data=_storage.updElt(i).getData();
3309                         data.append(i*timeStep);
3310                     }
3311                     int timeColumnIndex=_columnLabels.findIndex("time");
3312                     exchangeTimeColumnWith(timeColumnIndex-1);
3313                 }
3314             }
3315             else {  // No time specified altogether
3316                     throw (Exception("Storage::postProcessSIMMMotion no time column found."));
3317             }
3318         }
3319     }
3320 }
3321 /**
3322  * Compare column named "aColumnName" in two storage objects
3323  * If endTime is not specified the comparison goes to the end of the file
3324  * @returns the difference or SimTK::Infinity if times do not match up.
3325  *
3326  * NOTE: This assumes same time sampling between both Storages.
3327  */
compareColumn(Storage & aOtherStorage,const std::string & aColumnName,double startTime,double endTime)3328 double Storage::compareColumn(Storage& aOtherStorage, const std::string& aColumnName, double startTime, double endTime)
3329 {
3330     //Subtract one since, the data does not include the time column anymore.
3331     int thisColumnIndex=_columnLabels.findIndex(aColumnName)-1;
3332     int otherColumnIndex = aOtherStorage._columnLabels.findIndex(aColumnName)-1;
3333 
3334     double theDiff = SimTK::NaN;
3335 
3336     if ((thisColumnIndex==-2)||(otherColumnIndex==-2))// not found is now -2 since we subtracted 1 already
3337         return theDiff;
3338 
3339     // Now we have two columnNumbers. get the data and compare
3340     Array<double> thisData, otherData;
3341     Array<double> thisTime, otherTime;
3342     getDataColumn(thisColumnIndex, thisData);
3343     getTimeColumn(thisTime);
3344 
3345     aOtherStorage.getDataColumn(otherColumnIndex, otherData);
3346     aOtherStorage.getTimeColumn(otherTime);
3347 
3348     // make sure times match (we probably should make this more flexible to interpolate missing values
3349     // but that's not needed for now.
3350     theDiff = -SimTK::Infinity;
3351     int startIndex = findIndex(startTime);
3352     int startIndexOther = aOtherStorage.findIndex(startTime);
3353     int endIndex = (endTime==-1.0)?getSize():findIndex(endTime);
3354     int endIndexOther = (endTime==-1.0)?(aOtherStorage.getSize()):aOtherStorage.findIndex(endTime);
3355     // Make sure we have same number of rows
3356     if ((endIndex -startIndex)!= (endIndexOther -startIndexOther)) return (theDiff);
3357 
3358     for(int i=startIndex; i< endIndex; i++){
3359         if (abs(thisTime[i]-otherTime[startIndexOther+i-startIndex]) > 1E-3)
3360             return SimTK::Infinity;
3361         theDiff = std::max(theDiff, fabs(thisData[i]-otherData[startIndexOther+i-startIndex]));
3362     }
3363     return theDiff;
3364 }
3365 /**
3366  * Compare column named "aColumnName" in two storage objects
3367  * If endTime is not specified the comparison goes to the end of the file
3368  * @returns the root mean square, using a spline to calculate values where the times do not match up.
3369  */
compareColumnRMS(const Storage & aOtherStorage,const std::string & aColumnName,double startTime,double endTime) const3370 double Storage::compareColumnRMS(const Storage& aOtherStorage, const std::string& aColumnName, double startTime, double endTime) const
3371 {
3372     int thisColumnIndex = getStateIndex(aColumnName);
3373     int otherColumnIndex = aOtherStorage.getStateIndex(aColumnName);
3374 
3375     if ((thisColumnIndex < 0) || (otherColumnIndex < 0))
3376         return SimTK::NaN;
3377 
3378     // Now we have two columnNumbers. get the data and compare
3379     Array<double> thisData, otherData;
3380     Array<double> thisTime, otherTime;
3381     getDataColumn(thisColumnIndex, thisData);
3382     getTimeColumn(thisTime);
3383     aOtherStorage.getDataColumn(otherColumnIndex, otherData);
3384     aOtherStorage.getTimeColumn(otherTime);
3385 
3386     // get start and end indices
3387     if (SimTK::isNaN(startTime))
3388         startTime = max(thisTime[0], otherTime[0]);
3389     int startIndex = findIndex(startTime);
3390     if (thisTime[startIndex] < startTime)
3391         ++startIndex;
3392     if (SimTK::isNaN(endTime))
3393         endTime = min(thisTime.getLast(), otherTime.getLast());
3394     int endIndex = findIndex(endTime);
3395 
3396     // create spline in case time values do not match up
3397     GCVSpline spline(3, otherTime.getSize(), &otherTime[0], &otherData[0]);
3398 
3399     double rms = 0.;
3400 
3401     for(int i = startIndex; i <= endIndex; i++) {
3402         SimTK::Vector inputTime(1, thisTime[i]);
3403         double diff = thisData[i] - spline.calcValue(inputTime);
3404         rms += diff * diff;
3405     }
3406 
3407     rms = sqrt(rms/(endIndex - startIndex));
3408 
3409     return rms;
3410 }
3411 /**
3412  * Compare this storage object with a standard storage object. Find RMS
3413  * errors for columns occurring in both storage objects, and record the
3414  * values and column names in the comparisons and columnsUsed Arrays.
3415  */
compareWithStandard(const Storage & standard,std::vector<string> & columnsUsed,std::vector<double> & comparisons) const3416 void Storage::compareWithStandard(const Storage& standard, std::vector<string>& columnsUsed, std::vector<double>& comparisons) const
3417 {
3418     int maxColumns = _columnLabels.getSize();
3419 
3420     for (int i = 1; i < maxColumns; ++i) {
3421         double comparison = compareColumnRMS(standard, _columnLabels[i]);
3422         if (!SimTK::isNaN(comparison)) {
3423             comparisons.push_back(comparison);
3424             columnsUsed.push_back(_columnLabels[i]);
3425         }
3426     }
3427 }
3428 
makeStorageLabelsUnique()3429 bool Storage::makeStorageLabelsUnique() {
3430     Array<std::string> lbls = getColumnLabels();
3431     std::string offending = "";
3432     bool changedLabels = false;
3433     for(int i = 0; i < lbls.getSize(); i++){
3434         bool isUnique = (lbls.findIndex(lbls[i]) == i);
3435         if (!isUnique) { // Make new names
3436             offending = lbls[i];
3437             bool exist = true;
3438             std::string newName = offending;
3439             changedLabels = true;
3440             int c = 1;
3441             while (exist) {
3442                 char cString[20];
3443                 sprintf(cString,"%d", c);
3444                 newName = std::string(cString) + "_" + offending;
3445                 exist = (lbls.findIndex(newName) != -1);
3446                 c++;
3447             }
3448             lbls[i] = newName;
3449         }
3450     }
3451     if (changedLabels) setColumnLabels(lbls);
3452     const bool labelsWereUnique = (!changedLabels);
3453     return labelsWereUnique;
3454 }
3455 
storageLabelsAreUnique() const3456 bool Storage::storageLabelsAreUnique() const {
3457     const auto& lbls = getColumnLabels();
3458     for(int i = 0; i < lbls.getSize(); i++) {
3459         const bool isUnique = (lbls.findIndex(lbls[i]) == i);
3460         if (!isUnique) return false;
3461     }
3462     return true;
3463 }
3464