Actual source code: fgmres.c

  1: /* $Id: fgmres.c,v 1.29 2001/08/07 21:30:49 bsmith Exp $ */

  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/sles/ksp/impls/fgmres/fgmresp.h
 17: #define FGMRES_DELTA_DIRECTIONS 10
 18: #define FGMRES_DEFAULT_MAXK     30
 19: static int    FGMRESGetNewVectors(KSP,int);
 20: static int    FGMRESUpdateHessenberg(KSP,int,PetscTruth,PetscReal *);
 21: static int    BuildFgmresSoln(PetscScalar*,Vec,Vec,KSP,int);

 23: /*

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

 27:     This is called once, usually automatically by SLESSolve() or SLESSetUp(),
 28:     but can be called directly by KSPSetUp().

 30: */
 31: #undef __FUNCT__  
 33: int    KSPSetUp_FGMRES(KSP ksp)
 34: {
 35:   unsigned  int size,hh,hes,rs,cc;
 36:   int           ierr,max_k,k;
 37:   KSP_FGMRES    *fgmres = (KSP_FGMRES *)ksp->data;

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

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

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

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

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


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

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

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

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

113:   return(0);
114: }

116: /* 
117:     FGMRESResidual - This routine computes the initial residual (NOT PRECONDITIONED) 
118: */
119: #undef __FUNCT__  
121: static int FGMRESResidual(KSP ksp)
122: {
123:   KSP_FGMRES   *fgmres = (KSP_FGMRES *)(ksp->data);
124:   PetscScalar  mone = -1.0;
125:   Mat          Amat,Pmat;
126:   MatStructure pflag;
127:   int          ierr;

130:   PCGetOperators(ksp->B,&Amat,&Pmat,&pflag);

132:   /* put A*x into VEC_TEMP */
133:   MatMult(Amat,VEC_SOLN,VEC_TEMP);
134:   /* now put residual (-A*x + f) into vec_vv(0) */
135:   VecWAXPY(&mone,VEC_TEMP,VEC_RHS,VEC_VV(0));
136:   return(0);
137: }

