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