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