Actual source code: ibcgs.c

  1: #include <petsc/private/kspimpl.h>
  2: #include <petsc/private/vecimpl.h>

  4: static PetscErrorCode KSPSetUp_IBCGS(KSP ksp)
  5: {
  6:   PetscFunctionBegin;
  7:   PetscCall(KSPSetWorkVecs(ksp, 9));
  8:   PetscFunctionReturn(PETSC_SUCCESS);
  9: }

 11: /*
 12:     The code below "cheats" from PETSc style
 13:        1) VecRestoreArray() is called immediately after VecGetArray() and the array values are still accessed; the reason for the immediate
 14:           restore is that Vec operations are done on some of the vectors during the solve and if we did not restore immediately it would
 15:           generate two VecGetArray() (the second one inside the Vec operation) calls without a restore between them.
 16:        2) The vector operations on done directly on the arrays instead of with VecXXXX() calls

 18:        For clarity in the code we name single VECTORS with two names, for example, Rn_1 and R, but they actually always
 19:      the exact same memory. We do this with macro defines so that compiler won't think they are
 20:      two different variables.

 22: */
 23: #define Xn_1 Xn
 24: #define xn_1 xn
 25: #define Rn_1 Rn
 26: #define rn_1 rn
 27: #define Un_1 Un
 28: #define un_1 un
 29: #define Vn_1 Vn
 30: #define vn_1 vn
 31: #define Qn_1 Qn
 32: #define qn_1 qn
 33: #define Zn_1 Zn
 34: #define zn_1 zn
 35: static PetscErrorCode KSPSolve_IBCGS(KSP ksp)
 36: {
 37:   PetscInt  N;
 38:   PetscReal rnorm = 0.0, rnormin = 0.0;
 39: #if PetscDefined(HAVE_MPI_LONG_DOUBLE) && !PetscDefined(USE_COMPLEX) && (PetscDefined(USE_REAL_SINGLE) || PetscDefined(USE_REAL_DOUBLE))
 40:   /* Because of possible instabilities in the algorithm (as indicated by different residual histories for the same problem
 41:      on the same number of processes  with different runs) we support computing the inner products using Intel's 80 bit arithmetic
 42:      rather than just 64-bit. Thus we copy our double precision values into long doubles (hoping this keeps the 16 extra bits)
 43:      and tell MPI to do its ALlreduces with MPI_LONG_DOUBLE.

 45:      Note for developers that does not effect the code. Intel's long double is implemented by storing the 80 bits of extended double
 46:      precision into a 16 byte space (the rest of the space is ignored)  */
 47:   long double outsums[7];
 48: #else
 49:   PetscScalar outsums[7];
 50: #endif
 51:   PetscScalar                       sigman_2, sigman_1, sigman, pin_1, pin, phin_1, phin, tmp1, tmp2;
 52:   PetscScalar                       taun_1, taun, rhon, alphan_1, alphan, omegan_1, omegan;
 53:   const PetscScalar *PETSC_RESTRICT r0, *PETSC_RESTRICT f0, *PETSC_RESTRICT qn, *PETSC_RESTRICT b, *PETSC_RESTRICT un;
 54:   PetscScalar *PETSC_RESTRICT rn, *PETSC_RESTRICT xn, *PETSC_RESTRICT vn, *PETSC_RESTRICT zn;
 55:   /* the rest do not have to keep n_1 values */
 56:   PetscScalar                       kappan, thetan, etan, gamman, betan, deltan;
 57:   const PetscScalar *PETSC_RESTRICT tn;
 58:   PetscScalar *PETSC_RESTRICT       sn;
 59:   Vec                               R0, Rn, Xn, F0, Vn, Zn, Qn, Tn, Sn, B, Un;
 60:   Mat                               A;

 62:   PetscFunctionBegin;
 63:   PetscCheck(ksp->vec_rhs->petscnative, PetscObjectComm((PetscObject)ksp), PETSC_ERR_SUP, "Only coded for PETSc vectors");

 65: #if PetscDefined(HAVE_MPI_LONG_DOUBLE) && !PetscDefined(USE_COMPLEX) && (PetscDefined(USE_REAL_SINGLE) || PetscDefined(USE_REAL_DOUBLE))
 66:   /* since 80 bit long doubls do not fill the upper bits, we fill them initially so that
 67:      valgrind won't detect MPI_Allreduce() with uninitialized data */
 68:   PetscCall(PetscMemzero(outsums, sizeof(outsums)));
 69: #endif

 71:   PetscCall(PCGetOperators(ksp->pc, &A, NULL));
 72:   PetscCall(VecGetLocalSize(ksp->vec_sol, &N));
 73:   Xn = ksp->vec_sol;
 74:   PetscCall(VecGetArray(Xn_1, (PetscScalar **)&xn_1));
 75:   PetscCall(VecRestoreArray(Xn_1, NULL));
 76:   B = ksp->vec_rhs;
 77:   PetscCall(VecGetArrayRead(B, (const PetscScalar **)&b));
 78:   PetscCall(VecRestoreArrayRead(B, NULL));
 79:   R0 = ksp->work[0];
 80:   PetscCall(VecGetArrayRead(R0, (const PetscScalar **)&r0));
 81:   PetscCall(VecRestoreArrayRead(R0, NULL));
 82:   Rn = ksp->work[1];
 83:   PetscCall(VecGetArray(Rn_1, (PetscScalar **)&rn_1));
 84:   PetscCall(VecRestoreArray(Rn_1, NULL));
 85:   Un = ksp->work[2];
 86:   PetscCall(VecGetArrayRead(Un_1, (const PetscScalar **)&un_1));
 87:   PetscCall(VecRestoreArrayRead(Un_1, NULL));
 88:   F0 = ksp->work[3];
 89:   PetscCall(VecGetArrayRead(F0, (const PetscScalar **)&f0));
 90:   PetscCall(VecRestoreArrayRead(F0, NULL));
 91:   Vn = ksp->work[4];
 92:   PetscCall(VecGetArray(Vn_1, (PetscScalar **)&vn_1));
 93:   PetscCall(VecRestoreArray(Vn_1, NULL));
 94:   Zn = ksp->work[5];
 95:   PetscCall(VecGetArray(Zn_1, (PetscScalar **)&zn_1));
 96:   PetscCall(VecRestoreArray(Zn_1, NULL));
 97:   Qn = ksp->work[6];
 98:   PetscCall(VecGetArrayRead(Qn_1, (const PetscScalar **)&qn_1));
 99:   PetscCall(VecRestoreArrayRead(Qn_1, NULL));
100:   Tn = ksp->work[7];
101:   PetscCall(VecGetArrayRead(Tn, (const PetscScalar **)&tn));
102:   PetscCall(VecRestoreArrayRead(Tn, NULL));
103:   Sn = ksp->work[8];
104:   PetscCall(VecGetArrayRead(Sn, (const PetscScalar **)&sn));
105:   PetscCall(VecRestoreArrayRead(Sn, NULL));

107:   /* r0 = rn_1 = b - A*xn_1; */
108:   /* PetscCall(KSP_PCApplyBAorAB(ksp,Xn_1,Rn_1,Tn));
109:      PetscCall(VecAYPX(Rn_1,-1.0,B)); */
110:   PetscCall(KSPInitialResidual(ksp, Xn_1, Tn, Sn, Rn_1, B));
111:   if (ksp->normtype != KSP_NORM_NONE) {
112:     PetscCall(VecNorm(Rn_1, NORM_2, &rnorm));
113:     KSPCheckNorm(ksp, rnorm);
114:   }
115:   PetscCall(KSPMonitor(ksp, 0, rnorm));
116:   PetscCall((*ksp->converged)(ksp, 0, rnorm, &ksp->reason, ksp->cnvP));
117:   if (ksp->reason) PetscFunctionReturn(PETSC_SUCCESS);

119:   PetscCall(VecCopy(Rn_1, R0));

121:   /* un_1 = A*rn_1; */
122:   PetscCall(KSP_PCApplyBAorAB(ksp, Rn_1, Un_1, Tn));

124:   /* f0   = A'*rn_1; */
125:   if (ksp->pc_side == PC_RIGHT) { /* B' A' */
126:     PetscCall(KSP_MatMultTranspose(ksp, A, R0, Tn));
127:     PetscCall(KSP_PCApplyTranspose(ksp, Tn, F0));
128:   } else if (ksp->pc_side == PC_LEFT) { /* A' B' */
129:     PetscCall(KSP_PCApplyTranspose(ksp, R0, Tn));
130:     PetscCall(KSP_MatMultTranspose(ksp, A, Tn, F0));
131:   }

133:   /*qn_1 = vn_1 = zn_1 = 0.0; */
134:   PetscCall(VecSet(Qn_1, 0.0));
135:   PetscCall(VecSet(Vn_1, 0.0));
136:   PetscCall(VecSet(Zn_1, 0.0));

138:   sigman_2 = pin_1 = taun_1 = 0.0;

140:   /* the paper says phin_1 should be initialized to zero, it is actually R0'R0 */
141:   PetscCall(VecDot(R0, R0, &phin_1));
142:   KSPCheckDot(ksp, phin_1);

144:   /* sigman_1 = rn_1'un_1  */
145:   PetscCall(VecDot(R0, Un_1, &sigman_1));

147:   alphan_1 = omegan_1 = 1.0;

149:   for (ksp->its = 1; ksp->its < ksp->max_it + 1; ksp->its++) {
150:     rhon = phin_1 - omegan_1 * sigman_2 + omegan_1 * alphan_1 * pin_1;
151:     if (ksp->its == 1) deltan = rhon;
152:     else deltan = rhon / taun_1;
153:     betan = deltan / omegan_1;
154:     taun  = sigman_1 + betan * taun_1 - deltan * pin_1;
155:     if (taun == 0.0) {
156:       PetscCheck(!ksp->errorifnotconverged, PetscObjectComm((PetscObject)ksp), PETSC_ERR_NOT_CONVERGED, "KSPSolve has not converged due to taun is zero, iteration %" PetscInt_FMT, ksp->its);
157:       ksp->reason = KSP_DIVERGED_NANORINF;
158:       PetscFunctionReturn(PETSC_SUCCESS);
159:     }
160:     alphan = rhon / taun;
161:     PetscCall(PetscLogFlops(15.0));

163:     /*
164:         zn = alphan*rn_1 + (alphan/alphan_1)betan*zn_1 - alphan*deltan*vn_1
165:         vn = un_1 + betan*vn_1 - deltan*qn_1
166:         sn = rn_1 - alphan*vn

168:        The algorithm in the paper is missing the alphan/alphan_1 term in the zn update
169:     */
170:     PetscCall(PetscLogEventBegin(VEC_Ops, 0, 0, 0, 0));
171:     tmp1 = (alphan / alphan_1) * betan;
172:     tmp2 = alphan * deltan;
173:     for (PetscInt i = 0; i < N; i++) {
174:       zn[i] = alphan * rn_1[i] + tmp1 * zn_1[i] - tmp2 * vn_1[i];
175:       vn[i] = un_1[i] + betan * vn_1[i] - deltan * qn_1[i];
176:       sn[i] = rn_1[i] - alphan * vn[i];
177:     }
178:     PetscCall(PetscLogFlops(3.0 + 11.0 * N));
179:     PetscCall(PetscLogEventEnd(VEC_Ops, 0, 0, 0, 0));

181:     /*
182:         qn = A*vn
183:     */
184:     PetscCall(KSP_PCApplyBAorAB(ksp, Vn, Qn, Tn));

186:     /*
187:         tn = un_1 - alphan*qn
188:     */
189:     PetscCall(VecWAXPY(Tn, -alphan, Qn, Un_1));

191:     /*
192:         phin = r0'sn
193:         pin  = r0'qn
194:         gamman = f0'sn
195:         etan   = f0'tn
196:         thetan = sn'tn
197:         kappan = tn'tn
198:     */
199:     PetscCall(PetscLogEventBegin(VEC_ReduceArithmetic, 0, 0, 0, 0));
200:     phin = pin = gamman = etan = thetan = kappan = 0.0;
201:     for (PetscInt i = 0; i < N; i++) {
202:       phin += r0[i] * sn[i];
203:       pin += r0[i] * qn[i];
204:       gamman += f0[i] * sn[i];
205:       etan += f0[i] * tn[i];
206:       thetan += sn[i] * tn[i];
207:       kappan += tn[i] * tn[i];
208:     }
209:     PetscCall(PetscLogFlops(12.0 * N));
210:     PetscCall(PetscLogEventEnd(VEC_ReduceArithmetic, 0, 0, 0, 0));

212:     outsums[0] = phin;
213:     outsums[1] = pin;
214:     outsums[2] = gamman;
215:     outsums[3] = etan;
216:     outsums[4] = thetan;
217:     outsums[5] = kappan;
218:     outsums[6] = rnormin;

220:     PetscCall(PetscLogEventBegin(VEC_ReduceCommunication, 0, 0, 0, 0));
221: #if PetscDefined(HAVE_MPI_LONG_DOUBLE) && !PetscDefined(USE_COMPLEX) && (PetscDefined(USE_REAL_SINGLE) || PetscDefined(USE_REAL_DOUBLE))
222:     if (ksp->lagnorm && ksp->its > 1) {
223:       PetscCallMPI(MPIU_Allreduce(MPI_IN_PLACE, outsums, 7, MPI_LONG_DOUBLE, MPI_SUM, PetscObjectComm((PetscObject)ksp)));
224:     } else {
225:       PetscCallMPI(MPIU_Allreduce(MPI_IN_PLACE, outsums, 6, MPI_LONG_DOUBLE, MPI_SUM, PetscObjectComm((PetscObject)ksp)));
226:     }
227: #else
228:     if (ksp->lagnorm && ksp->its > 1 && ksp->normtype != KSP_NORM_NONE) {
229:       PetscCallMPI(MPIU_Allreduce(MPI_IN_PLACE, outsums, 7, MPIU_SCALAR, MPIU_SUM, PetscObjectComm((PetscObject)ksp)));
230:     } else {
231:       PetscCallMPI(MPIU_Allreduce(MPI_IN_PLACE, outsums, 6, MPIU_SCALAR, MPIU_SUM, PetscObjectComm((PetscObject)ksp)));
232:     }
233: #endif
234:     PetscCall(PetscLogEventEnd(VEC_ReduceCommunication, 0, 0, 0, 0));
235:     phin   = outsums[0];
236:     pin    = outsums[1];
237:     gamman = outsums[2];
238:     etan   = outsums[3];
239:     thetan = outsums[4];
240:     kappan = outsums[5];
241:     if (ksp->lagnorm && ksp->its > 1 && ksp->normtype != KSP_NORM_NONE) rnorm = PetscSqrtReal(PetscRealPart(outsums[6]));

243:     if (kappan == 0.0) {
244:       PetscCheck(!ksp->errorifnotconverged, PetscObjectComm((PetscObject)ksp), PETSC_ERR_NOT_CONVERGED, "KSPSolve has not converged due to kappan is zero, iteration %" PetscInt_FMT, ksp->its);
245:       ksp->reason = KSP_DIVERGED_NANORINF;
246:       PetscFunctionReturn(PETSC_SUCCESS);
247:     }
248:     if (thetan == 0.0) {
249:       PetscCheck(!ksp->errorifnotconverged, PetscObjectComm((PetscObject)ksp), PETSC_ERR_NOT_CONVERGED, "KSPSolve has not converged due to thetan is zero, iteration %" PetscInt_FMT, ksp->its);
250:       ksp->reason = KSP_DIVERGED_NANORINF;
251:       PetscFunctionReturn(PETSC_SUCCESS);
252:     }
253:     omegan = thetan / kappan;
254:     sigman = gamman - omegan * etan;

256:     /*
257:         rn = sn - omegan*tn
258:         xn = xn_1 + zn + omegan*sn
259:     */
260:     PetscCall(PetscLogEventBegin(VEC_Ops, 0, 0, 0, 0));
261:     rnormin = 0.0;
262:     for (PetscInt i = 0; i < N; i++) {
263:       rn[i] = sn[i] - omegan * tn[i];
264:       rnormin += PetscRealPart(PetscConj(rn[i]) * rn[i]);
265:       xn[i] += zn[i] + omegan * sn[i];
266:     }
267:     PetscCall(PetscObjectStateIncrease((PetscObject)Xn));
268:     PetscCall(PetscLogFlops(7.0 * N));
269:     PetscCall(PetscLogEventEnd(VEC_Ops, 0, 0, 0, 0));

271:     if (!ksp->lagnorm && ksp->chknorm < ksp->its && ksp->normtype != KSP_NORM_NONE) {
272:       PetscCall(PetscLogEventBegin(VEC_ReduceCommunication, 0, 0, 0, 0));
273:       PetscCallMPI(MPIU_Allreduce(MPI_IN_PLACE, &rnormin, 1, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)ksp)));
274:       PetscCall(PetscLogEventEnd(VEC_ReduceCommunication, 0, 0, 0, 0));
275:       rnorm = PetscSqrtReal(rnormin);
276:     }

