xref: /petsc/src/snes/utils/convest.c (revision e5a36eccef3d6b83a2c625c30d0dfd5adb4001f2)
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   PetscObject    disc;
295   MPI_Comm       comm;
296   const char    *uname, *dmname;
297   void          *ctx;
298   Vec            u;
299   PetscReal      t = 0.0, *x, *y, slope, intercept;
300   PetscInt      *dof, dim, Nr = ce->Nr, r, f, oldlevel, oldnlev;
301   PetscLogEvent  event;
302   PetscErrorCode ierr;
303 
304   PetscFunctionBegin;
305   ierr = PetscObjectGetComm((PetscObject) ce, &comm);CHKERRQ(ierr);
306   ierr = DMGetDimension(ce->idm, &dim);CHKERRQ(ierr);
307   ierr = DMGetApplicationContext(ce->idm, &ctx);CHKERRQ(ierr);
308   ierr = DMPlexSetRefinementUniform(ce->idm, PETSC_TRUE);CHKERRQ(ierr);
309   ierr = DMGetRefineLevel(ce->idm, &oldlevel);CHKERRQ(ierr);
310   ierr = PetscMalloc2((Nr+1), &dm, (Nr+1)*ce->Nf, &dof);CHKERRQ(ierr);
311   dm[0]  = ce->idm;
312   for (f = 0; f < ce->Nf; ++f) alpha[f] = 0.0;
313   /* Loop over meshes */
314   ierr = PetscLogEventRegister("ConvEst Error", PETSC_OBJECT_CLASSID, &event);CHKERRQ(ierr);
315   for (r = 0; r <= Nr; ++r) {
316     PetscLogStage stage;
317     char          stageName[PETSC_MAX_PATH_LEN];
318 
319     ierr = PetscSNPrintf(stageName, PETSC_MAX_PATH_LEN-1, "ConvEst Refinement Level %D", r);CHKERRQ(ierr);
320     ierr = PetscLogStageRegister(stageName, &stage);CHKERRQ(ierr);
321     ierr = PetscLogStagePush(stage);CHKERRQ(ierr);
322     if (r > 0) {
323       ierr = DMRefine(dm[r-1], MPI_COMM_NULL, &dm[r]);CHKERRQ(ierr);
324       ierr = DMSetCoarseDM(dm[r], dm[r-1]);CHKERRQ(ierr);
325       ierr = DMCopyDisc(ce->idm, dm[r]);CHKERRQ(ierr);
326       ierr = PetscObjectGetName((PetscObject) dm[r-1], &dmname);CHKERRQ(ierr);
327       ierr = PetscObjectSetName((PetscObject) dm[r], dmname);CHKERRQ(ierr);
328       for (f = 0; f <= ce->Nf; ++f) {
329         PetscErrorCode (*nspconstr)(DM, PetscInt, MatNullSpace *);
330         ierr = DMGetNullSpaceConstructor(dm[r-1], f, &nspconstr);CHKERRQ(ierr);
331         ierr = DMSetNullSpaceConstructor(dm[r],   f,  nspconstr);CHKERRQ(ierr);
332       }
333     }
334     ierr = DMViewFromOptions(dm[r], NULL, "-conv_dm_view");CHKERRQ(ierr);
335     /* Create solution */
336     ierr = DMCreateGlobalVector(dm[r], &u);CHKERRQ(ierr);
337     ierr = DMGetField(dm[r], 0, NULL, &disc);CHKERRQ(ierr);
338     ierr = PetscObjectGetName(disc, &uname);CHKERRQ(ierr);
339     ierr = PetscObjectSetName((PetscObject) u, uname);CHKERRQ(ierr);
340     /* Setup solver */
341     ierr = SNESReset(ce->snes);CHKERRQ(ierr);
342     ierr = SNESSetDM(ce->snes, dm[r]);CHKERRQ(ierr);
343     ierr = DMPlexSetSNESLocalFEM(dm[r], ctx, ctx, ctx);CHKERRQ(ierr);
344     ierr = SNESSetFromOptions(ce->snes);CHKERRQ(ierr);
345     /* Create initial guess */
346     ierr = DMProjectFunction(dm[r], t, ce->initGuess, NULL, INSERT_VALUES, u);CHKERRQ(ierr);
347     ierr = SNESSolve(ce->snes, NULL, u);CHKERRQ(ierr);
348     ierr = PetscLogEventBegin(event, ce, 0, 0, 0);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       PetscSection s, fs;
353       PetscInt     lsize;
354 
355       /* Could use DMGetOutputDM() to add in Dirichlet dofs */
356       ierr = DMGetSection(dm[r], &s);CHKERRQ(ierr);
357       ierr = PetscSectionGetField(s, f, &fs);CHKERRQ(ierr);
358       ierr = PetscSectionGetConstrainedStorageSize(fs, &lsize);CHKERRQ(ierr);
359       ierr = MPI_Allreduce(&lsize, &dof[r*ce->Nf+f], 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject) ce->snes));CHKERRQ(ierr);
360       ierr = PetscLogEventSetDof(event, f, dof[r*ce->Nf+f]);CHKERRQ(ierr);
361       ierr = PetscLogEventSetError(event, f, ce->errors[r*ce->Nf+f]);CHKERRQ(ierr);
362     }
363     /* Monitor */
364     if (ce->monitor) {
365       PetscReal *errors = &ce->errors[r*ce->Nf];
366 
367       ierr = PetscPrintf(comm, "L_2 Error: ");CHKERRQ(ierr);
368       if (ce->Nf > 1) {ierr = PetscPrintf(comm, "[");CHKERRQ(ierr);}
369       for (f = 0; f < ce->Nf; ++f) {
370         if (f > 0) {ierr = PetscPrintf(comm, ", ");CHKERRQ(ierr);}
371         if (errors[f] < 1.0e-11) {ierr = PetscPrintf(comm, "< 1e-11");CHKERRQ(ierr);}
372         else                     {ierr = PetscPrintf(comm, "%g", (double)errors[f]);CHKERRQ(ierr);}
373       }
374       if (ce->Nf > 1) {ierr = PetscPrintf(comm, "]");CHKERRQ(ierr);}
375       ierr = PetscPrintf(comm, "\n");CHKERRQ(ierr);
376     }
377     if (!r) {
378       /* PCReset() does not wipe out the level structure */
379       KSP ksp;
380       PC  pc;
381 
382       ierr = SNESGetKSP(ce->snes, &ksp);CHKERRQ(ierr);
383       ierr = KSPGetPC(ksp, &pc);CHKERRQ(ierr);
384       ierr = PCMGGetLevels(pc, &oldnlev);CHKERRQ(ierr);
385     }
386     /* Cleanup */
387     ierr = VecDestroy(&u);CHKERRQ(ierr);
388     ierr = PetscLogStagePop();CHKERRQ(ierr);
389   }
390   for (r = 1; r <= Nr; ++r) {
391     ierr = DMDestroy(&dm[r]);CHKERRQ(ierr);
392   }
393   /* Fit convergence rate */
394   ierr = PetscMalloc2(Nr+1, &x, Nr+1, &y);CHKERRQ(ierr);
395   for (f = 0; f < ce->Nf; ++f) {
396     for (r = 0; r <= Nr; ++r) {
397       x[r] = PetscLog10Real(dof[r*ce->Nf+f]);
398       y[r] = PetscLog10Real(ce->errors[r*ce->Nf+f]);
399     }
400     ierr = PetscConvEstLinearRegression_Private(ce, Nr+1, x, y, &slope, &intercept);CHKERRQ(ierr);
401     /* Since h^{-dim} = N, lg err = s lg N + b = -s dim lg h + b */
402     alpha[f] = -slope * dim;
403   }
404   ierr = PetscFree2(x, y);CHKERRQ(ierr);
405   ierr = PetscFree2(dm, dof);CHKERRQ(ierr);
406   /* Restore solver */
407   ierr = SNESReset(ce->snes);CHKERRQ(ierr);
408   {
409     /* PCReset() does not wipe out the level structure */
410     KSP ksp;
411     PC  pc;
412 
413     ierr = SNESGetKSP(ce->snes, &ksp);CHKERRQ(ierr);
414     ierr = KSPGetPC(ksp, &pc);CHKERRQ(ierr);
415     ierr = PCMGSetLevels(pc, oldnlev, NULL);CHKERRQ(ierr);
416     ierr = DMSetRefineLevel(ce->idm, oldlevel);CHKERRQ(ierr); /* The damn DMCoarsen() calls in PCMG can reset this */
417   }
418   ierr = SNESSetDM(ce->snes, ce->idm);CHKERRQ(ierr);
419   ierr = DMPlexSetSNESLocalFEM(ce->idm, ctx, ctx, ctx);CHKERRQ(ierr);
420   ierr = SNESSetFromOptions(ce->snes);CHKERRQ(ierr);
421   PetscFunctionReturn(0);
422 }
423 
424 /*@
425   PetscConvEstRateView - Displays the convergence rate to a viewer
426 
427    Collective on SNES
428 
429    Parameter:
430 +  snes - iterative context obtained from SNESCreate()
431 .  alpha - the convergence rate for each field
432 -  viewer - the viewer to display the reason
433 
434    Options Database Keys:
435 .  -snes_convergence_estimate - print the convergence rate
436 
437    Level: developer
438 
439 .seealso: PetscConvEstGetRate()
440 @*/
441 PetscErrorCode PetscConvEstRateView(PetscConvEst ce, const PetscReal alpha[], PetscViewer viewer)
442 {
443   PetscBool      isAscii;
444   PetscErrorCode ierr;
445 
446   PetscFunctionBegin;
447   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERASCII, &isAscii);CHKERRQ(ierr);
448   if (isAscii) {
449     DM       dm;
450     PetscInt Nf, f;
451 
452     ierr = SNESGetDM(ce->snes, &dm);CHKERRQ(ierr);
453     ierr = DMGetNumFields(dm, &Nf);CHKERRQ(ierr);
454     ierr = PetscViewerASCIIAddTab(viewer, ((PetscObject) ce)->tablevel);CHKERRQ(ierr);
455     ierr = PetscViewerASCIIPrintf(viewer, "L_2 convergence rate: ");CHKERRQ(ierr);
456     if (Nf > 1) {ierr = PetscViewerASCIIPrintf(viewer, "[");CHKERRQ(ierr);}
457     for (f = 0; f < Nf; ++f) {
458       if (f > 0) {ierr = PetscViewerASCIIPrintf(viewer, ", ");CHKERRQ(ierr);}
459       ierr = PetscViewerASCIIPrintf(viewer, "%#.2g", (double) alpha[f]);CHKERRQ(ierr);
460     }
461     if (Nf > 1) {ierr = PetscViewerASCIIPrintf(viewer, "]");CHKERRQ(ierr);}
462     ierr = PetscViewerASCIIPrintf(viewer, "\n");CHKERRQ(ierr);
463     ierr = PetscViewerASCIISubtractTab(viewer, ((PetscObject) ce)->tablevel);CHKERRQ(ierr);
464   }
465   PetscFunctionReturn(0);
466 }
467