Actual source code: gmres.c
1: /*
2: This file implements GMRES (a Generalized Minimal Residual) method.
3: Reference: Saad and Schultz, 1986.
5: Some comments on left vs. right preconditioning, and restarts.
6: Left and right preconditioning.
7: If right preconditioning is chosen, then the problem being solved
8: by GMRES is actually
9: My = AB^-1 y = f
10: so the initial residual is
11: r = f - M y
12: Note that B^-1 y = x or y = B x, and if x is non-zero, the initial
13: residual is
14: r = f - A x
15: The final solution is then
16: x = B^-1 y
18: If left preconditioning is chosen, then the problem being solved is
19: My = B^-1 A x = B^-1 f,
20: and the initial residual is
21: r = B^-1(f - Ax)
23: Restarts: Restarts are basically solves with x0 not equal to zero.
24: Note that we can eliminate an extra application of B^-1 between
25: restarts as long as we don't require that the solution at the end
26: of an unsuccessful gmres iteration always be the solution x.
27: */
29: #include <../src/ksp/ksp/impls/gmres/gmresimpl.h>
30: #define GMRES_DELTA_DIRECTIONS 10
31: #define GMRES_DEFAULT_MAXK 30
32: static PetscErrorCode KSPGMRESUpdateHessenberg(KSP, PetscInt, PetscBool, PetscReal *);
33: static PetscErrorCode KSPGMRESBuildSoln(PetscScalar *, Vec, Vec, KSP, PetscInt);
35: PetscErrorCode KSPSetUp_GMRES(KSP ksp)
36: {
37: PetscInt hh, hes, rs, cc;
38: PetscInt max_k, k;
39: KSP_GMRES *gmres = (KSP_GMRES *)ksp->data;
41: PetscFunctionBegin;
42: max_k = gmres->max_k; /* restart size */
43: hh = (max_k + 2) * (max_k + 1);
44: hes = (max_k + 1) * (max_k + 1);
45: rs = (max_k + 2);
46: cc = (max_k + 1);
48: PetscCall(PetscCalloc5(hh, &gmres->hh_origin, hes, &gmres->hes_origin, rs, &gmres->rs_origin, cc, &gmres->cc_origin, cc, &gmres->ss_origin));
50: if (ksp->calc_sings) {
51: /* Allocate workspace to hold Hessenberg matrix needed by LAPACK */
52: PetscCall(PetscMalloc1((max_k + 3) * (max_k + 9), &gmres->Rsvd));
53: PetscCall(PetscMalloc1(6 * (max_k + 2), &gmres->Dsvd));
54: }
56: /* Allocate array to hold pointers to user vectors. Note that we need
57: 4 + max_k + 1 (since we need it+1 vectors, and it <= max_k) */
58: gmres->vecs_allocated = VEC_OFFSET + 2 + max_k + gmres->nextra_vecs;
59: PetscCall(PetscMalloc1(gmres->vecs_allocated, &gmres->vecs));
60: PetscCall(PetscMalloc1(VEC_OFFSET + 2 + max_k, &gmres->user_work));
61: PetscCall(PetscMalloc1(VEC_OFFSET + 2 + max_k, &gmres->mwork_alloc));
62: if (gmres->q_preallocate || ksp->normtype == KSP_NORM_NONE) gmres->vv_allocated = VEC_OFFSET + 2 + PetscMin(max_k, ksp->max_it);
63: else gmres->vv_allocated = VEC_OFFSET + 2 + PetscMin(PetscMin(5, max_k), ksp->max_it);
64: PetscCall(KSPCreateVecs(ksp, gmres->vv_allocated, &gmres->user_work[0], 0, NULL));
65: gmres->mwork_alloc[0] = gmres->vv_allocated;
66: gmres->nwork_alloc = 1;
67: for (k = 0; k < gmres->vv_allocated; k++) gmres->vecs[k] = gmres->user_work[0][k];
68: PetscFunctionReturn(PETSC_SUCCESS);
69: }
71: /*
72: Run gmres, possibly with restart. Return residual history if requested.
73: input parameters:
75: . gmres - structure containing parameters and work areas
77: output parameters:
78: . nres - residuals (from preconditioned system) at each step.
79: If restarting, consider passing nres+it. If null,
80: ignored
81: . itcount - number of iterations used. nres[0] to nres[itcount]
82: are defined. If null, ignored.
84: Notes:
85: On entry, the value in vector VEC_VV(0) should be the initial residual
86: (this allows shortcuts where the initial preconditioned residual is 0).
87: */
88: static PetscErrorCode KSPGMRESCycle(PetscInt *itcount, KSP ksp)
89: {
90: KSP_GMRES *gmres = (KSP_GMRES *)ksp->data;
91: PetscReal res, hapbnd, tt;
92: PetscInt it = 0, max_k = gmres->max_k;
93: PetscBool hapend = PETSC_FALSE;
95: PetscFunctionBegin;
96: if (itcount) *itcount = 0;
97: PetscCall(VecNormalize(VEC_VV(0), &res));
98: KSPCheckNorm(ksp, res);
100: /* the constant .1 is arbitrary, just some measure at how incorrect the residuals are */
101: if ((ksp->rnorm > 0.0) && (PetscAbsReal(res - ksp->rnorm) > gmres->breakdowntol * gmres->rnorm0)) {
102: PetscCheck(!ksp->errorifnotconverged, PetscObjectComm((PetscObject)ksp), PETSC_ERR_CONV_FAILED, "Residual norm computed by GMRES recursion formula %g is far from the computed residual norm %g at restart, residual norm at start of cycle %g",
103: (double)ksp->rnorm, (double)res, (double)gmres->rnorm0);
104: PetscCall(PetscInfo(ksp, "Residual norm computed by GMRES recursion formula %g is far from the computed residual norm %g at restart, residual norm at start of cycle %g\n", (double)ksp->rnorm, (double)res, (double)gmres->rnorm0));
105: ksp->reason = KSP_DIVERGED_BREAKDOWN;
106: PetscFunctionReturn(PETSC_SUCCESS);
107: }
108: *GRS(0) = gmres->rnorm0 = res;
110: PetscCall(PetscObjectSAWsTakeAccess((PetscObject)ksp));
111: ksp->rnorm = res;
112: PetscCall(PetscObjectSAWsGrantAccess((PetscObject)ksp));
113: gmres->it = (it - 1);
114: PetscCall(KSPLogResidualHistory(ksp, res));
115: PetscCall(KSPLogErrorHistory(ksp));
116: PetscCall(KSPMonitor(ksp, ksp->its, res));
117: if (!res) {
118: ksp->reason = KSP_CONVERGED_ATOL;
119: PetscCall(PetscInfo(ksp, "Converged due to zero residual norm on entry\n"));
120: PetscFunctionReturn(PETSC_SUCCESS);
121: }
123: /* check for the convergence */
124: PetscCall((*ksp->converged)(ksp, ksp->its, res, &ksp->reason, ksp->cnvP));
125: while (!ksp->reason && it < max_k && ksp->its < ksp->max_it) {
126: if (it) {
127: PetscCall(KSPLogResidualHistory(ksp, res));
128: PetscCall(KSPLogErrorHistory(ksp));
129: PetscCall(KSPMonitor(ksp, ksp->its, res));
130: }
131: gmres->it = (it - 1);
132: if (gmres->vv_allocated <= it + VEC_OFFSET + 1) PetscCall(KSPGMRESGetNewVectors(ksp, it + 1));
133: PetscCall(KSP_PCApplyBAorAB(ksp, VEC_VV(it), VEC_VV(1 + it), VEC_TEMP_MATOP));
135: /* update Hessenberg matrix and do Gram-Schmidt */
136: PetscCall((*ksp->orthog)(ksp, &VEC_VV(0), it + 1, NULL, HH(0, it)));
137: PetscCall(PetscArraycpy(HES(0, it), HH(0, it), it + 1));
138: if (ksp->reason) break;
140: /* vv(i+1) . vv(i+1) */
141: PetscCall(VecNormalize(VEC_VV(it + 1), &tt));
142: KSPCheckNorm(ksp, tt);
144: /* save the magnitude */
145: *HH(it + 1, it) = tt;
146: *HES(it + 1, it) = tt;
148: /* check for the happy breakdown */
149: hapbnd = PetscAbsScalar(tt / *GRS(it));
150: if (hapbnd > gmres->haptol) hapbnd = gmres->haptol;
151: if (tt < hapbnd) {
152: PetscCall(PetscInfo(ksp, "Detected happy breakdown, current hapbnd = %14.12e tt = %14.12e\n", (double)hapbnd, (double)tt));
153: hapend = PETSC_TRUE;
154: }
155: PetscCall(KSPGMRESUpdateHessenberg(ksp, it, hapend, &res));
157: it++;
158: gmres->it = (it - 1); /* For converged */
159: ksp->its++;
160: ksp->rnorm = res;
161: if (ksp->reason) break;
163: PetscCall((*ksp->converged)(ksp, ksp->its, res, &ksp->reason, ksp->cnvP));
165: /* Catch error in happy breakdown and signal convergence and break from loop */
166: if (hapend) {
167: if (ksp->normtype == KSP_NORM_NONE) { /* convergence test was skipped in this case */
168: ksp->reason = KSP_CONVERGED_HAPPY_BREAKDOWN;
169: } else if (!ksp->reason) {
170: PetscCheck(!ksp->errorifnotconverged, PetscObjectComm((PetscObject)ksp), PETSC_ERR_NOT_CONVERGED, "Reached happy break down, but convergence was not indicated. Residual norm = %g", (double)res);
171: ksp->reason = KSP_DIVERGED_BREAKDOWN;
172: break;
173: }
174: }
175: }
177: if (itcount) *itcount = it;
179: /*
180: Down here we have to solve for the "best" coefficients of the Krylov
181: columns, add the solution values together, and possibly unwind the
182: preconditioning from the solution
183: */
184: /* Form the solution (or the solution so far) */
185: PetscCall(KSPGMRESBuildSoln(GRS(0), ksp->vec_sol, ksp->vec_sol, ksp, it - 1));
187: /* Monitor if we know that we will not return for a restart */
188: if (ksp->reason == KSP_CONVERGED_ITERATING && ksp->its >= ksp->max_it) ksp->reason = KSP_DIVERGED_ITS;
189: if (it && ksp->reason) {
190: PetscCall(KSPLogResidualHistory(ksp, res));
191: PetscCall(KSPLogErrorHistory(ksp));
192: PetscCall(KSPMonitor(ksp, ksp->its, res));
193: }
194: PetscFunctionReturn(PETSC_SUCCESS);
195: }
197: static PetscErrorCode KSPSolve_GMRES(KSP ksp)
198: {
199: PetscInt its, itcount, i;
200: KSP_GMRES *gmres = (KSP_GMRES *)ksp->data;
201: PetscBool guess_zero = ksp->guess_zero;
202: PetscInt N = gmres->max_k + 1;
204: PetscFunctionBegin;
205: PetscCheck(!ksp->calc_sings || gmres->Rsvd, PetscObjectComm((PetscObject)ksp), PETSC_ERR_ORDER, "Must call KSPSetComputeSingularValues() before KSPSetUp() is called");
207: PetscCall(PetscObjectSAWsTakeAccess((PetscObject)ksp));
208: ksp->its = 0;
209: PetscCall(PetscObjectSAWsGrantAccess((PetscObject)ksp));
211: itcount = 0;
212: gmres->fullcycle = 0;
213: ksp->rnorm = -1.0; /* special marker for KSPGMRESCycle() */
214: while (!ksp->reason || (ksp->rnorm == -1 && ksp->reason == KSP_DIVERGED_PC_FAILED)) {
215: PetscCall(KSPInitialResidual(ksp, ksp->vec_sol, VEC_TEMP, VEC_TEMP_MATOP, VEC_VV(0), ksp->vec_rhs));
216: PetscCall(KSPGMRESCycle(&its, ksp));
217: /* Store the Hessenberg matrix and the basis vectors of the Krylov subspace
218: if the cycle is complete for the computation of the Ritz pairs */
219: if (its == gmres->max_k) {
220: gmres->fullcycle++;
221: if (ksp->calc_ritz) {
222: if (!gmres->hes_ritz) {
223: PetscCall(PetscMalloc1(N * N, &gmres->hes_ritz));
224: PetscCall(VecDuplicateVecs(VEC_VV(0), N, &gmres->vecb));
225: }
226: PetscCall(PetscArraycpy(gmres->hes_ritz, gmres->hes_origin, N * N));
227: for (i = 0; i < gmres->max_k + 1; i++) PetscCall(VecCopy(VEC_VV(i), gmres->vecb[i]));
228: }
229: }
230: itcount += its;
231: if (itcount >= ksp->max_it) {
232: if (!ksp->reason) ksp->reason = KSP_DIVERGED_ITS;
233: break;
234: }
235: ksp->guess_zero = PETSC_FALSE; /* every future call to KSPInitialResidual() will have nonzero guess */
236: }
237: ksp->guess_zero = guess_zero; /* restore if user provided nonzero initial guess */
238: PetscFunctionReturn(PETSC_SUCCESS);
239: }
241: PetscErrorCode KSPReset_GMRES(KSP ksp)
242: {
243: KSP_GMRES *gmres = (KSP_GMRES *)ksp->data;
245: PetscFunctionBegin;
246: /* Free the Hessenberg matrices */
247: PetscCall(PetscFree5(gmres->hh_origin, gmres->hes_origin, gmres->rs_origin, gmres->cc_origin, gmres->ss_origin));
248: PetscCall(PetscFree(gmres->hes_ritz));
250: /* free work vectors */
251: PetscCall(PetscFree(gmres->vecs));
252: for (PetscInt i = 0; i < gmres->nwork_alloc; i++) PetscCall(VecDestroyVecs(gmres->mwork_alloc[i], &gmres->user_work[i]));
253: gmres->nwork_alloc = 0;
254: if (gmres->vecb) PetscCall(VecDestroyVecs(gmres->max_k + 1, &gmres->vecb));
256: PetscCall(PetscFree(gmres->user_work));
257: PetscCall(PetscFree(gmres->mwork_alloc));
258: PetscCall(PetscFree(gmres->nrs));
259: PetscCall(VecDestroy(&gmres->sol_temp));
260: PetscCall(PetscFree(gmres->Rsvd));
261: PetscCall(PetscFree(gmres->Dsvd));
263: gmres->vv_allocated = 0;
264: gmres->vecs_allocated = 0;
265: gmres->sol_temp = NULL;
266: PetscFunctionReturn(PETSC_SUCCESS);
267: }
269: PetscErrorCode KSPDestroy_GMRES(KSP ksp)
270: {
271: PetscFunctionBegin;
272: PetscCall(KSPReset_GMRES(ksp));
273: PetscCall(PetscFree(ksp->data));
274: /* clear composed functions */
275: PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESSetPreAllocateVectors_C", NULL));
276: PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESSetRestart_C", NULL));
277: PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESGetRestart_C", NULL));
278: PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESSetHapTol_C", NULL));
279: PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESSetBreakdownTolerance_C", NULL));
280: PetscFunctionReturn(PETSC_SUCCESS);
281: }
282: /*
283: KSPGMRESBuildSoln - create the solution from the starting vector and the
284: current iterates.
286: Input parameters:
287: nrs - work area of size it + 1.
288: vs - index of initial guess
289: vdest - index of result. Note that vs may == vdest (replace
290: guess with the solution).
292: This is an internal routine that knows about the GMRES internals.
293: */
294: static PetscErrorCode KSPGMRESBuildSoln(PetscScalar *nrs, Vec vs, Vec vdest, KSP ksp, PetscInt it)
295: {
296: PetscScalar tt;
297: PetscInt ii, k, j;
298: KSP_GMRES *gmres = (KSP_GMRES *)ksp->data;
300: PetscFunctionBegin;
301: /* Solve for solution vector that minimizes the residual */
303: /* If it is < 0, no gmres steps have been performed */
304: if (it < 0) {
305: PetscCall(VecCopy(vs, vdest)); /* VecCopy() is smart, exists immediately if vguess == vdest */
306: PetscFunctionReturn(PETSC_SUCCESS);
307: }
308: if (*HH(it, it) != 0.0) {
309: nrs[it] = *GRS(it) / *HH(it, it);
310: } else {
311: PetscCheck(!ksp->errorifnotconverged, PetscObjectComm((PetscObject)ksp), PETSC_ERR_NOT_CONVERGED, "You reached the break down in GMRES; HH(it,it) = 0");
312: ksp->reason = KSP_DIVERGED_BREAKDOWN;
314: PetscCall(PetscInfo(ksp, "Likely your matrix or preconditioner is singular. HH(it,it) is identically zero; it = %" PetscInt_FMT " GRS(it) = %g\n", it, (double)PetscAbsScalar(*GRS(it))));
315: PetscFunctionReturn(PETSC_SUCCESS);
316: }
317: for (ii = 1; ii <= it; ii++) {
318: k = it - ii;
319: tt = *GRS(k);
320: for (j = k + 1; j <= it; j++) tt = tt - *HH(k, j) * nrs[j];
321: if (*HH(k, k) == 0.0) {
322: PetscCheck(!ksp->errorifnotconverged, PetscObjectComm((PetscObject)ksp), PETSC_ERR_NOT_CONVERGED, "Likely your matrix or preconditioner is singular. HH(k,k) is identically zero; k = %" PetscInt_FMT, k);
323: ksp->reason = KSP_DIVERGED_BREAKDOWN;
324: PetscCall(PetscInfo(ksp, "Likely your matrix or preconditioner is singular. HH(k,k) is identically zero; k = %" PetscInt_FMT "\n", k));
325: PetscFunctionReturn(PETSC_SUCCESS);
326: }
327: nrs[k] = tt / *HH(k, k);
328: }
330: /* Accumulate the correction to the solution of the preconditioned problem in TEMP */
331: PetscCall(VecMAXPBY(VEC_TEMP, it + 1, nrs, 0, &VEC_VV(0)));
333: PetscCall(KSPUnwindPreconditioner(ksp, VEC_TEMP, VEC_TEMP_MATOP));
334: /* add solution to previous solution */
335: if (vdest != vs) PetscCall(VecCopy(vs, vdest));
336: PetscCall(VecAXPY(vdest, 1.0, VEC_TEMP));
337: PetscFunctionReturn(PETSC_SUCCESS);
338: }
339: /*
340: Do the scalar work for the orthogonalization. Return new residual norm.
341: */
342: static PetscErrorCode KSPGMRESUpdateHessenberg(KSP ksp, PetscInt it, PetscBool hapend, PetscReal *res)
343: {
344: PetscScalar *hh, *cc, *ss, tt;
345: KSP_GMRES *gmres = (KSP_GMRES *)ksp->data;
347: PetscFunctionBegin;
348: hh = HH(0, it);
349: cc = CC(0);
350: ss = SS(0);
352: /* Apply all the previously computed plane rotations to the new column
353: of the Hessenberg matrix */
354: for (PetscInt j = 1; j <= it; j++) {
355: tt = *hh;
356: *hh = PetscConj(*cc) * tt + *ss * *(hh + 1);
357: hh++;
358: *hh = *cc++ * *hh - (*ss++ * tt);
359: }
361: /*
362: compute the new plane rotation, and apply it to:
363: 1) the right-hand side of the Hessenberg system
364: 2) the new column of the Hessenberg matrix
365: thus obtaining the updated value of the residual
366: */
367: if (!hapend) {
368: tt = PetscSqrtScalar(PetscConj(*hh) * *hh + PetscConj(*(hh + 1)) * *(hh + 1));
369: if (tt == 0.0) {
370: PetscCheck(!ksp->errorifnotconverged, PetscObjectComm((PetscObject)ksp), PETSC_ERR_NOT_CONVERGED, "tt == 0.0");
371: ksp->reason = KSP_DIVERGED_NULL;
372: PetscFunctionReturn(PETSC_SUCCESS);
373: }
374: *cc = *hh / tt;
375: *ss = *(hh + 1) / tt;
376: *GRS(it + 1) = -(*ss * *GRS(it));
377: *GRS(it) = PetscConj(*cc) * *GRS(it);
378: *hh = PetscConj(*cc) * *hh + *ss * *(hh + 1);
379: *res = PetscAbsScalar(*GRS(it + 1));
380: } else {
381: /* happy breakdown: HH(it+1, it) = 0, therefore we don't need to apply
382: another rotation matrix (so RH doesn't change). The new residual is
383: always the new sine term times the residual from last time (GRS(it)),
384: but now the new sine rotation would be zero...so the residual should
385: be zero...so we will multiply "zero" by the last residual. This might
386: not be exactly what we want to do here -could just return "zero". */
388: *res = 0.0;
389: }
390: PetscFunctionReturn(PETSC_SUCCESS);
391: }
392: /*
393: This routine allocates more work vectors, starting from VEC_VV(it).
394: */
395: PetscErrorCode KSPGMRESGetNewVectors(KSP ksp, PetscInt it)
396: {
397: KSP_GMRES *gmres = (KSP_GMRES *)ksp->data;
398: PetscInt nwork = gmres->nwork_alloc, k, nalloc;
400: PetscFunctionBegin;
401: nalloc = PetscMin(ksp->max_it, gmres->delta_allocate);
402: /* Adjust the number to allocate to make sure that we don't exceed the
403: number of available slots */
404: if (it + VEC_OFFSET + nalloc >= gmres->vecs_allocated) nalloc = gmres->vecs_allocated - it - VEC_OFFSET;
405: if (!nalloc) PetscFunctionReturn(PETSC_SUCCESS);
407: gmres->vv_allocated += nalloc;
409: PetscCall(KSPCreateVecs(ksp, nalloc, &gmres->user_work[nwork], 0, NULL));
411: gmres->mwork_alloc[nwork] = nalloc;
412: for (k = 0; k < nalloc; k++) gmres->vecs[it + VEC_OFFSET + k] = gmres->user_work[nwork][k];
413: gmres->nwork_alloc++;
414: PetscFunctionReturn(PETSC_SUCCESS);
415: }
417: static PetscErrorCode KSPBuildSolution_GMRES(KSP ksp, Vec ptr, Vec *result)
418: {
419: KSP_GMRES *gmres = (KSP_GMRES *)ksp->data;
421: PetscFunctionBegin;
422: if (!ptr) {
423: if (!gmres->sol_temp) PetscCall(VecDuplicate(ksp->vec_sol, &gmres->sol_temp));
424: ptr = gmres->sol_temp;
425: }
426: if (!gmres->nrs) {
427: /* allocate the work area */
428: PetscCall(PetscMalloc1(gmres->max_k, &gmres->nrs));
429: }
431: PetscCall(KSPGMRESBuildSoln(gmres->nrs, ksp->vec_sol, ptr, ksp, gmres->it));
432: if (result) *result = ptr;
433: PetscFunctionReturn(PETSC_SUCCESS);
434: }
436: PetscErrorCode KSPView_GMRES(KSP ksp, PetscViewer viewer)
437: {
438: KSP_GMRES *gmres = (KSP_GMRES *)ksp->data;
439: const char *cstr;
440: PetscBool isascii, isstring;
442: PetscFunctionBegin;
443: PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &isascii));
444: PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERSTRING, &isstring));
445: if (ksp->orthog == KSPOrthogonalizationClassicalGramSchmidt) {
446: switch (ksp->cgstype) {
447: case KSP_ORTHOGONALIZATION_CGS_REFINE_NEVER:
448: cstr = "classical (unmodified) Gram-Schmidt orthogonalization with no iterative refinement";
449: break;
450: case KSP_ORTHOGONALIZATION_CGS_REFINE_ALWAYS:
451: cstr = "classical (unmodified) Gram-Schmidt orthogonalization with one step of iterative refinement";
452: break;
453: case KSP_ORTHOGONALIZATION_CGS_REFINE_IFNEEDED:
454: cstr = "classical (unmodified) Gram-Schmidt orthogonalization with one step of iterative refinement when needed";
455: break;
456: default:
457: SETERRQ(PetscObjectComm((PetscObject)ksp), PETSC_ERR_ARG_OUTOFRANGE, "Unknown orthogonalization");
458: }
459: } else if (ksp->orthog == KSPOrthogonalizationModifiedGramSchmidt) {
460: cstr = "modified Gram-Schmidt orthogonalization";
461: } else {
462: cstr = "unknown orthogonalization";
463: }
464: if (isascii) {
465: PetscCall(PetscViewerASCIIPrintf(viewer, " restart=%" PetscInt_FMT ", using %s\n", gmres->max_k, cstr));
466: PetscCall(PetscViewerASCIIPrintf(viewer, " happy breakdown tolerance=%g\n", (double)gmres->haptol));
467: } else if (isstring) {
468: PetscCall(PetscViewerStringSPrintf(viewer, "%s restart %" PetscInt_FMT, cstr, gmres->max_k));
469: }
470: PetscFunctionReturn(PETSC_SUCCESS);
471: }
473: /*@
474: KSPGMRESMonitorKrylov - Calls `VecView()` to monitor each new direction in the `KSPGMRES` accumulated Krylov space.
476: Collective
478: Input Parameters:
479: + ksp - the `KSP` context
480: . its - iteration number
481: . fgnorm - 2-norm of residual (or gradient)
482: - Viewers - a collection of viewers created with `PetscViewersCreate()`
484: Options Database Key:
485: . -ksp_gmres_krylov_monitor (true|false) - Plot the Krylov directions
487: Level: intermediate
489: Note:
490: A new `PETSCVIEWERDRAW` is created for each Krylov vector so they can all be simultaneously viewed
492: .seealso: [](ch_ksp), `KSPGMRES`, `KSPMonitorSet()`, `KSPMonitorResidual()`, `VecView()`, `PetscViewersCreate()`, `PetscViewersDestroy()`
493: @*/
494: PetscErrorCode KSPGMRESMonitorKrylov(KSP ksp, PetscInt its, PetscReal fgnorm, void *Viewers)
495: {
496: PetscViewers viewers = (PetscViewers)Viewers;
497: KSP_GMRES *gmres = (KSP_GMRES *)ksp->data;
498: Vec x;
499: PetscViewer viewer;
500: PetscBool flg;
502: PetscFunctionBegin;
503: PetscCall(PetscViewersGetViewer(viewers, gmres->it + 1, &viewer));
504: PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &flg));
505: if (!flg) {
506: PetscCall(PetscViewerSetType(viewer, PETSCVIEWERDRAW));
507: PetscCall(PetscViewerDrawSetInfo(viewer, NULL, "Krylov GMRES Monitor", PETSC_DECIDE, PETSC_DECIDE, 300, 300));
508: }
509: x = VEC_VV(gmres->it + 1);
510: PetscCall(VecView(x, viewer));
511: PetscFunctionReturn(PETSC_SUCCESS);
512: }
514: PetscErrorCode KSPSetFromOptions_GMRES(KSP ksp, PetscOptionItems PetscOptionsObject)
515: {
516: PetscInt restart;
517: PetscReal haptol, breakdowntol;
518: KSP_GMRES *gmres = (KSP_GMRES *)ksp->data;
519: PetscBool flg, set;
521: PetscFunctionBegin;
522: PetscOptionsHeadBegin(PetscOptionsObject, "KSP GMRES Options");
523: PetscCall(PetscOptionsInt("-ksp_gmres_restart", "Number of Krylov search directions", "KSPGMRESSetRestart", gmres->max_k, &restart, &flg));
524: if (flg) PetscCall(KSPGMRESSetRestart(ksp, restart));
525: PetscCall(PetscOptionsReal("-ksp_gmres_haptol", "Tolerance for exact convergence (happy breakdown)", "KSPGMRESSetHapTol", gmres->haptol, &haptol, &flg));
526: if (flg) PetscCall(KSPGMRESSetHapTol(ksp, haptol));
527: PetscCall(PetscOptionsReal("-ksp_gmres_breakdown_tolerance", "Divergence breakdown tolerance during GMRES restart", "KSPGMRESSetBreakdownTolerance", gmres->breakdowntol, &breakdowntol, &flg));
528: if (flg) PetscCall(KSPGMRESSetBreakdownTolerance(ksp, breakdowntol));
529: flg = PETSC_FALSE;
530: PetscCall(PetscOptionsBool("-ksp_gmres_preallocate", "Preallocate Krylov vectors", "KSPGMRESSetPreAllocateVectors", gmres->q_preallocate, &flg, &set));
531: PetscCheck(!set || flg, PetscObjectComm((PetscObject)ksp), PETSC_ERR_SUP, "Cannot turn off preallocation with -ksp_gmres_preallocate false");
532: if (set) PetscCall(KSPGMRESSetPreAllocateVectors(ksp));
533: flg = PETSC_FALSE;
534: PetscCall(PetscOptionsBool("-ksp_gmres_krylov_monitor", "Plot the Krylov directions", "KSPMonitorSet", flg, &flg, NULL));
535: if (flg) {
536: PetscViewers viewers;
538: PetscCall(PetscViewersCreate(PetscObjectComm((PetscObject)ksp), &viewers));
539: PetscCall(KSPMonitorSet(ksp, KSPGMRESMonitorKrylov, viewers, (PetscCtxDestroyFn *)PetscViewersDestroy));
540: }
541: PetscOptionsHeadEnd();
542: PetscFunctionReturn(PETSC_SUCCESS);
543: }
545: PetscErrorCode KSPGMRESSetHapTol_GMRES(KSP ksp, PetscReal tol)
546: {
547: KSP_GMRES *gmres = (KSP_GMRES *)ksp->data;
549: PetscFunctionBegin;
550: PetscCheck(tol >= 0.0, PetscObjectComm((PetscObject)ksp), PETSC_ERR_ARG_OUTOFRANGE, "Tolerance must be non-negative");
551: gmres->haptol = tol;
552: PetscFunctionReturn(PETSC_SUCCESS);
553: }
555: static PetscErrorCode KSPGMRESSetBreakdownTolerance_GMRES(KSP ksp, PetscReal tol)
556: {
557: KSP_GMRES *gmres = (KSP_GMRES *)ksp->data;
559: PetscFunctionBegin;
560: if (tol == (PetscReal)PETSC_DEFAULT) {
561: gmres->breakdowntol = 0.1;
562: PetscFunctionReturn(PETSC_SUCCESS);
563: }
564: PetscCheck(tol >= 0.0, PetscObjectComm((PetscObject)ksp), PETSC_ERR_ARG_OUTOFRANGE, "Breakdown tolerance must be non-negative");
565: gmres->breakdowntol = tol;
566: PetscFunctionReturn(PETSC_SUCCESS);
567: }
569: PetscErrorCode KSPGMRESGetRestart_GMRES(KSP ksp, PetscInt *max_k)
570: {
571: KSP_GMRES *gmres = (KSP_GMRES *)ksp->data;
573: PetscFunctionBegin;
574: *max_k = gmres->max_k;
575: PetscFunctionReturn(PETSC_SUCCESS);
576: }
578: PetscErrorCode KSPGMRESSetRestart_GMRES(KSP ksp, PetscInt max_k)
579: {
580: KSP_GMRES *gmres = (KSP_GMRES *)ksp->data;
582: PetscFunctionBegin;
583: PetscCheck(max_k >= 1, PetscObjectComm((PetscObject)ksp), PETSC_ERR_ARG_OUTOFRANGE, "Restart must be positive");
584: if (!ksp->setupstage) {
585: gmres->max_k = max_k;
586: } else if (gmres->max_k != max_k) {
587: gmres->max_k = max_k;
588: ksp->setupstage = KSP_SETUP_NEW;
589: /* free the data structures, then create them again */
590: PetscCall(KSPReset_GMRES(ksp));
591: }
592: PetscFunctionReturn(PETSC_SUCCESS);
593: }
595: PetscErrorCode KSPGMRESSetPreAllocateVectors_GMRES(KSP ksp)
596: {
597: KSP_GMRES *gmres;
599: PetscFunctionBegin;
600: gmres = (KSP_GMRES *)ksp->data;
601: gmres->q_preallocate = PETSC_TRUE;
602: PetscFunctionReturn(PETSC_SUCCESS);
603: }
605: /*@
606: KSPGMRESSetRestart - Sets number of iterations at which GMRES (`KSPGMRES`, `KSPFGMRES`, `KSPPGMRES`, `KSPDGMRES`, `KSPPIPEFGMRES`,
607: and `KSPLGMRES`) restarts.
609: Logically Collective
611: Input Parameters:
612: + ksp - the Krylov space solver context
613: - restart - integer restart value, this corresponds to the number of iterations of GMRES to perform before restarting
615: Options Database Key:
616: . -ksp_gmres_restart restart - integer restart value
618: Level: intermediate
620: Notes:
621: The default value is 30.
623: GMRES builds a Krylov subspace of increasing size, where each new vector is orthogonalized against the previous ones using a Gram-Schmidt process.
624: As the size of the Krylov subspace grows, the computational cost and memory requirements increase. To mitigate this issue, GMRES methods
625: usually employ restart strategies, which involve periodically deleting the Krylov subspace and beginning to generate a new one. This can help reduce
626: the computational cost and memory usage while still maintaining convergence. The maximum size of the Krylov subspace, that is the maximum number
627: of vectors orthogonalized is called the `restart` parameter.
629: A larger restart parameter generally leads to faster convergence of GMRES but the memory usage is higher than with a smaller `restart` parameter,
630: as is the average time to perform each iteration. For more ill-conditioned problems a larger restart value may be necessary.
632: `KSPBCGS` has the advantage over `KSPGMRES` in that it does not explicitly store the Krylov space and thus does not require as much memory
633: as GMRES might need.
635: .seealso: [](ch_ksp), `KSPGMRES`, `KSPSetTolerances()`, `KSPOrthogonalizationSet()`, `KSPGMRESSetPreAllocateVectors()`, `KSPGMRESGetRestart()`,
636: `KSPFGMRES`, `KSPLGMRES`, `KSPPGMRES`, `KSPDGMRES`, `KSPPIPEFGMRES`
637: @*/
638: PetscErrorCode KSPGMRESSetRestart(KSP ksp, PetscInt restart)
639: {
640: PetscFunctionBegin;
643: PetscTryMethod(ksp, "KSPGMRESSetRestart_C", (KSP, PetscInt), (ksp, restart));
644: PetscFunctionReturn(PETSC_SUCCESS);
645: }
647: /*@
648: KSPGMRESGetRestart - Gets number of iterations at which GMRES (`KSPGMRES`, `KSPFGMRES`, `KSPPGMRES`, `KSPDGMRES`, `KSPPIPEFGMRES`,
649: and `KSPLGMRES`) restarts.
651: Not Collective
653: Input Parameter:
654: . ksp - the Krylov space solver context
656: Output Parameter:
657: . restart - integer restart value
659: Level: intermediate
661: .seealso: [](ch_ksp), `KSPGMRES`, `KSPSetTolerances()`, `KSPOrthogonalizationSet()`, `KSPGMRESSetPreAllocateVectors()`, `KSPGMRESSetRestart()`,
662: `KSPFGMRES`, `KSPLGMRES`, `KSPPGMRES`, `KSPDGMRES`, `KSPPIPEFGMRES`
663: @*/
664: PetscErrorCode KSPGMRESGetRestart(KSP ksp, PetscInt *restart)
665: {
666: PetscFunctionBegin;
667: PetscUseMethod(ksp, "KSPGMRESGetRestart_C", (KSP, PetscInt *), (ksp, restart));
668: PetscFunctionReturn(PETSC_SUCCESS);
669: }
671: /*@
672: KSPGMRESSetHapTol - Sets the tolerance for detecting a happy breakdown in GMRES (`KSPGMRES`, `KSPFGMRES` and `KSPLGMRES` and others)
674: Logically Collective
676: Input Parameters:
677: + ksp - the Krylov space solver context
678: - tol - the tolerance for detecting a happy breakdown
680: Options Database Key:
681: . -ksp_gmres_haptol tol - set tolerance for determining happy breakdown
683: Level: intermediate
685: Note:
686: Happy breakdown is the rare case in `KSPGMRES` where a very near zero matrix entry is generated in the upper Hessenberg matrix indicating
687: an 'exact' solution has been obtained. If you attempt more iterations after this point with GMRES unstable
688: things can happen.
690: The default tolerance value for detecting a happy breakdown with GMRES in PETSc is 1.0e-30.
692: .seealso: [](ch_ksp), `KSPGMRES`, `KSPSetTolerances()`
693: @*/
694: PetscErrorCode KSPGMRESSetHapTol(KSP ksp, PetscReal tol)
695: {
696: PetscFunctionBegin;
698: PetscTryMethod(ksp, "KSPGMRESSetHapTol_C", (KSP, PetscReal), (ksp, tol));
699: PetscFunctionReturn(PETSC_SUCCESS);
700: }
702: /*@
703: KSPGMRESSetBreakdownTolerance - Sets the tolerance for determining divergence breakdown in `KSPGMRES` at restart.
705: Logically Collective
707: Input Parameters:
708: + ksp - the Krylov space solver context
709: - tol - the tolerance
711: Options Database Key:
712: . -ksp_gmres_breakdown_tolerance tol - set tolerance for determining divergence breakdown
714: Level: intermediate
716: Note:
717: Divergence breakdown occurs when the norm of the GMRES residual increases significantly at a restart.
718: This is defined to be $ | truenorm - gmresnorm | > tol * gmresnorm $ where $ gmresnorm $ is the norm computed
719: by the GMRES process at a restart iteration using the standard GMRES recursion formula and $ truenorm $ is computed after
720: the restart using the definition $ \| r \| = \| b - A x \|$.
722: Divergence breakdown stops the iterative solve with a `KSPConvergedReason` of `KSP_DIVERGED_BREAKDOWN` indicating the
723: GMRES solver has not converged.
725: Divergence breakdown can occur when there is an error (bug) in either the application of the matrix or the preconditioner,
726: or the preconditioner is extremely ill-conditioned.
728: The default is .1
730: .seealso: [](ch_ksp), `KSPGMRES`, `KSPSetTolerances()`, `KSPGMRESSetHapTol()`, `KSPConvergedReason`
731: @*/
732: PetscErrorCode KSPGMRESSetBreakdownTolerance(KSP ksp, PetscReal tol)
733: {
734: PetscFunctionBegin;
736: PetscTryMethod(ksp, "KSPGMRESSetBreakdownTolerance_C", (KSP, PetscReal), (ksp, tol));
737: PetscFunctionReturn(PETSC_SUCCESS);
738: }
740: /*MC
741: KSPGMRES - Implements the Generalized Minimal Residual method {cite}`saad.schultz:gmres` with restart for solving linear systems using `KSP`.
743: Options Database Keys:
744: + -ksp_gmres_restart restart - the number of Krylov directions to orthogonalize against
745: . -ksp_gmres_haptol tol - sets the tolerance for happy breakdown (exact convergence) of `KSPGMRES`
746: . -ksp_gmres_preallocate - preallocate all the Krylov search directions initially (otherwise groups of
747: vectors are allocated as needed), see `KSPGMRESSetPreAllocateVectors()`
748: - -ksp_gmres_krylov_monitor - plot the Krylov space generated
750: Level: beginner
752: Notes:
753: Left and right preconditioning are supported, but not symmetric preconditioning.
755: Using `KSPGMRESSetPreAllocateVectors()` or `-ksp_gmres_preallocate` can improve the efficiency of the orthogonalization step with certain vector implementations.
757: .seealso: [](ch_ksp), `KSPCreate()`, `KSPSetType()`, `KSPType`, `KSP`, `KSPFGMRES`, `KSPLGMRES`, `KSPPGMRES`, `KSPDGMRES`, `KSPPIPEFGMRES`,
758: `KSPGMRESSetRestart()`, `KSPGMRESSetHapTol()`, `KSPGMRESSetPreAllocateVectors()`, `KSPOrthogonalizationSet()`, `KSPOrthogonalizationGet()`,
759: `KSPOrthogonalizationClassicalGramSchmidt()`, `KSPOrthogonalizationModifiedGramSchmidt()`,
760: `KSPOrthogonalizationCGSRefinementType`, `KSPOrthogonalizationSetCGSRefinementType()`, `KSPOrthogonalizationGetCGSRefinementType()`, `KSPGMRESMonitorKrylov()`, `KSPSetPCSide()`
761: M*/
763: PETSC_EXTERN PetscErrorCode KSPCreate_GMRES(KSP ksp)
764: {
765: KSP_GMRES *gmres;
767: PetscFunctionBegin;
768: PetscCall(PetscNew(&gmres));
769: ksp->data = (void *)gmres;
771: PetscCall(KSPSetSupportedNorm(ksp, KSP_NORM_PRECONDITIONED, PC_LEFT, 4));
772: PetscCall(KSPSetSupportedNorm(ksp, KSP_NORM_UNPRECONDITIONED, PC_RIGHT, 3));
773: PetscCall(KSPSetSupportedNorm(ksp, KSP_NORM_PRECONDITIONED, PC_SYMMETRIC, 2));
774: PetscCall(KSPSetSupportedNorm(ksp, KSP_NORM_NONE, PC_RIGHT, 1));
775: PetscCall(KSPSetSupportedNorm(ksp, KSP_NORM_NONE, PC_LEFT, 1));
777: ksp->ops->buildsolution = KSPBuildSolution_GMRES;
778: ksp->ops->setup = KSPSetUp_GMRES;
779: ksp->ops->solve = KSPSolve_GMRES;
780: ksp->ops->reset = KSPReset_GMRES;
781: ksp->ops->destroy = KSPDestroy_GMRES;
782: ksp->ops->view = KSPView_GMRES;
783: ksp->ops->setfromoptions = KSPSetFromOptions_GMRES;
784: ksp->ops->computeextremesingularvalues = KSPComputeExtremeSingularValues_GMRES;
785: ksp->ops->computeeigenvalues = KSPComputeEigenvalues_GMRES;
786: ksp->ops->computeritz = KSPComputeRitz_GMRES;
787: PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESSetPreAllocateVectors_C", KSPGMRESSetPreAllocateVectors_GMRES));
788: PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESSetRestart_C", KSPGMRESSetRestart_GMRES));
789: PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESGetRestart_C", KSPGMRESGetRestart_GMRES));
790: PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESSetHapTol_C", KSPGMRESSetHapTol_GMRES));
791: PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESSetBreakdownTolerance_C", KSPGMRESSetBreakdownTolerance_GMRES));
793: gmres->haptol = 1.0e-30;
794: gmres->breakdowntol = 0.1;
795: gmres->q_preallocate = PETSC_FALSE;
796: gmres->delta_allocate = GMRES_DELTA_DIRECTIONS;
797: gmres->nrs = NULL;
798: gmres->sol_temp = NULL;
799: gmres->max_k = GMRES_DEFAULT_MAXK;
800: gmres->Rsvd = NULL;
801: PetscFunctionReturn(PETSC_SUCCESS);
802: }