278:     /* Test for convergence */
279:     PetscCall(KSPMonitor(ksp, ksp->its, rnorm));
280:     PetscCall((*ksp->converged)(ksp, ksp->its, rnorm, &ksp->reason, ksp->cnvP));
281:     if (ksp->reason) {
282:       PetscCall(KSPUnwindPreconditioner(ksp, Xn, Tn));
283:       PetscFunctionReturn(PETSC_SUCCESS);
284:     }

286:     /* un = A*rn */
287:     PetscCall(KSP_PCApplyBAorAB(ksp, Rn, Un, Tn));

289:     /* Update n-1 locations with n locations */
290:     sigman_2 = sigman_1;
291:     sigman_1 = sigman;
292:     pin_1    = pin;
293:     phin_1   = phin;
294:     alphan_1 = alphan;
295:     taun_1   = taun;
296:     omegan_1 = omegan;
297:   }
298:   if (ksp->its >= ksp->max_it) ksp->reason = KSP_DIVERGED_ITS;
299:   PetscCall(KSPUnwindPreconditioner(ksp, Xn, Tn));
300:   PetscFunctionReturn(PETSC_SUCCESS);
301: }

303: /*MC
304:    KSPIBCGS - Implements the IBiCGStab (Improved Stabilized version of BiConjugate Gradient) method {cite}`yang:brent:2002`
305:    in an alternative form to have only a single global reduction operation instead of the usual 3 (or 4)

307:    Level: beginner

309:    Notes:
310:    Supports left and right preconditioning

312:    See `KSPBCGSL` for additional stabilization

314:    Unlike the Bi-CG-stab algorithm, this requires one multiplication be the transpose of the operator
315:    before the iteration starts.

317:    The paper has two errors in the algorithm presented, they are fixed in the code in `KSPSolve_IBCGS()`

319:    For maximum reduction in the number of global reduction operations, this solver should be used with
320:    `KSPSetLagNorm()`.

322:    This is not supported for complex numbers.

324: .seealso: [](ch_ksp), `KSPCreate()`, `KSPSetType()`, `KSPType`, `KSP`, `KSPBICG`, `KSPBCGSL`, `KSPIBCGS`, `KSPSetLagNorm()`
325: M*/

