xref: /petsc/src/mat/impls/shell/shell.c (revision 1f428162ad0d3f3f8d1cec0ccb02b2fdaac4d2ec)
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 "src/mat/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 /*@
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_COOKIE,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, MatType,MatReuse,Mat*);
201 
202 #undef __FUNCT__
203 #define __FUNCT__ "MatSetBlockSize_Shell"
204 PetscErrorCode MatSetBlockSize_Shell(Mat A,PetscInt bs)
205 {
206   PetscFunctionBegin;
207   PetscFunctionReturn(0);
208 }
209 
210 static struct _MatOps MatOps_Values = {0,
211        0,
212        0,
213        0,
214 /* 4*/ 0,
215        0,
216        0,
217        0,
218        0,
219        0,
220 /*10*/ 0,
221        0,
222        0,
223        0,
224        0,
225 /*15*/ 0,
226        0,
227        0,
228        0,
229        0,
230 /*20*/ 0,
231        MatAssemblyEnd_Shell,
232        0,
233        0,
234        0,
235 /*25*/ 0,
236        0,
237        0,
238        0,
239        0,
240 /*30*/ 0,
241        0,
242        0,
243        0,
244        0,
245 /*35*/ 0,
246        0,
247        0,
248        0,
249        0,
250 /*40*/ 0,
251        0,
252        0,
253        0,
254        0,
255 /*45*/ 0,
256        MatScale_Shell,
257        MatShift_Shell,
258        0,
259        0,
260 /*50*/ MatSetBlockSize_Shell,
261        0,
262        0,
263        0,
264        0,
265 /*55*/ 0,
266        0,
267        0,
268        0,
269        0,
270 /*60*/ 0,
271        MatDestroy_Shell,
272        0,
273        0,
274        0,
275 /*65*/ 0,
276        0,
277        0,
278        0,
279        0,
280 /*70*/ 0,
281        MatConvert_Shell,
282        0,
283        0,
284        0,
285 /*75*/ 0,
286        0,
287        0,
288        0,
289        0,
290 /*80*/ 0,
291        0,
292        0,
293        0,
294        0,
295 /*85*/ 0,
296        0,
297        0,
298        0,
299        0,
300 /*90*/ 0,
301        0,
302        0,
303        0,
304        0,
305 /*95*/ 0,
306        0,
307        0,
308        0};
309 
310 /*MC
311    MATSHELL - MATSHELL = "shell" - A matrix type to be used to define your own matrix type -- perhaps matrix free.
312 
313   Level: advanced
314 
315 .seealso: MatCreateShell
316 M*/
317 
318 EXTERN_C_BEGIN
319 #undef __FUNCT__
320 #define __FUNCT__ "MatCreate_Shell"
321 PetscErrorCode PETSCMAT_DLLEXPORT MatCreate_Shell(Mat A)
322 {
323   Mat_Shell      *b;
324   PetscErrorCode ierr;
325 
326   PetscFunctionBegin;
327   ierr = PetscMemcpy(A->ops,&MatOps_Values,sizeof(struct _MatOps));CHKERRQ(ierr);
328 
329   ierr = PetscNew(Mat_Shell,&b);CHKERRQ(ierr);
330   ierr = PetscLogObjectMemory(A,sizeof(struct _p_Mat)+sizeof(Mat_Shell));CHKERRQ(ierr);
331   A->data = (void*)b;
332 
333   if (A->rmap.n == PETSC_DECIDE || A->cmap.n == PETSC_DECIDE) {
334     SETERRQ(PETSC_ERR_ARG_WRONG,"Must give local row and column count for matrix");
335   }
336 
337   ierr = PetscMapInitialize(A->comm,&A->rmap);CHKERRQ(ierr);
338   ierr = PetscMapInitialize(A->comm,&A->cmap);CHKERRQ(ierr);
339 
340   b->ctx           = 0;
341   b->scale         = PETSC_FALSE;
342   b->shift         = PETSC_FALSE;
343   b->vshift        = 0.0;
344   b->vscale        = 1.0;
345   b->mult          = 0;
346   b->multtranspose = 0;
347   b->getdiagonal   = 0;
348   A->assembled     = PETSC_TRUE;
349   A->preallocated  = PETSC_FALSE;
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 PETSCMAT_DLLEXPORT 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    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 PETSCMAT_DLLEXPORT MatShellSetContext(Mat mat,void *ctx)
452 {
453   Mat_Shell      *shell = (Mat_Shell*)mat->data;
454   PetscErrorCode ierr;
455   PetscTruth     flg;
456 
457   PetscFunctionBegin;
458   PetscValidHeaderSpecific(mat,MAT_COOKIE,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    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     Within each user-defined routine, the user should call
499     MatShellGetContext() to obtain the user-defined context that was
500     set by MatCreateShell().
501 
502 .keywords: matrix, shell, set, operation
503 
504 .seealso: MatCreateShell(), MatShellGetContext(), MatShellGetOperation(), MatShellSetContext()
505 @*/
506 PetscErrorCode PETSCMAT_DLLEXPORT MatShellSetOperation(Mat mat,MatOperation op,void (*f)(void))
507 {
508   PetscErrorCode ierr;
509   PetscTruth     flg;
510 
511   PetscFunctionBegin;
512   PetscValidHeaderSpecific(mat,MAT_COOKIE,1);
513   if (op == MATOP_DESTROY) {
514     ierr = PetscTypeCompare((PetscObject)mat,MATSHELL,&flg);CHKERRQ(ierr);
515     if (flg) {
516        Mat_Shell *shell = (Mat_Shell*)mat->data;
517        shell->destroy                 = (PetscErrorCode (*)(Mat)) f;
518     } else mat->ops->destroy          = (PetscErrorCode (*)(Mat)) f;
519   }
520   else if (op == MATOP_VIEW) mat->ops->view  = (PetscErrorCode (*)(Mat,PetscViewer)) f;
521   else                       (((void(**)(void))mat->ops)[op]) = f;
522 
523   PetscFunctionReturn(0);
524 }
525 
526 #undef __FUNCT__
527 #define __FUNCT__ "MatShellGetOperation"
528 /*@C
529     MatShellGetOperation - Gets a matrix function for a shell matrix.
530 
531     Not Collective
532 
533     Input Parameters:
534 +   mat - the shell matrix
535 -   op - the name of the operation
536 
537     Output Parameter:
538 .   f - the function that provides the operation.
539 
540     Level: advanced
541 
542     Notes:
543     See the file include/petscmat.h for a complete list of matrix
544     operations, which all have the form MATOP_<OPERATION>, where
545     <OPERATION> is the name (in all capital letters) of the
546     user interface routine (e.g., MatMult() -> MATOP_MULT).
547 
548     All user-provided functions have the same calling
549     sequence as the usual matrix interface routines, since they
550     are intended to be accessed via the usual matrix interface
551     routines, e.g.,
552 $       MatMult(Mat,Vec,Vec) -> usermult(Mat,Vec,Vec)
553 
554     Within each user-defined routine, the user should call
555     MatShellGetContext() to obtain the user-defined context that was
556     set by MatCreateShell().
557 
558 .keywords: matrix, shell, set, operation
559 
560 .seealso: MatCreateShell(), MatShellGetContext(), MatShellSetOperation(), MatShellSetContext()
561 @*/
562 PetscErrorCode PETSCMAT_DLLEXPORT MatShellGetOperation(Mat mat,MatOperation op,void(**f)(void))
563 {
564   PetscErrorCode ierr;
565   PetscTruth     flg;
566 
567   PetscFunctionBegin;
568   PetscValidHeaderSpecific(mat,MAT_COOKIE,1);
569   if (op == MATOP_DESTROY) {
570     ierr = PetscTypeCompare((PetscObject)mat,MATSHELL,&flg);CHKERRQ(ierr);
571     if (flg) {
572       Mat_Shell *shell = (Mat_Shell*)mat->data;
573       *f = (void(*)(void))shell->destroy;
574     } else {
575       *f = (void(*)(void))mat->ops->destroy;
576     }
577   } else if (op == MATOP_VIEW) {
578     *f = (void(*)(void))mat->ops->view;
579   } else {
580     *f = (((void(**)(void))mat->ops)[op]);
581   }
582 
583   PetscFunctionReturn(0);
584 }
585 
586