Actual source code: narnoldi.c

slepc-3.22.2 2024-12-02
Report Typos and Errors
  1: /*
  2:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  3:    SLEPc - Scalable Library for Eigenvalue Problem Computations
  4:    Copyright (c) 2002-, Universitat Politecnica de Valencia, Spain

  6:    This file is part of SLEPc.
  7:    SLEPc is distributed under a 2-clause BSD license (see LICENSE).
  8:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  9: */
 10: /*
 11:    SLEPc nonlinear eigensolver: "narnoldi"

 13:    Method: Nonlinear Arnoldi

 15:    Algorithm:

 17:        Arnoldi for nonlinear eigenproblems.

 19:    References:

 21:        [1] H. Voss, "An Arnoldi method for nonlinear eigenvalue problems",
 22:            BIT 44:387-401, 2004.
 23: */

 25: #include <slepc/private/nepimpl.h>
 26: #include <../src/nep/impls/nepdefl.h>

 28: typedef struct {
 29:   PetscInt lag;             /* interval to rebuild preconditioner */
 30:   KSP      ksp;             /* linear solver object */
 31: } NEP_NARNOLDI;

 33: static PetscErrorCode NEPSetUp_NArnoldi(NEP nep)
 34: {
 35:   PetscFunctionBegin;
 36:   PetscCall(NEPSetDimensions_Default(nep,nep->nev,&nep->ncv,&nep->mpd));
 37:   PetscCheck(nep->ncv<=nep->nev+nep->mpd,PetscObjectComm((PetscObject)nep),PETSC_ERR_USER_INPUT,"The value of ncv must not be larger than nev+mpd");
 38:   if (nep->max_it==PETSC_DETERMINE) nep->max_it = nep->nev*nep->ncv;
 39:   if (!nep->which) nep->which = NEP_TARGET_MAGNITUDE;
 40:   PetscCheck(nep->which==NEP_TARGET_MAGNITUDE,PetscObjectComm((PetscObject)nep),PETSC_ERR_SUP,"This solver supports only target magnitude eigenvalues");
 41:   NEPCheckUnsupported(nep,NEP_FEATURE_CALLBACK | NEP_FEATURE_REGION | NEP_FEATURE_TWOSIDED);
 42:   PetscCall(NEPAllocateSolution(nep,0));
 43:   PetscCall(NEPSetWorkVecs(nep,3));
 44:   PetscFunctionReturn(PETSC_SUCCESS);
 45: }

 47: static PetscErrorCode NEPSolve_NArnoldi(NEP nep)
 48: {
 49:   NEP_NARNOLDI       *ctx = (NEP_NARNOLDI*)nep->data;
 50:   Mat                T,H,A;
 51:   Vec                f,r,u,uu;
 52:   PetscScalar        *X,lambda=0.0,lambda2=0.0,*eigr,sigma;
 53:   PetscReal          beta,resnorm=0.0,nrm,perr=0.0;
 54:   PetscInt           n;
 55:   PetscBool          breakdown,skip=PETSC_FALSE;
 56:   BV                 Vext;
 57:   DS                 ds;
 58:   NEP_EXT_OP         extop=NULL;
 59:   SlepcSC            sc;
 60:   KSPConvergedReason kspreason;

 62:   PetscFunctionBegin;
 63:   /* get initial space and shift */
 64:   PetscCall(NEPGetDefaultShift(nep,&sigma));
 65:   if (!nep->nini) {
 66:     PetscCall(BVSetRandomColumn(nep->V,0));
 67:     PetscCall(BVNormColumn(nep->V,0,NORM_2,&nrm));
 68:     PetscCall(BVScaleColumn(nep->V,0,1.0/nrm));
 69:     n = 1;
 70:   } else n = nep->nini;

 72:   if (!ctx->ksp) PetscCall(NEPNArnoldiGetKSP(nep,&ctx->ksp));
 73:   PetscCall(NEPDeflationInitialize(nep,nep->V,ctx->ksp,PETSC_FALSE,nep->nev,&extop));
 74:   PetscCall(NEPDeflationCreateBV(extop,nep->ncv,&Vext));

 76:   /* prepare linear solver */
 77:   PetscCall(NEPDeflationSolveSetUp(extop,sigma));

 79:   PetscCall(BVGetColumn(Vext,0,&f));
 80:   PetscCall(VecDuplicate(f,&r));
 81:   PetscCall(VecDuplicate(f,&u));
 82:   PetscCall(BVGetColumn(nep->V,0,&uu));
 83:   PetscCall(NEPDeflationCopyToExtendedVec(extop,uu,NULL,f,PETSC_FALSE));
 84:   PetscCall(BVRestoreColumn(nep->V,0,&uu));
 85:   PetscCall(VecCopy(f,r));
 86:   PetscCall(NEPDeflationFunctionSolve(extop,r,f));
 87:   PetscCall(VecNorm(f,NORM_2,&nrm));
 88:   PetscCall(VecScale(f,1.0/nrm));
 89:   PetscCall(BVRestoreColumn(Vext,0,&f));

 91:   PetscCall(DSCreate(PetscObjectComm((PetscObject)nep),&ds));
 92:   PetscCall(DSSetType(ds,DSNEP));
 93:   PetscCall(DSNEPSetFN(ds,nep->nt,nep->f));
 94:   PetscCall(DSAllocate(ds,nep->ncv));
 95:   PetscCall(DSGetSlepcSC(ds,&sc));
 96:   sc->comparison    = nep->sc->comparison;
 97:   sc->comparisonctx = nep->sc->comparisonctx;
 98:   PetscCall(DSSetFromOptions(ds));

100:   /* build projected matrices for initial space */
101:   PetscCall(DSSetDimensions(ds,n,0,0));
102:   PetscCall(NEPDeflationProjectOperator(extop,Vext,ds,0,n));

104:   PetscCall(PetscMalloc1(nep->ncv,&eigr));

106:   /* Restart loop */
107:   while (nep->reason == NEP_CONVERGED_ITERATING) {
108:     nep->its++;

110:     /* solve projected problem */
111:     PetscCall(DSSetDimensions(ds,n,0,0));
112:     PetscCall(DSSetState(ds,DS_STATE_RAW));
113:     PetscCall(DSSolve(ds,eigr,NULL));
114:     PetscCall(DSSynchronize(ds,eigr,NULL));
115:     if (nep->its>1) lambda2 = lambda;
116:     lambda = eigr[0];
117:     nep->eigr[nep->nconv] = lambda;

119:     /* compute Ritz vector, x = V*s */
120:     PetscCall(DSGetArray(ds,DS_MAT_X,&X));
121:     PetscCall(BVSetActiveColumns(Vext,0,n));
122:     PetscCall(BVMultVec(Vext,1.0,0.0,u,X));
123:     PetscCall(DSRestoreArray(ds,DS_MAT_X,&X));

125:     /* compute the residual, r = T(lambda)*x */
126:     PetscCall(NEPDeflationComputeFunction(extop,lambda,&T));
127:     PetscCall(MatMult(T,u,r));

129:     /* convergence test */
130:     PetscCall(VecNorm(r,NORM_2,&resnorm));
131:     if (nep->its>1) perr=nep->errest[nep->nconv];
132:     PetscCall((*nep->converged)(nep,lambda,0,resnorm,&nep->errest[nep->nconv],nep->convergedctx));
133:     if (nep->errest[nep->nconv]<=nep->tol) {
134:       nep->nconv = nep->nconv + 1;
135:       PetscCall(NEPDeflationLocking(extop,u,lambda));
136:       skip = PETSC_TRUE;
137:     }
138:     PetscCall((*nep->stopping)(nep,nep->its,nep->max_it,nep->nconv,nep->nev,&nep->reason,nep->stoppingctx));
139:     if (!skip || nep->reason>0) PetscCall(NEPMonitor(nep,nep->its,nep->nconv,nep->eigr,nep->eigi,nep->errest,(nep->reason>0)?nep->nconv:nep->nconv+1));

141:     if (nep->reason == NEP_CONVERGED_ITERATING) {
142:       if (!skip) {
143:         if (n>=nep->ncv) {
144:           nep->reason = NEP_DIVERGED_SUBSPACE_EXHAUSTED;
145:           break;
146:         }
147:         if (ctx->lag && !(nep->its%ctx->lag) && nep->its>=2*ctx->lag && perr && nep->errest[nep->nconv]>.5*perr) PetscCall(NEPDeflationSolveSetUp(extop,lambda2));

149:         /* continuation vector: f = T(sigma)\r */
150:         PetscCall(BVGetColumn(Vext,n,&f));
151:         PetscCall(NEPDeflationFunctionSolve(extop,r,f));
152:         PetscCall(BVRestoreColumn(Vext,n,&f));
153:         PetscCall(KSPGetConvergedReason(ctx->ksp,&kspreason));
154:         if (kspreason<0) {
155:           PetscCall(PetscInfo(nep,"iter=%" PetscInt_FMT ", linear solve failed, stopping solve\n",nep->its));
156:           nep->reason = NEP_DIVERGED_LINEAR_SOLVE;
157:           break;
158:         }

160:         /* orthonormalize */
161:         PetscCall(BVOrthonormalizeColumn(Vext,n,PETSC_FALSE,&beta,&breakdown));
162:         if (breakdown || beta==0.0) {
163:           PetscCall(PetscInfo(nep,"iter=%" PetscInt_FMT ", orthogonalization failed, stopping solve\n",nep->its));
164:           nep->reason = NEP_DIVERGED_BREAKDOWN;
165:           break;
166:         }

168:         /* update projected matrices */
169:         PetscCall(DSSetDimensions(ds,n+1,0,0));
170:         PetscCall(NEPDeflationProjectOperator(extop,Vext,ds,n,n+1));
171:         n++;
172:       } else {
173:         nep->its--;  /* do not count this as a full iteration */
174:         PetscCall(BVGetColumn(Vext,0,&f));
175:         PetscCall(NEPDeflationSetRandomVec(extop,f));
176:         PetscCall(NEPDeflationSolveSetUp(extop,sigma));
177:         PetscCall(VecCopy(f,r));
178:         PetscCall(NEPDeflationFunctionSolve(extop,r,f));
179:         PetscCall(VecNorm(f,NORM_2,&nrm));
180:         PetscCall(VecScale(f,1.0/nrm));
181:         PetscCall(BVRestoreColumn(Vext,0,&f));
182:         n = 1;
183:         PetscCall(DSSetDimensions(ds,n,0,0));
184:         PetscCall(NEPDeflationProjectOperator(extop,Vext,ds,n-1,n));
185:         skip = PETSC_FALSE;
186:       }
187:     }
188:   }

190:   PetscCall(NEPDeflationGetInvariantPair(extop,NULL,&H));
191:   PetscCall(DSSetType(nep->ds,DSNHEP));
192:   PetscCall(DSAllocate(nep->ds,PetscMax(nep->nconv,1)));
193:   PetscCall(DSSetDimensions(nep->ds,nep->nconv,0,nep->nconv));
194:   PetscCall(DSGetMat(nep->ds,DS_MAT_A,&A));
195:   PetscCall(MatCopy(H,A,SAME_NONZERO_PATTERN));
196:   PetscCall(DSRestoreMat(nep->ds,DS_MAT_A,&A));
197:   PetscCall(MatDestroy(&H));
198:   PetscCall(DSSolve(nep->ds,nep->eigr,nep->eigi));
199:   PetscCall(NEPDeflationReset(extop));
200:   PetscCall(VecDestroy(&u));
201:   PetscCall(VecDestroy(&r));
202:   PetscCall(BVDestroy(&Vext));
203:   PetscCall(DSDestroy(&ds));
204:   PetscCall(PetscFree(eigr));
205:   PetscFunctionReturn(PETSC_SUCCESS);
206: }

