xref: /petsc/src/ksp/ksp/impls/cheby/cheby.c (revision 834855d6effb0d027771461c8e947ee1ce5a1e17)
1 #include "chebyshevimpl.h"
2 #include <../src/ksp/ksp/impls/cheby/chebyshevimpl.h> /*I "petscksp.h" I*/
3 
4 static const char *const KSPChebyshevKinds[] = {"FIRST", "FOURTH", "OPT_FOURTH", "KSPChebyshevKinds", "KSP_CHEBYSHEV_", NULL};
5 
KSPReset_Chebyshev(KSP ksp)6 static PetscErrorCode KSPReset_Chebyshev(KSP ksp)
7 {
8   KSP_Chebyshev *cheb = (KSP_Chebyshev *)ksp->data;
9 
10   PetscFunctionBegin;
11   if (cheb->kspest) PetscCall(KSPReset(cheb->kspest));
12   PetscFunctionReturn(PETSC_SUCCESS);
13 }
14 
15 /*
16     Must be passed a KSP solver that has "converged", with KSPSetComputeEigenvalues() called before the solve
17  */
KSPChebyshevComputeExtremeEigenvalues_Private(KSP kspest,PetscReal * emin,PetscReal * emax)18 static PetscErrorCode KSPChebyshevComputeExtremeEigenvalues_Private(KSP kspest, PetscReal *emin, PetscReal *emax)
19 {
20   PetscInt   n, neig;
21   PetscReal *re, *im, min, max;
22 
23   PetscFunctionBegin;
24   PetscCall(KSPGetIterationNumber(kspest, &n));
25   PetscCall(PetscMalloc2(n, &re, n, &im));
26   PetscCall(KSPComputeEigenvalues(kspest, n, re, im, &neig));
27   min = PETSC_MAX_REAL;
28   max = PETSC_MIN_REAL;
29   for (n = 0; n < neig; n++) {
30     min = PetscMin(min, re[n]);
31     max = PetscMax(max, re[n]);
32   }
33   PetscCall(PetscFree2(re, im));
34   *emax = max;
35   *emin = min;
36   PetscCall(PetscInfo(kspest, " eigen estimate min/max = %g %g\n", (double)min, (double)max));
37   PetscFunctionReturn(PETSC_SUCCESS);
38 }
39 
KSPChebyshevGetEigenvalues_Chebyshev(KSP ksp,PetscReal * emax,PetscReal * emin)40 static PetscErrorCode KSPChebyshevGetEigenvalues_Chebyshev(KSP ksp, PetscReal *emax, PetscReal *emin)
41 {
42   KSP_Chebyshev *cheb = (KSP_Chebyshev *)ksp->data;
43 
44   PetscFunctionBegin;
45   *emax = 0;
46   *emin = 0;
47   if (cheb->emax != 0.) {
48     *emax = cheb->emax;
49   } else if (cheb->emax_computed != 0.) {
50     *emax = cheb->tform[2] * cheb->emin_computed + cheb->tform[3] * cheb->emax_computed;
51   } else if (cheb->emax_provided != 0.) {
52     *emax = cheb->tform[2] * cheb->emin_provided + cheb->tform[3] * cheb->emax_provided;
53   }
54   if (cheb->emin != 0.) {
55     *emin = cheb->emin;
56   } else if (cheb->emin_computed != 0.) {
57     *emin = cheb->tform[0] * cheb->emin_computed + cheb->tform[1] * cheb->emax_computed;
58   } else if (cheb->emin_provided != 0.) {
59     *emin = cheb->tform[0] * cheb->emin_provided + cheb->tform[1] * cheb->emax_provided;
60   }
61   PetscFunctionReturn(PETSC_SUCCESS);
62 }
63 
KSPChebyshevSetEigenvalues_Chebyshev(KSP ksp,PetscReal emax,PetscReal emin)64 static PetscErrorCode KSPChebyshevSetEigenvalues_Chebyshev(KSP ksp, PetscReal emax, PetscReal emin)
65 {
66   KSP_Chebyshev *chebyshevP = (KSP_Chebyshev *)ksp->data;
67 
68   PetscFunctionBegin;
69   PetscCheck(emax > emin || (emax == 0 && emin == 0) || (emax == -1 && emin == -1), PetscObjectComm((PetscObject)ksp), PETSC_ERR_ARG_INCOMP, "Maximum eigenvalue must be larger than minimum: max %g min %g", (double)emax, (double)emin);
70   PetscCheck(emax * emin > 0.0 || (emax == 0 && emin == 0), PetscObjectComm((PetscObject)ksp), PETSC_ERR_ARG_INCOMP, "Both eigenvalues must be of the same sign: max %g min %g", (double)emax, (double)emin);
71   chebyshevP->emax = emax;
72   chebyshevP->emin = emin;
73 
74   PetscCall(KSPChebyshevEstEigSet(ksp, 0., 0., 0., 0.)); /* Destroy any estimation setup */
75   PetscFunctionReturn(PETSC_SUCCESS);
76 }
77 
KSPChebyshevEstEigSet_Chebyshev(KSP ksp,PetscReal a,PetscReal b,PetscReal c,PetscReal d)78 static PetscErrorCode KSPChebyshevEstEigSet_Chebyshev(KSP ksp, PetscReal a, PetscReal b, PetscReal c, PetscReal d)
79 {
80   KSP_Chebyshev *cheb = (KSP_Chebyshev *)ksp->data;
81   PetscInt       nestlevel;
82 
83   PetscFunctionBegin;
84   if (a != 0.0 || b != 0.0 || c != 0.0 || d != 0.0) {
85     if ((cheb->emin_provided == 0. || cheb->emax_provided == 0.) && !cheb->kspest) { /* should this block of code be moved to KSPSetUp_Chebyshev()? */
86       PetscCall(KSPCreate(PetscObjectComm((PetscObject)ksp), &cheb->kspest));
87       PetscCall(KSPGetNestLevel(ksp, &nestlevel));
88       PetscCall(KSPSetNestLevel(cheb->kspest, nestlevel + 1));
89       PetscCall(KSPSetErrorIfNotConverged(cheb->kspest, ksp->errorifnotconverged));
90       PetscCall(PetscObjectIncrementTabLevel((PetscObject)cheb->kspest, (PetscObject)ksp, 1));
91       /* use PetscObjectSet/AppendOptionsPrefix() instead of KSPSet/AppendOptionsPrefix() so that the PC prefix is not changed */
92       PetscCall(PetscObjectSetOptionsPrefix((PetscObject)cheb->kspest, ((PetscObject)ksp)->prefix));
93       PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)cheb->kspest, "esteig_"));
94       PetscCall(KSPSetSkipPCSetFromOptions(cheb->kspest, PETSC_TRUE));
95       PetscCall(KSPSetComputeEigenvalues(cheb->kspest, PETSC_TRUE));
96 
97       /* We cannot turn off convergence testing because GMRES will break down if you attempt to keep iterating after a zero norm is obtained */
98       PetscCall(KSPSetTolerances(cheb->kspest, 1.e-12, PETSC_CURRENT, PETSC_CURRENT, cheb->eststeps));
99       PetscCall(PetscInfo(ksp, "Created eigen estimator KSP\n"));
100     }
101     if (a >= 0) cheb->tform[0] = a;
102     if (b >= 0) cheb->tform[1] = b;
103     if (c >= 0) cheb->tform[2] = c;
104     if (d >= 0) cheb->tform[3] = d;
105     cheb->amatid    = 0;
106     cheb->pmatid    = 0;
107     cheb->amatstate = -1;
108     cheb->pmatstate = -1;
109   } else {
110     PetscCall(KSPDestroy(&cheb->kspest));
111   }
112   PetscFunctionReturn(PETSC_SUCCESS);
113 }
114 
KSPChebyshevEstEigSetUseNoisy_Chebyshev(KSP ksp,PetscBool use)115 static PetscErrorCode KSPChebyshevEstEigSetUseNoisy_Chebyshev(KSP ksp, PetscBool use)
116 {
117   KSP_Chebyshev *cheb = (KSP_Chebyshev *)ksp->data;
118 
119   PetscFunctionBegin;
120   cheb->usenoisy = use;
121   PetscFunctionReturn(PETSC_SUCCESS);
122 }
123 
KSPChebyshevSetKind_Chebyshev(KSP ksp,KSPChebyshevKind kind)124 static PetscErrorCode KSPChebyshevSetKind_Chebyshev(KSP ksp, KSPChebyshevKind kind)
125 {
126   KSP_Chebyshev *cheb = (KSP_Chebyshev *)ksp->data;
127 
128   PetscFunctionBegin;
129   cheb->chebykind = kind;
130   PetscFunctionReturn(PETSC_SUCCESS);
131 }
132 
KSPChebyshevGetKind_Chebyshev(KSP ksp,KSPChebyshevKind * kind)133 static PetscErrorCode KSPChebyshevGetKind_Chebyshev(KSP ksp, KSPChebyshevKind *kind)
134 {
135   KSP_Chebyshev *cheb = (KSP_Chebyshev *)ksp->data;
136 
137   PetscFunctionBegin;
138   *kind = cheb->chebykind;
139   PetscFunctionReturn(PETSC_SUCCESS);
140 }
141 /*@
142   KSPChebyshevSetEigenvalues - Sets estimates for the extreme eigenvalues of the preconditioned problem.
143 
144   Logically Collective
145 
146   Input Parameters:
147 + ksp  - the Krylov space context
148 . emax - the eigenvalue maximum estimate
149 - emin - the eigenvalue minimum estimate
150 
151   Options Database Key:
152 . -ksp_chebyshev_eigenvalues emin,emax - extreme eigenvalues
153 
154   Level: intermediate
155 
156   Notes:
157   Call `KSPChebyshevEstEigSet()` or use the option `-ksp_chebyshev_esteig a,b,c,d` to have the `KSP`
158   estimate the eigenvalues and use these estimated values automatically.
159 
160   When `KSPCHEBYSHEV` is used as a smoother, one often wants to target a portion of the spectrum rather than the entire
161   spectrum. This function takes the range of target eigenvalues for Chebyshev, which will often slightly over-estimate
162   the largest eigenvalue of the actual operator (for safety) and greatly overestimate the smallest eigenvalue to
163   improve the smoothing properties of Chebyshev iteration on the higher frequencies in the spectrum.
164 
165 .seealso: [](ch_ksp), `KSPCHEBYSHEV`, `KSPChebyshevEstEigSet()`,
166 @*/
KSPChebyshevSetEigenvalues(KSP ksp,PetscReal emax,PetscReal emin)167 PetscErrorCode KSPChebyshevSetEigenvalues(KSP ksp, PetscReal emax, PetscReal emin)
168 {
169   PetscFunctionBegin;
170   PetscValidHeaderSpecific(ksp, KSP_CLASSID, 1);
171   PetscValidLogicalCollectiveReal(ksp, emax, 2);
172   PetscValidLogicalCollectiveReal(ksp, emin, 3);
173   PetscTryMethod(ksp, "KSPChebyshevSetEigenvalues_C", (KSP, PetscReal, PetscReal), (ksp, emax, emin));
174   PetscFunctionReturn(PETSC_SUCCESS);
175 }
176 
177 /*@
178   KSPChebyshevEstEigSet - Automatically estimate the eigenvalues to use for Chebyshev
179 
180   Logically Collective
181 
182   Input Parameters:
183 + ksp - the Krylov space context
184 . a   - multiple of min eigenvalue estimate to use for min Chebyshev bound (or `PETSC_DECIDE`)
185 . b   - multiple of max eigenvalue estimate to use for min Chebyshev bound (or `PETSC_DECIDE`)
186 . c   - multiple of min eigenvalue estimate to use for max Chebyshev bound (or `PETSC_DECIDE`)
187 - d   - multiple of max eigenvalue estimate to use for max Chebyshev bound (or `PETSC_DECIDE`)
188 
189   Options Database Key:
190 . -ksp_chebyshev_esteig a,b,c,d - estimate eigenvalues using a Krylov method, then use this transform for Chebyshev eigenvalue bounds
191 
192   Notes:
193   The Chebyshev bounds are set using
194 .vb
195    minbound = a*minest + b*maxest
196    maxbound = c*minest + d*maxest
197 .ve
198   The default configuration targets the upper part of the spectrum for use as a multigrid smoother, so only the maximum eigenvalue estimate is used.
199   The minimum eigenvalue estimate obtained by Krylov iteration is typically not accurate until the method has converged.
200 
201   If 0.0 is passed for all transform arguments (a,b,c,d), eigenvalue estimation is disabled.
202 
203   The default transform is (0,0.1; 0,1.1) which targets the "upper" part of the spectrum, as desirable for use with multigrid.
204 
205   The eigenvalues are estimated using the Lanczos (`KSPCG`) or Arnoldi (`KSPGMRES`) process depending on if the operator is
206   symmetric definite or not.
207 
208   Level: intermediate
209 
210 .seealso: [](ch_ksp), `KSPCHEBYSHEV`, `KSPChebyshevEstEigSetUseNoisy()`, `KSPChebyshevEstEigGetKSP()`
211 @*/
KSPChebyshevEstEigSet(KSP ksp,PetscReal a,PetscReal b,PetscReal c,PetscReal d)212 PetscErrorCode KSPChebyshevEstEigSet(KSP ksp, PetscReal a, PetscReal b, PetscReal c, PetscReal d)
213 {
214   PetscFunctionBegin;
215   PetscValidHeaderSpecific(ksp, KSP_CLASSID, 1);
216   PetscValidLogicalCollectiveReal(ksp, a, 2);
217   PetscValidLogicalCollectiveReal(ksp, b, 3);
218   PetscValidLogicalCollectiveReal(ksp, c, 4);
219   PetscValidLogicalCollectiveReal(ksp, d, 5);
220   PetscTryMethod(ksp, "KSPChebyshevEstEigSet_C", (KSP, PetscReal, PetscReal, PetscReal, PetscReal), (ksp, a, b, c, d));
221   PetscFunctionReturn(PETSC_SUCCESS);
222 }
223 
224 /*@
225   KSPChebyshevEstEigSetUseNoisy - use a noisy random number generated right-hand side to estimate the extreme eigenvalues instead of the given right-hand side
226 
227   Logically Collective
228 
229   Input Parameters:
230 + ksp - linear solver context
231 - use - `PETSC_TRUE` to use noisy
232 
233   Options Database Key:
234 . -ksp_chebyshev_esteig_noisy <true,false> - Use noisy right-hand side for estimate
235 
236   Level: intermediate
237 
238   Note:
239   This allegedly works better for multigrid smoothers
240 
241 .seealso: [](ch_ksp), `KSPCHEBYSHEV`, `KSPChebyshevEstEigSet()`, `KSPChebyshevEstEigGetKSP()`
242 @*/
KSPChebyshevEstEigSetUseNoisy(KSP ksp,PetscBool use)243 PetscErrorCode KSPChebyshevEstEigSetUseNoisy(KSP ksp, PetscBool use)
244 {
245   PetscFunctionBegin;
246   PetscValidHeaderSpecific(ksp, KSP_CLASSID, 1);
247   PetscValidLogicalCollectiveBool(ksp, use, 2);
248   PetscTryMethod(ksp, "KSPChebyshevEstEigSetUseNoisy_C", (KSP, PetscBool), (ksp, use));
249   PetscFunctionReturn(PETSC_SUCCESS);
250 }
251 
252 /*@
253   KSPChebyshevEstEigGetKSP - Get the Krylov method context used to estimate the eigenvalues for the Chebyshev method.
254 
255   Input Parameter:
256 . ksp - the Krylov space context
257 
258   Output Parameter:
259 . kspest - the eigenvalue estimation Krylov space context
260 
261   Level: advanced
262 
263   Notes:
264   If a Krylov method is not being used for this purpose, `NULL` is returned.  The reference count of the returned `KSP` is
265   not incremented: it should not be destroyed by the user.
266 
267 .seealso: [](ch_ksp), `KSPCHEBYSHEV`, `KSPChebyshevEstEigSet()`
268 @*/
KSPChebyshevEstEigGetKSP(KSP ksp,KSP * kspest)269 PetscErrorCode KSPChebyshevEstEigGetKSP(KSP ksp, KSP *kspest)
270 {
271   PetscFunctionBegin;
272   PetscValidHeaderSpecific(ksp, KSP_CLASSID, 1);
273   PetscAssertPointer(kspest, 2);
274   *kspest = NULL;
275   PetscTryMethod(ksp, "KSPChebyshevEstEigGetKSP_C", (KSP, KSP *), (ksp, kspest));
276   PetscFunctionReturn(PETSC_SUCCESS);
277 }
278 
279 /*@
280   KSPChebyshevSetKind - set the kind of Chebyshev polynomial to use
281 
282   Logically Collective
283 
284   Input Parameters:
285 + ksp  - Linear solver context
286 - kind - The kind of Chebyshev polynomial to use, see `KSPChebyshevKind`, one of `KSP_CHEBYSHEV_FIRST`, `KSP_CHEBYSHEV_FOURTH`, or `KSP_CHEBYSHEV_OPT_FOURTH`
287 
288   Options Database Key:
289 . -ksp_chebyshev_kind <kind> - which kind of Chebyshev polynomial to use
290 
291   Level: intermediate
292 
293   Note:
294   When using multigrid methods for problems with a poor quality coarse space (e.g., due to anisotropy or aggressive
295   coarsening), it is necessary for the smoother to handle smaller eigenvalues. With first-kind Chebyshev smoothing, this
296   requires using higher degree Chebyhev polynomials and reducing the lower end of the target spectrum, at which point
297   the whole target spectrum experiences about the same damping. Fourth kind Chebyshev polynomials (and the "optimized"
298   fourth kind) avoid the ad-hoc choice of lower bound and extend smoothing to smaller eigenvalues while preferentially
299   smoothing higher modes faster as needed to minimize the energy norm of the error. {cite}`phillips2022optimal`, {cite}`lottes2023optimal`
300 
301 .seealso: [](ch_ksp), `KSPCHEBYSHEV`, `KSPChebyshevKind`, `KSPChebyshevGetKind()`, `KSP_CHEBYSHEV_FIRST`, `KSP_CHEBYSHEV_FOURTH`, `KSP_CHEBYSHEV_OPT_FOURTH`
302 @*/
KSPChebyshevSetKind(KSP ksp,KSPChebyshevKind kind)303 PetscErrorCode KSPChebyshevSetKind(KSP ksp, KSPChebyshevKind kind)
304 {
305   PetscFunctionBegin;
306   PetscValidHeaderSpecific(ksp, KSP_CLASSID, 1);
307   PetscValidLogicalCollectiveEnum(ksp, kind, 2);
308   PetscUseMethod(ksp, "KSPChebyshevSetKind_C", (KSP, KSPChebyshevKind), (ksp, kind));
309   PetscFunctionReturn(PETSC_SUCCESS);
310 }
311 
312 /*@
313   KSPChebyshevGetKind - get the kind of Chebyshev polynomial to use
314 
315   Logically Collective
316 
317   Input Parameters:
318 + ksp  - Linear solver context
319 - kind - The kind of Chebyshev polynomial used
320 
321   Level: intermediate
322 
323 .seealso: [](ch_ksp), `KSPCHEBYSHEV`, `KSPChebyshevKind`, `KSPChebyshevSetKind()`, `KSP_CHEBYSHEV_FIRST`, `KSP_CHEBYSHEV_FOURTH`, `KSP_CHEBYSHEV_OPT_FOURTH`
324 @*/
KSPChebyshevGetKind(KSP ksp,KSPChebyshevKind * kind)325 PetscErrorCode KSPChebyshevGetKind(KSP ksp, KSPChebyshevKind *kind)
326 {
327   PetscFunctionBegin;
328   PetscValidHeaderSpecific(ksp, KSP_CLASSID, 1);
329   PetscUseMethod(ksp, "KSPChebyshevGetKind_C", (KSP, KSPChebyshevKind *), (ksp, kind));
330   PetscFunctionReturn(PETSC_SUCCESS);
331 }
332 
KSPChebyshevEstEigGetKSP_Chebyshev(KSP ksp,KSP * kspest)333 static PetscErrorCode KSPChebyshevEstEigGetKSP_Chebyshev(KSP ksp, KSP *kspest)
334 {
335   KSP_Chebyshev *cheb = (KSP_Chebyshev *)ksp->data;
336 
337   PetscFunctionBegin;
338   *kspest = cheb->kspest;
339   PetscFunctionReturn(PETSC_SUCCESS);
340 }
341 
KSPSetFromOptions_Chebyshev(KSP ksp,PetscOptionItems PetscOptionsObject)342 static PetscErrorCode KSPSetFromOptions_Chebyshev(KSP ksp, PetscOptionItems PetscOptionsObject)
343 {
344   KSP_Chebyshev *cheb    = (KSP_Chebyshev *)ksp->data;
345   PetscInt       neigarg = 2, nestarg = 4;
346   PetscReal      eminmax[2] = {0., 0.};
347   PetscReal      tform[4]   = {PETSC_DECIDE, PETSC_DECIDE, PETSC_DECIDE, PETSC_DECIDE};
348   PetscBool      flgeig, flgest;
349 
350   PetscFunctionBegin;
351   PetscOptionsHeadBegin(PetscOptionsObject, "KSP Chebyshev Options");
352   PetscCall(PetscOptionsInt("-ksp_chebyshev_esteig_steps", "Number of est steps in Chebyshev", "", cheb->eststeps, &cheb->eststeps, NULL));
353   PetscCall(PetscOptionsRealArray("-ksp_chebyshev_eigenvalues", "extreme eigenvalues", "KSPChebyshevSetEigenvalues", eminmax, &neigarg, &flgeig));
354   if (flgeig) {
355     PetscCheck(neigarg == 2, PetscObjectComm((PetscObject)ksp), PETSC_ERR_ARG_INCOMP, "-ksp_chebyshev_eigenvalues: must specify 2 parameters, min and max eigenvalues");
356     PetscCall(KSPChebyshevSetEigenvalues(ksp, eminmax[1], eminmax[0]));
357   }
358   PetscCall(PetscOptionsRealArray("-ksp_chebyshev_esteig", "estimate eigenvalues using a Krylov method, then use this transform for Chebyshev eigenvalue bounds", "KSPChebyshevEstEigSet", tform, &nestarg, &flgest));
359   if (flgest) {
360     switch (nestarg) {
361     case 0:
362       PetscCall(KSPChebyshevEstEigSet(ksp, PETSC_DECIDE, PETSC_DECIDE, PETSC_DECIDE, PETSC_DECIDE));
363       break;
364     case 2: /* Base everything on the max eigenvalues */
365       PetscCall(KSPChebyshevEstEigSet(ksp, PETSC_DECIDE, tform[0], PETSC_DECIDE, tform[1]));
366       break;
367     case 4: /* Use the full 2x2 linear transformation */
368       PetscCall(KSPChebyshevEstEigSet(ksp, tform[0], tform[1], tform[2], tform[3]));
369       break;
370     default:
371       SETERRQ(PetscObjectComm((PetscObject)ksp), PETSC_ERR_ARG_INCOMP, "Must specify either 0, 2, or 4 parameters for eigenvalue estimation");
372     }
373   }
374 
375   cheb->chebykind = KSP_CHEBYSHEV_FIRST; /* Default to 1st-kind Chebyshev polynomial */
376   PetscCall(PetscOptionsEnum("-ksp_chebyshev_kind", "Type of Chebyshev polynomial", "KSPChebyshevKind", KSPChebyshevKinds, (PetscEnum)cheb->chebykind, (PetscEnum *)&cheb->chebykind, NULL));
377 
378   /* We need to estimate eigenvalues; need to set this here so that KSPSetFromOptions() is called on the estimator */
379   if ((cheb->emin == 0. || cheb->emax == 0.) && !cheb->kspest) PetscCall(KSPChebyshevEstEigSet(ksp, PETSC_DECIDE, PETSC_DECIDE, PETSC_DECIDE, PETSC_DECIDE));
380 
381   if (cheb->kspest) {
382     PetscCall(PetscOptionsBool("-ksp_chebyshev_esteig_noisy", "Use noisy random number generated right-hand side for estimate", "KSPChebyshevEstEigSetUseNoisy", cheb->usenoisy, &cheb->usenoisy, NULL));
383     PetscCall(KSPSetFromOptions(cheb->kspest));
384   }
385   PetscOptionsHeadEnd();
386   PetscFunctionReturn(PETSC_SUCCESS);
387 }
388 
KSPSolve_Chebyshev_FirstKind(KSP ksp)389 static PetscErrorCode KSPSolve_Chebyshev_FirstKind(KSP ksp)
390 {
391   PetscInt    k, kp1, km1, ktmp, i;
392   PetscScalar alpha, omegaprod, mu, omega, Gamma, c[3], scale;
393   PetscReal   rnorm = 0.0, emax, emin;
394   Vec         sol_orig, b, p[3], r;
395   Mat         Amat, Pmat;
396   PetscBool   diagonalscale;
397 
398   PetscFunctionBegin;
399   PetscCall(PCGetDiagonalScale(ksp->pc, &diagonalscale));
400   PetscCheck(!diagonalscale, PetscObjectComm((PetscObject)ksp), PETSC_ERR_SUP, "Krylov method %s does not support diagonal scaling", ((PetscObject)ksp)->type_name);
401 
402   PetscCall(PCGetOperators(ksp->pc, &Amat, &Pmat));
403   PetscCall(PetscObjectSAWsTakeAccess((PetscObject)ksp));
404   ksp->its = 0;
405   PetscCall(PetscObjectSAWsGrantAccess((PetscObject)ksp));
406   /* These three point to the three active solutions, we
407      rotate these three at each solution update */
408   km1      = 0;
409   k        = 1;
410   kp1      = 2;
411   sol_orig = ksp->vec_sol; /* ksp->vec_sol will be assigned to rotating vector p[k], thus save its address */
412   b        = ksp->vec_rhs;
413   p[km1]   = sol_orig;
414   p[k]     = ksp->work[0];
415   p[kp1]   = ksp->work[1];
416   r        = ksp->work[2];
417 
418   PetscCall(KSPChebyshevGetEigenvalues_Chebyshev(ksp, &emax, &emin));
419   /* use scale*B as our preconditioner */
420   scale = 2.0 / (emax + emin);
421 
422   /*   -alpha <=  scale*lambda(B^{-1}A) <= alpha   */
423   alpha     = 1.0 - scale * emin;
424   Gamma     = 1.0;
425   mu        = 1.0 / alpha;
426   omegaprod = 2.0 / alpha;
427 
428   c[km1] = 1.0;
429   c[k]   = mu;
430 
431   if (!ksp->guess_zero) {
432     PetscCall(KSP_MatMult(ksp, Amat, sol_orig, r)); /*  r = b - A*p[km1] */
433     PetscCall(VecAYPX(r, -1.0, b));
434   } else {
435     PetscCall(VecCopy(b, r));
436   }
437 
438   /* calculate residual norm if requested, we have done one iteration */
439   if (ksp->normtype) {
440     switch (ksp->normtype) {
441     case KSP_NORM_PRECONDITIONED:
442       PetscCall(KSP_PCApply(ksp, r, p[k])); /* p[k] = B^{-1}r */
443       PetscCall(VecNorm(p[k], NORM_2, &rnorm));
444       break;
445     case KSP_NORM_UNPRECONDITIONED:
446     case KSP_NORM_NATURAL:
447       PetscCall(VecNorm(r, NORM_2, &rnorm));
448       break;
449     default:
450       SETERRQ(PetscObjectComm((PetscObject)ksp), PETSC_ERR_SUP, "%s", KSPNormTypes[ksp->normtype]);
451     }
452     PetscCall(PetscObjectSAWsTakeAccess((PetscObject)ksp));
453     ksp->rnorm = rnorm;
454     PetscCall(PetscObjectSAWsGrantAccess((PetscObject)ksp));
455     PetscCall(KSPLogResidualHistory(ksp, rnorm));
456     PetscCall(KSPLogErrorHistory(ksp));
457     PetscCall(KSPMonitor(ksp, 0, rnorm));
458     PetscCall((*ksp->converged)(ksp, 0, rnorm, &ksp->reason, ksp->cnvP));
459   } else ksp->reason = KSP_CONVERGED_ITERATING;
460   if (ksp->reason || ksp->max_it == 0) {
461     if (ksp->max_it == 0) ksp->reason = KSP_DIVERGED_ITS; /* This for a V(0,x) cycle */
462     PetscFunctionReturn(PETSC_SUCCESS);
463   }
464   if (ksp->normtype != KSP_NORM_PRECONDITIONED) PetscCall(KSP_PCApply(ksp, r, p[k])); /* p[k] = B^{-1}r */
465   PetscCall(VecAYPX(p[k], scale, p[km1]));                                            /* p[k] = scale B^{-1}r + p[km1] */
466   PetscCall(PetscObjectSAWsTakeAccess((PetscObject)ksp));
467   ksp->its = 1;
468   PetscCall(PetscObjectSAWsGrantAccess((PetscObject)ksp));
469 
470   for (i = 1; i < ksp->max_it; i++) {
471     PetscCall(PetscObjectSAWsTakeAccess((PetscObject)ksp));
472     ksp->its++;
473     PetscCall(PetscObjectSAWsGrantAccess((PetscObject)ksp));
474 
475     PetscCall(KSP_MatMult(ksp, Amat, p[k], r)); /*  r = b - Ap[k]    */
476     PetscCall(VecAYPX(r, -1.0, b));
477     /* calculate residual norm if requested */
478     if (ksp->normtype) {
479       switch (ksp->normtype) {
480       case KSP_NORM_PRECONDITIONED:
481         PetscCall(KSP_PCApply(ksp, r, p[kp1])); /*  p[kp1] = B^{-1}r  */
482         PetscCall(VecNorm(p[kp1], NORM_2, &rnorm));
483         break;
484       case KSP_NORM_UNPRECONDITIONED:
485       case KSP_NORM_NATURAL:
486         PetscCall(VecNorm(r, NORM_2, &rnorm));
487         break;
488       default:
489         rnorm = 0.0;
490         break;
491       }
492       KSPCheckNorm(ksp, rnorm);
493       PetscCall(PetscObjectSAWsTakeAccess((PetscObject)ksp));
494       ksp->rnorm = rnorm;
495       PetscCall(PetscObjectSAWsGrantAccess((PetscObject)ksp));
496       PetscCall(KSPLogResidualHistory(ksp, rnorm));
497       PetscCall(KSPMonitor(ksp, i, rnorm));
498       PetscCall((*ksp->converged)(ksp, i, rnorm, &ksp->reason, ksp->cnvP));
499       if (ksp->reason) break;
500       if (ksp->normtype != KSP_NORM_PRECONDITIONED) PetscCall(KSP_PCApply(ksp, r, p[kp1])); /*  p[kp1] = B^{-1}r  */
501     } else {
502       PetscCall(KSP_PCApply(ksp, r, p[kp1])); /*  p[kp1] = B^{-1}r  */
503     }
504     ksp->vec_sol = p[k];
505     PetscCall(KSPLogErrorHistory(ksp));
506 
507     c[kp1] = 2.0 * mu * c[k] - c[km1];
508     omega  = omegaprod * c[k] / c[kp1];
509 
510     /* y^{k+1} = omega(y^{k} - y^{k-1} + Gamma*r^{k}) + y^{k-1} */
511     PetscCall(VecAXPBYPCZ(p[kp1], 1.0 - omega, omega, omega * Gamma * scale, p[km1], p[k]));
512 
513     ktmp = km1;
514     km1  = k;
515     k    = kp1;
516     kp1  = ktmp;
517   }
518   if (!ksp->reason) {
519     if (ksp->normtype) {
520       PetscCall(KSP_MatMult(ksp, Amat, p[k], r)); /*  r = b - Ap[k]    */
521       PetscCall(VecAYPX(r, -1.0, b));
522       switch (ksp->normtype) {
523       case KSP_NORM_PRECONDITIONED:
524         PetscCall(KSP_PCApply(ksp, r, p[kp1])); /* p[kp1] = B^{-1}r */
525         PetscCall(VecNorm(p[kp1], NORM_2, &rnorm));
526         break;
527       case KSP_NORM_UNPRECONDITIONED:
528       case KSP_NORM_NATURAL:
529         PetscCall(VecNorm(r, NORM_2, &rnorm));
530         break;
531       default:
532         rnorm = 0.0;
533         break;
534       }
535       KSPCheckNorm(ksp, rnorm);
536       PetscCall(PetscObjectSAWsTakeAccess((PetscObject)ksp));
537       ksp->rnorm = rnorm;
538       PetscCall(PetscObjectSAWsGrantAccess((PetscObject)ksp));
539       PetscCall(KSPLogResidualHistory(ksp, rnorm));
540       PetscCall(KSPMonitor(ksp, i, rnorm));
541     }
542     if (ksp->its >= ksp->max_it) {
543       if (ksp->normtype != KSP_NORM_NONE) {
544         PetscCall((*ksp->converged)(ksp, i, rnorm, &ksp->reason, ksp->cnvP));
545         if (!ksp->reason) ksp->reason = KSP_DIVERGED_ITS;
546       } else ksp->reason = KSP_CONVERGED_ITS;
547     }
548   }
549 
550   /* make sure solution is in vector x */
551   ksp->vec_sol = sol_orig;
552   if (k) PetscCall(VecCopy(p[k], sol_orig));
553   if (ksp->reason == KSP_CONVERGED_ITS) PetscCall(KSPLogErrorHistory(ksp));
554   PetscFunctionReturn(PETSC_SUCCESS);
555 }
556 
KSPSolve_Chebyshev_FourthKind(KSP ksp)557 static PetscErrorCode KSPSolve_Chebyshev_FourthKind(KSP ksp)
558 {
559   KSP_Chebyshev *cheb = (KSP_Chebyshev *)ksp->data;
560   PetscInt       i;
561   PetscScalar    scale, rScale, dScale;
562   PetscReal      rnorm = 0.0, emax, emin;
563   Vec            x, b, d, r, Br;
564   Mat            Amat, Pmat;
565   PetscBool      diagonalscale;
566   PetscReal     *betas = cheb->betas;
567 
568   PetscFunctionBegin;
569   PetscCall(PCGetDiagonalScale(ksp->pc, &diagonalscale));
570   PetscCheck(!diagonalscale, PetscObjectComm((PetscObject)ksp), PETSC_ERR_SUP, "Krylov method %s does not support diagonal scaling", ((PetscObject)ksp)->type_name);
571 
572   PetscCall(PCGetOperators(ksp->pc, &Amat, &Pmat));
573   PetscCall(PetscObjectSAWsTakeAccess((PetscObject)ksp));
574   ksp->its = 0;
575   PetscCall(PetscObjectSAWsGrantAccess((PetscObject)ksp));
576 
577   x  = ksp->vec_sol;
578   b  = ksp->vec_rhs;
579   r  = ksp->work[0];
580   d  = ksp->work[1];
581   Br = ksp->work[2];
582 
583   PetscCall(KSPChebyshevGetEigenvalues_Chebyshev(ksp, &emax, &emin));
584   /* use scale*B as our preconditioner */
585   scale = 1.0 / emax;
586 
587   if (!ksp->guess_zero) {
588     PetscCall(KSP_MatMult(ksp, Amat, x, r)); /*  r = b - A*x */
589     PetscCall(VecAYPX(r, -1.0, b));
590   } else {
591     PetscCall(VecCopy(b, r));
592   }
593 
594   /* calculate residual norm if requested, we have done one iteration */
595   if (ksp->normtype) {
596     switch (ksp->normtype) {
597     case KSP_NORM_PRECONDITIONED:
598       PetscCall(KSP_PCApply(ksp, r, Br)); /* Br = B^{-1}r */
599       PetscCall(VecNorm(Br, NORM_2, &rnorm));
600       break;
601     case KSP_NORM_UNPRECONDITIONED:
602     case KSP_NORM_NATURAL:
603       PetscCall(VecNorm(r, NORM_2, &rnorm));
604       break;
605     default:
606       SETERRQ(PetscObjectComm((PetscObject)ksp), PETSC_ERR_SUP, "%s", KSPNormTypes[ksp->normtype]);
607     }
608     PetscCall(PetscObjectSAWsTakeAccess((PetscObject)ksp));
609     ksp->rnorm = rnorm;
610     PetscCall(PetscObjectSAWsGrantAccess((PetscObject)ksp));
611     PetscCall(KSPLogResidualHistory(ksp, rnorm));
612     PetscCall(KSPLogErrorHistory(ksp));
613     PetscCall(KSPMonitor(ksp, 0, rnorm));
614     PetscCall((*ksp->converged)(ksp, 0, rnorm, &ksp->reason, ksp->cnvP));
615   } else ksp->reason = KSP_CONVERGED_ITERATING;
616   if (ksp->reason || ksp->max_it == 0) {
617     if (ksp->max_it == 0) ksp->reason = KSP_DIVERGED_ITS; /* This for a V(0,x) cycle */
618     PetscFunctionReturn(PETSC_SUCCESS);
619   }
620   if (ksp->normtype != KSP_NORM_PRECONDITIONED) PetscCall(KSP_PCApply(ksp, r, Br)); /* Br = B^{-1}r */
621   PetscCall(VecAXPBY(d, 4.0 / 3.0 * scale, 0.0, Br));                               /* d = 4/3 * scale B^{-1}r */
622   PetscCall(PetscObjectSAWsTakeAccess((PetscObject)ksp));
623   ksp->its = 1;
624   PetscCall(PetscObjectSAWsGrantAccess((PetscObject)ksp));
625 
626   for (i = 1; i < ksp->max_it; i++) {
627     PetscCall(PetscObjectSAWsTakeAccess((PetscObject)ksp));
628     ksp->its++;
629     PetscCall(PetscObjectSAWsGrantAccess((PetscObject)ksp));
630 
631     PetscCall(VecAXPBY(x, betas[i - 1], 1.0, d)); /* x = x + \beta_k d */
632 
633     PetscCall(KSP_MatMult(ksp, Amat, d, Br)); /*  r = r - Ad */
634     PetscCall(VecAXPBY(r, -1.0, 1.0, Br));
635 
636     /* calculate residual norm if requested */
637     if (ksp->normtype) {
638       switch (ksp->normtype) {
639       case KSP_NORM_PRECONDITIONED:
640         PetscCall(KSP_PCApply(ksp, r, Br)); /*  Br = B^{-1}r  */
641         PetscCall(VecNorm(Br, NORM_2, &rnorm));
642         break;
643       case KSP_NORM_UNPRECONDITIONED:
644       case KSP_NORM_NATURAL:
645         PetscCall(VecNorm(r, NORM_2, &rnorm));
646         break;
647       default:
648         rnorm = 0.0;
649         break;
650       }
651       KSPCheckNorm(ksp, rnorm);
652       PetscCall(PetscObjectSAWsTakeAccess((PetscObject)ksp));
653       ksp->rnorm = rnorm;
654       PetscCall(PetscObjectSAWsGrantAccess((PetscObject)ksp));
655       PetscCall(KSPLogResidualHistory(ksp, rnorm));
656       PetscCall(KSPMonitor(ksp, i, rnorm));
657       PetscCall((*ksp->converged)(ksp, i, rnorm, &ksp->reason, ksp->cnvP));
658       if (ksp->reason) break;
659       if (ksp->normtype != KSP_NORM_PRECONDITIONED) PetscCall(KSP_PCApply(ksp, r, Br)); /*  Br = B^{-1}r  */
660     } else {
661       PetscCall(KSP_PCApply(ksp, r, Br)); /*  Br = B^{-1}r  */
662     }
663     PetscCall(KSPLogErrorHistory(ksp));
664 
665     rScale = scale * (8.0 * i + 4.0) / (2.0 * i + 3.0);
666     dScale = (2.0 * i - 1.0) / (2.0 * i + 3.0);
667 
668     /* d_k+1 = \dfrac{2k-1}{2k+3} d_k + \dfrac{8k+4}{2k+3} \dfrac{1}{\rho(SA)} Br */
669     PetscCall(VecAXPBY(d, rScale, dScale, Br));
670   }
671 
672   /* on last pass, update solution vector */
673   PetscCall(VecAXPBY(x, betas[ksp->max_it - 1], 1.0, d)); /* x = x + d */
674 
675   if (!ksp->reason) {
676     if (ksp->normtype) {
677       PetscCall(KSP_MatMult(ksp, Amat, x, r)); /*  r = b - Ax    */
678       PetscCall(VecAYPX(r, -1.0, b));
679       switch (ksp->normtype) {
680       case KSP_NORM_PRECONDITIONED:
681         PetscCall(KSP_PCApply(ksp, r, Br)); /* Br= B^{-1}r */
682         PetscCall(VecNorm(Br, NORM_2, &rnorm));
683         break;
684       case KSP_NORM_UNPRECONDITIONED:
685       case KSP_NORM_NATURAL:
686         PetscCall(VecNorm(r, NORM_2, &rnorm));
687         break;
688       default:
689         rnorm = 0.0;
690         break;
691       }
692       KSPCheckNorm(ksp, rnorm);
693       PetscCall(PetscObjectSAWsTakeAccess((PetscObject)ksp));
694       ksp->rnorm = rnorm;
695       PetscCall(PetscObjectSAWsGrantAccess((PetscObject)ksp));
696       PetscCall(KSPLogResidualHistory(ksp, rnorm));
697       PetscCall(KSPMonitor(ksp, i, rnorm));
698     }
699     if (ksp->its >= ksp->max_it) {
700       if (ksp->normtype != KSP_NORM_NONE) {
701         PetscCall((*ksp->converged)(ksp, i, rnorm, &ksp->reason, ksp->cnvP));
702         if (!ksp->reason) ksp->reason = KSP_DIVERGED_ITS;
703       } else ksp->reason = KSP_CONVERGED_ITS;
704     }
705   }
706 
707   if (ksp->reason == KSP_CONVERGED_ITS) PetscCall(KSPLogErrorHistory(ksp));
708   PetscFunctionReturn(PETSC_SUCCESS);
709 }
710 
KSPView_Chebyshev(KSP ksp,PetscViewer viewer)711 static PetscErrorCode KSPView_Chebyshev(KSP ksp, PetscViewer viewer)
712 {
713   KSP_Chebyshev *cheb = (KSP_Chebyshev *)ksp->data;
714   PetscBool      isascii;
715 
716   PetscFunctionBegin;
717   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &isascii));
718   if (isascii) {
719     switch (cheb->chebykind) {
720     case KSP_CHEBYSHEV_FIRST:
721       PetscCall(PetscViewerASCIIPrintf(viewer, "  Chebyshev polynomial of first kind\n"));
722       break;
723     case KSP_CHEBYSHEV_FOURTH:
724       PetscCall(PetscViewerASCIIPrintf(viewer, "  Chebyshev polynomial of fourth kind\n"));
725       break;
726     case KSP_CHEBYSHEV_OPT_FOURTH:
727       PetscCall(PetscViewerASCIIPrintf(viewer, "  Chebyshev polynomial of opt. fourth kind\n"));
728       break;
729     }
730     PetscReal emax, emin;
731     PetscCall(KSPChebyshevGetEigenvalues_Chebyshev(ksp, &emax, &emin));
732     PetscCall(PetscViewerASCIIPrintf(viewer, "  eigenvalue targets used: min %g, max %g\n", (double)emin, (double)emax));
733     if (cheb->kspest) {
734       PetscCall(PetscViewerASCIIPrintf(viewer, "  eigenvalues estimated via %s: min %g, max %g\n", ((PetscObject)cheb->kspest)->type_name, (double)cheb->emin_computed, (double)cheb->emax_computed));
735       PetscCall(PetscViewerASCIIPrintf(viewer, "  eigenvalues estimated using %s with transform: [%g %g; %g %g]\n", ((PetscObject)cheb->kspest)->type_name, (double)cheb->tform[0], (double)cheb->tform[1], (double)cheb->tform[2], (double)cheb->tform[3]));
736       PetscCall(PetscViewerASCIIPushTab(viewer));
737       PetscCall(KSPView(cheb->kspest, viewer));
738       PetscCall(PetscViewerASCIIPopTab(viewer));
739       if (cheb->usenoisy) PetscCall(PetscViewerASCIIPrintf(viewer, "  estimating eigenvalues using a noisy random number generated right-hand side\n"));
740     } else if (cheb->emax_provided != 0.) {
741       PetscCall(PetscViewerASCIIPrintf(viewer, "  eigenvalues provided (min %g, max %g) with transform: [%g %g; %g %g]\n", (double)cheb->emin_provided, (double)cheb->emax_provided, (double)cheb->tform[0], (double)cheb->tform[1], (double)cheb->tform[2],
742                                        (double)cheb->tform[3]));
743     }
744   }
745   PetscFunctionReturn(PETSC_SUCCESS);
746 }
747 
KSPSetUp_Chebyshev(KSP ksp)748 static PetscErrorCode KSPSetUp_Chebyshev(KSP ksp)
749 {
750   KSP_Chebyshev   *cheb = (KSP_Chebyshev *)ksp->data;
751   PetscBool        isset, flg;
752   Mat              Pmat, Amat;
753   PetscObjectId    amatid, pmatid;
754   PetscObjectState amatstate, pmatstate;
755 
756   PetscFunctionBegin;
757   switch (cheb->chebykind) {
758   case KSP_CHEBYSHEV_FIRST:
759     ksp->ops->solve = KSPSolve_Chebyshev_FirstKind;
760     break;
761   case KSP_CHEBYSHEV_FOURTH:
762   case KSP_CHEBYSHEV_OPT_FOURTH:
763     ksp->ops->solve = KSPSolve_Chebyshev_FourthKind;
764     break;
765   }
766 
767   if (ksp->max_it > cheb->num_betas_alloc) {
768     PetscCall(PetscFree(cheb->betas));
769     PetscCall(PetscMalloc1(ksp->max_it, &cheb->betas));
770     cheb->num_betas_alloc = ksp->max_it;
771   }
772 
773   // coefficients for 4th-kind Chebyshev
774   for (PetscInt i = 0; i < ksp->max_it; i++) cheb->betas[i] = 1.0;
775 
776   // coefficients for optimized 4th-kind Chebyshev
777   if (cheb->chebykind == KSP_CHEBYSHEV_OPT_FOURTH) PetscCall(KSPChebyshevGetBetas_Private(ksp));
778 
779   PetscCall(KSPSetWorkVecs(ksp, 3));
780   if (cheb->emin == 0. || cheb->emax == 0.) { // User did not specify eigenvalues
781     PC pc;
782 
783     PetscCall(KSPGetPC(ksp, &pc));
784     PetscCall(PetscObjectTypeCompare((PetscObject)pc, PCJACOBI, &flg));
785     if (!flg) { // Provided estimates are only relevant for Jacobi
786       cheb->emax_provided = 0;
787       cheb->emin_provided = 0;
788     }
789     /* We need to estimate eigenvalues */
790     if (!cheb->kspest) PetscCall(KSPChebyshevEstEigSet(ksp, PETSC_DECIDE, PETSC_DECIDE, PETSC_DECIDE, PETSC_DECIDE));
791   }
792   if (cheb->kspest) {
793     PetscCall(KSPGetOperators(ksp, &Amat, &Pmat));
794     PetscCall(MatIsSPDKnown(Pmat, &isset, &flg));
795     if (isset && flg) {
796       const char *prefix;
797 
798       PetscCall(KSPGetOptionsPrefix(cheb->kspest, &prefix));
799       PetscCall(PetscOptionsHasName(NULL, prefix, "-ksp_type", &flg));
800       if (!flg) PetscCall(KSPSetType(cheb->kspest, KSPCG));
801     }
802     PetscCall(PetscObjectGetId((PetscObject)Amat, &amatid));
803     PetscCall(PetscObjectGetId((PetscObject)Pmat, &pmatid));
804     PetscCall(PetscObjectStateGet((PetscObject)Amat, &amatstate));
805     PetscCall(PetscObjectStateGet((PetscObject)Pmat, &pmatstate));
806     if (amatid != cheb->amatid || pmatid != cheb->pmatid || amatstate != cheb->amatstate || pmatstate != cheb->pmatstate) {
807       PetscReal          max = 0.0, min = 0.0;
808       Vec                B;
809       KSPConvergedReason reason;
810 
811       PetscCall(KSPSetPC(cheb->kspest, ksp->pc));
812       if (cheb->usenoisy) {
813         B = ksp->work[1];
814         PetscCall(KSPSetNoisy_Private(Amat, B));
815       } else {
816         PetscBool change;
817 
818         PetscCheck(ksp->vec_rhs, PetscObjectComm((PetscObject)ksp), PETSC_ERR_SUP, "Chebyshev must use a noisy random number generated right-hand side to estimate the eigenvalues when no right-hand side is available");
819         PetscCall(PCPreSolveChangeRHS(ksp->pc, &change));
820         if (change) {
821           B = ksp->work[1];
822           PetscCall(VecCopy(ksp->vec_rhs, B));
823         } else B = ksp->vec_rhs;
824       }
825       if (ksp->setfromoptionscalled && !cheb->kspest->setfromoptionscalled) PetscCall(KSPSetFromOptions(cheb->kspest));
826       PetscCall(KSPSolve(cheb->kspest, B, ksp->work[0]));
827       PetscCall(KSPGetConvergedReason(cheb->kspest, &reason));
828       if (reason == KSP_DIVERGED_ITS) {
829         PetscCall(PetscInfo(ksp, "Eigen estimator ran for prescribed number of iterations\n"));
830       } else if (reason == KSP_DIVERGED_PC_FAILED) {
831         PetscInt       its;
832         PCFailedReason pcreason;
833 
834         PetscCall(KSPGetIterationNumber(cheb->kspest, &its));
835         if (ksp->normtype == KSP_NORM_NONE) PetscCall(PCReduceFailedReason(ksp->pc));
836         PetscCall(PCGetFailedReason(ksp->pc, &pcreason));
837         ksp->reason = KSP_DIVERGED_PC_FAILED;
838         PetscCall(PetscInfo(ksp, "Eigen estimator failed: %s %s at iteration %" PetscInt_FMT "\n", KSPConvergedReasons[reason], PCFailedReasons[pcreason], its));
839         PetscFunctionReturn(PETSC_SUCCESS);
840       } else if (reason == KSP_CONVERGED_RTOL || reason == KSP_CONVERGED_ATOL) {
841         PetscCall(PetscInfo(ksp, "Eigen estimator converged prematurely. Should not happen except for small or low rank problem\n"));
842       } else if (reason < 0) {
843         PetscCall(PetscInfo(ksp, "Eigen estimator failed %s, using estimates anyway\n", KSPConvergedReasons[reason]));
844       }
845 
846       PetscCall(KSPChebyshevComputeExtremeEigenvalues_Private(cheb->kspest, &min, &max));
847       PetscCall(KSPSetPC(cheb->kspest, NULL));
848 
849       cheb->emin_computed = min;
850       cheb->emax_computed = max;
851 
852       cheb->amatid    = amatid;
853       cheb->pmatid    = pmatid;
854       cheb->amatstate = amatstate;
855       cheb->pmatstate = pmatstate;
856     }
857   }
858   if (ksp->monitor[0] == (PetscErrorCode (*)(KSP, PetscInt, PetscReal, void *))KSPMonitorResidual && !ksp->normtype) PetscCall(KSPSetNormType(ksp, KSP_NORM_PRECONDITIONED));
859   PetscFunctionReturn(PETSC_SUCCESS);
860 }
861 
KSPDestroy_Chebyshev(KSP ksp)862 static PetscErrorCode KSPDestroy_Chebyshev(KSP ksp)
863 {
864   KSP_Chebyshev *cheb = (KSP_Chebyshev *)ksp->data;
865 
866   PetscFunctionBegin;
867   PetscCall(PetscFree(cheb->betas));
868   PetscCall(KSPDestroy(&cheb->kspest));
869   PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPChebyshevSetEigenvalues_C", NULL));
870   PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPChebyshevEstEigSet_C", NULL));
871   PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPChebyshevEstEigSetUseNoisy_C", NULL));
872   PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPChebyshevSetKind_C", NULL));
873   PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPChebyshevGetKind_C", NULL));
874   PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPChebyshevEstEigGetKSP_C", NULL));
875   PetscCall(KSPDestroyDefault(ksp));
876   PetscFunctionReturn(PETSC_SUCCESS);
877 }
878 
879 /*MC
880      KSPCHEBYSHEV - The preconditioned Chebyshev iterative method
881 
882    Options Database Keys:
883 +   -ksp_chebyshev_eigenvalues <emin,emax> - set approximations to the smallest and largest eigenvalues
884                                              of the preconditioned operator. If these are accurate you will get much faster convergence.
885 .   -ksp_chebyshev_esteig <a,b,c,d>        - estimate eigenvalues using a Krylov method, then use this
886                                              transform for Chebyshev eigenvalue bounds (`KSPChebyshevEstEigSet()`)
887 .   -ksp_chebyshev_esteig_steps            - number of eigenvalue estimation steps
888 -   -ksp_chebyshev_esteig_noisy            - use a noisy random number generator to create right-hand side for eigenvalue estimator
889 
890    Level: beginner
891 
892    Notes:
893    The Chebyshev method requires both the matrix and preconditioner to be symmetric positive (semi) definite, but it can work
894    as a smoother in other situations
895 
896    Only has support for left preconditioning.
897 
898    Chebyshev is configured as a smoother by default, targeting the "upper" part of the spectrum.
899 
900    By default this uses `KSPGMRES` to estimate the extreme eigenvalues, if the matrix is known to be SPD then it uses `KSPCG` to estimate the eigenvalues.
901    See `MatIsSPDKnown()` for how to indicate a `Mat`, matrix is SPD.
902 
903 .seealso: [](ch_ksp), `KSPCreate()`, `KSPSetType()`, `KSPType`, `KSP`,
904           `KSPChebyshevSetEigenvalues()`, `KSPChebyshevEstEigSet()`, `KSPChebyshevEstEigSetUseNoisy()`
905           `KSPRICHARDSON`, `KSPCG`, `PCMG`
906 M*/
907 
KSPCreate_Chebyshev(KSP ksp)908 PETSC_EXTERN PetscErrorCode KSPCreate_Chebyshev(KSP ksp)
909 {
910   KSP_Chebyshev *chebyshevP;
911 
912   PetscFunctionBegin;
913   PetscCall(PetscNew(&chebyshevP));
914 
915   ksp->data = (void *)chebyshevP;
916   PetscCall(KSPSetSupportedNorm(ksp, KSP_NORM_PRECONDITIONED, PC_LEFT, 3));
917   PetscCall(KSPSetSupportedNorm(ksp, KSP_NORM_UNPRECONDITIONED, PC_LEFT, 2));
918   PetscCall(KSPSetSupportedNorm(ksp, KSP_NORM_NONE, PC_LEFT, 1));
919   PetscCall(KSPSetSupportedNorm(ksp, KSP_NORM_NONE, PC_RIGHT, 1));
920 
921   chebyshevP->emin = 0.;
922   chebyshevP->emax = 0.;
923 
924   chebyshevP->tform[0] = 0.0;
925   chebyshevP->tform[1] = 0.1;
926   chebyshevP->tform[2] = 0;
927   chebyshevP->tform[3] = 1.1;
928   chebyshevP->eststeps = 10;
929   chebyshevP->usenoisy = PETSC_TRUE;
930   ksp->setupnewmatrix  = PETSC_TRUE;
931 
932   ksp->ops->setup          = KSPSetUp_Chebyshev;
933   ksp->ops->destroy        = KSPDestroy_Chebyshev;
934   ksp->ops->buildsolution  = KSPBuildSolutionDefault;
935   ksp->ops->buildresidual  = KSPBuildResidualDefault;
936   ksp->ops->setfromoptions = KSPSetFromOptions_Chebyshev;
937   ksp->ops->view           = KSPView_Chebyshev;
938   ksp->ops->reset          = KSPReset_Chebyshev;
939 
940   PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPChebyshevSetEigenvalues_C", KSPChebyshevSetEigenvalues_Chebyshev));
941   PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPChebyshevEstEigSet_C", KSPChebyshevEstEigSet_Chebyshev));
942   PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPChebyshevEstEigSetUseNoisy_C", KSPChebyshevEstEigSetUseNoisy_Chebyshev));
943   PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPChebyshevSetKind_C", KSPChebyshevSetKind_Chebyshev));
944   PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPChebyshevGetKind_C", KSPChebyshevGetKind_Chebyshev));
945   PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPChebyshevEstEigGetKSP_C", KSPChebyshevEstEigGetKSP_Chebyshev));
946   PetscFunctionReturn(PETSC_SUCCESS);
947 }
948