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