139: /*

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

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

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

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


157:  */
158: #undef __FUNCT__  
160: int FGMREScycle(int *itcount,KSP ksp)
161: {

163:   KSP_FGMRES   *fgmres = (KSP_FGMRES *)(ksp->data);
164:   PetscReal    res_norm;
165:   PetscReal    hapbnd,tt;
166:   PetscScalar  zero = 0.0;
167:   PetscScalar  tmp;
168:   PetscTruth   hapend = PETSC_FALSE;  /* indicates happy breakdown ending */
169:   int          ierr;
170:   int          loc_it;                /* local count of # of dir. in Krylov space */
171:   int          max_k = fgmres->max_k; /* max # of directions Krylov space */
172:   int          max_it = ksp->max_it;  /* max # of overall iterations for the method */
173:   Mat          Amat,Pmat;
174:   MatStructure pflag;


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

182:   /* initial residual is in VEC_VV(0)  - compute its norm*/
183:   ierr   = VecNorm(VEC_VV(0),NORM_2,&res_norm);

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

189:   /* check for the convergence - maybe the current guess is good enough */
190:   (*ksp->converged)(ksp,ksp->its,res_norm,&ksp->reason,ksp->cnvP);
191:   if (ksp->reason) {
192:     if (itcount) *itcount = 0;
193:     return(0);
194:   }

196:   /* scale VEC_VV (the initial residual) */
197:   tmp = 1.0/res_norm; VecScale(&tmp,VEC_VV(0));

199:   /* FYI: AMS calls are for memory snooper */
200:   PetscObjectTakeAccess(ksp);
201:   ksp->rnorm = res_norm;
202:   PetscObjectGrantAccess(ksp);


205:   /* note: (fgmres->it) is always set one less than (loc_it) It is used in 
206:      KSPBUILDSolution_FGMRES, where it is passed to BuildFGmresSoln.  
207:      Note that when BuildFGmresSoln is called from this function, 
208:      (loc_it -1) is passed, so the two are equivalent */
209:   fgmres->it = (loc_it - 1);
210: 
211:   /* MAIN ITERATION LOOP BEGINNING*/
212:   /* keep iterating until we have converged OR generated the max number
213:      of directions OR reached the max number of iterations for the method */
214:   (*ksp->converged)(ksp,ksp->its,res_norm,&ksp->reason,ksp->cnvP);
215:   while (!ksp->reason && loc_it < max_k && ksp->its < max_it) {
216:     KSPLogResidualHistory(ksp,res_norm);
217:     fgmres->it = (loc_it - 1);
218:     KSPMonitor(ksp,ksp->its,res_norm);

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

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

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

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

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

272: 
273:     /* Now apply rotations to new col of hessenberg (and right side of system), 
274:        calculate new rotation, and get new residual norm at the same time*/
275:     FGMRESUpdateHessenberg(ksp,loc_it,hapend,&res_norm);
276:     loc_it++;
277:     fgmres->it  = (loc_it-1);  /* Add this here in case it has converged */
278: 
279:     PetscObjectTakeAccess(ksp);
280:     ksp->its++;
281:     ksp->rnorm = res_norm;
282:     PetscObjectGrantAccess(ksp);

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

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

296:   KSPLogResidualHistory(ksp,res_norm);

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

304:   if (itcount) *itcount    = loc_it;

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

316:   BuildFgmresSoln(RS(0),VEC_SOLN,VEC_SOLN,ksp,loc_it-1);

318:   return(0);
319: }

321: /*  
322:     KSPSolve_FGMRES - This routine applies the FGMRES method.


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

328:    Output Parameter:
329: .     outits - number of iterations used

331: */
332: #undef __FUNCT__  

335: int KSPSolve_FGMRES(KSP ksp,int *outits)
336: {
337:   int        ierr;
338:   int        cycle_its; /* iterations done in a call to FGMREScycle */
339:   int        itcount;   /* running total of iterations, incl. those in restarts */
340:   KSP_FGMRES *fgmres = (KSP_FGMRES *)ksp->data;
341:   PetscTruth diagonalscale;

344:   ierr    = PCDiagonalScale(ksp->B,&diagonalscale);
345:   if (diagonalscale) SETERRQ1(1,"Krylov method %s does not support diagonal scaling",ksp->type_name);

347:   PetscObjectTakeAccess(ksp);
348:   ksp->its = 0;
349:   PetscObjectGrantAccess(ksp);

351:   /* initialize */
352:   itcount  = 0;

354:   /* Compute the initial (NOT preconditioned) residual */
355:   if (!ksp->guess_zero) {
356:     FGMRESResidual(ksp);
357:   } else { /* guess is 0 so residual is F (which is in VEC_RHS) */
358:     VecCopy(VEC_RHS,VEC_VV(0));
359:   }
360:   /* now the residual is in VEC_VV(0) - which is what 
361:      FGMREScycle expects... */
362: 
363:   ierr    = FGMREScycle(&cycle_its,ksp);
364:   itcount += cycle_its;
365:   while (!ksp->reason) {
366:     ierr     = FGMRESResidual(ksp);
367:     if (itcount >= ksp->max_it) break;
368:     ierr     = FGMREScycle(&cycle_its,ksp);
369:     itcount += cycle_its;
370:   }
371:   /* mark lack of convergence */
372:   if (itcount >= ksp->max_it) ksp->reason = KSP_DIVERGED_ITS;

374:   *outits = itcount;
375:   return(0);
376: }

378: /*

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

382: */
383: #undef __FUNCT__  
385: int KSPDestroy_FGMRES(KSP ksp)
386: {
387:   KSP_FGMRES *fgmres = (KSP_FGMRES*)ksp->data;
388:   int       i,ierr;

391:   /* Free the Hessenberg matrices */
392:   if (fgmres->hh_origin) {PetscFree(fgmres->hh_origin);}

394:   /* Free pointers to user variables */
395:   if (fgmres->vecs) {PetscFree(fgmres->vecs);}
396:   if (fgmres->prevecs) {PetscFree (fgmres->prevecs);}

398:   /* free work vectors */
399:   for (i=0; i < fgmres->nwork_alloc; i++) {
400:     VecDestroyVecs(fgmres->user_work[i],fgmres->mwork_alloc[i]);
401:   }
402:   if (fgmres->user_work)  {PetscFree(fgmres->user_work);}

404:   for (i=0; i < fgmres->nwork_alloc; i++) {
405:     VecDestroyVecs(fgmres->prevecs_user_work[i],fgmres->mwork_alloc[i]);
406:   }
407:   if (fgmres->prevecs_user_work) {PetscFree(fgmres->prevecs_user_work);}

409:   if (fgmres->mwork_alloc) {PetscFree(fgmres->mwork_alloc);}
410:   if (fgmres->nrs) {PetscFree(fgmres->nrs);}
411:   if (fgmres->sol_temp) {VecDestroy(fgmres->sol_temp);}
412:   if (fgmres->Rsvd) {PetscFree(fgmres->Rsvd);}
413:   if (fgmres->Dsvd) {PetscFree(fgmres->Dsvd);}
414:   if (fgmres->modifydestroy) {
415:     (*fgmres->modifydestroy)(fgmres->modifyctx);
416:   }
417:   PetscFree(fgmres);
418:   return(0);
419: }

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

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

432:      This is an internal routine that knows about the FGMRES internals.
433:  */
434: #undef __FUNCT__  
436: static int BuildFgmresSoln(PetscScalar* nrs,Vec vguess,Vec vdest,KSP ksp,int it)
437: {
438:   PetscScalar  tt,zero = 0.0,one = 1.0;
439:   int          ierr,ii,k,j;
440:   KSP_FGMRES   *fgmres = (KSP_FGMRES *)(ksp->data);

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

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

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

455:   /* solve the upper triangular system - RS is the right side and HH is 
456:      the upper triangular matrix  - put soln in nrs */
457:   nrs[it] = *RS(it) / *HH(it,it);
458:   for (ii=1; ii<=it; ii++) {
459:     k   = it - ii;
460:     tt  = *RS(k);
461:     for (j=k+1; j<=it; j++) tt  = tt - *HH(k,j) * nrs[j];
462:     nrs[k]   = tt / *HH(k,k);
463:   }

465:   /* Accumulate the correction to the soln of the preconditioned prob. in 
466:      VEC_TEMP - note that we use the preconditioned vectors  */
467:   VecSet(&zero,VEC_TEMP); /* set VEC_TEMP components to 0 */
468:   VecMAXPY(it+1,nrs,VEC_TEMP,&PREVEC(0));

470:   /* put updated solution into vdest.*/
471:   if (vdest != vguess) {
472:     VecCopy(VEC_TEMP,vdest);
473:     VecAXPY(&one,vguess,vdest);
474:   } else  {/* replace guess with solution */
475:     VecAXPY(&one,VEC_TEMP,vdest);
476:   }
477:   return(0);
478: }

480: /*

482:     FGMRESUpdateHessenberg - Do the scalar work for the orthogonalization.  
483:                             Return new residual.

485:     input parameters:

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

492:     output parameters:
493: .        res - the new residual
494:         
495:  */
496: #undef __FUNCT__  
498: static int FGMRESUpdateHessenberg(KSP ksp,int it,PetscTruth hapend,PetscReal *res)
499: {
500:   PetscScalar   *hh,*cc,*ss,tt;
501:   int           j;
502:   KSP_FGMRES    *fgmres = (KSP_FGMRES *)(ksp->data);

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

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

515:   for (j=1; j<=it; j++) {
516:     tt  = *hh;
517: #if defined(PETSC_USE_COMPLEX)
518:     *hh = PetscConj(*cc) * tt + *ss * *(hh+1);
519: #else
520:     *hh = *cc * tt + *ss * *(hh+1);
521: #endif
522:     hh++;
523:     *hh = *cc++ * *hh - (*ss++ * tt);
524:     /* hh, cc, and ss have all been incremented one by end of loop */
525:   }

527:   /*
528:     compute the new plane rotation, and apply it to:
529:      1) the right-hand-side of the Hessenberg system (RS)
530:         note: it affects RS(it) and RS(it+1)
531:      2) the new column of the Hessenberg matrix
532:         note: it affects HH(it,it) which is currently pointed to 
533:         by hh and HH(it+1, it) (*(hh+1))  
534:     thus obtaining the updated value of the residual...
535:   */

537:   /* compute new plane rotation */

539:   if (!hapend) {
540: #if defined(PETSC_USE_COMPLEX)
541:     tt        = PetscSqrtScalar(PetscConj(*hh) * *hh + PetscConj(*(hh+1)) * *(hh+1));
542: #else
543:     tt        = PetscSqrtScalar(*hh * *hh + *(hh+1) * *(hh+1));
544: #endif
545:     if (tt == 0.0) {SETERRQ(PETSC_ERR_KSP_BRKDWN,"Your matrix or preconditioner is the null operator");}
546:     *cc       = *hh / tt;   /* new cosine value */
547:     *ss       = *(hh+1) / tt;  /* new sine value */

549:     /* apply to 1) and 2) */
550:     *RS(it+1) = - (*ss * *RS(it));
551: #if defined(PETSC_USE_COMPLEX)
552:     *RS(it)   = PetscConj(*cc) * *RS(it);
553:     *hh       = PetscConj(*cc) * *hh + *ss * *(hh+1);
554: #else
555:     *RS(it)   = *cc * *RS(it);
556:     *hh       = *cc * *hh + *ss * *(hh+1);
557: #endif

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

562:   } else { /* happy breakdown: HH(it+1, it) = 0, therfore we don't need to apply 
563:             another rotation matrix (so RH doesn't change).  The new residual is 
564:             always the new sine term times the residual from last time (RS(it)), 
565:             but now the new sine rotation would be zero...so the residual should
566:             be zero...so we will multiply "zero" by the last residual.  This might
567:             not be exactly what we want to do here -could just return "zero". */
568: 
569:     *res = 0.0;
570:   }
571:   return(0);
572: }

574: /*

576:    FGMRESGetNewVectors - This routine allocates more work vectors, starting from 
577:                          VEC_VV(it), and more preconditioned work vectors, starting 
578:                          from PREVEC(i).

580: */
581: #undef __FUNCT__  
583: static int FGMRESGetNewVectors(KSP ksp,int it)
584: {
585:   KSP_FGMRES *fgmres = (KSP_FGMRES *)ksp->data;
586:   int        nwork = fgmres->nwork_alloc; /* number of work vector chunks allocated */
587:   int        nalloc;                      /* number to allocate */
588:   int        k,ierr;
589: 
591:   nalloc = fgmres->delta_allocate; /* number of vectors to allocate 
592:                                       in a single chunk */

594:   /* Adjust the number to allocate to make sure that we don't exceed the
595:      number of available slots (fgmres->vecs_allocated)*/
596:   if (it + VEC_OFFSET + nalloc >= fgmres->vecs_allocated){
597:     nalloc = fgmres->vecs_allocated - it - VEC_OFFSET;
598:   }
599:   if (!nalloc) return(0);

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

603:   /* work vectors */
604:   VecDuplicateVecs(VEC_RHS,nalloc,&fgmres->user_work[nwork]);
605:   PetscLogObjectParents(ksp,nalloc,fgmres->user_work[nwork]);
606:   for (k=0; k < nalloc; k++) {
607:     fgmres->vecs[it+VEC_OFFSET+k] = fgmres->user_work[nwork][k];
608:   }
609:   /* specify size of chunk allocated */
610:   fgmres->mwork_alloc[nwork] = nalloc;

612:   /* preconditioned vectors */
613:   VecDuplicateVecs(VEC_RHS,nalloc,&fgmres->prevecs_user_work[nwork]);
614:   PetscLogObjectParents(ksp,nalloc,fgmres->prevecs_user_work[nwork]);
615:   for (k=0; k < nalloc; k++) {
616:     fgmres->prevecs[it+VEC_OFFSET+k] = fgmres->prevecs_user_work[nwork][k];
617:   }

619:   /* increment the number of work vector chunks */
620:   fgmres->nwork_alloc++;
621:   return(0);
622: }

624: /* 

626:    KSPBuildSolution_FGMRES

628:      Input Parameter:
629: .     ksp - the Krylov space object
630: .     ptr-

632:    Output Parameter:
633: .     result - the solution

635:    Note: this calls BuildFgmresSoln - the same function that FGMREScycle
636:    calls directly.  

638: */
639: #undef __FUNCT__  
641: int KSPBuildSolution_FGMRES(KSP ksp,Vec ptr,Vec *result)
642: {
643:   KSP_FGMRES *fgmres = (KSP_FGMRES *)ksp->data;
644:   int        ierr;

647:   if (!ptr) {
648:     if (!fgmres->sol_temp) {
649:       VecDuplicate(ksp->vec_sol,&fgmres->sol_temp);
650:       PetscLogObjectParent(ksp,fgmres->sol_temp);
651:     }
652:     ptr = fgmres->sol_temp;
653:   }
654:   if (!fgmres->nrs) {
655:     /* allocate the work area */
656:     PetscMalloc(fgmres->max_k*sizeof(PetscScalar),&fgmres->nrs);
657:     PetscLogObjectMemory(ksp,fgmres->max_k*sizeof(PetscScalar));
658:   }
659: 
660:   BuildFgmresSoln(fgmres->nrs,VEC_SOLN,ptr,ksp,fgmres->it);
661:   *result = ptr;
662: 
663:   return(0);
664: }

666: /*

668:    KSPView_FGMRES -Prints information about the current Krylov method 
669:                   being used.

671:  */
672: #undef __FUNCT__  
674: int KSPView_FGMRES(KSP ksp,PetscViewer viewer)
675: {
676:   KSP_FGMRES   *fgmres = (KSP_FGMRES *)ksp->data;
677:   char         *cstr;
678:   int          ierr;
679:   PetscTruth   isascii;

682:   PetscTypeCompare((PetscObject)viewer,PETSC_VIEWER_ASCII,&isascii);
683:   if (isascii) {
684:     if (fgmres->orthog == KSPGMRESUnmodifiedGramSchmidtOrthogonalization) {
685:       cstr = "Unmodified Gram-Schmidt Orthogonalization";
686:     } else if (fgmres->orthog == KSPGMRESModifiedGramSchmidtOrthogonalization) {
687:       cstr = "Modified Gram-Schmidt Orthogonalization";
688:     } else if (fgmres->orthog == KSPGMRESIROrthogonalization) {
689:       cstr = "Unmodified Gram-Schmidt + 1 step Iterative Refinement Orthogonalization";
690:     } else {
691:       cstr = "unknown orthogonalization";
692:     }
693:     PetscViewerASCIIPrintf(viewer,"  FGMRES: restart=%d, using %sn",fgmres->max_k,cstr);
694:   } else {
695:     SETERRQ(1,"Viewer type not supported for this object");
696:   }
697:   return(0);
698: }

700: #undef __FUNCT__  
702: int KSPSetFromOptions_FGMRES(KSP ksp)
703: {
704:   int         ierr,restart;
705:   PetscReal   haptol;
706:   KSP_FGMRES *gmres = (KSP_FGMRES*)ksp->data;
707:   PetscTruth  flg;

710:   PetscOptionsHead("KSP flexible GMRES Options");
711:     PetscOptionsInt("-ksp_gmres_restart","Number of Krylov search directions","KSPGMRESSetRestart",gmres->max_k,&restart,&flg);
712:     if (flg) { KSPGMRESSetRestart(ksp,restart); }
713:     PetscOptionsReal("-ksp_gmres_haptol","Tolerance for declaring exact convergence (happy ending)","KSPGMRESSetHapTol",gmres->haptol,&haptol,&flg);
714:     if (flg) { KSPGMRESSetHapTol(ksp,haptol); }
715:     PetscOptionsName("-ksp_gmres_preallocate","Preallocate all Krylov vectors","KSPGMRESSetPreAllocateVectors",&flg);
716:     if (flg) {KSPGMRESSetPreAllocateVectors(ksp);}
717:     PetscOptionsLogicalGroupBegin("-ksp_gmres_unmodifiedgramschmidt","Use classical (unmodified) Gram-Schmidt (fast)","KSPGMRESSetOrthogonalization",&flg);
718:     if (flg) {KSPGMRESSetOrthogonalization(ksp,KSPGMRESUnmodifiedGramSchmidtOrthogonalization);}
719:     PetscOptionsLogicalGroup("-ksp_gmres_modifiedgramschmidt","Use modified Gram-Schmidt (slow but more stable)","KSPGMRESSetOrthogonalization",&flg);
720:     if (flg) {KSPGMRESSetOrthogonalization(ksp,KSPGMRESModifiedGramSchmidtOrthogonalization);}
721:     PetscOptionsLogicalGroupEnd("-ksp_gmres_irorthog","Use classical Gram-Schmidt with iterative refinement","KSPGMRESSetOrthogonalization",&flg);
722:     if (flg) {KSPGMRESSetOrthogonalization(ksp,KSPGMRESIROrthogonalization);}
723:     PetscOptionsName("-ksp_gmres_krylov_monitor","Graphically plot the Krylov directions","KSPSetMonitor",&flg);
724:     if (flg) {
725:       PetscViewers viewers;
726:       PetscViewersCreate(ksp->comm,&viewers);
727:       KSPSetMonitor(ksp,KSPGMRESKrylovMonitor,viewers,(int (*)(void*))PetscViewersDestroy);
728:     }
729:     PetscOptionsLogicalGroupBegin("-ksp_fgmres_modifypcnochange","do not vary the preconditioner","KSPFGMRESSetModifyPC",&flg);
730:     if (flg) {KSPFGMRESSetModifyPC(ksp,KSPFGMRESModifyPCNoChange,0,0);}
731:     PetscOptionsLogicalGroupEnd("-ksp_fgmres_modifypcsles","vary the SLES based preconditioner","KSPFGMRESSetModifyPC",&flg);
732:     if (flg) {KSPFGMRESSetModifyPC(ksp,KSPFGMRESModifyPCSLES,0,0);}
733:   PetscOptionsTail();
734:   return(0);
735: }

737: EXTERN_C_BEGIN
738: #undef __FUNCT__  
740: int KSPFGMRESSetModifyPC_FGMRES(KSP ksp,int (*fcn)(KSP,int,int,PetscReal,void*),void *ctx,int (*d)(void*))
741: {
744:   ((KSP_FGMRES *)ksp->data)->modifypc      = fcn;
745:   ((KSP_FGMRES *)ksp->data)->modifydestroy = d;
746:   ((KSP_FGMRES *)ksp->data)->modifyctx     = ctx;
747:   return(0);
748: }
749: EXTERN_C_END

751: EXTERN_C_BEGIN
752: EXTERN int KSPGMRESSetPreAllocateVectors_GMRES(KSP);
753: EXTERN int KSPGMRESSetRestart_GMRES(KSP,int);
754: EXTERN int KSPGMRESSetOrthogonalization_GMRES(KSP,int (*)(KSP,int));
755: EXTERN_C_END

757: EXTERN_C_BEGIN
758: #undef __FUNCT__  
760: int KSPCreate_FGMRES(KSP ksp)
761: {
762:   KSP_FGMRES *fgmres;
763:   int        ierr;

766:   PetscNew(KSP_FGMRES,&fgmres);
767:   PetscMemzero(fgmres,sizeof(KSP_FGMRES));
768:   PetscLogObjectMemory(ksp,sizeof(KSP_FGMRES));
769:   ksp->data                              = (void*)fgmres;
770:   ksp->ops->buildsolution                = KSPBuildSolution_FGMRES;

772:   ksp->ops->setup                        = KSPSetUp_FGMRES;
773:   ksp->ops->solve                        = KSPSolve_FGMRES;
774:   ksp->ops->destroy                      = KSPDestroy_FGMRES;
775:   ksp->ops->view                         = KSPView_FGMRES;
776:   ksp->ops->setfromoptions               = KSPSetFromOptions_FGMRES;
777:   ksp->ops->computeextremesingularvalues = 0;
778:   ksp->ops->computeeigenvalues           = 0;

780:   PetscObjectComposeFunctionDynamic((PetscObject)ksp,"KSPGMRESSetPreAllocateVectors_C",
781:                                     "KSPGMRESSetPreAllocateVectors_GMRES",
782:                                      KSPGMRESSetPreAllocateVectors_GMRES);
783:   PetscObjectComposeFunctionDynamic((PetscObject)ksp,"KSPGMRESSetOrthogonalization_C",
784:                                     "KSPGMRESSetOrthogonalization_GMRES",
785:                                      KSPGMRESSetOrthogonalization_GMRES);
786:   PetscObjectComposeFunctionDynamic((PetscObject)ksp,"KSPGMRESSetRestart_C",
787:                                     "KSPGMRESSetRestart_GMRES",
788:                                      KSPGMRESSetRestart_GMRES);
789:   PetscObjectComposeFunctionDynamic((PetscObject)ksp,"KSPFGMRESSetModifyPC_C",
790:                                     "KSPFGMRESSetModifyPC_FGMRES",
791:                                      KSPFGMRESSetModifyPC_FGMRES);


794:   fgmres->haptol              = 1.0e-30;
795:   fgmres->q_preallocate       = 0;
796:   fgmres->delta_allocate      = FGMRES_DELTA_DIRECTIONS;
797:   fgmres->orthog              = KSPGMRESUnmodifiedGramSchmidtOrthogonalization;
798:   fgmres->nrs                 = 0;
799:   fgmres->sol_temp            = 0;
800:   fgmres->max_k               = FGMRES_DEFAULT_MAXK;
801:   fgmres->Rsvd                = 0;
802:   fgmres->modifypc            = KSPFGMRESModifyPCNoChange;
803:   fgmres->modifyctx           = PETSC_NULL;
804:   fgmres->modifydestroy       = PETSC_NULL;

806:   /*
807:         This is not great since it changes this without explicit request from the user
808:      but there is no left preconditioning in the FGMRES
809:   */
810:   PetscLogInfo(ksp,"Warning: Setting PC_SIDE for FGMRES to right!n");
811:   ksp->pc_side                = PC_RIGHT;

813:   return(0);
814: }
815: EXTERN_C_END