Actual source code: fgmres.c

  1: #define PETSCKSP_DLL

  3: /*
  4:     This file implements FGMRES (a Generalized Minimal Residual) method.  
  5:     Reference:  Saad, 1993.

  7:     Preconditioning:  It the preconditioner is constant then this fgmres
  8:     code is equivalent to RIGHT-PRECONDITIONED GMRES.

 10:     Restarts:  Restarts are basically solves with x0 not equal to zero.
 11:  
 12:        Contributed by Allison Baker

 14: */

 16:  #include src/ksp/ksp/impls/gmres/fgmres/fgmresp.h
 17: #define FGMRES_DELTA_DIRECTIONS 10
 18: #define FGMRES_DEFAULT_MAXK     30
 19: static PetscErrorCode FGMRESGetNewVectors(KSP,PetscInt);
 20: static PetscErrorCode FGMRESUpdateHessenberg(KSP,PetscInt,PetscTruth,PetscReal *);
 21: static PetscErrorCode BuildFgmresSoln(PetscScalar*,Vec,Vec,KSP,PetscInt);

 23: EXTERN PetscErrorCode KSPView_GMRES(KSP,PetscViewer);
 24: /*

 26:     KSPSetUp_FGMRES - Sets up the workspace needed by fgmres.

 28:     This is called once, usually automatically by KSPSolveQ() or KSPSetUp(),
 29:     but can be called directly by KSPSetUp().

 31: */
 34: PetscErrorCode    KSPSetUp_FGMRES(KSP ksp)
 35: {
 36:   PetscInt       size,hh,hes,rs,cc;
 38:   PetscInt       max_k,k;
 39:   KSP_FGMRES     *fgmres = (KSP_FGMRES *)ksp->data;

 42:   if (ksp->pc_side == PC_SYMMETRIC) {
 43:     SETERRQ(PETSC_ERR_SUP,"no symmetric preconditioning for KSPFGMRES");
 44:   } else if (ksp->pc_side == PC_LEFT) {
 45:     SETERRQ(PETSC_ERR_SUP,"no left preconditioning for KSPFGMRES");
 46:   }
 47:   max_k         = fgmres->max_k;
 48:   hh            = (max_k + 2) * (max_k + 1);
 49:   hes           = (max_k + 1) * (max_k + 1);
 50:   rs            = (max_k + 2);
 51:   cc            = (max_k + 1);  /* SS and CC are the same size */
 52:   size          = (hh + hes + rs + 2*cc) * sizeof(PetscScalar);

 54:   /* Allocate space and set pointers to beginning */
 55:   PetscMalloc(size,&fgmres->hh_origin);
 56:   PetscMemzero(fgmres->hh_origin,size);
 57:   PetscLogObjectMemory(ksp,size); /* HH - modified (by plane rotations) hessenburg */
 58:   fgmres->hes_origin = fgmres->hh_origin + hh;     /* HES - unmodified hessenburg */
 59:   fgmres->rs_origin  = fgmres->hes_origin + hes;   /* RS - the right-hand-side of the 
 60:                                                       Hessenberg system */
 61:   fgmres->cc_origin  = fgmres->rs_origin + rs;     /* CC - cosines for rotations */
 62:   fgmres->ss_origin  = fgmres->cc_origin + cc;     /* SS - sines for rotations */

 64:   if (ksp->calc_sings) {
 65:     /* Allocate workspace to hold Hessenberg matrix needed by Eispack */
 66:     size = (max_k + 3)*(max_k + 9)*sizeof(PetscScalar);
 67:     PetscMalloc(size,&fgmres->Rsvd);
 68:     PetscMalloc(5*(max_k+2)*sizeof(PetscReal),&fgmres->Dsvd);
 69:     PetscLogObjectMemory(ksp,size+5*(max_k+2)*sizeof(PetscReal));
 70:   }

 72:   /* Allocate array to hold pointers to user vectors.  Note that we need
 73:    4 + max_k + 1 (since we need it+1 vectors, and it <= max_k) */
 74:   PetscMalloc((VEC_OFFSET+2+max_k)*sizeof(void*),&fgmres->vecs);
 75:   fgmres->vecs_allocated = VEC_OFFSET + 2 + max_k;
 76:   PetscMalloc((VEC_OFFSET+2+max_k)*sizeof(void*),&fgmres->user_work);
 77:   PetscMalloc((VEC_OFFSET+2+max_k)*sizeof(PetscInt),&fgmres->mwork_alloc);
 78:   PetscLogObjectMemory(ksp,(VEC_OFFSET+2+max_k)*(2*sizeof(void*)+sizeof(PetscInt)));

 80:   /* New for FGMRES - Allocate array to hold pointers to preconditioned 
 81:      vectors - same sizes as user vectors above */
 82:   PetscMalloc((VEC_OFFSET+2+max_k)*sizeof(void*),&fgmres->prevecs);
 83:   PetscMalloc((VEC_OFFSET+2+max_k)*sizeof(void*),&fgmres->prevecs_user_work);
 84:   PetscLogObjectMemory(ksp,(VEC_OFFSET+2+max_k)*(2*sizeof(void*)));


 87:   /* if q_preallocate = 0 then only allocate one "chunck" of space (for 
 88:      5 vectors) - additional will then be allocated from FGMREScycle() 
 89:      as needed.  Otherwise, allocate all of the space that could be needed */
 90:   if (fgmres->q_preallocate) {
 91:     fgmres->vv_allocated   = VEC_OFFSET + 2 + max_k;
 92:   } else {
 93:     fgmres->vv_allocated    = 5;
 94:   }

 96:   /* space for work vectors */
 97:   KSPGetVecs(ksp,fgmres->vv_allocated,&fgmres->user_work[0],0,PETSC_NULL);
 98:   PetscLogObjectParents(ksp,fgmres->vv_allocated,fgmres->user_work[0]);
 99:   for (k=0; k < fgmres->vv_allocated; k++) {
100:     fgmres->vecs[k] = fgmres->user_work[0][k];
101:   }

103:   /* space for preconditioned vectors */
104:   KSPGetVecs(ksp,fgmres->vv_allocated,&fgmres->prevecs_user_work[0],0,PETSC_NULL);
105:   PetscLogObjectParents(ksp,fgmres->vv_allocated,fgmres->prevecs_user_work[0]);
106:   for (k=0; k < fgmres->vv_allocated; k++) {
107:     fgmres->prevecs[k] = fgmres->prevecs_user_work[0][k];
108:   }

110:   /* specify how many work vectors have been allocated in this 
111:      chunck" (the first one) */
112:   fgmres->mwork_alloc[0] = fgmres->vv_allocated;
113:   fgmres->nwork_alloc    = 1;

115:   return(0);
116: }

