xref: /petsc/src/mat/impls/mffd/mffd.c (revision 98d129c30f3ee9fdddc40fdbc5a989b7be64f888)
1 #include <../src/mat/impls/shell/shell.h> /*I  "petscmat.h"   I*/
2 #include <../src/mat/impls/mffd/mffdimpl.h>
3 
4 PetscFunctionList MatMFFDList              = NULL;
5 PetscBool         MatMFFDRegisterAllCalled = PETSC_FALSE;
6 
7 PetscClassId  MATMFFD_CLASSID;
8 PetscLogEvent MATMFFD_Mult;
9 
10 static PetscBool MatMFFDPackageInitialized = PETSC_FALSE;
11 
12 /*@C
13   MatMFFDFinalizePackage - This function destroys everything in the MATMFFD` package. It is
14   called from `PetscFinalize()`.
15 
16   Level: developer
17 
18 .seealso: [](ch_matrices), `Mat`, `MATMFFD`, `PetscFinalize()`, `MatCreateMFFD()`, `MatCreateSNESMF()`
19 @*/
20 PetscErrorCode MatMFFDFinalizePackage(void)
21 {
22   PetscFunctionBegin;
23   PetscCall(PetscFunctionListDestroy(&MatMFFDList));
24   MatMFFDPackageInitialized = PETSC_FALSE;
25   MatMFFDRegisterAllCalled  = PETSC_FALSE;
26   PetscFunctionReturn(PETSC_SUCCESS);
27 }
28 
29 /*@C
30   MatMFFDInitializePackage - This function initializes everything in the MATMFFD` package. It is called
31   from `MatInitializePackage()`.
32 
33   Level: developer
34 
35 .seealso: [](ch_matrices), `Mat`, `MATMFFD`, `PetscInitialize()`
36 @*/
37 PetscErrorCode MatMFFDInitializePackage(void)
38 {
39   char      logList[256];
40   PetscBool opt, pkg;
41 
42   PetscFunctionBegin;
43   if (MatMFFDPackageInitialized) PetscFunctionReturn(PETSC_SUCCESS);
44   MatMFFDPackageInitialized = PETSC_TRUE;
45   /* Register Classes */
46   PetscCall(PetscClassIdRegister("MatMFFD", &MATMFFD_CLASSID));
47   /* Register Constructors */
48   PetscCall(MatMFFDRegisterAll());
49   /* Register Events */
50   PetscCall(PetscLogEventRegister("MatMult MF", MATMFFD_CLASSID, &MATMFFD_Mult));
51   /* Process Info */
52   {
53     PetscClassId classids[1];
54 
55     classids[0] = MATMFFD_CLASSID;
56     PetscCall(PetscInfoProcessClass("matmffd", 1, classids));
57   }
58   /* Process summary exclusions */
59   PetscCall(PetscOptionsGetString(NULL, NULL, "-log_exclude", logList, sizeof(logList), &opt));
60   if (opt) {
61     PetscCall(PetscStrInList("matmffd", logList, ',', &pkg));
62     if (pkg) PetscCall(PetscLogEventExcludeClass(MATMFFD_CLASSID));
63   }
64   /* Register package finalizer */
65   PetscCall(PetscRegisterFinalize(MatMFFDFinalizePackage));
66   PetscFunctionReturn(PETSC_SUCCESS);
67 }
68 
69 static PetscErrorCode MatMFFDSetType_MFFD(Mat mat, MatMFFDType ftype)
70 {
71   MatMFFD   ctx;
72   PetscBool match;
73   PetscErrorCode (*r)(MatMFFD);
74 
75   PetscFunctionBegin;
76   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
77   PetscAssertPointer(ftype, 2);
78   PetscCall(MatShellGetContext(mat, &ctx));
79 
80   /* already set, so just return */
81   PetscCall(PetscObjectTypeCompare((PetscObject)ctx, ftype, &match));
82   if (match) PetscFunctionReturn(PETSC_SUCCESS);
83 
84   /* destroy the old one if it exists */
85   PetscTryTypeMethod(ctx, destroy);
86 
87   PetscCall(PetscFunctionListFind(MatMFFDList, ftype, &r));
88   PetscCheck(r, PetscObjectComm((PetscObject)mat), PETSC_ERR_ARG_UNKNOWN_TYPE, "Unknown MatMFFD type %s given", ftype);
89   PetscCall((*r)(ctx));
90   PetscCall(PetscObjectChangeTypeName((PetscObject)ctx, ftype));
91   PetscFunctionReturn(PETSC_SUCCESS);
92 }
93 
94 /*@C
95   MatMFFDSetType - Sets the method that is used to compute the
96   differencing parameter for finite difference matrix-free formulations.
97 
98   Input Parameters:
99 + mat   - the "matrix-free" matrix created via `MatCreateSNESMF()`, or `MatCreateMFFD()`
100           or `MatSetType`(mat,`MATMFFD`);
101 - ftype - the type requested, either `MATMFFD_WP` or `MATMFFD_DS`
102 
103   Level: advanced
104 
105   Note:
106   For example, such routines can compute `h` for use in
107   Jacobian-vector products of the form
108 .vb
109 
110                         F(x+ha) - F(x)
111           F'(u)a  ~=  ----------------
112                               h
113 .ve
114 
115 .seealso: [](ch_matrices), `Mat`, `MATMFFD`, `MATMFFD_WP`, `MATMFFD_DS`, `MatCreateSNESMF()`, `MatMFFDRegister()`, `MatMFFDSetFunction()`, `MatCreateMFFD()`
116 @*/
117 PetscErrorCode MatMFFDSetType(Mat mat, MatMFFDType ftype)
118 {
119   PetscFunctionBegin;
120   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
121   PetscAssertPointer(ftype, 2);
122   PetscTryMethod(mat, "MatMFFDSetType_C", (Mat, MatMFFDType), (mat, ftype));
123   PetscFunctionReturn(PETSC_SUCCESS);
124 }
125 
126 static PetscErrorCode MatGetDiagonal_MFFD(Mat, Vec);
127 
128 typedef PetscErrorCode (*FCN1)(void *, Vec); /* force argument to next function to not be extern C*/
129 static PetscErrorCode MatMFFDSetFunctioniBase_MFFD(Mat mat, FCN1 func)
130 {
131   MatMFFD ctx;
132 
133   PetscFunctionBegin;
134   PetscCall(MatShellGetContext(mat, &ctx));
135   ctx->funcisetbase = func;
136   PetscFunctionReturn(PETSC_SUCCESS);
137 }
138 
139 typedef PetscErrorCode (*FCN2)(void *, PetscInt, Vec, PetscScalar *); /* force argument to next function to not be extern C*/
140 static PetscErrorCode MatMFFDSetFunctioni_MFFD(Mat mat, FCN2 funci)
141 {
142   MatMFFD ctx;
143 
144   PetscFunctionBegin;
145   PetscCall(MatShellGetContext(mat, &ctx));
146   ctx->funci = funci;
147   PetscCall(MatShellSetOperation(mat, MATOP_GET_DIAGONAL, (void (*)(void))MatGetDiagonal_MFFD));
148   PetscFunctionReturn(PETSC_SUCCESS);
149 }
150 
151 static PetscErrorCode MatMFFDGetH_MFFD(Mat mat, PetscScalar *h)
152 {
153   MatMFFD ctx;
154 
155   PetscFunctionBegin;
156   PetscCall(MatShellGetContext(mat, &ctx));
157   *h = ctx->currenth;
158   PetscFunctionReturn(PETSC_SUCCESS);
159 }
160 
161 static PetscErrorCode MatMFFDResetHHistory_MFFD(Mat J)
162 {
163   MatMFFD ctx;
164 
165   PetscFunctionBegin;
166   PetscCall(MatShellGetContext(J, &ctx));
167   ctx->ncurrenth = 0;
168   PetscFunctionReturn(PETSC_SUCCESS);
169 }
170 
171 /*@C
172   MatMFFDRegister - Adds a method to the `MATMFFD` registry.
173 
174   Not Collective
175 
176   Input Parameters:
177 + sname    - name of a new user-defined compute-h module
178 - function - routine to create method context
179 
180   Level: developer
181 
182   Note:
183   `MatMFFDRegister()` may be called multiple times to add several user-defined solvers.
184 
185   Example Usage:
186 .vb
187    MatMFFDRegister("my_h", MyHCreate);
188 .ve
189 
190   Then, your solver can be chosen with the procedural interface via `MatMFFDSetType`(mfctx, "my_h")` or at runtime via the option
191   `-mat_mffd_type my_h`
192 
193 .seealso: [](ch_matrices), `Mat`, `MATMFFD`, `MatMFFDRegisterAll()`, `MatMFFDRegisterDestroy()`
194  @*/
195 PetscErrorCode MatMFFDRegister(const char sname[], PetscErrorCode (*function)(MatMFFD))
196 {
197   PetscFunctionBegin;
198   PetscCall(MatInitializePackage());
199   PetscCall(PetscFunctionListAdd(&MatMFFDList, sname, function));
200   PetscFunctionReturn(PETSC_SUCCESS);
201 }
202 
203 static PetscErrorCode MatDestroy_MFFD(Mat mat)
204 {
205   MatMFFD ctx;
206 
207   PetscFunctionBegin;
208   PetscCall(MatShellGetContext(mat, &ctx));
209   PetscCall(VecDestroy(&ctx->w));
210   PetscCall(VecDestroy(&ctx->current_u));
211   if (ctx->current_f_allocated) PetscCall(VecDestroy(&ctx->current_f));
212   PetscTryTypeMethod(ctx, destroy);
213   PetscCall(PetscHeaderDestroy(&ctx));
214 
215   PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatMFFDSetBase_C", NULL));
216   PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatMFFDSetFunctioniBase_C", NULL));
217   PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatMFFDSetFunctioni_C", NULL));
218   PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatMFFDSetFunction_C", NULL));
219   PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatMFFDSetFunctionError_C", NULL));
220   PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatMFFDSetCheckh_C", NULL));
221   PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatMFFDSetPeriod_C", NULL));
222   PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatMFFDResetHHistory_C", NULL));
223   PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatMFFDSetHHistory_C", NULL));
224   PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatMFFDSetType_C", NULL));
225   PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatMFFDGetH_C", NULL));
226   PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatSNESMFSetReuseBase_C", NULL));
227   PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatSNESMFGetReuseBase_C", NULL));
228   PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatShellSetContext_C", NULL));
229   PetscFunctionReturn(PETSC_SUCCESS);
230 }
231 
232 /*
233    MatMFFDView_MFFD - Views matrix-free parameters.
234 
235 */
236 static PetscErrorCode MatView_MFFD(Mat J, PetscViewer viewer)
237 {
238   MatMFFD     ctx;
239   PetscBool   iascii, viewbase, viewfunction;
240   const char *prefix;
241 
242   PetscFunctionBegin;
243   PetscCall(MatShellGetContext(J, &ctx));
244   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
245   if (iascii) {
246     PetscCall(PetscViewerASCIIPrintf(viewer, "Matrix-free approximation:\n"));
247     PetscCall(PetscViewerASCIIPushTab(viewer));
248     PetscCall(PetscViewerASCIIPrintf(viewer, "err=%g (relative error in function evaluation)\n", (double)ctx->error_rel));
249     if (!((PetscObject)ctx)->type_name) {
250       PetscCall(PetscViewerASCIIPrintf(viewer, "The compute h routine has not yet been set\n"));
251     } else {
252       PetscCall(PetscViewerASCIIPrintf(viewer, "Using %s compute h routine\n", ((PetscObject)ctx)->type_name));
253     }
254 #if defined(PETSC_USE_COMPLEX)
255     if (ctx->usecomplex) PetscCall(PetscViewerASCIIPrintf(viewer, "Using Lyness complex number trick to compute the matrix-vector product\n"));
256 #endif
257     PetscTryTypeMethod(ctx, view, viewer);
258     PetscCall(PetscObjectGetOptionsPrefix((PetscObject)J, &prefix));
259 
260     PetscCall(PetscOptionsHasName(((PetscObject)J)->options, prefix, "-mat_mffd_view_base", &viewbase));
261     if (viewbase) {
262       PetscCall(PetscViewerASCIIPrintf(viewer, "Base:\n"));
263       PetscCall(VecView(ctx->current_u, viewer));
264     }
265     PetscCall(PetscOptionsHasName(((PetscObject)J)->options, prefix, "-mat_mffd_view_function", &viewfunction));
266     if (viewfunction) {
267       PetscCall(PetscViewerASCIIPrintf(viewer, "Function:\n"));
268       PetscCall(VecView(ctx->current_f, viewer));
269     }
270     PetscCall(PetscViewerASCIIPopTab(viewer));
271   }
272   PetscFunctionReturn(PETSC_SUCCESS);
273 }
274 
275 /*
276    MatAssemblyEnd_MFFD - Resets the ctx->ncurrenth to zero. This
277    allows the user to indicate the beginning of a new linear solve by calling
278    MatAssemblyXXX() on the matrix-free matrix. This then allows the
279    MatCreateMFFD_WP() to properly compute ||U|| only the first time
280    in the linear solver rather than every time.
281 
282    This function is referenced directly from MatAssemblyEnd_SNESMF(), which may be in a different shared library hence
283    it must be labeled as PETSC_EXTERN
284 */
285 PETSC_EXTERN PetscErrorCode MatAssemblyEnd_MFFD(Mat J, MatAssemblyType mt)
286 {
287   MatMFFD j;
288 
289   PetscFunctionBegin;
290   PetscCall(MatShellGetContext(J, &j));
291   PetscCall(MatMFFDResetHHistory(J));
292   PetscFunctionReturn(PETSC_SUCCESS);
293 }
294 
295 /*
296   MatMult_MFFD - Default matrix-free form for Jacobian-vector product, y = F'(u)*a:
297 
298         y ~= (F(u + ha) - F(u))/h,
299   where F = nonlinear function, as set by SNESSetFunction()
300         u = current iterate
301         h = difference interval
302 */
303 static PetscErrorCode MatMult_MFFD(Mat mat, Vec a, Vec y)
304 {
305   MatMFFD     ctx;
306   PetscScalar h;
307   Vec         w, U, F;
308   PetscBool   zeroa;
309 
310   PetscFunctionBegin;
311   PetscCall(MatShellGetContext(mat, &ctx));
312   PetscCheck(ctx->current_u, PetscObjectComm((PetscObject)mat), PETSC_ERR_ARG_WRONGSTATE, "MatMFFDSetBase() has not been called, this is often caused by forgetting to call MatAssemblyBegin/End on the first Mat in the SNES compute function");
313   /* We log matrix-free matrix-vector products separately, so that we can
314      separate the performance monitoring from the cases that use conventional
315      storage.  We may eventually modify event logging to associate events
316      with particular objects, hence alleviating the more general problem. */
317   PetscCall(PetscLogEventBegin(MATMFFD_Mult, a, y, 0, 0));
318 
319   w = ctx->w;
320   U = ctx->current_u;
321   F = ctx->current_f;
322   /*
323       Compute differencing parameter
324   */
325   if (!((PetscObject)ctx)->type_name) {
326     PetscCall(MatMFFDSetType(mat, MATMFFD_WP));
327     PetscCall(MatSetFromOptions(mat));
328   }
329   PetscUseTypeMethod(ctx, compute, U, a, &h, &zeroa);
330   if (zeroa) {
331     PetscCall(VecSet(y, 0.0));
332     PetscCall(PetscLogEventEnd(MATMFFD_Mult, a, y, 0, 0));
333     PetscFunctionReturn(PETSC_SUCCESS);
334   }
335 
336   PetscCheck(!mat->erroriffailure || !PetscIsInfOrNanScalar(h), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Computed Nan differencing parameter h");
337   if (ctx->checkh) PetscCall((*ctx->checkh)(ctx->checkhctx, U, a, &h));
338 
339   /* keep a record of the current differencing parameter h */
340   ctx->currenth = h;
341 #if defined(PETSC_USE_COMPLEX)
342   PetscCall(PetscInfo(mat, "Current differencing parameter: %g + %g i\n", (double)PetscRealPart(h), (double)PetscImaginaryPart(h)));
343 #else
344   PetscCall(PetscInfo(mat, "Current differencing parameter: %15.12e\n", (double)PetscRealPart(h)));
345 #endif
346   if (ctx->historyh && ctx->ncurrenth < ctx->maxcurrenth) ctx->historyh[ctx->ncurrenth] = h;
347   ctx->ncurrenth++;
348 
349 #if defined(PETSC_USE_COMPLEX)
350   if (ctx->usecomplex) h = PETSC_i * h;
351 #endif
352 
353   /* w = u + ha */
354   PetscCall(VecWAXPY(w, h, a, U));
355 
356   /* compute func(U) as base for differencing; only needed first time in and not when provided by user */
357   if (ctx->ncurrenth == 1 && ctx->current_f_allocated) PetscCall((*ctx->func)(ctx->funcctx, U, F));
358   PetscCall((*ctx->func)(ctx->funcctx, w, y));
359 
360 #if defined(PETSC_USE_COMPLEX)
361   if (ctx->usecomplex) {
362     PetscCall(VecImaginaryPart(y));
363     h = PetscImaginaryPart(h);
364   } else {
365     PetscCall(VecAXPY(y, -1.0, F));
366   }
367 #else
368   PetscCall(VecAXPY(y, -1.0, F));
369 #endif
370   PetscCall(VecScale(y, 1.0 / h));
371   if (mat->nullsp) PetscCall(MatNullSpaceRemove(mat->nullsp, y));
372 
373   PetscCall(PetscLogEventEnd(MATMFFD_Mult, a, y, 0, 0));
374   PetscFunctionReturn(PETSC_SUCCESS);
375 }
376 
377 /*
378   MatGetDiagonal_MFFD - Gets the diagonal for a matrix-free matrix
379 
380         y ~= (F(u + ha) - F(u))/h,
381   where F = nonlinear function, as set by SNESSetFunction()
382         u = current iterate
383         h = difference interval
384 */
385 static PetscErrorCode MatGetDiagonal_MFFD(Mat mat, Vec a)
386 {
387   MatMFFD     ctx;
388   PetscScalar h, *aa, *ww, v;
389   PetscReal   epsilon = PETSC_SQRT_MACHINE_EPSILON, umin = 100.0 * PETSC_SQRT_MACHINE_EPSILON;
390   Vec         w, U;
391   PetscInt    i, rstart, rend;
392 
393   PetscFunctionBegin;
394   PetscCall(MatShellGetContext(mat, &ctx));
395   PetscCheck(ctx->func, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Requires calling MatMFFDSetFunction() first");
396   PetscCheck(ctx->funci, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Requires calling MatMFFDSetFunctioni() first");
397   w = ctx->w;
398   U = ctx->current_u;
399   PetscCall((*ctx->func)(ctx->funcctx, U, a));
400   if (ctx->funcisetbase) PetscCall((*ctx->funcisetbase)(ctx->funcctx, U));
401   PetscCall(VecCopy(U, w));
402 
403   PetscCall(VecGetOwnershipRange(a, &rstart, &rend));
404   PetscCall(VecGetArray(a, &aa));
405   for (i = rstart; i < rend; i++) {
406     PetscCall(VecGetArray(w, &ww));
407     h = ww[i - rstart];
408     if (h == 0.0) h = 1.0;
409     if (PetscAbsScalar(h) < umin && PetscRealPart(h) >= 0.0) h = umin;
410     else if (PetscRealPart(h) < 0.0 && PetscAbsScalar(h) < umin) h = -umin;
411     h *= epsilon;
412 
413     ww[i - rstart] += h;
414     PetscCall(VecRestoreArray(w, &ww));
415     PetscCall((*ctx->funci)(ctx->funcctx, i, w, &v));
416     aa[i - rstart] = (v - aa[i - rstart]) / h;
417 
418     PetscCall(VecGetArray(w, &ww));
419     ww[i - rstart] -= h;
420     PetscCall(VecRestoreArray(w, &ww));
421   }
422   PetscCall(VecRestoreArray(a, &aa));
423   PetscFunctionReturn(PETSC_SUCCESS);
424 }
425 
426 PETSC_EXTERN PetscErrorCode MatMFFDSetBase_MFFD(Mat J, Vec U, Vec F)
427 {
428   MatMFFD ctx;
429 
430   PetscFunctionBegin;
431   PetscCall(MatShellGetContext(J, &ctx));
432   PetscCall(MatMFFDResetHHistory(J));
433   if (!ctx->current_u) {
434     PetscCall(VecDuplicate(U, &ctx->current_u));
435     PetscCall(VecLockReadPush(ctx->current_u));
436   }
437   PetscCall(VecLockReadPop(ctx->current_u));
438   PetscCall(VecCopy(U, ctx->current_u));
439   PetscCall(VecLockReadPush(ctx->current_u));
440   if (F) {
441     if (ctx->current_f_allocated) PetscCall(VecDestroy(&ctx->current_f));
442     ctx->current_f           = F;
443     ctx->current_f_allocated = PETSC_FALSE;
444   } else if (!ctx->current_f_allocated) {
445     PetscCall(MatCreateVecs(J, NULL, &ctx->current_f));
446     ctx->current_f_allocated = PETSC_TRUE;
447   }
448   if (!ctx->w) PetscCall(VecDuplicate(ctx->current_u, &ctx->w));
449   J->assembled = PETSC_TRUE;
450   PetscFunctionReturn(PETSC_SUCCESS);
451 }
452 
453 typedef PetscErrorCode (*FCN3)(void *, Vec, Vec, PetscScalar *); /* force argument to next function to not be extern C*/
454 
455 static PetscErrorCode MatMFFDSetCheckh_MFFD(Mat J, FCN3 fun, void *ectx)
456 {
457   MatMFFD ctx;
458 
459   PetscFunctionBegin;
460   PetscCall(MatShellGetContext(J, &ctx));
461   ctx->checkh    = fun;
462   ctx->checkhctx = ectx;
463   PetscFunctionReturn(PETSC_SUCCESS);
464 }
465 
466 /*@C
467   MatMFFDSetOptionsPrefix - Sets the prefix used for searching for all
468   MATMFFD` options in the database.
469 
470   Collective
471 
472   Input Parameters:
473 + mat    - the `MATMFFD` context
474 - prefix - the prefix to prepend to all option names
475 
476   Note:
477   A hyphen (-) must NOT be given at the beginning of the prefix name.
478   The first character of all runtime options is AUTOMATICALLY the hyphen.
479 
480   Level: advanced
481 
482 .seealso: [](ch_matrices), `Mat`, `MATMFFD`, `MatSetFromOptions()`, `MatCreateSNESMF()`, `MatCreateMFFD()`
483 @*/
484 PetscErrorCode MatMFFDSetOptionsPrefix(Mat mat, const char prefix[])
485 {
486   MatMFFD mfctx;
487 
488   PetscFunctionBegin;
489   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
490   PetscCall(MatShellGetContext(mat, &mfctx));
491   PetscValidHeaderSpecific(mfctx, MATMFFD_CLASSID, 1);
492   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)mfctx, prefix));
493   PetscFunctionReturn(PETSC_SUCCESS);
494 }
495 
496 static PetscErrorCode MatSetFromOptions_MFFD(Mat mat, PetscOptionItems *PetscOptionsObject)
497 {
498   MatMFFD   mfctx;
499   PetscBool flg;
500   char      ftype[256];
501 
502   PetscFunctionBegin;
503   PetscCall(MatShellGetContext(mat, &mfctx));
504   PetscValidHeaderSpecific(mfctx, MATMFFD_CLASSID, 1);
505   PetscObjectOptionsBegin((PetscObject)mfctx);
506   PetscCall(PetscOptionsFList("-mat_mffd_type", "Matrix free type", "MatMFFDSetType", MatMFFDList, ((PetscObject)mfctx)->type_name, ftype, 256, &flg));
507   if (flg) PetscCall(MatMFFDSetType(mat, ftype));
508 
509   PetscCall(PetscOptionsReal("-mat_mffd_err", "set sqrt relative error in function", "MatMFFDSetFunctionError", mfctx->error_rel, &mfctx->error_rel, NULL));
510   PetscCall(PetscOptionsInt("-mat_mffd_period", "how often h is recomputed", "MatMFFDSetPeriod", mfctx->recomputeperiod, &mfctx->recomputeperiod, NULL));
511 
512   flg = PETSC_FALSE;
513   PetscCall(PetscOptionsBool("-mat_mffd_check_positivity", "Insure that U + h*a is nonnegative", "MatMFFDSetCheckh", flg, &flg, NULL));
514   if (flg) PetscCall(MatMFFDSetCheckh(mat, MatMFFDCheckPositivity, NULL));
515 #if defined(PETSC_USE_COMPLEX)
516   PetscCall(PetscOptionsBool("-mat_mffd_complex", "Use Lyness complex number trick to compute the matrix-vector product", "None", mfctx->usecomplex, &mfctx->usecomplex, NULL));
517 #endif
518   PetscTryTypeMethod(mfctx, setfromoptions, PetscOptionsObject);
519   PetscOptionsEnd();
520   PetscFunctionReturn(PETSC_SUCCESS);
521 }
522 
523 static PetscErrorCode MatMFFDSetPeriod_MFFD(Mat mat, PetscInt period)
524 {
525   MatMFFD ctx;
526 
527   PetscFunctionBegin;
528   PetscCall(MatShellGetContext(mat, &ctx));
529   ctx->recomputeperiod = period;
530   PetscFunctionReturn(PETSC_SUCCESS);
531 }
532 
533 static PetscErrorCode MatMFFDSetFunction_MFFD(Mat mat, PetscErrorCode (*func)(void *, Vec, Vec), void *funcctx)
534 {
535   MatMFFD ctx;
536 
537   PetscFunctionBegin;
538   PetscCall(MatShellGetContext(mat, &ctx));
539   ctx->func    = func;
540   ctx->funcctx = funcctx;
541   PetscFunctionReturn(PETSC_SUCCESS);
542 }
543 
544 static PetscErrorCode MatMFFDSetFunctionError_MFFD(Mat mat, PetscReal error)
545 {
546   PetscFunctionBegin;
547   if (error != (PetscReal)PETSC_DEFAULT) {
548     MatMFFD ctx;
549 
550     PetscCall(MatShellGetContext(mat, &ctx));
551     ctx->error_rel = error;
552   }
553   PetscFunctionReturn(PETSC_SUCCESS);
554 }
555 
556 static PetscErrorCode MatMFFDSetHHistory_MFFD(Mat J, PetscScalar history[], PetscInt nhistory)
557 {
558   MatMFFD ctx;
559 
560   PetscFunctionBegin;
561   PetscCall(MatShellGetContext(J, &ctx));
562   ctx->historyh    = history;
563   ctx->maxcurrenth = nhistory;
564   ctx->currenth    = 0.;
565   PetscFunctionReturn(PETSC_SUCCESS);
566 }
567 
568 /*MC
569   MATMFFD - "mffd" - A matrix-free matrix type.
570 
571   Level: advanced
572 
573   Developer Notes:
574   This is implemented on top of `MATSHELL` to get support for scaling and shifting without requiring duplicate code
575 
576   Users should not MatShell... operations on this class, there is some error checking for that incorrect usage
577 
578 .seealso: [](ch_matrices), `Mat`, `MatCreateMFFD()`, `MatCreateSNESMF()`, `MatMFFDSetFunction()`, `MatMFFDSetType()`,
579           `MatMFFDSetFunctionError()`, `MatMFFDDSSetUmin()`, `MatMFFDSetFunction()`
580           `MatMFFDSetHHistory()`, `MatMFFDResetHHistory()`, `MatCreateSNESMF()`,
581           `MatMFFDGetH()`,
582 M*/
583 PETSC_EXTERN PetscErrorCode MatCreate_MFFD(Mat A)
584 {
585   MatMFFD mfctx;
586 
587   PetscFunctionBegin;
588   PetscCall(MatMFFDInitializePackage());
589 
590   PetscCall(PetscHeaderCreate(mfctx, MATMFFD_CLASSID, "MatMFFD", "Matrix-free Finite Differencing", "Mat", PetscObjectComm((PetscObject)A), NULL, NULL));
591 
592   mfctx->error_rel                = PETSC_SQRT_MACHINE_EPSILON;
593   mfctx->recomputeperiod          = 1;
594   mfctx->count                    = 0;
595   mfctx->currenth                 = 0.0;
596   mfctx->historyh                 = NULL;
597   mfctx->ncurrenth                = 0;
598   mfctx->maxcurrenth              = 0;
599   ((PetscObject)mfctx)->type_name = NULL;
600 
601   /*
602      Create the empty data structure to contain compute-h routines.
603      These will be filled in below from the command line options or
604      a later call with MatMFFDSetType() or if that is not called
605      then it will default in the first use of MatMult_MFFD()
606   */
607   mfctx->ops->compute        = NULL;
608   mfctx->ops->destroy        = NULL;
609   mfctx->ops->view           = NULL;
610   mfctx->ops->setfromoptions = NULL;
611   mfctx->hctx                = NULL;
612 
613   mfctx->func    = NULL;
614   mfctx->funcctx = NULL;
615   mfctx->w       = NULL;
616   mfctx->mat     = A;
617 
618   PetscCall(MatSetType(A, MATSHELL));
619   PetscCall(MatShellSetContext(A, mfctx));
620   PetscCall(MatShellSetOperation(A, MATOP_MULT, (void (*)(void))MatMult_MFFD));
621   PetscCall(MatShellSetOperation(A, MATOP_DESTROY, (void (*)(void))MatDestroy_MFFD));
622   PetscCall(MatShellSetOperation(A, MATOP_VIEW, (void (*)(void))MatView_MFFD));
623   PetscCall(MatShellSetOperation(A, MATOP_ASSEMBLY_END, (void (*)(void))MatAssemblyEnd_MFFD));
624   PetscCall(MatShellSetOperation(A, MATOP_SET_FROM_OPTIONS, (void (*)(void))MatSetFromOptions_MFFD));
625   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatShellSetContext_C", MatShellSetContext_Immutable));
626   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatShellSetContextDestroy_C", MatShellSetContextDestroy_Immutable));
627   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatShellSetManageScalingShifts_C", MatShellSetManageScalingShifts_Immutable));
628 
629   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatMFFDSetBase_C", MatMFFDSetBase_MFFD));
630   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatMFFDSetFunctioniBase_C", MatMFFDSetFunctioniBase_MFFD));
631   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatMFFDSetFunctioni_C", MatMFFDSetFunctioni_MFFD));
632   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatMFFDSetFunction_C", MatMFFDSetFunction_MFFD));
633   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatMFFDSetCheckh_C", MatMFFDSetCheckh_MFFD));
634   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatMFFDSetPeriod_C", MatMFFDSetPeriod_MFFD));
635   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatMFFDSetFunctionError_C", MatMFFDSetFunctionError_MFFD));
636   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatMFFDResetHHistory_C", MatMFFDResetHHistory_MFFD));
637   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatMFFDSetHHistory_C", MatMFFDSetHHistory_MFFD));
638   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatMFFDSetType_C", MatMFFDSetType_MFFD));
639   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatMFFDGetH_C", MatMFFDGetH_MFFD));
640   PetscCall(PetscObjectChangeTypeName((PetscObject)A, MATMFFD));
641   PetscFunctionReturn(PETSC_SUCCESS);
642 }
643 
644 /*@
645   MatCreateMFFD - Creates a matrix-free matrix of type `MATMFFD`. See also `MatCreateSNESMF()`
646 
647   Collective
648 
649   Input Parameters:
650 + comm - MPI communicator
651 . m    - number of local rows (or `PETSC_DECIDE` to have calculated if `M` is given)
652            This value should be the same as the local size used in creating the
653            y vector for the matrix-vector product y = Ax.
654 . n    - This value should be the same as the local size used in creating the
655        x vector for the matrix-vector product y = Ax. (or `PETSC_DECIDE` to have
656        calculated if `N` is given) For square matrices `n` is almost always `m`.
657 . M    - number of global rows (or `PETSC_DETERMINE` to have calculated if `m` is given)
658 - N    - number of global columns (or `PETSC_DETERMINE` to have calculated if `n` is given)
659 
660   Output Parameter:
661 . J - the matrix-free matrix
662 
663   Options Database Keys:
664 + -mat_mffd_type             - wp or ds (see `MATMFFD_WP` or `MATMFFD_DS`)
665 . -mat_mffd_err              - square root of estimated relative error in function evaluation
666 . -mat_mffd_period           - how often h is recomputed, defaults to 1, every time
667 . -mat_mffd_check_positivity - possibly decrease `h` until U + h*a has only positive values
668 . -mat_mffd_umin <umin>      - Sets umin (for default PETSc routine that computes h only)
669 - -mat_mffd_complex          - use the Lyness trick with complex numbers to compute the matrix-vector product instead of differencing
670                        (requires real valued functions but that PETSc be configured for complex numbers)
671 
672   Level: advanced
673 
674   Notes:
675   The matrix-free matrix context merely contains the function pointers
676   and work space for performing finite difference approximations of
677   Jacobian-vector products, F'(u)*a,
678 
679   The default code uses the following approach to compute h
680 
681 .vb
682      F'(u)*a = [F(u+h*a) - F(u)]/h where
683      h = error_rel*u'a/||a||^2                        if  |u'a| > umin*||a||_{1}
684        = error_rel*umin*sign(u'a)*||a||_{1}/||a||^2   otherwise
685  where
686      error_rel = square root of relative error in function evaluation
687      umin = minimum iterate parameter
688 .ve
689 
690   You can call `SNESSetJacobian()` with `MatMFFDComputeJacobian()` if you are using matrix and not a different
691   preconditioner matrix
692 
693   The user can set the error_rel via `MatMFFDSetFunctionError()` and
694   umin via `MatMFFDDSSetUmin()`.
695 
696   The user should call `MatDestroy()` when finished with the matrix-free
697   matrix context.
698 
699 .seealso: [](ch_matrices), `Mat`, `MATMFFD`, `MatDestroy()`, `MatMFFDSetFunctionError()`, `MatMFFDDSSetUmin()`, `MatMFFDSetFunction()`
700           `MatMFFDSetHHistory()`, `MatMFFDResetHHistory()`, `MatCreateSNESMF()`,
701           `MatMFFDGetH()`, `MatMFFDRegister()`, `MatMFFDComputeJacobian()`
702 @*/
703 PetscErrorCode MatCreateMFFD(MPI_Comm comm, PetscInt m, PetscInt n, PetscInt M, PetscInt N, Mat *J)
704 {
705   PetscFunctionBegin;
706   PetscCall(MatCreate(comm, J));
707   PetscCall(MatSetSizes(*J, m, n, M, N));
708   PetscCall(MatSetType(*J, MATMFFD));
709   PetscCall(MatSetUp(*J));
710   PetscFunctionReturn(PETSC_SUCCESS);
711 }
712 
713 /*@
714   MatMFFDGetH - Gets the last value that was used as the differencing for a `MATMFFD` matrix
715   parameter.
716 
717   Not Collective
718 
719   Input Parameters:
720 . mat - the `MATMFFD` matrix
721 
722   Output Parameter:
723 . h - the differencing step size
724 
725   Level: advanced
726 
727 .seealso: [](ch_matrices), `Mat`, `MATMFFD`, `MatCreateSNESMF()`, `MatMFFDSetHHistory()`, `MatCreateMFFD()`, `MatMFFDResetHHistory()`
728 @*/
729 PetscErrorCode MatMFFDGetH(Mat mat, PetscScalar *h)
730 {
731   PetscFunctionBegin;
732   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
733   PetscAssertPointer(h, 2);
734   PetscUseMethod(mat, "MatMFFDGetH_C", (Mat, PetscScalar *), (mat, h));
735   PetscFunctionReturn(PETSC_SUCCESS);
736 }
737 
738 /*@C
739   MatMFFDSetFunction - Sets the function used in applying the matrix-free `MATMFFD` matrix.
740 
741   Logically Collective
742 
743   Input Parameters:
744 + mat     - the matrix-free matrix `MATMFFD` created via `MatCreateSNESMF()` or `MatCreateMFFD()`
745 . func    - the function to use
746 - funcctx - optional function context passed to function
747 
748   Calling sequence of `func`:
749 + funcctx - user provided context
750 . x       - input vector
751 - f       - computed output function
752 
753   Level: advanced
754 
755   Notes:
756   If you use this you MUST call `MatAssemblyBegin()` and `MatAssemblyEnd()` on the matrix-free
757   matrix inside your compute Jacobian routine
758 
759   If this is not set then it will use the function set with `SNESSetFunction()` if `MatCreateSNESMF()` was used.
760 
761 .seealso: [](ch_matrices), `Mat`, `MATMFFD`, `MatCreateSNESMF()`, `MatMFFDGetH()`, `MatCreateMFFD()`,
762           `MatMFFDSetHHistory()`, `MatMFFDResetHHistory()`, `SNESSetFunction()`
763 @*/
764 PetscErrorCode MatMFFDSetFunction(Mat mat, PetscErrorCode (*func)(void *funcctx, Vec x, Vec f), void *funcctx)
765 {
766   PetscFunctionBegin;
767   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
768   PetscTryMethod(mat, "MatMFFDSetFunction_C", (Mat, PetscErrorCode(*)(void *, Vec, Vec), void *), (mat, func, funcctx));
769   PetscFunctionReturn(PETSC_SUCCESS);
770 }
771 
772 /*@C
773   MatMFFDSetFunctioni - Sets the function for a single component for a `MATMFFD` matrix
774 
775   Logically Collective
776 
777   Input Parameters:
778 + mat   - the matrix-free matrix `MATMFFD`
779 - funci - the function to use
780 
781   Level: advanced
782 
783   Notes:
784   If you use this you MUST call `MatAssemblyBegin()` and `MatAssemblyEnd()` on the matrix-free
785   matrix inside your compute Jacobian routine.
786 
787   This function is necessary to compute the diagonal of the matrix.
788   funci must not contain any MPI call as it is called inside a loop on the local portion of the vector.
789 
790 .seealso: [](ch_matrices), `Mat`, `MATMFFD`, `MatCreateSNESMF()`, `MatMFFDGetH()`, `MatMFFDSetHHistory()`, `MatMFFDResetHHistory()`, `SNESetFunction()`, `MatGetDiagonal()`
791 @*/
792 PetscErrorCode MatMFFDSetFunctioni(Mat mat, PetscErrorCode (*funci)(void *, PetscInt, Vec, PetscScalar *))
793 {
794   PetscFunctionBegin;
795   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
796   PetscTryMethod(mat, "MatMFFDSetFunctioni_C", (Mat, PetscErrorCode(*)(void *, PetscInt, Vec, PetscScalar *)), (mat, funci));
797   PetscFunctionReturn(PETSC_SUCCESS);
798 }
799 
800 /*@C
801   MatMFFDSetFunctioniBase - Sets the base vector for a single component function evaluation for a `MATMFFD` matrix
802 
803   Logically Collective
804 
805   Input Parameters:
806 + mat  - the `MATMFFD` matrix-free matrix
807 - func - the function to use
808 
809   Level: advanced
810 
811   Notes:
812   If you use this you MUST call `MatAssemblyBegin()` and `MatAssemblyEnd()` on the matrix-free
813   matrix inside your compute Jacobian routine.
814 
815   This function is necessary to compute the diagonal of the matrix, used for example with `PCJACOBI`
816 
817 .seealso: [](ch_matrices), `Mat`, `MATMFFD`, `MatCreateSNESMF()`, `MatMFFDGetH()`, `MatCreateMFFD()`,
818           `MatMFFDSetHHistory()`, `MatMFFDResetHHistory()`, `SNESetFunction()`, `MatGetDiagonal()`
819 @*/
820 PetscErrorCode MatMFFDSetFunctioniBase(Mat mat, PetscErrorCode (*func)(void *, Vec))
821 {
822   PetscFunctionBegin;
823   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
824   PetscTryMethod(mat, "MatMFFDSetFunctioniBase_C", (Mat, PetscErrorCode(*)(void *, Vec)), (mat, func));
825   PetscFunctionReturn(PETSC_SUCCESS);
826 }
827 
828 /*@
829   MatMFFDSetPeriod - Sets how often h is recomputed for a `MATMFFD` matrix, by default it is every time
830 
831   Logically Collective
832 
833   Input Parameters:
834 + mat    - the `MATMFFD` matrix-free matrix
835 - period - 1 for every time, 2 for every second etc
836 
837   Options Database Key:
838 . -mat_mffd_period <period> - Sets how often `h` is recomputed
839 
840   Level: advanced
841 
842 .seealso: [](ch_matrices), `Mat`, `MATMFFD`, `MatCreateSNESMF()`, `MatMFFDGetH()`,
843           `MatMFFDSetHHistory()`, `MatMFFDResetHHistory()`
844 @*/
845 PetscErrorCode MatMFFDSetPeriod(Mat mat, PetscInt period)
846 {
847   PetscFunctionBegin;
848   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
849   PetscValidLogicalCollectiveInt(mat, period, 2);
850   PetscTryMethod(mat, "MatMFFDSetPeriod_C", (Mat, PetscInt), (mat, period));
851   PetscFunctionReturn(PETSC_SUCCESS);
852 }
853 
854 /*@
855   MatMFFDSetFunctionError - Sets the error_rel for the approximation of matrix-vector products using finite differences with the `MATMFFD` matrix
856 
857   Logically Collective
858 
859   Input Parameters:
860 + mat   - the `MATMFFD` matrix-free matrix
861 - error - relative error (should be set to the square root of the relative error in the function evaluations)
862 
863   Options Database Key:
864 . -mat_mffd_err <error_rel> - Sets error_rel
865 
866   Level: advanced
867 
868   Note:
869   The default matrix-free matrix-vector product routine computes
870 .vb
871      F'(u)*a = [F(u+h*a) - F(u)]/h where
872      h = error_rel*u'a/||a||^2                        if  |u'a| > umin*||a||_{1}
873        = error_rel*umin*sign(u'a)*||a||_{1}/||a||^2   else
874 .ve
875 
876 .seealso: [](ch_matrices), `Mat`, `MATMFFD`, `MatCreateSNESMF()`, `MatMFFDGetH()`, `MatCreateMFFD()`,
877           `MatMFFDSetHHistory()`, `MatMFFDResetHHistory()`
878 @*/
879 PetscErrorCode MatMFFDSetFunctionError(Mat mat, PetscReal error)
880 {
881   PetscFunctionBegin;
882   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
883   PetscValidLogicalCollectiveReal(mat, error, 2);
884   PetscTryMethod(mat, "MatMFFDSetFunctionError_C", (Mat, PetscReal), (mat, error));
885   PetscFunctionReturn(PETSC_SUCCESS);
886 }
887 
888 /*@
889   MatMFFDSetHHistory - Sets an array to collect a history of the
890   differencing values (h) computed for the matrix-free product `MATMFFD` matrix
891 
892   Logically Collective
893 
894   Input Parameters:
895 + J        - the `MATMFFD` matrix-free matrix
896 . history  - space to hold the history
897 - nhistory - number of entries in history, if more entries are generated than
898               nhistory, then the later ones are discarded
899 
900   Level: advanced
901 
902   Note:
903   Use `MatMFFDResetHHistory()` to reset the history counter and collect
904   a new batch of differencing parameters, h.
905 
906 .seealso: [](ch_matrices), `Mat`, `MATMFFD`, `MatMFFDGetH()`, `MatCreateSNESMF()`,
907           `MatMFFDResetHHistory()`, `MatMFFDSetFunctionError()`
908 @*/
909 PetscErrorCode MatMFFDSetHHistory(Mat J, PetscScalar history[], PetscInt nhistory)
910 {
911   PetscFunctionBegin;
912   PetscValidHeaderSpecific(J, MAT_CLASSID, 1);
913   if (history) PetscAssertPointer(history, 2);
914   PetscValidLogicalCollectiveInt(J, nhistory, 3);
915   PetscUseMethod(J, "MatMFFDSetHHistory_C", (Mat, PetscScalar[], PetscInt), (J, history, nhistory));
916   PetscFunctionReturn(PETSC_SUCCESS);
917 }
918 
919 /*@
920   MatMFFDResetHHistory - Resets the counter to zero to begin
921   collecting a new set of differencing histories for the `MATMFFD` matrix
922 
923   Logically Collective
924 
925   Input Parameter:
926 . J - the matrix-free matrix context
927 
928   Level: advanced
929 
930   Note:
931   Use `MatMFFDSetHHistory()` to create the original history counter.
932 
933 .seealso: [](ch_matrices), `Mat`, `MATMFFD`, `MatMFFDGetH()`, `MatCreateSNESMF()`,
934           `MatMFFDSetHHistory()`, `MatMFFDSetFunctionError()`
935 @*/
936 PetscErrorCode MatMFFDResetHHistory(Mat J)
937 {
938   PetscFunctionBegin;
939   PetscValidHeaderSpecific(J, MAT_CLASSID, 1);
940   PetscTryMethod(J, "MatMFFDResetHHistory_C", (Mat), (J));
941   PetscFunctionReturn(PETSC_SUCCESS);
942 }
943 
944 /*@
945   MatMFFDSetBase - Sets the vector `U` at which matrix vector products of the
946   Jacobian are computed for the `MATMFFD` matrix
947 
948   Logically Collective
949 
950   Input Parameters:
951 + J - the `MATMFFD` matrix
952 . U - the vector
953 - F - (optional) vector that contains F(u) if it has been already computed
954 
955   Level: advanced
956 
957   Notes:
958   This is rarely used directly
959 
960   If `F` is provided then it is not recomputed. Otherwise the function is evaluated at the base
961   point during the first `MatMult()` after each call to `MatMFFDSetBase()`.
962 
963 .seealso: [](ch_matrices), `Mat`, `MATMFFD`, `MatMult()`
964 @*/
965 PetscErrorCode MatMFFDSetBase(Mat J, Vec U, Vec F)
966 {
967   PetscFunctionBegin;
968   PetscValidHeaderSpecific(J, MAT_CLASSID, 1);
969   PetscValidHeaderSpecific(U, VEC_CLASSID, 2);
970   if (F) PetscValidHeaderSpecific(F, VEC_CLASSID, 3);
971   PetscTryMethod(J, "MatMFFDSetBase_C", (Mat, Vec, Vec), (J, U, F));
972   PetscFunctionReturn(PETSC_SUCCESS);
973 }
974 
975 /*@C
976   MatMFFDSetCheckh - Sets a function that checks the computed h and adjusts
977   it to satisfy some criteria for the `MATMFFD` matrix
978 
979   Logically Collective
980 
981   Input Parameters:
982 + J   - the `MATMFFD` matrix
983 . fun - the function that checks `h`
984 - ctx - any context needed by the function
985 
986   Options Database Keys:
987 . -mat_mffd_check_positivity <bool> - Insure that U + h*a is non-negative
988 
989   Level: advanced
990 
991   Notes:
992   For example, `MatMFFDCheckPositivity()` insures that all entries of U + h*a are non-negative
993 
994   The function you provide is called after the default `h` has been computed and allows you to
995   modify it.
996 
997 .seealso: [](ch_matrices), `Mat`, `MATMFFD`, `MatMFFDCheckPositivity()`
998 @*/
999 PetscErrorCode MatMFFDSetCheckh(Mat J, PetscErrorCode (*fun)(void *, Vec, Vec, PetscScalar *), void *ctx)
1000 {
1001   PetscFunctionBegin;
1002   PetscValidHeaderSpecific(J, MAT_CLASSID, 1);
1003   PetscTryMethod(J, "MatMFFDSetCheckh_C", (Mat, PetscErrorCode(*)(void *, Vec, Vec, PetscScalar *), void *), (J, fun, ctx));
1004   PetscFunctionReturn(PETSC_SUCCESS);
1005 }
1006 
1007 /*@
1008   MatMFFDCheckPositivity - Checks that all entries in U + h*a are positive or
1009   zero, decreases h until this is satisfied for a `MATMFFD` matrix
1010 
1011   Logically Collective
1012 
1013   Input Parameters:
1014 + U     - base vector that is added to
1015 . a     - vector that is added
1016 . h     - scaling factor on a
1017 - dummy - context variable (unused)
1018 
1019   Options Database Keys:
1020 . -mat_mffd_check_positivity <bool> - Insure that U + h*a is nonnegative
1021 
1022   Level: advanced
1023 
1024   Note:
1025   This is rarely used directly, rather it is passed as an argument to `MatMFFDSetCheckh()`
1026 
1027 .seealso: [](ch_matrices), `Mat`, `MATMFFD`, `MatMFFDSetCheckh()`
1028 @*/
1029 PetscErrorCode MatMFFDCheckPositivity(void *dummy, Vec U, Vec a, PetscScalar *h)
1030 {
1031   PetscReal    val, minval;
1032   PetscScalar *u_vec, *a_vec;
1033   PetscInt     i, n;
1034   MPI_Comm     comm;
1035 
1036   PetscFunctionBegin;
1037   PetscValidHeaderSpecific(U, VEC_CLASSID, 2);
1038   PetscValidHeaderSpecific(a, VEC_CLASSID, 3);
1039   PetscAssertPointer(h, 4);
1040   PetscCall(PetscObjectGetComm((PetscObject)U, &comm));
1041   PetscCall(VecGetArray(U, &u_vec));
1042   PetscCall(VecGetArray(a, &a_vec));
1043   PetscCall(VecGetLocalSize(U, &n));
1044   minval = PetscAbsScalar(*h) * PetscRealConstant(1.01);
1045   for (i = 0; i < n; i++) {
1046     if (PetscRealPart(u_vec[i] + *h * a_vec[i]) <= 0.0) {
1047       val = PetscAbsScalar(u_vec[i] / a_vec[i]);
1048       if (val < minval) minval = val;
1049     }
1050   }
1051   PetscCall(VecRestoreArray(U, &u_vec));
1052   PetscCall(VecRestoreArray(a, &a_vec));
1053   PetscCall(MPIU_Allreduce(&minval, &val, 1, MPIU_REAL, MPIU_MIN, comm));
1054   if (val <= PetscAbsScalar(*h)) {
1055     PetscCall(PetscInfo(U, "Scaling back h from %g to %g\n", (double)PetscRealPart(*h), (double)(.99 * val)));
1056     if (PetscRealPart(*h) > 0.0) *h = 0.99 * val;
1057     else *h = -0.99 * val;
1058   }
1059   PetscFunctionReturn(PETSC_SUCCESS);
1060 }
1061