Date: 2023-11-27T17:18:58+00:00

Actual source code: ex20.c
slepc-3.20.1 2023-11-27
Report Typos and Errors

      1: /*
      2:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      3:    SLEPc - Scalable Library for Eigenvalue Problem Computations
      4:    Copyright (c) 2002-, Universitat Politecnica de Valencia, Spain

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

     11: static char help[] = "Simple 1-D nonlinear eigenproblem.\n\n"
     12:   "The command line options are:\n"
     13:   "  -n <n>, where <n> = number of grid subdivisions.\n"
     14:   "  -draw_sol, to draw the computed solution.\n\n";

     16: /*
     17:    Solve 1-D PDE
     18:             -u'' = lambda*u
     19:    on [0,1] subject to
     20:             u(0)=0, u'(1)=u(1)*lambda*kappa/(kappa-lambda)
     21: */

     23: #include <slepcnep.h>

     25: /*
     26:    User-defined routines
     27: */
     28: PetscErrorCode FormInitialGuess(Vec);
     29: PetscErrorCode FormFunction(NEP,PetscScalar,Mat,Mat,void*);
     30: PetscErrorCode FormJacobian(NEP,PetscScalar,Mat,void*);
     31: PetscErrorCode CheckSolution(PetscScalar,Vec,PetscReal*,void*);
     32: PetscErrorCode FixSign(Vec);

     34: /*
     35:    User-defined application context
     36: */
     37: typedef struct {
     38:   PetscScalar kappa;   /* ratio between stiffness of spring and attached mass */
     39:   PetscReal   h;       /* mesh spacing */
     40: } ApplicationCtx;

     42: int main(int argc,char **argv)
     43: {
     44:   NEP            nep;             /* nonlinear eigensolver context */
     45:   Vec            x;               /* eigenvector */
     46:   PetscScalar    lambda;          /* eigenvalue */
     47:   Mat            F,J;             /* Function and Jacobian matrices */
     48:   ApplicationCtx ctx;             /* user-defined context */
     49:   NEPType        type;
     50:   PetscInt       n=128,nev,i,its,maxit,nconv;
     51:   PetscReal      re,im,tol,norm,error;
     52:   PetscBool      draw_sol=PETSC_FALSE;

     54:   PetscFunctionBeginUser;
     55:   PetscCall(SlepcInitialize(&argc,&argv,(char*)0,help));
     56:   PetscCall(PetscOptionsGetInt(NULL,NULL,"-n",&n,NULL));
     57:   PetscCall(PetscPrintf(PETSC_COMM_WORLD,"\n1-D Nonlinear Eigenproblem, n=%" PetscInt_FMT "\n\n",n));
     58:   ctx.h = 1.0/(PetscReal)n;
     59:   ctx.kappa = 1.0;
     60:   PetscCall(PetscOptionsGetBool(NULL,NULL,"-draw_sol",&draw_sol,NULL));

     62:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
     63:      Create nonlinear eigensolver context
     64:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

     66:   PetscCall(NEPCreate(PETSC_COMM_WORLD,&nep));

     68:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
     69:      Create matrix data structure; set Function evaluation routine
     70:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

     72:   PetscCall(MatCreate(PETSC_COMM_WORLD,&F));
     73:   PetscCall(MatSetSizes(F,PETSC_DECIDE,PETSC_DECIDE,n,n));
     74:   PetscCall(MatSetFromOptions(F));
     75:   PetscCall(MatSeqAIJSetPreallocation(F,3,NULL));
     76:   PetscCall(MatMPIAIJSetPreallocation(F,3,NULL,1,NULL));
     77:   PetscCall(MatSetUp(F));

     79:   /*
     80:      Set Function matrix data structure and default Function evaluation
     81:      routine
     82:   */
     83:   PetscCall(NEPSetFunction(nep,F,F,FormFunction,&ctx));

     85:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
     86:      Create matrix data structure; set Jacobian evaluation routine
     87:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

     89:   PetscCall(MatCreate(PETSC_COMM_WORLD,&J));
     90:   PetscCall(MatSetSizes(J,PETSC_DECIDE,PETSC_DECIDE,n,n));
     91:   PetscCall(MatSetFromOptions(J));
     92:   PetscCall(MatSeqAIJSetPreallocation(J,3,NULL));
     93:   PetscCall(MatMPIAIJSetPreallocation(J,3,NULL,1,NULL));
     94:   PetscCall(MatSetUp(J));

     96:   /*
     97:      Set Jacobian matrix data structure and default Jacobian evaluation
     98:      routine
     99:   */
    100:   PetscCall(NEPSetJacobian(nep,J,FormJacobian,&ctx));

    102:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    103:      Customize nonlinear solver; set runtime options
    104:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

    106:   PetscCall(NEPSetTolerances(nep,1e-9,PETSC_DEFAULT));
    107:   PetscCall(NEPSetDimensions(nep,1,PETSC_DEFAULT,PETSC_DEFAULT));

    109:   /*
    110:      Set solver parameters at runtime
    111:   */
    112:   PetscCall(NEPSetFromOptions(nep));

    114:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    115:                       Initialize application
    116:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

    118:   /*
    119:      Evaluate initial guess
    120:   */
    121:   PetscCall(MatCreateVecs(F,&x,NULL));
    122:   PetscCall(FormInitialGuess(x));
    123:   PetscCall(NEPSetInitialSpace(nep,1,&x));

    125:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    126:                       Solve the eigensystem
    127:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

    129:   PetscCall(NEPSolve(nep));
    130:   PetscCall(NEPGetIterationNumber(nep,&its));
    131:   PetscCall(PetscPrintf(PETSC_COMM_WORLD," Number of NEP iterations = %" PetscInt_FMT "\n\n",its));

    133:   /*
    134:      Optional: Get some information from the solver and display it
    135:   */
    136:   PetscCall(NEPGetType(nep,&type));
    137:   PetscCall(PetscPrintf(PETSC_COMM_WORLD," Solution method: %s\n",type));
    138:   PetscCall(NEPGetDimensions(nep,&nev,NULL,NULL));
    139:   PetscCall(PetscPrintf(PETSC_COMM_WORLD," Number of requested eigenvalues: %" PetscInt_FMT "\n",nev));
    140:   PetscCall(NEPGetTolerances(nep,&tol,&maxit));
    141:   PetscCall(PetscPrintf(PETSC_COMM_WORLD," Stopping condition: tol=%g, maxit=%" PetscInt_FMT "\n",(double)tol,maxit));

    143:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    144:                     Display solution and clean up
    145:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

    147:   /*
    148:      Get number of converged approximate eigenpairs
    149:   */
    150:   PetscCall(NEPGetConverged(nep,&nconv));
    151:   PetscCall(PetscPrintf(PETSC_COMM_WORLD," Number of converged approximate eigenpairs: %" PetscInt_FMT "\n\n",nconv));

    153:   if (nconv>0) {
    154:     /*
    155:        Display eigenvalues and relative errors
    156:     */
    157:     PetscCall(PetscPrintf(PETSC_COMM_WORLD,
    158:          "           k              ||T(k)x||           error\n"
    159:          "   ----------------- ------------------ ------------------\n"));
    160:     for (i=0;i<nconv;i++) {
    161:       /*
    162:         Get converged eigenpairs (in this example they are always real)
    163:       */
    164:       PetscCall(NEPGetEigenpair(nep,i,&lambda,NULL,x,NULL));
    165:       PetscCall(FixSign(x));
    166:       /*
    167:          Compute residual norm and error
    168:       */
    169:       PetscCall(NEPComputeError(nep,i,NEP_ERROR_RELATIVE,&norm));
    170:       PetscCall(CheckSolution(lambda,x,&error,&ctx));

    172: #if defined(PETSC_USE_COMPLEX)
    173:       re = PetscRealPart(lambda);
    174:       im = PetscImaginaryPart(lambda);
    175: #else
    176:       re = lambda;
    177:       im = 0.0;
    178: #endif
    179:       if (im!=0.0) PetscCall(PetscPrintf(PETSC_COMM_WORLD," %9f%+9fi %12g     %12g\n",(double)re,(double)im,(double)norm,(double)error));
    180:       else PetscCall(PetscPrintf(PETSC_COMM_WORLD,"   %12f         %12g     %12g\n",(double)re,(double)norm,(double)error));
    181:       if (draw_sol) {
    182:         PetscCall(PetscViewerDrawSetPause(PETSC_VIEWER_DRAW_WORLD,-1));
    183:         PetscCall(VecView(x,PETSC_VIEWER_DRAW_WORLD));
    184:       }
    185:     }
    186:     PetscCall(PetscPrintf(PETSC_COMM_WORLD,"\n"));
    187:   }

    189:   PetscCall(NEPDestroy(&nep));
    190:   PetscCall(MatDestroy(&F));
    191:   PetscCall(MatDestroy(&J));
    192:   PetscCall(VecDestroy(&x));
    193:   PetscCall(SlepcFinalize());
    194:   return 0;
    195: }

    197: /* ------------------------------------------------------------------- */
    198: /*
    199:    FormInitialGuess - Computes initial guess.

    201:    Input/Output Parameter:
    202: .  x - the solution vector
    203: */
    204: PetscErrorCode FormInitialGuess(Vec x)
    205: {
    206:   PetscFunctionBeginUser;
    207:   PetscCall(VecSet(x,1.0));
    208:   PetscFunctionReturn(PETSC_SUCCESS);
    209: }

    211: /* ------------------------------------------------------------------- */
    212: /*
    213:    FormFunction - Computes Function matrix  T(lambda)

    215:    Input Parameters:
    216: .  nep    - the NEP context
    217: .  lambda - the scalar argument
    218: .  ctx    - optional user-defined context, as set by NEPSetFunction()

    220:    Output Parameters:
    221: .  fun - Function matrix
    222: .  B   - optionally different preconditioning matrix
    223: */
    224: PetscErrorCode FormFunction(NEP nep,PetscScalar lambda,Mat fun,Mat B,void *ctx)
    225: {
    226:   ApplicationCtx *user = (ApplicationCtx*)ctx;
    227:   PetscScalar    A[3],c,d;
    228:   PetscReal      h;
    229:   PetscInt       i,n,j[3],Istart,Iend;
    230:   PetscBool      FirstBlock=PETSC_FALSE,LastBlock=PETSC_FALSE;

    232:   PetscFunctionBeginUser;
    233:   /*
    234:      Compute Function entries and insert into matrix
    235:   */
    236:   PetscCall(MatGetSize(fun,&n,NULL));
    237:   PetscCall(MatGetOwnershipRange(fun,&Istart,&Iend));
    238:   if (Istart==0) FirstBlock=PETSC_TRUE;
    239:   if (Iend==n) LastBlock=PETSC_TRUE;
    240:   h = user->h;
    241:   c = user->kappa/(lambda-user->kappa);
    242:   d = n;

    244:   /*
    245:      Interior grid points
    246:   */
    247:   for (i=(FirstBlock? Istart+1: Istart);i<(LastBlock? Iend-1: Iend);i++) {
    248:     j[0] = i-1; j[1] = i; j[2] = i+1;
    249:     A[0] = A[2] = -d-lambda*h/6.0; A[1] = 2.0*(d-lambda*h/3.0);
    250:     PetscCall(MatSetValues(fun,1,&i,3,j,A,INSERT_VALUES));
    251:   }

    253:   /*
    254:      Boundary points
    255:   */
    256:   if (FirstBlock) {
    257:     i = 0;
    258:     j[0] = 0; j[1] = 1;
    259:     A[0] = 2.0*(d-lambda*h/3.0); A[1] = -d-lambda*h/6.0;
    260:     PetscCall(MatSetValues(fun,1,&i,2,j,A,INSERT_VALUES));
    261:   }

    263:   if (LastBlock) {
    264:     i = n-1;
    265:     j[0] = n-2; j[1] = n-1;
    266:     A[0] = -d-lambda*h/6.0; A[1] = d-lambda*h/3.0+lambda*c;
    267:     PetscCall(MatSetValues(fun,1,&i,2,j,A,INSERT_VALUES));
    268:   }

    270:   /*
    271:      Assemble matrix
    272:   */
    273:   PetscCall(MatAssemblyBegin(B,MAT_FINAL_ASSEMBLY));
    274:   PetscCall(MatAssemblyEnd(B,MAT_FINAL_ASSEMBLY));
    275:   if (fun != B) {
    276:     PetscCall(MatAssemblyBegin(fun,MAT_FINAL_ASSEMBLY));
    277:     PetscCall(MatAssemblyEnd(fun,MAT_FINAL_ASSEMBLY));
    278:   }
    279:   PetscFunctionReturn(PETSC_SUCCESS);
    280: }

    282: /* ------------------------------------------------------------------- */
    283: /*
    284:    FormJacobian - Computes Jacobian matrix  T'(lambda)

    286:    Input Parameters:
    287: .  nep    - the NEP context
    288: .  lambda - the scalar argument
    289: .  ctx    - optional user-defined context, as set by NEPSetJacobian()

    291:    Output Parameters:
    292: .  jac - Jacobian matrix
    293: */
    294: PetscErrorCode FormJacobian(NEP nep,PetscScalar lambda,Mat jac,void *ctx)
    295: {
    296:   ApplicationCtx *user = (ApplicationCtx*)ctx;
    297:   PetscScalar    A[3],c;
    298:   PetscReal      h;
    299:   PetscInt       i,n,j[3],Istart,Iend;
    300:   PetscBool      FirstBlock=PETSC_FALSE,LastBlock=PETSC_FALSE;

    302:   PetscFunctionBeginUser;
    303:   /*
    304:      Compute Jacobian entries and insert into matrix
    305:   */
    306:   PetscCall(MatGetSize(jac,&n,NULL));
    307:   PetscCall(MatGetOwnershipRange(jac,&Istart,&Iend));
    308:   if (Istart==0) FirstBlock=PETSC_TRUE;
    309:   if (Iend==n) LastBlock=PETSC_TRUE;
    310:   h = user->h;
    311:   c = user->kappa/(lambda-user->kappa);

    313:   /*
    314:      Interior grid points
    315:   */
    316:   for (i=(FirstBlock? Istart+1: Istart);i<(LastBlock? Iend-1: Iend);i++) {
    317:     j[0] = i-1; j[1] = i; j[2] = i+1;
    318:     A[0] = A[2] = -h/6.0; A[1] = -2.0*h/3.0;
    319:     PetscCall(MatSetValues(jac,1,&i,3,j,A,INSERT_VALUES));
    320:   }

    322:   /*
    323:      Boundary points
    324:   */
    325:   if (FirstBlock) {
    326:     i = 0;
    327:     j[0] = 0; j[1] = 1;
    328:     A[0] = -2.0*h/3.0; A[1] = -h/6.0;
    329:     PetscCall(MatSetValues(jac,1,&i,2,j,A,INSERT_VALUES));
    330:   }

    332:   if (LastBlock) {
    333:     i = n-1;
    334:     j[0] = n-2; j[1] = n-1;
    335:     A[0] = -h/6.0; A[1] = -h/3.0-c*c;
    336:     PetscCall(MatSetValues(jac,1,&i,2,j,A,INSERT_VALUES));
    337:   }

    339:   /*
    340:      Assemble matrix
    341:   */
    342:   PetscCall(MatAssemblyBegin(jac,MAT_FINAL_ASSEMBLY));
    343:   PetscCall(MatAssemblyEnd(jac,MAT_FINAL_ASSEMBLY));
    344:   PetscFunctionReturn(PETSC_SUCCESS);
    345: }

    347: /* ------------------------------------------------------------------- */
    348: /*
    349:    CheckSolution - Given a computed solution (lambda,x) check if it
    350:    satisfies the analytic solution.

    352:    Input Parameters:
    353: +  lambda - the computed eigenvalue
    354: -  y      - the computed eigenvector

    356:    Output Parameter:
    357: .  error - norm of difference between the computed and exact eigenvector
    358: */
    359: PetscErrorCode CheckSolution(PetscScalar lambda,Vec y,PetscReal *error,void *ctx)
    360: {
    361:   PetscScalar    *uu;
    362:   PetscInt       i,n,Istart,Iend;
    363:   PetscReal      nu,x;
    364:   Vec            u;
    365:   ApplicationCtx *user = (ApplicationCtx*)ctx;

    367:   PetscFunctionBeginUser;
    368:   nu = PetscSqrtReal(PetscRealPart(lambda));
    369:   PetscCall(VecDuplicate(y,&u));
    370:   PetscCall(VecGetSize(u,&n));
    371:   PetscCall(VecGetOwnershipRange(y,&Istart,&Iend));
    372:   PetscCall(VecGetArray(u,&uu));
    373:   for (i=Istart;i<Iend;i++) {
    374:     x = (i+1)*user->h;
    375:     uu[i-Istart] = PetscSinReal(nu*x);
    376:   }
    377:   PetscCall(VecRestoreArray(u,&uu));
    378:   PetscCall(VecNormalize(u,NULL));
    379:   PetscCall(VecAXPY(u,-1.0,y));
    380:   PetscCall(VecNorm(u,NORM_2,error));
    381:   PetscCall(VecDestroy(&u));
    382:   PetscFunctionReturn(PETSC_SUCCESS);
    383: }

    385: /* ------------------------------------------------------------------- */
    386: /*
    387:    FixSign - Force the eigenfunction to be real and positive, since
    388:    some eigensolvers may return the eigenvector multiplied by a
    389:    complex number of modulus one.

    391:    Input/Output Parameter:
    392: .  x - the computed vector
    393: */
    394: PetscErrorCode FixSign(Vec x)
    395: {
    396:   PetscMPIInt       rank;
    397:   PetscScalar       sign=0.0;
    398:   const PetscScalar *xx;

    400:   PetscFunctionBeginUser;
    401:   PetscCallMPI(MPI_Comm_rank(PETSC_COMM_WORLD,&rank));
    402:   if (!rank) {
    403:     PetscCall(VecGetArrayRead(x,&xx));
    404:     sign = *xx/PetscAbsScalar(*xx);
    405:     PetscCall(VecRestoreArrayRead(x,&xx));
    406:   }
    407:   PetscCallMPI(MPI_Bcast(&sign,1,MPIU_SCALAR,0,PETSC_COMM_WORLD));
    408:   PetscCall(VecScale(x,1.0/sign));
    409:   PetscFunctionReturn(PETSC_SUCCESS);
    410: }

    412: /*TEST

    414:    build:
    415:       requires: !single

    417:    test:
    418:       suffix: 1
    419:       args: -nep_target 4
    420:       filter: sed -e "s/[0-9]\.[0-9]*e-\([0-9]*\)/removed/g" -e "s/ Number of NEP iterations = \([0-9]*\)/ Number of NEP iterations = /"
    421:       requires: !single

    423:    testset:
    424:       args: -nep_type nleigs -nep_target 10 -nep_nev 4
    425:       test:
    426:          suffix: 2
    427:          filter: sed -e "s/[0-9]\.[0-9]*e-\([0-9]*\)/removed/g" -e "s/0\.\([0-9]*\)/removed/g" -e "s/ Number of NEP iterations = \([0-9]*\)/ Number of NEP iterations = /"
    428:          args: -rg_interval_endpoints 4,200
    429:          requires: !single !complex
    430:       test:
    431:          suffix: 2_complex
    432:          filter: sed -e "s/[+-]0.[0-9]*i//" -e "s/[0-9]\.[0-9]*e-\([0-9]*\)/removed/g" -e "s/0\.\([0-9]*\)/removed/g" -e "s/ Number of NEP iterations = \([0-9]*\)/ Number of NEP iterations = /"
    433:          output_file: output/ex20_2.out
    434:          args: -rg_interval_endpoints 4,200,-.1,.1 -nep_target_real
    435:          requires: !single complex

    437:    testset:
    438:       args: -nep_type nleigs -nep_target 10 -nep_nev 4 -nep_ncv 18 -nep_two_sided {{0 1}} -nep_nleigs_full_basis
    439:       test:
    440:          suffix: 3
    441:          filter: sed -e "s/[0-9]\.[0-9]*e-\([0-9]*\)/removed/g" -e "s/0\.\([0-9]*\)/removed/g" -e "s/ Number of NEP iterations = \([0-9]*\)/ Number of NEP iterations = /"
    442:          output_file: output/ex20_2.out
    443:          args: -rg_interval_endpoints 4,200
    444:          requires: !single !complex
    445:       test:
    446:          suffix: 3_complex
    447:          filter: sed -e "s/[+-]0.[0-9]*i//" -e "s/[0-9]\.[0-9]*e-\([0-9]*\)/removed/g" -e "s/0\.\([0-9]*\)/removed/g" -e "s/ Number of NEP iterations = \([0-9]*\)/ Number of NEP iterations = /"
    448:          output_file: output/ex20_2.out
    449:          args: -rg_interval_endpoints 4,200,-.1,.1
    450:          requires: !single complex

    452:    test:
    453:       suffix: 4
    454:       args: -n 256 -nep_nev 2 -nep_target 10
    455:       filter: sed -e "s/[+-]0\.0*i//g" -e "s/[0-9]\.[0-9]*e-\([0-9]*\)/removed/g" -e "s/ Number of NEP iterations = \([0-9]*\)/ Number of NEP iterations = /"
    456:       requires: !single

    458: TEST*/