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