118: /* 
119:     FGMRESResidual - This routine computes the initial residual (NOT PRECONDITIONED) 
120: */
123: static PetscErrorCode FGMRESResidual(KSP ksp)
124: {
125:   KSP_FGMRES     *fgmres = (KSP_FGMRES *)(ksp->data);
126:   Mat            Amat,Pmat;
127:   MatStructure   pflag;

131:   PCGetOperators(ksp->pc,&Amat,&Pmat,&pflag);

133:   /* put A*x into VEC_TEMP */
134:   MatMult(Amat,ksp->vec_sol,VEC_TEMP);
135:   /* now put residual (-A*x + f) into vec_vv(0) */
136:   VecWAXPY(VEC_VV(0),-1.0,VEC_TEMP,ksp->vec_rhs);
137:   return(0);
138: }

140: /*

142:     FGMRESCycle - Run fgmres, possibly with restart.  Return residual 
143:                   history if requested.

145:     input parameters:
146: .         fgmres  - structure containing parameters and work areas

148:     output parameters:
149: .        itcount - number of iterations used.  If null, ignored.
150: .        converged - 0 if not converged

152:                   
153:     Notes:
154:     On entry, the value in vector VEC_VV(0) should be 
155:     the initial residual.


158:  */
161: PetscErrorCode FGMREScycle(PetscInt *itcount,KSP ksp)
162: {

164:   KSP_FGMRES     *fgmres = (KSP_FGMRES *)(ksp->data);
165:   PetscReal      res_norm;
166:   PetscReal      hapbnd,tt;
167:   PetscTruth     hapend = PETSC_FALSE;  /* indicates happy breakdown ending */
169:   PetscInt       loc_it;                /* local count of # of dir. in Krylov space */
170:   PetscInt       max_k = fgmres->max_k; /* max # of directions Krylov space */
171:   Mat            Amat,Pmat;
172:   MatStructure   pflag;


176:   /* Number of pseudo iterations since last restart is the number 
177:      of prestart directions */
178:   loc_it = 0;

180:   /* note: (fgmres->it) is always set one less than (loc_it) It is used in 
181:      KSPBUILDSolution_FGMRES, where it is passed to BuildFGmresSoln.  
182:      Note that when BuildFGmresSoln is called from this function, 
183:      (loc_it -1) is passed, so the two are equivalent */
184:   fgmres->it = (loc_it - 1);

186:   /* initial residual is in VEC_VV(0)  - compute its norm*/
187:   VecNorm(VEC_VV(0),NORM_2,&res_norm);

189:   /* first entry in right-hand-side of hessenberg system is just 
190:      the initial residual norm */
191:   *RS(0) = res_norm;

193:   /* FYI: AMS calls are for memory snooper */
194:   PetscObjectTakeAccess(ksp);
195:   ksp->rnorm = res_norm;
196:   PetscObjectGrantAccess(ksp);
197:   KSPLogResidualHistory(ksp,res_norm);

199:   /* check for the convergence - maybe the current guess is good enough */
200:   (*ksp->converged)(ksp,ksp->its,res_norm,&ksp->reason,ksp->cnvP);
201:   if (ksp->reason) {
202:     if (itcount) *itcount = 0;
203:     return(0);
204:   }

206:   /* scale VEC_VV (the initial residual) */
207:   VecScale(VEC_VV(0),1.0/res_norm);
208: 
209:   /* MAIN ITERATION LOOP BEGINNING*/
210:   /* keep iterating until we have converged OR generated the max number
211:      of directions OR reached the max number of iterations for the method */
212:   while (!ksp->reason && loc_it < max_k && ksp->its < ksp->max_it) {
213:     KSPLogResidualHistory(ksp,res_norm);
214:     fgmres->it = (loc_it - 1);
215:     KSPMonitor(ksp,ksp->its,res_norm);

217:     /* see if more space is needed for work vectors */
218:     if (fgmres->vv_allocated <= loc_it + VEC_OFFSET + 1) {
219:       FGMRESGetNewVectors(ksp,loc_it+1);
220:       /* (loc_it+1) is passed in as number of the first vector that should
221:          be allocated */
222:     }

224:     /* CHANGE THE PRECONDITIONER? */
225:     /* ModifyPC is the callback function that can be used to
226:        change the PC or its attributes before its applied */
227:     (*fgmres->modifypc)(ksp,ksp->its,loc_it,res_norm,fgmres->modifyctx);
228: 
229: 
230:     /* apply PRECONDITIONER to direction vector and store with 
231:        preconditioned vectors in prevec */
232:     KSP_PCApply(ksp,VEC_VV(loc_it),PREVEC(loc_it));
233: 
234:     PCGetOperators(ksp->pc,&Amat,&Pmat,&pflag);
235:     /* Multiply preconditioned vector by operator - put in VEC_VV(loc_it+1) */
236:     MatMult(Amat,PREVEC(loc_it),VEC_VV(1+loc_it));

238: 
239:     /* update hessenberg matrix and do Gram-Schmidt - new direction is in
240:        VEC_VV(1+loc_it)*/
241:     (*fgmres->orthog)(ksp,loc_it);

243:     /* new entry in hessenburg is the 2-norm of our new direction */
244:     VecNorm(VEC_VV(loc_it+1),NORM_2,&tt);
245:     *HH(loc_it+1,loc_it)   = tt;
246:     *HES(loc_it+1,loc_it)  = tt;

248:     /* Happy Breakdown Check */
249:     hapbnd  = PetscAbsScalar((tt) / *RS(loc_it));
250:     /* RS(loc_it) contains the res_norm from the last iteration  */
251:     hapbnd = PetscMin(fgmres->haptol,hapbnd);
252:     if (tt > hapbnd) {
253:         /* scale new direction by its norm */
254:         VecScale(VEC_VV(loc_it+1),1.0/tt);
255:     } else {
256:         /* This happens when the solution is exactly reached. */
257:         /* So there is no new direction... */
258:           VecSet(VEC_TEMP,0.0); /* set VEC_TEMP to 0 */
259:           hapend = PETSC_TRUE;
260:     }
261:     /* note that for FGMRES we could get HES(loc_it+1, loc_it)  = 0 and the
262:        current solution would not be exact if HES was singular.  Note that 
263:        HH non-singular implies that HES is no singular, and HES is guaranteed
264:        to be nonsingular when PREVECS are linearly independent and A is 
265:        nonsingular (in GMRES, the nonsingularity of A implies the nonsingularity 
266:        of HES). So we should really add a check to verify that HES is nonsingular.*/

268: 
269:     /* Now apply rotations to new col of hessenberg (and right side of system), 
270:        calculate new rotation, and get new residual norm at the same time*/
271:     FGMRESUpdateHessenberg(ksp,loc_it,hapend,&res_norm);
272:     if (ksp->reason) break;

274:     loc_it++;
275:     fgmres->it  = (loc_it-1);  /* Add this here in case it has converged */
276: 
277:     PetscObjectTakeAccess(ksp);
278:     ksp->its++;
279:     ksp->rnorm = res_norm;
280:     PetscObjectGrantAccess(ksp);

282:     (*ksp->converged)(ksp,ksp->its,res_norm,&ksp->reason,ksp->cnvP);

284:     /* Catch error in happy breakdown and signal convergence and break from loop */
285:     if (hapend) {
286:       if (!ksp->reason) {
287:         SETERRQ(0,"You reached the happy break down,but convergence was not indicated.");
288:       }
289:       break;
290:     }
291:   }
292:   /* END OF ITERATION LOOP */

294:   KSPLogResidualHistory(ksp,res_norm);

296:   /*
297:      Monitor if we know that we will not return for a restart */
298:   if (ksp->reason || ksp->its >= ksp->max_it) {
299:     KSPMonitor(ksp,ksp->its,res_norm);
300:   }

302:   if (itcount) *itcount    = loc_it;

304:   /*
305:     Down here we have to solve for the "best" coefficients of the Krylov
306:     columns, add the solution values together, and possibly unwind the
307:     preconditioning from the solution
308:    */
309: 
310:   /* Form the solution (or the solution so far) */
311:   /* Note: must pass in (loc_it-1) for iteration count so that BuildFgmresSoln
312:      properly navigates */

314:   BuildFgmresSoln(RS(0),ksp->vec_sol,ksp->vec_sol,ksp,loc_it-1);

316:   return(0);
317: }

