Actual source code: ex5.c

  2: static char help[] = "Tests the multigrid code.  The input parameters are:\n\
  3:   -x N              Use a mesh in the x direction of N.  \n\
  4:   -c N              Use N V-cycles.  \n\
  5:   -l N              Use N Levels.  \n\
  6:   -smooths N        Use N pre smooths and N post smooths.  \n\
  7:   -j                Use Jacobi smoother.  \n\
  8:   -a use additive multigrid \n\
  9:   -f use full multigrid (preconditioner variant) \n\
 10: This example also demonstrates matrix-free methods\n\n";

 12: /*
 13:   This is not a good example to understand the use of multigrid with PETSc.
 14: */
 15:  #include petscmg.h

 17: PetscErrorCode  residual(Mat,Vec,Vec,Vec);
 18: PetscErrorCode  gauss_seidel(void*,Vec,Vec,Vec,PetscReal,PetscReal,PetscReal,PetscInt);
 19: PetscErrorCode  jacobi(void*,Vec,Vec,Vec,PetscReal,PetscReal,PetscReal,PetscInt);
 20: PetscErrorCode  interpolate(Mat,Vec,Vec,Vec);
 21: PetscErrorCode  restrct(Mat,Vec,Vec);
 22: PetscErrorCode  Create1dLaplacian(PetscInt,Mat*);
 23: PetscErrorCode  CalculateRhs(Vec);
 24: PetscErrorCode  CalculateError(Vec,Vec,Vec,PetscReal*);
 25: PetscErrorCode  CalculateSolution(PetscInt,Vec*);
 26: PetscErrorCode  amult(Mat,Vec,Vec);

 30: int main(int Argc,char **Args)
 31: {
 32:   PetscInt        x_mesh = 15,levels = 3,cycles = 1,use_jacobi = 0;
 33:   PetscInt        i,smooths = 1,*N,its;
 34:   PetscErrorCode  ierr;
 35:   PCMGType        am = PC_MG_MULTIPLICATIVE;
 36:   Mat             cmat,mat[20],fmat;
 37:   KSP             cksp,ksp[20],kspmg;
 38:   PetscReal       e[3]; /* l_2 error,max error, residual */
 39:   char            *shellname;
 40:   Vec             x,solution,X[20],R[20],B[20];
 41:   PetscScalar     zero = 0.0;
 42:   PC              pcmg,pc;
 43:   PetscTruth      flg;

 45:   PetscInitialize(&Argc,&Args,(char *)0,help);

 47:   PetscOptionsGetInt(PETSC_NULL,"-x",&x_mesh,PETSC_NULL);
 48:   PetscOptionsGetInt(PETSC_NULL,"-l",&levels,PETSC_NULL);
 49:   PetscOptionsGetInt(PETSC_NULL,"-c",&cycles,PETSC_NULL);
 50:   PetscOptionsGetInt(PETSC_NULL,"-smooths",&smooths,PETSC_NULL);
 51:   PetscOptionsHasName(PETSC_NULL,"-a",&flg);
 52:   if (flg) {am = PC_MG_ADDITIVE;}
 53:   PetscOptionsHasName(PETSC_NULL,"-f",&flg);
 54:   if (flg) {am = PC_MG_FULL;}
 55:   PetscOptionsHasName(PETSC_NULL,"-j",&flg);
 56:   if (flg) {use_jacobi = 1;}
 57: 
 58:   PetscMalloc(levels*sizeof(PetscInt),&N);
 59:   N[0] = x_mesh;
 60:   for (i=1; i<levels; i++) {
 61:     N[i] = N[i-1]/2;
 62:     if (N[i] < 1) {SETERRQ(1,"Too many levels");}
 63:   }

 65:   Create1dLaplacian(N[levels-1],&cmat);

 67:   KSPCreate(PETSC_COMM_WORLD,&kspmg);
 68:   KSPGetPC(kspmg,&pcmg);
 69:   KSPSetFromOptions(kspmg);
 70:   PCSetType(pcmg,PCMG);
 71:   PCMGSetLevels(pcmg,levels,PETSC_NULL);
 72:   PCMGSetType(pcmg,am);

 74:   PCMGGetCoarseSolve(pcmg,&cksp);
 75:   KSPSetOperators(cksp,cmat,cmat,DIFFERENT_NONZERO_PATTERN);
 76:   KSPGetPC(cksp,&pc);
 77:   PCSetType(pc,PCLU);
 78:   KSPSetType(cksp,KSPPREONLY);

 80:   /* zero is finest level */
 81:   for (i=0; i<levels-1; i++) {
 82:     PCMGSetResidual(pcmg,levels - 1 - i,residual,(Mat)0);
 83:     MatCreateShell(PETSC_COMM_WORLD,N[i+1],N[i],N[i+1],N[i],(void*)0,&mat[i]);
 84:     MatShellSetOperation(mat[i],MATOP_MULT,(void(*)(void))restrct);
 85:     MatShellSetOperation(mat[i],MATOP_MULT_TRANSPOSE_ADD,(void(*)(void))interpolate);
 86:     PCMGSetInterpolate(pcmg,levels - 1 - i,mat[i]);
 87:     PCMGSetRestriction(pcmg,levels - 1 - i,mat[i]);
 88:     PCMGSetCyclesOnLevel(pcmg,levels - 1 - i,cycles);

 90:     /* set smoother */
 91:     PCMGGetSmoother(pcmg,levels - 1 - i,&ksp[i]);
 92:     KSPGetPC(ksp[i],&pc);
 93:     PCSetType(pc,PCSHELL);
 94:     PCShellSetName(pc,"user_precond");
 95:     PCShellGetName(pc,&shellname);
 96:     PetscPrintf(PETSC_COMM_WORLD,"level=%D, PCShell name is %s\n",i,shellname);

 98:     /* this is a dummy! since KSP requires a matrix passed in  */
 99:     KSPSetOperators(ksp[i],mat[i],mat[i],DIFFERENT_NONZERO_PATTERN);
100:     /* 
101:         We override the matrix passed in by forcing it to use Richardson with 
102:         a user provided application. This is non-standard and this practice
103:         should be avoided.
104:     */
105:     PCShellSetApplyRichardson(pc,gauss_seidel);
106:     if (use_jacobi) {
107:       PCShellSetApplyRichardson(pc,jacobi);
108:     }
109:     KSPSetType(ksp[i],KSPRICHARDSON);
110:     KSPSetInitialGuessNonzero(ksp[i],PETSC_TRUE);
111:     KSPSetTolerances(ksp[i],PETSC_DEFAULT,PETSC_DEFAULT,PETSC_DEFAULT,smooths);

113:     VecCreateSeq(PETSC_COMM_SELF,N[i],&x);
114:     X[levels - 1 - i] = x;
115:     if (i > 0) {
116:       PCMGSetX(pcmg,levels - 1 - i,x);
117:     }
118:     VecCreateSeq(PETSC_COMM_SELF,N[i],&x);
119:     B[levels -1 - i] = x;
120:     if (i > 0) {
121:       PCMGSetRhs(pcmg,levels - 1 - i,x);
122:     }
123:     VecCreateSeq(PETSC_COMM_SELF,N[i],&x);
124:     R[levels - 1 - i] = x;
125:     PCMGSetR(pcmg,levels - 1 - i,x);
126:   }
127:   /* create coarse level vectors */
128:   VecCreateSeq(PETSC_COMM_SELF,N[levels-1],&x);
129:   PCMGSetX(pcmg,0,x); X[0] = x;
130:   VecCreateSeq(PETSC_COMM_SELF,N[levels-1],&x);
131:   PCMGSetRhs(pcmg,0,x); B[0] = x;

133:   /* create matrix multiply for finest level */
134:   MatCreateShell(PETSC_COMM_WORLD,N[0],N[0],N[0],N[0],(void*)0,&fmat);
135:   MatShellSetOperation(fmat,MATOP_MULT,(void(*)(void))amult);
136:   KSPSetOperators(kspmg,fmat,fmat,DIFFERENT_NONZERO_PATTERN);

138:   CalculateSolution(N[0],&solution);
139:   CalculateRhs(B[levels-1]);
140:   VecSet(X[levels-1],zero);

142:   residual((Mat)0,B[levels-1],X[levels-1],R[levels-1]);
143:   CalculateError(solution,X[levels-1],R[levels-1],e);
144:   PetscPrintf(PETSC_COMM_SELF,"l_2 error %g max error %g resi %g\n",e[0],e[1],e[2]);

146:   KSPSolve(kspmg,B[levels-1],X[levels-1]);
147:   KSPGetIterationNumber(kspmg,&its);
148:   residual((Mat)0,B[levels-1],X[levels-1],R[levels-1]);
149:   CalculateError(solution,X[levels-1],R[levels-1],e);
150:   PetscPrintf(PETSC_COMM_SELF,"its %D l_2 error %g max error %g resi %g\n",its,e[0],e[1],e[2]);

152:   PetscFree(N);
153:   VecDestroy(solution);

155:   /* note we have to keep a list of all vectors allocated, this is 
156:      not ideal, but putting it in MGDestroy is not so good either*/
157:   for (i=0; i<levels; i++) {
158:     VecDestroy(X[i]);
159:     VecDestroy(B[i]);
160:     if(i){VecDestroy(R[i]);}
161:   }
162:   for (i=0; i<levels-1; i++) {
163:     MatDestroy(mat[i]);
164:   }
165:   MatDestroy(cmat);
166:   MatDestroy(fmat);
167:   KSPDestroy(kspmg);
168:   PetscFinalize();
169:   return 0;
170: }

