xref: /petsc/src/mat/impls/shell/shell.c (revision 7fb7d96aa659053597e5761399d97552ca6180df) !
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   PetscScalar    vscale,vshift;
17   Vec            dshift;
18   Vec            left,right;
19   Vec            dshift_owned,left_owned,right_owned;
20   Vec            left_work,right_work;
21   PetscBool      usingscaled;
22   void           *ctx;
23 } Mat_Shell;
24 /*
25  The most general expression for the matrix is
26 
27  S = L (a A + B) R
28 
29  where
30  A is the matrix defined by the user's function
31  a is a scalar multiple
32  L is left scaling
33  R is right scaling
34  B is a diagonal shift defined by
35    diag(dshift) if the vector dshift is non-NULL
36    vscale*identity otherwise
37 
38  The following identities apply:
39 
40  Scale by c:
41   c [L (a A + B) R] = L [(a c) A + c B] R
42 
43  Shift by c:
44   [L (a A + B) R] + c = L [a A + (B + c Linv Rinv)] R
45 
46  Diagonal scaling is achieved by simply multiplying with existing L and R vectors
47 
48  In the data structure:
49 
50  vscale=1.0  means no special scaling will be applied
51  dshift=NULL means a constant diagonal shift (fall back to vshift)
52  vshift=0.0  means no constant diagonal shift, note that vshift is only used if dshift is NULL
53 */
54 
55 static PetscErrorCode MatMult_Shell(Mat,Vec,Vec);
56 static PetscErrorCode MatMultTranspose_Shell(Mat,Vec,Vec);
57 static PetscErrorCode MatGetDiagonal_Shell(Mat,Vec);
58 
59 #undef __FUNCT__
60 #define __FUNCT__ "MatShellUseScaledMethods"
61 static PetscErrorCode MatShellUseScaledMethods(Mat Y)
62 {
63   Mat_Shell *shell = (Mat_Shell*)Y->data;
64 
65   PetscFunctionBegin;
66   if (shell->usingscaled) PetscFunctionReturn(0);
67   shell->mult  = Y->ops->mult;
68   Y->ops->mult = MatMult_Shell;
69   if (Y->ops->multtranspose) {
70     shell->multtranspose  = Y->ops->multtranspose;
71     Y->ops->multtranspose = MatMultTranspose_Shell;
72   }
73   if (Y->ops->getdiagonal) {
74     shell->getdiagonal  = Y->ops->getdiagonal;
75     Y->ops->getdiagonal = MatGetDiagonal_Shell;
76   }
77   shell->usingscaled = PETSC_TRUE;
78   PetscFunctionReturn(0);
79 }
80 
81 #undef __FUNCT__
82 #define __FUNCT__ "MatShellPreScaleLeft"
83 static PetscErrorCode MatShellPreScaleLeft(Mat A,Vec x,Vec *xx)
84 {
85   Mat_Shell *shell = (Mat_Shell*)A->data;
86   PetscErrorCode ierr;
87 
88   PetscFunctionBegin;
89   if (!shell->left) {
90     *xx = x;
91   } else {
92     if (!shell->left_work) {ierr = VecDuplicate(shell->left,&shell->left_work);CHKERRQ(ierr);}
93     ierr = VecPointwiseMult(shell->left_work,x,shell->left);CHKERRQ(ierr);
94     *xx = shell->left_work;
95   }
96   PetscFunctionReturn(0);
97 }
98 
99 #undef __FUNCT__
100 #define __FUNCT__ "MatShellPreScaleRight"
101 static PetscErrorCode MatShellPreScaleRight(Mat A,Vec x,Vec *xx)
102 {
103   Mat_Shell *shell = (Mat_Shell*)A->data;
104   PetscErrorCode ierr;
105 
106   PetscFunctionBegin;
107   if (!shell->right) {
108     *xx = x;
109   } else {
110     if (!shell->right_work) {ierr = VecDuplicate(shell->right,&shell->right_work);CHKERRQ(ierr);}
111     ierr = VecPointwiseMult(shell->right_work,x,shell->right);CHKERRQ(ierr);
112     *xx = shell->right_work;
113   }
114   PetscFunctionReturn(0);
115 }
116 
117 #undef __FUNCT__
118 #define __FUNCT__ "MatShellPostScaleLeft"
119 static PetscErrorCode MatShellPostScaleLeft(Mat A,Vec x)
120 {
121   Mat_Shell *shell = (Mat_Shell*)A->data;
122   PetscErrorCode ierr;
123 
124   PetscFunctionBegin;
125   if (shell->left) {ierr = VecPointwiseMult(x,x,shell->left);CHKERRQ(ierr);}
126   PetscFunctionReturn(0);
127 }
128 
129 #undef __FUNCT__
130 #define __FUNCT__ "MatShellPostScaleRight"
131 static PetscErrorCode MatShellPostScaleRight(Mat A,Vec x)
132 {
133   Mat_Shell *shell = (Mat_Shell*)A->data;
134   PetscErrorCode ierr;
135 
136   PetscFunctionBegin;
137   if (shell->right) {ierr = VecPointwiseMult(x,x,shell->right);CHKERRQ(ierr);}
138   PetscFunctionReturn(0);
139 }
140 
141 #undef __FUNCT__
142 #define __FUNCT__ "MatShellShiftAndScale"
143 static PetscErrorCode MatShellShiftAndScale(Mat A,Vec X,Vec Y)
144 {
145   Mat_Shell *shell = (Mat_Shell*)A->data;
146   PetscErrorCode ierr;
147 
148   PetscFunctionBegin;
149   if (shell->dshift) {          /* get arrays because there is no VecPointwiseMultAdd() */
150     PetscInt i,m;
151     const PetscScalar *x,*d;
152     PetscScalar *y;
153     ierr = VecGetLocalSize(X,&m);CHKERRQ(ierr);
154     ierr = VecGetArrayRead(shell->dshift,&d);CHKERRQ(ierr);
155     ierr = VecGetArrayRead(X,&x);CHKERRQ(ierr);
156     ierr = VecGetArray(Y,&y);CHKERRQ(ierr);
157     for (i=0; i<m; i++) y[i] = shell->vscale*y[i] + d[i]*x[i];
158     ierr = VecRestoreArrayRead(shell->dshift,&d);CHKERRQ(ierr);
159     ierr = VecRestoreArrayRead(X,&x);CHKERRQ(ierr);
160     ierr = VecRestoreArray(Y,&y);CHKERRQ(ierr);
161   } else {
162     ierr = VecAXPBY(Y,shell->vshift,shell->vscale,X);CHKERRQ(ierr);
163   }
164   PetscFunctionReturn(0);
165 }
166 
167 #undef __FUNCT__
168 #define __FUNCT__ "MatShellGetContext"
169 /*@C
170     MatShellGetContext - Returns the user-provided context associated with a shell matrix.
171 
172     Not Collective
173 
174     Input Parameter:
175 .   mat - the matrix, should have been created with MatCreateShell()
176 
177     Output Parameter:
178 .   ctx - the user provided context
179 
180     Level: advanced
181 
182     Notes:
183     This routine is intended for use within various shell matrix routines,
184     as set with MatShellSetOperation().
185 
186 .keywords: matrix, shell, get, context
187 
188 .seealso: MatCreateShell(), MatShellSetOperation(), MatShellSetContext()
189 @*/
190 PetscErrorCode  MatShellGetContext(Mat mat,void *ctx)
191 {
192   PetscErrorCode ierr;
193   PetscBool      flg;
194 
195   PetscFunctionBegin;
196   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
197   PetscValidPointer(ctx,2);
198   ierr = PetscTypeCompare((PetscObject)mat,MATSHELL,&flg);CHKERRQ(ierr);
199   if (!flg) *(void**)ctx = 0;
200   else      *(void**)ctx = ((Mat_Shell*)(mat->data))->ctx;
201   PetscFunctionReturn(0);
202 }
203 
204 #undef __FUNCT__
205 #define __FUNCT__ "MatDestroy_Shell"
206 PetscErrorCode MatDestroy_Shell(Mat mat)
207 {
208   PetscErrorCode ierr;
209   Mat_Shell      *shell = (Mat_Shell*)mat->data;
210 
211   PetscFunctionBegin;
212   if (shell->destroy) {
213     ierr = (*shell->destroy)(mat);CHKERRQ(ierr);
214   }
215   ierr = VecDestroy(&shell->left_owned);CHKERRQ(ierr);
216   ierr = VecDestroy(&shell->right_owned);CHKERRQ(ierr);
217   ierr = VecDestroy(&shell->dshift_owned);CHKERRQ(ierr);
218   ierr = VecDestroy(&shell->left_work);CHKERRQ(ierr);
219   ierr = VecDestroy(&shell->right_work);CHKERRQ(ierr);
220   ierr = PetscFree(mat->data);CHKERRQ(ierr);
221   PetscFunctionReturn(0);
222 }
223 
224 #undef __FUNCT__
225 #define __FUNCT__ "MatMult_Shell"
226 PetscErrorCode MatMult_Shell(Mat A,Vec x,Vec y)
227 {
228   Mat_Shell      *shell = (Mat_Shell*)A->data;
229   PetscErrorCode ierr;
230   Vec            xx;
231 
232   PetscFunctionBegin;
233   ierr = MatShellPreScaleRight(A,x,&xx);CHKERRQ(ierr);
234   ierr = (*shell->mult)(A,xx,y);CHKERRQ(ierr);
235   ierr = MatShellShiftAndScale(A,xx,y);CHKERRQ(ierr);
236   ierr = MatShellPostScaleLeft(A,y);CHKERRQ(ierr);
237   PetscFunctionReturn(0);
238 }
239 
240 #undef __FUNCT__
241 #define __FUNCT__ "MatMultTranspose_Shell"
242 PetscErrorCode MatMultTranspose_Shell(Mat A,Vec x,Vec y)
243 {
244   Mat_Shell      *shell = (Mat_Shell*)A->data;
245   PetscErrorCode ierr;
246   Vec            xx;
247 
248   PetscFunctionBegin;
249   ierr = MatShellPreScaleLeft(A,x,&xx);CHKERRQ(ierr);
250   ierr = (*shell->multtranspose)(A,xx,y);CHKERRQ(ierr);
251   ierr = MatShellShiftAndScale(A,xx,y);CHKERRQ(ierr);
252   ierr = MatShellPostScaleRight(A,y);CHKERRQ(ierr);
253   PetscFunctionReturn(0);
254 }
255 
256 #undef __FUNCT__
257 #define __FUNCT__ "MatGetDiagonal_Shell"
258 PetscErrorCode MatGetDiagonal_Shell(Mat A,Vec v)
259 {
260   Mat_Shell      *shell = (Mat_Shell*)A->data;
261   PetscErrorCode ierr;
262 
263   PetscFunctionBegin;
264   ierr = (*shell->getdiagonal)(A,v);CHKERRQ(ierr);
265   ierr = VecScale(v,shell->vscale);CHKERRQ(ierr);
266   if (shell->dshift) {
267     ierr = VecPointwiseMult(v,v,shell->dshift);CHKERRQ(ierr);
268   } else {
269     ierr = VecShift(v,shell->vshift);CHKERRQ(ierr);
270   }
271   if (shell->left)  {ierr = VecPointwiseMult(v,v,shell->left);CHKERRQ(ierr);}
272   if (shell->right) {ierr = VecPointwiseMult(v,v,shell->right);CHKERRQ(ierr);}
273   PetscFunctionReturn(0);
274 }
275 
276 #undef __FUNCT__
277 #define __FUNCT__ "MatShift_Shell"
278 PetscErrorCode MatShift_Shell(Mat Y,PetscScalar a)
279 {
280   Mat_Shell *shell = (Mat_Shell*)Y->data;
281   PetscErrorCode ierr;
282 
283   PetscFunctionBegin;
284   if (shell->left || shell->right || shell->dshift) {
285     if (!shell->dshift) {
286       if (!shell->dshift_owned) {ierr = VecDuplicate(shell->left ? shell->left : shell->right, &shell->dshift_owned);CHKERRQ(ierr);}
287       shell->dshift = shell->dshift_owned;
288       ierr = VecSet(shell->dshift,shell->vshift+a);CHKERRQ(ierr);
289     } else {ierr = VecScale(shell->dshift,a);CHKERRQ(ierr);}
290     if (shell->left)  {ierr = VecPointwiseDivide(shell->dshift,shell->dshift,shell->left);CHKERRQ(ierr);}
291     if (shell->right) {ierr = VecPointwiseDivide(shell->dshift,shell->dshift,shell->right);CHKERRQ(ierr);}
292   } else shell->vshift += a;
293   ierr = MatShellUseScaledMethods(Y);CHKERRQ(ierr);
294   PetscFunctionReturn(0);
295 }
296 
297 #undef __FUNCT__
298 #define __FUNCT__ "MatScale_Shell"
299 PetscErrorCode MatScale_Shell(Mat Y,PetscScalar a)
300 {
301   Mat_Shell      *shell = (Mat_Shell*)Y->data;
302   PetscErrorCode ierr;
303 
304   PetscFunctionBegin;
305   shell->vscale *= a;
306   if (shell->dshift) {ierr = VecScale(shell->dshift,a);CHKERRQ(ierr);}
307   else shell->vshift *= a;
308   ierr = MatShellUseScaledMethods(Y);CHKERRQ(ierr);
309   PetscFunctionReturn(0);
310 }
311 
312 #undef __FUNCT__
313 #define __FUNCT__ "MatDiagonalScale_Shell"
314 static PetscErrorCode MatDiagonalScale_Shell(Mat Y,Vec left,Vec right)
315 {
316   Mat_Shell *shell = (Mat_Shell*)Y->data;
317   PetscErrorCode ierr;
318 
319   PetscFunctionBegin;
320   if (left) {
321     if (!shell->left_owned) {ierr = VecDuplicate(left,&shell->left_owned);CHKERRQ(ierr);}
322     if (shell->left) {ierr = VecPointwiseMult(shell->left,shell->left,left);CHKERRQ(ierr);}
323     else {
324       shell->left = shell->left_owned;
325       ierr = VecCopy(left,shell->left);CHKERRQ(ierr);
326     }
327   }
328   if (right) {
329     if (!shell->right_owned) {ierr = VecDuplicate(right,&shell->right_owned);CHKERRQ(ierr);}
330     if (shell->right) {ierr = VecPointwiseMult(shell->right,shell->right,right);CHKERRQ(ierr);}
331     else {
332       shell->right = shell->right_owned;
333       ierr = VecCopy(right,shell->right);CHKERRQ(ierr);
334     }
335   }
336   ierr = MatShellUseScaledMethods(Y);CHKERRQ(ierr);
337   PetscFunctionReturn(0);
338 }
339 
340 #undef __FUNCT__
341 #define __FUNCT__ "MatAssemblyEnd_Shell"
342 PetscErrorCode MatAssemblyEnd_Shell(Mat Y,MatAssemblyType t)
343 {
344   Mat_Shell *shell = (Mat_Shell*)Y->data;
345 
346   PetscFunctionBegin;
347   if (t == MAT_FINAL_ASSEMBLY) {
348     shell->vshift      = 0.0;
349     shell->vscale      = 1.0;
350     shell->dshift      = PETSC_NULL;
351     shell->left        = PETSC_NULL;
352     shell->right       = PETSC_NULL;
353     if (shell->mult) {
354       Y->ops->mult = shell->mult;
355       shell->mult  = PETSC_NULL;
356     }
357     if (shell->multtranspose) {
358       Y->ops->multtranspose = shell->multtranspose;
359       shell->multtranspose  = PETSC_NULL;
360     }
361     if (shell->getdiagonal) {
362       Y->ops->getdiagonal = shell->getdiagonal;
363       shell->getdiagonal  = PETSC_NULL;
364     }
365     shell->usingscaled = PETSC_FALSE;
366   }
367   PetscFunctionReturn(0);
368 }
369 
370 extern PetscErrorCode MatConvert_Shell(Mat, const MatType,MatReuse,Mat*);
371 
372 #undef __FUNCT__
373 #define __FUNCT__ "MatSetBlockSize_Shell"
374 PetscErrorCode MatSetBlockSize_Shell(Mat A,PetscInt bs)
375 {
376   PetscErrorCode ierr;
377 
378   PetscFunctionBegin;
379   ierr = PetscLayoutSetBlockSize(A->rmap,bs);CHKERRQ(ierr);
380   ierr = PetscLayoutSetBlockSize(A->cmap,bs);CHKERRQ(ierr);
381   PetscFunctionReturn(0);
382 }
383 
384 static struct _MatOps MatOps_Values = {0,
385        0,
386        0,
387        0,
388 /* 4*/ 0,
389        0,
390        0,
391        0,
392        0,
393        0,
394 /*10*/ 0,
395        0,
396        0,
397        0,
398        0,
399 /*15*/ 0,
400        0,
401        0,
402        MatDiagonalScale_Shell,
403        0,
404 /*20*/ 0,
405        MatAssemblyEnd_Shell,
406        0,
407        0,
408 /*24*/ 0,
409        0,
410        0,
411        0,
412        0,
413 /*29*/ 0,
414        0,
415        0,
416        0,
417        0,
418 /*34*/ 0,
419        0,
420        0,
421        0,
422        0,
423 /*39*/ 0,
424        0,
425        0,
426        0,
427        0,
428 /*44*/ 0,
429        MatScale_Shell,
430        MatShift_Shell,
431        0,
432        0,
433 /*49*/ MatSetBlockSize_Shell,
434        0,
435        0,
436        0,
437        0,
438 /*54*/ 0,
439        0,
440        0,
441        0,
442        0,
443 /*59*/ 0,
444        MatDestroy_Shell,
445        0,
446        0,
447        0,
448 /*64*/ 0,
449        0,
450        0,
451        0,
452        0,
453 /*69*/ 0,
454        0,
455        MatConvert_Shell,
456        0,
457        0,
458 /*74*/ 0,
459        0,
460        0,
461        0,
462        0,
463 /*79*/ 0,
464        0,
465        0,
466        0,
467        0,
468 /*84*/ 0,
469        0,
470        0,
471        0,
472        0,
473 /*89*/ 0,
474        0,
475        0,
476        0,
477        0,
478 /*94*/ 0,
479        0,
480        0,
481        0};
482 
483 /*MC
484    MATSHELL - MATSHELL = "shell" - A matrix type to be used to define your own matrix type -- perhaps matrix free.
485 
486   Level: advanced
487 
488 .seealso: MatCreateShell
489 M*/
490 
491 EXTERN_C_BEGIN
492 #undef __FUNCT__
493 #define __FUNCT__ "MatCreate_Shell"
494 PetscErrorCode  MatCreate_Shell(Mat A)
495 {
496   Mat_Shell      *b;
497   PetscErrorCode ierr;
498 
499   PetscFunctionBegin;
500   ierr = PetscMemcpy(A->ops,&MatOps_Values,sizeof(struct _MatOps));CHKERRQ(ierr);
501 
502   ierr = PetscNewLog(A,Mat_Shell,&b);CHKERRQ(ierr);
503   A->data = (void*)b;
504 
505   ierr = PetscLayoutSetBlockSize(A->rmap,1);CHKERRQ(ierr);
506   ierr = PetscLayoutSetBlockSize(A->cmap,1);CHKERRQ(ierr);
507   ierr = PetscLayoutSetUp(A->rmap);CHKERRQ(ierr);
508   ierr = PetscLayoutSetUp(A->cmap);CHKERRQ(ierr);
509 
510   b->ctx           = 0;
511   b->vshift        = 0.0;
512   b->vscale        = 1.0;
513   b->mult          = 0;
514   b->multtranspose = 0;
515   b->getdiagonal   = 0;
516   A->assembled     = PETSC_TRUE;
517   A->preallocated  = PETSC_FALSE;
518   ierr = PetscObjectChangeTypeName((PetscObject)A,MATSHELL);CHKERRQ(ierr);
519   PetscFunctionReturn(0);
520 }
521 EXTERN_C_END
522 
523 #undef __FUNCT__
524 #define __FUNCT__ "MatCreateShell"
525 /*@C
526    MatCreateShell - Creates a new matrix class for use with a user-defined
527    private data storage format.
528 
529   Collective on MPI_Comm
530 
531    Input Parameters:
532 +  comm - MPI communicator
533 .  m - number of local rows (must be given)
534 .  n - number of local columns (must be given)
535 .  M - number of global rows (may be PETSC_DETERMINE)
536 .  N - number of global columns (may be PETSC_DETERMINE)
537 -  ctx - pointer to data needed by the shell matrix routines
538 
539    Output Parameter:
540 .  A - the matrix
541 
542    Level: advanced
543 
544   Usage:
545 $    extern int mult(Mat,Vec,Vec);
546 $    MatCreateShell(comm,m,n,M,N,ctx,&mat);
547 $    MatShellSetOperation(mat,MATOP_MULT,(void(*)(void))mult);
548 $    [ Use matrix for operations that have been set ]
549 $    MatDestroy(mat);
550 
551    Notes:
552    The shell matrix type is intended to provide a simple class to use
553    with KSP (such as, for use with matrix-free methods). You should not
554    use the shell type if you plan to define a complete matrix class.
555 
556    Fortran Notes: The context can only be an integer or a PetscObject
557       unfortunately it cannot be a Fortran array or derived type.
558 
559    PETSc requires that matrices and vectors being used for certain
560    operations are partitioned accordingly.  For example, when
561    creating a shell matrix, A, that supports parallel matrix-vector
562    products using MatMult(A,x,y) the user should set the number
563    of local matrix rows to be the number of local elements of the
564    corresponding result vector, y. Note that this is information is
565    required for use of the matrix interface routines, even though
566    the shell matrix may not actually be physically partitioned.
567    For example,
568 
569 $
570 $     Vec x, y
571 $     extern int mult(Mat,Vec,Vec);
572 $     Mat A
573 $
574 $     VecCreateMPI(comm,PETSC_DECIDE,M,&y);
575 $     VecCreateMPI(comm,PETSC_DECIDE,N,&x);
576 $     VecGetLocalSize(y,&m);
577 $     VecGetLocalSize(x,&n);
578 $     MatCreateShell(comm,m,n,M,N,ctx,&A);
579 $     MatShellSetOperation(mat,MATOP_MULT,(void(*)(void))mult);
580 $     MatMult(A,x,y);
581 $     MatDestroy(A);
582 $     VecDestroy(y); VecDestroy(x);
583 $
584 
585 .keywords: matrix, shell, create
586 
587 .seealso: MatShellSetOperation(), MatHasOperation(), MatShellGetContext(), MatShellSetContext()
588 @*/
589 PetscErrorCode  MatCreateShell(MPI_Comm comm,PetscInt m,PetscInt n,PetscInt M,PetscInt N,void *ctx,Mat *A)
590 {
591   PetscErrorCode ierr;
592 
593   PetscFunctionBegin;
594   ierr = MatCreate(comm,A);CHKERRQ(ierr);
595   ierr = MatSetSizes(*A,m,n,M,N);CHKERRQ(ierr);
596 
597   ierr = MatSetType(*A,MATSHELL);CHKERRQ(ierr);
598   ierr = MatShellSetContext(*A,ctx);CHKERRQ(ierr);
599   PetscFunctionReturn(0);
600 }
601 
602 #undef __FUNCT__
603 #define __FUNCT__ "MatShellSetContext"
604 /*@
605     MatShellSetContext - sets the context for a shell matrix
606 
607    Logically Collective on Mat
608 
609     Input Parameters:
610 +   mat - the shell matrix
611 -   ctx - the context
612 
613    Level: advanced
614 
615    Fortran Notes: The context can only be an integer or a PetscObject
616       unfortunately it cannot be a Fortran array or derived type.
617 
618 .seealso: MatCreateShell(), MatShellGetContext(), MatShellGetOperation()
619 @*/
620 PetscErrorCode  MatShellSetContext(Mat mat,void *ctx)
621 {
622   Mat_Shell      *shell = (Mat_Shell*)mat->data;
623   PetscErrorCode ierr;
624   PetscBool      flg;
625 
626   PetscFunctionBegin;
627   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
628   ierr = PetscTypeCompare((PetscObject)mat,MATSHELL,&flg);CHKERRQ(ierr);
629   if (flg) {
630     shell->ctx = ctx;
631   }
632   PetscFunctionReturn(0);
633 }
634 
635 #undef __FUNCT__
636 #define __FUNCT__ "MatShellSetOperation"
637 /*@C
638     MatShellSetOperation - Allows user to set a matrix operation for
639                            a shell matrix.
640 
641    Logically Collective on Mat
642 
643     Input Parameters:
644 +   mat - the shell matrix
645 .   op - the name of the operation
646 -   f - the function that provides the operation.
647 
648    Level: advanced
649 
650     Usage:
651 $      extern PetscErrorCode usermult(Mat,Vec,Vec);
652 $      ierr = MatCreateShell(comm,m,n,M,N,ctx,&A);
653 $      ierr = MatShellSetOperation(A,MATOP_MULT,(void(*)(void))usermult);
654 
655     Notes:
656     See the file include/petscmat.h for a complete list of matrix
657     operations, which all have the form MATOP_<OPERATION>, where
658     <OPERATION> is the name (in all capital letters) of the
659     user interface routine (e.g., MatMult() -> MATOP_MULT).
660 
661     All user-provided functions should have the same calling
662     sequence as the usual matrix interface routines, since they
663     are intended to be accessed via the usual matrix interface
664     routines, e.g.,
665 $       MatMult(Mat,Vec,Vec) -> usermult(Mat,Vec,Vec)
666 
667     In particular each function MUST return an error code of 0 on success and
668     nonzero on failure.
669 
670     Within each user-defined routine, the user should call
671     MatShellGetContext() to obtain the user-defined context that was
672     set by MatCreateShell().
673 
674     Fortran Notes: For MatGetVecs() the user code should check if the input left or right matrix is -1 and in that case not
675        generate a matrix. See src/mat/examples/tests/ex120f.F
676 
677 .keywords: matrix, shell, set, operation
678 
679 .seealso: MatCreateShell(), MatShellGetContext(), MatShellGetOperation(), MatShellSetContext()
680 @*/
681 PetscErrorCode  MatShellSetOperation(Mat mat,MatOperation op,void (*f)(void))
682 {
683   PetscErrorCode ierr;
684   PetscBool      flg;
685 
686   PetscFunctionBegin;
687   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
688   if (op == MATOP_DESTROY) {
689     ierr = PetscTypeCompare((PetscObject)mat,MATSHELL,&flg);CHKERRQ(ierr);
690     if (flg) {
691        Mat_Shell *shell = (Mat_Shell*)mat->data;
692        shell->destroy                 = (PetscErrorCode (*)(Mat)) f;
693     } else mat->ops->destroy          = (PetscErrorCode (*)(Mat)) f;
694   }
695   else if (op == MATOP_VIEW) mat->ops->view  = (PetscErrorCode (*)(Mat,PetscViewer)) f;
696   else                       (((void(**)(void))mat->ops)[op]) = f;
697 
698   PetscFunctionReturn(0);
699 }
700 
701 #undef __FUNCT__
702 #define __FUNCT__ "MatShellGetOperation"
703 /*@C
704     MatShellGetOperation - Gets a matrix function for a shell matrix.
705 
706     Not Collective
707 
708     Input Parameters:
709 +   mat - the shell matrix
710 -   op - the name of the operation
711 
712     Output Parameter:
713 .   f - the function that provides the operation.
714 
715     Level: advanced
716 
717     Notes:
718     See the file include/petscmat.h for a complete list of matrix
719     operations, which all have the form MATOP_<OPERATION>, where
720     <OPERATION> is the name (in all capital letters) of the
721     user interface routine (e.g., MatMult() -> MATOP_MULT).
722 
723     All user-provided functions have the same calling
724     sequence as the usual matrix interface routines, since they
725     are intended to be accessed via the usual matrix interface
726     routines, e.g.,
727 $       MatMult(Mat,Vec,Vec) -> usermult(Mat,Vec,Vec)
728 
729     Within each user-defined routine, the user should call
730     MatShellGetContext() to obtain the user-defined context that was
731     set by MatCreateShell().
732 
733 .keywords: matrix, shell, set, operation
734 
735 .seealso: MatCreateShell(), MatShellGetContext(), MatShellSetOperation(), MatShellSetContext()
736 @*/
737 PetscErrorCode  MatShellGetOperation(Mat mat,MatOperation op,void(**f)(void))
738 {
739   PetscErrorCode ierr;
740   PetscBool      flg;
741 
742   PetscFunctionBegin;
743   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
744   if (op == MATOP_DESTROY) {
745     ierr = PetscTypeCompare((PetscObject)mat,MATSHELL,&flg);CHKERRQ(ierr);
746     if (flg) {
747       Mat_Shell *shell = (Mat_Shell*)mat->data;
748       *f = (void(*)(void))shell->destroy;
749     } else {
750       *f = (void(*)(void))mat->ops->destroy;
751     }
752   } else if (op == MATOP_VIEW) {
753     *f = (void(*)(void))mat->ops->view;
754   } else {
755     *f = (((void(**)(void))mat->ops)[op]);
756   }
757 
758   PetscFunctionReturn(0);
759 }
760 
761