1 #include <petscconvest.h> /*I "petscconvest.h" I*/ 2 #include <petscdmplex.h> 3 #include <petscds.h> 4 #include <petscblaslapack.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 0; 13 } 14 15 16 /*@ 17 PetscConvEstCreate - Create a PetscConvEst object 18 19 Collective on MPI_Comm 20 21 Input Parameter: 22 . comm - The communicator for the PetscConvEst object 23 24 Output Parameter: 25 . ce - The PetscConvEst object 26 27 Level: beginner 28 29 .keywords: PetscConvEst, convergence, create 30 .seealso: PetscConvEstDestroy(), PetscConvEstGetConvRate() 31 @*/ 32 PetscErrorCode PetscConvEstCreate(MPI_Comm comm, PetscConvEst *ce) 33 { 34 PetscErrorCode ierr; 35 36 PetscFunctionBegin; 37 PetscValidPointer(ce, 2); 38 ierr = PetscSysInitializePackage();CHKERRQ(ierr); 39 ierr = PetscHeaderCreate(*ce, PETSC_OBJECT_CLASSID, "PetscConvEst", "ConvergenceEstimator", "SNES", comm, PetscConvEstDestroy, PetscConvEstView);CHKERRQ(ierr); 40 (*ce)->monitor = PETSC_FALSE; 41 (*ce)->Nr = 4; 42 PetscFunctionReturn(0); 43 } 44 45 /*@ 46 PetscConvEstDestroy - Destroys a PetscConvEst object 47 48 Collective on PetscConvEst 49 50 Input Parameter: 51 . ce - The PetscConvEst object 52 53 Level: beginner 54 55 .keywords: PetscConvEst, convergence, destroy 56 .seealso: PetscConvEstCreate(), PetscConvEstGetConvRate() 57 @*/ 58 PetscErrorCode PetscConvEstDestroy(PetscConvEst *ce) 59 { 60 PetscErrorCode ierr; 61 62 PetscFunctionBegin; 63 if (!*ce) PetscFunctionReturn(0); 64 PetscValidHeaderSpecific((*ce),PETSC_OBJECT_CLASSID,1); 65 if (--((PetscObject)(*ce))->refct > 0) { 66 *ce = NULL; 67 PetscFunctionReturn(0); 68 } 69 ierr = PetscFree2((*ce)->initGuess, (*ce)->exactSol);CHKERRQ(ierr); 70 ierr = PetscFree((*ce)->errors);CHKERRQ(ierr); 71 ierr = PetscHeaderDestroy(ce);CHKERRQ(ierr); 72 PetscFunctionReturn(0); 73 } 74 75 /*@ 76 PetscConvEstSetFromOptions - Sets a PetscConvEst object from options 77 78 Collective on PetscConvEst 79 80 Input Parameters: 81 . ce - The PetscConvEst object 82 83 Level: beginner 84 85 .keywords: PetscConvEst, convergence, options 86 .seealso: PetscConvEstCreate(), PetscConvEstGetConvRate() 87 @*/ 88 PetscErrorCode PetscConvEstSetFromOptions(PetscConvEst ce) 89 { 90 PetscErrorCode ierr; 91 92 PetscFunctionBegin; 93 ierr = PetscOptionsBegin(PetscObjectComm((PetscObject) ce), "", "Convergence Estimator Options", "PetscConvEst");CHKERRQ(ierr); 94 ierr = PetscOptionsInt("-convest_num_refine", "The number of refinements for the convergence check", "PetscConvEst", ce->Nr, &ce->Nr, NULL);CHKERRQ(ierr); 95 ierr = PetscOptionsBool("-convest_monitor", "Monitor the error for each convergence check", "PetscConvEst", ce->monitor, &ce->monitor, NULL);CHKERRQ(ierr); 96 ierr = PetscOptionsEnd(); 97 PetscFunctionReturn(0); 98 } 99 100 /*@ 101 PetscConvEstView - Views a PetscConvEst object 102 103 Collective on PetscConvEst 104 105 Input Parameters: 106 + ce - The PetscConvEst object 107 - viewer - The PetscViewer object 108 109 Level: beginner 110 111 .keywords: PetscConvEst, convergence, view 112 .seealso: PetscConvEstCreate(), PetscConvEstGetConvRate() 113 @*/ 114 PetscErrorCode PetscConvEstView(PetscConvEst ce, PetscViewer viewer) 115 { 116 PetscErrorCode ierr; 117 118 PetscFunctionBegin; 119 ierr = PetscObjectPrintClassNamePrefixType((PetscObject) ce, viewer);CHKERRQ(ierr); 120 ierr = PetscViewerASCIIPrintf(viewer, "ConvEst with %D levels\n", ce->Nr+1);CHKERRQ(ierr); 121 PetscFunctionReturn(0); 122 } 123 124 /*@ 125 PetscConvEstGetSolver - Gets the solver used to produce discrete solutions 126 127 Not collective 128 129 Input Parameter: 130 . ce - The PetscConvEst object 131 132 Output Parameter: 133 . snes - The solver 134 135 Level: intermediate 136 137 .keywords: PetscConvEst, convergence 138 .seealso: PetscConvEstSetSolver(), PetscConvEstCreate(), PetscConvEstGetConvRate() 139 @*/ 140 PetscErrorCode PetscConvEstGetSolver(PetscConvEst ce, SNES *snes) 141 { 142 PetscFunctionBegin; 143 PetscValidHeaderSpecific(ce, PETSC_OBJECT_CLASSID, 1); 144 PetscValidPointer(snes, 2); 145 *snes = ce->snes; 146 PetscFunctionReturn(0); 147 } 148 149 /*@ 150 PetscConvEstSetSolver - Sets the solver used to produce discrete solutions 151 152 Not collective 153 154 Input Parameters: 155 + ce - The PetscConvEst object 156 - snes - The solver 157 158 Level: intermediate 159 160 Note: The solver MUST have an attached DM/DS, so that we know the exact solution 161 162 .keywords: PetscConvEst, convergence 163 .seealso: PetscConvEstGetSolver(), PetscConvEstCreate(), PetscConvEstGetConvRate() 164 @*/ 165 PetscErrorCode PetscConvEstSetSolver(PetscConvEst ce, SNES snes) 166 { 167 PetscErrorCode ierr; 168 169 PetscFunctionBegin; 170 PetscValidHeaderSpecific(ce, PETSC_OBJECT_CLASSID, 1); 171 PetscValidHeaderSpecific(snes, SNES_CLASSID, 2); 172 ce->snes = snes; 173 ierr = SNESGetDM(ce->snes, &ce->idm);CHKERRQ(ierr); 174 PetscFunctionReturn(0); 175 } 176 177 /*@ 178 PetscConvEstSetUp - After the solver is specified, we create structures for estimating convergence 179 180 Collective on PetscConvEst 181 182 Input Parameters: 183 . ce - The PetscConvEst object 184 185 Level: beginner 186 187 .keywords: PetscConvEst, convergence, setup 188 .seealso: PetscConvEstCreate(), PetscConvEstGetConvRate() 189 @*/ 190 PetscErrorCode PetscConvEstSetUp(PetscConvEst ce) 191 { 192 PetscDS prob; 193 PetscInt f; 194 PetscErrorCode ierr; 195 196 PetscFunctionBegin; 197 ierr = DMGetDS(ce->idm, &prob);CHKERRQ(ierr); 198 ierr = PetscDSGetNumFields(prob, &ce->Nf);CHKERRQ(ierr); 199 ierr = PetscMalloc1((ce->Nr+1)*ce->Nf, &ce->errors);CHKERRQ(ierr); 200 ierr = PetscMalloc2(ce->Nf, &ce->initGuess, ce->Nf, &ce->exactSol);CHKERRQ(ierr); 201 for (f = 0; f < ce->Nf; ++f) ce->initGuess[f] = zero_private; 202 for (f = 0; f < ce->Nf; ++f) { 203 ierr = PetscDSGetExactSolution(prob, f, &ce->exactSol[f]);CHKERRQ(ierr); 204 if (!ce->exactSol[f]) SETERRQ1(PetscObjectComm((PetscObject) ce), PETSC_ERR_ARG_WRONG, "DS must contain exact solution functions in order to estimate convergence, missing for field %D", f); 205 } 206 PetscFunctionReturn(0); 207 } 208 209 static PetscErrorCode PetscConvEstLinearRegression_Private(PetscConvEst ce, PetscInt n, const PetscReal x[], const PetscReal y[], PetscReal *slope, PetscReal *intercept) 210 { 211 PetscScalar H[4]; 212 PetscReal *X, *Y, beta[2]; 213 PetscInt i, j, k; 214 PetscErrorCode ierr; 215 216 PetscFunctionBegin; 217 *slope = *intercept = 0.0; 218 ierr = PetscMalloc2(n*2, &X, n*2, &Y);CHKERRQ(ierr); 219 for (k = 0; k < n; ++k) { 220 /* X[n,2] = [1, x] */ 221 X[k*2+0] = 1.0; 222 X[k*2+1] = x[k]; 223 } 224 /* H = X^T X */ 225 for (i = 0; i < 2; ++i) { 226 for (j = 0; j < 2; ++j) { 227 H[i*2+j] = 0.0; 228 for (k = 0; k < n; ++k) { 229 H[i*2+j] += X[k*2+i] * X[k*2+j]; 230 } 231 } 232 } 233 /* H = (X^T X)^{-1} */ 234 { 235 PetscBLASInt two = 2, ipiv[2], info; 236 PetscScalar work[2]; 237 238 ierr = PetscFPTrapPush(PETSC_FP_TRAP_OFF);CHKERRQ(ierr); 239 PetscStackCallBLAS("LAPACKgetrf", LAPACKgetrf_(&two, &two, H, &two, ipiv, &info)); 240 PetscStackCallBLAS("LAPACKgetri", LAPACKgetri_(&two, H, &two, ipiv, work, &two, &info)); 241 ierr = PetscFPTrapPop();CHKERRQ(ierr); 242 } 243 /* Y = H X^T */ 244 for (i = 0; i < 2; ++i) { 245 for (k = 0; k < n; ++k) { 246 Y[i*n+k] = 0.0; 247 for (j = 0; j < 2; ++j) { 248 Y[i*n+k] += PetscRealPart(H[i*2+j]) * X[k*2+j]; 249 } 250 } 251 } 252 /* beta = Y error = [y-intercept, slope] */ 253 for (i = 0; i < 2; ++i) { 254 beta[i] = 0.0; 255 for (k = 0; k < n; ++k) { 256 beta[i] += Y[i*n+k] * y[k]; 257 } 258 } 259 ierr = PetscFree2(X, Y);CHKERRQ(ierr); 260 *intercept = beta[0]; 261 *slope = beta[1]; 262 PetscFunctionReturn(0); 263 } 264 265 /*@ 266 PetscConvEstGetConvRate - Returns an estimate of the convergence rate for the discretization 267 268 Not collective 269 270 Input Parameter: 271 . ce - The PetscConvEst object 272 273 Output Parameter: 274 . alpha - The convergence rate for each field 275 276 Note: The convergence rate alpha is defined by 277 $ || u_h - u_exact || < C h^alpha 278 where u_h is the discrete solution, and h is a measure of the discretization size. 279 280 We solve a series of problems on refined meshes, calculate an error based upon the exact solution in the DS, 281 and then fit the result to our model above using linear regression. 282 283 Options database keys: 284 . -snes_convergence_estimate : Execute convergence estimation and print out the rate 285 286 Level: intermediate 287 288 .keywords: PetscConvEst, convergence 289 .seealso: PetscConvEstSetSolver(), PetscConvEstCreate(), PetscConvEstGetConvRate() 290 @*/ 291 PetscErrorCode PetscConvEstGetConvRate(PetscConvEst ce, PetscReal alpha[]) 292 { 293 DM *dm; 294 PetscDS prob; 295 PetscObject disc; 296 MPI_Comm comm; 297 const char *uname, *dmname; 298 void *ctx; 299 Vec u; 300 PetscReal t = 0.0, *x, *y, slope, intercept; 301 PetscInt *dof, dim, Nr = ce->Nr, r, f; 302 PetscLogEvent event; 303 PetscErrorCode ierr; 304 305 PetscFunctionBegin; 306 ierr = PetscObjectGetComm((PetscObject) ce, &comm);CHKERRQ(ierr); 307 ierr = DMGetDimension(ce->idm, &dim);CHKERRQ(ierr); 308 ierr = DMGetApplicationContext(ce->idm, &ctx);CHKERRQ(ierr); 309 ierr = DMGetDS(ce->idm, &prob);CHKERRQ(ierr); 310 ierr = DMPlexSetRefinementUniform(ce->idm, PETSC_TRUE);CHKERRQ(ierr); 311 ierr = PetscMalloc2((Nr+1), &dm, (Nr+1)*ce->Nf, &dof);CHKERRQ(ierr); 312 dm[0] = ce->idm; 313 for (f = 0; f < ce->Nf; ++f) alpha[f] = 0.0; 314 /* Loop over meshes */ 315 ierr = PetscLogEventRegister("ConvEst Error", PETSC_OBJECT_CLASSID, &event);CHKERRQ(ierr); 316 for (r = 0; r <= Nr; ++r) { 317 PetscLogStage stage; 318 char stageName[PETSC_MAX_PATH_LEN]; 319 320 ierr = PetscSNPrintf(stageName, PETSC_MAX_PATH_LEN-1, "ConvEst Refinement Level %D", r);CHKERRQ(ierr); 321 ierr = PetscLogStageRegister(stageName, &stage);CHKERRQ(ierr); 322 ierr = PetscLogStagePush(stage);CHKERRQ(ierr); 323 if (r > 0) { 324 ierr = DMRefine(dm[r-1], MPI_COMM_NULL, &dm[r]);CHKERRQ(ierr); 325 ierr = DMSetCoarseDM(dm[r], dm[r-1]);CHKERRQ(ierr); 326 ierr = DMSetDS(dm[r], prob);CHKERRQ(ierr); 327 ierr = PetscObjectGetName((PetscObject) dm[r-1], &dmname);CHKERRQ(ierr); 328 ierr = PetscObjectSetName((PetscObject) dm[r], dmname);CHKERRQ(ierr); 329 for (f = 0; f <= ce->Nf; ++f) { 330 PetscErrorCode (*nspconstr)(DM, PetscInt, MatNullSpace *); 331 ierr = DMGetNullSpaceConstructor(dm[r-1], f, &nspconstr);CHKERRQ(ierr); 332 ierr = DMSetNullSpaceConstructor(dm[r], f, nspconstr);CHKERRQ(ierr); 333 } 334 } 335 ierr = DMViewFromOptions(dm[r], NULL, "-conv_dm_view");CHKERRQ(ierr); 336 /* Create solution */ 337 ierr = DMCreateGlobalVector(dm[r], &u);CHKERRQ(ierr); 338 ierr = PetscDSGetDiscretization(prob, 0, &disc);CHKERRQ(ierr); 339 ierr = PetscObjectGetName(disc, &uname);CHKERRQ(ierr); 340 ierr = PetscObjectSetName((PetscObject) u, uname);CHKERRQ(ierr); 341 /* Setup solver */ 342 ierr = SNESReset(ce->snes);CHKERRQ(ierr); 343 ierr = SNESSetDM(ce->snes, dm[r]);CHKERRQ(ierr); 344 ierr = DMPlexSetSNESLocalFEM(dm[r], ctx, ctx, ctx);CHKERRQ(ierr); 345 ierr = SNESSetFromOptions(ce->snes);CHKERRQ(ierr); 346 /* Create initial guess */ 347 ierr = DMProjectFunction(dm[r], t, ce->initGuess, NULL, INSERT_VALUES, u);CHKERRQ(ierr); 348 ierr = SNESSolve(ce->snes, NULL, u);CHKERRQ(ierr); 349 ierr = DMComputeL2FieldDiff(dm[r], t, ce->exactSol, NULL, u, &ce->errors[r*ce->Nf]);CHKERRQ(ierr); 350 ierr = PetscLogEventEnd(event, ce, 0, 0, 0);CHKERRQ(ierr); 351 for (f = 0; f < ce->Nf; ++f) { 352 ierr = DMPlexGetHeightStratum(dm[r], 0, NULL, &dof[r*ce->Nf+f]);CHKERRQ(ierr); 353 } 354 /* Monitor */ 355 if (ce->monitor) { 356 PetscReal *errors = &ce->errors[r*ce->Nf]; 357 358 ierr = PetscPrintf(comm, "L_2 Error: ");CHKERRQ(ierr); 359 if (ce->Nf > 1) {ierr = PetscPrintf(comm, "[");CHKERRQ(ierr);} 360 for (f = 0; f < ce->Nf; ++f) { 361 if (f > 0) {ierr = PetscPrintf(comm, ", ");CHKERRQ(ierr);} 362 if (errors[f] < 1.0e-11) {ierr = PetscPrintf(comm, "< 1e-11");CHKERRQ(ierr);} 363 else {ierr = PetscPrintf(comm, "%g", (double)errors[f]);CHKERRQ(ierr);} 364 } 365 if (ce->Nf > 1) {ierr = PetscPrintf(comm, "]");CHKERRQ(ierr);} 366 ierr = PetscPrintf(comm, "\n");CHKERRQ(ierr); 367 } 368 /* Cleanup */ 369 ierr = VecDestroy(&u);CHKERRQ(ierr); 370 ierr = PetscLogStagePop();CHKERRQ(ierr); 371 } 372 for (r = 1; r <= Nr; ++r) { 373 ierr = DMDestroy(&dm[r]);CHKERRQ(ierr); 374 } 375 /* Fit convergence rate */ 376 ierr = PetscMalloc2(Nr+1, &x, Nr+1, &y);CHKERRQ(ierr); 377 for (f = 0; f < ce->Nf; ++f) { 378 for (r = 0; r <= Nr; ++r) { 379 x[r] = PetscLog10Real(dof[r*ce->Nf+f]); 380 y[r] = PetscLog10Real(ce->errors[r*ce->Nf+f]); 381 } 382 ierr = PetscConvEstLinearRegression_Private(ce, Nr+1, x, y, &slope, &intercept);CHKERRQ(ierr); 383 /* Since h^{-dim} = N, lg err = s lg N + b = -s dim lg h + b */ 384 alpha[f] = -slope * dim; 385 } 386 ierr = PetscFree2(x, y);CHKERRQ(ierr); 387 ierr = PetscFree2(dm, dof);CHKERRQ(ierr); 388 /* Restore solver */ 389 ierr = SNESReset(ce->snes);CHKERRQ(ierr); 390 ierr = SNESSetDM(ce->snes, ce->idm);CHKERRQ(ierr); 391 ierr = DMPlexSetSNESLocalFEM(ce->idm, ctx, ctx, ctx);CHKERRQ(ierr); 392 ierr = SNESSetFromOptions(ce->snes);CHKERRQ(ierr); 393 PetscFunctionReturn(0); 394 } 395 396 /*@ 397 PetscConvEstRateView - Displays the convergence rate to a viewer 398 399 Collective on SNES 400 401 Parameter: 402 + snes - iterative context obtained from SNESCreate() 403 . alpha - the convergence rate for each field 404 - viewer - the viewer to display the reason 405 406 Options Database Keys: 407 . -snes_convergence_estimate - print the convergence rate 408 409 Level: developer 410 411 .seealso: PetscConvEstGetRate() 412 @*/ 413 PetscErrorCode PetscConvEstRateView(PetscConvEst ce, PetscReal alpha[], PetscViewer viewer) 414 { 415 PetscBool isAscii; 416 PetscInt f; 417 PetscErrorCode ierr; 418 419 PetscFunctionBegin; 420 ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERASCII, &isAscii);CHKERRQ(ierr); 421 if (isAscii) { 422 ierr = PetscViewerASCIIAddTab(viewer, ((PetscObject) ce)->tablevel);CHKERRQ(ierr); 423 ierr = PetscViewerASCIIPrintf(viewer, "L_2 convergence rate: ");CHKERRQ(ierr); 424 if (ce->Nf > 1) {ierr = PetscViewerASCIIPrintf(viewer, "[");CHKERRQ(ierr);} 425 for (f = 0; f < ce->Nf; ++f) { 426 if (f > 0) {ierr = PetscViewerASCIIPrintf(viewer, ", ");CHKERRQ(ierr);} 427 ierr = PetscViewerASCIIPrintf(viewer, "%g", (double) alpha[f]);CHKERRQ(ierr); 428 } 429 if (ce->Nf > 1) {ierr = PetscViewerASCIIPrintf(viewer, "]");CHKERRQ(ierr);} 430 ierr = PetscViewerASCIIPrintf(viewer, "\n");CHKERRQ(ierr); 431 ierr = PetscViewerASCIISubtractTab(viewer, ((PetscObject) ce)->tablevel);CHKERRQ(ierr); 432 } 433 PetscFunctionReturn(0); 434 } 435