319: /*  
320:     KSPSolve_FGMRES - This routine applies the FGMRES method.


323:    Input Parameter:
324: .     ksp - the Krylov space object that was set to use fgmres

326:    Output Parameter:
327: .     outits - number of iterations used

329: */

333: PetscErrorCode KSPSolve_FGMRES(KSP ksp)
334: {
336:   PetscInt       cycle_its = 0; /* iterations done in a call to FGMREScycle */
337:   KSP_FGMRES     *fgmres = (KSP_FGMRES *)ksp->data;
338:   PetscTruth     diagonalscale;

341:   PCDiagonalScale(ksp->pc,&diagonalscale);
342:   if (diagonalscale) SETERRQ1(PETSC_ERR_SUP,"Krylov method %s does not support diagonal scaling",ksp->type_name);
343:   if (ksp->normtype != KSP_NORM_UNPRECONDITIONED) SETERRQ(PETSC_ERR_ARG_WRONGSTATE,"Can only use FGMRES with unpreconditioned residual (it is coded with right preconditioning)");

345:   PetscObjectTakeAccess(ksp);
346:   ksp->its = 0;
347:   PetscObjectGrantAccess(ksp);

349:   /* Compute the initial (NOT preconditioned) residual */
350:   if (!ksp->guess_zero) {
351:     FGMRESResidual(ksp);
352:   } else { /* guess is 0 so residual is F (which is in ksp->vec_rhs) */
353:     VecCopy(ksp->vec_rhs,VEC_VV(0));
354:   }
355:   /* now the residual is in VEC_VV(0) - which is what 
356:      FGMREScycle expects... */
357: 
358:   FGMREScycle(&cycle_its,ksp);
359:   while (!ksp->reason) {
360:     FGMRESResidual(ksp);
361:     if (ksp->its >= ksp->max_it) break;
362:     FGMREScycle(&cycle_its,ksp);
363:   }
364:   /* mark lack of convergence */
365:   if (ksp->its >= ksp->max_it) ksp->reason = KSP_DIVERGED_ITS;

367:   return(0);
368: }