208: static PetscErrorCode NEPNArnoldiSetLagPreconditioner_NArnoldi(NEP nep,PetscInt lag)
209: {
210:   NEP_NARNOLDI *ctx = (NEP_NARNOLDI*)nep->data;

212:   PetscFunctionBegin;
213:   PetscCheck(lag>=0,PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Lag must be non-negative");
214:   ctx->lag = lag;
215:   PetscFunctionReturn(PETSC_SUCCESS);
216: }

218: /*@
219:    NEPNArnoldiSetLagPreconditioner - Determines when the preconditioner is rebuilt in the
220:    nonlinear solve.

222:    Logically Collective

224:    Input Parameters:
225: +  nep - nonlinear eigenvalue solver
226: -  lag - 0 indicates NEVER rebuild, 1 means rebuild every time the Jacobian is
227:           computed within the nonlinear iteration, 2 means every second time
228:           the Jacobian is built, etc.

230:    Options Database Keys:
231: .  -nep_narnoldi_lag_preconditioner <lag> - the lag value

233:    Notes:
234:    The default is 1.
235:    The preconditioner is ALWAYS built in the first iteration of a nonlinear solve.

237:    Level: intermediate

239: .seealso: NEPNArnoldiGetLagPreconditioner()
240: @*/
241: PetscErrorCode NEPNArnoldiSetLagPreconditioner(NEP nep,PetscInt lag)
242: {
243:   PetscFunctionBegin;
246:   PetscTryMethod(nep,"NEPNArnoldiSetLagPreconditioner_C",(NEP,PetscInt),(nep,lag));
247:   PetscFunctionReturn(PETSC_SUCCESS);
248: }

250: static PetscErrorCode NEPNArnoldiGetLagPreconditioner_NArnoldi(NEP nep,PetscInt *lag)
251: {
252:   NEP_NARNOLDI *ctx = (NEP_NARNOLDI*)nep->data;

254:   PetscFunctionBegin;
255:   *lag = ctx->lag;
256:   PetscFunctionReturn(PETSC_SUCCESS);
257: }

259: /*@
260:    NEPNArnoldiGetLagPreconditioner - Indicates how often the preconditioner is rebuilt.

262:    Not Collective

264:    Input Parameter:
265: .  nep - nonlinear eigenvalue solver

267:    Output Parameter:
268: .  lag - the lag parameter

270:    Level: intermediate

272: .seealso: NEPNArnoldiSetLagPreconditioner()
273: @*/
274: PetscErrorCode NEPNArnoldiGetLagPreconditioner(NEP nep,PetscInt *lag)
275: {
276:   PetscFunctionBegin;
278:   PetscAssertPointer(lag,2);
279:   PetscUseMethod(nep,"NEPNArnoldiGetLagPreconditioner_C",(NEP,PetscInt*),(nep,lag));
280:   PetscFunctionReturn(PETSC_SUCCESS);
281: }

283: static PetscErrorCode NEPSetFromOptions_NArnoldi(NEP nep,PetscOptionItems *PetscOptionsObject)
284: {
285:   PetscInt       i;
286:   PetscBool      flg;
287:   NEP_NARNOLDI   *ctx = (NEP_NARNOLDI*)nep->data;

289:   PetscFunctionBegin;
290:   PetscOptionsHeadBegin(PetscOptionsObject,"NEP N-Arnoldi Options");
291:     i = 0;
292:     PetscCall(PetscOptionsInt("-nep_narnoldi_lag_preconditioner","Interval to rebuild preconditioner","NEPNArnoldiSetLagPreconditioner",ctx->lag,&i,&flg));
293:     if (flg) PetscCall(NEPNArnoldiSetLagPreconditioner(nep,i));

295:   PetscOptionsHeadEnd();

297:   if (!ctx->ksp) PetscCall(NEPNArnoldiGetKSP(nep,&ctx->ksp));
298:   PetscCall(KSPSetFromOptions(ctx->ksp));
299:   PetscFunctionReturn(PETSC_SUCCESS);
300: }

302: static PetscErrorCode NEPNArnoldiSetKSP_NArnoldi(NEP nep,KSP ksp)
303: {
304:   NEP_NARNOLDI   *ctx = (NEP_NARNOLDI*)nep->data;

306:   PetscFunctionBegin;
307:   PetscCall(PetscObjectReference((PetscObject)ksp));
308:   PetscCall(KSPDestroy(&ctx->ksp));
309:   ctx->ksp = ksp;
310:   nep->state = NEP_STATE_INITIAL;
311:   PetscFunctionReturn(PETSC_SUCCESS);
312: }

314: /*@
315:    NEPNArnoldiSetKSP - Associate a linear solver object (KSP) to the nonlinear
316:    eigenvalue solver.

318:    Collective

320:    Input Parameters:
321: +  nep - eigenvalue solver
322: -  ksp - the linear solver object

324:    Level: advanced

326: .seealso: NEPNArnoldiGetKSP()
327: @*/
328: PetscErrorCode NEPNArnoldiSetKSP(NEP nep,KSP ksp)
329: {
330:   PetscFunctionBegin;
333:   PetscCheckSameComm(nep,1,ksp,2);
334:   PetscTryMethod(nep,"NEPNArnoldiSetKSP_C",(NEP,KSP),(nep,ksp));
335:   PetscFunctionReturn(PETSC_SUCCESS);
336: }

338: static PetscErrorCode NEPNArnoldiGetKSP_NArnoldi(NEP nep,KSP *ksp)
339: {
340:   NEP_NARNOLDI   *ctx = (NEP_NARNOLDI*)nep->data;

342:   PetscFunctionBegin;
343:   if (!ctx->ksp) {
344:     PetscCall(KSPCreate(PetscObjectComm((PetscObject)nep),&ctx->ksp));
345:     PetscCall(PetscObjectIncrementTabLevel((PetscObject)ctx->ksp,(PetscObject)nep,1));
346:     PetscCall(KSPSetOptionsPrefix(ctx->ksp,((PetscObject)nep)->prefix));
347:     PetscCall(KSPAppendOptionsPrefix(ctx->ksp,"nep_narnoldi_"));
348:     PetscCall(PetscObjectSetOptions((PetscObject)ctx->ksp,((PetscObject)nep)->options));
349:     PetscCall(KSPSetErrorIfNotConverged(ctx->ksp,PETSC_TRUE));
350:     PetscCall(KSPSetTolerances(ctx->ksp,SlepcDefaultTol(nep->tol),PETSC_CURRENT,PETSC_CURRENT,PETSC_CURRENT));
351:   }
352:   *ksp = ctx->ksp;
353:   PetscFunctionReturn(PETSC_SUCCESS);
354: }

356: /*@
357:    NEPNArnoldiGetKSP - Retrieve the linear solver object (KSP) associated with
358:    the nonlinear eigenvalue solver.

360:    Collective

362:    Input Parameter:
363: .  nep - nonlinear eigenvalue solver

365:    Output Parameter:
366: .  ksp - the linear solver object

368:    Level: advanced

370: .seealso: NEPNArnoldiSetKSP()
371: @*/
372: PetscErrorCode NEPNArnoldiGetKSP(NEP nep,KSP *ksp)
373: {
374:   PetscFunctionBegin;
376:   PetscAssertPointer(ksp,2);
377:   PetscUseMethod(nep,"NEPNArnoldiGetKSP_C",(NEP,KSP*),(nep,ksp));
378:   PetscFunctionReturn(PETSC_SUCCESS);
379: }

381: static PetscErrorCode NEPView_NArnoldi(NEP nep,PetscViewer viewer)
382: {
383:   NEP_NARNOLDI   *ctx = (NEP_NARNOLDI*)nep->data;
384:   PetscBool      isascii;

386:   PetscFunctionBegin;
387:   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&isascii));
388:   if (isascii) {
389:     if (ctx->lag) PetscCall(PetscViewerASCIIPrintf(viewer,"  updating the preconditioner every %" PetscInt_FMT " iterations\n",ctx->lag));
390:     if (!ctx->ksp) PetscCall(NEPNArnoldiGetKSP(nep,&ctx->ksp));
391:     PetscCall(PetscViewerASCIIPushTab(viewer));
392:     PetscCall(KSPView(ctx->ksp,viewer));
393:     PetscCall(PetscViewerASCIIPopTab(viewer));
394:   }
395:   PetscFunctionReturn(PETSC_SUCCESS);
396: }

