Actual source code: gmres.c
2: /*
3: This file implements GMRES (a Generalized Minimal Residual) method.
4: Reference: Saad and Schultz, 1986.
6: Some comments on left vs. right preconditioning, and restarts.
7: Left and right preconditioning.
8: If right preconditioning is chosen, then the problem being solved
9: by gmres is actually
10: My = AB^-1 y = f
11: so the initial residual is
12: r = f - Mx
13: Note that B^-1 y = x or y = B x, and if x is non-zero, the initial
14: residual is
15: r = f - A x
16: The final solution is then
17: x = B^-1 y
19: If left preconditioning is chosen, then the problem being solved is
20: My = B^-1 A x = B^-1 f,
21: and the initial residual is
22: r = B^-1(f - Ax)
24: Restarts: Restarts are basically solves with x0 not equal to zero.
25: Note that we can eliminate an extra application of B^-1 between
26: restarts as long as we don't require that the solution at the end
27: of an unsuccessful gmres iteration always be the solution x.
28: */
30: #include <../src/ksp/ksp/impls/gmres/gmresimpl.h>
31: #define GMRES_DELTA_DIRECTIONS 10
32: #define GMRES_DEFAULT_MAXK 30
33: static PetscErrorCode KSPGMRESUpdateHessenberg(KSP, PetscInt, PetscBool, PetscReal *);
34: static PetscErrorCode KSPGMRESBuildSoln(PetscScalar *, Vec, Vec, KSP, PetscInt);
36: PetscErrorCode KSPSetUp_GMRES(KSP ksp)
37: {
38: PetscInt hh, hes, rs, cc;
39: PetscInt max_k, k;
40: KSP_GMRES *gmres = (KSP_GMRES *)ksp->data;
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: 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: PetscMalloc1((max_k + 3) * (max_k + 9), &gmres->Rsvd);
53: 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;
60: PetscMalloc1(gmres->vecs_allocated, &gmres->vecs);
61: PetscMalloc1(VEC_OFFSET + 2 + max_k, &gmres->user_work);
62: PetscMalloc1(VEC_OFFSET + 2 + max_k, &gmres->mwork_alloc);
64: if (gmres->q_preallocate) {
65: gmres->vv_allocated = VEC_OFFSET + 2 + max_k;
67: KSPCreateVecs(ksp, gmres->vv_allocated, &gmres->user_work[0], 0, NULL);
69: gmres->mwork_alloc[0] = gmres->vv_allocated;
70: gmres->nwork_alloc = 1;
71: for (k = 0; k < gmres->vv_allocated; k++) gmres->vecs[k] = gmres->user_work[0][k];
72: } else {
73: gmres->vv_allocated = 5;
75: KSPCreateVecs(ksp, 5, &gmres->user_work[0], 0, NULL);
77: gmres->mwork_alloc[0] = 5;
78: gmres->nwork_alloc = 1;
79: for (k = 0; k < gmres->vv_allocated; k++) gmres->vecs[k] = gmres->user_work[0][k];
80: }
81: return 0;
82: }
84: /*
85: Run gmres, possibly with restart. Return residual history if requested.
86: input parameters:
88: . gmres - structure containing parameters and work areas
90: output parameters:
91: . nres - residuals (from preconditioned system) at each step.
92: If restarting, consider passing nres+it. If null,
93: ignored
94: . itcount - number of iterations used. nres[0] to nres[itcount]
95: are defined. If null, ignored.
97: Notes:
98: On entry, the value in vector VEC_VV(0) should be the initial residual
99: (this allows shortcuts where the initial preconditioned residual is 0).
100: */
101: PetscErrorCode KSPGMRESCycle(PetscInt *itcount, KSP ksp)
102: {
103: KSP_GMRES *gmres = (KSP_GMRES *)(ksp->data);
104: PetscReal res, hapbnd, tt;
105: PetscInt it = 0, max_k = gmres->max_k;
106: PetscBool hapend = PETSC_FALSE;
108: if (itcount) *itcount = 0;
109: VecNormalize(VEC_VV(0), &res);
110: KSPCheckNorm(ksp, res);
112: /* the constant .1 is arbitrary, just some measure at how incorrect the residuals are */
113: if ((ksp->rnorm > 0.0) && (PetscAbsReal(res - ksp->rnorm) > gmres->breakdowntol * gmres->rnorm0)) {
115: (double)ksp->rnorm, (double)res, (double)gmres->rnorm0);
116: 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", (double)ksp->rnorm, (double)res, (double)gmres->rnorm0);
117: ksp->reason = KSP_DIVERGED_BREAKDOWN;
118: return 0;
119: }
120: *GRS(0) = gmres->rnorm0 = res;
122: /* check for the convergence */
123: PetscObjectSAWsTakeAccess((PetscObject)ksp);
124: ksp->rnorm = res;
125: PetscObjectSAWsGrantAccess((PetscObject)ksp);
126: gmres->it = (it - 1);
127: KSPLogResidualHistory(ksp, res);
128: KSPLogErrorHistory(ksp);
129: KSPMonitor(ksp, ksp->its, res);
130: if (!res) {
131: ksp->reason = KSP_CONVERGED_ATOL;
132: PetscInfo(ksp, "Converged due to zero residual norm on entry\n");
133: return 0;
134: }
136: (*ksp->converged)(ksp, ksp->its, res, &ksp->reason, ksp->cnvP);
137: while (!ksp->reason && it < max_k && ksp->its < ksp->max_it) {
138: if (it) {
139: KSPLogResidualHistory(ksp, res);
140: KSPLogErrorHistory(ksp);
141: KSPMonitor(ksp, ksp->its, res);
142: }
143: gmres->it = (it - 1);
144: if (gmres->vv_allocated <= it + VEC_OFFSET + 1) KSPGMRESGetNewVectors(ksp, it + 1);
145: KSP_PCApplyBAorAB(ksp, VEC_VV(it), VEC_VV(1 + it), VEC_TEMP_MATOP);
147: /* update hessenberg matrix and do Gram-Schmidt */
148: (*gmres->orthog)(ksp, it);
149: if (ksp->reason) break;
151: /* vv(i+1) . vv(i+1) */
152: VecNormalize(VEC_VV(it + 1), &tt);
153: KSPCheckNorm(ksp, tt);
155: /* save the magnitude */
156: *HH(it + 1, it) = tt;
157: *HES(it + 1, it) = tt;
159: /* check for the happy breakdown */
160: hapbnd = PetscAbsScalar(tt / *GRS(it));
161: if (hapbnd > gmres->haptol) hapbnd = gmres->haptol;
162: if (tt < hapbnd) {
163: PetscInfo(ksp, "Detected happy breakdown, current hapbnd = %14.12e tt = %14.12e\n", (double)hapbnd, (double)tt);
164: hapend = PETSC_TRUE;
165: }
166: KSPGMRESUpdateHessenberg(ksp, it, hapend, &res);
168: it++;
169: gmres->it = (it - 1); /* For converged */
170: ksp->its++;
171: ksp->rnorm = res;
172: if (ksp->reason) break;
174: (*ksp->converged)(ksp, ksp->its, res, &ksp->reason, ksp->cnvP);
176: /* Catch error in happy breakdown and signal convergence and break from loop */
177: if (hapend) {
178: if (ksp->normtype == KSP_NORM_NONE) { /* convergence test was skipped in this case */
179: ksp->reason = KSP_CONVERGED_HAPPY_BREAKDOWN;
180: } else if (!ksp->reason) {
182: ksp->reason = KSP_DIVERGED_BREAKDOWN;
183: break;
184: }
185: }
186: }
188: /* Monitor if we know that we will not return for a restart */
189: if (it && (ksp->reason || ksp->its >= ksp->max_it)) {
190: KSPLogResidualHistory(ksp, res);
191: KSPLogErrorHistory(ksp);
192: KSPMonitor(ksp, ksp->its, res);
193: }
195: if (itcount) *itcount = it;
197: /*
198: Down here we have to solve for the "best" coefficients of the Krylov
199: columns, add the solution values together, and possibly unwind the
200: preconditioning from the solution
201: */
202: /* Form the solution (or the solution so far) */
203: KSPGMRESBuildSoln(GRS(0), ksp->vec_sol, ksp->vec_sol, ksp, it - 1);
204: return 0;
205: }
207: PetscErrorCode KSPSolve_GMRES(KSP ksp)
208: {
209: PetscInt its, itcount, i;
210: KSP_GMRES *gmres = (KSP_GMRES *)ksp->data;
211: PetscBool guess_zero = ksp->guess_zero;
212: PetscInt N = gmres->max_k + 1;
216: PetscObjectSAWsTakeAccess((PetscObject)ksp);
217: ksp->its = 0;
218: PetscObjectSAWsGrantAccess((PetscObject)ksp);
220: itcount = 0;
221: gmres->fullcycle = 0;
222: ksp->rnorm = -1.0; /* special marker for KSPGMRESCycle() */
223: while (!ksp->reason || (ksp->rnorm == -1 && ksp->reason == KSP_DIVERGED_PC_FAILED)) {
224: KSPInitialResidual(ksp, ksp->vec_sol, VEC_TEMP, VEC_TEMP_MATOP, VEC_VV(0), ksp->vec_rhs);
225: KSPGMRESCycle(&its, ksp);
226: /* Store the Hessenberg matrix and the basis vectors of the Krylov subspace
227: if the cycle is complete for the computation of the Ritz pairs */
228: if (its == gmres->max_k) {
229: gmres->fullcycle++;
230: if (ksp->calc_ritz) {
231: if (!gmres->hes_ritz) {
232: PetscMalloc1(N * N, &gmres->hes_ritz);
233: VecDuplicateVecs(VEC_VV(0), N, &gmres->vecb);
234: }
235: PetscArraycpy(gmres->hes_ritz, gmres->hes_origin, N * N);
236: for (i = 0; i < gmres->max_k + 1; i++) VecCopy(VEC_VV(i), gmres->vecb[i]);
237: }
238: }
239: itcount += its;
240: if (itcount >= ksp->max_it) {
241: if (!ksp->reason) ksp->reason = KSP_DIVERGED_ITS;
242: break;
243: }
244: ksp->guess_zero = PETSC_FALSE; /* every future call to KSPInitialResidual() will have nonzero guess */
245: }
246: ksp->guess_zero = guess_zero; /* restore if user provided nonzero initial guess */
247: return 0;
248: }
250: PetscErrorCode KSPReset_GMRES(KSP ksp)
251: {
252: KSP_GMRES *gmres = (KSP_GMRES *)ksp->data;
253: PetscInt i;
255: /* Free the Hessenberg matrices */
256: PetscFree5(gmres->hh_origin, gmres->hes_origin, gmres->rs_origin, gmres->cc_origin, gmres->ss_origin);
257: PetscFree(gmres->hes_ritz);
259: /* free work vectors */
260: PetscFree(gmres->vecs);
261: for (i = 0; i < gmres->nwork_alloc; i++) VecDestroyVecs(gmres->mwork_alloc[i], &gmres->user_work[i]);
262: gmres->nwork_alloc = 0;
263: if (gmres->vecb) VecDestroyVecs(gmres->max_k + 1, &gmres->vecb);
265: PetscFree(gmres->user_work);
266: PetscFree(gmres->mwork_alloc);
267: PetscFree(gmres->nrs);
268: VecDestroy(&gmres->sol_temp);
269: PetscFree(gmres->Rsvd);
270: PetscFree(gmres->Dsvd);
271: PetscFree(gmres->orthogwork);
273: gmres->vv_allocated = 0;
274: gmres->vecs_allocated = 0;
275: gmres->sol_temp = NULL;
276: return 0;
277: }
279: PetscErrorCode KSPDestroy_GMRES(KSP ksp)
280: {
281: KSPReset_GMRES(ksp);
282: PetscFree(ksp->data);
283: /* clear composed functions */
284: PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESSetPreAllocateVectors_C", NULL);
285: PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESSetOrthogonalization_C", NULL);
286: PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESGetOrthogonalization_C", NULL);
287: PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESSetRestart_C", NULL);
288: PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESGetRestart_C", NULL);
289: PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESSetHapTol_C", NULL);
290: PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESSetBreakdownTolerance_C", NULL);
291: PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESSetCGSRefinementType_C", NULL);
292: PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESGetCGSRefinementType_C", NULL);
293: return 0;
294: }
295: /*
296: KSPGMRESBuildSoln - create the solution from the starting vector and the
297: current iterates.
299: Input parameters:
300: nrs - work area of size it + 1.
301: vs - index of initial guess
302: vdest - index of result. Note that vs may == vdest (replace
303: guess with the solution).
305: This is an internal routine that knows about the GMRES internals.
306: */
307: static PetscErrorCode KSPGMRESBuildSoln(PetscScalar *nrs, Vec vs, Vec vdest, KSP ksp, PetscInt it)
308: {
309: PetscScalar tt;
310: PetscInt ii, k, j;
311: KSP_GMRES *gmres = (KSP_GMRES *)(ksp->data);
313: /* Solve for solution vector that minimizes the residual */
315: /* If it is < 0, no gmres steps have been performed */
316: if (it < 0) {
317: VecCopy(vs, vdest); /* VecCopy() is smart, exists immediately if vguess == vdest */
318: return 0;
319: }
320: if (*HH(it, it) != 0.0) {
321: nrs[it] = *GRS(it) / *HH(it, it);
322: } else {
324: ksp->reason = KSP_DIVERGED_BREAKDOWN;
326: 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)));
327: return 0;
328: }
329: for (ii = 1; ii <= it; ii++) {
330: k = it - ii;
331: tt = *GRS(k);
332: for (j = k + 1; j <= it; j++) tt = tt - *HH(k, j) * nrs[j];
333: if (*HH(k, k) == 0.0) {
335: ksp->reason = KSP_DIVERGED_BREAKDOWN;
336: PetscInfo(ksp, "Likely your matrix or preconditioner is singular. HH(k,k) is identically zero; k = %" PetscInt_FMT "\n", k);
337: return 0;
338: }
339: nrs[k] = tt / *HH(k, k);
340: }
342: /* Accumulate the correction to the solution of the preconditioned problem in TEMP */
343: VecSet(VEC_TEMP, 0.0);
344: VecMAXPY(VEC_TEMP, it + 1, nrs, &VEC_VV(0));
346: KSPUnwindPreconditioner(ksp, VEC_TEMP, VEC_TEMP_MATOP);
347: /* add solution to previous solution */
348: if (vdest != vs) VecCopy(vs, vdest);
349: VecAXPY(vdest, 1.0, VEC_TEMP);
350: return 0;
351: }
352: /*
353: Do the scalar work for the orthogonalization. Return new residual norm.
354: */
355: static PetscErrorCode KSPGMRESUpdateHessenberg(KSP ksp, PetscInt it, PetscBool hapend, PetscReal *res)
356: {
357: PetscScalar *hh, *cc, *ss, tt;
358: PetscInt j;
359: KSP_GMRES *gmres = (KSP_GMRES *)(ksp->data);
361: hh = HH(0, it);
362: cc = CC(0);
363: ss = SS(0);
365: /* Apply all the previously computed plane rotations to the new column
366: of the Hessenberg matrix */
367: for (j = 1; j <= it; j++) {
368: tt = *hh;
369: *hh = PetscConj(*cc) * tt + *ss * *(hh + 1);
370: hh++;
371: *hh = *cc++ * *hh - (*ss++ * tt);
372: }
374: /*
375: compute the new plane rotation, and apply it to:
376: 1) the right-hand-side of the Hessenberg system
377: 2) the new column of the Hessenberg matrix
378: thus obtaining the updated value of the residual
379: */
380: if (!hapend) {
381: tt = PetscSqrtScalar(PetscConj(*hh) * *hh + PetscConj(*(hh + 1)) * *(hh + 1));
382: if (tt == 0.0) {
384: ksp->reason = KSP_DIVERGED_NULL;
385: return 0;
386: }
387: *cc = *hh / tt;
388: *ss = *(hh + 1) / tt;
389: *GRS(it + 1) = -(*ss * *GRS(it));
390: *GRS(it) = PetscConj(*cc) * *GRS(it);
391: *hh = PetscConj(*cc) * *hh + *ss * *(hh + 1);
392: *res = PetscAbsScalar(*GRS(it + 1));
393: } else {
394: /* happy breakdown: HH(it+1, it) = 0, therefore we don't need to apply
395: another rotation matrix (so RH doesn't change). The new residual is
396: always the new sine term times the residual from last time (GRS(it)),
397: but now the new sine rotation would be zero...so the residual should
398: be zero...so we will multiply "zero" by the last residual. This might
399: not be exactly what we want to do here -could just return "zero". */
401: *res = 0.0;
402: }
403: return 0;
404: }
405: /*
406: This routine allocates more work vectors, starting from VEC_VV(it).
407: */
408: PetscErrorCode KSPGMRESGetNewVectors(KSP ksp, PetscInt it)
409: {
410: KSP_GMRES *gmres = (KSP_GMRES *)ksp->data;
411: PetscInt nwork = gmres->nwork_alloc, k, nalloc;
413: nalloc = PetscMin(ksp->max_it, gmres->delta_allocate);
414: /* Adjust the number to allocate to make sure that we don't exceed the
415: number of available slots */
416: if (it + VEC_OFFSET + nalloc >= gmres->vecs_allocated) nalloc = gmres->vecs_allocated - it - VEC_OFFSET;
417: if (!nalloc) return 0;
419: gmres->vv_allocated += nalloc;
421: KSPCreateVecs(ksp, nalloc, &gmres->user_work[nwork], 0, NULL);
423: gmres->mwork_alloc[nwork] = nalloc;
424: for (k = 0; k < nalloc; k++) gmres->vecs[it + VEC_OFFSET + k] = gmres->user_work[nwork][k];
425: gmres->nwork_alloc++;
426: return 0;
427: }
429: PetscErrorCode KSPBuildSolution_GMRES(KSP ksp, Vec ptr, Vec *result)
430: {
431: KSP_GMRES *gmres = (KSP_GMRES *)ksp->data;
433: if (!ptr) {
434: if (!gmres->sol_temp) { VecDuplicate(ksp->vec_sol, &gmres->sol_temp); }
435: ptr = gmres->sol_temp;
436: }
437: if (!gmres->nrs) {
438: /* allocate the work area */
439: PetscMalloc1(gmres->max_k, &gmres->nrs);
440: }
442: KSPGMRESBuildSoln(gmres->nrs, ksp->vec_sol, ptr, ksp, gmres->it);
443: if (result) *result = ptr;
444: return 0;
445: }
447: PetscErrorCode KSPView_GMRES(KSP ksp, PetscViewer viewer)
448: {
449: KSP_GMRES *gmres = (KSP_GMRES *)ksp->data;
450: const char *cstr;
451: PetscBool iascii, isstring;
453: PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii);
454: PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERSTRING, &isstring);
455: if (gmres->orthog == KSPGMRESClassicalGramSchmidtOrthogonalization) {
456: switch (gmres->cgstype) {
457: case (KSP_GMRES_CGS_REFINE_NEVER):
458: cstr = "Classical (unmodified) Gram-Schmidt Orthogonalization with no iterative refinement";
459: break;
460: case (KSP_GMRES_CGS_REFINE_ALWAYS):
461: cstr = "Classical (unmodified) Gram-Schmidt Orthogonalization with one step of iterative refinement";
462: break;
463: case (KSP_GMRES_CGS_REFINE_IFNEEDED):
464: cstr = "Classical (unmodified) Gram-Schmidt Orthogonalization with one step of iterative refinement when needed";
465: break;
466: default:
467: SETERRQ(PetscObjectComm((PetscObject)ksp), PETSC_ERR_ARG_OUTOFRANGE, "Unknown orthogonalization");
468: }
469: } else if (gmres->orthog == KSPGMRESModifiedGramSchmidtOrthogonalization) {
470: cstr = "Modified Gram-Schmidt Orthogonalization";
471: } else {
472: cstr = "unknown orthogonalization";
473: }
474: if (iascii) {
475: PetscViewerASCIIPrintf(viewer, " restart=%" PetscInt_FMT ", using %s\n", gmres->max_k, cstr);
476: PetscViewerASCIIPrintf(viewer, " happy breakdown tolerance %g\n", (double)gmres->haptol);
477: } else if (isstring) {
478: PetscViewerStringSPrintf(viewer, "%s restart %" PetscInt_FMT, cstr, gmres->max_k);
479: }
480: return 0;
481: }
483: /*@C
484: KSPGMRESMonitorKrylov - Calls VecView() for each new direction in the GMRES accumulated Krylov space.
486: Collective on ksp
488: Input Parameters:
489: + ksp - the KSP context
490: . its - iteration number
491: . fgnorm - 2-norm of residual (or gradient)
492: - dummy - an collection of viewers created with KSPViewerCreate()
494: Options Database Keys:
495: . -ksp_gmres_krylov_monitor <bool> - Plot the Krylov directions
497: Notes:
498: A new PETSCVIEWERDRAW is created for each Krylov vector so they can all be simultaneously viewed
499: Level: intermediate
501: .seealso: `KSPMonitorSet()`, `KSPMonitorResidual()`, `VecView()`, `KSPViewersCreate()`, `KSPViewersDestroy()`
502: @*/
503: PetscErrorCode KSPGMRESMonitorKrylov(KSP ksp, PetscInt its, PetscReal fgnorm, void *dummy)
504: {
505: PetscViewers viewers = (PetscViewers)dummy;
506: KSP_GMRES *gmres = (KSP_GMRES *)ksp->data;
507: Vec x;
508: PetscViewer viewer;
509: PetscBool flg;
511: PetscViewersGetViewer(viewers, gmres->it + 1, &viewer);
512: PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &flg);
513: if (!flg) {
514: PetscViewerSetType(viewer, PETSCVIEWERDRAW);
515: PetscViewerDrawSetInfo(viewer, NULL, "Krylov GMRES Monitor", PETSC_DECIDE, PETSC_DECIDE, 300, 300);
516: }
517: x = VEC_VV(gmres->it + 1);
518: VecView(x, viewer);
519: return 0;
520: }
522: PetscErrorCode KSPSetFromOptions_GMRES(KSP ksp, PetscOptionItems *PetscOptionsObject)
523: {
524: PetscInt restart;
525: PetscReal haptol, breakdowntol;
526: KSP_GMRES *gmres = (KSP_GMRES *)ksp->data;
527: PetscBool flg;
529: PetscOptionsHeadBegin(PetscOptionsObject, "KSP GMRES Options");
530: PetscOptionsInt("-ksp_gmres_restart", "Number of Krylov search directions", "KSPGMRESSetRestart", gmres->max_k, &restart, &flg);
531: if (flg) KSPGMRESSetRestart(ksp, restart);
532: PetscOptionsReal("-ksp_gmres_haptol", "Tolerance for exact convergence (happy ending)", "KSPGMRESSetHapTol", gmres->haptol, &haptol, &flg);
533: if (flg) KSPGMRESSetHapTol(ksp, haptol);
534: PetscOptionsReal("-ksp_gmres_breakdown_tolerance", "Divergence breakdown tolerance during GMRES restart", "KSPGMRESSetBreakdownTolerance", gmres->breakdowntol, &breakdowntol, &flg);
535: if (flg) KSPGMRESSetBreakdownTolerance(ksp, breakdowntol);
536: flg = PETSC_FALSE;
537: PetscOptionsBool("-ksp_gmres_preallocate", "Preallocate Krylov vectors", "KSPGMRESSetPreAllocateVectors", flg, &flg, NULL);
538: if (flg) KSPGMRESSetPreAllocateVectors(ksp);
539: PetscOptionsBoolGroupBegin("-ksp_gmres_classicalgramschmidt", "Classical (unmodified) Gram-Schmidt (fast)", "KSPGMRESSetOrthogonalization", &flg);
540: if (flg) KSPGMRESSetOrthogonalization(ksp, KSPGMRESClassicalGramSchmidtOrthogonalization);
541: PetscOptionsBoolGroupEnd("-ksp_gmres_modifiedgramschmidt", "Modified Gram-Schmidt (slow,more stable)", "KSPGMRESSetOrthogonalization", &flg);
542: if (flg) KSPGMRESSetOrthogonalization(ksp, KSPGMRESModifiedGramSchmidtOrthogonalization);
543: PetscOptionsEnum("-ksp_gmres_cgs_refinement_type", "Type of iterative refinement for classical (unmodified) Gram-Schmidt", "KSPGMRESSetCGSRefinementType", KSPGMRESCGSRefinementTypes, (PetscEnum)gmres->cgstype, (PetscEnum *)&gmres->cgstype, &flg);
544: flg = PETSC_FALSE;
545: PetscOptionsBool("-ksp_gmres_krylov_monitor", "Plot the Krylov directions", "KSPMonitorSet", flg, &flg, NULL);
546: if (flg) {
547: PetscViewers viewers;
548: PetscViewersCreate(PetscObjectComm((PetscObject)ksp), &viewers);
549: KSPMonitorSet(ksp, KSPGMRESMonitorKrylov, viewers, (PetscErrorCode(*)(void **))PetscViewersDestroy);
550: }
551: PetscOptionsHeadEnd();
552: return 0;
553: }
555: PetscErrorCode KSPGMRESSetHapTol_GMRES(KSP ksp, PetscReal tol)
556: {
557: KSP_GMRES *gmres = (KSP_GMRES *)ksp->data;
560: gmres->haptol = tol;
561: return 0;
562: }
564: PetscErrorCode KSPGMRESSetBreakdownTolerance_GMRES(KSP ksp, PetscReal tol)
565: {
566: KSP_GMRES *gmres = (KSP_GMRES *)ksp->data;
568: if (tol == PETSC_DEFAULT) {
569: gmres->breakdowntol = 0.1;
570: return 0;
571: }
573: gmres->breakdowntol = tol;
574: return 0;
575: }
577: PetscErrorCode KSPGMRESGetRestart_GMRES(KSP ksp, PetscInt *max_k)
578: {
579: KSP_GMRES *gmres = (KSP_GMRES *)ksp->data;
581: *max_k = gmres->max_k;
582: return 0;
583: }
585: PetscErrorCode KSPGMRESSetRestart_GMRES(KSP ksp, PetscInt max_k)
586: {
587: KSP_GMRES *gmres = (KSP_GMRES *)ksp->data;
590: if (!ksp->setupstage) {
591: gmres->max_k = max_k;
592: } else if (gmres->max_k != max_k) {
593: gmres->max_k = max_k;
594: ksp->setupstage = KSP_SETUP_NEW;
595: /* free the data structures, then create them again */
596: KSPReset_GMRES(ksp);
597: }
598: return 0;
599: }
601: PetscErrorCode KSPGMRESSetOrthogonalization_GMRES(KSP ksp, FCN fcn)
602: {
603: ((KSP_GMRES *)ksp->data)->orthog = fcn;
604: return 0;
605: }
607: PetscErrorCode KSPGMRESGetOrthogonalization_GMRES(KSP ksp, FCN *fcn)
608: {
609: *fcn = ((KSP_GMRES *)ksp->data)->orthog;
610: return 0;
611: }
613: PetscErrorCode KSPGMRESSetPreAllocateVectors_GMRES(KSP ksp)
614: {
615: KSP_GMRES *gmres;
617: gmres = (KSP_GMRES *)ksp->data;
618: gmres->q_preallocate = 1;
619: return 0;
620: }
622: PetscErrorCode KSPGMRESSetCGSRefinementType_GMRES(KSP ksp, KSPGMRESCGSRefinementType type)
623: {
624: KSP_GMRES *gmres = (KSP_GMRES *)ksp->data;
626: gmres->cgstype = type;
627: return 0;
628: }
630: PetscErrorCode KSPGMRESGetCGSRefinementType_GMRES(KSP ksp, KSPGMRESCGSRefinementType *type)
631: {
632: KSP_GMRES *gmres = (KSP_GMRES *)ksp->data;
634: *type = gmres->cgstype;
635: return 0;
636: }
638: /*@
639: KSPGMRESSetCGSRefinementType - Sets the type of iterative refinement to use
640: in the classical Gram Schmidt orthogonalization.
642: Logically Collective on ksp
644: Input Parameters:
645: + ksp - the Krylov space context
646: - type - the type of refinement
648: Options Database:
649: . -ksp_gmres_cgs_refinement_type <refine_never,refine_ifneeded,refine_always> - refinement type
651: Level: intermediate
653: .seealso: `KSPGMRESSetOrthogonalization()`, `KSPGMRESCGSRefinementType`, `KSPGMRESClassicalGramSchmidtOrthogonalization()`, `KSPGMRESGetCGSRefinementType()`,
654: `KSPGMRESGetOrthogonalization()`
655: @*/
656: PetscErrorCode KSPGMRESSetCGSRefinementType(KSP ksp, KSPGMRESCGSRefinementType type)
657: {
660: PetscTryMethod(ksp, "KSPGMRESSetCGSRefinementType_C", (KSP, KSPGMRESCGSRefinementType), (ksp, type));
661: return 0;
662: }
664: /*@
665: KSPGMRESGetCGSRefinementType - Gets the type of iterative refinement to use
666: in the classical Gram Schmidt orthogonalization.
668: Not Collective
670: Input Parameter:
671: . ksp - the Krylov space context
673: Output Parameter:
674: . type - the type of refinement
676: Options Database:
677: . -ksp_gmres_cgs_refinement_type <refine_never,refine_ifneeded,refine_always> - type of refinement
679: Level: intermediate
681: .seealso: `KSPGMRESSetOrthogonalization()`, `KSPGMRESCGSRefinementType`, `KSPGMRESClassicalGramSchmidtOrthogonalization()`, `KSPGMRESSetCGSRefinementType()`,
682: `KSPGMRESGetOrthogonalization()`
683: @*/
684: PetscErrorCode KSPGMRESGetCGSRefinementType(KSP ksp, KSPGMRESCGSRefinementType *type)
685: {
687: PetscUseMethod(ksp, "KSPGMRESGetCGSRefinementType_C", (KSP, KSPGMRESCGSRefinementType *), (ksp, type));
688: return 0;
689: }
691: /*@
692: KSPGMRESSetRestart - Sets number of iterations at which GMRES, FGMRES and LGMRES restarts.
694: Logically Collective on ksp
696: Input Parameters:
697: + ksp - the Krylov space context
698: - restart - integer restart value
700: Options Database:
701: . -ksp_gmres_restart <positive integer> - integer restart value
703: Note: The default value is 30.
705: Level: intermediate
707: .seealso: `KSPSetTolerances()`, `KSPGMRESSetOrthogonalization()`, `KSPGMRESSetPreAllocateVectors()`, `KSPGMRESGetRestart()`
708: @*/
709: PetscErrorCode KSPGMRESSetRestart(KSP ksp, PetscInt restart)
710: {
713: PetscTryMethod(ksp, "KSPGMRESSetRestart_C", (KSP, PetscInt), (ksp, restart));
714: return 0;
715: }
717: /*@
718: KSPGMRESGetRestart - Gets number of iterations at which GMRES, FGMRES and LGMRES restarts.
720: Not Collective
722: Input Parameter:
723: . ksp - the Krylov space context
725: Output Parameter:
726: . restart - integer restart value
728: Note: The default value is 30.
730: Level: intermediate
732: .seealso: `KSPSetTolerances()`, `KSPGMRESSetOrthogonalization()`, `KSPGMRESSetPreAllocateVectors()`, `KSPGMRESSetRestart()`
733: @*/
734: PetscErrorCode KSPGMRESGetRestart(KSP ksp, PetscInt *restart)
735: {
736: PetscUseMethod(ksp, "KSPGMRESGetRestart_C", (KSP, PetscInt *), (ksp, restart));
737: return 0;
738: }
740: /*@
741: KSPGMRESSetHapTol - Sets tolerance for determining happy breakdown in GMRES, FGMRES and LGMRES.
743: Logically Collective on ksp
745: Input Parameters:
746: + ksp - the Krylov space context
747: - tol - the tolerance
749: Options Database:
750: . -ksp_gmres_haptol <positive real value> - set tolerance for determining happy breakdown
752: Note: Happy breakdown is the rare case in GMRES where an 'exact' solution is obtained after
753: a certain number of iterations. If you attempt more iterations after this point unstable
754: things can happen hence very occasionally you may need to set this value to detect this condition
756: Level: intermediate
758: .seealso: `KSPSetTolerances()`
759: @*/
760: PetscErrorCode KSPGMRESSetHapTol(KSP ksp, PetscReal tol)
761: {
763: PetscTryMethod((ksp), "KSPGMRESSetHapTol_C", (KSP, PetscReal), ((ksp), (tol)));
764: return 0;
765: }
767: /*@
768: KSPGMRESSetBreakdownTolerance - Sets tolerance for determining divergence breakdown in GMRES.
770: Logically Collective on ksp
772: Input Parameters:
773: + ksp - the Krylov space context
774: - tol - the tolerance
776: Options Database:
777: . -ksp_gmres_breakdown_tolerance <positive real value> - set tolerance for determining divergence breakdown
779: Note: divergence breakdown occurs when GMRES residual increases significantly
780: during restart
782: Level: intermediate
784: .seealso: `KSPSetTolerances()`, `KSPGMRESSetHapTol()`
785: @*/
786: PetscErrorCode KSPGMRESSetBreakdownTolerance(KSP ksp, PetscReal tol)
787: {
789: PetscTryMethod((ksp), "KSPGMRESSetBreakdownTolerance_C", (KSP, PetscReal), (ksp, tol));
790: return 0;
791: }
793: /*MC
794: KSPGMRES - Implements the Generalized Minimal Residual method.
795: (Saad and Schultz, 1986) with restart
797: Options Database Keys:
798: + -ksp_gmres_restart <restart> - the number of Krylov directions to orthogonalize against
799: . -ksp_gmres_haptol <tol> - sets the tolerance for "happy ending" (exact convergence)
800: . -ksp_gmres_preallocate - preallocate all the Krylov search directions initially (otherwise groups of
801: vectors are allocated as needed)
802: . -ksp_gmres_classicalgramschmidt - use classical (unmodified) Gram-Schmidt to orthogonalize against the Krylov space (fast) (the default)
803: . -ksp_gmres_modifiedgramschmidt - use modified Gram-Schmidt in the orthogonalization (more stable, but slower)
804: . -ksp_gmres_cgs_refinement_type <refine_never,refine_ifneeded,refine_always> - determine if iterative refinement is used to increase the
805: stability of the classical Gram-Schmidt orthogonalization.
806: - -ksp_gmres_krylov_monitor - plot the Krylov space generated
808: Level: beginner
810: Notes:
811: Left and right preconditioning are supported, but not symmetric preconditioning.
813: References:
814: . * - YOUCEF SAAD AND MARTIN H. SCHULTZ, GMRES: A GENERALIZED MINIMAL RESIDUAL ALGORITHM FOR SOLVING NONSYMMETRIC LINEAR SYSTEMS.
815: SIAM J. ScI. STAT. COMPUT. Vo|. 7, No. 3, July 1986.
817: .seealso: `KSPCreate()`, `KSPSetType()`, `KSPType`, `KSP`, `KSPFGMRES`, `KSPLGMRES`,
818: `KSPGMRESSetRestart()`, `KSPGMRESSetHapTol()`, `KSPGMRESSetPreAllocateVectors()`, `KSPGMRESSetOrthogonalization()`, `KSPGMRESGetOrthogonalization()`,
819: `KSPGMRESClassicalGramSchmidtOrthogonalization()`, `KSPGMRESModifiedGramSchmidtOrthogonalization()`,
820: `KSPGMRESCGSRefinementType`, `KSPGMRESSetCGSRefinementType()`, `KSPGMRESGetCGSRefinementType()`, `KSPGMRESMonitorKrylov()`, `KSPSetPCSide()`
822: M*/
824: PETSC_EXTERN PetscErrorCode KSPCreate_GMRES(KSP ksp)
825: {
826: KSP_GMRES *gmres;
828: PetscNew(&gmres);
829: ksp->data = (void *)gmres;
831: KSPSetSupportedNorm(ksp, KSP_NORM_PRECONDITIONED, PC_LEFT, 4);
832: KSPSetSupportedNorm(ksp, KSP_NORM_UNPRECONDITIONED, PC_RIGHT, 3);
833: KSPSetSupportedNorm(ksp, KSP_NORM_PRECONDITIONED, PC_SYMMETRIC, 2);
834: KSPSetSupportedNorm(ksp, KSP_NORM_NONE, PC_RIGHT, 1);
835: KSPSetSupportedNorm(ksp, KSP_NORM_NONE, PC_LEFT, 1);
837: ksp->ops->buildsolution = KSPBuildSolution_GMRES;
838: ksp->ops->setup = KSPSetUp_GMRES;
839: ksp->ops->solve = KSPSolve_GMRES;
840: ksp->ops->reset = KSPReset_GMRES;
841: ksp->ops->destroy = KSPDestroy_GMRES;
842: ksp->ops->view = KSPView_GMRES;
843: ksp->ops->setfromoptions = KSPSetFromOptions_GMRES;
844: ksp->ops->computeextremesingularvalues = KSPComputeExtremeSingularValues_GMRES;
845: ksp->ops->computeeigenvalues = KSPComputeEigenvalues_GMRES;
846: ksp->ops->computeritz = KSPComputeRitz_GMRES;
847: PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESSetPreAllocateVectors_C", KSPGMRESSetPreAllocateVectors_GMRES);
848: PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESSetOrthogonalization_C", KSPGMRESSetOrthogonalization_GMRES);
849: PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESGetOrthogonalization_C", KSPGMRESGetOrthogonalization_GMRES);
850: PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESSetRestart_C", KSPGMRESSetRestart_GMRES);
851: PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESGetRestart_C", KSPGMRESGetRestart_GMRES);
852: PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESSetHapTol_C", KSPGMRESSetHapTol_GMRES);
853: PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESSetBreakdownTolerance_C", KSPGMRESSetBreakdownTolerance_GMRES);
854: PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESSetCGSRefinementType_C", KSPGMRESSetCGSRefinementType_GMRES);
855: PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESGetCGSRefinementType_C", KSPGMRESGetCGSRefinementType_GMRES);
857: gmres->haptol = 1.0e-30;
858: gmres->breakdowntol = 0.1;
859: gmres->q_preallocate = 0;
860: gmres->delta_allocate = GMRES_DELTA_DIRECTIONS;
861: gmres->orthog = KSPGMRESClassicalGramSchmidtOrthogonalization;
862: gmres->nrs = NULL;
863: gmres->sol_temp = NULL;
864: gmres->max_k = GMRES_DEFAULT_MAXK;
865: gmres->Rsvd = NULL;
866: gmres->cgstype = KSP_GMRES_CGS_REFINE_NEVER;
867: gmres->orthogwork = NULL;
868: return 0;
869: }