xref: /petsc/src/mat/impls/shell/shell.c (revision c6db04a5321582041def2b1e244c75985478b3ef)
1 
2 /*
3    This provides a simple shell for Fortran (and C programmers) to
4   create a very simple matrix class for use with KSP without coding
5   much of anything.
6 */
7 
8 #include <private/matimpl.h>        /*I "petscmat.h" I*/
9 #include <private/vecimpl.h>
10 
11 typedef struct {
12   PetscErrorCode (*destroy)(Mat);
13   PetscErrorCode (*mult)(Mat,Vec,Vec);
14   PetscErrorCode (*multtranspose)(Mat,Vec,Vec);
15   PetscErrorCode (*getdiagonal)(Mat,Vec);
16   PetscBool      scale,shift;
17   PetscScalar    vscale,vshift;
18   void           *ctx;
19 } Mat_Shell;
20 
21 #undef __FUNCT__
22 #define __FUNCT__ "MatShellGetContext"
23 /*@C
24     MatShellGetContext - Returns the user-provided context associated with a shell matrix.
25 
26     Not Collective
27 
28     Input Parameter:
29 .   mat - the matrix, should have been created with MatCreateShell()
30 
31     Output Parameter:
32 .   ctx - the user provided context
33 
34     Level: advanced
35 
36     Notes:
37     This routine is intended for use within various shell matrix routines,
38     as set with MatShellSetOperation().
39 
40 .keywords: matrix, shell, get, context
41 
42 .seealso: MatCreateShell(), MatShellSetOperation(), MatShellSetContext()
43 @*/
44 PetscErrorCode  MatShellGetContext(Mat mat,void **ctx)
45 {
46   PetscErrorCode ierr;
47   PetscBool      flg;
48 
49   PetscFunctionBegin;
50   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
51   PetscValidPointer(ctx,2);
52   ierr = PetscTypeCompare((PetscObject)mat,MATSHELL,&flg);CHKERRQ(ierr);
53   if (!flg) *ctx = 0;
54   else      *ctx = ((Mat_Shell*)(mat->data))->ctx;
55   PetscFunctionReturn(0);
56 }
57 
58 #undef __FUNCT__
59 #define __FUNCT__ "MatDestroy_Shell"
60 PetscErrorCode MatDestroy_Shell(Mat mat)
61 {
62   PetscErrorCode ierr;
63   Mat_Shell      *shell;
64 
65   PetscFunctionBegin;
66   shell = (Mat_Shell*)mat->data;
67   if (shell->destroy) {ierr = (*shell->destroy)(mat);CHKERRQ(ierr);}
68   ierr = PetscFree(shell);CHKERRQ(ierr);
69   PetscFunctionReturn(0);
70 }
71 
72 #undef __FUNCT__
73 #define __FUNCT__ "MatMult_Shell"
74 PetscErrorCode MatMult_Shell(Mat A,Vec x,Vec y)
75 {
76   Mat_Shell      *shell = (Mat_Shell*)A->data;
77   PetscErrorCode ierr;
78 
79   PetscFunctionBegin;
80   ierr = (*shell->mult)(A,x,y);CHKERRQ(ierr);
81   if (shell->shift && shell->scale) {
82     ierr = VecAXPBY(y,shell->vshift,shell->vscale,x);CHKERRQ(ierr);
83   } else if (shell->scale) {
84     ierr = VecScale(y,shell->vscale);CHKERRQ(ierr);
85   } else {
86     ierr = VecAXPY(y,shell->vshift,x);CHKERRQ(ierr);
87   }
88   PetscFunctionReturn(0);
89 }
90 
91 #undef __FUNCT__
92 #define __FUNCT__ "MatMultTranspose_Shell"
93 PetscErrorCode MatMultTranspose_Shell(Mat A,Vec x,Vec y)
94 {
95   Mat_Shell      *shell = (Mat_Shell*)A->data;
96   PetscErrorCode ierr;
97 
98   PetscFunctionBegin;
99   ierr = (*shell->multtranspose)(A,x,y);CHKERRQ(ierr);
100   if (shell->shift && shell->scale) {
101     ierr = VecAXPBY(y,shell->vshift,shell->vscale,x);CHKERRQ(ierr);
102   } else if (shell->scale) {
103     ierr = VecScale(y,shell->vscale);CHKERRQ(ierr);
104   } else {
105     ierr = VecAXPY(y,shell->vshift,x);CHKERRQ(ierr);
106   }
107   PetscFunctionReturn(0);
108 }
109 
110 #undef __FUNCT__
111 #define __FUNCT__ "MatGetDiagonal_Shell"
112 PetscErrorCode MatGetDiagonal_Shell(Mat A,Vec v)
113 {
114   Mat_Shell      *shell = (Mat_Shell*)A->data;
115   PetscErrorCode ierr;
116 
117   PetscFunctionBegin;
118   ierr = (*shell->getdiagonal)(A,v);CHKERRQ(ierr);
119   if (shell->scale) {
120     ierr = VecScale(v,shell->vscale);CHKERRQ(ierr);
121   }
122   if (shell->shift) {
123     ierr = VecShift(v,shell->vshift);CHKERRQ(ierr);
124   }
125   PetscFunctionReturn(0);
126 }
127 
128 #undef __FUNCT__
129 #define __FUNCT__ "MatShift_Shell"
130 PetscErrorCode MatShift_Shell(Mat Y,PetscScalar a)
131 {
132   Mat_Shell *shell = (Mat_Shell*)Y->data;
133 
134   PetscFunctionBegin;
135   if (shell->scale || shell->shift) {
136     shell->vshift += a;
137   } else {
138     shell->mult  = Y->ops->mult;
139     Y->ops->mult = MatMult_Shell;
140     if (Y->ops->multtranspose) {
141       shell->multtranspose  = Y->ops->multtranspose;
142       Y->ops->multtranspose = MatMultTranspose_Shell;
143     }
144     if (Y->ops->getdiagonal) {
145       shell->getdiagonal  = Y->ops->getdiagonal;
146       Y->ops->getdiagonal = MatGetDiagonal_Shell;
147     }
148     shell->vshift = a;
149   }
150   shell->shift  =  PETSC_TRUE;
151   PetscFunctionReturn(0);
152 }
153 
154 #undef __FUNCT__
155 #define __FUNCT__ "MatScale_Shell"
156 PetscErrorCode MatScale_Shell(Mat Y,PetscScalar a)
157 {
158   Mat_Shell *shell = (Mat_Shell*)Y->data;
159 
160   PetscFunctionBegin;
161   if (shell->scale || shell->shift) {
162     shell->vscale *= a;
163   } else {
164     shell->mult  = Y->ops->mult;
165     Y->ops->mult = MatMult_Shell;
166     if (Y->ops->multtranspose) {
167       shell->multtranspose  = Y->ops->multtranspose;
168       Y->ops->multtranspose = MatMultTranspose_Shell;
169     }
170     if (Y->ops->getdiagonal) {
171       shell->getdiagonal  = Y->ops->getdiagonal;
172       Y->ops->getdiagonal = MatGetDiagonal_Shell;
173     }
174     shell->vscale = a;
175   }
176   shell->scale  =  PETSC_TRUE;
177   PetscFunctionReturn(0);
178 }
179 
180 #undef __FUNCT__
181 #define __FUNCT__ "MatAssemblyEnd_Shell"
182 PetscErrorCode MatAssemblyEnd_Shell(Mat Y,MatAssemblyType t)
183 {
184   Mat_Shell *shell = (Mat_Shell*)Y->data;
185 
186   PetscFunctionBegin;
187   if ((shell->shift || shell->scale) && t == MAT_FINAL_ASSEMBLY) {
188     shell->scale  = PETSC_FALSE;
189     shell->shift  = PETSC_FALSE;
190     shell->vshift = 0.0;
191     shell->vscale = 1.0;
192     Y->ops->mult          = shell->mult;
193     Y->ops->multtranspose = shell->multtranspose;
194     Y->ops->getdiagonal   = shell->getdiagonal;
195   }
196   PetscFunctionReturn(0);
197 }
198 
199 extern PetscErrorCode MatConvert_Shell(Mat, const MatType,MatReuse,Mat*);
200 
201 #undef __FUNCT__
202 #define __FUNCT__ "MatSetBlockSize_Shell"
203 PetscErrorCode MatSetBlockSize_Shell(Mat A,PetscInt bs)
204 {
205   PetscErrorCode ierr;
206 
207   PetscFunctionBegin;
208   ierr = PetscLayoutSetBlockSize(A->rmap,bs);CHKERRQ(ierr);
209   ierr = PetscLayoutSetBlockSize(A->cmap,bs);CHKERRQ(ierr);
210   PetscFunctionReturn(0);
211 }
212 
213 static struct _MatOps MatOps_Values = {0,
214        0,
215        0,
216        0,
217 /* 4*/ 0,
218        0,
219        0,
220        0,
221        0,
222        0,
223 /*10*/ 0,
224        0,
225        0,
226        0,
227        0,
228 /*15*/ 0,
229        0,
230        0,
231        0,
232        0,
233 /*20*/ 0,
234        MatAssemblyEnd_Shell,
235        0,
236        0,
237 /*24*/ 0,
238        0,
239        0,
240        0,
241        0,
242 /*29*/ 0,
243        0,
244        0,
245        0,
246        0,
247 /*34*/ 0,
248        0,
249        0,
250        0,
251        0,
252 /*39*/ 0,
253        0,
254        0,
255        0,
256        0,
257 /*44*/ 0,
258        MatScale_Shell,
259        MatShift_Shell,
260        0,
261        0,
262 /*49*/ MatSetBlockSize_Shell,
263        0,
264        0,
265        0,
266        0,
267 /*54*/ 0,
268        0,
269        0,
270        0,
271        0,
272 /*59*/ 0,
273        MatDestroy_Shell,
274        0,
275        0,
276        0,
277 /*64*/ 0,
278        0,
279        0,
280        0,
281        0,
282 /*69*/ 0,
283        0,
284        MatConvert_Shell,
285        0,
286        0,
287 /*74*/ 0,
288        0,
289        0,
290        0,
291        0,
292 /*79*/ 0,
293        0,
294        0,
295        0,
296        0,
297 /*84*/ 0,
298        0,
299        0,
300        0,
301        0,
302 /*89*/ 0,
303        0,
304        0,
305        0,
306        0,
307 /*94*/ 0,
308        0,
309        0,
310        0};
311 
312 /*MC
313    MATSHELL - MATSHELL = "shell" - A matrix type to be used to define your own matrix type -- perhaps matrix free.
314 
315   Level: advanced
316 
317 .seealso: MatCreateShell
318 M*/
319 
320 EXTERN_C_BEGIN
321 #undef __FUNCT__
322 #define __FUNCT__ "MatCreate_Shell"
323 PetscErrorCode  MatCreate_Shell(Mat A)
324 {
325   Mat_Shell      *b;
326   PetscErrorCode ierr;
327 
328   PetscFunctionBegin;
329   ierr = PetscMemcpy(A->ops,&MatOps_Values,sizeof(struct _MatOps));CHKERRQ(ierr);
330 
331   ierr = PetscNewLog(A,Mat_Shell,&b);CHKERRQ(ierr);
332   A->data = (void*)b;
333 
334   ierr = PetscLayoutSetBlockSize(A->rmap,1);CHKERRQ(ierr);
335   ierr = PetscLayoutSetBlockSize(A->cmap,1);CHKERRQ(ierr);
336   ierr = PetscLayoutSetUp(A->rmap);CHKERRQ(ierr);
337   ierr = PetscLayoutSetUp(A->cmap);CHKERRQ(ierr);
338 
339   b->ctx           = 0;
340   b->scale         = PETSC_FALSE;
341   b->shift         = PETSC_FALSE;
342   b->vshift        = 0.0;
343   b->vscale        = 1.0;
344   b->mult          = 0;
345   b->multtranspose = 0;
346   b->getdiagonal   = 0;
347   A->assembled     = PETSC_TRUE;
348   A->preallocated  = PETSC_FALSE;
349   ierr = PetscObjectChangeTypeName((PetscObject)A,MATSHELL);CHKERRQ(ierr);
350   PetscFunctionReturn(0);
351 }
352 EXTERN_C_END
353 
354 #undef __FUNCT__
355 #define __FUNCT__ "MatCreateShell"
356 /*@C
357    MatCreateShell - Creates a new matrix class for use with a user-defined
358    private data storage format.
359 
360   Collective on MPI_Comm
361 
362    Input Parameters:
363 +  comm - MPI communicator
364 .  m - number of local rows (must be given)
365 .  n - number of local columns (must be given)
366 .  M - number of global rows (may be PETSC_DETERMINE)
367 .  N - number of global columns (may be PETSC_DETERMINE)
368 -  ctx - pointer to data needed by the shell matrix routines
369 
370    Output Parameter:
371 .  A - the matrix
372 
373    Level: advanced
374 
375   Usage:
376 $    extern int mult(Mat,Vec,Vec);
377 $    MatCreateShell(comm,m,n,M,N,ctx,&mat);
378 $    MatShellSetOperation(mat,MATOP_MULT,(void(*)(void))mult);
379 $    [ Use matrix for operations that have been set ]
380 $    MatDestroy(mat);
381 
382    Notes:
383    The shell matrix type is intended to provide a simple class to use
384    with KSP (such as, for use with matrix-free methods). You should not
385    use the shell type if you plan to define a complete matrix class.
386 
387    Fortran Notes: The context can only be an integer or a PetscObject
388       unfortunately it cannot be a Fortran array or derived type.
389 
390    PETSc requires that matrices and vectors being used for certain
391    operations are partitioned accordingly.  For example, when
392    creating a shell matrix, A, that supports parallel matrix-vector
393    products using MatMult(A,x,y) the user should set the number
394    of local matrix rows to be the number of local elements of the
395    corresponding result vector, y. Note that this is information is
396    required for use of the matrix interface routines, even though
397    the shell matrix may not actually be physically partitioned.
398    For example,
399 
400 $
401 $     Vec x, y
402 $     extern int mult(Mat,Vec,Vec);
403 $     Mat A
404 $
405 $     VecCreateMPI(comm,PETSC_DECIDE,M,&y);
406 $     VecCreateMPI(comm,PETSC_DECIDE,N,&x);
407 $     VecGetLocalSize(y,&m);
408 $     VecGetLocalSize(x,&n);
409 $     MatCreateShell(comm,m,n,M,N,ctx,&A);
410 $     MatShellSetOperation(mat,MATOP_MULT,(void(*)(void))mult);
411 $     MatMult(A,x,y);
412 $     MatDestroy(A);
413 $     VecDestroy(y); VecDestroy(x);
414 $
415 
416 .keywords: matrix, shell, create
417 
418 .seealso: MatShellSetOperation(), MatHasOperation(), MatShellGetContext(), MatShellSetContext()
419 @*/
420 PetscErrorCode  MatCreateShell(MPI_Comm comm,PetscInt m,PetscInt n,PetscInt M,PetscInt N,void *ctx,Mat *A)
421 {
422   PetscErrorCode ierr;
423 
424   PetscFunctionBegin;
425   ierr = MatCreate(comm,A);CHKERRQ(ierr);
426   ierr = MatSetSizes(*A,m,n,M,N);CHKERRQ(ierr);
427 
428   ierr = MatSetType(*A,MATSHELL);CHKERRQ(ierr);
429   ierr = MatShellSetContext(*A,ctx);CHKERRQ(ierr);
430   PetscFunctionReturn(0);
431 }
432 
433 #undef __FUNCT__
434 #define __FUNCT__ "MatShellSetContext"
435 /*@
436     MatShellSetContext - sets the context for a shell matrix
437 
438    Logically Collective on Mat
439 
440     Input Parameters:
441 +   mat - the shell matrix
442 -   ctx - the context
443 
444    Level: advanced
445 
446    Fortran Notes: The context can only be an integer or a PetscObject
447       unfortunately it cannot be a Fortran array or derived type.
448 
449 .seealso: MatCreateShell(), MatShellGetContext(), MatShellGetOperation()
450 @*/
451 PetscErrorCode  MatShellSetContext(Mat mat,void *ctx)
452 {
453   Mat_Shell      *shell = (Mat_Shell*)mat->data;
454   PetscErrorCode ierr;
455   PetscBool      flg;
456 
457   PetscFunctionBegin;
458   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
459   ierr = PetscTypeCompare((PetscObject)mat,MATSHELL,&flg);CHKERRQ(ierr);
460   if (flg) {
461     shell->ctx = ctx;
462   }
463   PetscFunctionReturn(0);
464 }
465 
466 #undef __FUNCT__
467 #define __FUNCT__ "MatShellSetOperation"
468 /*@C
469     MatShellSetOperation - Allows user to set a matrix operation for
470                            a shell matrix.
471 
472    Logically Collective on Mat
473 
474     Input Parameters:
475 +   mat - the shell matrix
476 .   op - the name of the operation
477 -   f - the function that provides the operation.
478 
479    Level: advanced
480 
481     Usage:
482 $      extern PetscErrorCode usermult(Mat,Vec,Vec);
483 $      ierr = MatCreateShell(comm,m,n,M,N,ctx,&A);
484 $      ierr = MatShellSetOperation(A,MATOP_MULT,(void(*)(void))usermult);
485 
486     Notes:
487     See the file include/petscmat.h for a complete list of matrix
488     operations, which all have the form MATOP_<OPERATION>, where
489     <OPERATION> is the name (in all capital letters) of the
490     user interface routine (e.g., MatMult() -> MATOP_MULT).
491 
492     All user-provided functions should have the same calling
493     sequence as the usual matrix interface routines, since they
494     are intended to be accessed via the usual matrix interface
495     routines, e.g.,
496 $       MatMult(Mat,Vec,Vec) -> usermult(Mat,Vec,Vec)
497 
498     In particular each function MUST return an error code of 0 on success and
499     nonzero on failure.
500 
501     Within each user-defined routine, the user should call
502     MatShellGetContext() to obtain the user-defined context that was
503     set by MatCreateShell().
504 
505     Fortran Notes: For MatGetVecs() the user code should check if the input left or right matrix is -1 and in that case not
506        generate a matrix. See src/mat/examples/tests/ex120f.F
507 
508 .keywords: matrix, shell, set, operation
509 
510 .seealso: MatCreateShell(), MatShellGetContext(), MatShellGetOperation(), MatShellSetContext()
511 @*/
512 PetscErrorCode  MatShellSetOperation(Mat mat,MatOperation op,void (*f)(void))
513 {
514   PetscErrorCode ierr;
515   PetscBool      flg;
516 
517   PetscFunctionBegin;
518   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
519   if (op == MATOP_DESTROY) {
520     ierr = PetscTypeCompare((PetscObject)mat,MATSHELL,&flg);CHKERRQ(ierr);
521     if (flg) {
522        Mat_Shell *shell = (Mat_Shell*)mat->data;
523        shell->destroy                 = (PetscErrorCode (*)(Mat)) f;
524     } else mat->ops->destroy          = (PetscErrorCode (*)(Mat)) f;
525   }
526   else if (op == MATOP_VIEW) mat->ops->view  = (PetscErrorCode (*)(Mat,PetscViewer)) f;
527   else                       (((void(**)(void))mat->ops)[op]) = f;
528 
529   PetscFunctionReturn(0);
530 }
531 
532 #undef __FUNCT__
533 #define __FUNCT__ "MatShellGetOperation"
534 /*@C
535     MatShellGetOperation - Gets a matrix function for a shell matrix.
536 
537     Not Collective
538 
539     Input Parameters:
540 +   mat - the shell matrix
541 -   op - the name of the operation
542 
543     Output Parameter:
544 .   f - the function that provides the operation.
545 
546     Level: advanced
547 
548     Notes:
549     See the file include/petscmat.h for a complete list of matrix
550     operations, which all have the form MATOP_<OPERATION>, where
551     <OPERATION> is the name (in all capital letters) of the
552     user interface routine (e.g., MatMult() -> MATOP_MULT).
553 
554     All user-provided functions have the same calling
555     sequence as the usual matrix interface routines, since they
556     are intended to be accessed via the usual matrix interface
557     routines, e.g.,
558 $       MatMult(Mat,Vec,Vec) -> usermult(Mat,Vec,Vec)
559 
560     Within each user-defined routine, the user should call
561     MatShellGetContext() to obtain the user-defined context that was
562     set by MatCreateShell().
563 
564 .keywords: matrix, shell, set, operation
565 
566 .seealso: MatCreateShell(), MatShellGetContext(), MatShellSetOperation(), MatShellSetContext()
567 @*/
568 PetscErrorCode  MatShellGetOperation(Mat mat,MatOperation op,void(**f)(void))
569 {
570   PetscErrorCode ierr;
571   PetscBool      flg;
572 
573   PetscFunctionBegin;
574   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
575   if (op == MATOP_DESTROY) {
576     ierr = PetscTypeCompare((PetscObject)mat,MATSHELL,&flg);CHKERRQ(ierr);
577     if (flg) {
578       Mat_Shell *shell = (Mat_Shell*)mat->data;
579       *f = (void(*)(void))shell->destroy;
580     } else {
581       *f = (void(*)(void))mat->ops->destroy;
582     }
583   } else if (op == MATOP_VIEW) {
584     *f = (void(*)(void))mat->ops->view;
585   } else {
586     *f = (((void(**)(void))mat->ops)[op]);
587   }
588 
589   PetscFunctionReturn(0);
590 }
591 
592