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