370: /*

372:    KSPDestroy_FGMRES - Frees all memory space used by the Krylov method.

374: */
377: PetscErrorCode KSPDestroy_FGMRES(KSP ksp)
378: {
379:   KSP_FGMRES     *fgmres = (KSP_FGMRES*)ksp->data;
381:   PetscInt       i;

384:   /* Free the Hessenberg matrices */
385:   PetscFree(fgmres->hh_origin);

387:   /* Free pointers to user variables */
388:   PetscFree(fgmres->vecs);
389:   PetscFree (fgmres->prevecs);

391:   /* free work vectors */
392:   for (i=0; i < fgmres->nwork_alloc; i++) {
393:     VecDestroyVecs(fgmres->user_work[i],fgmres->mwork_alloc[i]);
394:   }
395:   PetscFree(fgmres->user_work);

397:   for (i=0; i < fgmres->nwork_alloc; i++) {
398:     VecDestroyVecs(fgmres->prevecs_user_work[i],fgmres->mwork_alloc[i]);
399:   }
400:   PetscFree(fgmres->prevecs_user_work);

402:   PetscFree(fgmres->mwork_alloc);
403:   PetscFree(fgmres->nrs);
404:   if (fgmres->sol_temp) {VecDestroy(fgmres->sol_temp);}
405:   PetscFree(fgmres->Rsvd);
406:   PetscFree(fgmres->Dsvd);
407:   PetscFree(fgmres->orthogwork);
408:   if (fgmres->modifydestroy) {
409:     (*fgmres->modifydestroy)(fgmres->modifyctx);
410:   }
411:   PetscFree(ksp->data);

413:   /* clear composed functions */
414:   PetscObjectComposeFunctionDynamic((PetscObject)ksp,"KSPGMRESSetPreAllocateVectors_C","",PETSC_NULL);
415:   PetscObjectComposeFunctionDynamic((PetscObject)ksp,"KSPGMRESSetOrthogonalization_C","",PETSC_NULL);
416:   PetscObjectComposeFunctionDynamic((PetscObject)ksp,"KSPGMRESSetRestart_C","",PETSC_NULL);
417:   PetscObjectComposeFunctionDynamic((PetscObject)ksp,"KSPFGMRESSetModifyPC_C","",PETSC_NULL);
418:   PetscObjectComposeFunctionDynamic((PetscObject)ksp,"KSPGMRESSetCGSRefinementType_C","",PETSC_NULL);
419:   return(0);
420: }

