Actual source code: dsnep.c

  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: */

 11: #include <slepc/private/dsimpl.h>
 12: #include <slepcblaslapack.h>

 14: typedef struct {
 15:   PetscInt              nf;                     /* number of functions in f[] */
 16:   FN                    f[DS_NUM_EXTRA];        /* functions defining the nonlinear operator */
 17:   PetscInt              max_mid;                /* maximum minimality index */
 18:   PetscInt              nnod;                   /* number of nodes for quadrature rules */
 19:   PetscInt              spls;                   /* number of sampling columns for quadrature rules */
 20:   PetscInt              Nit;                    /* number of refinement iterations */
 21:   PetscReal             rtol;                   /* tolerance of Newton refinement */
 22:   RG                    rg;                     /* region for contour integral */
 23:   PetscLayout           map;                    /* used to distribute work among MPI processes */
 24:   DSNEPMatrixFunctionFn *computematrix;         /* user-provided compute matrix function */
 25:   void                  *computematrixctx;      /* context for the compute matrix function */
 26:   PetscCtxDestroyFn     *computematrixdestroy;  /* context destroy function */
 27: } DS_NEP;

 29: /*
 30:    DSNEPComputeMatrix - Build the matrix associated with a nonlinear operator
 31:    T(lambda) or its derivative T'(lambda), given the parameter lambda, where
 32:    T(lambda) = sum_i E_i*f_i(lambda). The result is written in mat.
 33: */
 34: static PetscErrorCode DSNEPComputeMatrix(DS ds,PetscScalar lambda,PetscBool deriv,DSMatType mat)
 35: {
 36:   DS_NEP            *ctx = (DS_NEP*)ds->data;
 37:   PetscScalar       *T,alpha;
 38:   const PetscScalar *E;
 39:   PetscInt          i,ld,n;
 40:   PetscBLASInt      k,inc=1;

 42:   PetscFunctionBegin;
 43:   PetscCall(PetscLogEventBegin(DS_Other,ds,0,0,0));
 44:   if (ctx->computematrix) PetscCall((*ctx->computematrix)(ds,lambda,deriv,mat,ctx->computematrixctx));
 45:   else {
 46:     PetscCall(DSGetDimensions(ds,&n,NULL,NULL,NULL));
 47:     PetscCall(DSGetLeadingDimension(ds,&ld));
 48:     PetscCall(PetscBLASIntCast(ld*n,&k));
 49:     PetscCall(MatDenseGetArray(ds->omat[mat],&T));
 50:     PetscCall(PetscArrayzero(T,k));
 51:     for (i=0;i<ctx->nf;i++) {
 52:       if (deriv) PetscCall(FNEvaluateDerivative(ctx->f[i],lambda,&alpha));
 53:       else PetscCall(FNEvaluateFunction(ctx->f[i],lambda,&alpha));
 54:       PetscCall(MatDenseGetArrayRead(ds->omat[DSMatExtra[i]],&E));
 55:       PetscCallBLAS("BLASaxpy",BLASaxpy_(&k,&alpha,E,&inc,T,&inc));
 56:       PetscCall(MatDenseRestoreArrayRead(ds->omat[DSMatExtra[i]],&E));
 57:     }
 58:     PetscCall(MatDenseRestoreArray(ds->omat[mat],&T));
 59:   }
 60:   PetscCall(PetscLogEventEnd(DS_Other,ds,0,0,0));
 61:   PetscFunctionReturn(PETSC_SUCCESS);
 62: }

 64: static PetscErrorCode DSAllocate_NEP(DS ds,PetscInt ld)
 65: {
 66:   DS_NEP         *ctx = (DS_NEP*)ds->data;
 67:   PetscInt       i;

 69:   PetscFunctionBegin;
 70:   PetscCall(DSAllocateMat_Private(ds,DS_MAT_X));
 71:   for (i=0;i<ctx->nf;i++) PetscCall(DSAllocateMat_Private(ds,DSMatExtra[i]));
 72:   PetscCall(PetscFree(ds->perm));
 73:   PetscCall(PetscMalloc1(ld*ctx->max_mid,&ds->perm));
 74:   PetscFunctionReturn(PETSC_SUCCESS);
 75: }

 77: static PetscErrorCode DSView_NEP(DS ds,PetscViewer viewer)
 78: {
 79:   DS_NEP            *ctx = (DS_NEP*)ds->data;
 80:   PetscViewerFormat format;
 81:   PetscInt          i;
 82:   const char        *methodname[] = {
 83:                      "Successive Linear Problems",
 84:                      "Contour Integral"
 85:   };
 86:   const int         nmeth=PETSC_STATIC_ARRAY_LENGTH(methodname);

 88:   PetscFunctionBegin;
 89:   PetscCall(PetscViewerGetFormat(viewer,&format));
 90:   if (format == PETSC_VIEWER_ASCII_INFO || format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
 91:     if (ds->method<nmeth) PetscCall(PetscViewerASCIIPrintf(viewer,"solving the problem with: %s\n",methodname[ds->method]));
 92: #if PetscDefined(USE_COMPLEX)
 93:     if (ds->method==1) {  /* contour integral method */
 94:       PetscCall(PetscViewerASCIIPrintf(viewer,"number of integration points: %" PetscInt_FMT "\n",ctx->nnod));
 95:       PetscCall(PetscViewerASCIIPrintf(viewer,"maximum minimality index: %" PetscInt_FMT "\n",ctx->max_mid));
 96:       if (ctx->spls) PetscCall(PetscViewerASCIIPrintf(viewer,"number of sampling columns for quadrature: %" PetscInt_FMT "\n",ctx->spls));
 97:       if (ctx->Nit) PetscCall(PetscViewerASCIIPrintf(viewer,"doing iterative refinement (%" PetscInt_FMT " its, tolerance %g)\n",ctx->Nit,(double)ctx->rtol));
 98:       PetscCall(RGView(ctx->rg,viewer));
 99:     }
100: #endif
101:     if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) PetscCall(PetscViewerASCIIPrintf(viewer,"number of functions: %" PetscInt_FMT "\n",ctx->nf));
102:     PetscFunctionReturn(PETSC_SUCCESS);
103:   }
104:   for (i=0;i<ctx->nf;i++) {
105:     PetscCall(FNView(ctx->f[i],viewer));
106:     PetscCall(DSViewMat(ds,viewer,DSMatExtra[i]));
107:   }
108:   if (ds->state>DS_STATE_INTERMEDIATE) PetscCall(DSViewMat(ds,viewer,DS_MAT_X));
109:   PetscFunctionReturn(PETSC_SUCCESS);
110: }

112: static PetscErrorCode DSVectors_NEP(DS ds,DSMatType mat,PetscInt *j,PetscReal *rnorm)
113: {
114:   PetscFunctionBegin;
115:   PetscCheck(!rnorm,PetscObjectComm((PetscObject)ds),PETSC_ERR_SUP,"Not implemented yet");
116:   switch (mat) {
117:     case DS_MAT_X:
118:       break;
119:     case DS_MAT_Y:
120:       SETERRQ(PetscObjectComm((PetscObject)ds),PETSC_ERR_SUP,"Not implemented yet");
121:     default:
122:       SETERRQ(PetscObjectComm((PetscObject)ds),PETSC_ERR_ARG_OUTOFRANGE,"Invalid mat parameter");
123:   }
124:   PetscFunctionReturn(PETSC_SUCCESS);
125: }

127: static PetscErrorCode DSSort_NEP(DS ds,PetscScalar *wr,PetscScalar *wi,PetscScalar *rr,PetscScalar *ri,PetscInt *dummy)
128: {
129:   DS_NEP         *ctx = (DS_NEP*)ds->data;
130:   PetscInt       n,l,i,*perm,lds;
131:   PetscScalar    *Q;

133:   PetscFunctionBegin;
134:   if (!ds->sc) PetscFunctionReturn(PETSC_SUCCESS);
135:   if (!ds->method) PetscFunctionReturn(PETSC_SUCCESS);  /* SLP computes just one eigenvalue */
136:   n = ds->n*ctx->max_mid;
137:   lds = ds->ld*ctx->max_mid;
138:   l = ds->l;
139:   perm = ds->perm;
140:   for (i=0;i<n;i++) perm[i] = i;
141:   if (rr) PetscCall(DSSortEigenvalues_Private(ds,rr,ri,perm,PETSC_FALSE));
142:   else PetscCall(DSSortEigenvalues_Private(ds,wr,NULL,perm,PETSC_FALSE));
143:   PetscCall(MatDenseGetArray(ds->omat[DS_MAT_Q],&Q));
144:   for (i=l;i<ds->t;i++) Q[i+i*lds] = wr[perm[i]];
145:   for (i=l;i<ds->t;i++) wr[i] = Q[i+i*lds];
146:   PetscCall(MatDenseRestoreArray(ds->omat[DS_MAT_Q],&Q));
147:   /* n != ds->n */
148:   PetscCall(DSPermuteColumns_Private(ds,0,ds->t,ds->n,DS_MAT_X,perm));
149:   PetscFunctionReturn(PETSC_SUCCESS);
150: }

152: #if defined(SLEPC_MISSING_LAPACK_GGEV3)
153: #define LAPGEEV "ggev"
154: #else
155: #define LAPGEEV "ggev3"
156: #endif

