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