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: }