158: static PetscErrorCode DSSolve_NEP_SLP(DS ds,PetscScalar *wr,PetscScalar *wi)
159: {
160:   PetscScalar    *A,*B,*W,*X,*work,*alpha,*beta,a;
161:   PetscScalar    sigma,lambda,mu,re,re2,sone=1.0,szero=0.0;
162:   PetscBLASInt   n,ld,lwork,one=1,zero=0;
163:   PetscInt       it,pos,j,maxit=100,result;
164:   PetscReal      norm,tol,done=1.0;
165: #if !PetscDefined(USE_COMPLEX)
166:   PetscReal      *alphai,im,im2;
167: #endif

169:   PetscFunctionBegin;
170:   PetscCall(PetscBLASIntCast(ds->n,&n));
171:   PetscCall(PetscBLASIntCast(ds->ld,&ld));
172:   PetscCall(DSAllocateMat_Private(ds,DS_MAT_A));
173:   PetscCall(DSAllocateMat_Private(ds,DS_MAT_B));
174:   PetscCall(DSAllocateMat_Private(ds,DS_MAT_W));
175:   PetscCall(MatDenseGetArray(ds->omat[DS_MAT_A],&A));
176:   PetscCall(MatDenseGetArray(ds->omat[DS_MAT_B],&B));
177:   PetscCall(MatDenseGetArray(ds->omat[DS_MAT_W],&W));
178:   PetscCall(MatDenseGetArray(ds->omat[DS_MAT_X],&X));

180:   /* workspace query and memory allocation */
181:   lwork = -1;
182: #if PetscDefined(USE_COMPLEX)
183:   PetscCallLAPACKInfo("LAPACK" LAPGEEV,LAPACKggevalt_("N","V",&n,A,&ld,B,&ld,NULL,NULL,NULL,&ld,W,&ld,&a,&lwork,NULL,&info));
184:   PetscCall(PetscBLASIntCast((PetscInt)PetscRealPart(a),&lwork));
185:   PetscCall(DSAllocateWork_Private(ds,lwork+2*ds->n,8*ds->n,0));
186:   alpha = ds->work;
187:   beta  = ds->work + ds->n;
188:   work  = ds->work + 2*ds->n;
189: #else
190:   PetscCallLAPACKInfo("LAPACK" LAPGEEV,LAPACKggevalt_("N","V",&n,A,&ld,B,&ld,NULL,NULL,NULL,NULL,&ld,W,&ld,&a,&lwork,&info));
191:   PetscCall(PetscBLASIntCast((PetscInt)a,&lwork));
192:   PetscCall(DSAllocateWork_Private(ds,lwork+3*ds->n,0,0));
193:   alpha  = ds->work;
194:   beta   = ds->work + ds->n;
195:   alphai = ds->work + 2*ds->n;
196:   work   = ds->work + 3*ds->n;
197: #endif

199:   sigma = 0.0;
200:   if (ds->sc->comparison==SlepcCompareTargetMagnitude || ds->sc->comparison==SlepcCompareTargetReal) sigma = *(PetscScalar*)ds->sc->comparisonctx;
201:   lambda = sigma;
202:   tol = n*PETSC_MACHINE_EPSILON/PetscSqrtReal(PETSC_SQRT_MACHINE_EPSILON);

204:   for (it=0;it<maxit;it++) {

206:     /* evaluate T and T' */
207:     PetscCall(DSNEPComputeMatrix(ds,lambda,PETSC_FALSE,DS_MAT_A));
208:     if (it) {
209:       PetscCallBLAS("BLASgemv",BLASgemv_("N",&n,&n,&sone,A,&ld,X,&one,&szero,X+ld,&one));
210:       norm = BLASnrm2_(&n,X+ld,&one);
211:       if (norm/PetscAbsScalar(lambda)<=tol) break;
212:     }
213:     PetscCall(DSNEPComputeMatrix(ds,lambda,PETSC_TRUE,DS_MAT_B));

215:     /* compute eigenvalue correction mu and eigenvector u */
216: #if PetscDefined(USE_COMPLEX)
217:     PetscCallLAPACKInfo("LAPACK" LAPGEEV,LAPACKggevalt_("N","V",&n,A,&ld,B,&ld,alpha,beta,NULL,&ld,W,&ld,work,&lwork,ds->rwork,&info));
218: #else
219:     PetscCallLAPACKInfo("LAPACK" LAPGEEV,LAPACKggevalt_("N","V",&n,A,&ld,B,&ld,alpha,alphai,beta,NULL,&ld,W,&ld,work,&lwork,&info));
220: #endif

222:     /* find smallest eigenvalue */
223:     j = 0;
224:     if (beta[j]==0.0) re = (PetscRealPart(alpha[j])>0.0)? PETSC_MAX_REAL: PETSC_MIN_REAL;
225:     else re = alpha[j]/beta[j];
226: #if !PetscDefined(USE_COMPLEX)
227:     if (beta[j]==0.0) im = (alphai[j]>0.0)? PETSC_MAX_REAL: PETSC_MIN_REAL;
228:     else im = alphai[j]/beta[j];
229: #endif
230:     pos = 0;
231:     for (j=1;j<n;j++) {
232:       if (beta[j]==0.0) re2 = (PetscRealPart(alpha[j])>0.0)? PETSC_MAX_REAL: PETSC_MIN_REAL;
233:       else re2 = alpha[j]/beta[j];
234: #if !PetscDefined(USE_COMPLEX)
235:       if (beta[j]==0.0) im2 = (alphai[j]>0.0)? PETSC_MAX_REAL: PETSC_MIN_REAL;
236:       else im2 = alphai[j]/beta[j];
237:       PetscCall(SlepcCompareSmallestMagnitude(re,im,re2,im2,&result,NULL));
238: #else
239:       PetscCall(SlepcCompareSmallestMagnitude(re,0.0,re2,0.0,&result,NULL));
240: #endif
241:       if (result > 0) {
242:         re = re2;
243: #if !PetscDefined(USE_COMPLEX)
244:         im = im2;
245: #endif
246:         pos = j;
247:       }
248:     }

250: #if !PetscDefined(USE_COMPLEX)
251:     PetscCheck(im==0.0,PETSC_COMM_SELF,PETSC_ERR_SUP,"DSNEP found a complex eigenvalue; try rerunning with complex scalars");
252: #endif
253:     mu = alpha[pos]/beta[pos];
254:     PetscCall(PetscArraycpy(X,W+pos*ld,n));
255:     norm = BLASnrm2_(&n,X,&one);
256:     PetscCallLAPACKInfo("LAPACKlascl",LAPACKlascl_("G",&zero,&zero,&norm,&done,&n,&one,X,&n,&info));

258:     /* correct eigenvalue approximation */
259:     lambda = lambda - mu;
260:   }
261:   PetscCall(MatDenseRestoreArray(ds->omat[DS_MAT_A],&A));
262:   PetscCall(MatDenseRestoreArray(ds->omat[DS_MAT_B],&B));
263:   PetscCall(MatDenseRestoreArray(ds->omat[DS_MAT_W],&W));
264:   PetscCall(MatDenseRestoreArray(ds->omat[DS_MAT_X],&X));

266:   PetscCheck(it<maxit,PETSC_COMM_SELF,PETSC_ERR_CONV_FAILED,"DSNEP did not converge");
267:   ds->t = 1;
268:   wr[0] = lambda;
269:   if (wi) wi[0] = 0.0;
270:   PetscFunctionReturn(PETSC_SUCCESS);
271: }

