1 2 /* 3 This file implements PGMRES (a Pipelined Generalized Minimal Residual method) 4 */ 5 6 #include <../src/ksp/ksp/impls/gmres/pgmres/pgmresimpl.h> /*I "petscksp.h" I*/ 7 #define PGMRES_DELTA_DIRECTIONS 10 8 #define PGMRES_DEFAULT_MAXK 30 9 10 static PetscErrorCode KSPPGMRESUpdateHessenberg(KSP, PetscInt, PetscBool *, PetscReal *); 11 static PetscErrorCode KSPPGMRESBuildSoln(PetscScalar *, Vec, Vec, KSP, PetscInt); 12 13 /* 14 15 KSPSetUp_PGMRES - Sets up the workspace needed by pgmres. 16 17 This is called once, usually automatically by KSPSolve() or KSPSetUp(), 18 but can be called directly by KSPSetUp(). 19 20 */ 21 static PetscErrorCode KSPSetUp_PGMRES(KSP ksp) { 22 PetscFunctionBegin; 23 PetscCall(KSPSetUp_GMRES(ksp)); 24 PetscFunctionReturn(0); 25 } 26 27 /* 28 29 KSPPGMRESCycle - Run pgmres, possibly with restart. Return residual 30 history if requested. 31 32 input parameters: 33 . pgmres - structure containing parameters and work areas 34 35 output parameters: 36 . itcount - number of iterations used. If null, ignored. 37 . converged - 0 if not converged 38 39 Notes: 40 On entry, the value in vector VEC_VV(0) should be 41 the initial residual. 42 43 */ 44 static PetscErrorCode KSPPGMRESCycle(PetscInt *itcount, KSP ksp) { 45 KSP_PGMRES *pgmres = (KSP_PGMRES *)(ksp->data); 46 PetscReal res_norm, res, newnorm; 47 PetscInt it = 0, j, k; 48 PetscBool hapend = PETSC_FALSE; 49 50 PetscFunctionBegin; 51 if (itcount) *itcount = 0; 52 PetscCall(VecNormalize(VEC_VV(0), &res_norm)); 53 KSPCheckNorm(ksp, res_norm); 54 res = res_norm; 55 *RS(0) = res_norm; 56 57 /* check for the convergence */ 58 PetscCall(PetscObjectSAWsTakeAccess((PetscObject)ksp)); 59 if (ksp->normtype != KSP_NORM_NONE) ksp->rnorm = res; 60 else ksp->rnorm = 0; 61 PetscCall(PetscObjectSAWsGrantAccess((PetscObject)ksp)); 62 pgmres->it = it - 2; 63 PetscCall(KSPLogResidualHistory(ksp, ksp->rnorm)); 64 PetscCall(KSPMonitor(ksp, ksp->its, ksp->rnorm)); 65 if (!res) { 66 ksp->reason = KSP_CONVERGED_ATOL; 67 PetscCall(PetscInfo(ksp, "Converged due to zero residual norm on entry\n")); 68 PetscFunctionReturn(0); 69 } 70 71 PetscCall((*ksp->converged)(ksp, ksp->its, ksp->rnorm, &ksp->reason, ksp->cnvP)); 72 for (; !ksp->reason; it++) { 73 Vec Zcur, Znext; 74 if (pgmres->vv_allocated <= it + VEC_OFFSET + 1) PetscCall(KSPGMRESGetNewVectors(ksp, it + 1)); 75 /* VEC_VV(it-1) is orthogonal, it will be normalized once the VecNorm arrives. */ 76 Zcur = VEC_VV(it); /* Zcur is not yet orthogonal, but the VecMDot to orthogonalize it has been started. */ 77 Znext = VEC_VV(it + 1); /* This iteration will compute Znext, update with a deferred correction once we know how 78 * Zcur relates to the previous vectors, and start the reduction to orthogonalize it. */ 79 80 if (it < pgmres->max_k + 1 && ksp->its + 1 < PetscMax(2, ksp->max_it)) { /* We don't know whether what we have computed is enough, so apply the matrix. */ 81 PetscCall(KSP_PCApplyBAorAB(ksp, Zcur, Znext, VEC_TEMP_MATOP)); 82 } 83 84 if (it > 1) { /* Complete the pending reduction */ 85 PetscCall(VecNormEnd(VEC_VV(it - 1), NORM_2, &newnorm)); 86 *HH(it - 1, it - 2) = newnorm; 87 } 88 if (it > 0) { /* Finish the reduction computing the latest column of H */ 89 PetscCall(VecMDotEnd(Zcur, it, &(VEC_VV(0)), HH(0, it - 1))); 90 } 91 92 if (it > 1) { 93 /* normalize the base vector from two iterations ago, basis is complete up to here */ 94 PetscCall(VecScale(VEC_VV(it - 1), 1. / *HH(it - 1, it - 2))); 95 96 PetscCall(KSPPGMRESUpdateHessenberg(ksp, it - 2, &hapend, &res)); 97 pgmres->it = it - 2; 98 ksp->its++; 99 if (ksp->normtype != KSP_NORM_NONE) ksp->rnorm = res; 100 else ksp->rnorm = 0; 101 102 PetscCall((*ksp->converged)(ksp, ksp->its, ksp->rnorm, &ksp->reason, ksp->cnvP)); 103 if (it < pgmres->max_k + 1 || ksp->reason || ksp->its == ksp->max_it) { /* Monitor if we are done or still iterating, but not before a restart. */ 104 PetscCall(KSPLogResidualHistory(ksp, ksp->rnorm)); 105 PetscCall(KSPMonitor(ksp, ksp->its, ksp->rnorm)); 106 } 107 if (ksp->reason) break; 108 /* Catch error in happy breakdown and signal convergence and break from loop */ 109 if (hapend) { 110 PetscCheck(!ksp->errorifnotconverged, PetscObjectComm((PetscObject)ksp), PETSC_ERR_NOT_CONVERGED, "You reached the happy break down, but convergence was not indicated. Residual norm = %g", (double)res); 111 ksp->reason = KSP_DIVERGED_BREAKDOWN; 112 break; 113 } 114 115 if (!(it < pgmres->max_k + 1 && ksp->its < ksp->max_it)) break; 116 117 /* The it-2 column of H was not scaled when we computed Zcur, apply correction */ 118 PetscCall(VecScale(Zcur, 1. / *HH(it - 1, it - 2))); 119 /* And Znext computed in this iteration was computed using the under-scaled Zcur */ 120 PetscCall(VecScale(Znext, 1. / *HH(it - 1, it - 2))); 121 122 /* In the previous iteration, we projected an unnormalized Zcur against the Krylov basis, so we need to fix the column of H resulting from that projection. */ 123 for (k = 0; k < it; k++) *HH(k, it - 1) /= *HH(it - 1, it - 2); 124 /* When Zcur was projected against the Krylov basis, VV(it-1) was still not normalized, so fix that too. This 125 * column is complete except for HH(it,it-1) which we won't know until the next iteration. */ 126 *HH(it - 1, it - 1) /= *HH(it - 1, it - 2); 127 } 128 129 if (it > 0) { 130 PetscScalar *work; 131 if (!pgmres->orthogwork) PetscCall(PetscMalloc1(pgmres->max_k + 2, &pgmres->orthogwork)); 132 work = pgmres->orthogwork; 133 /* Apply correction computed by the VecMDot in the last iteration to Znext. The original form is 134 * 135 * Znext -= sum_{j=0}^{i-1} Z[j+1] * H[j,i-1] 136 * 137 * where 138 * 139 * Z[j] = sum_{k=0}^j V[k] * H[k,j-1] 140 * 141 * substituting 142 * 143 * Znext -= sum_{j=0}^{i-1} sum_{k=0}^{j+1} V[k] * H[k,j] * H[j,i-1] 144 * 145 * rearranging the iteration space from row-column to column-row 146 * 147 * Znext -= sum_{k=0}^i sum_{j=k-1}^{i-1} V[k] * H[k,j] * H[j,i-1] 148 * 149 * Note that column it-1 of HH is correct. For all previous columns, we must look at HES because HH has already 150 * been transformed to upper triangular form. 151 */ 152 for (k = 0; k < it + 1; k++) { 153 work[k] = 0; 154 for (j = PetscMax(0, k - 1); j < it - 1; j++) work[k] -= *HES(k, j) * *HH(j, it - 1); 155 } 156 PetscCall(VecMAXPY(Znext, it + 1, work, &VEC_VV(0))); 157 PetscCall(VecAXPY(Znext, -*HH(it - 1, it - 1), Zcur)); 158 159 /* Orthogonalize Zcur against existing basis vectors. */ 160 for (k = 0; k < it; k++) work[k] = -*HH(k, it - 1); 161 PetscCall(VecMAXPY(Zcur, it, work, &VEC_VV(0))); 162 /* Zcur is now orthogonal, and will be referred to as VEC_VV(it) again, though it is still not normalized. */ 163 /* Begin computing the norm of the new vector, will be normalized after the MatMult in the next iteration. */ 164 PetscCall(VecNormBegin(VEC_VV(it), NORM_2, &newnorm)); 165 } 166 167 /* Compute column of H (to the diagonal, but not the subdiagonal) to be able to orthogonalize the newest vector. */ 168 PetscCall(VecMDotBegin(Znext, it + 1, &VEC_VV(0), HH(0, it))); 169 170 /* Start an asynchronous split-mode reduction, the result of the MDot and Norm will be collected on the next iteration. */ 171 PetscCall(PetscCommSplitReductionBegin(PetscObjectComm((PetscObject)Znext))); 172 } 173 174 if (itcount) *itcount = it - 1; /* Number of iterations actually completed. */ 175 176 /* 177 Down here we have to solve for the "best" coefficients of the Krylov 178 columns, add the solution values together, and possibly unwind the 179 preconditioning from the solution 180 */ 181 /* Form the solution (or the solution so far) */ 182 PetscCall(KSPPGMRESBuildSoln(RS(0), ksp->vec_sol, ksp->vec_sol, ksp, it - 2)); 183 PetscFunctionReturn(0); 184 } 185 186 /* 187 KSPSolve_PGMRES - This routine applies the PGMRES method. 188 189 Input Parameter: 190 . ksp - the Krylov space object that was set to use pgmres 191 192 Output Parameter: 193 . outits - number of iterations used 194 195 */ 196 static PetscErrorCode KSPSolve_PGMRES(KSP ksp) { 197 PetscInt its, itcount; 198 KSP_PGMRES *pgmres = (KSP_PGMRES *)ksp->data; 199 PetscBool guess_zero = ksp->guess_zero; 200 201 PetscFunctionBegin; 202 PetscCheck(!ksp->calc_sings || pgmres->Rsvd, PetscObjectComm((PetscObject)ksp), PETSC_ERR_ORDER, "Must call KSPSetComputeSingularValues() before KSPSetUp() is called"); 203 PetscCall(PetscObjectSAWsTakeAccess((PetscObject)ksp)); 204 ksp->its = 0; 205 PetscCall(PetscObjectSAWsGrantAccess((PetscObject)ksp)); 206 207 itcount = 0; 208 ksp->reason = KSP_CONVERGED_ITERATING; 209 while (!ksp->reason) { 210 PetscCall(KSPInitialResidual(ksp, ksp->vec_sol, VEC_TEMP, VEC_TEMP_MATOP, VEC_VV(0), ksp->vec_rhs)); 211 PetscCall(KSPPGMRESCycle(&its, ksp)); 212 itcount += its; 213 if (itcount >= ksp->max_it) { 214 if (!ksp->reason) ksp->reason = KSP_DIVERGED_ITS; 215 break; 216 } 217 ksp->guess_zero = PETSC_FALSE; /* every future call to KSPInitialResidual() will have nonzero guess */ 218 } 219 ksp->guess_zero = guess_zero; /* restore if user provided nonzero initial guess */ 220 PetscFunctionReturn(0); 221 } 222 223 static PetscErrorCode KSPDestroy_PGMRES(KSP ksp) { 224 PetscFunctionBegin; 225 PetscCall(KSPDestroy_GMRES(ksp)); 226 PetscFunctionReturn(0); 227 } 228 229 /* 230 KSPPGMRESBuildSoln - create the solution from the starting vector and the 231 current iterates. 232 233 Input parameters: 234 nrs - work area of size it + 1. 235 vguess - index of initial guess 236 vdest - index of result. Note that vguess may == vdest (replace 237 guess with the solution). 238 it - HH upper triangular part is a block of size (it+1) x (it+1) 239 240 This is an internal routine that knows about the PGMRES internals. 241 */ 242 static PetscErrorCode KSPPGMRESBuildSoln(PetscScalar *nrs, Vec vguess, Vec vdest, KSP ksp, PetscInt it) { 243 PetscScalar tt; 244 PetscInt k, j; 245 KSP_PGMRES *pgmres = (KSP_PGMRES *)(ksp->data); 246 247 PetscFunctionBegin; 248 /* Solve for solution vector that minimizes the residual */ 249 250 if (it < 0) { /* no pgmres steps have been performed */ 251 PetscCall(VecCopy(vguess, vdest)); /* VecCopy() is smart, exits immediately if vguess == vdest */ 252 PetscFunctionReturn(0); 253 } 254 255 /* solve the upper triangular system - RS is the right side and HH is 256 the upper triangular matrix - put soln in nrs */ 257 if (*HH(it, it) != 0.0) nrs[it] = *RS(it) / *HH(it, it); 258 else nrs[it] = 0.0; 259 260 for (k = it - 1; k >= 0; k--) { 261 tt = *RS(k); 262 for (j = k + 1; j <= it; j++) tt -= *HH(k, j) * nrs[j]; 263 nrs[k] = tt / *HH(k, k); 264 } 265 266 /* Accumulate the correction to the solution of the preconditioned problem in TEMP */ 267 PetscCall(VecZeroEntries(VEC_TEMP)); 268 PetscCall(VecMAXPY(VEC_TEMP, it + 1, nrs, &VEC_VV(0))); 269 PetscCall(KSPUnwindPreconditioner(ksp, VEC_TEMP, VEC_TEMP_MATOP)); 270 /* add solution to previous solution */ 271 if (vdest == vguess) { 272 PetscCall(VecAXPY(vdest, 1.0, VEC_TEMP)); 273 } else { 274 PetscCall(VecWAXPY(vdest, 1.0, VEC_TEMP, vguess)); 275 } 276 PetscFunctionReturn(0); 277 } 278 279 /* 280 281 KSPPGMRESUpdateHessenberg - Do the scalar work for the orthogonalization. 282 Return new residual. 283 284 input parameters: 285 286 . ksp - Krylov space object 287 . it - plane rotations are applied to the (it+1)th column of the 288 modified hessenberg (i.e. HH(:,it)) 289 . hapend - PETSC_FALSE not happy breakdown ending. 290 291 output parameters: 292 . res - the new residual 293 294 */ 295 /* 296 . it - column of the Hessenberg that is complete, PGMRES is actually computing two columns ahead of this 297 */ 298 static PetscErrorCode KSPPGMRESUpdateHessenberg(KSP ksp, PetscInt it, PetscBool *hapend, PetscReal *res) { 299 PetscScalar *hh, *cc, *ss, *rs; 300 PetscInt j; 301 PetscReal hapbnd; 302 KSP_PGMRES *pgmres = (KSP_PGMRES *)(ksp->data); 303 304 PetscFunctionBegin; 305 hh = HH(0, it); /* pointer to beginning of column to update */ 306 cc = CC(0); /* beginning of cosine rotations */ 307 ss = SS(0); /* beginning of sine rotations */ 308 rs = RS(0); /* right hand side of least squares system */ 309 310 /* The Hessenberg matrix is now correct through column it, save that form for possible spectral analysis */ 311 for (j = 0; j <= it + 1; j++) *HES(j, it) = hh[j]; 312 313 /* check for the happy breakdown */ 314 hapbnd = PetscMin(PetscAbsScalar(hh[it + 1] / rs[it]), pgmres->haptol); 315 if (PetscAbsScalar(hh[it + 1]) < hapbnd) { 316 PetscCall(PetscInfo(ksp, "Detected happy breakdown, current hapbnd = %14.12e H(%" PetscInt_FMT ",%" PetscInt_FMT ") = %14.12e\n", (double)hapbnd, it + 1, it, (double)PetscAbsScalar(*HH(it + 1, it)))); 317 *hapend = PETSC_TRUE; 318 } 319 320 /* Apply all the previously computed plane rotations to the new column 321 of the Hessenberg matrix */ 322 /* Note: this uses the rotation [conj(c) s ; -s c], c= cos(theta), s= sin(theta), 323 and some refs have [c s ; -conj(s) c] (don't be confused!) */ 324 325 for (j = 0; j < it; j++) { 326 PetscScalar hhj = hh[j]; 327 hh[j] = PetscConj(cc[j]) * hhj + ss[j] * hh[j + 1]; 328 hh[j + 1] = -ss[j] * hhj + cc[j] * hh[j + 1]; 329 } 330 331 /* 332 compute the new plane rotation, and apply it to: 333 1) the right-hand-side of the Hessenberg system (RS) 334 note: it affects RS(it) and RS(it+1) 335 2) the new column of the Hessenberg matrix 336 note: it affects HH(it,it) which is currently pointed to 337 by hh and HH(it+1, it) (*(hh+1)) 338 thus obtaining the updated value of the residual... 339 */ 340 341 /* compute new plane rotation */ 342 343 if (!*hapend) { 344 PetscReal delta = PetscSqrtReal(PetscSqr(PetscAbsScalar(hh[it])) + PetscSqr(PetscAbsScalar(hh[it + 1]))); 345 if (delta == 0.0) { 346 ksp->reason = KSP_DIVERGED_NULL; 347 PetscFunctionReturn(0); 348 } 349 350 cc[it] = hh[it] / delta; /* new cosine value */ 351 ss[it] = hh[it + 1] / delta; /* new sine value */ 352 353 hh[it] = PetscConj(cc[it]) * hh[it] + ss[it] * hh[it + 1]; 354 rs[it + 1] = -ss[it] * rs[it]; 355 rs[it] = PetscConj(cc[it]) * rs[it]; 356 *res = PetscAbsScalar(rs[it + 1]); 357 } else { /* happy breakdown: HH(it+1, it) = 0, therefore we don't need to apply 358 another rotation matrix (so RH doesn't change). The new residual is 359 always the new sine term times the residual from last time (RS(it)), 360 but now the new sine rotation would be zero...so the residual should 361 be zero...so we will multiply "zero" by the last residual. This might 362 not be exactly what we want to do here -could just return "zero". */ 363 364 *res = 0.0; 365 } 366 PetscFunctionReturn(0); 367 } 368 369 /* 370 KSPBuildSolution_PGMRES 371 372 Input Parameter: 373 . ksp - the Krylov space object 374 . ptr- 375 376 Output Parameter: 377 . result - the solution 378 379 Note: this calls KSPPGMRESBuildSoln - the same function that KSPPGMRESCycle 380 calls directly. 381 382 */ 383 PetscErrorCode KSPBuildSolution_PGMRES(KSP ksp, Vec ptr, Vec *result) { 384 KSP_PGMRES *pgmres = (KSP_PGMRES *)ksp->data; 385 386 PetscFunctionBegin; 387 if (!ptr) { 388 if (!pgmres->sol_temp) { 389 PetscCall(VecDuplicate(ksp->vec_sol, &pgmres->sol_temp)); 390 PetscCall(PetscLogObjectParent((PetscObject)ksp, (PetscObject)pgmres->sol_temp)); 391 } 392 ptr = pgmres->sol_temp; 393 } 394 if (!pgmres->nrs) { 395 /* allocate the work area */ 396 PetscCall(PetscMalloc1(pgmres->max_k, &pgmres->nrs)); 397 PetscCall(PetscLogObjectMemory((PetscObject)ksp, pgmres->max_k * sizeof(PetscScalar))); 398 } 399 400 PetscCall(KSPPGMRESBuildSoln(pgmres->nrs, ksp->vec_sol, ptr, ksp, pgmres->it)); 401 if (result) *result = ptr; 402 PetscFunctionReturn(0); 403 } 404 405 PetscErrorCode KSPSetFromOptions_PGMRES(KSP ksp, PetscOptionItems *PetscOptionsObject) { 406 PetscFunctionBegin; 407 PetscCall(KSPSetFromOptions_GMRES(ksp, PetscOptionsObject)); 408 PetscOptionsHeadBegin(PetscOptionsObject, "KSP pipelined GMRES Options"); 409 PetscOptionsHeadEnd(); 410 PetscFunctionReturn(0); 411 } 412 413 PetscErrorCode KSPReset_PGMRES(KSP ksp) { 414 PetscFunctionBegin; 415 PetscCall(KSPReset_GMRES(ksp)); 416 PetscFunctionReturn(0); 417 } 418 419 /*MC 420 KSPPGMRES - Implements the Pipelined Generalized Minimal Residual method. 421 422 Options Database Keys: 423 + -ksp_gmres_restart <restart> - the number of Krylov directions to orthogonalize against 424 . -ksp_gmres_haptol <tol> - sets the tolerance for "happy ending" (exact convergence) 425 . -ksp_gmres_preallocate - preallocate all the Krylov search directions initially (otherwise groups of 426 vectors are allocated as needed) 427 . -ksp_gmres_classicalgramschmidt - use classical (unmodified) Gram-Schmidt to orthogonalize against the Krylov space (fast) (the default) 428 . -ksp_gmres_modifiedgramschmidt - use modified Gram-Schmidt in the orthogonalization (more stable, but slower) 429 . -ksp_gmres_cgs_refinement_type <refine_never,refine_ifneeded,refine_always> - determine if iterative refinement is used to increase the 430 stability of the classical Gram-Schmidt orthogonalization. 431 - -ksp_gmres_krylov_monitor - plot the Krylov space generated 432 433 Level: beginner 434 435 Notes: 436 MPI configuration may be necessary for reductions to make asynchronous progress, which is important for performance of pipelined methods. 437 See the FAQ on the PETSc website for details. 438 439 Reference: 440 Ghysels, Ashby, Meerbergen, Vanroose, Hiding global communication latencies in the GMRES algorithm on massively parallel machines, 2012. 441 442 Developer Notes: 443 This object is subclassed off of KSPGMRES 444 445 .seealso: `KSPCreate()`, `KSPSetType()`, `KSPType`, `KSP`, `KSPGMRES`, `KSPLGMRES`, `KSPPIPECG`, `KSPPIPECR`, 446 `KSPGMRESSetRestart()`, `KSPGMRESSetHapTol()`, `KSPGMRESSetPreAllocateVectors()`, `KSPGMRESSetOrthogonalization()`, `KSPGMRESGetOrthogonalization()`, 447 `KSPGMRESClassicalGramSchmidtOrthogonalization()`, `KSPGMRESModifiedGramSchmidtOrthogonalization()`, 448 `KSPGMRESCGSRefinementType`, `KSPGMRESSetCGSRefinementType()`, `KSPGMRESGetCGSRefinementType()`, `KSPGMRESMonitorKrylov()` 449 M*/ 450 451 PETSC_EXTERN PetscErrorCode KSPCreate_PGMRES(KSP ksp) { 452 KSP_PGMRES *pgmres; 453 454 PetscFunctionBegin; 455 PetscCall(PetscNewLog(ksp, &pgmres)); 456 457 ksp->data = (void *)pgmres; 458 ksp->ops->buildsolution = KSPBuildSolution_PGMRES; 459 ksp->ops->setup = KSPSetUp_PGMRES; 460 ksp->ops->solve = KSPSolve_PGMRES; 461 ksp->ops->reset = KSPReset_PGMRES; 462 ksp->ops->destroy = KSPDestroy_PGMRES; 463 ksp->ops->view = KSPView_GMRES; 464 ksp->ops->setfromoptions = KSPSetFromOptions_PGMRES; 465 ksp->ops->computeextremesingularvalues = KSPComputeExtremeSingularValues_GMRES; 466 ksp->ops->computeeigenvalues = KSPComputeEigenvalues_GMRES; 467 468 PetscCall(KSPSetSupportedNorm(ksp, KSP_NORM_PRECONDITIONED, PC_LEFT, 3)); 469 PetscCall(KSPSetSupportedNorm(ksp, KSP_NORM_UNPRECONDITIONED, PC_RIGHT, 2)); 470 PetscCall(KSPSetSupportedNorm(ksp, KSP_NORM_NONE, PC_RIGHT, 1)); 471 472 PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESSetPreAllocateVectors_C", KSPGMRESSetPreAllocateVectors_GMRES)); 473 PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESSetOrthogonalization_C", KSPGMRESSetOrthogonalization_GMRES)); 474 PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESGetOrthogonalization_C", KSPGMRESGetOrthogonalization_GMRES)); 475 PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESSetRestart_C", KSPGMRESSetRestart_GMRES)); 476 PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESGetRestart_C", KSPGMRESGetRestart_GMRES)); 477 PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESSetCGSRefinementType_C", KSPGMRESSetCGSRefinementType_GMRES)); 478 PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPGMRESGetCGSRefinementType_C", KSPGMRESGetCGSRefinementType_GMRES)); 479 480 pgmres->nextra_vecs = 1; 481 pgmres->haptol = 1.0e-30; 482 pgmres->q_preallocate = 0; 483 pgmres->delta_allocate = PGMRES_DELTA_DIRECTIONS; 484 pgmres->orthog = KSPGMRESClassicalGramSchmidtOrthogonalization; 485 pgmres->nrs = NULL; 486 pgmres->sol_temp = NULL; 487 pgmres->max_k = PGMRES_DEFAULT_MAXK; 488 pgmres->Rsvd = NULL; 489 pgmres->orthogwork = NULL; 490 pgmres->cgstype = KSP_GMRES_CGS_REFINE_NEVER; 491 PetscFunctionReturn(0); 492 } 493