1 /* Siconos is a program dedicated to modeling, simulation and control
2 * of non smooth dynamical systems.
3 *
4 * Copyright 2021 INRIA.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18 #include "GlobalFrictionContactProblem.h"
19 #include "FrictionContactProblem.h"
20 #include <assert.h> // for assert
21 #include <stdlib.h> // for free, malloc, exit, EXIT_FAILURE
22 #include <sys/errno.h> // for errno
23 #include <string.h> // for memcpy
24 #include "SiconosBlas.h" // for cblas_dscal, cblas_dcopy
25 #include "NumericsMatrix.h" // for NumericsMatrix, NM_display, NM_clear
26 #include "NumericsSparseMatrix.h" // for NumericsMatrix, NM_display, NM_clear
27 #include "SparseBlockMatrix.h" // for SBM_gemv, SBM_free
28 #include "sanitizer.h" // for cblas_dcopy_msan
29 #include "numerics_verbose.h" // for CHECK_IO
30 #include "io_tools.h"
31
32 #include "siconos_debug.h"
33 #if defined(WITH_FCLIB)
34 #include "fclib_interface.h"
35 #endif
36
37 //#define OUTPUT_DEBUG 1
38 #ifdef OUTPUT_DEBUG
39 #include "NumericsVector.h"
40 #endif
41
globalFrictionContactProblem_new(void)42 GlobalFrictionContactProblem* globalFrictionContactProblem_new(void)
43 {
44 GlobalFrictionContactProblem* problem = malloc(sizeof(GlobalFrictionContactProblem));
45 problem->M = NULL;
46 problem->H = NULL;
47 problem->q = NULL;
48 problem->b = NULL;
49 problem->mu = NULL;
50 problem->env = NULL;
51 problem->numberOfContacts = 0;
52 problem->dimension = 0;
53 return problem;
54 }
55
globalFrictionContact_printInFile(GlobalFrictionContactProblem * problem,FILE * file)56 int globalFrictionContact_printInFile(GlobalFrictionContactProblem* problem, FILE* file)
57 {
58 if(! problem)
59 {
60 fprintf(stderr, "Numerics, GlobalFrictionContactProblem printInFile failed, NULL input.\n");
61 exit(EXIT_FAILURE);
62 }
63 int i;
64
65 int d = problem->dimension;
66 fprintf(file, "%d\n", d);
67 int nc = problem->numberOfContacts;
68 fprintf(file, "%d\n", nc);
69 NM_write_in_file(problem->M, file);
70 NM_write_in_file(problem->H, file);
71 for(i = 0; i < problem->M->size1; i++)
72 {
73 fprintf(file, "%32.24e ", problem->q[i]);
74 }
75 fprintf(file, "\n");
76 for(i = 0; i < problem->H->size1; i++)
77 {
78 fprintf(file, "%32.24e ", problem->b[i]);
79 }
80 fprintf(file, "\n");
81 for(i = 0; i < nc; i++)
82 {
83 fprintf(file, "%32.24e ", problem->mu[i]);
84 }
85 fprintf(file, "\n");
86 return 0;
87 }
88
globalFrictionContact_newFromFile(FILE * file)89 GlobalFrictionContactProblem* globalFrictionContact_newFromFile(FILE* file)
90 {
91 GlobalFrictionContactProblem* problem = globalFrictionContactProblem_new();
92 int nc = 0, d = 0;
93 int info = 0;
94 CHECK_IO(fscanf(file, "%d\n", &d), &info);
95 problem->dimension = d;
96 CHECK_IO(fscanf(file, "%d\n", &nc), &info);
97 problem->numberOfContacts = nc;
98 problem->M = NM_new_from_file(file);
99
100 problem->H = NM_new_from_file(file);
101
102 problem->q = (double *) malloc(problem->M->size1 * sizeof(double));
103 for(int i = 0; i < problem->M->size1; ++i)
104 {
105 CHECK_IO(fscanf(file, "%lf ", &(problem->q[i])), &info);
106 }
107 problem->b = (double *) malloc(problem->H->size1 * sizeof(double));
108 for(int i = 0; i < problem->H->size1; ++i)
109 {
110 CHECK_IO(fscanf(file, "%lf ", &(problem->b[i])), &info);
111 }
112
113 problem->mu = (double *) malloc(nc * sizeof(double));
114 for(int i = 0; i < nc; ++i)
115 {
116 CHECK_IO(fscanf(file, "%lf ", &(problem->mu[i])), &info);
117 }
118
119 if(info)
120 {
121 problem = NULL;
122 }
123 return problem;
124 }
125
globalFrictionContact_new_from_filename(const char * filename)126 GlobalFrictionContactProblem* globalFrictionContact_new_from_filename(const char* filename)
127 {
128 GlobalFrictionContactProblem* problem = NULL;
129 int is_hdf5 = check_hdf5_file(filename);
130 if(is_hdf5)
131 {
132 #if defined(WITH_FCLIB)
133 problem = globalFrictionContact_fclib_read(filename);
134 #else
135 numerics_error("GlobalFrictionContactProblem",
136 "Try to read an hdf5 file, while fclib interface is not active. Recompile Siconos with fclib.",
137 filename);
138 #endif
139 }
140 else
141 {
142 FILE * file = fopen(filename, "r");
143 if(!file)
144 numerics_error("GlobalFrictionContactProblem", "Can not open file ", filename);
145
146 problem = globalFrictionContact_newFromFile(file);
147 fclose(file);
148 }
149 return problem;
150
151 }
152
globalFrictionContact_printInFileName(GlobalFrictionContactProblem * problem,const char * filename)153 int globalFrictionContact_printInFileName(GlobalFrictionContactProblem* problem, const char* filename)
154 {
155 int info = 0;
156 FILE * file = fopen(filename, "w");
157
158 if(!file)
159 {
160 return errno;
161 }
162
163 info = globalFrictionContact_printInFile(problem, file);
164
165 fclose(file);
166 return info;
167 }
168
globalFrictionContact_free(GlobalFrictionContactProblem * problem)169 void globalFrictionContact_free(GlobalFrictionContactProblem* problem)
170 {
171
172 if(problem->M)
173 {
174 NM_clear(problem->M);
175 free(problem->M);
176 }
177 problem->M = NULL;
178
179 if(problem->H)
180 {
181 NM_clear(problem->H);
182 free(problem->H);
183 }
184 problem->H = NULL;
185
186 if(problem->mu)
187 {
188 free(problem->mu);
189 }
190 problem->mu = NULL;
191
192 if(problem->q)
193 {
194 free(problem->q);
195 }
196 problem->q = NULL;
197
198 if(problem->b)
199 {
200 free(problem->b);
201 }
202 problem->b = NULL;
203
204 if(problem->env) assert(0 && "globalFrictionContact_free :: problem->env != NULL, don't know what to do");
205
206 free(problem);
207 }
208
globalFrictionContact_display(GlobalFrictionContactProblem * problem)209 void globalFrictionContact_display(GlobalFrictionContactProblem* problem)
210 {
211
212 assert(problem);
213 int i, n = problem->dimension * problem->numberOfContacts;
214 printf("GlobalFrictionContact Display :\n-------------\n");
215 printf("dimension :%d \n", problem->dimension);
216 printf("numberOfContacts:%d \n", problem->numberOfContacts);
217 int m = problem->M->size0;
218 if(problem->M)
219 {
220 printf("M matrix:\n");
221 NM_display(problem->M);
222 }
223 else
224 printf("No M matrix:\n");
225 if(problem->H)
226 {
227 printf("H matrix:\n");
228 NM_display(problem->H);
229 }
230 else
231 printf("No H matrix:\n");
232
233 if(problem->q)
234 {
235 printf("q vector:\n");
236 for(i = 0; i < m; i++) printf("q[ %i ] = %12.8e\n", i, problem->q[i]);
237 }
238 else
239 printf("No q vector:\n");
240
241 if(problem->b)
242 {
243 printf("b vector:\n");
244 for(i = 0; i < n; i++) printf("b[ %i ] = %12.8e\n", i, problem->b[i]);
245 }
246 else
247 printf("No q vector:\n");
248
249 if(problem->mu)
250 {
251 printf("mu vector:\n");
252 for(i = 0; i < problem->numberOfContacts; i++) printf("mu[ %i ] = %12.8e\n", i, problem->mu[i]);
253 }
254 else
255 printf("No mu vector:\n");
256
257 }
258
globalFrictionContact_copy(GlobalFrictionContactProblem * problem)259 GlobalFrictionContactProblem* globalFrictionContact_copy(GlobalFrictionContactProblem* problem)
260 {
261 assert(problem);
262
263 int nc = problem->numberOfContacts;
264 int n = problem->M->size0;
265 int m = 3 * nc;
266
267 GlobalFrictionContactProblem* new = (GlobalFrictionContactProblem*) malloc(sizeof(GlobalFrictionContactProblem));
268 new->dimension = problem->dimension;
269 new->numberOfContacts = problem->numberOfContacts;
270 new->M = NM_new();
271 NM_copy(problem->M, new->M);
272 new->H = NM_new();
273 NM_copy(problem->H, new->H);
274 new->q = (double*)malloc(n*sizeof(double));
275 memcpy(new->q, problem->q, n*sizeof(double));
276 new->b = (double*)malloc(m*sizeof(double));
277 memcpy(new->b, problem->b, m*sizeof(double));
278 new->mu = (double*)malloc(nc*sizeof(double));
279 memcpy(new->mu, problem->mu, nc*sizeof(double));
280 new->env = NULL;
281 return new;
282 }
283
globalFrictionContact_computeGlobalVelocity(GlobalFrictionContactProblem * problem,double * reaction,double * globalVelocity)284 int globalFrictionContact_computeGlobalVelocity(
285 GlobalFrictionContactProblem* problem,
286 double * reaction,
287 double * globalVelocity)
288 {
289 int info = -1;
290
291 int n = problem->M->size0;
292 int m = problem->H->size1;
293
294 /* globalVelocity <- problem->q */
295 cblas_dcopy(n, problem->q, 1, globalVelocity, 1);
296
297 // We add the reaction part only if the problem has contacts
298 if(m>0)
299 {
300 /* globalVelocity <- H*reaction + globalVelocity*/
301 NM_gemv(1.0, problem->H, reaction, 1.0, globalVelocity);
302 DEBUG_EXPR(NM_vector_display(reaction, m));
303 }
304
305 /* Compute globalVelocity <- M^(-1) globalVelocity*/
306 //info = NM_gesv_expert(problem->M, globalVelocity, NM_PRESERVE);
307 info = NM_LU_solve(problem->M, globalVelocity, 1);
308 DEBUG_EXPR(NM_vector_display(globalVelocity, n));
309
310 return info;
311 }
312
globalFrictionContact_reformulation_FrictionContact(GlobalFrictionContactProblem * problem)313 FrictionContactProblem * globalFrictionContact_reformulation_FrictionContact(GlobalFrictionContactProblem* problem)
314 {
315 int info = -1;
316
317 NumericsMatrix *M = problem->M;
318 NumericsMatrix *H = problem->H;
319
320 int n = M->size0;
321 int m = H->size1;
322
323 FrictionContactProblem* localproblem = frictionContactProblem_new();
324
325 localproblem->numberOfContacts = problem->numberOfContacts;
326 localproblem->dimension = problem->dimension;
327 localproblem->mu = (double*)calloc(problem->numberOfContacts,sizeof(double));
328 cblas_dcopy_msan(problem->numberOfContacts, problem->mu, 1, localproblem->mu, 1);
329
330 /* assert(M); */
331 /* assert(H); */
332 /* NM_display(M); */
333 /* NM_display(H); */
334 /* NumericsMatrix *MMtmp = NM_new(); */
335 /* NumericsMatrix *HHtmp = NM_new(); */
336
337 /* NM_copy(M,MMtmp); */
338 /* NM_copy(H,HHtmp); */
339
340 /* NM_clearSparse(M); */
341 /* NM_clearSparse(H); */
342
343 /* M = NM_create(NM_DENSE, n, n); */
344 /* H = NM_create(NM_DENSE, n, m); */
345
346 /* NM_to_dense(MMtmp,M); */
347 /* NM_to_dense(HHtmp,H); */
348
349 /* NM_display(M); */
350 /* NM_display(H); */
351
352 if(H->storageType != M->storageType)
353 {
354 // if(verbose==1)
355 printf(" ->storageType != M->storageType :This case is not taken into account\n");
356 return NULL;
357 }
358 #ifdef OUTPUT_DEBUG
359 FILE * fileout;
360 #endif
361 if(M->storageType == NM_DENSE)
362 {
363
364
365
366 int nm = n * m;
367
368 double *Htmp = (double*)calloc(nm, sizeof(double));
369 // compute W = H^T M^-1 H
370 //Copy Htmp <- H
371 cblas_dcopy_msan(nm, H->matrix0, 1, Htmp, 1);
372
373 //Compute Htmp <- M^-1 Htmp
374 #ifdef USE_LAPACK_DGETRS
375 lapack_int* ipiv = (lapack_int*)NM_iWork(M, M->size0, sizeof(lapack_int));
376 lapack_int infoDGETRF;
377 lapack_int infoDGETRS;
378 DGETRF(n, n, M->matrix0, n, ipiv, &infoDGETRF);
379 assert(!infoDGETRF);
380 NM_set_LU_factorized(M, true);
381 DGETRS(LA_NOTRANS, n, m, M->matrix0, n, ipiv, Htmp, n, &infoDGETRS);
382 #else
383 // NM_gesv_expert_multiple_rhs(M,Htmp,m,NM_KEEP_FACTORS);
384 NM_LU_solve(M, Htmp, m);
385 #endif
386
387 /* assert(!infoDGETRS); */
388 /* DGESV(n, m, M->matrix0, n, ipiv, Htmp, n, infoDGESV); */
389
390 localproblem->M = NM_new();
391 NumericsMatrix *Wnum = localproblem->M;
392 Wnum->storageType = 0;
393 Wnum-> size0 = m;
394 Wnum-> size1 = m;
395 Wnum->matrix0 = (double*)calloc(m * m, sizeof(double));
396 Wnum->matrix1 = NULL;
397 Wnum->matrix2 = NULL;
398 Wnum->internalData = NULL;
399 // Compute W <- H^T M^1 H
400
401 assert(H->matrix0);
402 assert(Htmp);
403 assert(Wnum->matrix0);
404
405 cblas_dgemm(CblasColMajor,CblasTrans, CblasNoTrans, m, m, n, 1.0, H->matrix0, n, Htmp, n, 0.0, Wnum->matrix0, m);
406 /* DGEMM(CblasTrans,CblasNoTrans,m,m,n,1.0,H->matrix0,n,Htmp,n,0.0,Wnum->matrix0,m); */
407
408 // compute localq = H^T M^(-1) q +b
409
410 //Copy localq <- b
411 localproblem->q = (double*)calloc(m, sizeof(double));
412 cblas_dcopy_msan(m, problem->b, 1, localproblem->q, 1);
413
414 double* qtmp = (double*)calloc(n , sizeof(double));
415 cblas_dcopy_msan(n, problem->q, 1, qtmp, 1);
416
417 // compute H^T M^(-1) q + b
418 #ifdef USE_LAPACK_DGETRS
419 DGETRS(LA_NOTRANS, n, 1, M->matrix0, n, ipiv, qtmp, n, &infoDGETRS);
420 #else
421 // NM_gesv_expert(M,qtmp,NM_KEEP_FACTORS);
422 NM_LU_solve(M, qtmp, 1);
423 #endif
424
425 cblas_dgemv(CblasColMajor,CblasTrans, n, m, 1.0, H->matrix0, n, qtmp, 1, 1.0, localproblem->q, 1);
426
427 frictionContact_display(localproblem);
428
429 free(Htmp);
430 free(qtmp);
431
432
433 }
434
435 else if(M->storageType == NM_SPARSE_BLOCK)
436 {
437 int n = M->size0;
438 int m = H->size1;
439
440 // compute W = H^T M^-1 H
441 // compute MinvH <- M^-1 H
442 /* int infoMInv = 0; */
443 /* infoMInv = NM_inverse_diagonal_block_matrix_in_place(M); */
444 assert(!NM_inverse_diagonal_block_matrix_in_place(M));
445
446 DEBUG_PRINT("M inverse :");
447 DEBUG_EXPR(NM_display(M));
448
449
450 NumericsMatrix * MinvH = NM_multiply(M,H);
451
452 /* NumericsMatrix * MinvH= NM_create(NM_SPARSE_BLOCK, m, m); */
453 /* double alpha = 1.0, beta = 0.0; */
454 /* NM_gemm(alpha, M, H, beta, MinvH); */
455
456 NumericsMatrix * Htrans= NM_create(NM_SPARSE_BLOCK, H->size1, H->size0);
457 SBM_transpose(H->matrix1, Htrans->matrix1);
458
459 /* localproblem->M = NM_create(NM_SPARSE_BLOCK, m, m ); */
460 /* NumericsMatrix *W = localproblem->M; */
461 /* NM_gemm(alpha, Htrans, MinvH, beta, W); */
462
463 localproblem->M = NM_multiply(Htrans,MinvH);
464
465
466 #ifdef OUTPUT_DEBUG
467 FILE * fileout;
468 fileout = fopen("dataW.sci", "w");
469 NM_write_in_file_scilab(localproblem->M, fileout);
470 fclose(fileout);
471 #endif
472
473 #ifdef TEST_COND
474 NumericsMatrix *WnumInverse = NM_new();
475 WnumInverse->storageType = 0;
476 WnumInverse-> size0 = m;
477 WnumInverse-> size1 = m;
478 WnumInverse->matrix1 = NULL;
479 WnumInverse->matrix2 = NULL;
480 WnumInverse->internalData = NULL;
481 WnumInverse->matrix0 = (double*)calloc(m * m , sizeof(double));
482 double * WInverse = WnumInverse->matrix0;
483 SBM_to_dense(W, WnumInverse->matrix0);
484
485 FILE * file1 = fopen("dataW.dat", "w");
486 NM_write_in_file_scilab(WnumInverse, file1);
487 fclose(file1);
488
489 double * WInversetmp = (double*)calloc(m * m, sizeof(double));
490 memcpy(WInversetmp, WInverse, m * m * sizeof(double));
491 double condW;
492 condW = cond(WInverse, m, m);
493
494 lapack_int* ipiv = (lapack_int *)calloc(m , sizeof(lapack_int));
495 int infoDGETRF = 0;
496 DGETRF(m, m, WInverse, m, ipiv, &infoDGETRF);
497 assert(!infoDGETRF);
498 int infoDGETRI = 0;
499 DGETRI(m, WInverse, m, ipiv, &infoDGETRI);
500
501
502 free(ipiv);
503 assert(!infoDGETRI);
504
505
506 double condWInverse;
507 condWInverse = cond(WInverse, m, m);
508
509 FILE * file2 = fopen("dataWInverse.dat", "w");
510 NM_write_in_file_scilab(WnumInverse, file2);
511 fclose(file2);
512
513 double tol = 1e-24;
514 pinv(WInversetmp, m, m, tol);
515 NumericsMatrix *WnumInversetmp = NM_new();
516 WnumInversetmp->storageType = 0;
517 WnumInversetmp-> size0 = m;
518 WnumInversetmp-> size1 = m;
519 WnumInversetmp->matrix1 = NULL;
520 WnumInversetmp->matrix2 = NULL;
521 WnumInversetmp->internalData = NULL;
522 WnumInversetmp->matrix0 = WInversetmp ;
523
524 FILE * file3 = fopen("dataWPseudoInverse.dat", "w");
525 NM_write_in_file_scilab(WnumInversetmp, file3);
526 fclose(file3);
527
528
529 free(WInverse);
530 free(WInversetmp);
531 free(WnumInverse);
532 #endif
533
534 localproblem->q = (double*)calloc(m, sizeof(double));
535
536 //Copy localq<- b
537 cblas_dcopy_msan(m, problem->b, 1, localproblem->q, 1);
538
539 // compute H^T M^-1 q+ b
540 double* qtmp = (double*)calloc(n, sizeof(double));
541 double alpha = 1.0, beta = 1.0;
542 double beta2 = 0.0;
543 NM_gemv(alpha, M, problem->q, beta2, qtmp); /* Warning M contains Minv */
544 NM_gemv(alpha, Htrans, qtmp, beta, localproblem->q);
545
546
547 NM_clear(MinvH);
548 NM_clear(Htrans);
549 free(MinvH);
550 free(Htrans);
551 free(qtmp);
552 }
553 else if(M->storageType == NM_SPARSE)
554 {
555
556 #ifdef OUTPUT_DEBUG
557 fileout = fopen("dataM.py", "w");
558 NM_write_in_file_python(M, fileout);
559 fclose(fileout);
560 fileout = fopen("dataH.py", "w");
561 NM_write_in_file_python(H, fileout);
562 fclose(fileout);
563 fileout = fopen("dataq.py", "w");
564 NV_write_in_file_python(problem->q, M->size0, fileout);
565 fclose(fileout);
566 fileout = fopen("datab.py", "w");
567 NV_write_in_file_python(problem->b, H->size1, fileout);
568 fclose(fileout);
569 #endif
570
571
572
573
574 // Product M^-1 H
575 DEBUG_EXPR(NM_display(H););
576 numerics_printf_verbose(1,"inversion of the matrix M ...");
577 NumericsMatrix * Minv = NM_LU_inv(M);
578 DEBUG_EXPR(NM_display(Minv););
579
580
581 /* NumericsMatrix* MinvH = NM_create(NM_SPARSE,n,m); */
582 /* NM_triplet_alloc(MinvH, n); */
583 /* MinvH->matrix2->origin = NSM_TRIPLET; */
584 /* DEBUG_EXPR(NM_display(MinvH);); */
585 /* NM_gemm(1.0, Minv, H, 0.0, MinvH); */
586 numerics_printf_verbose(1,"multiplication H^T M^{-1} H ...");
587 NumericsMatrix* MinvH = NM_multiply(Minv,H);
588 DEBUG_EXPR(NM_display(MinvH););
589
590 // Product H^T M^-1 H
591 NM_csc_trans(H);
592
593
594 NumericsMatrix* Htrans = NM_new();
595 Htrans->storageType = NM_SPARSE;
596 Htrans-> size0 = m;
597 Htrans-> size1 = n;
598 NM_csc_alloc(Htrans, 0);
599 Htrans->matrix2->origin = NSM_CSC;
600 Htrans->matrix2->csc = NM_csc_trans(H);
601 DEBUG_EXPR(NM_display(Htrans););
602
603 /* localproblem->M = NM_create(NM_SPARSE, m, m); */
604 /* NumericsMatrix *W = localproblem->M; */
605 /* int nzmax= m*m; */
606 /* NM_csc_empty_alloc(W, nzmax); */
607 /* W->matrix2->origin = NSM_CSC; */
608 /* NM_gemm(1.0, Htrans, MinvH, 0.0, W); */
609
610 localproblem->M = NM_multiply(Htrans,MinvH);
611 DEBUG_EXPR(NM_display(localproblem->M););
612
613 NSM_fix_csc(NM_csc(localproblem->M));
614
615 #ifdef OUTPUT_DEBUG
616 fileout = fopen("dataW.py", "w");
617 NM_write_in_file_python(localproblem->M, fileout);
618 fclose(fileout);
619 #endif
620
621 /* compute localq <- H^T M^(-1) q + b */
622 numerics_printf_verbose(1,"Compute localq = H^T M^(-1) q +b ...");
623 double* qtmp = (double*)calloc(n, sizeof(double));
624 NM_gemv(1.0, Minv, problem->q, 0.0, qtmp); /* qtmp <- M^(-1) q */
625 DEBUG_EXPR(NV_display(qtmp,n););
626 DEBUG_EXPR(NV_display(problem->q,n););
627 localproblem->q = (double*)calloc(m, sizeof(double));
628 cblas_dcopy_msan(m, problem->b, 1, localproblem->q, 1); /*Copy localq <- b */
629 NM_gemv(1.0, Htrans, qtmp, 1.0, localproblem->q); /* localq <- H^T qtmp + localq */
630
631 #ifdef OUTPUT_DEBUG
632 fileout = fopen("dataqloc.py", "w");
633 NV_write_in_file_python(localproblem->q, H->size1, fileout);
634 fclose(fileout);
635 #endif
636
637
638 DEBUG_EXPR(frictionContact_display(localproblem););
639 //getchar();
640 }
641 else
642 {
643 printf("globalFrictionContact_reformulation_FrictionContact :: unknown matrix storage");
644 exit(EXIT_FAILURE);
645 }
646 return localproblem;
647 }
648