273: #if PetscDefined(USE_COMPLEX)
274: /*
275:   Newton refinement for eigenpairs computed with contour integral.
276:   k  - number of eigenpairs to refine
277:   wr - eigenvalues (eigenvectors are stored in DS_MAT_X)
278: */
279: static PetscErrorCode DSNEPNewtonRefine(DS ds,PetscInt k,PetscScalar *wr)
280: {
281:   DS_NEP         *ctx = (DS_NEP*)ds->data;
282:   PetscScalar    *X,*W,*U,*R,sone=1.0,szero=0.0;
283:   PetscReal      norm;
284:   PetscInt       i,j,ii,nwu=0,*p,jstart=0,jend=k;
285:   const PetscInt *range;
286:   PetscBLASInt   n,*perm,ld,one=1,n1;
287:   PetscMPIInt    len,size,root;
288:   PetscLayout    map;

290:   PetscFunctionBegin;
291:   PetscCall(MatDenseGetArray(ds->omat[DS_MAT_X],&X));
292:   PetscCall(PetscBLASIntCast(ds->n,&n));
293:   PetscCall(PetscBLASIntCast(ds->ld,&ld));
294:   n1 = n+1;
295:   p  = ds->perm;
296:   PetscCall(PetscArrayzero(p,k));
297:   PetscCall(DSAllocateWork_Private(ds,(n+2)*(n+1),0,n+1));
298:   U    = ds->work+nwu;    nwu += (n+1)*(n+1);
299:   R    = ds->work+nwu;    /*nwu += n+1;*/
300:   perm = ds->iwork;
301:   if (ds->pmode==DS_PARALLEL_DISTRIBUTED) {
302:     PetscCall(PetscLayoutCreateFromSizes(PetscObjectComm((PetscObject)ds),PETSC_DECIDE,k,1,&map));
303:     PetscCall(PetscLayoutGetRange(map,&jstart,&jend));
304:   }
305:   for (ii=0;ii<ctx->Nit;ii++) {
306:     for (j=jstart;j<jend;j++) {
307:       if (p[j]<2) {
308:         PetscCall(DSNEPComputeMatrix(ds,wr[j],PETSC_FALSE,DS_MAT_W));
309:         PetscCall(MatDenseGetArray(ds->omat[DS_MAT_W],&W));
310:         PetscCallBLAS("BLASgemv",BLASgemv_("N",&n,&n,&sone,W,&ld,X+ld*j,&one,&szero,R,&one));
311:         PetscCall(MatDenseRestoreArray(ds->omat[DS_MAT_W],&W));
312:         norm = BLASnrm2_(&n,R,&one);
313:         if (norm/PetscAbsScalar(wr[j]) > ctx->rtol) {
314:           PetscCall(PetscInfo(NULL,"Refining eigenpair %" PetscInt_FMT ", residual=%g\n",j,(double)(norm/PetscAbsScalar(wr[j]))));
315:           p[j] = 1;
316:           R[n] = 0.0;
317:           PetscCall(MatDenseGetArray(ds->omat[DS_MAT_W],&W));
318:           for (i=0;i<n;i++) {
319:             PetscCall(PetscArraycpy(U+i*n1,W+i*ld,n));
320:             U[n+i*n1] = PetscConj(X[j*ld+i]);
321:           }
322:           PetscCall(MatDenseRestoreArray(ds->omat[DS_MAT_W],&W));
323:           U[n+n*n1] = 0.0;
324:           PetscCall(DSNEPComputeMatrix(ds,wr[j],PETSC_TRUE,DS_MAT_W));
325:           PetscCall(MatDenseGetArray(ds->omat[DS_MAT_W],&W));
326:           PetscCallBLAS("BLASgemv",BLASgemv_("N",&n,&n,&sone,W,&ld,X+ld*j,&one,&szero,U+n*(n+1),&one));
327:           PetscCall(MatDenseRestoreArray(ds->omat[DS_MAT_W],&W));
328:           /* solve system  */
329:           PetscCallLAPACKInfo("LAPACKgetrf",LAPACKgetrf_(&n1,&n1,U,&n1,perm,&info));
330:           PetscCallLAPACKInfo("LAPACKgetrs",LAPACKgetrs_("N",&n1,&one,U,&n1,perm,R,&n1,&info));
331:           wr[j] -= R[n];
332:           for (i=0;i<n;i++) X[j*ld+i] -= R[i];
333:           /* normalization */
334:           norm = BLASnrm2_(&n,X+ld*j,&one);
335:           for (i=0;i<n;i++) X[ld*j+i] /= norm;
336:         } else p[j] = 2;
337:       }
338:     }
339:   }
340:   if (ds->pmode==DS_PARALLEL_DISTRIBUTED) {  /* communicate results */
341:     PetscCall(PetscMPIIntCast(k,&len));
342:     PetscCallMPI(MPIU_Allreduce(MPI_IN_PLACE,p,len,MPIU_INT,MPI_SUM,PetscObjectComm((PetscObject)ds)));
343:     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)ds),&size));
344:     PetscCall(PetscLayoutGetRanges(map,&range));
345:     for (j=0;j<k;j++) {
346:       if (p[j]) {  /* j-th eigenpair has been refined */
347:         for (root=0;root<size;root++) if (range[root+1]>j) break;
348:         PetscCall(PetscMPIIntCast(1,&len));
349:         PetscCallMPI(MPI_Bcast(wr+j,len,MPIU_SCALAR,root,PetscObjectComm((PetscObject)ds)));
350:         PetscCall(PetscMPIIntCast(n,&len));
351:         PetscCallMPI(MPI_Bcast(X+ld*j,len,MPIU_SCALAR,root,PetscObjectComm((PetscObject)ds)));
352:       }
353:     }
354:     PetscCall(PetscLayoutDestroy(&map));
355:   }
356:   PetscCall(MatDenseRestoreArray(ds->omat[DS_MAT_X],&X));
357:   PetscFunctionReturn(PETSC_SUCCESS);
358: }

