xref: /petsc/src/ksp/pc/impls/spai/ispai.c (revision ebead697dbf761eb322f829370bbe90b3bd93fa3)
1 
2 /*
3    3/99 Modified by Stephen Barnard to support SPAI version 3.0
4 */
5 
6 /*
7       Provides an interface to the SPAI Sparse Approximate Inverse Preconditioner
8    Code written by Stephen Barnard.
9 
10       Note: there is some BAD memory bleeding below!
11 
12       This code needs work
13 
14    1) get rid of all memory bleeding
15    2) fix PETSc/interface so that it gets if the matrix is symmetric from the matrix
16       rather than having the sp flag for PC_SPAI
17    3) fix to set the block size based on the matrix block size
18 
19 */
20 #define PETSC_SKIP_COMPLEX /* since spai uses I which conflicts with some complex implementations */
21 
22 #include <petsc/private/pcimpl.h>        /*I "petscpc.h" I*/
23 #include <../src/ksp/pc/impls/spai/petscspai.h>
24 
25 /*
26     These are the SPAI include files
27 */
28 EXTERN_C_BEGIN
29 #define SPAI_USE_MPI /* required for setting SPAI_Comm correctly in basics.h */
30 #include <spai.h>
31 #include <matrix.h>
32 EXTERN_C_END
33 
34 extern PetscErrorCode ConvertMatToMatrix(MPI_Comm,Mat,Mat,matrix**);
35 extern PetscErrorCode ConvertMatrixToMat(MPI_Comm,matrix*,Mat*);
36 extern PetscErrorCode ConvertVectorToVec(MPI_Comm,vector*,Vec*);
37 extern PetscErrorCode MM_to_PETSC(char*,char*,char*);
38 
39 typedef struct {
40 
41   matrix *B;                /* matrix in SPAI format */
42   matrix *BT;               /* transpose of matrix in SPAI format */
43   matrix *M;                /* the approximate inverse in SPAI format */
44 
45   Mat PM;                   /* the approximate inverse PETSc format */
46 
47   double epsilon;           /* tolerance */
48   int    nbsteps;           /* max number of "improvement" steps per line */
49   int    max;               /* max dimensions of is_I, q, etc. */
50   int    maxnew;            /* max number of new entries per step */
51   int    block_size;        /* constant block size */
52   int    cache_size;        /* one of (1,2,3,4,5,6) indicting size of cache */
53   int    verbose;           /* SPAI prints timing and statistics */
54 
55   int      sp;              /* symmetric nonzero pattern */
56   MPI_Comm comm_spai;     /* communicator to be used with spai */
57 } PC_SPAI;
58 
59 /**********************************************************************/
60 
61 static PetscErrorCode PCSetUp_SPAI(PC pc)
62 {
63   PC_SPAI *ispai = (PC_SPAI*)pc->data;
64   Mat      AT;
65 
66   PetscFunctionBegin;
67   init_SPAI();
68 
69   if (ispai->sp) {
70     PetscCall(ConvertMatToMatrix(ispai->comm_spai,pc->pmat,pc->pmat,&ispai->B));
71   } else {
72     /* Use the transpose to get the column nonzero structure. */
73     PetscCall(MatTranspose(pc->pmat,MAT_INITIAL_MATRIX,&AT));
74     PetscCall(ConvertMatToMatrix(ispai->comm_spai,pc->pmat,AT,&ispai->B));
75     PetscCall(MatDestroy(&AT));
76   }
77 
78   /* Destroy the transpose */
79   /* Don't know how to do it. PETSc developers? */
80 
81   /* construct SPAI preconditioner */
82   /* FILE *messages */     /* file for warning messages */
83   /* double epsilon */     /* tolerance */
84   /* int nbsteps */        /* max number of "improvement" steps per line */
85   /* int max */            /* max dimensions of is_I, q, etc. */
86   /* int maxnew */         /* max number of new entries per step */
87   /* int block_size */     /* block_size == 1 specifies scalar elments
88                               block_size == n specifies nxn constant-block elements
89                               block_size == 0 specifies variable-block elements */
90   /* int cache_size */     /* one of (1,2,3,4,5,6) indicting size of cache. cache_size == 0 indicates no caching */
91   /* int    verbose    */  /* verbose == 0 specifies that SPAI is silent
92                               verbose == 1 prints timing and matrix statistics */
93 
94   PetscCall(bspai(ispai->B,&ispai->M,
95                 stdout,
96                 ispai->epsilon,
97                 ispai->nbsteps,
98                 ispai->max,
99                 ispai->maxnew,
100                 ispai->block_size,
101                 ispai->cache_size,
102                 ispai->verbose));
103 
104   PetscCall(ConvertMatrixToMat(PetscObjectComm((PetscObject)pc),ispai->M,&ispai->PM));
105 
106   /* free the SPAI matrices */
107   sp_free_matrix(ispai->B);
108   sp_free_matrix(ispai->M);
109   PetscFunctionReturn(0);
110 }
111 
112 /**********************************************************************/
113 
114 static PetscErrorCode PCApply_SPAI(PC pc,Vec xx,Vec y)
115 {
116   PC_SPAI *ispai = (PC_SPAI*)pc->data;
117 
118   PetscFunctionBegin;
119   /* Now using PETSc's multiply */
120   PetscCall(MatMult(ispai->PM,xx,y));
121   PetscFunctionReturn(0);
122 }
123 
124 static PetscErrorCode PCMatApply_SPAI(PC pc,Mat X,Mat Y)
125 {
126   PC_SPAI *ispai = (PC_SPAI*)pc->data;
127 
128   PetscFunctionBegin;
129   /* Now using PETSc's multiply */
130   PetscCall(MatMatMult(ispai->PM,X,MAT_REUSE_MATRIX,PETSC_DEFAULT,&Y));
131   PetscFunctionReturn(0);
132 }
133 
134 /**********************************************************************/
135 
136 static PetscErrorCode PCDestroy_SPAI(PC pc)
137 {
138   PC_SPAI *ispai = (PC_SPAI*)pc->data;
139 
140   PetscFunctionBegin;
141   PetscCall(MatDestroy(&ispai->PM));
142   PetscCallMPI(MPI_Comm_free(&(ispai->comm_spai)));
143   PetscCall(PetscFree(pc->data));
144   PetscCall(PetscObjectComposeFunction((PetscObject)pc,"PCSPAISetEpsilon_C",NULL));
145   PetscCall(PetscObjectComposeFunction((PetscObject)pc,"PCSPAISetNBSteps_C",NULL));
146   PetscCall(PetscObjectComposeFunction((PetscObject)pc,"PCSPAISetMax_C",NULL));
147   PetscCall(PetscObjectComposeFunction((PetscObject)pc,"PCSPAISetMaxNew_C",NULL));
148   PetscCall(PetscObjectComposeFunction((PetscObject)pc,"PCSPAISetBlockSize_C",NULL));
149   PetscCall(PetscObjectComposeFunction((PetscObject)pc,"PCSPAISetCacheSize_C",NULL));
150   PetscCall(PetscObjectComposeFunction((PetscObject)pc,"PCSPAISetVerbose_C",NULL));
151   PetscCall(PetscObjectComposeFunction((PetscObject)pc,"PCSPAISetSp_C",NULL));
152   PetscFunctionReturn(0);
153 }
154 
155 /**********************************************************************/
156 
157 static PetscErrorCode PCView_SPAI(PC pc,PetscViewer viewer)
158 {
159   PC_SPAI   *ispai = (PC_SPAI*)pc->data;
160   PetscBool  iascii;
161 
162   PetscFunctionBegin;
163   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&iascii));
164   if (iascii) {
165     PetscCall(PetscViewerASCIIPrintf(viewer,"    epsilon %g\n",   ispai->epsilon));
166     PetscCall(PetscViewerASCIIPrintf(viewer,"    nbsteps %d\n",   ispai->nbsteps));
167     PetscCall(PetscViewerASCIIPrintf(viewer,"    max %d\n",       ispai->max));
168     PetscCall(PetscViewerASCIIPrintf(viewer,"    maxnew %d\n",    ispai->maxnew));
169     PetscCall(PetscViewerASCIIPrintf(viewer,"    block_size %d\n",ispai->block_size));
170     PetscCall(PetscViewerASCIIPrintf(viewer,"    cache_size %d\n",ispai->cache_size));
171     PetscCall(PetscViewerASCIIPrintf(viewer,"    verbose %d\n",   ispai->verbose));
172     PetscCall(PetscViewerASCIIPrintf(viewer,"    sp %d\n",        ispai->sp));
173   }
174   PetscFunctionReturn(0);
175 }
176 
177 static PetscErrorCode  PCSPAISetEpsilon_SPAI(PC pc, PetscReal epsilon1)
178 {
179   PC_SPAI *ispai = (PC_SPAI*)pc->data;
180 
181   PetscFunctionBegin;
182   ispai->epsilon = (double)epsilon1;
183   PetscFunctionReturn(0);
184 }
185 
186 /**********************************************************************/
187 
188 static PetscErrorCode  PCSPAISetNBSteps_SPAI(PC pc,PetscInt nbsteps1)
189 {
190   PC_SPAI *ispai = (PC_SPAI*)pc->data;
191 
192   PetscFunctionBegin;
193   ispai->nbsteps = (int)nbsteps1;
194   PetscFunctionReturn(0);
195 }
196 
197 /**********************************************************************/
198 
199 /* added 1/7/99 g.h. */
200 static PetscErrorCode  PCSPAISetMax_SPAI(PC pc,PetscInt max1)
201 {
202   PC_SPAI *ispai = (PC_SPAI*)pc->data;
203 
204   PetscFunctionBegin;
205   ispai->max = (int)max1;
206   PetscFunctionReturn(0);
207 }
208 
209 /**********************************************************************/
210 
211 static PetscErrorCode  PCSPAISetMaxNew_SPAI(PC pc,PetscInt maxnew1)
212 {
213   PC_SPAI *ispai = (PC_SPAI*)pc->data;
214 
215   PetscFunctionBegin;
216   ispai->maxnew = (int)maxnew1;
217   PetscFunctionReturn(0);
218 }
219 
220 /**********************************************************************/
221 
222 static PetscErrorCode  PCSPAISetBlockSize_SPAI(PC pc,PetscInt block_size1)
223 {
224   PC_SPAI *ispai = (PC_SPAI*)pc->data;
225 
226   PetscFunctionBegin;
227   ispai->block_size = (int)block_size1;
228   PetscFunctionReturn(0);
229 }
230 
231 /**********************************************************************/
232 
233 static PetscErrorCode  PCSPAISetCacheSize_SPAI(PC pc,PetscInt cache_size)
234 {
235   PC_SPAI *ispai = (PC_SPAI*)pc->data;
236 
237   PetscFunctionBegin;
238   ispai->cache_size = (int)cache_size;
239   PetscFunctionReturn(0);
240 }
241 
242 /**********************************************************************/
243 
244 static PetscErrorCode  PCSPAISetVerbose_SPAI(PC pc,PetscInt verbose)
245 {
246   PC_SPAI *ispai = (PC_SPAI*)pc->data;
247 
248   PetscFunctionBegin;
249   ispai->verbose = (int)verbose;
250   PetscFunctionReturn(0);
251 }
252 
253 /**********************************************************************/
254 
255 static PetscErrorCode  PCSPAISetSp_SPAI(PC pc,PetscInt sp)
256 {
257   PC_SPAI *ispai = (PC_SPAI*)pc->data;
258 
259   PetscFunctionBegin;
260   ispai->sp = (int)sp;
261   PetscFunctionReturn(0);
262 }
263 
264 /* -------------------------------------------------------------------*/
265 
266 /*@
267   PCSPAISetEpsilon -- Set the tolerance for the SPAI preconditioner
268 
269   Input Parameters:
270 + pc - the preconditioner
271 - eps - epsilon (default .4)
272 
273   Notes:
274     Espilon must be between 0 and 1. It controls the
275                  quality of the approximation of M to the inverse of
276                  A. Higher values of epsilon lead to more work, more
277                  fill, and usually better preconditioners. In many
278                  cases the best choice of epsilon is the one that
279                  divides the total solution time equally between the
280                  preconditioner and the solver.
281 
282   Level: intermediate
283 
284 .seealso: `PCSPAI`, `PCSetType()`
285   @*/
286 PetscErrorCode  PCSPAISetEpsilon(PC pc,PetscReal epsilon1)
287 {
288   PetscFunctionBegin;
289   PetscTryMethod(pc,"PCSPAISetEpsilon_C",(PC,PetscReal),(pc,epsilon1));
290   PetscFunctionReturn(0);
291 }
292 
293 /**********************************************************************/
294 
295 /*@
296   PCSPAISetNBSteps - set maximum number of improvement steps per row in
297         the SPAI preconditioner
298 
299   Input Parameters:
300 + pc - the preconditioner
301 - n - number of steps (default 5)
302 
303   Notes:
304     SPAI constructs to approximation to every column of
305                  the exact inverse of A in a series of improvement
306                  steps. The quality of the approximation is determined
307                  by epsilon. If an approximation achieving an accuracy
308                  of epsilon is not obtained after ns steps, SPAI simply
309                  uses the best approximation constructed so far.
310 
311   Level: intermediate
312 
313 .seealso: `PCSPAI`, `PCSetType()`, `PCSPAISetMaxNew()`
314 @*/
315 PetscErrorCode  PCSPAISetNBSteps(PC pc,PetscInt nbsteps1)
316 {
317   PetscFunctionBegin;
318   PetscTryMethod(pc,"PCSPAISetNBSteps_C",(PC,PetscInt),(pc,nbsteps1));
319   PetscFunctionReturn(0);
320 }
321 
322 /**********************************************************************/
323 
324 /* added 1/7/99 g.h. */
325 /*@
326   PCSPAISetMax - set the size of various working buffers in
327         the SPAI preconditioner
328 
329   Input Parameters:
330 + pc - the preconditioner
331 - n - size (default is 5000)
332 
333   Level: intermediate
334 
335 .seealso: `PCSPAI`, `PCSetType()`
336 @*/
337 PetscErrorCode  PCSPAISetMax(PC pc,PetscInt max1)
338 {
339   PetscFunctionBegin;
340   PetscTryMethod(pc,"PCSPAISetMax_C",(PC,PetscInt),(pc,max1));
341   PetscFunctionReturn(0);
342 }
343 
344 /**********************************************************************/
345 
346 /*@
347   PCSPAISetMaxNew - set maximum number of new nonzero candidates per step
348    in SPAI preconditioner
349 
350   Input Parameters:
351 + pc - the preconditioner
352 - n - maximum number (default 5)
353 
354   Level: intermediate
355 
356 .seealso: `PCSPAI`, `PCSetType()`, `PCSPAISetNBSteps()`
357 @*/
358 PetscErrorCode  PCSPAISetMaxNew(PC pc,PetscInt maxnew1)
359 {
360   PetscFunctionBegin;
361   PetscTryMethod(pc,"PCSPAISetMaxNew_C",(PC,PetscInt),(pc,maxnew1));
362   PetscFunctionReturn(0);
363 }
364 
365 /**********************************************************************/
366 
367 /*@
368   PCSPAISetBlockSize - set the block size for the SPAI preconditioner
369 
370   Input Parameters:
371 + pc - the preconditioner
372 - n - block size (default 1)
373 
374   Notes:
375     A block
376                  size of 1 treats A as a matrix of scalar elements. A
377                  block size of s > 1 treats A as a matrix of sxs
378                  blocks. A block size of 0 treats A as a matrix with
379                  variable sized blocks, which are determined by
380                  searching for dense square diagonal blocks in A.
381                  This can be very effective for finite-element
382                  matrices.
383 
384                  SPAI will convert A to block form, use a block
385                  version of the preconditioner algorithm, and then
386                  convert the result back to scalar form.
387 
388                  In many cases the a block-size parameter other than 1
389                  can lead to very significant improvement in
390                  performance.
391 
392   Level: intermediate
393 
394 .seealso: `PCSPAI`, `PCSetType()`
395 @*/
396 PetscErrorCode  PCSPAISetBlockSize(PC pc,PetscInt block_size1)
397 {
398   PetscFunctionBegin;
399   PetscTryMethod(pc,"PCSPAISetBlockSize_C",(PC,PetscInt),(pc,block_size1));
400   PetscFunctionReturn(0);
401 }
402 
403 /**********************************************************************/
404 
405 /*@
406   PCSPAISetCacheSize - specify cache size in the SPAI preconditioner
407 
408   Input Parameters:
409 + pc - the preconditioner
410 - n -  cache size {0,1,2,3,4,5} (default 5)
411 
412   Notes:
413     SPAI uses a hash table to cache messages and avoid
414                  redundant communication. If suggest always using
415                  5. This parameter is irrelevant in the serial
416                  version.
417 
418   Level: intermediate
419 
420 .seealso: `PCSPAI`, `PCSetType()`
421 @*/
422 PetscErrorCode  PCSPAISetCacheSize(PC pc,PetscInt cache_size)
423 {
424   PetscFunctionBegin;
425   PetscTryMethod(pc,"PCSPAISetCacheSize_C",(PC,PetscInt),(pc,cache_size));
426   PetscFunctionReturn(0);
427 }
428 
429 /**********************************************************************/
430 
431 /*@
432   PCSPAISetVerbose - verbosity level for the SPAI preconditioner
433 
434   Input Parameters:
435 + pc - the preconditioner
436 - n - level (default 1)
437 
438   Notes:
439     print parameters, timings and matrix statistics
440 
441   Level: intermediate
442 
443 .seealso: `PCSPAI`, `PCSetType()`
444 @*/
445 PetscErrorCode  PCSPAISetVerbose(PC pc,PetscInt verbose)
446 {
447   PetscFunctionBegin;
448   PetscTryMethod(pc,"PCSPAISetVerbose_C",(PC,PetscInt),(pc,verbose));
449   PetscFunctionReturn(0);
450 }
451 
452 /**********************************************************************/
453 
454 /*@
455   PCSPAISetSp - specify a symmetric matrix sparsity pattern in the SPAI preconditioner
456 
457   Input Parameters:
458 + pc - the preconditioner
459 - n - 0 or 1
460 
461   Notes:
462     If A has a symmetric nonzero pattern use -sp 1 to
463                  improve performance by eliminating some communication
464                  in the parallel version. Even if A does not have a
465                  symmetric nonzero pattern -sp 1 may well lead to good
466                  results, but the code will not follow the published
467                  SPAI algorithm exactly.
468 
469   Level: intermediate
470 
471 .seealso: `PCSPAI`, `PCSetType()`
472 @*/
473 PetscErrorCode  PCSPAISetSp(PC pc,PetscInt sp)
474 {
475   PetscFunctionBegin;
476   PetscTryMethod(pc,"PCSPAISetSp_C",(PC,PetscInt),(pc,sp));
477   PetscFunctionReturn(0);
478 }
479 
480 /**********************************************************************/
481 
482 /**********************************************************************/
483 
484 static PetscErrorCode PCSetFromOptions_SPAI(PetscOptionItems *PetscOptionsObject,PC pc)
485 {
486   PC_SPAI        *ispai = (PC_SPAI*)pc->data;
487   int            nbsteps1,max1,maxnew1,block_size1,cache_size,verbose,sp;
488   double         epsilon1;
489   PetscBool      flg;
490 
491   PetscFunctionBegin;
492   PetscOptionsHeadBegin(PetscOptionsObject,"SPAI options");
493   PetscCall(PetscOptionsReal("-pc_spai_epsilon","","PCSPAISetEpsilon",ispai->epsilon,&epsilon1,&flg));
494   if (flg) PetscCall(PCSPAISetEpsilon(pc,epsilon1));
495   PetscCall(PetscOptionsInt("-pc_spai_nbsteps","","PCSPAISetNBSteps",ispai->nbsteps,&nbsteps1,&flg));
496   if (flg) PetscCall(PCSPAISetNBSteps(pc,nbsteps1));
497   /* added 1/7/99 g.h. */
498   PetscCall(PetscOptionsInt("-pc_spai_max","","PCSPAISetMax",ispai->max,&max1,&flg));
499   if (flg) PetscCall(PCSPAISetMax(pc,max1));
500   PetscCall(PetscOptionsInt("-pc_spai_maxnew","","PCSPAISetMaxNew",ispai->maxnew,&maxnew1,&flg));
501   if (flg) PetscCall(PCSPAISetMaxNew(pc,maxnew1));
502   PetscCall(PetscOptionsInt("-pc_spai_block_size","","PCSPAISetBlockSize",ispai->block_size,&block_size1,&flg));
503   if (flg) PetscCall(PCSPAISetBlockSize(pc,block_size1));
504   PetscCall(PetscOptionsInt("-pc_spai_cache_size","","PCSPAISetCacheSize",ispai->cache_size,&cache_size,&flg));
505   if (flg) PetscCall(PCSPAISetCacheSize(pc,cache_size));
506   PetscCall(PetscOptionsInt("-pc_spai_verbose","","PCSPAISetVerbose",ispai->verbose,&verbose,&flg));
507   if (flg) PetscCall(PCSPAISetVerbose(pc,verbose));
508   PetscCall(PetscOptionsInt("-pc_spai_sp","","PCSPAISetSp",ispai->sp,&sp,&flg));
509   if (flg) PetscCall(PCSPAISetSp(pc,sp));
510   PetscOptionsHeadEnd();
511   PetscFunctionReturn(0);
512 }
513 
514 /**********************************************************************/
515 
516 /*MC
517    PCSPAI - Use the Sparse Approximate Inverse method of Grote and Barnard
518      as a preconditioner (SIAM J. Sci. Comput.; vol 18, nr 3)
519 
520    Options Database Keys:
521 +  -pc_spai_epsilon <eps> - set tolerance
522 .  -pc_spai_nbstep <n> - set nbsteps
523 .  -pc_spai_max <m> - set max
524 .  -pc_spai_max_new <m> - set maxnew
525 .  -pc_spai_block_size <n> - set block size
526 .  -pc_spai_cache_size <n> - set cache size
527 .  -pc_spai_sp <m> - set sp
528 -  -pc_spai_set_verbose <true,false> - verbose output
529 
530    Notes:
531     This only works with AIJ matrices.
532 
533    Level: beginner
534 
535 .seealso: `PCCreate()`, `PCSetType()`, `PCType`, `PC`,
536           `PCSPAISetEpsilon()`, `PCSPAISetMax()`, `PCSPAISetMaxNew()`, `PCSPAISetBlockSize()`,
537           `PCSPAISetVerbose()`, `PCSPAISetSp()`
538 M*/
539 
540 PETSC_EXTERN PetscErrorCode PCCreate_SPAI(PC pc)
541 {
542   PC_SPAI *ispai;
543 
544   PetscFunctionBegin;
545   PetscCall(PetscNewLog(pc,&ispai));
546   pc->data = ispai;
547 
548   pc->ops->destroy         = PCDestroy_SPAI;
549   pc->ops->apply           = PCApply_SPAI;
550   pc->ops->matapply        = PCMatApply_SPAI;
551   pc->ops->applyrichardson = 0;
552   pc->ops->setup           = PCSetUp_SPAI;
553   pc->ops->view            = PCView_SPAI;
554   pc->ops->setfromoptions  = PCSetFromOptions_SPAI;
555 
556   ispai->epsilon    = .4;
557   ispai->nbsteps    = 5;
558   ispai->max        = 5000;
559   ispai->maxnew     = 5;
560   ispai->block_size = 1;
561   ispai->cache_size = 5;
562   ispai->verbose    = 0;
563 
564   ispai->sp = 1;
565   PetscCallMPI(MPI_Comm_dup(PetscObjectComm((PetscObject)pc),&(ispai->comm_spai)));
566 
567   PetscCall(PetscObjectComposeFunction((PetscObject)pc,"PCSPAISetEpsilon_C",PCSPAISetEpsilon_SPAI));
568   PetscCall(PetscObjectComposeFunction((PetscObject)pc,"PCSPAISetNBSteps_C",PCSPAISetNBSteps_SPAI));
569   PetscCall(PetscObjectComposeFunction((PetscObject)pc,"PCSPAISetMax_C",PCSPAISetMax_SPAI));
570   PetscCall(PetscObjectComposeFunction((PetscObject)pc,"PCSPAISetMaxNew_C",PCSPAISetMaxNew_SPAI));
571   PetscCall(PetscObjectComposeFunction((PetscObject)pc,"PCSPAISetBlockSize_C",PCSPAISetBlockSize_SPAI));
572   PetscCall(PetscObjectComposeFunction((PetscObject)pc,"PCSPAISetCacheSize_C",PCSPAISetCacheSize_SPAI));
573   PetscCall(PetscObjectComposeFunction((PetscObject)pc,"PCSPAISetVerbose_C",PCSPAISetVerbose_SPAI));
574   PetscCall(PetscObjectComposeFunction((PetscObject)pc,"PCSPAISetSp_C",PCSPAISetSp_SPAI));
575   PetscFunctionReturn(0);
576 }
577 
578 /**********************************************************************/
579 
580 /*
581    Converts from a PETSc matrix to an SPAI matrix
582 */
583 PetscErrorCode ConvertMatToMatrix(MPI_Comm comm, Mat A,Mat AT,matrix **B)
584 {
585   matrix                  *M;
586   int                     i,j,col;
587   int                     row_indx;
588   int                     len,pe,local_indx,start_indx;
589   int                     *mapping;
590   const int               *cols;
591   const double            *vals;
592   int                     n,mnl,nnl,nz,rstart,rend;
593   PetscMPIInt             size,rank;
594   struct compressed_lines *rows;
595 
596   PetscFunctionBegin;
597   PetscCallMPI(MPI_Comm_size(comm,&size));
598   PetscCallMPI(MPI_Comm_rank(comm,&rank));
599   PetscCall(MatGetSize(A,&n,&n));
600   PetscCall(MatGetLocalSize(A,&mnl,&nnl));
601 
602   /*
603     not sure why a barrier is required. commenting out
604   PetscCallMPI(MPI_Barrier(comm));
605   */
606 
607   M = new_matrix((SPAI_Comm)comm);
608 
609   M->n              = n;
610   M->bs             = 1;
611   M->max_block_size = 1;
612 
613   M->mnls          = (int*)malloc(sizeof(int)*size);
614   M->start_indices = (int*)malloc(sizeof(int)*size);
615   M->pe            = (int*)malloc(sizeof(int)*n);
616   M->block_sizes   = (int*)malloc(sizeof(int)*n);
617   for (i=0; i<n; i++) M->block_sizes[i] = 1;
618 
619   PetscCallMPI(MPI_Allgather(&mnl,1,MPI_INT,M->mnls,1,MPI_INT,comm));
620 
621   M->start_indices[0] = 0;
622   for (i=1; i<size; i++) M->start_indices[i] = M->start_indices[i-1] + M->mnls[i-1];
623 
624   M->mnl            = M->mnls[M->myid];
625   M->my_start_index = M->start_indices[M->myid];
626 
627   for (i=0; i<size; i++) {
628     start_indx = M->start_indices[i];
629     for (j=0; j<M->mnls[i]; j++) M->pe[start_indx+j] = i;
630   }
631 
632   if (AT) {
633     M->lines = new_compressed_lines(M->mnls[rank],1);
634   } else {
635     M->lines = new_compressed_lines(M->mnls[rank],0);
636   }
637 
638   rows = M->lines;
639 
640   /* Determine the mapping from global indices to pointers */
641   PetscCall(PetscMalloc1(M->n,&mapping));
642   pe         = 0;
643   local_indx = 0;
644   for (i=0; i<M->n; i++) {
645     if (local_indx >= M->mnls[pe]) {
646       pe++;
647       local_indx = 0;
648     }
649     mapping[i] = local_indx + M->start_indices[pe];
650     local_indx++;
651   }
652 
653   /*********************************************************/
654   /************** Set up the row structure *****************/
655   /*********************************************************/
656 
657   PetscCall(MatGetOwnershipRange(A,&rstart,&rend));
658   for (i=rstart; i<rend; i++) {
659     row_indx = i - rstart;
660     PetscCall(MatGetRow(A,i,&nz,&cols,&vals));
661     /* allocate buffers */
662     rows->ptrs[row_indx] = (int*)malloc(nz*sizeof(int));
663     rows->A[row_indx]    = (double*)malloc(nz*sizeof(double));
664     /* copy the matrix */
665     for (j=0; j<nz; j++) {
666       col = cols[j];
667       len = rows->len[row_indx]++;
668 
669       rows->ptrs[row_indx][len] = mapping[col];
670       rows->A[row_indx][len]    = vals[j];
671     }
672     rows->slen[row_indx] = rows->len[row_indx];
673 
674     PetscCall(MatRestoreRow(A,i,&nz,&cols,&vals));
675   }
676 
677   /************************************************************/
678   /************** Set up the column structure *****************/
679   /*********************************************************/
680 
681   if (AT) {
682 
683     for (i=rstart; i<rend; i++) {
684       row_indx = i - rstart;
685       PetscCall(MatGetRow(AT,i,&nz,&cols,&vals));
686       /* allocate buffers */
687       rows->rptrs[row_indx] = (int*)malloc(nz*sizeof(int));
688       /* copy the matrix (i.e., the structure) */
689       for (j=0; j<nz; j++) {
690         col = cols[j];
691         len = rows->rlen[row_indx]++;
692 
693         rows->rptrs[row_indx][len] = mapping[col];
694       }
695       PetscCall(MatRestoreRow(AT,i,&nz,&cols,&vals));
696     }
697   }
698 
699   PetscCall(PetscFree(mapping));
700 
701   order_pointers(M);
702   M->maxnz = calc_maxnz(M);
703   *B       = M;
704   PetscFunctionReturn(0);
705 }
706 
707 /**********************************************************************/
708 
709 /*
710    Converts from an SPAI matrix B  to a PETSc matrix PB.
711    This assumes that the SPAI matrix B is stored in
712    COMPRESSED-ROW format.
713 */
714 PetscErrorCode ConvertMatrixToMat(MPI_Comm comm,matrix *B,Mat *PB)
715 {
716   PetscMPIInt    size,rank;
717   int            m,n,M,N;
718   int            d_nz,o_nz;
719   int            *d_nnz,*o_nnz;
720   int            i,k,global_row,global_col,first_diag_col,last_diag_col;
721   PetscScalar    val;
722 
723   PetscFunctionBegin;
724   PetscCallMPI(MPI_Comm_size(comm,&size));
725   PetscCallMPI(MPI_Comm_rank(comm,&rank));
726 
727   m    = n = B->mnls[rank];
728   d_nz = o_nz = 0;
729 
730   /* Determine preallocation for MatCreateAIJ */
731   PetscCall(PetscMalloc1(m,&d_nnz));
732   PetscCall(PetscMalloc1(m,&o_nnz));
733   for (i=0; i<m; i++) d_nnz[i] = o_nnz[i] = 0;
734   first_diag_col = B->start_indices[rank];
735   last_diag_col  = first_diag_col + B->mnls[rank];
736   for (i=0; i<B->mnls[rank]; i++) {
737     for (k=0; k<B->lines->len[i]; k++) {
738       global_col = B->lines->ptrs[i][k];
739       if ((global_col >= first_diag_col) && (global_col < last_diag_col)) d_nnz[i]++;
740       else o_nnz[i]++;
741     }
742   }
743 
744   M = N = B->n;
745   /* Here we only know how to create AIJ format */
746   PetscCall(MatCreate(comm,PB));
747   PetscCall(MatSetSizes(*PB,m,n,M,N));
748   PetscCall(MatSetType(*PB,MATAIJ));
749   PetscCall(MatSeqAIJSetPreallocation(*PB,d_nz,d_nnz));
750   PetscCall(MatMPIAIJSetPreallocation(*PB,d_nz,d_nnz,o_nz,o_nnz));
751 
752   for (i=0; i<B->mnls[rank]; i++) {
753     global_row = B->start_indices[rank]+i;
754     for (k=0; k<B->lines->len[i]; k++) {
755       global_col = B->lines->ptrs[i][k];
756 
757       val  = B->lines->A[i][k];
758       PetscCall(MatSetValues(*PB,1,&global_row,1,&global_col,&val,ADD_VALUES));
759     }
760   }
761 
762   PetscCall(PetscFree(d_nnz));
763   PetscCall(PetscFree(o_nnz));
764 
765   PetscCall(MatAssemblyBegin(*PB,MAT_FINAL_ASSEMBLY));
766   PetscCall(MatAssemblyEnd(*PB,MAT_FINAL_ASSEMBLY));
767   PetscFunctionReturn(0);
768 }
769 
770 /**********************************************************************/
771 
772 /*
773    Converts from an SPAI vector v  to a PETSc vec Pv.
774 */
775 PetscErrorCode ConvertVectorToVec(MPI_Comm comm,vector *v,Vec *Pv)
776 {
777   PetscMPIInt size,rank;
778   int         m,M,i,*mnls,*start_indices,*global_indices;
779 
780   PetscFunctionBegin;
781   PetscCallMPI(MPI_Comm_size(comm,&size));
782   PetscCallMPI(MPI_Comm_rank(comm,&rank));
783 
784   m = v->mnl;
785   M = v->n;
786 
787   PetscCall(VecCreateMPI(comm,m,M,Pv));
788 
789   PetscCall(PetscMalloc1(size,&mnls));
790   PetscCallMPI(MPI_Allgather(&v->mnl,1,MPI_INT,mnls,1,MPI_INT,comm));
791 
792   PetscCall(PetscMalloc1(size,&start_indices));
793 
794   start_indices[0] = 0;
795   for (i=1; i<size; i++) start_indices[i] = start_indices[i-1] +mnls[i-1];
796 
797   PetscCall(PetscMalloc1(v->mnl,&global_indices));
798   for (i=0; i<v->mnl; i++) global_indices[i] = start_indices[rank] + i;
799 
800   PetscCall(PetscFree(mnls));
801   PetscCall(PetscFree(start_indices));
802 
803   PetscCall(VecSetValues(*Pv,v->mnl,global_indices,v->v,INSERT_VALUES));
804   PetscCall(VecAssemblyBegin(*Pv));
805   PetscCall(VecAssemblyEnd(*Pv));
806 
807   PetscCall(PetscFree(global_indices));
808   PetscFunctionReturn(0);
809 }
810