Actual source code: ex20.c
slepc-main 2024-11-15
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,NULL,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));
78: /*
79: Set Function matrix data structure and default Function evaluation
80: routine
81: */
82: PetscCall(NEPSetFunction(nep,F,F,FormFunction,&ctx));
84: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
85: Create matrix data structure; set Jacobian evaluation routine
86: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
88: PetscCall(MatCreate(PETSC_COMM_WORLD,&J));
89: PetscCall(MatSetSizes(J,PETSC_DECIDE,PETSC_DECIDE,n,n));
90: PetscCall(MatSetFromOptions(J));
91: PetscCall(MatSeqAIJSetPreallocation(J,3,NULL));
92: PetscCall(MatMPIAIJSetPreallocation(J,3,NULL,1,NULL));
94: /*
95: Set Jacobian matrix data structure and default Jacobian evaluation
96: routine
97: */
98: PetscCall(NEPSetJacobian(nep,J,FormJacobian,&ctx));
100: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
101: Customize nonlinear solver; set runtime options
102: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
104: PetscCall(NEPSetTolerances(nep,1e-9,PETSC_CURRENT));
105: PetscCall(NEPSetDimensions(nep,1,PETSC_DETERMINE,PETSC_DETERMINE));
107: /*
108: Set solver parameters at runtime
109: */
110: PetscCall(NEPSetFromOptions(nep));
112: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
113: Initialize application
114: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
116: /*
117: Evaluate initial guess
118: */
119: PetscCall(MatCreateVecs(F,&x,NULL));
120: PetscCall(FormInitialGuess(x));
121: PetscCall(NEPSetInitialSpace(nep,1,&x));
123: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
124: Solve the eigensystem
125: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
127: PetscCall(NEPSolve(nep));
128: PetscCall(NEPGetIterationNumber(nep,&its));
129: PetscCall(PetscPrintf(PETSC_COMM_WORLD," Number of NEP iterations = %" PetscInt_FMT "\n\n",its));
131: /*
132: Optional: Get some information from the solver and display it
133: */
134: PetscCall(NEPGetType(nep,&type));
135: PetscCall(PetscPrintf(PETSC_COMM_WORLD," Solution method: %s\n",type));
136: PetscCall(NEPGetDimensions(nep,&nev,NULL,NULL));
137: PetscCall(PetscPrintf(PETSC_COMM_WORLD," Number of requested eigenvalues: %" PetscInt_FMT "\n",nev));
138: PetscCall(NEPGetTolerances(nep,&tol,&maxit));
139: PetscCall(PetscPrintf(PETSC_COMM_WORLD," Stopping condition: tol=%g, maxit=%" PetscInt_FMT "\n",(double)tol,maxit));
141: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
142: Display solution and clean up
143: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
145: /*
146: Get number of converged approximate eigenpairs
147: */
148: PetscCall(NEPGetConverged(nep,&nconv));
149: PetscCall(PetscPrintf(PETSC_COMM_WORLD," Number of converged approximate eigenpairs: %" PetscInt_FMT "\n\n",nconv));
151: if (nconv>0) {
152: /*
153: Display eigenvalues and relative errors
154: */
155: PetscCall(PetscPrintf(PETSC_COMM_WORLD,
156: " k ||T(k)x|| error\n"
157: " ----------------- ------------------ ------------------\n"));
158: for (i=0;i<nconv;i++) {
159: /*
160: Get converged eigenpairs (in this example they are always real)
161: */
162: PetscCall(NEPGetEigenpair(nep,i,&lambda,NULL,x,NULL));
163: PetscCall(FixSign(x));
164: /*
165: Compute residual norm and error
166: */
167: PetscCall(NEPComputeError(nep,i,NEP_ERROR_RELATIVE,&norm));
168: PetscCall(CheckSolution(lambda,x,&error,&ctx));
170: #if defined(PETSC_USE_COMPLEX)
171: re = PetscRealPart(lambda);
172: im = PetscImaginaryPart(lambda);
173: #else
174: re = lambda;
175: im = 0.0;
176: #endif
177: if (im!=0.0) PetscCall(PetscPrintf(PETSC_COMM_WORLD," %9f%+9fi %12g %12g\n",(double)re,(double)im,(double)norm,(double)error));
178: else PetscCall(PetscPrintf(PETSC_COMM_WORLD," %12f %12g %12g\n",(double)re,(double)norm,(double)error));
179: if (draw_sol) {
180: PetscCall(PetscViewerDrawSetPause(PETSC_VIEWER_DRAW_WORLD,-1));
181: PetscCall(VecView(x,PETSC_VIEWER_DRAW_WORLD));
182: }
183: }
184: PetscCall(PetscPrintf(PETSC_COMM_WORLD,"\n"));
185: }
187: PetscCall(NEPDestroy(&nep));
188: PetscCall(MatDestroy(&F));
189: PetscCall(MatDestroy(&J));
190: PetscCall(VecDestroy(&x));
191: PetscCall(SlepcFinalize());
192: return 0;
193: }
195: /* ------------------------------------------------------------------- */
196: /*
197: FormInitialGuess - Computes initial guess.
199: Input/Output Parameter:
200: . x - the solution vector
201: */
202: PetscErrorCode FormInitialGuess(Vec x)
203: {
204: PetscFunctionBeginUser;
205: PetscCall(VecSet(x,1.0));
206: PetscFunctionReturn(PETSC_SUCCESS);
207: }
209: /* ------------------------------------------------------------------- */
210: /*
211: FormFunction - Computes Function matrix T(lambda)
213: Input Parameters:
214: . nep - the NEP context
215: . lambda - the scalar argument
216: . ctx - optional user-defined context, as set by NEPSetFunction()
218: Output Parameters:
219: . fun - Function matrix
220: . B - optionally different preconditioning matrix
221: */
222: PetscErrorCode FormFunction(NEP nep,PetscScalar lambda,Mat fun,Mat B,void *ctx)
223: {
224: ApplicationCtx *user = (ApplicationCtx*)ctx;
225: PetscScalar A[3],c,d;
226: PetscReal h;
227: PetscInt i,n,j[3],Istart,Iend;
228: PetscBool FirstBlock=PETSC_FALSE,LastBlock=PETSC_FALSE;
230: PetscFunctionBeginUser;
231: /*
232: Compute Function entries and insert into matrix
233: */
234: PetscCall(MatGetSize(fun,&n,NULL));
235: PetscCall(MatGetOwnershipRange(fun,&Istart,&Iend));
236: if (Istart==0) FirstBlock=PETSC_TRUE;
237: if (Iend==n) LastBlock=PETSC_TRUE;
238: h = user->h;
239: c = user->kappa/(lambda-user->kappa);
240: d = n;
242: /*
243: Interior grid points
244: */
245: for (i=(FirstBlock? Istart+1: Istart);i<(LastBlock? Iend-1: Iend);i++) {
246: j[0] = i-1; j[1] = i; j[2] = i+1;
247: A[0] = A[2] = -d-lambda*h/6.0; A[1] = 2.0*(d-lambda*h/3.0);
248: PetscCall(MatSetValues(fun,1,&i,3,j,A,INSERT_VALUES));
249: }
251: /*
252: Boundary points
253: */
254: if (FirstBlock) {
255: i = 0;
256: j[0] = 0; j[1] = 1;
257: A[0] = 2.0*(d-lambda*h/3.0); A[1] = -d-lambda*h/6.0;
258: PetscCall(MatSetValues(fun,1,&i,2,j,A,INSERT_VALUES));
259: }
261: if (LastBlock) {
262: i = n-1;
263: j[0] = n-2; j[1] = n-1;
264: A[0] = -d-lambda*h/6.0; A[1] = d-lambda*h/3.0+lambda*c;
265: PetscCall(MatSetValues(fun,1,&i,2,j,A,INSERT_VALUES));
266: }
268: /*
269: Assemble matrix
270: */
271: PetscCall(MatAssemblyBegin(B,MAT_FINAL_ASSEMBLY));
272: PetscCall(MatAssemblyEnd(B,MAT_FINAL_ASSEMBLY));
273: if (fun != B) {
274: PetscCall(MatAssemblyBegin(fun,MAT_FINAL_ASSEMBLY));
275: PetscCall(MatAssemblyEnd(fun,MAT_FINAL_ASSEMBLY));
276: }
277: PetscFunctionReturn(PETSC_SUCCESS);
278: }
280: /* ------------------------------------------------------------------- */
281: /*
282: FormJacobian - Computes Jacobian matrix T'(lambda)
284: Input Parameters:
285: . nep - the NEP context
286: . lambda - the scalar argument
287: . ctx - optional user-defined context, as set by NEPSetJacobian()
289: Output Parameters:
290: . jac - Jacobian matrix
291: */
292: PetscErrorCode FormJacobian(NEP nep,PetscScalar lambda,Mat jac,void *ctx)
293: {
294: ApplicationCtx *user = (ApplicationCtx*)ctx;
295: PetscScalar A[3],c;
296: PetscReal h;
297: PetscInt i,n,j[3],Istart,Iend;
298: PetscBool FirstBlock=PETSC_FALSE,LastBlock=PETSC_FALSE;
300: PetscFunctionBeginUser;
301: /*
302: Compute Jacobian entries and insert into matrix
303: */
304: PetscCall(MatGetSize(jac,&n,NULL));
305: PetscCall(MatGetOwnershipRange(jac,&Istart,&Iend));
306: if (Istart==0) FirstBlock=PETSC_TRUE;
307: if (Iend==n) LastBlock=PETSC_TRUE;
308: h = user->h;
309: c = user->kappa/(lambda-user->kappa);
311: /*
312: Interior grid points
313: */
314: for (i=(FirstBlock? Istart+1: Istart);i<(LastBlock? Iend-1: Iend);i++) {
315: j[0] = i-1; j[1] = i; j[2] = i+1;
316: A[0] = A[2] = -h/6.0; A[1] = -2.0*h/3.0;
317: PetscCall(MatSetValues(jac,1,&i,3,j,A,INSERT_VALUES));
318: }
320: /*
321: Boundary points
322: */
323: if (FirstBlock) {
324: i = 0;
325: j[0] = 0; j[1] = 1;
326: A[0] = -2.0*h/3.0; A[1] = -h/6.0;
327: PetscCall(MatSetValues(jac,1,&i,2,j,A,INSERT_VALUES));
328: }
330: if (LastBlock) {
331: i = n-1;
332: j[0] = n-2; j[1] = n-1;
333: A[0] = -h/6.0; A[1] = -h/3.0-c*c;
334: PetscCall(MatSetValues(jac,1,&i,2,j,A,INSERT_VALUES));
335: }
337: /*
338: Assemble matrix
339: */
340: PetscCall(MatAssemblyBegin(jac,MAT_FINAL_ASSEMBLY));
341: PetscCall(MatAssemblyEnd(jac,MAT_FINAL_ASSEMBLY));
342: PetscFunctionReturn(PETSC_SUCCESS);
343: }
345: /* ------------------------------------------------------------------- */
346: /*
347: CheckSolution - Given a computed solution (lambda,x) check if it
348: satisfies the analytic solution.
350: Input Parameters:
351: + lambda - the computed eigenvalue
352: - y - the computed eigenvector
354: Output Parameter:
355: . error - norm of difference between the computed and exact eigenvector
356: */
357: PetscErrorCode CheckSolution(PetscScalar lambda,Vec y,PetscReal *error,void *ctx)
358: {
359: PetscScalar *uu;
360: PetscInt i,n,Istart,Iend;
361: PetscReal nu,x;
362: Vec u;
363: ApplicationCtx *user = (ApplicationCtx*)ctx;
365: PetscFunctionBeginUser;
366: nu = PetscSqrtReal(PetscRealPart(lambda));
367: PetscCall(VecDuplicate(y,&u));
368: PetscCall(VecGetSize(u,&n));
369: PetscCall(VecGetOwnershipRange(y,&Istart,&Iend));
370: PetscCall(VecGetArray(u,&uu));
371: for (i=Istart;i<Iend;i++) {
372: x = (i+1)*user->h;
373: uu[i-Istart] = PetscSinReal(nu*x);
374: }
375: PetscCall(VecRestoreArray(u,&uu));
376: PetscCall(VecNormalize(u,NULL));
377: PetscCall(VecAXPY(u,-1.0,y));
378: PetscCall(VecNorm(u,NORM_2,error));
379: PetscCall(VecDestroy(&u));
380: PetscFunctionReturn(PETSC_SUCCESS);
381: }
383: /* ------------------------------------------------------------------- */
384: /*
385: FixSign - Force the eigenfunction to be real and positive, since
386: some eigensolvers may return the eigenvector multiplied by a
387: complex number of modulus one.
389: Input/Output Parameter:
390: . x - the computed vector
391: */
392: PetscErrorCode FixSign(Vec x)
393: {
394: PetscMPIInt rank;
395: PetscScalar sign=0.0;
396: const PetscScalar *xx;
398: PetscFunctionBeginUser;
399: PetscCallMPI(MPI_Comm_rank(PETSC_COMM_WORLD,&rank));
400: if (!rank) {
401: PetscCall(VecGetArrayRead(x,&xx));
402: sign = *xx/PetscAbsScalar(*xx);
403: PetscCall(VecRestoreArrayRead(x,&xx));
404: }
405: PetscCallMPI(MPI_Bcast(&sign,1,MPIU_SCALAR,0,PETSC_COMM_WORLD));
406: PetscCall(VecScale(x,1.0/sign));
407: PetscFunctionReturn(PETSC_SUCCESS);
408: }
410: /*TEST
412: build:
413: requires: !single
415: test:
416: suffix: 1
417: args: -nep_target 4
418: 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 = /"
419: requires: !single
421: testset:
422: args: -nep_type nleigs -nep_target 10 -nep_nev 4
423: test:
424: suffix: 2
425: 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 = /"
426: args: -rg_interval_endpoints 4,200
427: requires: !single !complex
428: test:
429: suffix: 2_complex
430: 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 = /"
431: output_file: output/ex20_2.out
432: args: -rg_interval_endpoints 4,200,-.1,.1 -nep_target_real
433: requires: !single complex
435: testset:
436: args: -nep_type nleigs -nep_target 10 -nep_nev 4 -nep_ncv 18 -nep_two_sided {{0 1}} -nep_nleigs_full_basis
437: test:
438: suffix: 3
439: 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 = /"
440: output_file: output/ex20_2.out
441: args: -rg_interval_endpoints 4,200
442: requires: !single !complex
443: test:
444: suffix: 3_complex
445: 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 = /"
446: output_file: output/ex20_2.out
447: args: -rg_interval_endpoints 4,200,-.1,.1
448: requires: !single complex
450: test:
451: suffix: 4
452: args: -n 256 -nep_nev 2 -nep_target 10
453: 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 = /"
454: requires: !single
456: TEST*/