xref: /petsc/src/mat/impls/shell/shell.c (revision 2692d6ee476c3ad7c6573405ba3f5a5bb0473457)
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) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONG,"Must give local row and column count for matrix");
336 
337   ierr = PetscLayoutSetBlockSize(A->rmap,1);CHKERRQ(ierr);
338   ierr = PetscLayoutSetBlockSize(A->cmap,1);CHKERRQ(ierr);
339   ierr = PetscLayoutSetUp(A->rmap);CHKERRQ(ierr);
340   ierr = PetscLayoutSetUp(A->cmap);CHKERRQ(ierr);
341 
342   b->ctx           = 0;
343   b->scale         = PETSC_FALSE;
344   b->shift         = PETSC_FALSE;
345   b->vshift        = 0.0;
346   b->vscale        = 1.0;
347   b->mult          = 0;
348   b->multtranspose = 0;
349   b->getdiagonal   = 0;
350   A->assembled     = PETSC_TRUE;
351   A->preallocated  = PETSC_FALSE;
352   ierr = PetscObjectChangeTypeName((PetscObject)A,MATSHELL);CHKERRQ(ierr);
353   PetscFunctionReturn(0);
354 }
355 EXTERN_C_END
356 
357 #undef __FUNCT__
358 #define __FUNCT__ "MatCreateShell"
359 /*@C
360    MatCreateShell - Creates a new matrix class for use with a user-defined
361    private data storage format.
362 
363   Collective on MPI_Comm
364 
365    Input Parameters:
366 +  comm - MPI communicator
367 .  m - number of local rows (must be given)
368 .  n - number of local columns (must be given)
369 .  M - number of global rows (may be PETSC_DETERMINE)
370 .  N - number of global columns (may be PETSC_DETERMINE)
371 -  ctx - pointer to data needed by the shell matrix routines
372 
373    Output Parameter:
374 .  A - the matrix
375 
376    Level: advanced
377 
378   Usage:
379 $    extern int mult(Mat,Vec,Vec);
380 $    MatCreateShell(comm,m,n,M,N,ctx,&mat);
381 $    MatShellSetOperation(mat,MATOP_MULT,(void(*)(void))mult);
382 $    [ Use matrix for operations that have been set ]
383 $    MatDestroy(mat);
384 
385    Notes:
386    The shell matrix type is intended to provide a simple class to use
387    with KSP (such as, for use with matrix-free methods). You should not
388    use the shell type if you plan to define a complete matrix class.
389 
390    Fortran Notes: The context can only be an integer or a PetscObject
391       unfortunately it cannot be a Fortran array or derived type.
392 
393    PETSc requires that matrices and vectors being used for certain
394    operations are partitioned accordingly.  For example, when
395    creating a shell matrix, A, that supports parallel matrix-vector
396    products using MatMult(A,x,y) the user should set the number
397    of local matrix rows to be the number of local elements of the
398    corresponding result vector, y. Note that this is information is
399    required for use of the matrix interface routines, even though
400    the shell matrix may not actually be physically partitioned.
401    For example,
402 
403 $
404 $     Vec x, y
405 $     extern int mult(Mat,Vec,Vec);
406 $     Mat A
407 $
408 $     VecCreateMPI(comm,PETSC_DECIDE,M,&y);
409 $     VecCreateMPI(comm,PETSC_DECIDE,N,&x);
410 $     VecGetLocalSize(y,&m);
411 $     VecGetLocalSize(x,&n);
412 $     MatCreateShell(comm,m,n,M,N,ctx,&A);
413 $     MatShellSetOperation(mat,MATOP_MULT,(void(*)(void))mult);
414 $     MatMult(A,x,y);
415 $     MatDestroy(A);
416 $     VecDestroy(y); VecDestroy(x);
417 $
418 
419 .keywords: matrix, shell, create
420 
421 .seealso: MatShellSetOperation(), MatHasOperation(), MatShellGetContext(), MatShellSetContext()
422 @*/
423 PetscErrorCode PETSCMAT_DLLEXPORT MatCreateShell(MPI_Comm comm,PetscInt m,PetscInt n,PetscInt M,PetscInt N,void *ctx,Mat *A)
424 {
425   PetscErrorCode ierr;
426 
427   PetscFunctionBegin;
428   ierr = MatCreate(comm,A);CHKERRQ(ierr);
429   ierr = MatSetSizes(*A,m,n,M,N);CHKERRQ(ierr);
430 
431   ierr = MatSetType(*A,MATSHELL);CHKERRQ(ierr);
432   ierr = MatShellSetContext(*A,ctx);CHKERRQ(ierr);
433   PetscFunctionReturn(0);
434 }
435 
436 #undef __FUNCT__
437 #define __FUNCT__ "MatShellSetContext"
438 /*@
439     MatShellSetContext - sets the context for a shell matrix
440 
441    Collective on Mat
442 
443     Input Parameters:
444 +   mat - the shell matrix
445 -   ctx - the context
446 
447    Level: advanced
448 
449    Fortran Notes: The context can only be an integer or a PetscObject
450       unfortunately it cannot be a Fortran array or derived type.
451 
452 .seealso: MatCreateShell(), MatShellGetContext(), MatShellGetOperation()
453 @*/
454 PetscErrorCode PETSCMAT_DLLEXPORT MatShellSetContext(Mat mat,void *ctx)
455 {
456   Mat_Shell      *shell = (Mat_Shell*)mat->data;
457   PetscErrorCode ierr;
458   PetscTruth     flg;
459 
460   PetscFunctionBegin;
461   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
462   ierr = PetscTypeCompare((PetscObject)mat,MATSHELL,&flg);CHKERRQ(ierr);
463   if (flg) {
464     shell->ctx = ctx;
465   }
466   PetscFunctionReturn(0);
467 }
468 
469 #undef __FUNCT__
470 #define __FUNCT__ "MatShellSetOperation"
471 /*@C
472     MatShellSetOperation - Allows user to set a matrix operation for
473                            a shell matrix.
474 
475    Collective on Mat
476 
477     Input Parameters:
478 +   mat - the shell matrix
479 .   op - the name of the operation
480 -   f - the function that provides the operation.
481 
482    Level: advanced
483 
484     Usage:
485 $      extern PetscErrorCode usermult(Mat,Vec,Vec);
486 $      ierr = MatCreateShell(comm,m,n,M,N,ctx,&A);
487 $      ierr = MatShellSetOperation(A,MATOP_MULT,(void(*)(void))usermult);
488 
489     Notes:
490     See the file include/petscmat.h for a complete list of matrix
491     operations, which all have the form MATOP_<OPERATION>, where
492     <OPERATION> is the name (in all capital letters) of the
493     user interface routine (e.g., MatMult() -> MATOP_MULT).
494 
495     All user-provided functions should have the same calling
496     sequence as the usual matrix interface routines, since they
497     are intended to be accessed via the usual matrix interface
498     routines, e.g.,
499 $       MatMult(Mat,Vec,Vec) -> usermult(Mat,Vec,Vec)
500 
501     In particular each function MUST return an error code of 0 on success and
502     nonzero on failure.
503 
504     Within each user-defined routine, the user should call
505     MatShellGetContext() to obtain the user-defined context that was
506     set by MatCreateShell().
507 
508     Fortran Notes: For MatGetVecs() the user code should check if the input left or right matrix is -1 and in that case not
509        generate a matrix. See src/mat/examples/tests/ex120f.F
510 
511 .keywords: matrix, shell, set, operation
512 
513 .seealso: MatCreateShell(), MatShellGetContext(), MatShellGetOperation(), MatShellSetContext()
514 @*/
515 PetscErrorCode PETSCMAT_DLLEXPORT MatShellSetOperation(Mat mat,MatOperation op,void (*f)(void))
516 {
517   PetscErrorCode ierr;
518   PetscTruth     flg;
519 
520   PetscFunctionBegin;
521   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
522   if (op == MATOP_DESTROY) {
523     ierr = PetscTypeCompare((PetscObject)mat,MATSHELL,&flg);CHKERRQ(ierr);
524     if (flg) {
525        Mat_Shell *shell = (Mat_Shell*)mat->data;
526        shell->destroy                 = (PetscErrorCode (*)(Mat)) f;
527     } else mat->ops->destroy          = (PetscErrorCode (*)(Mat)) f;
528   }
529   else if (op == MATOP_VIEW) mat->ops->view  = (PetscErrorCode (*)(Mat,PetscViewer)) f;
530   else                       (((void(**)(void))mat->ops)[op]) = f;
531 
532   PetscFunctionReturn(0);
533 }
534 
535 #undef __FUNCT__
536 #define __FUNCT__ "MatShellGetOperation"
537 /*@C
538     MatShellGetOperation - Gets a matrix function for a shell matrix.
539 
540     Not Collective
541 
542     Input Parameters:
543 +   mat - the shell matrix
544 -   op - the name of the operation
545 
546     Output Parameter:
547 .   f - the function that provides the operation.
548 
549     Level: advanced
550 
551     Notes:
552     See the file include/petscmat.h for a complete list of matrix
553     operations, which all have the form MATOP_<OPERATION>, where
554     <OPERATION> is the name (in all capital letters) of the
555     user interface routine (e.g., MatMult() -> MATOP_MULT).
556 
557     All user-provided functions have the same calling
558     sequence as the usual matrix interface routines, since they
559     are intended to be accessed via the usual matrix interface
560     routines, e.g.,
561 $       MatMult(Mat,Vec,Vec) -> usermult(Mat,Vec,Vec)
562 
563     Within each user-defined routine, the user should call
564     MatShellGetContext() to obtain the user-defined context that was
565     set by MatCreateShell().
566 
567 .keywords: matrix, shell, set, operation
568 
569 .seealso: MatCreateShell(), MatShellGetContext(), MatShellSetOperation(), MatShellSetContext()
570 @*/
571 PetscErrorCode PETSCMAT_DLLEXPORT MatShellGetOperation(Mat mat,MatOperation op,void(**f)(void))
572 {
573   PetscErrorCode ierr;
574   PetscTruth     flg;
575 
576   PetscFunctionBegin;
577   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
578   if (op == MATOP_DESTROY) {
579     ierr = PetscTypeCompare((PetscObject)mat,MATSHELL,&flg);CHKERRQ(ierr);
580     if (flg) {
581       Mat_Shell *shell = (Mat_Shell*)mat->data;
582       *f = (void(*)(void))shell->destroy;
583     } else {
584       *f = (void(*)(void))mat->ops->destroy;
585     }
586   } else if (op == MATOP_VIEW) {
587     *f = (void(*)(void))mat->ops->view;
588   } else {
589     *f = (((void(**)(void))mat->ops)[op]);
590   }
591 
592   PetscFunctionReturn(0);
593 }
594 
595