327: PETSC_EXTERN PetscErrorCode KSPCreate_IBCGS(KSP ksp)
328: {
329:   PetscFunctionBegin;
330:   PetscCall(KSPSetSupportedNorm(ksp, KSP_NORM_PRECONDITIONED, PC_LEFT, 3));
331:   PetscCall(KSPSetSupportedNorm(ksp, KSP_NORM_UNPRECONDITIONED, PC_RIGHT, 2));
332:   PetscCall(KSPSetSupportedNorm(ksp, KSP_NORM_NONE, PC_RIGHT, 1));

334:   ksp->ops->setup          = KSPSetUp_IBCGS;
335:   ksp->ops->solve          = KSPSolve_IBCGS;
336:   ksp->ops->destroy        = KSPDestroyDefault;
337:   ksp->ops->buildsolution  = KSPBuildSolutionDefault;
338:   ksp->ops->buildresidual  = KSPBuildResidualDefault;
339:   ksp->ops->setfromoptions = NULL;
340:   ksp->ops->view           = NULL;
341:   PetscCheck(!PetscDefined(USE_COMPLEX), PetscObjectComm((PetscObject)ksp), PETSC_ERR_SUP, "This is not supported for complex numbers");
342:   PetscFunctionReturn(PETSC_SUCCESS);
343: }