xref: /petsc/src/snes/utils/convest.c (revision a69119a591a03a9d906b29c0a4e9802e4d7c9795)
1 #include <petscconvest.h> /*I "petscconvest.h" I*/
2 #include <petscdmplex.h>
3 #include <petscds.h>
4 
5 #include <petsc/private/petscconvestimpl.h>
6 
7 static PetscErrorCode zero_private(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar *u, void *ctx) {
8   PetscInt c;
9   for (c = 0; c < Nc; ++c) u[c] = 0.0;
10   return 0;
11 }
12 
13 /*@
14   PetscConvEstDestroy - Destroys a PetscConvEst object
15 
16   Collective on PetscConvEst
17 
18   Input Parameter:
19 . ce - The PetscConvEst object
20 
21   Level: beginner
22 
23 .seealso: `PetscConvEstCreate()`, `PetscConvEstGetConvRate()`
24 @*/
25 PetscErrorCode PetscConvEstDestroy(PetscConvEst *ce) {
26   PetscFunctionBegin;
27   if (!*ce) PetscFunctionReturn(0);
28   PetscValidHeaderSpecific((*ce), PETSC_OBJECT_CLASSID, 1);
29   if (--((PetscObject)(*ce))->refct > 0) {
30     *ce = NULL;
31     PetscFunctionReturn(0);
32   }
33   PetscCall(PetscFree3((*ce)->initGuess, (*ce)->exactSol, (*ce)->ctxs));
34   PetscCall(PetscFree2((*ce)->dofs, (*ce)->errors));
35   PetscCall(PetscHeaderDestroy(ce));
36   PetscFunctionReturn(0);
37 }
38 
39 /*@
40   PetscConvEstSetFromOptions - Sets a PetscConvEst object from options
41 
42   Collective on PetscConvEst
43 
44   Input Parameters:
45 . ce - The PetscConvEst object
46 
47   Level: beginner
48 
49 .seealso: `PetscConvEstCreate()`, `PetscConvEstGetConvRate()`
50 @*/
51 PetscErrorCode PetscConvEstSetFromOptions(PetscConvEst ce) {
52   PetscFunctionBegin;
53   PetscOptionsBegin(PetscObjectComm((PetscObject)ce), "", "Convergence Estimator Options", "PetscConvEst");
54   PetscCall(PetscOptionsInt("-convest_num_refine", "The number of refinements for the convergence check", "PetscConvEst", ce->Nr, &ce->Nr, NULL));
55   PetscCall(PetscOptionsReal("-convest_refine_factor", "The increase in resolution in each dimension", "PetscConvEst", ce->r, &ce->r, NULL));
56   PetscCall(PetscOptionsBool("-convest_monitor", "Monitor the error for each convergence check", "PetscConvEst", ce->monitor, &ce->monitor, NULL));
57   PetscCall(PetscOptionsBool("-convest_no_refine", "Debugging flag to run on the same mesh each time", "PetscConvEst", ce->noRefine, &ce->noRefine, NULL));
58   PetscOptionsEnd();
59   PetscFunctionReturn(0);
60 }
61 
62 /*@
63   PetscConvEstView - Views a PetscConvEst object
64 
65   Collective on PetscConvEst
66 
67   Input Parameters:
68 + ce     - The PetscConvEst object
69 - viewer - The PetscViewer object
70 
71   Level: beginner
72 
73 .seealso: `PetscConvEstCreate()`, `PetscConvEstGetConvRate()`
74 @*/
75 PetscErrorCode PetscConvEstView(PetscConvEst ce, PetscViewer viewer) {
76   PetscFunctionBegin;
77   PetscCall(PetscObjectPrintClassNamePrefixType((PetscObject)ce, viewer));
78   PetscCall(PetscViewerASCIIPrintf(viewer, "ConvEst with %" PetscInt_FMT " levels\n", ce->Nr + 1));
79   PetscFunctionReturn(0);
80 }
81 
82 /*@
83   PetscConvEstGetSolver - Gets the solver used to produce discrete solutions
84 
85   Not collective
86 
87   Input Parameter:
88 . ce     - The PetscConvEst object
89 
90   Output Parameter:
91 . solver - The solver
92 
93   Level: intermediate
94 
95 .seealso: `PetscConvEstSetSolver()`, `PetscConvEstCreate()`, `PetscConvEstGetConvRate()`
96 @*/
97 PetscErrorCode PetscConvEstGetSolver(PetscConvEst ce, PetscObject *solver) {
98   PetscFunctionBegin;
99   PetscValidHeaderSpecific(ce, PETSC_OBJECT_CLASSID, 1);
100   PetscValidPointer(solver, 2);
101   *solver = ce->solver;
102   PetscFunctionReturn(0);
103 }
104 
105 /*@
106   PetscConvEstSetSolver - Sets the solver used to produce discrete solutions
107 
108   Not collective
109 
110   Input Parameters:
111 + ce     - The PetscConvEst object
112 - solver - The solver
113 
114   Level: intermediate
115 
116   Note: The solver MUST have an attached DM/DS, so that we know the exact solution
117 
118 .seealso: `PetscConvEstGetSNES()`, `PetscConvEstCreate()`, `PetscConvEstGetConvRate()`
119 @*/
120 PetscErrorCode PetscConvEstSetSolver(PetscConvEst ce, PetscObject solver) {
121   PetscFunctionBegin;
122   PetscValidHeaderSpecific(ce, PETSC_OBJECT_CLASSID, 1);
123   PetscValidHeader(solver, 2);
124   ce->solver = solver;
125   PetscUseTypeMethod(ce, setsolver, solver);
126   PetscFunctionReturn(0);
127 }
128 
129 /*@
130   PetscConvEstSetUp - After the solver is specified, we create structures for estimating convergence
131 
132   Collective on PetscConvEst
133 
134   Input Parameters:
135 . ce - The PetscConvEst object
136 
137   Level: beginner
138 
139 .seealso: `PetscConvEstCreate()`, `PetscConvEstGetConvRate()`
140 @*/
141 PetscErrorCode PetscConvEstSetUp(PetscConvEst ce) {
142   PetscInt Nf, f, Nds, s;
143 
144   PetscFunctionBegin;
145   PetscCall(DMGetNumFields(ce->idm, &Nf));
146   ce->Nf = PetscMax(Nf, 1);
147   PetscCall(PetscMalloc2((ce->Nr + 1) * ce->Nf, &ce->dofs, (ce->Nr + 1) * ce->Nf, &ce->errors));
148   PetscCall(PetscCalloc3(ce->Nf, &ce->initGuess, ce->Nf, &ce->exactSol, ce->Nf, &ce->ctxs));
149   for (f = 0; f < Nf; ++f) ce->initGuess[f] = zero_private;
150   PetscCall(DMGetNumDS(ce->idm, &Nds));
151   for (s = 0; s < Nds; ++s) {
152     PetscDS         ds;
153     DMLabel         label;
154     IS              fieldIS;
155     const PetscInt *fields;
156     PetscInt        dsNf;
157 
158     PetscCall(DMGetRegionNumDS(ce->idm, s, &label, &fieldIS, &ds));
159     PetscCall(PetscDSGetNumFields(ds, &dsNf));
160     if (fieldIS) PetscCall(ISGetIndices(fieldIS, &fields));
161     for (f = 0; f < dsNf; ++f) {
162       const PetscInt field = fields[f];
163       PetscCall(PetscDSGetExactSolution(ds, field, &ce->exactSol[field], &ce->ctxs[field]));
164     }
165     if (fieldIS) PetscCall(ISRestoreIndices(fieldIS, &fields));
166   }
167   for (f = 0; f < Nf; ++f) PetscCheck(ce->exactSol[f], PetscObjectComm((PetscObject)ce), PETSC_ERR_ARG_WRONG, "DS must contain exact solution functions in order to estimate convergence, missing for field %" PetscInt_FMT, f);
168   PetscFunctionReturn(0);
169 }
170 
171 PetscErrorCode PetscConvEstComputeInitialGuess(PetscConvEst ce, PetscInt r, DM dm, Vec u) {
172   PetscFunctionBegin;
173   PetscValidHeaderSpecific(ce, PETSC_OBJECT_CLASSID, 1);
174   if (dm) PetscValidHeaderSpecific(dm, DM_CLASSID, 3);
175   PetscValidHeaderSpecific(u, VEC_CLASSID, 4);
176   PetscUseTypeMethod(ce, initguess, r, dm, u);
177   PetscFunctionReturn(0);
178 }
179 
180 PetscErrorCode PetscConvEstComputeError(PetscConvEst ce, PetscInt r, DM dm, Vec u, PetscReal errors[]) {
181   PetscFunctionBegin;
182   PetscValidHeaderSpecific(ce, PETSC_OBJECT_CLASSID, 1);
183   if (dm) PetscValidHeaderSpecific(dm, DM_CLASSID, 3);
184   PetscValidHeaderSpecific(u, VEC_CLASSID, 4);
185   PetscValidRealPointer(errors, 5);
186   PetscUseTypeMethod(ce, computeerror, r, dm, u, errors);
187   PetscFunctionReturn(0);
188 }
189 
190 /*@
191   PetscConvEstMonitorDefault - Monitors the convergence estimation loop
192 
193   Collective on PetscConvEst
194 
195   Input Parameters:
196 + ce - The PetscConvEst object
197 - r  - The refinement level
198 
199   Options database keys:
200 . -convest_monitor - Activate the monitor
201 
202   Level: intermediate
203 
204 .seealso: `PetscConvEstCreate()`, `PetscConvEstGetConvRate()`, `SNESSolve()`, `TSSolve()`
205 @*/
206 PetscErrorCode PetscConvEstMonitorDefault(PetscConvEst ce, PetscInt r) {
207   MPI_Comm comm;
208   PetscInt f;
209 
210   PetscFunctionBegin;
211   if (ce->monitor) {
212     PetscInt  *dofs   = &ce->dofs[r * ce->Nf];
213     PetscReal *errors = &ce->errors[r * ce->Nf];
214 
215     PetscCall(PetscObjectGetComm((PetscObject)ce, &comm));
216     PetscCall(PetscPrintf(comm, "N: "));
217     if (ce->Nf > 1) PetscCall(PetscPrintf(comm, "["));
218     for (f = 0; f < ce->Nf; ++f) {
219       if (f > 0) PetscCall(PetscPrintf(comm, ", "));
220       PetscCall(PetscPrintf(comm, "%7" PetscInt_FMT, dofs[f]));
221     }
222     if (ce->Nf > 1) PetscCall(PetscPrintf(comm, "]"));
223     PetscCall(PetscPrintf(comm, "  "));
224     PetscCall(PetscPrintf(comm, "L_2 Error: "));
225     if (ce->Nf > 1) PetscCall(PetscPrintf(comm, "["));
226     for (f = 0; f < ce->Nf; ++f) {
227       if (f > 0) PetscCall(PetscPrintf(comm, ", "));
228       if (errors[f] < 1.0e-11) PetscCall(PetscPrintf(comm, "< 1e-11"));
229       else PetscCall(PetscPrintf(comm, "%g", (double)errors[f]));
230     }
231     if (ce->Nf > 1) PetscCall(PetscPrintf(comm, "]"));
232     PetscCall(PetscPrintf(comm, "\n"));
233   }
234   PetscFunctionReturn(0);
235 }
236 
237 static PetscErrorCode PetscConvEstSetSNES_Private(PetscConvEst ce, PetscObject solver) {
238   PetscClassId id;
239 
240   PetscFunctionBegin;
241   PetscCall(PetscObjectGetClassId(ce->solver, &id));
242   PetscCheck(id == SNES_CLASSID, PetscObjectComm((PetscObject)ce), PETSC_ERR_ARG_WRONG, "Solver was not a SNES");
243   PetscCall(SNESGetDM((SNES)ce->solver, &ce->idm));
244   PetscFunctionReturn(0);
245 }
246 
247 static PetscErrorCode PetscConvEstInitGuessSNES_Private(PetscConvEst ce, PetscInt r, DM dm, Vec u) {
248   PetscFunctionBegin;
249   PetscCall(DMProjectFunction(dm, 0.0, ce->initGuess, ce->ctxs, INSERT_VALUES, u));
250   PetscFunctionReturn(0);
251 }
252 
253 static PetscErrorCode PetscConvEstComputeErrorSNES_Private(PetscConvEst ce, PetscInt r, DM dm, Vec u, PetscReal errors[]) {
254   PetscFunctionBegin;
255   PetscCall(DMComputeL2FieldDiff(dm, 0.0, ce->exactSol, ce->ctxs, u, errors));
256   PetscFunctionReturn(0);
257 }
258 
259 static PetscErrorCode PetscConvEstSetJacobianNullspace_Private(PetscConvEst ce, SNES snes) {
260   DM       dm;
261   PetscInt f;
262 
263   PetscFunctionBegin;
264   PetscCall(SNESGetDM(snes, &dm));
265   for (f = 0; f < ce->Nf; ++f) {
266     PetscErrorCode (*nspconstr)(DM, PetscInt, PetscInt, MatNullSpace *);
267 
268     PetscCall(DMGetNullSpaceConstructor(dm, f, &nspconstr));
269     if (nspconstr) {
270       MatNullSpace nullsp;
271       Mat          J;
272 
273       PetscCall((*nspconstr)(dm, f, f, &nullsp));
274       PetscCall(SNESSetUp(snes));
275       PetscCall(SNESGetJacobian(snes, &J, NULL, NULL, NULL));
276       PetscCall(MatSetNullSpace(J, nullsp));
277       PetscCall(MatNullSpaceDestroy(&nullsp));
278       break;
279     }
280   }
281   PetscFunctionReturn(0);
282 }
283 
284 static PetscErrorCode PetscConvEstGetConvRateSNES_Private(PetscConvEst ce, PetscReal alpha[]) {
285   SNES        snes = (SNES)ce->solver;
286   DM         *dm;
287   PetscObject disc;
288   PetscReal  *x, *y, slope, intercept;
289   PetscInt    Nr = ce->Nr, r, f, dim, oldlevel, oldnlev;
290   void       *ctx;
291 
292   PetscFunctionBegin;
293   PetscCheck(ce->r == 2.0, PetscObjectComm((PetscObject)ce), PETSC_ERR_SUP, "Only refinement factor 2 is currently supported (not %g)", (double)ce->r);
294   PetscCall(DMGetDimension(ce->idm, &dim));
295   PetscCall(DMGetApplicationContext(ce->idm, &ctx));
296   PetscCall(DMPlexSetRefinementUniform(ce->idm, PETSC_TRUE));
297   PetscCall(DMGetRefineLevel(ce->idm, &oldlevel));
298   PetscCall(PetscMalloc1((Nr + 1), &dm));
299   /* Loop over meshes */
300   dm[0] = ce->idm;
301   for (r = 0; r <= Nr; ++r) {
302     Vec u;
303 #if defined(PETSC_USE_LOG)
304     PetscLogStage stage;
305 #endif
306     char        stageName[PETSC_MAX_PATH_LEN];
307     const char *dmname, *uname;
308 
309     PetscCall(PetscSNPrintf(stageName, PETSC_MAX_PATH_LEN - 1, "ConvEst Refinement Level %" PetscInt_FMT, r));
310 #if defined(PETSC_USE_LOG)
311     PetscCall(PetscLogStageGetId(stageName, &stage));
312     if (stage < 0) PetscCall(PetscLogStageRegister(stageName, &stage));
313 #endif
314     PetscCall(PetscLogStagePush(stage));
315     if (r > 0) {
316       if (!ce->noRefine) {
317         PetscCall(DMRefine(dm[r - 1], MPI_COMM_NULL, &dm[r]));
318         PetscCall(DMSetCoarseDM(dm[r], dm[r - 1]));
319       } else {
320         DM cdm, rcdm;
321 
322         PetscCall(DMClone(dm[r - 1], &dm[r]));
323         PetscCall(DMCopyDisc(dm[r - 1], dm[r]));
324         PetscCall(DMGetCoordinateDM(dm[r - 1], &cdm));
325         PetscCall(DMGetCoordinateDM(dm[r], &rcdm));
326         PetscCall(DMCopyDisc(cdm, rcdm));
327       }
328       PetscCall(DMCopyTransform(ce->idm, dm[r]));
329       PetscCall(PetscObjectGetName((PetscObject)dm[r - 1], &dmname));
330       PetscCall(PetscObjectSetName((PetscObject)dm[r], dmname));
331       for (f = 0; f < ce->Nf; ++f) {
332         PetscErrorCode (*nspconstr)(DM, PetscInt, PetscInt, MatNullSpace *);
333 
334         PetscCall(DMGetNullSpaceConstructor(dm[r - 1], f, &nspconstr));
335         PetscCall(DMSetNullSpaceConstructor(dm[r], f, nspconstr));
336       }
337     }
338     PetscCall(DMViewFromOptions(dm[r], NULL, "-conv_dm_view"));
339     /* Create solution */
340     PetscCall(DMCreateGlobalVector(dm[r], &u));
341     PetscCall(DMGetField(dm[r], 0, NULL, &disc));
342     PetscCall(PetscObjectGetName(disc, &uname));
343     PetscCall(PetscObjectSetName((PetscObject)u, uname));
344     /* Setup solver */
345     PetscCall(SNESReset(snes));
346     PetscCall(SNESSetDM(snes, dm[r]));
347     PetscCall(DMPlexSetSNESLocalFEM(dm[r], ctx, ctx, ctx));
348     PetscCall(SNESSetFromOptions(snes));
349     /* Set nullspace for Jacobian */
350     PetscCall(PetscConvEstSetJacobianNullspace_Private(ce, snes));
351     /* Create initial guess */
352     PetscCall(PetscConvEstComputeInitialGuess(ce, r, dm[r], u));
353     PetscCall(SNESSolve(snes, NULL, u));
354     PetscCall(PetscLogEventBegin(ce->event, ce, 0, 0, 0));
355     PetscCall(PetscConvEstComputeError(ce, r, dm[r], u, &ce->errors[r * ce->Nf]));
356     PetscCall(PetscLogEventEnd(ce->event, ce, 0, 0, 0));
357     for (f = 0; f < ce->Nf; ++f) {
358       PetscSection s, fs;
359       PetscInt     lsize;
360 
361       /* Could use DMGetOutputDM() to add in Dirichlet dofs */
362       PetscCall(DMGetLocalSection(dm[r], &s));
363       PetscCall(PetscSectionGetField(s, f, &fs));
364       PetscCall(PetscSectionGetConstrainedStorageSize(fs, &lsize));
365       PetscCallMPI(MPI_Allreduce(&lsize, &ce->dofs[r * ce->Nf + f], 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)snes)));
366       PetscCall(PetscLogEventSetDof(ce->event, f, ce->dofs[r * ce->Nf + f]));
367       PetscCall(PetscLogEventSetError(ce->event, f, ce->errors[r * ce->Nf + f]));
368     }
369     /* Monitor */
370     PetscCall(PetscConvEstMonitorDefault(ce, r));
371     if (!r) {
372       /* PCReset() does not wipe out the level structure */
373       KSP ksp;
374       PC  pc;
375 
376       PetscCall(SNESGetKSP(snes, &ksp));
377       PetscCall(KSPGetPC(ksp, &pc));
378       PetscCall(PCMGGetLevels(pc, &oldnlev));
379     }
380     /* Cleanup */
381     PetscCall(VecDestroy(&u));
382     PetscCall(PetscLogStagePop());
383   }
384   for (r = 1; r <= Nr; ++r) PetscCall(DMDestroy(&dm[r]));
385   /* Fit convergence rate */
386   PetscCall(PetscMalloc2(Nr + 1, &x, Nr + 1, &y));
387   for (f = 0; f < ce->Nf; ++f) {
388     for (r = 0; r <= Nr; ++r) {
389       x[r] = PetscLog10Real(ce->dofs[r * ce->Nf + f]);
390       y[r] = PetscLog10Real(ce->errors[r * ce->Nf + f]);
391     }
392     PetscCall(PetscLinearRegression(Nr + 1, x, y, &slope, &intercept));
393     /* Since h^{-dim} = N, lg err = s lg N + b = -s dim lg h + b */
394     alpha[f] = -slope * dim;
395   }
396   PetscCall(PetscFree2(x, y));
397   PetscCall(PetscFree(dm));
398   /* Restore solver */
399   PetscCall(SNESReset(snes));
400   {
401     /* PCReset() does not wipe out the level structure */
402     KSP ksp;
403     PC  pc;
404 
405     PetscCall(SNESGetKSP(snes, &ksp));
406     PetscCall(KSPGetPC(ksp, &pc));
407     PetscCall(PCMGSetLevels(pc, oldnlev, NULL));
408     PetscCall(DMSetRefineLevel(ce->idm, oldlevel)); /* The damn DMCoarsen() calls in PCMG can reset this */
409   }
410   PetscCall(SNESSetDM(snes, ce->idm));
411   PetscCall(DMPlexSetSNESLocalFEM(ce->idm, ctx, ctx, ctx));
412   PetscCall(SNESSetFromOptions(snes));
413   PetscCall(PetscConvEstSetJacobianNullspace_Private(ce, snes));
414   PetscFunctionReturn(0);
415 }
416 
417 /*@
418   PetscConvEstGetConvRate - Returns an estimate of the convergence rate for the discretization
419 
420   Not collective
421 
422   Input Parameter:
423 . ce   - The PetscConvEst object
424 
425   Output Parameter:
426 . alpha - The convergence rate for each field
427 
428   Note: The convergence rate alpha is defined by
429 $ || u_\Delta - u_exact || < C \Delta^alpha
430 where u_\Delta is the discrete solution, and Delta is a measure of the discretization size. We usually use h for the
431 spatial resolution and \Delta t for the temporal resolution.
432 
433 We solve a series of problems using increasing resolution (refined meshes or decreased timesteps), calculate an error
434 based upon the exact solution in the DS, and then fit the result to our model above using linear regression.
435 
436   Options database keys:
437 + -snes_convergence_estimate - Execute convergence estimation inside SNESSolve() and print out the rate
438 - -ts_convergence_estimate - Execute convergence estimation inside TSSolve() and print out the rate
439 
440   Level: intermediate
441 
442 .seealso: `PetscConvEstSetSolver()`, `PetscConvEstCreate()`, `PetscConvEstGetConvRate()`, `SNESSolve()`, `TSSolve()`
443 @*/
444 PetscErrorCode PetscConvEstGetConvRate(PetscConvEst ce, PetscReal alpha[]) {
445   PetscInt f;
446 
447   PetscFunctionBegin;
448   if (ce->event < 0) PetscCall(PetscLogEventRegister("ConvEst Error", PETSC_OBJECT_CLASSID, &ce->event));
449   for (f = 0; f < ce->Nf; ++f) alpha[f] = 0.0;
450   PetscUseTypeMethod(ce, getconvrate, alpha);
451   PetscFunctionReturn(0);
452 }
453 
454 /*@
455   PetscConvEstRateView - Displays the convergence rate to a viewer
456 
457    Collective on SNES
458 
459    Parameter:
460 +  snes - iterative context obtained from SNESCreate()
461 .  alpha - the convergence rate for each field
462 -  viewer - the viewer to display the reason
463 
464    Options Database Keys:
465 .  -snes_convergence_estimate - print the convergence rate
466 
467    Level: developer
468 
469 .seealso: `PetscConvEstGetRate()`
470 @*/
471 PetscErrorCode PetscConvEstRateView(PetscConvEst ce, const PetscReal alpha[], PetscViewer viewer) {
472   PetscBool isAscii;
473 
474   PetscFunctionBegin;
475   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &isAscii));
476   if (isAscii) {
477     PetscInt Nf = ce->Nf, f;
478 
479     PetscCall(PetscViewerASCIIAddTab(viewer, ((PetscObject)ce)->tablevel));
480     PetscCall(PetscViewerASCIIPrintf(viewer, "L_2 convergence rate: "));
481     if (Nf > 1) PetscCall(PetscViewerASCIIPrintf(viewer, "["));
482     for (f = 0; f < Nf; ++f) {
483       if (f > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
484       PetscCall(PetscViewerASCIIPrintf(viewer, "%#.2g", (double)alpha[f]));
485     }
486     if (Nf > 1) PetscCall(PetscViewerASCIIPrintf(viewer, "]"));
487     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
488     PetscCall(PetscViewerASCIISubtractTab(viewer, ((PetscObject)ce)->tablevel));
489   }
490   PetscFunctionReturn(0);
491 }
492 
493 /*@
494   PetscConvEstCreate - Create a PetscConvEst object
495 
496   Collective
497 
498   Input Parameter:
499 . comm - The communicator for the PetscConvEst object
500 
501   Output Parameter:
502 . ce   - The PetscConvEst object
503 
504   Level: beginner
505 
506 .seealso: `PetscConvEstDestroy()`, `PetscConvEstGetConvRate()`
507 @*/
508 PetscErrorCode PetscConvEstCreate(MPI_Comm comm, PetscConvEst *ce) {
509   PetscFunctionBegin;
510   PetscValidPointer(ce, 2);
511   PetscCall(PetscSysInitializePackage());
512   PetscCall(PetscHeaderCreate(*ce, PETSC_OBJECT_CLASSID, "PetscConvEst", "ConvergenceEstimator", "SNES", comm, PetscConvEstDestroy, PetscConvEstView));
513   (*ce)->monitor           = PETSC_FALSE;
514   (*ce)->r                 = 2.0;
515   (*ce)->Nr                = 4;
516   (*ce)->event             = -1;
517   (*ce)->ops->setsolver    = PetscConvEstSetSNES_Private;
518   (*ce)->ops->initguess    = PetscConvEstInitGuessSNES_Private;
519   (*ce)->ops->computeerror = PetscConvEstComputeErrorSNES_Private;
520   (*ce)->ops->getconvrate  = PetscConvEstGetConvRateSNES_Private;
521   PetscFunctionReturn(0);
522 }
523