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