422: /*
423:     BuildFgmresSoln - create the solution from the starting vector and the
424:                       current iterates.

426:     Input parameters:
427:         nrs - work area of size it + 1.
428:         vguess  - index of initial guess
429:         vdest - index of result.  Note that vguess may == vdest (replace
430:                 guess with the solution).
431:         it - HH upper triangular part is a block of size (it+1) x (it+1)  

433:      This is an internal routine that knows about the FGMRES internals.
434:  */
437: static PetscErrorCode BuildFgmresSoln(PetscScalar* nrs,Vec vguess,Vec vdest,KSP ksp,PetscInt it)
438: {
439:   PetscScalar    tt;
441:   PetscInt       ii,k,j;
442:   KSP_FGMRES     *fgmres = (KSP_FGMRES *)(ksp->data);

445:   /* Solve for solution vector that minimizes the residual */

447:   /* If it is < 0, no fgmres steps have been performed */
448:   if (it < 0) {
449:     if (vdest != vguess) {
450:       VecCopy(vguess,vdest);
451:     }
452:     return(0);
453:   }

455:   /* so fgmres steps HAVE been performed */

457:   /* solve the upper triangular system - RS is the right side and HH is 
458:      the upper triangular matrix  - put soln in nrs */
459:   if (*HH(it,it) != 0.0) {
460:     nrs[it] = *RS(it) / *HH(it,it);
461:   } else {
462:     nrs[it] = 0.0;
463:   }
464:   for (ii=1; ii<=it; ii++) {
465:     k   = it - ii;
466:     tt  = *RS(k);
467:     for (j=k+1; j<=it; j++) tt  = tt - *HH(k,j) * nrs[j];
468:     nrs[k]   = tt / *HH(k,k);
469:   }

471:   /* Accumulate the correction to the soln of the preconditioned prob. in 
472:      VEC_TEMP - note that we use the preconditioned vectors  */
473:   VecSet(VEC_TEMP,0.0); /* set VEC_TEMP components to 0 */
474:   VecMAXPY(VEC_TEMP,it+1,nrs,&PREVEC(0));

476:   /* put updated solution into vdest.*/
477:   if (vdest != vguess) {
478:     VecCopy(VEC_TEMP,vdest);
479:     VecAXPY(vdest,1.0,vguess);
480:   } else  {/* replace guess with solution */
481:     VecAXPY(vdest,1.0,VEC_TEMP);
482:   }
483:   return(0);
484: }

486: /*

488:     FGMRESUpdateHessenberg - Do the scalar work for the orthogonalization.  
489:                             Return new residual.

491:     input parameters:

493: .        ksp -    Krylov space object
494: .         it  -    plane rotations are applied to the (it+1)th column of the 
495:                   modified hessenberg (i.e. HH(:,it))
496: .        hapend - PETSC_FALSE not happy breakdown ending.

498:     output parameters:
499: .        res - the new residual
500:         
501:  */
504: static PetscErrorCode FGMRESUpdateHessenberg(KSP ksp,PetscInt it,PetscTruth hapend,PetscReal *res)
505: {
506:   PetscScalar   *hh,*cc,*ss,tt;
507:   PetscInt      j;
508:   KSP_FGMRES    *fgmres = (KSP_FGMRES *)(ksp->data);

511:   hh  = HH(0,it);  /* pointer to beginning of column to update - so 
512:                       incrementing hh "steps down" the (it+1)th col of HH*/
513:   cc  = CC(0);     /* beginning of cosine rotations */
514:   ss  = SS(0);     /* beginning of sine rotations */

516:   /* Apply all the previously computed plane rotations to the new column
517:      of the Hessenberg matrix */
518:   /* Note: this uses the rotation [conj(c)  s ; -s   c], c= cos(theta), s= sin(theta),
519:      and some refs have [c   s ; -conj(s)  c] (don't be confused!) */

521:   for (j=1; j<=it; j++) {
522:     tt  = *hh;
523: #if defined(PETSC_USE_COMPLEX)
524:     *hh = PetscConj(*cc) * tt + *ss * *(hh+1);
525: #else
526:     *hh = *cc * tt + *ss * *(hh+1);
527: #endif
528:     hh++;
529:     *hh = *cc++ * *hh - (*ss++ * tt);
530:     /* hh, cc, and ss have all been incremented one by end of loop */
531:   }

533:   /*
534:     compute the new plane rotation, and apply it to:
535:      1) the right-hand-side of the Hessenberg system (RS)
536:         note: it affects RS(it) and RS(it+1)
537:      2) the new column of the Hessenberg matrix
538:         note: it affects HH(it,it) which is currently pointed to 
539:         by hh and HH(it+1, it) (*(hh+1))  
540:     thus obtaining the updated value of the residual...
541:   */

543:   /* compute new plane rotation */

545:   if (!hapend) {
546: #if defined(PETSC_USE_COMPLEX)
547:     tt        = PetscSqrtScalar(PetscConj(*hh) * *hh + PetscConj(*(hh+1)) * *(hh+1));
548: #else
549:     tt        = PetscSqrtScalar(*hh * *hh + *(hh+1) * *(hh+1));
550: #endif
551:     if (tt == 0.0) {
552:       ksp->reason = KSP_DIVERGED_NULL;
553:       return(0);
554:     }

556:     *cc       = *hh / tt;   /* new cosine value */
557:     *ss       = *(hh+1) / tt;  /* new sine value */

559:     /* apply to 1) and 2) */
560:     *RS(it+1) = - (*ss * *RS(it));
561: #if defined(PETSC_USE_COMPLEX)
562:     *RS(it)   = PetscConj(*cc) * *RS(it);
563:     *hh       = PetscConj(*cc) * *hh + *ss * *(hh+1);
564: #else
565:     *RS(it)   = *cc * *RS(it);
566:     *hh       = *cc * *hh + *ss * *(hh+1);
567: #endif

569:     /* residual is the last element (it+1) of right-hand side! */
570:     *res      = PetscAbsScalar(*RS(it+1));

572:   } else { /* happy breakdown: HH(it+1, it) = 0, therfore we don't need to apply 
573:             another rotation matrix (so RH doesn't change).  The new residual is 
574:             always the new sine term times the residual from last time (RS(it)), 
575:             but now the new sine rotation would be zero...so the residual should
576:             be zero...so we will multiply "zero" by the last residual.  This might
577:             not be exactly what we want to do here -could just return "zero". */
578: 
579:     *res = 0.0;
580:   }
581:   return(0);
582: }

