1 /* -------------------------------------------------------------------------- *
2  *                     OpenSim:  SimulationUtilities.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-2018 Stanford University and the Authors                *
11  * Author(s): OpenSim Team                                                    *
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 #include "SimulationUtilities.h"
25 
26 #include "Model/Model.h"
27 #include "Manager/Manager.h"
28 #include <simbody/internal/Visualizer_InputListener.h>
29 
30 using namespace OpenSim;
31 
simulate(Model & model,const SimTK::State & initialState,double finalTime,bool saveStatesFile)32 SimTK::State OpenSim::simulate(Model& model,
33     const SimTK::State& initialState,
34     double finalTime,
35     bool saveStatesFile)
36 {
37     // Returned state begins as a copy of the initial state
38     SimTK::State state = initialState;
39     SimTK::Visualizer::InputSilo* silo;
40 
41     bool simulateOnce = true;
42 
43     // Ensure the final time is in the future.
44     const double initialTime = initialState.getTime();
45     if (finalTime <= initialTime) {
46         std::cout << "The final time must be in the future (current time is "
47                   << initialTime << "); simulation aborted." << std::endl;
48         return state;
49     }
50 
51     // Configure the visualizer.
52     if (model.getUseVisualizer()) {
53         SimTK::Visualizer& viz = model.updVisualizer().updSimbodyVisualizer();
54         // We use the input silo to get key presses.
55         silo = &model.updVisualizer().updInputSilo();
56 
57         SimTK::DecorativeText help("Press any key to start a new simulation; "
58             "ESC to quit.");
59         help.setIsScreenText(true);
60         viz.addDecoration(SimTK::MobilizedBodyIndex(0), SimTK::Vec3(0), help);
61 
62         viz.setShowSimTime(true);
63         viz.drawFrameNow(state);
64         std::cout << "A visualizer window has opened." << std::endl;
65 
66         // if visualizing enable replay
67         simulateOnce = false;
68     }
69 
70     // Simulate until the user presses ESC (or enters 'q' if visualization has
71     // been disabled).
72     do {
73         if (model.getUseVisualizer()) {
74             // Get a key press.
75             silo->clear(); // Ignore any previous key presses.
76             unsigned key, modifiers;
77             silo->waitForKeyHit(key, modifiers);
78             if (key == SimTK::Visualizer::InputListener::KeyEsc) { break; }
79         }
80 
81         // reset the state to the initial state
82         state = initialState;
83         // Set up manager and simulate.
84         Manager manager(model);
85         state.setTime(initialTime);
86         manager.initialize(state);
87         state = manager.integrate(finalTime);
88 
89         // Save the states to a storage file (if requested).
90         if (saveStatesFile) {
91             manager.getStateStorage().print(model.getName() + "_states.sto");
92         }
93     } while (!simulateOnce);
94 
95     return state;
96 }
97 
98 std::unique_ptr<Storage>
updatePre40KinematicsStorageFor40MotionType(const Model & pre40Model,const Storage & kinematics)99 OpenSim::updatePre40KinematicsStorageFor40MotionType(const Model& pre40Model,
100         const Storage &kinematics)
101 {
102     // There is no issue if the kinematics are in internal values (i.e. not
103     // converted to degrees)
104     if(!kinematics.isInDegrees()) return nullptr;
105 
106     if (pre40Model.getDocumentFileVersion() >= 30415) {
107         throw Exception("updateKinematicsStorageForUpdatedModel has no updates "
108             "to make because the model '" + pre40Model.getName() + "'is up-to-date.\n"
109             "If input motion files were generated with this model version, "
110             "nothing further must be done. Otherwise, provide the original model "
111             "file used to generate the motion files and try again.");
112     }
113 
114     std::vector<const Coordinate*> problemCoords;
115     auto coordinates = pre40Model.getComponentList<Coordinate>();
116     for (auto& coord : coordinates) {
117         const Coordinate::MotionType oldMotionType =
118                 coord.getUserSpecifiedMotionTypePriorTo40();
119         const Coordinate::MotionType motionType = coord.getMotionType();
120 
121         if ((oldMotionType != Coordinate::MotionType::Undefined) &&
122             (oldMotionType != motionType)) {
123             problemCoords.push_back(&coord);
124         }
125     }
126 
127     if (problemCoords.size() == 0)
128         return nullptr;
129 
130     std::unique_ptr<Storage> updatedKinematics(kinematics.clone());
131     // Cycle the inconsistent Coordinates
132     for (const auto& coord : problemCoords) {
133         // Get the corresponding column of data and if in degrees
134         // undo the radians to degrees conversion on that column.
135         int ix = updatedKinematics->getStateIndex(coord->getName());
136 
137         if (ix < 0) {
138             std::cout << "updateKinematicsStorageForUpdatedModel(): motion '"
139             << kinematics.getName() << "' does not contain inconsistent "
140             << "coordinate '" << coord->getName() << "'." << std::endl;
141         }
142         else {
143             // convert this column back to internal values by undoing the
144             // 180/pi conversion to degrees
145             updatedKinematics->multiplyColumn(ix, SimTK_DTR);
146         }
147     }
148     return updatedKinematics;
149 }
150 
151 
updatePre40KinematicsFilesFor40MotionType(const Model & model,const std::vector<std::string> & filePaths,std::string suffix)152 void OpenSim::updatePre40KinematicsFilesFor40MotionType(const Model& model,
153         const std::vector<std::string>& filePaths,
154         std::string suffix)
155 {
156     // Cycle through the data files
157     for (const auto& filePath : filePaths) {
158         Storage motion(filePath);
159         auto updatedMotion =
160             updatePre40KinematicsStorageFor40MotionType(model, motion);
161 
162         if (updatedMotion == nullptr) {
163             continue; // no update was required, move on to next file
164         }
165 
166         std::string outFilePath = filePath;
167         if (suffix.size()) {
168             auto back = filePath.rfind(".");
169             outFilePath = filePath.substr(0, back) + suffix +
170                             filePath.substr(back);
171         }
172         std::cout << "Writing converted motion '" << filePath << "' to '"
173             << outFilePath << "'." << std::endl;
174 
175         updatedMotion->print(outFilePath);
176     }
177 }
178 
updateSocketConnecteesBySearch(Model & model)179 void OpenSim::updateSocketConnecteesBySearch(Model& model)
180 {
181     int numSocketsUpdated = 0;
182     for (auto& comp : model.updComponentList()) {
183         const auto socketNames = comp.getSocketNames();
184         for (int i = 0; i < socketNames.size(); ++i) {
185             auto& socket = comp.updSocket(socketNames[i]);
186             try {
187                 socket.finalizeConnection(model);
188             } catch (const ComponentNotFoundOnSpecifiedPath&) {
189                 const ComponentPath path(socket.getConnecteePath());
190                 if (path.getNumPathLevels() >= 1) {
191                     const Component* found =
192                         model.findComponent(path.getComponentName());
193                     if (found) {
194                         socket.connect(*found);
195                         socket.finalizeConnection(model);
196                         numSocketsUpdated += 1;
197                     } else {
198                         std::cout << "Socket '" << socketNames[i] << "' in "
199                                 << "Component " << comp.getAbsolutePathString()
200                                 << " needs updating but a connectee with the "
201                                    "specified name could not be found."
202                                 << std::endl;
203                     }
204                 }
205             } catch (const std::exception& e) {
206                 std::cout << "Warning: Caught exception when processing "
207                     "Socket " << socketNames[i] << " in " <<
208                     comp.getConcreteClassName() << " at " <<
209                     comp.getAbsolutePathString() << ": " << e.what() <<
210                     std::endl;
211             }
212         }
213     }
214     if (numSocketsUpdated) {
215         std::cout << "OpenSim::updateSocketConnecteesBySearch(): updated "
216                 << numSocketsUpdated << " Sockets in Model '"
217                 << model.getName() << "'." << std::endl;
218     } else {
219         std::cout << "OpenSim::updateSocketConnecteesBySearch(): "
220                      "no Sockets updated in Model '"
221                   << model.getName() << "'." << std::endl;
222     }
223 }
224