xref: /petsc/src/mat/impls/shell/shell.c (revision fbf9dbe564678ed6eff1806adbc4c4f01b9743f4)
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(PETSC_SUCCESS);
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(PETSC_SUCCESS);
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(PETSC_SUCCESS);
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(PETSC_SUCCESS);
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(PETSC_SUCCESS);
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(PETSC_SUCCESS);
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(PETSC_SUCCESS);
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(PETSC_SUCCESS);
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(PETSC_SUCCESS);
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(PETSC_SUCCESS);
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     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: [](ch_matrices), `Mat`, `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(PETSC_SUCCESS);
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(PETSC_SUCCESS);
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(PETSC_SUCCESS);
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(PETSC_SUCCESS);
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(PETSC_SUCCESS);
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(PETSC_SUCCESS);
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(PETSC_SUCCESS);
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 = PETSC_FALSE;
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(PETSC_SUCCESS);
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(PETSC_SUCCESS);
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(PETSC_SUCCESS);
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(PETSC_SUCCESS);
842 }
843 
844 /*@C
845     MatShellSetMatProductOperation - Allows user to set a matrix matrix operation for a `MATSHELL` shell matrix.
846 
847    Logically Collective; No Fortran Support
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 .vb
862       extern PetscErrorCode usersymbolic(Mat,Mat,Mat,void**);
863       extern PetscErrorCode usernumeric(Mat,Mat,Mat,void*);
864       extern PetscErrorCode userdestroy(void*);
865       MatCreateShell(comm,m,n,M,N,ctx,&A);
866       MatShellSetMatProductOperation(A,MATPRODUCT_AB,usersymbolic,usernumeric,userdestroy,MATSEQAIJ,MATDENSE);
867       [ create B of type SEQAIJ etc..]
868       MatProductCreate(A,B,NULL,&C);
869       MatProductSetType(C,MATPRODUCT_AB);
870       MatProductSetFromOptions(C);
871       MatProductSymbolic(C); -> actually runs the user defined symbolic operation
872       MatProductNumeric(C); -> actually runs the user defined numeric operation
873       [ use C = A*B ]
874 .ve
875 
876     Notes:
877     `MATPRODUCT_ABC` is not supported yet.
878 
879     If the symbolic phase is not specified, `MatSetUp()` is called on the result matrix that must have its type set if Ctype is `NULL`.
880 
881     Any additional data needed by the matrix product needs to be returned during the symbolic phase and destroyed with the destroy callback.
882     PETSc will take care of calling the user-defined callbacks.
883     It is allowed to specify the same callbacks for different Btype matrix types.
884     The couple (Btype,ptype) uniquely identifies the operation, the last specified callbacks takes precedence.
885 
886 .seealso: [](ch_matrices), `Mat`, `MATSHELL`, `MatCreateShell()`, `MatShellGetContext()`, `MatShellGetOperation()`, `MatShellSetContext()`, `MatSetOperation()`, `MatProductType`, `MatType`, `MatSetUp()`
887 @*/
888 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)
889 {
890   PetscFunctionBegin;
891   PetscValidHeaderSpecific(A, MAT_CLASSID, 1);
892   PetscValidLogicalCollectiveEnum(A, ptype, 2);
893   PetscCheck(ptype != MATPRODUCT_ABC, PetscObjectComm((PetscObject)A), PETSC_ERR_SUP, "Not for product type %s", MatProductTypes[ptype]);
894   PetscCheck(numeric, PetscObjectComm((PetscObject)A), PETSC_ERR_USER, "Missing numeric routine, argument 4");
895   PetscValidCharPointer(Btype, 6);
896   if (Ctype) PetscValidCharPointer(Ctype, 7);
897   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));
898   PetscFunctionReturn(PETSC_SUCCESS);
899 }
900 
901 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)
902 {
903   PetscBool   flg;
904   char        composedname[256];
905   MatRootName Bnames = MatRootNameList, Cnames = MatRootNameList;
906   PetscMPIInt size;
907 
908   PetscFunctionBegin;
909   PetscValidType(A, 1);
910   while (Bnames) { /* user passed in the root name */
911     PetscCall(PetscStrcmp(Btype, Bnames->rname, &flg));
912     if (flg) break;
913     Bnames = Bnames->next;
914   }
915   while (Cnames) { /* user passed in the root name */
916     PetscCall(PetscStrcmp(Ctype, Cnames->rname, &flg));
917     if (flg) break;
918     Cnames = Cnames->next;
919   }
920   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)A), &size));
921   Btype = Bnames ? (size > 1 ? Bnames->mname : Bnames->sname) : Btype;
922   Ctype = Cnames ? (size > 1 ? Cnames->mname : Cnames->sname) : Ctype;
923   PetscCall(PetscSNPrintf(composedname, sizeof(composedname), "MatProductSetFromOptions_%s_%s_C", ((PetscObject)A)->type_name, Btype));
924   PetscCall(MatShellSetMatProductOperation_Private(A, ptype, symbolic, numeric, destroy, composedname, Ctype));
925   PetscFunctionReturn(PETSC_SUCCESS);
926 }
927 
928 static PetscErrorCode MatCopy_Shell(Mat A, Mat B, MatStructure str)
929 {
930   Mat_Shell              *shellA = (Mat_Shell *)A->data, *shellB = (Mat_Shell *)B->data;
931   PetscBool               matflg;
932   MatShellMatFunctionList matmatA;
933 
934   PetscFunctionBegin;
935   PetscCall(MatIsShell(B, &matflg));
936   PetscCheck(matflg, PetscObjectComm((PetscObject)A), PETSC_ERR_ARG_WRONG, "Matrix %s not derived from MATSHELL", ((PetscObject)B)->type_name);
937 
938   B->ops[0]      = A->ops[0];
939   shellB->ops[0] = shellA->ops[0];
940 
941   if (shellA->ops->copy) PetscCall((*shellA->ops->copy)(A, B, str));
942   shellB->vscale = shellA->vscale;
943   shellB->vshift = shellA->vshift;
944   if (shellA->dshift) {
945     if (!shellB->dshift) PetscCall(VecDuplicate(shellA->dshift, &shellB->dshift));
946     PetscCall(VecCopy(shellA->dshift, shellB->dshift));
947   } else {
948     PetscCall(VecDestroy(&shellB->dshift));
949   }
950   if (shellA->left) {
951     if (!shellB->left) PetscCall(VecDuplicate(shellA->left, &shellB->left));
952     PetscCall(VecCopy(shellA->left, shellB->left));
953   } else {
954     PetscCall(VecDestroy(&shellB->left));
955   }
956   if (shellA->right) {
957     if (!shellB->right) PetscCall(VecDuplicate(shellA->right, &shellB->right));
958     PetscCall(VecCopy(shellA->right, shellB->right));
959   } else {
960     PetscCall(VecDestroy(&shellB->right));
961   }
962   PetscCall(MatDestroy(&shellB->axpy));
963   shellB->axpy_vscale = 0.0;
964   shellB->axpy_state  = 0;
965   if (shellA->axpy) {
966     PetscCall(PetscObjectReference((PetscObject)shellA->axpy));
967     shellB->axpy        = shellA->axpy;
968     shellB->axpy_vscale = shellA->axpy_vscale;
969     shellB->axpy_state  = shellA->axpy_state;
970   }
971   if (shellA->zrows) {
972     PetscCall(ISDuplicate(shellA->zrows, &shellB->zrows));
973     if (shellA->zcols) PetscCall(ISDuplicate(shellA->zcols, &shellB->zcols));
974     PetscCall(VecDuplicate(shellA->zvals, &shellB->zvals));
975     PetscCall(VecCopy(shellA->zvals, shellB->zvals));
976     PetscCall(VecDuplicate(shellA->zvals_w, &shellB->zvals_w));
977     PetscCall(PetscObjectReference((PetscObject)shellA->zvals_sct_r));
978     PetscCall(PetscObjectReference((PetscObject)shellA->zvals_sct_c));
979     shellB->zvals_sct_r = shellA->zvals_sct_r;
980     shellB->zvals_sct_c = shellA->zvals_sct_c;
981   }
982 
983   matmatA = shellA->matmat;
984   if (matmatA) {
985     while (matmatA->next) {
986       PetscCall(MatShellSetMatProductOperation_Private(B, matmatA->ptype, matmatA->symbolic, matmatA->numeric, matmatA->destroy, matmatA->composedname, matmatA->resultname));
987       matmatA = matmatA->next;
988     }
989   }
990   PetscFunctionReturn(PETSC_SUCCESS);
991 }
992 
993 static PetscErrorCode MatDuplicate_Shell(Mat mat, MatDuplicateOption op, Mat *M)
994 {
995   PetscFunctionBegin;
996   PetscCall(MatCreateShell(PetscObjectComm((PetscObject)mat), mat->rmap->n, mat->cmap->n, mat->rmap->N, mat->cmap->N, NULL, M));
997   ((Mat_Shell *)(*M)->data)->ctxcontainer = ((Mat_Shell *)mat->data)->ctxcontainer;
998   PetscCall(PetscObjectCompose((PetscObject)(*M), "MatShell ctx", (PetscObject)((Mat_Shell *)(*M)->data)->ctxcontainer));
999   PetscCall(PetscObjectChangeTypeName((PetscObject)(*M), ((PetscObject)mat)->type_name));
1000   if (op != MAT_DO_NOT_COPY_VALUES) PetscCall(MatCopy(mat, *M, SAME_NONZERO_PATTERN));
1001   PetscFunctionReturn(PETSC_SUCCESS);
1002 }
1003 
1004 PetscErrorCode MatMult_Shell(Mat A, Vec x, Vec y)
1005 {
1006   Mat_Shell       *shell = (Mat_Shell *)A->data;
1007   Vec              xx;
1008   PetscObjectState instate, outstate;
1009 
1010   PetscFunctionBegin;
1011   PetscCall(MatShellPreZeroRight(A, x, &xx));
1012   PetscCall(MatShellPreScaleRight(A, xx, &xx));
1013   PetscCall(PetscObjectStateGet((PetscObject)y, &instate));
1014   PetscCall((*shell->ops->mult)(A, xx, y));
1015   PetscCall(PetscObjectStateGet((PetscObject)y, &outstate));
1016   if (instate == outstate) {
1017     /* increase the state of the output vector since the user did not update its state themself as should have been done */
1018     PetscCall(PetscObjectStateIncrease((PetscObject)y));
1019   }
1020   PetscCall(MatShellShiftAndScale(A, xx, y));
1021   PetscCall(MatShellPostScaleLeft(A, y));
1022   PetscCall(MatShellPostZeroLeft(A, y));
1023 
1024   if (shell->axpy) {
1025     Mat              X;
1026     PetscObjectState axpy_state;
1027 
1028     PetscCall(MatShellGetContext(shell->axpy, &X));
1029     PetscCall(PetscObjectStateGet((PetscObject)X, &axpy_state));
1030     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,...)");
1031 
1032     PetscCall(MatCreateVecs(shell->axpy, shell->axpy_right ? NULL : &shell->axpy_right, shell->axpy_left ? NULL : &shell->axpy_left));
1033     PetscCall(VecCopy(x, shell->axpy_right));
1034     PetscCall(MatMult(shell->axpy, shell->axpy_right, shell->axpy_left));
1035     PetscCall(VecAXPY(y, shell->axpy_vscale, shell->axpy_left));
1036   }
1037   PetscFunctionReturn(PETSC_SUCCESS);
1038 }
1039 
1040 static PetscErrorCode MatMultAdd_Shell(Mat A, Vec x, Vec y, Vec z)
1041 {
1042   Mat_Shell *shell = (Mat_Shell *)A->data;
1043 
1044   PetscFunctionBegin;
1045   if (y == z) {
1046     if (!shell->right_add_work) PetscCall(VecDuplicate(z, &shell->right_add_work));
1047     PetscCall(MatMult(A, x, shell->right_add_work));
1048     PetscCall(VecAXPY(z, 1.0, shell->right_add_work));
1049   } else {
1050     PetscCall(MatMult(A, x, z));
1051     PetscCall(VecAXPY(z, 1.0, y));
1052   }
1053   PetscFunctionReturn(PETSC_SUCCESS);
1054 }
1055 
1056 static PetscErrorCode MatMultTranspose_Shell(Mat A, Vec x, Vec y)
1057 {
1058   Mat_Shell       *shell = (Mat_Shell *)A->data;
1059   Vec              xx;
1060   PetscObjectState instate, outstate;
1061 
1062   PetscFunctionBegin;
1063   PetscCall(MatShellPreZeroLeft(A, x, &xx));
1064   PetscCall(MatShellPreScaleLeft(A, xx, &xx));
1065   PetscCall(PetscObjectStateGet((PetscObject)y, &instate));
1066   PetscCall((*shell->ops->multtranspose)(A, xx, y));
1067   PetscCall(PetscObjectStateGet((PetscObject)y, &outstate));
1068   if (instate == outstate) {
1069     /* increase the state of the output vector since the user did not update its state themself as should have been done */
1070     PetscCall(PetscObjectStateIncrease((PetscObject)y));
1071   }
1072   PetscCall(MatShellShiftAndScale(A, xx, y));
1073   PetscCall(MatShellPostScaleRight(A, y));
1074   PetscCall(MatShellPostZeroRight(A, y));
1075 
1076   if (shell->axpy) {
1077     Mat              X;
1078     PetscObjectState axpy_state;
1079 
1080     PetscCall(MatShellGetContext(shell->axpy, &X));
1081     PetscCall(PetscObjectStateGet((PetscObject)X, &axpy_state));
1082     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,...)");
1083     PetscCall(MatCreateVecs(shell->axpy, shell->axpy_right ? NULL : &shell->axpy_right, shell->axpy_left ? NULL : &shell->axpy_left));
1084     PetscCall(VecCopy(x, shell->axpy_left));
1085     PetscCall(MatMultTranspose(shell->axpy, shell->axpy_left, shell->axpy_right));
1086     PetscCall(VecAXPY(y, shell->axpy_vscale, shell->axpy_right));
1087   }
1088   PetscFunctionReturn(PETSC_SUCCESS);
1089 }
1090 
1091 static PetscErrorCode MatMultTransposeAdd_Shell(Mat A, Vec x, Vec y, Vec z)
1092 {
1093   Mat_Shell *shell = (Mat_Shell *)A->data;
1094 
1095   PetscFunctionBegin;
1096   if (y == z) {
1097     if (!shell->left_add_work) PetscCall(VecDuplicate(z, &shell->left_add_work));
1098     PetscCall(MatMultTranspose(A, x, shell->left_add_work));
1099     PetscCall(VecAXPY(z, 1.0, shell->left_add_work));
1100   } else {
1101     PetscCall(MatMultTranspose(A, x, z));
1102     PetscCall(VecAXPY(z, 1.0, y));
1103   }
1104   PetscFunctionReturn(PETSC_SUCCESS);
1105 }
1106 
1107 /*
1108           diag(left)(vscale*A + diag(dshift) + vshift I)diag(right)
1109 */
1110 static PetscErrorCode MatGetDiagonal_Shell(Mat A, Vec v)
1111 {
1112   Mat_Shell *shell = (Mat_Shell *)A->data;
1113 
1114   PetscFunctionBegin;
1115   if (shell->ops->getdiagonal) {
1116     PetscCall((*shell->ops->getdiagonal)(A, v));
1117   } else SETERRQ(PetscObjectComm((PetscObject)A), PETSC_ERR_ARG_WRONGSTATE, "Must provide shell matrix with routine to return diagonal using\nMatShellSetOperation(S,MATOP_GET_DIAGONAL,...)");
1118   PetscCall(VecScale(v, shell->vscale));
1119   if (shell->dshift) PetscCall(VecAXPY(v, 1.0, shell->dshift));
1120   PetscCall(VecShift(v, shell->vshift));
1121   if (shell->left) PetscCall(VecPointwiseMult(v, v, shell->left));
1122   if (shell->right) PetscCall(VecPointwiseMult(v, v, shell->right));
1123   if (shell->zrows) {
1124     PetscCall(VecScatterBegin(shell->zvals_sct_r, shell->zvals, v, INSERT_VALUES, SCATTER_REVERSE));
1125     PetscCall(VecScatterEnd(shell->zvals_sct_r, shell->zvals, v, INSERT_VALUES, SCATTER_REVERSE));
1126   }
1127   if (shell->axpy) {
1128     Mat              X;
1129     PetscObjectState axpy_state;
1130 
1131     PetscCall(MatShellGetContext(shell->axpy, &X));
1132     PetscCall(PetscObjectStateGet((PetscObject)X, &axpy_state));
1133     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,...)");
1134     PetscCall(MatCreateVecs(shell->axpy, NULL, shell->axpy_left ? NULL : &shell->axpy_left));
1135     PetscCall(MatGetDiagonal(shell->axpy, shell->axpy_left));
1136     PetscCall(VecAXPY(v, shell->axpy_vscale, shell->axpy_left));
1137   }
1138   PetscFunctionReturn(PETSC_SUCCESS);
1139 }
1140 
1141 static PetscErrorCode MatShift_Shell(Mat Y, PetscScalar a)
1142 {
1143   Mat_Shell *shell = (Mat_Shell *)Y->data;
1144   PetscBool  flg;
1145 
1146   PetscFunctionBegin;
1147   PetscCall(MatHasCongruentLayouts(Y, &flg));
1148   PetscCheck(flg, PetscObjectComm((PetscObject)Y), PETSC_ERR_SUP, "Cannot shift shell matrix if it is not congruent");
1149   if (shell->left || shell->right) {
1150     if (!shell->dshift) {
1151       PetscCall(VecDuplicate(shell->left ? shell->left : shell->right, &shell->dshift));
1152       PetscCall(VecSet(shell->dshift, a));
1153     } else {
1154       if (shell->left) PetscCall(VecPointwiseMult(shell->dshift, shell->dshift, shell->left));
1155       if (shell->right) PetscCall(VecPointwiseMult(shell->dshift, shell->dshift, shell->right));
1156       PetscCall(VecShift(shell->dshift, a));
1157     }
1158     if (shell->left) PetscCall(VecPointwiseDivide(shell->dshift, shell->dshift, shell->left));
1159     if (shell->right) PetscCall(VecPointwiseDivide(shell->dshift, shell->dshift, shell->right));
1160   } else shell->vshift += a;
1161   if (shell->zrows) PetscCall(VecShift(shell->zvals, a));
1162   PetscFunctionReturn(PETSC_SUCCESS);
1163 }
1164 
1165 static PetscErrorCode MatDiagonalSet_Shell_Private(Mat A, Vec D, PetscScalar s)
1166 {
1167   Mat_Shell *shell = (Mat_Shell *)A->data;
1168 
1169   PetscFunctionBegin;
1170   if (!shell->dshift) PetscCall(VecDuplicate(D, &shell->dshift));
1171   if (shell->left || shell->right) {
1172     if (!shell->right_work) PetscCall(VecDuplicate(shell->left ? shell->left : shell->right, &shell->right_work));
1173     if (shell->left && shell->right) {
1174       PetscCall(VecPointwiseDivide(shell->right_work, D, shell->left));
1175       PetscCall(VecPointwiseDivide(shell->right_work, shell->right_work, shell->right));
1176     } else if (shell->left) {
1177       PetscCall(VecPointwiseDivide(shell->right_work, D, shell->left));
1178     } else {
1179       PetscCall(VecPointwiseDivide(shell->right_work, D, shell->right));
1180     }
1181     PetscCall(VecAXPY(shell->dshift, s, shell->right_work));
1182   } else {
1183     PetscCall(VecAXPY(shell->dshift, s, D));
1184   }
1185   PetscFunctionReturn(PETSC_SUCCESS);
1186 }
1187 
1188 PetscErrorCode MatDiagonalSet_Shell(Mat A, Vec D, InsertMode ins)
1189 {
1190   Mat_Shell *shell = (Mat_Shell *)A->data;
1191   Vec        d;
1192   PetscBool  flg;
1193 
1194   PetscFunctionBegin;
1195   PetscCall(MatHasCongruentLayouts(A, &flg));
1196   PetscCheck(flg, PetscObjectComm((PetscObject)A), PETSC_ERR_SUP, "Cannot diagonal set or shift shell matrix if it is not congruent");
1197   if (ins == INSERT_VALUES) {
1198     PetscCall(VecDuplicate(D, &d));
1199     PetscCall(MatGetDiagonal(A, d));
1200     PetscCall(MatDiagonalSet_Shell_Private(A, d, -1.));
1201     PetscCall(MatDiagonalSet_Shell_Private(A, D, 1.));
1202     PetscCall(VecDestroy(&d));
1203     if (shell->zrows) PetscCall(VecCopy(D, shell->zvals));
1204   } else {
1205     PetscCall(MatDiagonalSet_Shell_Private(A, D, 1.));
1206     if (shell->zrows) PetscCall(VecAXPY(shell->zvals, 1.0, D));
1207   }
1208   PetscFunctionReturn(PETSC_SUCCESS);
1209 }
1210 
1211 static PetscErrorCode MatScale_Shell(Mat Y, PetscScalar a)
1212 {
1213   Mat_Shell *shell = (Mat_Shell *)Y->data;
1214 
1215   PetscFunctionBegin;
1216   shell->vscale *= a;
1217   shell->vshift *= a;
1218   if (shell->dshift) PetscCall(VecScale(shell->dshift, a));
1219   shell->axpy_vscale *= a;
1220   if (shell->zrows) PetscCall(VecScale(shell->zvals, a));
1221   PetscFunctionReturn(PETSC_SUCCESS);
1222 }
1223 
1224 static PetscErrorCode MatDiagonalScale_Shell(Mat Y, Vec left, Vec right)
1225 {
1226   Mat_Shell *shell = (Mat_Shell *)Y->data;
1227 
1228   PetscFunctionBegin;
1229   if (left) {
1230     if (!shell->left) {
1231       PetscCall(VecDuplicate(left, &shell->left));
1232       PetscCall(VecCopy(left, shell->left));
1233     } else {
1234       PetscCall(VecPointwiseMult(shell->left, shell->left, left));
1235     }
1236     if (shell->zrows) PetscCall(VecPointwiseMult(shell->zvals, shell->zvals, left));
1237   }
1238   if (right) {
1239     if (!shell->right) {
1240       PetscCall(VecDuplicate(right, &shell->right));
1241       PetscCall(VecCopy(right, shell->right));
1242     } else {
1243       PetscCall(VecPointwiseMult(shell->right, shell->right, right));
1244     }
1245     if (shell->zrows) {
1246       if (!shell->left_work) PetscCall(MatCreateVecs(Y, NULL, &shell->left_work));
1247       PetscCall(VecSet(shell->zvals_w, 1.0));
1248       PetscCall(VecScatterBegin(shell->zvals_sct_c, right, shell->zvals_w, INSERT_VALUES, SCATTER_FORWARD));
1249       PetscCall(VecScatterEnd(shell->zvals_sct_c, right, shell->zvals_w, INSERT_VALUES, SCATTER_FORWARD));
1250       PetscCall(VecPointwiseMult(shell->zvals, shell->zvals, shell->zvals_w));
1251     }
1252   }
1253   if (shell->axpy) PetscCall(MatDiagonalScale(shell->axpy, left, right));
1254   PetscFunctionReturn(PETSC_SUCCESS);
1255 }
1256 
1257 PetscErrorCode MatAssemblyEnd_Shell(Mat Y, MatAssemblyType t)
1258 {
1259   Mat_Shell *shell = (Mat_Shell *)Y->data;
1260 
1261   PetscFunctionBegin;
1262   if (t == MAT_FINAL_ASSEMBLY) {
1263     shell->vshift      = 0.0;
1264     shell->vscale      = 1.0;
1265     shell->axpy_vscale = 0.0;
1266     shell->axpy_state  = 0;
1267     PetscCall(VecDestroy(&shell->dshift));
1268     PetscCall(VecDestroy(&shell->left));
1269     PetscCall(VecDestroy(&shell->right));
1270     PetscCall(MatDestroy(&shell->axpy));
1271     PetscCall(VecDestroy(&shell->axpy_left));
1272     PetscCall(VecDestroy(&shell->axpy_right));
1273     PetscCall(VecScatterDestroy(&shell->zvals_sct_c));
1274     PetscCall(VecScatterDestroy(&shell->zvals_sct_r));
1275     PetscCall(ISDestroy(&shell->zrows));
1276     PetscCall(ISDestroy(&shell->zcols));
1277   }
1278   PetscFunctionReturn(PETSC_SUCCESS);
1279 }
1280 
1281 static PetscErrorCode MatMissingDiagonal_Shell(Mat A, PetscBool *missing, PetscInt *d)
1282 {
1283   PetscFunctionBegin;
1284   *missing = PETSC_FALSE;
1285   PetscFunctionReturn(PETSC_SUCCESS);
1286 }
1287 
1288 PetscErrorCode MatAXPY_Shell(Mat Y, PetscScalar a, Mat X, MatStructure str)
1289 {
1290   Mat_Shell *shell = (Mat_Shell *)Y->data;
1291 
1292   PetscFunctionBegin;
1293   if (X == Y) {
1294     PetscCall(MatScale(Y, 1.0 + a));
1295     PetscFunctionReturn(PETSC_SUCCESS);
1296   }
1297   if (!shell->axpy) {
1298     PetscCall(MatConvertFrom_Shell(X, MATSHELL, MAT_INITIAL_MATRIX, &shell->axpy));
1299     shell->axpy_vscale = a;
1300     PetscCall(PetscObjectStateGet((PetscObject)X, &shell->axpy_state));
1301   } else {
1302     PetscCall(MatAXPY(shell->axpy, a / shell->axpy_vscale, X, str));
1303   }
1304   PetscFunctionReturn(PETSC_SUCCESS);
1305 }
1306 
1307 static struct _MatOps MatOps_Values = {NULL,
1308                                        NULL,
1309                                        NULL,
1310                                        NULL,
1311                                        /* 4*/ MatMultAdd_Shell,
1312                                        NULL,
1313                                        MatMultTransposeAdd_Shell,
1314                                        NULL,
1315                                        NULL,
1316                                        NULL,
1317                                        /*10*/ NULL,
1318                                        NULL,
1319                                        NULL,
1320                                        NULL,
1321                                        NULL,
1322                                        /*15*/ NULL,
1323                                        NULL,
1324                                        NULL,
1325                                        MatDiagonalScale_Shell,
1326                                        NULL,
1327                                        /*20*/ NULL,
1328                                        MatAssemblyEnd_Shell,
1329                                        NULL,
1330                                        NULL,
1331                                        /*24*/ MatZeroRows_Shell,
1332                                        NULL,
1333                                        NULL,
1334                                        NULL,
1335                                        NULL,
1336                                        /*29*/ NULL,
1337                                        NULL,
1338                                        NULL,
1339                                        NULL,
1340                                        NULL,
1341                                        /*34*/ MatDuplicate_Shell,
1342                                        NULL,
1343                                        NULL,
1344                                        NULL,
1345                                        NULL,
1346                                        /*39*/ MatAXPY_Shell,
1347                                        NULL,
1348                                        NULL,
1349                                        NULL,
1350                                        MatCopy_Shell,
1351                                        /*44*/ NULL,
1352                                        MatScale_Shell,
1353                                        MatShift_Shell,
1354                                        MatDiagonalSet_Shell,
1355                                        MatZeroRowsColumns_Shell,
1356                                        /*49*/ NULL,
1357                                        NULL,
1358                                        NULL,
1359                                        NULL,
1360                                        NULL,
1361                                        /*54*/ NULL,
1362                                        NULL,
1363                                        NULL,
1364                                        NULL,
1365                                        NULL,
1366                                        /*59*/ NULL,
1367                                        MatDestroy_Shell,
1368                                        NULL,
1369                                        MatConvertFrom_Shell,
1370                                        NULL,
1371                                        /*64*/ NULL,
1372                                        NULL,
1373                                        NULL,
1374                                        NULL,
1375                                        NULL,
1376                                        /*69*/ NULL,
1377                                        NULL,
1378                                        MatConvert_Shell,
1379                                        NULL,
1380                                        NULL,
1381                                        /*74*/ NULL,
1382                                        NULL,
1383                                        NULL,
1384                                        NULL,
1385                                        NULL,
1386                                        /*79*/ NULL,
1387                                        NULL,
1388                                        NULL,
1389                                        NULL,
1390                                        NULL,
1391                                        /*84*/ NULL,
1392                                        NULL,
1393                                        NULL,
1394                                        NULL,
1395                                        NULL,
1396                                        /*89*/ NULL,
1397                                        NULL,
1398                                        NULL,
1399                                        NULL,
1400                                        NULL,
1401                                        /*94*/ NULL,
1402                                        NULL,
1403                                        NULL,
1404                                        NULL,
1405                                        NULL,
1406                                        /*99*/ NULL,
1407                                        NULL,
1408                                        NULL,
1409                                        NULL,
1410                                        NULL,
1411                                        /*104*/ NULL,
1412                                        NULL,
1413                                        NULL,
1414                                        NULL,
1415                                        NULL,
1416                                        /*109*/ NULL,
1417                                        NULL,
1418                                        NULL,
1419                                        NULL,
1420                                        MatMissingDiagonal_Shell,
1421                                        /*114*/ NULL,
1422                                        NULL,
1423                                        NULL,
1424                                        NULL,
1425                                        NULL,
1426                                        /*119*/ NULL,
1427                                        NULL,
1428                                        NULL,
1429                                        NULL,
1430                                        NULL,
1431                                        /*124*/ NULL,
1432                                        NULL,
1433                                        NULL,
1434                                        NULL,
1435                                        NULL,
1436                                        /*129*/ NULL,
1437                                        NULL,
1438                                        NULL,
1439                                        NULL,
1440                                        NULL,
1441                                        /*134*/ NULL,
1442                                        NULL,
1443                                        NULL,
1444                                        NULL,
1445                                        NULL,
1446                                        /*139*/ NULL,
1447                                        NULL,
1448                                        NULL,
1449                                        NULL,
1450                                        NULL,
1451                                        /*144*/ NULL,
1452                                        NULL,
1453                                        NULL,
1454                                        NULL,
1455                                        NULL,
1456                                        NULL,
1457                                        /*150*/ NULL,
1458                                        NULL};
1459 
1460 static PetscErrorCode MatShellSetContext_Shell(Mat mat, void *ctx)
1461 {
1462   Mat_Shell *shell = (Mat_Shell *)mat->data;
1463 
1464   PetscFunctionBegin;
1465   if (ctx) {
1466     PetscContainer ctxcontainer;
1467     PetscCall(PetscContainerCreate(PetscObjectComm((PetscObject)mat), &ctxcontainer));
1468     PetscCall(PetscContainerSetPointer(ctxcontainer, ctx));
1469     PetscCall(PetscObjectCompose((PetscObject)mat, "MatShell ctx", (PetscObject)ctxcontainer));
1470     shell->ctxcontainer = ctxcontainer;
1471     PetscCall(PetscContainerDestroy(&ctxcontainer));
1472   } else {
1473     PetscCall(PetscObjectCompose((PetscObject)mat, "MatShell ctx", NULL));
1474     shell->ctxcontainer = NULL;
1475   }
1476 
1477   PetscFunctionReturn(PETSC_SUCCESS);
1478 }
1479 
1480 PetscErrorCode MatShellSetContextDestroy_Shell(Mat mat, PetscErrorCode (*f)(void *))
1481 {
1482   Mat_Shell *shell = (Mat_Shell *)mat->data;
1483 
1484   PetscFunctionBegin;
1485   if (shell->ctxcontainer) PetscCall(PetscContainerSetUserDestroy(shell->ctxcontainer, f));
1486   PetscFunctionReturn(PETSC_SUCCESS);
1487 }
1488 
1489 static PetscErrorCode MatShellSetVecType_Shell(Mat mat, VecType vtype)
1490 {
1491   PetscFunctionBegin;
1492   PetscCall(PetscFree(mat->defaultvectype));
1493   PetscCall(PetscStrallocpy(vtype, (char **)&mat->defaultvectype));
1494   PetscFunctionReturn(PETSC_SUCCESS);
1495 }
1496 
1497 PetscErrorCode MatShellSetManageScalingShifts_Shell(Mat A)
1498 {
1499   Mat_Shell *shell = (Mat_Shell *)A->data;
1500 
1501   PetscFunctionBegin;
1502   shell->managescalingshifts = PETSC_FALSE;
1503   A->ops->diagonalset        = NULL;
1504   A->ops->diagonalscale      = NULL;
1505   A->ops->scale              = NULL;
1506   A->ops->shift              = NULL;
1507   A->ops->axpy               = NULL;
1508   PetscFunctionReturn(PETSC_SUCCESS);
1509 }
1510 
1511 static PetscErrorCode MatShellSetOperation_Shell(Mat mat, MatOperation op, void (*f)(void))
1512 {
1513   Mat_Shell *shell = (Mat_Shell *)mat->data;
1514 
1515   PetscFunctionBegin;
1516   switch (op) {
1517   case MATOP_DESTROY:
1518     shell->ops->destroy = (PetscErrorCode(*)(Mat))f;
1519     break;
1520   case MATOP_VIEW:
1521     if (!mat->ops->viewnative) mat->ops->viewnative = mat->ops->view;
1522     mat->ops->view = (PetscErrorCode(*)(Mat, PetscViewer))f;
1523     break;
1524   case MATOP_COPY:
1525     shell->ops->copy = (PetscErrorCode(*)(Mat, Mat, MatStructure))f;
1526     break;
1527   case MATOP_DIAGONAL_SET:
1528   case MATOP_DIAGONAL_SCALE:
1529   case MATOP_SHIFT:
1530   case MATOP_SCALE:
1531   case MATOP_AXPY:
1532   case MATOP_ZERO_ROWS:
1533   case MATOP_ZERO_ROWS_COLUMNS:
1534     PetscCheck(!shell->managescalingshifts, PetscObjectComm((PetscObject)mat), PETSC_ERR_ARG_WRONGSTATE, "MATSHELL is managing scalings and shifts, see MatShellSetManageScalingShifts()");
1535     (((void (**)(void))mat->ops)[op]) = f;
1536     break;
1537   case MATOP_GET_DIAGONAL:
1538     if (shell->managescalingshifts) {
1539       shell->ops->getdiagonal = (PetscErrorCode(*)(Mat, Vec))f;
1540       mat->ops->getdiagonal   = MatGetDiagonal_Shell;
1541     } else {
1542       shell->ops->getdiagonal = NULL;
1543       mat->ops->getdiagonal   = (PetscErrorCode(*)(Mat, Vec))f;
1544     }
1545     break;
1546   case MATOP_MULT:
1547     if (shell->managescalingshifts) {
1548       shell->ops->mult = (PetscErrorCode(*)(Mat, Vec, Vec))f;
1549       mat->ops->mult   = MatMult_Shell;
1550     } else {
1551       shell->ops->mult = NULL;
1552       mat->ops->mult   = (PetscErrorCode(*)(Mat, Vec, Vec))f;
1553     }
1554     break;
1555   case MATOP_MULT_TRANSPOSE:
1556     if (shell->managescalingshifts) {
1557       shell->ops->multtranspose = (PetscErrorCode(*)(Mat, Vec, Vec))f;
1558       mat->ops->multtranspose   = MatMultTranspose_Shell;
1559     } else {
1560       shell->ops->multtranspose = NULL;
1561       mat->ops->multtranspose   = (PetscErrorCode(*)(Mat, Vec, Vec))f;
1562     }
1563     break;
1564   default:
1565     (((void (**)(void))mat->ops)[op]) = f;
1566     break;
1567   }
1568   PetscFunctionReturn(PETSC_SUCCESS);
1569 }
1570 
1571 static PetscErrorCode MatShellGetOperation_Shell(Mat mat, MatOperation op, void (**f)(void))
1572 {
1573   Mat_Shell *shell = (Mat_Shell *)mat->data;
1574 
1575   PetscFunctionBegin;
1576   switch (op) {
1577   case MATOP_DESTROY:
1578     *f = (void (*)(void))shell->ops->destroy;
1579     break;
1580   case MATOP_VIEW:
1581     *f = (void (*)(void))mat->ops->view;
1582     break;
1583   case MATOP_COPY:
1584     *f = (void (*)(void))shell->ops->copy;
1585     break;
1586   case MATOP_DIAGONAL_SET:
1587   case MATOP_DIAGONAL_SCALE:
1588   case MATOP_SHIFT:
1589   case MATOP_SCALE:
1590   case MATOP_AXPY:
1591   case MATOP_ZERO_ROWS:
1592   case MATOP_ZERO_ROWS_COLUMNS:
1593     *f = (((void (**)(void))mat->ops)[op]);
1594     break;
1595   case MATOP_GET_DIAGONAL:
1596     if (shell->ops->getdiagonal) *f = (void (*)(void))shell->ops->getdiagonal;
1597     else *f = (((void (**)(void))mat->ops)[op]);
1598     break;
1599   case MATOP_MULT:
1600     if (shell->ops->mult) *f = (void (*)(void))shell->ops->mult;
1601     else *f = (((void (**)(void))mat->ops)[op]);
1602     break;
1603   case MATOP_MULT_TRANSPOSE:
1604     if (shell->ops->multtranspose) *f = (void (*)(void))shell->ops->multtranspose;
1605     else *f = (((void (**)(void))mat->ops)[op]);
1606     break;
1607   default:
1608     *f = (((void (**)(void))mat->ops)[op]);
1609   }
1610   PetscFunctionReturn(PETSC_SUCCESS);
1611 }
1612 
1613 /*MC
1614    MATSHELL - MATSHELL = "shell" - A matrix type to be used to define your own matrix type -- perhaps matrix free.
1615 
1616   Level: advanced
1617 
1618 .seealso: [](ch_matrices), `Mat`, `MatCreateShell()`
1619 M*/
1620 
1621 PETSC_EXTERN PetscErrorCode MatCreate_Shell(Mat A)
1622 {
1623   Mat_Shell *b;
1624 
1625   PetscFunctionBegin;
1626   PetscCall(PetscNew(&b));
1627   A->data   = (void *)b;
1628   A->ops[0] = MatOps_Values;
1629 
1630   b->ctxcontainer        = NULL;
1631   b->vshift              = 0.0;
1632   b->vscale              = 1.0;
1633   b->managescalingshifts = PETSC_TRUE;
1634   A->assembled           = PETSC_TRUE;
1635   A->preallocated        = PETSC_FALSE;
1636 
1637   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatShellGetContext_C", MatShellGetContext_Shell));
1638   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatShellSetContext_C", MatShellSetContext_Shell));
1639   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatShellSetContextDestroy_C", MatShellSetContextDestroy_Shell));
1640   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatShellSetVecType_C", MatShellSetVecType_Shell));
1641   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatShellSetManageScalingShifts_C", MatShellSetManageScalingShifts_Shell));
1642   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatShellSetOperation_C", MatShellSetOperation_Shell));
1643   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatShellGetOperation_C", MatShellGetOperation_Shell));
1644   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatShellSetMatProductOperation_C", MatShellSetMatProductOperation_Shell));
1645   PetscCall(PetscObjectChangeTypeName((PetscObject)A, MATSHELL));
1646   PetscFunctionReturn(PETSC_SUCCESS);
1647 }
1648 
1649 /*@C
1650    MatCreateShell - Creates a new matrix of `MatType` `MATSHELL` for use with a user-defined
1651    private data storage format.
1652 
1653   Collective
1654 
1655    Input Parameters:
1656 +  comm - MPI communicator
1657 .  m - number of local rows (must be given)
1658 .  n - number of local columns (must be given)
1659 .  M - number of global rows (may be `PETSC_DETERMINE`)
1660 .  N - number of global columns (may be `PETSC_DETERMINE`)
1661 -  ctx - pointer to data needed by the shell matrix routines
1662 
1663    Output Parameter:
1664 .  A - the matrix
1665 
1666    Level: advanced
1667 
1668   Usage:
1669 .vb
1670     extern PetscErrorCode mult(Mat,Vec,Vec);
1671     MatCreateShell(comm,m,n,M,N,ctx,&mat);
1672     MatShellSetOperation(mat,MATOP_MULT,(void(*)(void))mult);
1673     [ Use matrix for operations that have been set ]
1674     MatDestroy(mat);
1675 .ve
1676 
1677    Notes:
1678    The shell matrix type is intended to provide a simple class to use
1679    with `KSP` (such as, for use with matrix-free methods). You should not
1680    use the shell type if you plan to define a complete matrix class.
1681 
1682    PETSc requires that matrices and vectors being used for certain
1683    operations are partitioned accordingly.  For example, when
1684    creating a shell matrix, `A`, that supports parallel matrix-vector
1685    products using `MatMult`(A,x,y) the user should set the number
1686    of local matrix rows to be the number of local elements of the
1687    corresponding result vector, y. Note that this is information is
1688    required for use of the matrix interface routines, even though
1689    the shell matrix may not actually be physically partitioned.
1690    For example,
1691 
1692 .vb
1693      Vec x, y
1694      extern PetscErrorCode mult(Mat,Vec,Vec);
1695      Mat A
1696 
1697      VecCreateMPI(comm,PETSC_DECIDE,M,&y);
1698      VecCreateMPI(comm,PETSC_DECIDE,N,&x);
1699      VecGetLocalSize(y,&m);
1700      VecGetLocalSize(x,&n);
1701      MatCreateShell(comm,m,n,M,N,ctx,&A);
1702      MatShellSetOperation(mat,MATOP_MULT,(void(*)(void))mult);
1703      MatMult(A,x,y);
1704      MatDestroy(&A);
1705      VecDestroy(&y);
1706      VecDestroy(&x);
1707 .ve
1708 
1709    `MATSHELL` handles `MatShift()`, `MatDiagonalSet()`, `MatDiagonalScale()`, `MatAXPY()`, `MatScale()`, `MatZeroRows()` and `MatZeroRowsColumns()`
1710    internally, so these  operations cannot be overwritten unless `MatShellSetManageScalingShifts()` is called.
1711 
1712     Developer Notes:
1713     For rectangular matrices do all the scalings and shifts make sense?
1714 
1715     Regarding shifting and scaling. The general form is
1716 
1717           diag(left)(vscale*A + diag(dshift) + vshift I)diag(right)
1718 
1719       The order you apply the operations is important. For example if you have a dshift then
1720       apply a MatScale(s) you get s*vscale*A + s*diag(shift). But if you first scale and then shift
1721       you get s*vscale*A + diag(shift)
1722 
1723           A is the user provided function.
1724 
1725    `KSP`/`PC` uses changes in the `Mat`'s "state" to decide if preconditioners need to be rebuilt `PCSetUp()` only calls the setup() for
1726    for the `PC` implementation if the `Mat` state has increased from the previous call. Thus to get changes in a `MATSHELL` to trigger
1727    an update in the preconditioner you must call `MatAssemblyBegin()` and `MatAssemblyEnd()` or `PetscObjectStateIncrease`((`PetscObject`)mat);
1728    each time the `MATSHELL` matrix has changed.
1729 
1730    Matrix product operations (i.e. `MatMat()`, `MatTransposeMat()` etc) can be specified using `MatShellSetMatProductOperation()`
1731 
1732    Calling `MatAssemblyBegin()`/`MatAssemblyEnd()` on a `MATSHELL` removes any previously supplied shift and scales that were provided
1733    with `MatDiagonalSet()`, `MatShift()`, `MatScale()`, or `MatDiagonalScale()`.
1734 
1735    Fortran Note:
1736     To use this from Fortran with a `ctx` you must write an interface definition for this
1737     function and for `MatShellGetContext()` that tells Fortran the Fortran derived data type you are passing
1738     in as the `ctx` argument.
1739 
1740 .seealso: [](ch_matrices), `Mat`, `MATSHELL`, `MatShellSetOperation()`, `MatHasOperation()`, `MatShellGetContext()`, `MatShellSetContext()`, `MATSHELL`, `MatShellSetManageScalingShifts()`, `MatShellSetMatProductOperation()`
1741 @*/
1742 PetscErrorCode MatCreateShell(MPI_Comm comm, PetscInt m, PetscInt n, PetscInt M, PetscInt N, void *ctx, Mat *A)
1743 {
1744   PetscFunctionBegin;
1745   PetscCall(MatCreate(comm, A));
1746   PetscCall(MatSetSizes(*A, m, n, M, N));
1747   PetscCall(MatSetType(*A, MATSHELL));
1748   PetscCall(MatShellSetContext(*A, ctx));
1749   PetscCall(MatSetUp(*A));
1750   PetscFunctionReturn(PETSC_SUCCESS);
1751 }
1752 
1753 /*@
1754     MatShellSetContext - sets the context for a `MATSHELL` shell matrix
1755 
1756    Logically Collective
1757 
1758     Input Parameters:
1759 +   mat - the `MATSHELL` shell matrix
1760 -   ctx - the context
1761 
1762    Level: advanced
1763 
1764    Fortran Note:
1765     You must write a Fortran interface definition for this
1766     function that tells Fortran the Fortran derived data type that you are passing in as the `ctx` argument.
1767 
1768 .seealso: [](ch_matrices), `Mat`, `MATSHELL`, `MatCreateShell()`, `MatShellGetContext()`, `MatShellGetOperation()`
1769 @*/
1770 PetscErrorCode MatShellSetContext(Mat mat, void *ctx)
1771 {
1772   PetscFunctionBegin;
1773   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
1774   PetscTryMethod(mat, "MatShellSetContext_C", (Mat, void *), (mat, ctx));
1775   PetscFunctionReturn(PETSC_SUCCESS);
1776 }
1777 
1778 /*@
1779     MatShellSetContextDestroy - sets the destroy function for a `MATSHELL` shell matrix context
1780 
1781    Logically Collective
1782 
1783     Input Parameters:
1784 +   mat - the shell matrix
1785 -   f - the context destroy function
1786 
1787    Level: advanced
1788 
1789     Note:
1790     If the `MatShell` is never duplicated, the behavior of this function is equivalent
1791     to `MatShellSetOperation`(`Mat`,`MATOP_DESTROY`,f). However, `MatShellSetContextDestroy()`
1792     ensures proper reference counting for the user provided context data in the case that
1793     the `MATSHELL` is duplicated.
1794 
1795 .seealso: [](ch_matrices), `Mat`, `MATSHELL`, `MatCreateShell()`, `MatShellSetContext()`
1796 @*/
1797 PetscErrorCode MatShellSetContextDestroy(Mat mat, PetscErrorCode (*f)(void *))
1798 {
1799   PetscFunctionBegin;
1800   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
1801   PetscTryMethod(mat, "MatShellSetContextDestroy_C", (Mat, PetscErrorCode(*)(void *)), (mat, f));
1802   PetscFunctionReturn(PETSC_SUCCESS);
1803 }
1804 
1805 /*@C
1806  MatShellSetVecType - Sets the `VecType` of `Vec` returned by `MatCreateVecs()`
1807 
1808  Logically Collective
1809 
1810     Input Parameters:
1811 +   mat   - the `MATSHELL` shell matrix
1812 -   vtype - type to use for creating vectors
1813 
1814  Level: advanced
1815 
1816 .seealso: [](ch_matrices), `Mat`, `MATSHELL`, `MatCreateVecs()`
1817 @*/
1818 PetscErrorCode MatShellSetVecType(Mat mat, VecType vtype)
1819 {
1820   PetscFunctionBegin;
1821   PetscTryMethod(mat, "MatShellSetVecType_C", (Mat, VecType), (mat, vtype));
1822   PetscFunctionReturn(PETSC_SUCCESS);
1823 }
1824 
1825 /*@
1826     MatShellSetManageScalingShifts - Allows the user to control the scaling and shift operations of the `MATSHELL`. Must be called immediately
1827           after `MatCreateShell()`
1828 
1829    Logically Collective
1830 
1831     Input Parameter:
1832 .   mat - the `MATSHELL` shell matrix
1833 
1834   Level: advanced
1835 
1836 .seealso: [](ch_matrices), `Mat`, `MATSHELL`, `MatCreateShell()`, `MatShellGetContext()`, `MatShellGetOperation()`, `MatShellSetContext()`, `MatShellSetOperation()`
1837 @*/
1838 PetscErrorCode MatShellSetManageScalingShifts(Mat A)
1839 {
1840   PetscFunctionBegin;
1841   PetscValidHeaderSpecific(A, MAT_CLASSID, 1);
1842   PetscTryMethod(A, "MatShellSetManageScalingShifts_C", (Mat), (A));
1843   PetscFunctionReturn(PETSC_SUCCESS);
1844 }
1845 
1846 /*@C
1847     MatShellTestMult - Compares the multiply routine provided to the `MATSHELL` with differencing on a given function.
1848 
1849    Logically Collective; No Fortran Support
1850 
1851     Input Parameters:
1852 +   mat - the `MATSHELL` shell matrix
1853 .   f - the function
1854 .   base - differences are computed around this vector, see `MatMFFDSetBase()`, for Jacobians this is the point at which the Jacobian is being evaluated
1855 -   ctx - an optional context for the function
1856 
1857    Output Parameter:
1858 .   flg - `PETSC_TRUE` if the multiply is likely correct
1859 
1860    Options Database Key:
1861 .   -mat_shell_test_mult_view - print if any differences are detected between the products and print the difference
1862 
1863    Level: advanced
1864 
1865 .seealso: [](ch_matrices), `Mat`, `MATSHELL`, `MatCreateShell()`, `MatShellGetContext()`, `MatShellGetOperation()`, `MatShellTestMultTranspose()`
1866 @*/
1867 PetscErrorCode MatShellTestMult(Mat mat, PetscErrorCode (*f)(void *, Vec, Vec), Vec base, void *ctx, PetscBool *flg)
1868 {
1869   PetscInt  m, n;
1870   Mat       mf, Dmf, Dmat, Ddiff;
1871   PetscReal Diffnorm, Dmfnorm;
1872   PetscBool v = PETSC_FALSE, flag = PETSC_TRUE;
1873 
1874   PetscFunctionBegin;
1875   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
1876   PetscCall(PetscOptionsHasName(NULL, ((PetscObject)mat)->prefix, "-mat_shell_test_mult_view", &v));
1877   PetscCall(MatGetLocalSize(mat, &m, &n));
1878   PetscCall(MatCreateMFFD(PetscObjectComm((PetscObject)mat), m, n, PETSC_DECIDE, PETSC_DECIDE, &mf));
1879   PetscCall(MatMFFDSetFunction(mf, f, ctx));
1880   PetscCall(MatMFFDSetBase(mf, base, NULL));
1881 
1882   PetscCall(MatComputeOperator(mf, MATAIJ, &Dmf));
1883   PetscCall(MatComputeOperator(mat, MATAIJ, &Dmat));
1884 
1885   PetscCall(MatDuplicate(Dmat, MAT_COPY_VALUES, &Ddiff));
1886   PetscCall(MatAXPY(Ddiff, -1.0, Dmf, DIFFERENT_NONZERO_PATTERN));
1887   PetscCall(MatNorm(Ddiff, NORM_FROBENIUS, &Diffnorm));
1888   PetscCall(MatNorm(Dmf, NORM_FROBENIUS, &Dmfnorm));
1889   if (Diffnorm / Dmfnorm > 10 * PETSC_SQRT_MACHINE_EPSILON) {
1890     flag = PETSC_FALSE;
1891     if (v) {
1892       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)));
1893       PetscCall(MatViewFromOptions(Ddiff, (PetscObject)mat, "-mat_shell_test_mult_view"));
1894       PetscCall(MatViewFromOptions(Dmf, (PetscObject)mat, "-mat_shell_test_mult_view"));
1895       PetscCall(MatViewFromOptions(Dmat, (PetscObject)mat, "-mat_shell_test_mult_view"));
1896     }
1897   } else if (v) {
1898     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)mat), "MATSHELL and matrix free multiple appear to produce the same results\n"));
1899   }
1900   if (flg) *flg = flag;
1901   PetscCall(MatDestroy(&Ddiff));
1902   PetscCall(MatDestroy(&mf));
1903   PetscCall(MatDestroy(&Dmf));
1904   PetscCall(MatDestroy(&Dmat));
1905   PetscFunctionReturn(PETSC_SUCCESS);
1906 }
1907 
1908 /*@C
1909     MatShellTestMultTranspose - Compares the multiply transpose routine provided to the `MATSHELL` with differencing on a given function.
1910 
1911    Logically Collective; No Fortran Support
1912 
1913     Input Parameters:
1914 +   mat - the `MATSHELL` shell matrix
1915 .   f - the function
1916 .   base - differences are computed around this vector, see `MatMFFDSetBase()`, for Jacobians this is the point at which the Jacobian is being evaluated
1917 -   ctx - an optional context for the function
1918 
1919    Output Parameter:
1920 .   flg - `PETSC_TRUE` if the multiply is likely correct
1921 
1922    Options Database Key:
1923 .   -mat_shell_test_mult_view - print if any differences are detected between the products and print the difference
1924 
1925    Level: advanced
1926 
1927 .seealso: [](ch_matrices), `Mat`, `MATSHELL`, `MatCreateShell()`, `MatShellGetContext()`, `MatShellGetOperation()`, `MatShellTestMult()`
1928 @*/
1929 PetscErrorCode MatShellTestMultTranspose(Mat mat, PetscErrorCode (*f)(void *, Vec, Vec), Vec base, void *ctx, PetscBool *flg)
1930 {
1931   Vec       x, y, z;
1932   PetscInt  m, n, M, N;
1933   Mat       mf, Dmf, Dmat, Ddiff;
1934   PetscReal Diffnorm, Dmfnorm;
1935   PetscBool v = PETSC_FALSE, flag = PETSC_TRUE;
1936 
1937   PetscFunctionBegin;
1938   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
1939   PetscCall(PetscOptionsHasName(NULL, ((PetscObject)mat)->prefix, "-mat_shell_test_mult_transpose_view", &v));
1940   PetscCall(MatCreateVecs(mat, &x, &y));
1941   PetscCall(VecDuplicate(y, &z));
1942   PetscCall(MatGetLocalSize(mat, &m, &n));
1943   PetscCall(MatGetSize(mat, &M, &N));
1944   PetscCall(MatCreateMFFD(PetscObjectComm((PetscObject)mat), m, n, M, N, &mf));
1945   PetscCall(MatMFFDSetFunction(mf, f, ctx));
1946   PetscCall(MatMFFDSetBase(mf, base, NULL));
1947   PetscCall(MatComputeOperator(mf, MATAIJ, &Dmf));
1948   PetscCall(MatTranspose(Dmf, MAT_INPLACE_MATRIX, &Dmf));
1949   PetscCall(MatComputeOperatorTranspose(mat, MATAIJ, &Dmat));
1950 
1951   PetscCall(MatDuplicate(Dmat, MAT_COPY_VALUES, &Ddiff));
1952   PetscCall(MatAXPY(Ddiff, -1.0, Dmf, DIFFERENT_NONZERO_PATTERN));
1953   PetscCall(MatNorm(Ddiff, NORM_FROBENIUS, &Diffnorm));
1954   PetscCall(MatNorm(Dmf, NORM_FROBENIUS, &Dmfnorm));
1955   if (Diffnorm / Dmfnorm > 10 * PETSC_SQRT_MACHINE_EPSILON) {
1956     flag = PETSC_FALSE;
1957     if (v) {
1958       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)));
1959       PetscCall(MatViewFromOptions(Ddiff, (PetscObject)mat, "-mat_shell_test_mult_transpose_view"));
1960       PetscCall(MatViewFromOptions(Dmf, (PetscObject)mat, "-mat_shell_test_mult_transpose_view"));
1961       PetscCall(MatViewFromOptions(Dmat, (PetscObject)mat, "-mat_shell_test_mult_transpose_view"));
1962     }
1963   } else if (v) {
1964     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)mat), "MATSHELL transpose and matrix free multiple appear to produce the same results\n"));
1965   }
1966   if (flg) *flg = flag;
1967   PetscCall(MatDestroy(&mf));
1968   PetscCall(MatDestroy(&Dmat));
1969   PetscCall(MatDestroy(&Ddiff));
1970   PetscCall(MatDestroy(&Dmf));
1971   PetscCall(VecDestroy(&x));
1972   PetscCall(VecDestroy(&y));
1973   PetscCall(VecDestroy(&z));
1974   PetscFunctionReturn(PETSC_SUCCESS);
1975 }
1976 
1977 /*@C
1978     MatShellSetOperation - Allows user to set a matrix operation for a `MATSHELL` shell matrix.
1979 
1980    Logically Collective
1981 
1982     Input Parameters:
1983 +   mat - the `MATSHELL` shell matrix
1984 .   op - the name of the operation
1985 -   g - the function that provides the operation.
1986 
1987    Level: advanced
1988 
1989     Usage:
1990 .vb
1991       extern PetscErrorCode usermult(Mat,Vec,Vec);
1992       MatCreateShell(comm,m,n,M,N,ctx,&A);
1993       MatShellSetOperation(A,MATOP_MULT,(void(*)(void))usermult);
1994 .ve
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)
2016     use `MatShellSetMatProductOperation()`
2017 
2018     Fortran Note:
2019     For `MatCreateVecs()` the user code should check if the input left or right matrix is -1 and in that case not
2020        generate a matrix. See src/mat/tests/ex120f.F
2021 
2022 .seealso: [](ch_matrices), `Mat`, `MATSHELL`, `MatCreateShell()`, `MatShellGetContext()`, `MatShellGetOperation()`, `MatShellSetContext()`, `MatSetOperation()`, `MatShellSetManageScalingShifts()`, `MatShellSetMatProductOperation()`
2023 @*/
2024 PetscErrorCode MatShellSetOperation(Mat mat, MatOperation op, void (*g)(void))
2025 {
2026   PetscFunctionBegin;
2027   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
2028   PetscTryMethod(mat, "MatShellSetOperation_C", (Mat, MatOperation, void (*)(void)), (mat, op, g));
2029   PetscFunctionReturn(PETSC_SUCCESS);
2030 }
2031 
2032 /*@C
2033     MatShellGetOperation - Gets a matrix function for a `MATSHELL` shell matrix.
2034 
2035     Not Collective
2036 
2037     Input Parameters:
2038 +   mat - the `MATSHELL` shell matrix
2039 -   op - the name of the operation
2040 
2041     Output Parameter:
2042 .   g - the function that provides the operation.
2043 
2044     Level: advanced
2045 
2046     Notes:
2047     See the file include/petscmat.h for a complete list of matrix
2048     operations, which all have the form MATOP_<OPERATION>, where
2049     <OPERATION> is the name (in all capital letters) of the
2050     user interface routine (e.g., `MatMult()` -> `MATOP_MULT`).
2051 
2052     All user-provided functions have the same calling
2053     sequence as the usual matrix interface routines, since they
2054     are intended to be accessed via the usual matrix interface
2055     routines, e.g.,
2056 $       MatMult(Mat, Vec, Vec) -> usermult(Mat, Vec, Vec)
2057 
2058     Within each user-defined routine, the user should call
2059     `MatShellGetContext()` to obtain the user-defined context that was
2060     set by `MatCreateShell()`.
2061 
2062 .seealso: [](ch_matrices), `Mat`, `MATSHELL`, `MatCreateShell()`, `MatShellGetContext()`, `MatShellSetOperation()`, `MatShellSetContext()`
2063 @*/
2064 PetscErrorCode MatShellGetOperation(Mat mat, MatOperation op, void (**g)(void))
2065 {
2066   PetscFunctionBegin;
2067   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
2068   PetscUseMethod(mat, "MatShellGetOperation_C", (Mat, MatOperation, void (**)(void)), (mat, op, g));
2069   PetscFunctionReturn(PETSC_SUCCESS);
2070 }
2071 
2072 /*@
2073     MatIsShell - Inquires if a matrix is derived from `MATSHELL`
2074 
2075     Input Parameter:
2076 .   mat - the matrix
2077 
2078     Output Parameter:
2079 .   flg - the boolean value
2080 
2081     Level: developer
2082 
2083     Developer Note:
2084     In the future, we should allow the object type name to be changed still using the `MATSHELL` data structure for other matrices
2085     (i.e. `MATTRANSPOSEVIRTUAL`, `MATSCHURCOMPLEMENT` etc)
2086 
2087 .seealso: [](ch_matrices), `Mat`, `MATSHELL`, `MATMFFD`, `MatCreateShell()`, `MATTRANSPOSEVIRTUAL`, `MATSCHURCOMPLEMENT`
2088 @*/
2089 PetscErrorCode MatIsShell(Mat mat, PetscBool *flg)
2090 {
2091   PetscFunctionBegin;
2092   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
2093   PetscValidBoolPointer(flg, 2);
2094   *flg = (PetscBool)(mat->ops->destroy == MatDestroy_Shell);
2095   PetscFunctionReturn(PETSC_SUCCESS);
2096 }
2097