360: PetscErrorCode DSSolve_NEP_Contour(DS ds,PetscScalar *wr,PetscScalar *wi)
361: {
362:   DS_NEP         *ctx = (DS_NEP*)ds->data;
363:   PetscScalar    *alpha,*beta,*Q,*Z,*X,*U,*V,*W,*work,*Rc,*R,*w,*z,*zn,*S;
364:   PetscScalar    sone=1.0,szero=0.0,center,a;
365:   PetscReal      *rwork,norm,radius,vscale,rgscale,*sigma;
366:   PetscBLASInt   n,*perm,p,pp,ld,lwork,k_,rk_,colA,rowA,one=1;
367:   PetscInt       mid,lds,nnod=ctx->nnod,k,i,ii,jj,j,s,off,rk,nwu=0,nw,lrwork,*inside,kstart=0,kend=nnod;
368:   PetscMPIInt    len;
369:   PetscBool      isellipse;
370:   PetscRandom    rand;

372:   PetscFunctionBegin;
373:   PetscCheck(ctx->rg,PetscObjectComm((PetscObject)ds),PETSC_ERR_ORDER,"The contour solver requires a region passed with DSNEPSetRG()");
374:   /* Contour parameters */
375:   PetscCall(PetscObjectTypeCompare((PetscObject)ctx->rg,RGELLIPSE,&isellipse));
376:   PetscCheck(isellipse,PetscObjectComm((PetscObject)ds),PETSC_ERR_SUP,"Region must be Ellipse");
377:   PetscCall(RGEllipseGetParameters(ctx->rg,&center,&radius,&vscale));
378:   PetscCall(RGGetScale(ctx->rg,&rgscale));
379:   if (ds->pmode==DS_PARALLEL_DISTRIBUTED) {
380:     if (!ctx->map) PetscCall(PetscLayoutCreateFromSizes(PetscObjectComm((PetscObject)ds),PETSC_DECIDE,ctx->nnod,1,&ctx->map));
381:     PetscCall(PetscLayoutGetRange(ctx->map,&kstart,&kend));
382:   }

384:   PetscCall(DSAllocateMat_Private(ds,DS_MAT_W)); /* size n */
385:   PetscCall(DSAllocateMat_Private(ds,DS_MAT_Q)); /* size mid*n */
386:   PetscCall(DSAllocateMat_Private(ds,DS_MAT_Z)); /* size mid*n */
387:   PetscCall(DSAllocateMat_Private(ds,DS_MAT_U)); /* size mid*n */
388:   PetscCall(DSAllocateMat_Private(ds,DS_MAT_V)); /* size mid*n */
389:   PetscCall(MatDenseGetArray(ds->omat[DS_MAT_Q],&Q));
390:   PetscCall(MatDenseGetArray(ds->omat[DS_MAT_Z],&Z));
391:   PetscCall(MatDenseGetArray(ds->omat[DS_MAT_U],&U));
392:   PetscCall(MatDenseGetArray(ds->omat[DS_MAT_V],&V));
393:   mid  = ctx->max_mid;
394:   PetscCall(PetscBLASIntCast(ds->n,&n));
395:   p    = n;   /* maximum number of columns for the probing matrix */
396:   PetscCall(PetscBLASIntCast(ds->ld,&ld));
397:   PetscCall(PetscBLASIntCast(mid*n,&rowA));
398:   nw     = 2*n*(p+mid)+3*nnod+2*mid*n*p;
399:   lrwork = 9*mid*n;

401:   /* workspace query and memory allocation */
402:   lwork = -1;
403:   PetscCallLAPACKInfo("LAPACK" LAPGEEV,LAPACKggevalt_("N","V",&rowA,Q,&rowA,Z,&rowA,NULL,NULL,NULL,&ld,V,&rowA,&a,&lwork,NULL,&info));
404:   PetscCall(PetscBLASIntCast((PetscInt)PetscRealPart(a),&lwork));
405:   PetscCall(DSAllocateWork_Private(ds,lwork+nw,lrwork,n+1));

407:   sigma = ds->rwork;
408:   rwork = ds->rwork+mid*n;
409:   perm  = ds->iwork;
410:   z     = ds->work+nwu;    nwu += nnod;         /* quadrature points */
411:   zn    = ds->work+nwu;    nwu += nnod;         /* normalized quadrature points */
412:   w     = ds->work+nwu;    nwu += nnod;         /* quadrature weights */
413:   Rc    = ds->work+nwu;    nwu += n*p;
414:   R     = ds->work+nwu;    nwu += n*p;
415:   alpha = ds->work+nwu;    nwu += mid*n;
416:   beta  = ds->work+nwu;    nwu += mid*n;
417:   S     = ds->work+nwu;    nwu += 2*mid*n*p;
418:   work  = ds->work+nwu;

420:   /* Compute quadrature parameters */
421:   PetscCall(RGComputeQuadrature(ctx->rg,RG_QUADRULE_TRAPEZOIDAL,nnod,z,zn,w));

423:   /* Set random matrix */
424:   PetscCall(PetscRandomCreate(PetscObjectComm((PetscObject)ds),&rand));
425:   PetscCall(PetscRandomSetSeed(rand,0x12345678));
426:   PetscCall(PetscRandomSeed(rand));
427:   for (j=0;j<p;j++)
428:     for (i=0;i<n;i++) PetscCall(PetscRandomGetValue(rand,Rc+i+j*n));
429:   PetscCall(PetscArrayzero(S,2*mid*n*p));
430:   /* Loop of integration points */
431:   for (k=kstart;k<kend;k++) {
432:     PetscCall(PetscInfo(NULL,"Solving integration point %" PetscInt_FMT "\n",k));
433:     PetscCall(PetscArraycpy(R,Rc,p*n));
434:     PetscCall(DSNEPComputeMatrix(ds,z[k],PETSC_FALSE,DS_MAT_W));

436:     /* LU factorization */
437:     PetscCall(MatDenseGetArray(ds->omat[DS_MAT_W],&W));
438:     PetscCallLAPACKInfo("LAPACKgetrf",LAPACKgetrf_(&n,&n,W,&ld,perm,&info));
439:     PetscCallLAPACKInfo("LAPACKgetrs",LAPACKgetrs_("N",&n,&p,W,&ld,perm,R,&n,&info));
440:     PetscCall(MatDenseRestoreArray(ds->omat[DS_MAT_W],&W));

442:     /* Moments computation */
443:     for (s=0;s<2*ctx->max_mid;s++) {
444:       off = s*n*p;
445:       for (j=0;j<p;j++)
446:         for (i=0;i<n;i++) S[off+i+j*n] += w[k]*R[j*n+i];
447:       w[k] *= zn[k];
448:     }
449:   }

451:   if (ds->pmode==DS_PARALLEL_DISTRIBUTED) {  /* compute final S via reduction */
452:     PetscCall(PetscMPIIntCast(2*mid*n*p,&len));
453:     PetscCallMPI(MPIU_Allreduce(MPI_IN_PLACE,S,len,MPIU_SCALAR,MPIU_SUM,PetscObjectComm((PetscObject)ds)));
454:   }
455:   PetscCall(PetscBLASIntCast(ctx->spls?PetscMin(ctx->spls,n):n,&p));
456:   pp = p;
457:   do {
458:     p = pp;
459:     PetscCall(PetscBLASIntCast(mid*p,&colA));

461:     PetscCall(PetscInfo(ds,"Computing SVD of size %" PetscBLASInt_FMT "x%" PetscBLASInt_FMT "\n",rowA,colA));
462:     for (jj=0;jj<mid;jj++) {
463:       for (ii=0;ii<mid;ii++) {
464:         off = jj*p*rowA+ii*n;
465:         for (j=0;j<p;j++)
466:           for (i=0;i<n;i++) Q[off+j*rowA+i] = S[((jj+ii)*n+j)*n+i];
467:       }
468:     }
469:     PetscCallLAPACKInfo("LAPACKgesvd",LAPACKgesvd_("S","S",&rowA,&colA,Q,&rowA,sigma,U,&rowA,V,&colA,work,&lwork,rwork,&info));

471:     rk = colA;
472:     for (i=1;i<colA;i++) if (sigma[i]/sigma[0]<PETSC_MACHINE_EPSILON*1e4) {rk = i; break;}
473:     if (rk<colA || p==n) break;
474:     pp *= 2;
475:   } while (pp<=n);
476:   PetscCall(PetscInfo(ds,"Solving generalized eigenproblem of size %" PetscInt_FMT "\n",rk));
477:   for (jj=0;jj<mid;jj++) {
478:     for (ii=0;ii<mid;ii++) {
479:       off = jj*p*rowA+ii*n;
480:       for (j=0;j<p;j++)
481:         for (i=0;i<n;i++) Q[off+j*rowA+i] = S[((jj+ii+1)*n+j)*n+i];
482:     }
483:   }
484:   PetscCall(PetscBLASIntCast(rk,&rk_));
485:   PetscCallBLAS("BLASgemm",BLASgemm_("N","C",&rowA,&rk_,&colA,&sone,Q,&rowA,V,&colA,&szero,Z,&rowA));
486:   PetscCallBLAS("BLASgemm",BLASgemm_("C","N",&rk_,&rk_,&rowA,&sone,U,&rowA,Z,&rowA,&szero,Q,&rk_));
487:   PetscCall(PetscArrayzero(Z,n*mid*n*mid));
488:   for (j=0;j<rk;j++) Z[j+j*rk_] = sigma[j];
489:   PetscCallLAPACKInfo("LAPACK" LAPGEEV,LAPACKggevalt_("N","V",&rk_,Q,&rk_,Z,&rk_,alpha,beta,NULL,&ld,V,&rk_,work,&lwork,rwork,&info));
490:   for (i=0;i<rk;i++) wr[i] = (center+alpha[i]*radius/beta[i])*rgscale;
491:   PetscCall(PetscMalloc1(rk,&inside));
492:   PetscCall(RGCheckInside(ctx->rg,rk,wr,wi,inside));
493:   k=0;
494:   for (i=0;i<rk;i++)
495:     if (inside[i]==1) inside[k++] = i;
496:   /* Discard values outside region */
497:   lds = ld*mid;
498:   PetscCall(PetscArrayzero(Q,lds*lds));
499:   PetscCall(PetscArrayzero(Z,lds*lds));
500:   for (i=0;i<k;i++) Q[i+i*lds] = (center*beta[inside[i]]+radius*alpha[inside[i]])*rgscale;
501:   for (i=0;i<k;i++) Z[i+i*lds] = beta[inside[i]];
502:   for (i=0;i<k;i++) wr[i] = Q[i+i*lds]/Z[i+i*lds];
503:   for (j=0;j<k;j++) for (i=0;i<rk;i++) V[j*rk+i] = sigma[i]*V[inside[j]*rk+i];
504:   PetscCall(PetscBLASIntCast(k,&k_));
505:   PetscCall(MatDenseGetArray(ds->omat[DS_MAT_X],&X));
506:   PetscCallBLAS("BLASgemm",BLASgemm_("N","N",&n,&k_,&rk_,&sone,U,&rowA,V,&rk_,&szero,X,&ld));
507:   /* Normalize */
508:   for (j=0;j<k;j++) {
509:     norm = BLASnrm2_(&n,X+ld*j,&one);
510:     for (i=0;i<n;i++) X[ld*j+i] /= norm;
511:   }
512:   PetscCall(PetscFree(inside));
513:   PetscCall(MatDenseRestoreArray(ds->omat[DS_MAT_X],&X));
514:   PetscCall(MatDenseRestoreArray(ds->omat[DS_MAT_Q],&Q));
515:   PetscCall(MatDenseRestoreArray(ds->omat[DS_MAT_Z],&Z));
516:   PetscCall(MatDenseRestoreArray(ds->omat[DS_MAT_U],&U));
517:   PetscCall(MatDenseRestoreArray(ds->omat[DS_MAT_V],&V));

519:   /* Newton refinement */
520:   if (ctx->Nit) PetscCall(DSNEPNewtonRefine(ds,k,wr));
521:   ds->t = k;
522:   PetscCall(PetscRandomDestroy(&rand));
523:   PetscFunctionReturn(PETSC_SUCCESS);
524: }
525: #endif

527: #if !PetscDefined(HAVE_MPIUNI)
528: static PetscErrorCode DSSynchronize_NEP(DS ds,PetscScalar eigr[],PetscScalar eigi[])
529: {
530:   DS_NEP         *ctx = (DS_NEP*)ds->data;
531:   PetscInt       ld=ds->ld,k=0;
532:   PetscMPIInt    n,n2,rank,size,off=0;
533:   PetscScalar    *X;

535:   PetscFunctionBegin;
536:   if (!ds->method) { /* SLP */
537:     if (ds->state>=DS_STATE_CONDENSED) k += ds->n;
538:     if (eigr) k += 1;
539:     if (eigi) k += 1;
540:     PetscCall(PetscMPIIntCast(1,&n));
541:     PetscCall(PetscMPIIntCast(ds->n,&n2));
542:   } else { /* Contour */
543:     if (ds->state>=DS_STATE_CONDENSED) k += ctx->max_mid*ds->n*ld;
544:     if (eigr) k += ctx->max_mid*ds->n;
545:     if (eigi) k += ctx->max_mid*ds->n;
546:     PetscCall(PetscMPIIntCast(ctx->max_mid*ds->n,&n));
547:     PetscCall(PetscMPIIntCast(ctx->max_mid*ds->n*ld,&n2));
548:   }
549:   PetscCall(DSAllocateWork_Private(ds,k,0,0));
550:   PetscCall(PetscMPIIntCast(k*sizeof(PetscScalar),&size));
551:   if (ds->state>=DS_STATE_CONDENSED) PetscCall(MatDenseGetArray(ds->omat[DS_MAT_X],&X));
552:   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)ds),&rank));
553:   if (!rank) {
554:     if (ds->state>=DS_STATE_CONDENSED) PetscCallMPI(MPI_Pack(X,n2,MPIU_SCALAR,ds->work,size,&off,PetscObjectComm((PetscObject)ds)));
555:     if (eigr) PetscCallMPI(MPI_Pack(eigr,n,MPIU_SCALAR,ds->work,size,&off,PetscObjectComm((PetscObject)ds)));
556: #if !PetscDefined(USE_COMPLEX)
557:     if (eigi) PetscCallMPI(MPI_Pack(eigi,n,MPIU_SCALAR,ds->work,size,&off,PetscObjectComm((PetscObject)ds)));
558: #endif
559:   }
560:   PetscCallMPI(MPI_Bcast(ds->work,size,MPI_BYTE,0,PetscObjectComm((PetscObject)ds)));
561:   if (rank) {
562:     if (ds->state>=DS_STATE_CONDENSED) PetscCallMPI(MPI_Unpack(ds->work,size,&off,X,n2,MPIU_SCALAR,PetscObjectComm((PetscObject)ds)));
563:     if (eigr) PetscCallMPI(MPI_Unpack(ds->work,size,&off,eigr,n,MPIU_SCALAR,PetscObjectComm((PetscObject)ds)));
564: #if !PetscDefined(USE_COMPLEX)
565:     if (eigi) PetscCallMPI(MPI_Unpack(ds->work,size,&off,eigi,n,MPIU_SCALAR,PetscObjectComm((PetscObject)ds)));
566: #endif
567:   }
568:   if (ds->state>=DS_STATE_CONDENSED) PetscCall(MatDenseRestoreArray(ds->omat[DS_MAT_X],&X));
569:   PetscFunctionReturn(PETSC_SUCCESS);
570: }
571: #endif

