1 #include <../src/ksp/ksp/impls/gmres/lgmres/lgmresimpl.h> /*I "petscksp.h" I*/
2
3 static PetscErrorCode KSPLGMRESGetNewVectors(KSP, PetscInt);
4 static PetscErrorCode KSPLGMRESUpdateHessenberg(KSP, PetscInt, PetscBool, PetscReal *);
5 static PetscErrorCode KSPLGMRESBuildSoln(PetscScalar *, Vec, Vec, KSP, PetscInt);
6
7 /*@
8 KSPLGMRESSetAugDim - Set the number of error approximations to include in the approximation space (default is 2) for `KSPLGMRES`
9
10 Collective
11
12 Input Parameters:
13 + ksp - the `KSP` context
14 - dim - the number of vectors to use
15
16 Options Database Key:
17 . -ksp_lgmres_augment dim - the number of error approximations to include
18
19 Level: intermediate
20
21 Note:
22 If this is set to zero, then this method is equivalent to `KSPGMRES`
23
24 .seealso: [](ch_ksp), `KSPLGMRES`, `KSPLGMRESSetConstant()`
25 @*/
KSPLGMRESSetAugDim(KSP ksp,PetscInt dim)26 PetscErrorCode KSPLGMRESSetAugDim(KSP ksp, PetscInt dim)
27 {
28 PetscFunctionBegin;
29 PetscTryMethod(ksp, "KSPLGMRESSetAugDim_C", (KSP, PetscInt), (ksp, dim));
30 PetscFunctionReturn(PETSC_SUCCESS);
31 }
32
33 /*@
34 KSPLGMRESSetConstant - keep the error approximation space a constant size for every restart cycle
35
36 Collective
37
38 Input Parameters:
39 . ksp - the `KSP` context
40
41 Options Database Key:
42 . -ksp_lgmres_constant - set the size to be constant
43
44 Level: intermediate
45
46 Note:
47 This only affects the first couple of restart cycles when the total number of desired error approximations may not
48 be available.
49
50 .seealso: [](ch_ksp), `KSPLGMRES`, `KSPLGMRESSetAugDim()`
51 @*/
KSPLGMRESSetConstant(KSP ksp)52 PetscErrorCode KSPLGMRESSetConstant(KSP ksp)
53 {
54 PetscFunctionBegin;
55 PetscTryMethod(ksp, "KSPLGMRESSetConstant_C", (KSP), (ksp));
56 PetscFunctionReturn(PETSC_SUCCESS);
57 }
58
KSPSetUp_LGMRES(KSP ksp)59 static PetscErrorCode KSPSetUp_LGMRES(KSP ksp)
60 {
61 PetscInt max_k, k, aug_dim;
62 KSP_LGMRES *lgmres = (KSP_LGMRES *)ksp->data;
63
64 PetscFunctionBegin;
65 max_k = lgmres->max_k;
66 aug_dim = lgmres->aug_dim;
67 PetscCall(KSPSetUp_GMRES(ksp));
68
69 /* need array of pointers to augvecs*/
70 PetscCall(PetscMalloc1(2 * aug_dim + AUG_OFFSET, &lgmres->augvecs));
71
72 lgmres->aug_vecs_allocated = 2 * aug_dim + AUG_OFFSET;
73
74 PetscCall(PetscMalloc1(2 * aug_dim + AUG_OFFSET, &lgmres->augvecs_user_work));
75 PetscCall(PetscMalloc1(aug_dim, &lgmres->aug_order));
76
77 /* for now we will preallocate the augvecs - because aug_dim << restart
78 ... also keep in mind that we need to keep augvecs from cycle to cycle*/
79 lgmres->aug_vv_allocated = 2 * aug_dim + AUG_OFFSET;
80 lgmres->augwork_alloc = 2 * aug_dim + AUG_OFFSET;
81
82 PetscCall(KSPCreateVecs(ksp, lgmres->aug_vv_allocated, &lgmres->augvecs_user_work[0], 0, NULL));
83 PetscCall(PetscMalloc1(max_k + 1, &lgmres->hwork));
84 for (k = 0; k < lgmres->aug_vv_allocated; k++) lgmres->augvecs[k] = lgmres->augvecs_user_work[0][k];
85 PetscFunctionReturn(PETSC_SUCCESS);
86 }
87
KSPLGMRESCycle(PetscInt * itcount,KSP ksp)88 static PetscErrorCode KSPLGMRESCycle(PetscInt *itcount, KSP ksp)
89 {
90 KSP_LGMRES *lgmres = (KSP_LGMRES *)ksp->data;
91 PetscReal res_norm, res;
92 PetscReal hapbnd, tt;
93 PetscScalar tmp;
94 PetscBool hapend = PETSC_FALSE; /* indicates happy breakdown ending */
95 PetscInt loc_it; /* local count of # of dir. in Krylov space */
96 PetscInt max_k = lgmres->max_k; /* max approx space size */
97 PetscInt max_it = ksp->max_it; /* max # of overall iterations for the method */
98
99 /* LGMRES_MOD - new variables*/
100 PetscInt aug_dim = lgmres->aug_dim;
101 PetscInt spot = 0;
102 PetscInt order = 0;
103 PetscInt it_arnoldi; /* number of arnoldi steps to take */
104 PetscInt it_total; /* total number of its to take (=approx space size)*/
105 PetscInt ii, jj;
106 PetscReal tmp_norm;
107 PetscScalar inv_tmp_norm;
108 PetscScalar *avec;
109
110 PetscFunctionBegin;
111 /* Number of pseudo iterations since last restart is the number of prestart directions */
112 loc_it = 0;
113
114 /* LGMRES_MOD: determine number of arnoldi steps to take */
115 /* if approx_constant then we keep the space the same size even if
116 we don't have the full number of aug vectors yet*/
117 if (lgmres->approx_constant) it_arnoldi = max_k - lgmres->aug_ct;
118 else it_arnoldi = max_k - aug_dim;
119
120 it_total = it_arnoldi + lgmres->aug_ct;
121
122 /* initial residual is in VEC_VV(0) - compute its norm*/
123 PetscCall(VecNorm(VEC_VV(0), NORM_2, &res_norm));
124 KSPCheckNorm(ksp, res_norm);
125 res = res_norm;
126
127 /* first entry in the right-hand side of the Hessenberg system is just the initial residual norm */
128 *GRS(0) = res_norm;
129
130 /* check for the convergence */
131 if (!res) {
132 if (itcount) *itcount = 0;
133 ksp->reason = KSP_CONVERGED_ATOL;
134 PetscCall(PetscInfo(ksp, "Converged due to zero residual norm on entry\n"));
135 PetscFunctionReturn(PETSC_SUCCESS);
136 }
137
138 /* scale VEC_VV (the initial residual) */
139 tmp = 1.0 / res_norm;
140 PetscCall(VecScale(VEC_VV(0), tmp));
141
142 if (ksp->normtype != KSP_NORM_NONE) ksp->rnorm = res;
143 else ksp->rnorm = 0.0;
144
145 /* note: (lgmres->it) is always set one less than (loc_it) It is used in
146 KSPBUILDSolution_LGMRES, where it is passed to KSPLGMRESBuildSoln.
147 Note that when KSPLGMRESBuildSoln is called from this function,
148 (loc_it -1) is passed, so the two are equivalent */
149 lgmres->it = (loc_it - 1);
150
151 /* MAIN ITERATION LOOP BEGINNING*/
152
153 /* keep iterating until we have converged OR generated the max number
154 of directions OR reached the max number of iterations for the method */
155 PetscCall((*ksp->converged)(ksp, ksp->its, res, &ksp->reason, ksp->cnvP));
156
157 while (!ksp->reason && loc_it < it_total && ksp->its < max_it) { /* LGMRES_MOD: changed to it_total */
158 PetscCall(KSPLogResidualHistory(ksp, res));
159 lgmres->it = (loc_it - 1);
160 PetscCall(KSPMonitor(ksp, ksp->its, res));
161
162 /* see if more space is needed for work vectors */
163 if (lgmres->vv_allocated <= loc_it + VEC_OFFSET + 1) {
164 PetscCall(KSPLGMRESGetNewVectors(ksp, loc_it + 1));
165 /* (loc_it+1) is passed in as number of the first vector that should be allocated */
166 }
167
168 /* LGMRES_MOD: decide whether this is an arnoldi step or an aug step */
169 if (loc_it < it_arnoldi) { /* Arnoldi */
170 PetscCall(KSP_PCApplyBAorAB(ksp, VEC_VV(loc_it), VEC_VV(1 + loc_it), VEC_TEMP_MATOP));
171 } else { /* aug step */
172 order = loc_it - it_arnoldi + 1; /* which aug step */
173 for (ii = 0; ii < aug_dim; ii++) {
174 if (lgmres->aug_order[ii] == order) {
175 spot = ii;
176 break; /* must have this because there will be duplicates before aug_ct = aug_dim */
177 }
178 }
179
180 PetscCall(VecCopy(A_AUGVEC(spot), VEC_VV(1 + loc_it)));
181 /* note: an alternate implementation choice would be to only save the AUGVECS and
182 not A_AUGVEC and then apply the PC here to the augvec */
183 }
184
185 /* update Hessenberg matrix and do Gram-Schmidt - new direction is in VEC_VV(1+loc_it)*/
186 PetscCall((*lgmres->orthog)(ksp, loc_it));
187
188 /* new entry in hessenburg is the 2-norm of our new direction */
189 PetscCall(VecNorm(VEC_VV(loc_it + 1), NORM_2, &tt));
190
191 *HH(loc_it + 1, loc_it) = tt;
192 *HES(loc_it + 1, loc_it) = tt;
193
194 /* check for the happy breakdown */
195 hapbnd = PetscAbsScalar(tt / *GRS(loc_it)); /* GRS(loc_it) contains the res_norm from the last iteration */
196 if (hapbnd > lgmres->haptol) hapbnd = lgmres->haptol;
197 if (tt > hapbnd) {
198 tmp = 1.0 / tt;
199 PetscCall(VecScale(VEC_VV(loc_it + 1), tmp)); /* scale new direction by its norm */
200 } else {
201 PetscCall(PetscInfo(ksp, "Detected happy breakdown, current hapbnd = %g tt = %g\n", (double)hapbnd, (double)tt));
202 hapend = PETSC_TRUE;
203 }
204
205 /* apply rotations to the new column of the Hessenberg (and the right-hand side of the system),
206 calculate new rotation, and get new residual norm at the same time*/
207 PetscCall(KSPLGMRESUpdateHessenberg(ksp, loc_it, hapend, &res));
208 if (ksp->reason) break;
209
210 loc_it++;
211 lgmres->it = (loc_it - 1); /* Add this here in case it has converged */
212
213 PetscCall(PetscObjectSAWsTakeAccess((PetscObject)ksp));
214 ksp->its++;
215 if (ksp->normtype != KSP_NORM_NONE) ksp->rnorm = res;
216 else ksp->rnorm = 0.0;
217 PetscCall(PetscObjectSAWsGrantAccess((PetscObject)ksp));
218
219 PetscCall((*ksp->converged)(ksp, ksp->its, res, &ksp->reason, ksp->cnvP));
220
221 /* Catch error in happy breakdown and signal convergence and break from loop */
222 if (hapend) {
223 if (!ksp->reason) {
224 PetscCheck(!ksp->errorifnotconverged, PetscObjectComm((PetscObject)ksp), PETSC_ERR_NOT_CONVERGED, "Reached happy break down, but convergence was not indicated. Residual norm = %g", (double)res);
225 ksp->reason = KSP_DIVERGED_BREAKDOWN;
226 break;
227 }
228 }
229 }
230 /* END OF ITERATION LOOP */
231 PetscCall(KSPLogResidualHistory(ksp, res));
232
233 if (itcount) *itcount = loc_it;
234
235 /*
236 Solve for the "best" coefficients of the Krylov
237 columns, add the solution values together, and possibly unwind the
238 preconditioning from the solution
239 */
240
241 /* Form the solution (or the solution so far) */
242 /* Note: must pass in (loc_it-1) for iteration count so that KSPLGMRESBuildSoln properly navigates */
243
244 PetscCall(KSPLGMRESBuildSoln(GRS(0), ksp->vec_sol, ksp->vec_sol, ksp, loc_it - 1));
245
246 /* Monitor if we know that we will not return for a restart */
247 if (ksp->reason == KSP_CONVERGED_ITERATING && ksp->its >= ksp->max_it) ksp->reason = KSP_DIVERGED_ITS;
248 if (ksp->reason) PetscCall(KSPMonitor(ksp, ksp->its, res));
249
250 /* LGMRES_MOD collect aug vector and A*augvector for future restarts -
251 only if we will be restarting (i.e. this cycle performed it_total iterations) */
252 if (!ksp->reason && ksp->its < max_it && aug_dim > 0) {
253 /* AUG_TEMP contains the new augmentation vector (assigned in KSPLGMRESBuildSoln) */
254 if (!lgmres->aug_ct) {
255 spot = 0;
256 lgmres->aug_ct++;
257 } else if (lgmres->aug_ct < aug_dim) {
258 spot = lgmres->aug_ct;
259 lgmres->aug_ct++;
260 } else { /* truncate */
261 for (ii = 0; ii < aug_dim; ii++) {
262 if (lgmres->aug_order[ii] == aug_dim) spot = ii;
263 }
264 }
265
266 PetscCall(VecCopy(AUG_TEMP, AUGVEC(spot)));
267 /* need to normalize */
268 PetscCall(VecNorm(AUGVEC(spot), NORM_2, &tmp_norm));
269
270 inv_tmp_norm = 1.0 / tmp_norm;
271
272 PetscCall(VecScale(AUGVEC(spot), inv_tmp_norm));
273
274 /* set new aug vector to order 1 - move all others back one */
275 for (ii = 0; ii < aug_dim; ii++) AUG_ORDER(ii)++;
276 AUG_ORDER(spot) = 1;
277
278 /* now add the A*aug vector to A_AUGVEC(spot) - this is independent of preconditioning type */
279 /* want V*H*y - y is in GRS, V is in VEC_VV and H is in HES */
280
281 /* do H+*y */
282 avec = lgmres->hwork;
283 PetscCall(PetscArrayzero(avec, it_total + 1));
284 for (ii = 0; ii < it_total + 1; ii++) {
285 for (jj = 0; jj <= ii + 1 && jj < it_total + 1; jj++) avec[jj] += *HES(jj, ii) * *GRS(ii);
286 }
287
288 /* multiply result by V+ */
289 PetscCall(VecMAXPBY(VEC_TEMP, it_total + 1, avec, 0, &VEC_VV(0))); /* answer is in VEC_TEMP */
290
291 /* copy answer to aug location and scale */
292 PetscCall(VecCopy(VEC_TEMP, A_AUGVEC(spot)));
293 PetscCall(VecScale(A_AUGVEC(spot), inv_tmp_norm));
294 }
295 PetscFunctionReturn(PETSC_SUCCESS);
296 }
297
KSPSolve_LGMRES(KSP ksp)298 static PetscErrorCode KSPSolve_LGMRES(KSP ksp)
299 {
300 PetscInt itcount; /* running total of iterations, incl. those in restarts */
301 KSP_LGMRES *lgmres = (KSP_LGMRES *)ksp->data;
302 PetscBool guess_zero = ksp->guess_zero;
303 PetscInt ii; /* LGMRES_MOD variable */
304
305 PetscFunctionBegin;
306 PetscCheck(!ksp->calc_sings || lgmres->Rsvd, PetscObjectComm((PetscObject)ksp), PETSC_ERR_ORDER, "Must call KSPSetComputeSingularValues() before KSPSetUp() is called");
307
308 PetscCall(PetscObjectSAWsTakeAccess((PetscObject)ksp));
309
310 ksp->its = 0;
311 lgmres->aug_ct = 0;
312 lgmres->matvecs = 0;
313
314 PetscCall(PetscObjectSAWsGrantAccess((PetscObject)ksp));
315
316 /* initialize */
317 itcount = 0;
318 /* LGMRES_MOD */
319 for (ii = 0; ii < lgmres->aug_dim; ii++) lgmres->aug_order[ii] = 0;
320
321 while (!ksp->reason) {
322 PetscInt cycle_its = 0; /* iterations done in a call to KSPLGMRESCycle */
323 /* calc residual - puts in VEC_VV(0) */
324 PetscCall(KSPInitialResidual(ksp, ksp->vec_sol, VEC_TEMP, VEC_TEMP_MATOP, VEC_VV(0), ksp->vec_rhs));
325 PetscCall(KSPLGMRESCycle(&cycle_its, ksp));
326 itcount += cycle_its;
327 if (itcount >= ksp->max_it) {
328 if (!ksp->reason) ksp->reason = KSP_DIVERGED_ITS;
329 break;
330 }
331 ksp->guess_zero = PETSC_FALSE; /* every future call to KSPInitialResidual() will have nonzero guess */
332 }
333 ksp->guess_zero = guess_zero; /* restore if user provided nonzero initial guess */
334 PetscFunctionReturn(PETSC_SUCCESS);
335 }
336
KSPDestroy_LGMRES(KSP ksp)337 static PetscErrorCode KSPDestroy_LGMRES(KSP ksp)
338 {
339 KSP_LGMRES *lgmres = (KSP_LGMRES *)ksp->data;
340
341 PetscFunctionBegin;
342 PetscCall(PetscFree(lgmres->augvecs));
343 if (lgmres->augwork_alloc) PetscCall(VecDestroyVecs(lgmres->augwork_alloc, &lgmres->augvecs_user_work[0]));
344 PetscCall(PetscFree(lgmres->augvecs_user_work));
345 PetscCall(PetscFree(lgmres->aug_order));
346 PetscCall(PetscFree(lgmres->hwork));
347 PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPLGMRESSetConstant_C", NULL));
348 PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPLGMRESSetAugDim_C", NULL));
349 PetscCall(KSPDestroy_GMRES(ksp));
350 PetscFunctionReturn(PETSC_SUCCESS);
351 }
352
KSPLGMRESBuildSoln(PetscScalar * nrs,Vec vguess,Vec vdest,KSP ksp,PetscInt it)353 static PetscErrorCode KSPLGMRESBuildSoln(PetscScalar *nrs, Vec vguess, Vec vdest, KSP ksp, PetscInt it)
354 {
355 PetscScalar tt;
356 PetscInt ii, k, j;
357 KSP_LGMRES *lgmres = (KSP_LGMRES *)ksp->data;
358 /* LGMRES_MOD */
359 PetscInt it_arnoldi, it_aug;
360 PetscInt jj, spot = 0;
361
362 PetscFunctionBegin;
363 /* Solve for solution vector that minimizes the residual */
364
365 /* If it is < 0, no lgmres steps have been performed */
366 if (it < 0) {
367 PetscCall(VecCopy(vguess, vdest)); /* VecCopy() is smart, exists immediately if vguess == vdest */
368 PetscFunctionReturn(PETSC_SUCCESS);
369 }
370
371 /* so (it+1) lgmres steps HAVE been performed */
372
373 /* LGMRES_MOD - determine if we need to use augvecs for the soln - do not assume that
374 this is called after the total its allowed for an approx space */
375 if (lgmres->approx_constant) {
376 it_arnoldi = lgmres->max_k - lgmres->aug_ct;
377 } else {
378 it_arnoldi = lgmres->max_k - lgmres->aug_dim;
379 }
380 if (it_arnoldi >= it + 1) {
381 it_aug = 0;
382 it_arnoldi = it + 1;
383 } else {
384 it_aug = (it + 1) - it_arnoldi;
385 }
386
387 /* now it_arnoldi indicates the number of matvecs that took place */
388 lgmres->matvecs += it_arnoldi;
389
390 /* solve the upper triangular system - GRS is the right side and HH is
391 the upper triangular matrix - put soln in nrs */
392 PetscCheck(*HH(it, it) != 0.0, PETSC_COMM_SELF, PETSC_ERR_CONV_FAILED, "HH(it,it) is identically zero; it = %" PetscInt_FMT " GRS(it) = %g", it, (double)PetscAbsScalar(*GRS(it)));
393 if (*HH(it, it) != 0.0) {
394 nrs[it] = *GRS(it) / *HH(it, it);
395 } else {
396 nrs[it] = 0.0;
397 }
398
399 for (ii = 1; ii <= it; ii++) {
400 k = it - ii;
401 tt = *GRS(k);
402 for (j = k + 1; j <= it; j++) tt = tt - *HH(k, j) * nrs[j];
403 nrs[k] = tt / *HH(k, k);
404 }
405
406 /* Accumulate the correction to the soln of the preconditioned prob. in VEC_TEMP */
407
408 /* LGMRES_MOD - if augmenting has happened we need to form the solution using the augvecs */
409 if (!it_aug) { /* all its are from arnoldi */
410 PetscCall(VecMAXPBY(VEC_TEMP, it + 1, nrs, 0, &VEC_VV(0)));
411 } else { /* use aug vecs */
412 /* first do regular Krylov directions */
413 PetscCall(VecMAXPBY(VEC_TEMP, it_arnoldi, nrs, 0, &VEC_VV(0)));
414 /* now add augmented portions - add contribution of aug vectors one at a time*/
415
416 for (ii = 0; ii < it_aug; ii++) {
417 for (jj = 0; jj < lgmres->aug_dim; jj++) {
418 if (lgmres->aug_order[jj] == (ii + 1)) {
419 spot = jj;
420 break; /* must have this because there will be duplicates before aug_ct = aug_dim */
421 }
422 }
423 PetscCall(VecAXPY(VEC_TEMP, nrs[it_arnoldi + ii], AUGVEC(spot)));
424 }
425 }
426 /* now VEC_TEMP is what we want to keep for augmenting purposes - grab before the
427 preconditioner is "unwound" from right-precondtioning*/
428 PetscCall(VecCopy(VEC_TEMP, AUG_TEMP));
429
430 PetscCall(KSPUnwindPreconditioner(ksp, VEC_TEMP, VEC_TEMP_MATOP));
431
432 /* add solution to previous solution */
433 /* put updated solution into vdest.*/
434 PetscCall(VecCopy(vguess, vdest));
435 PetscCall(VecAXPY(vdest, 1.0, VEC_TEMP));
436 PetscFunctionReturn(PETSC_SUCCESS);
437 }
438
KSPLGMRESUpdateHessenberg(KSP ksp,PetscInt it,PetscBool hapend,PetscReal * res)439 static PetscErrorCode KSPLGMRESUpdateHessenberg(KSP ksp, PetscInt it, PetscBool hapend, PetscReal *res)
440 {
441 PetscScalar *hh, *cc, *ss, tt;
442 PetscInt j;
443 KSP_LGMRES *lgmres = (KSP_LGMRES *)ksp->data;
444
445 PetscFunctionBegin;
446 hh = HH(0, it); /* pointer to beginning of column to update - so incrementing hh "steps down" the (it+1)th col of HH*/
447 cc = CC(0); /* beginning of cosine rotations */
448 ss = SS(0); /* beginning of sine rotations */
449
450 /* Apply all the previously computed plane rotations to the new column
451 of the Hessenberg matrix */
452 /* Note: this uses the rotation [conj(c) s ; -s c], c= cos(theta), s= sin(theta) */
453
454 for (j = 1; j <= it; j++) {
455 tt = *hh;
456 *hh = PetscConj(*cc) * tt + *ss * *(hh + 1);
457 hh++;
458 *hh = *cc++ * *hh - (*ss++ * tt);
459 /* hh, cc, and ss have all been incremented one by end of loop */
460 }
461
462 /*
463 compute the new plane rotation, and apply it to:
464 1) the right-hand side of the Hessenberg system (GRS)
465 note: it affects GRS(it) and GRS(it+1)
466 2) the new column of the Hessenberg matrix
467 note: it affects HH(it,it) which is currently pointed to
468 by hh and HH(it+1, it) (*(hh+1))
469 thus obtaining the updated value of the residual...
470 */
471
472 /* compute new plane rotation */
473
474 if (!hapend) {
475 tt = PetscSqrtScalar(PetscConj(*hh) * *hh + PetscConj(*(hh + 1)) * *(hh + 1));
476 if (tt == 0.0) {
477 ksp->reason = KSP_DIVERGED_NULL;
478 PetscFunctionReturn(PETSC_SUCCESS);
479 }
480 *cc = *hh / tt; /* new cosine value */
481 *ss = *(hh + 1) / tt; /* new sine value */
482
483 /* apply to 1) and 2) */
484 *GRS(it + 1) = -(*ss * *GRS(it));
485 *GRS(it) = PetscConj(*cc) * *GRS(it);
486 *hh = PetscConj(*cc) * *hh + *ss * *(hh + 1);
487
488 /* residual is the last element (it+1) of right-hand side! */
489 *res = PetscAbsScalar(*GRS(it + 1));
490
491 } else { /* happy breakdown: HH(it+1, it) = 0, therefore we don't need to apply
492 another rotation matrix (so RH doesn't change). The new residual is
493 always the new sine term times the residual from last time (GRS(it)),
494 but now the new sine rotation would be zero...so the residual should
495 be zero...so we will multiply "zero" by the last residual. This might
496 not be exactly what we want to do here -could just return "zero". */
497
498 *res = 0.0;
499 }
500 PetscFunctionReturn(PETSC_SUCCESS);
501 }
502
503 /*
504 KSPLGMRESGetNewVectors - Allocates more work vectors, starting from VEC_VV(it)
505
506 */
KSPLGMRESGetNewVectors(KSP ksp,PetscInt it)507 static PetscErrorCode KSPLGMRESGetNewVectors(KSP ksp, PetscInt it)
508 {
509 KSP_LGMRES *lgmres = (KSP_LGMRES *)ksp->data;
510 PetscInt nwork = lgmres->nwork_alloc; /* number of work vector chunks allocated */
511 PetscInt nalloc; /* number to allocate */
512 PetscInt k;
513
514 PetscFunctionBegin;
515 nalloc = lgmres->delta_allocate; /* number of vectors to allocate in a single chunk */
516
517 /* Adjust the number to allocate to make sure that we don't exceed the
518 number of available slots (lgmres->vecs_allocated)*/
519 if (it + VEC_OFFSET + nalloc >= lgmres->vecs_allocated) nalloc = lgmres->vecs_allocated - it - VEC_OFFSET;
520 if (!nalloc) PetscFunctionReturn(PETSC_SUCCESS);
521
522 lgmres->vv_allocated += nalloc; /* vv_allocated is the number of vectors allocated */
523
524 /* work vectors */
525 PetscCall(KSPCreateVecs(ksp, nalloc, &lgmres->user_work[nwork], 0, NULL));
526 /* specify size of chunk allocated */
527 lgmres->mwork_alloc[nwork] = nalloc;
528
529 for (k = 0; k < nalloc; k++) lgmres->vecs[it + VEC_OFFSET + k] = lgmres->user_work[nwork][k];
530
531 /* LGMRES_MOD - for now we are preallocating the augmentation vectors */
532
533 /* increment the number of work vector chunks */
534 lgmres->nwork_alloc++;
535 PetscFunctionReturn(PETSC_SUCCESS);
536 }
537
KSPBuildSolution_LGMRES(KSP ksp,Vec ptr,Vec * result)538 static PetscErrorCode KSPBuildSolution_LGMRES(KSP ksp, Vec ptr, Vec *result)
539 {
540 KSP_LGMRES *lgmres = (KSP_LGMRES *)ksp->data;
541
542 PetscFunctionBegin;
543 if (!ptr) {
544 if (!lgmres->sol_temp) PetscCall(VecDuplicate(ksp->vec_sol, &lgmres->sol_temp));
545 ptr = lgmres->sol_temp;
546 }
547 if (!lgmres->nrs) {
548 /* allocate the work area */
549 PetscCall(PetscMalloc1(lgmres->max_k, &lgmres->nrs));
550 }
551
552 PetscCall(KSPLGMRESBuildSoln(lgmres->nrs, ksp->vec_sol, ptr, ksp, lgmres->it));
553 if (result) *result = ptr;
554 PetscFunctionReturn(PETSC_SUCCESS);
555 }
556
KSPView_LGMRES(KSP ksp,PetscViewer viewer)557 static PetscErrorCode KSPView_LGMRES(KSP ksp, PetscViewer viewer)
558 {
559 KSP_LGMRES *lgmres = (KSP_LGMRES *)ksp->data;
560 PetscBool isascii;
561
562 PetscFunctionBegin;
563 PetscCall(KSPView_GMRES(ksp, viewer));
564 PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &isascii));
565 if (isascii) {
566 /* LGMRES_MOD */
567 PetscCall(PetscViewerASCIIPrintf(viewer, " aug. dimension=%" PetscInt_FMT "\n", lgmres->aug_dim));
568 if (lgmres->approx_constant) PetscCall(PetscViewerASCIIPrintf(viewer, " approx. space size was kept constant.\n"));
569 PetscCall(PetscViewerASCIIPrintf(viewer, " number of matvecs=%" PetscInt_FMT "\n", lgmres->matvecs));
570 }
571 PetscFunctionReturn(PETSC_SUCCESS);
572 }
573
KSPSetFromOptions_LGMRES(KSP ksp,PetscOptionItems PetscOptionsObject)574 static PetscErrorCode KSPSetFromOptions_LGMRES(KSP ksp, PetscOptionItems PetscOptionsObject)
575 {
576 PetscInt aug;
577 KSP_LGMRES *lgmres = (KSP_LGMRES *)ksp->data;
578 PetscBool flg = PETSC_FALSE;
579
580 PetscFunctionBegin;
581 PetscCall(KSPSetFromOptions_GMRES(ksp, PetscOptionsObject));
582 PetscOptionsHeadBegin(PetscOptionsObject, "KSP LGMRES Options");
583 PetscCall(PetscOptionsBool("-ksp_lgmres_constant", "Use constant approx. space size", "KSPGMRESSetConstant", lgmres->approx_constant, &lgmres->approx_constant, NULL));
584 PetscCall(PetscOptionsInt("-ksp_lgmres_augment", "Number of error approximations to augment the Krylov space with", "KSPLGMRESSetAugDim", lgmres->aug_dim, &aug, &flg));
585 if (flg) PetscCall(KSPLGMRESSetAugDim(ksp, aug));
586 PetscOptionsHeadEnd();
587 PetscFunctionReturn(PETSC_SUCCESS);
588 }
589
KSPLGMRESSetConstant_LGMRES(KSP ksp)590 static PetscErrorCode KSPLGMRESSetConstant_LGMRES(KSP ksp)
591 {
592 KSP_LGMRES *lgmres = (KSP_LGMRES *)ksp->data;
593
594 PetscFunctionBegin;
595 lgmres->approx_constant = PETSC_TRUE;
596 PetscFunctionReturn(PETSC_SUCCESS);
597 }
598
KSPLGMRESSetAugDim_LGMRES(KSP ksp,PetscInt aug_dim)599 static PetscErrorCode KSPLGMRESSetAugDim_LGMRES(KSP ksp, PetscInt aug_dim)
600 {
601 KSP_LGMRES *lgmres = (KSP_LGMRES *)ksp->data;
602
603 PetscFunctionBegin;
604 PetscCheck(aug_dim >= 0, PetscObjectComm((PetscObject)ksp), PETSC_ERR_ARG_OUTOFRANGE, "Augmentation dimension must be nonegative");
605 PetscCheck(aug_dim <= (lgmres->max_k - 1), PetscObjectComm((PetscObject)ksp), PETSC_ERR_ARG_OUTOFRANGE, "Augmentation dimension must be <= (restart size-1)");
606 lgmres->aug_dim = aug_dim;
607 PetscFunctionReturn(PETSC_SUCCESS);
608 }
609
610 /*MC
611 KSPLGMRES - Augments the standard GMRES approximation space with approximations to the error from previous restart cycles as in {cite}`bjm2005`.
612
613 Options Database Keys:
614 + -ksp_gmres_restart <restart> - total approximation space size (Krylov directions + error approximations)
615 . -ksp_gmres_haptol <tol> - sets the tolerance for "happy ending" (exact convergence)
616 . -ksp_gmres_preallocate - preallocate all the Krylov search directions initially
617 (otherwise groups of vectors are allocated as needed)
618 . -ksp_gmres_classicalgramschmidt - use classical (unmodified) Gram-Schmidt to orthogonalize against
619 the Krylov space (fast) (the default)
620 . -ksp_gmres_modifiedgramschmidt - use modified Gram-Schmidt in the orthogonalization (more stable, but slower)
621 . -ksp_gmres_cgs_refinement_type <refine_never,refine_ifneeded,refine_always> - determine if iterative refinement is used to increase the
622 stability of the classical Gram-Schmidt orthogonalization.
623 . -ksp_gmres_krylov_monitor - plot the Krylov space generated
624 . -ksp_lgmres_augment <k> - number of error approximations to augment the Krylov space with
625 - -ksp_lgmres_constant - use a constant approximate space size
626 (only affects restart cycles < num. error approx.(k), i.e. the first k restarts)
627
628 Level: beginner
629
630 Notes:
631 Supports both left and right preconditioning, but not symmetric.
632
633 Augmenting with 1,2, or 3 approximations is generally optimal.
634
635 This method is an accelerator for `KSPGMRES` - it is not useful for problems that stall. When gmres(m) stalls then lgmres with a size m
636 approximation space will also generally stall.
637
638 If gmres(m) converges in a small number of restart cycles, then lgmres also tends not to be very helpful.
639
640 Developer Notes:
641 To run LGMRES(m, k) as described in {cite}`bjm2005`, use\:
642 .vb
643 -ksp_gmres_restart <m+k>
644 -ksp_lgmres_augment <k>
645 .ve
646
647 This object is subclassed off of `KSPGMRES`, see the source code in src/ksp/ksp/impls/gmres for comments on the structure of the code
648
649 Contributed by:
650 Allison Baker
651
652 .seealso: [](ch_ksp), `KSPCreate()`, `KSPSetType()`, `KSPType`, `KSP`, `KSPFGMRES`, `KSPGMRES`,
653 `KSPGMRESSetRestart()`, `KSPGMRESSetHapTol()`, `KSPGMRESSetPreAllocateVectors()`, `KSPGMRESSetOrthogonalization()`, `KSPGMRESGetOrthogonalization()`,
654 `KSPGMRESClassicalGramSchmidtOrthogonalization()`, `KSPGMRESModifiedGramSchmidtOrthogonalization()`,
655 `KSPGMRESCGSRefinementType`, `KSPGMRESSetCGSRefinementType()`, `KSPGMRESGetCGSRefinementType()`, `KSPGMRESMonitorKrylov()`, `KSPLGMRESSetAugDim()`,
656 `KSPGMRESSetConstant()`, `KSPLGMRESSetConstant()`, `KSPLGMRESSetAugDim()`
657 M*/
658
KSPCreate_LGMRES(KSP ksp)659 PETSC_EXTERN PetscErrorCode KSPCreate_LGMRES(KSP ksp)
660 {
661 KSP_LGMRES *lgmres;
662
663 PetscFunctionBegin;
664 PetscCall(PetscNew(&lgmres));
665
666 ksp->data = (void *)lgmres;
667 ksp->ops->buildsolution = KSPBuildSolution_LGMRES;
668
669 ksp->ops->setup = KSPSetUp_LGMRES;
670 ksp->ops->solve = KSPSolve_LGMRES;
671 ksp->ops->destroy = KSPDestroy_LGMRES;
672 ksp->ops->view = KSPView_LGMRES;
673 ksp->ops->setfromoptions = KSPSetFromOptions_LGMRES;
674 ksp->ops->computeextremesingularvalues = KSPComputeExtremeSingularValues_GMRES;
675 ksp->ops->computeeigenvalues = KSPComputeEigenvalues_GMRES;
676
677 PetscCall(KSPSetSupportedNorm(ksp, KSP_NORM_PRECONDITIONED, PC_LEFT, 3));
678 PetscCall(KSPSetSupportedNorm(ksp, KSP_NORM_UNPRECONDITIONED, PC_RIGHT, 2));
679 PetscCall(KSPSetSupportedNorm(ksp, KSP_NORM_NONE, PC_RIGHT, 1));
680
681 PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESSetPreAllocateVectors_C", KSPGMRESSetPreAllocateVectors_GMRES));
682 PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESSetOrthogonalization_C", KSPGMRESSetOrthogonalization_GMRES));
683 PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESGetOrthogonalization_C", KSPGMRESGetOrthogonalization_GMRES));
684 PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESSetRestart_C", KSPGMRESSetRestart_GMRES));
685 PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESGetRestart_C", KSPGMRESGetRestart_GMRES));
686 PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESSetHapTol_C", KSPGMRESSetHapTol_GMRES));
687 PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESSetCGSRefinementType_C", KSPGMRESSetCGSRefinementType_GMRES));
688 PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESGetCGSRefinementType_C", KSPGMRESGetCGSRefinementType_GMRES));
689
690 /* LGMRES_MOD add extra functions here - like the one to set num of aug vectors */
691 PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPLGMRESSetConstant_C", KSPLGMRESSetConstant_LGMRES));
692 PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPLGMRESSetAugDim_C", KSPLGMRESSetAugDim_LGMRES));
693
694 /* defaults */
695 lgmres->haptol = 1.0e-30;
696 lgmres->q_preallocate = PETSC_FALSE;
697 lgmres->delta_allocate = LGMRES_DELTA_DIRECTIONS;
698 lgmres->orthog = KSPGMRESClassicalGramSchmidtOrthogonalization;
699 lgmres->nrs = NULL;
700 lgmres->sol_temp = NULL;
701 lgmres->max_k = LGMRES_DEFAULT_MAXK;
702 lgmres->Rsvd = NULL;
703 lgmres->cgstype = KSP_GMRES_CGS_REFINE_NEVER;
704 lgmres->orthogwork = NULL;
705
706 /* LGMRES_MOD - new defaults */
707 lgmres->aug_dim = LGMRES_DEFAULT_AUGDIM;
708 lgmres->aug_ct = 0; /* start with no aug vectors */
709 lgmres->approx_constant = PETSC_FALSE;
710 lgmres->matvecs = 0;
711 PetscFunctionReturn(PETSC_SUCCESS);
712 }
713