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 #include "chrono/solver/ChSystemDescriptor.h"
16 #include "chrono/solver/ChConstraintTwoTuplesContactN.h"
17 #include "chrono/solver/ChConstraintTwoTuplesFrictionT.h"
18 #include "chrono/core/ChMatrix.h"
19 
20 namespace chrono {
21 
22 // Register into the object factory, to enable run-time
23 // dynamic creation and persistence
CH_FACTORY_REGISTER(ChSystemDescriptor)24 CH_FACTORY_REGISTER(ChSystemDescriptor)
25 
26 #define CH_SPINLOCK_HASHSIZE 203
27 
28 ChSystemDescriptor::ChSystemDescriptor() : n_q(0), n_c(0), c_a(1.0), freeze_count(false) {
29     vconstraints.clear();
30     vvariables.clear();
31     vstiffness.clear();
32 }
33 
~ChSystemDescriptor()34 ChSystemDescriptor::~ChSystemDescriptor() {
35     vconstraints.clear();
36     vvariables.clear();
37     vstiffness.clear();
38 }
39 
ComputeFeasabilityViolation(double & resulting_maxviolation,double & resulting_feasability)40 void ChSystemDescriptor::ComputeFeasabilityViolation(double& resulting_maxviolation, double& resulting_feasability) {
41     resulting_maxviolation = 0;
42     resulting_feasability = 0;
43 
44     auto vc_size = vconstraints.size();
45 
46     for (size_t ic = 0; ic < vc_size; ic++) {
47         // the the residual of the constraint..
48         double mres_i = vconstraints[ic]->Compute_c_i();
49 
50         double candidate_violation = fabs(vconstraints[ic]->Violation(mres_i));
51 
52         if (candidate_violation > resulting_maxviolation)
53             resulting_maxviolation = candidate_violation;
54 
55         if (vconstraints[ic]->IsUnilateral()) {
56             double candidate_feas = fabs(mres_i * vconstraints[ic]->Get_l_i());  // =|c*l|
57             if (candidate_feas > resulting_feasability)
58                 resulting_feasability = candidate_feas;
59         }
60     }
61 }
62 
CountActiveVariables()63 int ChSystemDescriptor::CountActiveVariables() {
64     if (freeze_count)  // optimization, avoid list count all times
65         return n_q;
66 
67     auto vv_size = vvariables.size();
68 
69     n_q = 0;
70     for (size_t iv = 0; iv < vv_size; iv++) {
71         if (vvariables[iv]->IsActive()) {
72             vvariables[iv]->SetOffset(n_q);  // also store offsets in state and MC matrix
73             n_q += vvariables[iv]->Get_ndof();
74         }
75     }
76     return n_q;
77 }
78 
CountActiveConstraints()79 int ChSystemDescriptor::CountActiveConstraints() {
80     if (freeze_count)  // optimization, avoid list count all times
81         return n_c;
82 
83     auto vc_size = vconstraints.size();
84 
85     n_c = 0;
86     for (size_t ic = 0; ic < vc_size; ic++) {
87         if (vconstraints[ic]->IsActive()) {
88             vconstraints[ic]->SetOffset(n_c);  // also store offsets in state and MC matrix
89             n_c++;
90         }
91     }
92     return n_c;
93 }
94 
UpdateCountsAndOffsets()95 void ChSystemDescriptor::UpdateCountsAndOffsets() {
96     freeze_count = false;
97     CountActiveVariables();
98     CountActiveConstraints();
99     freeze_count = true;
100 }
101 
ConvertToMatrixForm(ChSparseMatrix * Cq,ChSparseMatrix * H,ChSparseMatrix * E,ChVectorDynamic<> * Fvector,ChVectorDynamic<> * Bvector,ChVectorDynamic<> * Frict,bool only_bilaterals,bool skip_contacts_uv)102 void ChSystemDescriptor::ConvertToMatrixForm(ChSparseMatrix* Cq,
103                                              ChSparseMatrix* H,
104                                              ChSparseMatrix* E,
105                                              ChVectorDynamic<>* Fvector,
106                                              ChVectorDynamic<>* Bvector,
107                                              ChVectorDynamic<>* Frict,
108                                              bool only_bilaterals,
109                                              bool skip_contacts_uv) {
110     std::vector<ChConstraint*>& mconstraints = GetConstraintsList();
111     std::vector<ChVariables*>& mvariables = GetVariablesList();
112 
113     auto mv_size = mvariables.size();
114     auto mc_size = mconstraints.size();
115     auto vs_size = vstiffness.size();
116 
117     // Count bilateral and other constraints.. (if wanted, bilaterals only)
118 
119     int mn_c = 0;
120     for (size_t ic = 0; ic < mc_size; ic++) {
121         if (mconstraints[ic]->IsActive())
122             if (!((mconstraints[ic]->GetMode() == CONSTRAINT_FRIC) && only_bilaterals))
123                 if (!((dynamic_cast<ChConstraintTwoTuplesFrictionTall*>(mconstraints[ic])) && skip_contacts_uv)) {
124                     mn_c++;
125                 }
126     }
127 
128     // Count active variables, by scanning through all variable blocks,
129     // and set offsets
130 
131     n_q = CountActiveVariables();
132 
133     // Reset and resize (if needed) auxiliary vectors
134 
135     if (Cq)
136         Cq->resize(mn_c, n_q);
137     if (H)
138         H->resize(n_q, n_q);
139     if (E)
140         E->resize(mn_c, mn_c);
141     if (Fvector)
142         Fvector->setZero(n_q);
143     if (Bvector)
144         Bvector->setZero(mn_c);
145     if (Frict)
146         Frict->setZero(mn_c);
147 
148     // Fills H submasses and 'f' vector,
149     // by looping on variables
150     int s_q = 0;
151     for (size_t iv = 0; iv < mv_size; iv++) {
152         if (mvariables[iv]->IsActive()) {
153             if (H)
154                 mvariables[iv]->Build_M(*H, s_q, s_q, c_a);  // .. fills  H  (often H=M , the mass)
155             if (Fvector)
156                 Fvector->segment(s_q, vvariables[iv]->Get_ndof()) = vvariables[iv]->Get_fb();  // .. fills  'f'
157             s_q += mvariables[iv]->Get_ndof();
158         }
159     }
160 
161     // If some stiffness / hessian matrix has been added to H ,
162     // also add it to the sparse H
163     if (H) {
164         for (size_t ik = 0; ik < vs_size; ik++) {
165             vstiffness[ik]->Build_K(*H, true);
166         }
167     }
168 
169     // Fills Cq jacobian, E 'compliance' matrix , the 'b' vector and friction coeff.vector,
170     // by looping on constraints
171     int s_c = 0;
172     for (size_t ic = 0; ic < mc_size; ic++) {
173         if (mconstraints[ic]->IsActive())
174             if (!((mconstraints[ic]->GetMode() == CONSTRAINT_FRIC) && only_bilaterals))
175                 if (!((dynamic_cast<ChConstraintTwoTuplesFrictionTall*>(mconstraints[ic])) && skip_contacts_uv)) {
176                     if (Cq)
177                         mconstraints[ic]->Build_Cq(*Cq, s_c);  // .. fills Cq
178                     if (E)
179                         E->SetElement(s_c, s_c, mconstraints[ic]->Get_cfm_i());  // .. fills E ( = cfm )
180                     if (Bvector)
181                         (*Bvector)(s_c) = mconstraints[ic]->Get_b_i();  // .. fills 'b'
182                     if (Frict)                                          // .. fills vector of friction coefficients
183                     {
184                         (*Frict)(s_c) = -2;  // mark with -2 flag for bilaterals (default)
185                         if (auto mcon = dynamic_cast<ChConstraintTwoTuplesContactNall*>(mconstraints[ic]))
186                             (*Frict)(s_c) =
187                                 mcon->GetFrictionCoefficient();  // friction coeff only in row of normal component
188                         if (dynamic_cast<ChConstraintTwoTuplesFrictionTall*>(mconstraints[ic]))
189                             (*Frict)(s_c) = -1;  // mark with -1 flag for rows of tangential components
190                     }
191                     s_c++;
192                 }
193     }
194 }
195 
ConvertToMatrixForm(ChSparseMatrix * Z,ChVectorDynamic<> * rhs)196 void ChSystemDescriptor::ConvertToMatrixForm(ChSparseMatrix* Z, ChVectorDynamic<>* rhs) {
197     std::vector<ChConstraint*>& mconstraints = GetConstraintsList();
198     std::vector<ChVariables*>& mvariables = GetVariablesList();
199 
200     auto mv_size = mvariables.size();
201     auto mc_size = mconstraints.size();
202     auto vs_size = vstiffness.size();
203 
204     // Count constraints.
205     int mn_c = 0;
206     for (size_t ic = 0; ic < mc_size; ic++) {
207         if (mconstraints[ic]->IsActive())
208             mn_c++;
209     }
210 
211     // Count active variables, by scanning through all variable blocks, and set offsets.
212     n_q = CountActiveVariables();
213 
214     if (Z) {
215         Z->conservativeResize(n_q + mn_c, n_q + mn_c);
216         Z->setZeroValues();
217 
218         // Fill Z with masses and inertias.
219         int s_q = 0;
220         for (size_t iv = 0; iv < mv_size; iv++) {
221             if (mvariables[iv]->IsActive()) {
222                 // Masses and inertias in upper-left block of Z
223                 mvariables[iv]->Build_M(*Z, s_q, s_q, c_a);
224                 s_q += mvariables[iv]->Get_ndof();
225             }
226         }
227 
228         // If present, add stiffness matrix K to upper-left block of Z.
229         for (size_t ik = 0; ik < vs_size; ik++) {
230             vstiffness[ik]->Build_K(*Z, true);
231         }
232 
233         // Fill Z by looping over constraints.
234         int s_c = 0;
235         for (size_t ic = 0; ic < mc_size; ic++) {
236             if (mconstraints[ic]->IsActive()) {
237                 // Constraint Jacobian in lower-left block of Z
238                 mconstraints[ic]->Build_Cq(*Z, n_q + s_c);
239                 // Transposed constraint Jacobian in upper-right block of Z
240                 mconstraints[ic]->Build_CqT(*Z, n_q + s_c);
241                 // E ( = cfm ) in lower-right block of Z
242                 Z->SetElement(n_q + s_c, n_q + s_c, mconstraints[ic]->Get_cfm_i());
243                 s_c++;
244             }
245         }
246     }
247 
248     if (rhs) {
249         rhs->setZero(n_q + mn_c, 1);
250 
251         // Fill rhs with forces.
252         int s_q = 0;
253         for (size_t iv = 0; iv < mv_size; iv++) {
254             if (mvariables[iv]->IsActive()) {
255                 // Forces in upper section of rhs
256                 rhs->segment(s_q, vvariables[iv]->Get_ndof()) = vvariables[iv]->Get_fb();
257                 s_q += mvariables[iv]->Get_ndof();
258             }
259         }
260 
261         // Fill rhs by looping over constraints.
262         int s_c = 0;
263         for (size_t ic = 0; ic < mc_size; ic++) {
264             if (mconstraints[ic]->IsActive()) {
265                 // -b term in lower section of rhs
266                 (*rhs)(n_q + s_c) = -(mconstraints[ic]->Get_b_i());
267                 s_c++;
268             }
269         }
270     }
271 }
272 
DumpLastMatrices(bool assembled,const char * path)273 void ChSystemDescriptor::DumpLastMatrices(bool assembled, const char* path) {
274     char filename[300];
275     try {
276         const char* numformat = "%.12g";
277 
278         if (assembled) {
279             ChSparseMatrix Z;
280             ChVectorDynamic<double> rhs;
281             ConvertToMatrixForm(&Z, &rhs);
282 
283             sprintf(filename, "%s%s", path, "Z.dat");
284             ChStreamOutAsciiFile file_Z(filename);
285             file_Z.SetNumFormat(numformat);
286             StreamOUTsparseMatlabFormat(Z, file_Z);
287 
288             sprintf(filename, "%s%s", path, "rhs.dat");
289             ChStreamOutAsciiFile file_rhs(filename);
290             file_rhs.SetNumFormat(numformat);
291             StreamOUTdenseMatlabFormat(rhs, file_rhs);
292         } else {
293             ChSparseMatrix mdM;
294             ChSparseMatrix mdCq;
295             ChSparseMatrix mdE;
296             ChVectorDynamic<double> mdf;
297             ChVectorDynamic<double> mdb;
298             ChVectorDynamic<double> mdfric;
299             ConvertToMatrixForm(&mdCq, &mdM, &mdE, &mdf, &mdb, &mdfric);
300 
301             sprintf(filename, "%s%s", path, "M.dat");
302             ChStreamOutAsciiFile file_M(filename);
303             file_M.SetNumFormat(numformat);
304             StreamOUTsparseMatlabFormat(mdM, file_M);
305 
306             sprintf(filename, "%s%s", path, "Cq.dat");
307             ChStreamOutAsciiFile file_Cq(filename);
308             file_Cq.SetNumFormat(numformat);
309             StreamOUTsparseMatlabFormat(mdCq, file_Cq);
310 
311             sprintf(filename, "%s%s", path, "E.dat");
312             ChStreamOutAsciiFile file_E(filename);
313             file_E.SetNumFormat(numformat);
314             StreamOUTsparseMatlabFormat(mdE, file_E);
315 
316             sprintf(filename, "%s%s", path, "f.dat");
317             ChStreamOutAsciiFile file_f(filename);
318             file_f.SetNumFormat(numformat);
319             StreamOUTdenseMatlabFormat(mdf, file_f);
320 
321             sprintf(filename, "%s%s", path, "b.dat");
322             ChStreamOutAsciiFile file_b(filename);
323             file_b.SetNumFormat(numformat);
324             StreamOUTdenseMatlabFormat(mdb, file_b);
325 
326             sprintf(filename, "%s%s", path, "fric.dat");
327             ChStreamOutAsciiFile file_fric(filename);
328             file_fric.SetNumFormat(numformat);
329             StreamOUTdenseMatlabFormat(mdfric, file_fric);
330         }
331     } catch (const chrono::ChException &myexc) {
332         chrono::GetLog() << myexc.what();
333     }
334 }
335 
BuildFbVector(ChVectorDynamic<> & Fvector)336 int ChSystemDescriptor::BuildFbVector(ChVectorDynamic<>& Fvector) {
337     n_q = CountActiveVariables();
338     Fvector.setZero(n_q);
339 
340     auto vv_size = vvariables.size();
341 
342     // Fills the 'f' vector
343     for (size_t iv = 0; iv < vv_size; iv++) {
344         if (vvariables[iv]->IsActive()) {
345             Fvector.segment(vvariables[iv]->GetOffset(), vvariables[iv]->Get_ndof()) = vvariables[iv]->Get_fb();
346         }
347     }
348     return n_q;
349 }
350 
BuildBiVector(ChVectorDynamic<> & Bvector)351 int ChSystemDescriptor::BuildBiVector(ChVectorDynamic<>& Bvector) {
352     n_c = CountActiveConstraints();
353     Bvector.setZero(n_c);
354 
355     auto vc_size = vconstraints.size();
356 
357     // Fill the 'b' vector
358     for (size_t ic = 0; ic < vc_size; ic++) {
359         if (vconstraints[ic]->IsActive()) {
360             Bvector(vconstraints[ic]->GetOffset()) = vconstraints[ic]->Get_b_i();
361         }
362     }
363 
364     return n_c;
365 }
366 
BuildDiVector(ChVectorDynamic<> & Dvector)367 int ChSystemDescriptor::BuildDiVector(ChVectorDynamic<>& Dvector) {
368     n_q = CountActiveVariables();
369     n_c = CountActiveConstraints();
370     Dvector.setZero(n_q + n_c);
371 
372     auto vv_size = vvariables.size();
373     auto vc_size = vconstraints.size();
374 
375     // Fills the 'f' vector part
376     for (size_t iv = 0; iv < vv_size; iv++) {
377         if (vvariables[iv]->IsActive()) {
378             Dvector.segment(vvariables[iv]->GetOffset(), vvariables[iv]->Get_ndof()) = vvariables[iv]->Get_fb();
379         }
380     }
381 
382     // Fill the '-b' vector (with flipped sign!)
383     for (size_t ic = 0; ic < vc_size; ic++) {
384         if (vconstraints[ic]->IsActive()) {
385             Dvector(vconstraints[ic]->GetOffset() + n_q) = -vconstraints[ic]->Get_b_i();
386         }
387     }
388 
389     return n_q + n_c;
390 }
391 
BuildDiagonalVector(ChVectorDynamic<> & Diagonal_vect)392 int ChSystemDescriptor::BuildDiagonalVector(ChVectorDynamic<>& Diagonal_vect) {
393     n_q = CountActiveVariables();
394     n_c = CountActiveConstraints();
395     Diagonal_vect.setZero(n_q + n_c);
396 
397     auto vs_size = vstiffness.size();
398     auto vv_size = vvariables.size();
399     auto vc_size = vconstraints.size();
400 
401     // Fill the diagonal values given by ChKblock objects , if any
402     // (This cannot be easily parallelized because of possible write concurrency).
403     for (size_t is = 0; is < vs_size; is++) {
404         vstiffness[is]->DiagonalAdd(Diagonal_vect);
405     }
406 
407     // Get the 'M' diagonal terms given by ChVariables objects
408     for (size_t iv = 0; iv < vv_size; iv++) {
409         if (vvariables[iv]->IsActive()) {
410             vvariables[iv]->DiagonalAdd(Diagonal_vect, c_a);
411         }
412     }
413 
414     // Get the 'E' diagonal terms (E_i = cfm_i )
415     for (size_t ic = 0; ic < vc_size; ic++) {
416         if (vconstraints[ic]->IsActive()) {
417             Diagonal_vect(vconstraints[ic]->GetOffset() + n_q) = vconstraints[ic]->Get_cfm_i();
418         }
419     }
420 
421     return n_q + n_c;
422 }
423 
FromVariablesToVector(ChVectorDynamic<> & mvector,bool resize_vector)424 int ChSystemDescriptor::FromVariablesToVector(ChVectorDynamic<>& mvector, bool resize_vector) {
425     // Count active variables and resize vector if necessary
426     if (resize_vector) {
427         n_q = CountActiveVariables();
428         mvector.setZero(n_q);
429     }
430 
431     // Fill the vector
432     auto vv_size = vvariables.size();
433     for (size_t iv = 0; iv < vv_size; iv++) {
434         if (vvariables[iv]->IsActive()) {
435             mvector.segment(vvariables[iv]->GetOffset(), vvariables[iv]->Get_ndof()) = vvariables[iv]->Get_qb();
436         }
437     }
438 
439     return n_q;
440 }
441 
FromVectorToVariables(const ChVectorDynamic<> & mvector)442 int ChSystemDescriptor::FromVectorToVariables(const ChVectorDynamic<>& mvector) {
443     assert(CountActiveVariables() == mvector.rows());
444 
445     // fetch from the vector
446     auto vv_size = vvariables.size();
447     for (size_t iv = 0; iv < vv_size; iv++) {
448         if (vvariables[iv]->IsActive()) {
449             vvariables[iv]->Get_qb() = mvector.segment(vvariables[iv]->GetOffset(), vvariables[iv]->Get_ndof());
450         }
451     }
452 
453     return n_q;
454 }
455 
FromConstraintsToVector(ChVectorDynamic<> & mvector,bool resize_vector)456 int ChSystemDescriptor::FromConstraintsToVector(ChVectorDynamic<>& mvector, bool resize_vector) {
457     // Count active constraints and resize vector if necessary
458     if (resize_vector) {
459         n_c = CountActiveConstraints();
460         mvector.setZero(n_c);
461     }
462 
463     auto vc_size = (int)vconstraints.size();
464 
465     // Fill the vector
466     for (size_t ic = 0; ic < vc_size; ic++) {
467         if (vconstraints[ic]->IsActive()) {
468             mvector(vconstraints[ic]->GetOffset()) = vconstraints[ic]->Get_l_i();
469         }
470     }
471 
472     return n_c;
473 }
474 
FromVectorToConstraints(const ChVectorDynamic<> & mvector)475 int ChSystemDescriptor::FromVectorToConstraints(const ChVectorDynamic<>& mvector) {
476     n_c = CountActiveConstraints();
477 
478     assert(n_c == mvector.size());
479 
480     auto vc_size = (int)vconstraints.size();
481 
482     // Fill the vector
483     for (size_t ic = 0; ic < vc_size; ic++) {
484         if (vconstraints[ic]->IsActive()) {
485             vconstraints[ic]->Set_l_i(mvector(vconstraints[ic]->GetOffset()));
486         }
487     }
488 
489     return n_c;
490 }
491 
FromUnknownsToVector(ChVectorDynamic<> & mvector,bool resize_vector)492 int ChSystemDescriptor::FromUnknownsToVector(ChVectorDynamic<>& mvector, bool resize_vector) {
493     // Count active variables & constraints and resize vector if necessary
494     n_q = CountActiveVariables();
495     n_c = CountActiveConstraints();
496 
497     if (resize_vector) {
498         mvector.setZero(n_q + n_c);
499     }
500 
501     auto vv_size = (int)vvariables.size();
502     auto vc_size = (int)vconstraints.size();
503 
504     // Fill the first part of vector, x.q ,with variables q
505     for (size_t iv = 0; iv < vv_size; iv++) {
506         if (vvariables[iv]->IsActive()) {
507             mvector.segment(vvariables[iv]->GetOffset(), vvariables[iv]->Get_ndof()) = vvariables[iv]->Get_qb();
508         }
509     }
510 
511     // Fill the second part of vector, x.l, with constraint multipliers -l (with flipped sign!)
512     for (size_t ic = 0; ic < vc_size; ic++) {
513         if (vconstraints[ic]->IsActive()) {
514             mvector(vconstraints[ic]->GetOffset() + n_q) = -vconstraints[ic]->Get_l_i();
515         }
516     }
517 
518     return n_q + n_c;
519 }
520 
FromVectorToUnknowns(const ChVectorDynamic<> & mvector)521 int ChSystemDescriptor::FromVectorToUnknowns(const ChVectorDynamic<>& mvector) {
522     n_q = CountActiveVariables();
523     n_c = CountActiveConstraints();
524 
525     assert((n_q + n_c) == mvector.size());
526 
527     auto vv_size = vvariables.size();
528     auto vc_size = vconstraints.size();
529 
530     // Fetch from the first part of vector (x.q = q)
531     for (size_t iv = 0; iv < vv_size; iv++) {
532         if (vvariables[iv]->IsActive()) {
533             vvariables[iv]->Get_qb() = mvector.segment(vvariables[iv]->GetOffset(), vvariables[iv]->Get_ndof());
534         }
535     }
536 
537     // Fetch from the second part of vector (x.l = -l), with flipped sign!
538     for (size_t ic = 0; ic < vc_size; ic++) {
539         if (vconstraints[ic]->IsActive()) {
540             vconstraints[ic]->Set_l_i(-mvector(vconstraints[ic]->GetOffset() + n_q));
541         }
542     }
543 
544     return n_q + n_c;
545 }
546 
ShurComplementProduct(ChVectorDynamic<> & result,const ChVectorDynamic<> & lvector,std::vector<bool> * enabled)547 void ChSystemDescriptor::ShurComplementProduct(ChVectorDynamic<>& result,
548                                                const ChVectorDynamic<>& lvector,
549                                                std::vector<bool>* enabled) {
550     // currently, the case with ChKblock items is not supported (only diagonal M is supported, no K)
551     assert(vstiffness.size() == 0);
552     assert(lvector.size() == CountActiveConstraints());
553 
554     result.setZero(n_c);
555 
556     // Performs the sparse product    result = [N]*l = [ [Cq][M^(-1)][Cq'] - [E] ] *l
557     // in different phases:
558 
559     auto vv_size = vvariables.size();
560     auto vc_size = vconstraints.size();
561 
562     // 1 - set the qb vector (aka speeds, in each ChVariable sparse data) as zero
563 
564     for (size_t iv = 0; iv < vv_size; iv++) {
565         if (vvariables[iv]->IsActive())
566             vvariables[iv]->Get_qb().setZero();
567     }
568 
569     // 2 - performs    qb=[M^(-1)][Cq']*l  by
570     //     iterating over all constraints (when implemented in parallel this
571     //     could be non-trivial because race conditions might occur -> reduction buffer etc.)
572     //     Also, begin to add the cfm term ( -[E]*l ) to the result.
573 
574     // ATTENTION:  this loop cannot be parallelized! Concurrent write to some q may happen
575     for (size_t ic = 0; ic < vc_size; ic++) {
576         if (vconstraints[ic]->IsActive()) {
577             int s_c = vconstraints[ic]->GetOffset();
578 
579             bool process = (!enabled) || (*enabled)[s_c];
580 
581             if (process) {
582                 double li = lvector(s_c);
583 
584                 // Compute qb += [M^(-1)][Cq']*l_i
585                 //  NOTE! concurrent update to same q data, risk of collision if parallel.
586                 vconstraints[ic]->Increment_q(li);  // computationally intensive
587 
588                 // Add constraint force mixing term  result = cfm * l_i = [E]*l_i
589                 result(s_c) = vconstraints[ic]->Get_cfm_i() * li;
590             }
591         }
592     }
593 
594     // 3 - performs    result=[Cq']*qb    by
595     //     iterating over all constraints
596 
597     for (size_t ic = 0; ic < vc_size; ic++) {
598         if (vconstraints[ic]->IsActive()) {
599             bool process = (!enabled) || (*enabled)[vconstraints[ic]->GetOffset()];
600 
601             if (process)
602                 result(vconstraints[ic]->GetOffset()) += vconstraints[ic]->Compute_Cq_q();  // computationally intensive
603             else
604                 result(vconstraints[ic]->GetOffset()) = 0;  // not enabled constraints, just set to 0 result
605         }
606     }
607 }
608 
SystemProduct(ChVectorDynamic<> & result,const ChVectorDynamic<> & x)609 void ChSystemDescriptor::SystemProduct(ChVectorDynamic<>& result, const ChVectorDynamic<>& x) {
610     n_q = CountActiveVariables();
611     n_c = CountActiveConstraints();
612 
613     result.setZero(n_q + n_c);
614 
615     auto vv_size = vvariables.size();
616     auto vc_size = vconstraints.size();
617     auto vs_size = vstiffness.size();
618 
619     // 1) First row: result.q part =  [M + K]*x.q + [Cq']*x.l
620 
621     // 1.1)  do  M*x.q
622     for (size_t iv = 0; iv < vv_size; iv++) {
623         if (vvariables[iv]->IsActive()) {
624             vvariables[iv]->MultiplyAndAdd(result, x, c_a);
625         }
626     }
627 
628     // 1.2)  add also K*x.q  (NON straight parallelizable - risk of concurrency in writing)
629     for (size_t ik = 0; ik < vs_size; ik++) {
630         vstiffness[ik]->MultiplyAndAdd(result, x);
631     }
632 
633     // 1.3)  add also [Cq]'*x.l  (NON straight parallelizable - risk of concurrency in writing)
634     for (size_t ic = 0; ic < vc_size; ic++) {
635         if (vconstraints[ic]->IsActive()) {
636             vconstraints[ic]->MultiplyTandAdd(result, x(vconstraints[ic]->GetOffset() + n_q));
637         }
638     }
639 
640     // 2) Second row: result.l part =  [C_q]*x.q + [E]*x.l
641     for (size_t ic = 0; ic < vc_size; ic++) {
642         if (vconstraints[ic]->IsActive()) {
643             int s_c = vconstraints[ic]->GetOffset() + n_q;
644             vconstraints[ic]->MultiplyAndAdd(result(s_c), x);       // result.l_i += [C_q_i]*x.q
645             result(s_c) += vconstraints[ic]->Get_cfm_i() * x(s_c);  // result.l_i += [E]*x.l_i
646         }
647     }
648 }
649 
ConstraintsProject(ChVectorDynamic<> & multipliers)650 void ChSystemDescriptor::ConstraintsProject(ChVectorDynamic<>& multipliers) {
651     FromVectorToConstraints(multipliers);
652 
653     auto vc_size = vconstraints.size();
654 
655     for (size_t ic = 0; ic < vc_size; ic++) {
656         if (vconstraints[ic]->IsActive())
657             vconstraints[ic]->Project();
658     }
659 
660     FromConstraintsToVector(multipliers, false);
661 }
662 
UnknownsProject(ChVectorDynamic<> & mx)663 void ChSystemDescriptor::UnknownsProject(ChVectorDynamic<>& mx) {
664     n_q = CountActiveVariables();
665 
666     auto vc_size = vconstraints.size();
667 
668     // vector -> constraints
669     // Fetch from the second part of vector (x.l = -l), with flipped sign!
670     for (size_t ic = 0; ic < vc_size; ic++) {
671         if (vconstraints[ic]->IsActive()) {
672             vconstraints[ic]->Set_l_i(-mx(vconstraints[ic]->GetOffset() + n_q));
673         }
674     }
675 
676     // constraint projection!
677     for (size_t ic = 0; ic < vc_size; ic++) {
678         if (vconstraints[ic]->IsActive())
679             vconstraints[ic]->Project();
680     }
681 
682     // constraints -> vector
683     // Fill the second part of vector, x.l, with constraint multipliers -l (with flipped sign!)
684     for (size_t ic = 0; ic < vc_size; ic++) {
685         if (vconstraints[ic]->IsActive()) {
686             mx(vconstraints[ic]->GetOffset() + n_q) = -vconstraints[ic]->Get_l_i();
687         }
688     }
689 }
690 
691 }  // end namespace chrono
692