1 // =============================================================================
2 // PROJECT CHRONO - http://projectchrono.org
3 //
4 // Copyright (c) 2014 projectchrono.org
5 // All rights reserved.
6 //
7 // Use of this source code is governed by a BSD-style license that can be found
8 // in the LICENSE file at the top level of the distribution and at
9 // http://projectchrono.org/license-chrono.txt.
10 //
11 // =============================================================================
12 // Authors: Alessandro Tasora, Radu Serban
13 // =============================================================================
14 
15 #ifndef CHCONSTRAINTTUPLE_H
16 #define CHCONSTRAINTTUPLE_H
17 
18 #include "chrono/solver/ChConstraint.h"
19 #include "chrono/solver/ChVariables.h"
20 
21 namespace chrono {
22 
23 /// This is a container for 'half' of a constraint, and contains a tuple of
24 /// 1 or 2 or 3 differently-sized jacobian chunks. For instance, this might
25 /// happen because you want a constraint between an edge (i.e. two xyz variables, each
26 /// per end nodes) and a triangle face (i.e. three xyz variables, each per corner), so
27 /// the jacobian row matrix is split in 2 + 3 chunks, here as two tuples.
28 /// The complete constraint, ChConstraintTwoTuples, will use two of these classes.
29 /// Template T is a class of ChVariableTupleCarrier_Nvars type
30 
31 template <class T>
32 class ChConstraintTuple_1vars {
33   protected:
34     ChVariables* variables;              ///< The constrained object
35     ChRowVectorN<double, T::nvars1> Cq;  ///< The [Cq] jacobian of the constraint
36     ChVectorN<double, T::nvars1> Eq;     ///< The [Eq] product [Eq]=[invM]*[Cq]'
37 
38   public:
39     /// Default constructor
ChConstraintTuple_1vars()40     ChConstraintTuple_1vars() : variables(nullptr) {
41         Cq.setZero();
42         Eq.setZero();
43     }
44 
45     /// Copy constructor
ChConstraintTuple_1vars(const ChConstraintTuple_1vars & other)46     ChConstraintTuple_1vars(const ChConstraintTuple_1vars& other) {
47         variables = other.variables;
48         Cq = other.Cq;
49         Eq = other.Eq;
50     }
51 
~ChConstraintTuple_1vars()52     ~ChConstraintTuple_1vars() {}
53 
54     /// Assignment operator: copy from other object
55     ChConstraintTuple_1vars& operator=(const ChConstraintTuple_1vars& other) {
56         variables = other.variables;
57         Cq = other.Cq;
58         Eq = other.Eq;
59         return *this;
60     }
61 
Get_Cq()62     ChRowVectorRef Get_Cq() { return Cq; }
63 
Get_Eq()64     ChVectorRef Get_Eq() { return Eq; }
65 
GetVariables()66     ChVariables* GetVariables() { return variables; }
67 
SetVariables(T & m_tuple_carrier)68     void SetVariables(T& m_tuple_carrier) {
69         if (!m_tuple_carrier.GetVariables1()) {
70             throw ChException("ERROR. SetVariables() getting null pointer. \n");
71         }
72         variables = m_tuple_carrier.GetVariables1();
73     }
74 
Update_auxiliary(double & g_i)75     void Update_auxiliary(double& g_i) {
76         // 1- Assuming jacobians are already computed, now compute
77         //   the matrices [Eq]=[invM]*[Cq]' and [Eq]
78         if (variables->IsActive()) {
79             variables->Compute_invMb_v(Eq, Cq.transpose());
80         }
81 
82         // 2- Compute g_i = [Cq_i]*[invM_i]*[Cq_i]' + cfm_i
83         if (variables->IsActive()) {
84             g_i += Cq * Eq;
85         }
86     }
87 
Compute_Cq_q()88     double Compute_Cq_q() {
89         double ret = 0;
90 
91         if (variables->IsActive()) {
92             ret += Cq * variables->Get_qb();
93         }
94 
95         return ret;
96     }
97 
Increment_q(const double deltal)98     void Increment_q(const double deltal) {
99         if (variables->IsActive()) {
100             variables->Get_qb() += Eq * deltal;
101         }
102     }
103 
MultiplyAndAdd(double & result,const ChVectorDynamic<double> & vect)104     void MultiplyAndAdd(double& result, const ChVectorDynamic<double>& vect) const {
105         if (variables->IsActive()) {
106             result += Cq * vect.segment(variables->GetOffset(), T::nvars1);
107         }
108     }
109 
MultiplyTandAdd(ChVectorDynamic<double> & result,double l)110     void MultiplyTandAdd(ChVectorDynamic<double>& result, double l) {
111         if (variables->IsActive()) {
112             result.segment(variables->GetOffset(), T::nvars1) += Cq.transpose() * l;
113         }
114     }
115 
Build_Cq(ChSparseMatrix & storage,int insrow)116     void Build_Cq(ChSparseMatrix& storage, int insrow) {
117         if (variables->IsActive())
118             PasteMatrix(storage, Cq, insrow, variables->GetOffset());
119     }
120 
Build_CqT(ChSparseMatrix & storage,int inscol)121     void Build_CqT(ChSparseMatrix& storage, int inscol) {
122         if (variables->IsActive())
123             PasteMatrix(storage, Cq.transpose(), variables->GetOffset(), inscol);
124     }
125 };
126 
127 /// Case of tuple with reference to 2 ChVariable objects:
128 
129 template <class T>
130 class ChConstraintTuple_2vars {
131   protected:
132     ChVariables* variables_1;
133     ChVariables* variables_2;
134 
135     /// The [Cq] jacobian of the constraint, split in horizontal chunks
136     ChRowVectorN<double, T::nvars1> Cq_1;
137     ChRowVectorN<double, T::nvars2> Cq_2;
138 
139     /// The [Eq] product [Eq]=[invM]*[Cq]'
140     ChVectorN<double, T::nvars1> Eq_1;
141     ChVectorN<double, T::nvars2> Eq_2;
142 
143   public:
144     /// Default constructor
ChConstraintTuple_2vars()145     ChConstraintTuple_2vars() : variables_1(nullptr), variables_2(nullptr) {
146         Cq_1.setZero();
147         Cq_2.setZero();
148     }
149 
150     /// Copy constructor
ChConstraintTuple_2vars(const ChConstraintTuple_2vars & other)151     ChConstraintTuple_2vars(const ChConstraintTuple_2vars& other) {
152         variables_1 = other.variables_1;
153         variables_2 = other.variables_2;
154         Cq_1 = other.Cq_1;
155         Cq_2 = other.Cq_2;
156         Eq_1 = other.Eq_1;
157         Eq_2 = other.Eq_2;
158     }
159 
~ChConstraintTuple_2vars()160     ~ChConstraintTuple_2vars() {}
161 
162     /// Assignment operator: copy from other object
163     ChConstraintTuple_2vars& operator=(const ChConstraintTuple_2vars& other) {
164         variables_1 = other.variables_1;
165         variables_2 = other.variables_2;
166         Cq_1 = other.Cq_1;
167         Cq_2 = other.Cq_2;
168         Eq_1 = other.Eq_1;
169         Eq_2 = other.Eq_2;
170         return *this;
171     }
172 
Get_Cq_1()173     ChRowVectorRef Get_Cq_1() { return Cq_1; }
Get_Cq_2()174     ChRowVectorRef Get_Cq_2() { return Cq_2; }
175 
Get_Eq_1()176     ChVectorRef Get_Eq_1() { return Eq_1; }
Get_Eq_2()177     ChVectorRef Get_Eq_2() { return Eq_2; }
178 
GetVariables_1()179     ChVariables* GetVariables_1() { return variables_1; }
GetVariables_2()180     ChVariables* GetVariables_2() { return variables_2; }
181 
SetVariables(T & m_tuple_carrier)182     void SetVariables(T& m_tuple_carrier) {
183         if (!m_tuple_carrier.GetVariables1() || !m_tuple_carrier.GetVariables2()) {
184             throw ChException("ERROR. SetVariables() getting null pointer. \n");
185         }
186         variables_1 = m_tuple_carrier.GetVariables1();
187         variables_2 = m_tuple_carrier.GetVariables2();
188     }
189 
Update_auxiliary(double & g_i)190     void Update_auxiliary(double& g_i) {
191         // 1- Assuming jacobians are already computed, now compute
192         //   the matrices [Eq_a]=[invM_a]*[Cq_a]' and [Eq_b]
193         if (variables_1->IsActive()) {
194             variables_1->Compute_invMb_v(Eq_1, Cq_1.transpose());
195         }
196         if (variables_2->IsActive()) {
197             variables_2->Compute_invMb_v(Eq_2, Cq_2.transpose());
198         }
199 
200         // 2- Compute g_i = [Cq_i]*[invM_i]*[Cq_i]' + cfm_i
201         if (variables_1->IsActive()) {
202             g_i += Cq_1 * Eq_1;
203         }
204         if (variables_2->IsActive()) {
205             g_i += Cq_2 * Eq_2;
206         }
207     }
208 
Compute_Cq_q()209     double Compute_Cq_q() {
210         double ret = 0;
211 
212         if (variables_1->IsActive()) {
213             ret += Cq_1 * variables_1->Get_qb();
214         }
215 
216         if (variables_2->IsActive()) {
217             ret += Cq_2 * variables_2->Get_qb();
218         }
219 
220         return ret;
221     }
222 
Increment_q(const double deltal)223     void Increment_q(const double deltal) {
224         if (variables_1->IsActive()) {
225             variables_1->Get_qb() += Eq_1 * deltal;
226         }
227 
228         if (variables_2->IsActive()) {
229             variables_2->Get_qb() += Eq_2 * deltal;
230         }
231     }
232 
MultiplyAndAdd(double & result,const ChVectorDynamic<double> & vect)233     void MultiplyAndAdd(double& result, const ChVectorDynamic<double>& vect) const {
234         if (variables_1->IsActive()) {
235             result += Cq_1 * vect.segment(variables_1->GetOffset(), T::nvars1);
236         }
237 
238         if (variables_2->IsActive()) {
239             result += Cq_2 * vect.segment(variables_2->GetOffset(), T::nvars2);
240         }
241     }
242 
MultiplyTandAdd(ChVectorDynamic<double> & result,double l)243     void MultiplyTandAdd(ChVectorDynamic<double>& result, double l) {
244         if (variables_1->IsActive()) {
245             result.segment(variables_1->GetOffset(), T::nvars1) += Cq_1.transpose() * l;
246         }
247 
248         if (variables_2->IsActive()) {
249             result.segment(variables_2->GetOffset(), T::nvars2) += Cq_2.transpose() * l;
250         }
251     }
252 
Build_Cq(ChSparseMatrix & storage,int insrow)253     void Build_Cq(ChSparseMatrix& storage, int insrow) {
254         if (variables_1->IsActive())
255             PasteMatrix(storage, Cq_1, insrow, variables_1->GetOffset());
256         if (variables_2->IsActive())
257             PasteMatrix(storage, Cq_2, insrow, variables_2->GetOffset());
258     }
259 
Build_CqT(ChSparseMatrix & storage,int inscol)260     void Build_CqT(ChSparseMatrix& storage, int inscol) {
261         if (variables_1->IsActive())
262             PasteMatrix(storage, Cq_1.transpose(), variables_1->GetOffset(), inscol);
263         if (variables_2->IsActive())
264             PasteMatrix(storage, Cq_2.transpose(), variables_2->GetOffset(), inscol);
265     }
266 };
267 
268 /// Case of tuple with reference to 3 ChVariable objects:
269 
270 template <class T>
271 class ChConstraintTuple_3vars {
272   protected:
273     ChVariables* variables_1;
274     ChVariables* variables_2;
275     ChVariables* variables_3;
276 
277     /// The [Cq] jacobian of the constraint, split in horizontal chunks
278     ChRowVectorN<double, T::nvars1> Cq_1;
279     ChRowVectorN<double, T::nvars2> Cq_2;
280     ChRowVectorN<double, T::nvars3> Cq_3;
281 
282     /// The [Eq] product [Eq]=[invM]*[Cq]'
283     ChVectorN<double, T::nvars1> Eq_1;
284     ChVectorN<double, T::nvars2> Eq_2;
285     ChVectorN<double, T::nvars3> Eq_3;
286 
287   public:
288     /// Default constructor
ChConstraintTuple_3vars()289     ChConstraintTuple_3vars() : variables_1(nullptr), variables_2(nullptr), variables_3(nullptr) {
290         Cq_1.setZero();
291         Cq_2.setZero();
292         Cq_3.setZero();
293     }
294 
295     /// Copy constructor
ChConstraintTuple_3vars(const ChConstraintTuple_3vars & other)296     ChConstraintTuple_3vars(const ChConstraintTuple_3vars& other) {
297         variables_1 = other.variables_1;
298         variables_2 = other.variables_2;
299         variables_3 = other.variables_3;
300         Cq_1 = other.Cq_1;
301         Cq_2 = other.Cq_2;
302         Cq_3 = other.Cq_3;
303         Eq_1 = other.Eq_1;
304         Eq_2 = other.Eq_2;
305         Eq_3 = other.Eq_3;
306     }
307 
~ChConstraintTuple_3vars()308     ~ChConstraintTuple_3vars() {}
309 
310     /// Assignment operator: copy from other object
311     ChConstraintTuple_3vars& operator=(const ChConstraintTuple_3vars& other) {
312         variables_1 = other.variables_1;
313         variables_2 = other.variables_2;
314         variables_3 = other.variables_3;
315         Cq_1 = other.Cq_1;
316         Cq_2 = other.Cq_2;
317         Cq_3 = other.Cq_3;
318         Eq_1 = other.Eq_1;
319         Eq_2 = other.Eq_2;
320         Eq_3 = other.Eq_3;
321         return *this;
322     }
323 
Get_Cq_1()324     ChRowVectorRef Get_Cq_1() { return Cq_1; }
Get_Cq_2()325     ChRowVectorRef Get_Cq_2() { return Cq_2; }
Get_Cq_3()326     ChRowVectorRef Get_Cq_3() { return Cq_3; }
327 
Get_Eq_1()328     ChVectorRef Get_Eq_1() { return Eq_1; }
Get_Eq_2()329     ChVectorRef Get_Eq_2() { return Eq_2; }
Get_Eq_3()330     ChVectorRef Get_Eq_3() { return Eq_3; }
331 
GetVariables_1()332     ChVariables* GetVariables_1() { return variables_1; }
GetVariables_2()333     ChVariables* GetVariables_2() { return variables_2; }
GetVariables_3()334     ChVariables* GetVariables_3() { return variables_3; }
335 
SetVariables(T & m_tuple_carrier)336     void SetVariables(T& m_tuple_carrier) {
337         if (!m_tuple_carrier.GetVariables1() || !m_tuple_carrier.GetVariables2() || !m_tuple_carrier.GetVariables3()) {
338             throw ChException("ERROR. SetVariables() getting null pointer. \n");
339         }
340         variables_1 = m_tuple_carrier.GetVariables1();
341         variables_2 = m_tuple_carrier.GetVariables2();
342         variables_3 = m_tuple_carrier.GetVariables3();
343     }
344 
Update_auxiliary(double & g_i)345     void Update_auxiliary(double& g_i) {
346         // 1- Assuming jacobians are already computed, now compute
347         //   the matrices [Eq_a]=[invM_a]*[Cq_a]' and [Eq_b]
348         if (variables_1->IsActive()) {
349             variables_1->Compute_invMb_v(Eq_1, Cq_1.transpose());
350         }
351         if (variables_2->IsActive()) {
352             variables_2->Compute_invMb_v(Eq_2, Cq_2.transpose());
353         }
354         if (variables_3->IsActive()) {
355             variables_3->Compute_invMb_v(Eq_3, Cq_3.transpose());
356         }
357 
358         // 2- Compute g_i = [Cq_i]*[invM_i]*[Cq_i]' + cfm_i
359         if (variables_1->IsActive()) {
360             g_i += Cq_1 * Eq_1;
361         }
362         if (variables_2->IsActive()) {
363             g_i += Cq_2 * Eq_2;
364         }
365         if (variables_3->IsActive()) {
366             g_i += Cq_3 * Eq_3;
367         }
368     }
369 
Compute_Cq_q()370     double Compute_Cq_q() {
371         double ret = 0;
372 
373         if (variables_1->IsActive()) {
374             ret += Cq_1 * variables_1->Get_qb();
375         }
376 
377         if (variables_2->IsActive()) {
378             ret += Cq_2 * variables_2->Get_qb();
379         }
380 
381         if (variables_3->IsActive()) {
382             ret += Cq_3 * variables_3->Get_qb();
383         }
384 
385         return ret;
386     }
387 
Increment_q(const double deltal)388     void Increment_q(const double deltal) {
389         if (variables_1->IsActive()) {
390             variables_1->Get_qb() += Eq_1 * deltal;
391         }
392 
393         if (variables_2->IsActive()) {
394             variables_2->Get_qb() += Eq_2 * deltal;
395         }
396 
397         if (variables_3->IsActive()) {
398             variables_3->Get_qb() += Eq_3 * deltal;
399         }
400     }
401 
MultiplyAndAdd(double & result,const ChVectorDynamic<double> & vect)402     void MultiplyAndAdd(double& result, const ChVectorDynamic<double>& vect) const {
403         if (variables_1->IsActive()) {
404             result += Cq_1 * vect.segment(variables_1->GetOffset(), T::nvars1);
405         }
406 
407         if (variables_2->IsActive()) {
408             result += Cq_2 * vect.segment(variables_2->GetOffset(), T::nvars2);
409         }
410 
411         if (variables_3->IsActive()) {
412             result += Cq_3 * vect.segment(variables_3->GetOffset(), T::nvars3);
413         }
414     }
415 
MultiplyTandAdd(ChVectorDynamic<double> & result,double l)416     void MultiplyTandAdd(ChVectorDynamic<double>& result, double l) {
417         if (variables_1->IsActive()) {
418             result.segment(variables_1->GetOffset(), T::nvars1) += Cq_1.transpose() * l;
419         }
420 
421         if (variables_2->IsActive()) {
422             result.segment(variables_2->GetOffset(), T::nvars2) += Cq_2.transpose() * l;
423         }
424 
425         if (variables_3->IsActive()) {
426             result.segment(variables_3->GetOffset(), T::nvars3) += Cq_3.transpose() * l;
427         }
428     }
429 
Build_Cq(ChSparseMatrix & storage,int insrow)430     void Build_Cq(ChSparseMatrix& storage, int insrow) {
431         if (variables_1->IsActive())
432             PasteMatrix(storage, Cq_1, insrow, variables_1->GetOffset());
433         if (variables_2->IsActive())
434             PasteMatrix(storage, Cq_2, insrow, variables_2->GetOffset());
435         if (variables_3->IsActive())
436             PasteMatrix(storage, Cq_3, insrow, variables_3->GetOffset());
437     }
438 
Build_CqT(ChSparseMatrix & storage,int inscol)439     void Build_CqT(ChSparseMatrix& storage, int inscol) {
440         if (variables_1->IsActive())
441             PasteMatrix(storage, Cq_1.transpose(), variables_1->GetOffset(), inscol);
442         if (variables_2->IsActive())
443             PasteMatrix(storage, Cq_2.transpose(), variables_2->GetOffset(), inscol);
444         if (variables_3->IsActive())
445             PasteMatrix(storage, Cq_3.transpose(), variables_3->GetOffset(), inscol);
446     }
447 };
448 
449 
450 /// Case of tuple with reference to 4 ChVariable objects:
451 
452 template <class T>
453 class ChConstraintTuple_4vars {
454   protected:
455     ChVariables* variables_1;
456     ChVariables* variables_2;
457     ChVariables* variables_3;
458     ChVariables* variables_4;
459 
460     /// The [Cq] jacobian of the constraint, split in horizontal chunks
461     ChRowVectorN<double, T::nvars1> Cq_1;
462     ChRowVectorN<double, T::nvars2> Cq_2;
463     ChRowVectorN<double, T::nvars3> Cq_3;
464     ChRowVectorN<double, T::nvars4> Cq_4;
465 
466     /// The [Eq] product [Eq]=[invM]*[Cq]'
467     ChVectorN<double, T::nvars1> Eq_1;
468     ChVectorN<double, T::nvars2> Eq_2;
469     ChVectorN<double, T::nvars3> Eq_3;
470     ChVectorN<double, T::nvars4> Eq_4;
471 
472   public:
473     /// Default constructor
ChConstraintTuple_4vars()474     ChConstraintTuple_4vars() : variables_1(nullptr), variables_2(nullptr), variables_3(nullptr), variables_4(nullptr) {
475         Cq_1.setZero();
476         Cq_2.setZero();
477         Cq_3.setZero();
478         Cq_4.setZero();
479     }
480 
481     /// Copy constructor
ChConstraintTuple_4vars(const ChConstraintTuple_4vars & other)482     ChConstraintTuple_4vars(const ChConstraintTuple_4vars& other) {
483         variables_1 = other.variables_1;
484         variables_2 = other.variables_2;
485         variables_3 = other.variables_3;
486         variables_4 = other.variables_4;
487         Cq_1 = other.Cq_1;
488         Cq_2 = other.Cq_2;
489         Cq_3 = other.Cq_3;
490         Cq_4 = other.Cq_4;
491         Eq_1 = other.Eq_1;
492         Eq_2 = other.Eq_2;
493         Eq_3 = other.Eq_3;
494         Eq_4 = other.Eq_4;
495     }
496 
~ChConstraintTuple_4vars()497     ~ChConstraintTuple_4vars() {}
498 
499     /// Assignment operator: copy from other object
500     ChConstraintTuple_4vars& operator=(const ChConstraintTuple_4vars& other) {
501         variables_1 = other.variables_1;
502         variables_2 = other.variables_2;
503         variables_3 = other.variables_3;
504         variables_4 = other.variables_4;
505         Cq_1 = other.Cq_1;
506         Cq_2 = other.Cq_2;
507         Cq_3 = other.Cq_3;
508         Cq_4 = other.Cq_4;
509         Eq_1 = other.Eq_1;
510         Eq_2 = other.Eq_2;
511         Eq_3 = other.Eq_3;
512         Eq_4 = other.Eq_4;
513         return *this;
514     }
515 
Get_Cq_1()516     ChRowVectorRef Get_Cq_1() { return Cq_1; }
Get_Cq_2()517     ChRowVectorRef Get_Cq_2() { return Cq_2; }
Get_Cq_3()518     ChRowVectorRef Get_Cq_3() { return Cq_3; }
Get_Cq_4()519     ChRowVectorRef Get_Cq_4() { return Cq_4; }
520 
Get_Eq_1()521     ChVectorRef Get_Eq_1() { return Eq_1; }
Get_Eq_2()522     ChVectorRef Get_Eq_2() { return Eq_2; }
Get_Eq_3()523     ChVectorRef Get_Eq_3() { return Eq_3; }
Get_Eq_4()524     ChVectorRef Get_Eq_4() { return Eq_4; }
525 
GetVariables_1()526     ChVariables* GetVariables_1() { return variables_1; }
GetVariables_2()527     ChVariables* GetVariables_2() { return variables_2; }
GetVariables_3()528     ChVariables* GetVariables_3() { return variables_3; }
GetVariables_4()529     ChVariables* GetVariables_4() { return variables_4; }
530 
SetVariables(T & m_tuple_carrier)531     void SetVariables(T& m_tuple_carrier) {
532         if (!m_tuple_carrier.GetVariables1() || !m_tuple_carrier.GetVariables2() || !m_tuple_carrier.GetVariables3() || !m_tuple_carrier.GetVariables4() ) {
533             throw ChException("ERROR. SetVariables() getting null pointer. \n");
534         }
535         variables_1 = m_tuple_carrier.GetVariables1();
536         variables_2 = m_tuple_carrier.GetVariables2();
537         variables_3 = m_tuple_carrier.GetVariables3();
538         variables_4 = m_tuple_carrier.GetVariables4();
539     }
540 
Update_auxiliary(double & g_i)541     void Update_auxiliary(double& g_i) {
542         // 1- Assuming jacobians are already computed, now compute
543         //   the matrices [Eq_a]=[invM_a]*[Cq_a]' and [Eq_b]
544         if (variables_1->IsActive()) {
545             variables_1->Compute_invMb_v(Eq_1, Cq_1.transpose());
546         }
547         if (variables_2->IsActive()) {
548             variables_2->Compute_invMb_v(Eq_2, Cq_2.transpose());
549         }
550         if (variables_3->IsActive()) {
551             variables_3->Compute_invMb_v(Eq_3, Cq_3.transpose());
552         }
553         if (variables_4->IsActive()) {
554             variables_4->Compute_invMb_v(Eq_4, Cq_4.transpose());
555         }
556 
557         // 2- Compute g_i = [Cq_i]*[invM_i]*[Cq_i]' + cfm_i
558         if (variables_1->IsActive()) {
559             g_i += Cq_1 * Eq_1;
560         }
561         if (variables_2->IsActive()) {
562             g_i += Cq_2 * Eq_2;
563         }
564         if (variables_3->IsActive()) {
565             g_i += Cq_3 * Eq_3;
566         }
567         if (variables_4->IsActive()) {
568             g_i += Cq_4 * Eq_4;
569         }
570     }
571 
Compute_Cq_q()572     double Compute_Cq_q() {
573         double ret = 0;
574 
575         if (variables_1->IsActive()) {
576             ret += Cq_1 * variables_1->Get_qb();
577         }
578 
579         if (variables_2->IsActive()) {
580             ret += Cq_2 * variables_2->Get_qb();
581         }
582 
583         if (variables_3->IsActive()) {
584             ret += Cq_3 * variables_3->Get_qb();
585         }
586 
587         if (variables_4->IsActive()) {
588             ret += Cq_4 * variables_4->Get_qb();
589         }
590 
591         return ret;
592     }
593 
Increment_q(const double deltal)594     void Increment_q(const double deltal) {
595         if (variables_1->IsActive()) {
596             variables_1->Get_qb() += Eq_1 * deltal;
597         }
598 
599         if (variables_2->IsActive()) {
600             variables_2->Get_qb() += Eq_2 * deltal;
601         }
602 
603         if (variables_3->IsActive()) {
604             variables_3->Get_qb() += Eq_3 * deltal;
605         }
606 
607         if (variables_4->IsActive()) {
608             variables_4->Get_qb() += Eq_4 * deltal;
609         }
610     }
611 
MultiplyAndAdd(double & result,const ChVectorDynamic<double> & vect)612     void MultiplyAndAdd(double& result, const ChVectorDynamic<double>& vect) const {
613         if (variables_1->IsActive()) {
614             result += Cq_1 * vect.segment(variables_1->GetOffset(), T::nvars1);
615         }
616 
617         if (variables_2->IsActive()) {
618             result += Cq_2 * vect.segment(variables_2->GetOffset(), T::nvars2);
619         }
620 
621         if (variables_3->IsActive()) {
622             result += Cq_3 * vect.segment(variables_3->GetOffset(), T::nvars3);
623         }
624 
625         if (variables_4->IsActive()) {
626             result += Cq_4 * vect.segment(variables_4->GetOffset(), T::nvars4);
627         }
628     }
629 
MultiplyTandAdd(ChVectorDynamic<double> & result,double l)630     void MultiplyTandAdd(ChVectorDynamic<double>& result, double l) {
631         if (variables_1->IsActive()) {
632             result.segment(variables_1->GetOffset(), T::nvars1) += Cq_1.transpose() * l;
633         }
634 
635         if (variables_2->IsActive()) {
636             result.segment(variables_2->GetOffset(), T::nvars2) += Cq_2.transpose() * l;
637         }
638 
639         if (variables_3->IsActive()) {
640             result.segment(variables_3->GetOffset(), T::nvars3) += Cq_3.transpose() * l;
641         }
642 
643         if (variables_4->IsActive()) {
644             result.segment(variables_4->GetOffset(), T::nvars4) += Cq_4.transpose() * l;
645         }
646     }
647 
Build_Cq(ChSparseMatrix & storage,int insrow)648     void Build_Cq(ChSparseMatrix& storage, int insrow) {
649         if (variables_1->IsActive())
650             PasteMatrix(storage, Cq_1, insrow, variables_1->GetOffset());
651         if (variables_2->IsActive())
652             PasteMatrix(storage, Cq_2, insrow, variables_2->GetOffset());
653         if (variables_3->IsActive())
654             PasteMatrix(storage, Cq_3, insrow, variables_3->GetOffset());
655         if (variables_4->IsActive())
656             PasteMatrix(storage, Cq_4, insrow, variables_4->GetOffset());
657     }
658 
Build_CqT(ChSparseMatrix & storage,int inscol)659     void Build_CqT(ChSparseMatrix& storage, int inscol) {
660         if (variables_1->IsActive())
661             PasteMatrix(storage, Cq_1.transpose(), variables_1->GetOffset(), inscol);
662         if (variables_2->IsActive())
663             PasteMatrix(storage, Cq_2.transpose(), variables_2->GetOffset(), inscol);
664         if (variables_3->IsActive())
665             PasteMatrix(storage, Cq_3.transpose(), variables_3->GetOffset(), inscol);
666         if (variables_4->IsActive())
667             PasteMatrix(storage, Cq_4.transpose(), variables_4->GetOffset(), inscol);
668     }
669 };
670 
671 /// This is a set of 'helper' classes that make easier to manage the templated
672 /// structure of the tuple constraints.
673 
674 template <int N1>
675 class ChVariableTupleCarrier_1vars {
676   public:
677     typedef ChConstraintTuple_1vars<ChVariableTupleCarrier_1vars<N1>> type_constraint_tuple;
678     static const int nvars1 = N1;
~ChVariableTupleCarrier_1vars()679     virtual ~ChVariableTupleCarrier_1vars() {}
680     virtual ChVariables* GetVariables1() = 0;
681 };
682 
683 template <int N1, int N2>
684 class ChVariableTupleCarrier_2vars {
685   public:
686     typedef ChConstraintTuple_2vars<ChVariableTupleCarrier_2vars<N1, N2>> type_constraint_tuple;
687     static int const nvars1 = N1;
688     static int const nvars2 = N2;
~ChVariableTupleCarrier_2vars()689     virtual ~ChVariableTupleCarrier_2vars() {}
690     virtual ChVariables* GetVariables1() = 0;
691     virtual ChVariables* GetVariables2() = 0;
692 };
693 
694 template <int N1, int N2, int N3>
695 class ChVariableTupleCarrier_3vars {
696   public:
697     typedef ChConstraintTuple_3vars<ChVariableTupleCarrier_3vars<N1, N2, N3>> type_constraint_tuple;
698     static int const nvars1 = N1;
699     static int const nvars2 = N2;
700     static int const nvars3 = N3;
~ChVariableTupleCarrier_3vars()701     virtual ~ChVariableTupleCarrier_3vars() {}
702     virtual ChVariables* GetVariables1() = 0;
703     virtual ChVariables* GetVariables2() = 0;
704     virtual ChVariables* GetVariables3() = 0;
705 };
706 
707 template <int N1, int N2, int N3, int N4>
708 class ChVariableTupleCarrier_4vars {
709   public:
710     typedef ChConstraintTuple_4vars<ChVariableTupleCarrier_4vars<N1, N2, N3, N4>> type_constraint_tuple;
711     static int const nvars1 = N1;
712     static int const nvars2 = N2;
713     static int const nvars3 = N3;
714     static int const nvars4 = N4;
~ChVariableTupleCarrier_4vars()715     virtual ~ChVariableTupleCarrier_4vars() {}
716     virtual ChVariables* GetVariables1() = 0;
717     virtual ChVariables* GetVariables2() = 0;
718     virtual ChVariables* GetVariables3() = 0;
719     virtual ChVariables* GetVariables4() = 0;
720 };
721 
722 }  // end namespace chrono
723 
724 #endif
725