xref: /petsc/src/mat/impls/shell/shell.c (revision 8fe8eb27199adc5d66a03012966d3bad3b3b086f)
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     shell->usingscaled = PETSC_FALSE;
354     Y->ops->mult          = shell->mult;
355     Y->ops->multtranspose = shell->multtranspose;
356     Y->ops->getdiagonal   = shell->getdiagonal;
357   }
358   PetscFunctionReturn(0);
359 }
360 
361 extern PetscErrorCode MatConvert_Shell(Mat, const MatType,MatReuse,Mat*);
362 
363 #undef __FUNCT__
364 #define __FUNCT__ "MatSetBlockSize_Shell"
365 PetscErrorCode MatSetBlockSize_Shell(Mat A,PetscInt bs)
366 {
367   PetscErrorCode ierr;
368 
369   PetscFunctionBegin;
370   ierr = PetscLayoutSetBlockSize(A->rmap,bs);CHKERRQ(ierr);
371   ierr = PetscLayoutSetBlockSize(A->cmap,bs);CHKERRQ(ierr);
372   PetscFunctionReturn(0);
373 }
374 
375 static struct _MatOps MatOps_Values = {0,
376        0,
377        0,
378        0,
379 /* 4*/ 0,
380        0,
381        0,
382        0,
383        0,
384        0,
385 /*10*/ 0,
386        0,
387        0,
388        0,
389        0,
390 /*15*/ 0,
391        0,
392        0,
393        MatDiagonalScale_Shell,
394        0,
395 /*20*/ 0,
396        MatAssemblyEnd_Shell,
397        0,
398        0,
399 /*24*/ 0,
400        0,
401        0,
402        0,
403        0,
404 /*29*/ 0,
405        0,
406        0,
407        0,
408        0,
409 /*34*/ 0,
410        0,
411        0,
412        0,
413        0,
414 /*39*/ 0,
415        0,
416        0,
417        0,
418        0,
419 /*44*/ 0,
420        MatScale_Shell,
421        MatShift_Shell,
422        0,
423        0,
424 /*49*/ MatSetBlockSize_Shell,
425        0,
426        0,
427        0,
428        0,
429 /*54*/ 0,
430        0,
431        0,
432        0,
433        0,
434 /*59*/ 0,
435        MatDestroy_Shell,
436        0,
437        0,
438        0,
439 /*64*/ 0,
440        0,
441        0,
442        0,
443        0,
444 /*69*/ 0,
445        0,
446        MatConvert_Shell,
447        0,
448        0,
449 /*74*/ 0,
450        0,
451        0,
452        0,
453        0,
454 /*79*/ 0,
455        0,
456        0,
457        0,
458        0,
459 /*84*/ 0,
460        0,
461        0,
462        0,
463        0,
464 /*89*/ 0,
465        0,
466        0,
467        0,
468        0,
469 /*94*/ 0,
470        0,
471        0,
472        0};
473 
474 /*MC
475    MATSHELL - MATSHELL = "shell" - A matrix type to be used to define your own matrix type -- perhaps matrix free.
476 
477   Level: advanced
478 
479 .seealso: MatCreateShell
480 M*/
481 
482 EXTERN_C_BEGIN
483 #undef __FUNCT__
484 #define __FUNCT__ "MatCreate_Shell"
485 PetscErrorCode  MatCreate_Shell(Mat A)
486 {
487   Mat_Shell      *b;
488   PetscErrorCode ierr;
489 
490   PetscFunctionBegin;
491   ierr = PetscMemcpy(A->ops,&MatOps_Values,sizeof(struct _MatOps));CHKERRQ(ierr);
492 
493   ierr = PetscNewLog(A,Mat_Shell,&b);CHKERRQ(ierr);
494   A->data = (void*)b;
495 
496   ierr = PetscLayoutSetBlockSize(A->rmap,1);CHKERRQ(ierr);
497   ierr = PetscLayoutSetBlockSize(A->cmap,1);CHKERRQ(ierr);
498   ierr = PetscLayoutSetUp(A->rmap);CHKERRQ(ierr);
499   ierr = PetscLayoutSetUp(A->cmap);CHKERRQ(ierr);
500 
501   b->ctx           = 0;
502   b->vshift        = 0.0;
503   b->vscale        = 1.0;
504   b->mult          = 0;
505   b->multtranspose = 0;
506   b->getdiagonal   = 0;
507   A->assembled     = PETSC_TRUE;
508   A->preallocated  = PETSC_FALSE;
509   ierr = PetscObjectChangeTypeName((PetscObject)A,MATSHELL);CHKERRQ(ierr);
510   PetscFunctionReturn(0);
511 }
512 EXTERN_C_END
513 
514 #undef __FUNCT__
515 #define __FUNCT__ "MatCreateShell"
516 /*@C
517    MatCreateShell - Creates a new matrix class for use with a user-defined
518    private data storage format.
519 
520   Collective on MPI_Comm
521 
522    Input Parameters:
523 +  comm - MPI communicator
524 .  m - number of local rows (must be given)
525 .  n - number of local columns (must be given)
526 .  M - number of global rows (may be PETSC_DETERMINE)
527 .  N - number of global columns (may be PETSC_DETERMINE)
528 -  ctx - pointer to data needed by the shell matrix routines
529 
530    Output Parameter:
531 .  A - the matrix
532 
533    Level: advanced
534 
535   Usage:
536 $    extern int mult(Mat,Vec,Vec);
537 $    MatCreateShell(comm,m,n,M,N,ctx,&mat);
538 $    MatShellSetOperation(mat,MATOP_MULT,(void(*)(void))mult);
539 $    [ Use matrix for operations that have been set ]
540 $    MatDestroy(mat);
541 
542    Notes:
543    The shell matrix type is intended to provide a simple class to use
544    with KSP (such as, for use with matrix-free methods). You should not
545    use the shell type if you plan to define a complete matrix class.
546 
547    Fortran Notes: The context can only be an integer or a PetscObject
548       unfortunately it cannot be a Fortran array or derived type.
549 
550    PETSc requires that matrices and vectors being used for certain
551    operations are partitioned accordingly.  For example, when
552    creating a shell matrix, A, that supports parallel matrix-vector
553    products using MatMult(A,x,y) the user should set the number
554    of local matrix rows to be the number of local elements of the
555    corresponding result vector, y. Note that this is information is
556    required for use of the matrix interface routines, even though
557    the shell matrix may not actually be physically partitioned.
558    For example,
559 
560 $
561 $     Vec x, y
562 $     extern int mult(Mat,Vec,Vec);
563 $     Mat A
564 $
565 $     VecCreateMPI(comm,PETSC_DECIDE,M,&y);
566 $     VecCreateMPI(comm,PETSC_DECIDE,N,&x);
567 $     VecGetLocalSize(y,&m);
568 $     VecGetLocalSize(x,&n);
569 $     MatCreateShell(comm,m,n,M,N,ctx,&A);
570 $     MatShellSetOperation(mat,MATOP_MULT,(void(*)(void))mult);
571 $     MatMult(A,x,y);
572 $     MatDestroy(A);
573 $     VecDestroy(y); VecDestroy(x);
574 $
575 
576 .keywords: matrix, shell, create
577 
578 .seealso: MatShellSetOperation(), MatHasOperation(), MatShellGetContext(), MatShellSetContext()
579 @*/
580 PetscErrorCode  MatCreateShell(MPI_Comm comm,PetscInt m,PetscInt n,PetscInt M,PetscInt N,void *ctx,Mat *A)
581 {
582   PetscErrorCode ierr;
583 
584   PetscFunctionBegin;
585   ierr = MatCreate(comm,A);CHKERRQ(ierr);
586   ierr = MatSetSizes(*A,m,n,M,N);CHKERRQ(ierr);
587 
588   ierr = MatSetType(*A,MATSHELL);CHKERRQ(ierr);
589   ierr = MatShellSetContext(*A,ctx);CHKERRQ(ierr);
590   PetscFunctionReturn(0);
591 }
592 
593 #undef __FUNCT__
594 #define __FUNCT__ "MatShellSetContext"
595 /*@
596     MatShellSetContext - sets the context for a shell matrix
597 
598    Logically Collective on Mat
599 
600     Input Parameters:
601 +   mat - the shell matrix
602 -   ctx - the context
603 
604    Level: advanced
605 
606    Fortran Notes: The context can only be an integer or a PetscObject
607       unfortunately it cannot be a Fortran array or derived type.
608 
609 .seealso: MatCreateShell(), MatShellGetContext(), MatShellGetOperation()
610 @*/
611 PetscErrorCode  MatShellSetContext(Mat mat,void *ctx)
612 {
613   Mat_Shell      *shell = (Mat_Shell*)mat->data;
614   PetscErrorCode ierr;
615   PetscBool      flg;
616 
617   PetscFunctionBegin;
618   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
619   ierr = PetscTypeCompare((PetscObject)mat,MATSHELL,&flg);CHKERRQ(ierr);
620   if (flg) {
621     shell->ctx = ctx;
622   }
623   PetscFunctionReturn(0);
624 }
625 
626 #undef __FUNCT__
627 #define __FUNCT__ "MatShellSetOperation"
628 /*@C
629     MatShellSetOperation - Allows user to set a matrix operation for
630                            a shell matrix.
631 
632    Logically Collective on Mat
633 
634     Input Parameters:
635 +   mat - the shell matrix
636 .   op - the name of the operation
637 -   f - the function that provides the operation.
638 
639    Level: advanced
640 
641     Usage:
642 $      extern PetscErrorCode usermult(Mat,Vec,Vec);
643 $      ierr = MatCreateShell(comm,m,n,M,N,ctx,&A);
644 $      ierr = MatShellSetOperation(A,MATOP_MULT,(void(*)(void))usermult);
645 
646     Notes:
647     See the file include/petscmat.h for a complete list of matrix
648     operations, which all have the form MATOP_<OPERATION>, where
649     <OPERATION> is the name (in all capital letters) of the
650     user interface routine (e.g., MatMult() -> MATOP_MULT).
651 
652     All user-provided functions should have the same calling
653     sequence as the usual matrix interface routines, since they
654     are intended to be accessed via the usual matrix interface
655     routines, e.g.,
656 $       MatMult(Mat,Vec,Vec) -> usermult(Mat,Vec,Vec)
657 
658     In particular each function MUST return an error code of 0 on success and
659     nonzero on failure.
660 
661     Within each user-defined routine, the user should call
662     MatShellGetContext() to obtain the user-defined context that was
663     set by MatCreateShell().
664 
665     Fortran Notes: For MatGetVecs() the user code should check if the input left or right matrix is -1 and in that case not
666        generate a matrix. See src/mat/examples/tests/ex120f.F
667 
668 .keywords: matrix, shell, set, operation
669 
670 .seealso: MatCreateShell(), MatShellGetContext(), MatShellGetOperation(), MatShellSetContext()
671 @*/
672 PetscErrorCode  MatShellSetOperation(Mat mat,MatOperation op,void (*f)(void))
673 {
674   PetscErrorCode ierr;
675   PetscBool      flg;
676 
677   PetscFunctionBegin;
678   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
679   if (op == MATOP_DESTROY) {
680     ierr = PetscTypeCompare((PetscObject)mat,MATSHELL,&flg);CHKERRQ(ierr);
681     if (flg) {
682        Mat_Shell *shell = (Mat_Shell*)mat->data;
683        shell->destroy                 = (PetscErrorCode (*)(Mat)) f;
684     } else mat->ops->destroy          = (PetscErrorCode (*)(Mat)) f;
685   }
686   else if (op == MATOP_VIEW) mat->ops->view  = (PetscErrorCode (*)(Mat,PetscViewer)) f;
687   else                       (((void(**)(void))mat->ops)[op]) = f;
688 
689   PetscFunctionReturn(0);
690 }
691 
692 #undef __FUNCT__
693 #define __FUNCT__ "MatShellGetOperation"
694 /*@C
695     MatShellGetOperation - Gets a matrix function for a shell matrix.
696 
697     Not Collective
698 
699     Input Parameters:
700 +   mat - the shell matrix
701 -   op - the name of the operation
702 
703     Output Parameter:
704 .   f - the function that provides the operation.
705 
706     Level: advanced
707 
708     Notes:
709     See the file include/petscmat.h for a complete list of matrix
710     operations, which all have the form MATOP_<OPERATION>, where
711     <OPERATION> is the name (in all capital letters) of the
712     user interface routine (e.g., MatMult() -> MATOP_MULT).
713 
714     All user-provided functions have the same calling
715     sequence as the usual matrix interface routines, since they
716     are intended to be accessed via the usual matrix interface
717     routines, e.g.,
718 $       MatMult(Mat,Vec,Vec) -> usermult(Mat,Vec,Vec)
719 
720     Within each user-defined routine, the user should call
721     MatShellGetContext() to obtain the user-defined context that was
722     set by MatCreateShell().
723 
724 .keywords: matrix, shell, set, operation
725 
726 .seealso: MatCreateShell(), MatShellGetContext(), MatShellSetOperation(), MatShellSetContext()
727 @*/
728 PetscErrorCode  MatShellGetOperation(Mat mat,MatOperation op,void(**f)(void))
729 {
730   PetscErrorCode ierr;
731   PetscBool      flg;
732 
733   PetscFunctionBegin;
734   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
735   if (op == MATOP_DESTROY) {
736     ierr = PetscTypeCompare((PetscObject)mat,MATSHELL,&flg);CHKERRQ(ierr);
737     if (flg) {
738       Mat_Shell *shell = (Mat_Shell*)mat->data;
739       *f = (void(*)(void))shell->destroy;
740     } else {
741       *f = (void(*)(void))mat->ops->destroy;
742     }
743   } else if (op == MATOP_VIEW) {
744     *f = (void(*)(void))mat->ops->view;
745   } else {
746     *f = (((void(**)(void))mat->ops)[op]);
747   }
748 
749   PetscFunctionReturn(0);
750 }
751 
752