xref: /petsc/src/mat/impls/shell/shell.c (revision d5b43468fb8780a8feea140ccd6fa3e6a50411cc)
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 struct _MatShellOps {
12   /*  3 */ PetscErrorCode (*mult)(Mat, Vec, Vec);
13   /*  5 */ PetscErrorCode (*multtranspose)(Mat, Vec, Vec);
14   /* 17 */ PetscErrorCode (*getdiagonal)(Mat, Vec);
15   /* 43 */ PetscErrorCode (*copy)(Mat, Mat, MatStructure);
16   /* 60 */ PetscErrorCode (*destroy)(Mat);
17 };
18 
19 struct _n_MatShellMatFunctionList {
20   PetscErrorCode (*symbolic)(Mat, Mat, Mat, void **);
21   PetscErrorCode (*numeric)(Mat, Mat, Mat, void *);
22   PetscErrorCode (*destroy)(void *);
23   MatProductType ptype;
24   char          *composedname; /* string to identify routine with double dispatch */
25   char          *resultname;   /* result matrix type */
26 
27   struct _n_MatShellMatFunctionList *next;
28 };
29 typedef struct _n_MatShellMatFunctionList *MatShellMatFunctionList;
30 
31 typedef struct {
32   struct _MatShellOps ops[1];
33 
34   /* The user will manage the scaling and shifts for the MATSHELL, not the default */
35   PetscBool managescalingshifts;
36 
37   /* support for MatScale, MatShift and MatMultAdd */
38   PetscScalar vscale, vshift;
39   Vec         dshift;
40   Vec         left, right;
41   Vec         left_work, right_work;
42   Vec         left_add_work, right_add_work;
43 
44   /* support for MatAXPY */
45   Mat              axpy;
46   PetscScalar      axpy_vscale;
47   Vec              axpy_left, axpy_right;
48   PetscObjectState axpy_state;
49 
50   /* support for ZeroRows/Columns operations */
51   IS         zrows;
52   IS         zcols;
53   Vec        zvals;
54   Vec        zvals_w;
55   VecScatter zvals_sct_r;
56   VecScatter zvals_sct_c;
57 
58   /* MatMat operations */
59   MatShellMatFunctionList matmat;
60 
61   /* user defined context */
62   PetscContainer ctxcontainer;
63 } Mat_Shell;
64 
65 /*
66      Store and scale values on zeroed rows
67      xx = [x_1, 0], 0 on zeroed columns
68 */
69 static PetscErrorCode MatShellPreZeroRight(Mat A, Vec x, Vec *xx)
70 {
71   Mat_Shell *shell = (Mat_Shell *)A->data;
72 
73   PetscFunctionBegin;
74   *xx = x;
75   if (shell->zrows) {
76     PetscCall(VecSet(shell->zvals_w, 0.0));
77     PetscCall(VecScatterBegin(shell->zvals_sct_c, x, shell->zvals_w, INSERT_VALUES, SCATTER_FORWARD));
78     PetscCall(VecScatterEnd(shell->zvals_sct_c, x, shell->zvals_w, INSERT_VALUES, SCATTER_FORWARD));
79     PetscCall(VecPointwiseMult(shell->zvals_w, shell->zvals_w, shell->zvals));
80   }
81   if (shell->zcols) {
82     if (!shell->right_work) PetscCall(MatCreateVecs(A, &shell->right_work, NULL));
83     PetscCall(VecCopy(x, shell->right_work));
84     PetscCall(VecISSet(shell->right_work, shell->zcols, 0.0));
85     *xx = shell->right_work;
86   }
87   PetscFunctionReturn(0);
88 }
89 
90 /* Insert properly diagonally scaled values stored in MatShellPreZeroRight */
91 static PetscErrorCode MatShellPostZeroLeft(Mat A, Vec x)
92 {
93   Mat_Shell *shell = (Mat_Shell *)A->data;
94 
95   PetscFunctionBegin;
96   if (shell->zrows) {
97     PetscCall(VecScatterBegin(shell->zvals_sct_r, shell->zvals_w, x, INSERT_VALUES, SCATTER_REVERSE));
98     PetscCall(VecScatterEnd(shell->zvals_sct_r, shell->zvals_w, x, INSERT_VALUES, SCATTER_REVERSE));
99   }
100   PetscFunctionReturn(0);
101 }
102 
103 /*
104      Store and scale values on zeroed rows
105      xx = [x_1, 0], 0 on zeroed rows
106 */
107 static PetscErrorCode MatShellPreZeroLeft(Mat A, Vec x, Vec *xx)
108 {
109   Mat_Shell *shell = (Mat_Shell *)A->data;
110 
111   PetscFunctionBegin;
112   *xx = NULL;
113   if (!shell->zrows) {
114     *xx = x;
115   } else {
116     if (!shell->left_work) PetscCall(MatCreateVecs(A, NULL, &shell->left_work));
117     PetscCall(VecCopy(x, shell->left_work));
118     PetscCall(VecSet(shell->zvals_w, 0.0));
119     PetscCall(VecScatterBegin(shell->zvals_sct_r, shell->zvals_w, shell->left_work, INSERT_VALUES, SCATTER_REVERSE));
120     PetscCall(VecScatterEnd(shell->zvals_sct_r, shell->zvals_w, shell->left_work, INSERT_VALUES, SCATTER_REVERSE));
121     PetscCall(VecScatterBegin(shell->zvals_sct_r, x, shell->zvals_w, INSERT_VALUES, SCATTER_FORWARD));
122     PetscCall(VecScatterEnd(shell->zvals_sct_r, x, shell->zvals_w, INSERT_VALUES, SCATTER_FORWARD));
123     PetscCall(VecPointwiseMult(shell->zvals_w, shell->zvals_w, shell->zvals));
124     *xx = shell->left_work;
125   }
126   PetscFunctionReturn(0);
127 }
128 
129 /* Zero zero-columns contributions, sum contributions from properly scaled values stored in MatShellPreZeroLeft */
130 static PetscErrorCode MatShellPostZeroRight(Mat A, Vec x)
131 {
132   Mat_Shell *shell = (Mat_Shell *)A->data;
133 
134   PetscFunctionBegin;
135   if (shell->zcols) PetscCall(VecISSet(x, shell->zcols, 0.0));
136   if (shell->zrows) {
137     PetscCall(VecScatterBegin(shell->zvals_sct_c, shell->zvals_w, x, ADD_VALUES, SCATTER_REVERSE));
138     PetscCall(VecScatterEnd(shell->zvals_sct_c, shell->zvals_w, x, ADD_VALUES, SCATTER_REVERSE));
139   }
140   PetscFunctionReturn(0);
141 }
142 
143 /*
144       xx = diag(left)*x
145 */
146 static PetscErrorCode MatShellPreScaleLeft(Mat A, Vec x, Vec *xx)
147 {
148   Mat_Shell *shell = (Mat_Shell *)A->data;
149 
150   PetscFunctionBegin;
151   *xx = NULL;
152   if (!shell->left) {
153     *xx = x;
154   } else {
155     if (!shell->left_work) PetscCall(VecDuplicate(shell->left, &shell->left_work));
156     PetscCall(VecPointwiseMult(shell->left_work, x, shell->left));
157     *xx = shell->left_work;
158   }
159   PetscFunctionReturn(0);
160 }
161 
162 /*
163      xx = diag(right)*x
164 */
165 static PetscErrorCode MatShellPreScaleRight(Mat A, Vec x, Vec *xx)
166 {
167   Mat_Shell *shell = (Mat_Shell *)A->data;
168 
169   PetscFunctionBegin;
170   *xx = NULL;
171   if (!shell->right) {
172     *xx = x;
173   } else {
174     if (!shell->right_work) PetscCall(VecDuplicate(shell->right, &shell->right_work));
175     PetscCall(VecPointwiseMult(shell->right_work, x, shell->right));
176     *xx = shell->right_work;
177   }
178   PetscFunctionReturn(0);
179 }
180 
181 /*
182     x = diag(left)*x
183 */
184 static PetscErrorCode MatShellPostScaleLeft(Mat A, Vec x)
185 {
186   Mat_Shell *shell = (Mat_Shell *)A->data;
187 
188   PetscFunctionBegin;
189   if (shell->left) PetscCall(VecPointwiseMult(x, x, shell->left));
190   PetscFunctionReturn(0);
191 }
192 
193 /*
194     x = diag(right)*x
195 */
196 static PetscErrorCode MatShellPostScaleRight(Mat A, Vec x)
197 {
198   Mat_Shell *shell = (Mat_Shell *)A->data;
199 
200   PetscFunctionBegin;
201   if (shell->right) PetscCall(VecPointwiseMult(x, x, shell->right));
202   PetscFunctionReturn(0);
203 }
204 
205 /*
206          Y = vscale*Y + diag(dshift)*X + vshift*X
207 
208          On input Y already contains A*x
209 */
210 static PetscErrorCode MatShellShiftAndScale(Mat A, Vec X, Vec Y)
211 {
212   Mat_Shell *shell = (Mat_Shell *)A->data;
213 
214   PetscFunctionBegin;
215   if (shell->dshift) { /* get arrays because there is no VecPointwiseMultAdd() */
216     PetscInt           i, m;
217     const PetscScalar *x, *d;
218     PetscScalar       *y;
219     PetscCall(VecGetLocalSize(X, &m));
220     PetscCall(VecGetArrayRead(shell->dshift, &d));
221     PetscCall(VecGetArrayRead(X, &x));
222     PetscCall(VecGetArray(Y, &y));
223     for (i = 0; i < m; i++) y[i] = shell->vscale * y[i] + d[i] * x[i];
224     PetscCall(VecRestoreArrayRead(shell->dshift, &d));
225     PetscCall(VecRestoreArrayRead(X, &x));
226     PetscCall(VecRestoreArray(Y, &y));
227   } else {
228     PetscCall(VecScale(Y, shell->vscale));
229   }
230   if (shell->vshift != 0.0) PetscCall(VecAXPY(Y, shell->vshift, X)); /* if test is for non-square matrices */
231   PetscFunctionReturn(0);
232 }
233 
234 static PetscErrorCode MatShellGetContext_Shell(Mat mat, void *ctx)
235 {
236   Mat_Shell *shell = (Mat_Shell *)mat->data;
237 
238   PetscFunctionBegin;
239   if (shell->ctxcontainer) PetscCall(PetscContainerGetPointer(shell->ctxcontainer, (void **)ctx));
240   else *(void **)ctx = NULL;
241   PetscFunctionReturn(0);
242 }
243 
244 /*@
245     MatShellGetContext - Returns the user-provided context associated with a `MATSHELL` shell matrix.
246 
247     Not Collective
248 
249     Input Parameter:
250 .   mat - the matrix, should have been created with `MatCreateShell()`
251 
252     Output Parameter:
253 .   ctx - the user provided context
254 
255     Level: advanced
256 
257    Fortran Note:
258     To use this from Fortran you must write a Fortran interface definition for this
259     function that tells Fortran the Fortran derived data type that you are passing in as the ctx argument.
260 
261 .seealso: `MATSHELL`, `MatCreateShell()`, `MatShellSetOperation()`, `MatShellSetContext()`
262 @*/
263 PetscErrorCode MatShellGetContext(Mat mat, void *ctx)
264 {
265   PetscFunctionBegin;
266   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
267   PetscValidPointer(ctx, 2);
268   PetscUseMethod(mat, "MatShellGetContext_C", (Mat, void *), (mat, ctx));
269   PetscFunctionReturn(0);
270 }
271 
272 static PetscErrorCode MatZeroRowsColumns_Local_Shell(Mat mat, PetscInt nr, PetscInt rows[], PetscInt nc, PetscInt cols[], PetscScalar diag, PetscBool rc)
273 {
274   Mat_Shell      *shell = (Mat_Shell *)mat->data;
275   Vec             x = NULL, b = NULL;
276   IS              is1, is2;
277   const PetscInt *ridxs;
278   PetscInt       *idxs, *gidxs;
279   PetscInt        cum, rst, cst, i;
280 
281   PetscFunctionBegin;
282   if (!shell->zvals) PetscCall(MatCreateVecs(mat, NULL, &shell->zvals));
283   if (!shell->zvals_w) PetscCall(VecDuplicate(shell->zvals, &shell->zvals_w));
284   PetscCall(MatGetOwnershipRange(mat, &rst, NULL));
285   PetscCall(MatGetOwnershipRangeColumn(mat, &cst, NULL));
286 
287   /* Expand/create index set of zeroed rows */
288   PetscCall(PetscMalloc1(nr, &idxs));
289   for (i = 0; i < nr; i++) idxs[i] = rows[i] + rst;
290   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, nr, idxs, PETSC_OWN_POINTER, &is1));
291   PetscCall(ISSort(is1));
292   PetscCall(VecISSet(shell->zvals, is1, diag));
293   if (shell->zrows) {
294     PetscCall(ISSum(shell->zrows, is1, &is2));
295     PetscCall(ISDestroy(&shell->zrows));
296     PetscCall(ISDestroy(&is1));
297     shell->zrows = is2;
298   } else shell->zrows = is1;
299 
300   /* Create scatters for diagonal values communications */
301   PetscCall(VecScatterDestroy(&shell->zvals_sct_c));
302   PetscCall(VecScatterDestroy(&shell->zvals_sct_r));
303 
304   /* row scatter: from/to left vector */
305   PetscCall(MatCreateVecs(mat, &x, &b));
306   PetscCall(VecScatterCreate(b, shell->zrows, shell->zvals_w, shell->zrows, &shell->zvals_sct_r));
307 
308   /* col scatter: from right vector to left vector */
309   PetscCall(ISGetIndices(shell->zrows, &ridxs));
310   PetscCall(ISGetLocalSize(shell->zrows, &nr));
311   PetscCall(PetscMalloc1(nr, &gidxs));
312   for (i = 0, cum = 0; i < nr; i++) {
313     if (ridxs[i] >= mat->cmap->N) continue;
314     gidxs[cum] = ridxs[i];
315     cum++;
316   }
317   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)mat), cum, gidxs, PETSC_OWN_POINTER, &is1));
318   PetscCall(VecScatterCreate(x, is1, shell->zvals_w, is1, &shell->zvals_sct_c));
319   PetscCall(ISDestroy(&is1));
320   PetscCall(VecDestroy(&x));
321   PetscCall(VecDestroy(&b));
322 
323   /* Expand/create index set of zeroed columns */
324   if (rc) {
325     PetscCall(PetscMalloc1(nc, &idxs));
326     for (i = 0; i < nc; i++) idxs[i] = cols[i] + cst;
327     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, nc, idxs, PETSC_OWN_POINTER, &is1));
328     PetscCall(ISSort(is1));
329     if (shell->zcols) {
330       PetscCall(ISSum(shell->zcols, is1, &is2));
331       PetscCall(ISDestroy(&shell->zcols));
332       PetscCall(ISDestroy(&is1));
333       shell->zcols = is2;
334     } else shell->zcols = is1;
335   }
336   PetscFunctionReturn(0);
337 }
338 
339 static PetscErrorCode MatZeroRows_Shell(Mat mat, PetscInt n, const PetscInt rows[], PetscScalar diag, Vec x, Vec b)
340 {
341   Mat_Shell *shell = (Mat_Shell *)mat->data;
342   PetscInt   nr, *lrows;
343 
344   PetscFunctionBegin;
345   if (x && b) {
346     Vec          xt;
347     PetscScalar *vals;
348     PetscInt    *gcols, i, st, nl, nc;
349 
350     PetscCall(PetscMalloc1(n, &gcols));
351     for (i = 0, nc = 0; i < n; i++)
352       if (rows[i] < mat->cmap->N) gcols[nc++] = rows[i];
353 
354     PetscCall(MatCreateVecs(mat, &xt, NULL));
355     PetscCall(VecCopy(x, xt));
356     PetscCall(PetscCalloc1(nc, &vals));
357     PetscCall(VecSetValues(xt, nc, gcols, vals, INSERT_VALUES)); /* xt = [x1, 0] */
358     PetscCall(PetscFree(vals));
359     PetscCall(VecAssemblyBegin(xt));
360     PetscCall(VecAssemblyEnd(xt));
361     PetscCall(VecAYPX(xt, -1.0, x)); /* xt = [0, x2] */
362 
363     PetscCall(VecGetOwnershipRange(xt, &st, NULL));
364     PetscCall(VecGetLocalSize(xt, &nl));
365     PetscCall(VecGetArray(xt, &vals));
366     for (i = 0; i < nl; i++) {
367       PetscInt g = i + st;
368       if (g > mat->rmap->N) continue;
369       if (PetscAbsScalar(vals[i]) == 0.0) continue;
370       PetscCall(VecSetValue(b, g, diag * vals[i], INSERT_VALUES));
371     }
372     PetscCall(VecRestoreArray(xt, &vals));
373     PetscCall(VecAssemblyBegin(b));
374     PetscCall(VecAssemblyEnd(b)); /* b  = [b1, x2 * diag] */
375     PetscCall(VecDestroy(&xt));
376     PetscCall(PetscFree(gcols));
377   }
378   PetscCall(PetscLayoutMapLocal(mat->rmap, n, rows, &nr, &lrows, NULL));
379   PetscCall(MatZeroRowsColumns_Local_Shell(mat, nr, lrows, 0, NULL, diag, PETSC_FALSE));
380   if (shell->axpy) PetscCall(MatZeroRows(shell->axpy, n, rows, 0.0, NULL, NULL));
381   PetscCall(PetscFree(lrows));
382   PetscFunctionReturn(0);
383 }
384 
385 static PetscErrorCode MatZeroRowsColumns_Shell(Mat mat, PetscInt n, const PetscInt rowscols[], PetscScalar diag, Vec x, Vec b)
386 {
387   Mat_Shell *shell = (Mat_Shell *)mat->data;
388   PetscInt  *lrows, *lcols;
389   PetscInt   nr, nc;
390   PetscBool  congruent;
391 
392   PetscFunctionBegin;
393   if (x && b) {
394     Vec          xt, bt;
395     PetscScalar *vals;
396     PetscInt    *grows, *gcols, i, st, nl;
397 
398     PetscCall(PetscMalloc2(n, &grows, n, &gcols));
399     for (i = 0, nr = 0; i < n; i++)
400       if (rowscols[i] < mat->rmap->N) grows[nr++] = rowscols[i];
401     for (i = 0, nc = 0; i < n; i++)
402       if (rowscols[i] < mat->cmap->N) gcols[nc++] = rowscols[i];
403     PetscCall(PetscCalloc1(n, &vals));
404 
405     PetscCall(MatCreateVecs(mat, &xt, &bt));
406     PetscCall(VecCopy(x, xt));
407     PetscCall(VecSetValues(xt, nc, gcols, vals, INSERT_VALUES)); /* xt = [x1, 0] */
408     PetscCall(VecAssemblyBegin(xt));
409     PetscCall(VecAssemblyEnd(xt));
410     PetscCall(VecAXPY(xt, -1.0, x));                             /* xt = [0, -x2] */
411     PetscCall(MatMult(mat, xt, bt));                             /* bt = [-A12*x2,-A22*x2] */
412     PetscCall(VecSetValues(bt, nr, grows, vals, INSERT_VALUES)); /* bt = [-A12*x2,0] */
413     PetscCall(VecAssemblyBegin(bt));
414     PetscCall(VecAssemblyEnd(bt));
415     PetscCall(VecAXPY(b, 1.0, bt));                              /* b  = [b1 - A12*x2, b2] */
416     PetscCall(VecSetValues(bt, nr, grows, vals, INSERT_VALUES)); /* b  = [b1 - A12*x2, 0] */
417     PetscCall(VecAssemblyBegin(bt));
418     PetscCall(VecAssemblyEnd(bt));
419     PetscCall(PetscFree(vals));
420 
421     PetscCall(VecGetOwnershipRange(xt, &st, NULL));
422     PetscCall(VecGetLocalSize(xt, &nl));
423     PetscCall(VecGetArray(xt, &vals));
424     for (i = 0; i < nl; i++) {
425       PetscInt g = i + st;
426       if (g > mat->rmap->N) continue;
427       if (PetscAbsScalar(vals[i]) == 0.0) continue;
428       PetscCall(VecSetValue(b, g, -diag * vals[i], INSERT_VALUES));
429     }
430     PetscCall(VecRestoreArray(xt, &vals));
431     PetscCall(VecAssemblyBegin(b));
432     PetscCall(VecAssemblyEnd(b)); /* b  = [b1 - A12*x2, x2 * diag] */
433     PetscCall(VecDestroy(&xt));
434     PetscCall(VecDestroy(&bt));
435     PetscCall(PetscFree2(grows, gcols));
436   }
437   PetscCall(PetscLayoutMapLocal(mat->rmap, n, rowscols, &nr, &lrows, NULL));
438   PetscCall(MatHasCongruentLayouts(mat, &congruent));
439   if (congruent) {
440     nc    = nr;
441     lcols = lrows;
442   } else { /* MatZeroRowsColumns implicitly assumes the rowscols indices are for a square matrix, here we handle a more general case */
443     PetscInt i, nt, *t;
444 
445     PetscCall(PetscMalloc1(n, &t));
446     for (i = 0, nt = 0; i < n; i++)
447       if (rowscols[i] < mat->cmap->N) t[nt++] = rowscols[i];
448     PetscCall(PetscLayoutMapLocal(mat->cmap, nt, t, &nc, &lcols, NULL));
449     PetscCall(PetscFree(t));
450   }
451   PetscCall(MatZeroRowsColumns_Local_Shell(mat, nr, lrows, nc, lcols, diag, PETSC_TRUE));
452   if (!congruent) PetscCall(PetscFree(lcols));
453   PetscCall(PetscFree(lrows));
454   if (shell->axpy) PetscCall(MatZeroRowsColumns(shell->axpy, n, rowscols, 0.0, NULL, NULL));
455   PetscFunctionReturn(0);
456 }
457 
458 static PetscErrorCode MatDestroy_Shell(Mat mat)
459 {
460   Mat_Shell              *shell = (Mat_Shell *)mat->data;
461   MatShellMatFunctionList matmat;
462 
463   PetscFunctionBegin;
464   if (shell->ops->destroy) PetscCall((*shell->ops->destroy)(mat));
465   PetscCall(PetscMemzero(shell->ops, sizeof(struct _MatShellOps)));
466   PetscCall(VecDestroy(&shell->left));
467   PetscCall(VecDestroy(&shell->right));
468   PetscCall(VecDestroy(&shell->dshift));
469   PetscCall(VecDestroy(&shell->left_work));
470   PetscCall(VecDestroy(&shell->right_work));
471   PetscCall(VecDestroy(&shell->left_add_work));
472   PetscCall(VecDestroy(&shell->right_add_work));
473   PetscCall(VecDestroy(&shell->axpy_left));
474   PetscCall(VecDestroy(&shell->axpy_right));
475   PetscCall(MatDestroy(&shell->axpy));
476   PetscCall(VecDestroy(&shell->zvals_w));
477   PetscCall(VecDestroy(&shell->zvals));
478   PetscCall(VecScatterDestroy(&shell->zvals_sct_c));
479   PetscCall(VecScatterDestroy(&shell->zvals_sct_r));
480   PetscCall(ISDestroy(&shell->zrows));
481   PetscCall(ISDestroy(&shell->zcols));
482 
483   matmat = shell->matmat;
484   while (matmat) {
485     MatShellMatFunctionList next = matmat->next;
486 
487     PetscCall(PetscObjectComposeFunction((PetscObject)mat, matmat->composedname, NULL));
488     PetscCall(PetscFree(matmat->composedname));
489     PetscCall(PetscFree(matmat->resultname));
490     PetscCall(PetscFree(matmat));
491     matmat = next;
492   }
493   PetscCall(MatShellSetContext(mat, NULL));
494   PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatShellGetContext_C", NULL));
495   PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatShellSetContext_C", NULL));
496   PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatShellSetContextDestroy_C", NULL));
497   PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatShellSetVecType_C", NULL));
498   PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatShellSetManageScalingShifts_C", NULL));
499   PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatShellSetOperation_C", NULL));
500   PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatShellGetOperation_C", NULL));
501   PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatShellSetMatProductOperation_C", NULL));
502   PetscCall(PetscFree(mat->data));
503   PetscFunctionReturn(0);
504 }
505 
506 typedef struct {
507   PetscErrorCode (*numeric)(Mat, Mat, Mat, void *);
508   PetscErrorCode (*destroy)(void *);
509   void *userdata;
510   Mat   B;
511   Mat   Bt;
512   Mat   axpy;
513 } MatMatDataShell;
514 
515 static PetscErrorCode DestroyMatMatDataShell(void *data)
516 {
517   MatMatDataShell *mmdata = (MatMatDataShell *)data;
518 
519   PetscFunctionBegin;
520   if (mmdata->destroy) PetscCall((*mmdata->destroy)(mmdata->userdata));
521   PetscCall(MatDestroy(&mmdata->B));
522   PetscCall(MatDestroy(&mmdata->Bt));
523   PetscCall(MatDestroy(&mmdata->axpy));
524   PetscCall(PetscFree(mmdata));
525   PetscFunctionReturn(0);
526 }
527 
528 static PetscErrorCode MatProductNumeric_Shell_X(Mat D)
529 {
530   Mat_Product     *product;
531   Mat              A, B;
532   MatMatDataShell *mdata;
533   PetscScalar      zero = 0.0;
534 
535   PetscFunctionBegin;
536   MatCheckProduct(D, 1);
537   product = D->product;
538   PetscCheck(product->data, PetscObjectComm((PetscObject)D), PETSC_ERR_PLIB, "Product data empty");
539   A     = product->A;
540   B     = product->B;
541   mdata = (MatMatDataShell *)product->data;
542   if (mdata->numeric) {
543     Mat_Shell *shell                = (Mat_Shell *)A->data;
544     PetscErrorCode (*stashsym)(Mat) = D->ops->productsymbolic;
545     PetscErrorCode (*stashnum)(Mat) = D->ops->productnumeric;
546     PetscBool useBmdata = PETSC_FALSE, newB = PETSC_TRUE;
547 
548     if (shell->managescalingshifts) {
549       PetscCheck(!shell->zcols && !shell->zrows, PetscObjectComm((PetscObject)D), PETSC_ERR_SUP, "MatProduct not supported with zeroed rows/columns");
550       if (shell->right || shell->left) {
551         useBmdata = PETSC_TRUE;
552         if (!mdata->B) {
553           PetscCall(MatDuplicate(B, MAT_SHARE_NONZERO_PATTERN, &mdata->B));
554         } else {
555           newB = PETSC_FALSE;
556         }
557         PetscCall(MatCopy(B, mdata->B, SAME_NONZERO_PATTERN));
558       }
559       switch (product->type) {
560       case MATPRODUCT_AB: /* s L A R B + v L R B + L D R B */
561         if (shell->right) PetscCall(MatDiagonalScale(mdata->B, shell->right, NULL));
562         break;
563       case MATPRODUCT_AtB: /* s R A^t L B + v R L B + R D L B */
564         if (shell->left) PetscCall(MatDiagonalScale(mdata->B, shell->left, NULL));
565         break;
566       case MATPRODUCT_ABt: /* s L A R B^t + v L R B^t + L D R B^t */
567         if (shell->right) PetscCall(MatDiagonalScale(mdata->B, NULL, shell->right));
568         break;
569       case MATPRODUCT_RARt: /* s B L A R B^t + v B L R B^t + B L D R B^t */
570         if (shell->right && shell->left) {
571           PetscBool flg;
572 
573           PetscCall(VecEqual(shell->right, shell->left, &flg));
574           PetscCheck(flg, PetscObjectComm((PetscObject)D), PETSC_ERR_SUP, "MatProductSymbolic type %s not supported for %s and %s matrices because left scaling != from right scaling", MatProductTypes[product->type], ((PetscObject)A)->type_name,
575                      ((PetscObject)B)->type_name);
576         }
577         if (shell->right) PetscCall(MatDiagonalScale(mdata->B, NULL, shell->right));
578         break;
579       case MATPRODUCT_PtAP: /* s B^t L A R B + v B^t L R B + B^t L D R B */
580         if (shell->right && shell->left) {
581           PetscBool flg;
582 
583           PetscCall(VecEqual(shell->right, shell->left, &flg));
584           PetscCheck(flg, PetscObjectComm((PetscObject)D), PETSC_ERR_SUP, "MatProductSymbolic type %s not supported for %s and %s matrices because left scaling != from right scaling", MatProductTypes[product->type], ((PetscObject)A)->type_name,
585                      ((PetscObject)B)->type_name);
586         }
587         if (shell->right) PetscCall(MatDiagonalScale(mdata->B, shell->right, NULL));
588         break;
589       default:
590         SETERRQ(PetscObjectComm((PetscObject)D), PETSC_ERR_SUP, "MatProductSymbolic type %s not supported for %s and %s matrices", MatProductTypes[product->type], ((PetscObject)A)->type_name, ((PetscObject)B)->type_name);
591       }
592     }
593     /* allow the user to call MatMat operations on D */
594     D->product              = NULL;
595     D->ops->productsymbolic = NULL;
596     D->ops->productnumeric  = NULL;
597 
598     PetscCall((*mdata->numeric)(A, useBmdata ? mdata->B : B, D, mdata->userdata));
599 
600     /* clear any leftover user data and restore D pointers */
601     PetscCall(MatProductClear(D));
602     D->ops->productsymbolic = stashsym;
603     D->ops->productnumeric  = stashnum;
604     D->product              = product;
605 
606     if (shell->managescalingshifts) {
607       PetscCall(MatScale(D, shell->vscale));
608       switch (product->type) {
609       case MATPRODUCT_AB:  /* s L A R B + v L R B + L D R B */
610       case MATPRODUCT_ABt: /* s L A R B^t + v L R B^t + L D R B^t */
611         if (shell->left) {
612           PetscCall(MatDiagonalScale(D, shell->left, NULL));
613           if (shell->dshift || shell->vshift != zero) {
614             if (!shell->left_work) PetscCall(MatCreateVecs(A, NULL, &shell->left_work));
615             if (shell->dshift) {
616               PetscCall(VecCopy(shell->dshift, shell->left_work));
617               PetscCall(VecShift(shell->left_work, shell->vshift));
618               PetscCall(VecPointwiseMult(shell->left_work, shell->left_work, shell->left));
619             } else {
620               PetscCall(VecSet(shell->left_work, shell->vshift));
621             }
622             if (product->type == MATPRODUCT_ABt) {
623               MatReuse     reuse = mdata->Bt ? MAT_REUSE_MATRIX : MAT_INITIAL_MATRIX;
624               MatStructure str   = mdata->Bt ? SUBSET_NONZERO_PATTERN : DIFFERENT_NONZERO_PATTERN;
625 
626               PetscCall(MatTranspose(mdata->B, reuse, &mdata->Bt));
627               PetscCall(MatDiagonalScale(mdata->Bt, shell->left_work, NULL));
628               PetscCall(MatAXPY(D, 1.0, mdata->Bt, str));
629             } else {
630               MatStructure str = newB ? DIFFERENT_NONZERO_PATTERN : SUBSET_NONZERO_PATTERN;
631 
632               PetscCall(MatDiagonalScale(mdata->B, shell->left_work, NULL));
633               PetscCall(MatAXPY(D, 1.0, mdata->B, str));
634             }
635           }
636         }
637         break;
638       case MATPRODUCT_AtB: /* s R A^t L B + v R L B + R D L B */
639         if (shell->right) {
640           PetscCall(MatDiagonalScale(D, shell->right, NULL));
641           if (shell->dshift || shell->vshift != zero) {
642             MatStructure str = newB ? DIFFERENT_NONZERO_PATTERN : SUBSET_NONZERO_PATTERN;
643 
644             if (!shell->right_work) PetscCall(MatCreateVecs(A, &shell->right_work, NULL));
645             if (shell->dshift) {
646               PetscCall(VecCopy(shell->dshift, shell->right_work));
647               PetscCall(VecShift(shell->right_work, shell->vshift));
648               PetscCall(VecPointwiseMult(shell->right_work, shell->right_work, shell->right));
649             } else {
650               PetscCall(VecSet(shell->right_work, shell->vshift));
651             }
652             PetscCall(MatDiagonalScale(mdata->B, shell->right_work, NULL));
653             PetscCall(MatAXPY(D, 1.0, mdata->B, str));
654           }
655         }
656         break;
657       case MATPRODUCT_PtAP: /* s B^t L A R B + v B^t L R B + B^t L D R B */
658       case MATPRODUCT_RARt: /* s B L A R B^t + v B L R B^t + B L D R B^t */
659         PetscCheck(!shell->dshift && shell->vshift == zero, PetscObjectComm((PetscObject)D), PETSC_ERR_SUP, "MatProductSymbolic type %s not supported for %s and %s matrices with diagonal shift", MatProductTypes[product->type], ((PetscObject)A)->type_name,
660                    ((PetscObject)B)->type_name);
661         break;
662       default:
663         SETERRQ(PetscObjectComm((PetscObject)D), PETSC_ERR_SUP, "MatProductSymbolic type %s not supported for %s and %s matrices", MatProductTypes[product->type], ((PetscObject)A)->type_name, ((PetscObject)B)->type_name);
664       }
665       if (shell->axpy && shell->axpy_vscale != zero) {
666         Mat              X;
667         PetscObjectState axpy_state;
668         MatStructure     str = DIFFERENT_NONZERO_PATTERN; /* not sure it is safe to ever use SUBSET_NONZERO_PATTERN */
669 
670         PetscCall(MatShellGetContext(shell->axpy, &X));
671         PetscCall(PetscObjectStateGet((PetscObject)X, &axpy_state));
672         PetscCheck(shell->axpy_state == axpy_state, PetscObjectComm((PetscObject)A), PETSC_ERR_ORDER, "Invalid AXPY state: cannot modify the X matrix passed to MatAXPY(Y,a,X,...)");
673         if (!mdata->axpy) {
674           str = DIFFERENT_NONZERO_PATTERN;
675           PetscCall(MatProductCreate(shell->axpy, B, NULL, &mdata->axpy));
676           PetscCall(MatProductSetType(mdata->axpy, product->type));
677           PetscCall(MatProductSetFromOptions(mdata->axpy));
678           PetscCall(MatProductSymbolic(mdata->axpy));
679         } else { /* May be that shell->axpy has changed */
680           PetscBool flg;
681 
682           PetscCall(MatProductReplaceMats(shell->axpy, B, NULL, mdata->axpy));
683           PetscCall(MatHasOperation(mdata->axpy, MATOP_PRODUCTSYMBOLIC, &flg));
684           if (!flg) {
685             str = DIFFERENT_NONZERO_PATTERN;
686             PetscCall(MatProductSetFromOptions(mdata->axpy));
687             PetscCall(MatProductSymbolic(mdata->axpy));
688           }
689         }
690         PetscCall(MatProductNumeric(mdata->axpy));
691         PetscCall(MatAXPY(D, shell->axpy_vscale, mdata->axpy, str));
692       }
693     }
694   } else SETERRQ(PetscObjectComm((PetscObject)D), PETSC_ERR_PLIB, "Missing numeric operation");
695   PetscFunctionReturn(0);
696 }
697 
698 static PetscErrorCode MatProductSymbolic_Shell_X(Mat D)
699 {
700   Mat_Product            *product;
701   Mat                     A, B;
702   MatShellMatFunctionList matmat;
703   Mat_Shell              *shell;
704   PetscBool               flg;
705   char                    composedname[256];
706   MatMatDataShell        *mdata;
707 
708   PetscFunctionBegin;
709   MatCheckProduct(D, 1);
710   product = D->product;
711   PetscCheck(!product->data, PetscObjectComm((PetscObject)D), PETSC_ERR_PLIB, "Product data not empty");
712   A      = product->A;
713   B      = product->B;
714   shell  = (Mat_Shell *)A->data;
715   matmat = shell->matmat;
716   PetscCall(PetscSNPrintf(composedname, sizeof(composedname), "MatProductSetFromOptions_%s_%s_C", ((PetscObject)A)->type_name, ((PetscObject)B)->type_name));
717   while (matmat) {
718     PetscCall(PetscStrcmp(composedname, matmat->composedname, &flg));
719     flg = (PetscBool)(flg && (matmat->ptype == product->type));
720     if (flg) break;
721     matmat = matmat->next;
722   }
723   PetscCheck(flg, PetscObjectComm((PetscObject)A), PETSC_ERR_PLIB, "Composedname \"%s\" for product type %s not found", composedname, MatProductTypes[product->type]);
724   switch (product->type) {
725   case MATPRODUCT_AB:
726     PetscCall(MatSetSizes(D, A->rmap->n, B->cmap->n, A->rmap->N, B->cmap->N));
727     break;
728   case MATPRODUCT_AtB:
729     PetscCall(MatSetSizes(D, A->cmap->n, B->cmap->n, A->cmap->N, B->cmap->N));
730     break;
731   case MATPRODUCT_ABt:
732     PetscCall(MatSetSizes(D, A->rmap->n, B->rmap->n, A->rmap->N, B->rmap->N));
733     break;
734   case MATPRODUCT_RARt:
735     PetscCall(MatSetSizes(D, B->rmap->n, B->rmap->n, B->rmap->N, B->rmap->N));
736     break;
737   case MATPRODUCT_PtAP:
738     PetscCall(MatSetSizes(D, B->cmap->n, B->cmap->n, B->cmap->N, B->cmap->N));
739     break;
740   default:
741     SETERRQ(PetscObjectComm((PetscObject)D), PETSC_ERR_SUP, "MatProductSymbolic type %s not supported for %s and %s matrices", MatProductTypes[product->type], ((PetscObject)A)->type_name, ((PetscObject)B)->type_name);
742   }
743   /* respect users who passed in a matrix for which resultname is the base type */
744   if (matmat->resultname) {
745     PetscCall(PetscObjectBaseTypeCompare((PetscObject)D, matmat->resultname, &flg));
746     if (!flg) PetscCall(MatSetType(D, matmat->resultname));
747   }
748   /* If matrix type was not set or different, we need to reset this pointers */
749   D->ops->productsymbolic = MatProductSymbolic_Shell_X;
750   D->ops->productnumeric  = MatProductNumeric_Shell_X;
751   /* attach product data */
752   PetscCall(PetscNew(&mdata));
753   mdata->numeric = matmat->numeric;
754   mdata->destroy = matmat->destroy;
755   if (matmat->symbolic) {
756     PetscCall((*matmat->symbolic)(A, B, D, &mdata->userdata));
757   } else { /* call general setup if symbolic operation not provided */
758     PetscCall(MatSetUp(D));
759   }
760   PetscCheck(D->product, PetscObjectComm((PetscObject)D), PETSC_ERR_COR, "Product disappeared after user symbolic phase");
761   PetscCheck(!D->product->data, PetscObjectComm((PetscObject)D), PETSC_ERR_COR, "Product data not empty after user symbolic phase");
762   D->product->data    = mdata;
763   D->product->destroy = DestroyMatMatDataShell;
764   /* Be sure to reset these pointers if the user did something unexpected */
765   D->ops->productsymbolic = MatProductSymbolic_Shell_X;
766   D->ops->productnumeric  = MatProductNumeric_Shell_X;
767   PetscFunctionReturn(0);
768 }
769 
770 static PetscErrorCode MatProductSetFromOptions_Shell_X(Mat D)
771 {
772   Mat_Product            *product;
773   Mat                     A, B;
774   MatShellMatFunctionList matmat;
775   Mat_Shell              *shell;
776   PetscBool               flg;
777   char                    composedname[256];
778 
779   PetscFunctionBegin;
780   MatCheckProduct(D, 1);
781   product = D->product;
782   A       = product->A;
783   B       = product->B;
784   PetscCall(MatIsShell(A, &flg));
785   if (!flg) PetscFunctionReturn(0);
786   shell  = (Mat_Shell *)A->data;
787   matmat = shell->matmat;
788   PetscCall(PetscSNPrintf(composedname, sizeof(composedname), "MatProductSetFromOptions_%s_%s_C", ((PetscObject)A)->type_name, ((PetscObject)B)->type_name));
789   while (matmat) {
790     PetscCall(PetscStrcmp(composedname, matmat->composedname, &flg));
791     flg = (PetscBool)(flg && (matmat->ptype == product->type));
792     if (flg) break;
793     matmat = matmat->next;
794   }
795   if (flg) {
796     D->ops->productsymbolic = MatProductSymbolic_Shell_X;
797   } else PetscCall(PetscInfo(D, "  symbolic product %s not registered for product type %s\n", composedname, MatProductTypes[product->type]));
798   PetscFunctionReturn(0);
799 }
800 
801 static PetscErrorCode MatShellSetMatProductOperation_Private(Mat A, MatProductType ptype, PetscErrorCode (*symbolic)(Mat, Mat, Mat, void **), PetscErrorCode (*numeric)(Mat, Mat, Mat, void *), PetscErrorCode (*destroy)(void *), char *composedname, const char *resultname)
802 {
803   PetscBool               flg;
804   Mat_Shell              *shell;
805   MatShellMatFunctionList matmat;
806 
807   PetscFunctionBegin;
808   PetscCheck(numeric, PetscObjectComm((PetscObject)A), PETSC_ERR_PLIB, "Missing numeric routine");
809   PetscCheck(composedname, PetscObjectComm((PetscObject)A), PETSC_ERR_PLIB, "Missing composed name");
810 
811   /* add product callback */
812   shell  = (Mat_Shell *)A->data;
813   matmat = shell->matmat;
814   if (!matmat) {
815     PetscCall(PetscNew(&shell->matmat));
816     matmat = shell->matmat;
817   } else {
818     MatShellMatFunctionList entry = matmat;
819     while (entry) {
820       PetscCall(PetscStrcmp(composedname, entry->composedname, &flg));
821       flg    = (PetscBool)(flg && (entry->ptype == ptype));
822       matmat = entry;
823       if (flg) goto set;
824       entry = entry->next;
825     }
826     PetscCall(PetscNew(&matmat->next));
827     matmat = matmat->next;
828   }
829 
830 set:
831   matmat->symbolic = symbolic;
832   matmat->numeric  = numeric;
833   matmat->destroy  = destroy;
834   matmat->ptype    = ptype;
835   PetscCall(PetscFree(matmat->composedname));
836   PetscCall(PetscFree(matmat->resultname));
837   PetscCall(PetscStrallocpy(composedname, &matmat->composedname));
838   PetscCall(PetscStrallocpy(resultname, &matmat->resultname));
839   PetscCall(PetscInfo(A, "Composing %s for product type %s with result %s\n", matmat->composedname, MatProductTypes[matmat->ptype], matmat->resultname ? matmat->resultname : "not specified"));
840   PetscCall(PetscObjectComposeFunction((PetscObject)A, matmat->composedname, MatProductSetFromOptions_Shell_X));
841   PetscFunctionReturn(0);
842 }
843 
844 /*@C
845     MatShellSetMatProductOperation - Allows user to set a matrix matrix operation for a `MATSHELL` shell matrix.
846 
847    Logically Collective on A
848 
849     Input Parameters:
850 +   A - the `MATSHELL` shell matrix
851 .   ptype - the product type
852 .   symbolic - the function for the symbolic phase (can be NULL)
853 .   numeric - the function for the numerical phase
854 .   destroy - the function for the destruction of the needed data generated during the symbolic phase (can be NULL)
855 .   Btype - the matrix type for the matrix to be multiplied against
856 -   Ctype - the matrix type for the result (can be NULL)
857 
858    Level: advanced
859 
860     Usage:
861 $      extern PetscErrorCode usersymbolic(Mat,Mat,Mat,void**);
862 $      extern PetscErrorCode usernumeric(Mat,Mat,Mat,void*);
863 $      extern PetscErrorCode userdestroy(void*);
864 $      MatCreateShell(comm,m,n,M,N,ctx,&A);
865 $      MatShellSetMatProductOperation(A,MATPRODUCT_AB,usersymbolic,usernumeric,userdestroy,MATSEQAIJ,MATDENSE);
866 $      [ create B of type SEQAIJ etc..]
867 $      MatProductCreate(A,B,NULL,&C);
868 $      MatProductSetType(C,MATPRODUCT_AB);
869 $      MatProductSetFromOptions(C);
870 $      MatProductSymbolic(C); -> actually runs the user defined symbolic operation
871 $      MatProductNumeric(C); -> actually runs the user defined numeric operation
872 $      [ use C = A*B ]
873 
874     Notes:
875     `MATPRODUCT_ABC` is not supported yet. Not supported in Fortran.
876 
877     If the symbolic phase is not specified, `MatSetUp()` is called on the result matrix that must have its type set if Ctype is NULL.
878 
879     Any additional data needed by the matrix product needs to be returned during the symbolic phase and destroyed with the destroy callback.
880     PETSc will take care of calling the user-defined callbacks.
881     It is allowed to specify the same callbacks for different Btype matrix types.
882     The couple (Btype,ptype) uniquely identifies the operation: the last specified callbacks takes precedence.
883 
884 .seealso: `MATSHELL`, `MatCreateShell()`, `MatShellGetContext()`, `MatShellGetOperation()`, `MatShellSetContext()`, `MatSetOperation()`, `MatProductType`, `MatType`, `MatSetUp()`
885 @*/
886 PetscErrorCode MatShellSetMatProductOperation(Mat A, MatProductType ptype, PetscErrorCode (*symbolic)(Mat, Mat, Mat, void **), PetscErrorCode (*numeric)(Mat, Mat, Mat, void *), PetscErrorCode (*destroy)(void *), MatType Btype, MatType Ctype)
887 {
888   PetscFunctionBegin;
889   PetscValidHeaderSpecific(A, MAT_CLASSID, 1);
890   PetscValidLogicalCollectiveEnum(A, ptype, 2);
891   PetscCheck(ptype != MATPRODUCT_ABC, PetscObjectComm((PetscObject)A), PETSC_ERR_SUP, "Not for product type %s", MatProductTypes[ptype]);
892   PetscCheck(numeric, PetscObjectComm((PetscObject)A), PETSC_ERR_USER, "Missing numeric routine, argument 4");
893   PetscValidCharPointer(Btype, 6);
894   if (Ctype) PetscValidCharPointer(Ctype, 7);
895   PetscTryMethod(A, "MatShellSetMatProductOperation_C", (Mat, MatProductType, PetscErrorCode(*)(Mat, Mat, Mat, void **), PetscErrorCode(*)(Mat, Mat, Mat, void *), PetscErrorCode(*)(void *), MatType, MatType), (A, ptype, symbolic, numeric, destroy, Btype, Ctype));
896   PetscFunctionReturn(0);
897 }
898 
899 static PetscErrorCode MatShellSetMatProductOperation_Shell(Mat A, MatProductType ptype, PetscErrorCode (*symbolic)(Mat, Mat, Mat, void **), PetscErrorCode (*numeric)(Mat, Mat, Mat, void *), PetscErrorCode (*destroy)(void *), MatType Btype, MatType Ctype)
900 {
901   PetscBool   flg;
902   char        composedname[256];
903   MatRootName Bnames = MatRootNameList, Cnames = MatRootNameList;
904   PetscMPIInt size;
905 
906   PetscFunctionBegin;
907   PetscValidType(A, 1);
908   while (Bnames) { /* user passed in the root name */
909     PetscCall(PetscStrcmp(Btype, Bnames->rname, &flg));
910     if (flg) break;
911     Bnames = Bnames->next;
912   }
913   while (Cnames) { /* user passed in the root name */
914     PetscCall(PetscStrcmp(Ctype, Cnames->rname, &flg));
915     if (flg) break;
916     Cnames = Cnames->next;
917   }
918   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)A), &size));
919   Btype = Bnames ? (size > 1 ? Bnames->mname : Bnames->sname) : Btype;
920   Ctype = Cnames ? (size > 1 ? Cnames->mname : Cnames->sname) : Ctype;
921   PetscCall(PetscSNPrintf(composedname, sizeof(composedname), "MatProductSetFromOptions_%s_%s_C", ((PetscObject)A)->type_name, Btype));
922   PetscCall(MatShellSetMatProductOperation_Private(A, ptype, symbolic, numeric, destroy, composedname, Ctype));
923   PetscFunctionReturn(0);
924 }
925 
926 static PetscErrorCode MatCopy_Shell(Mat A, Mat B, MatStructure str)
927 {
928   Mat_Shell              *shellA = (Mat_Shell *)A->data, *shellB = (Mat_Shell *)B->data;
929   PetscBool               matflg;
930   MatShellMatFunctionList matmatA;
931 
932   PetscFunctionBegin;
933   PetscCall(MatIsShell(B, &matflg));
934   PetscCheck(matflg, PetscObjectComm((PetscObject)A), PETSC_ERR_ARG_WRONG, "Matrix %s not derived from MATSHELL", ((PetscObject)B)->type_name);
935 
936   PetscCall(PetscMemcpy(B->ops, A->ops, sizeof(struct _MatOps)));
937   PetscCall(PetscMemcpy(shellB->ops, shellA->ops, sizeof(struct _MatShellOps)));
938 
939   if (shellA->ops->copy) PetscCall((*shellA->ops->copy)(A, B, str));
940   shellB->vscale = shellA->vscale;
941   shellB->vshift = shellA->vshift;
942   if (shellA->dshift) {
943     if (!shellB->dshift) PetscCall(VecDuplicate(shellA->dshift, &shellB->dshift));
944     PetscCall(VecCopy(shellA->dshift, shellB->dshift));
945   } else {
946     PetscCall(VecDestroy(&shellB->dshift));
947   }
948   if (shellA->left) {
949     if (!shellB->left) PetscCall(VecDuplicate(shellA->left, &shellB->left));
950     PetscCall(VecCopy(shellA->left, shellB->left));
951   } else {
952     PetscCall(VecDestroy(&shellB->left));
953   }
954   if (shellA->right) {
955     if (!shellB->right) PetscCall(VecDuplicate(shellA->right, &shellB->right));
956     PetscCall(VecCopy(shellA->right, shellB->right));
957   } else {
958     PetscCall(VecDestroy(&shellB->right));
959   }
960   PetscCall(MatDestroy(&shellB->axpy));
961   shellB->axpy_vscale = 0.0;
962   shellB->axpy_state  = 0;
963   if (shellA->axpy) {
964     PetscCall(PetscObjectReference((PetscObject)shellA->axpy));
965     shellB->axpy        = shellA->axpy;
966     shellB->axpy_vscale = shellA->axpy_vscale;
967     shellB->axpy_state  = shellA->axpy_state;
968   }
969   if (shellA->zrows) {
970     PetscCall(ISDuplicate(shellA->zrows, &shellB->zrows));
971     if (shellA->zcols) PetscCall(ISDuplicate(shellA->zcols, &shellB->zcols));
972     PetscCall(VecDuplicate(shellA->zvals, &shellB->zvals));
973     PetscCall(VecCopy(shellA->zvals, shellB->zvals));
974     PetscCall(VecDuplicate(shellA->zvals_w, &shellB->zvals_w));
975     PetscCall(PetscObjectReference((PetscObject)shellA->zvals_sct_r));
976     PetscCall(PetscObjectReference((PetscObject)shellA->zvals_sct_c));
977     shellB->zvals_sct_r = shellA->zvals_sct_r;
978     shellB->zvals_sct_c = shellA->zvals_sct_c;
979   }
980 
981   matmatA = shellA->matmat;
982   if (matmatA) {
983     while (matmatA->next) {
984       PetscCall(MatShellSetMatProductOperation_Private(B, matmatA->ptype, matmatA->symbolic, matmatA->numeric, matmatA->destroy, matmatA->composedname, matmatA->resultname));
985       matmatA = matmatA->next;
986     }
987   }
988   PetscFunctionReturn(0);
989 }
990 
991 static PetscErrorCode MatDuplicate_Shell(Mat mat, MatDuplicateOption op, Mat *M)
992 {
993   PetscFunctionBegin;
994   PetscCall(MatCreateShell(PetscObjectComm((PetscObject)mat), mat->rmap->n, mat->cmap->n, mat->rmap->N, mat->cmap->N, NULL, M));
995   ((Mat_Shell *)(*M)->data)->ctxcontainer = ((Mat_Shell *)mat->data)->ctxcontainer;
996   PetscCall(PetscObjectCompose((PetscObject)(*M), "MatShell ctx", (PetscObject)((Mat_Shell *)(*M)->data)->ctxcontainer));
997   PetscCall(PetscObjectChangeTypeName((PetscObject)(*M), ((PetscObject)mat)->type_name));
998   if (op != MAT_DO_NOT_COPY_VALUES) PetscCall(MatCopy(mat, *M, SAME_NONZERO_PATTERN));
999   PetscFunctionReturn(0);
1000 }
1001 
1002 PetscErrorCode MatMult_Shell(Mat A, Vec x, Vec y)
1003 {
1004   Mat_Shell       *shell = (Mat_Shell *)A->data;
1005   Vec              xx;
1006   PetscObjectState instate, outstate;
1007 
1008   PetscFunctionBegin;
1009   PetscCall(MatShellPreZeroRight(A, x, &xx));
1010   PetscCall(MatShellPreScaleRight(A, xx, &xx));
1011   PetscCall(PetscObjectStateGet((PetscObject)y, &instate));
1012   PetscCall((*shell->ops->mult)(A, xx, y));
1013   PetscCall(PetscObjectStateGet((PetscObject)y, &outstate));
1014   if (instate == outstate) {
1015     /* increase the state of the output vector since the user did not update its state themself as should have been done */
1016     PetscCall(PetscObjectStateIncrease((PetscObject)y));
1017   }
1018   PetscCall(MatShellShiftAndScale(A, xx, y));
1019   PetscCall(MatShellPostScaleLeft(A, y));
1020   PetscCall(MatShellPostZeroLeft(A, y));
1021 
1022   if (shell->axpy) {
1023     Mat              X;
1024     PetscObjectState axpy_state;
1025 
1026     PetscCall(MatShellGetContext(shell->axpy, &X));
1027     PetscCall(PetscObjectStateGet((PetscObject)X, &axpy_state));
1028     PetscCheck(shell->axpy_state == axpy_state, PetscObjectComm((PetscObject)A), PETSC_ERR_ORDER, "Invalid AXPY state: cannot modify the X matrix passed to MatAXPY(Y,a,X,...)");
1029 
1030     PetscCall(MatCreateVecs(shell->axpy, shell->axpy_right ? NULL : &shell->axpy_right, shell->axpy_left ? NULL : &shell->axpy_left));
1031     PetscCall(VecCopy(x, shell->axpy_right));
1032     PetscCall(MatMult(shell->axpy, shell->axpy_right, shell->axpy_left));
1033     PetscCall(VecAXPY(y, shell->axpy_vscale, shell->axpy_left));
1034   }
1035   PetscFunctionReturn(0);
1036 }
1037 
1038 static PetscErrorCode MatMultAdd_Shell(Mat A, Vec x, Vec y, Vec z)
1039 {
1040   Mat_Shell *shell = (Mat_Shell *)A->data;
1041 
1042   PetscFunctionBegin;
1043   if (y == z) {
1044     if (!shell->right_add_work) PetscCall(VecDuplicate(z, &shell->right_add_work));
1045     PetscCall(MatMult(A, x, shell->right_add_work));
1046     PetscCall(VecAXPY(z, 1.0, shell->right_add_work));
1047   } else {
1048     PetscCall(MatMult(A, x, z));
1049     PetscCall(VecAXPY(z, 1.0, y));
1050   }
1051   PetscFunctionReturn(0);
1052 }
1053 
1054 static PetscErrorCode MatMultTranspose_Shell(Mat A, Vec x, Vec y)
1055 {
1056   Mat_Shell       *shell = (Mat_Shell *)A->data;
1057   Vec              xx;
1058   PetscObjectState instate, outstate;
1059 
1060   PetscFunctionBegin;
1061   PetscCall(MatShellPreZeroLeft(A, x, &xx));
1062   PetscCall(MatShellPreScaleLeft(A, xx, &xx));
1063   PetscCall(PetscObjectStateGet((PetscObject)y, &instate));
1064   PetscCall((*shell->ops->multtranspose)(A, xx, y));
1065   PetscCall(PetscObjectStateGet((PetscObject)y, &outstate));
1066   if (instate == outstate) {
1067     /* increase the state of the output vector since the user did not update its state themself as should have been done */
1068     PetscCall(PetscObjectStateIncrease((PetscObject)y));
1069   }
1070   PetscCall(MatShellShiftAndScale(A, xx, y));
1071   PetscCall(MatShellPostScaleRight(A, y));
1072   PetscCall(MatShellPostZeroRight(A, y));
1073 
1074   if (shell->axpy) {
1075     Mat              X;
1076     PetscObjectState axpy_state;
1077 
1078     PetscCall(MatShellGetContext(shell->axpy, &X));
1079     PetscCall(PetscObjectStateGet((PetscObject)X, &axpy_state));
1080     PetscCheck(shell->axpy_state == axpy_state, PetscObjectComm((PetscObject)A), PETSC_ERR_ORDER, "Invalid AXPY state: cannot modify the X matrix passed to MatAXPY(Y,a,X,...)");
1081     PetscCall(MatCreateVecs(shell->axpy, shell->axpy_right ? NULL : &shell->axpy_right, shell->axpy_left ? NULL : &shell->axpy_left));
1082     PetscCall(VecCopy(x, shell->axpy_left));
1083     PetscCall(MatMultTranspose(shell->axpy, shell->axpy_left, shell->axpy_right));
1084     PetscCall(VecAXPY(y, shell->axpy_vscale, shell->axpy_right));
1085   }
1086   PetscFunctionReturn(0);
1087 }
1088 
1089 static PetscErrorCode MatMultTransposeAdd_Shell(Mat A, Vec x, Vec y, Vec z)
1090 {
1091   Mat_Shell *shell = (Mat_Shell *)A->data;
1092 
1093   PetscFunctionBegin;
1094   if (y == z) {
1095     if (!shell->left_add_work) PetscCall(VecDuplicate(z, &shell->left_add_work));
1096     PetscCall(MatMultTranspose(A, x, shell->left_add_work));
1097     PetscCall(VecAXPY(z, 1.0, shell->left_add_work));
1098   } else {
1099     PetscCall(MatMultTranspose(A, x, z));
1100     PetscCall(VecAXPY(z, 1.0, y));
1101   }
1102   PetscFunctionReturn(0);
1103 }
1104 
1105 /*
1106           diag(left)(vscale*A + diag(dshift) + vshift I)diag(right)
1107 */
1108 static PetscErrorCode MatGetDiagonal_Shell(Mat A, Vec v)
1109 {
1110   Mat_Shell *shell = (Mat_Shell *)A->data;
1111 
1112   PetscFunctionBegin;
1113   if (shell->ops->getdiagonal) {
1114     PetscCall((*shell->ops->getdiagonal)(A, v));
1115   } else SETERRQ(PetscObjectComm((PetscObject)A), PETSC_ERR_ARG_WRONGSTATE, "Must provide shell matrix with routine to return diagonal using\nMatShellSetOperation(S,MATOP_GET_DIAGONAL,...)");
1116   PetscCall(VecScale(v, shell->vscale));
1117   if (shell->dshift) PetscCall(VecAXPY(v, 1.0, shell->dshift));
1118   PetscCall(VecShift(v, shell->vshift));
1119   if (shell->left) PetscCall(VecPointwiseMult(v, v, shell->left));
1120   if (shell->right) PetscCall(VecPointwiseMult(v, v, shell->right));
1121   if (shell->zrows) {
1122     PetscCall(VecScatterBegin(shell->zvals_sct_r, shell->zvals, v, INSERT_VALUES, SCATTER_REVERSE));
1123     PetscCall(VecScatterEnd(shell->zvals_sct_r, shell->zvals, v, INSERT_VALUES, SCATTER_REVERSE));
1124   }
1125   if (shell->axpy) {
1126     Mat              X;
1127     PetscObjectState axpy_state;
1128 
1129     PetscCall(MatShellGetContext(shell->axpy, &X));
1130     PetscCall(PetscObjectStateGet((PetscObject)X, &axpy_state));
1131     PetscCheck(shell->axpy_state == axpy_state, PetscObjectComm((PetscObject)A), PETSC_ERR_ORDER, "Invalid AXPY state: cannot modify the X matrix passed to MatAXPY(Y,a,X,...)");
1132     PetscCall(MatCreateVecs(shell->axpy, NULL, shell->axpy_left ? NULL : &shell->axpy_left));
1133     PetscCall(MatGetDiagonal(shell->axpy, shell->axpy_left));
1134     PetscCall(VecAXPY(v, shell->axpy_vscale, shell->axpy_left));
1135   }
1136   PetscFunctionReturn(0);
1137 }
1138 
1139 static PetscErrorCode MatShift_Shell(Mat Y, PetscScalar a)
1140 {
1141   Mat_Shell *shell = (Mat_Shell *)Y->data;
1142   PetscBool  flg;
1143 
1144   PetscFunctionBegin;
1145   PetscCall(MatHasCongruentLayouts(Y, &flg));
1146   PetscCheck(flg, PetscObjectComm((PetscObject)Y), PETSC_ERR_SUP, "Cannot shift shell matrix if it is not congruent");
1147   if (shell->left || shell->right) {
1148     if (!shell->dshift) {
1149       PetscCall(VecDuplicate(shell->left ? shell->left : shell->right, &shell->dshift));
1150       PetscCall(VecSet(shell->dshift, a));
1151     } else {
1152       if (shell->left) PetscCall(VecPointwiseMult(shell->dshift, shell->dshift, shell->left));
1153       if (shell->right) PetscCall(VecPointwiseMult(shell->dshift, shell->dshift, shell->right));
1154       PetscCall(VecShift(shell->dshift, a));
1155     }
1156     if (shell->left) PetscCall(VecPointwiseDivide(shell->dshift, shell->dshift, shell->left));
1157     if (shell->right) PetscCall(VecPointwiseDivide(shell->dshift, shell->dshift, shell->right));
1158   } else shell->vshift += a;
1159   if (shell->zrows) PetscCall(VecShift(shell->zvals, a));
1160   PetscFunctionReturn(0);
1161 }
1162 
1163 static PetscErrorCode MatDiagonalSet_Shell_Private(Mat A, Vec D, PetscScalar s)
1164 {
1165   Mat_Shell *shell = (Mat_Shell *)A->data;
1166 
1167   PetscFunctionBegin;
1168   if (!shell->dshift) PetscCall(VecDuplicate(D, &shell->dshift));
1169   if (shell->left || shell->right) {
1170     if (!shell->right_work) PetscCall(VecDuplicate(shell->left ? shell->left : shell->right, &shell->right_work));
1171     if (shell->left && shell->right) {
1172       PetscCall(VecPointwiseDivide(shell->right_work, D, shell->left));
1173       PetscCall(VecPointwiseDivide(shell->right_work, shell->right_work, shell->right));
1174     } else if (shell->left) {
1175       PetscCall(VecPointwiseDivide(shell->right_work, D, shell->left));
1176     } else {
1177       PetscCall(VecPointwiseDivide(shell->right_work, D, shell->right));
1178     }
1179     PetscCall(VecAXPY(shell->dshift, s, shell->right_work));
1180   } else {
1181     PetscCall(VecAXPY(shell->dshift, s, D));
1182   }
1183   PetscFunctionReturn(0);
1184 }
1185 
1186 PetscErrorCode MatDiagonalSet_Shell(Mat A, Vec D, InsertMode ins)
1187 {
1188   Mat_Shell *shell = (Mat_Shell *)A->data;
1189   Vec        d;
1190   PetscBool  flg;
1191 
1192   PetscFunctionBegin;
1193   PetscCall(MatHasCongruentLayouts(A, &flg));
1194   PetscCheck(flg, PetscObjectComm((PetscObject)A), PETSC_ERR_SUP, "Cannot diagonal set or shift shell matrix if it is not congruent");
1195   if (ins == INSERT_VALUES) {
1196     PetscCall(VecDuplicate(D, &d));
1197     PetscCall(MatGetDiagonal(A, d));
1198     PetscCall(MatDiagonalSet_Shell_Private(A, d, -1.));
1199     PetscCall(MatDiagonalSet_Shell_Private(A, D, 1.));
1200     PetscCall(VecDestroy(&d));
1201     if (shell->zrows) PetscCall(VecCopy(D, shell->zvals));
1202   } else {
1203     PetscCall(MatDiagonalSet_Shell_Private(A, D, 1.));
1204     if (shell->zrows) PetscCall(VecAXPY(shell->zvals, 1.0, D));
1205   }
1206   PetscFunctionReturn(0);
1207 }
1208 
1209 static PetscErrorCode MatScale_Shell(Mat Y, PetscScalar a)
1210 {
1211   Mat_Shell *shell = (Mat_Shell *)Y->data;
1212 
1213   PetscFunctionBegin;
1214   shell->vscale *= a;
1215   shell->vshift *= a;
1216   if (shell->dshift) PetscCall(VecScale(shell->dshift, a));
1217   shell->axpy_vscale *= a;
1218   if (shell->zrows) PetscCall(VecScale(shell->zvals, a));
1219   PetscFunctionReturn(0);
1220 }
1221 
1222 static PetscErrorCode MatDiagonalScale_Shell(Mat Y, Vec left, Vec right)
1223 {
1224   Mat_Shell *shell = (Mat_Shell *)Y->data;
1225 
1226   PetscFunctionBegin;
1227   if (left) {
1228     if (!shell->left) {
1229       PetscCall(VecDuplicate(left, &shell->left));
1230       PetscCall(VecCopy(left, shell->left));
1231     } else {
1232       PetscCall(VecPointwiseMult(shell->left, shell->left, left));
1233     }
1234     if (shell->zrows) PetscCall(VecPointwiseMult(shell->zvals, shell->zvals, left));
1235   }
1236   if (right) {
1237     if (!shell->right) {
1238       PetscCall(VecDuplicate(right, &shell->right));
1239       PetscCall(VecCopy(right, shell->right));
1240     } else {
1241       PetscCall(VecPointwiseMult(shell->right, shell->right, right));
1242     }
1243     if (shell->zrows) {
1244       if (!shell->left_work) PetscCall(MatCreateVecs(Y, NULL, &shell->left_work));
1245       PetscCall(VecSet(shell->zvals_w, 1.0));
1246       PetscCall(VecScatterBegin(shell->zvals_sct_c, right, shell->zvals_w, INSERT_VALUES, SCATTER_FORWARD));
1247       PetscCall(VecScatterEnd(shell->zvals_sct_c, right, shell->zvals_w, INSERT_VALUES, SCATTER_FORWARD));
1248       PetscCall(VecPointwiseMult(shell->zvals, shell->zvals, shell->zvals_w));
1249     }
1250   }
1251   if (shell->axpy) PetscCall(MatDiagonalScale(shell->axpy, left, right));
1252   PetscFunctionReturn(0);
1253 }
1254 
1255 PetscErrorCode MatAssemblyEnd_Shell(Mat Y, MatAssemblyType t)
1256 {
1257   Mat_Shell *shell = (Mat_Shell *)Y->data;
1258 
1259   PetscFunctionBegin;
1260   if (t == MAT_FINAL_ASSEMBLY) {
1261     shell->vshift      = 0.0;
1262     shell->vscale      = 1.0;
1263     shell->axpy_vscale = 0.0;
1264     shell->axpy_state  = 0;
1265     PetscCall(VecDestroy(&shell->dshift));
1266     PetscCall(VecDestroy(&shell->left));
1267     PetscCall(VecDestroy(&shell->right));
1268     PetscCall(MatDestroy(&shell->axpy));
1269     PetscCall(VecDestroy(&shell->axpy_left));
1270     PetscCall(VecDestroy(&shell->axpy_right));
1271     PetscCall(VecScatterDestroy(&shell->zvals_sct_c));
1272     PetscCall(VecScatterDestroy(&shell->zvals_sct_r));
1273     PetscCall(ISDestroy(&shell->zrows));
1274     PetscCall(ISDestroy(&shell->zcols));
1275   }
1276   PetscFunctionReturn(0);
1277 }
1278 
1279 static PetscErrorCode MatMissingDiagonal_Shell(Mat A, PetscBool *missing, PetscInt *d)
1280 {
1281   PetscFunctionBegin;
1282   *missing = PETSC_FALSE;
1283   PetscFunctionReturn(0);
1284 }
1285 
1286 PetscErrorCode MatAXPY_Shell(Mat Y, PetscScalar a, Mat X, MatStructure str)
1287 {
1288   Mat_Shell *shell = (Mat_Shell *)Y->data;
1289 
1290   PetscFunctionBegin;
1291   if (X == Y) {
1292     PetscCall(MatScale(Y, 1.0 + a));
1293     PetscFunctionReturn(0);
1294   }
1295   if (!shell->axpy) {
1296     PetscCall(MatConvertFrom_Shell(X, MATSHELL, MAT_INITIAL_MATRIX, &shell->axpy));
1297     shell->axpy_vscale = a;
1298     PetscCall(PetscObjectStateGet((PetscObject)X, &shell->axpy_state));
1299   } else {
1300     PetscCall(MatAXPY(shell->axpy, a / shell->axpy_vscale, X, str));
1301   }
1302   PetscFunctionReturn(0);
1303 }
1304 
1305 static struct _MatOps MatOps_Values = {NULL,
1306                                        NULL,
1307                                        NULL,
1308                                        NULL,
1309                                        /* 4*/ MatMultAdd_Shell,
1310                                        NULL,
1311                                        MatMultTransposeAdd_Shell,
1312                                        NULL,
1313                                        NULL,
1314                                        NULL,
1315                                        /*10*/ NULL,
1316                                        NULL,
1317                                        NULL,
1318                                        NULL,
1319                                        NULL,
1320                                        /*15*/ NULL,
1321                                        NULL,
1322                                        NULL,
1323                                        MatDiagonalScale_Shell,
1324                                        NULL,
1325                                        /*20*/ NULL,
1326                                        MatAssemblyEnd_Shell,
1327                                        NULL,
1328                                        NULL,
1329                                        /*24*/ MatZeroRows_Shell,
1330                                        NULL,
1331                                        NULL,
1332                                        NULL,
1333                                        NULL,
1334                                        /*29*/ NULL,
1335                                        NULL,
1336                                        NULL,
1337                                        NULL,
1338                                        NULL,
1339                                        /*34*/ MatDuplicate_Shell,
1340                                        NULL,
1341                                        NULL,
1342                                        NULL,
1343                                        NULL,
1344                                        /*39*/ MatAXPY_Shell,
1345                                        NULL,
1346                                        NULL,
1347                                        NULL,
1348                                        MatCopy_Shell,
1349                                        /*44*/ NULL,
1350                                        MatScale_Shell,
1351                                        MatShift_Shell,
1352                                        MatDiagonalSet_Shell,
1353                                        MatZeroRowsColumns_Shell,
1354                                        /*49*/ NULL,
1355                                        NULL,
1356                                        NULL,
1357                                        NULL,
1358                                        NULL,
1359                                        /*54*/ NULL,
1360                                        NULL,
1361                                        NULL,
1362                                        NULL,
1363                                        NULL,
1364                                        /*59*/ NULL,
1365                                        MatDestroy_Shell,
1366                                        NULL,
1367                                        MatConvertFrom_Shell,
1368                                        NULL,
1369                                        /*64*/ NULL,
1370                                        NULL,
1371                                        NULL,
1372                                        NULL,
1373                                        NULL,
1374                                        /*69*/ NULL,
1375                                        NULL,
1376                                        MatConvert_Shell,
1377                                        NULL,
1378                                        NULL,
1379                                        /*74*/ NULL,
1380                                        NULL,
1381                                        NULL,
1382                                        NULL,
1383                                        NULL,
1384                                        /*79*/ NULL,
1385                                        NULL,
1386                                        NULL,
1387                                        NULL,
1388                                        NULL,
1389                                        /*84*/ NULL,
1390                                        NULL,
1391                                        NULL,
1392                                        NULL,
1393                                        NULL,
1394                                        /*89*/ NULL,
1395                                        NULL,
1396                                        NULL,
1397                                        NULL,
1398                                        NULL,
1399                                        /*94*/ NULL,
1400                                        NULL,
1401                                        NULL,
1402                                        NULL,
1403                                        NULL,
1404                                        /*99*/ NULL,
1405                                        NULL,
1406                                        NULL,
1407                                        NULL,
1408                                        NULL,
1409                                        /*104*/ NULL,
1410                                        NULL,
1411                                        NULL,
1412                                        NULL,
1413                                        NULL,
1414                                        /*109*/ NULL,
1415                                        NULL,
1416                                        NULL,
1417                                        NULL,
1418                                        MatMissingDiagonal_Shell,
1419                                        /*114*/ NULL,
1420                                        NULL,
1421                                        NULL,
1422                                        NULL,
1423                                        NULL,
1424                                        /*119*/ NULL,
1425                                        NULL,
1426                                        NULL,
1427                                        NULL,
1428                                        NULL,
1429                                        /*124*/ NULL,
1430                                        NULL,
1431                                        NULL,
1432                                        NULL,
1433                                        NULL,
1434                                        /*129*/ NULL,
1435                                        NULL,
1436                                        NULL,
1437                                        NULL,
1438                                        NULL,
1439                                        /*134*/ NULL,
1440                                        NULL,
1441                                        NULL,
1442                                        NULL,
1443                                        NULL,
1444                                        /*139*/ NULL,
1445                                        NULL,
1446                                        NULL,
1447                                        NULL,
1448                                        NULL,
1449                                        /*144*/ NULL,
1450                                        NULL,
1451                                        NULL,
1452                                        NULL,
1453                                        NULL,
1454                                        NULL,
1455                                        /*150*/ NULL};
1456 
1457 static PetscErrorCode MatShellSetContext_Shell(Mat mat, void *ctx)
1458 {
1459   Mat_Shell *shell = (Mat_Shell *)mat->data;
1460 
1461   PetscFunctionBegin;
1462   if (ctx) {
1463     PetscContainer ctxcontainer;
1464     PetscCall(PetscContainerCreate(PetscObjectComm((PetscObject)mat), &ctxcontainer));
1465     PetscCall(PetscContainerSetPointer(ctxcontainer, ctx));
1466     PetscCall(PetscObjectCompose((PetscObject)mat, "MatShell ctx", (PetscObject)ctxcontainer));
1467     shell->ctxcontainer = ctxcontainer;
1468     PetscCall(PetscContainerDestroy(&ctxcontainer));
1469   } else {
1470     PetscCall(PetscObjectCompose((PetscObject)mat, "MatShell ctx", NULL));
1471     shell->ctxcontainer = NULL;
1472   }
1473 
1474   PetscFunctionReturn(0);
1475 }
1476 
1477 PetscErrorCode MatShellSetContextDestroy_Shell(Mat mat, PetscErrorCode (*f)(void *))
1478 {
1479   Mat_Shell *shell = (Mat_Shell *)mat->data;
1480 
1481   PetscFunctionBegin;
1482   if (shell->ctxcontainer) PetscCall(PetscContainerSetUserDestroy(shell->ctxcontainer, f));
1483   PetscFunctionReturn(0);
1484 }
1485 
1486 static PetscErrorCode MatShellSetVecType_Shell(Mat mat, VecType vtype)
1487 {
1488   PetscFunctionBegin;
1489   PetscCall(PetscFree(mat->defaultvectype));
1490   PetscCall(PetscStrallocpy(vtype, (char **)&mat->defaultvectype));
1491   PetscFunctionReturn(0);
1492 }
1493 
1494 PetscErrorCode MatShellSetManageScalingShifts_Shell(Mat A)
1495 {
1496   Mat_Shell *shell = (Mat_Shell *)A->data;
1497 
1498   PetscFunctionBegin;
1499   shell->managescalingshifts = PETSC_FALSE;
1500   A->ops->diagonalset        = NULL;
1501   A->ops->diagonalscale      = NULL;
1502   A->ops->scale              = NULL;
1503   A->ops->shift              = NULL;
1504   A->ops->axpy               = NULL;
1505   PetscFunctionReturn(0);
1506 }
1507 
1508 static PetscErrorCode MatShellSetOperation_Shell(Mat mat, MatOperation op, void (*f)(void))
1509 {
1510   Mat_Shell *shell = (Mat_Shell *)mat->data;
1511 
1512   PetscFunctionBegin;
1513   switch (op) {
1514   case MATOP_DESTROY:
1515     shell->ops->destroy = (PetscErrorCode(*)(Mat))f;
1516     break;
1517   case MATOP_VIEW:
1518     if (!mat->ops->viewnative) mat->ops->viewnative = mat->ops->view;
1519     mat->ops->view = (PetscErrorCode(*)(Mat, PetscViewer))f;
1520     break;
1521   case MATOP_COPY:
1522     shell->ops->copy = (PetscErrorCode(*)(Mat, Mat, MatStructure))f;
1523     break;
1524   case MATOP_DIAGONAL_SET:
1525   case MATOP_DIAGONAL_SCALE:
1526   case MATOP_SHIFT:
1527   case MATOP_SCALE:
1528   case MATOP_AXPY:
1529   case MATOP_ZERO_ROWS:
1530   case MATOP_ZERO_ROWS_COLUMNS:
1531     PetscCheck(!shell->managescalingshifts, PetscObjectComm((PetscObject)mat), PETSC_ERR_ARG_WRONGSTATE, "MATSHELL is managing scalings and shifts, see MatShellSetManageScalingShifts()");
1532     (((void (**)(void))mat->ops)[op]) = f;
1533     break;
1534   case MATOP_GET_DIAGONAL:
1535     if (shell->managescalingshifts) {
1536       shell->ops->getdiagonal = (PetscErrorCode(*)(Mat, Vec))f;
1537       mat->ops->getdiagonal   = MatGetDiagonal_Shell;
1538     } else {
1539       shell->ops->getdiagonal = NULL;
1540       mat->ops->getdiagonal   = (PetscErrorCode(*)(Mat, Vec))f;
1541     }
1542     break;
1543   case MATOP_MULT:
1544     if (shell->managescalingshifts) {
1545       shell->ops->mult = (PetscErrorCode(*)(Mat, Vec, Vec))f;
1546       mat->ops->mult   = MatMult_Shell;
1547     } else {
1548       shell->ops->mult = NULL;
1549       mat->ops->mult   = (PetscErrorCode(*)(Mat, Vec, Vec))f;
1550     }
1551     break;
1552   case MATOP_MULT_TRANSPOSE:
1553     if (shell->managescalingshifts) {
1554       shell->ops->multtranspose = (PetscErrorCode(*)(Mat, Vec, Vec))f;
1555       mat->ops->multtranspose   = MatMultTranspose_Shell;
1556     } else {
1557       shell->ops->multtranspose = NULL;
1558       mat->ops->multtranspose   = (PetscErrorCode(*)(Mat, Vec, Vec))f;
1559     }
1560     break;
1561   default:
1562     (((void (**)(void))mat->ops)[op]) = f;
1563     break;
1564   }
1565   PetscFunctionReturn(0);
1566 }
1567 
1568 static PetscErrorCode MatShellGetOperation_Shell(Mat mat, MatOperation op, void (**f)(void))
1569 {
1570   Mat_Shell *shell = (Mat_Shell *)mat->data;
1571 
1572   PetscFunctionBegin;
1573   switch (op) {
1574   case MATOP_DESTROY:
1575     *f = (void (*)(void))shell->ops->destroy;
1576     break;
1577   case MATOP_VIEW:
1578     *f = (void (*)(void))mat->ops->view;
1579     break;
1580   case MATOP_COPY:
1581     *f = (void (*)(void))shell->ops->copy;
1582     break;
1583   case MATOP_DIAGONAL_SET:
1584   case MATOP_DIAGONAL_SCALE:
1585   case MATOP_SHIFT:
1586   case MATOP_SCALE:
1587   case MATOP_AXPY:
1588   case MATOP_ZERO_ROWS:
1589   case MATOP_ZERO_ROWS_COLUMNS:
1590     *f = (((void (**)(void))mat->ops)[op]);
1591     break;
1592   case MATOP_GET_DIAGONAL:
1593     if (shell->ops->getdiagonal) *f = (void (*)(void))shell->ops->getdiagonal;
1594     else *f = (((void (**)(void))mat->ops)[op]);
1595     break;
1596   case MATOP_MULT:
1597     if (shell->ops->mult) *f = (void (*)(void))shell->ops->mult;
1598     else *f = (((void (**)(void))mat->ops)[op]);
1599     break;
1600   case MATOP_MULT_TRANSPOSE:
1601     if (shell->ops->multtranspose) *f = (void (*)(void))shell->ops->multtranspose;
1602     else *f = (((void (**)(void))mat->ops)[op]);
1603     break;
1604   default:
1605     *f = (((void (**)(void))mat->ops)[op]);
1606   }
1607   PetscFunctionReturn(0);
1608 }
1609 
1610 /*MC
1611    MATSHELL - MATSHELL = "shell" - A matrix type to be used to define your own matrix type -- perhaps matrix free.
1612 
1613   Level: advanced
1614 
1615 .seealso: `Mat`, `MatCreateShell()`
1616 M*/
1617 
1618 PETSC_EXTERN PetscErrorCode MatCreate_Shell(Mat A)
1619 {
1620   Mat_Shell *b;
1621 
1622   PetscFunctionBegin;
1623   PetscCall(PetscMemcpy(A->ops, &MatOps_Values, sizeof(struct _MatOps)));
1624 
1625   PetscCall(PetscNew(&b));
1626   A->data = (void *)b;
1627 
1628   b->ctxcontainer        = NULL;
1629   b->vshift              = 0.0;
1630   b->vscale              = 1.0;
1631   b->managescalingshifts = PETSC_TRUE;
1632   A->assembled           = PETSC_TRUE;
1633   A->preallocated        = PETSC_FALSE;
1634 
1635   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatShellGetContext_C", MatShellGetContext_Shell));
1636   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatShellSetContext_C", MatShellSetContext_Shell));
1637   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatShellSetContextDestroy_C", MatShellSetContextDestroy_Shell));
1638   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatShellSetVecType_C", MatShellSetVecType_Shell));
1639   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatShellSetManageScalingShifts_C", MatShellSetManageScalingShifts_Shell));
1640   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatShellSetOperation_C", MatShellSetOperation_Shell));
1641   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatShellGetOperation_C", MatShellGetOperation_Shell));
1642   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatShellSetMatProductOperation_C", MatShellSetMatProductOperation_Shell));
1643   PetscCall(PetscObjectChangeTypeName((PetscObject)A, MATSHELL));
1644   PetscFunctionReturn(0);
1645 }
1646 
1647 /*@C
1648    MatCreateShell - Creates a new matrix of `MatType` `MATSHELL` for use with a user-defined
1649    private data storage format.
1650 
1651   Collective
1652 
1653    Input Parameters:
1654 +  comm - MPI communicator
1655 .  m - number of local rows (must be given)
1656 .  n - number of local columns (must be given)
1657 .  M - number of global rows (may be PETSC_DETERMINE)
1658 .  N - number of global columns (may be PETSC_DETERMINE)
1659 -  ctx - pointer to data needed by the shell matrix routines
1660 
1661    Output Parameter:
1662 .  A - the matrix
1663 
1664    Level: advanced
1665 
1666   Usage:
1667 $    extern PetscErrorCode mult(Mat,Vec,Vec);
1668 $    MatCreateShell(comm,m,n,M,N,ctx,&mat);
1669 $    MatShellSetOperation(mat,MATOP_MULT,(void(*)(void))mult);
1670 $    [ Use matrix for operations that have been set ]
1671 $    MatDestroy(mat);
1672 
1673    Notes:
1674    The shell matrix type is intended to provide a simple class to use
1675    with `KSP` (such as, for use with matrix-free methods). You should not
1676    use the shell type if you plan to define a complete matrix class.
1677 
1678    PETSc requires that matrices and vectors being used for certain
1679    operations are partitioned accordingly.  For example, when
1680    creating a shell matrix, A, that supports parallel matrix-vector
1681    products using `MatMult`(A,x,y) the user should set the number
1682    of local matrix rows to be the number of local elements of the
1683    corresponding result vector, y. Note that this is information is
1684    required for use of the matrix interface routines, even though
1685    the shell matrix may not actually be physically partitioned.
1686    For example,
1687 
1688 $
1689 $     Vec x, y
1690 $     extern PetscErrorCode mult(Mat,Vec,Vec);
1691 $     Mat A
1692 $
1693 $     VecCreateMPI(comm,PETSC_DECIDE,M,&y);
1694 $     VecCreateMPI(comm,PETSC_DECIDE,N,&x);
1695 $     VecGetLocalSize(y,&m);
1696 $     VecGetLocalSize(x,&n);
1697 $     MatCreateShell(comm,m,n,M,N,ctx,&A);
1698 $     MatShellSetOperation(mat,MATOP_MULT,(void(*)(void))mult);
1699 $     MatMult(A,x,y);
1700 $     MatDestroy(&A);
1701 $     VecDestroy(&y);
1702 $     VecDestroy(&x);
1703 $
1704 
1705    `MATSHELL` handles `MatShift()`, `MatDiagonalSet()`, `MatDiagonalScale()`, `MatAXPY()`, `MatScale()`, `MatZeroRows()` and `MatZeroRowsColumns()` internally, so these
1706    operations cannot be overwritten unless `MatShellSetManageScalingShifts()` is called.
1707 
1708     For rectangular matrices do all the scalings and shifts make sense?
1709 
1710     Developers Notes:
1711     Regarding shifting and scaling. The general form is
1712 
1713           diag(left)(vscale*A + diag(dshift) + vshift I)diag(right)
1714 
1715       The order you apply the operations is important. For example if you have a dshift then
1716       apply a MatScale(s) you get s*vscale*A + s*diag(shift). But if you first scale and then shift
1717       you get s*vscale*A + diag(shift)
1718 
1719           A is the user provided function.
1720 
1721    `KSP`/`PC` uses changes in the` Mat`'s "state" to decide if preconditioners need to be rebuilt: `PCSetUp()` only calls the setup() for
1722    for the PC implementation if the Mat state has increased from the previous call. Thus to get changes in a `MATSHELL` to trigger
1723    an update in the preconditioner you must call `MatAssemblyBegin()` and `MatAssemblyEnd()` or `PetscObjectStateIncrease`((`PetscObject`)mat);
1724    each time the `MATSHELL` matrix has changed.
1725 
1726    Matrix product operations (i.e. `MatMat()`, `MatTransposeMat()` etc) can be specified using `MatShellSetMatProductOperation()`
1727 
1728    Calling `MatAssemblyBegin()`/`MatAssemblyEnd()` on a `MATSHELL` removes any previously supplied shift and scales that were provided
1729    with `MatDiagonalSet()`, `MatShift()`, `MatScale()`, or `MatDiagonalScale()`.
1730 
1731    Fortran Note:
1732     To use this from Fortran with a ctx you must write an interface definition for this
1733     function and for `MatShellGetContext()` that tells Fortran the Fortran derived data type you are passing
1734     in as the ctx argument.
1735 
1736 .seealso: `MATSHELL`, `MatShellSetOperation()`, `MatHasOperation()`, `MatShellGetContext()`, `MatShellSetContext()`, `MATSHELL`, `MatShellSetManageScalingShifts()`, `MatShellSetMatProductOperation()`
1737 @*/
1738 PetscErrorCode MatCreateShell(MPI_Comm comm, PetscInt m, PetscInt n, PetscInt M, PetscInt N, void *ctx, Mat *A)
1739 {
1740   PetscFunctionBegin;
1741   PetscCall(MatCreate(comm, A));
1742   PetscCall(MatSetSizes(*A, m, n, M, N));
1743   PetscCall(MatSetType(*A, MATSHELL));
1744   PetscCall(MatShellSetContext(*A, ctx));
1745   PetscCall(MatSetUp(*A));
1746   PetscFunctionReturn(0);
1747 }
1748 
1749 /*@
1750     MatShellSetContext - sets the context for a `MATSHELL` shell matrix
1751 
1752    Logically Collective on mat
1753 
1754     Input Parameters:
1755 +   mat - the `MATSHELL` shell matrix
1756 -   ctx - the context
1757 
1758    Level: advanced
1759 
1760    Fortran Note:
1761     To use this from Fortran you must write a Fortran interface definition for this
1762     function that tells Fortran the Fortran derived data type that you are passing in as the ctx argument.
1763 
1764 .seealso: `MATSHELL`, `MatCreateShell()`, `MatShellGetContext()`, `MatShellGetOperation()`
1765 @*/
1766 PetscErrorCode MatShellSetContext(Mat mat, void *ctx)
1767 {
1768   PetscFunctionBegin;
1769   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
1770   PetscTryMethod(mat, "MatShellSetContext_C", (Mat, void *), (mat, ctx));
1771   PetscFunctionReturn(0);
1772 }
1773 
1774 /*@
1775     MatShellSetContextDestroy - sets the destroy function for a `MATSHELL` shell matrix context
1776 
1777    Logically Collective on mat
1778 
1779     Input Parameters:
1780 +   mat - the shell matrix
1781 -   f - the context destroy function
1782 
1783     Note:
1784     If the `MatShell` is never duplicated, the behavior of this function is equivalent
1785     to `MatShellSetOperation`(`Mat`,`MATOP_DESTROY`,f). However, `MatShellSetContextDestroy()`
1786     ensures proper reference counting for the user provided context data in the case that
1787     the `MATSHELL` is duplicated.
1788 
1789    Level: advanced
1790 
1791 .seealso: `MATSHELL`, `MatCreateShell()`, `MatShellSetContext()`
1792 @*/
1793 PetscErrorCode MatShellSetContextDestroy(Mat mat, PetscErrorCode (*f)(void *))
1794 {
1795   PetscFunctionBegin;
1796   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
1797   PetscTryMethod(mat, "MatShellSetContextDestroy_C", (Mat, PetscErrorCode(*)(void *)), (mat, f));
1798   PetscFunctionReturn(0);
1799 }
1800 
1801 /*@C
1802  MatShellSetVecType - Sets the type of `Vec` returned by `MatCreateVecs()`
1803 
1804  Logically collective
1805 
1806     Input Parameters:
1807 +   mat   - the `MATSHELL` shell matrix
1808 -   vtype - type to use for creating vectors
1809 
1810  Level: advanced
1811 
1812 .seealso: `MATSHELL`, `MatCreateVecs()`
1813 @*/
1814 PetscErrorCode MatShellSetVecType(Mat mat, VecType vtype)
1815 {
1816   PetscFunctionBegin;
1817   PetscTryMethod(mat, "MatShellSetVecType_C", (Mat, VecType), (mat, vtype));
1818   PetscFunctionReturn(0);
1819 }
1820 
1821 /*@
1822     MatShellSetManageScalingShifts - Allows the user to control the scaling and shift operations of the `MATSHELL`. Must be called immediately
1823           after `MatCreateShell()`
1824 
1825    Logically Collective on A
1826 
1827     Input Parameter:
1828 .   mat - the `MATSHELL` shell matrix
1829 
1830   Level: advanced
1831 
1832 .seealso: `MATSHELL`, `MatCreateShell()`, `MatShellGetContext()`, `MatShellGetOperation()`, `MatShellSetContext()`, `MatShellSetOperation()`
1833 @*/
1834 PetscErrorCode MatShellSetManageScalingShifts(Mat A)
1835 {
1836   PetscFunctionBegin;
1837   PetscValidHeaderSpecific(A, MAT_CLASSID, 1);
1838   PetscTryMethod(A, "MatShellSetManageScalingShifts_C", (Mat), (A));
1839   PetscFunctionReturn(0);
1840 }
1841 
1842 /*@C
1843     MatShellTestMult - Compares the multiply routine provided to the `MATSHELL` with differencing on a given function.
1844 
1845    Logically Collective on mat
1846 
1847     Input Parameters:
1848 +   mat - the `MATSHELL` shell matrix
1849 .   f - the function
1850 .   base - differences are computed around this vector, see `MatMFFDSetBase()`, for Jacobians this is the point at which the Jacobian is being evaluated
1851 -   ctx - an optional context for the function
1852 
1853    Output Parameter:
1854 .   flg - `PETSC_TRUE` if the multiply is likely correct
1855 
1856    Options Database:
1857 .   -mat_shell_test_mult_view - print if any differences are detected between the products and print the difference
1858 
1859    Level: advanced
1860 
1861    Fortran Note:
1862     Not supported from Fortran
1863 
1864 .seealso: `MATSHELL`, `MatCreateShell()`, `MatShellGetContext()`, `MatShellGetOperation()`, `MatShellTestMultTranspose()`
1865 @*/
1866 PetscErrorCode MatShellTestMult(Mat mat, PetscErrorCode (*f)(void *, Vec, Vec), Vec base, void *ctx, PetscBool *flg)
1867 {
1868   PetscInt  m, n;
1869   Mat       mf, Dmf, Dmat, Ddiff;
1870   PetscReal Diffnorm, Dmfnorm;
1871   PetscBool v = PETSC_FALSE, flag = PETSC_TRUE;
1872 
1873   PetscFunctionBegin;
1874   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
1875   PetscCall(PetscOptionsHasName(NULL, ((PetscObject)mat)->prefix, "-mat_shell_test_mult_view", &v));
1876   PetscCall(MatGetLocalSize(mat, &m, &n));
1877   PetscCall(MatCreateMFFD(PetscObjectComm((PetscObject)mat), m, n, PETSC_DECIDE, PETSC_DECIDE, &mf));
1878   PetscCall(MatMFFDSetFunction(mf, f, ctx));
1879   PetscCall(MatMFFDSetBase(mf, base, NULL));
1880 
1881   PetscCall(MatComputeOperator(mf, MATAIJ, &Dmf));
1882   PetscCall(MatComputeOperator(mat, MATAIJ, &Dmat));
1883 
1884   PetscCall(MatDuplicate(Dmat, MAT_COPY_VALUES, &Ddiff));
1885   PetscCall(MatAXPY(Ddiff, -1.0, Dmf, DIFFERENT_NONZERO_PATTERN));
1886   PetscCall(MatNorm(Ddiff, NORM_FROBENIUS, &Diffnorm));
1887   PetscCall(MatNorm(Dmf, NORM_FROBENIUS, &Dmfnorm));
1888   if (Diffnorm / Dmfnorm > 10 * PETSC_SQRT_MACHINE_EPSILON) {
1889     flag = PETSC_FALSE;
1890     if (v) {
1891       PetscCall(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)));
1892       PetscCall(MatViewFromOptions(Ddiff, (PetscObject)mat, "-mat_shell_test_mult_view"));
1893       PetscCall(MatViewFromOptions(Dmf, (PetscObject)mat, "-mat_shell_test_mult_view"));
1894       PetscCall(MatViewFromOptions(Dmat, (PetscObject)mat, "-mat_shell_test_mult_view"));
1895     }
1896   } else if (v) {
1897     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)mat), "MATSHELL and matrix free multiple appear to produce the same results\n"));
1898   }
1899   if (flg) *flg = flag;
1900   PetscCall(MatDestroy(&Ddiff));
1901   PetscCall(MatDestroy(&mf));
1902   PetscCall(MatDestroy(&Dmf));
1903   PetscCall(MatDestroy(&Dmat));
1904   PetscFunctionReturn(0);
1905 }
1906 
1907 /*@C
1908     MatShellTestMultTranspose - Compares the multiply transpose routine provided to the `MATSHELL` with differencing on a given function.
1909 
1910    Logically Collective on mat
1911 
1912     Input Parameters:
1913 +   mat - the `MATSHELL` shell matrix
1914 .   f - the function
1915 .   base - differences are computed around this vector, see `MatMFFDSetBase()`, for Jacobians this is the point at which the Jacobian is being evaluated
1916 -   ctx - an optional context for the function
1917 
1918    Output Parameter:
1919 .   flg - `PETSC_TRUE` if the multiply is likely correct
1920 
1921    Options Database:
1922 .   -mat_shell_test_mult_view - print if any differences are detected between the products and print the difference
1923 
1924    Level: advanced
1925 
1926    Fortran Note:
1927     Not supported from Fortran
1928 
1929 .seealso: `MATSHELL`, `MatCreateShell()`, `MatShellGetContext()`, `MatShellGetOperation()`, `MatShellTestMult()`
1930 @*/
1931 PetscErrorCode MatShellTestMultTranspose(Mat mat, PetscErrorCode (*f)(void *, Vec, Vec), Vec base, void *ctx, PetscBool *flg)
1932 {
1933   Vec       x, y, z;
1934   PetscInt  m, n, M, N;
1935   Mat       mf, Dmf, Dmat, Ddiff;
1936   PetscReal Diffnorm, Dmfnorm;
1937   PetscBool v = PETSC_FALSE, flag = PETSC_TRUE;
1938 
1939   PetscFunctionBegin;
1940   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
1941   PetscCall(PetscOptionsHasName(NULL, ((PetscObject)mat)->prefix, "-mat_shell_test_mult_transpose_view", &v));
1942   PetscCall(MatCreateVecs(mat, &x, &y));
1943   PetscCall(VecDuplicate(y, &z));
1944   PetscCall(MatGetLocalSize(mat, &m, &n));
1945   PetscCall(MatGetSize(mat, &M, &N));
1946   PetscCall(MatCreateMFFD(PetscObjectComm((PetscObject)mat), m, n, M, N, &mf));
1947   PetscCall(MatMFFDSetFunction(mf, f, ctx));
1948   PetscCall(MatMFFDSetBase(mf, base, NULL));
1949   PetscCall(MatComputeOperator(mf, MATAIJ, &Dmf));
1950   PetscCall(MatTranspose(Dmf, MAT_INPLACE_MATRIX, &Dmf));
1951   PetscCall(MatComputeOperatorTranspose(mat, MATAIJ, &Dmat));
1952 
1953   PetscCall(MatDuplicate(Dmat, MAT_COPY_VALUES, &Ddiff));
1954   PetscCall(MatAXPY(Ddiff, -1.0, Dmf, DIFFERENT_NONZERO_PATTERN));
1955   PetscCall(MatNorm(Ddiff, NORM_FROBENIUS, &Diffnorm));
1956   PetscCall(MatNorm(Dmf, NORM_FROBENIUS, &Dmfnorm));
1957   if (Diffnorm / Dmfnorm > 10 * PETSC_SQRT_MACHINE_EPSILON) {
1958     flag = PETSC_FALSE;
1959     if (v) {
1960       PetscCall(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)));
1961       PetscCall(MatViewFromOptions(Ddiff, (PetscObject)mat, "-mat_shell_test_mult_transpose_view"));
1962       PetscCall(MatViewFromOptions(Dmf, (PetscObject)mat, "-mat_shell_test_mult_transpose_view"));
1963       PetscCall(MatViewFromOptions(Dmat, (PetscObject)mat, "-mat_shell_test_mult_transpose_view"));
1964     }
1965   } else if (v) {
1966     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)mat), "MATSHELL transpose and matrix free multiple appear to produce the same results\n"));
1967   }
1968   if (flg) *flg = flag;
1969   PetscCall(MatDestroy(&mf));
1970   PetscCall(MatDestroy(&Dmat));
1971   PetscCall(MatDestroy(&Ddiff));
1972   PetscCall(MatDestroy(&Dmf));
1973   PetscCall(VecDestroy(&x));
1974   PetscCall(VecDestroy(&y));
1975   PetscCall(VecDestroy(&z));
1976   PetscFunctionReturn(0);
1977 }
1978 
1979 /*@C
1980     MatShellSetOperation - Allows user to set a matrix operation for a `MATSHELL` shell matrix.
1981 
1982    Logically Collective on mat
1983 
1984     Input Parameters:
1985 +   mat - the `MATSHELL` shell matrix
1986 .   op - the name of the operation
1987 -   g - the function that provides the operation.
1988 
1989    Level: advanced
1990 
1991     Usage:
1992 $      extern PetscErrorCode usermult(Mat,Vec,Vec);
1993 $      MatCreateShell(comm,m,n,M,N,ctx,&A);
1994 $      MatShellSetOperation(A,MATOP_MULT,(void(*)(void))usermult);
1995 
1996     Notes:
1997     See the file include/petscmat.h for a complete list of matrix
1998     operations, which all have the form MATOP_<OPERATION>, where
1999     <OPERATION> is the name (in all capital letters) of the
2000     user interface routine (e.g., `MatMult()` -> `MATOP_MULT`).
2001 
2002     All user-provided functions (except for `MATOP_DESTROY`) should have the same calling
2003     sequence as the usual matrix interface routines, since they
2004     are intended to be accessed via the usual matrix interface
2005     routines, e.g.,
2006 $       MatMult(Mat,Vec,Vec) -> usermult(Mat,Vec,Vec)
2007 
2008     In particular each function MUST return an error code of 0 on success and
2009     nonzero on failure.
2010 
2011     Within each user-defined routine, the user should call
2012     `MatShellGetContext()` to obtain the user-defined context that was
2013     set by `MatCreateShell()`.
2014 
2015     Use `MatSetOperation()` to set an operation for any matrix type. For matrix product operations (i.e. `MatMatXXX()`, `MatTransposeMatXXX()` etc) use `MatShellSetMatProductOperation()`
2016 
2017     Fortran Note:
2018     For `MatCreateVecs()` the user code should check if the input left or right matrix is -1 and in that case not
2019        generate a matrix. See src/mat/tests/ex120f.F
2020 
2021 .seealso: `MATSHELL`, `MatCreateShell()`, `MatShellGetContext()`, `MatShellGetOperation()`, `MatShellSetContext()`, `MatSetOperation()`, `MatShellSetManageScalingShifts()`, `MatShellSetMatProductOperation()`
2022 @*/
2023 PetscErrorCode MatShellSetOperation(Mat mat, MatOperation op, void (*g)(void))
2024 {
2025   PetscFunctionBegin;
2026   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
2027   PetscTryMethod(mat, "MatShellSetOperation_C", (Mat, MatOperation, void (*)(void)), (mat, op, g));
2028   PetscFunctionReturn(0);
2029 }
2030 
2031 /*@C
2032     MatShellGetOperation - Gets a matrix function for a `MATSHELL` shell matrix.
2033 
2034     Not Collective
2035 
2036     Input Parameters:
2037 +   mat - the `MATSHELL` shell matrix
2038 -   op - the name of the operation
2039 
2040     Output Parameter:
2041 .   g - the function that provides the operation.
2042 
2043     Level: advanced
2044 
2045     Note:
2046     See the file include/petscmat.h for a complete list of matrix
2047     operations, which all have the form MATOP_<OPERATION>, where
2048     <OPERATION> is the name (in all capital letters) of the
2049     user interface routine (e.g., `MatMult()` -> `MATOP_MULT`).
2050 
2051     All user-provided functions have the same calling
2052     sequence as the usual matrix interface routines, since they
2053     are intended to be accessed via the usual matrix interface
2054     routines, e.g.,
2055 $       MatMult(Mat,Vec,Vec) -> usermult(Mat,Vec,Vec)
2056 
2057     Within each user-defined routine, the user should call
2058     `MatShellGetContext()` to obtain the user-defined context that was
2059     set by `MatCreateShell()`.
2060 
2061 .seealso: `MATSHELL`, `MatCreateShell()`, `MatShellGetContext()`, `MatShellSetOperation()`, `MatShellSetContext()`
2062 @*/
2063 PetscErrorCode MatShellGetOperation(Mat mat, MatOperation op, void (**g)(void))
2064 {
2065   PetscFunctionBegin;
2066   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
2067   PetscUseMethod(mat, "MatShellGetOperation_C", (Mat, MatOperation, void (**)(void)), (mat, op, g));
2068   PetscFunctionReturn(0);
2069 }
2070 
2071 /*@
2072     MatIsShell - Inquires if a matrix is derived from `MATSHELL`
2073 
2074     Input Parameter:
2075 .   mat - the matrix
2076 
2077     Output Parameter:
2078 .   flg - the boolean value
2079 
2080     Level: developer
2081 
2082     Developer Note:
2083     In the future, we should allow the object type name to be changed still using the `MATSHELL` data structure for other matrices (i.e. `MATTRANSPOSEVIRTUAL`, `MATSCHURCOMPLEMENT` etc)
2084 
2085 .seealso: `MATSHELL`, `MATMFFD`, `MatCreateShell()`, `MATTRANSPOSEVIRTUAL`, `MATSCHURCOMPLEMENT`
2086 @*/
2087 PetscErrorCode MatIsShell(Mat mat, PetscBool *flg)
2088 {
2089   PetscFunctionBegin;
2090   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
2091   PetscValidBoolPointer(flg, 2);
2092   *flg = (PetscBool)(mat->ops->destroy == MatDestroy_Shell);
2093   PetscFunctionReturn(0);
2094 }
2095