398: static PetscErrorCode NEPReset_NArnoldi(NEP nep)
399: {
400:   NEP_NARNOLDI   *ctx = (NEP_NARNOLDI*)nep->data;

402:   PetscFunctionBegin;
403:   PetscCall(KSPReset(ctx->ksp));
404:   PetscFunctionReturn(PETSC_SUCCESS);
405: }

407: static PetscErrorCode NEPDestroy_NArnoldi(NEP nep)
408: {
409:   NEP_NARNOLDI   *ctx = (NEP_NARNOLDI*)nep->data;

411:   PetscFunctionBegin;
412:   PetscCall(KSPDestroy(&ctx->ksp));
413:   PetscCall(PetscFree(nep->data));
414:   PetscCall(PetscObjectComposeFunction((PetscObject)nep,"NEPNArnoldiSetLagPreconditioner_C",NULL));
415:   PetscCall(PetscObjectComposeFunction((PetscObject)nep,"NEPNArnoldiGetLagPreconditioner_C",NULL));
416:   PetscCall(PetscObjectComposeFunction((PetscObject)nep,"NEPNArnoldiSetKSP_C",NULL));
417:   PetscCall(PetscObjectComposeFunction((PetscObject)nep,"NEPNArnoldiGetKSP_C",NULL));
418:   PetscFunctionReturn(PETSC_SUCCESS);
419: }