573: static PetscErrorCode DSNEPSetFN_NEP(DS ds,PetscInt n,FN fn[])
574: {
575:   DS_NEP         *ctx = (DS_NEP*)ds->data;
576:   PetscInt       i;

578:   PetscFunctionBegin;
579:   PetscCheck(n>0,PetscObjectComm((PetscObject)ds),PETSC_ERR_ARG_OUTOFRANGE,"Must have one or more functions, you have %" PetscInt_FMT,n);
580:   PetscCheck(n<=DS_NUM_EXTRA,PetscObjectComm((PetscObject)ds),PETSC_ERR_ARG_OUTOFRANGE,"Too many functions, you specified %" PetscInt_FMT " but the limit is %d",n,DS_NUM_EXTRA);
581:   if (ds->ld) PetscCall(PetscInfo(ds,"DSNEPSetFN() called after DSAllocate()\n"));
582:   for (i=0;i<n;i++) PetscCall(PetscObjectReference((PetscObject)fn[i]));
583:   for (i=0;i<ctx->nf;i++) PetscCall(FNDestroy(&ctx->f[i]));
584:   for (i=0;i<n;i++) ctx->f[i] = fn[i];
585:   ctx->nf = n;
586:   PetscFunctionReturn(PETSC_SUCCESS);
587: }

589: /*@
590:    DSNEPSetFN - Sets a number of functions that define the nonlinear
591:    eigenproblem.

593:    Collective

595:    Input Parameters:
596: +  ds - the direct solver context
597: .  n  - number of functions
598: -  fn - array of functions

600:    Notes:
601:    The nonlinear eigenproblem is defined in terms of the split nonlinear
602:    operator $T(\lambda) = \sum_i E_i f_i(\lambda)$.

604:    This function must be called before `DSAllocate()`. Then `DSAllocate()`
605:    will allocate an extra matrix $E_i$ per each function, that can be
606:    filled in the usual way.

608:    Level: advanced

610: .seealso: [](sec:ds), `DSNEP`, `DSNEPGetFN()`, `DSAllocate()`
611: @*/
612: PetscErrorCode DSNEPSetFN(DS ds,PetscInt n,FN fn[])
613: {
614:   PetscInt       i;

616:   PetscFunctionBegin;
619:   PetscAssertPointer(fn,3);
620:   for (i=0;i<n;i++) {
622:     PetscCheckSameComm(ds,1,fn[i],3);
623:   }
624:   PetscTryMethod(ds,"DSNEPSetFN_C",(DS,PetscInt,FN[]),(ds,n,fn));
625:   PetscFunctionReturn(PETSC_SUCCESS);
626: }

628: static PetscErrorCode DSNEPGetFN_NEP(DS ds,PetscInt k,FN *fn)
629: {
630:   DS_NEP *ctx = (DS_NEP*)ds->data;

632:   PetscFunctionBegin;
633:   PetscCheck(k>=0 && k<ctx->nf,PetscObjectComm((PetscObject)ds),PETSC_ERR_ARG_OUTOFRANGE,"k must be between 0 and %" PetscInt_FMT,ctx->nf-1);
634:   *fn = ctx->f[k];
635:   PetscFunctionReturn(PETSC_SUCCESS);
636: }

638: /*@
639:    DSNEPGetFN - Gets the functions associated with the nonlinear `DS`.

641:    Not Collective

643:    Input Parameters:
644: +  ds - the direct solver context
645: -  k  - the index of the requested function (starting in 0)

647:    Output Parameter:
648: .  fn - the function

650:    Level: advanced

652: .seealso: [](sec:ds), `DSNEP`, `DSNEPSetFN()`
653: @*/
654: PetscErrorCode DSNEPGetFN(DS ds,PetscInt k,FN *fn)
655: {
656:   PetscFunctionBegin;
658:   PetscAssertPointer(fn,3);
659:   PetscUseMethod(ds,"DSNEPGetFN_C",(DS,PetscInt,FN*),(ds,k,fn));
660:   PetscFunctionReturn(PETSC_SUCCESS);
661: }

663: static PetscErrorCode DSNEPGetNumFN_NEP(DS ds,PetscInt *n)
664: {
665:   DS_NEP *ctx = (DS_NEP*)ds->data;

667:   PetscFunctionBegin;
668:   *n = ctx->nf;
669:   PetscFunctionReturn(PETSC_SUCCESS);
670: }

672: /*@
673:    DSNEPGetNumFN - Returns the number of functions stored internally by
674:    the `DS`.

676:    Not Collective

678:    Input Parameter:
679: .  ds - the direct solver context

681:    Output Parameter:
682: .  n - the number of functions passed in `DSNEPSetFN()`

684:    Level: advanced

686: .seealso: [](sec:ds), `DSNEP`, `DSNEPSetFN()`
687: @*/
688: PetscErrorCode DSNEPGetNumFN(DS ds,PetscInt *n)
689: {
690:   PetscFunctionBegin;
692:   PetscAssertPointer(n,2);
693:   PetscUseMethod(ds,"DSNEPGetNumFN_C",(DS,PetscInt*),(ds,n));
694:   PetscFunctionReturn(PETSC_SUCCESS);
695: }

697: static PetscErrorCode DSNEPSetMinimality_NEP(DS ds,PetscInt n)
698: {
699:   DS_NEP *ctx = (DS_NEP*)ds->data;

701:   PetscFunctionBegin;
702:   if (n == PETSC_DECIDE || n == PETSC_DEFAULT) ctx->max_mid = 4;
703:   else {
704:     PetscCheck(n>0,PetscObjectComm((PetscObject)ds),PETSC_ERR_ARG_OUTOFRANGE,"The minimality value must be > 0");
705:     ctx->max_mid = n;
706:   }
707:   PetscFunctionReturn(PETSC_SUCCESS);
708: }

710: /*@
711:    DSNEPSetMinimality - Sets the maximum minimality index used internally by
712:    the `DSNEP`.

714:    Logically Collective

716:    Input Parameters:
717: +  ds - the direct solver context
718: -  n  - the maximum minimality index

720:    Options Database Key:
721: .  -ds_nep_minimality n - sets the maximum minimality index

723:    Notes:
724:    The maximum minimality index is used only in the contour integral method,
725:    and is related to the highest moments used in the method. The default
726:    value is 1, a larger value might give better accuracy in some cases, but
727:    at a higher cost.

729:    Level: advanced

731: .seealso: [](sec:ds), `DSNEP`, `DSNEPGetMinimality()`
732: @*/
733: PetscErrorCode DSNEPSetMinimality(DS ds,PetscInt n)
734: {
735:   PetscFunctionBegin;
738:   PetscTryMethod(ds,"DSNEPSetMinimality_C",(DS,PetscInt),(ds,n));
739:   PetscFunctionReturn(PETSC_SUCCESS);
740: }

742: static PetscErrorCode DSNEPGetMinimality_NEP(DS ds,PetscInt *n)
743: {
744:   DS_NEP *ctx = (DS_NEP*)ds->data;

746:   PetscFunctionBegin;
747:   *n = ctx->max_mid;
748:   PetscFunctionReturn(PETSC_SUCCESS);
749: }

751: /*@
752:    DSNEPGetMinimality - Returns the maximum minimality index used internally by
753:    the `DSNEP`.

755:    Not Collective

757:    Input Parameter:
758: .  ds - the direct solver context

760:    Output Parameter:
761: .  n - the maximum minimality index passed in `DSNEPSetMinimality()`

763:    Level: advanced

765: .seealso: [](sec:ds), `DSNEP`, `DSNEPSetMinimality()`
766: @*/
767: PetscErrorCode DSNEPGetMinimality(DS ds,PetscInt *n)
768: {
769:   PetscFunctionBegin;
771:   PetscAssertPointer(n,2);
772:   PetscUseMethod(ds,"DSNEPGetMinimality_C",(DS,PetscInt*),(ds,n));
773:   PetscFunctionReturn(PETSC_SUCCESS);
774: }