584: /*

586:    FGMRESGetNewVectors - This routine allocates more work vectors, starting from 
587:                          VEC_VV(it), and more preconditioned work vectors, starting 
588:                          from PREVEC(i).

590: */
593: static PetscErrorCode FGMRESGetNewVectors(KSP ksp,PetscInt it)
594: {
595:   KSP_FGMRES     *fgmres = (KSP_FGMRES *)ksp->data;
596:   PetscInt       nwork = fgmres->nwork_alloc; /* number of work vector chunks allocated */
597:   PetscInt       nalloc;                      /* number to allocate */
599:   PetscInt       k;
600: 
602:   nalloc = fgmres->delta_allocate; /* number of vectors to allocate 
603:                                       in a single chunk */

605:   /* Adjust the number to allocate to make sure that we don't exceed the
606:      number of available slots (fgmres->vecs_allocated)*/
607:   if (it + VEC_OFFSET + nalloc >= fgmres->vecs_allocated){
608:     nalloc = fgmres->vecs_allocated - it - VEC_OFFSET;
609:   }
610:   if (!nalloc) return(0);

612:   fgmres->vv_allocated += nalloc; /* vv_allocated is the number of vectors allocated */

614:   /* work vectors */
615:   KSPGetVecs(ksp,nalloc,&fgmres->user_work[nwork],0,PETSC_NULL);
616:   PetscLogObjectParents(ksp,nalloc,fgmres->user_work[nwork]);
617:   for (k=0; k < nalloc; k++) {
618:     fgmres->vecs[it+VEC_OFFSET+k] = fgmres->user_work[nwork][k];
619:   }
620:   /* specify size of chunk allocated */
621:   fgmres->mwork_alloc[nwork] = nalloc;

623:   /* preconditioned vectors */
624:   KSPGetVecs(ksp,nalloc,&fgmres->prevecs_user_work[nwork],0,PETSC_NULL);
625:   PetscLogObjectParents(ksp,nalloc,fgmres->prevecs_user_work[nwork]);
626:   for (k=0; k < nalloc; k++) {
627:     fgmres->prevecs[it+VEC_OFFSET+k] = fgmres->prevecs_user_work[nwork][k];
628:   }

630:   /* increment the number of work vector chunks */
631:   fgmres->nwork_alloc++;
632:   return(0);
633: }

635: /* 

637:    KSPBuildSolution_FGMRES

639:      Input Parameter:
640: .     ksp - the Krylov space object
641: .     ptr-

643:    Output Parameter:
644: .     result - the solution

646:    Note: this calls BuildFgmresSoln - the same function that FGMREScycle
647:    calls directly.  

649: */
652: PetscErrorCode KSPBuildSolution_FGMRES(KSP ksp,Vec ptr,Vec *result)
653: {
654:   KSP_FGMRES     *fgmres = (KSP_FGMRES *)ksp->data;

658:   if (!ptr) {
659:     if (!fgmres->sol_temp) {
660:       VecDuplicate(ksp->vec_sol,&fgmres->sol_temp);
661:       PetscLogObjectParent(ksp,fgmres->sol_temp);
662:     }
663:     ptr = fgmres->sol_temp;
664:   }
665:   if (!fgmres->nrs) {
666:     /* allocate the work area */
667:     PetscMalloc(fgmres->max_k*sizeof(PetscScalar),&fgmres->nrs);
668:     PetscLogObjectMemory(ksp,fgmres->max_k*sizeof(PetscScalar));
669:   }
670: 
671:   BuildFgmresSoln(fgmres->nrs,ksp->vec_sol,ptr,ksp,fgmres->it);
672:   *result = ptr;
673: 
674:   return(0);
675: }


