Actual source code: convest.c

  1: #include "petscsys.h"
  2: #include <petscconvest.h>
  3: #include <petscdmplex.h>
  4: #include <petscds.h>

  6: #include <petsc/private/petscconvestimpl.h>

  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: }

 15: /*@
 16:   PetscConvEstDestroy - Destroys a PETSc convergence estimator `PetscConvEst` object

 18:   Collective

 20:   Input Parameter:
 21: . ce - The `PetscConvEst` object

 23:   Level: beginner

 25: .seealso: `PetscConvEst`, `PetscConvEstCreate()`, `PetscConvEstGetConvRate()`
 26: @*/
 27: PetscErrorCode PetscConvEstDestroy(PetscConvEst *ce)
 28: {
 29:   PetscFunctionBegin;
 30:   if (!*ce) PetscFunctionReturn(PETSC_SUCCESS);
 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: }

 42: /*@
 43:   PetscConvEstSetFromOptions - Sets a convergence estimator `PetscConvEst` object based on values in the options database

 45:   Collective

 47:   Input Parameter:
 48: . ce - The `PetscConvEst` object

 50:   Level: beginner

 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: }

 66: /*@
 67:   PetscConvEstView - Views a `PetscConvEst` object

 69:   Collective

 71:   Input Parameters:
 72: + ce     - The `PetscConvEst` object
 73: - viewer - The `PetscViewer`

 75:   Level: beginner

 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: }

 87: /*@
 88:   PetscConvEstGetSolver - Gets the solver used to produce discrete solutions

 90:   Not Collective

 92:   Input Parameter:
 93: . ce - The `PetscConvEst` object

 95:   Output Parameter:
 96: . solver - The solver

 98:   Level: intermediate

100: .seealso: `PetscConvEst`, `PetscConvEstSetSolver()`, `PetscConvEstCreate()`, `PetscConvEstGetConvRate()`
101: @*/
102: PetscErrorCode PetscConvEstGetSolver(PetscConvEst ce, PetscObject *solver)
103: {
104:   PetscFunctionBegin;
106:   PetscAssertPointer(solver, 2);
107:   *solver = ce->solver;
108:   PetscFunctionReturn(PETSC_SUCCESS);
109: }

111: /*@
112:   PetscConvEstSetSolver - Sets the solver used to produce discrete solutions

114:   Not Collective

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

120:   Level: intermediate

122: .seealso: `PetscConvEst`, `PetscConvEstGetSNES()`, `PetscConvEstCreate()`, `PetscConvEstGetConvRate()`
123: @*/
124: PetscErrorCode PetscConvEstSetSolver(PetscConvEst ce, PetscObject solver)
125: {
126:   PetscFunctionBegin;
129:   ce->solver = solver;
130:   PetscUseTypeMethod(ce, setsolver, solver);
131:   PetscFunctionReturn(PETSC_SUCCESS);
132: }

134: /*@
135:   PetscConvEstSetUp - After the solver is specified, create data structures needed for estimating convergence

137:   Collective

139:   Input Parameter:
140: . ce - The `PetscConvEst` object

142:   Level: beginner

144: .seealso: `PetscConvEst`, `PetscConvEstCreate()`, `PetscConvEstGetConvRate()`
145: @*/
146: PetscErrorCode PetscConvEstSetUp(PetscConvEst ce)
147: {
148:   PetscInt Nf, f, Nds, s;

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;

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: }

177: PetscErrorCode PetscConvEstComputeInitialGuess(PetscConvEst ce, PetscInt r, DM dm, Vec u)
178: {
179:   PetscFunctionBegin;
183:   PetscUseTypeMethod(ce, initguess, r, dm, u);
184:   PetscFunctionReturn(PETSC_SUCCESS);
185: }

