Actual source code: minres.c
petsc-3.14.2 2020-12-03
2: #include <petsc/private/kspimpl.h>
4: typedef struct {
5: PetscReal haptol;
6: } KSP_MINRES;
8: static PetscErrorCode KSPSetUp_MINRES(KSP ksp)
9: {
13: if (ksp->pc_side == PC_RIGHT) SETERRQ(PetscObjectComm((PetscObject)ksp),PETSC_ERR_SUP,"No right preconditioning for KSPMINRES");
14: else if (ksp->pc_side == PC_SYMMETRIC) SETERRQ(PetscObjectComm((PetscObject)ksp),PETSC_ERR_SUP,"No symmetric preconditioning for KSPMINRES");
15: KSPSetWorkVecs(ksp,9);
16: return(0);
17: }
20: static PetscErrorCode KSPSolve_MINRES(KSP ksp)
21: {
22: PetscErrorCode ierr;
23: PetscInt i;
24: PetscScalar alpha,beta,ibeta,betaold,eta,c=1.0,ceta,cold=1.0,coold,s=0.0,sold=0.0,soold;
25: PetscScalar rho0,rho1,irho1,rho2,rho3,dp = 0.0;
26: const PetscScalar none = -1.0;
27: PetscReal np;
28: Vec X,B,R,Z,U,V,W,UOLD,VOLD,WOLD,WOOLD;
29: Mat Amat,Pmat;
30: KSP_MINRES *minres = (KSP_MINRES*)ksp->data;
31: PetscBool diagonalscale;
34: PCGetDiagonalScale(ksp->pc,&diagonalscale);
35: if (diagonalscale) SETERRQ1(PetscObjectComm((PetscObject)ksp),PETSC_ERR_SUP,"Krylov method %s does not support diagonal scaling",((PetscObject)ksp)->type_name);
37: X = ksp->vec_sol;
38: B = ksp->vec_rhs;
39: R = ksp->work[0];
40: Z = ksp->work[1];
41: U = ksp->work[2];
42: V = ksp->work[3];
43: W = ksp->work[4];
44: UOLD = ksp->work[5];
45: VOLD = ksp->work[6];
46: WOLD = ksp->work[7];
47: WOOLD = ksp->work[8];
49: PCGetOperators(ksp->pc,&Amat,&Pmat);
51: ksp->its = 0;
53: VecSet(UOLD,0.0); /* u_old <- 0 */
54: VecSet(VOLD,0.0); /* v_old <- 0 */
55: VecSet(W,0.0); /* w <- 0 */
56: VecSet(WOLD,0.0); /* w_old <- 0 */
58: if (!ksp->guess_zero) {
59: KSP_MatMult(ksp,Amat,X,R); /* r <- b - A*x */
60: VecAYPX(R,-1.0,B);
61: } else {
62: VecCopy(B,R); /* r <- b (x is 0) */
63: }
64: KSP_PCApply(ksp,R,Z); /* z <- B*r */
65: VecNorm(Z,NORM_2,&np); /* np <- ||z|| */
66: KSPCheckNorm(ksp,np);
67: VecDot(R,Z,&dp);
68: KSPCheckDot(ksp,dp);
70: if (PetscRealPart(dp) < minres->haptol && np > minres->haptol) {
71: if (ksp->errorifnotconverged) SETERRQ2(PetscObjectComm((PetscObject)ksp),PETSC_ERR_CONV_FAILED,"Detected indefinite operator %g tolerance %g",(double)PetscRealPart(dp),(double)minres->haptol);
72: PetscInfo2(ksp,"Detected indefinite operator %g tolerance %g\n",(double)PetscRealPart(dp),(double)minres->haptol);
73: ksp->reason = KSP_DIVERGED_INDEFINITE_MAT;
74: return(0);
75: }
77: ksp->rnorm = 0.0;
78: if (ksp->normtype != KSP_NORM_NONE) ksp->rnorm = np;
79: KSPLogResidualHistory(ksp,ksp->rnorm);
80: KSPMonitor(ksp,0,ksp->rnorm);
81: (*ksp->converged)(ksp,0,ksp->rnorm,&ksp->reason,ksp->cnvP); /* test for convergence */
82: if (ksp->reason) return(0);
84: dp = PetscAbsScalar(dp);
85: dp = PetscSqrtScalar(dp);
86: beta = dp; /* beta <- sqrt(r'*z */
87: eta = beta;
89: VecCopy(R,V);
90: VecCopy(Z,U);
91: ibeta = 1.0 / beta;
92: VecScale(V,ibeta); /* v <- r / beta */
93: VecScale(U,ibeta); /* u <- z / beta */
95: i = 0;
96: do {
97: ksp->its = i+1;
99: /* Lanczos */
101: KSP_MatMult(ksp,Amat,U,R); /* r <- A*u */
102: VecDot(U,R,&alpha); /* alpha <- r'*u */
103: KSP_PCApply(ksp,R,Z); /* z <- B*r */
105: VecAXPY(R,-alpha,V); /* r <- r - alpha v */
106: VecAXPY(Z,-alpha,U); /* z <- z - alpha u */
107: VecAXPY(R,-beta,VOLD); /* r <- r - beta v_old */
108: VecAXPY(Z,-beta,UOLD); /* z <- z - beta u_old */
110: betaold = beta;
112: VecDot(R,Z,&dp);
113: KSPCheckDot(ksp,dp);
114: dp = PetscAbsScalar(dp);
115: beta = PetscSqrtScalar(dp); /* beta <- sqrt(r'*z) */
117: /* QR factorisation */
119: coold = cold; cold = c; soold = sold; sold = s;
121: rho0 = cold * alpha - coold * sold * betaold;
122: rho1 = PetscSqrtScalar(rho0*rho0 + beta*beta);
123: rho2 = sold * alpha + coold * cold * betaold;
124: rho3 = soold * betaold;
126: /* Givens rotation */
128: c = rho0 / rho1;
129: s = beta / rho1;
131: /* Update */
133: VecCopy(WOLD,WOOLD); /* w_oold <- w_old */
134: VecCopy(W,WOLD); /* w_old <- w */
136: VecCopy(U,W); /* w <- u */
137: VecAXPY(W,-rho2,WOLD); /* w <- w - rho2 w_old */
138: VecAXPY(W,-rho3,WOOLD); /* w <- w - rho3 w_oold */
139: irho1 = 1.0 / rho1;
140: VecScale(W,irho1); /* w <- w / rho1 */
142: ceta = c * eta;
143: VecAXPY(X,ceta,W); /* x <- x + c eta w */
145: /*
146: when dp is really small we have either convergence or an indefinite operator so compute true
147: residual norm to check for convergence
148: */
149: if (PetscRealPart(dp) < minres->haptol) {
150: PetscInfo2(ksp,"Possible indefinite operator %g tolerance %g\n",(double)PetscRealPart(dp),(double)minres->haptol);
151: KSP_MatMult(ksp,Amat,X,VOLD);
152: VecAXPY(VOLD,none,B);
153: VecNorm(VOLD,NORM_2,&np);
154: KSPCheckNorm(ksp,np);
155: } else {
156: /* otherwise compute new residual norm via recurrence relation */
157: np *= PetscAbsScalar(s);
158: }
160: if (ksp->normtype != KSP_NORM_NONE) ksp->rnorm = np;
161: KSPLogResidualHistory(ksp,ksp->rnorm);
162: KSPMonitor(ksp,i+1,ksp->rnorm);
163: (*ksp->converged)(ksp,i+1,ksp->rnorm,&ksp->reason,ksp->cnvP); /* test for convergence */
164: if (ksp->reason) break;
166: if (PetscRealPart(dp) < minres->haptol) {
167: if (ksp->errorifnotconverged) SETERRQ2(PetscObjectComm((PetscObject)ksp),PETSC_ERR_CONV_FAILED,"Detected indefinite operator %g tolerance %g",(double)PetscRealPart(dp),(double)minres->haptol);
168: PetscInfo2(ksp,"Detected indefinite operator %g tolerance %g\n",(double)PetscRealPart(dp),(double)minres->haptol);
169: ksp->reason = KSP_DIVERGED_INDEFINITE_MAT;
170: break;
171: }
173: eta = -s * eta;
174: VecCopy(V,VOLD);
175: VecCopy(U,UOLD);
176: VecCopy(R,V);
177: VecCopy(Z,U);
178: ibeta = 1.0 / beta;
179: VecScale(V,ibeta); /* v <- r / beta */
180: VecScale(U,ibeta); /* u <- z / beta */
182: i++;
183: } while (i<ksp->max_it);
184: if (i >= ksp->max_it) ksp->reason = KSP_DIVERGED_ITS;
185: return(0);
186: }
188: /*MC
189: KSPMINRES - This code implements the MINRES (Minimum Residual) method.
191: Options Database Keys:
192: . see KSPSolve()
194: Level: beginner
196: Notes:
197: The operator and the preconditioner must be symmetric and the preconditioner must
198: be positive definite for this method.
199: Supports only left preconditioning.
201: Reference: Paige & Saunders, 1975.
203: Contributed by: Robert Scheichl: maprs@maths.bath.ac.uk
205: .seealso: KSPCreate(), KSPSetType(), KSPType (for list of available types), KSP, KSPCG, KSPCR
206: M*/
207: PETSC_EXTERN PetscErrorCode KSPCreate_MINRES(KSP ksp)
208: {
209: KSP_MINRES *minres;
213: KSPSetSupportedNorm(ksp,KSP_NORM_PRECONDITIONED,PC_LEFT,3);
214: KSPSetSupportedNorm(ksp,KSP_NORM_NONE,PC_LEFT,1);
215: PetscNewLog(ksp,&minres);
217: /* this parameter is arbitrary; but e-50 didn't work for __float128 in one example */
218: #if defined(PETSC_USE_REAL___FLOAT128)
219: minres->haptol = 1.e-100;
220: #elif defined(PETSC_USE_REAL_SINGLE)
221: minres->haptol = 1.e-25;
222: #else
223: minres->haptol = 1.e-50;
224: #endif
225: ksp->data = (void*)minres;
227: /*
228: Sets the functions that are associated with this data structure
229: (in C++ this is the same as defining virtual functions)
230: */
231: ksp->ops->setup = KSPSetUp_MINRES;
232: ksp->ops->solve = KSPSolve_MINRES;
233: ksp->ops->destroy = KSPDestroyDefault;
234: ksp->ops->setfromoptions = NULL;
235: ksp->ops->buildsolution = KSPBuildSolutionDefault;
236: ksp->ops->buildresidual = KSPBuildResidualDefault;
237: return(0);
238: }