421: SLEPC_EXTERN PetscErrorCode NEPCreate_NArnoldi(NEP nep)
422: {
423:   NEP_NARNOLDI   *ctx;

425:   PetscFunctionBegin;
426:   PetscCall(PetscNew(&ctx));
427:   nep->data = (void*)ctx;
428:   ctx->lag  = 1;

430:   nep->useds = PETSC_TRUE;

432:   nep->ops->solve          = NEPSolve_NArnoldi;
433:   nep->ops->setup          = NEPSetUp_NArnoldi;
434:   nep->ops->setfromoptions = NEPSetFromOptions_NArnoldi;
435:   nep->ops->reset          = NEPReset_NArnoldi;
436:   nep->ops->destroy        = NEPDestroy_NArnoldi;
437:   nep->ops->view           = NEPView_NArnoldi;
438:   nep->ops->computevectors = NEPComputeVectors_Schur;

440:   PetscCall(PetscObjectComposeFunction((PetscObject)nep,"NEPNArnoldiSetLagPreconditioner_C",NEPNArnoldiSetLagPreconditioner_NArnoldi));
441:   PetscCall(PetscObjectComposeFunction((PetscObject)nep,"NEPNArnoldiGetLagPreconditioner_C",NEPNArnoldiGetLagPreconditioner_NArnoldi));
442:   PetscCall(PetscObjectComposeFunction((PetscObject)nep,"NEPNArnoldiSetKSP_C",NEPNArnoldiSetKSP_NArnoldi));
443:   PetscCall(PetscObjectComposeFunction((PetscObject)nep,"NEPNArnoldiGetKSP_C",NEPNArnoldiGetKSP_NArnoldi));
444:   PetscFunctionReturn(PETSC_SUCCESS);
445: }