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