LCOV - code coverage report
Current view: top level - eps/impls/krylov/krylovschur - ks-slice.c (source / functions) Hit Total Coverage
Test: SLEPc Lines: 850 919 92.5 %
Date: 2024-12-18 00:42:09 Functions: 21 21 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
       3             :    SLEPc - Scalable Library for Eigenvalue Problem Computations
       4             :    Copyright (c) 2002-, Universitat Politecnica de Valencia, Spain
       5             : 
       6             :    This file is part of SLEPc.
       7             :    SLEPc is distributed under a 2-clause BSD license (see LICENSE).
       8             :    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
       9             : */
      10             : /*
      11             :    SLEPc eigensolver: "krylovschur"
      12             : 
      13             :    Method: Krylov-Schur with spectrum slicing for symmetric eigenproblems
      14             : 
      15             :    References:
      16             : 
      17             :        [1] R.G. Grimes et al., "A shifted block Lanczos algorithm for
      18             :            solving sparse symmetric generalized eigenproblems", SIAM J.
      19             :            Matrix Anal. Appl. 15(1):228-272, 1994.
      20             : 
      21             :        [2] C. Campos and J.E. Roman, "Spectrum slicing strategies based
      22             :            on restarted Lanczos methods", Numer. Algor. 60(2):279-295,
      23             :            2012.
      24             : */
      25             : 
      26             : #include <slepc/private/epsimpl.h>
      27             : #include "krylovschur.h"
      28             : 
      29             : static PetscBool  cited = PETSC_FALSE;
      30             : static const char citation[] =
      31             :   "@Article{slepc-slice,\n"
      32             :   "   author = \"C. Campos and J. E. Roman\",\n"
      33             :   "   title = \"Strategies for spectrum slicing based on restarted {Lanczos} methods\",\n"
      34             :   "   journal = \"Numer. Algorithms\",\n"
      35             :   "   volume = \"60\",\n"
      36             :   "   number = \"2\",\n"
      37             :   "   pages = \"279--295\",\n"
      38             :   "   year = \"2012,\"\n"
      39             :   "   doi = \"https://doi.org/10.1007/s11075-012-9564-z\"\n"
      40             :   "}\n";
      41             : 
      42             : #define SLICE_PTOL PETSC_SQRT_MACHINE_EPSILON
      43             : 
      44          94 : static PetscErrorCode EPSSliceResetSR(EPS eps)
      45             : {
      46          94 :   EPS_KRYLOVSCHUR *ctx=(EPS_KRYLOVSCHUR*)eps->data;
      47          94 :   EPS_SR          sr=ctx->sr;
      48          94 :   EPS_shift       s;
      49             : 
      50          94 :   PetscFunctionBegin;
      51          94 :   if (sr) {
      52          36 :     if (ctx->npart>1) {
      53          14 :       PetscCall(BVDestroy(&sr->V));
      54          14 :       PetscCall(PetscFree4(sr->eigr,sr->eigi,sr->errest,sr->perm));
      55             :     }
      56             :     /* Reviewing list of shifts to free memory */
      57          36 :     s = sr->s0;
      58          36 :     if (s) {
      59          30 :       while (s->neighb[1]) {
      60          12 :         s = s->neighb[1];
      61          30 :         PetscCall(PetscFree(s->neighb[0]));
      62             :       }
      63          18 :       PetscCall(PetscFree(s));
      64             :     }
      65          36 :     PetscCall(PetscFree(sr));
      66             :   }
      67          94 :   ctx->sr = NULL;
      68          94 :   PetscFunctionReturn(PETSC_SUCCESS);
      69             : }
      70             : 
      71          72 : PetscErrorCode EPSReset_KrylovSchur_Slice(EPS eps)
      72             : {
      73          72 :   EPS_KRYLOVSCHUR *ctx=(EPS_KRYLOVSCHUR*)eps->data;
      74             : 
      75          72 :   PetscFunctionBegin;
      76          72 :   if (!ctx->global) PetscFunctionReturn(PETSC_SUCCESS);
      77             :   /* Reset auxiliary EPS */
      78          29 :   PetscCall(EPSSliceResetSR(ctx->eps));
      79          29 :   PetscCall(EPSReset(ctx->eps));
      80          29 :   PetscCall(EPSSliceResetSR(eps));
      81          29 :   PetscCall(PetscFree(ctx->inertias));
      82          29 :   PetscCall(PetscFree(ctx->shifts));
      83          29 :   PetscFunctionReturn(PETSC_SUCCESS);
      84             : }
      85             : 
      86          28 : PetscErrorCode EPSDestroy_KrylovSchur_Slice(EPS eps)
      87             : {
      88          28 :   EPS_KRYLOVSCHUR *ctx=(EPS_KRYLOVSCHUR*)eps->data;
      89             : 
      90          28 :   PetscFunctionBegin;
      91          28 :   if (!ctx->global) PetscFunctionReturn(PETSC_SUCCESS);
      92             :   /* Destroy auxiliary EPS */
      93          14 :   PetscCall(EPSReset_KrylovSchur_Slice(eps));
      94          14 :   PetscCall(EPSDestroy(&ctx->eps));
      95          14 :   if (ctx->npart>1) {
      96           5 :     PetscCall(PetscSubcommDestroy(&ctx->subc));
      97           5 :     if (ctx->commset) {
      98           5 :       PetscCallMPI(MPI_Comm_free(&ctx->commrank));
      99           5 :       ctx->commset = PETSC_FALSE;
     100             :     }
     101           5 :     PetscCall(ISDestroy(&ctx->isrow));
     102           5 :     PetscCall(ISDestroy(&ctx->iscol));
     103           5 :     PetscCall(MatDestroyMatrices(1,&ctx->submata));
     104           5 :     PetscCall(MatDestroyMatrices(1,&ctx->submatb));
     105             :   }
     106          14 :   PetscCall(PetscFree(ctx->subintervals));
     107          14 :   PetscCall(PetscFree(ctx->nconv_loc));
     108          14 :   PetscFunctionReturn(PETSC_SUCCESS);
     109             : }
     110             : 
     111             : /*
     112             :   EPSSliceAllocateSolution - Allocate memory storage for common variables such
     113             :   as eigenvalues and eigenvectors. The argument extra is used for methods
     114             :   that require a working basis slightly larger than ncv.
     115             : */
     116           7 : static PetscErrorCode EPSSliceAllocateSolution(EPS eps,PetscInt extra)
     117             : {
     118           7 :   EPS_KRYLOVSCHUR    *ctx=(EPS_KRYLOVSCHUR*)eps->data;
     119           7 :   PetscReal          eta;
     120           7 :   PetscInt           k;
     121           7 :   BVType             type;
     122           7 :   BVOrthogType       orthog_type;
     123           7 :   BVOrthogRefineType orthog_ref;
     124           7 :   BVOrthogBlockType  ob_type;
     125           7 :   Mat                matrix;
     126           7 :   Vec                t;
     127           7 :   EPS_SR             sr = ctx->sr;
     128             : 
     129           7 :   PetscFunctionBegin;
     130             :   /* allocate space for eigenvalues and friends */
     131           7 :   k = PetscMax(1,sr->numEigs);
     132           7 :   PetscCall(PetscFree4(sr->eigr,sr->eigi,sr->errest,sr->perm));
     133           7 :   PetscCall(PetscMalloc4(k,&sr->eigr,k,&sr->eigi,k,&sr->errest,k,&sr->perm));
     134             : 
     135             :   /* allocate sr->V and transfer options from eps->V */
     136           7 :   PetscCall(BVDestroy(&sr->V));
     137           7 :   PetscCall(BVCreate(PetscObjectComm((PetscObject)eps),&sr->V));
     138           7 :   if (!eps->V) PetscCall(EPSGetBV(eps,&eps->V));
     139           7 :   if (!((PetscObject)eps->V)->type_name) PetscCall(BVSetType(sr->V,BVMAT));
     140             :   else {
     141           7 :     PetscCall(BVGetType(eps->V,&type));
     142           7 :     PetscCall(BVSetType(sr->V,type));
     143             :   }
     144           7 :   PetscCall(STMatCreateVecsEmpty(eps->st,&t,NULL));
     145           7 :   PetscCall(BVSetSizesFromVec(sr->V,t,k));
     146           7 :   PetscCall(VecDestroy(&t));
     147           7 :   PetscCall(EPS_SetInnerProduct(eps));
     148           7 :   PetscCall(BVGetMatrix(eps->V,&matrix,NULL));
     149           7 :   PetscCall(BVSetMatrix(sr->V,matrix,PETSC_FALSE));
     150           7 :   PetscCall(BVGetOrthogonalization(eps->V,&orthog_type,&orthog_ref,&eta,&ob_type));
     151           7 :   PetscCall(BVSetOrthogonalization(sr->V,orthog_type,orthog_ref,eta,ob_type));
     152           7 :   PetscFunctionReturn(PETSC_SUCCESS);
     153             : }
     154             : 
     155          18 : static PetscErrorCode EPSSliceGetEPS(EPS eps)
     156             : {
     157          18 :   EPS_KRYLOVSCHUR    *ctx=(EPS_KRYLOVSCHUR*)eps->data,*ctx_local;
     158          18 :   BV                 V;
     159          18 :   BVType             type;
     160          18 :   PetscReal          eta;
     161          18 :   BVOrthogType       orthog_type;
     162          18 :   BVOrthogRefineType orthog_ref;
     163          18 :   BVOrthogBlockType  ob_type;
     164          18 :   PetscInt           i;
     165          18 :   PetscReal          h,a,b;
     166          18 :   PetscRandom        rand;
     167          18 :   EPS_SR             sr=ctx->sr;
     168             : 
     169          18 :   PetscFunctionBegin;
     170          18 :   if (!ctx->eps) PetscCall(EPSKrylovSchurGetChildEPS(eps,&ctx->eps));
     171             : 
     172             :   /* Determine subintervals */
     173          18 :   if (ctx->npart==1) {
     174          11 :     a = eps->inta; b = eps->intb;
     175             :   } else {
     176           7 :     if (!ctx->subintset) { /* uniform distribution if no set by user */
     177           3 :       PetscCheck(sr->hasEnd,PetscObjectComm((PetscObject)eps),PETSC_ERR_ARG_WRONG,"Global interval must be bounded for splitting it in uniform subintervals");
     178           3 :       h = (eps->intb-eps->inta)/ctx->npart;
     179           3 :       a = eps->inta+ctx->subc->color*h;
     180           3 :       b = (ctx->subc->color==ctx->npart-1)?eps->intb:eps->inta+(ctx->subc->color+1)*h;
     181           3 :       PetscCall(PetscFree(ctx->subintervals));
     182           3 :       PetscCall(PetscMalloc1(ctx->npart+1,&ctx->subintervals));
     183           9 :       for (i=0;i<ctx->npart;i++) ctx->subintervals[i] = eps->inta+h*i;
     184           3 :       ctx->subintervals[ctx->npart] = eps->intb;
     185             :     } else {
     186           4 :       a = ctx->subintervals[ctx->subc->color];
     187           4 :       b = ctx->subintervals[ctx->subc->color+1];
     188             :     }
     189             :   }
     190          18 :   PetscCall(EPSSetInterval(ctx->eps,a,b));
     191          18 :   PetscCall(EPSSetConvergenceTest(ctx->eps,eps->conv));
     192          18 :   PetscCall(EPSSetDimensions(ctx->eps,ctx->nev,ctx->ncv,ctx->mpd));
     193          18 :   PetscCall(EPSKrylovSchurSetLocking(ctx->eps,ctx->lock));
     194             : 
     195          18 :   ctx_local = (EPS_KRYLOVSCHUR*)ctx->eps->data;
     196          18 :   ctx_local->detect = ctx->detect;
     197             : 
     198             :   /* transfer options from eps->V */
     199          18 :   PetscCall(EPSGetBV(ctx->eps,&V));
     200          18 :   PetscCall(BVGetRandomContext(V,&rand));  /* make sure the random context is available when duplicating */
     201          18 :   if (!eps->V) PetscCall(EPSGetBV(eps,&eps->V));
     202          18 :   if (!((PetscObject)eps->V)->type_name) PetscCall(BVSetType(V,BVMAT));
     203             :   else {
     204          17 :     PetscCall(BVGetType(eps->V,&type));
     205          17 :     PetscCall(BVSetType(V,type));
     206             :   }
     207          18 :   PetscCall(BVGetOrthogonalization(eps->V,&orthog_type,&orthog_ref,&eta,&ob_type));
     208          18 :   PetscCall(BVSetOrthogonalization(V,orthog_type,orthog_ref,eta,ob_type));
     209             : 
     210          18 :   ctx->eps->which = eps->which;
     211          18 :   ctx->eps->max_it = eps->max_it;
     212          18 :   ctx->eps->tol = eps->tol;
     213          18 :   ctx->eps->purify = eps->purify;
     214          18 :   if (eps->tol==(PetscReal)PETSC_DETERMINE) eps->tol = SLEPC_DEFAULT_TOL;
     215          18 :   PetscCall(EPSSetProblemType(ctx->eps,eps->problem_type));
     216          18 :   PetscCall(EPSSetUp(ctx->eps));
     217          18 :   ctx->eps->nconv = 0;
     218          18 :   ctx->eps->its   = 0;
     219        1358 :   for (i=0;i<ctx->eps->ncv;i++) {
     220        1340 :     ctx->eps->eigr[i]   = 0.0;
     221        1340 :     ctx->eps->eigi[i]   = 0.0;
     222        1340 :     ctx->eps->errest[i] = 0.0;
     223             :   }
     224          18 :   PetscFunctionReturn(PETSC_SUCCESS);
     225             : }
     226             : 
     227          44 : static PetscErrorCode EPSSliceGetInertia(EPS eps,PetscReal shift,PetscInt *inertia,PetscInt *zeros)
     228             : {
     229          44 :   KSP            ksp,kspr;
     230          44 :   PC             pc;
     231          44 :   Mat            F;
     232          44 :   PetscReal      nzshift=shift;
     233          44 :   PetscBool      flg;
     234             : 
     235          44 :   PetscFunctionBegin;
     236          44 :   if (shift >= PETSC_MAX_REAL) { /* Right-open interval */
     237           0 :     if (inertia) *inertia = eps->n;
     238          44 :   } else if (shift <= PETSC_MIN_REAL) {
     239           1 :     if (inertia) *inertia = 0;
     240           1 :     if (zeros) *zeros = 0;
     241             :   } else {
     242             :     /* If the shift is zero, perturb it to a very small positive value.
     243             :        The goal is that the nonzero pattern is the same in all cases and reuse
     244             :        the symbolic factorizations */
     245          43 :     nzshift = (shift==0.0)? 10.0/PETSC_MAX_REAL: shift;
     246          43 :     PetscCall(STSetShift(eps->st,nzshift));
     247          43 :     PetscCall(STGetKSP(eps->st,&ksp));
     248          43 :     PetscCall(KSPGetPC(ksp,&pc));
     249          43 :     PetscCall(PetscObjectTypeCompare((PetscObject)pc,PCREDUNDANT,&flg));
     250          43 :     if (flg) {
     251           4 :       PetscCall(PCRedundantGetKSP(pc,&kspr));
     252           4 :       PetscCall(KSPGetPC(kspr,&pc));
     253             :     }
     254          43 :     PetscCall(PCFactorGetMatrix(pc,&F));
     255          43 :     PetscCall(MatGetInertia(F,inertia,zeros,NULL));
     256             :   }
     257          44 :   if (inertia) PetscCall(PetscInfo(eps,"Computed inertia at shift %g: %" PetscInt_FMT "\n",(double)nzshift,*inertia));
     258          44 :   PetscFunctionReturn(PETSC_SUCCESS);
     259             : }
     260             : 
     261             : /*
     262             :    Dummy backtransform operation
     263             :  */
     264          18 : static PetscErrorCode EPSBackTransform_Skip(EPS eps)
     265             : {
     266          18 :   PetscFunctionBegin;
     267          18 :   PetscFunctionReturn(PETSC_SUCCESS);
     268             : }
     269             : 
     270          36 : PetscErrorCode EPSSetUp_KrylovSchur_Slice(EPS eps)
     271             : {
     272          36 :   EPS_KRYLOVSCHUR *ctx = (EPS_KRYLOVSCHUR*)eps->data,*ctx_glob;
     273          36 :   EPS_SR          sr,sr_loc,sr_glob;
     274          36 :   PetscInt        nEigs,dssz=1,i,zeros=0,off=0,method,hiteig=0;
     275          36 :   PetscMPIInt     nproc,rank=0,aux;
     276          36 :   PetscReal       r;
     277          36 :   MPI_Request     req;
     278          36 :   Mat             A,B=NULL;
     279          36 :   DSParallelType  ptype;
     280          36 :   MPI_Comm        child;
     281             : 
     282          36 :   PetscFunctionBegin;
     283          36 :   if (eps->nev==0) eps->nev = 1;
     284          36 :   if (ctx->global) {
     285          18 :     EPSCheckHermitianDefiniteCondition(eps,PETSC_TRUE," with spectrum slicing");
     286          18 :     EPSCheckSinvertCayleyCondition(eps,PETSC_TRUE," with spectrum slicing");
     287          18 :     PetscCheck(eps->inta!=eps->intb,PetscObjectComm((PetscObject)eps),PETSC_ERR_SUP,"This solver does not support computing all eigenvalues unless you provide a computational interval with EPSSetInterval()");
     288          18 :     PetscCheck(eps->intb<PETSC_MAX_REAL || eps->inta>PETSC_MIN_REAL,PetscObjectComm((PetscObject)eps),PETSC_ERR_ARG_WRONG,"The defined computational interval should have at least one of their sides bounded");
     289          18 :     PetscCheck(eps->nds==0,PetscObjectComm((PetscObject)eps),PETSC_ERR_SUP,"Spectrum slicing not supported in combination with deflation space");
     290          18 :     EPSCheckUnsupportedCondition(eps,EPS_FEATURE_ARBITRARY | EPS_FEATURE_REGION | EPS_FEATURE_STOPPING,PETSC_TRUE," with spectrum slicing");
     291          18 :     EPSCheckIgnoredCondition(eps,EPS_FEATURE_BALANCE,PETSC_TRUE," with spectrum slicing");
     292          18 :     if (eps->tol==(PetscReal)PETSC_DETERMINE) {
     293             : #if defined(PETSC_USE_REAL_SINGLE)
     294             :       eps->tol = SLEPC_DEFAULT_TOL;
     295             : #else
     296             :       /* use tighter tolerance */
     297           9 :       eps->tol = SLEPC_DEFAULT_TOL*1e-2;
     298             : #endif
     299             :     }
     300          18 :     if (eps->max_it==PETSC_DETERMINE) eps->max_it = 100;
     301          18 :     if (ctx->nev==1) ctx->nev = PetscMin(40,eps->n);  /* nev not set, use default value */
     302          18 :     PetscCheck(eps->n<=10 || ctx->nev>=10,PetscObjectComm((PetscObject)eps),PETSC_ERR_ARG_WRONG,"nev cannot be less than 10 in spectrum slicing runs");
     303             :   }
     304          36 :   eps->ops->backtransform = EPSBackTransform_Skip;
     305             : 
     306             :   /* create spectrum slicing context and initialize it */
     307          36 :   PetscCall(EPSSliceResetSR(eps));
     308          36 :   PetscCall(PetscNew(&sr));
     309          36 :   ctx->sr = sr;
     310          36 :   sr->itsKs = 0;
     311          36 :   sr->nleap = 0;
     312          36 :   sr->nMAXCompl = eps->nev/4;
     313          36 :   sr->iterCompl = eps->max_it/4;
     314          36 :   sr->sPres = NULL;
     315          36 :   sr->nS = 0;
     316             : 
     317          36 :   if (ctx->npart==1 || ctx->global) {
     318             :     /* check presence of ends and finding direction */
     319          29 :     if ((eps->inta > PETSC_MIN_REAL && !(ctx->subintervals && ctx->subintervals[0]==ctx->subintervals[1])) || eps->intb >= PETSC_MAX_REAL) {
     320          27 :       sr->int0 = eps->inta;
     321          27 :       sr->int1 = eps->intb;
     322          27 :       sr->dir = 1;
     323          27 :       if (eps->intb >= PETSC_MAX_REAL) { /* Right-open interval */
     324           0 :         sr->hasEnd = PETSC_FALSE;
     325          27 :       } else sr->hasEnd = PETSC_TRUE;
     326             :     } else {
     327           2 :       sr->int0 = eps->intb;
     328           2 :       sr->int1 = eps->inta;
     329           2 :       sr->dir = -1;
     330           2 :       sr->hasEnd = PetscNot(eps->inta <= PETSC_MIN_REAL);
     331             :     }
     332             :   }
     333          36 :   if (ctx->global) {
     334          18 :     PetscCall(EPSSetDimensions_Default(eps,&ctx->nev,&ctx->ncv,&ctx->mpd));
     335             :     /* create subintervals and initialize auxiliary eps for slicing runs */
     336          18 :     PetscCall(EPSKrylovSchurGetChildEPS(eps,&ctx->eps));
     337             :     /* prevent computation of factorization in global eps */
     338          18 :     PetscCall(STSetTransform(eps->st,PETSC_FALSE));
     339          18 :     PetscCall(EPSSliceGetEPS(eps));
     340          18 :     sr_loc = ((EPS_KRYLOVSCHUR*)ctx->eps->data)->sr;
     341          18 :     if (ctx->npart>1) {
     342           7 :       PetscCall(PetscSubcommGetChild(ctx->subc,&child));
     343           7 :       if ((sr->dir>0&&ctx->subc->color==0)||(sr->dir<0&&ctx->subc->color==ctx->npart-1)) sr->inertia0 = sr_loc->inertia0;
     344           7 :       PetscCallMPI(MPI_Comm_rank(child,&rank));
     345           7 :       if (!rank) {
     346           6 :         PetscCall(PetscMPIIntCast((sr->dir>0)?0:ctx->npart-1,&aux));
     347          12 :         PetscCallMPI(MPI_Bcast(&sr->inertia0,1,MPIU_INT,aux,ctx->commrank));
     348             :       }
     349          14 :       PetscCallMPI(MPI_Bcast(&sr->inertia0,1,MPIU_INT,0,child));
     350           7 :       PetscCall(PetscFree(ctx->nconv_loc));
     351           7 :       PetscCall(PetscMalloc1(ctx->npart,&ctx->nconv_loc));
     352           7 :       PetscCallMPI(MPI_Comm_size(((PetscObject)eps)->comm,&nproc));
     353           7 :       if (sr->dir<0) off = 1;
     354           7 :       if (nproc%ctx->npart==0) { /* subcommunicators with the same size */
     355           4 :         PetscCall(PetscMPIIntCast(sr_loc->numEigs,&aux));
     356           8 :         PetscCallMPI(MPI_Allgather(&aux,1,MPI_INT,ctx->nconv_loc,1,MPI_INT,ctx->commrank));
     357           8 :         PetscCallMPI(MPI_Allgather(sr_loc->dir==sr->dir?&sr_loc->int0:&sr_loc->int1,1,MPIU_REAL,ctx->subintervals+off,1,MPIU_REAL,ctx->commrank));
     358             :       } else {
     359           3 :         PetscCallMPI(MPI_Comm_rank(child,&rank));
     360           3 :         if (!rank) {
     361           2 :           PetscCall(PetscMPIIntCast(sr_loc->numEigs,&aux));
     362           4 :           PetscCallMPI(MPI_Allgather(&aux,1,MPI_INT,ctx->nconv_loc,1,MPI_INT,ctx->commrank));
     363           4 :           PetscCallMPI(MPI_Allgather(sr_loc->dir==sr->dir?&sr_loc->int0:&sr_loc->int1,1,MPIU_REAL,ctx->subintervals+off,1,MPIU_REAL,ctx->commrank));
     364             :         }
     365           3 :         PetscCall(PetscMPIIntCast(ctx->npart,&aux));
     366           6 :         PetscCallMPI(MPI_Bcast(ctx->nconv_loc,aux,MPI_INT,0,child));
     367           6 :         PetscCallMPI(MPI_Bcast(ctx->subintervals+off,aux,MPIU_REAL,0,child));
     368             :       }
     369           7 :       nEigs = 0;
     370          21 :       for (i=0;i<ctx->npart;i++) nEigs += ctx->nconv_loc[i];
     371             :     } else {
     372          11 :       nEigs = sr_loc->numEigs;
     373          11 :       sr->inertia0 = sr_loc->inertia0;
     374          11 :       sr->dir = sr_loc->dir;
     375             :     }
     376          18 :     sr->inertia1 = sr->inertia0+sr->dir*nEigs;
     377          18 :     sr->numEigs = nEigs;
     378          18 :     eps->nev = nEigs;
     379          18 :     eps->ncv = nEigs;
     380          18 :     eps->mpd = nEigs;
     381             :   } else {
     382          18 :     ctx_glob = (EPS_KRYLOVSCHUR*)ctx->eps->data;
     383          18 :     sr_glob = ctx_glob->sr;
     384          18 :     if (ctx->npart>1) {
     385           7 :       sr->dir = sr_glob->dir;
     386           7 :       sr->int0 = (sr->dir==1)?eps->inta:eps->intb;
     387           7 :       sr->int1 = (sr->dir==1)?eps->intb:eps->inta;
     388           7 :       if ((sr->dir>0&&ctx->subc->color==ctx->npart-1)||(sr->dir<0&&ctx->subc->color==0)) sr->hasEnd = sr_glob->hasEnd;
     389           4 :       else sr->hasEnd = PETSC_TRUE;
     390             :     }
     391             :     /* sets first shift */
     392          36 :     PetscCall(STSetShift(eps->st,(sr->int0==0.0)?10.0/PETSC_MAX_REAL:sr->int0));
     393          18 :     PetscCall(STSetUp(eps->st));
     394             : 
     395             :     /* compute inertia0 */
     396          31 :     PetscCall(EPSSliceGetInertia(eps,sr->int0,&sr->inertia0,ctx->detect?&zeros:NULL));
     397             :     /* undocumented option to control what to do when an eigenvalue is found:
     398             :        - error out if it's the endpoint of the user-provided interval (or sub-interval)
     399             :        - if it's an endpoint computed internally:
     400             :           + if hiteig=0 error out
     401             :           + else if hiteig=1 the subgroup that hit the eigenvalue does nothing
     402             :           + otherwise the subgroup that hit the eigenvalue perturbs the shift and recomputes inertia
     403             :     */
     404          18 :     PetscCall(PetscOptionsGetInt(NULL,NULL,"-eps_krylovschur_hiteigenvalue",&hiteig,NULL));
     405          18 :     if (zeros) { /* error in factorization */
     406           0 :       PetscCheck(sr->int0!=ctx->eps->inta && sr->int0!=ctx->eps->intb,((PetscObject)eps)->comm,PETSC_ERR_USER,"Found singular matrix for the transformed problem in the interval endpoint");
     407           0 :       PetscCheck(!ctx_glob->subintset || hiteig,((PetscObject)eps)->comm,PETSC_ERR_USER,"Found singular matrix for the transformed problem in an interval endpoint defined by user");
     408           0 :       if (hiteig==1) { /* idle subgroup */
     409           0 :         sr->inertia0 = -1;
     410             :       } else { /* perturb shift */
     411           0 :         sr->int0 *= (1.0+SLICE_PTOL);
     412           0 :         PetscCall(EPSSliceGetInertia(eps,sr->int0,&sr->inertia0,&zeros));
     413           0 :         PetscCheck(zeros==0,((PetscObject)eps)->comm,PETSC_ERR_CONV_FAILED,"Inertia computation fails in %g",(double)sr->int1);
     414             :       }
     415             :     }
     416          18 :     if (ctx->npart>1) {
     417           7 :       PetscCall(PetscSubcommGetChild(ctx->subc,&child));
     418             :       /* inertia1 is received from neighbour */
     419           7 :       PetscCallMPI(MPI_Comm_rank(child,&rank));
     420           7 :       if (!rank) {
     421           6 :         if (sr->inertia0!=-1 && ((sr->dir>0 && ctx->subc->color>0) || (sr->dir<0 && ctx->subc->color<ctx->npart-1))) { /* send inertia0 to neighbour0 */
     422           3 :           PetscCall(PetscMPIIntCast(ctx->subc->color-sr->dir,&aux));
     423           3 :           PetscCallMPI(MPI_Isend(&sr->inertia0,1,MPIU_INT,aux,0,ctx->commrank,&req));
     424           3 :           PetscCallMPI(MPI_Isend(&sr->int0,1,MPIU_REAL,aux,0,ctx->commrank,&req));
     425             :         }
     426           6 :         if ((sr->dir>0 && ctx->subc->color<ctx->npart-1)|| (sr->dir<0 && ctx->subc->color>0)) { /* receive inertia1 from neighbour1 */
     427           3 :           PetscCall(PetscMPIIntCast(ctx->subc->color+sr->dir,&aux));
     428           3 :           PetscCallMPI(MPI_Recv(&sr->inertia1,1,MPIU_INT,aux,0,ctx->commrank,MPI_STATUS_IGNORE));
     429           3 :           PetscCallMPI(MPI_Recv(&sr->int1,1,MPIU_REAL,aux,0,ctx->commrank,MPI_STATUS_IGNORE));
     430             :         }
     431           6 :         if (sr->inertia0==-1 && !(sr->dir>0 && ctx->subc->color==ctx->npart-1) && !(sr->dir<0 && ctx->subc->color==0)) {
     432           0 :           sr->inertia0 = sr->inertia1; sr->int0 = sr->int1;
     433           0 :           PetscCall(PetscMPIIntCast(ctx->subc->color-sr->dir,&aux));
     434           0 :           PetscCallMPI(MPI_Isend(&sr->inertia0,1,MPIU_INT,aux,0,ctx->commrank,&req));
     435           0 :           PetscCallMPI(MPI_Isend(&sr->int0,1,MPIU_REAL,aux,0,ctx->commrank,&req));
     436             :         }
     437             :       }
     438           7 :       if ((sr->dir>0 && ctx->subc->color<ctx->npart-1)||(sr->dir<0 && ctx->subc->color>0)) {
     439           8 :         PetscCallMPI(MPI_Bcast(&sr->inertia1,1,MPIU_INT,0,child));
     440           8 :         PetscCallMPI(MPI_Bcast(&sr->int1,1,MPIU_REAL,0,child));
     441           3 :       } else sr_glob->inertia1 = sr->inertia1;
     442             :     }
     443             : 
     444             :     /* last process in eps comm computes inertia1 */
     445          18 :     if (ctx->npart==1 || ((sr->dir>0 && ctx->subc->color==ctx->npart-1) || (sr->dir<0 && ctx->subc->color==0))) {
     446          25 :       PetscCall(EPSSliceGetInertia(eps,sr->int1,&sr->inertia1,ctx->detect?&zeros:NULL));
     447          14 :       PetscCheck(zeros==0,((PetscObject)eps)->comm,PETSC_ERR_USER,"Found singular matrix for the transformed problem in an interval endpoint defined by user");
     448          14 :       if (!rank && sr->inertia0==-1) {
     449           0 :         sr->inertia0 = sr->inertia1; sr->int0 = sr->int1;
     450           0 :         PetscCall(PetscMPIIntCast(ctx->subc->color-sr->dir,&aux));
     451           0 :         PetscCallMPI(MPI_Isend(&sr->inertia0,1,MPIU_INT,aux,0,ctx->commrank,&req));
     452           0 :         PetscCallMPI(MPI_Isend(&sr->int0,1,MPIU_REAL,aux,0,ctx->commrank,&req));
     453             :       }
     454          14 :       if (sr->hasEnd) {
     455          13 :         sr->dir = -sr->dir; r = sr->int0; sr->int0 = sr->int1; sr->int1 = r;
     456          13 :         i = sr->inertia0; sr->inertia0 = sr->inertia1; sr->inertia1 = i;
     457             :       }
     458             :     }
     459             : 
     460             :     /* number of eigenvalues in interval */
     461          18 :     sr->numEigs = (sr->dir)*(sr->inertia1 - sr->inertia0);
     462          18 :     if (ctx->npart>1) {
     463             :       /* memory allocate for subinterval eigenpairs */
     464           7 :       PetscCall(EPSSliceAllocateSolution(eps,1));
     465             :     }
     466          18 :     dssz = eps->ncv+1;
     467          18 :     PetscCall(DSGetParallel(ctx->eps->ds,&ptype));
     468          18 :     PetscCall(DSSetParallel(eps->ds,ptype));
     469          18 :     PetscCall(DSGetMethod(ctx->eps->ds,&method));
     470          18 :     PetscCall(DSSetMethod(eps->ds,method));
     471             :   }
     472          36 :   PetscCall(DSSetType(eps->ds,DSHEP));
     473          36 :   PetscCall(DSSetCompact(eps->ds,PETSC_TRUE));
     474          36 :   PetscCall(DSAllocate(eps->ds,dssz));
     475             :   /* keep state of subcomm matrices to check that the user does not modify them */
     476          36 :   PetscCall(EPSGetOperators(eps,&A,&B));
     477          36 :   PetscCall(MatGetState(A,&ctx->Astate));
     478          36 :   PetscCall(PetscObjectGetId((PetscObject)A,&ctx->Aid));
     479          36 :   if (B) {
     480          28 :     PetscCall(MatGetState(B,&ctx->Bstate));
     481          28 :     PetscCall(PetscObjectGetId((PetscObject)B,&ctx->Bid));
     482             :   } else {
     483           8 :     ctx->Bstate=0;
     484           8 :     ctx->Bid=0;
     485             :   }
     486          36 :   PetscFunctionReturn(PETSC_SUCCESS);
     487             : }
     488             : 
     489           5 : static PetscErrorCode EPSSliceGatherEigenVectors(EPS eps)
     490             : {
     491           5 :   Vec             v,vg,v_loc;
     492           5 :   IS              is1,is2;
     493           5 :   VecScatter      vec_sc;
     494           5 :   EPS_KRYLOVSCHUR *ctx=(EPS_KRYLOVSCHUR*)eps->data;
     495           5 :   PetscInt        nloc,m0,n0,i,si,idx,*idx1,*idx2,j;
     496           5 :   PetscScalar     *array;
     497           5 :   EPS_SR          sr_loc;
     498           5 :   BV              V_loc;
     499             : 
     500           5 :   PetscFunctionBegin;
     501           5 :   sr_loc = ((EPS_KRYLOVSCHUR*)ctx->eps->data)->sr;
     502           5 :   V_loc = sr_loc->V;
     503             : 
     504             :   /* Gather parallel eigenvectors */
     505           5 :   PetscCall(BVGetColumn(eps->V,0,&v));
     506           5 :   PetscCall(VecGetOwnershipRange(v,&n0,&m0));
     507           5 :   PetscCall(BVRestoreColumn(eps->V,0,&v));
     508           5 :   PetscCall(BVGetColumn(ctx->eps->V,0,&v));
     509           5 :   PetscCall(VecGetLocalSize(v,&nloc));
     510           5 :   PetscCall(BVRestoreColumn(ctx->eps->V,0,&v));
     511           5 :   PetscCall(PetscMalloc2(m0-n0,&idx1,m0-n0,&idx2));
     512           5 :   PetscCall(VecCreateMPI(PetscObjectComm((PetscObject)eps),nloc,PETSC_DECIDE,&vg));
     513             :   idx = -1;
     514          15 :   for (si=0;si<ctx->npart;si++) {
     515          10 :     j = 0;
     516        6510 :     for (i=n0;i<m0;i++) {
     517        6500 :       idx1[j]   = i;
     518        6500 :       idx2[j++] = i+eps->n*si;
     519             :     }
     520          10 :     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)eps),(m0-n0),idx1,PETSC_COPY_VALUES,&is1));
     521          10 :     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)eps),(m0-n0),idx2,PETSC_COPY_VALUES,&is2));
     522          10 :     PetscCall(BVGetColumn(eps->V,0,&v));
     523          10 :     PetscCall(VecScatterCreate(v,is1,vg,is2,&vec_sc));
     524          10 :     PetscCall(BVRestoreColumn(eps->V,0,&v));
     525          10 :     PetscCall(ISDestroy(&is1));
     526          10 :     PetscCall(ISDestroy(&is2));
     527         168 :     for (i=0;i<ctx->nconv_loc[si];i++) {
     528         158 :       PetscCall(BVGetColumn(eps->V,++idx,&v));
     529         158 :       if (ctx->subc->color==si) {
     530          78 :         PetscCall(BVGetColumn(V_loc,i,&v_loc));
     531          78 :         PetscCall(VecGetArray(v_loc,&array));
     532          78 :         PetscCall(VecPlaceArray(vg,array));
     533             :       }
     534         158 :       PetscCall(VecScatterBegin(vec_sc,vg,v,INSERT_VALUES,SCATTER_REVERSE));
     535         158 :       PetscCall(VecScatterEnd(vec_sc,vg,v,INSERT_VALUES,SCATTER_REVERSE));
     536         158 :       if (ctx->subc->color==si) {
     537          78 :         PetscCall(VecResetArray(vg));
     538          78 :         PetscCall(VecRestoreArray(v_loc,&array));
     539          78 :         PetscCall(BVRestoreColumn(V_loc,i,&v_loc));
     540             :       }
     541         158 :       PetscCall(BVRestoreColumn(eps->V,idx,&v));
     542             :     }
     543          10 :     PetscCall(VecScatterDestroy(&vec_sc));
     544             :   }
     545           5 :   PetscCall(PetscFree2(idx1,idx2));
     546           5 :   PetscCall(VecDestroy(&vg));
     547           5 :   PetscFunctionReturn(PETSC_SUCCESS);
     548             : }
     549             : 
     550             : /*
     551             :   EPSComputeVectors_Slice - Recover Eigenvectors from subcomunicators
     552             :  */
     553          21 : PetscErrorCode EPSComputeVectors_Slice(EPS eps)
     554             : {
     555          21 :   EPS_KRYLOVSCHUR *ctx=(EPS_KRYLOVSCHUR*)eps->data;
     556             : 
     557          21 :   PetscFunctionBegin;
     558          21 :   if (ctx->global && ctx->npart>1) {
     559           5 :     PetscCall(EPSComputeVectors(ctx->eps));
     560           5 :     PetscCall(EPSSliceGatherEigenVectors(eps));
     561             :   }
     562          21 :   PetscFunctionReturn(PETSC_SUCCESS);
     563             : }
     564             : 
     565          18 : static PetscErrorCode EPSSliceGetInertias(EPS eps,PetscInt *n,PetscReal **shifts,PetscInt **inertias)
     566             : {
     567          18 :   EPS_KRYLOVSCHUR *ctx=(EPS_KRYLOVSCHUR*)eps->data;
     568          18 :   PetscInt        i=0,j,tmpi;
     569          18 :   PetscReal       v,tmpr;
     570          18 :   EPS_shift       s;
     571             : 
     572          18 :   PetscFunctionBegin;
     573          18 :   PetscCheck(eps->state,PetscObjectComm((PetscObject)eps),PETSC_ERR_ARG_WRONGSTATE,"Must call EPSSetUp() first");
     574          18 :   PetscCheck(ctx->sr,PetscObjectComm((PetscObject)eps),PETSC_ERR_ARG_WRONGSTATE,"Only available in interval computations, see EPSSetInterval()");
     575          18 :   if (!ctx->sr->s0) {  /* EPSSolve not called yet */
     576           0 :     *n = 2;
     577             :   } else {
     578          18 :     *n = 1;
     579          18 :     s = ctx->sr->s0;
     580          48 :     while (s) {
     581          30 :       (*n)++;
     582          30 :       s = s->neighb[1];
     583             :     }
     584             :   }
     585          18 :   PetscCall(PetscMalloc1(*n,shifts));
     586          18 :   PetscCall(PetscMalloc1(*n,inertias));
     587          18 :   if (!ctx->sr->s0) {  /* EPSSolve not called yet */
     588           0 :     (*shifts)[0]   = ctx->sr->int0;
     589           0 :     (*shifts)[1]   = ctx->sr->int1;
     590           0 :     (*inertias)[0] = ctx->sr->inertia0;
     591           0 :     (*inertias)[1] = ctx->sr->inertia1;
     592             :   } else {
     593             :     s = ctx->sr->s0;
     594          48 :     while (s) {
     595          30 :       (*shifts)[i]     = s->value;
     596          30 :       (*inertias)[i++] = s->inertia;
     597          30 :       s = s->neighb[1];
     598             :     }
     599          18 :     (*shifts)[i]   = ctx->sr->int1;
     600          18 :     (*inertias)[i] = ctx->sr->inertia1;
     601             :   }
     602             :   /* remove possible duplicate in last position */
     603          18 :   if ((*shifts)[(*n)-1]==(*shifts)[(*n)-2]) (*n)--;
     604             :   /* sort result */
     605          64 :   for (i=0;i<*n;i++) {
     606          46 :     v = (*shifts)[i];
     607          87 :     for (j=i+1;j<*n;j++) {
     608          41 :       if (v > (*shifts)[j]) {
     609          35 :         SlepcSwap((*shifts)[i],(*shifts)[j],tmpr);
     610          35 :         SlepcSwap((*inertias)[i],(*inertias)[j],tmpi);
     611          35 :         v = (*shifts)[i];
     612             :       }
     613             :     }
     614             :   }
     615          18 :   PetscFunctionReturn(PETSC_SUCCESS);
     616             : }
     617             : 
     618           7 : static PetscErrorCode EPSSliceGatherSolution(EPS eps)
     619             : {
     620           7 :   PetscMPIInt     rank,nproc,*disp,*ns_loc,aux;
     621           7 :   EPS_KRYLOVSCHUR *ctx=(EPS_KRYLOVSCHUR*)eps->data;
     622           7 :   PetscInt        i,idx,j,*perm_loc,off=0,*inertias_loc,ns;
     623           7 :   PetscScalar     *eigr_loc;
     624           7 :   EPS_SR          sr_loc;
     625           7 :   PetscReal       *shifts_loc;
     626           7 :   MPI_Comm        child;
     627             : 
     628           7 :   PetscFunctionBegin;
     629           7 :   eps->nconv = 0;
     630          21 :   for (i=0;i<ctx->npart;i++) eps->nconv += ctx->nconv_loc[i];
     631           7 :   sr_loc = ((EPS_KRYLOVSCHUR*)ctx->eps->data)->sr;
     632             : 
     633             :   /* Gather the shifts used and the inertias computed */
     634           7 :   PetscCall(EPSSliceGetInertias(ctx->eps,&ns,&shifts_loc,&inertias_loc));
     635           7 :   if (ctx->sr->dir>0 && shifts_loc[ns-1]==sr_loc->int1 && ctx->subc->color<ctx->npart-1) ns--;
     636           7 :   if (ctx->sr->dir<0 && shifts_loc[ns-1]==sr_loc->int0 && ctx->subc->color>0) {
     637           0 :     ns--;
     638           0 :     for (i=0;i<ns;i++) {
     639           0 :       inertias_loc[i] = inertias_loc[i+1];
     640           0 :       shifts_loc[i] = shifts_loc[i+1];
     641             :     }
     642             :   }
     643           7 :   PetscCall(PetscMalloc1(ctx->npart,&ns_loc));
     644           7 :   PetscCall(PetscSubcommGetChild(ctx->subc,&child));
     645           7 :   PetscCallMPI(MPI_Comm_rank(child,&rank));
     646           7 :   PetscCall(PetscMPIIntCast(ns,&aux));
     647          13 :   if (!rank) PetscCallMPI(MPI_Allgather(&aux,1,MPI_INT,ns_loc,1,MPI_INT,ctx->commrank));
     648           7 :   PetscCall(PetscMPIIntCast(ctx->npart,&aux));
     649          14 :   PetscCallMPI(MPI_Bcast(ns_loc,aux,MPI_INT,0,child));
     650           7 :   ctx->nshifts = 0;
     651          21 :   for (i=0;i<ctx->npart;i++) ctx->nshifts += ns_loc[i];
     652           7 :   PetscCall(PetscFree(ctx->inertias));
     653           7 :   PetscCall(PetscFree(ctx->shifts));
     654           7 :   PetscCall(PetscMalloc1(ctx->nshifts,&ctx->inertias));
     655           7 :   PetscCall(PetscMalloc1(ctx->nshifts,&ctx->shifts));
     656             : 
     657             :   /* Gather eigenvalues (same ranks have fully set of eigenvalues)*/
     658           7 :   eigr_loc = sr_loc->eigr;
     659           7 :   perm_loc = sr_loc->perm;
     660           7 :   PetscCallMPI(MPI_Comm_size(((PetscObject)eps)->comm,&nproc));
     661           7 :   PetscCall(PetscMalloc1(ctx->npart,&disp));
     662           7 :   disp[0] = 0;
     663          14 :   for (i=1;i<ctx->npart;i++) disp[i] = disp[i-1]+ctx->nconv_loc[i-1];
     664           7 :   if (nproc%ctx->npart==0) { /* subcommunicators with the same size */
     665           4 :     PetscCall(PetscMPIIntCast(sr_loc->numEigs,&aux));
     666           8 :     PetscCallMPI(MPI_Allgatherv(eigr_loc,aux,MPIU_SCALAR,eps->eigr,ctx->nconv_loc,disp,MPIU_SCALAR,ctx->commrank)); /* eigenvalues */
     667           8 :     PetscCallMPI(MPI_Allgatherv(perm_loc,aux,MPIU_INT,eps->perm,ctx->nconv_loc,disp,MPIU_INT,ctx->commrank)); /* perm */
     668           8 :     for (i=1;i<ctx->npart;i++) disp[i] = disp[i-1]+ns_loc[i-1];
     669           4 :     PetscCall(PetscMPIIntCast(ns,&aux));
     670           8 :     PetscCallMPI(MPI_Allgatherv(shifts_loc,aux,MPIU_REAL,ctx->shifts,ns_loc,disp,MPIU_REAL,ctx->commrank)); /* shifts */
     671           8 :     PetscCallMPI(MPI_Allgatherv(inertias_loc,aux,MPIU_INT,ctx->inertias,ns_loc,disp,MPIU_INT,ctx->commrank)); /* inertias */
     672          12 :     PetscCallMPI(MPIU_Allreduce(&sr_loc->itsKs,&eps->its,1,MPIU_INT,MPI_SUM,ctx->commrank));
     673             :   } else { /* subcommunicators with different size */
     674           3 :     if (!rank) {
     675           2 :       PetscCall(PetscMPIIntCast(sr_loc->numEigs,&aux));
     676           4 :       PetscCallMPI(MPI_Allgatherv(eigr_loc,aux,MPIU_SCALAR,eps->eigr,ctx->nconv_loc,disp,MPIU_SCALAR,ctx->commrank)); /* eigenvalues */
     677           4 :       PetscCallMPI(MPI_Allgatherv(perm_loc,aux,MPIU_INT,eps->perm,ctx->nconv_loc,disp,MPIU_INT,ctx->commrank)); /* perm */
     678           4 :       for (i=1;i<ctx->npart;i++) disp[i] = disp[i-1]+ns_loc[i-1];
     679           2 :       PetscCall(PetscMPIIntCast(ns,&aux));
     680           4 :       PetscCallMPI(MPI_Allgatherv(shifts_loc,aux,MPIU_REAL,ctx->shifts,ns_loc,disp,MPIU_REAL,ctx->commrank)); /* shifts */
     681           4 :       PetscCallMPI(MPI_Allgatherv(inertias_loc,aux,MPIU_INT,ctx->inertias,ns_loc,disp,MPIU_INT,ctx->commrank)); /* inertias */
     682           6 :       PetscCallMPI(MPIU_Allreduce(&sr_loc->itsKs,&eps->its,1,MPIU_INT,MPI_SUM,ctx->commrank));
     683             :     }
     684           3 :     PetscCall(PetscMPIIntCast(eps->nconv,&aux));
     685           6 :     PetscCallMPI(MPI_Bcast(eps->eigr,aux,MPIU_SCALAR,0,child));
     686           6 :     PetscCallMPI(MPI_Bcast(eps->perm,aux,MPIU_INT,0,child));
     687           3 :     PetscCall(PetscMPIIntCast(ctx->nshifts,&aux));
     688           6 :     PetscCallMPI(MPI_Bcast(ctx->shifts,aux,MPIU_REAL,0,child));
     689           6 :     PetscCallMPI(MPI_Bcast(ctx->inertias,aux,MPIU_INT,0,child));
     690           6 :     PetscCallMPI(MPI_Bcast(&eps->its,1,MPIU_INT,0,child));
     691             :   }
     692             :   /* Update global array eps->perm */
     693           7 :   idx = ctx->nconv_loc[0];
     694          14 :   for (i=1;i<ctx->npart;i++) {
     695           7 :     off += ctx->nconv_loc[i-1];
     696         169 :     for (j=0;j<ctx->nconv_loc[i];j++) eps->perm[idx++] += off;
     697             :   }
     698             : 
     699             :   /* Gather parallel eigenvectors */
     700           7 :   PetscCall(PetscFree(ns_loc));
     701           7 :   PetscCall(PetscFree(disp));
     702           7 :   PetscCall(PetscFree(shifts_loc));
     703           7 :   PetscCall(PetscFree(inertias_loc));
     704           7 :   PetscFunctionReturn(PETSC_SUCCESS);
     705             : }
     706             : 
     707             : /*
     708             :    Fills the fields of a shift structure
     709             : */
     710          30 : static PetscErrorCode EPSCreateShift(EPS eps,PetscReal val,EPS_shift neighb0,EPS_shift neighb1)
     711             : {
     712          30 :   EPS_shift       s,*pending2;
     713          30 :   PetscInt        i;
     714          30 :   EPS_SR          sr;
     715          30 :   EPS_KRYLOVSCHUR *ctx=(EPS_KRYLOVSCHUR*)eps->data;
     716             : 
     717          30 :   PetscFunctionBegin;
     718          30 :   sr = ctx->sr;
     719          30 :   if ((neighb0 && val==neighb0->value) || (neighb1 && val==neighb1->value)) {
     720           0 :     sr->nPend++;
     721           0 :     PetscFunctionReturn(PETSC_SUCCESS);
     722             :   }
     723          30 :   PetscCall(PetscNew(&s));
     724          30 :   s->value = val;
     725          30 :   s->neighb[0] = neighb0;
     726          30 :   if (neighb0) neighb0->neighb[1] = s;
     727          30 :   s->neighb[1] = neighb1;
     728          30 :   if (neighb1) neighb1->neighb[0] = s;
     729          30 :   s->comp[0] = PETSC_FALSE;
     730          30 :   s->comp[1] = PETSC_FALSE;
     731          30 :   s->index = -1;
     732          30 :   s->neigs = 0;
     733          30 :   s->nconv[0] = s->nconv[1] = 0;
     734          30 :   s->nsch[0] = s->nsch[1]=0;
     735             :   /* Inserts in the stack of pending shifts */
     736             :   /* If needed, the array is resized */
     737          30 :   if (sr->nPend >= sr->maxPend) {
     738           0 :     sr->maxPend *= 2;
     739           0 :     PetscCall(PetscMalloc1(sr->maxPend,&pending2));
     740           0 :     for (i=0;i<sr->nPend;i++) pending2[i] = sr->pending[i];
     741           0 :     PetscCall(PetscFree(sr->pending));
     742           0 :     sr->pending = pending2;
     743             :   }
     744          30 :   sr->pending[sr->nPend++]=s;
     745          30 :   PetscFunctionReturn(PETSC_SUCCESS);
     746             : }
     747             : 
     748             : /* Prepare for Rational Krylov update */
     749          12 : static PetscErrorCode EPSPrepareRational(EPS eps)
     750             : {
     751          12 :   EPS_KRYLOVSCHUR *ctx=(EPS_KRYLOVSCHUR*)eps->data;
     752          12 :   PetscInt        dir,i,k,ld,nv;
     753          12 :   PetscScalar     *A;
     754          12 :   EPS_SR          sr = ctx->sr;
     755          12 :   Vec             v;
     756             : 
     757          12 :   PetscFunctionBegin;
     758          12 :   PetscCall(DSGetLeadingDimension(eps->ds,&ld));
     759          12 :   dir = (sr->sPres->neighb[0] == sr->sPrev)?1:-1;
     760          12 :   dir*=sr->dir;
     761          12 :   k = 0;
     762         340 :   for (i=0;i<sr->nS;i++) {
     763         335 :     if (dir*PetscRealPart(sr->S[i])>0.0) {
     764         167 :       sr->S[k] = sr->S[i];
     765         167 :       sr->S[sr->nS+k] = sr->S[sr->nS+i];
     766         167 :       PetscCall(BVGetColumn(sr->Vnext,k,&v));
     767         167 :       PetscCall(BVCopyVec(eps->V,eps->nconv+i,v));
     768         167 :       PetscCall(BVRestoreColumn(sr->Vnext,k,&v));
     769         167 :       k++;
     770         167 :       if (k>=sr->nS/2) break;
     771             :     }
     772             :   }
     773             :   /* Copy to DS */
     774          12 :   PetscCall(DSSetCompact(eps->ds,PETSC_FALSE));  /* make sure DS_MAT_A is allocated */
     775          12 :   PetscCall(DSGetArray(eps->ds,DS_MAT_A,&A));
     776          12 :   PetscCall(PetscArrayzero(A,ld*ld));
     777         179 :   for (i=0;i<k;i++) {
     778         167 :     A[i*(1+ld)] = sr->S[i];
     779         167 :     A[k+i*ld] = sr->S[sr->nS+i];
     780             :   }
     781          12 :   sr->nS = k;
     782          12 :   PetscCall(DSRestoreArray(eps->ds,DS_MAT_A,&A));
     783          12 :   PetscCall(DSGetDimensions(eps->ds,&nv,NULL,NULL,NULL));
     784          12 :   PetscCall(DSSetDimensions(eps->ds,nv,0,k));
     785             :   /* Append u to V */
     786          12 :   PetscCall(BVGetColumn(sr->Vnext,sr->nS,&v));
     787          12 :   PetscCall(BVCopyVec(eps->V,sr->nv,v));
     788          12 :   PetscCall(BVRestoreColumn(sr->Vnext,sr->nS,&v));
     789          12 :   PetscFunctionReturn(PETSC_SUCCESS);
     790             : }
     791             : 
     792             : /* Provides next shift to be computed */
     793          30 : static PetscErrorCode EPSExtractShift(EPS eps)
     794             : {
     795          30 :   PetscInt        iner,zeros=0;
     796          30 :   EPS_KRYLOVSCHUR *ctx=(EPS_KRYLOVSCHUR*)eps->data;
     797          30 :   EPS_SR          sr;
     798          30 :   PetscReal       newShift,diam,ptol;
     799          30 :   EPS_shift       sPres;
     800             : 
     801          30 :   PetscFunctionBegin;
     802          30 :   sr = ctx->sr;
     803          30 :   if (sr->nPend > 0) {
     804          12 :     if (sr->sPres==sr->pending[sr->nPend-1]) {
     805           0 :       eps->reason = EPS_CONVERGED_ITERATING;
     806           0 :       eps->its = 0;
     807           0 :       sr->nPend--;
     808           0 :       sr->sPres->rep = PETSC_TRUE;
     809           0 :       PetscFunctionReturn(PETSC_SUCCESS);
     810             :     }
     811          12 :     sr->sPrev = sr->sPres;
     812          12 :     sr->sPres = sr->pending[--sr->nPend];
     813          12 :     sPres = sr->sPres;
     814          19 :     PetscCall(EPSSliceGetInertia(eps,sPres->value,&iner,ctx->detect?&zeros:NULL));
     815          12 :     if (zeros) {
     816           0 :       diam = PetscMin(PetscAbsReal(sPres->neighb[0]->value-sPres->value),PetscAbsReal(sPres->neighb[1]->value-sPres->value));
     817           0 :       ptol = PetscMin(SLICE_PTOL,diam/2);
     818           0 :       newShift = sPres->value*(1.0+ptol);
     819           0 :       if (sr->dir*(sPres->neighb[0] && newShift-sPres->neighb[0]->value) < 0) newShift = (sPres->value+sPres->neighb[0]->value)/2;
     820           0 :       else if (sPres->neighb[1] && sr->dir*(sPres->neighb[1]->value-newShift) < 0) newShift = (sPres->value+sPres->neighb[1]->value)/2;
     821           0 :       PetscCall(EPSSliceGetInertia(eps,newShift,&iner,&zeros));
     822           0 :       PetscCheck(zeros==0,((PetscObject)eps)->comm,PETSC_ERR_CONV_FAILED,"Inertia computation fails in %g",(double)newShift);
     823           0 :       sPres->value = newShift;
     824             :     }
     825          12 :     sr->sPres->inertia = iner;
     826          12 :     eps->target = sr->sPres->value;
     827          12 :     eps->reason = EPS_CONVERGED_ITERATING;
     828          12 :     eps->its = 0;
     829          18 :   } else sr->sPres = NULL;
     830          30 :   PetscFunctionReturn(PETSC_SUCCESS);
     831             : }
     832             : 
     833             : /*
     834             :    Symmetric KrylovSchur adapted to spectrum slicing:
     835             :    Allows searching an specific amount of eigenvalues in the subintervals left and right.
     836             :    Returns whether the search has succeeded
     837             : */
     838          30 : static PetscErrorCode EPSKrylovSchur_Slice(EPS eps)
     839             : {
     840          30 :   EPS_KRYLOVSCHUR *ctx=(EPS_KRYLOVSCHUR*)eps->data;
     841          30 :   PetscInt        i,k,l,ld,nv,*iwork,j,count0,count1,iterCompl=0,n0,n1;
     842          30 :   Mat             U,Op,T;
     843          30 :   PetscScalar     *Q,*A;
     844          30 :   PetscReal       *a,*b,beta,lambda;
     845          30 :   EPS_shift       sPres;
     846          30 :   PetscBool       breakdown,complIterating,sch0,sch1;
     847          30 :   EPS_SR          sr = ctx->sr;
     848             : 
     849          30 :   PetscFunctionBegin;
     850             :   /* Spectrum slicing data */
     851          30 :   sPres = sr->sPres;
     852          30 :   complIterating =PETSC_FALSE;
     853          30 :   sch1 = sch0 = PETSC_TRUE;
     854          30 :   PetscCall(DSGetLeadingDimension(eps->ds,&ld));
     855          30 :   PetscCall(PetscMalloc1(2*ld,&iwork));
     856          30 :   count0=0;count1=0; /* Found on both sides */
     857          30 :   if (!sPres->rep && sr->nS > 0 && (sPres->neighb[0] == sr->sPrev || sPres->neighb[1] == sr->sPrev)) {
     858             :     /* Rational Krylov */
     859          12 :     PetscCall(DSTranslateRKS(eps->ds,sr->sPrev->value-sPres->value));
     860          12 :     PetscCall(DSGetDimensions(eps->ds,NULL,NULL,&l,NULL));
     861          12 :     PetscCall(DSSetDimensions(eps->ds,l+1,0,0));
     862          12 :     PetscCall(BVSetActiveColumns(eps->V,0,l+1));
     863          12 :     PetscCall(DSGetMat(eps->ds,DS_MAT_Q,&U));
     864          12 :     PetscCall(BVMultInPlace(eps->V,U,0,l+1));
     865          12 :     PetscCall(DSRestoreMat(eps->ds,DS_MAT_Q,&U));
     866             :   } else {
     867             :     /* Get the starting Lanczos vector */
     868          18 :     PetscCall(EPSGetStartVector(eps,0,NULL));
     869          18 :     l = 0;
     870             :   }
     871             :   /* Restart loop */
     872          92 :   while (eps->reason == EPS_CONVERGED_ITERATING) {
     873          62 :     eps->its++; sr->itsKs++;
     874             :     /* Compute an nv-step Lanczos factorization */
     875          62 :     nv = PetscMin(eps->nconv+eps->mpd,eps->ncv);
     876          62 :     PetscCall(DSSetDimensions(eps->ds,nv,eps->nconv,eps->nconv+l));
     877          62 :     PetscCall(DSGetMat(eps->ds,DS_MAT_T,&T));
     878          62 :     PetscCall(STGetOperator(eps->st,&Op));
     879          62 :     PetscCall(BVMatLanczos(eps->V,Op,T,eps->nconv+l,&nv,&beta,&breakdown));
     880          62 :     PetscCall(STRestoreOperator(eps->st,&Op));
     881          62 :     sr->nv = nv;
     882          62 :     PetscCall(DSRestoreMat(eps->ds,DS_MAT_T,&T));
     883          62 :     PetscCall(DSSetDimensions(eps->ds,nv,eps->nconv,eps->nconv+l));
     884          62 :     if (l==0) PetscCall(DSSetState(eps->ds,DS_STATE_INTERMEDIATE));
     885          44 :     else PetscCall(DSSetState(eps->ds,DS_STATE_RAW));
     886          62 :     PetscCall(BVSetActiveColumns(eps->V,eps->nconv,nv));
     887             : 
     888             :     /* Solve projected problem and compute residual norm estimates */
     889          62 :     if (eps->its == 1 && l > 0) { /* After rational update, DS_MAT_A is available */
     890          12 :       PetscCall(DSGetArray(eps->ds,DS_MAT_A,&A));
     891          12 :       PetscCall(DSGetArrayReal(eps->ds,DS_MAT_T,&a));
     892          12 :       b = a + ld;
     893          12 :       k = eps->nconv+l;
     894          12 :       A[k*ld+k-1] = A[(k-1)*ld+k];
     895          12 :       A[k*ld+k] = a[k];
     896         693 :       for (j=k+1; j< nv; j++) {
     897         681 :         A[j*ld+j] = a[j];
     898         681 :         A[j*ld+j-1] = b[j-1] ;
     899         681 :         A[(j-1)*ld+j] = b[j-1];
     900             :       }
     901          12 :       PetscCall(DSRestoreArray(eps->ds,DS_MAT_A,&A));
     902          12 :       PetscCall(DSRestoreArrayReal(eps->ds,DS_MAT_T,&a));
     903          12 :       PetscCall(DSSolve(eps->ds,eps->eigr,NULL));
     904          12 :       PetscCall(DSSort(eps->ds,eps->eigr,NULL,NULL,NULL,NULL));
     905          12 :       PetscCall(DSSetCompact(eps->ds,PETSC_TRUE));
     906             :     } else { /* Restart */
     907          50 :       PetscCall(DSSolve(eps->ds,eps->eigr,NULL));
     908          50 :       PetscCall(DSSort(eps->ds,eps->eigr,NULL,NULL,NULL,NULL));
     909             :     }
     910          62 :     PetscCall(DSSynchronize(eps->ds,eps->eigr,NULL));
     911             : 
     912             :     /* Residual */
     913          62 :     PetscCall(EPSKrylovConvergence(eps,PETSC_TRUE,eps->nconv,nv-eps->nconv,beta,0.0,1.0,&k));
     914             :     /* Checking values obtained for completing */
     915        1949 :     for (i=0;i<k;i++) {
     916        1887 :       sr->back[i]=eps->eigr[i];
     917             :     }
     918          62 :     PetscCall(STBackTransform(eps->st,k,sr->back,eps->eigi));
     919             :     count0=count1=0;
     920        1949 :     for (i=0;i<k;i++) {
     921        1887 :       lambda = PetscRealPart(sr->back[i]);
     922        1887 :       if ((sr->dir*(sPres->value - lambda) > 0) && (sr->dir*(lambda - sPres->ext[0]) > 0)) count0++;
     923        1887 :       if ((sr->dir*(lambda - sPres->value) > 0) && (sr->dir*(sPres->ext[1] - lambda) > 0)) count1++;
     924             :     }
     925          62 :     if (k>eps->nev && eps->ncv-k<5) eps->reason = EPS_CONVERGED_TOL;
     926             :     else {
     927             :       /* Checks completion */
     928          62 :       if ((!sch0||count0 >= sPres->nsch[0]) && (!sch1 ||count1 >= sPres->nsch[1])) {
     929          18 :         eps->reason = EPS_CONVERGED_TOL;
     930             :       } else {
     931          44 :         if (!complIterating && eps->its >= eps->max_it) eps->reason = EPS_DIVERGED_ITS;
     932          44 :         if (complIterating) {
     933           5 :           if (--iterCompl <= 0) eps->reason = EPS_DIVERGED_ITS;
     934          39 :         } else if (k >= eps->nev) {
     935          13 :           n0 = sPres->nsch[0]-count0;
     936          13 :           n1 = sPres->nsch[1]-count1;
     937          13 :           if (sr->iterCompl>0 && ((n0>0 && n0<= sr->nMAXCompl)||(n1>0&&n1<=sr->nMAXCompl))) {
     938             :             /* Iterating for completion*/
     939           1 :             complIterating = PETSC_TRUE;
     940           1 :             if (n0 >sr->nMAXCompl)sch0 = PETSC_FALSE;
     941           1 :             if (n1 >sr->nMAXCompl)sch1 = PETSC_FALSE;
     942             :             iterCompl = sr->iterCompl;
     943          12 :           } else eps->reason = EPS_CONVERGED_TOL;
     944             :         }
     945             :       }
     946             :     }
     947             :     /* Update l */
     948          62 :     if (eps->reason == EPS_CONVERGED_ITERATING) l = PetscMax(1,(PetscInt)((nv-k)*ctx->keep));
     949          30 :     else l = nv-k;
     950          62 :     if (breakdown) l=0;
     951          62 :     if (!ctx->lock && l>0 && eps->reason == EPS_CONVERGED_ITERATING) { l += k; k = 0; } /* non-locking variant: reset no. of converged pairs */
     952             : 
     953          62 :     if (eps->reason == EPS_CONVERGED_ITERATING) {
     954          32 :       if (breakdown) {
     955             :         /* Start a new Lanczos factorization */
     956           0 :         PetscCall(PetscInfo(eps,"Breakdown in Krylov-Schur method (it=%" PetscInt_FMT " norm=%g)\n",eps->its,(double)beta));
     957           0 :         PetscCall(EPSGetStartVector(eps,k,&breakdown));
     958           0 :         if (breakdown) {
     959           0 :           eps->reason = EPS_DIVERGED_BREAKDOWN;
     960           0 :           PetscCall(PetscInfo(eps,"Unable to generate more start vectors\n"));
     961             :         }
     962             :       } else {
     963             :         /* Prepare the Rayleigh quotient for restart */
     964          32 :         PetscCall(DSGetArrayReal(eps->ds,DS_MAT_T,&a));
     965          32 :         PetscCall(DSGetArray(eps->ds,DS_MAT_Q,&Q));
     966          32 :         b = a + ld;
     967        1190 :         for (i=k;i<k+l;i++) {
     968        1158 :           a[i] = PetscRealPart(eps->eigr[i]);
     969        1158 :           b[i] = PetscRealPart(Q[nv-1+i*ld]*beta);
     970             :         }
     971          32 :         PetscCall(DSRestoreArrayReal(eps->ds,DS_MAT_T,&a));
     972          32 :         PetscCall(DSRestoreArray(eps->ds,DS_MAT_Q,&Q));
     973             :       }
     974             :     }
     975             :     /* Update the corresponding vectors V(:,idx) = V*Q(:,idx) */
     976          62 :     PetscCall(DSGetMat(eps->ds,DS_MAT_Q,&U));
     977          62 :     PetscCall(BVMultInPlace(eps->V,U,eps->nconv,k+l));
     978          62 :     PetscCall(DSRestoreMat(eps->ds,DS_MAT_Q,&U));
     979             : 
     980             :     /* Normalize u and append it to V */
     981          62 :     if (eps->reason == EPS_CONVERGED_ITERATING && !breakdown) PetscCall(BVCopyColumn(eps->V,nv,k+l));
     982          62 :     eps->nconv = k;
     983          62 :     if (eps->reason != EPS_CONVERGED_ITERATING) {
     984             :       /* Store approximated values for next shift */
     985          30 :       PetscCall(DSGetArray(eps->ds,DS_MAT_Q,&Q));
     986          30 :       sr->nS = l;
     987        1151 :       for (i=0;i<l;i++) {
     988        1121 :         sr->S[i] = eps->eigr[i+k];/* Diagonal elements */
     989        1121 :         sr->S[i+l] = Q[nv-1+(i+k)*ld]*beta; /* Out of diagonal elements */
     990             :       }
     991         122 :       PetscCall(DSRestoreArray(eps->ds,DS_MAT_Q,&Q));
     992             :     }
     993             :   }
     994             :   /* Check for completion */
     995        1109 :   for (i=0;i< eps->nconv; i++) {
     996        1079 :     if (sr->dir*PetscRealPart(eps->eigr[i])>0) sPres->nconv[1]++;
     997         571 :     else sPres->nconv[0]++;
     998             :   }
     999          30 :   sPres->comp[0] = PetscNot(count0 < sPres->nsch[0]);
    1000          30 :   sPres->comp[1] = PetscNot(count1 < sPres->nsch[1]);
    1001          42 :   PetscCall(PetscInfo(eps,"Lanczos: %" PetscInt_FMT " evals in [%g,%g]%s and %" PetscInt_FMT " evals in [%g,%g]%s\n",count0,(double)(sr->dir==1?sPres->ext[0]:sPres->value),(double)(sr->dir==1?sPres->value:sPres->ext[0]),sPres->comp[0]?"*":"",count1,(double)(sr->dir==1?sPres->value:sPres->ext[1]),(double)(sr->dir==1?sPres->ext[1]:sPres->value),sPres->comp[1]?"*":""));
    1002          30 :   PetscCheck(count0<=sPres->nsch[0] && count1<=sPres->nsch[1],PetscObjectComm((PetscObject)eps),PETSC_ERR_PLIB,"Mismatch between number of values found and information from inertia%s",ctx->detect?"":", consider using EPSKrylovSchurSetDetectZeros()");
    1003          30 :   PetscCall(PetscFree(iwork));
    1004          30 :   PetscFunctionReturn(PETSC_SUCCESS);
    1005             : }
    1006             : 
    1007             : /*
    1008             :   Obtains value of subsequent shift
    1009             : */
    1010          12 : static PetscErrorCode EPSGetNewShiftValue(EPS eps,PetscInt side,PetscReal *newS)
    1011             : {
    1012          12 :   PetscReal       lambda,d_prev;
    1013          12 :   PetscInt        i,idxP;
    1014          12 :   EPS_SR          sr;
    1015          12 :   EPS_shift       sPres,s;
    1016          12 :   EPS_KRYLOVSCHUR *ctx=(EPS_KRYLOVSCHUR*)eps->data;
    1017             : 
    1018          12 :   PetscFunctionBegin;
    1019          12 :   sr = ctx->sr;
    1020          12 :   sPres = sr->sPres;
    1021          12 :   if (sPres->neighb[side]) {
    1022             :     /* Completing a previous interval */
    1023           2 :     *newS = (sPres->value + sPres->neighb[side]->value)/2;
    1024           2 :     if (PetscAbsReal(sPres->value - *newS)/PetscAbsReal(sPres->value)<=100*PETSC_SQRT_MACHINE_EPSILON) *newS = sPres->value;
    1025             :   } else { /* (Only for side=1). Creating a new interval. */
    1026          10 :     if (sPres->neigs==0) {/* No value has been accepted*/
    1027           0 :       if (sPres->neighb[0]) {
    1028             :         /* Multiplying by 10 the previous distance */
    1029           0 :         *newS = sPres->value + 10*sr->dir*PetscAbsReal(sPres->value - sPres->neighb[0]->value);
    1030           0 :         sr->nleap++;
    1031             :         /* Stops when the interval is open and no values are found in the last 5 shifts (there might be infinite eigenvalues) */
    1032           0 :         PetscCheck(sr->hasEnd || sr->nleap<=5,PetscObjectComm((PetscObject)eps),PETSC_ERR_PLIB,"Unable to compute the wanted eigenvalues with open interval");
    1033             :       } else { /* First shift */
    1034           0 :         PetscCheck(eps->nconv!=0,PetscObjectComm((PetscObject)eps),PETSC_ERR_PLIB,"First shift renders no information");
    1035             :         /* Unaccepted values give information for next shift */
    1036             :         idxP=0;/* Number of values left from shift */
    1037           0 :         for (i=0;i<eps->nconv;i++) {
    1038           0 :           lambda = PetscRealPart(eps->eigr[i]);
    1039           0 :           if (sr->dir*(lambda - sPres->value) <0) idxP++;
    1040             :           else break;
    1041             :         }
    1042             :         /* Avoiding subtraction of eigenvalues (might be the same).*/
    1043           0 :         if (idxP>0) {
    1044           0 :           d_prev = PetscAbsReal(sPres->value - PetscRealPart(eps->eigr[0]))/(idxP+0.3);
    1045             :         } else {
    1046           0 :           d_prev = PetscAbsReal(sPres->value - PetscRealPart(eps->eigr[eps->nconv-1]))/(eps->nconv+0.3);
    1047             :         }
    1048           0 :         *newS = sPres->value + (sr->dir*d_prev*eps->nev)/2;
    1049             :       }
    1050             :     } else { /* Accepted values found */
    1051          10 :       sr->nleap = 0;
    1052             :       /* Average distance of values in previous subinterval */
    1053          10 :       s = sPres->neighb[0];
    1054          10 :       while (s && PetscAbs(s->inertia - sPres->inertia)==0) {
    1055           0 :         s = s->neighb[0];/* Looking for previous shifts with eigenvalues within */
    1056             :       }
    1057          10 :       if (s) {
    1058           0 :         d_prev = PetscAbsReal((sPres->value - s->value)/(sPres->inertia - s->inertia));
    1059             :       } else { /* First shift. Average distance obtained with values in this shift */
    1060             :         /* first shift might be too far from first wanted eigenvalue (no values found outside the interval)*/
    1061          10 :         if (sr->dir*(PetscRealPart(sr->eigr[0])-sPres->value)>0 && PetscAbsReal((PetscRealPart(sr->eigr[sr->indexEig-1]) - PetscRealPart(sr->eigr[0]))/PetscRealPart(sr->eigr[0])) > PetscSqrtReal(eps->tol)) {
    1062          10 :           d_prev =  PetscAbsReal((PetscRealPart(sr->eigr[sr->indexEig-1]) - PetscRealPart(sr->eigr[0])))/(sPres->neigs+0.3);
    1063             :         } else {
    1064           0 :           d_prev = PetscAbsReal(PetscRealPart(sr->eigr[sr->indexEig-1]) - sPres->value)/(sPres->neigs+0.3);
    1065             :         }
    1066             :       }
    1067             :       /* Average distance is used for next shift by adding it to value on the right or to shift */
    1068          10 :       if (sr->dir*(PetscRealPart(sr->eigr[sPres->index + sPres->neigs -1]) - sPres->value)>0) {
    1069          10 :         *newS = PetscRealPart(sr->eigr[sPres->index + sPres->neigs -1])+ (sr->dir*d_prev*eps->nev)/2;
    1070             :       } else { /* Last accepted value is on the left of shift. Adding to shift */
    1071           0 :         *newS = sPres->value + (sr->dir*d_prev*eps->nev)/2;
    1072             :       }
    1073             :     }
    1074             :     /* End of interval can not be surpassed */
    1075          10 :     if (sr->dir*(sr->int1 - *newS) < 0) *newS = sr->int1;
    1076             :   }/* of neighb[side]==null */
    1077          12 :   PetscFunctionReturn(PETSC_SUCCESS);
    1078             : }
    1079             : 
    1080             : /*
    1081             :   Function for sorting an array of real values
    1082             : */
    1083          60 : static PetscErrorCode sortRealEigenvalues(PetscScalar *r,PetscInt *perm,PetscInt nr,PetscBool prev,PetscInt dir)
    1084             : {
    1085          60 :   PetscReal re;
    1086          60 :   PetscInt  i,j,tmp;
    1087             : 
    1088          60 :   PetscFunctionBegin;
    1089        1139 :   if (!prev) for (i=0;i<nr;i++) perm[i] = i;
    1090             :   /* Insertion sort */
    1091        1852 :   for (i=1;i<nr;i++) {
    1092        1792 :     re = PetscRealPart(r[perm[i]]);
    1093        1792 :     j = i-1;
    1094       12224 :     while (j>=0 && dir*(re - PetscRealPart(r[perm[j]])) <= 0) {
    1095       10432 :       tmp = perm[j]; perm[j] = perm[j+1]; perm[j+1] = tmp; j--;
    1096             :     }
    1097             :   }
    1098          60 :   PetscFunctionReturn(PETSC_SUCCESS);
    1099             : }
    1100             : 
    1101             : /* Stores the pairs obtained since the last shift in the global arrays */
    1102          30 : static PetscErrorCode EPSStoreEigenpairs(EPS eps)
    1103             : {
    1104          30 :   EPS_KRYLOVSCHUR *ctx=(EPS_KRYLOVSCHUR*)eps->data;
    1105          30 :   PetscReal       lambda,err,norm;
    1106          30 :   PetscInt        i,count;
    1107          30 :   PetscBool       iscayley;
    1108          30 :   EPS_SR          sr = ctx->sr;
    1109          30 :   EPS_shift       sPres;
    1110          30 :   Vec             v,w;
    1111             : 
    1112          30 :   PetscFunctionBegin;
    1113          30 :   sPres = sr->sPres;
    1114          30 :   sPres->index = sr->indexEig;
    1115          30 :   count = sr->indexEig;
    1116             :   /* Back-transform */
    1117          30 :   PetscCall(STBackTransform(eps->st,eps->nconv,eps->eigr,eps->eigi));
    1118          30 :   PetscCall(PetscObjectTypeCompare((PetscObject)eps->st,STCAYLEY,&iscayley));
    1119             :   /* Sort eigenvalues */
    1120          30 :   PetscCall(sortRealEigenvalues(eps->eigr,eps->perm,eps->nconv,PETSC_FALSE,sr->dir));
    1121             :   /* Values stored in global array */
    1122        1109 :   for (i=0;i<eps->nconv;i++) {
    1123        1079 :     lambda = PetscRealPart(eps->eigr[eps->perm[i]]);
    1124        1079 :     err = eps->errest[eps->perm[i]];
    1125             : 
    1126        1079 :     if (sr->dir*(lambda - sPres->ext[0]) > 0 && (sr->dir)*(sPres->ext[1] - lambda) > 0) {/* Valid value */
    1127         496 :       PetscCheck(count<sr->numEigs,PetscObjectComm((PetscObject)eps),PETSC_ERR_PLIB,"Unexpected error in Spectrum Slicing");
    1128         496 :       sr->eigr[count] = lambda;
    1129         496 :       sr->errest[count] = err;
    1130             :       /* Explicit purification */
    1131         496 :       PetscCall(BVGetColumn(eps->V,eps->perm[i],&w));
    1132         496 :       if (eps->purify) {
    1133         419 :         PetscCall(BVGetColumn(sr->V,count,&v));
    1134         419 :         PetscCall(STApply(eps->st,w,v));
    1135         419 :         PetscCall(BVRestoreColumn(sr->V,count,&v));
    1136          77 :       } else PetscCall(BVInsertVec(sr->V,count,w));
    1137         496 :       PetscCall(BVRestoreColumn(eps->V,eps->perm[i],&w));
    1138         496 :       PetscCall(BVNormColumn(sr->V,count,NORM_2,&norm));
    1139         496 :       PetscCall(BVScaleColumn(sr->V,count,1.0/norm));
    1140         496 :       count++;
    1141             :     }
    1142             :   }
    1143          30 :   sPres->neigs = count - sr->indexEig;
    1144          30 :   sr->indexEig = count;
    1145             :   /* Global ordering array updating */
    1146          30 :   PetscCall(sortRealEigenvalues(sr->eigr,sr->perm,count,PETSC_TRUE,sr->dir));
    1147          30 :   PetscFunctionReturn(PETSC_SUCCESS);
    1148             : }
    1149             : 
    1150          30 : static PetscErrorCode EPSLookForDeflation(EPS eps)
    1151             : {
    1152          30 :   PetscReal       val;
    1153          30 :   PetscInt        i,count0=0,count1=0;
    1154          30 :   EPS_shift       sPres;
    1155          30 :   PetscInt        ini,fin,k,idx0,idx1;
    1156          30 :   EPS_SR          sr;
    1157          30 :   Vec             v;
    1158          30 :   EPS_KRYLOVSCHUR *ctx=(EPS_KRYLOVSCHUR*)eps->data;
    1159             : 
    1160          30 :   PetscFunctionBegin;
    1161          30 :   sr = ctx->sr;
    1162          30 :   sPres = sr->sPres;
    1163             : 
    1164          30 :   if (sPres->neighb[0]) ini = (sr->dir)*(sPres->neighb[0]->inertia - sr->inertia0);
    1165             :   else ini = 0;
    1166          30 :   fin = sr->indexEig;
    1167             :   /* Selection of ends for searching new values */
    1168          30 :   if (!sPres->neighb[0]) sPres->ext[0] = sr->int0;/* First shift */
    1169          12 :   else sPres->ext[0] = sPres->neighb[0]->value;
    1170          30 :   if (!sPres->neighb[1]) {
    1171          28 :     if (sr->hasEnd) sPres->ext[1] = sr->int1;
    1172           4 :     else sPres->ext[1] = (sr->dir > 0)?PETSC_MAX_REAL:PETSC_MIN_REAL;
    1173           2 :   } else sPres->ext[1] = sPres->neighb[1]->value;
    1174             :   /* Selection of values between right and left ends */
    1175         279 :   for (i=ini;i<fin;i++) {
    1176         251 :     val=PetscRealPart(sr->eigr[sr->perm[i]]);
    1177             :     /* Values to the right of left shift */
    1178         251 :     if (sr->dir*(val - sPres->ext[1]) < 0) {
    1179         249 :       if (sr->dir*(val - sPres->value) < 0) count0++;
    1180          20 :       else count1++;
    1181             :     } else break;
    1182             :   }
    1183             :   /* The number of values on each side are found */
    1184          30 :   if (sPres->neighb[0]) {
    1185          12 :     sPres->nsch[0] = (sr->dir)*(sPres->inertia - sPres->neighb[0]->inertia)-count0;
    1186          12 :     PetscCheck(sPres->nsch[0]>=0,PetscObjectComm((PetscObject)eps),PETSC_ERR_PLIB,"Mismatch between number of values found and information from inertia%s",ctx->detect?"":", consider using EPSKrylovSchurSetDetectZeros()");
    1187          18 :   } else sPres->nsch[0] = 0;
    1188             : 
    1189          30 :   if (sPres->neighb[1]) {
    1190           2 :     sPres->nsch[1] = (sr->dir)*(sPres->neighb[1]->inertia - sPres->inertia) - count1;
    1191           2 :     PetscCheck(sPres->nsch[1]>=0,PetscObjectComm((PetscObject)eps),PETSC_ERR_PLIB,"Mismatch between number of values found and information from inertia%s",ctx->detect?"":", consider using EPSKrylovSchurSetDetectZeros()");
    1192          28 :   } else sPres->nsch[1] = (sr->dir)*(sr->inertia1 - sPres->inertia);
    1193             : 
    1194             :   /* Completing vector of indexes for deflation */
    1195          30 :   idx0 = ini;
    1196          30 :   idx1 = ini+count0+count1;
    1197          30 :   k=0;
    1198         279 :   for (i=idx0;i<idx1;i++) sr->idxDef[k++]=sr->perm[i];
    1199          30 :   PetscCall(BVDuplicateResize(eps->V,k+eps->ncv+1,&sr->Vnext));
    1200          30 :   PetscCall(BVSetNumConstraints(sr->Vnext,k));
    1201         279 :   for (i=0;i<k;i++) {
    1202         249 :     PetscCall(BVGetColumn(sr->Vnext,-i-1,&v));
    1203         249 :     PetscCall(BVCopyVec(sr->V,sr->idxDef[i],v));
    1204         249 :     PetscCall(BVRestoreColumn(sr->Vnext,-i-1,&v));
    1205             :   }
    1206             : 
    1207             :   /* For rational Krylov */
    1208          30 :   if (!sr->sPres->rep && sr->nS>0 && (sr->sPrev == sr->sPres->neighb[0] || sr->sPrev == sr->sPres->neighb[1])) PetscCall(EPSPrepareRational(eps));
    1209          30 :   eps->nconv = 0;
    1210             :   /* Get rid of temporary Vnext */
    1211          30 :   PetscCall(BVDestroy(&eps->V));
    1212          30 :   eps->V = sr->Vnext;
    1213          30 :   sr->Vnext = NULL;
    1214          30 :   PetscFunctionReturn(PETSC_SUCCESS);
    1215             : }
    1216             : 
    1217          36 : PetscErrorCode EPSSolve_KrylovSchur_Slice(EPS eps)
    1218             : {
    1219          36 :   PetscInt         i,lds,ti;
    1220          36 :   PetscReal        newS;
    1221          36 :   EPS_KRYLOVSCHUR  *ctx=(EPS_KRYLOVSCHUR*)eps->data;
    1222          36 :   EPS_SR           sr=ctx->sr;
    1223          36 :   Mat              A,B=NULL;
    1224          36 :   PetscObjectState Astate,Bstate=0;
    1225          36 :   PetscObjectId    Aid,Bid=0;
    1226             : 
    1227          36 :   PetscFunctionBegin;
    1228          36 :   PetscCall(PetscCitationsRegister(citation,&cited));
    1229          36 :   if (ctx->global) {
    1230          18 :     PetscCall(EPSSolve_KrylovSchur_Slice(ctx->eps));
    1231          18 :     ctx->eps->state = EPS_STATE_SOLVED;
    1232          18 :     eps->reason = EPS_CONVERGED_TOL;
    1233          18 :     if (ctx->npart>1) {
    1234             :       /* Gather solution from subsolvers */
    1235           7 :       PetscCall(EPSSliceGatherSolution(eps));
    1236             :     } else {
    1237          11 :       eps->nconv = sr->numEigs;
    1238          11 :       eps->its   = ctx->eps->its;
    1239          11 :       PetscCall(PetscFree(ctx->inertias));
    1240          11 :       PetscCall(PetscFree(ctx->shifts));
    1241          11 :       PetscCall(EPSSliceGetInertias(ctx->eps,&ctx->nshifts,&ctx->shifts,&ctx->inertias));
    1242             :     }
    1243             :   } else {
    1244          18 :     if (ctx->npart==1) {
    1245          11 :       sr->eigr   = ctx->eps->eigr;
    1246          11 :       sr->eigi   = ctx->eps->eigi;
    1247          11 :       sr->perm   = ctx->eps->perm;
    1248          11 :       sr->errest = ctx->eps->errest;
    1249          11 :       sr->V      = ctx->eps->V;
    1250             :     }
    1251             :     /* Check that the user did not modify subcomm matrices */
    1252          18 :     PetscCall(EPSGetOperators(eps,&A,&B));
    1253          18 :     PetscCall(MatGetState(A,&Astate));
    1254          18 :     PetscCall(PetscObjectGetId((PetscObject)A,&Aid));
    1255          18 :     if (B) {
    1256          14 :       PetscCall(MatGetState(B,&Bstate));
    1257          14 :       PetscCall(PetscObjectGetId((PetscObject)B,&Bid));
    1258             :     }
    1259          18 :     PetscCheck(Astate==ctx->Astate && (!B || Bstate==ctx->Bstate) && Aid==ctx->Aid && (!B || Bid==ctx->Bid),PETSC_COMM_SELF,PETSC_ERR_ARG_WRONGSTATE,"Subcomm matrices have been modified by user");
    1260             :     /* Only with eigenvalues present in the interval ...*/
    1261          18 :     if (sr->numEigs==0) {
    1262           0 :       eps->reason = EPS_CONVERGED_TOL;
    1263           0 :       PetscFunctionReturn(PETSC_SUCCESS);
    1264             :     }
    1265             :     /* Array of pending shifts */
    1266          18 :     sr->maxPend = 100; /* Initial size */
    1267          18 :     sr->nPend = 0;
    1268          18 :     PetscCall(PetscMalloc1(sr->maxPend,&sr->pending));
    1269          18 :     PetscCall(EPSCreateShift(eps,sr->int0,NULL,NULL));
    1270             :     /* extract first shift */
    1271          18 :     sr->sPrev = NULL;
    1272          18 :     sr->sPres = sr->pending[--sr->nPend];
    1273          18 :     sr->sPres->inertia = sr->inertia0;
    1274          18 :     eps->target = sr->sPres->value;
    1275          18 :     sr->s0 = sr->sPres;
    1276          18 :     sr->indexEig = 0;
    1277             :     /* Memory reservation for auxiliary variables */
    1278          18 :     lds = PetscMin(eps->mpd,eps->ncv);
    1279          18 :     PetscCall(PetscCalloc1(lds*lds,&sr->S));
    1280          18 :     PetscCall(PetscMalloc1(eps->ncv,&sr->back));
    1281         514 :     for (i=0;i<sr->numEigs;i++) {
    1282         496 :       sr->eigr[i]   = 0.0;
    1283         496 :       sr->eigi[i]   = 0.0;
    1284         496 :       sr->errest[i] = 0.0;
    1285         496 :       sr->perm[i]   = i;
    1286             :     }
    1287             :     /* Vectors for deflation */
    1288          18 :     PetscCall(PetscMalloc1(sr->numEigs,&sr->idxDef));
    1289          18 :     sr->indexEig = 0;
    1290             :     /* Main loop */
    1291          18 :     while (sr->sPres) {
    1292             :       /* Search for deflation */
    1293          30 :       PetscCall(EPSLookForDeflation(eps));
    1294             :       /* KrylovSchur */
    1295          30 :       PetscCall(EPSKrylovSchur_Slice(eps));
    1296             : 
    1297          30 :       PetscCall(EPSStoreEigenpairs(eps));
    1298             :       /* Select new shift */
    1299          30 :       if (!sr->sPres->comp[1]) {
    1300          11 :         PetscCall(EPSGetNewShiftValue(eps,1,&newS));
    1301          11 :         PetscCall(EPSCreateShift(eps,newS,sr->sPres,sr->sPres->neighb[1]));
    1302             :       }
    1303          30 :       if (!sr->sPres->comp[0]) {
    1304             :         /* Completing earlier interval */
    1305           1 :         PetscCall(EPSGetNewShiftValue(eps,0,&newS));
    1306           1 :         PetscCall(EPSCreateShift(eps,newS,sr->sPres->neighb[0],sr->sPres));
    1307             :       }
    1308             :       /* Preparing for a new search of values */
    1309          48 :       PetscCall(EPSExtractShift(eps));
    1310             :     }
    1311             : 
    1312             :     /* Updating eps values prior to exit */
    1313          18 :     PetscCall(PetscFree(sr->S));
    1314          18 :     PetscCall(PetscFree(sr->idxDef));
    1315          18 :     PetscCall(PetscFree(sr->pending));
    1316          18 :     PetscCall(PetscFree(sr->back));
    1317          18 :     PetscCall(BVDuplicateResize(eps->V,eps->ncv+1,&sr->Vnext));
    1318          18 :     PetscCall(BVSetNumConstraints(sr->Vnext,0));
    1319          18 :     PetscCall(BVDestroy(&eps->V));
    1320          18 :     eps->V      = sr->Vnext;
    1321          18 :     eps->nconv  = sr->indexEig;
    1322          18 :     eps->reason = EPS_CONVERGED_TOL;
    1323          18 :     eps->its    = sr->itsKs;
    1324          18 :     eps->nds    = 0;
    1325          18 :     if (sr->dir<0) {
    1326         218 :       for (i=0;i<eps->nconv/2;i++) {
    1327         204 :         ti = sr->perm[i]; sr->perm[i] = sr->perm[eps->nconv-1-i]; sr->perm[eps->nconv-1-i] = ti;
    1328             :       }
    1329             :     }
    1330             :   }
    1331          36 :   PetscFunctionReturn(PETSC_SUCCESS);
    1332             : }

Generated by: LCOV version 1.14