Actual source code: shell.c
petsc-3.11.0 2019-03-29
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: */
8: #include <petsc/private/matimpl.h>
10: struct _MatShellOps {
11: /* 3 */ PetscErrorCode (*mult)(Mat,Vec,Vec);
12: /* 5 */ PetscErrorCode (*multtranspose)(Mat,Vec,Vec);
13: /* 17 */ PetscErrorCode (*getdiagonal)(Mat,Vec);
14: /* 43 */ PetscErrorCode (*copy)(Mat,Mat,MatStructure);
15: /* 60 */ PetscErrorCode (*destroy)(Mat);
16: };
18: typedef struct {
19: struct _MatShellOps ops[1];
21: PetscScalar vscale,vshift;
22: Vec dshift;
23: Vec left,right;
24: Vec left_work,right_work;
25: Vec left_add_work,right_add_work;
26: Mat axpy;
27: PetscScalar axpy_vscale;
28: PetscBool managescalingshifts; /* The user will manage the scaling and shifts for the MATSHELL, not the default */
29: void *ctx;
30: } Mat_Shell;
32: /*
33: xx = diag(left)*x
34: */
35: static PetscErrorCode MatShellPreScaleLeft(Mat A,Vec x,Vec *xx)
36: {
37: Mat_Shell *shell = (Mat_Shell*)A->data;
41: *xx = NULL;
42: if (!shell->left) {
43: *xx = x;
44: } else {
45: if (!shell->left_work) {VecDuplicate(shell->left,&shell->left_work);}
46: VecPointwiseMult(shell->left_work,x,shell->left);
47: *xx = shell->left_work;
48: }
49: return(0);
50: }
52: /*
53: xx = diag(right)*x
54: */
55: static PetscErrorCode MatShellPreScaleRight(Mat A,Vec x,Vec *xx)
56: {
57: Mat_Shell *shell = (Mat_Shell*)A->data;
61: *xx = NULL;
62: if (!shell->right) {
63: *xx = x;
64: } else {
65: if (!shell->right_work) {VecDuplicate(shell->right,&shell->right_work);}
66: VecPointwiseMult(shell->right_work,x,shell->right);
67: *xx = shell->right_work;
68: }
69: return(0);
70: }
72: /*
73: x = diag(left)*x
74: */
75: static PetscErrorCode MatShellPostScaleLeft(Mat A,Vec x)
76: {
77: Mat_Shell *shell = (Mat_Shell*)A->data;
81: if (shell->left) {VecPointwiseMult(x,x,shell->left);}
82: return(0);
83: }
85: /*
86: x = diag(right)*x
87: */
88: static PetscErrorCode MatShellPostScaleRight(Mat A,Vec x)
89: {
90: Mat_Shell *shell = (Mat_Shell*)A->data;
94: if (shell->right) {VecPointwiseMult(x,x,shell->right);}
95: return(0);
96: }
98: /*
99: Y = vscale*Y + diag(dshift)*X + vshift*X
101: On input Y already contains A*x
102: */
103: static PetscErrorCode MatShellShiftAndScale(Mat A,Vec X,Vec Y)
104: {
105: Mat_Shell *shell = (Mat_Shell*)A->data;
109: if (shell->dshift) { /* get arrays because there is no VecPointwiseMultAdd() */
110: PetscInt i,m;
111: const PetscScalar *x,*d;
112: PetscScalar *y;
113: VecGetLocalSize(X,&m);
114: VecGetArrayRead(shell->dshift,&d);
115: VecGetArrayRead(X,&x);
116: VecGetArray(Y,&y);
117: for (i=0; i<m; i++) y[i] = shell->vscale*y[i] + d[i]*x[i];
118: VecRestoreArrayRead(shell->dshift,&d);
119: VecRestoreArrayRead(X,&x);
120: VecRestoreArray(Y,&y);
121: } else {
122: VecScale(Y,shell->vscale);
123: }
124: if (shell->vshift != 0.0) {VecAXPY(Y,shell->vshift,X);} /* if test is for non-square matrices */
125: return(0);
126: }
128: /*@
129: MatShellGetContext - Returns the user-provided context associated with a shell matrix.
131: Not Collective
133: Input Parameter:
134: . mat - the matrix, should have been created with MatCreateShell()
136: Output Parameter:
137: . ctx - the user provided context
139: Level: advanced
141: Fortran Notes:
142: To use this from Fortran you must write a Fortran interface definition for this
143: function that tells Fortran the Fortran derived data type that you are passing in as the ctx argument.
145: .keywords: matrix, shell, get, context
147: .seealso: MatCreateShell(), MatShellSetOperation(), MatShellSetContext()
148: @*/
149: PetscErrorCode MatShellGetContext(Mat mat,void *ctx)
150: {
152: PetscBool flg;
157: PetscObjectTypeCompare((PetscObject)mat,MATSHELL,&flg);
158: if (flg) *(void**)ctx = ((Mat_Shell*)(mat->data))->ctx;
159: else SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_SUP,"Cannot get context from non-shell matrix");
160: return(0);
161: }
163: PetscErrorCode MatDestroy_Shell(Mat mat)
164: {
166: Mat_Shell *shell = (Mat_Shell*)mat->data;
169: if (shell->ops->destroy) {
170: (*shell->ops->destroy)(mat);
171: }
172: VecDestroy(&shell->left);
173: VecDestroy(&shell->right);
174: VecDestroy(&shell->dshift);
175: VecDestroy(&shell->left_work);
176: VecDestroy(&shell->right_work);
177: VecDestroy(&shell->left_add_work);
178: VecDestroy(&shell->right_add_work);
179: MatDestroy(&shell->axpy);
180: PetscFree(mat->data);
181: return(0);
182: }
184: PetscErrorCode MatCopy_Shell(Mat A,Mat B,MatStructure str)
185: {
186: Mat_Shell *shellA = (Mat_Shell*)A->data,*shellB = (Mat_Shell*)B->data;
187: PetscErrorCode ierr;
188: PetscBool matflg;
191: PetscObjectTypeCompare((PetscObject)B,MATSHELL,&matflg);
192: if (!matflg) SETERRQ(PetscObjectComm((PetscObject)A),PETSC_ERR_ARG_NOTSAMETYPE,"Output matrix must be a MATSHELL");
194: PetscMemcpy(B->ops,A->ops,sizeof(struct _MatOps));
195: PetscMemcpy(shellB->ops,shellA->ops,sizeof(struct _MatShellOps));
197: if (shellA->ops->copy) {
198: (*shellA->ops->copy)(A,B,str);
199: }
200: shellB->vscale = shellA->vscale;
201: shellB->vshift = shellA->vshift;
202: if (shellA->dshift) {
203: if (!shellB->dshift) {
204: VecDuplicate(shellA->dshift,&shellB->dshift);
205: }
206: VecCopy(shellA->dshift,shellB->dshift);
207: } else {
208: VecDestroy(&shellB->dshift);
209: }
210: if (shellA->left) {
211: if (!shellB->left) {
212: VecDuplicate(shellA->left,&shellB->left);
213: }
214: VecCopy(shellA->left,shellB->left);
215: } else {
216: VecDestroy(&shellB->left);
217: }
218: if (shellA->right) {
219: if (!shellB->right) {
220: VecDuplicate(shellA->right,&shellB->right);
221: }
222: VecCopy(shellA->right,shellB->right);
223: } else {
224: VecDestroy(&shellB->right);
225: }
226: MatDestroy(&shellB->axpy);
227: if (shellA->axpy) {
228: PetscObjectReference((PetscObject)shellA->axpy);
229: shellB->axpy = shellA->axpy;
230: shellB->axpy_vscale = shellA->axpy_vscale;
231: }
232: return(0);
233: }
235: PetscErrorCode MatDuplicate_Shell(Mat mat,MatDuplicateOption op,Mat *M)
236: {
238: void *ctx;
241: MatShellGetContext(mat,&ctx);
242: MatCreateShell(PetscObjectComm((PetscObject)mat),mat->rmap->n,mat->cmap->n,mat->rmap->N,mat->cmap->N,ctx,M);
243: MatCopy(mat,*M,SAME_NONZERO_PATTERN);
244: return(0);
245: }
247: PetscErrorCode MatMult_Shell(Mat A,Vec x,Vec y)
248: {
249: Mat_Shell *shell = (Mat_Shell*)A->data;
250: PetscErrorCode ierr;
251: Vec xx;
252: PetscObjectState instate,outstate;
255: MatShellPreScaleRight(A,x,&xx);
256: PetscObjectStateGet((PetscObject)y, &instate);
257: if (!shell->ops->mult) SETERRQ(PetscObjectComm((PetscObject)A),PETSC_ERR_ARG_WRONGSTATE,"Have not provided a MatMult() for this MATSHELL");
258: (*shell->ops->mult)(A,xx,y);
259: PetscObjectStateGet((PetscObject)y, &outstate);
260: if (instate == outstate) {
261: /* increase the state of the output vector since the user did not update its state themself as should have been done */
262: PetscObjectStateIncrease((PetscObject)y);
263: }
264: MatShellShiftAndScale(A,xx,y);
265: MatShellPostScaleLeft(A,y);
267: if (shell->axpy) {
268: if (!shell->left_work) {MatCreateVecs(A,&shell->left_work,NULL);}
269: MatMult(shell->axpy,x,shell->left_work);
270: VecAXPY(y,shell->axpy_vscale,shell->left_work);
271: }
272: return(0);
273: }
275: PetscErrorCode MatMultAdd_Shell(Mat A,Vec x,Vec y,Vec z)
276: {
277: Mat_Shell *shell = (Mat_Shell*)A->data;
281: if (y == z) {
282: if (!shell->right_add_work) {VecDuplicate(z,&shell->right_add_work);}
283: MatMult(A,x,shell->right_add_work);
284: VecAXPY(z,1.0,shell->right_add_work);
285: } else {
286: MatMult(A,x,z);
287: VecAXPY(z,1.0,y);
288: }
289: return(0);
290: }
292: PetscErrorCode MatMultTranspose_Shell(Mat A,Vec x,Vec y)
293: {
294: Mat_Shell *shell = (Mat_Shell*)A->data;
295: PetscErrorCode ierr;
296: Vec xx;
297: PetscObjectState instate,outstate;
300: MatShellPreScaleLeft(A,x,&xx);
301: PetscObjectStateGet((PetscObject)y, &instate);
302: if (!shell->ops->multtranspose) SETERRQ(PetscObjectComm((PetscObject)A),PETSC_ERR_ARG_WRONGSTATE,"Have not provided a MatMultTranspose() for this MATSHELL");
303: (*shell->ops->multtranspose)(A,xx,y);
304: PetscObjectStateGet((PetscObject)y, &outstate);
305: if (instate == outstate) {
306: /* increase the state of the output vector since the user did not update its state themself as should have been done */
307: PetscObjectStateIncrease((PetscObject)y);
308: }
309: MatShellShiftAndScale(A,xx,y);
310: MatShellPostScaleRight(A,y);
311: return(0);
312: }
314: PetscErrorCode MatMultTransposeAdd_Shell(Mat A,Vec x,Vec y,Vec z)
315: {
316: Mat_Shell *shell = (Mat_Shell*)A->data;
320: if (y == z) {
321: if (!shell->left_add_work) {VecDuplicate(z,&shell->left_add_work);}
322: MatMultTranspose(A,x,shell->left_add_work);
323: VecAXPY(z,1.0,shell->left_add_work);
324: } else {
325: MatMultTranspose(A,x,z);
326: VecAXPY(z,1.0,y);
327: }
328: return(0);
329: }
331: /*
332: diag(left)(vscale*A + diag(dshift) + vshift I)diag(right)
333: */
334: PetscErrorCode MatGetDiagonal_Shell(Mat A,Vec v)
335: {
336: Mat_Shell *shell = (Mat_Shell*)A->data;
340: if (shell->ops->getdiagonal) {
341: (*shell->ops->getdiagonal)(A,v);
342: } else SETERRQ(PetscObjectComm((PetscObject)A),PETSC_ERR_ARG_WRONGSTATE,"Must provide shell matrix with routine to return diagonal using\nMatShellSetOperation(S,MATOP_GET_DIAGONAL,...)");
343: VecScale(v,shell->vscale);
344: if (shell->dshift) {
345: VecAXPY(v,1.0,shell->dshift);
346: }
347: VecShift(v,shell->vshift);
348: if (shell->left) {VecPointwiseMult(v,v,shell->left);}
349: if (shell->right) {VecPointwiseMult(v,v,shell->right);}
350: if (shell->axpy) {
351: if (!shell->left_work) {VecDuplicate(v,&shell->left_work);}
352: MatGetDiagonal(shell->axpy,shell->left_work);
353: VecAXPY(v,shell->axpy_vscale,shell->left_work);
354: }
355: return(0);
356: }
358: PetscErrorCode MatShift_Shell(Mat Y,PetscScalar a)
359: {
360: Mat_Shell *shell = (Mat_Shell*)Y->data;
364: if (shell->left || shell->right) {
365: if (!shell->dshift) {
366: VecDuplicate(shell->left ? shell->left : shell->right, &shell->dshift);
367: VecSet(shell->dshift,a);
368: } else {
369: if (shell->left) {VecPointwiseMult(shell->dshift,shell->dshift,shell->left);}
370: if (shell->right) {VecPointwiseMult(shell->dshift,shell->dshift,shell->right);}
371: VecShift(shell->dshift,a);
372: }
373: if (shell->left) {VecPointwiseDivide(shell->dshift,shell->dshift,shell->left);}
374: if (shell->right) {VecPointwiseDivide(shell->dshift,shell->dshift,shell->right);}
375: } else shell->vshift += a;
376: return(0);
377: }
379: PetscErrorCode MatDiagonalSet_Shell_Private(Mat A,Vec D,PetscScalar s)
380: {
381: Mat_Shell *shell = (Mat_Shell*)A->data;
385: if (!shell->dshift) {VecDuplicate(D,&shell->dshift);}
386: if (shell->left || shell->right) {
387: if (!shell->right_work) {VecDuplicate(shell->left ? shell->left : shell->right, &shell->right_work);}
388: if (shell->left && shell->right) {
389: VecPointwiseDivide(shell->right_work,D,shell->left);
390: VecPointwiseDivide(shell->right_work,shell->right_work,shell->right);
391: } else if (shell->left) {
392: VecPointwiseDivide(shell->right_work,D,shell->left);
393: } else {
394: VecPointwiseDivide(shell->right_work,D,shell->right);
395: }
396: if (!shell->dshift) {
397: VecDuplicate(shell->left ? shell->left : shell->right, &shell->dshift);
398: VecCopy(shell->dshift,shell->right_work);
399: } else {
400: VecAXPY(shell->dshift,s,shell->right_work);
401: }
402: } else {
403: VecAXPY(shell->dshift,s,D);
404: }
405: return(0);
406: }
408: PetscErrorCode MatDiagonalSet_Shell(Mat A,Vec D,InsertMode ins)
409: {
410: Vec d;
414: if (ins == INSERT_VALUES) {
415: if (!A->ops->getdiagonal) SETERRQ(PetscObjectComm((PetscObject)A),PETSC_ERR_SUP,"Operation MATOP_GETDIAGONAL must be set first");
416: VecDuplicate(D,&d);
417: MatGetDiagonal(A,d);
418: MatDiagonalSet_Shell_Private(A,d,-1.);
419: MatDiagonalSet_Shell_Private(A,D,1.);
420: VecDestroy(&d);
421: } else {
422: MatDiagonalSet_Shell_Private(A,D,1.);
423: }
424: return(0);
425: }
427: PetscErrorCode MatScale_Shell(Mat Y,PetscScalar a)
428: {
429: Mat_Shell *shell = (Mat_Shell*)Y->data;
433: shell->vscale *= a;
434: shell->vshift *= a;
435: if (shell->dshift) {
436: VecScale(shell->dshift,a);
437: }
438: shell->axpy_vscale *= a;
439: return(0);
440: }
442: static PetscErrorCode MatDiagonalScale_Shell(Mat Y,Vec left,Vec right)
443: {
444: Mat_Shell *shell = (Mat_Shell*)Y->data;
448: if (shell->axpy) SETERRQ(PetscObjectComm((PetscObject)Y),PETSC_ERR_SUP,"Cannot diagonal scale MATSHELL after MatAXPY operation");
449: if (left) {
450: if (!shell->left) {
451: VecDuplicate(left,&shell->left);
452: VecCopy(left,shell->left);
453: } else {
454: VecPointwiseMult(shell->left,shell->left,left);
455: }
456: }
457: if (right) {
458: if (!shell->right) {
459: VecDuplicate(right,&shell->right);
460: VecCopy(right,shell->right);
461: } else {
462: VecPointwiseMult(shell->right,shell->right,right);
463: }
464: }
465: return(0);
466: }
468: PetscErrorCode MatAssemblyEnd_Shell(Mat Y,MatAssemblyType t)
469: {
470: Mat_Shell *shell = (Mat_Shell*)Y->data;
474: if (t == MAT_FINAL_ASSEMBLY) {
475: shell->vshift = 0.0;
476: shell->vscale = 1.0;
477: VecDestroy(&shell->dshift);
478: VecDestroy(&shell->left);
479: VecDestroy(&shell->right);
480: MatDestroy(&shell->axpy);
481: }
482: return(0);
483: }
485: static PetscErrorCode MatMissingDiagonal_Shell(Mat A,PetscBool *missing,PetscInt *d)
486: {
488: *missing = PETSC_FALSE;
489: return(0);
490: }
492: PetscErrorCode MatAXPY_Shell(Mat Y,PetscScalar a,Mat X,MatStructure str)
493: {
494: Mat_Shell *shell = (Mat_Shell*)Y->data;
498: PetscObjectReference((PetscObject)X);
499: MatDestroy(&shell->axpy);
500: shell->axpy = X;
501: shell->axpy_vscale = a;
502: return(0);
503: }
505: static struct _MatOps MatOps_Values = {0,
506: 0,
507: 0,
508: 0,
509: /* 4*/ MatMultAdd_Shell,
510: 0,
511: MatMultTransposeAdd_Shell,
512: 0,
513: 0,
514: 0,
515: /*10*/ 0,
516: 0,
517: 0,
518: 0,
519: 0,
520: /*15*/ 0,
521: 0,
522: 0,
523: MatDiagonalScale_Shell,
524: 0,
525: /*20*/ 0,
526: MatAssemblyEnd_Shell,
527: 0,
528: 0,
529: /*24*/ 0,
530: 0,
531: 0,
532: 0,
533: 0,
534: /*29*/ 0,
535: 0,
536: 0,
537: 0,
538: 0,
539: /*34*/ MatDuplicate_Shell,
540: 0,
541: 0,
542: 0,
543: 0,
544: /*39*/ MatAXPY_Shell,
545: 0,
546: 0,
547: 0,
548: MatCopy_Shell,
549: /*44*/ 0,
550: MatScale_Shell,
551: MatShift_Shell,
552: MatDiagonalSet_Shell,
553: 0,
554: /*49*/ 0,
555: 0,
556: 0,
557: 0,
558: 0,
559: /*54*/ 0,
560: 0,
561: 0,
562: 0,
563: 0,
564: /*59*/ 0,
565: MatDestroy_Shell,
566: 0,
567: 0,
568: 0,
569: /*64*/ 0,
570: 0,
571: 0,
572: 0,
573: 0,
574: /*69*/ 0,
575: 0,
576: MatConvert_Shell,
577: 0,
578: 0,
579: /*74*/ 0,
580: 0,
581: 0,
582: 0,
583: 0,
584: /*79*/ 0,
585: 0,
586: 0,
587: 0,
588: 0,
589: /*84*/ 0,
590: 0,
591: 0,
592: 0,
593: 0,
594: /*89*/ 0,
595: 0,
596: 0,
597: 0,
598: 0,
599: /*94*/ 0,
600: 0,
601: 0,
602: 0,
603: 0,
604: /*99*/ 0,
605: 0,
606: 0,
607: 0,
608: 0,
609: /*104*/ 0,
610: 0,
611: 0,
612: 0,
613: 0,
614: /*109*/ 0,
615: 0,
616: 0,
617: 0,
618: MatMissingDiagonal_Shell,
619: /*114*/ 0,
620: 0,
621: 0,
622: 0,
623: 0,
624: /*119*/ 0,
625: 0,
626: 0,
627: 0,
628: 0,
629: /*124*/ 0,
630: 0,
631: 0,
632: 0,
633: 0,
634: /*129*/ 0,
635: 0,
636: 0,
637: 0,
638: 0,
639: /*134*/ 0,
640: 0,
641: 0,
642: 0,
643: 0,
644: /*139*/ 0,
645: 0,
646: 0
647: };
649: /*MC
650: MATSHELL - MATSHELL = "shell" - A matrix type to be used to define your own matrix type -- perhaps matrix free.
652: Level: advanced
654: .seealso: MatCreateShell()
655: M*/
657: PETSC_EXTERN PetscErrorCode MatCreate_Shell(Mat A)
658: {
659: Mat_Shell *b;
663: PetscMemcpy(A->ops,&MatOps_Values,sizeof(struct _MatOps));
665: PetscNewLog(A,&b);
666: A->data = (void*)b;
668: PetscLayoutSetUp(A->rmap);
669: PetscLayoutSetUp(A->cmap);
671: b->ctx = 0;
672: b->vshift = 0.0;
673: b->vscale = 1.0;
674: b->managescalingshifts = PETSC_TRUE;
675: A->assembled = PETSC_TRUE;
676: A->preallocated = PETSC_FALSE;
678: PetscObjectChangeTypeName((PetscObject)A,MATSHELL);
679: return(0);
680: }
682: /*@C
683: MatCreateShell - Creates a new matrix class for use with a user-defined
684: private data storage format.
686: Collective on MPI_Comm
688: Input Parameters:
689: + comm - MPI communicator
690: . m - number of local rows (must be given)
691: . n - number of local columns (must be given)
692: . M - number of global rows (may be PETSC_DETERMINE)
693: . N - number of global columns (may be PETSC_DETERMINE)
694: - ctx - pointer to data needed by the shell matrix routines
696: Output Parameter:
697: . A - the matrix
699: Level: advanced
701: Usage:
702: $ extern int mult(Mat,Vec,Vec);
703: $ MatCreateShell(comm,m,n,M,N,ctx,&mat);
704: $ MatShellSetOperation(mat,MATOP_MULT,(void(*)(void))mult);
705: $ [ Use matrix for operations that have been set ]
706: $ MatDestroy(mat);
708: Notes:
709: The shell matrix type is intended to provide a simple class to use
710: with KSP (such as, for use with matrix-free methods). You should not
711: use the shell type if you plan to define a complete matrix class.
713: Fortran Notes:
714: To use this from Fortran with a ctx you must write an interface definition for this
715: function and for MatShellGetContext() that tells Fortran the Fortran derived data type you are passing
716: in as the ctx argument.
718: PETSc requires that matrices and vectors being used for certain
719: operations are partitioned accordingly. For example, when
720: creating a shell matrix, A, that supports parallel matrix-vector
721: products using MatMult(A,x,y) the user should set the number
722: of local matrix rows to be the number of local elements of the
723: corresponding result vector, y. Note that this is information is
724: required for use of the matrix interface routines, even though
725: the shell matrix may not actually be physically partitioned.
726: For example,
728: $
729: $ Vec x, y
730: $ extern int mult(Mat,Vec,Vec);
731: $ Mat A
732: $
733: $ VecCreateMPI(comm,PETSC_DECIDE,M,&y);
734: $ VecCreateMPI(comm,PETSC_DECIDE,N,&x);
735: $ VecGetLocalSize(y,&m);
736: $ VecGetLocalSize(x,&n);
737: $ MatCreateShell(comm,m,n,M,N,ctx,&A);
738: $ MatShellSetOperation(mat,MATOP_MULT,(void(*)(void))mult);
739: $ MatMult(A,x,y);
740: $ MatDestroy(A);
741: $ VecDestroy(y); VecDestroy(x);
742: $
745: MATSHELL handles MatShift(), MatDiagonalSet(), MatDiagonalScale(), MatAXPY(), and MatScale() internally so these
746: operations cannot be overwritten unless MatShellSetManageScalingShifts() is called.
749: For rectangular matrices do all the scalings and shifts make sense?
751: Developers Notes:
752: Regarding shifting and scaling. The general form is
754: diag(left)(vscale*A + diag(dshift) + vshift I)diag(right)
756: The order you apply the operations is important. For example if you have a dshift then
757: apply a MatScale(s) you get s*vscale*A + s*diag(shift). But if you first scale and then shift
758: you get s*vscale*A + diag(shift)
760: A is the user provided function.
762: .keywords: matrix, shell, create
764: .seealso: MatShellSetOperation(), MatHasOperation(), MatShellGetContext(), MatShellSetContext(), MATSHELL, MatShellSetManageScalingShifts()
765: @*/
766: PetscErrorCode MatCreateShell(MPI_Comm comm,PetscInt m,PetscInt n,PetscInt M,PetscInt N,void *ctx,Mat *A)
767: {
771: MatCreate(comm,A);
772: MatSetSizes(*A,m,n,M,N);
773: MatSetType(*A,MATSHELL);
774: MatShellSetContext(*A,ctx);
775: MatSetUp(*A);
776: return(0);
777: }
779: /*@
780: MatShellSetContext - sets the context for a shell matrix
782: Logically Collective on Mat
784: Input Parameters:
785: + mat - the shell matrix
786: - ctx - the context
788: Level: advanced
790: Fortran Notes:
791: To use this from Fortran you must write a Fortran interface definition for this
792: function that tells Fortran the Fortran derived data type that you are passing in as the ctx argument.
794: .seealso: MatCreateShell(), MatShellGetContext(), MatShellGetOperation()
795: @*/
796: PetscErrorCode MatShellSetContext(Mat mat,void *ctx)
797: {
798: Mat_Shell *shell = (Mat_Shell*)mat->data;
800: PetscBool flg;
804: PetscObjectTypeCompare((PetscObject)mat,MATSHELL,&flg);
805: if (flg) {
806: shell->ctx = ctx;
807: } else SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_SUP,"Cannot attach context to non-shell matrix");
808: return(0);
809: }
811: /*@
812: MatShellSetManageScalingShifts - Allows the user to control the scaling and shift operations of the MATSHELL. Must be called immediately
813: after MatCreateShell()
815: Logically Collective on Mat
817: Input Parameter:
818: . mat - the shell matrix
820: Level: advanced
822: .seealso: MatCreateShell(), MatShellGetContext(), MatShellGetOperation(), MatShellSetContext(), MatShellSetOperation()
823: @*/
824: PetscErrorCode MatShellSetManageScalingShifts(Mat A)
825: {
827: Mat_Shell *shell;
828: PetscBool flg;
832: PetscObjectTypeCompare((PetscObject)A,MATSHELL,&flg);
833: if (!flg) SETERRQ(PetscObjectComm((PetscObject)A),PETSC_ERR_SUP,"Can only use with MATSHELL matrices");
834: shell = (Mat_Shell*)A->data;
835: shell->managescalingshifts = PETSC_FALSE;
836: A->ops->diagonalset = NULL;
837: A->ops->diagonalscale = NULL;
838: A->ops->scale = NULL;
839: A->ops->shift = NULL;
840: A->ops->axpy = NULL;
841: return(0);
842: }
844: /*@C
845: MatShellTestMult - Compares the multiply routine provided to the MATSHELL with differencing on a given function.
847: Logically Collective on Mat
849: Input Parameters:
850: + mat - the shell matrix
851: . f - the function
852: . base - differences are computed around this vector, see MatMFFDSetBase(), for Jacobians this is the point at which the Jacobian is being evaluated
853: - ctx - an optional context for the function
855: Output Parameter:
856: . flg - PETSC_TRUE if the multiply is likely correct
858: Options Database:
859: . -mat_shell_test_mult_view - print if any differences are detected between the products and print the difference
861: Level: advanced
863: Fortran Notes:
864: Not supported from Fortran
866: .seealso: MatCreateShell(), MatShellGetContext(), MatShellGetOperation(), MatShellTestMultTranspose()
867: @*/
868: PetscErrorCode MatShellTestMult(Mat mat,PetscErrorCode (*f)(void*,Vec,Vec),Vec base,void *ctx,PetscBool *flg)
869: {
871: PetscInt m,n;
872: Mat mf,Dmf,Dmat,Ddiff;
873: PetscReal Diffnorm,Dmfnorm;
874: PetscBool v = PETSC_FALSE, flag = PETSC_TRUE;
878: PetscOptionsHasName(NULL,((PetscObject)mat)->prefix,"-mat_shell_test_mult_view",&v);
879: MatGetLocalSize(mat,&m,&n);
880: MatCreateMFFD(PetscObjectComm((PetscObject)mat),m,n,PETSC_DECIDE,PETSC_DECIDE,&mf);
881: MatMFFDSetFunction(mf,f,ctx);
882: MatMFFDSetBase(mf,base,NULL);
884: MatComputeExplicitOperator(mf,&Dmf);
885: MatComputeExplicitOperator(mat,&Dmat);
887: MatDuplicate(Dmat,MAT_COPY_VALUES,&Ddiff);
888: MatAXPY(Ddiff,-1.0,Dmf,DIFFERENT_NONZERO_PATTERN);
889: MatNorm(Ddiff,NORM_FROBENIUS,&Diffnorm);
890: MatNorm(Dmf,NORM_FROBENIUS,&Dmfnorm);
891: if (Diffnorm/Dmfnorm > 10*PETSC_SQRT_MACHINE_EPSILON) {
892: flag = PETSC_FALSE;
893: if (v) {
894: PetscPrintf(PetscObjectComm((PetscObject)mat),"MATSHELL and matrix free multiple appear to produce different results.\n Norm Ratio %g Difference results followed by finite difference one\n",(double)(Diffnorm/Dmfnorm));
895: MatViewFromOptions(Ddiff,(PetscObject)mat,"-mat_shell_test_mult_view");
896: MatViewFromOptions(Dmf,(PetscObject)mat,"-mat_shell_test_mult_view");
897: MatViewFromOptions(Dmat,(PetscObject)mat,"-mat_shell_test_mult_view");
898: }
899: } else if (v) {
900: PetscPrintf(PetscObjectComm((PetscObject)mat),"MATSHELL and matrix free multiple appear to produce the same results\n");
901: }
902: if (flg) *flg = flag;
903: MatDestroy(&Ddiff);
904: MatDestroy(&mf);
905: MatDestroy(&Dmf);
906: MatDestroy(&Dmat);
907: return(0);
908: }
910: /*@C
911: MatShellTestMultTranpose - Compares the multiply transpose routine provided to the MATSHELL with differencing on a given function.
913: Logically Collective on Mat
915: Input Parameters:
916: + mat - the shell matrix
917: . f - the function
918: . base - differences are computed around this vector, see MatMFFDSetBase(), for Jacobians this is the point at which the Jacobian is being evaluated
919: - ctx - an optional context for the function
921: Output Parameter:
922: . flg - PETSC_TRUE if the multiply is likely correct
924: Options Database:
925: . -mat_shell_test_mult_view - print if any differences are detected between the products and print the difference
927: Level: advanced
929: Fortran Notes:
930: Not supported from Fortran
932: .seealso: MatCreateShell(), MatShellGetContext(), MatShellGetOperation(), MatShellTestMult()
933: @*/
934: PetscErrorCode MatShellTestMultTranspose(Mat mat,PetscErrorCode (*f)(void*,Vec,Vec),Vec base,void *ctx,PetscBool *flg)
935: {
937: Vec x,y,z;
938: PetscInt m,n,M,N;
939: Mat mf,Dmf,Dmat,Ddiff;
940: PetscReal Diffnorm,Dmfnorm;
941: PetscBool v = PETSC_FALSE, flag = PETSC_TRUE;
945: PetscOptionsHasName(NULL,((PetscObject)mat)->prefix,"-mat_shell_test_mult_transpose_view",&v);
946: MatCreateVecs(mat,&x,&y);
947: VecDuplicate(y,&z);
948: MatGetLocalSize(mat,&m,&n);
949: MatGetSize(mat,&M,&N);
950: MatCreateMFFD(PetscObjectComm((PetscObject)mat),m,n,M,N,&mf);
951: MatMFFDSetFunction(mf,f,ctx);
952: MatMFFDSetBase(mf,base,NULL);
953: MatComputeExplicitOperator(mf,&Dmf);
954: MatTranspose(Dmf,MAT_INPLACE_MATRIX,&Dmf);
955: MatComputeExplicitOperatorTranspose(mat,&Dmat);
957: MatDuplicate(Dmat,MAT_COPY_VALUES,&Ddiff);
958: MatAXPY(Ddiff,-1.0,Dmf,DIFFERENT_NONZERO_PATTERN);
959: MatNorm(Ddiff,NORM_FROBENIUS,&Diffnorm);
960: MatNorm(Dmf,NORM_FROBENIUS,&Dmfnorm);
961: if (Diffnorm/Dmfnorm > 10*PETSC_SQRT_MACHINE_EPSILON) {
962: flag = PETSC_FALSE;
963: if (v) {
964: PetscPrintf(PetscObjectComm((PetscObject)mat),"MATSHELL and matrix free multiple appear to produce different results.\n Norm Ratio %g Difference results followed by finite difference one\n",(double)(Diffnorm/Dmfnorm));
965: MatViewFromOptions(Ddiff,(PetscObject)mat,"-mat_shell_test_mult_transpose_view");
966: MatViewFromOptions(Dmf,(PetscObject)mat,"-mat_shell_test_mult_transpose_view");
967: MatViewFromOptions(Dmat,(PetscObject)mat,"-mat_shell_test_mult_transpose_view");
968: }
969: } else if (v) {
970: PetscPrintf(PetscObjectComm((PetscObject)mat),"MATSHELL transpose and matrix free multiple appear to produce the same results\n");
971: }
972: if (flg) *flg = flag;
973: MatDestroy(&mf);
974: MatDestroy(&Dmat);
975: MatDestroy(&Ddiff);
976: MatDestroy(&Dmf);
977: VecDestroy(&x);
978: VecDestroy(&y);
979: VecDestroy(&z);
980: return(0);
981: }
983: /*@C
984: MatShellSetOperation - Allows user to set a matrix operation for a shell matrix.
986: Logically Collective on Mat
988: Input Parameters:
989: + mat - the shell matrix
990: . op - the name of the operation
991: - f - the function that provides the operation.
993: Level: advanced
995: Usage:
996: $ extern PetscErrorCode usermult(Mat,Vec,Vec);
997: $ MatCreateShell(comm,m,n,M,N,ctx,&A);
998: $ MatShellSetOperation(A,MATOP_MULT,(void(*)(void))usermult);
1000: Notes:
1001: See the file include/petscmat.h for a complete list of matrix
1002: operations, which all have the form MATOP_<OPERATION>, where
1003: <OPERATION> is the name (in all capital letters) of the
1004: user interface routine (e.g., MatMult() -> MATOP_MULT).
1006: All user-provided functions (except for MATOP_DESTROY) should have the same calling
1007: sequence as the usual matrix interface routines, since they
1008: are intended to be accessed via the usual matrix interface
1009: routines, e.g.,
1010: $ MatMult(Mat,Vec,Vec) -> usermult(Mat,Vec,Vec)
1012: In particular each function MUST return an error code of 0 on success and
1013: nonzero on failure.
1015: Within each user-defined routine, the user should call
1016: MatShellGetContext() to obtain the user-defined context that was
1017: set by MatCreateShell().
1019: Fortran Notes:
1020: For MatCreateVecs() the user code should check if the input left or right matrix is -1 and in that case not
1021: generate a matrix. See src/mat/examples/tests/ex120f.F
1023: Use MatSetOperation() to set an operation for any matrix type
1025: .keywords: matrix, shell, set, operation
1027: .seealso: MatCreateShell(), MatShellGetContext(), MatShellGetOperation(), MatShellSetContext(), MatSetOperation(), MatShellSetManageScalingShifts()
1028: @*/
1029: PetscErrorCode MatShellSetOperation(Mat mat,MatOperation op,void (*f)(void))
1030: {
1031: PetscBool flg;
1032: Mat_Shell *shell;
1037: PetscObjectTypeCompare((PetscObject)mat,MATSHELL,&flg);
1038: if (!flg) SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_SUP,"Can only use with MATSHELL matrices");
1039: shell = (Mat_Shell*)mat->data;
1041: switch (op) {
1042: case MATOP_DESTROY:
1043: shell->ops->destroy = (PetscErrorCode (*)(Mat))f;
1044: break;
1045: case MATOP_VIEW:
1046: if (!mat->ops->viewnative) {
1047: mat->ops->viewnative = mat->ops->view;
1048: }
1049: mat->ops->view = (PetscErrorCode (*)(Mat,PetscViewer))f;
1050: break;
1051: case MATOP_COPY:
1052: shell->ops->copy = (PetscErrorCode (*)(Mat,Mat,MatStructure))f;
1053: break;
1054: case MATOP_DIAGONAL_SET:
1055: case MATOP_DIAGONAL_SCALE:
1056: case MATOP_SHIFT:
1057: case MATOP_SCALE:
1058: case MATOP_AXPY:
1059: if (shell->managescalingshifts) SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_ARG_WRONGSTATE,"MATSHELL is managing scalings and shifts, see MatShellSetManageScalingShifts()");
1060: (((void(**)(void))mat->ops)[op]) = f;
1061: break;
1062: case MATOP_GET_DIAGONAL:
1063: if (shell->managescalingshifts) {
1064: shell->ops->getdiagonal = (PetscErrorCode (*)(Mat,Vec))f;
1065: mat->ops->getdiagonal = MatGetDiagonal_Shell;
1066: } else {
1067: shell->ops->getdiagonal = NULL;
1068: mat->ops->getdiagonal = (PetscErrorCode (*)(Mat,Vec))f;
1069: }
1070: break;
1071: case MATOP_MULT:
1072: if (shell->managescalingshifts) {
1073: shell->ops->mult = (PetscErrorCode (*)(Mat,Vec,Vec))f;
1074: mat->ops->mult = MatMult_Shell;
1075: } else {
1076: shell->ops->mult = NULL;
1077: mat->ops->mult = (PetscErrorCode (*)(Mat,Vec,Vec))f;
1078: }
1079: break;
1080: case MATOP_MULT_TRANSPOSE:
1081: if (shell->managescalingshifts) {
1082: shell->ops->multtranspose = (PetscErrorCode (*)(Mat,Vec,Vec))f;
1083: mat->ops->multtranspose = MatMultTranspose_Shell;
1084: } else {
1085: shell->ops->multtranspose = NULL;
1086: mat->ops->multtranspose = (PetscErrorCode (*)(Mat,Vec,Vec))f;
1087: }
1088: break;
1089: default:
1090: (((void(**)(void))mat->ops)[op]) = f;
1091: break;
1092: }
1093: return(0);
1094: }
1096: /*@C
1097: MatShellGetOperation - Gets a matrix function for a shell matrix.
1099: Not Collective
1101: Input Parameters:
1102: + mat - the shell matrix
1103: - op - the name of the operation
1105: Output Parameter:
1106: . f - the function that provides the operation.
1108: Level: advanced
1110: Notes:
1111: See the file include/petscmat.h for a complete list of matrix
1112: operations, which all have the form MATOP_<OPERATION>, where
1113: <OPERATION> is the name (in all capital letters) of the
1114: user interface routine (e.g., MatMult() -> MATOP_MULT).
1116: All user-provided functions have the same calling
1117: sequence as the usual matrix interface routines, since they
1118: are intended to be accessed via the usual matrix interface
1119: routines, e.g.,
1120: $ MatMult(Mat,Vec,Vec) -> usermult(Mat,Vec,Vec)
1122: Within each user-defined routine, the user should call
1123: MatShellGetContext() to obtain the user-defined context that was
1124: set by MatCreateShell().
1126: .keywords: matrix, shell, set, operation
1128: .seealso: MatCreateShell(), MatShellGetContext(), MatShellSetOperation(), MatShellSetContext()
1129: @*/
1130: PetscErrorCode MatShellGetOperation(Mat mat,MatOperation op,void(**f)(void))
1131: {
1132: PetscBool flg;
1133: Mat_Shell *shell;
1138: PetscObjectTypeCompare((PetscObject)mat,MATSHELL,&flg);
1139: if (!flg) SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_SUP,"Can only use with MATSHELL matrices");
1140: shell = (Mat_Shell*)mat->data;
1142: switch (op) {
1143: case MATOP_DESTROY:
1144: *f = (void (*)(void))shell->ops->destroy;
1145: break;
1146: case MATOP_VIEW:
1147: *f = (void (*)(void))mat->ops->view;
1148: break;
1149: case MATOP_COPY:
1150: *f = (void (*)(void))shell->ops->copy;
1151: break;
1152: case MATOP_DIAGONAL_SET:
1153: case MATOP_DIAGONAL_SCALE:
1154: case MATOP_SHIFT:
1155: case MATOP_SCALE:
1156: case MATOP_AXPY:
1157: *f = (((void (**)(void))mat->ops)[op]);
1158: break;
1159: case MATOP_GET_DIAGONAL:
1160: if (shell->ops->getdiagonal)
1161: *f = (void (*)(void))shell->ops->getdiagonal;
1162: else
1163: *f = (((void (**)(void))mat->ops)[op]);
1164: break;
1165: case MATOP_MULT:
1166: if (shell->ops->mult)
1167: *f = (void (*)(void))shell->ops->mult;
1168: else
1169: *f = (((void (**)(void))mat->ops)[op]);
1170: break;
1171: case MATOP_MULT_TRANSPOSE:
1172: if (shell->ops->multtranspose)
1173: *f = (void (*)(void))shell->ops->multtranspose;
1174: else
1175: *f = (((void (**)(void))mat->ops)[op]);
1176: break;
1177: default:
1178: *f = (((void (**)(void))mat->ops)[op]);
1179: }
1180: return(0);
1181: }