1 /* -------------------------------------------------------------------------- *
2  *                               Simbody(tm)                                  *
3  * -------------------------------------------------------------------------- *
4  * This is part of the SimTK biosimulation toolkit originating from           *
5  * Simbios, the NIH National Center for Physics-Based Simulation of           *
6  * Biological Structures at Stanford, funded under the NIH Roadmap for        *
7  * Medical Research, grant U54 GM072970. See https://simtk.org/home/simbody.  *
8  *                                                                            *
9  * Portions copyright (c) 2007-14 Stanford University and the Authors.        *
10  * Authors: Michael Sherman                                                   *
11  * Contributors:                                                              *
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 /**@file
25  *
26  * Private implementation of Constraint, and its included subclasses which
27  * represent the built-in constraint types.
28  */
29 
30 #include "SimTKcommon.h"
31 #include "simbody/internal/common.h"
32 #include "simbody/internal/Constraint.h"
33 #include "simbody/internal/Constraint_BuiltIns.h"
34 
35 #include "ConstraintImpl.h"
36 #include "SimbodyMatterSubsystemRep.h"
37 #include "MobilizedBodyImpl.h"
38 
39 #include <algorithm>
40 
41 namespace SimTK {
42 
43 
44 //==============================================================================
45 //                               CONSTRAINT
46 //==============================================================================
47 
disable(State & s) const48 void Constraint::disable(State& s) const {
49     getImpl().setDisabled(s, true);
50 }
enable(State & s) const51 void Constraint::enable(State& s) const {
52     getImpl().setDisabled(s, false);
53 }
isDisabled(const State & s) const54 bool Constraint::isDisabled(const State& s) const {
55     return getImpl().isDisabled(s);
56 }
isDisabledByDefault() const57 bool Constraint::isDisabledByDefault() const {
58     return getImpl().isDisabledByDefault();
59 }
setDisabledByDefault(bool shouldBeDisabled)60 void Constraint::setDisabledByDefault(bool shouldBeDisabled) {
61     updImpl().setDisabledByDefault(shouldBeDisabled);
62 }
63 
setIsConditional(bool isConditional)64 void Constraint::setIsConditional(bool isConditional) {
65     updImpl().setIsConditional(isConditional);
66 }
67 
isConditional() const68 bool Constraint::isConditional() const {
69     return getImpl().isConditional();
70 }
71 
getMatterSubsystem() const72 const SimbodyMatterSubsystem& Constraint::getMatterSubsystem() const {
73     SimTK_ASSERT_ALWAYS(isInSubsystem(),
74         "getMatterSubsystem() called on a Constraint that is not part of a subsystem.");
75     return getImpl().getMyMatterSubsystemRep().getMySimbodyMatterSubsystemHandle();
76 }
77 
getConstraintIndex() const78 ConstraintIndex Constraint::getConstraintIndex() const {
79     SimTK_ASSERT_ALWAYS(isInSubsystem(),
80         "getConstraintIndex() called on a Constraint that is not part of a subsystem.");
81     return getImpl().getMyConstraintIndex();
82 }
83 
updMatterSubsystem()84 SimbodyMatterSubsystem& Constraint::updMatterSubsystem() {
85     SimTK_ASSERT_ALWAYS(isInSubsystem(),
86         "updMatterSubsystem() called on a Constraint that is not part of a subsystem.");
87     return updImpl().updMyMatterSubsystemRep().updMySimbodyMatterSubsystemHandle();
88 }
89 
isInSubsystem() const90 bool Constraint::isInSubsystem() const {
91     return getImpl().isInSubsystem();
92 }
93 
isInSameSubsystem(const MobilizedBody & body) const94 bool Constraint::isInSameSubsystem(const MobilizedBody& body) const {
95     return getImpl().isInSameSubsystem(body);
96 }
97 
getNumConstrainedBodies() const98 int Constraint::getNumConstrainedBodies() const {
99     return getImpl().getNumConstrainedBodies();
100 }
getNumConstrainedMobilizers() const101 int Constraint::getNumConstrainedMobilizers() const {
102     return getImpl().getNumConstrainedMobilizers();
103 }
104 
getNumConstrainedQ(const State & s) const105 int Constraint::getNumConstrainedQ(const State& s) const {
106     return getImpl().getNumConstrainedQ(s);
107 }
getNumConstrainedU(const State & s) const108 int Constraint::getNumConstrainedU(const State& s) const {
109     return getImpl().getNumConstrainedU(s);
110 }
111 
getQIndexOfConstrainedQ(const State & state,ConstrainedQIndex consQIndex) const112 QIndex Constraint::getQIndexOfConstrainedQ(const State&      state,
113                                            ConstrainedQIndex consQIndex) const {
114     return getImpl().getQIndexOfConstrainedQ(state,consQIndex);
115 }
116 
getUIndexOfConstrainedU(const State & state,ConstrainedUIndex consUIndex) const117 UIndex Constraint::getUIndexOfConstrainedU(const State&      state,
118                                            ConstrainedUIndex consUIndex) const {
119     return getImpl().getUIndexOfConstrainedU(state,consUIndex);
120 }
121 
getNumConstrainedQ(const State & s,ConstrainedMobilizerIndex M) const122 int Constraint::getNumConstrainedQ(const State& s, ConstrainedMobilizerIndex M) const {
123     return getImpl().getNumConstrainedQ(s,M);
124 }
getNumConstrainedU(const State & s,ConstrainedMobilizerIndex M) const125 int Constraint::getNumConstrainedU(const State& s, ConstrainedMobilizerIndex M) const {
126     return getImpl().getNumConstrainedU(s,M);
127 }
128 
getConstrainedQIndex(const State & s,ConstrainedMobilizerIndex M,MobilizerQIndex which) const129 ConstrainedQIndex Constraint::getConstrainedQIndex
130    (const State& s, ConstrainedMobilizerIndex M, MobilizerQIndex which) const {
131     return getImpl().getConstrainedQIndex(s,M,which);
132 }
getConstrainedUIndex(const State & s,ConstrainedMobilizerIndex M,MobilizerUIndex which) const133 ConstrainedUIndex Constraint::getConstrainedUIndex
134    (const State& s, ConstrainedMobilizerIndex M, MobilizerUIndex which) const {
135     return getImpl().getConstrainedUIndex(s,M,which);
136 }
137 
getMobilizedBodyFromConstrainedMobilizer(ConstrainedMobilizerIndex M) const138 const MobilizedBody& Constraint::getMobilizedBodyFromConstrainedMobilizer(ConstrainedMobilizerIndex M) const {
139     return getImpl().getMobilizedBodyFromConstrainedMobilizer(M);
140 }
141 
getMobilizedBodyFromConstrainedBody(ConstrainedBodyIndex B) const142 const MobilizedBody& Constraint::getMobilizedBodyFromConstrainedBody(ConstrainedBodyIndex B) const {
143     return getImpl().getMobilizedBodyFromConstrainedBody(B);
144 }
getAncestorMobilizedBody() const145 const MobilizedBody& Constraint::getAncestorMobilizedBody() const {
146     return getImpl().getAncestorMobilizedBody();
147 }
148 
getSubtree() const149 const SimbodyMatterSubtree& Constraint::getSubtree() const {
150     assert(getImpl().subsystemTopologyHasBeenRealized());
151     return getImpl().mySubtree;
152 }
153 
154 // Find out how many holonomic (position), nonholonomic (velocity),
155 // and acceleration-only constraint equations are generated by this Constraint.
156 void Constraint::
getNumConstraintEquationsInUse(const State & s,int & mp,int & mv,int & ma) const157 getNumConstraintEquationsInUse(const State& s, int& mp, int& mv, int& ma) const
158 {   getImpl().getNumConstraintEquationsInUse(s,mp,mv,ma); }
159 
160 void Constraint::
getIndexOfMultipliersInUse(const State & s,MultiplierIndex & px0,MultiplierIndex & vx0,MultiplierIndex & ax0) const161 getIndexOfMultipliersInUse(const State& s,
162                            MultiplierIndex& px0,
163                            MultiplierIndex& vx0,
164                            MultiplierIndex& ax0) const
165 {   getImpl().getIndexOfMultipliersInUse(s,px0,vx0,ax0); }
166 
setMyPartInConstraintSpaceVector(const State & state,const Vector & myPart,Vector & constraintSpace) const167 void Constraint::setMyPartInConstraintSpaceVector(const State& state,
168                                       const Vector& myPart,
169                                       Vector& constraintSpace) const
170 {   getImpl().setMyPartInConstraintSpaceVector(state,myPart,constraintSpace); }
171 
172 void Constraint::
getMyPartFromConstraintSpaceVector(const State & state,const Vector & constraintSpace,Vector & myPart) const173 getMyPartFromConstraintSpaceVector(const State& state,
174                                    const Vector& constraintSpace,
175                                    Vector& myPart) const
176 {   getImpl().getMyPartFromConstraintSpaceVector(state,constraintSpace,myPart);}
177 
getPositionErrorsAsVector(const State & s) const178 Vector Constraint::getPositionErrorsAsVector(const State& s) const {
179     int mp,mv,ma;
180     getNumConstraintEquationsInUse(s, mp, mv, ma);
181 
182     Vector perr(mp);
183     if (mp) getImpl().getPositionErrors(s, mp, &perr[0]);
184     return perr;
185 }
186 
getVelocityErrorsAsVector(const State & s) const187 Vector Constraint::getVelocityErrorsAsVector(const State& s) const {
188     int mp,mv,ma;
189     getNumConstraintEquationsInUse(s, mp, mv, ma);
190 
191     Vector pverr(mp+mv);
192     if (mp+mv) getImpl().getVelocityErrors(s, mp+mv, &pverr[0]);
193     return pverr;
194 }
195 
getAccelerationErrorsAsVector(const State & s) const196 Vector Constraint::getAccelerationErrorsAsVector(const State& s) const {
197     int mp,mv,ma;
198     getNumConstraintEquationsInUse(s, mp, mv, ma);
199 
200     Vector pvaerr(mp+mv+ma);
201     if (mp+mv+ma) getImpl().getAccelerationErrors(s, mp+mv+ma, &pvaerr[0]);
202     return pvaerr;
203 }
204 
getMultipliersAsVector(const State & s) const205 Vector Constraint::getMultipliersAsVector(const State& s) const {
206     int mp,mv,ma;
207     getNumConstraintEquationsInUse(s, mp, mv, ma);
208 
209     Vector mult(mp+mv+ma);
210     if (mp+mv+ma) getImpl().getMultipliers(s, mp+mv+ma, &mult[0]);
211     return mult;
212 }
213 
getConstraintForcesAsVectors(const State & state,Vector_<SpatialVec> & bodyForcesInG,Vector & mobilityForces) const214 void Constraint::getConstraintForcesAsVectors
215    (const State&         state,
216     Vector_<SpatialVec>& bodyForcesInG,
217     Vector&              mobilityForces) const
218 {
219     SimTK_ERRCHK1_ALWAYS(!isDisabled(state),
220         "Constraint::getConstraintForcesAsVector()",
221         "Constraint %d is currently disabled; you can't ask for its forces."
222         " Use isDisabled() to check.", (int)getConstraintIndex());
223 
224     ArrayViewConst_<SpatialVec,ConstrainedBodyIndex>
225         bodyF_G   = getImpl().getConstrainedBodyForcesInGFromState(state);
226     ArrayViewConst_<Real,ConstrainedUIndex>
227         mobilityF = getImpl().getConstrainedMobilityForcesFromState(state);
228 
229     const int ncb = bodyF_G.size();
230     bodyForcesInG.resize(ncb);
231     for (ConstrainedBodyIndex cbx(0); cbx < ncb; ++cbx)
232         bodyForcesInG[cbx] = bodyF_G[cbx];
233 
234     const int ncu = mobilityF.size();
235     mobilityForces.resize(ncu);
236     for (ConstrainedUIndex cux(0); cux < ncu; ++cux)
237         mobilityForces[cux] = mobilityF[cux];
238 }
239 
240 // Multiply constraint forces (negated) by velocities to get power.
241 // (Remember that constraint forces are on the LHS so have the opposite
242 // sign from applied forces.)
calcPower(const State & state) const243 Real Constraint::calcPower(const State& state) const {
244     const ConstraintImpl& impl = getImpl();
245     const SimbodyMatterSubsystemRep& matter = impl.getMyMatterSubsystemRep();
246 
247     ArrayViewConst_<SpatialVec,ConstrainedBodyIndex>
248         bodyF_G   = impl.getConstrainedBodyForcesInGFromState(state);
249     ArrayViewConst_<Real,ConstrainedUIndex>
250         mobilityF = impl.getConstrainedMobilityForcesFromState(state);
251 
252     Real power = 0;
253     for (ConstrainedBodyIndex cbx(0); cbx < bodyF_G.size(); ++cbx) {
254         const MobilizedBodyIndex mbx =
255             impl.getMobilizedBodyIndexOfConstrainedBody(cbx);
256         const SpatialVec& V_GB = matter.getBodyVelocity(state, mbx);
257         power -= ~bodyF_G[cbx] * V_GB;
258     }
259     const Vector& u = matter.getU(state);
260     for (ConstrainedUIndex cux(0); cux < mobilityF.size(); ++cux) {
261         const UIndex ux = impl.getUIndexOfConstrainedU(state, cux);
262         power -= mobilityF[cux] * u[ux];
263     }
264 
265     return power;
266 }
267 
268 
calcPositionErrorFromQ(const State &,const Vector & q) const269 Vector Constraint::calcPositionErrorFromQ(const State&, const Vector& q) const {
270     SimTK_THROW1(Exception::UnimplementedMethod, "Constraint::calcPositionErrorFromQ");
271 }
272 
calcVelocityErrorFromU(const State &,const Vector & u) const273 Vector Constraint::calcVelocityErrorFromU(const State&, const Vector& u) const {
274     SimTK_THROW1(Exception::UnimplementedMethod, "Constraint::calcVelocityErrorFromU");
275 }
276 
calcAccelerationErrorFromUDot(const State &,const Vector & udot) const277 Vector Constraint::calcAccelerationErrorFromUDot(const State&, const Vector& udot) const {
278     SimTK_THROW1(Exception::UnimplementedMethod, "Constraint::calcAccelerationErrorFromUDot");
279 }
280 
281 
282 // TODO: change to use operator form to avoid State copying kludge.
calcPositionConstraintMatrixP(const State & s) const283 Matrix Constraint::calcPositionConstraintMatrixP(const State& s) const {
284     int mp,mv,ma;
285     getNumConstraintEquationsInUse(s, mp, mv, ma);
286 
287     const SimbodyMatterSubsystem& matter = getMatterSubsystem();
288     const System&                 system = matter.getSystem();
289 
290     const int nu = matter.getNU(s);
291 
292     Matrix P(mp, nu);
293     if (mp && nu) {
294         Vector  pverr0(mp), pverr(mp); // we're interested in the first mp of these
295         State   tmp = s;      // don't change s
296 
297         matter.updU(tmp) = 0;   // first calculate the bias term -c(t,q)
298         system.realize(tmp, Stage::Velocity);
299         pverr0 = getVelocityErrorsAsVector(tmp)(0,mp);
300 
301         // Now calculate sensitivity of d(perr)/dt=Pu-c(t,q) to each u in turn.
302         for (int j=0; j<nu; ++j) {
303             matter.updU(tmp)[j] = 1;
304             system.realize(tmp, Stage::Velocity);
305             pverr = getVelocityErrorsAsVector(tmp)(0,mp);
306             matter.updU(tmp)[j] = 0;
307             P(j) = pverr - pverr0;
308         }
309     }
310     return P;
311 }
312 
calcPositionConstraintMatrixPt(const State & s) const313 Matrix Constraint::calcPositionConstraintMatrixPt(const State& s) const {
314     int mp,mv,ma;
315     getNumConstraintEquationsInUse(s, mp, mv, ma);
316 
317     const SimbodyMatterSubsystem& matter = getMatterSubsystem();
318     const System&                 system = matter.getSystem();
319 
320     const int nu = matter.getNU(s);
321     const int nb = matter.getNumBodies();
322 
323 
324     Matrix Pt(nu, mp);
325     if (mp==0 || nu==0)
326         return Pt;
327 
328     const ConstraintImpl& rep = getImpl();
329     const int ncb = rep.getNumConstrainedBodies();
330     const int ncq = rep.getNumConstrainedQ(s);
331     const int ncu = rep.getNumConstrainedU(s);
332 
333     // Any of these may be zero length.
334     Array_<SpatialVec,ConstrainedBodyIndex> bodyForcesInA(ncb);
335     Array_<Real,ConstrainedQIndex>          qForces(ncq);
336     Array_<Real,ConstrainedUIndex>          mobilityForces(ncu);
337 
338     Array_<Real> lambdap(mp, Real(0));
339 
340     if (ncb == 0) {
341         // Mobility forces only
342         for (int i=0; i<mp; ++i) {
343             lambdap[i] = 1;
344             qForces.fill(Real(0));
345             rep.addInPositionConstraintForces(s, lambdap,
346                                               bodyForcesInA, qForces);
347             rep.convertQForcesToUForces(s, qForces, mobilityForces);  // fu = ~N fq
348             lambdap[i] = 0;
349             Pt(i) = 0;
350             for (ConstrainedUIndex cux(0); cux < ncu; ++cux)
351                 Pt(rep.getUIndexOfConstrainedU(s, cux), i) = mobilityForces[cux]; // unpack
352         }
353     } else {
354         // There are some body forces
355         Vector_<SpatialVec> bodyForcesInG(nb);
356         bodyForcesInG.setToZero();
357 
358         // For converting those A-relative forces to G
359         const Rotation& R_GA = rep.getAncestorMobilizedBody().getBodyRotation(s);
360 
361         // Calculate Pt*lambda with each lambda set to 1 in turn.
362         for (int i=0; i<mp; ++i) {
363             lambdap[i] = 1;
364             bodyForcesInA.fill(SpatialVec(Vec3(0), Vec3(0)));
365             qForces.fill(Real(0));
366             rep.addInPositionConstraintForces(s, lambdap,
367                                               bodyForcesInA, qForces);
368             rep.convertQForcesToUForces(s, qForces, mobilityForces);  // fu = ~N fq
369             for (ConstrainedBodyIndex cb(0); cb < ncb; ++cb) {
370                 bodyForcesInG[rep.getMobilizedBodyIndexOfConstrainedBody(cb)] =
371                     R_GA*bodyForcesInA[cb];
372             }
373             lambdap[i] = 0;
374 
375             rep.getMyMatterSubsystem().multiplyBySystemJacobianTranspose
376                                                     (s,bodyForcesInG,Pt(i));
377             for (ConstrainedUIndex cux(0); cux < ncu; ++cux)
378                 Pt(rep.getUIndexOfConstrainedU(s, cux), i) += mobilityForces[cux]; // unpack
379         }
380     }
381     return Pt;
382 }
383 
384 // Calculate the constraint matrix V= partial(verr)/partial(u) for just
385 // the nonholonomic constraints. For this we use the acceleration error
386 // equations obtained from differentiation of the nonholonomic constraints:
387 //    verr_dot = V udot - b(t,q,u)
388 //
calcVelocityConstraintMatrixV(const State & s) const389 Matrix Constraint::calcVelocityConstraintMatrixV(const State& s) const {
390     int mp,mv,ma;
391     getNumConstraintEquationsInUse(s, mp, mv, ma);
392 
393     const SimbodyMatterSubsystem& matter = getMatterSubsystem();
394     const System&                 system = matter.getSystem();
395 
396     const int nu = matter.getNU(s); // same as nudot
397 
398     Matrix V(mv, nu);
399     if (mv && nu) {
400         Vector  vaerr0(mv), vaerr(mv); // we're interested in the middle mv of these (mp-mv-ma)
401 
402         Vector udot(nu);
403         udot = 0;
404 
405         // Calculate the bias term -b(t,q,u)
406         vaerr0 = calcAccelerationErrorFromUDot(s, udot)(mp, mv);
407 
408         // Now calculate sensitivity of d(verr)/dt=Vudot-b(t,q,u) to each udot in turn.
409         for (int j=0; j<nu; ++j) {
410             udot[j] = 1;
411             vaerr = calcAccelerationErrorFromUDot(s, udot)(mp, mv);
412             udot[j] = 0;
413             V(j) = vaerr - vaerr0;
414         }
415     }
416     return V;
417 }
418 
calcVelocityConstraintMatrixVt(const State & s) const419 Matrix Constraint::calcVelocityConstraintMatrixVt(const State& s) const {
420     int mp,mv,ma;
421     getNumConstraintEquationsInUse(s, mp, mv, ma);
422 
423     const SimbodyMatterSubsystem& matter = getMatterSubsystem();
424     const System&                 system = matter.getSystem();
425 
426     const int nu = matter.getNU(s);
427     const int nb = matter.getNumBodies();
428 
429 
430     Matrix Vt(nu, mv);
431     if (mv==0 || nu==0)
432         return Vt;
433 
434     const ConstraintImpl& rep = getImpl();
435     const int ncb = rep.getNumConstrainedBodies();
436     const int ncu = rep.getNumConstrainedU(s);
437 
438         // Either of these may be zero length.
439     Array_<SpatialVec,ConstrainedBodyIndex> bodyForcesInA(ncb);
440     Array_<Real,ConstrainedUIndex>          mobilityForces(ncu);
441     Array_<Real> lambdav(mv, Real(0));
442 
443     if (ncb == 0) {
444         // Mobility forces only
445         for (int i=0; i<mv; ++i) {
446             lambdav[i] = 1;
447             mobilityForces.fill(Real(0));
448             rep.addInVelocityConstraintForces(s, lambdav,
449                                               bodyForcesInA, mobilityForces);
450             lambdav[i] = 0;
451             Vt(i) = 0; // set column i to zero
452             for (ConstrainedUIndex cux(0); cux < ncu; ++cux)
453                 Vt(rep.getUIndexOfConstrainedU(s, cux), i) =
454                     mobilityForces[cux]; // unpack
455         }
456     } else {
457         // There are some body forces
458         Vector_<SpatialVec> bodyForcesInG(nb);
459         bodyForcesInG = SpatialVec(Vec3(0), Vec3(0));
460 
461         // For converting those A-relative forces to G
462         const Rotation& R_GA =
463             rep.getAncestorMobilizedBody().getBodyRotation(s);
464 
465         // Calculate Vt*lambda with each lambda set to 1 in turn.
466         for (int i=0; i<mv; ++i) {
467             lambdav[i] = 1;
468             bodyForcesInA.fill(SpatialVec(Vec3(0), Vec3(0)));
469             mobilityForces.fill(Real(0));
470             rep.addInVelocityConstraintForces(s, lambdav,
471                                               bodyForcesInA, mobilityForces);
472             for (ConstrainedBodyIndex cb(0); cb < ncb; ++cb) {
473                 bodyForcesInG[rep.getMobilizedBodyIndexOfConstrainedBody(cb)] =
474                     R_GA*bodyForcesInA[cb];
475             }
476             lambdav[i] = 0;
477 
478             // Convert body forces F to generalized forces f=~J*F.
479             // TODO: this should result in generalized forces only on the
480             // participating mobilities - does it?
481             rep.getMyMatterSubsystem().multiplyBySystemJacobianTranspose
482                                                         (s,bodyForcesInG,Vt(i));
483 
484             // Now add in the generalized forces produced directly by the
485             // Constraint, unpacking into the right global mobility slot.
486             for (ConstrainedUIndex cux(0); cux < ncu; ++cux)
487                 Vt(rep.getUIndexOfConstrainedU(s,cux), i)
488                                                         += mobilityForces[cux];
489         }
490     }
491 
492     return Vt;
493 }
494 
495 // Calculate the constraint matrix A= partial(aerr)/partial(udot) for just
496 // the acceleration-only constraints. For this we use the acceleration error
497 // equations directly because we're *requiring* that acceleration-only constraints
498 // are linear in the udots.
499 //    aerr = A udot - b(t,q,u)
500 //
calcAccelerationConstraintMatrixA(const State & s) const501 Matrix Constraint::calcAccelerationConstraintMatrixA(const State& s) const {
502     int mp,mv,ma;
503     getNumConstraintEquationsInUse(s, mp, mv, ma);
504 
505     const SimbodyMatterSubsystem& matter = getMatterSubsystem();
506     const System&                 system = matter.getSystem();
507 
508     const int nudot = matter.getNU(s); // same as nudot
509 
510     Matrix A(ma, nudot);
511     if (ma && nudot) {
512         Vector  aerr0(ma), aerr(ma); // we're interested in the final ma of these (mp-mv-ma)
513 
514         Vector udot(nudot);
515         udot = 0;
516 
517         // Calculate the bias term -b(t,q,u)
518         aerr0 = calcAccelerationErrorFromUDot(s, udot)(mp+mv, ma);
519 
520         // Now calculate sensitivity of d(aerr)/dt=Audot-b(t,q,u) to each udot in turn.
521         for (int j=0; j<nudot; ++j) {
522             udot[j] = 1;
523             aerr = calcAccelerationErrorFromUDot(s, udot)(mp+mv, ma);
524             udot[j] = 0;
525             A(j) = aerr - aerr0;
526         }
527     }
528     return A;
529 }
530 
calcAccelerationConstraintMatrixAt(const State & s) const531 Matrix Constraint::calcAccelerationConstraintMatrixAt(const State& s) const {
532     int mp,mv,ma;
533     getNumConstraintEquationsInUse(s, mp, mv, ma);
534 
535     const SimbodyMatterSubsystem& matter = getMatterSubsystem();
536     const System&                 system = matter.getSystem();
537 
538     const int nu = matter.getNU(s);
539     const int nb = matter.getNumBodies();
540 
541 
542     Matrix At(nu, ma);
543     if (ma==0 || nu==0)
544         return At;
545 
546     const ConstraintImpl& rep = getImpl();
547     const int ncb = rep.getNumConstrainedBodies();
548     const int ncu = rep.getNumConstrainedU(s);
549 
550         // Either of these may be zero length.
551     Array_<SpatialVec,ConstrainedBodyIndex> bodyForcesInA(ncb);
552     Array_<Real,ConstrainedUIndex>          mobilityForces(ncu);
553     Array_<Real> lambdaa(ma, Real(0));
554 
555     if (ncb == 0) {
556         // Mobility forces only
557         for (int i=0; i<ma; ++i) {
558             lambdaa[i] = 1;
559             mobilityForces.fill(Real(0));
560             rep.addInAccelerationConstraintForces(s, lambdaa,
561                                         bodyForcesInA, mobilityForces);
562             lambdaa[i] = 0;
563             At(i) = 0;
564             for (ConstrainedUIndex cux(0); cux < ncu; ++cux)
565                 At(rep.getUIndexOfConstrainedU(s, cux), i) = mobilityForces[cux]; // unpack
566         }
567     } else {
568         // There are some body forces
569         Vector_<SpatialVec> bodyForcesInG(nb);
570         bodyForcesInG = SpatialVec(Vec3(0), Vec3(0));
571 
572         // For converting those A-relative forces to G
573         const Rotation& R_GA = rep.getAncestorMobilizedBody().getBodyRotation(s);
574 
575         // Calculate At*lambda with each lambda set to 1 in turn.
576         for (int i=0; i<ma; ++i) {
577             lambdaa[i] = 1;
578             bodyForcesInA.fill(SpatialVec(Vec3(0), Vec3(0)));
579             mobilityForces.fill(Real(0));
580             rep.addInAccelerationConstraintForces(s, lambdaa,
581                                         bodyForcesInA, mobilityForces);
582             for (ConstrainedBodyIndex cb(0); cb < ncb; ++cb) {
583                 bodyForcesInG[rep.getMobilizedBodyIndexOfConstrainedBody(cb)] =
584                     R_GA*bodyForcesInA[cb];
585             }
586             lambdaa[i] = 0;
587 
588             rep.getMyMatterSubsystem().multiplyBySystemJacobianTranspose
589                                                         (s,bodyForcesInG,At(i));
590             for (ConstrainedUIndex cux(0); cux < ncu; ++cux)
591                 At(rep.getUIndexOfConstrainedU(s, cux), i) += mobilityForces[cux]; // unpack
592         }
593     }
594     return At;
595 }
596 
calcPositionConstraintMatrixPNInv(const State & s) const597 Matrix Constraint::calcPositionConstraintMatrixPNInv(const State& s) const {
598     int mp,mv,ma;
599     getNumConstraintEquationsInUse(s, mp, mv, ma);
600 
601     const SimbodyMatterSubsystem& matter = getMatterSubsystem();
602     const System&                 system = matter.getSystem();
603 
604     const int nu = matter.getNU(s);
605     const int nq = matter.getNQ(s);
606 
607     const Matrix P = calcPositionConstraintMatrixP(s);
608     assert(P.nrow()==mp && P.ncol()==nu);
609 
610     Matrix PNInv(mp, nq); // = P*N^-1
611     if (mp && nq) {
612         // The routine below calculates qlikeRow = ulikeRow * N^-1 which is what
613         // we need but it actually works with Vectors (transpose of RowVectors)
614         // and at the moment they have to be contiguous so we'll have to copy.
615         Vector uin(nu);
616         Vector qout(nq);
617         for (int i=0; i < mp; ++i) {
618             uin = ~P[i];
619             matter.multiplyByNInv(s, true, uin, qout);
620             PNInv[i] = ~qout;
621         }
622     }
623     return PNInv;
624 }
625 
calcConstraintForcesFromMultipliers(const State & s,const Vector & lambda,Vector_<SpatialVec> & bodyForcesInA,Vector & mobilityForces) const626 void Constraint::calcConstraintForcesFromMultipliers(
627     const State&         s,
628     const Vector&        lambda,
629     Vector_<SpatialVec>& bodyForcesInA,
630     Vector&              mobilityForces) const
631 {
632     int mp, mv, ma;
633     getNumConstraintEquationsInUse(s, mp, mv, ma);
634 
635     SimTK_APIARGCHECK2_ALWAYS(lambda.size() == mp+mv+ma,
636         "Constraint", "calcConstraintForcesFromMultipliers()",
637         "Expected %d multipliers but got %d.", mp+mv+ma, lambda.size());
638 
639     // We have to convert to and from Arrays since the underlying
640     // constraint methods use those for speed.
641 
642     Array_<Real> lambdap(mp), lambdav(mv), lambdaa(ma);
643     for (int i=0; i<mp; ++i) lambdap[i] = lambda[i];
644     for (int i=0; i<mv; ++i) lambdav[i] = lambda[mp+i];
645     for (int i=0; i<ma; ++i) lambdaa[i] = lambda[mp+mv+i];
646 
647     Array_<SpatialVec,ConstrainedBodyIndex> tmpBodyForcesInA;
648     Array_<Real,      ConstrainedUIndex>    tmpMobilityForces;
649 
650     getImpl().calcConstraintForcesFromMultipliers
651        (s,lambdap,lambdav,lambdaa,tmpBodyForcesInA,tmpMobilityForces);
652 
653     const int ncb = tmpBodyForcesInA.size();  // # constrained bodies
654     const int ncu = tmpMobilityForces.size(); // # constrained u's
655 
656     bodyForcesInA.resize(ncb); mobilityForces.resize(ncu);
657 
658     for (ConstrainedBodyIndex i(0); i<ncb; ++i)
659         bodyForcesInA[i] = tmpBodyForcesInA[i];
660 
661     for (ConstrainedUIndex i(0); i<ncu; ++i)
662         mobilityForces[i] = tmpMobilityForces[i];
663 }
664 
665 
666 
667 //==============================================================================
668 //                         CONSTRAINT::POINT IN PLANE
669 //==============================================================================
670 SimTK_INSERT_DERIVED_HANDLE_DEFINITIONS(Constraint::PointInPlane, Constraint::PointInPlaneImpl, Constraint);
671 
PointInPlane(MobilizedBody & planeBody,const UnitVec3 & defPlaneNormal,Real defPlaneHeight,MobilizedBody & followerBody,const Vec3 & defFollowerPoint)672 Constraint::PointInPlane::PointInPlane
673    (MobilizedBody& planeBody,    const UnitVec3& defPlaneNormal, Real defPlaneHeight,
674     MobilizedBody& followerBody, const Vec3&     defFollowerPoint)
675   : Constraint(new PointInPlaneImpl())
676 {
677     SimTK_ASSERT_ALWAYS(planeBody.isInSubsystem() && followerBody.isInSubsystem(),
678         "Constraint::PointInPlane(): both bodies must already be in a SimbodyMatterSubsystem.");
679     SimTK_ASSERT_ALWAYS(planeBody.isInSameSubsystem(followerBody),
680         "Constraint::PointInPlane(): both bodies to be connected must be in the same SimbodyMatterSubsystem.");
681 
682     //rep = new PointInPlaneRep(); rep->setMyHandle(*this);
683     planeBody.updMatterSubsystem().adoptConstraint(*this);
684 
685     updImpl().planeBody    = updImpl().addConstrainedBody(planeBody);
686     updImpl().followerBody = updImpl().addConstrainedBody(followerBody);
687     updImpl().defaultPlaneNormal   = defPlaneNormal;
688     updImpl().defaultPlaneHeight   = defPlaneHeight;
689     updImpl().defaultFollowerPoint = defFollowerPoint;
690 }
691 
setDefaultPlaneNormal(const UnitVec3 & n)692 Constraint::PointInPlane& Constraint::PointInPlane::setDefaultPlaneNormal(const UnitVec3& n) {
693     getImpl().invalidateTopologyCache();
694     updImpl().defaultPlaneNormal = n;
695     return *this;
696 }
697 
setDefaultPlaneHeight(Real h)698 Constraint::PointInPlane& Constraint::PointInPlane::setDefaultPlaneHeight(Real h) {
699     getImpl().invalidateTopologyCache();
700     updImpl().defaultPlaneHeight = h;
701     return *this;
702 }
703 
setDefaultFollowerPoint(const Vec3 & p)704 Constraint::PointInPlane& Constraint::PointInPlane::setDefaultFollowerPoint(const Vec3& p) {
705     getImpl().invalidateTopologyCache();
706     updImpl().defaultFollowerPoint = p;
707     return *this;
708 }
709 
getPlaneMobilizedBodyIndex() const710 MobilizedBodyIndex Constraint::PointInPlane::getPlaneMobilizedBodyIndex() const {
711     return getImpl().getMobilizedBodyIndexOfConstrainedBody(getImpl().planeBody);
712 }
getFollowerMobilizedBodyIndex() const713 MobilizedBodyIndex Constraint::PointInPlane::getFollowerMobilizedBodyIndex() const {
714     return getImpl().getMobilizedBodyIndexOfConstrainedBody(getImpl().followerBody);
715 }
getDefaultPlaneNormal() const716 const UnitVec3& Constraint::PointInPlane::getDefaultPlaneNormal() const {
717     return getImpl().defaultPlaneNormal;
718 }
getDefaultPlaneHeight() const719 Real Constraint::PointInPlane::getDefaultPlaneHeight() const {
720     return getImpl().defaultPlaneHeight;
721 }
getDefaultFollowerPoint() const722 const Vec3& Constraint::PointInPlane::getDefaultFollowerPoint() const {
723     return getImpl().defaultFollowerPoint;
724 }
725 
setPlaneDisplayHalfWidth(Real h)726 Constraint::PointInPlane& Constraint::PointInPlane::setPlaneDisplayHalfWidth(Real h) {
727     updImpl().setPlaneDisplayHalfWidth(h);
728     return *this;
729 }
setPointDisplayRadius(Real r)730 Constraint::PointInPlane& Constraint::PointInPlane::setPointDisplayRadius(Real r) {
731     updImpl().setPointDisplayRadius(r);
732     return *this;
733 }
734 
getPlaneDisplayHalfWidth() const735 Real Constraint::PointInPlane::getPlaneDisplayHalfWidth() const {
736     return getImpl().getPlaneDisplayHalfWidth();
737 }
738 
getPointDisplayRadius() const739 Real Constraint::PointInPlane::getPointDisplayRadius() const {
740     return getImpl().getPointDisplayRadius();
741 }
742 
getPositionError(const State & s) const743 Real Constraint::PointInPlane::getPositionError(const State& s) const {
744     Real perr;
745     getImpl().getPositionErrors(s, 1, &perr);
746     return perr;
747 }
748 
getVelocityError(const State & s) const749 Real Constraint::PointInPlane::getVelocityError(const State& s) const {
750     Real pverr;
751     getImpl().getVelocityErrors(s, 1, &pverr);
752     return pverr;
753 }
754 
getAccelerationError(const State & s) const755 Real Constraint::PointInPlane::getAccelerationError(const State& s) const {
756     Real pvaerr;
757     getImpl().getAccelerationErrors(s, 1, &pvaerr);
758     return pvaerr;
759 }
760 
getMultiplier(const State & s) const761 Real Constraint::PointInPlane::getMultiplier(const State& s) const {
762     Real mult;
763     getImpl().getMultipliers(s, 1, &mult);
764     return mult;
765 }
766 
767     // PointInPlaneImpl
768 
calcDecorativeGeometryAndAppendVirtual(const State & s,Stage stage,Array_<DecorativeGeometry> & geom) const769 void Constraint::PointInPlane::PointInPlaneImpl::calcDecorativeGeometryAndAppendVirtual
770    (const State& s, Stage stage, Array_<DecorativeGeometry>& geom) const
771 {
772     // We can't generate the artwork until we know the normal, height, and follower
773     // point location, which might not be until Instance stage.
774     if (stage == Stage::Instance && getMyMatterSubsystemRep().getShowDefaultGeometry()) {
775         const SimbodyMatterSubsystemRep& matterRep = getMyMatterSubsystemRep();
776         // TODO: should be instance-stage data from State rather than topological data
777         // This makes z axis point along plane normal
778         const Transform X_B1(Rotation(defaultPlaneNormal,ZAxis), defaultPlaneHeight*defaultPlaneNormal);
779         const Transform X_B2(Rotation(), defaultFollowerPoint);
780 
781         const MobilizedBodyIndex planeMBId = getMobilizedBodyIndexOfConstrainedBody(planeBody);
782         const MobilizedBodyIndex followerMBId = getMobilizedBodyIndexOfConstrainedBody(followerBody);
783 
784         if (planeHalfWidth > 0 && pointRadius > 0) {
785             // On the inboard body, draw a gray transparent rectangle, outlined in black lines.
786             geom.push_back(DecorativeBrick(Vec3(planeHalfWidth,planeHalfWidth,pointRadius/2))
787                                                 .setColor(Gray)
788                                                 .setRepresentation(DecorativeGeometry::DrawSurface)
789                                                 .setOpacity(Real(0.3))
790                                                 .setBodyId(planeMBId)
791                                                 .setTransform(X_B1));
792             geom.push_back(DecorativeBrick(Vec3(planeHalfWidth,planeHalfWidth,pointRadius/2))
793                                                 .setColor(Black)
794                                                 .setRepresentation(DecorativeGeometry::DrawWireframe)
795                                                 .setBodyId(planeMBId)
796                                                 .setTransform(X_B1));
797 
798             // On the follower body draw an orange mesh sphere at the point radius.
799             geom.push_back(DecorativeSphere(pointRadius)
800                                                 .setColor(Orange)
801                                                 .setRepresentation(DecorativeGeometry::DrawWireframe)
802                                                 .setResolution(Real(0.5))
803                                                 .setBodyId(followerMBId)
804                                                 .setTransform(X_B2));
805         }
806     }
807 }
808 
809 
810 
811 //==============================================================================
812 //                        CONSTRAINT::POINT ON LINE
813 //==============================================================================
814 SimTK_INSERT_DERIVED_HANDLE_DEFINITIONS(Constraint::PointOnLine, Constraint::PointOnLineImpl, Constraint);
815 
PointOnLine(MobilizedBody & lineBody,const UnitVec3 & defLineDirection,const Vec3 & defPointOnLine,MobilizedBody & followerBody,const Vec3 & defFollowerPoint)816 Constraint::PointOnLine::PointOnLine
817    (MobilizedBody& lineBody,     const UnitVec3& defLineDirection, const Vec3& defPointOnLine,
818     MobilizedBody& followerBody, const Vec3&     defFollowerPoint)
819   : Constraint(new PointOnLineImpl())
820 {
821     SimTK_ASSERT_ALWAYS(lineBody.isInSubsystem() && followerBody.isInSubsystem(),
822         "Constraint::PointOnLine(): both bodies must already be in a SimbodyMatterSubsystem.");
823     SimTK_ASSERT_ALWAYS(lineBody.isInSameSubsystem(followerBody),
824         "Constraint::PointOnLine(): both bodies to be connected must be in the same SimbodyMatterSubsystem.");
825 
826     //rep = new PointOnLineRep(); rep->setMyHandle(*this);
827     lineBody.updMatterSubsystem().adoptConstraint(*this);
828 
829     updImpl().lineBody     = updImpl().addConstrainedBody(lineBody);
830     updImpl().followerBody = updImpl().addConstrainedBody(followerBody);
831     updImpl().defaultLineDirection = defLineDirection;
832     updImpl().defaultPointOnLine   = defPointOnLine;
833     updImpl().defaultFollowerPoint = defFollowerPoint;
834 }
835 
setDefaultLineDirection(const UnitVec3 & z)836 Constraint::PointOnLine& Constraint::PointOnLine::setDefaultLineDirection(const UnitVec3& z) {
837     getImpl().invalidateTopologyCache();
838     updImpl().defaultLineDirection = z;
839     return *this;
840 }
841 
setDefaultPointOnLine(const Vec3 & P)842 Constraint::PointOnLine& Constraint::PointOnLine::setDefaultPointOnLine(const Vec3& P) {
843     getImpl().invalidateTopologyCache();
844     updImpl().defaultPointOnLine = P;
845     return *this;
846 }
847 
setDefaultFollowerPoint(const Vec3 & S)848 Constraint::PointOnLine& Constraint::PointOnLine::setDefaultFollowerPoint(const Vec3& S) {
849     getImpl().invalidateTopologyCache();
850     updImpl().defaultFollowerPoint = S;
851     return *this;
852 }
853 
getLineMobilizedBodyIndex() const854 MobilizedBodyIndex Constraint::PointOnLine::getLineMobilizedBodyIndex() const {
855     return getImpl().getMobilizedBodyIndexOfConstrainedBody(getImpl().lineBody);
856 }
getFollowerMobilizedBodyIndex() const857 MobilizedBodyIndex Constraint::PointOnLine::getFollowerMobilizedBodyIndex() const {
858     return getImpl().getMobilizedBodyIndexOfConstrainedBody(getImpl().followerBody);
859 }
getDefaultLineDirection() const860 const UnitVec3& Constraint::PointOnLine::getDefaultLineDirection() const {
861     return getImpl().defaultLineDirection;
862 }
getDefaultPointOnLine() const863 const Vec3& Constraint::PointOnLine::getDefaultPointOnLine() const {
864     return getImpl().defaultPointOnLine;
865 }
getDefaultFollowerPoint() const866 const Vec3& Constraint::PointOnLine::getDefaultFollowerPoint() const {
867     return getImpl().defaultFollowerPoint;
868 }
869 
setLineDisplayHalfLength(Real h)870 Constraint::PointOnLine& Constraint::PointOnLine::setLineDisplayHalfLength(Real h) {
871     updImpl().setLineDisplayHalfLength(h);
872     return *this;
873 }
setPointDisplayRadius(Real r)874 Constraint::PointOnLine& Constraint::PointOnLine::setPointDisplayRadius(Real r) {
875     updImpl().setPointDisplayRadius(r);
876     return *this;
877 }
878 
getLineDisplayHalfLength() const879 Real Constraint::PointOnLine::getLineDisplayHalfLength() const {
880     return getImpl().getLineDisplayHalfLength();
881 }
882 
getPointDisplayRadius() const883 Real Constraint::PointOnLine::getPointDisplayRadius() const {
884     return getImpl().getPointDisplayRadius();
885 }
886 
getPositionErrors(const State & s) const887 Vec2 Constraint::PointOnLine::getPositionErrors(const State& s) const {
888     Vec2 perr;
889     getImpl().getPositionErrors(s, 2, &perr[0]);
890     return perr;
891 }
892 
getVelocityErrors(const State & s) const893 Vec2 Constraint::PointOnLine::getVelocityErrors(const State& s) const {
894     Vec2 pverr;
895     getImpl().getVelocityErrors(s, 2, &pverr[0]);
896     return pverr;
897 }
898 
getAccelerationErrors(const State & s) const899 Vec2 Constraint::PointOnLine::getAccelerationErrors(const State& s) const {
900     Vec2 pvaerr;
901     getImpl().getAccelerationErrors(s, 2, &pvaerr[0]);
902     return pvaerr;
903 }
904 
getMultipliers(const State & s) const905 Vec2 Constraint::PointOnLine::getMultipliers(const State& s) const {
906     Vec2 mult;
907     getImpl().getMultipliers(s, 2, &mult[0]);
908     return mult;
909 }
910 
911 
912     // PointOnLineImpl
913 
calcDecorativeGeometryAndAppendVirtual(const State & s,Stage stage,Array_<DecorativeGeometry> & geom) const914 void Constraint::PointOnLine::PointOnLineImpl::calcDecorativeGeometryAndAppendVirtual
915    (const State& s, Stage stage, Array_<DecorativeGeometry>& geom) const
916 {
917     // We can't generate the artwork until we know the direction, point on line, and follower
918     // point location, which might not be until Instance stage.
919     if (stage == Stage::Instance && getMyMatterSubsystemRep().getShowDefaultGeometry()) {
920         const SimbodyMatterSubsystemRep& matterRep = getMyMatterSubsystemRep();
921         // TODO: should be instance-stage data from State rather than topological data
922         // This makes z axis point along line
923         const Transform X_B1(Rotation(defaultLineDirection,ZAxis), defaultPointOnLine);
924         const Transform X_B2(Rotation(), defaultFollowerPoint);
925 
926         const MobilizedBodyIndex lineMBId = getMobilizedBodyIndexOfConstrainedBody(lineBody);
927         const MobilizedBodyIndex followerMBId = getMobilizedBodyIndexOfConstrainedBody(followerBody);
928 
929         if (lineHalfLength > 0 && pointRadius > 0) {
930             // On the line body, draw a black line centered at the point-on-line.
931             geom.push_back(DecorativeLine(Vec3(0,0,-lineHalfLength), Vec3(0,0,lineHalfLength))
932                                                 .setColor(Black)
933                                                 .setLineThickness(3)
934                                                 .setBodyId(lineMBId)
935                                                 .setTransform(X_B1));
936 
937             // On the line body draw a blue mesh sphere at the line center.
938             geom.push_back(DecorativeSphere(pointRadius)
939                                                 .setColor(Blue)
940                                                 .setRepresentation(DecorativeGeometry::DrawWireframe)
941                                                 .setResolution(0.5)
942                                                 .setBodyId(lineMBId)
943                                                 .setTransform(X_B1));
944 
945             // On the follower body draw an orange mesh sphere at the point radius.
946             geom.push_back(DecorativeSphere(pointRadius)
947                                                 .setColor(Orange)
948                                                 .setRepresentation(DecorativeGeometry::DrawWireframe)
949                                                 .setResolution(0.5)
950                                                 .setBodyId(followerMBId)
951                                                 .setTransform(X_B2));
952         }
953     }
954 }
955 
956 
957 
958 //==============================================================================
959 //                         CONSTRAINT::CONSTANT ANGLE
960 //==============================================================================
961 SimTK_INSERT_DERIVED_HANDLE_DEFINITIONS(Constraint::ConstantAngle, Constraint::ConstantAngleImpl, Constraint);
962 
ConstantAngle(MobilizedBody & baseBody,const UnitVec3 & defaultAxisOnB,MobilizedBody & followerBody,const UnitVec3 & defaultAxisOnF,Real angle)963 Constraint::ConstantAngle::ConstantAngle
964    (MobilizedBody& baseBody,     const UnitVec3& defaultAxisOnB,
965     MobilizedBody& followerBody, const UnitVec3& defaultAxisOnF,
966     Real angle)
967   : Constraint(new ConstantAngleImpl())
968 {
969     SimTK_ASSERT_ALWAYS(baseBody.isInSubsystem() && followerBody.isInSubsystem(),
970         "Constraint::ConstantAngle(): both bodies must already be in a SimbodyMatterSubsystem.");
971     SimTK_ASSERT_ALWAYS(baseBody.isInSameSubsystem(followerBody),
972         "Constraint::ConstantAngle(): both bodies to be connected must be in the same SimbodyMatterSubsystem.");
973 
974     //rep = new ConstantAngleRep(); rep->setMyHandle(*this);
975     baseBody.updMatterSubsystem().adoptConstraint(*this);
976 
977     updImpl().B = updImpl().addConstrainedBody(baseBody);
978     updImpl().F = updImpl().addConstrainedBody(followerBody);
979     updImpl().defaultAxisB = defaultAxisOnB;
980     updImpl().defaultAxisF = defaultAxisOnF;
981     updImpl().defaultAngle = angle;
982 }
983 
setDefaultBaseAxis(const UnitVec3 & a)984 Constraint::ConstantAngle& Constraint::ConstantAngle::setDefaultBaseAxis(const UnitVec3& a) {
985     getImpl().invalidateTopologyCache();
986     updImpl().defaultAxisB = a;
987     return *this;
988 }
989 
setDefaultFollowerAxis(const UnitVec3 & a)990 Constraint::ConstantAngle& Constraint::ConstantAngle::setDefaultFollowerAxis(const UnitVec3& a) {
991     getImpl().invalidateTopologyCache();
992     updImpl().defaultAxisF = a;
993     return *this;
994 }
995 
setDefaultAngle(Real t)996 Constraint::ConstantAngle& Constraint::ConstantAngle::setDefaultAngle(Real t) {
997     getImpl().invalidateTopologyCache();
998     updImpl().defaultAngle = t;
999     return *this;
1000 }
1001 
getBaseMobilizedBodyIndex() const1002 MobilizedBodyIndex Constraint::ConstantAngle::getBaseMobilizedBodyIndex() const {
1003     return getImpl().getMobilizedBodyIndexOfConstrainedBody(getImpl().B);
1004 }
getFollowerMobilizedBodyIndex() const1005 MobilizedBodyIndex Constraint::ConstantAngle::getFollowerMobilizedBodyIndex() const {
1006     return getImpl().getMobilizedBodyIndexOfConstrainedBody(getImpl().F);
1007 }
getDefaultBaseAxis() const1008 const UnitVec3& Constraint::ConstantAngle::getDefaultBaseAxis() const {
1009     return getImpl().defaultAxisB;
1010 }
getDefaultFollowerAxis() const1011 const UnitVec3& Constraint::ConstantAngle::getDefaultFollowerAxis() const {
1012     return getImpl().defaultAxisF;
1013 }
getDefaultAngle() const1014 Real Constraint::ConstantAngle::getDefaultAngle() const {
1015     return getImpl().defaultAngle;
1016 }
1017 
setAxisDisplayLength(Real l)1018 Constraint::ConstantAngle& Constraint::ConstantAngle::setAxisDisplayLength(Real l) {
1019     updImpl().axisLength = l;
1020     return *this;
1021 }
setAxisDisplayWidth(Real w)1022 Constraint::ConstantAngle& Constraint::ConstantAngle::setAxisDisplayWidth(Real w) {
1023     updImpl().axisThickness = w;
1024     return *this;
1025 }
1026 
getAxisDisplayLength() const1027 Real Constraint::ConstantAngle::getAxisDisplayLength() const {
1028     return getImpl().axisLength;
1029 }
1030 
getAxisDisplayWidth() const1031 Real Constraint::ConstantAngle::getAxisDisplayWidth() const {
1032     return getImpl().axisThickness;
1033 }
1034 
getPositionError(const State & s) const1035 Real Constraint::ConstantAngle::getPositionError(const State& s) const {
1036     Real perr;
1037     getImpl().getPositionErrors(s, 1, &perr);
1038     return perr;
1039 }
1040 
getVelocityError(const State & s) const1041 Real Constraint::ConstantAngle::getVelocityError(const State& s) const {
1042     Real pverr;
1043     getImpl().getVelocityErrors(s, 1, &pverr);
1044     return pverr;
1045 }
1046 
getAccelerationError(const State & s) const1047 Real Constraint::ConstantAngle::getAccelerationError(const State& s) const {
1048     Real pvaerr;
1049     getImpl().getAccelerationErrors(s, 1, &pvaerr);
1050     return pvaerr;
1051 }
1052 
getMultiplier(const State & s) const1053 Real Constraint::ConstantAngle::getMultiplier(const State& s) const {
1054     Real mult;
1055     getImpl().getMultipliers(s, 1, &mult);
1056     return mult;
1057 }
1058 
1059     // ConstantAngleImpl
1060 
calcDecorativeGeometryAndAppendVirtual(const State & s,Stage stage,Array_<DecorativeGeometry> & geom) const1061 void Constraint::ConstantAngle::ConstantAngleImpl::calcDecorativeGeometryAndAppendVirtual
1062    (const State& s, Stage stage, Array_<DecorativeGeometry>& geom) const
1063 {
1064     // We can't generate the artwork until we know the normal, height, and follower
1065     // point location, which might not be until Instance stage.
1066     if (stage == Stage::Instance && getMyMatterSubsystemRep().getShowDefaultGeometry()) {
1067         const SimbodyMatterSubsystemRep& matterRep = getMyMatterSubsystemRep();
1068         //TODO
1069     }
1070 }
1071 
1072 
1073 
1074 //==============================================================================
1075 //                              CONSTRAINT::BALL
1076 //==============================================================================
1077 SimTK_INSERT_DERIVED_HANDLE_DEFINITIONS(Constraint::Ball, Constraint::BallImpl, Constraint);
1078 
Ball(MobilizedBody & body1,MobilizedBody & body2)1079 Constraint::Ball::Ball(MobilizedBody& body1, MobilizedBody& body2)
1080   : Constraint(new BallImpl())
1081 {
1082     SimTK_ASSERT_ALWAYS(body1.isInSubsystem() && body2.isInSubsystem(),
1083         "Constraint::Ball(): both bodies must already be in a MatterSubsystem.");
1084     SimTK_ASSERT_ALWAYS(body1.isInSameSubsystem(body2),
1085         "Constraint::Ball(): both bodies to be connected must be in the same MatterSubsystem.");
1086 
1087     body1.updMatterSubsystem().adoptConstraint(*this);
1088 
1089     updImpl().B1 = updImpl().addConstrainedBody(body1);
1090     updImpl().B2 = updImpl().addConstrainedBody(body2);
1091 }
1092 
Ball(MobilizedBody & body1,const Vec3 & point1,MobilizedBody & body2,const Vec3 & point2)1093 Constraint::Ball::Ball(MobilizedBody& body1, const Vec3& point1,
1094                        MobilizedBody& body2, const Vec3& point2)
1095   : Constraint(new BallImpl())
1096 {
1097     SimTK_ASSERT_ALWAYS(body1.isInSubsystem() && body2.isInSubsystem(),
1098         "Constraint::Ball(): both bodies must already be in a MatterSubsystem.");
1099     SimTK_ASSERT_ALWAYS(body1.isInSameSubsystem(body2),
1100         "Constraint::Ball(): both bodies to be connected must be in the same MatterSubsystem.");
1101 
1102     body1.updMatterSubsystem().adoptConstraint(*this);
1103 
1104     updImpl().B1 = updImpl().addConstrainedBody(body1);
1105     updImpl().B2 = updImpl().addConstrainedBody(body2);
1106 
1107     updImpl().defaultPoint1 = point1;
1108     updImpl().defaultPoint2 = point2;
1109 }
1110 
setDefaultPointOnBody1(const Vec3 & p1)1111 Constraint::Ball& Constraint::Ball::setDefaultPointOnBody1(const Vec3& p1) {
1112     getImpl().invalidateTopologyCache();
1113     updImpl().defaultPoint1 = p1;
1114     return *this;
1115 }
1116 
setDefaultPointOnBody2(const Vec3 & p2)1117 Constraint::Ball& Constraint::Ball::setDefaultPointOnBody2(const Vec3& p2) {
1118     getImpl().invalidateTopologyCache();
1119     updImpl().defaultPoint2 = p2;
1120     return *this;
1121 }
1122 
getBody1MobilizedBodyIndex() const1123 MobilizedBodyIndex Constraint::Ball::getBody1MobilizedBodyIndex() const {
1124     return getImpl().getMobilizedBodyIndexOfConstrainedBody(getImpl().B1);
1125 }
getBody2MobilizedBodyIndex() const1126 MobilizedBodyIndex Constraint::Ball::getBody2MobilizedBodyIndex() const {
1127     return getImpl().getMobilizedBodyIndexOfConstrainedBody(getImpl().B2);
1128 }
getDefaultPointOnBody1() const1129 const Vec3& Constraint::Ball::getDefaultPointOnBody1() const {
1130     return getImpl().defaultPoint1;
1131 }
getDefaultPointOnBody2() const1132 const Vec3& Constraint::Ball::getDefaultPointOnBody2() const {
1133     return getImpl().defaultPoint2;
1134 }
1135 
setDefaultRadius(Real r)1136 Constraint::Ball& Constraint::Ball::setDefaultRadius(Real r) {
1137     getImpl().invalidateTopologyCache();
1138     updImpl().setDefaultRadius(r);
1139     return *this;
1140 }
1141 
getDefaultRadius() const1142 Real Constraint::Ball::getDefaultRadius() const {
1143     return getImpl().getDefaultRadius();
1144 }
1145 
1146 void Constraint::Ball::
setPointOnBody1(State & state,const Vec3 & point_B1) const1147 setPointOnBody1(State& state, const Vec3& point_B1) const {
1148     getImpl().updBodyStations(state).first = point_B1;
1149 }
1150 
1151 void Constraint::Ball::
setPointOnBody2(State & state,const Vec3 & point_B2) const1152 setPointOnBody2(State& state, const Vec3& point_B2) const {
1153     getImpl().updBodyStations(state).second = point_B2;
1154 }
1155 
1156 const Vec3& Constraint::Ball::
getPointOnBody1(const State & state) const1157 getPointOnBody1(const State& state) const {
1158     return getImpl().getBodyStations(state).first;
1159 }
1160 const Vec3& Constraint::Ball::
getPointOnBody2(const State & state) const1161 getPointOnBody2(const State& state) const {
1162     return getImpl().getBodyStations(state).second;
1163 }
1164 
1165 
getPositionErrors(const State & s) const1166 Vec3 Constraint::Ball::getPositionErrors(const State& s) const {
1167     Vec3 perr;
1168     getImpl().getPositionErrors(s, 3, &perr[0]);
1169     return perr;
1170 }
1171 
getVelocityErrors(const State & s) const1172 Vec3 Constraint::Ball::getVelocityErrors(const State& s) const {
1173     Vec3 pverr;
1174     getImpl().getVelocityErrors(s, 3, &pverr[0]);
1175     return pverr;
1176 }
1177 
getAccelerationErrors(const State & s) const1178 Vec3 Constraint::Ball::getAccelerationErrors(const State& s) const {
1179     Vec3 pvaerr;
1180     getImpl().getAccelerationErrors(s, 3, &pvaerr[0]);
1181     return pvaerr;
1182 }
1183 
getMultipliers(const State & s) const1184 Vec3 Constraint::Ball::getMultipliers(const State& s) const {
1185     Vec3 mult;
1186     getImpl().getMultipliers(s, 3, &mult[0]);
1187     return mult;
1188 }
1189 
1190 // The multipliers are the A-frame force vector as applied to body 2 at
1191 // the user-defined station point on body 2. We want to return the
1192 // force applied to body 1, expressed in body 1's frame. Note that this
1193 // is the force applied to body 1 at the point coincident with body 2's
1194 // station point -- if instead we returned the force at body 1's station
1195 // point there would also be a small moment since that point in general
1196 // differs from the contact point.
1197 Vec3 Constraint::Ball::
getBallReactionForceOnBody1(const State & s) const1198 getBallReactionForceOnBody1(const State& s) const {
1199     const BallImpl& impl = getImpl();
1200     Vec3 force2_A;
1201     impl.getMultipliers(s, 3, &force2_A[0]);
1202     const Rotation& R_AB1 = impl.getBodyRotationFromState(s, impl.B1);
1203     return -(~R_AB1*force2_A);
1204 }
1205 
1206 Vec3 Constraint::Ball::
getBallReactionForceOnBody2(const State & s) const1207 getBallReactionForceOnBody2(const State& s) const{
1208     const BallImpl& impl = getImpl();
1209     Vec3 force2_A;
1210     impl.getMultipliers(s, 3, &force2_A[0]);
1211     const Rotation& R_AB2 = impl.getBodyRotationFromState(s, impl.B2);
1212     return ~R_AB2*force2_A;
1213 }
1214 
1215     // BallImpl
1216 
1217 // The default body stations may be overridden by setting instance variables
1218 // in the state. We allocate the state resources here.
1219 void Constraint::Ball::BallImpl::
realizeTopologyVirtual(State & state) const1220 realizeTopologyVirtual(State& state) const {
1221     stationsIx = getMyMatterSubsystemRep().
1222         allocateDiscreteVariable(state, Stage::Instance,
1223             new Value< std::pair<Vec3,Vec3> >
1224                (std::make_pair(defaultPoint1,defaultPoint2)));
1225 }
1226 
1227 // Return the pair of constrained station points, with the first expressed
1228 // in the body 1 frame and the second in the body 2 frame. Note that although
1229 // these are used to define the position error, only the station on body 2
1230 // is used to generate constraint forces; the point of body 1 that is
1231 // coincident with the body 2 point receives the equal and opposite force.
1232 const std::pair<Vec3,Vec3>& Constraint::Ball::BallImpl::
getBodyStations(const State & state) const1233 getBodyStations(const State& state) const {
1234     return Value< std::pair<Vec3,Vec3> >::downcast
1235        (getMyMatterSubsystemRep().getDiscreteVariable(state,stationsIx));
1236 }
1237 
1238 // Return a writable reference into the Instance-stage state variable
1239 // containing the pair of constrained station points, with the first expressed
1240 // in the body 1 frame and the second in the body 2 frame. Calling this
1241 // method invalidates the Instance stage and above in the given state.
1242 std::pair<Vec3,Vec3>& Constraint::Ball::BallImpl::
updBodyStations(State & state) const1243 updBodyStations(State& state) const {
1244     return Value< std::pair<Vec3,Vec3> >::updDowncast
1245        (getMyMatterSubsystemRep().updDiscreteVariable(state,stationsIx));
1246 }
1247 
calcDecorativeGeometryAndAppendVirtual(const State & s,Stage stage,Array_<DecorativeGeometry> & geom) const1248 void Constraint::Ball::BallImpl::calcDecorativeGeometryAndAppendVirtual
1249    (const State& s, Stage stage, Array_<DecorativeGeometry>& geom) const
1250 {
1251     const SimbodyMatterSubsystemRep& matterRep = getMyMatterSubsystemRep();
1252 
1253     // We can't generate the ball until we know the radius, and we can't place
1254     // the geometry on the body until we know the body1 and body2 point
1255     // placements on the bodies, which might not be until Instance stage.
1256     if (   stage == Stage::Instance
1257         && getDefaultRadius() > 0
1258         && matterRep.getShowDefaultGeometry())
1259     {
1260         const std::pair<Vec3,Vec3>& pts = getBodyStations(s);
1261 
1262         const Transform X_B1(Rotation(), pts.first); // should be point from State
1263         const Transform X_B2(Rotation(), pts.second);
1264 
1265         // On the inboard body, draw a solid sphere and a wireframe one attached to it for
1266         // easier visualization of its rotation. These are at about 90% of the radius.
1267         geom.push_back(DecorativeSphere(Real(0.92)*getDefaultRadius())
1268                         .setColor(Gray)
1269                         .setRepresentation(DecorativeGeometry::DrawSurface)
1270                         .setOpacity(Real(0.5))
1271                         .setResolution(Real(0.75))
1272                         .setBodyId(getMobilizedBodyIndexOfConstrainedBody(B1))
1273                         .setTransform(X_B1));
1274         geom.push_back(DecorativeSphere(Real(0.90)*getDefaultRadius())
1275                         .setColor(White)
1276                         .setRepresentation(DecorativeGeometry::DrawWireframe)
1277                         .setResolution(Real(0.75))
1278                         .setLineThickness(3)
1279                         .setOpacity(Real(0.1))
1280                         .setBodyId(getMobilizedBodyIndexOfConstrainedBody(B1))
1281                         .setTransform(X_B1));
1282 
1283         // Draw connector line back to body origin.
1284         if (pts.first.norm() >= SignificantReal)
1285             geom.push_back(DecorativeLine(Vec3(0), pts.first)
1286                         .setColor(Gray)
1287                         .setLineThickness(2)
1288                         .setBodyId(getMobilizedBodyIndexOfConstrainedBody(B1)));
1289 
1290         // On the outboard body draw an orange mesh sphere at the ball radius.
1291         geom.push_back(DecorativeSphere(getDefaultRadius())
1292                         .setColor(Orange)
1293                         .setRepresentation(DecorativeGeometry::DrawWireframe)
1294                         .setOpacity(Real(0.5))
1295                         .setResolution(Real(0.5))
1296                         .setBodyId(getMobilizedBodyIndexOfConstrainedBody(B2))
1297                         .setTransform(X_B2));
1298 
1299         // Draw connector line back to body origin.
1300         if (pts.second.norm() >= SignificantReal)
1301             geom.push_back(DecorativeLine(Vec3(0), pts.second)
1302                         .setColor(Gray)
1303                         .setLineThickness(2)
1304                         .setBodyId(getMobilizedBodyIndexOfConstrainedBody(B2)));
1305     }
1306 }
1307 
1308 
1309 
1310 //==============================================================================
1311 //                      CONSTRAINT::CONSTANT ORIENTATION
1312 //==============================================================================
1313 SimTK_INSERT_DERIVED_HANDLE_DEFINITIONS(Constraint::ConstantOrientation, Constraint::ConstantOrientationImpl, Constraint);
1314 
ConstantOrientation(MobilizedBody & baseBody,const Rotation & defaultFrameOnB,MobilizedBody & followerBody,const Rotation & defaultFrameOnF)1315 Constraint::ConstantOrientation::ConstantOrientation
1316    (MobilizedBody& baseBody,     const Rotation& defaultFrameOnB,
1317     MobilizedBody& followerBody, const Rotation& defaultFrameOnF)
1318   : Constraint(new ConstantOrientationImpl())
1319 {
1320     SimTK_ASSERT_ALWAYS(baseBody.isInSubsystem() && followerBody.isInSubsystem(),
1321         "Constraint::ConstantOrientation(): both bodies must already be in a SimbodyMatterSubsystem.");
1322     SimTK_ASSERT_ALWAYS(baseBody.isInSameSubsystem(followerBody),
1323         "Constraint::ConstantOrientation(): both bodies to be connected must be in the same SimbodyMatterSubsystem.");
1324 
1325     //rep = new ConstantOrientationRep(); rep->setMyHandle(*this);
1326     baseBody.updMatterSubsystem().adoptConstraint(*this);
1327 
1328     updImpl().B = updImpl().addConstrainedBody(baseBody);
1329     updImpl().F = updImpl().addConstrainedBody(followerBody);
1330     updImpl().defaultRB = defaultFrameOnB;
1331     updImpl().defaultRF = defaultFrameOnF;
1332 }
1333 
setDefaultBaseRotation(const Rotation & R)1334 Constraint::ConstantOrientation& Constraint::ConstantOrientation::setDefaultBaseRotation(const Rotation& R) {
1335     getImpl().invalidateTopologyCache();
1336     updImpl().defaultRB = R;
1337     return *this;
1338 }
1339 
setDefaultFollowerRotation(const Rotation & R)1340 Constraint::ConstantOrientation& Constraint::ConstantOrientation::setDefaultFollowerRotation(const Rotation& R) {
1341     getImpl().invalidateTopologyCache();
1342     updImpl().defaultRF = R;
1343     return *this;
1344 }
1345 
1346 
getBaseMobilizedBodyIndex() const1347 MobilizedBodyIndex Constraint::ConstantOrientation::getBaseMobilizedBodyIndex() const {
1348     return getImpl().getMobilizedBodyIndexOfConstrainedBody(getImpl().B);
1349 }
getFollowerMobilizedBodyIndex() const1350 MobilizedBodyIndex Constraint::ConstantOrientation::getFollowerMobilizedBodyIndex() const {
1351     return getImpl().getMobilizedBodyIndexOfConstrainedBody(getImpl().F);
1352 }
getDefaultBaseRotation() const1353 const Rotation& Constraint::ConstantOrientation::getDefaultBaseRotation() const {
1354     return getImpl().defaultRB;
1355 }
getDefaultFollowerRotation() const1356 const Rotation& Constraint::ConstantOrientation::getDefaultFollowerRotation() const {
1357     return getImpl().defaultRF;
1358 }
1359 
getPositionErrors(const State & s) const1360 Vec3 Constraint::ConstantOrientation::getPositionErrors(const State& s) const {
1361     Vec3 perr;
1362     getImpl().getPositionErrors(s, 3, &perr[0]);
1363     return perr;
1364 }
1365 
getVelocityErrors(const State & s) const1366 Vec3 Constraint::ConstantOrientation::getVelocityErrors(const State& s) const {
1367     Vec3 pverr;
1368     getImpl().getVelocityErrors(s, 3, &pverr[0]);
1369     return pverr;
1370 }
1371 
getAccelerationErrors(const State & s) const1372 Vec3 Constraint::ConstantOrientation::getAccelerationErrors(const State& s) const {
1373     Vec3 pvaerr;
1374     getImpl().getAccelerationErrors(s, 3, &pvaerr[0]);
1375     return pvaerr;
1376 }
1377 
getMultipliers(const State & s) const1378 Vec3 Constraint::ConstantOrientation::getMultipliers(const State& s) const {
1379     Vec3 mult;
1380     getImpl().getMultipliers(s, 3, &mult[0]);
1381     return mult;
1382 }
1383 
1384 
1385     // ConstantOrientationImpl
1386 
1387     //TODO: no visualization yet
1388 
1389 
1390 
1391 //==============================================================================
1392 //                            CONSTRAINT::WELD
1393 //==============================================================================
1394 SimTK_INSERT_DERIVED_HANDLE_DEFINITIONS(Constraint::Weld, Constraint::WeldImpl, Constraint);
1395 
Weld(MobilizedBody & body1,MobilizedBody & body2)1396 Constraint::Weld::Weld(MobilizedBody& body1, MobilizedBody& body2)
1397   : Constraint(new WeldImpl())
1398 {
1399     SimTK_ASSERT_ALWAYS(body1.isInSubsystem() && body2.isInSubsystem(),
1400         "Constraint::Weld(): both bodies must already be in a MatterSubsystem.");
1401     SimTK_ASSERT_ALWAYS(body1.isInSameSubsystem(body2),
1402         "Constraint::Weld(): both bodies to be connected must be in the same MatterSubsystem.");
1403 
1404     //rep = new WeldRep(); rep->setMyHandle(*this);
1405     body1.updMatterSubsystem().adoptConstraint(*this);
1406 
1407     updImpl().B = updImpl().addConstrainedBody(body1);
1408     updImpl().F = updImpl().addConstrainedBody(body2);
1409 }
1410 
Weld(MobilizedBody & body1,const Transform & frame1,MobilizedBody & body2,const Transform & frame2)1411 Constraint::Weld::Weld(MobilizedBody& body1, const Transform& frame1,
1412                        MobilizedBody& body2, const Transform& frame2)
1413   : Constraint(new WeldImpl())
1414 {
1415     SimTK_ASSERT_ALWAYS(body1.isInSubsystem() && body2.isInSubsystem(),
1416         "Constraint::Weld(): both bodies must already be in a MatterSubsystem.");
1417     SimTK_ASSERT_ALWAYS(body1.isInSameSubsystem(body2),
1418         "Constraint::Weld(): both bodies to be connected must be in the same MatterSubsystem.");
1419 
1420     //rep = new WeldRep(); rep->setMyHandle(*this);
1421     body1.updMatterSubsystem().adoptConstraint(*this);
1422 
1423     updImpl().B = updImpl().addConstrainedBody(body1);
1424     updImpl().F = updImpl().addConstrainedBody(body2);
1425 
1426     updImpl().defaultFrameB = frame1;
1427     updImpl().defaultFrameF = frame2;
1428 }
1429 
setDefaultFrameOnBody1(const Transform & f1)1430 Constraint::Weld& Constraint::Weld::setDefaultFrameOnBody1(const Transform& f1) {
1431     updImpl().defaultFrameB = f1;
1432     return *this;
1433 }
1434 
setDefaultFrameOnBody2(const Transform & f2)1435 Constraint::Weld& Constraint::Weld::setDefaultFrameOnBody2(const Transform& f2) {
1436     updImpl().defaultFrameF = f2;
1437     return *this;
1438 }
1439 
getBody1MobilizedBodyIndex() const1440 MobilizedBodyIndex Constraint::Weld::getBody1MobilizedBodyIndex() const {
1441     return getImpl().getMobilizedBodyIndexOfConstrainedBody(getImpl().B);
1442 }
getBody2MobilizedBodyIndex() const1443 MobilizedBodyIndex Constraint::Weld::getBody2MobilizedBodyIndex() const {
1444     return getImpl().getMobilizedBodyIndexOfConstrainedBody(getImpl().F);
1445 }
getDefaultFrameOnBody1() const1446 const Transform& Constraint::Weld::getDefaultFrameOnBody1() const {
1447     return getImpl().defaultFrameB;
1448 }
getDefaultFrameOnBody2() const1449 const Transform& Constraint::Weld::getDefaultFrameOnBody2() const {
1450     return getImpl().defaultFrameF;
1451 }
1452 
getPositionErrors(const State & s) const1453 Vec6 Constraint::Weld::getPositionErrors(const State& s) const {
1454     Vec6 perr;
1455     getImpl().getPositionErrors(s, 6, &perr[0]);
1456     return perr;
1457 }
1458 
getVelocityErrors(const State & s) const1459 Vec6 Constraint::Weld::getVelocityErrors(const State& s) const {
1460     Vec6 pverr;
1461     getImpl().getVelocityErrors(s, 6, &pverr[0]);
1462     return pverr;
1463 }
1464 
getAccelerationErrors(const State & s) const1465 Vec6 Constraint::Weld::getAccelerationErrors(const State& s) const {
1466     Vec6 pvaerr;
1467     getImpl().getAccelerationErrors(s, 6, &pvaerr[0]);
1468     return pvaerr;
1469 }
1470 
getMultipliers(const State & s) const1471 Vec6 Constraint::Weld::getMultipliers(const State& s) const {
1472     Vec6 mult;
1473     getImpl().getMultipliers(s, 6, &mult[0]);
1474     return mult;
1475 }
1476 
1477     // WeldImpl
1478 
calcDecorativeGeometryAndAppendVirtual(const State & s,Stage stage,Array_<DecorativeGeometry> & geom) const1479 void Constraint::Weld::WeldImpl::calcDecorativeGeometryAndAppendVirtual
1480    (const State& s, Stage stage, Array_<DecorativeGeometry>& geom) const
1481 {
1482     // We can't generate the frames until we know the axis lengths to use, and we can't place
1483     // the geometry on the bodies until we know the body1 and body2 frame
1484     // placements, which might not be until Instance stage.
1485     if (stage == Stage::Instance && getAxisDisplayLength() != 0 && getMyMatterSubsystemRep().getShowDefaultGeometry()) {
1486 
1487         geom.push_back(DecorativeFrame(getAxisDisplayLength())
1488                                             .setColor(getFrameColor(0))
1489                                             .setLineThickness(2)
1490                                             .setBodyId(getMobilizedBodyIndexOfConstrainedBody(B))
1491                                             .setTransform(defaultFrameB));
1492 
1493         // Draw connector line back to body origin.
1494         if (defaultFrameB.p().norm() >= SignificantReal)
1495             geom.push_back(DecorativeLine(Vec3(0), defaultFrameB.p())
1496                              .setColor(getFrameColor(0))
1497                              .setLineThickness(2)
1498                              .setBodyId(getMobilizedBodyIndexOfConstrainedBody(B)));
1499 
1500 
1501         geom.push_back(DecorativeFrame(Real(0.67)*getAxisDisplayLength())
1502                                             .setColor(getFrameColor(1))
1503                                             .setLineThickness(4)
1504                                             .setBodyId(getMobilizedBodyIndexOfConstrainedBody(F))
1505                                             .setTransform(defaultFrameF));
1506 
1507         if (defaultFrameF.p().norm() >= SignificantReal)
1508             geom.push_back(DecorativeLine(Vec3(0), defaultFrameF.p())
1509                              .setColor(getFrameColor(1))
1510                              .setLineThickness(4)
1511                              .setBodyId(getMobilizedBodyIndexOfConstrainedBody(F)));
1512     }
1513 }
1514 
1515 
1516 
1517 //==============================================================================
1518 //                            CONSTRAINT::NO SLIP 1D
1519 //==============================================================================
1520 SimTK_INSERT_DERIVED_HANDLE_DEFINITIONS(Constraint::NoSlip1D, Constraint::NoSlip1DImpl, Constraint);
1521 
NoSlip1D(MobilizedBody & caseBody,const Vec3 & P_C,const UnitVec3 & n_C,MobilizedBody & movingBody0,MobilizedBody & movingBody1)1522 Constraint::NoSlip1D::NoSlip1D
1523    (MobilizedBody& caseBody, const Vec3& P_C, const UnitVec3& n_C,
1524     MobilizedBody& movingBody0, MobilizedBody& movingBody1)
1525   : Constraint(new NoSlip1DImpl())
1526 {
1527     SimTK_ASSERT_ALWAYS(caseBody.isInSubsystem() && movingBody0.isInSubsystem()&& movingBody1.isInSubsystem(),
1528         "Constraint::NoSlip1D(): all three bodies must already be in a SimbodyMatterSubsystem.");
1529     SimTK_ASSERT_ALWAYS(caseBody.isInSameSubsystem(movingBody0) && caseBody.isInSameSubsystem(movingBody1),
1530         "Constraint::NoSlip1D(): all three bodies must be in the same SimbodyMatterSubsystem.");
1531 
1532     //rep = new NoSlip1DRep(); rep->setMyHandle(*this);
1533     caseBody.updMatterSubsystem().adoptConstraint(*this);
1534 
1535     updImpl().caseBody    = updImpl().addConstrainedBody(caseBody);
1536     updImpl().movingBody0 = updImpl().addConstrainedBody(movingBody0);
1537     updImpl().movingBody1 = updImpl().addConstrainedBody(movingBody1);
1538     updImpl().defaultNoSlipDirection = n_C;
1539     updImpl().defaultContactPoint    = P_C;
1540 }
1541 
setDefaultDirection(const UnitVec3 & n)1542 Constraint::NoSlip1D& Constraint::NoSlip1D::setDefaultDirection(const UnitVec3& n) {
1543     getImpl().invalidateTopologyCache();
1544     updImpl().defaultNoSlipDirection = n;
1545     return *this;
1546 }
1547 
setDefaultContactPoint(const Vec3 & p)1548 Constraint::NoSlip1D& Constraint::NoSlip1D::setDefaultContactPoint(const Vec3& p) {
1549     getImpl().invalidateTopologyCache();
1550     updImpl().defaultContactPoint = p;
1551     return *this;
1552 }
1553 
getCaseMobilizedBodyIndex() const1554 MobilizedBodyIndex Constraint::NoSlip1D::getCaseMobilizedBodyIndex() const {
1555     return getImpl().getMobilizedBodyIndexOfConstrainedBody(getImpl().caseBody);
1556 }
getMovingBodyMobilizedBodyIndex(int which) const1557 MobilizedBodyIndex Constraint::NoSlip1D::getMovingBodyMobilizedBodyIndex(int which) const {
1558     assert(which==0 || which==1);
1559     return getImpl().getMobilizedBodyIndexOfConstrainedBody(
1560         which==0 ? getImpl().movingBody0 : getImpl().movingBody1);
1561 }
getDefaultDirection() const1562 const UnitVec3& Constraint::NoSlip1D::getDefaultDirection() const {
1563     return getImpl().defaultNoSlipDirection;
1564 }
getDefaultContactPoint() const1565 const Vec3& Constraint::NoSlip1D::getDefaultContactPoint() const {
1566     return getImpl().defaultContactPoint;
1567 }
1568 
setDirectionDisplayLength(Real l)1569 Constraint::NoSlip1D& Constraint::NoSlip1D::setDirectionDisplayLength(Real l) {
1570     updImpl().setDirectionDisplayLength(l);
1571     return *this;
1572 }
setPointDisplayRadius(Real r)1573 Constraint::NoSlip1D& Constraint::NoSlip1D::setPointDisplayRadius(Real r) {
1574     updImpl().setPointDisplayRadius(r);
1575     return *this;
1576 }
1577 
getDirectionDisplayLength() const1578 Real Constraint::NoSlip1D::getDirectionDisplayLength() const {
1579     return getImpl().getDirectionDisplayLength();
1580 }
1581 
getPointDisplayRadius() const1582 Real Constraint::NoSlip1D::getPointDisplayRadius() const {
1583     return getImpl().getPointDisplayRadius();
1584 }
1585 
1586 void Constraint::NoSlip1D::
setContactPoint(State & state,const Vec3 & point_C) const1587 setContactPoint(State& state, const Vec3& point_C) const {
1588     getImpl().updContactInfo(state).first = point_C;
1589 }
1590 
1591 void Constraint::NoSlip1D::
setDirection(State & state,const UnitVec3 & direction_C) const1592 setDirection(State& state, const UnitVec3& direction_C) const {
1593     getImpl().updContactInfo(state).second = direction_C;
1594 }
1595 
1596 const Vec3& Constraint::NoSlip1D::
getContactPoint(const State & state) const1597 getContactPoint(const State& state) const {
1598     return getImpl().getContactInfo(state).first;
1599 }
1600 const UnitVec3& Constraint::NoSlip1D::
getDirection(const State & state) const1601 getDirection(const State& state) const {
1602     return getImpl().getContactInfo(state).second;
1603 }
1604 
1605 
getVelocityError(const State & s) const1606 Real Constraint::NoSlip1D::getVelocityError(const State& s) const {
1607     Real pverr;
1608     getImpl().getVelocityErrors(s, 1, &pverr);
1609     return pverr;
1610 }
1611 
getAccelerationError(const State & s) const1612 Real Constraint::NoSlip1D::getAccelerationError(const State& s) const {
1613     Real pvaerr;
1614     getImpl().getAccelerationErrors(s, 1, &pvaerr);
1615     return pvaerr;
1616 }
1617 
getMultiplier(const State & s) const1618 Real Constraint::NoSlip1D::getMultiplier(const State& s) const {
1619     Real mult;
1620     getImpl().getMultipliers(s, 1, &mult);
1621     return mult;
1622 }
1623 
getForceAtContactPoint(const State & s) const1624 Real Constraint::NoSlip1D::getForceAtContactPoint(const State& s) const {
1625     return -getMultiplier(s); // applied forces have opposite sign from lambda
1626 }
1627 
1628     // NoSlip1DImpl
1629 
1630 
1631 // The default contact point and no-slip direction may be overridden by setting
1632 // an instance variable in the state. We allocate the state resources here.
1633 void Constraint::NoSlip1D::NoSlip1DImpl::
realizeTopologyVirtual(State & state) const1634 realizeTopologyVirtual(State& state) const {
1635     contactInfoIx = getMyMatterSubsystemRep().
1636         allocateDiscreteVariable(state, Stage::Instance,
1637             new Value< std::pair<Vec3,UnitVec3> >
1638                (std::make_pair(defaultContactPoint,defaultNoSlipDirection)));
1639 }
1640 
1641 // Return the pair of constrained station points, with the first expressed
1642 // in the body 1 frame and the second in the body 2 frame. Note that although
1643 // these are used to define the position error, only the station on body 2
1644 // is used to generate constraint forces; the point of body 1 that is
1645 // coincident with the body 2 point receives the equal and opposite force.
1646 const std::pair<Vec3,UnitVec3>& Constraint::NoSlip1D::NoSlip1DImpl::
getContactInfo(const State & state) const1647 getContactInfo(const State& state) const {
1648     return Value< std::pair<Vec3,UnitVec3> >::downcast
1649        (getMyMatterSubsystemRep().getDiscreteVariable(state,contactInfoIx));
1650 }
1651 
1652 // Return a writable reference into the Instance-stage state variable
1653 // containing the pair of constrained station points, with the first expressed
1654 // in the body 1 frame and the second in the body 2 frame. Calling this
1655 // method invalidates the Instance stage and above in the given state.
1656 std::pair<Vec3,UnitVec3>& Constraint::NoSlip1D::NoSlip1DImpl::
updContactInfo(State & state) const1657 updContactInfo(State& state) const {
1658     return Value< std::pair<Vec3,UnitVec3> >::updDowncast
1659        (getMyMatterSubsystemRep().updDiscreteVariable(state,contactInfoIx));
1660 }
1661 
calcDecorativeGeometryAndAppendVirtual(const State & s,Stage stage,Array_<DecorativeGeometry> & geom) const1662 void Constraint::NoSlip1D::NoSlip1DImpl::calcDecorativeGeometryAndAppendVirtual
1663    (const State& s, Stage stage, Array_<DecorativeGeometry>& geom) const
1664 {
1665     // We can't generate the artwork until we know the direction and contact
1666     // point location, which might not be until Instance stage.
1667     if (stage == Stage::Instance && getMyMatterSubsystemRep().getShowDefaultGeometry()) {
1668         const SimbodyMatterSubsystemRep& matterRep = getMyMatterSubsystemRep();
1669 
1670         const std::pair<Vec3,UnitVec3>& info = getContactInfo(s);
1671         const Vec3&     P_C = info.first;
1672         const UnitVec3& n_C = info.second;
1673 
1674         // This makes x axis point along no-slip direction, origin at contact point
1675         const Transform X_CP(Rotation(n_C,XAxis), P_C);
1676 
1677         const MobilizedBodyIndex caseMBId = getMobilizedBodyIndexOfConstrainedBody(caseBody);
1678 
1679         if (directionLength > 0) {
1680             // On the case body, draw a gray line in the no-slip direction, starting at contact point.
1681             geom.push_back(DecorativeLine(Vec3(0), Vec3(directionLength,0,0))
1682                                                 .setColor(Gray)
1683                                                 .setBodyId(caseMBId)
1684                                                 .setTransform(X_CP));
1685         }
1686         if (pointRadius > 0) {
1687             // On the follower body draw an orange mesh sphere at the point radius.
1688             geom.push_back(DecorativeSphere(pointRadius)
1689                                                 .setColor(Orange)
1690                                                 .setRepresentation(DecorativeGeometry::DrawWireframe)
1691                                                 .setResolution(0.5)
1692                                                 .setBodyId(caseMBId)
1693                                                 .setTransform(X_CP));
1694         }
1695     }
1696 }
1697 
1698 
1699 
1700 //==============================================================================
1701 //                      CONSTRAINT::CONSTANT COORDINATE
1702 //==============================================================================
1703 SimTK_INSERT_DERIVED_HANDLE_DEFINITIONS
1704 (Constraint::ConstantCoordinate,Constraint::ConstantCoordinateImpl,Constraint);
1705 
1706 // This picks one of the coordinates from a multiple-coordinate mobilizer.
ConstantCoordinate(MobilizedBody & mobilizer,MobilizerQIndex whichQ,Real defaultPosition)1707 Constraint::ConstantCoordinate::ConstantCoordinate
1708    (MobilizedBody& mobilizer, MobilizerQIndex whichQ, Real defaultPosition)
1709   : Constraint(new ConstantCoordinateImpl())
1710 {
1711     SimTK_ASSERT_ALWAYS(mobilizer.isInSubsystem(),
1712         "Constraint::ConstantCoordinate(): the mobilizer must already be"
1713         " in a SimbodyMatterSubsystem.");
1714 
1715     mobilizer.updMatterSubsystem().adoptConstraint(*this);
1716 
1717     updImpl().theMobilizer = updImpl().addConstrainedMobilizer(mobilizer);
1718     updImpl().whichCoordinate = whichQ;
1719     updImpl().defaultPosition = defaultPosition;
1720 }
1721 
1722 // This is for mobilizers with only 1 mobility.
ConstantCoordinate(MobilizedBody & mobilizer,Real defaultPosition)1723 Constraint::ConstantCoordinate::ConstantCoordinate
1724    (MobilizedBody& mobilizer, Real defaultPosition)
1725 :   Constraint(new ConstantCoordinateImpl())
1726 {
1727     SimTK_ASSERT_ALWAYS(mobilizer.isInSubsystem(),
1728         "Constraint::ConstantCoordinate(): the mobilizer must already be"
1729         " in a SimbodyMatterSubsystem.");
1730 
1731     mobilizer.updMatterSubsystem().adoptConstraint(*this);
1732 
1733     updImpl().theMobilizer = updImpl().addConstrainedMobilizer(mobilizer);
1734     updImpl().whichCoordinate = MobilizerQIndex(0);
1735     updImpl().defaultPosition = defaultPosition;
1736 }
1737 
getMobilizedBodyIndex() const1738 MobilizedBodyIndex Constraint::ConstantCoordinate::getMobilizedBodyIndex() const {
1739     return getImpl().getMobilizedBodyIndexOfConstrainedMobilizer
1740                                                     (getImpl().theMobilizer);
1741 }
getWhichQ() const1742 MobilizerQIndex Constraint::ConstantCoordinate::getWhichQ() const {
1743     return getImpl().whichCoordinate;
1744 }
1745 
getDefaultPosition() const1746 Real Constraint::ConstantCoordinate::getDefaultPosition() const {
1747     return getImpl().defaultPosition;
1748 }
1749 
1750 Constraint::ConstantCoordinate& Constraint::ConstantCoordinate::
setDefaultPosition(Real position)1751 setDefaultPosition(Real position) {
1752     getImpl().invalidateTopologyCache();
1753     updImpl().defaultPosition = position;
1754     return *this;
1755 }
1756 
1757 void Constraint::ConstantCoordinate::
setPosition(State & state,Real position) const1758 setPosition(State& state, Real position) const {
1759     getImpl().updPosition(state) = position;
1760 }
1761 
1762 Real Constraint::ConstantCoordinate::
getPosition(const State & state) const1763 getPosition(const State& state) const {
1764     return getImpl().getPosition(state);
1765 }
1766 
getPositionError(const State & s) const1767 Real Constraint::ConstantCoordinate::getPositionError(const State& s) const {
1768     Real perr;
1769     getImpl().getPositionErrors(s, 1, &perr);
1770     return perr;
1771 }
1772 
getVelocityError(const State & s) const1773 Real Constraint::ConstantCoordinate::getVelocityError(const State& s) const {
1774     Real pverr;
1775     getImpl().getVelocityErrors(s, 1, &pverr);
1776     return pverr;
1777 }
1778 
getAccelerationError(const State & s) const1779 Real Constraint::ConstantCoordinate::getAccelerationError(const State& s) const {
1780     Real paerr;
1781     getImpl().getAccelerationErrors(s, 1, &paerr);
1782     return paerr;
1783 }
1784 
getMultiplier(const State & s) const1785 Real Constraint::ConstantCoordinate::getMultiplier(const State& s) const {
1786     Real mult;
1787     getImpl().getMultipliers(s, 1, &mult);
1788     return mult;
1789 }
1790 
1791 
1792     // ConstantCoordinateImpl
1793 
1794 // Allocate a state variable to hold the desired position. Changing this
1795 // variable invalidates Stage::Position.
1796 void Constraint::ConstantCoordinateImpl::
realizeTopologyVirtual(State & state) const1797 realizeTopologyVirtual(State& state) const {
1798     ConstantCoordinateImpl* mThis = // mutable momentarily
1799         const_cast<ConstantCoordinateImpl*>(this);
1800     mThis->positionIx = getMyMatterSubsystemRep().
1801         allocateDiscreteVariable(state, Stage::Position,
1802             new Value<Real>(defaultPosition));
1803 }
1804 
1805 Real Constraint::ConstantCoordinateImpl::
getPosition(const State & state) const1806 getPosition(const State& state) const {
1807     const SimbodyMatterSubsystemRep& matter = getMyMatterSubsystemRep();
1808     return Value<Real>::downcast(matter.getDiscreteVariable(state,positionIx));
1809 }
1810 
1811 Real& Constraint::ConstantCoordinateImpl::
updPosition(State & state) const1812 updPosition(State& state) const {
1813     const SimbodyMatterSubsystemRep& matter = getMyMatterSubsystemRep();
1814     return Value<Real>::updDowncast
1815                                 (matter.updDiscreteVariable(state,positionIx));
1816 }
1817 
1818 
1819 
1820 //==============================================================================
1821 //                         CONSTRAINT::CONSTANT SPEED
1822 //==============================================================================
1823 SimTK_INSERT_DERIVED_HANDLE_DEFINITIONS
1824    (Constraint::ConstantSpeed, Constraint::ConstantSpeedImpl, Constraint);
1825 
1826 // This picks one of the mobilities from a multiple-mobility mobilizer.
ConstantSpeed(MobilizedBody & mobilizer,MobilizerUIndex whichU,Real defaultSpeed)1827 Constraint::ConstantSpeed::ConstantSpeed
1828    (MobilizedBody& mobilizer, MobilizerUIndex whichU, Real defaultSpeed)
1829   : Constraint(new ConstantSpeedImpl())
1830 {
1831     SimTK_ASSERT_ALWAYS(mobilizer.isInSubsystem(),
1832         "Constraint::ConstantSpeed(): the mobilizer must already be"
1833         " in a SimbodyMatterSubsystem.");
1834 
1835     mobilizer.updMatterSubsystem().adoptConstraint(*this);
1836 
1837     updImpl().theMobilizer = updImpl().addConstrainedMobilizer(mobilizer);
1838     updImpl().whichMobility = whichU;
1839     updImpl().defaultSpeed = defaultSpeed;
1840 }
1841 
1842 // This is for mobilizers with only 1 mobility.
ConstantSpeed(MobilizedBody & mobilizer,Real defaultSpeed)1843 Constraint::ConstantSpeed::ConstantSpeed
1844    (MobilizedBody& mobilizer, Real defaultSpeed)
1845 :   Constraint(new ConstantSpeedImpl())
1846 {
1847     SimTK_ASSERT_ALWAYS(mobilizer.isInSubsystem(),
1848         "Constraint::ConstantSpeed(): the mobilizer must already be"
1849         " in a SimbodyMatterSubsystem.");
1850 
1851     mobilizer.updMatterSubsystem().adoptConstraint(*this);
1852 
1853     updImpl().theMobilizer = updImpl().addConstrainedMobilizer(mobilizer);
1854     updImpl().whichMobility = MobilizerUIndex(0);
1855     updImpl().defaultSpeed = defaultSpeed;
1856 }
1857 
getMobilizedBodyIndex() const1858 MobilizedBodyIndex Constraint::ConstantSpeed::getMobilizedBodyIndex() const {
1859     return getImpl().getMobilizedBodyIndexOfConstrainedMobilizer
1860                                                     (getImpl().theMobilizer);
1861 }
getWhichU() const1862 MobilizerUIndex Constraint::ConstantSpeed::getWhichU() const {
1863     return getImpl().whichMobility;
1864 }
1865 
getDefaultSpeed() const1866 Real Constraint::ConstantSpeed::getDefaultSpeed() const {
1867     return getImpl().defaultSpeed;
1868 }
1869 
1870 Constraint::ConstantSpeed& Constraint::ConstantSpeed::
setDefaultSpeed(Real u)1871 setDefaultSpeed(Real u) {
1872     getImpl().invalidateTopologyCache();
1873     updImpl().defaultSpeed = u;
1874     return *this;
1875 }
1876 
1877 void Constraint::ConstantSpeed::
setSpeed(State & state,Real speed) const1878 setSpeed(State& state, Real speed) const {
1879     getImpl().updSpeed(state) = speed;
1880 }
1881 
1882 Real Constraint::ConstantSpeed::
getSpeed(const State & state) const1883 getSpeed(const State& state) const {
1884     return getImpl().getSpeed(state);
1885 }
1886 
getVelocityError(const State & s) const1887 Real Constraint::ConstantSpeed::getVelocityError(const State& s) const {
1888     Real verr;
1889     getImpl().getVelocityErrors(s, 1, &verr);
1890     return verr;
1891 }
1892 
getAccelerationError(const State & s) const1893 Real Constraint::ConstantSpeed::getAccelerationError(const State& s) const {
1894     Real vaerr;
1895     getImpl().getAccelerationErrors(s, 1, &vaerr);
1896     return vaerr;
1897 }
1898 
getMultiplier(const State & s) const1899 Real Constraint::ConstantSpeed::getMultiplier(const State& s) const {
1900     Real mult;
1901     getImpl().getMultipliers(s, 1, &mult);
1902     return mult;
1903 }
1904 
1905 
1906     // ConstantSpeedImpl
1907 
1908 // Allocate a state variable to hold the desired speed.
1909 void Constraint::ConstantSpeedImpl::
realizeTopologyVirtual(State & state) const1910 realizeTopologyVirtual(State& state) const {
1911     ConstantSpeedImpl* mThis = // mutable momentarily
1912         const_cast<ConstantSpeedImpl*>(this);
1913     mThis->speedIx = getMyMatterSubsystemRep().
1914         allocateDiscreteVariable(state, Stage::Velocity,
1915             new Value<Real>(defaultSpeed));
1916 }
1917 
1918 Real Constraint::ConstantSpeedImpl::
getSpeed(const State & state) const1919 getSpeed(const State& state) const {
1920     const SimbodyMatterSubsystemRep& matter = getMyMatterSubsystemRep();
1921     return Value<Real>::downcast(matter.getDiscreteVariable(state,speedIx));
1922 }
1923 
1924 Real& Constraint::ConstantSpeedImpl::
updSpeed(State & state) const1925 updSpeed(State& state) const {
1926     const SimbodyMatterSubsystemRep& matter = getMyMatterSubsystemRep();
1927     return Value<Real>::updDowncast(matter.updDiscreteVariable(state,speedIx));
1928 }
1929 
1930 
1931 //==============================================================================
1932 //                     CONSTRAINT::CONSTANT ACCELERATION
1933 //==============================================================================
1934 SimTK_INSERT_DERIVED_HANDLE_DEFINITIONS
1935    (Constraint::ConstantAcceleration, Constraint::ConstantAccelerationImpl,
1936     Constraint);
1937 
1938 // This picks one of the mobilities from a multiple-mobility mobilizer.
ConstantAcceleration(MobilizedBody & mobilizer,MobilizerUIndex whichU,Real defaultAcceleration)1939 Constraint::ConstantAcceleration::ConstantAcceleration
1940    (MobilizedBody& mobilizer, MobilizerUIndex whichU, Real defaultAcceleration)
1941 :   Constraint(new ConstantAccelerationImpl())
1942 {
1943     SimTK_ASSERT_ALWAYS(mobilizer.isInSubsystem(),
1944     "Constraint::ConstantAcceleration(): the mobilizer must already be"
1945     " in a SimbodyMatterSubsystem.");
1946 
1947     mobilizer.updMatterSubsystem().adoptConstraint(*this);
1948 
1949     updImpl().theMobilizer = updImpl().addConstrainedMobilizer(mobilizer);
1950     updImpl().whichMobility = whichU;
1951     updImpl().defaultAcceleration = defaultAcceleration;
1952 }
1953 
1954 // This is for mobilizers with only 1 mobility.
ConstantAcceleration(MobilizedBody & mobilizer,Real defaultAcceleration)1955 Constraint::ConstantAcceleration::ConstantAcceleration
1956    (MobilizedBody& mobilizer, Real defaultAcceleration)
1957 :   Constraint(new ConstantAccelerationImpl())
1958 {
1959     SimTK_ASSERT_ALWAYS(mobilizer.isInSubsystem(),
1960     "Constraint::ConstantAcceleration(): the mobilizer must already be"
1961     " in a SimbodyMatterSubsystem.");
1962 
1963     mobilizer.updMatterSubsystem().adoptConstraint(*this);
1964 
1965     updImpl().theMobilizer = updImpl().addConstrainedMobilizer(mobilizer);
1966     updImpl().whichMobility = MobilizerUIndex(0);
1967     updImpl().defaultAcceleration = defaultAcceleration;
1968 }
1969 
1970 MobilizedBodyIndex Constraint::ConstantAcceleration::
getMobilizedBodyIndex() const1971 getMobilizedBodyIndex() const {
1972     return getImpl().getMobilizedBodyIndexOfConstrainedMobilizer
1973                                                     (getImpl().theMobilizer);
1974 }
getWhichU() const1975 MobilizerUIndex Constraint::ConstantAcceleration::getWhichU() const {
1976     return getImpl().whichMobility;
1977 }
getDefaultAcceleration() const1978 Real Constraint::ConstantAcceleration::getDefaultAcceleration() const {
1979     return getImpl().defaultAcceleration;
1980 }
1981 Constraint::ConstantAcceleration& Constraint::ConstantAcceleration::
setDefaultAcceleration(Real udot)1982 setDefaultAcceleration(Real udot) {
1983     getImpl().invalidateTopologyCache();
1984     updImpl().defaultAcceleration = udot;
1985     return *this;
1986 }
1987 
1988 void Constraint::ConstantAcceleration::
setAcceleration(State & state,Real accel) const1989 setAcceleration(State& state, Real accel) const {
1990     getImpl().updAcceleration(state) = accel;
1991 }
1992 
1993 Real Constraint::ConstantAcceleration::
getAcceleration(const State & state) const1994 getAcceleration(const State& state) const {
1995     return getImpl().getAcceleration(state);
1996 }
1997 
getAccelerationError(const State & s) const1998 Real Constraint::ConstantAcceleration::getAccelerationError(const State& s) const {
1999     Real pvaerr;
2000     getImpl().getAccelerationErrors(s, 1, &pvaerr);
2001     return pvaerr;
2002 }
2003 
getMultiplier(const State & s) const2004 Real Constraint::ConstantAcceleration::getMultiplier(const State& s) const {
2005     Real mult;
2006     getImpl().getMultipliers(s, 1, &mult);
2007     return mult;
2008 }
2009 
2010     // ConstantAccelerationImpl
2011 
2012 // Allocate a state variable to hold the desired acceleration.
2013 void Constraint::ConstantAccelerationImpl::
realizeTopologyVirtual(State & state) const2014 realizeTopologyVirtual(State& state) const {
2015     ConstantAccelerationImpl* mThis = // mutable momentarily
2016         const_cast<ConstantAccelerationImpl*>(this);
2017     mThis->accelIx = getMyMatterSubsystemRep().
2018         allocateDiscreteVariable(state, Stage::Acceleration,
2019             new Value<Real>(defaultAcceleration));
2020 }
2021 
2022 Real Constraint::ConstantAccelerationImpl::
getAcceleration(const State & state) const2023 getAcceleration(const State& state) const {
2024     const SimbodyMatterSubsystemRep& matter = getMyMatterSubsystemRep();
2025     return Value<Real>::downcast(matter.getDiscreteVariable(state,accelIx));
2026 }
2027 
2028 Real& Constraint::ConstantAccelerationImpl::
updAcceleration(State & state) const2029 updAcceleration(State& state) const {
2030     const SimbodyMatterSubsystemRep& matter = getMyMatterSubsystemRep();
2031     return Value<Real>::updDowncast(matter.updDiscreteVariable(state,accelIx));
2032 }
2033 
2034 
2035 
2036 //==============================================================================
2037 //                            CONSTRAINT::CUSTOM
2038 //==============================================================================
2039 SimTK_INSERT_DERIVED_HANDLE_DEFINITIONS
2040    (Constraint::Custom, Constraint::CustomImpl, Constraint);
2041 
2042 // We are given an Implementation object which is already holding a CustomImpl
2043 // object for us. We'll first take away ownership of the CustomImpl, then
2044 // make the CustomImpl take over ownership of the Implementation object.
Custom(Constraint::Custom::Implementation * implementation)2045 Constraint::Custom::Custom(Constraint::Custom::Implementation* implementation)
2046 :   Constraint(implementation
2047                 ? implementation->updImpl().removeOwnershipOfCustomImpl()
2048                 : 0)
2049 {
2050     SimTK_ASSERT_ALWAYS(implementation,
2051         "Constraint::Custom::Custom(): Implementation pointer was NULL.");
2052 
2053     // Now store the Implementation pointer in our CustomImpl. The Implementation
2054     // object retains its original pointer to the CustomImpl object so it can
2055     // operate as a proxy for the CustomImpl. However the Custom handle now owns the
2056     // CustomImpl and the CustomImpl owns the Implementation.
2057     updImpl().takeOwnershipOfImplementation(implementation);
2058 
2059     updImpl().updMyMatterSubsystemRep().adoptConstraint(*this);
2060 }
2061 
2062 const Constraint::Custom::Implementation&
getImplementation() const2063 Constraint::Custom::getImplementation() const {
2064     return getImpl().getImplementation();
2065 }
2066 
2067 Constraint::Custom::Implementation&
updImplementation()2068 Constraint::Custom::updImplementation() {
2069     return updImpl().updImplementation();
2070 }
2071 
2072     // Constraint::CustomImpl
2073 
2074 // The Implementation object should already contain a pointer to this CustomImpl object.
2075 void Constraint::CustomImpl::
takeOwnershipOfImplementation(Custom::Implementation * userImpl)2076 takeOwnershipOfImplementation(Custom::Implementation* userImpl) {
2077     assert(!implementation); // you can only do this once!
2078     assert(userImpl);
2079     const Custom::ImplementationImpl& impImpl = userImpl->getImpl();
2080     assert(&impImpl.getCustomImpl() == this && !impImpl.isOwnerOfCustomImpl());
2081     implementation = userImpl;
2082 }
2083 
2084 
2085 
2086 //==============================================================================
2087 //                     CONSTRAINT::CUSTOM::IMPLEMENTATION
2088 //==============================================================================
2089 
2090 // Default constructor allocates a CustomImpl object and saves it in the
2091 // ImplementationImpl object. When this gets passed to a Custom handle we'll
2092 // turn over ownership of the CustomImpl object to the Custom handle.
Implementation(SimbodyMatterSubsystem & matter)2093 Constraint::Custom::Implementation::Implementation
2094    (SimbodyMatterSubsystem& matter)
2095 :   PIMPLHandle<Implementation,ImplementationImpl>
2096         (new ImplementationImpl(new CustomImpl()))
2097 {
2098     // We don't know the ConstraintIndex yet since this hasn't been adopted
2099     // by the MatterSubsystem.
2100     updImpl().updCustomImpl().setMyMatterSubsystem(matter, ConstraintIndex());
2101 }
2102 
Implementation(SimbodyMatterSubsystem & matter,int mp,int mv,int ma)2103 Constraint::Custom::Implementation::Implementation
2104    (SimbodyMatterSubsystem& matter, int mp, int mv, int ma)
2105 :   PIMPLHandle<Implementation,ImplementationImpl>
2106         (new ImplementationImpl(new CustomImpl(mp,mv,ma)))
2107 {
2108     // We don't know the ConstraintIndex yet since this hasn't been adopted
2109     // by the MatterSubsystem.
2110     updImpl().updCustomImpl().setMyMatterSubsystem(matter, ConstraintIndex());
2111 }
2112 
2113 const SimbodyMatterSubsystem&
getMatterSubsystem() const2114 Constraint::Custom::Implementation::getMatterSubsystem() const {
2115     return getImpl().getCustomImpl().getMyMatterSubsystem();
2116 }
2117 
invalidateTopologyCache() const2118 void Constraint::Custom::Implementation::invalidateTopologyCache() const {
2119     getImpl().getCustomImpl().invalidateTopologyCache();
2120 }
2121 
2122 Constraint::Custom::Implementation& Constraint::Custom::Implementation::
setDefaultNumConstraintEquations(int mp,int mv,int ma)2123 setDefaultNumConstraintEquations(int mp, int mv, int ma) {
2124     updImpl().updCustomImpl().setDefaultNumConstraintEquations(mp,mv,ma);
2125     return *this;
2126 }
2127 
2128 Constraint::Custom::Implementation& Constraint::Custom::Implementation::
setDisabledByDefault(bool shouldBeDisabled)2129 setDisabledByDefault(bool shouldBeDisabled) {
2130     updImpl().updCustomImpl().setDisabledByDefault(shouldBeDisabled);
2131     return *this;
2132 }
2133 
2134 ConstrainedBodyIndex Constraint::Custom::Implementation::
addConstrainedBody(const MobilizedBody & mb)2135 addConstrainedBody(const MobilizedBody& mb) {
2136     return updImpl().updCustomImpl().addConstrainedBody(mb);
2137 }
2138 ConstrainedMobilizerIndex Constraint::Custom::Implementation::
addConstrainedMobilizer(const MobilizedBody & mb)2139 addConstrainedMobilizer(const MobilizedBody& mb) {
2140     return updImpl().updCustomImpl().addConstrainedMobilizer(mb);
2141 }
2142 
2143 MobilizedBodyIndex Constraint::Custom::Implementation::
getMobilizedBodyIndexOfConstrainedBody(ConstrainedBodyIndex c) const2144 getMobilizedBodyIndexOfConstrainedBody(ConstrainedBodyIndex c) const {
2145     return getImpl().getCustomImpl().getMobilizedBodyIndexOfConstrainedBody(c);
2146 }
2147 MobilizedBodyIndex Constraint::Custom::Implementation::
getMobilizedBodyIndexOfConstrainedMobilizer(ConstrainedMobilizerIndex c) const2148 getMobilizedBodyIndexOfConstrainedMobilizer(ConstrainedMobilizerIndex c) const {
2149     return getImpl().getCustomImpl().getMobilizedBodyIndexOfConstrainedMobilizer(c);
2150 }
2151 
2152 Real Constraint::Custom::Implementation::
getOneQ(const State & state,const Array_<Real,ConstrainedQIndex> & constrainedQ,ConstrainedMobilizerIndex mobilizer,MobilizerQIndex whichQ) const2153 getOneQ(const State&                                state,
2154              const Array_<Real,ConstrainedQIndex>&  constrainedQ,
2155              ConstrainedMobilizerIndex              mobilizer,
2156              MobilizerQIndex                        whichQ) const
2157 {   return getImpl().getCustomImpl()
2158             .getOneQ(state,constrainedQ,mobilizer,whichQ); }
2159 
2160 Real Constraint::Custom::Implementation::
getOneQDot(const State & state,const Array_<Real,ConstrainedQIndex> & constrainedQDot,ConstrainedMobilizerIndex mobilizer,MobilizerQIndex whichQ) const2161 getOneQDot(const State&                                state,
2162                 const Array_<Real,ConstrainedQIndex>&  constrainedQDot,
2163                 ConstrainedMobilizerIndex              mobilizer,
2164                 MobilizerQIndex                        whichQ) const
2165 {   return getImpl().getCustomImpl()
2166             .getOneQDot(state,constrainedQDot,mobilizer,whichQ); }
2167 
2168 Real Constraint::Custom::Implementation::
getOneQDotDot(const State & state,const Array_<Real,ConstrainedQIndex> & constrainedQDotDot,ConstrainedMobilizerIndex mobilizer,MobilizerQIndex whichQ) const2169 getOneQDotDot(const State&                                state,
2170                    const Array_<Real,ConstrainedQIndex>&  constrainedQDotDot,
2171                    ConstrainedMobilizerIndex              mobilizer,
2172                    MobilizerQIndex                        whichQ) const
2173 {   return getImpl().getCustomImpl()
2174             .getOneQDotDot(state,constrainedQDotDot,mobilizer,whichQ); }
2175 
2176 
2177 Real Constraint::Custom::Implementation::
getOneU(const State & state,const Array_<Real,ConstrainedUIndex> & constrainedU,ConstrainedMobilizerIndex mobilizer,MobilizerUIndex whichU) const2178 getOneU(const State&                                state,
2179              const Array_<Real,ConstrainedUIndex>&  constrainedU,
2180              ConstrainedMobilizerIndex              mobilizer,
2181              MobilizerUIndex                        whichU) const
2182 {   return getImpl().getCustomImpl()
2183             .getOneU(state,constrainedU,mobilizer,whichU); }
2184 
2185 Real Constraint::Custom::Implementation::
getOneUDot(const State & state,const Array_<Real,ConstrainedUIndex> & constrainedUDot,ConstrainedMobilizerIndex mobilizer,MobilizerUIndex whichU) const2186 getOneUDot(const State&                                state,
2187                 const Array_<Real,ConstrainedUIndex>&  constrainedUDot,
2188                 ConstrainedMobilizerIndex              mobilizer,
2189                 MobilizerUIndex                        whichU) const
2190 {   return getImpl().getCustomImpl()
2191             .getOneUDot(state,constrainedUDot,mobilizer,whichU); }
2192 
2193 Real Constraint::Custom::Implementation::
getOneQFromState(const State & s,ConstrainedMobilizerIndex cmx,MobilizerQIndex mqx) const2194 getOneQFromState(const State& s, ConstrainedMobilizerIndex cmx, MobilizerQIndex mqx) const {
2195     return getImpl().getCustomImpl().getOneQFromState(s,cmx,mqx);
2196 }
2197 
2198 Real Constraint::Custom::Implementation::
getOneUFromState(const State & s,ConstrainedMobilizerIndex cmx,MobilizerUIndex mux) const2199 getOneUFromState(const State& s, ConstrainedMobilizerIndex cmx, MobilizerUIndex mux) const {
2200     return getImpl().getCustomImpl().getOneUFromState(s,cmx,mux);
2201 }
2202 
2203 
2204 Real Constraint::Custom::Implementation::
getOneQDotFromState(const State & s,ConstrainedMobilizerIndex cmx,MobilizerQIndex mqx) const2205 getOneQDotFromState(const State&                s,
2206                     ConstrainedMobilizerIndex   cmx,
2207                     MobilizerQIndex             mqx) const {
2208     return getImpl().getCustomImpl().getOneQDotFromState(s,cmx,mqx);
2209 }
2210 
2211 
2212 // Apply a generalized (mobility) force to a particular mobility of the given constrained body B,
2213 // adding it in to the appropriate slot of the mobilityForces vector.
2214 void Constraint::Custom::Implementation::
addInOneMobilityForce(const State & s,ConstrainedMobilizerIndex M,MobilizerUIndex whichU,Real fu,Array_<Real,ConstrainedUIndex> & mobilityForces) const2215 addInOneMobilityForce
2216    (const State& s, ConstrainedMobilizerIndex M, MobilizerUIndex whichU,
2217     Real fu, Array_<Real,ConstrainedUIndex>& mobilityForces) const
2218 {
2219     getImpl().getCustomImpl().addInOneMobilityForce(s,M,whichU,fu,mobilityForces);
2220 }
2221 
2222 void Constraint::Custom::Implementation::
addInOneQForce(const State & state,ConstrainedMobilizerIndex mobilizer,MobilizerQIndex whichQ,Real fq,Array_<Real,ConstrainedQIndex> & qForces) const2223 addInOneQForce
2224    (const State&                    state,
2225     ConstrainedMobilizerIndex       mobilizer,
2226     MobilizerQIndex                 whichQ,
2227     Real                            fq,
2228     Array_<Real,ConstrainedQIndex>& qForces) const
2229 {
2230     getImpl().getCustomImpl().addInOneQForce(state,mobilizer,whichQ,fq,qForces);
2231 }
2232 
2233 
2234 const Transform& Constraint::Custom::Implementation::
getBodyTransform(const Array_<Transform,ConstrainedBodyIndex> & allX_AB,ConstrainedBodyIndex bodyB) const2235 getBodyTransform
2236    (const Array_<Transform,ConstrainedBodyIndex>&   allX_AB,
2237     ConstrainedBodyIndex                            bodyB) const
2238 {   return getImpl().getCustomImpl().getBodyTransform(allX_AB,bodyB); }
2239 
2240 const SpatialVec& Constraint::Custom::Implementation::
getBodyVelocity(const Array_<SpatialVec,ConstrainedBodyIndex> & allV_AB,ConstrainedBodyIndex bodyB) const2241 getBodyVelocity
2242    (const Array_<SpatialVec,ConstrainedBodyIndex>&  allV_AB,
2243     ConstrainedBodyIndex                            bodyB) const
2244 {   return getImpl().getCustomImpl().getBodyVelocity(allV_AB,bodyB); }
2245 
2246 const SpatialVec& Constraint::Custom::Implementation::
getBodyAcceleration(const Array_<SpatialVec,ConstrainedBodyIndex> & allA_AB,ConstrainedBodyIndex bodyB) const2247 getBodyAcceleration
2248    (const Array_<SpatialVec,ConstrainedBodyIndex>&  allA_AB,
2249     ConstrainedBodyIndex                            bodyB) const
2250 {   return getImpl().getCustomImpl().getBodyAcceleration(allA_AB,bodyB); }
2251 
2252 const Transform&  Constraint::Custom::Implementation::
getBodyTransformFromState(const State & s,ConstrainedBodyIndex B) const2253 getBodyTransformFromState(const State& s, ConstrainedBodyIndex B) const
2254 {
2255     return getImpl().getCustomImpl().getBodyTransformFromState(s,B);
2256 }
2257 
2258 const SpatialVec& Constraint::Custom::Implementation::
getBodyVelocityFromState(const State & s,ConstrainedBodyIndex B) const2259 getBodyVelocityFromState(const State& s, ConstrainedBodyIndex B) const
2260 {
2261     return getImpl().getCustomImpl().getBodyVelocityFromState(s,B);
2262 }
2263 
2264 void Constraint::Custom::Implementation::
addInStationForce(const State & s,ConstrainedBodyIndex B,const Vec3 & p_B,const Vec3 & forceInA,Array_<SpatialVec,ConstrainedBodyIndex> & bodyForcesInA) const2265 addInStationForce
2266    (const State& s, ConstrainedBodyIndex B, const Vec3& p_B,
2267     const Vec3& forceInA,
2268     Array_<SpatialVec,ConstrainedBodyIndex>& bodyForcesInA) const
2269 {
2270     getImpl().getCustomImpl().addInStationForce(s,B,p_B,forceInA,bodyForcesInA);
2271 }
2272 
2273 void Constraint::Custom::Implementation::
addInBodyTorque(const State & s,ConstrainedBodyIndex B,const Vec3 & torqueInA,Array_<SpatialVec,ConstrainedBodyIndex> & bodyForcesInA) const2274 addInBodyTorque
2275    (const State& s, ConstrainedBodyIndex B,
2276     const Vec3& torqueInA,
2277     Array_<SpatialVec,ConstrainedBodyIndex>& bodyForcesInA) const
2278 {
2279     getImpl().getCustomImpl().addInBodyTorque(s,B,torqueInA,bodyForcesInA);
2280 }
2281 
2282 void Constraint::Custom::Implementation::
getMultipliers(const State & s,Array_<Real> & multipliers) const2283 getMultipliers(const State&  s,
2284                Array_<Real>& multipliers) const
2285 {
2286     int mp, mv, ma;
2287     const Constraint::CustomImpl& cimpl = getImpl().getCustomImpl();
2288     cimpl.getNumConstraintEquationsInUse(s,mp,mv,ma);
2289     multipliers.resize(mp+mv+ma);
2290     cimpl.getMultipliers(s, multipliers.size(), &multipliers[0]);
2291 }
2292 
2293 
2294 
2295 // Default implementations for ConstraintImpl virtuals throw "unimplemented"
2296 // exceptions. These shouldn't be called unless the concrete constraint has
2297 // given a non-zero value for mp, mv, and/or ma which is a promise to
2298 // implement the associated methods.
2299 
2300     // These must be defined if there are any position (holonomic) constraints
2301     // defined.
2302 
2303 void Constraint::Custom::Implementation::
calcPositionErrors(const State & state,const Array_<Transform,ConstrainedBodyIndex> & X_AB,const Array_<Real,ConstrainedQIndex> & constrainedQ,Array_<Real> & perr) const2304 calcPositionErrors
2305    (const State&                                    state,
2306     const Array_<Transform,ConstrainedBodyIndex>&   X_AB,
2307     const Array_<Real,     ConstrainedQIndex>&      constrainedQ,
2308     Array_<Real>&                                   perr) const
2309 {
2310     SimTK_THROW2(Exception::UnimplementedVirtualMethod,
2311         "Constraint::Custom::Implementation", "calcPositionErrors");
2312 }
2313 
2314 void Constraint::Custom::Implementation::
calcPositionDotErrors(const State & state,const Array_<SpatialVec,ConstrainedBodyIndex> & V_AB,const Array_<Real,ConstrainedQIndex> & constrainedQDot,Array_<Real> & pverr) const2315 calcPositionDotErrors
2316    (const State&                                    state,
2317     const Array_<SpatialVec,ConstrainedBodyIndex>&  V_AB,
2318     const Array_<Real,      ConstrainedQIndex>&     constrainedQDot,
2319     Array_<Real>&                                   pverr) const
2320 {
2321     SimTK_THROW2(Exception::UnimplementedVirtualMethod,
2322         "Constraint::Custom::Implementation", "realizePositionDotErrors");
2323 }
2324 
2325 void Constraint::Custom::Implementation::
calcPositionDotDotErrors(const State & state,const Array_<SpatialVec,ConstrainedBodyIndex> & A_AB,const Array_<Real,ConstrainedQIndex> & constrainedQDotDot,Array_<Real> & paerr) const2326 calcPositionDotDotErrors
2327    (const State&                                    state,
2328     const Array_<SpatialVec,ConstrainedBodyIndex>&  A_AB,
2329     const Array_<Real,      ConstrainedQIndex>&     constrainedQDotDot,
2330     Array_<Real>&                                   paerr) const
2331 {
2332     SimTK_THROW2(Exception::UnimplementedVirtualMethod,
2333         "Constraint::Custom::Implementation", "calcPositionDotDotErrors");
2334 }
2335 
2336 void Constraint::Custom::Implementation::
addInPositionConstraintForces(const State & state,const Array_<Real> & multipliers,Array_<SpatialVec,ConstrainedBodyIndex> & bodyForcesInA,Array_<Real,ConstrainedQIndex> & qForces) const2337 addInPositionConstraintForces
2338    (const State&                                state,
2339     const Array_<Real>&                         multipliers,
2340     Array_<SpatialVec,ConstrainedBodyIndex>&    bodyForcesInA,
2341     Array_<Real,ConstrainedQIndex>&             qForces) const
2342 {
2343     SimTK_THROW2(Exception::UnimplementedVirtualMethod,
2344         "Constraint::Custom::Implementation", "addInPositionConstraintForces");
2345 }
2346 
2347     // These must be defined if there are any velocity (nonholonomic)
2348     // constraints defined.
2349 
2350 void Constraint::Custom::Implementation::
calcVelocityErrors(const State & state,const Array_<SpatialVec,ConstrainedBodyIndex> & V_AB,const Array_<Real,ConstrainedUIndex> & constrainedU,Array_<Real> & verr) const2351 calcVelocityErrors
2352    (const State&                                    state,
2353     const Array_<SpatialVec,ConstrainedBodyIndex>&  V_AB,
2354     const Array_<Real,      ConstrainedUIndex>&     constrainedU,
2355     Array_<Real>&                                   verr) const
2356 {
2357     SimTK_THROW2(Exception::UnimplementedVirtualMethod,
2358         "Constraint::Custom::Implementation", "calcVelocityErrors");
2359 }
2360 
2361 
2362 void Constraint::Custom::Implementation::
calcVelocityDotErrors(const State & state,const Array_<SpatialVec,ConstrainedBodyIndex> & A_AB,const Array_<Real,ConstrainedUIndex> & constrainedUDot,Array_<Real> & vaerr) const2363 calcVelocityDotErrors
2364    (const State&                                    state,
2365     const Array_<SpatialVec,ConstrainedBodyIndex>&  A_AB,
2366     const Array_<Real,      ConstrainedUIndex>&     constrainedUDot,
2367     Array_<Real>&                                   vaerr) const
2368 {
2369     SimTK_THROW2(Exception::UnimplementedVirtualMethod,
2370         "Constraint::Custom::Implementation", "calcVelocityDotErrors");
2371 }
2372 
2373 
2374 void Constraint::Custom::Implementation::
addInVelocityConstraintForces(const State & state,const Array_<Real> & multipliers,Array_<SpatialVec,ConstrainedBodyIndex> & bodyForcesInA,Array_<Real,ConstrainedUIndex> & mobilityForces) const2375 addInVelocityConstraintForces
2376    (const State&                                state,
2377     const Array_<Real>&                         multipliers,
2378     Array_<SpatialVec,ConstrainedBodyIndex>&    bodyForcesInA,
2379     Array_<Real,ConstrainedUIndex>&             mobilityForces) const
2380 {
2381     SimTK_THROW2(Exception::UnimplementedVirtualMethod,
2382         "Constraint::Custom::Implementation", "addInVelocityConstraintForces");
2383 }
2384 
2385 
2386 
2387     // These must be defined if there are any acceleration-only constraints
2388     // defined.
2389 
2390 void Constraint::Custom::Implementation::
calcAccelerationErrors(const State & state,const Array_<SpatialVec,ConstrainedBodyIndex> & A_AB,const Array_<Real,ConstrainedUIndex> & constrainedUDot,Array_<Real> & aerr) const2391 calcAccelerationErrors
2392    (const State&                                    state,
2393     const Array_<SpatialVec,ConstrainedBodyIndex>&  A_AB,
2394     const Array_<Real,      ConstrainedUIndex>&     constrainedUDot,
2395     Array_<Real>&                                   aerr) const
2396 {
2397     SimTK_THROW2(Exception::UnimplementedVirtualMethod,
2398         "Constraint::Custom::Implementation", "calcAccelerationErrors");
2399 }
2400 
2401 void Constraint::Custom::Implementation::
addInAccelerationConstraintForces(const State & state,const Array_<Real> & multipliers,Array_<SpatialVec,ConstrainedBodyIndex> & bodyForcesInA,Array_<Real,ConstrainedUIndex> & mobilityForces) const2402 addInAccelerationConstraintForces
2403    (const State&                                state,
2404     const Array_<Real>&                         multipliers,
2405     Array_<SpatialVec,ConstrainedBodyIndex>&    bodyForcesInA,
2406     Array_<Real,ConstrainedUIndex>&             mobilityForces) const
2407 {
2408     SimTK_THROW2(Exception::UnimplementedVirtualMethod,
2409       "Constraint::Custom::Implementation", "addInAccelerationConstraintForces");
2410 }
2411 
2412 
2413 
2414 //==============================================================================
2415 //                       CONSTRAINT::COORDINATE COUPLER
2416 //==============================================================================
CoordinateCoupler(SimbodyMatterSubsystem & matter,const Function * function,const Array_<MobilizedBodyIndex> & coordBody,const Array_<MobilizerQIndex> & coordIndex)2417 Constraint::CoordinateCoupler::CoordinateCoupler
2418    (SimbodyMatterSubsystem&             matter,
2419     const Function*                     function,
2420     const Array_<MobilizedBodyIndex>&   coordBody,
2421     const Array_<MobilizerQIndex>&      coordIndex)
2422 :   Custom(new CoordinateCouplerImpl(matter, function, coordBody, coordIndex))
2423 {}
2424 
CoordinateCouplerImpl(SimbodyMatterSubsystem & matter,const Function * function,const Array_<MobilizedBodyIndex> & coordMobod,const Array_<MobilizerQIndex> & coordQIndex)2425 Constraint::CoordinateCouplerImpl::CoordinateCouplerImpl
2426    (SimbodyMatterSubsystem&             matter,
2427     const Function*                     function,
2428     const Array_<MobilizedBodyIndex>&   coordMobod,
2429     const Array_<MobilizerQIndex>&      coordQIndex)
2430 :   Implementation(matter, 1, 0, 0), function(function),
2431     coordBodies(coordMobod.size()), coordIndices(coordQIndex),
2432     temp(coordBodies.size()), referenceCount(new int[1])
2433 {
2434     assert(coordBodies.size() == coordIndices.size());
2435     assert(coordIndices.size() == function->getArgumentSize());
2436     assert(function->getMaxDerivativeOrder() >= 2);
2437     referenceCount[0] = 1;
2438     for (int i = 0; i < (int)coordBodies.size(); ++i) {
2439         const MobilizedBody& mobod = matter.getMobilizedBody(coordMobod[i]);
2440         coordBodies[i] =  addConstrainedMobilizer(mobod);
2441     }
2442 }
2443 
2444 void Constraint::CoordinateCouplerImpl::
calcPositionErrors(const State & s,const Array_<Transform,ConstrainedBodyIndex> & X_AB,const Array_<Real,ConstrainedQIndex> & constrainedQ,Array_<Real> & perr) const2445 calcPositionErrors
2446    (const State&                                    s,
2447     const Array_<Transform,ConstrainedBodyIndex>&   X_AB,
2448     const Array_<Real,     ConstrainedQIndex>&      constrainedQ,
2449     Array_<Real>&                                   perr) const
2450 {
2451     for (int i = 0; i < temp.size(); ++i)
2452         temp[i] = getOneQ(s, constrainedQ, coordBodies[i], coordIndices[i]);
2453     perr[0] = function->calcValue(temp);
2454 }
2455 
2456 void Constraint::CoordinateCouplerImpl::
calcPositionDotErrors(const State & s,const Array_<SpatialVec,ConstrainedBodyIndex> & V_AB,const Array_<Real,ConstrainedQIndex> & constrainedQDot,Array_<Real> & pverr) const2457 calcPositionDotErrors
2458    (const State&                                    s,
2459     const Array_<SpatialVec,ConstrainedBodyIndex>&  V_AB,
2460     const Array_<Real,      ConstrainedQIndex>&     constrainedQDot,
2461     Array_<Real>&                                   pverr) const
2462 {
2463     pverr[0] = 0;
2464     for (int i = 0; i < temp.size(); ++i)
2465         temp[i] = getOneQFromState(s, coordBodies[i], coordIndices[i]);
2466     Array_<int> components(1);
2467     for (int i = 0; i < temp.size(); ++i) {
2468         components[0] = i;
2469         pverr[0] += function->calcDerivative(components, temp)
2470                     * getOneQDot(s, constrainedQDot,
2471                                  coordBodies[i], coordIndices[i]);
2472     }
2473 }
2474 
2475 void Constraint::CoordinateCouplerImpl::
calcPositionDotDotErrors(const State & s,const Array_<SpatialVec,ConstrainedBodyIndex> & A_AB,const Array_<Real,ConstrainedQIndex> & constrainedQDotDot,Array_<Real> & paerr) const2476 calcPositionDotDotErrors
2477    (const State&                                    s,
2478     const Array_<SpatialVec,ConstrainedBodyIndex>&  A_AB,
2479     const Array_<Real,      ConstrainedQIndex>&     constrainedQDotDot,
2480     Array_<Real>&                                   paerr) const
2481 {
2482     paerr[0] = 0.0;
2483     for (int i = 0; i < temp.size(); ++i)
2484         temp[i] = getOneQFromState(s, coordBodies[i], coordIndices[i]);
2485 
2486     // TODO this could be made faster by using symmetry.
2487     Array_<int> components(2);
2488     for (int i = 0; i < temp.size(); ++i) {
2489         components[0] = i;
2490         Real qdoti = getOneQDotFromState(s, coordBodies[i], coordIndices[i]);
2491         for (int j = 0; j < temp.size(); ++j) {
2492             components[1] = j;
2493             Real qdotj = getOneQDotFromState(s, coordBodies[j], coordIndices[j]);
2494             paerr[0] += function->calcDerivative(components, temp)
2495                         * qdoti * qdotj;
2496         }
2497     }
2498 
2499     Array_<int> component(1);
2500     for (int i = 0; i < temp.size(); ++i) {
2501         component[0] = i;
2502         paerr[0] += function->calcDerivative(component, temp)
2503                     * getOneQDotDot(s, constrainedQDotDot,
2504                                     coordBodies[i], coordIndices[i]);
2505     }
2506 }
2507 
2508 void Constraint::CoordinateCouplerImpl::
addInPositionConstraintForces(const State & s,const Array_<Real> & multipliers,Array_<SpatialVec,ConstrainedBodyIndex> & bodyForces,Array_<Real,ConstrainedQIndex> & qForces) const2509 addInPositionConstraintForces
2510    (const State&                                s,
2511     const Array_<Real>&                         multipliers,
2512     Array_<SpatialVec,ConstrainedBodyIndex>&    bodyForces,
2513     Array_<Real,ConstrainedQIndex>&             qForces) const
2514 {
2515     assert(multipliers.size() == 1);
2516     assert(bodyForces.size() == 0);
2517 
2518     const Real lambda = multipliers[0];
2519 
2520     for (int i = 0; i < temp.size(); ++i)
2521         temp[i] = getOneQFromState(s, coordBodies[i], coordIndices[i]);
2522 
2523     Array_<int> components(1);
2524     for (int i = 0; i < temp.size(); ++i) {
2525         components[0] = i;
2526         const Real fq = lambda * function->calcDerivative(components, temp);
2527         addInOneQForce(s, coordBodies[i], coordIndices[i], fq, qForces);
2528     }
2529 }
2530 
2531 
2532 
2533 //==============================================================================
2534 //                          CONSTRAINT::SPEED COUPLER
2535 //==============================================================================
SpeedCoupler(SimbodyMatterSubsystem & matter,const Function * function,const Array_<MobilizedBodyIndex> & speedBody,const Array_<MobilizerUIndex> & speedIndex)2536 Constraint::SpeedCoupler::SpeedCoupler
2537    (SimbodyMatterSubsystem&             matter,
2538     const Function*                     function,
2539     const Array_<MobilizedBodyIndex>&   speedBody,
2540     const Array_<MobilizerUIndex>&      speedIndex)
2541 :   Custom(new SpeedCouplerImpl(matter, function, speedBody, speedIndex,
2542                                 Array_<MobilizedBodyIndex>(),
2543                                 Array_<MobilizerQIndex>())) {}
2544 
SpeedCoupler(SimbodyMatterSubsystem & matter,const Function * function,const Array_<MobilizedBodyIndex> & speedBody,const Array_<MobilizerUIndex> & speedIndex,const Array_<MobilizedBodyIndex> & coordBody,const Array_<MobilizerQIndex> & coordIndex)2545 Constraint::SpeedCoupler::SpeedCoupler
2546    (SimbodyMatterSubsystem&             matter,
2547     const Function*                     function,
2548     const Array_<MobilizedBodyIndex>&   speedBody,
2549     const Array_<MobilizerUIndex>&      speedIndex,
2550     const Array_<MobilizedBodyIndex>&   coordBody,
2551     const Array_<MobilizerQIndex>&      coordIndex)
2552 :   Custom(new SpeedCouplerImpl(matter, function, speedBody, speedIndex,
2553                                 coordBody, coordIndex)) {}
2554 
SpeedCouplerImpl(SimbodyMatterSubsystem & matter,const Function * function,const Array_<MobilizedBodyIndex> & speedBody,const Array_<MobilizerUIndex> & speedIndex,const Array_<MobilizedBodyIndex> & coordBody,const Array_<MobilizerQIndex> & coordIndex)2555 Constraint::SpeedCouplerImpl::SpeedCouplerImpl
2556    (SimbodyMatterSubsystem& matter,
2557     const Function* function,
2558     const Array_<MobilizedBodyIndex>& speedBody,
2559     const Array_<MobilizerUIndex>& speedIndex,
2560     const Array_<MobilizedBodyIndex>& coordBody,
2561     const Array_<MobilizerQIndex>& coordIndex)
2562 :   Implementation(matter, 0, 1, 0), function(function),
2563     speedBodies(speedBody.size()), speedIndices(speedIndex),
2564     coordBodies(coordBody), coordIndices(coordIndex),
2565     temp(speedBody.size()+coordBody.size()), referenceCount(new int[1])
2566 {
2567     assert(speedBodies.size() == speedIndices.size());
2568     assert(coordBodies.size() == coordIndices.size());
2569     assert(temp.size() == function->getArgumentSize());
2570     assert(function->getMaxDerivativeOrder() >= 2);
2571 
2572     referenceCount[0] = 1;
2573     for (int i = 0; i < (int)speedBodies.size(); ++i) {
2574         const MobilizedBody& mobod = matter.getMobilizedBody(speedBody[i]);
2575         speedBodies[i] = addConstrainedMobilizer(mobod);
2576     }
2577 }
2578 
2579 // Constraint is f(q,u)=0, i.e. verr=f(q,u).
2580 void Constraint::SpeedCouplerImpl::
calcVelocityErrors(const State & s,const Array_<SpatialVec,ConstrainedBodyIndex> & V_AB,const Array_<Real,ConstrainedUIndex> & constrainedU,Array_<Real> & verr) const2581 calcVelocityErrors
2582    (const State&                                    s,
2583     const Array_<SpatialVec,ConstrainedBodyIndex>&  V_AB,
2584     const Array_<Real,      ConstrainedUIndex>&     constrainedU,
2585     Array_<Real>&                                   verr) const
2586 {
2587     for (int i = 0; i < (int) speedBodies.size(); ++i)
2588         temp[i] = getOneU(s, constrainedU, speedBodies[i], speedIndices[i]);
2589     for (int i = 0; i < (int) coordBodies.size(); ++i)
2590         temp[i+speedBodies.size()] =
2591             getMatterSubsystem().getMobilizedBody(coordBodies[i])
2592                                 .getOneQ(s, coordIndices[i]);
2593     verr[0] = function->calcValue(temp);
2594 }
2595 
2596 // d verr / dt = (df/du)*udot + (df/dq)*qdot.
2597 void Constraint::SpeedCouplerImpl::
calcVelocityDotErrors(const State & s,const Array_<SpatialVec,ConstrainedBodyIndex> & A_AB,const Array_<Real,ConstrainedUIndex> & constrainedUDot,Array_<Real> & vaerr) const2598 calcVelocityDotErrors
2599    (const State&                                    s,
2600     const Array_<SpatialVec,ConstrainedBodyIndex>&  A_AB,
2601     const Array_<Real,      ConstrainedUIndex>&     constrainedUDot,
2602     Array_<Real>&                                   vaerr) const
2603 {
2604     for (int i = 0; i < (int)speedBodies.size(); ++i)
2605         temp[i] = getOneUFromState(s, speedBodies[i], speedIndices[i]);
2606     for (int i = 0; i < (int)coordBodies.size(); ++i) {
2607         const Real q = getMatterSubsystem().getMobilizedBody(coordBodies[i])
2608                                            .getOneQ(s, coordIndices[i]);
2609         temp[i+speedBodies.size()] = q;
2610     }
2611 
2612     Array_<int> components(1);
2613     vaerr[0] = 0;
2614     // Differentiate the u-dependent terms here.
2615     for (int i = 0; i < (int)speedBodies.size(); ++i) {
2616         components[0] = i;
2617         vaerr[0] += function->calcDerivative(components, temp)
2618                     * getOneUDot(s, constrainedUDot,
2619                                  speedBodies[i], speedIndices[i]);
2620     }
2621     // Differentiate the q-dependent terms here.
2622     for (int i = 0; i < (int)coordBodies.size(); ++i) {
2623         components[0] = i + speedBodies.size();
2624         const Real qdot = getMatterSubsystem().getMobilizedBody(coordBodies[i])
2625                                               .getOneQDot(s, coordIndices[i]);
2626         vaerr[0] += function->calcDerivative(components, temp) * qdot;
2627     }
2628 }
2629 
2630 // Force is (df/du)*lambda.
2631 void Constraint::SpeedCouplerImpl::
addInVelocityConstraintForces(const State & s,const Array_<Real> & multipliers,Array_<SpatialVec,ConstrainedBodyIndex> & bodyForces,Array_<Real,ConstrainedUIndex> & mobilityForces) const2632 addInVelocityConstraintForces
2633    (const State&                                s,
2634     const Array_<Real>&                         multipliers,
2635     Array_<SpatialVec,ConstrainedBodyIndex>&    bodyForces,
2636     Array_<Real,ConstrainedUIndex>&             mobilityForces) const
2637 {
2638     assert(multipliers.size() == 1);
2639     const Real lambda = multipliers[0];
2640 
2641     for (int i = 0; i < (int) speedBodies.size(); ++i)
2642         temp[i] = getOneUFromState(s, speedBodies[i], speedIndices[i]);
2643     for (int i = 0; i < (int) coordBodies.size(); ++i)
2644         temp[i+speedBodies.size()] =
2645             getMatterSubsystem().getMobilizedBody(coordBodies[i])
2646                                 .getOneQ(s, coordIndices[i]);
2647 
2648     Array_<int> components(1);
2649     // Only the u-dependent terms generate forces.
2650     for (int i = 0; i < (int) speedBodies.size(); ++i) {
2651         components[0] = i;
2652         const Real force = function->calcDerivative(components, temp)
2653                            * lambda;
2654         addInOneMobilityForce(s, speedBodies[i], speedIndices[i],
2655                               force, mobilityForces);
2656     }
2657 }
2658 
2659 
2660 
2661 //==============================================================================
2662 //                        CONSTRAINT::PRESCRIBED MOTION
2663 //==============================================================================
PrescribedMotion(SimbodyMatterSubsystem & matter,const Function * function,MobilizedBodyIndex coordBody,MobilizerQIndex coordIndex)2664 Constraint::PrescribedMotion::PrescribedMotion
2665    (SimbodyMatterSubsystem&     matter,
2666     const Function*             function,
2667     MobilizedBodyIndex          coordBody,
2668     MobilizerQIndex             coordIndex)
2669 :   Custom(new PrescribedMotionImpl(matter, function, coordBody, coordIndex)) {}
2670 
PrescribedMotionImpl(SimbodyMatterSubsystem & matter,const Function * function,MobilizedBodyIndex coordBody,MobilizerQIndex coordIndex)2671 Constraint::PrescribedMotionImpl::PrescribedMotionImpl
2672    (SimbodyMatterSubsystem& matter,
2673     const Function* function,
2674     MobilizedBodyIndex coordBody,
2675     MobilizerQIndex coordIndex)
2676 :   Implementation(matter, 1, 0, 0), function(function),
2677     coordIndex(coordIndex), temp(1), referenceCount(new int[1])
2678 {
2679     assert(function->getArgumentSize() == 1);
2680     assert(function->getMaxDerivativeOrder() >= 2);
2681 
2682     referenceCount[0] = 1;
2683     const MobilizedBody& mobod = matter.getMobilizedBody(coordBody);
2684     this->coordBody = addConstrainedMobilizer(mobod);
2685 }
2686 
2687 void Constraint::PrescribedMotionImpl::
calcPositionErrors(const State & s,const Array_<Transform,ConstrainedBodyIndex> & X_AB,const Array_<Real,ConstrainedQIndex> & constrainedQ,Array_<Real> & perr) const2688 calcPositionErrors
2689    (const State&                                    s,
2690     const Array_<Transform,ConstrainedBodyIndex>&   X_AB,
2691     const Array_<Real,     ConstrainedQIndex>&      constrainedQ,
2692     Array_<Real>&                                   perr) const
2693 {
2694     temp[0] = s.getTime();
2695     perr[0] = getOneQ(s, constrainedQ, coordBody, coordIndex)
2696               - function->calcValue(temp);
2697 }
2698 
2699 void Constraint::PrescribedMotionImpl::
calcPositionDotErrors(const State & s,const Array_<SpatialVec,ConstrainedBodyIndex> & V_AB,const Array_<Real,ConstrainedQIndex> & constrainedQDot,Array_<Real> & pverr) const2700 calcPositionDotErrors
2701    (const State&                                    s,
2702     const Array_<SpatialVec,ConstrainedBodyIndex>&  V_AB,
2703     const Array_<Real,      ConstrainedQIndex>&     constrainedQDot,
2704     Array_<Real>&                                   pverr) const
2705 {
2706     temp[0] = s.getTime();
2707     Array_<int> components(1, 0); // i.e., components={0}
2708     pverr[0] = getOneQDot(s, constrainedQDot, coordBody, coordIndex)
2709                - function->calcDerivative(components, temp);
2710 }
2711 
2712 void Constraint::PrescribedMotionImpl::
calcPositionDotDotErrors(const State & s,const Array_<SpatialVec,ConstrainedBodyIndex> & A_AB,const Array_<Real,ConstrainedQIndex> & constrainedQDotDot,Array_<Real> & paerr) const2713 calcPositionDotDotErrors
2714    (const State&                                    s,
2715     const Array_<SpatialVec,ConstrainedBodyIndex>&  A_AB,
2716     const Array_<Real,      ConstrainedQIndex>&     constrainedQDotDot,
2717     Array_<Real>&                                   paerr) const
2718 {
2719     temp[0] = s.getTime();
2720     Array_<int> components(2, 0); // i.e., components={0,0}
2721     paerr[0] = getOneQDotDot(s, constrainedQDotDot, coordBody, coordIndex)
2722                - function->calcDerivative(components, temp);
2723 }
2724 
2725 void Constraint::PrescribedMotionImpl::
addInPositionConstraintForces(const State & s,const Array_<Real> & multipliers,Array_<SpatialVec,ConstrainedBodyIndex> & bodyForces,Array_<Real,ConstrainedQIndex> & qForces) const2726 addInPositionConstraintForces
2727    (const State&                                s,
2728     const Array_<Real>&                         multipliers,
2729     Array_<SpatialVec,ConstrainedBodyIndex>&    bodyForces,
2730     Array_<Real,ConstrainedQIndex>&             qForces) const
2731 {
2732     const Real fq = multipliers[0];
2733     addInOneQForce(s, coordBody, coordIndex, fq, qForces);
2734 }
2735 
2736 
2737 
2738 //==============================================================================
2739 //                              CONSTRAINT IMPL
2740 //==============================================================================
2741 
2742 
2743 // =============================================================================
2744 //                              REALIZE TOPOLOGY
2745 // =============================================================================
realizeTopology(State & s) const2746 void ConstraintImpl::realizeTopology(State& s) const {
2747     // Calculate the relevant Subtree. There might not be any Constrained
2748     // Bodies here but we want to make sure we have a properly initialized
2749     // empty Subtree in that case.
2750     mySubtree.clear();
2751     mySubtree.setSimbodyMatterSubsystem(getMyMatterSubsystem());
2752     for (ConstrainedBodyIndex b(0); b < myConstrainedBodies.size(); ++b)
2753         mySubtree.addTerminalBody(myConstrainedBodies[b]);
2754     mySubtree.realizeTopology();
2755 
2756     myAncestorBodyIsNotGround = myConstrainedBodies.size()
2757                                 && mySubtree.getAncestorMobilizedBodyIndex() != GroundIndex;
2758 
2759     // If the Ancestor isn't Ground, reserve slots in the State cache
2760     // ancestor constrained body pools for each constrained body here
2761     // except the Ancestor (which may or may not be a constrained body).
2762     if (myAncestorBodyIsNotGround) {
2763         myPoolIndex.resize(myConstrainedBodies.size());
2764         for (ConstrainedBodyIndex b(0); b < myConstrainedBodies.size(); ++b) {
2765             if (myConstrainedBodies[b] == mySubtree.getAncestorMobilizedBodyIndex())
2766                 myPoolIndex[b].invalidate();
2767             else myPoolIndex[b] =
2768                 getMyMatterSubsystemRep().allocateNextAncestorConstrainedBodyPoolSlot();
2769         }
2770     } else
2771         myPoolIndex.clear(); // ancestor is Ground; no need for pool entries
2772 
2773     realizeTopologyVirtual(s); // delegate to concrete constraint
2774 }
2775 
2776 
2777 
2778 // =============================================================================
2779 //                               REALIZE MODEL
2780 // =============================================================================
realizeModel(State & s) const2781 void ConstraintImpl::realizeModel(State& s) const
2782 {
2783     SimTK_ASSERT(subsystemTopologyHasBeenRealized(),
2784         "ConstraintImpl::realizeModel() can't be called until after realizeToplogy().");
2785 
2786     realizeModelVirtual(s); // delegate to concrete constraint
2787 }
2788 
2789 
2790 
2791 // =============================================================================
2792 //                              REALIZE INSTANCE
2793 // =============================================================================
2794 // There are several tasks here that can be performed now that we have values
2795 // for the Instance stage state variables (including the enable/disable flag
2796 // for Constraints):
2797 // (1) Count up the number of holonomic, nonholonomic, and acceleration-only
2798 //     constraint equations to be contributed by each Constraint, and assign
2799 //     corresponding slots in constraint-equation ordered arrays, such as the
2800 //     State's constraint error arrays.
2801 // (2) Count up the number of constrained bodies, mobilizers, and corresoponding
2802 //     constrained mobilities to be affected by each Constraint, and assign
2803 //     corresponding slots for use in "pools" that are indexed by these.
2804 // (3) Above we assigned q's and u's to each mobilizer and stored the results in
2805 //     the Model cache, now we can determine which of those q's and u's are
2806 //     involved in each constraint. We need to collect up both the set of
2807 //     directly-constrained q's and u's resulting from ConstrainedMobilizers,
2808 //     and indirectly-constrained ones arising from their effects on
2809 //     ConstrainedBodies. Together we call those "participating q's" and
2810 //     "participating u's" (or "participating mobilities").
2811 // The results of these computations goes in the Instance cache.
2812 
realizeInstance(const State & s) const2813 void ConstraintImpl::realizeInstance(const State& s) const {
2814     const SimbodyMatterSubsystemRep& matter = getMyMatterSubsystemRep();
2815     const SBInstanceVars& instanceVars  = matter.getInstanceVars(s);
2816     SBInstanceCache&      ic = matter.updInstanceCache(s);
2817 
2818     const int ncb = getNumConstrainedBodies();
2819     const int ncm = getNumConstrainedMobilizers();
2820 
2821     // Note that there is a "per constraint info" object for every declared
2822     // constraint, whether enabled or not.
2823     SBInstancePerConstraintInfo& cInfo =
2824         ic.updConstraintInstanceInfo(myConstraintIndex);
2825 
2826     cInfo.clear();
2827     cInfo.allocateConstrainedMobilizerInstanceInfo(ncm);
2828 
2829     // We're in the process of counting up constraint equations in the
2830     // totalN...Constraints variables; on entry they are set to the number
2831     // we've seen so far and we'll increment them here to add in the
2832     // contributions from this Constraint.
2833 
2834     if (isDisabled(s)) {
2835         // Just to be neat, we'll assign zero-width segments where our slots
2836         // would have gone if this constraint were enabled.
2837         cInfo.holoErrSegment    =
2838             Segment(0,ic.totalNHolonomicConstraintEquationsInUse);
2839         cInfo.nonholoErrSegment =
2840             Segment(0,ic.totalNNonholonomicConstraintEquationsInUse);
2841         cInfo.accOnlyErrSegment =
2842             Segment(0,ic.totalNAccelerationOnlyConstraintEquationsInUse);
2843 
2844         cInfo.consBodySegment      = Segment(0, ic.totalNConstrainedBodiesInUse);
2845         cInfo.consMobilizerSegment = Segment(0, ic.totalNConstrainedMobilizersInUse);
2846         cInfo.consQSegment = Segment(0, ic.totalNConstrainedQInUse);
2847         cInfo.consUSegment = Segment(0, ic.totalNConstrainedUInUse);
2848         return;
2849     }
2850 
2851     // This constraint is enabled.
2852 
2853     // These count just the primary contraint equations, not their time
2854     // derivatives.
2855     int mHolo, mNonholo, mAccOnly;
2856     calcNumConstraintEquationsInUse(s, mHolo, mNonholo, mAccOnly);
2857 
2858     // Must allocate space for primary constraint equations & time derivatives.
2859     //                                length         offset
2860     cInfo.holoErrSegment    = Segment(mHolo,    ic.totalNHolonomicConstraintEquationsInUse);
2861     cInfo.nonholoErrSegment = Segment(mNonholo, ic.totalNNonholonomicConstraintEquationsInUse);
2862     cInfo.accOnlyErrSegment = Segment(mAccOnly, ic.totalNAccelerationOnlyConstraintEquationsInUse);
2863 
2864     ic.totalNHolonomicConstraintEquationsInUse        += mHolo;
2865     ic.totalNNonholonomicConstraintEquationsInUse     += mNonholo;
2866     ic.totalNAccelerationOnlyConstraintEquationsInUse += mAccOnly;
2867 
2868     cInfo.consBodySegment      = Segment(ncb, ic.totalNConstrainedBodiesInUse);
2869     cInfo.consMobilizerSegment = Segment(ncm, ic.totalNConstrainedMobilizersInUse);
2870     ic.totalNConstrainedBodiesInUse     += ncb;
2871     ic.totalNConstrainedMobilizersInUse += ncm;
2872 
2873     // At this point we can find out how many q's and u's are associated with
2874     // each of the constrained mobilizers. We'll create packed arrays of q's and
2875     // u's ordered corresponding to the ConstrainedMobilizerIndices. We'll
2876     // record these in the InstanceCache, by storing the ConstrainedQIndex and
2877     // ConstrainedUIndex of the lowest-numbered coordinate and mobility
2878     // associated with each of the ConstrainedMobilizers, along with the number
2879     // of q's and u's.
2880 
2881     for (ConstrainedMobilizerIndex cmx(0); cmx < ncm; ++cmx) {
2882         SBInstancePerConstrainedMobilizerInfo& mInfo =
2883             cInfo.updConstrainedMobilizerInstanceInfo(cmx);
2884 
2885         const MobilizedBodyIndex mbx =
2886             getMobilizedBodyIndexOfConstrainedMobilizer(cmx);
2887         QIndex qix; int nq;
2888         UIndex uix; int nu;
2889         matter.findMobilizerQs(s,mbx,qix,nq);
2890         matter.findMobilizerUs(s,mbx,uix,nu);
2891         mInfo.nQInUse = nq;
2892         if (nq) {
2893             mInfo.firstConstrainedQIndex = cInfo.addConstrainedQ(qix);
2894             for (int i=1; i<nq; ++i) cInfo.addConstrainedQ(QIndex(qix+i));
2895         }
2896         mInfo.nUInUse = nu;
2897         if (nu) {
2898             mInfo.firstConstrainedUIndex = cInfo.addConstrainedU(uix);
2899             for (int i=1; i<nu; ++i) cInfo.addConstrainedU(UIndex(uix+i));
2900         }
2901     }
2902 
2903     // Now we can assign slots for Qs and Us.
2904     const int ncq = cInfo.getNumConstrainedQ();
2905     const int ncu = cInfo.getNumConstrainedU();
2906     cInfo.consQSegment = Segment(ncq, ic.totalNConstrainedQInUse);
2907     cInfo.consUSegment = Segment(ncu, ic.totalNConstrainedUInUse);
2908     ic.totalNConstrainedQInUse += ncq;
2909     ic.totalNConstrainedUInUse += ncu;
2910 
2911     // Now collect all the participating mobilities. This includes the
2912     // constrained mobilities as well as every q and u that can affect the
2913     // constraint equations which involve constrained bodies. At the end we'll
2914     // sort this list by subsystem QIndex/UIndex and remove duplicates.
2915     cInfo.participatingQ = cInfo.constrainedQ;
2916     cInfo.participatingU = cInfo.constrainedU;
2917 
2918     const Array_<MobilizedBodyIndex>& bodies = mySubtree.getAllBodies();
2919     for (int b=1; b<(int)bodies.size(); ++b) { // skip the Ancestor body 0
2920         QIndex qix; int nq;
2921         UIndex uix; int nu;
2922         matter.findMobilizerQs(s,bodies[b],qix,nq);
2923         matter.findMobilizerUs(s,bodies[b],uix,nu);
2924         for (int i=0; i<nq; ++i) cInfo.participatingQ.push_back(QIndex(qix+i));
2925         for (int i=0; i<nu; ++i) cInfo.participatingU.push_back(UIndex(uix+i));
2926     }
2927 
2928     // Caution: std::unique does not automatically shorten the original list.
2929     std::sort(cInfo.participatingQ.begin(), cInfo.participatingQ.end());
2930     Array_<QIndex>::iterator newEnd =
2931         std::unique(cInfo.participatingQ.begin(), cInfo.participatingQ.end());
2932     cInfo.participatingQ.erase(newEnd, cInfo.participatingQ.end());
2933 
2934     realizeInstanceVirtual(s); // delegate to concrete constraint
2935 }
2936 
2937 
2938 
2939 // =============================================================================
2940 //                                REALIZE TIME
2941 // =============================================================================
realizeTime(const SBStateDigest & sbs) const2942 void ConstraintImpl::realizeTime(const SBStateDigest& sbs) const {
2943     const SBInstanceVars& instanceVars  = sbs.getInstanceVars();
2944     if (instanceVars.constraintIsDisabled[myConstraintIndex]) return;
2945 
2946     realizeTimeVirtual(sbs.getState()); // nothing to do in the base class
2947 }
2948 
2949 
2950 
2951 // =============================================================================
2952 //                             REALIZE POSITION
2953 // =============================================================================
realizePosition(const SBStateDigest & sbs) const2954 void ConstraintImpl::realizePosition(const SBStateDigest& sbs) const {
2955     const SBInstanceVars& instanceVars  = sbs.getInstanceVars();
2956     if (instanceVars.constraintIsDisabled[myConstraintIndex]) return;
2957     realizePositionVirtual(sbs.getState()); // delegate to concrete constraint
2958 }
2959 
2960 
2961 
2962 // =============================================================================
2963 //                              REALIZE VELOCITY
2964 // =============================================================================
realizeVelocity(const SBStateDigest & sbs) const2965 void ConstraintImpl::realizeVelocity(const SBStateDigest& sbs) const {
2966     const SBInstanceVars& instanceVars  = sbs.getInstanceVars();
2967     if (instanceVars.constraintIsDisabled[myConstraintIndex]) return;
2968     realizeVelocityVirtual(sbs.getState()); // delegate to concrete constraint
2969 }
2970 
2971 
2972 
2973 // =============================================================================
2974 //                              REALIZE DYNAMICS
2975 // =============================================================================
realizeDynamics(const SBStateDigest & sbs) const2976 void ConstraintImpl::realizeDynamics(const SBStateDigest& sbs) const {
2977     const SBInstanceVars& instanceVars  = sbs.getInstanceVars();
2978     if (instanceVars.constraintIsDisabled[myConstraintIndex]) return;
2979     realizeDynamicsVirtual(sbs.getState()); // delegate to concrete constraint
2980 }
2981 
2982 
2983 
2984 // =============================================================================
2985 //                            REALIZE ACCELERATION
2986 // =============================================================================
realizeAcceleration(const SBStateDigest & sbs) const2987 void ConstraintImpl::realizeAcceleration(const SBStateDigest& sbs) const {
2988     const SBInstanceVars& instanceVars  = sbs.getInstanceVars();
2989     if (instanceVars.constraintIsDisabled[myConstraintIndex]) return;
2990     realizeAccelerationVirtual(sbs.getState()); // delegate to concrete constraint
2991 }
2992 
2993 
2994 
2995 // =============================================================================
2996 //                             REALIZE REPORT
2997 // =============================================================================
realizeReport(const State & s) const2998 void ConstraintImpl::realizeReport(const State& s) const {
2999     if (isDisabled(s)) return;
3000     realizeReportVirtual(s); // nothing to do in the base class
3001 }
3002 
3003 
3004 
invalidateTopologyCache() const3005 void ConstraintImpl::invalidateTopologyCache() const {
3006     if (myMatterSubsystemRep)
3007         myMatterSubsystemRep->invalidateSubsystemTopologyCache();
3008 }
3009 
subsystemTopologyHasBeenRealized() const3010 bool ConstraintImpl::subsystemTopologyHasBeenRealized() const {
3011     return myMatterSubsystemRep && myMatterSubsystemRep->subsystemTopologyHasBeenRealized();
3012 }
3013 
setMyMatterSubsystem(SimbodyMatterSubsystem & matter,ConstraintIndex id)3014 void ConstraintImpl::setMyMatterSubsystem
3015    (SimbodyMatterSubsystem& matter, ConstraintIndex id)
3016 {
3017     // If this is already set it has to be to the same MatterSubsystem.
3018     assert(!myMatterSubsystemRep || myMatterSubsystemRep == &matter.getRep());
3019     myMatterSubsystemRep = &matter.updRep();
3020     myConstraintIndex = id;
3021 }
3022 
3023 const SimbodyMatterSubsystem&
getMyMatterSubsystem() const3024 ConstraintImpl::getMyMatterSubsystem() const {
3025     return getMyMatterSubsystemRep().getMySimbodyMatterSubsystemHandle();
3026 }
3027 
isInSameSubsystem(const MobilizedBody & body) const3028 bool ConstraintImpl::isInSameSubsystem(const MobilizedBody& body) const {
3029     return isInSubsystem() && body.isInSubsystem()
3030         && getMyMatterSubsystem().isSameSubsystem(body.getMatterSubsystem());
3031 }
3032 
3033 const MobilizedBody&
getMobilizedBodyFromConstrainedMobilizer(ConstrainedMobilizerIndex M) const3034 ConstraintImpl::getMobilizedBodyFromConstrainedMobilizer(ConstrainedMobilizerIndex M) const {
3035     SimTK_ASSERT(subsystemTopologyHasBeenRealized(),
3036         "Constrained mobilizers are not available until Topology stage has been realized.");
3037     return getMyMatterSubsystemRep().getMobilizedBody(myConstrainedMobilizers[M]);
3038 }
3039 
3040 const MobilizedBody&
getMobilizedBodyFromConstrainedBody(ConstrainedBodyIndex B) const3041 ConstraintImpl::getMobilizedBodyFromConstrainedBody(ConstrainedBodyIndex B) const {
3042     SimTK_ASSERT(subsystemTopologyHasBeenRealized(),
3043         "Constrained bodies are not available until Topology stage has been realized.");
3044     return getMyMatterSubsystemRep().getMobilizedBody(myConstrainedBodies[B]);
3045 }
3046 
3047 const MobilizedBody&
getAncestorMobilizedBody() const3048 ConstraintImpl::getAncestorMobilizedBody() const {
3049     SimTK_ASSERT(subsystemTopologyHasBeenRealized(),
3050         "The ancestor body is not available until Topology stage has been realized.");
3051     return getMyMatterSubsystemRep().getMobilizedBody(mySubtree.getAncestorMobilizedBodyIndex()); ;
3052 }
3053 
getOneQFromState(const State & s,ConstrainedMobilizerIndex cmx,MobilizerQIndex whichQ) const3054 Real ConstraintImpl::getOneQFromState
3055    (const State& s, ConstrainedMobilizerIndex cmx, MobilizerQIndex whichQ) const
3056 {
3057     const QIndex qx = getQIndexOfConstrainedQ(s, getConstrainedQIndex(s, cmx, whichQ));
3058     return getMyMatterSubsystemRep().getQ(s)[qx];
3059 }
3060 
getOneUFromState(const State & s,ConstrainedMobilizerIndex cmx,MobilizerUIndex whichU) const3061 Real ConstraintImpl::getOneUFromState
3062    (const State& s, ConstrainedMobilizerIndex cmx, MobilizerUIndex whichU) const
3063 {
3064     const UIndex ux = getUIndexOfConstrainedU(s, getConstrainedUIndex(s, cmx, whichU));
3065     return getMyMatterSubsystemRep().getU(s)[ux];
3066 }
3067 
getOneQDotFromState(const State & s,ConstrainedMobilizerIndex cmx,MobilizerQIndex whichQ) const3068 Real ConstraintImpl::getOneQDotFromState
3069    (const State&                s,
3070     ConstrainedMobilizerIndex   cmx,
3071     MobilizerQIndex             whichQ) const
3072 {
3073     const QIndex qx = getQIndexOfConstrainedQ
3074                                 (s, getConstrainedQIndex(s, cmx, whichQ));
3075     const SimbodyMatterSubsystemRep& matter = getMyMatterSubsystemRep();
3076     return matter.getQDot(s)[qx];
3077 }
3078 
getOneUDotFromState(const State & s,ConstrainedMobilizerIndex cmx,MobilizerUIndex whichU) const3079 Real ConstraintImpl::getOneUDotFromState
3080    (const State&                s,
3081     ConstrainedMobilizerIndex   cmx,
3082     MobilizerUIndex             whichU) const
3083 {
3084     const UIndex ux = getUIndexOfConstrainedU
3085                                 (s, getConstrainedUIndex(s, cmx, whichU));
3086     const SimbodyMatterSubsystemRep& matter = getMyMatterSubsystemRep();
3087     return matter.getUDot(s)[ux];
3088 }
3089 
3090 
getOneQDotDotFromState(const State & s,ConstrainedMobilizerIndex cmx,MobilizerQIndex whichQ) const3091 Real ConstraintImpl::getOneQDotDotFromState
3092    (const State&                s,
3093     ConstrainedMobilizerIndex   cmx,
3094     MobilizerQIndex             whichQ) const
3095 {
3096     const QIndex qx = getQIndexOfConstrainedQ(s, getConstrainedQIndex(s, cmx, whichQ));
3097     const SimbodyMatterSubsystemRep& matter = getMyMatterSubsystemRep();
3098     return matter.getQDotDot(s)[qx];
3099 }
3100 
3101 // These are used to retrieve the indicated values from the State cache.
getBodyTransformFromState(const State & s,ConstrainedBodyIndex B) const3102 const Transform& ConstraintImpl::getBodyTransformFromState
3103    (const State& s, ConstrainedBodyIndex B) const
3104 {
3105     const SimbodyMatterSubsystemRep& matter    = getMyMatterSubsystemRep();
3106     const MobilizedBodyIndex         bodyB     = myConstrainedBodies[B];
3107 
3108     if (!myAncestorBodyIsNotGround)
3109         return matter.getBodyTransform(s,bodyB); // X_AB==X_GB
3110 
3111     static const Transform X_AA; // identity Transform
3112     const MobilizedBodyIndex ancestorA = mySubtree.getAncestorMobilizedBodyIndex();
3113     if (bodyB == ancestorA)
3114         return X_AA;
3115 
3116     const AncestorConstrainedBodyPoolIndex bx = myPoolIndex[B];
3117     return matter.getTreePositionCache(s)
3118                     .constrainedBodyConfigInAncestor[bx]; // X_AB
3119 }
getBodyVelocityFromState(const State & s,ConstrainedBodyIndex B) const3120 const SpatialVec& ConstraintImpl::getBodyVelocityFromState
3121    (const State& s, ConstrainedBodyIndex B) const
3122 {
3123     const SimbodyMatterSubsystemRep& matter    = getMyMatterSubsystemRep();
3124     const MobilizedBodyIndex         bodyB     = myConstrainedBodies[B];
3125 
3126     if (!myAncestorBodyIsNotGround)
3127         return matter.getBodyVelocity(s,bodyB); // V_AB==V_GB
3128 
3129     static const SpatialVec V_AA(Vec3(0),Vec3(0)); // zero velocity
3130     const MobilizedBodyIndex ancestorA = mySubtree.getAncestorMobilizedBodyIndex();
3131     if (bodyB == ancestorA)
3132         return V_AA;
3133 
3134     const AncestorConstrainedBodyPoolIndex bx = myPoolIndex[B];
3135     return matter.getTreeVelocityCache(s)
3136                     .constrainedBodyVelocityInAncestor[bx]; // V_AB
3137 }
3138 
3139 
3140 
3141 // =============================================================================
3142 //                 CALC CONSTRAINED BODY TRANSFORM IN ANCESTOR
3143 // =============================================================================
3144 // 63 flops per constrained body
calcConstrainedBodyTransformInAncestor(const SBInstanceVars & instanceVars,SBTreePositionCache & tpc) const3145 void ConstraintImpl::calcConstrainedBodyTransformInAncestor      // X_AB
3146    (const SBInstanceVars& instanceVars, SBTreePositionCache& tpc) const
3147 {
3148     if (instanceVars.constraintIsDisabled[myConstraintIndex]) return;
3149     if (!myAncestorBodyIsNotGround) return;
3150     const MobilizedBodyIndex ancestorA = mySubtree.getAncestorMobilizedBodyIndex();
3151 
3152     // We expect Ground-relative kinematics already to have been calculated in
3153     // tpc, but we can't verify that.
3154     const Transform& X_GA = tpc.getX_GB(ancestorA);
3155 
3156     for (ConstrainedBodyIndex cbx(0); cbx < getNumConstrainedBodies(); ++cbx) {
3157         const MobilizedBodyIndex bodyB = myConstrainedBodies[cbx];
3158         if (bodyB == ancestorA) continue; // skip ancestor if it's a constrained body also
3159         const AncestorConstrainedBodyPoolIndex px = myPoolIndex[cbx];
3160         assert(px.isValid());
3161 
3162         const Transform& X_GB = tpc.getX_GB(bodyB);
3163         tpc.updX_AB(px) = ~X_GA*X_GB;  // 63 flops
3164     }
3165 }
3166 
3167 
3168 
3169 // =============================================================================
3170 //                  CALC CONSTRAINED BODY VELOCITY IN ANCESTOR
3171 // =============================================================================
3172 // 51 flops per constrained body
calcConstrainedBodyVelocityInAncestor(const SBInstanceVars & instanceVars,const SBTreePositionCache & tpc,SBTreeVelocityCache & tvc) const3173 void ConstraintImpl::calcConstrainedBodyVelocityInAncestor       // V_AB
3174    (const SBInstanceVars& instanceVars, const SBTreePositionCache& tpc,
3175     SBTreeVelocityCache& tvc) const
3176 {
3177     if (instanceVars.constraintIsDisabled[myConstraintIndex]) return;
3178     if (!myAncestorBodyIsNotGround) return;
3179     const MobilizedBodyIndex ancestorA = mySubtree.getAncestorMobilizedBodyIndex();
3180 
3181     // All position kinematics has been calculated, and we also expect
3182     // Ground-relative velocity kinematics already to have been calculated in tvc,
3183     // but we can't verify that.
3184     const Transform&  X_GA = tpc.getX_GB(ancestorA);
3185     const SpatialVec& V_GA = tvc.getV_GB(ancestorA);
3186 
3187     for (ConstrainedBodyIndex cbx(0); cbx < getNumConstrainedBodies(); ++cbx) {
3188         const MobilizedBodyIndex bodyB = myConstrainedBodies[cbx];
3189         if (bodyB == ancestorA) continue; // skip the ancestor itself if it is a constrained body also
3190         const AncestorConstrainedBodyPoolIndex px = myPoolIndex[cbx];
3191         assert(px.isValid());
3192 
3193         const Transform&  X_GB = tpc.getX_GB(bodyB);
3194         const SpatialVec& V_GB = tvc.getV_GB(bodyB);
3195 
3196         // 6 flops
3197         const Vec3 p_AB_G     = X_GB.p() - X_GA.p();
3198         const Vec3 p_AB_G_dot = V_GB[1]  - V_GA[1];        // d/dt p taken in G
3199 
3200         // 3 flops
3201         const Vec3 w_AB_G = V_GB[0] - V_GA[0];             // relative angular velocity of B in A, exp. in G
3202 
3203         // To get d/dt p taken in A, get derivative in G and remove the contribution generated by
3204         // A's velocity in G.
3205         // 12 flops
3206         const Vec3 v_AB_G = p_AB_G_dot - V_GA[0] % p_AB_G; // time deriv of p in A, exp in G
3207 
3208         // 30 flops
3209         tvc.updV_AB(px) = ~X_GA.R() * SpatialVec(w_AB_G, v_AB_G);     // re-express in A
3210     }
3211 }
3212 
3213 
3214 // Find out how many holonomic (position), nonholonomic (velocity),
3215 // and acceleration-only constraint equations are generated by this Constraint
3216 // as it is currently being modeled.
getNumConstraintEquationsInUse(const State & s,int & mHolo,int & mNonholo,int & mAccOnly) const3217 void ConstraintImpl::getNumConstraintEquationsInUse
3218    (const State& s, int& mHolo, int& mNonholo, int& mAccOnly) const
3219 {
3220     const SBInstancePerConstraintInfo& cInfo =
3221         getInstanceCache(s).getConstraintInstanceInfo(myConstraintIndex);
3222 
3223     mHolo    = cInfo.holoErrSegment.length;
3224     mNonholo = cInfo.nonholoErrSegment.length;
3225     mAccOnly = cInfo.accOnlyErrSegment.length;
3226 }
3227 
3228 void ConstraintImpl::
getIndexOfMultipliersInUse(const State & s,MultiplierIndex & px0,MultiplierIndex & vx0,MultiplierIndex & ax0) const3229 getIndexOfMultipliersInUse(const State&     s,
3230                            MultiplierIndex& px0,
3231                            MultiplierIndex& vx0,
3232                            MultiplierIndex& ax0) const
3233 {
3234     const SBInstanceCache& ic = getInstanceCache(s);
3235     const SBInstancePerConstraintInfo& cInfo =
3236         ic.getConstraintInstanceInfo(myConstraintIndex);
3237 
3238     // Find the offset to our first multiplier in the ModelCache.
3239     const int firstHoloErr = cInfo.holoErrSegment.offset;
3240     const int mHolo        = cInfo.holoErrSegment.length;
3241 
3242     const int firstNonholoErr =
3243           ic.totalNHolonomicConstraintEquationsInUse //total for whole subsystem
3244         + cInfo.nonholoErrSegment.offset;
3245     const int mNonholo = cInfo.nonholoErrSegment.length;
3246 
3247     const int firstAccOnlyErr =
3248           ic.totalNHolonomicConstraintEquationsInUse
3249         + ic.totalNNonholonomicConstraintEquationsInUse
3250         + cInfo.accOnlyErrSegment.offset;
3251     const int mAccOnly = cInfo.accOnlyErrSegment.length;
3252 
3253     px0.invalidate(); if (mHolo)    px0=MultiplierIndex(firstHoloErr);
3254     vx0.invalidate(); if (mNonholo) vx0=MultiplierIndex(firstNonholoErr);
3255     ax0.invalidate(); if (mAccOnly) ax0=MultiplierIndex(firstAccOnlyErr);
3256 }
3257 
3258 void ConstraintImpl::
setMyPartInConstraintSpaceVector(const State & s,const Vector & myPart,Vector & constraintSpace) const3259 setMyPartInConstraintSpaceVector(const State& s,
3260                                  const Vector& myPart,
3261                                  Vector& constraintSpace) const
3262 {
3263     const SBInstanceCache& ic = getInstanceCache(s);
3264     // Global problem dimensions.
3265     const int mHolo    = ic.totalNHolonomicConstraintEquationsInUse;
3266     const int mNonholo = ic.totalNNonholonomicConstraintEquationsInUse;
3267     const int mAccOnly = ic.totalNAccelerationOnlyConstraintEquationsInUse;
3268 
3269     const int m = mHolo+mNonholo+mAccOnly;
3270     if (constraintSpace.size() == 0) {
3271         constraintSpace.resize(m);
3272         constraintSpace.setToZero();
3273     }
3274 
3275     SimTK_ERRCHK2_ALWAYS(constraintSpace.size()==m,
3276         "Constraint::setMyPartInConstraintSpaceVector()",
3277         "Output vector had size %d but should have had size zero or m=%d.",
3278         constraintSpace.size(), m);
3279 
3280     int mp, mv, ma;
3281     getNumConstraintEquationsInUse(s, mp, mv, ma);
3282     MultiplierIndex px0, vx0, ax0;
3283     getIndexOfMultipliersInUse(s, px0, vx0, ax0);
3284 
3285     for (int i=0; i<mp; ++i)
3286         constraintSpace[px0+i] = myPart[i];
3287     for (int i=0; i<mv; ++i)
3288         constraintSpace[vx0+i] = myPart[mp+i];
3289     for (int i=0; i<ma; ++i)
3290         constraintSpace[ax0+i] = myPart[mp+mv+i];
3291 }
3292 
3293 void ConstraintImpl::
getMyPartFromConstraintSpaceVector(const State & s,const Vector & constraintSpace,Vector & myPart) const3294 getMyPartFromConstraintSpaceVector(const State& s,
3295                                    const Vector& constraintSpace,
3296                                    Vector& myPart) const
3297 {
3298     const SBInstanceCache& ic = getInstanceCache(s);
3299     // Global problem dimensions.
3300     const int mHolo    = ic.totalNHolonomicConstraintEquationsInUse;
3301     const int mNonholo = ic.totalNNonholonomicConstraintEquationsInUse;
3302     const int mAccOnly = ic.totalNAccelerationOnlyConstraintEquationsInUse;
3303 
3304     const int m = mHolo+mNonholo+mAccOnly;
3305 
3306     SimTK_ERRCHK2_ALWAYS(constraintSpace.size()==m,
3307         "Constraint::getMyPartFromConstraintSpaceVector()",
3308         "Input vector had size %d but should have had size m=%d.",
3309         constraintSpace.size(), m);
3310 
3311     int mp, mv, ma;
3312     getNumConstraintEquationsInUse(s, mp, mv, ma);
3313     MultiplierIndex px0, vx0, ax0;
3314     getIndexOfMultipliersInUse(s, px0, vx0, ax0);
3315 
3316     myPart.resize(mp+mv+ma);
3317 
3318     for (int i=0; i<mp; ++i)
3319          myPart[i] = constraintSpace[px0+i];
3320     for (int i=0; i<mv; ++i)
3321          myPart[mp+i] = constraintSpace[vx0+i];
3322     for (int i=0; i<ma; ++i)
3323          myPart[mp+mv+i] = constraintSpace[ax0+i];
3324 }
3325 
setDisabled(State & s,bool shouldBeDisabled) const3326 void ConstraintImpl::setDisabled(State& s, bool shouldBeDisabled) const {
3327     getMyMatterSubsystemRep().setConstraintIsDisabled(s, myConstraintIndex, shouldBeDisabled);
3328 }
3329 
isDisabled(const State & s) const3330 bool ConstraintImpl::isDisabled(const State& s) const {
3331     return getMyMatterSubsystemRep().isConstraintDisabled(s, myConstraintIndex);
3332 }
3333 
3334 // Call this during construction phase to add a body to the topological
3335 // structure of this Constraint. This body's mobilizer's mobilities are
3336 // *not* part of the constraint; mobilizers must be added separately. It is OK
3337 // to add the same body multiple times; it will only get inserted once and
3338 // you'll get the same index every time.
3339 ConstrainedBodyIndex ConstraintImpl::
addConstrainedBody(const MobilizedBody & b)3340 addConstrainedBody(const MobilizedBody& b) {
3341     assert(isInSameSubsystem(b));
3342     invalidateTopologyCache();
3343 
3344     const ConstrainedBodyIndex nextIx((int)myConstrainedBodies.size());
3345 
3346     // Add to the Mobilized->Constrained map and check for duplicates.
3347     std::pair<MobilizedBody2ConstrainedBodyMap::iterator, bool> result;
3348     result = myMobilizedBody2ConstrainedBodyMap.insert(
3349         MobilizedBody2ConstrainedBodyMap::value_type(b.getMobilizedBodyIndex(),
3350                                                      nextIx));
3351     if (!result.second) {
3352         // It was already there.
3353         return result.first->second; // the index we assigned before
3354     }
3355 
3356     // This is a new constrained body -- add it to the
3357     // ConstrainedBody->MobilizedBody map too.
3358     myConstrainedBodies.push_back(b.getMobilizedBodyIndex());
3359     return nextIx;
3360 }
3361 
3362 // Call this during construction phase to add a mobilizer to the topological
3363 // structure of this Constraint. All the coordinates q and mobilities u for this
3364 // mobilizer are added also, but we don't know how many of those there will be
3365 // until Stage::Model. It is OK to add the same mobilizer multiple times; it will
3366 // only get inserted once and you'll get the same index every time.
3367 ConstrainedMobilizerIndex ConstraintImpl::
addConstrainedMobilizer(const MobilizedBody & b)3368 addConstrainedMobilizer(const MobilizedBody& b) {
3369     assert(isInSameSubsystem(b));
3370     invalidateTopologyCache();
3371 
3372     const ConstrainedMobilizerIndex nextIx((int)myConstrainedMobilizers.size());
3373 
3374     // Add to the Mobilized->Constrained map and check for duplicates.
3375     std::pair<MobilizedBody2ConstrainedMobilizerMap::iterator, bool> result;
3376     result = myMobilizedBody2ConstrainedMobilizerMap.insert
3377        (MobilizedBody2ConstrainedMobilizerMap::value_type
3378                                             (b.getMobilizedBodyIndex(), nextIx));
3379 
3380     if (!result.second) {
3381         // It was already there.
3382         return result.first->second; // the index we assigned before
3383     }
3384 
3385     // This is a new constrained mobilizer -- add it to the
3386     // ConstrainedMobilizer->MobilizedBody map too.
3387     myConstrainedMobilizers.push_back(b.getMobilizedBodyIndex());
3388     return nextIx;
3389 }
3390 
getQIndexOfConstrainedQ(const State & s,ConstrainedQIndex cqx) const3391 QIndex ConstraintImpl::getQIndexOfConstrainedQ(const State& s, ConstrainedQIndex cqx) const {
3392     const SBInstanceCache& ic = getInstanceCache(s);
3393     const SBInstancePerConstraintInfo&
3394         cInfo = ic.getConstraintInstanceInfo(myConstraintIndex);
3395     return cInfo.getQIndexFromConstrainedQ(cqx);
3396 }
3397 
getUIndexOfConstrainedU(const State & s,ConstrainedUIndex cux) const3398 UIndex ConstraintImpl::getUIndexOfConstrainedU(const State& s, ConstrainedUIndex cux) const {
3399     const SBInstanceCache&             ic    = getInstanceCache(s);
3400     const SBInstancePerConstraintInfo& cInfo =
3401         ic.getConstraintInstanceInfo(myConstraintIndex);
3402     return cInfo.getUIndexFromConstrainedU(cux);
3403 }
3404 
getNumConstrainedQ(const State & s) const3405 int ConstraintImpl::getNumConstrainedQ(const State& s) const {
3406     return getInstanceCache(s).getConstraintInstanceInfo(myConstraintIndex)
3407                               .getNumConstrainedQ();
3408 }
3409 
getNumConstrainedQ(const State & s,ConstrainedMobilizerIndex M) const3410 int ConstraintImpl::getNumConstrainedQ
3411    (const State& s, ConstrainedMobilizerIndex M) const
3412 {
3413     const SBInstancePerConstrainedMobilizerInfo& mInfo =
3414         getInstanceCache(s).getConstraintInstanceInfo(myConstraintIndex)
3415                            .getConstrainedMobilizerInstanceInfo(M);
3416     return mInfo.nQInUse; // same as corresponding Mobod, or 0 if disabled
3417 }
3418 
getConstrainedQIndex(const State & s,ConstrainedMobilizerIndex M,MobilizerQIndex which) const3419 ConstrainedQIndex ConstraintImpl::getConstrainedQIndex
3420    (const State& s, ConstrainedMobilizerIndex M, MobilizerQIndex which) const
3421 {
3422     const int nq = getNumConstrainedQ(s,M);
3423     assert(0 <= which && which < nq);
3424     const SBInstancePerConstrainedMobilizerInfo& mInfo =
3425         getInstanceCache(s).getConstraintInstanceInfo(myConstraintIndex)
3426                            .getConstrainedMobilizerInstanceInfo(M);
3427     return ConstrainedQIndex(mInfo.firstConstrainedQIndex + which);
3428 }
3429 
getNumConstrainedU(const State & s) const3430 int ConstraintImpl::getNumConstrainedU(const State& s) const {
3431     return getInstanceCache(s).getConstraintInstanceInfo(myConstraintIndex)
3432                               .getNumConstrainedU();
3433 }
3434 
getNumConstrainedU(const State & s,ConstrainedMobilizerIndex M) const3435 int ConstraintImpl::getNumConstrainedU
3436    (const State& s, ConstrainedMobilizerIndex M) const
3437 {
3438     const SBInstancePerConstrainedMobilizerInfo& mInfo =
3439         getInstanceCache(s).getConstraintInstanceInfo(myConstraintIndex)
3440                            .getConstrainedMobilizerInstanceInfo(M);
3441     return mInfo.nUInUse; // same as corr. MobilizedBody, or 0 if disabled
3442 }
3443 
getConstrainedUIndex(const State & s,ConstrainedMobilizerIndex M,MobilizerUIndex which) const3444 ConstrainedUIndex ConstraintImpl::getConstrainedUIndex
3445    (const State& s, ConstrainedMobilizerIndex M, MobilizerUIndex which) const
3446 {
3447     const int nu = getNumConstrainedU(s,M);
3448     assert(0 <= which && which < nu);
3449     const SBInstancePerConstrainedMobilizerInfo& mInfo =
3450         getInstanceCache(s).getConstraintInstanceInfo(myConstraintIndex)
3451                            .getConstrainedMobilizerInstanceInfo(M);
3452     return ConstrainedUIndex(mInfo.firstConstrainedUIndex + which);
3453 }
3454 
3455 
3456 
3457 //==============================================================================
3458 //                       CONVERT Q FORCES TO U FORCES
3459 //==============================================================================
3460 // uForces = ~N * qForces
3461 void ConstraintImpl::
convertQForcesToUForces(const State & s,const Array_<Real,ConstrainedQIndex> & qForces,Array_<Real,ConstrainedUIndex> & uForces) const3462 convertQForcesToUForces(const State&                          s,
3463                         const Array_<Real,ConstrainedQIndex>& qForces,
3464                         Array_<Real,ConstrainedUIndex>&       uForces) const
3465 {
3466     const int ncm = getNumConstrainedMobilizers();
3467     if (ncm == 0)
3468         return; // very common!
3469 
3470     const SBInstanceCache& ic = getInstanceCache(s);
3471     const SBInstancePerConstraintInfo&
3472         cInfo = ic.getConstraintInstanceInfo(myConstraintIndex);
3473 
3474     assert(cInfo.getNumConstrainedMobilizers() == ncm);
3475 
3476     const int ncu = cInfo.getNumConstrainedU();
3477     const int ncq = cInfo.getNumConstrainedQ();
3478 
3479     assert(qForces.size() == ncq);
3480     uForces.resize(ncu);
3481 
3482     if (ncu == 0)
3483         return;
3484 
3485     for (ConstrainedMobilizerIndex cmx(0); cmx < ncm; ++cmx) {
3486         const SBInstancePerConstrainedMobilizerInfo& mInfo =
3487             cInfo.getConstrainedMobilizerInstanceInfo(cmx);
3488         const int nq = mInfo.nQInUse, nu = mInfo.nUInUse;
3489         if (nu == 0) continue;
3490 
3491         const Real* firstQ = &qForces[mInfo.firstConstrainedQIndex];
3492         Real* firstU = &uForces[mInfo.firstConstrainedUIndex];
3493         const ArrayViewConst_<Real,MobilizerQIndex> fq(firstQ, firstQ+nq);
3494         ArrayView_<Real,MobilizerUIndex> fu(firstU, firstU+nu);
3495         const MobilizedBody& mobod =
3496             getMobilizedBodyFromConstrainedMobilizer(cmx);
3497         mobod.convertQForceToUForce(s, fq, fu);
3498     }
3499 }
3500 
3501 
3502 
3503 //==============================================================================
3504 //               CONVERT BODY ACCEL TO CONSTRAINED BODY ACCEL
3505 //==============================================================================
convertBodyAccelToConstrainedBodyAccel(const State & s,const Array_<SpatialVec,MobilizedBodyIndex> & allA_GB,Array_<SpatialVec,ConstrainedBodyIndex> & A_AB) const3506 void ConstraintImpl::convertBodyAccelToConstrainedBodyAccel
3507    (const State&                                    s,
3508     const Array_<SpatialVec, MobilizedBodyIndex>&   allA_GB,
3509     Array_<SpatialVec, ConstrainedBodyIndex>&       A_AB) const
3510 {
3511     const SimbodyMatterSubsystemRep& matter = getMyMatterSubsystemRep();
3512     assert(allA_GB.size() == matter.getNumBodies());
3513 
3514     const int ncb = getNumConstrainedBodies();
3515     A_AB.resize(ncb);
3516 
3517     // If the Ancestor is Ground we're just reordering.
3518     if (!isAncestorDifferentFromGround()) {
3519         for (ConstrainedBodyIndex cbx(0); cbx < ncb; ++cbx) {
3520             const MobilizedBodyIndex mbx =
3521                 getMobilizedBodyIndexOfConstrainedBody(cbx);
3522             A_AB[cbx] = allA_GB[mbx];
3523         }
3524         return;
3525     }
3526 
3527     // If the Ancestor is not Ground we have to transform the accelerations
3528     // from Ground to Ancestor, at a cost of 105 flops/constrained body (not
3529     // just re-expressing).
3530 
3531     const MobilizedBody& ancestor = getAncestorMobilizedBody();
3532     const MobilizedBodyIndex acx = ancestor.getMobilizedBodyIndex();
3533     const Transform&  X_GA  = ancestor.getBodyTransform(s);
3534     const SpatialVec& V_GA  = ancestor.getBodyVelocity(s);
3535     const SpatialVec& A_GA  = allA_GB[acx]; // from input argument
3536 
3537     for (ConstrainedBodyIndex cbx(0); cbx < ncb; ++cbx) {
3538         const MobilizedBodyIndex mbx =
3539             getMobilizedBodyIndexOfConstrainedBody(cbx);
3540         if (mbx == acx) { // common: Ancestor is a constrained body
3541             A_AB[cbx] = SpatialVec(Vec3(0));
3542             continue;
3543         }
3544         const MobilizedBody& consBody = matter.getMobilizedBody(mbx);
3545         const Transform&  X_GB = consBody.getBodyTransform(s);
3546         const SpatialVec& V_GB = consBody.getBodyVelocity(s);
3547         const SpatialVec& A_GB = allA_GB[mbx];  // from input arg
3548         A_AB[cbx] = findRelativeAcceleration(X_GA, V_GA, A_GA,
3549                                              X_GB, V_GB, A_GB);
3550     }
3551 }
3552 
3553 
3554 
3555 //==============================================================================
3556 //             CONVERT BODY VELOCITY TO CONSTRAINED BODY VELOCITY
3557 //==============================================================================
convertBodyVelocityToConstrainedBodyVelocity(const State & s,const Array_<SpatialVec,MobilizedBodyIndex> & allV_GB,Array_<SpatialVec,ConstrainedBodyIndex> & V_AB) const3558 void ConstraintImpl::convertBodyVelocityToConstrainedBodyVelocity
3559    (const State&                                    s,
3560     const Array_<SpatialVec, MobilizedBodyIndex>&   allV_GB,
3561     Array_<SpatialVec, ConstrainedBodyIndex>&       V_AB) const
3562 {
3563     const SimbodyMatterSubsystemRep& matter = getMyMatterSubsystemRep();
3564     assert(allV_GB.size() == matter.getNumBodies());
3565 
3566     const int ncb = getNumConstrainedBodies();
3567     V_AB.resize(ncb);
3568 
3569     // If the Ancestor is Ground we're just reordering.
3570     if (!isAncestorDifferentFromGround()) {
3571         for (ConstrainedBodyIndex cbx(0); cbx < ncb; ++cbx) {
3572             const MobilizedBodyIndex mbx =
3573                 getMobilizedBodyIndexOfConstrainedBody(cbx);
3574             V_AB[cbx] = allV_GB[mbx];
3575         }
3576         return;
3577     }
3578 
3579     // If the Ancestor is not Ground we have to transform the velocities
3580     // from Ground to Ancestor, at a cost of 51 flops/constrained body (not
3581     // just re-expressing).
3582 
3583     const MobilizedBody& ancestor = getAncestorMobilizedBody();
3584     const MobilizedBodyIndex acx = ancestor.getMobilizedBodyIndex();
3585     const Transform&  X_GA  = ancestor.getBodyTransform(s);
3586     const SpatialVec& V_GA  = allV_GB[acx]; // from input argument
3587 
3588     for (ConstrainedBodyIndex cbx(0); cbx < ncb; ++cbx) {
3589         const MobilizedBodyIndex mbx =
3590             getMobilizedBodyIndexOfConstrainedBody(cbx);
3591         if (mbx == acx) { // common: Ancestor is a constrained body
3592             V_AB[cbx] = SpatialVec(Vec3(0));
3593             continue;
3594         }
3595         const MobilizedBody& consBody = matter.getMobilizedBody(mbx);
3596         const Transform&  X_GB = consBody.getBodyTransform(s);
3597         const SpatialVec& V_GB = allV_GB[mbx];  // from input arg
3598         V_AB[cbx] = findRelativeVelocity(X_GA, V_GA,    // 51 flops
3599                                          X_GB, V_GB);
3600     }
3601 }
3602 
3603 
3604 
3605 //==============================================================================
3606 //               GET POSITION, VELOCITY, ACCELERATION ERRORS
3607 //==============================================================================
3608 
3609 // Given a state realized to Position stage, extract the position constraint
3610 // errors corresponding to this Constraint. The 'mp' argument is for sanity
3611 // checking -- it is an error if that isn't an exact match for the current
3612 // number of holonomic constraint equations generated by this Constraint. We
3613 // expect that perr points to an array of at least mp elements that we can
3614 // write on.
getPositionErrors(const State & s,int mp,Real * perr) const3615 void ConstraintImpl::getPositionErrors(const State& s, int mp, Real* perr) const {
3616     const SBInstancePerConstraintInfo& cInfo =
3617         getInstanceCache(s).getConstraintInstanceInfo(myConstraintIndex);
3618 
3619     assert(mp == cInfo.holoErrSegment.length);
3620 
3621     // Find the offset to our first qerr in the ModelCache.
3622     const int firstQErr = cInfo.holoErrSegment.offset;
3623 
3624     // Get all qerr's for the subsystem.
3625     const Vector& qerr = getMyMatterSubsystemRep().getQErr(s);
3626 
3627     // Copy out the qerr's belonging to this constraint.
3628     for (int i=0; i < mp; ++i)
3629         perr[i] = qerr[firstQErr + i];
3630 }
3631 
3632 // Given a State realized to Velocity stage, extract the velocity constraint errors
3633 // corresponding to this Constraint. This includes velocity constraints which were
3634 // produced by differentiation of holonomic (position) constraints, and nonholonomic
3635 // constraints which are introduced at the velocity level. The 'mpv' argument is
3636 // for sanity checking -- it is an error if that isn't an exact match for the
3637 // current number of holonomic+nonholonomic (mp+mv) constraint equations generated
3638 // by this Constraint. We expect that pverr points to an array of at least mp+mv
3639 // elements that we can write on.
getVelocityErrors(const State & s,int mpv,Real * pverr) const3640 void ConstraintImpl::getVelocityErrors(const State& s, int mpv, Real* pverr) const {
3641     const SBInstanceCache&             ic    = getInstanceCache(s);
3642     const SBInstancePerConstraintInfo& cInfo =
3643         ic.getConstraintInstanceInfo(myConstraintIndex);
3644 
3645     assert(mpv ==  cInfo.holoErrSegment.length
3646                  + cInfo.nonholoErrSegment.length);
3647 
3648     // Get reference to all uerr's for the subsystem.
3649     const Vector& uerr = getMyMatterSubsystemRep().getUErr(s);
3650 
3651     // Find the offset to our first uerr in the ModelCache.
3652     const int firstHoloErr = cInfo.holoErrSegment.offset;
3653     const int mHolo        = cInfo.holoErrSegment.length;
3654 
3655     for (int i=0; i < mHolo; ++i)
3656         pverr[i] = uerr[firstHoloErr+i];
3657 
3658     const int firstNonholoErr = ic.totalNHolonomicConstraintEquationsInUse // total for whole subsystem
3659                                 + cInfo.nonholoErrSegment.offset;
3660     const int mNonholo        = cInfo.nonholoErrSegment.length;
3661 
3662     for (int i=0; i < mNonholo; ++i)
3663         pverr[mHolo+i] = uerr[firstNonholoErr+i];
3664 }
3665 
3666 // Given a State realized to Acceleration stage, extract the acceleration
3667 // constraint errors corresponding to this Constraint. This includes
3668 // acceleration constraints which were produced by twice differentiation of
3669 // holonomic (position) constraints, and differentiation of nonholonomic
3670 // (velocity) constraints, and acceleration-only constraints which are
3671 // first introduced at the acceleration level. The 'mpva' argument is
3672 // for sanity checking -- it is an error if that isn't an exact match for the
3673 // current number of holonomic+nonholonomic+accelerationOnly (mp+mv+ma)
3674 // constraint equations generated by this Constraint. We expect that pvaerr
3675 // points to an array of at least mp+mv+ma elements that we can write on.
getAccelerationErrors(const State & s,int mpva,Real * pvaerr) const3676 void ConstraintImpl::getAccelerationErrors(const State& s, int mpva, Real* pvaerr) const {
3677     const SBInstanceCache&             ic    = getInstanceCache(s);
3678     const SBInstancePerConstraintInfo& cInfo =
3679         ic.getConstraintInstanceInfo(myConstraintIndex);
3680 
3681     assert(mpva ==   cInfo.holoErrSegment.length
3682                    + cInfo.nonholoErrSegment.length
3683                    + cInfo.accOnlyErrSegment.length);
3684 
3685     // Get reference to all udoterr's for the subsystem.
3686     const Vector& udoterr = getMyMatterSubsystemRep().getUDotErr(s);
3687 
3688     // Find the offset to our first uerr in the ModelCache.
3689     const int firstHoloErr = cInfo.holoErrSegment.offset;
3690     const int mHolo        = cInfo.holoErrSegment.length;
3691 
3692     for (int i=0; i < mHolo; ++i)
3693         pvaerr[i] = udoterr[firstHoloErr+i];
3694 
3695     const int firstNonholoErr = ic.totalNHolonomicConstraintEquationsInUse // total for whole subsystem
3696                                 + cInfo.nonholoErrSegment.offset;
3697     const int mNonholo        = cInfo.nonholoErrSegment.length;
3698 
3699     for (int i=0; i < mNonholo; ++i)
3700         pvaerr[mHolo+i] = udoterr[firstNonholoErr+i];
3701 
3702     const int firstAccOnlyErr = ic.totalNHolonomicConstraintEquationsInUse
3703                                 + ic.totalNNonholonomicConstraintEquationsInUse // total for whole subsystem
3704                                 + cInfo.accOnlyErrSegment.offset;
3705     const int mAccOnly        = cInfo.accOnlyErrSegment.length;
3706 
3707     for (int i=0; i < mAccOnly; ++i)
3708         pvaerr[mHolo+mNonholo+i] = udoterr[firstAccOnlyErr+i];
3709 }
3710 
3711 // Given a State realized to Acceleration stage, extract the Lagrange
3712 // multipliers corresponding to this Constraint. The 'mpva' argument is for
3713 // sanity checking -- it is an error if that isn't an exact match for the
3714 // current number of holonomic+nonholonomic+accelerationOnly (mp+mv+ma)
3715 // constraint equations generated by this Constraint. We expect that lambda
3716 // points to an array of at least mp+mv+ma elements that we can write on.
getMultipliers(const State & s,int mpva,Real * lambda) const3717 void ConstraintImpl::getMultipliers(const State& s, int mpva, Real* lambda) const {
3718     const SBInstanceCache&             ic    = getInstanceCache(s);
3719     const SBInstancePerConstraintInfo& cInfo =
3720         ic.getConstraintInstanceInfo(myConstraintIndex);
3721 
3722     assert(mpva ==   cInfo.holoErrSegment.length
3723                    + cInfo.nonholoErrSegment.length
3724                    + cInfo.accOnlyErrSegment.length);
3725 
3726     // Get reference to all multipliers for the subsystem. This will throw
3727     // an error if the matter subsystem hasn't already been realized through
3728     // acceleration stage.
3729     const Vector& multipliers = getMyMatterSubsystemRep().getMultipliers(s);
3730 
3731     // Find the offset to our first multiplier in the ModelCache.
3732     const int firstHoloErr = cInfo.holoErrSegment.offset;
3733     const int mHolo        = cInfo.holoErrSegment.length;
3734 
3735     for (int i=0; i < mHolo; ++i)
3736         lambda[i] = multipliers[firstHoloErr+i];
3737 
3738     const int firstNonholoErr = ic.totalNHolonomicConstraintEquationsInUse // total for whole subsystem
3739                                 + cInfo.nonholoErrSegment.offset;
3740     const int mNonholo        = cInfo.nonholoErrSegment.length;
3741 
3742     for (int i=0; i < mNonholo; ++i)
3743         lambda[mHolo+i] = multipliers[firstNonholoErr+i];
3744 
3745     const int firstAccOnlyErr = ic.totalNHolonomicConstraintEquationsInUse
3746                                 + ic.totalNNonholonomicConstraintEquationsInUse // total for whole subsystem
3747                                 + cInfo.accOnlyErrSegment.offset;
3748     const int mAccOnly        = cInfo.accOnlyErrSegment.length;
3749 
3750     for (int i=0; i < mAccOnly; ++i)
3751         lambda[mHolo+mNonholo+i] = multipliers[firstAccOnlyErr+i];
3752 }
3753 
3754 // Reference this constraint's assigned partition within the larger array.
3755 ArrayView_<SpatialVec,ConstrainedBodyIndex> ConstraintImpl::
updConstrainedBodyForces(const State & state,Array_<SpatialVec> & allConsBodyForces) const3756 updConstrainedBodyForces(const State&        state,
3757                          Array_<SpatialVec>& allConsBodyForces) const
3758 {
3759     const SBInstanceCache& ic = getInstanceCache(state);
3760     assert(allConsBodyForces.size() == ic.totalNConstrainedBodiesInUse);
3761 
3762     const ConstraintIndex              cx    = getMyConstraintIndex();
3763     const SBInstancePerConstraintInfo& cInfo = ic.getConstraintInstanceInfo(cx);
3764 
3765     const Segment& consBodySegment = cInfo.consBodySegment;
3766     const int ncb = consBodySegment.length;
3767 
3768     // No heap allocation is being done here. The longer array can be
3769     // empty so we're using begin() here rather than &array[0] which
3770     // would be illegal in that case. This pointer may be null.
3771     SpatialVec* firstBodySlot = allConsBodyForces.begin()
3772                                 + consBodySegment.offset;
3773 
3774     return ArrayView_<SpatialVec,ConstrainedBodyIndex>
3775                 (firstBodySlot, firstBodySlot + ncb);
3776 }
3777 
3778 ArrayView_<Real,ConstrainedUIndex> ConstraintImpl::
updConstrainedMobilityForces(const State & state,Array_<Real> & allConsMobForces) const3779 updConstrainedMobilityForces(const State&  state,
3780                              Array_<Real>& allConsMobForces) const
3781 {
3782     const SBInstanceCache& ic = getInstanceCache(state);
3783     assert(allConsMobForces.size() == ic.totalNConstrainedUInUse);
3784 
3785     const ConstraintIndex              cx    = getMyConstraintIndex();
3786     const SBInstancePerConstraintInfo& cInfo = ic.getConstraintInstanceInfo(cx);
3787 
3788     const Segment& consUSegment = cInfo.consUSegment;
3789     const int ncu = consUSegment.length;
3790 
3791     // No heap allocation is being done here. The longer array can be
3792     // empty so we're using begin() here rather than &array[0] which
3793     // would be illegal in that case. This pointer may be null.
3794     Real* firstMobSlot = allConsMobForces.begin()
3795                          + consUSegment.offset;
3796 
3797     return ArrayView_<Real,ConstrainedUIndex>
3798                 (firstMobSlot, firstMobSlot + ncu);
3799 }
3800 
getInstanceCache(const State & s) const3801 const SBInstanceCache& ConstraintImpl::getInstanceCache(const State& s) const {
3802     return getMyMatterSubsystemRep().getInstanceCache(s);
3803 }
getModelCache(const State & s) const3804 const SBModelCache& ConstraintImpl::getModelCache(const State& s) const {
3805     return getMyMatterSubsystemRep().getModelCache(s);
3806 }
getTreePositionCache(const State & s) const3807 const SBTreePositionCache& ConstraintImpl::getTreePositionCache(const State& s) const {
3808     return getMyMatterSubsystemRep().getTreePositionCache(s);
3809 }
getTreeVelocityCache(const State & s) const3810 const SBTreeVelocityCache& ConstraintImpl::getTreeVelocityCache(const State& s) const {
3811     return getMyMatterSubsystemRep().getTreeVelocityCache(s);
3812 }
getTreeAccelerationCache(const State & s) const3813 const SBTreeAccelerationCache& ConstraintImpl::getTreeAccelerationCache(const State& s) const {
3814     return getMyMatterSubsystemRep().getTreeAccelerationCache(s);
3815 }
3816 const SBConstrainedAccelerationCache& ConstraintImpl::
getConstrainedAccelerationCache(const State & s) const3817 getConstrainedAccelerationCache(const State& s) const {
3818     return getMyMatterSubsystemRep().getConstrainedAccelerationCache(s);
3819 }
3820 SBConstrainedAccelerationCache& ConstraintImpl::
updConstrainedAccelerationCache(const State & s) const3821 updConstrainedAccelerationCache(const State& s) const {
3822     return getMyMatterSubsystemRep().updConstrainedAccelerationCache(s);
3823 }
3824 
3825 //------------------------------------------------------------------------------
3826 // Default implementations for ConstraintImpl virtuals throw "unimplemented"
3827 // exceptions. These shouldn't be called unless the concrete constraint has
3828 // given a non-zero value for mp, mv, and/or ma which is a promise to
3829 // implement the associated methods.
3830 //------------------------------------------------------------------------------
3831 
3832     // These four must be defined if there are any position (holonomic)
3833     // constraints defined.
3834 
calcPositionErrorsVirtual(const State & state,const Array_<Transform,ConstrainedBodyIndex> & X_AB,const Array_<Real,ConstrainedQIndex> & constrainedQ,Array_<Real> & perr) const3835 void ConstraintImpl::calcPositionErrorsVirtual
3836    (const State&                                    state,
3837     const Array_<Transform,ConstrainedBodyIndex>&   X_AB,
3838     const Array_<Real,     ConstrainedQIndex>&      constrainedQ,
3839     Array_<Real>&                                   perr)
3840     const {
3841     SimTK_THROW2(Exception::UnimplementedVirtualMethod,
3842         "ConstraintImpl", "calcPositionErrorsVirtual");
3843 }
3844 
calcPositionDotErrorsVirtual(const State & state,const Array_<SpatialVec,ConstrainedBodyIndex> & V_AB,const Array_<Real,ConstrainedQIndex> & constrainedQDot,Array_<Real> & pverr) const3845 void ConstraintImpl::calcPositionDotErrorsVirtual
3846    (const State&                                    state,
3847     const Array_<SpatialVec,ConstrainedBodyIndex>&  V_AB,
3848     const Array_<Real,      ConstrainedQIndex>&     constrainedQDot,
3849     Array_<Real>&                                   pverr)
3850     const {
3851     SimTK_THROW2(Exception::UnimplementedVirtualMethod,
3852         "ConstraintImpl", "calcPositionDotErrorsVirtual");
3853 }
3854 
calcPositionDotDotErrorsVirtual(const State & state,const Array_<SpatialVec,ConstrainedBodyIndex> & A_AB,const Array_<Real,ConstrainedQIndex> & constrainedQDotDot,Array_<Real> & paerr) const3855 void ConstraintImpl::calcPositionDotDotErrorsVirtual
3856    (const State&                                    state,
3857     const Array_<SpatialVec,ConstrainedBodyIndex>&  A_AB,
3858     const Array_<Real,      ConstrainedQIndex>&     constrainedQDotDot,
3859     Array_<Real>&                                   paerr)
3860     const {
3861     SimTK_THROW2(Exception::UnimplementedVirtualMethod,
3862         "ConstraintImpl", "calcPositionDotDotErrorsVirtual");
3863 }
3864 
addInPositionConstraintForcesVirtual(const State & state,const Array_<Real> & multipliers,Array_<SpatialVec,ConstrainedBodyIndex> & bodyForces,Array_<Real,ConstrainedQIndex> & qForces) const3865 void ConstraintImpl::addInPositionConstraintForcesVirtual
3866    (const State&                                    state,
3867     const Array_<Real>&                             multipliers,
3868     Array_<SpatialVec,ConstrainedBodyIndex>&        bodyForces,
3869     Array_<Real,      ConstrainedQIndex>&           qForces)
3870     const
3871 {
3872     SimTK_THROW2(Exception::UnimplementedVirtualMethod,
3873         "ConstraintImpl", "addInPositionConstraintForcesVirtual");
3874 }
3875 
3876 
3877     // These three must be defined if there are any velocity (nonholonomic)
3878     // constraints defined.
3879 
calcVelocityErrorsVirtual(const State & state,const Array_<SpatialVec,ConstrainedBodyIndex> & V_AB,const Array_<Real,ConstrainedUIndex> & constrainedU,Array_<Real> & verr) const3880 void ConstraintImpl::calcVelocityErrorsVirtual
3881    (const State&                                    state,
3882     const Array_<SpatialVec,ConstrainedBodyIndex>&  V_AB,
3883     const Array_<Real,      ConstrainedUIndex>&     constrainedU,
3884     Array_<Real>&                                   verr)
3885     const {
3886     SimTK_THROW2(Exception::UnimplementedVirtualMethod,
3887         "ConstraintImpl", "calcVelocityErrorsVirtual");
3888 }
3889 
calcVelocityDotErrorsVirtual(const State & state,const Array_<SpatialVec,ConstrainedBodyIndex> & A_AB,const Array_<Real,ConstrainedUIndex> & constrainedUDot,Array_<Real> & vaerr) const3890 void ConstraintImpl::calcVelocityDotErrorsVirtual
3891    (const State&                                    state,
3892     const Array_<SpatialVec,ConstrainedBodyIndex>&  A_AB,
3893     const Array_<Real,      ConstrainedUIndex>&     constrainedUDot,
3894     Array_<Real>&                                   vaerr)
3895     const {
3896     SimTK_THROW2(Exception::UnimplementedVirtualMethod,
3897         "ConstraintImpl", "calcVelocityDotErrorsVirtual");
3898 }
3899 
addInVelocityConstraintForcesVirtual(const State & state,const Array_<Real> & multipliers,Array_<SpatialVec,ConstrainedBodyIndex> & bodyForces,Array_<Real,ConstrainedUIndex> & mobilityForces) const3900 void ConstraintImpl::addInVelocityConstraintForcesVirtual
3901    (const State&                                    state,
3902     const Array_<Real>&                             multipliers,
3903     Array_<SpatialVec,ConstrainedBodyIndex>&        bodyForces,
3904     Array_<Real,      ConstrainedUIndex>&           mobilityForces)
3905     const
3906 {
3907     SimTK_THROW2(Exception::UnimplementedVirtualMethod,
3908         "ConstraintImpl", "addInVelocityConstraintForcesVirtual");
3909 }
3910 
3911 
3912     // These two must be defined if there are any acceleration-only constraints
3913     // defined.
3914 
calcAccelerationErrorsVirtual(const State & state,const Array_<SpatialVec,ConstrainedBodyIndex> & A_AB,const Array_<Real,ConstrainedUIndex> & constrainedUDot,Array_<Real> & aerr) const3915 void ConstraintImpl::calcAccelerationErrorsVirtual
3916    (const State&                                    state,
3917     const Array_<SpatialVec,ConstrainedBodyIndex>&  A_AB,
3918     const Array_<Real,      ConstrainedUIndex>&     constrainedUDot,
3919     Array_<Real>&                                   aerr)
3920     const {
3921     SimTK_THROW2(Exception::UnimplementedVirtualMethod,
3922         "ConstraintImpl", "calcAccelerationErrorsVirtual");
3923 }
3924 
addInAccelerationConstraintForcesVirtual(const State & state,const Array_<Real> & multipliers,Array_<SpatialVec,ConstrainedBodyIndex> & bodyForces,Array_<Real,ConstrainedUIndex> & mobilityForces) const3925 void ConstraintImpl::addInAccelerationConstraintForcesVirtual
3926    (const State&                                    state,
3927     const Array_<Real>&                             multipliers,
3928     Array_<SpatialVec,ConstrainedBodyIndex>&        bodyForces,
3929     Array_<Real,      ConstrainedUIndex>&           mobilityForces)
3930     const
3931 {
3932     SimTK_THROW2(Exception::UnimplementedVirtualMethod,
3933         "ConstraintImpl", "addInAccelerationConstraintForcesVirtual");
3934 }
3935 
3936 //------------------------------------------------------------------------------
3937 // These are interfaces to the constraint operators which first extract
3938 // the operands from a given state. These are thus suitable for use when
3939 // realizing that state at the point where the constraint operator results
3940 // are about to go into the state cache. The cache is not updated here,
3941 // however. Instead the result is returned explicitly in an argument.
3942 //------------------------------------------------------------------------------
3943 
3944 // Calculate the mp position errors that would result from the configuration
3945 // present in the supplied state (that is, q's and body transforms). The state
3946 // must be realized through Time stage and part way through realization of
3947 // Position stage.
3948 void ConstraintImpl::
calcPositionErrorsFromState(const State & s,Array_<Real> & perr) const3949 calcPositionErrorsFromState(const State& s, Array_<Real>& perr) const {
3950     const SBInstanceCache&             ic    = getInstanceCache(s);
3951     const SBInstancePerConstraintInfo& cInfo =
3952         ic.getConstraintInstanceInfo(myConstraintIndex);
3953     const Vector& q = s.getQ();
3954 
3955     const int ncb = getNumConstrainedBodies();
3956     const int ncq = cInfo.getNumConstrainedQ();
3957 
3958     Array_<Transform, ConstrainedBodyIndex> X_AB(ncb);
3959     Array_<Real, ConstrainedQIndex>         cq(ncq);
3960 
3961     for (ConstrainedBodyIndex cbx(0); cbx < ncb; ++cbx)
3962         X_AB[cbx] = getBodyTransformFromState(s, cbx);
3963 
3964     for (ConstrainedQIndex cqx(0); cqx < ncq; ++cqx)
3965         cq[cqx] = q[cInfo.getQIndexFromConstrainedQ(cqx)];
3966 
3967     calcPositionErrors(s,X_AB,cq,perr);
3968 }
3969 
3970 // Calculate the mp velocity errors resulting from pdot equations, given a
3971 // configuration and velocities in the supplied state which must be realized
3972 // through Position stage and part way through realization of Velocity stage.
3973 void ConstraintImpl::
calcPositionDotErrorsFromState(const State & s,Array_<Real> & pverr) const3974 calcPositionDotErrorsFromState(const State& s, Array_<Real>& pverr) const {
3975     const SBInstanceCache&             ic    = getInstanceCache(s);
3976     const SBInstancePerConstraintInfo& cInfo =
3977         ic.getConstraintInstanceInfo(myConstraintIndex);
3978 
3979     // We're not checking, but the tree velocity cache better have been
3980     // marked valid by now, indicating that we finished calculating qdots.
3981     // The state doesn't know about that yet, so we have to use updQDot()
3982     // here rather than getQDot().
3983     const Vector& qdot = s.updQDot();
3984 
3985     const int ncb = getNumConstrainedBodies();
3986     const int ncq = cInfo.getNumConstrainedQ();
3987 
3988     Array_<SpatialVec, ConstrainedBodyIndex> V_AB(ncb);
3989     Array_<Real, ConstrainedQIndex>          cqdot(ncq);
3990 
3991     for (ConstrainedBodyIndex cbx(0); cbx < ncb; ++cbx)
3992         V_AB[cbx] = getBodyVelocityFromState(s, cbx);
3993 
3994     for (ConstrainedQIndex cqx(0); cqx < ncq; ++cqx)
3995         cqdot[cqx] = qdot[cInfo.getQIndexFromConstrainedQ(cqx)];
3996 
3997     calcPositionDotErrors(s,V_AB,cqdot,pverr);
3998 }
3999 
4000 
4001 
4002 // Calculate the mv velocity errors resulting from the nonholonomic constraint
4003 // equations here, taking the configuration and velocities (u, qdot, body
4004 // spatial velocities) from the supplied state, which must be realized through
4005 // Position stage and part way through realization of Velocity stage.
4006 void ConstraintImpl::
calcVelocityErrorsFromState(const State & s,Array_<Real> & verr) const4007 calcVelocityErrorsFromState(const State& s, Array_<Real>& verr) const {
4008     const SBInstanceCache&             ic = getInstanceCache(s);
4009     const SBInstancePerConstraintInfo& cInfo =
4010         ic.getConstraintInstanceInfo(myConstraintIndex);
4011     const Vector& u = s.getU();
4012 
4013     const int ncb = getNumConstrainedBodies();
4014     const int ncu = cInfo.getNumConstrainedU();
4015 
4016     Array_<SpatialVec, ConstrainedBodyIndex> V_AB(ncb);
4017     Array_<Real, ConstrainedUIndex>          cu(ncu);
4018 
4019     for (ConstrainedBodyIndex cbx(0); cbx < ncb; ++cbx)
4020         V_AB[cbx] = getBodyVelocityFromState(s, cbx);
4021 
4022     for (ConstrainedUIndex cux(0); cux < ncu; ++cux)
4023         cu[cux] = u[cInfo.getUIndexFromConstrainedU(cux)];
4024 
4025     calcVelocityErrorsVirtual(s,V_AB,cu,verr);
4026 }
4027 
4028 
4029 
4030 } // namespace SimTK
4031 
4032