xref: /petsc/src/ksp/ksp/impls/fcg/fcg.c (revision 146c4e52bea1447a74c83f4ad3bbe8c33a4a90e3)
1 /*
2     This file implements the FCG (Flexible Conjugate Gradient) method
3 
4 */
5 
6 #include <../src/ksp/ksp/impls/fcg/fcgimpl.h> /*I  "petscksp.h"  I*/
7 extern PetscErrorCode KSPComputeExtremeSingularValues_CG(KSP, PetscReal *, PetscReal *);
8 extern PetscErrorCode KSPComputeEigenvalues_CG(KSP, PetscInt, PetscReal *, PetscReal *, PetscInt *);
9 
10 #define KSPFCG_DEFAULT_MMAX       30 /* maximum number of search directions to keep */
11 #define KSPFCG_DEFAULT_NPREALLOC  10 /* number of search directions to preallocate */
12 #define KSPFCG_DEFAULT_VECB       5  /* number of search directions to allocate each time new direction vectors are needed */
13 #define KSPFCG_DEFAULT_TRUNCSTRAT KSP_FCD_TRUNC_TYPE_NOTAY
14 
15 static PetscErrorCode KSPAllocateVectors_FCG(KSP ksp, PetscInt nvecsneeded, PetscInt chunksize) {
16   PetscInt i;
17   KSP_FCG *fcg = (KSP_FCG *)ksp->data;
18   PetscInt nnewvecs, nvecsprev;
19 
20   PetscFunctionBegin;
21   /* Allocate enough new vectors to add chunksize new vectors, reach nvecsneedtotal, or to reach mmax+1, whichever is smallest */
22   if (fcg->nvecs < PetscMin(fcg->mmax + 1, nvecsneeded)) {
23     nvecsprev = fcg->nvecs;
24     nnewvecs  = PetscMin(PetscMax(nvecsneeded - fcg->nvecs, chunksize), fcg->mmax + 1 - fcg->nvecs);
25     PetscCall(KSPCreateVecs(ksp, nnewvecs, &fcg->pCvecs[fcg->nchunks], 0, NULL));
26     PetscCall(PetscLogObjectParents((PetscObject)ksp, nnewvecs, fcg->pCvecs[fcg->nchunks]));
27     PetscCall(KSPCreateVecs(ksp, nnewvecs, &fcg->pPvecs[fcg->nchunks], 0, NULL));
28     PetscCall(PetscLogObjectParents((PetscObject)ksp, nnewvecs, fcg->pPvecs[fcg->nchunks]));
29     fcg->nvecs += nnewvecs;
30     for (i = 0; i < nnewvecs; ++i) {
31       fcg->Cvecs[nvecsprev + i] = fcg->pCvecs[fcg->nchunks][i];
32       fcg->Pvecs[nvecsprev + i] = fcg->pPvecs[fcg->nchunks][i];
33     }
34     fcg->chunksizes[fcg->nchunks] = nnewvecs;
35     ++fcg->nchunks;
36   }
37   PetscFunctionReturn(0);
38 }
39 
40 static PetscErrorCode KSPSetUp_FCG(KSP ksp) {
41   KSP_FCG       *fcg      = (KSP_FCG *)ksp->data;
42   PetscInt       maxit    = ksp->max_it;
43   const PetscInt nworkstd = 2;
44 
45   PetscFunctionBegin;
46 
47   /* Allocate "standard" work vectors (not including the basis and transformed basis vectors) */
48   PetscCall(KSPSetWorkVecs(ksp, nworkstd));
49 
50   /* Allocated space for pointers to additional work vectors
51    note that mmax is the number of previous directions, so we add 1 for the current direction,
52    and an extra 1 for the prealloc (which might be empty) */
53   PetscCall(PetscMalloc5(fcg->mmax + 1, &fcg->Pvecs, fcg->mmax + 1, &fcg->Cvecs, fcg->mmax + 1, &fcg->pPvecs, fcg->mmax + 1, &fcg->pCvecs, fcg->mmax + 2, &fcg->chunksizes));
54   PetscCall(PetscLogObjectMemory((PetscObject)ksp, 2 * (fcg->mmax + 1) * sizeof(Vec *) + 2 * (fcg->mmax + 1) * sizeof(Vec **) + (fcg->mmax + 2) * sizeof(PetscInt)));
55 
56   /* If the requested number of preallocated vectors is greater than mmax reduce nprealloc */
57   if (fcg->nprealloc > fcg->mmax + 1) { PetscCall(PetscInfo(NULL, "Requested nprealloc=%" PetscInt_FMT " is greater than m_max+1=%" PetscInt_FMT ". Resetting nprealloc = m_max+1.\n", fcg->nprealloc, fcg->mmax + 1)); }
58 
59   /* Preallocate additional work vectors */
60   PetscCall(KSPAllocateVectors_FCG(ksp, fcg->nprealloc, fcg->nprealloc));
61   /*
62   If user requested computations of eigenvalues then allocate work
63   work space needed
64   */
65   if (ksp->calc_sings) {
66     /* get space to store tridiagonal matrix for Lanczos */
67     PetscCall(PetscMalloc4(maxit, &fcg->e, maxit, &fcg->d, maxit, &fcg->ee, maxit, &fcg->dd));
68     PetscCall(PetscLogObjectMemory((PetscObject)ksp, 2 * (maxit + 1) * (sizeof(PetscScalar) + sizeof(PetscReal))));
69 
70     ksp->ops->computeextremesingularvalues = KSPComputeExtremeSingularValues_CG;
71     ksp->ops->computeeigenvalues           = KSPComputeEigenvalues_CG;
72   }
73   PetscFunctionReturn(0);
74 }
75 
76 static PetscErrorCode KSPSolve_FCG(KSP ksp) {
77   PetscInt    i, k, idx, mi;
78   KSP_FCG    *fcg   = (KSP_FCG *)ksp->data;
79   PetscScalar alpha = 0.0, beta = 0.0, dpi, s;
80   PetscReal   dp = 0.0;
81   Vec         B, R, Z, X, Pcurr, Ccurr;
82   Mat         Amat, Pmat;
83   PetscInt    eigs          = ksp->calc_sings; /* Variables for eigen estimation - START*/
84   PetscInt    stored_max_it = ksp->max_it;
85   PetscScalar alphaold = 0, betaold = 1.0, *e = NULL, *d = NULL; /* Variables for eigen estimation  - FINISH */
86 
87   PetscFunctionBegin;
88 
89 #define VecXDot(x, y, a)     (((fcg->type) == (KSP_CG_HERMITIAN)) ? VecDot(x, y, a) : VecTDot(x, y, a))
90 #define VecXMDot(a, b, c, d) (((fcg->type) == (KSP_CG_HERMITIAN)) ? VecMDot(a, b, c, d) : VecMTDot(a, b, c, d))
91 
92   X = ksp->vec_sol;
93   B = ksp->vec_rhs;
94   R = ksp->work[0];
95   Z = ksp->work[1];
96 
97   PetscCall(PCGetOperators(ksp->pc, &Amat, &Pmat));
98   if (eigs) {
99     e    = fcg->e;
100     d    = fcg->d;
101     e[0] = 0.0;
102   }
103   /* Compute initial residual needed for convergence check*/
104   ksp->its = 0;
105   if (!ksp->guess_zero) {
106     PetscCall(KSP_MatMult(ksp, Amat, X, R));
107     PetscCall(VecAYPX(R, -1.0, B)); /*   r <- b - Ax     */
108   } else {
109     PetscCall(VecCopy(B, R)); /*   r <- b (x is 0) */
110   }
111   switch (ksp->normtype) {
112   case KSP_NORM_PRECONDITIONED:
113     PetscCall(KSP_PCApply(ksp, R, Z));  /*   z <- Br         */
114     PetscCall(VecNorm(Z, NORM_2, &dp)); /*   dp <- dqrt(z'*z) = sqrt(e'*A'*B'*B*A*e)     */
115     KSPCheckNorm(ksp, dp);
116     break;
117   case KSP_NORM_UNPRECONDITIONED:
118     PetscCall(VecNorm(R, NORM_2, &dp)); /*   dp <- sqrt(r'*r) = sqrt(e'*A'*A*e)     */
119     KSPCheckNorm(ksp, dp);
120     break;
121   case KSP_NORM_NATURAL:
122     PetscCall(KSP_PCApply(ksp, R, Z)); /*   z <- Br         */
123     PetscCall(VecXDot(R, Z, &s));
124     KSPCheckDot(ksp, s);
125     dp = PetscSqrtReal(PetscAbsScalar(s)); /*   dp <- sqrt(r'*z) = sqrt(e'*A'*B*A*e)  */
126     break;
127   case KSP_NORM_NONE: dp = 0.0; break;
128   default: SETERRQ(PetscObjectComm((PetscObject)ksp), PETSC_ERR_SUP, "%s", KSPNormTypes[ksp->normtype]);
129   }
130 
131   /* Initial Convergence Check */
132   PetscCall(KSPLogResidualHistory(ksp, dp));
133   PetscCall(KSPMonitor(ksp, 0, dp));
134   ksp->rnorm = dp;
135   if (ksp->normtype == KSP_NORM_NONE) {
136     PetscCall(KSPConvergedSkip(ksp, 0, dp, &ksp->reason, ksp->cnvP));
137   } else {
138     PetscCall((*ksp->converged)(ksp, 0, dp, &ksp->reason, ksp->cnvP));
139   }
140   if (ksp->reason) PetscFunctionReturn(0);
141 
142   /* Apply PC if not already done for convergence check */
143   if (ksp->normtype == KSP_NORM_UNPRECONDITIONED || ksp->normtype == KSP_NORM_NONE) { PetscCall(KSP_PCApply(ksp, R, Z)); /*   z <- Br         */ }
144 
145   i = 0;
146   do {
147     ksp->its = i + 1;
148 
149     /*  If needbe, allocate a new chunk of vectors in P and C */
150     PetscCall(KSPAllocateVectors_FCG(ksp, i + 1, fcg->vecb));
151 
152     /* Note that we wrap around and start clobbering old vectors */
153     idx   = i % (fcg->mmax + 1);
154     Pcurr = fcg->Pvecs[idx];
155     Ccurr = fcg->Cvecs[idx];
156 
157     /* number of old directions to orthogonalize against */
158     switch (fcg->truncstrat) {
159     case KSP_FCD_TRUNC_TYPE_STANDARD: mi = fcg->mmax; break;
160     case KSP_FCD_TRUNC_TYPE_NOTAY: mi = ((i - 1) % fcg->mmax) + 1; break;
161     default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unrecognized Truncation Strategy");
162     }
163 
164     /* Compute a new column of P (Currently does not support modified G-S or iterative refinement)*/
165     PetscCall(VecCopy(Z, Pcurr));
166 
167     {
168       PetscInt l, ndots;
169 
170       l     = PetscMax(0, i - mi);
171       ndots = i - l;
172       if (ndots) {
173         PetscInt     j;
174         Vec         *Pold, *Cold;
175         PetscScalar *dots;
176 
177         PetscCall(PetscMalloc3(ndots, &dots, ndots, &Cold, ndots, &Pold));
178         for (k = l, j = 0; j < ndots; ++k, ++j) {
179           idx     = k % (fcg->mmax + 1);
180           Cold[j] = fcg->Cvecs[idx];
181           Pold[j] = fcg->Pvecs[idx];
182         }
183         PetscCall(VecXMDot(Z, ndots, Cold, dots));
184         for (k = 0; k < ndots; ++k) { dots[k] = -dots[k]; }
185         PetscCall(VecMAXPY(Pcurr, ndots, dots, Pold));
186         PetscCall(PetscFree3(dots, Cold, Pold));
187       }
188     }
189 
190     /* Update X and R */
191     betaold = beta;
192     PetscCall(VecXDot(Pcurr, R, &beta)); /*  beta <- pi'*r       */
193     KSPCheckDot(ksp, beta);
194     PetscCall(KSP_MatMult(ksp, Amat, Pcurr, Ccurr)); /*  w <- A*pi (stored in ci)   */
195     PetscCall(VecXDot(Pcurr, Ccurr, &dpi));          /*  dpi <- pi'*w        */
196     alphaold = alpha;
197     alpha    = beta / dpi;                /*  alpha <- beta/dpi    */
198     PetscCall(VecAXPY(X, alpha, Pcurr));  /*  x <- x + alpha * pi  */
199     PetscCall(VecAXPY(R, -alpha, Ccurr)); /*  r <- r - alpha * wi  */
200 
201     /* Compute norm for convergence check */
202     switch (ksp->normtype) {
203     case KSP_NORM_PRECONDITIONED:
204       PetscCall(KSP_PCApply(ksp, R, Z));  /*   z <- Br             */
205       PetscCall(VecNorm(Z, NORM_2, &dp)); /*   dp <- sqrt(z'*z) = sqrt(e'*A'*B'*B*A*e)  */
206       KSPCheckNorm(ksp, dp);
207       break;
208     case KSP_NORM_UNPRECONDITIONED:
209       PetscCall(VecNorm(R, NORM_2, &dp)); /*   dp <- sqrt(r'*r) = sqrt(e'*A'*A*e)   */
210       KSPCheckNorm(ksp, dp);
211       break;
212     case KSP_NORM_NATURAL:
213       PetscCall(KSP_PCApply(ksp, R, Z)); /*   z <- Br             */
214       PetscCall(VecXDot(R, Z, &s));
215       KSPCheckDot(ksp, s);
216       dp = PetscSqrtReal(PetscAbsScalar(s)); /*   dp <- sqrt(r'*z) = sqrt(e'*A'*B*A*e)  */
217       break;
218     case KSP_NORM_NONE: dp = 0.0; break;
219     default: SETERRQ(PetscObjectComm((PetscObject)ksp), PETSC_ERR_SUP, "%s", KSPNormTypes[ksp->normtype]);
220     }
221 
222     /* Check for convergence */
223     ksp->rnorm = dp;
224     PetscCall(KSPLogResidualHistory(ksp, dp));
225     PetscCall(KSPMonitor(ksp, i + 1, dp));
226     PetscCall((*ksp->converged)(ksp, i + 1, dp, &ksp->reason, ksp->cnvP));
227     if (ksp->reason) break;
228 
229     /* Apply PC if not already done for convergence check */
230     if (ksp->normtype == KSP_NORM_UNPRECONDITIONED || ksp->normtype == KSP_NORM_NONE) { PetscCall(KSP_PCApply(ksp, R, Z)); /*   z <- Br         */ }
231 
232     /* Compute current C (which is W/dpi) */
233     PetscCall(VecScale(Ccurr, 1.0 / dpi)); /*   w <- ci/dpi   */
234 
235     if (eigs) {
236       if (i > 0) {
237         PetscCheck(ksp->max_it == stored_max_it, PetscObjectComm((PetscObject)ksp), PETSC_ERR_SUP, "Can not change maxit AND calculate eigenvalues");
238         e[i] = PetscSqrtReal(PetscAbsScalar(beta / betaold)) / alphaold;
239         d[i] = PetscSqrtReal(PetscAbsScalar(beta / betaold)) * e[i] + 1.0 / alpha;
240       } else {
241         d[i] = PetscSqrtReal(PetscAbsScalar(beta)) * e[i] + 1.0 / alpha;
242       }
243       fcg->ned = ksp->its - 1;
244     }
245     ++i;
246   } while (i < ksp->max_it);
247   if (i >= ksp->max_it) ksp->reason = KSP_DIVERGED_ITS;
248   PetscFunctionReturn(0);
249 }
250 
251 static PetscErrorCode KSPDestroy_FCG(KSP ksp) {
252   PetscInt i;
253   KSP_FCG *fcg = (KSP_FCG *)ksp->data;
254 
255   PetscFunctionBegin;
256 
257   /* Destroy "standard" work vecs */
258   VecDestroyVecs(ksp->nwork, &ksp->work);
259 
260   /* Destroy P and C vectors and the arrays that manage pointers to them */
261   if (fcg->nvecs) {
262     for (i = 0; i < fcg->nchunks; ++i) {
263       PetscCall(VecDestroyVecs(fcg->chunksizes[i], &fcg->pPvecs[i]));
264       PetscCall(VecDestroyVecs(fcg->chunksizes[i], &fcg->pCvecs[i]));
265     }
266   }
267   PetscCall(PetscFree5(fcg->Pvecs, fcg->Cvecs, fcg->pPvecs, fcg->pCvecs, fcg->chunksizes));
268   /* free space used for singular value calculations */
269   if (ksp->calc_sings) PetscCall(PetscFree4(fcg->e, fcg->d, fcg->ee, fcg->dd));
270   PetscCall(KSPDestroyDefault(ksp));
271   PetscFunctionReturn(0);
272 }
273 
274 static PetscErrorCode KSPView_FCG(KSP ksp, PetscViewer viewer) {
275   KSP_FCG    *fcg = (KSP_FCG *)ksp->data;
276   PetscBool   iascii, isstring;
277   const char *truncstr;
278 
279   PetscFunctionBegin;
280   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
281   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERSTRING, &isstring));
282 
283   if (fcg->truncstrat == KSP_FCD_TRUNC_TYPE_STANDARD) truncstr = "Using standard truncation strategy";
284   else if (fcg->truncstrat == KSP_FCD_TRUNC_TYPE_NOTAY) truncstr = "Using Notay's truncation strategy";
285   else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Undefined FCG truncation strategy");
286 
287   if (iascii) {
288     PetscCall(PetscViewerASCIIPrintf(viewer, "  m_max=%" PetscInt_FMT "\n", fcg->mmax));
289     PetscCall(PetscViewerASCIIPrintf(viewer, "  preallocated %" PetscInt_FMT " directions\n", PetscMin(fcg->nprealloc, fcg->mmax + 1)));
290     PetscCall(PetscViewerASCIIPrintf(viewer, "  %s\n", truncstr));
291   } else if (isstring) {
292     PetscCall(PetscViewerStringSPrintf(viewer, "m_max %" PetscInt_FMT " nprealloc %" PetscInt_FMT " %s", fcg->mmax, fcg->nprealloc, truncstr));
293   }
294   PetscFunctionReturn(0);
295 }
296 
297 /*@
298   KSPFCGSetMmax - set the maximum number of previous directions FCG will store for orthogonalization
299 
300   Note: mmax + 1 directions are stored (mmax previous ones along with a current one)
301   and whether all are used in each iteration also depends on the truncation strategy
302   (see KSPFCGSetTruncationType())
303 
304   Logically Collective on ksp
305 
306   Input Parameters:
307 +  ksp - the Krylov space context
308 -  mmax - the maximum number of previous directions to orthogonalize againt
309 
310   Level: intermediate
311 
312 .seealso: `KSPFCG`, `KSPFCGGetTruncationType()`, `KSPFCGGetNprealloc()`
313 @*/
314 PetscErrorCode KSPFCGSetMmax(KSP ksp, PetscInt mmax) {
315   KSP_FCG *fcg = (KSP_FCG *)ksp->data;
316 
317   PetscFunctionBegin;
318   PetscValidHeaderSpecific(ksp, KSP_CLASSID, 1);
319   PetscValidLogicalCollectiveInt(ksp, mmax, 2);
320   fcg->mmax = mmax;
321   PetscFunctionReturn(0);
322 }
323 
324 /*@
325   KSPFCGGetMmax - get the maximum number of previous directions FCG will store
326 
327   Note: FCG stores mmax+1 directions at most (mmax previous ones, and one current one)
328 
329    Not Collective
330 
331    Input Parameter:
332 .  ksp - the Krylov space context
333 
334    Output Parameter:
335 .  mmax - the maximum number of previous directions allowed for orthogonalization
336 
337    Level: intermediate
338 
339 .seealso: `KSPFCG`, `KSPFCGGetTruncationType()`, `KSPFCGGetNprealloc()`, `KSPFCGSetMmax()`
340 @*/
341 
342 PetscErrorCode KSPFCGGetMmax(KSP ksp, PetscInt *mmax) {
343   KSP_FCG *fcg = (KSP_FCG *)ksp->data;
344 
345   PetscFunctionBegin;
346   PetscValidHeaderSpecific(ksp, KSP_CLASSID, 1);
347   *mmax = fcg->mmax;
348   PetscFunctionReturn(0);
349 }
350 
351 /*@
352   KSPFCGSetNprealloc - set the number of directions to preallocate with FCG
353 
354   Logically Collective on ksp
355 
356   Input Parameters:
357 +  ksp - the Krylov space context
358 -  nprealloc - the number of vectors to preallocate
359 
360   Level: advanced
361 
362   Options Database:
363 . -ksp_fcg_nprealloc <N> - number of directions to preallocate
364 
365 .seealso: `KSPFCG`, `KSPFCGGetTruncationType()`, `KSPFCGGetNprealloc()`
366 @*/
367 PetscErrorCode KSPFCGSetNprealloc(KSP ksp, PetscInt nprealloc) {
368   KSP_FCG *fcg = (KSP_FCG *)ksp->data;
369 
370   PetscFunctionBegin;
371   PetscValidHeaderSpecific(ksp, KSP_CLASSID, 1);
372   PetscValidLogicalCollectiveInt(ksp, nprealloc, 2);
373   PetscCheck(nprealloc <= fcg->mmax + 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cannot preallocate more than m_max+1 vectors");
374   fcg->nprealloc = nprealloc;
375   PetscFunctionReturn(0);
376 }
377 
378 /*@
379   KSPFCGGetNprealloc - get the number of directions preallocate by FCG
380 
381    Not Collective
382 
383    Input Parameter:
384 .  ksp - the Krylov space context
385 
386    Output Parameter:
387 .  nprealloc - the number of directions preallocated
388 
389    Level: advanced
390 
391 .seealso: `KSPFCG`, `KSPFCGGetTruncationType()`, `KSPFCGSetNprealloc()`
392 @*/
393 PetscErrorCode KSPFCGGetNprealloc(KSP ksp, PetscInt *nprealloc) {
394   KSP_FCG *fcg = (KSP_FCG *)ksp->data;
395 
396   PetscFunctionBegin;
397   PetscValidHeaderSpecific(ksp, KSP_CLASSID, 1);
398   *nprealloc = fcg->nprealloc;
399   PetscFunctionReturn(0);
400 }
401 
402 /*@
403   KSPFCGSetTruncationType - specify how many of its stored previous directions FCG uses during orthoganalization
404 
405   Logically Collective on ksp
406 
407   KSP_FCD_TRUNC_TYPE_STANDARD uses all (up to mmax) stored directions
408   KSP_FCD_TRUNC_TYPE_NOTAY uses the last max(1,mod(i,mmax)) stored directions at iteration i=0,1,..
409 
410   Input Parameters:
411 +  ksp - the Krylov space context
412 -  truncstrat - the choice of strategy
413 
414   Level: intermediate
415 
416   Options Database:
417 . -ksp_fcg_truncation_type <standard, notay> - specify how many of its stored previous directions FCG uses during orthoganalization
418 
419   .seealso: `KSPFCDTruncationType`, `KSPFCGGetTruncationType`
420 @*/
421 PetscErrorCode KSPFCGSetTruncationType(KSP ksp, KSPFCDTruncationType truncstrat) {
422   KSP_FCG *fcg = (KSP_FCG *)ksp->data;
423 
424   PetscFunctionBegin;
425   PetscValidHeaderSpecific(ksp, KSP_CLASSID, 1);
426   PetscValidLogicalCollectiveEnum(ksp, truncstrat, 2);
427   fcg->truncstrat = truncstrat;
428   PetscFunctionReturn(0);
429 }
430 
431 /*@
432   KSPFCGGetTruncationType - get the truncation strategy employed by FCG
433 
434    Not Collective
435 
436    Input Parameter:
437 .  ksp - the Krylov space context
438 
439    Output Parameter:
440 .  truncstrat - the strategy type
441 
442    Level: intermediate
443 
444 .seealso: `KSPFCG`, `KSPFCGSetTruncationType`, `KSPFCDTruncationType`
445 @*/
446 PetscErrorCode KSPFCGGetTruncationType(KSP ksp, KSPFCDTruncationType *truncstrat) {
447   KSP_FCG *fcg = (KSP_FCG *)ksp->data;
448 
449   PetscFunctionBegin;
450   PetscValidHeaderSpecific(ksp, KSP_CLASSID, 1);
451   *truncstrat = fcg->truncstrat;
452   PetscFunctionReturn(0);
453 }
454 
455 static PetscErrorCode KSPSetFromOptions_FCG(KSP ksp, PetscOptionItems *PetscOptionsObject) {
456   KSP_FCG  *fcg = (KSP_FCG *)ksp->data;
457   PetscInt  mmax, nprealloc;
458   PetscBool flg;
459 
460   PetscFunctionBegin;
461   PetscOptionsHeadBegin(PetscOptionsObject, "KSP FCG Options");
462   PetscCall(PetscOptionsInt("-ksp_fcg_mmax", "Maximum number of search directions to store", "KSPFCGSetMmax", fcg->mmax, &mmax, &flg));
463   if (flg) PetscCall(KSPFCGSetMmax(ksp, mmax));
464   PetscCall(PetscOptionsInt("-ksp_fcg_nprealloc", "Number of directions to preallocate", "KSPFCGSetNprealloc", fcg->nprealloc, &nprealloc, &flg));
465   if (flg) PetscCall(KSPFCGSetNprealloc(ksp, nprealloc));
466   PetscCall(PetscOptionsEnum("-ksp_fcg_truncation_type", "Truncation approach for directions", "KSPFCGSetTruncationType", KSPFCDTruncationTypes, (PetscEnum)fcg->truncstrat, (PetscEnum *)&fcg->truncstrat, NULL));
467   PetscOptionsHeadEnd();
468   PetscFunctionReturn(0);
469 }
470 
471 /*MC
472       KSPFCG - Implements the Flexible Conjugate Gradient method (FCG)
473 
474   Options Database Keys:
475 +   -ksp_fcg_mmax <N>  - maximum number of search directions
476 .   -ksp_fcg_nprealloc <N> - number of directions to preallocate
477 -   -ksp_fcg_truncation_type <standard,notay> - truncation approach for directions
478 
479     Contributed by Patrick Sanan
480 
481    Notes:
482    Supports left preconditioning only.
483 
484    Level: beginner
485 
486   References:
487 + * - Notay, Y."Flexible Conjugate Gradients", SIAM J. Sci. Comput. 22:4, 2000
488 - * - Axelsson, O. and Vassilevski, P. S. "A Black Box Generalized Conjugate Gradient Solver with Inner Iterations and Variable step Preconditioning",
489     SIAM J. Matrix Anal. Appl. 12:4, 1991
490 
491  .seealso `:` `KSPGCR`, `KSPFGMRES`, `KSPCG`, `KSPFCGSetMmax()`, `KSPFCGGetMmax()`, `KSPFCGSetNprealloc()`, `KSPFCGGetNprealloc()`, `KSPFCGSetTruncationType()`, `KSPFCGGetTruncationType()`
492 
493 M*/
494 PETSC_EXTERN PetscErrorCode KSPCreate_FCG(KSP ksp) {
495   KSP_FCG *fcg;
496 
497   PetscFunctionBegin;
498   PetscCall(PetscNewLog(ksp, &fcg));
499 #if !defined(PETSC_USE_COMPLEX)
500   fcg->type = KSP_CG_SYMMETRIC;
501 #else
502   fcg->type = KSP_CG_HERMITIAN;
503 #endif
504   fcg->mmax       = KSPFCG_DEFAULT_MMAX;
505   fcg->nprealloc  = KSPFCG_DEFAULT_NPREALLOC;
506   fcg->nvecs      = 0;
507   fcg->vecb       = KSPFCG_DEFAULT_VECB;
508   fcg->nchunks    = 0;
509   fcg->truncstrat = KSPFCG_DEFAULT_TRUNCSTRAT;
510 
511   ksp->data = (void *)fcg;
512 
513   PetscCall(KSPSetSupportedNorm(ksp, KSP_NORM_PRECONDITIONED, PC_LEFT, 2));
514   PetscCall(KSPSetSupportedNorm(ksp, KSP_NORM_UNPRECONDITIONED, PC_LEFT, 1));
515   PetscCall(KSPSetSupportedNorm(ksp, KSP_NORM_NATURAL, PC_LEFT, 1));
516   PetscCall(KSPSetSupportedNorm(ksp, KSP_NORM_NONE, PC_LEFT, 1));
517 
518   ksp->ops->setup          = KSPSetUp_FCG;
519   ksp->ops->solve          = KSPSolve_FCG;
520   ksp->ops->destroy        = KSPDestroy_FCG;
521   ksp->ops->view           = KSPView_FCG;
522   ksp->ops->setfromoptions = KSPSetFromOptions_FCG;
523   ksp->ops->buildsolution  = KSPBuildSolutionDefault;
524   ksp->ops->buildresidual  = KSPBuildResidualDefault;
525   PetscFunctionReturn(0);
526 }
527