Actual source code: ex24.c
slepc-3.16.3 2022-04-11
1: /*
2: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3: SLEPc - Scalable Library for Eigenvalue Problem Computations
4: Copyright (c) 2002-2021, 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[] = "Spectrum folding for a standard symmetric eigenproblem.\n\n"
12: "The problem matrix is the 2-D Laplacian.\n\n"
13: "The command line options are:\n"
14: " -n <n>, where <n> = number of grid subdivisions in x dimension.\n"
15: " -m <m>, where <m> = number of grid subdivisions in y dimension.\n";
17: #include <slepceps.h>
19: /*
20: User context for spectrum folding
21: */
22: typedef struct {
23: Mat A;
24: Vec w;
25: PetscReal target;
26: } CTX_FOLD;
28: /*
29: Auxiliary routines
30: */
31: PetscErrorCode MatMult_Fold(Mat,Vec,Vec);
32: PetscErrorCode RayleighQuotient(Mat,Vec,PetscScalar*);
33: PetscErrorCode ComputeResidualNorm(Mat,PetscScalar,Vec,PetscReal*);
35: int main(int argc,char **argv)
36: {
37: Mat A,M,P; /* problem matrix, shell matrix and preconditioner */
38: Vec x; /* eigenvector */
39: EPS eps; /* eigenproblem solver context */
40: ST st; /* spectral transformation */
41: KSP ksp;
42: PC pc;
43: EPSType type;
44: CTX_FOLD *ctx;
45: PetscInt nconv,N,n=10,m,nloc,mloc,Istart,Iend,II,i,j;
46: PetscReal *error,*evals,target=0.0,tol;
47: PetscScalar lambda;
48: PetscBool flag,terse,errok,hasmat;
51: SlepcInitialize(&argc,&argv,(char*)0,help);if (ierr) return ierr;
53: PetscOptionsGetInt(NULL,NULL,"-n",&n,NULL);
54: PetscOptionsGetInt(NULL,NULL,"-m",&m,&flag);
55: if (!flag) m=n;
56: PetscOptionsGetReal(NULL,NULL,"-target",&target,NULL);
57: N = n*m;
58: PetscPrintf(PETSC_COMM_WORLD,"\nSpectrum Folding, N=%D (%Dx%D grid) target=%f\n\n",N,n,m,(double)target);
60: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
61: Compute the 5-point stencil Laplacian
62: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
64: MatCreate(PETSC_COMM_WORLD,&A);
65: MatSetSizes(A,PETSC_DECIDE,PETSC_DECIDE,N,N);
66: MatSetFromOptions(A);
67: MatSetUp(A);
69: MatGetOwnershipRange(A,&Istart,&Iend);
70: for (II=Istart;II<Iend;II++) {
71: i = II/n; j = II-i*n;
72: if (i>0) { MatSetValue(A,II,II-n,-1.0,INSERT_VALUES); }
73: if (i<m-1) { MatSetValue(A,II,II+n,-1.0,INSERT_VALUES); }
74: if (j>0) { MatSetValue(A,II,II-1,-1.0,INSERT_VALUES); }
75: if (j<n-1) { MatSetValue(A,II,II+1,-1.0,INSERT_VALUES); }
76: MatSetValue(A,II,II,4.0,INSERT_VALUES);
77: }
79: MatAssemblyBegin(A,MAT_FINAL_ASSEMBLY);
80: MatAssemblyEnd(A,MAT_FINAL_ASSEMBLY);
81: MatCreateVecs(A,&x,NULL);
82: MatGetLocalSize(A,&nloc,&mloc);
84: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
85: Create shell matrix to perform spectrum folding
86: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
87: PetscNew(&ctx);
88: ctx->A = A;
89: ctx->target = target;
90: VecDuplicate(x,&ctx->w);
92: MatCreateShell(PETSC_COMM_WORLD,nloc,mloc,N,N,ctx,&M);
93: MatShellSetOperation(M,MATOP_MULT,(void(*)(void))MatMult_Fold);
95: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
96: Create the eigensolver and set various options
97: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
99: EPSCreate(PETSC_COMM_WORLD,&eps);
100: EPSSetOperators(eps,M,NULL);
101: EPSSetProblemType(eps,EPS_HEP);
102: EPSSetWhichEigenpairs(eps,EPS_SMALLEST_REAL);
103: EPSSetFromOptions(eps);
105: PetscObjectTypeCompareAny((PetscObject)eps,&flag,EPSGD,EPSJD,EPSBLOPEX,EPSLOBPCG,EPSRQCG,"");
106: if (flag) {
107: /*
108: Build preconditioner specific for this application (diagonal of A^2)
109: */
110: MatGetRowSum(A,x);
111: VecScale(x,-1.0);
112: VecShift(x,20.0);
113: MatCreate(PETSC_COMM_WORLD,&P);
114: MatSetSizes(P,PETSC_DECIDE,PETSC_DECIDE,N,N);
115: MatSetFromOptions(P);
116: MatSetUp(P);
117: MatDiagonalSet(P,x,INSERT_VALUES);
118: /*
119: Set diagonal preconditioner
120: */
121: EPSGetST(eps,&st);
122: STSetType(st,STPRECOND);
123: STSetPreconditionerMat(st,P);
124: MatDestroy(&P);
125: STGetKSP(st,&ksp);
126: KSPGetPC(ksp,&pc);
127: PCSetType(pc,PCJACOBI);
128: STPrecondGetKSPHasMat(st,&hasmat);
129: PetscPrintf(PETSC_COMM_WORLD," Preconditioned solver, hasmat=%s\n",hasmat?"true":"false");
130: }
132: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
133: Solve the eigensystem
134: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
136: EPSSolve(eps);
137: EPSGetType(eps,&type);
138: PetscPrintf(PETSC_COMM_WORLD," Solution method: %s\n\n",type);
139: EPSGetTolerances(eps,&tol,NULL);
141: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
142: Display solution and clean up
143: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
145: EPSGetConverged(eps,&nconv);
146: PetscPrintf(PETSC_COMM_WORLD," Number of converged eigenpairs: %D\n\n",nconv);
147: if (nconv>0) {
148: PetscMalloc2(nconv,&evals,nconv,&error);
149: for (i=0;i<nconv;i++) {
150: /* Get i-th eigenvector, compute eigenvalue approximation from
151: Rayleigh quotient and compute residual norm */
152: EPSGetEigenpair(eps,i,NULL,NULL,x,NULL);
153: RayleighQuotient(A,x,&lambda);
154: ComputeResidualNorm(A,lambda,x,&error[i]);
155: #if defined(PETSC_USE_COMPLEX)
156: evals[i] = PetscRealPart(lambda);
157: #else
158: evals[i] = lambda;
159: #endif
160: }
161: PetscOptionsHasName(NULL,NULL,"-terse",&terse);
162: if (!terse) {
163: PetscPrintf(PETSC_COMM_WORLD,
164: " k ||Ax-kx||\n"
165: " ----------------- ------------------\n");
166: for (i=0;i<nconv;i++) {
167: PetscPrintf(PETSC_COMM_WORLD," %12f %12.2g\n",(double)evals[i],(double)error[i]);
168: }
169: } else {
170: errok = PETSC_TRUE;
171: for (i=0;i<nconv;i++) errok = (errok && error[i]<5.0*tol)? PETSC_TRUE: PETSC_FALSE;
172: if (!errok) {
173: PetscPrintf(PETSC_COMM_WORLD," Problem: some of the first %D relative errors are higher than the tolerance\n\n",nconv);
174: } else {
175: PetscPrintf(PETSC_COMM_WORLD," nconv=%D eigenvalues computed up to the required tolerance:",nconv);
176: for (i=0;i<nconv;i++) {
177: PetscPrintf(PETSC_COMM_WORLD," %.5f",(double)evals[i]);
178: }
179: }
180: }
181: PetscPrintf(PETSC_COMM_WORLD,"\n");
182: PetscFree2(evals,error);
183: }
185: EPSDestroy(&eps);
186: MatDestroy(&A);
187: MatDestroy(&M);
188: VecDestroy(&ctx->w);
189: VecDestroy(&x);
190: PetscFree(ctx);
191: SlepcFinalize();
192: return ierr;
193: }
195: /*
196: Matrix-vector product subroutine for the spectrum folding.
197: y <-- (A-t*I)^2*x
198: */
199: PetscErrorCode MatMult_Fold(Mat M,Vec x,Vec y)
200: {
201: CTX_FOLD *ctx;
202: PetscScalar sigma;
206: MatShellGetContext(M,&ctx);
207: sigma = -ctx->target;
208: MatMult(ctx->A,x,ctx->w);
209: VecAXPY(ctx->w,sigma,x);
210: MatMult(ctx->A,ctx->w,y);
211: VecAXPY(y,sigma,ctx->w);
212: return(0);
213: }
215: /*
216: Computes the Rayleigh quotient of a vector x
217: r <-- x^T*A*x (assumes x has unit norm)
218: */
219: PetscErrorCode RayleighQuotient(Mat A,Vec x,PetscScalar *r)
220: {
221: Vec Ax;
225: VecDuplicate(x,&Ax);
226: MatMult(A,x,Ax);
227: VecDot(Ax,x,r);
228: VecDestroy(&Ax);
229: return(0);
230: }
232: /*
233: Computes the residual norm of an approximate eigenvector x, |A*x-lambda*x|
234: */
235: PetscErrorCode ComputeResidualNorm(Mat A,PetscScalar lambda,Vec x,PetscReal *r)
236: {
237: Vec Ax;
241: VecDuplicate(x,&Ax);
242: MatMult(A,x,Ax);
243: VecAXPY(Ax,-lambda,x);
244: VecNorm(Ax,NORM_2,r);
245: VecDestroy(&Ax);
246: return(0);
247: }
249: /*TEST
251: testset:
252: args: -n 15 -eps_nev 1 -eps_ncv 12 -eps_max_it 1000 -eps_tol 1e-5 -terse
253: filter: grep -v Solution
254: test:
255: suffix: 1
256: test:
257: suffix: 1_lobpcg
258: args: -eps_type lobpcg
259: requires: !single
260: test:
261: suffix: 1_gd
262: args: -eps_type gd
263: requires: !single
265: TEST*/