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