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