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