776: static PetscErrorCode DSNEPSetRefine_NEP(DS ds,PetscReal tol,PetscInt its)
777: {
778:   DS_NEP *ctx = (DS_NEP*)ds->data;

780:   PetscFunctionBegin;
781:   if (tol == (PetscReal)PETSC_DETERMINE) {
782:     ctx->rtol = PETSC_MACHINE_EPSILON/PetscSqrtReal(PETSC_SQRT_MACHINE_EPSILON);
783:   } else if (tol != (PetscReal)PETSC_CURRENT) {
784:     PetscCheck(tol>0.0,PetscObjectComm((PetscObject)ds),PETSC_ERR_ARG_OUTOFRANGE,"The tolerance must be > 0");
785:     ctx->rtol = tol;
786:   }
787:   if (its == PETSC_DETERMINE) {
788:     ctx->Nit = 3;
789:   } else if (its != PETSC_CURRENT) {
790:     PetscCheck(its>=0,PetscObjectComm((PetscObject)ds),PETSC_ERR_ARG_OUTOFRANGE,"The number of iterations must be >= 0");
791:     ctx->Nit = its;
792:   }
793:   PetscFunctionReturn(PETSC_SUCCESS);
794: }

796: /*@
797:    DSNEPSetRefine - Sets the tolerance and the number of iterations of Newton iterative
798:    refinement for eigenpairs when solving a `DSNEP`.

800:    Logically Collective

802:    Input Parameters:
803: +  ds  - the direct solver context
804: .  tol - the tolerance
805: -  its - the number of iterations

807:    Options Database Keys:
808: +  -ds_nep_refine_tol tol - sets the tolerance
809: -  -ds_nep_refine_its its - sets the number of Newton iterations

811:    Notes:
812:    Iterative refinement of eigenpairs is currently used only in the contour
813:    integral method.

815:    Use `PETSC_CURRENT` to retain the current value of any of the parameters.
816:    Use `PETSC_DETERMINE` for either argument to assign a default value computed
817:    internally.

819:    Level: advanced

821: .seealso: [](sec:ds), `DSNEP`, `DSNEPGetRefine()`
822: @*/
823: PetscErrorCode DSNEPSetRefine(DS ds,PetscReal tol,PetscInt its)
824: {
825:   PetscFunctionBegin;
829:   PetscTryMethod(ds,"DSNEPSetRefine_C",(DS,PetscReal,PetscInt),(ds,tol,its));
830:   PetscFunctionReturn(PETSC_SUCCESS);
831: }

833: static PetscErrorCode DSNEPGetRefine_NEP(DS ds,PetscReal *tol,PetscInt *its)
834: {
835:   DS_NEP *ctx = (DS_NEP*)ds->data;

837:   PetscFunctionBegin;
838:   if (tol) *tol = ctx->rtol;
839:   if (its) *its = ctx->Nit;
840:   PetscFunctionReturn(PETSC_SUCCESS);
841: }

843: /*@
844:    DSNEPGetRefine - Returns the tolerance and the number of iterations of Newton iterative
845:    refinement for eigenpairs.

847:    Not Collective

849:    Input Parameter:
850: .  ds - the direct solver context

852:    Output Parameters:
853: +  tol - the tolerance
854: -  its - the number of iterations

856:    Level: advanced

858: .seealso: [](sec:ds), `DSNEP`, `DSNEPSetRefine()`
859: @*/
860: PetscErrorCode DSNEPGetRefine(DS ds,PetscReal *tol,PetscInt *its)
861: {
862:   PetscFunctionBegin;
864:   PetscUseMethod(ds,"DSNEPGetRefine_C",(DS,PetscReal*,PetscInt*),(ds,tol,its));
865:   PetscFunctionReturn(PETSC_SUCCESS);
866: }

868: static PetscErrorCode DSNEPSetIntegrationPoints_NEP(DS ds,PetscInt ip)
869: {
870:   DS_NEP         *ctx = (DS_NEP*)ds->data;

872:   PetscFunctionBegin;
873:   if (ip == PETSC_DECIDE || ip == PETSC_DEFAULT) ctx->nnod = 64;
874:   else {
875:     PetscCheck(ip>0,PetscObjectComm((PetscObject)ds),PETSC_ERR_ARG_OUTOFRANGE,"The number of integration points must be > 0");
876:     ctx->nnod = ip;
877:   }
878:   PetscCall(PetscLayoutDestroy(&ctx->map));  /* need to redistribute at next solve */
879:   PetscFunctionReturn(PETSC_SUCCESS);
880: }

882: /*@
883:    DSNEPSetIntegrationPoints - Sets the number of integration points to be
884:    used in the contour integral method when solving a `DSNEP`.

886:    Logically Collective

888:    Input Parameters:
889: +  ds - the direct solver context
890: -  ip - the number of integration points

892:    Options Database Key:
893: .  -ds_nep_integration_points ip - sets the number of integration points

895:    Notes:
896:    This parameter is relevant only in the contour integral method.

898:    Level: advanced

900: .seealso: [](sec:ds), `DSNEP`, `DSNEPGetIntegrationPoints()`
901: @*/
902: PetscErrorCode DSNEPSetIntegrationPoints(DS ds,PetscInt ip)
903: {
904:   PetscFunctionBegin;
907:   PetscTryMethod(ds,"DSNEPSetIntegrationPoints_C",(DS,PetscInt),(ds,ip));
908:   PetscFunctionReturn(PETSC_SUCCESS);
909: }

911: static PetscErrorCode DSNEPGetIntegrationPoints_NEP(DS ds,PetscInt *ip)
912: {
913:   DS_NEP *ctx = (DS_NEP*)ds->data;

915:   PetscFunctionBegin;
916:   *ip = ctx->nnod;
917:   PetscFunctionReturn(PETSC_SUCCESS);
918: }

920: /*@
921:    DSNEPGetIntegrationPoints - Returns the number of integration points used
922:    in the contour integral method.

924:    Not Collective

926:    Input Parameter:
927: .  ds - the direct solver context

929:    Output Parameter:
930: .  ip - the number of integration points

932:    Level: advanced

934: .seealso: [](sec:ds), `DSNEP`, `DSNEPSetIntegrationPoints()`
935: @*/
936: PetscErrorCode DSNEPGetIntegrationPoints(DS ds,PetscInt *ip)
937: {
938:   PetscFunctionBegin;
940:   PetscAssertPointer(ip,2);
941:   PetscUseMethod(ds,"DSNEPGetIntegrationPoints_C",(DS,PetscInt*),(ds,ip));
942:   PetscFunctionReturn(PETSC_SUCCESS);
943: }

945: static PetscErrorCode DSNEPSetSamplingSize_NEP(DS ds,PetscInt p)
946: {
947:   DS_NEP *ctx = (DS_NEP*)ds->data;

949:   PetscFunctionBegin;
950:   if (p == PETSC_DECIDE || p == PETSC_DEFAULT) ctx->spls = 0;
951:   else {
952:     PetscCheck(p>0,PetscObjectComm((PetscObject)ds),PETSC_ERR_ARG_OUTOFRANGE,"The sample size must be > 0");
953:     PetscCheck(p>=20,PetscObjectComm((PetscObject)ds),PETSC_ERR_ARG_OUTOFRANGE,"The sample size cannot be smaller than 20");
954:     ctx->spls = p;
955:   }
956:   PetscFunctionReturn(PETSC_SUCCESS);
957: }

959: /*@
960:    DSNEPSetSamplingSize - Sets the number of sampling columns to be
961:    used in the contour integral method when solving a `DSNEP`.

963:    Logically Collective

965:    Input Parameters:
966: +  ds - the direct solver context
967: -  p  - the number of columns for the sampling matrix

969:    Options Database Key:
970: .  -ds_nep_sampling_size p - set the number of sampling columns

972:    Note:
973:    This parameter is relevant only in the contour integral method.

975:    Level: advanced

977: .seealso: [](sec:ds), `DSNEP`, `DSNEPGetSamplingSize()`
978: @*/
979: PetscErrorCode DSNEPSetSamplingSize(DS ds,PetscInt p)
980: {
981:   PetscFunctionBegin;
984:   PetscTryMethod(ds,"DSNEPSetSamplingSize_C",(DS,PetscInt),(ds,p));
985:   PetscFunctionReturn(PETSC_SUCCESS);
986: }

988: static PetscErrorCode DSNEPGetSamplingSize_NEP(DS ds,PetscInt *p)
989: {
990:   DS_NEP *ctx = (DS_NEP*)ds->data;

992:   PetscFunctionBegin;
993:   *p = ctx->spls;
994:   PetscFunctionReturn(PETSC_SUCCESS);
995: }

997: /*@
998:    DSNEPGetSamplingSize - Returns the number of sampling columns used
999:    in the contour integral method.

1001:    Not Collective

1003:    Input Parameter:
1004: .  ds - the direct solver context

1006:    Output Parameter:
1007: .  p -  the number of columns for the sampling matrix

1009:    Level: advanced

1011: .seealso: [](sec:ds), `DSNEP`, `DSNEPSetSamplingSize()`
1012: @*/
1013: PetscErrorCode DSNEPGetSamplingSize(DS ds,PetscInt *p)
1014: {
1015:   PetscFunctionBegin;
1017:   PetscAssertPointer(p,2);
1018:   PetscUseMethod(ds,"DSNEPGetSamplingSize_C",(DS,PetscInt*),(ds,p));
1019:   PetscFunctionReturn(PETSC_SUCCESS);
1020: }