187: PetscErrorCode PetscConvEstComputeError(PetscConvEst ce, PetscInt r, DM dm, Vec u, PetscReal errors[])
188: {
189:   PetscFunctionBegin;
193:   PetscAssertPointer(errors, 5);
194:   PetscUseTypeMethod(ce, computeerror, r, dm, u, errors);
195:   PetscFunctionReturn(PETSC_SUCCESS);
196: }

198: /*@
199:   PetscConvEstMonitorDefault - Monitors the convergence estimation loop

201:   Collective

203:   Input Parameters:
204: + ce - The `PetscConvEst` object
205: - r  - The refinement level

207:   Options Database Key:
208: . -convest_monitor - Activate the monitor

210:   Level: intermediate

212: .seealso: `PetscConvEst`, `PetscConvEstCreate()`, `PetscConvEstGetConvRate()`, `SNESSolve()`, `TSSolve()`
213: @*/
214: PetscErrorCode PetscConvEstMonitorDefault(PetscConvEst ce, PetscInt r)
215: {
216:   MPI_Comm comm;
217:   PetscInt f;

219:   PetscFunctionBegin;
220:   if (ce->monitor) {
221:     PetscInt  *dofs   = &ce->dofs[r * ce->Nf];
222:     PetscReal *errors = &ce->errors[r * ce->Nf];

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: }

246: static PetscErrorCode PetscConvEstSetSNES_Private(PetscConvEst ce, PetscObject solver)
247: {
248:   PetscClassId id;

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: }

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: }

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;

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;

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: }

307: static PetscErrorCode PetscConvEstSetJacobianNullSpace_Private(PetscConvEst ce, SNES snes)
308: {
309:   DM       dm;
310:   PetscInt f;

312:   PetscFunctionBegin;
313:   PetscCall(SNESGetDM(snes, &dm));
314:   for (f = 0; f < ce->Nf; ++f) {
315:     PetscErrorCode (*nspconstr)(DM, PetscInt, PetscInt, MatNullSpace *);

317:     PetscCall(DMGetNullSpaceConstructor(dm, f, &nspconstr));
318:     if (nspconstr) {
319:       MatNullSpace nullsp;
320:       Mat          J;

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: }

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;

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;

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;

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 *);

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;

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;

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;

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: }

465: /*@
466:   PetscConvEstGetConvRate - Returns an estimate of the convergence rate for the discretization

468:   Not Collective

470:   Input Parameter:
471: . ce - The `PetscConvEst` object

473:   Output Parameter:
474: . alpha - The convergence rate for each field

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

480:   Level: intermediate

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.

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.

491: .seealso: `PetscConvEstSetSolver()`, `PetscConvEstCreate()`, `SNESSolve()`, `TSSolve()`
492: @*/
493: PetscErrorCode PetscConvEstGetConvRate(PetscConvEst ce, PetscReal alpha[])
494: {
495:   PetscInt f;

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: }

504: /*@
505:   PetscConvEstRateView - Displays the convergence rate obtained from `PetscConvEstGetConvRate()` using a `PetscViewer`

507:   Collective

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

514:   Options Database Key:
515: . -snes_convergence_estimate - print the convergence rate

517:   Level: developer

519: .seealso: `PetscConvEst`, `PetscConvEstGetConvRate()`
520: @*/
521: PetscErrorCode PetscConvEstRateView(PetscConvEst ce, const PetscReal alpha[], PetscViewer viewer)
522: {
523:   PetscBool isAscii;

525:   PetscFunctionBegin;
526:   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &isAscii));
527:   if (isAscii) {
528:     PetscInt Nf = ce->Nf, f;

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: }

544: /*@
545:   PetscConvEstCreate - Create a `PetscConvEst` object. This is used to study the convergence rate of approximations on grids to a continuum solution

547:   Collective

549:   Input Parameter:
550: . comm - The communicator for the `PetscConvEst` object

552:   Output Parameter:
553: . ce - The `PetscConvEst` object

555:   Level: beginner

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: }