681: PetscErrorCode KSPSetFromOptions_FGMRES(KSP ksp)
682: {
684:   PetscTruth     flg;

687:   KSPSetFromOptions_GMRES(ksp);
688:   PetscOptionsHead("KSP flexible GMRES Options");
689:     PetscOptionsTruthGroupBegin("-ksp_fgmres_modifypcnochange","do not vary the preconditioner","KSPFGMRESSetModifyPC",&flg);
690:     if (flg) {KSPFGMRESSetModifyPC(ksp,KSPFGMRESModifyPCNoChange,0,0);}
691:     PetscOptionsTruthGroupEnd("-ksp_fgmres_modifypcksp","vary the KSP based preconditioner","KSPFGMRESSetModifyPC",&flg);
692:     if (flg) {KSPFGMRESSetModifyPC(ksp,KSPFGMRESModifyPCKSP,0,0);}
693:   PetscOptionsTail();
694:   return(0);
695: }

697: EXTERN PetscErrorCode KSPComputeExtremeSingularValues_GMRES(KSP,PetscReal *,PetscReal *);
698: EXTERN PetscErrorCode KSPComputeEigenvalues_GMRES(KSP,PetscInt,PetscReal *,PetscReal *,PetscInt *);

701: typedef PetscErrorCode (*FCN2)(void*);
705: PetscErrorCode  KSPFGMRESSetModifyPC_FGMRES(KSP ksp,FCN1 fcn,void *ctx,FCN2 d)
706: {
709:   ((KSP_FGMRES *)ksp->data)->modifypc      = fcn;
710:   ((KSP_FGMRES *)ksp->data)->modifydestroy = d;
711:   ((KSP_FGMRES *)ksp->data)->modifyctx     = ctx;
712:   return(0);
713: }

717: EXTERN PetscErrorCode  KSPGMRESSetPreAllocateVectors_GMRES(KSP);
718: EXTERN PetscErrorCode  KSPGMRESSetRestart_GMRES(KSP,PetscInt);
719: EXTERN PetscErrorCode  KSPGMRESSetOrthogonalization_GMRES(KSP,PetscErrorCode (*)(KSP,PetscInt));

722: EXTERN PetscErrorCode KSPDestroy_GMRES_Internal(KSP);

726: PetscErrorCode KSPDestroy_FGMRES_Internal(KSP ksp)
727: {
728:   KSP_FGMRES     *gmres = (KSP_FGMRES*)ksp->data;

732:   KSPDestroy_GMRES_Internal(ksp);
733:   PetscFree (gmres->prevecs);
734:   PetscFree(gmres->prevecs_user_work);
735:   if (gmres->modifydestroy) {
736:     (*gmres->modifydestroy)(gmres->modifyctx);
737:   }
738:   return(0);
739: }

744: PetscErrorCode  KSPGMRESSetRestart_FGMRES(KSP ksp,PetscInt max_k)
745: {
746:   KSP_FGMRES     *gmres = (KSP_FGMRES *)ksp->data;

750:   if (max_k < 1) SETERRQ(PETSC_ERR_ARG_OUTOFRANGE,"Restart must be positive");
751:   if (!ksp->setupcalled) {
752:     gmres->max_k = max_k;
753:   } else if (gmres->max_k != max_k) {
754:      gmres->max_k = max_k;
755:      ksp->setupcalled = 0;
756:      /* free the data structures, then create them again */
757:      KSPDestroy_FGMRES_Internal(ksp);
758:   }
759:   return(0);
760: }

764: EXTERN PetscErrorCode  KSPGMRESSetCGSRefinementType_GMRES(KSP,KSPGMRESCGSRefinementType);

