1 /* -------------------------------------------------------------------------- *
2 * OpenSim: Constraint.cpp *
3 * -------------------------------------------------------------------------- *
4 * The OpenSim API is a toolkit for musculoskeletal modeling and simulation. *
5 * See http://opensim.stanford.edu and the NOTICE file for more information. *
6 * OpenSim is developed at Stanford University and supported by the US *
7 * National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA *
8 * through the Warrior Web program. *
9 * *
10 * Copyright (c) 2005-2017 Stanford University and the Authors *
11 * Author(s): Frank C. Anderson, Ajay Seth *
12 * *
13 * Licensed under the Apache License, Version 2.0 (the "License"); you may *
14 * not use this file except in compliance with the License. You may obtain a *
15 * copy of the License at http://www.apache.org/licenses/LICENSE-2.0. *
16 * *
17 * Unless required by applicable law or agreed to in writing, software *
18 * distributed under the License is distributed on an "AS IS" BASIS, *
19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
20 * See the License for the specific language governing permissions and *
21 * limitations under the License. *
22 * -------------------------------------------------------------------------- */
23
24 //=============================================================================
25 // INCLUDES
26 //=============================================================================
27 #include "Constraint.h"
28 #include <OpenSim/Simulation/Model/Model.h>
29 #include "simbody/internal/Constraint.h"
30
31 //=============================================================================
32 // STATICS
33 //=============================================================================
34 using namespace std;
35 //using namespace SimTK;
36 using namespace OpenSim;
37
38 //=============================================================================
39 // CONSTRUCTOR(S) AND DESTRUCTOR
40 //=============================================================================
41 //_____________________________________________________________________________
42 /**
43 * Default constructor.
44 */
Constraint()45 Constraint::Constraint()
46 {
47 setNull();
48 constructProperties();
49 }
50
51 //_____________________________________________________________________________
52 /**
53 * Destructor.
54 */
~Constraint()55 Constraint::~Constraint()
56 {
57 }
58
59 //_____________________________________________________________________________
60 /**
61 * Set the data members of this Constraint to their null values.
62 */
setNull(void)63 void Constraint::setNull(void)
64 {
65 setAuthors("Frank Anderson, Ajay Seth");
66 }
67
68 //_____________________________________________________________________________
69 /**
70 * Connect properties to local pointers.
71 */
constructProperties(void)72 void Constraint::constructProperties(void)
73 {
74 constructProperty_isEnforced(true);
75 }
76
updateFromXMLNode(SimTK::Xml::Element & node,int versionNumber)77 void Constraint::updateFromXMLNode(SimTK::Xml::Element& node,
78 int versionNumber) {
79 if (versionNumber < XMLDocument::getLatestVersion()) {
80 if (versionNumber < 30509) {
81 // Rename property 'isDisabled' to 'isEnforced' and
82 // negate the contained value.
83 std::string oldName{ "isDisabled" };
84 std::string newName{ "isEnforced" };
85 if (node.hasElement(oldName)) {
86 auto elem = node.getRequiredElement(oldName);
87 bool isDisabled = false;
88 elem.getValue().tryConvertToBool(isDisabled);
89
90 // now update tag name to 'isEnforced'
91 elem.setElementTag(newName);
92 // update its value to be the opposite of 'isDisabled'
93 elem.setValue(SimTK::String(!isDisabled));
94 }
95 }
96 }
97
98 Super::updateFromXMLNode(node, versionNumber);
99 }
100
101 //_____________________________________________________________________________
102 /**
103 * Perform some set up functions that happen after the
104 * object has been deserialized or copied.
105 *
106 * @param aModel OpenSim model containing this Constraint.
107 */
extendConnectToModel(Model & aModel)108 void Constraint::extendConnectToModel(Model& aModel)
109 {
110 Super::extendConnectToModel(aModel);
111 }
112
extendInitStateFromProperties(SimTK::State & s) const113 void Constraint::extendInitStateFromProperties(SimTK::State& s) const
114 {
115 Super::extendInitStateFromProperties(s);
116 SimTK::Constraint& simConstraint =
117 _model->updMatterSubsystem().updConstraint(_index);
118
119 // Otherwise we have to change the status of the constraint
120 if(get_isEnforced())
121 simConstraint.enable(s);
122 else
123 simConstraint.disable(s);
124 }
125
extendSetPropertiesFromState(const SimTK::State & state)126 void Constraint::extendSetPropertiesFromState(const SimTK::State& state)
127 {
128 Super::extendSetPropertiesFromState(state);
129 set_isEnforced(isEnforced(state));
130 }
131
132 //=============================================================================
133 // UTILITY
134 //=============================================================================
135 //_____________________________________________________________________________
136 /**
137 * Update an existing Constraint with parameter values from a
138 * new one, but only for the parameters that were explicitly
139 * specified in the XML node.
140 *
141 * @param aConstraint Constraint to update from
142 */
updateFromConstraint(SimTK::State & s,const Constraint & aConstraint)143 void Constraint::updateFromConstraint(SimTK::State& s,
144 const Constraint &aConstraint)
145 {
146 setIsEnforced(s, aConstraint.isEnforced(s));
147 }
148
149 //=============================================================================
150 // GET AND SET
151 //=============================================================================
152 //-----------------------------------------------------------------------------
153 // ENFORCED
154 //-----------------------------------------------------------------------------
155
isEnforced(const SimTK::State & s) const156 bool Constraint::isEnforced(const SimTK::State& s) const
157 {
158 return !_model->updMatterSubsystem().updConstraint(_index).isDisabled(s);
159 }
160
setIsEnforced(SimTK::State & s,bool isEnforced)161 bool Constraint::setIsEnforced(SimTK::State& s, bool isEnforced)
162 {
163 SimTK::Constraint& simConstraint =
164 _model->updMatterSubsystem().updConstraint(_index);
165 bool modelConstraintIsEnforced = !simConstraint.isDisabled(s);
166
167 // Check if we already have the correct enabling of the constraint then
168 // do nothing
169 if(isEnforced == modelConstraintIsEnforced)
170 return true;
171
172 // Otherwise we have to change the status of the constraint
173 if(isEnforced)
174 simConstraint.enable(s);
175 else
176 simConstraint.disable(s);
177
178 _model->updateAssemblyConditions(s);
179 set_isEnforced(isEnforced);
180
181 return true;
182 }
183
184
185 //-----------------------------------------------------------------------------
186 // FORCES
187 //-----------------------------------------------------------------------------
188 //_____________________________________________________________________________
189 /**
190 * Ask the constraint for the forces it is imposing on the system
191 * Simbody multibody system must be realized to at least position
192 * Returns: the bodyForces on those bodies being constrained (constrainedBodies)
193 * a SpatialVec (6 components) describing resulting torque and force
194 * mobilityForces acting along constrained mobilities
195 *
196 * @param state State of model
197 * @param bodyForcesInAncestor is a Vector of SpatialVecs contain constraint
198 forces
199 * @param mobilityForces is a Vector of forces that act along the constrained
200 * mobilities associated with this constraint
201 */
202 void Constraint::
calcConstraintForces(const SimTK::State & s,SimTK::Vector_<SimTK::SpatialVec> & bodyForcesInAncestor,SimTK::Vector & mobilityForces) const203 calcConstraintForces(const SimTK::State& s,
204 SimTK::Vector_<SimTK::SpatialVec>& bodyForcesInAncestor,
205 SimTK::Vector& mobilityForces) const {
206 SimTK::Constraint& simConstraint =
207 _model->updMatterSubsystem().updConstraint(_index);
208 if(!simConstraint.isDisabled(s)){
209 SimTK::Vector multipliers = simConstraint.getMultipliersAsVector(s);
210 simConstraint.calcConstraintForcesFromMultipliers(s, multipliers,
211 bodyForcesInAncestor,
212 mobilityForces);
213 }
214 }
215
216 /**
217 * Methods to query a Constraint forces for the value actually applied during simulation
218 * The names of the quantities (column labels) is returned by this first function
219 * getRecordLabels()
220 */
getRecordLabels() const221 Array<std::string> Constraint::getRecordLabels() const
222 {
223 SimTK::Constraint& simConstraint = _model->updMatterSubsystem().updConstraint(_index);
224 const SimTK::State &ds = _model->getWorkingState();
225
226 // number of bodies being directly constrained
227 int ncb = simConstraint.getNumConstrainedBodies();
228 // number of mobilities being directly constrained
229 int ncm = simConstraint.getNumConstrainedU(ds);
230
231 const auto& physicalFrames = _model->getComponentList<PhysicalFrame>();
232
233 Array<std::string> labels("");
234
235 for(int i=0; i<ncb; ++i){
236 const SimTK::MobilizedBody &b = simConstraint.getMobilizedBodyFromConstrainedBody(SimTK::ConstrainedBodyIndex(i));
237 const SimTK::MobilizedBodyIndex &bx = b.getMobilizedBodyIndex();
238 const PhysicalFrame *frame = nullptr;
239 for(auto& phf : physicalFrames){
240 if(phf.getMobilizedBodyIndex() == bx){
241 frame = &phf;
242 break;
243 }
244 }
245 if(frame == nullptr){
246 throw Exception("Constraint "+getName()+" does not have an identifiable body index.");
247 }
248 string prefix = getName()+"_"+frame->getName();
249 labels.append(prefix+"_Fx");
250 labels.append(prefix+"_Fy");
251 labels.append(prefix+"_Fz");
252 labels.append(prefix+"_Mx");
253 labels.append(prefix+"_My");
254 labels.append(prefix+"_Mz");
255 }
256
257 char c[2] = "";
258 for(int i=0; i<ncm; ++i){
259 sprintf(c, "%d", i);
260 labels.append(getName()+"_mobility_F"+c);
261 }
262
263 return labels;
264 }
265
266 /**
267 * Given SimTK::State object extract all the values necessary to report constraint forces, application
268 * location frame, etc. used in conjunction with getRecordLabels and should return same size Array
269 */
getRecordValues(const SimTK::State & state) const270 Array<double> Constraint::getRecordValues(const SimTK::State& state) const
271 {
272 // EOMs are solved for accelerations (udots) and constraint multipliers (lambdas)
273 // simultaneously, so system must be realized to acceleration
274 _model->getMultibodySystem().realize(state, SimTK::Stage::Acceleration);
275 SimTK::Constraint& simConstraint = _model->updMatterSubsystem().updConstraint(_index);
276
277 // number of bodies being directly constrained
278 int ncb = simConstraint.getNumConstrainedBodies();
279 // number of mobilities being directly constrained
280 int ncm = simConstraint.getNumConstrainedU(state);
281
282 SimTK::Vector_<SimTK::SpatialVec> bodyForcesInAncestor(ncb);
283 bodyForcesInAncestor.setToZero();
284 SimTK::Vector mobilityForces(ncm, 0.0);
285
286 Array<double> values(0.0,6*ncb+ncm);
287
288 calcConstraintForces(state, bodyForcesInAncestor, mobilityForces);
289
290 for(int i=0; i<ncb; ++i){
291 for(int j=0; j<3; ++j){
292 // Simbody constraints have reaction moments first and OpenSim reports forces first
293 // so swap them here
294 values[i*6+j] = (bodyForcesInAncestor(i)[1])[j]; // moments on constrained body i
295 values[i*6+3+j] = (bodyForcesInAncestor(i)[0])[j]; // forces on constrained body i
296 }
297 }
298 for(int i=0; i<ncm; ++i){
299 values[6*ncb+i] = mobilityForces[i];
300 }
301
302 return values;
303 };
304