xref: /petsc/src/mat/impls/mffd/mffd.c (revision d5b43468fb8780a8feea140ccd6fa3e6a50411cc)
1 
2 #include <petsc/private/matimpl.h>
3 #include <../src/mat/impls/mffd/mffdimpl.h> /*I  "petscmat.h"   I*/
4 
5 PetscFunctionList MatMFFDList              = NULL;
6 PetscBool         MatMFFDRegisterAllCalled = PETSC_FALSE;
7 
8 PetscClassId  MATMFFD_CLASSID;
9 PetscLogEvent MATMFFD_Mult;
10 
11 static PetscBool MatMFFDPackageInitialized = PETSC_FALSE;
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: `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(0);
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: `MATMFFD`, `PetscInitialize()`
36 @*/
37 PetscErrorCode MatMFFDInitializePackage(void)
38 {
39   char      logList[256];
40   PetscBool opt, pkg;
41 
42   PetscFunctionBegin;
43   if (MatMFFDPackageInitialized) PetscFunctionReturn(0);
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(0);
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   PetscValidCharPointer(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(0);
83 
84   /* destroy the old one if it exists */
85   PetscTryTypeMethod(ctx, destroy);
86 
87   PetscCall(PetscFunctionListFind(MatMFFDList, ftype, &r));
88   PetscCheck(r, PETSC_COMM_SELF, PETSC_ERR_ARG_UNKNOWN_TYPE, "Unknown MatMFFD type %s given", ftype);
89   PetscCall((*r)(ctx));
90   PetscCall(PetscObjectChangeTypeName((PetscObject)ctx, ftype));
91   PetscFunctionReturn(0);
92 }
93 
94 /*@C
95     MatMFFDSetType - Sets the method that is used to compute the
96     differencing parameter for finite differene 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 
109                         F(x+ha) - F(x)
110           F'(u)a  ~=  ----------------
111                               h
112 
113 .seealso: `MATMFFD`, `MATMFFD_WP`, `MATMFFD_DS`, `MatCreateSNESMF()`, `MatMFFDRegister()`, `MatMFFDSetFunction()`, `MatCreateMFFD()`
114 @*/
115 PetscErrorCode MatMFFDSetType(Mat mat, MatMFFDType ftype)
116 {
117   PetscFunctionBegin;
118   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
119   PetscValidCharPointer(ftype, 2);
120   PetscTryMethod(mat, "MatMFFDSetType_C", (Mat, MatMFFDType), (mat, ftype));
121   PetscFunctionReturn(0);
122 }
123 
124 static PetscErrorCode MatGetDiagonal_MFFD(Mat, Vec);
125 
126 typedef PetscErrorCode (*FCN1)(void *, Vec); /* force argument to next function to not be extern C*/
127 static PetscErrorCode MatMFFDSetFunctioniBase_MFFD(Mat mat, FCN1 func)
128 {
129   MatMFFD ctx;
130 
131   PetscFunctionBegin;
132   PetscCall(MatShellGetContext(mat, &ctx));
133   ctx->funcisetbase = func;
134   PetscFunctionReturn(0);
135 }
136 
137 typedef PetscErrorCode (*FCN2)(void *, PetscInt, Vec, PetscScalar *); /* force argument to next function to not be extern C*/
138 static PetscErrorCode MatMFFDSetFunctioni_MFFD(Mat mat, FCN2 funci)
139 {
140   MatMFFD ctx;
141 
142   PetscFunctionBegin;
143   PetscCall(MatShellGetContext(mat, &ctx));
144   ctx->funci = funci;
145   PetscCall(MatShellSetOperation(mat, MATOP_GET_DIAGONAL, (void (*)(void))MatGetDiagonal_MFFD));
146   PetscFunctionReturn(0);
147 }
148 
149 static PetscErrorCode MatMFFDGetH_MFFD(Mat mat, PetscScalar *h)
150 {
151   MatMFFD ctx;
152 
153   PetscFunctionBegin;
154   PetscCall(MatShellGetContext(mat, &ctx));
155   *h = ctx->currenth;
156   PetscFunctionReturn(0);
157 }
158 
159 static PetscErrorCode MatMFFDResetHHistory_MFFD(Mat J)
160 {
161   MatMFFD ctx;
162 
163   PetscFunctionBegin;
164   PetscCall(MatShellGetContext(J, &ctx));
165   ctx->ncurrenth = 0;
166   PetscFunctionReturn(0);
167 }
168 
169 /*@C
170    MatMFFDRegister - Adds a method to the MATMFFD` registry.
171 
172    Not Collective
173 
174    Input Parameters:
175 +  name_solver - name of a new user-defined compute-h module
176 -  routine_create - routine to create method context
177 
178    Level: developer
179 
180    Note:
181    `MatMFFDRegister()` may be called multiple times to add several user-defined solvers.
182 
183    Sample usage:
184 .vb
185    MatMFFDRegister("my_h",MyHCreate);
186 .ve
187 
188    Then, your solver can be chosen with the procedural interface via
189 $     `MatMFFDSetType`(mfctx,"my_h")
190    or at runtime via the option
191 $     -mat_mffd_type my_h
192 
193 .seealso: `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(0);
201 }
202 
203 /* ----------------------------------------------------------------------------------------*/
204 static PetscErrorCode MatDestroy_MFFD(Mat mat)
205 {
206   MatMFFD ctx;
207 
208   PetscFunctionBegin;
209   PetscCall(MatShellGetContext(mat, &ctx));
210   PetscCall(VecDestroy(&ctx->w));
211   PetscCall(VecDestroy(&ctx->current_u));
212   if (ctx->current_f_allocated) PetscCall(VecDestroy(&ctx->current_f));
213   PetscTryTypeMethod(ctx, destroy);
214   PetscCall(PetscHeaderDestroy(&ctx));
215 
216   PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatMFFDSetBase_C", NULL));
217   PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatMFFDSetFunctioniBase_C", NULL));
218   PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatMFFDSetFunctioni_C", NULL));
219   PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatMFFDSetFunction_C", NULL));
220   PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatMFFDSetFunctionError_C", NULL));
221   PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatMFFDSetCheckh_C", NULL));
222   PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatMFFDSetPeriod_C", NULL));
223   PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatMFFDResetHHistory_C", NULL));
224   PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatMFFDSetHHistory_C", NULL));
225   PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatMFFDSetType_C", NULL));
226   PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatMFFDGetH_C", NULL));
227   PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatSNESMFSetReuseBase_C", NULL));
228   PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatSNESMFGetReuseBase_C", NULL));
229   PetscFunctionReturn(0);
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(0);
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(0);
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 \n\t\tMatAssemblyBegin/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(0);
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(0);
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 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(0);
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(0);
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(0);
464 }
465 
466 /*@C
467    MatMFFDSetOptionsPrefix - Sets the prefix used for searching for all
468    MATMFFD` options in the database.
469 
470    Collective on mat
471 
472    Input Parameters:
473 +  A - 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: `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(0);
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(0);
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(0);
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(0);
542 }
543 
544 static PetscErrorCode MatMFFDSetFunctionError_MFFD(Mat mat, PetscReal error)
545 {
546   MatMFFD ctx;
547 
548   PetscFunctionBegin;
549   PetscCall(MatShellGetContext(mat, &ctx));
550   if (error != PETSC_DEFAULT) ctx->error_rel = error;
551   PetscFunctionReturn(0);
552 }
553 
554 PetscErrorCode MatMFFDSetHHistory_MFFD(Mat J, PetscScalar history[], PetscInt nhistory)
555 {
556   MatMFFD ctx;
557 
558   PetscFunctionBegin;
559   PetscCall(MatShellGetContext(J, &ctx));
560   ctx->historyh    = history;
561   ctx->maxcurrenth = nhistory;
562   ctx->currenth    = 0.;
563   PetscFunctionReturn(0);
564 }
565 
566 /*MC
567   MATMFFD - MATMFFD = "mffd" - A matrix free matrix type.
568 
569   Level: advanced
570 
571   Developers Note: This is implemented on top of `MATSHELL` to get support for scaling and shifting without requiring duplicate code
572 
573 .seealso: `MatCreateMFFD()`, `MatCreateSNESMF()`, `MatMFFDSetFunction()`, `MatMFFDSetType()`,
574           `MatMFFDSetFunctionError()`, `MatMFFDDSSetUmin()`, `MatMFFDSetFunction()`
575           `MatMFFDSetHHistory()`, `MatMFFDResetHHistory()`, `MatCreateSNESMF()`,
576           `MatMFFDGetH()`,
577 M*/
578 PETSC_EXTERN PetscErrorCode MatCreate_MFFD(Mat A)
579 {
580   MatMFFD mfctx;
581 
582   PetscFunctionBegin;
583   PetscCall(MatMFFDInitializePackage());
584 
585   PetscCall(PetscHeaderCreate(mfctx, MATMFFD_CLASSID, "MatMFFD", "Matrix-free Finite Differencing", "Mat", PetscObjectComm((PetscObject)A), NULL, NULL));
586 
587   mfctx->error_rel                = PETSC_SQRT_MACHINE_EPSILON;
588   mfctx->recomputeperiod          = 1;
589   mfctx->count                    = 0;
590   mfctx->currenth                 = 0.0;
591   mfctx->historyh                 = NULL;
592   mfctx->ncurrenth                = 0;
593   mfctx->maxcurrenth              = 0;
594   ((PetscObject)mfctx)->type_name = NULL;
595 
596   /*
597      Create the empty data structure to contain compute-h routines.
598      These will be filled in below from the command line options or
599      a later call with MatMFFDSetType() or if that is not called
600      then it will default in the first use of MatMult_MFFD()
601   */
602   mfctx->ops->compute        = NULL;
603   mfctx->ops->destroy        = NULL;
604   mfctx->ops->view           = NULL;
605   mfctx->ops->setfromoptions = NULL;
606   mfctx->hctx                = NULL;
607 
608   mfctx->func    = NULL;
609   mfctx->funcctx = NULL;
610   mfctx->w       = NULL;
611   mfctx->mat     = A;
612 
613   PetscCall(MatSetType(A, MATSHELL));
614   PetscCall(MatShellSetContext(A, mfctx));
615   PetscCall(MatShellSetOperation(A, MATOP_MULT, (void (*)(void))MatMult_MFFD));
616   PetscCall(MatShellSetOperation(A, MATOP_DESTROY, (void (*)(void))MatDestroy_MFFD));
617   PetscCall(MatShellSetOperation(A, MATOP_VIEW, (void (*)(void))MatView_MFFD));
618   PetscCall(MatShellSetOperation(A, MATOP_ASSEMBLY_END, (void (*)(void))MatAssemblyEnd_MFFD));
619   PetscCall(MatShellSetOperation(A, MATOP_SET_FROM_OPTIONS, (void (*)(void))MatSetFromOptions_MFFD));
620 
621   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatMFFDSetBase_C", MatMFFDSetBase_MFFD));
622   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatMFFDSetFunctioniBase_C", MatMFFDSetFunctioniBase_MFFD));
623   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatMFFDSetFunctioni_C", MatMFFDSetFunctioni_MFFD));
624   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatMFFDSetFunction_C", MatMFFDSetFunction_MFFD));
625   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatMFFDSetCheckh_C", MatMFFDSetCheckh_MFFD));
626   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatMFFDSetPeriod_C", MatMFFDSetPeriod_MFFD));
627   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatMFFDSetFunctionError_C", MatMFFDSetFunctionError_MFFD));
628   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatMFFDResetHHistory_C", MatMFFDResetHHistory_MFFD));
629   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatMFFDSetHHistory_C", MatMFFDSetHHistory_MFFD));
630   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatMFFDSetType_C", MatMFFDSetType_MFFD));
631   PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatMFFDGetH_C", MatMFFDGetH_MFFD));
632   PetscCall(PetscObjectChangeTypeName((PetscObject)A, MATMFFD));
633   PetscFunctionReturn(0);
634 }
635 
636 /*@
637    MatCreateMFFD - Creates a matrix-free matrix of type `MATMFFD`. See also `MatCreateSNESMF()`
638 
639    Collective
640 
641    Input Parameters:
642 +  comm - MPI communicator
643 .  m - number of local rows (or `PETSC_DECIDE` to have calculated if M is given)
644            This value should be the same as the local size used in creating the
645            y vector for the matrix-vector product y = Ax.
646 .  n - This value should be the same as the local size used in creating the
647        x vector for the matrix-vector product y = Ax. (or `PETSC_DECIDE` to have
648        calculated if N is given) For square matrices n is almost always m.
649 .  M - number of global rows (or `PETSC_DETERMINE` to have calculated if m is given)
650 -  N - number of global columns (or `PETSC_DETERMINE` to have calculated if n is given)
651 
652    Output Parameter:
653 .  J - the matrix-free matrix
654 
655    Options Database Keys:
656 +  -mat_mffd_type - wp or ds (see `MATMFFD_WP` or `MATMFFD_DS`)
657 .  -mat_mffd_err - square root of estimated relative error in function evaluation
658 .  -mat_mffd_period - how often h is recomputed, defaults to 1, every time
659 .  -mat_mffd_check_positivity - possibly decrease h until U + h*a has only positive values
660 .  -mat_mffd_umin <umin> - Sets umin (for default PETSc routine that computes h only)
661 -  -mat_mffd_complex - use the Lyness trick with complex numbers to compute the matrix-vector product instead of differencing
662                        (requires real valued functions but that PETSc be configured for complex numbers)
663 
664    Level: advanced
665 
666    Notes:
667    The matrix-free matrix context merely contains the function pointers
668    and work space for performing finite difference approximations of
669    Jacobian-vector products, F'(u)*a,
670 
671    The default code uses the following approach to compute h
672 
673 .vb
674      F'(u)*a = [F(u+h*a) - F(u)]/h where
675      h = error_rel*u'a/||a||^2                        if  |u'a| > umin*||a||_{1}
676        = error_rel*umin*sign(u'a)*||a||_{1}/||a||^2   otherwise
677  where
678      error_rel = square root of relative error in function evaluation
679      umin = minimum iterate parameter
680 .ve
681 
682    You can call `SNESSetJacobian()` with `MatMFFDComputeJacobian()` if you are using matrix and not a different
683    preconditioner matrix
684 
685    The user can set the error_rel via `MatMFFDSetFunctionError()` and
686    umin via `MatMFFDDSSetUmin()`; see Users-Manual: ch_snes for details.
687 
688    The user should call `MatDestroy()` when finished with the matrix-free
689    matrix context.
690 
691 .seealso: `MATMFFD`, `MatDestroy()`, `MatMFFDSetFunctionError()`, `MatMFFDDSSetUmin()`, `MatMFFDSetFunction()`
692           `MatMFFDSetHHistory()`, `MatMFFDResetHHistory()`, `MatCreateSNESMF()`,
693           `MatMFFDGetH()`, `MatMFFDRegister()`, `MatMFFDComputeJacobian()`
694 @*/
695 PetscErrorCode MatCreateMFFD(MPI_Comm comm, PetscInt m, PetscInt n, PetscInt M, PetscInt N, Mat *J)
696 {
697   PetscFunctionBegin;
698   PetscCall(MatCreate(comm, J));
699   PetscCall(MatSetSizes(*J, m, n, M, N));
700   PetscCall(MatSetType(*J, MATMFFD));
701   PetscCall(MatSetUp(*J));
702   PetscFunctionReturn(0);
703 }
704 
705 /*@
706    MatMFFDGetH - Gets the last value that was used as the differencing for a `MATMFFD` matrix
707    parameter.
708 
709    Not Collective
710 
711    Input Parameters:
712 .  mat - the `MATMFFD` matrix
713 
714    Output Parameter:
715 .  h - the differencing step size
716 
717    Level: advanced
718 
719 .seealso: `MATMFFD`, `MatCreateSNESMF()`, `MatMFFDSetHHistory()`, `MatCreateMFFD()`, `MATMFFD`, `MatMFFDResetHHistory()`
720 @*/
721 PetscErrorCode MatMFFDGetH(Mat mat, PetscScalar *h)
722 {
723   PetscFunctionBegin;
724   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
725   PetscValidScalarPointer(h, 2);
726   PetscUseMethod(mat, "MatMFFDGetH_C", (Mat, PetscScalar *), (mat, h));
727   PetscFunctionReturn(0);
728 }
729 
730 /*@C
731    MatMFFDSetFunction - Sets the function used in applying the matrix free `MATMFFD` matrix.
732 
733    Logically Collective on mat
734 
735    Input Parameters:
736 +  mat - the matrix free matrix `MATMFFD` created via `MatCreateSNESMF()` or `MatCreateMFFD()`
737 .  func - the function to use
738 -  funcctx - optional function context passed to function
739 
740    Calling Sequence of func:
741 $     func (void *funcctx, Vec x, Vec f)
742 
743 +  funcctx - user provided context
744 .  x - input vector
745 -  f - computed output function
746 
747    Level: advanced
748 
749    Notes:
750     If you use this you MUST call `MatAssemblyBegin()` and `MatAssemblyEnd()` on the matrix free
751     matrix inside your compute Jacobian routine
752 
753     If this is not set then it will use the function set with `SNESSetFunction()` if `MatCreateSNESMF()` was used.
754 
755 .seealso: `MATMFFD`, `MatCreateSNESMF()`, `MatMFFDGetH()`, `MatCreateMFFD()`, `MATMFFD`,
756           `MatMFFDSetHHistory()`, `MatMFFDResetHHistory()`, `SNESetFunction()`
757 @*/
758 PetscErrorCode MatMFFDSetFunction(Mat mat, PetscErrorCode (*func)(void *, Vec, Vec), void *funcctx)
759 {
760   PetscFunctionBegin;
761   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
762   PetscTryMethod(mat, "MatMFFDSetFunction_C", (Mat, PetscErrorCode(*)(void *, Vec, Vec), void *), (mat, func, funcctx));
763   PetscFunctionReturn(0);
764 }
765 
766 /*@C
767    MatMFFDSetFunctioni - Sets the function for a single component for a `MATMFFD` matrix
768 
769    Logically Collective on mat
770 
771    Input Parameters:
772 +  mat - the matrix free matrix `MATMFFD`
773 -  funci - the function to use
774 
775    Level: advanced
776 
777    Notes:
778     If you use this you MUST call `MatAssemblyBegin()` and `MatAssemblyEnd()` on the matrix free
779     matrix inside your compute Jacobian routine.
780 
781     This function is necessary to compute the diagonal of the matrix.
782     funci must not contain any MPI call as it is called inside a loop on the local portion of the vector.
783 
784 .seealso: `MATMFFD`, `MatCreateSNESMF()`, `MatMFFDGetH()`, `MatMFFDSetHHistory()`, `MatMFFDResetHHistory()`, `SNESetFunction()`, `MatGetDiagonal()`
785 @*/
786 PetscErrorCode MatMFFDSetFunctioni(Mat mat, PetscErrorCode (*funci)(void *, PetscInt, Vec, PetscScalar *))
787 {
788   PetscFunctionBegin;
789   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
790   PetscTryMethod(mat, "MatMFFDSetFunctioni_C", (Mat, PetscErrorCode(*)(void *, PetscInt, Vec, PetscScalar *)), (mat, funci));
791   PetscFunctionReturn(0);
792 }
793 
794 /*@C
795    MatMFFDSetFunctioniBase - Sets the base vector for a single component function evaluation for a `MATMFFD` matrix
796 
797    Logically Collective on mat
798 
799    Input Parameters:
800 +  mat - the `MATMFFD` matrix free matrix
801 -  func - the function to use
802 
803    Level: advanced
804 
805    Notes:
806     If you use this you MUST call `MatAssemblyBegin()` and `MatAssemblyEnd()` on the matrix free
807     matrix inside your compute Jacobian routine.
808 
809     This function is necessary to compute the diagonal of the matrix, used for example with `PCJACOBI`
810 
811 .seealso: `MATMFFD`, `MatCreateSNESMF()`, `MatMFFDGetH()`, `MatCreateMFFD()`, `MATMFFD`
812           `MatMFFDSetHHistory()`, `MatMFFDResetHHistory()`, `SNESetFunction()`, `MatGetDiagonal()`
813 @*/
814 PetscErrorCode MatMFFDSetFunctioniBase(Mat mat, PetscErrorCode (*func)(void *, Vec))
815 {
816   PetscFunctionBegin;
817   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
818   PetscTryMethod(mat, "MatMFFDSetFunctioniBase_C", (Mat, PetscErrorCode(*)(void *, Vec)), (mat, func));
819   PetscFunctionReturn(0);
820 }
821 
822 /*@
823    MatMFFDSetPeriod - Sets how often h is recomputed for a `MATMFFD` matrix, by default it is every time
824 
825    Logically Collective on mat
826 
827    Input Parameters:
828 +  mat - the `MATMFFD` matrix free matrix
829 -  period - 1 for every time, 2 for every second etc
830 
831    Options Database Keys:
832 .  -mat_mffd_period <period> - Sets how often h is recomputed
833 
834    Level: advanced
835 
836 .seealso: `MATMFFD`, `MatCreateSNESMF()`, `MatMFFDGetH()`,
837           `MatMFFDSetHHistory()`, `MatMFFDResetHHistory()`
838 @*/
839 PetscErrorCode MatMFFDSetPeriod(Mat mat, PetscInt period)
840 {
841   PetscFunctionBegin;
842   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
843   PetscValidLogicalCollectiveInt(mat, period, 2);
844   PetscTryMethod(mat, "MatMFFDSetPeriod_C", (Mat, PetscInt), (mat, period));
845   PetscFunctionReturn(0);
846 }
847 
848 /*@
849    MatMFFDSetFunctionError - Sets the error_rel for the approximation of matrix-vector products using finite differences with the `MATMFFD` matrix
850 
851    Logically Collective on mat
852 
853    Input Parameters:
854 +  mat - the `MATMFFD` matrix free matrix
855 -  error_rel - relative error (should be set to the square root of the relative error in the function evaluations)
856 
857    Options Database Keys:
858 .  -mat_mffd_err <error_rel> - Sets error_rel
859 
860    Level: advanced
861 
862    Note:
863    The default matrix-free matrix-vector product routine computes
864 .vb
865      F'(u)*a = [F(u+h*a) - F(u)]/h where
866      h = error_rel*u'a/||a||^2                        if  |u'a| > umin*||a||_{1}
867        = error_rel*umin*sign(u'a)*||a||_{1}/||a||^2   else
868 .ve
869 
870 .seealso: `MATMFFD`, `MatCreateSNESMF()`, `MatMFFDGetH()`, `MatCreateMFFD()`, `MATMFFD`
871           `MatMFFDSetHHistory()`, `MatMFFDResetHHistory()`
872 @*/
873 PetscErrorCode MatMFFDSetFunctionError(Mat mat, PetscReal error)
874 {
875   PetscFunctionBegin;
876   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
877   PetscValidLogicalCollectiveReal(mat, error, 2);
878   PetscTryMethod(mat, "MatMFFDSetFunctionError_C", (Mat, PetscReal), (mat, error));
879   PetscFunctionReturn(0);
880 }
881 
882 /*@
883    MatMFFDSetHHistory - Sets an array to collect a history of the
884    differencing values (h) computed for the matrix-free product `MATMFFD` matrix
885 
886    Logically Collective on mat
887 
888    Input Parameters:
889 +  J - the `MATMFFD` matrix-free matrix
890 .  history - space to hold the history
891 -  nhistory - number of entries in history, if more entries are generated than
892               nhistory, then the later ones are discarded
893 
894    Level: advanced
895 
896    Note:
897    Use `MatMFFDResetHHistory()` to reset the history counter and collect
898    a new batch of differencing parameters, h.
899 
900 .seealso: `MATMFFD`, `MatMFFDGetH()`, `MatCreateSNESMF()`,
901           `MatMFFDResetHHistory()`, `MatMFFDSetFunctionError()`
902 @*/
903 PetscErrorCode MatMFFDSetHHistory(Mat J, PetscScalar history[], PetscInt nhistory)
904 {
905   PetscFunctionBegin;
906   PetscValidHeaderSpecific(J, MAT_CLASSID, 1);
907   if (history) PetscValidScalarPointer(history, 2);
908   PetscValidLogicalCollectiveInt(J, nhistory, 3);
909   PetscUseMethod(J, "MatMFFDSetHHistory_C", (Mat, PetscScalar[], PetscInt), (J, history, nhistory));
910   PetscFunctionReturn(0);
911 }
912 
913 /*@
914    MatMFFDResetHHistory - Resets the counter to zero to begin
915    collecting a new set of differencing histories for the `MATMFFD` matrix
916 
917    Logically Collective on mat
918 
919    Input Parameters:
920 .  J - the matrix-free matrix context
921 
922    Level: advanced
923 
924    Note:
925    Use `MatMFFDSetHHistory()` to create the original history counter.
926 
927 .seealso: `MATMFFD`, `MatMFFDGetH()`, `MatCreateSNESMF()`,
928           `MatMFFDSetHHistory()`, `MatMFFDSetFunctionError()`
929 @*/
930 PetscErrorCode MatMFFDResetHHistory(Mat J)
931 {
932   PetscFunctionBegin;
933   PetscValidHeaderSpecific(J, MAT_CLASSID, 1);
934   PetscTryMethod(J, "MatMFFDResetHHistory_C", (Mat), (J));
935   PetscFunctionReturn(0);
936 }
937 
938 /*@
939     MatMFFDSetBase - Sets the vector U at which matrix vector products of the
940         Jacobian are computed for the `MATMFFD` matrix
941 
942     Logically Collective on mat
943 
944     Input Parameters:
945 +   J - the `MATMFFD` matrix
946 .   U - the vector
947 -   F - (optional) vector that contains F(u) if it has been already computed
948 
949     Notes:
950     This is rarely used directly
951 
952     If F is provided then it is not recomputed. Otherwise the function is evaluated at the base
953     point during the first `MatMult()` after each call to `MatMFFDSetBase()`.
954 
955     Level: advanced
956 
957 .seealso: `MATMFFD`, `MatMult()`, `MatMFFDSetBase()`
958 @*/
959 PetscErrorCode MatMFFDSetBase(Mat J, Vec U, Vec F)
960 {
961   PetscFunctionBegin;
962   PetscValidHeaderSpecific(J, MAT_CLASSID, 1);
963   PetscValidHeaderSpecific(U, VEC_CLASSID, 2);
964   if (F) PetscValidHeaderSpecific(F, VEC_CLASSID, 3);
965   PetscTryMethod(J, "MatMFFDSetBase_C", (Mat, Vec, Vec), (J, U, F));
966   PetscFunctionReturn(0);
967 }
968 
969 /*@C
970     MatMFFDSetCheckh - Sets a function that checks the computed h and adjusts
971         it to satisfy some criteria for the `MATMFFD` matrix
972 
973     Logically Collective on mat
974 
975     Input Parameters:
976 +   J - the `MATMFFD` matrix
977 .   fun - the function that checks h
978 -   ctx - any context needed by the function
979 
980     Options Database Keys:
981 .   -mat_mffd_check_positivity <bool> - Insure that U + h*a is non-negative
982 
983     Level: advanced
984 
985     Notes:
986     For example, `MatMFFDCheckPositivity()` insures that all entries
987        of U + h*a are non-negative
988 
989      The function you provide is called after the default h has been computed and allows you to
990      modify it.
991 
992 .seealso: `MATMFFD`, `MatMFFDCheckPositivity()`
993 @*/
994 PetscErrorCode MatMFFDSetCheckh(Mat J, PetscErrorCode (*fun)(void *, Vec, Vec, PetscScalar *), void *ctx)
995 {
996   PetscFunctionBegin;
997   PetscValidHeaderSpecific(J, MAT_CLASSID, 1);
998   PetscTryMethod(J, "MatMFFDSetCheckh_C", (Mat, PetscErrorCode(*)(void *, Vec, Vec, PetscScalar *), void *), (J, fun, ctx));
999   PetscFunctionReturn(0);
1000 }
1001 
1002 /*@
1003     MatMFFDCheckPositivity - Checks that all entries in U + h*a are positive or
1004         zero, decreases h until this is satisfied for a `MATMFFD` matrix
1005 
1006     Logically Collective on U
1007 
1008     Input Parameters:
1009 +   U - base vector that is added to
1010 .   a - vector that is added
1011 .   h - scaling factor on a
1012 -   dummy - context variable (unused)
1013 
1014     Options Database Keys:
1015 .   -mat_mffd_check_positivity <bool> - Insure that U + h*a is nonnegative
1016 
1017     Level: advanced
1018 
1019     Note:
1020     This is rarely used directly, rather it is passed as an argument to
1021            `MatMFFDSetCheckh()`
1022 
1023 .seealso: `MATMFFD`, `MatMFFDSetCheckh()`
1024 @*/
1025 PetscErrorCode MatMFFDCheckPositivity(void *dummy, Vec U, Vec a, PetscScalar *h)
1026 {
1027   PetscReal    val, minval;
1028   PetscScalar *u_vec, *a_vec;
1029   PetscInt     i, n;
1030   MPI_Comm     comm;
1031 
1032   PetscFunctionBegin;
1033   PetscValidHeaderSpecific(U, VEC_CLASSID, 2);
1034   PetscValidHeaderSpecific(a, VEC_CLASSID, 3);
1035   PetscValidScalarPointer(h, 4);
1036   PetscCall(PetscObjectGetComm((PetscObject)U, &comm));
1037   PetscCall(VecGetArray(U, &u_vec));
1038   PetscCall(VecGetArray(a, &a_vec));
1039   PetscCall(VecGetLocalSize(U, &n));
1040   minval = PetscAbsScalar(*h) * PetscRealConstant(1.01);
1041   for (i = 0; i < n; i++) {
1042     if (PetscRealPart(u_vec[i] + *h * a_vec[i]) <= 0.0) {
1043       val = PetscAbsScalar(u_vec[i] / a_vec[i]);
1044       if (val < minval) minval = val;
1045     }
1046   }
1047   PetscCall(VecRestoreArray(U, &u_vec));
1048   PetscCall(VecRestoreArray(a, &a_vec));
1049   PetscCall(MPIU_Allreduce(&minval, &val, 1, MPIU_REAL, MPIU_MIN, comm));
1050   if (val <= PetscAbsScalar(*h)) {
1051     PetscCall(PetscInfo(U, "Scaling back h from %g to %g\n", (double)PetscRealPart(*h), (double)(.99 * val)));
1052     if (PetscRealPart(*h) > 0.0) *h = 0.99 * val;
1053     else *h = -0.99 * val;
1054   }
1055   PetscFunctionReturn(0);
1056 }
1057