767: /*MC
768:      KSPFGMRES - Implements the Flexible Generalized Minimal Residual method.  
769:                 developed by Saad with restart


772:    Options Database Keys:
773: +   -ksp_gmres_restart <restart> - the number of Krylov directions to orthogonalize against
774: .   -ksp_gmres_haptol <tol> - sets the tolerance for "happy ending" (exact convergence)
775: .   -ksp_gmres_preallocate - preallocate all the Krylov search directions initially (otherwise groups of 
776:                              vectors are allocated as needed)
777: .   -ksp_gmres_classicalgramschmidt - use classical (unmodified) Gram-Schmidt to orthogonalize against the Krylov space (fast) (the default)
778: .   -ksp_gmres_modifiedgramschmidt - use modified Gram-Schmidt in the orthogonalization (more stable, but slower)
779: .   -ksp_gmres_cgs_refinement_type <never,ifneeded,always> - determine if iterative refinement is used to increase the 
780:                                    stability of the classical Gram-Schmidt  orthogonalization.
781: .   -ksp_gmres_krylov_monitor - plot the Krylov space generated
782: .   -ksp_fgmres_modifypcnochange - do not change the preconditioner between iterations
783: -   -ksp_fgmres_modifypcksp - modify the preconditioner using KSPFGMRESModifyPCKSP()

785:    Level: beginner

787:     Notes: See KSPFGMRESSetModifyPC() for how to vary the preconditioner between iterations
788:            This object is subclassed off of KSPGMRES

790: .seealso:  KSPCreate(), KSPSetType(), KSPType (for list of available types), KSP, KSPGMRES, KSPLGMRES,
791:            KSPGMRESSetRestart(), KSPGMRESSetHapTol(), KSPGMRESSetPreAllocateVectors(), KSPGMRESSetOrthogonalization()
792:            KSPGMRESClassicalGramSchmidtOrthogonalization(), KSPGMRESModifiedGramSchmidtOrthogonalization(),
793:            KSPGMRESCGSRefinementType, KSPGMRESSetCGSRefinementType(), KSPGMRESMonitorKrylov(), KSPFGMRESSetModifyPC(),
794:            KSPFGMRESModifyPCKSP()

796: M*/

801: PetscErrorCode  KSPCreate_FGMRES(KSP ksp)
802: {
803:   KSP_FGMRES     *fgmres;

807:   PetscNew(KSP_FGMRES,&fgmres);
808:   PetscLogObjectMemory(ksp,sizeof(KSP_FGMRES));
809:   ksp->data                              = (void*)fgmres;
810:   ksp->ops->buildsolution                = KSPBuildSolution_FGMRES;

812:   ksp->ops->setup                        = KSPSetUp_FGMRES;
813:   ksp->ops->solve                        = KSPSolve_FGMRES;
814:   ksp->ops->destroy                      = KSPDestroy_FGMRES;
815:   ksp->ops->view                         = KSPView_GMRES;
816:   ksp->ops->setfromoptions               = KSPSetFromOptions_FGMRES;
817:   ksp->ops->computeextremesingularvalues = KSPComputeExtremeSingularValues_GMRES;
818:   ksp->ops->computeeigenvalues           = KSPComputeEigenvalues_GMRES;

820:   PetscObjectComposeFunctionDynamic((PetscObject)ksp,"KSPGMRESSetPreAllocateVectors_C",
821:                                     "KSPGMRESSetPreAllocateVectors_GMRES",
822:                                      KSPGMRESSetPreAllocateVectors_GMRES);
823:   PetscObjectComposeFunctionDynamic((PetscObject)ksp,"KSPGMRESSetOrthogonalization_C",
824:                                     "KSPGMRESSetOrthogonalization_GMRES",
825:                                      KSPGMRESSetOrthogonalization_GMRES);
826:   PetscObjectComposeFunctionDynamic((PetscObject)ksp,"KSPGMRESSetRestart_C",
827:                                     "KSPGMRESSetRestart_FGMRES",
828:                                      KSPGMRESSetRestart_FGMRES);
829:   PetscObjectComposeFunctionDynamic((PetscObject)ksp,"KSPFGMRESSetModifyPC_C",
830:                                     "KSPFGMRESSetModifyPC_FGMRES",
831:                                      KSPFGMRESSetModifyPC_FGMRES);
832:   PetscObjectComposeFunctionDynamic((PetscObject)ksp,"KSPGMRESSetCGSRefinementType_C",
833:                                     "KSPGMRESSetCGSRefinementType_GMRES",
834:                                      KSPGMRESSetCGSRefinementType_GMRES);


837:   fgmres->haptol              = 1.0e-30;
838:   fgmres->q_preallocate       = 0;
839:   fgmres->delta_allocate      = FGMRES_DELTA_DIRECTIONS;
840:   fgmres->orthog              = KSPGMRESClassicalGramSchmidtOrthogonalization;
841:   fgmres->nrs                 = 0;
842:   fgmres->sol_temp            = 0;
843:   fgmres->max_k               = FGMRES_DEFAULT_MAXK;
844:   fgmres->Rsvd                = 0;
845:   fgmres->orthogwork          = 0;
846:   fgmres->modifypc            = KSPFGMRESModifyPCNoChange;
847:   fgmres->modifyctx           = PETSC_NULL;
848:   fgmres->modifydestroy       = PETSC_NULL;
849:   fgmres->cgstype             = KSP_GMRES_CGS_REFINE_NEVER;
850:   /*
851:         This is not great since it changes this without explicit request from the user
852:      but there is no left preconditioning in the FGMRES
853:   */
854:   PetscInfo(ksp,"WARNING! Setting PC_SIDE for FGMRES to right!\n");
855:   ksp->pc_side  = PC_RIGHT;
856:   ksp->normtype = KSP_NORM_UNPRECONDITIONED;
857:   return(0);
858: }