1022: static PetscErrorCode DSNEPSetComputeMatrixFunction_NEP(DS ds,DSNEPMatrixFunctionFn *fun,PetscCtx ctx,PetscCtxDestroyFn *destroy)
1023: {
1024:   DS_NEP *dsctx = (DS_NEP*)ds->data;

1026:   PetscFunctionBegin;
1027:   if (dsctx->computematrixdestroy) PetscCall((*dsctx->computematrixdestroy)(&dsctx->computematrixctx));
1028:   dsctx->computematrix        = fun;
1029:   dsctx->computematrixctx     = ctx;
1030:   dsctx->computematrixdestroy = destroy;
1031:   PetscFunctionReturn(PETSC_SUCCESS);
1032: }

1034: /*@
1035:    DSNEPSetComputeMatrixFunction - Sets a user-provided subroutine to compute
1036:    the matrices $T(\lambda)$ or $T'(\lambda)$.

1038:    Logically Collective

1040:    Input Parameters:
1041: +  ds      - the direct solver context
1042: .  fun     - matrix function evaluation routine, see `DSNEPMatrixFunctionFn` for the calling sequence
1043: .  ctx     - a context pointer (the last parameter to the user function)
1044: -  destroy - a routine for destroying the context (may be `NULL`), see `PetscCtxDestroyFn`
1045:              for the calling sequence

1047:    Note:
1048:    The result is computed as $T(\lambda) = \sum_i E_i f_i(\lambda)$, and similarly
1049:    for the derivative, where $E_i$ are the extra matrices, see `DSMatType`.

1051:    Level: developer

1053: .seealso: [](sec:ds), `DSNEP`, `DSNEPGetComputeMatrixFunction()`
1054: @*/
1055: PetscErrorCode DSNEPSetComputeMatrixFunction(DS ds,DSNEPMatrixFunctionFn *fun,PetscCtx ctx,PetscCtxDestroyFn *destroy)
1056: {
1057:   PetscFunctionBegin;
1059:   PetscTryMethod(ds,"DSNEPSetComputeMatrixFunction_C",(DS,DSNEPMatrixFunctionFn*,PetscCtx,PetscCtxDestroyFn*),(ds,fun,ctx,destroy));
1060:   PetscFunctionReturn(PETSC_SUCCESS);
1061: }

1063: static PetscErrorCode DSNEPGetComputeMatrixFunction_NEP(DS ds,DSNEPMatrixFunctionFn **fun,PetscCtxRt ctx,PetscCtxDestroyFn **destroy)
1064: {
1065:   DS_NEP *dsctx = (DS_NEP*)ds->data;

1067:   PetscFunctionBegin;
1068:   if (fun) *fun = dsctx->computematrix;
1069:   if (ctx) *(void**)ctx = dsctx->computematrixctx;
1070:   if (destroy) *destroy = dsctx->computematrixdestroy;
1071:   PetscFunctionReturn(PETSC_SUCCESS);
1072: }

1074: /*@
1075:    DSNEPGetComputeMatrixFunction - Returns the user-provided callback function
1076:    set in `DSNEPSetComputeMatrixFunction()`.

1078:    Not Collective

1080:    Input Parameter:
1081: .  ds  - the direct solver context

1083:    Output Parameters:
1084: +  fun     - the pointer to the user function
1085: .  ctx     - the context pointer
1086: -  destroy - a routine for destroying the context (may be `NULL`)

1088:    Level: developer

1090: .seealso: [](sec:ds), `DSNEP`, `DSNEPSetComputeMatrixFunction()`
1091: @*/
1092: PetscErrorCode DSNEPGetComputeMatrixFunction(DS ds,DSNEPMatrixFunctionFn **fun,PetscCtxRt ctx,PetscCtxDestroyFn **destroy)
1093: {
1094:   PetscFunctionBegin;
1096:   PetscUseMethod(ds,"DSNEPGetComputeMatrixFunction_C",(DS,DSNEPMatrixFunctionFn**,PetscCtxRt,PetscCtxDestroyFn**),(ds,fun,ctx,destroy));
1097:   PetscFunctionReturn(PETSC_SUCCESS);
1098: }

1100: static PetscErrorCode DSNEPSetRG_NEP(DS ds,RG rg)
1101: {
1102:   DS_NEP         *dsctx = (DS_NEP*)ds->data;

1104:   PetscFunctionBegin;
1105:   PetscCall(PetscObjectReference((PetscObject)rg));
1106:   PetscCall(RGDestroy(&dsctx->rg));
1107:   dsctx->rg = rg;
1108:   PetscFunctionReturn(PETSC_SUCCESS);
1109: }

1111: /*@
1112:    DSNEPSetRG - Associates a region object to the `DSNEP` solver.

1114:    Collective

1116:    Input Parameters:
1117: +  ds  - the direct solver context
1118: -  rg  - the region context

1120:    Notes:
1121:    The region is used only in the contour integral method, and
1122:    should enclose the wanted eigenvalues.

1124:    Level: developer

1126: .seealso: [](sec:ds), `DSNEP`, `DSNEPGetRG()`
1127: @*/
1128: PetscErrorCode DSNEPSetRG(DS ds,RG rg)
1129: {
1130:   PetscFunctionBegin;
1132:   if (rg) {
1134:     PetscCheckSameComm(ds,1,rg,2);
1135:   }
1136:   PetscTryMethod(ds,"DSNEPSetRG_C",(DS,RG),(ds,rg));
1137:   PetscFunctionReturn(PETSC_SUCCESS);
1138: }

1140: static PetscErrorCode DSNEPGetRG_NEP(DS ds,RG *rg)
1141: {
1142:   DS_NEP         *ctx = (DS_NEP*)ds->data;

1144:   PetscFunctionBegin;
1145:   if (!ctx->rg) {
1146:     PetscCall(RGCreate(PetscObjectComm((PetscObject)ds),&ctx->rg));
1147:     PetscCall(PetscObjectIncrementTabLevel((PetscObject)ctx->rg,(PetscObject)ds,1));
1148:     PetscCall(RGSetOptionsPrefix(ctx->rg,((PetscObject)ds)->prefix));
1149:     PetscCall(RGAppendOptionsPrefix(ctx->rg,"ds_nep_"));
1150:     PetscCall(PetscObjectSetOptions((PetscObject)ctx->rg,((PetscObject)ds)->options));
1151:   }
1152:   *rg = ctx->rg;
1153:   PetscFunctionReturn(PETSC_SUCCESS);
1154: }

1156: /*@
1157:    DSNEPGetRG - Obtain the region object associated to the `DSNEP` solver.

1159:    Collective

1161:    Input Parameter:
1162: .  ds  - the direct solver context

1164:    Output Parameter:
1165: .  rg  - the region context

1167:    Level: developer

1169: .seealso: [](sec:ds), `DSNEP`, `DSNEPSetRG()`
1170: @*/
1171: PetscErrorCode DSNEPGetRG(DS ds,RG *rg)
1172: {
1173:   PetscFunctionBegin;
1175:   PetscAssertPointer(rg,2);
1176:   PetscUseMethod(ds,"DSNEPGetRG_C",(DS,RG*),(ds,rg));
1177:   PetscFunctionReturn(PETSC_SUCCESS);
1178: }

1180: static PetscErrorCode DSSetFromOptions_NEP(DS ds,PetscOptionItems PetscOptionsObject)
1181: {
1182:   PetscInt       k;
1183:   PetscBool      flg;
1184: #if PetscDefined(USE_COMPLEX)
1185:   PetscReal      r;
1186:   PetscBool      flg1;
1187:   DS_NEP         *ctx = (DS_NEP*)ds->data;
1188: #endif

1190:   PetscFunctionBegin;
1191:   PetscOptionsHeadBegin(PetscOptionsObject,"DS NEP Options");

1193:     PetscCall(PetscOptionsInt("-ds_nep_minimality","Maximum minimality index","DSNEPSetMinimality",4,&k,&flg));
1194:     if (flg) PetscCall(DSNEPSetMinimality(ds,k));

1196:     PetscCall(PetscOptionsInt("-ds_nep_integration_points","Number of integration points","DSNEPSetIntegrationPoints",64,&k,&flg));
1197:     if (flg) PetscCall(DSNEPSetIntegrationPoints(ds,k));

1199:     PetscCall(PetscOptionsInt("-ds_nep_sampling_size","Number of sampling columns","DSNEPSetSamplingSize",0,&k,&flg));
1200:     if (flg) PetscCall(DSNEPSetSamplingSize(ds,k));

1202: #if PetscDefined(USE_COMPLEX)
1203:     r = ctx->rtol;
1204:     PetscCall(PetscOptionsReal("-ds_nep_refine_tol","Refinement tolerance","DSNEPSetRefine",ctx->rtol,&r,&flg1));
1205:     k = ctx->Nit;
1206:     PetscCall(PetscOptionsInt("-ds_nep_refine_its","Number of iterative refinement iterations","DSNEPSetRefine",ctx->Nit,&k,&flg));
1207:     if (flg1||flg) PetscCall(DSNEPSetRefine(ds,r,k));

1209:     if (ds->method==1) {
1210:       if (!ctx->rg) PetscCall(DSNEPGetRG(ds,&ctx->rg));
1211:       PetscCall(RGSetFromOptions(ctx->rg));
1212:     }
1213: #endif

1215:   PetscOptionsHeadEnd();
1216:   PetscFunctionReturn(PETSC_SUCCESS);
1217: }

