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