172: /* --------------------------------------------------------------------- */
175: PetscErrorCode residual(Mat mat,Vec bb,Vec xx,Vec rr)
176: {
177:   PetscInt       i,n1;
179:   PetscScalar    *b,*x,*r;

182:   VecGetSize(bb,&n1);
183:   VecGetArray(bb,&b);
184:   VecGetArray(xx,&x);
185:   VecGetArray(rr,&r);
186:   n1--;
187:   r[0] = b[0] + x[1] - 2.0*x[0];
188:   r[n1] = b[n1] + x[n1-1] - 2.0*x[n1];
189:   for (i=1; i<n1; i++) {
190:     r[i] = b[i] + x[i+1] + x[i-1] - 2.0*x[i];
191:   }
192:   VecRestoreArray(bb,&b);
193:   VecRestoreArray(xx,&x);
194:   VecRestoreArray(rr,&r);
195:   return(0);
196: }
199: PetscErrorCode amult(Mat mat,Vec xx,Vec yy)
200: {
201:   PetscInt       i,n1;
203:   PetscScalar    *y,*x;

206:   VecGetSize(xx,&n1);
207:   VecGetArray(xx,&x);
208:   VecGetArray(yy,&y);
209:   n1--;
210:   y[0] =  -x[1] + 2.0*x[0];
211:   y[n1] = -x[n1-1] + 2.0*x[n1];
212:   for (i=1; i<n1; i++) {
213:     y[i] = -x[i+1] - x[i-1] + 2.0*x[i];
214:   }
215:   VecRestoreArray(xx,&x);
216:   VecRestoreArray(yy,&y);
217:   return(0);
218: }
219: /* --------------------------------------------------------------------- */
222: PetscErrorCode gauss_seidel(void *ptr,Vec bb,Vec xx,Vec w,PetscReal rtol,PetscReal abstol,PetscReal dtol,PetscInt m)
223: {
224:   PetscInt       i,n1;
226:   PetscScalar    *x,*b;

229:   VecGetSize(bb,&n1); n1--;
230:   VecGetArray(bb,&b);
231:   VecGetArray(xx,&x);
232:   while (m--) {
233:     x[0] =  .5*(x[1] + b[0]);
234:     for (i=1; i<n1; i++) {
235:       x[i] = .5*(x[i+1] + x[i-1] + b[i]);
236:     }
237:     x[n1] = .5*(x[n1-1] + b[n1]);
238:     for (i=n1-1; i>0; i--) {
239:       x[i] = .5*(x[i+1] + x[i-1] + b[i]);
240:     }
241:     x[0] =  .5*(x[1] + b[0]);
242:   }
243:   VecRestoreArray(bb,&b);
244:   VecRestoreArray(xx,&x);
245:   return(0);
246: }
247: /* --------------------------------------------------------------------- */
250: PetscErrorCode jacobi(void *ptr,Vec bb,Vec xx,Vec w,PetscReal rtol,PetscReal abstol,PetscReal dtol,PetscInt m)
251: {
252:   PetscInt       i,n,n1;
254:   PetscScalar    *r,*b,*x;

257:   VecGetSize(bb,&n); n1 = n - 1;
258:   VecGetArray(bb,&b);
259:   VecGetArray(xx,&x);
260:   VecGetArray(w,&r);

262:   while (m--) {
263:     r[0] = .5*(x[1] + b[0]);
264:     for (i=1; i<n1; i++) {
265:        r[i] = .5*(x[i+1] + x[i-1] + b[i]);
266:     }
267:     r[n1] = .5*(x[n1-1] + b[n1]);
268:     for (i=0; i<n; i++) x[i] = (2.0*r[i] + x[i])/3.0;
269:   }
270:   VecRestoreArray(bb,&b);
271:   VecRestoreArray(xx,&x);
272:   VecRestoreArray(w,&r);
273:   return(0);
274: }
275: /*
276:    We know for this application that yy  and zz are the same
277: */
278: /* --------------------------------------------------------------------- */
281: PetscErrorCode interpolate(Mat mat,Vec xx,Vec yy,Vec zz)
282: {
283:   PetscInt       i,n,N,i2;
285:   PetscScalar    *x,*y;

288:   VecGetSize(yy,&N);
289:   VecGetArray(xx,&x);
290:   VecGetArray(yy,&y);
291:   n = N/2;
292:   for (i=0; i<n; i++) {
293:     i2 = 2*i;
294:     y[i2] +=  .5*x[i];
295:     y[i2+1] +=  x[i];
296:     y[i2+2] +=  .5*x[i];
297:   }
298:   VecRestoreArray(xx,&x);
299:   VecRestoreArray(yy,&y);
300:   return(0);
301: }
302: /* --------------------------------------------------------------------- */
305: PetscErrorCode restrct(Mat mat,Vec rr,Vec bb)
306: {
307:   PetscInt       i,n,N,i2;
309:   PetscScalar    *r,*b;

312:   VecGetSize(rr,&N);
313:   VecGetArray(rr,&r);
314:   VecGetArray(bb,&b);
315:   n = N/2;

317:   for (i=0; i<n; i++) {
318:     i2 = 2*i;
319:     b[i] = (r[i2] + 2.0*r[i2+1] + r[i2+2]);
320:   }
321:   VecRestoreArray(rr,&r);
322:   VecRestoreArray(bb,&b);
323:   return(0);
324: }
325: /* --------------------------------------------------------------------- */
328: PetscErrorCode Create1dLaplacian(PetscInt n,Mat *mat)
329: {
330:   PetscScalar    mone = -1.0,two = 2.0;
331:   PetscInt       i,idx;

335:   MatCreateSeqAIJ(PETSC_COMM_SELF,n,n,3,PETSC_NULL,mat);
336: 
337:   idx= n-1;
338:   MatSetValues(*mat,1,&idx,1,&idx,&two,INSERT_VALUES);
339:   for (i=0; i<n-1; i++) {
340:     MatSetValues(*mat,1,&i,1,&i,&two,INSERT_VALUES);
341:     idx = i+1;
342:     MatSetValues(*mat,1,&idx,1,&i,&mone,INSERT_VALUES);
343:     MatSetValues(*mat,1,&i,1,&idx,&mone,INSERT_VALUES);
344:   }
345:   MatAssemblyBegin(*mat,MAT_FINAL_ASSEMBLY);
346:   MatAssemblyEnd(*mat,MAT_FINAL_ASSEMBLY);
347:   return(0);
348: }
349: /* --------------------------------------------------------------------- */
352: PetscErrorCode CalculateRhs(Vec u)
353: {
355:   PetscInt       i,n;
356:   PetscReal      h,x = 0.0;
357:   PetscScalar    uu;

360:   VecGetSize(u,&n);
361:   h = 1.0/((PetscReal)(n+1));
362:   for (i=0; i<n; i++) {
363:     x += h; uu = 2.0*h*h;
364:     VecSetValues(u,1,&i,&uu,INSERT_VALUES);
365:   }

367:   return(0);
368: }
369: /* --------------------------------------------------------------------- */
372: PetscErrorCode CalculateSolution(PetscInt n,Vec *solution)
373: {
375:   PetscInt       i;
376:   PetscReal      h,x = 0.0;
377:   PetscScalar    uu;

380:   VecCreateSeq(PETSC_COMM_SELF,n,solution);
381:   h = 1.0/((PetscReal)(n+1));
382:   for (i=0; i<n; i++) {
383:     x += h; uu = x*(1.-x);
384:     VecSetValues(*solution,1,&i,&uu,INSERT_VALUES);
385:   }
386:   return(0);
387: }
388: /* --------------------------------------------------------------------- */
391: PetscErrorCode CalculateError(Vec solution,Vec u,Vec r,PetscReal *e)
392: {
393:   PetscScalar    mone = -1.0;

397:   VecNorm(r,NORM_2,e+2);
398:   VecWAXPY(r,mone,u,solution);
399:   VecNorm(r,NORM_2,e);
400:   VecNorm(r,NORM_1,e+1);
401:   return(0);
402: }