1219: static PetscErrorCode DSDestroy_NEP(DS ds)
1220: {
1221:   DS_NEP         *ctx = (DS_NEP*)ds->data;
1222:   PetscInt       i;

1224:   PetscFunctionBegin;
1225:   for (i=0;i<ctx->nf;i++) PetscCall(FNDestroy(&ctx->f[i]));
1226:   PetscCall(RGDestroy(&ctx->rg));
1227:   PetscCall(PetscLayoutDestroy(&ctx->map));
1228:   if (ctx->computematrixdestroy) PetscCall((*ctx->computematrixdestroy)(&ctx->computematrixctx));
1229:   PetscCall(PetscFree(ds->data));
1230:   PetscCall(PetscObjectComposeFunction((PetscObject)ds,"DSNEPSetFN_C",NULL));
1231:   PetscCall(PetscObjectComposeFunction((PetscObject)ds,"DSNEPGetFN_C",NULL));
1232:   PetscCall(PetscObjectComposeFunction((PetscObject)ds,"DSNEPGetNumFN_C",NULL));
1233:   PetscCall(PetscObjectComposeFunction((PetscObject)ds,"DSNEPGetMinimality_C",NULL));
1234:   PetscCall(PetscObjectComposeFunction((PetscObject)ds,"DSNEPSetMinimality_C",NULL));
1235:   PetscCall(PetscObjectComposeFunction((PetscObject)ds,"DSNEPGetRefine_C",NULL));
1236:   PetscCall(PetscObjectComposeFunction((PetscObject)ds,"DSNEPSetRefine_C",NULL));
1237:   PetscCall(PetscObjectComposeFunction((PetscObject)ds,"DSNEPGetIntegrationPoints_C",NULL));
1238:   PetscCall(PetscObjectComposeFunction((PetscObject)ds,"DSNEPSetIntegrationPoints_C",NULL));
1239:   PetscCall(PetscObjectComposeFunction((PetscObject)ds,"DSNEPGetSamplingSize_C",NULL));
1240:   PetscCall(PetscObjectComposeFunction((PetscObject)ds,"DSNEPSetSamplingSize_C",NULL));
1241:   PetscCall(PetscObjectComposeFunction((PetscObject)ds,"DSNEPSetRG_C",NULL));
1242:   PetscCall(PetscObjectComposeFunction((PetscObject)ds,"DSNEPGetRG_C",NULL));
1243:   PetscCall(PetscObjectComposeFunction((PetscObject)ds,"DSNEPSetComputeMatrixFunction_C",NULL));
1244:   PetscCall(PetscObjectComposeFunction((PetscObject)ds,"DSNEPGetComputeMatrixFunction_C",NULL));
1245:   PetscFunctionReturn(PETSC_SUCCESS);
1246: }

1248: static PetscErrorCode DSMatGetSize_NEP(DS ds,DSMatType t,PetscInt *rows,PetscInt *cols)
1249: {
1250:   DS_NEP *ctx = (DS_NEP*)ds->data;

1252:   PetscFunctionBegin;
1253:   *rows = ds->n;
1254:   if (t==DS_MAT_Q || t==DS_MAT_Z || t==DS_MAT_U || t==DS_MAT_V) *rows *= ctx->max_mid;
1255:   *cols = ds->n;
1256:   if (t==DS_MAT_Q || t==DS_MAT_Z || t==DS_MAT_U || t==DS_MAT_V || t==DS_MAT_X || t==DS_MAT_Y) *cols *= ctx->max_mid;
1257:   PetscFunctionReturn(PETSC_SUCCESS);
1258: }

1260: /*MC
1261:    DSNEP - Dense Nonlinear Eigenvalue Problem.

1263:    Notes:
1264:    The problem is expressed as $T(\lambda)x = 0$, where $T(\lambda)$ is a
1265:    parameter-dependent matrix written as $T(\lambda) = \sum_i E_i f_i(\lambda)$.
1266:    The eigenvalues $\lambda$ are the arguments returned by `DSSolve()`.

1268:    The coefficient matrices $E_i$ are the extra matrices of the `DS`, and
1269:    the scalar functions $f_i$ are passed via `DSNEPSetFN()`. Optionally, a
1270:    callback function to fill the $E_i$ matrices can be set with
1271:    `DSNEPSetComputeMatrixFunction()`.

1273:    Used DS matrices:
1274: +  `DS_MAT_E0` to `DS_MAT_E9` - coefficient matrices of the split form of $T(\lambda)$
1275: .  `DS_MAT_X`  - eigenvectors
1276: .  `DS_MAT_A`  - (workspace) $T(\lambda)$ evaluated at a given $\lambda$ (SLP only)
1277: .  `DS_MAT_B`  - (workspace) $T'(\lambda)$ evaluated at a given $\lambda$ (SLP only)
1278: .  `DS_MAT_Q`  - (workspace) left Hankel matrix (contour only)
1279: .  `DS_MAT_Z`  - (workspace) right Hankel matrix (contour only)
1280: .  `DS_MAT_U`  - (workspace) left singular vectors (contour only)
1281: .  `DS_MAT_V`  - (workspace) right singular vectors (contour only)
1282: -  `DS_MAT_W`  - (workspace) auxiliary matrix of size $n\times n$

1284:    Implemented methods:
1285: +  0 - Successive Linear Problems (SLP), computes just one eigenpair
1286: -  1 - Contour integral, computes all eigenvalues inside a region

1288:    Level: beginner

1290: .seealso: [](sec:ds), `DSCreate()`, `DSSetType()`, `DSType`, `DSNEPSetFN()`, `DSNEPSetComputeMatrixFunction()`
1291: M*/
1292: SLEPC_EXTERN PetscErrorCode DSCreate_NEP(DS ds)
1293: {
1294:   DS_NEP         *ctx;

1296:   PetscFunctionBegin;
1297:   PetscCall(PetscNew(&ctx));
1298:   ds->data = (void*)ctx;
1299:   ctx->max_mid = 4;
1300:   ctx->nnod    = 64;
1301:   ctx->Nit     = 3;
1302:   ctx->rtol    = PETSC_MACHINE_EPSILON/PetscSqrtReal(PETSC_SQRT_MACHINE_EPSILON);

1304:   ds->ops->allocate       = DSAllocate_NEP;
1305:   ds->ops->setfromoptions = DSSetFromOptions_NEP;
1306:   ds->ops->view           = DSView_NEP;
1307:   ds->ops->vectors        = DSVectors_NEP;
1308:   ds->ops->solve[0]       = DSSolve_NEP_SLP;
1309: #if PetscDefined(USE_COMPLEX)
1310:   ds->ops->solve[1]       = DSSolve_NEP_Contour;
1311: #endif
1312:   ds->ops->sort           = DSSort_NEP;
1313: #if !PetscDefined(HAVE_MPIUNI)
1314:   ds->ops->synchronize    = DSSynchronize_NEP;
1315: #endif
1316:   ds->ops->destroy        = DSDestroy_NEP;
1317:   ds->ops->matgetsize     = DSMatGetSize_NEP;

1319:   PetscCall(PetscObjectComposeFunction((PetscObject)ds,"DSNEPSetFN_C",DSNEPSetFN_NEP));
1320:   PetscCall(PetscObjectComposeFunction((PetscObject)ds,"DSNEPGetFN_C",DSNEPGetFN_NEP));
1321:   PetscCall(PetscObjectComposeFunction((PetscObject)ds,"DSNEPGetNumFN_C",DSNEPGetNumFN_NEP));
1322:   PetscCall(PetscObjectComposeFunction((PetscObject)ds,"DSNEPGetMinimality_C",DSNEPGetMinimality_NEP));
1323:   PetscCall(PetscObjectComposeFunction((PetscObject)ds,"DSNEPSetMinimality_C",DSNEPSetMinimality_NEP));
1324:   PetscCall(PetscObjectComposeFunction((PetscObject)ds,"DSNEPGetRefine_C",DSNEPGetRefine_NEP));
1325:   PetscCall(PetscObjectComposeFunction((PetscObject)ds,"DSNEPSetRefine_C",DSNEPSetRefine_NEP));
1326:   PetscCall(PetscObjectComposeFunction((PetscObject)ds,"DSNEPGetIntegrationPoints_C",DSNEPGetIntegrationPoints_NEP));
1327:   PetscCall(PetscObjectComposeFunction((PetscObject)ds,"DSNEPSetIntegrationPoints_C",DSNEPSetIntegrationPoints_NEP));
1328:   PetscCall(PetscObjectComposeFunction((PetscObject)ds,"DSNEPGetSamplingSize_C",DSNEPGetSamplingSize_NEP));
1329:   PetscCall(PetscObjectComposeFunction((PetscObject)ds,"DSNEPSetSamplingSize_C",DSNEPSetSamplingSize_NEP));
1330:   PetscCall(PetscObjectComposeFunction((PetscObject)ds,"DSNEPSetRG_C",DSNEPSetRG_NEP));
1331:   PetscCall(PetscObjectComposeFunction((PetscObject)ds,"DSNEPGetRG_C",DSNEPGetRG_NEP));
1332:   PetscCall(PetscObjectComposeFunction((PetscObject)ds,"DSNEPSetComputeMatrixFunction_C",DSNEPSetComputeMatrixFunction_NEP));
1333:   PetscCall(PetscObjectComposeFunction((PetscObject)ds,"DSNEPGetComputeMatrixFunction_C",DSNEPGetComputeMatrixFunction_NEP));
1334:   PetscFunctionReturn(PETSC_SUCCESS);
1335: }