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