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