xref: /petsc/src/snes/tutorials/ex58.c (revision 8cc725e69398de546bdc828d7b714aa2223f5218)
1 
2 #include <petscsnes.h>
3 #include <petscdm.h>
4 #include <petscdmda.h>
5 
6 static const char help[] = "Parallel version of the minimum surface area problem in 2D using DMDA.\n\
7  It solves a system of nonlinear equations in mixed\n\
8 complementarity form.This example is based on a\n\
9 problem from the MINPACK-2 test suite.  Given a rectangular 2-D domain and\n\
10 boundary values along the edges of the domain, the objective is to find the\n\
11 surface with the minimal area that satisfies the boundary conditions.\n\
12 This application solves this problem using complimentarity -- We are actually\n\
13 solving the system  (grad f)_i >= 0, if x_i == l_i \n\
14                     (grad f)_i = 0, if l_i < x_i < u_i \n\
15                     (grad f)_i <= 0, if x_i == u_i  \n\
16 where f is the function to be minimized. \n\
17 \n\
18 The command line options are:\n\
19   -da_grid_x <nx>, where <nx> = number of grid points in the 1st coordinate direction\n\
20   -da_grid_y <ny>, where <ny> = number of grid points in the 2nd coordinate direction\n\
21   -start <st>, where <st> =0 for zero vector, and an average of the boundary conditions otherwise\n\
22   -lb <value>, lower bound on the variables\n\
23   -ub <value>, upper bound on the variables\n\n";
24 
25 /*
26    User-defined application context - contains data needed by the
27    application-provided call-back routines, FormJacobian() and
28    FormFunction().
29 */
30 
31 /*
32      This is a new version of the ../tests/ex8.c code
33 
34      Run, for example, with the options ./ex58 -snes_vi_monitor -ksp_monitor -mg_levels_ksp_monitor -pc_type mg -pc_mg_levels 2 -pc_mg_galerkin pmat -ksp_type fgmres
35 
36      Or to run with grid sequencing on the nonlinear problem (note that you do not need to provide the number of
37          multigrid levels, it will be determined automatically based on the number of refinements done)
38 
39       ./ex58 -pc_type mg -ksp_monitor  -snes_view -pc_mg_galerkin pmat -snes_grid_sequence 3
40              -mg_levels_ksp_monitor -snes_vi_monitor -mg_levels_pc_type sor -pc_mg_type full
41 
42 */
43 
44 typedef struct {
45   PetscScalar *bottom, *top, *left, *right;
46   PetscScalar lb,ub;
47 } AppCtx;
48 
49 /* -------- User-defined Routines --------- */
50 
51 extern PetscErrorCode FormBoundaryConditions(SNES,AppCtx**);
52 extern PetscErrorCode DestroyBoundaryConditions(AppCtx**);
53 extern PetscErrorCode ComputeInitialGuess(SNES,Vec,void*);
54 extern PetscErrorCode FormGradient(SNES,Vec,Vec,void*);
55 extern PetscErrorCode FormJacobian(SNES,Vec,Mat,Mat,void*);
56 extern PetscErrorCode FormBounds(SNES,Vec,Vec);
57 
58 int main(int argc, char **argv)
59 {
60   Vec            x,r;               /* solution and residual vectors */
61   SNES           snes;              /* nonlinear solver context */
62   Mat            J;                 /* Jacobian matrix */
63   DM             da;
64 
65   PetscCall(PetscInitialize(&argc, &argv, (char*)0, help));
66 
67   /* Create distributed array to manage the 2d grid */
68   PetscCall(DMDACreate2d(PETSC_COMM_WORLD, DM_BOUNDARY_NONE, DM_BOUNDARY_NONE,DMDA_STENCIL_BOX,4,4,PETSC_DECIDE,PETSC_DECIDE,1,1,NULL,NULL,&da));
69   PetscCall(DMSetFromOptions(da));
70   PetscCall(DMSetUp(da));
71 
72   /* Extract global vectors from DMDA; */
73   PetscCall(DMCreateGlobalVector(da,&x));
74   PetscCall(VecDuplicate(x, &r));
75 
76   PetscCall(DMSetMatType(da,MATAIJ));
77   PetscCall(DMCreateMatrix(da,&J));
78 
79   /* Create nonlinear solver context */
80   PetscCall(SNESCreate(PETSC_COMM_WORLD,&snes));
81   PetscCall(SNESSetDM(snes,da));
82 
83   /*  Set function evaluation and Jacobian evaluation  routines */
84   PetscCall(SNESSetFunction(snes,r,FormGradient,NULL));
85   PetscCall(SNESSetJacobian(snes,J,J,FormJacobian,NULL));
86 
87   PetscCall(SNESSetComputeApplicationContext(snes,(PetscErrorCode (*)(SNES,void**))FormBoundaryConditions,(PetscErrorCode (*)(void**))DestroyBoundaryConditions));
88 
89   PetscCall(SNESSetComputeInitialGuess(snes,ComputeInitialGuess,NULL));
90 
91   PetscCall(SNESVISetComputeVariableBounds(snes,FormBounds));
92 
93   PetscCall(SNESSetFromOptions(snes));
94 
95   /* Solve the application */
96   PetscCall(SNESSolve(snes,NULL,x));
97 
98   /* Free memory */
99   PetscCall(VecDestroy(&x));
100   PetscCall(VecDestroy(&r));
101   PetscCall(MatDestroy(&J));
102   PetscCall(SNESDestroy(&snes));
103 
104   /* Free user-created data structures */
105   PetscCall(DMDestroy(&da));
106 
107   PetscCall(PetscFinalize());
108   return 0;
109 }
110 
111 /* -------------------------------------------------------------------- */
112 
113 /*  FormBounds - sets the upper and lower bounds
114 
115     Input Parameters:
116 .   snes  - the SNES context
117 
118     Output Parameters:
119 .   xl - lower bounds
120 .   xu - upper bounds
121 */
122 PetscErrorCode FormBounds(SNES snes, Vec xl, Vec xu)
123 {
124   AppCtx         *ctx;
125 
126   PetscFunctionBeginUser;
127   PetscCall(SNESGetApplicationContext(snes,&ctx));
128   PetscCall(VecSet(xl,ctx->lb));
129   PetscCall(VecSet(xu,ctx->ub));
130   PetscFunctionReturn(0);
131 }
132 
133 /* -------------------------------------------------------------------- */
134 
135 /*  FormGradient - Evaluates gradient of f.
136 
137     Input Parameters:
138 .   snes  - the SNES context
139 .   X     - input vector
140 .   ptr   - optional user-defined context, as set by SNESSetFunction()
141 
142     Output Parameters:
143 .   G - vector containing the newly evaluated gradient
144 */
145 PetscErrorCode FormGradient(SNES snes, Vec X, Vec G, void *ptr)
146 {
147   AppCtx      *user;
148   PetscInt    i,j;
149   PetscInt    mx, my;
150   PetscScalar hx,hy, hydhx, hxdhy;
151   PetscScalar f1,f2,f3,f4,f5,f6,d1,d2,d3,d4,d5,d6,d7,d8,xc,xl,xr,xt,xb,xlt,xrb;
152   PetscScalar df1dxc,df2dxc,df3dxc,df4dxc,df5dxc,df6dxc;
153   PetscScalar **g, **x;
154   PetscInt    xs,xm,ys,ym;
155   Vec         localX;
156   DM          da;
157 
158   PetscFunctionBeginUser;
159   PetscCall(SNESGetDM(snes,&da));
160   PetscCall(SNESGetApplicationContext(snes,&user));
161   PetscCall(DMDAGetInfo(da,PETSC_IGNORE,&mx,&my,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE));
162   hx   = 1.0/(mx+1);hy=1.0/(my+1); hydhx=hy/hx; hxdhy=hx/hy;
163 
164   PetscCall(VecSet(G,0.0));
165 
166   /* Get local vector */
167   PetscCall(DMGetLocalVector(da,&localX));
168   /* Get ghost points */
169   PetscCall(DMGlobalToLocalBegin(da,X,INSERT_VALUES,localX));
170   PetscCall(DMGlobalToLocalEnd(da,X,INSERT_VALUES,localX));
171   /* Get pointer to local vector data */
172   PetscCall(DMDAVecGetArray(da,localX, &x));
173   PetscCall(DMDAVecGetArray(da,G, &g));
174 
175   PetscCall(DMDAGetCorners(da,&xs,&ys,NULL,&xm,&ym,NULL));
176   /* Compute function over the locally owned part of the mesh */
177   for (j=ys; j < ys+ym; j++) {
178     for (i=xs; i< xs+xm; i++) {
179 
180       xc = x[j][i];
181       xlt=xrb=xl=xr=xb=xt=xc;
182 
183       if (i==0) { /* left side */
184         xl  = user->left[j+1];
185         xlt = user->left[j+2];
186       } else xl = x[j][i-1];
187 
188       if (j==0) { /* bottom side */
189         xb  = user->bottom[i+1];
190         xrb = user->bottom[i+2];
191       } else xb = x[j-1][i];
192 
193       if (i+1 == mx) { /* right side */
194         xr  = user->right[j+1];
195         xrb = user->right[j];
196       } else xr = x[j][i+1];
197 
198       if (j+1==0+my) { /* top side */
199         xt  = user->top[i+1];
200         xlt = user->top[i];
201       } else xt = x[j+1][i];
202 
203       if (i>0 && j+1<my) xlt = x[j+1][i-1]; /* left top side */
204       if (j>0 && i+1<mx) xrb = x[j-1][i+1]; /* right bottom */
205 
206       d1 = (xc-xl);
207       d2 = (xc-xr);
208       d3 = (xc-xt);
209       d4 = (xc-xb);
210       d5 = (xr-xrb);
211       d6 = (xrb-xb);
212       d7 = (xlt-xl);
213       d8 = (xt-xlt);
214 
215       df1dxc = d1*hydhx;
216       df2dxc = (d1*hydhx + d4*hxdhy);
217       df3dxc = d3*hxdhy;
218       df4dxc = (d2*hydhx + d3*hxdhy);
219       df5dxc = d2*hydhx;
220       df6dxc = d4*hxdhy;
221 
222       d1 /= hx;
223       d2 /= hx;
224       d3 /= hy;
225       d4 /= hy;
226       d5 /= hy;
227       d6 /= hx;
228       d7 /= hy;
229       d8 /= hx;
230 
231       f1 = PetscSqrtScalar(1.0 + d1*d1 + d7*d7);
232       f2 = PetscSqrtScalar(1.0 + d1*d1 + d4*d4);
233       f3 = PetscSqrtScalar(1.0 + d3*d3 + d8*d8);
234       f4 = PetscSqrtScalar(1.0 + d3*d3 + d2*d2);
235       f5 = PetscSqrtScalar(1.0 + d2*d2 + d5*d5);
236       f6 = PetscSqrtScalar(1.0 + d4*d4 + d6*d6);
237 
238       df1dxc /= f1;
239       df2dxc /= f2;
240       df3dxc /= f3;
241       df4dxc /= f4;
242       df5dxc /= f5;
243       df6dxc /= f6;
244 
245       g[j][i] = (df1dxc+df2dxc+df3dxc+df4dxc+df5dxc+df6dxc)/2.0;
246 
247     }
248   }
249 
250   /* Restore vectors */
251   PetscCall(DMDAVecRestoreArray(da,localX, &x));
252   PetscCall(DMDAVecRestoreArray(da,G, &g));
253   PetscCall(DMRestoreLocalVector(da,&localX));
254   PetscCall(PetscLogFlops(67.0*mx*my));
255   PetscFunctionReturn(0);
256 }
257 
258 /* ------------------------------------------------------------------- */
259 /*
260    FormJacobian - Evaluates Jacobian matrix.
261 
262    Input Parameters:
263 .  snes - SNES context
264 .  X    - input vector
265 .  ptr  - optional user-defined context, as set by SNESSetJacobian()
266 
267    Output Parameters:
268 .  tH    - Jacobian matrix
269 
270 */
271 PetscErrorCode FormJacobian(SNES snes, Vec X, Mat H, Mat tHPre, void *ptr)
272 {
273   AppCtx         *user;
274   PetscInt       i,j,k;
275   PetscInt       mx, my;
276   MatStencil     row,col[7];
277   PetscScalar    hx, hy, hydhx, hxdhy;
278   PetscScalar    f1,f2,f3,f4,f5,f6,d1,d2,d3,d4,d5,d6,d7,d8,xc,xl,xr,xt,xb,xlt,xrb;
279   PetscScalar    hl,hr,ht,hb,hc,htl,hbr;
280   PetscScalar    **x, v[7];
281   PetscBool      assembled;
282   PetscInt       xs,xm,ys,ym;
283   Vec            localX;
284   DM             da;
285 
286   PetscFunctionBeginUser;
287   PetscCall(SNESGetDM(snes,&da));
288   PetscCall(SNESGetApplicationContext(snes,&user));
289   PetscCall(DMDAGetInfo(da,PETSC_IGNORE,&mx,&my,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE));
290   hx   = 1.0/(mx+1); hy=1.0/(my+1); hydhx=hy/hx; hxdhy=hx/hy;
291 
292 /* Set various matrix options */
293   PetscCall(MatAssembled(H,&assembled));
294   if (assembled) PetscCall(MatZeroEntries(H));
295 
296   /* Get local vector */
297   PetscCall(DMGetLocalVector(da,&localX));
298   /* Get ghost points */
299   PetscCall(DMGlobalToLocalBegin(da,X,INSERT_VALUES,localX));
300   PetscCall(DMGlobalToLocalEnd(da,X,INSERT_VALUES,localX));
301 
302   /* Get pointers to vector data */
303   PetscCall(DMDAVecGetArray(da,localX, &x));
304 
305   PetscCall(DMDAGetCorners(da,&xs,&ys,NULL,&xm,&ym,NULL));
306   /* Compute Jacobian over the locally owned part of the mesh */
307   for (j=ys; j< ys+ym; j++) {
308     for (i=xs; i< xs+xm; i++) {
309       xc = x[j][i];
310       xlt=xrb=xl=xr=xb=xt=xc;
311 
312       /* Left */
313       if (i==0) {
314         xl  = user->left[j+1];
315         xlt = user->left[j+2];
316       } else xl = x[j][i-1];
317 
318       /* Bottom */
319       if (j==0) {
320         xb  =user->bottom[i+1];
321         xrb = user->bottom[i+2];
322       } else xb = x[j-1][i];
323 
324       /* Right */
325       if (i+1 == mx) {
326         xr  =user->right[j+1];
327         xrb = user->right[j];
328       } else xr = x[j][i+1];
329 
330       /* Top */
331       if (j+1==my) {
332         xt  =user->top[i+1];
333         xlt = user->top[i];
334       } else xt = x[j+1][i];
335 
336       /* Top left */
337       if (i>0 && j+1<my) xlt = x[j+1][i-1];
338 
339       /* Bottom right */
340       if (j>0 && i+1<mx) xrb = x[j-1][i+1];
341 
342       d1 = (xc-xl)/hx;
343       d2 = (xc-xr)/hx;
344       d3 = (xc-xt)/hy;
345       d4 = (xc-xb)/hy;
346       d5 = (xrb-xr)/hy;
347       d6 = (xrb-xb)/hx;
348       d7 = (xlt-xl)/hy;
349       d8 = (xlt-xt)/hx;
350 
351       f1 = PetscSqrtScalar(1.0 + d1*d1 + d7*d7);
352       f2 = PetscSqrtScalar(1.0 + d1*d1 + d4*d4);
353       f3 = PetscSqrtScalar(1.0 + d3*d3 + d8*d8);
354       f4 = PetscSqrtScalar(1.0 + d3*d3 + d2*d2);
355       f5 = PetscSqrtScalar(1.0 + d2*d2 + d5*d5);
356       f6 = PetscSqrtScalar(1.0 + d4*d4 + d6*d6);
357 
358       hl = (-hydhx*(1.0+d7*d7)+d1*d7)/(f1*f1*f1)+
359            (-hydhx*(1.0+d4*d4)+d1*d4)/(f2*f2*f2);
360       hr = (-hydhx*(1.0+d5*d5)+d2*d5)/(f5*f5*f5)+
361            (-hydhx*(1.0+d3*d3)+d2*d3)/(f4*f4*f4);
362       ht = (-hxdhy*(1.0+d8*d8)+d3*d8)/(f3*f3*f3)+
363            (-hxdhy*(1.0+d2*d2)+d2*d3)/(f4*f4*f4);
364       hb = (-hxdhy*(1.0+d6*d6)+d4*d6)/(f6*f6*f6)+
365            (-hxdhy*(1.0+d1*d1)+d1*d4)/(f2*f2*f2);
366 
367       hbr = -d2*d5/(f5*f5*f5) - d4*d6/(f6*f6*f6);
368       htl = -d1*d7/(f1*f1*f1) - d3*d8/(f3*f3*f3);
369 
370       hc = hydhx*(1.0+d7*d7)/(f1*f1*f1) + hxdhy*(1.0+d8*d8)/(f3*f3*f3) +
371            hydhx*(1.0+d5*d5)/(f5*f5*f5) + hxdhy*(1.0+d6*d6)/(f6*f6*f6) +
372            (hxdhy*(1.0+d1*d1)+hydhx*(1.0+d4*d4)-2.0*d1*d4)/(f2*f2*f2) +
373            (hxdhy*(1.0+d2*d2)+hydhx*(1.0+d3*d3)-2.0*d2*d3)/(f4*f4*f4);
374 
375       hl/=2.0; hr/=2.0; ht/=2.0; hb/=2.0; hbr/=2.0; htl/=2.0;  hc/=2.0;
376 
377       k     =0;
378       row.i = i;row.j= j;
379       /* Bottom */
380       if (j>0) {
381         v[k]     =hb;
382         col[k].i = i; col[k].j=j-1; k++;
383       }
384 
385       /* Bottom right */
386       if (j>0 && i < mx -1) {
387         v[k]     =hbr;
388         col[k].i = i+1; col[k].j = j-1; k++;
389       }
390 
391       /* left */
392       if (i>0) {
393         v[k]     = hl;
394         col[k].i = i-1; col[k].j = j; k++;
395       }
396 
397       /* Centre */
398       v[k]= hc; col[k].i= row.i; col[k].j = row.j; k++;
399 
400       /* Right */
401       if (i < mx-1) {
402         v[k]    = hr;
403         col[k].i= i+1; col[k].j = j;k++;
404       }
405 
406       /* Top left */
407       if (i>0 && j < my-1) {
408         v[k]     = htl;
409         col[k].i = i-1;col[k].j = j+1; k++;
410       }
411 
412       /* Top */
413       if (j < my-1) {
414         v[k]     = ht;
415         col[k].i = i; col[k].j = j+1; k++;
416       }
417 
418       PetscCall(MatSetValuesStencil(H,1,&row,k,col,v,INSERT_VALUES));
419     }
420   }
421 
422   /* Assemble the matrix */
423   PetscCall(MatAssemblyBegin(H,MAT_FINAL_ASSEMBLY));
424   PetscCall(DMDAVecRestoreArray(da,localX,&x));
425   PetscCall(MatAssemblyEnd(H,MAT_FINAL_ASSEMBLY));
426   PetscCall(DMRestoreLocalVector(da,&localX));
427 
428   PetscCall(PetscLogFlops(199.0*mx*my));
429   PetscFunctionReturn(0);
430 }
431 
432 /* ------------------------------------------------------------------- */
433 /*
434    FormBoundaryConditions -  Calculates the boundary conditions for
435    the region.
436 
437    Input Parameter:
438 .  user - user-defined application context
439 
440    Output Parameter:
441 .  user - user-defined application context
442 */
443 PetscErrorCode FormBoundaryConditions(SNES snes,AppCtx **ouser)
444 {
445   PetscInt       i,j,k,limit=0,maxits=5;
446   PetscInt       mx,my;
447   PetscInt       bsize=0, lsize=0, tsize=0, rsize=0;
448   PetscScalar    one  =1.0, two=2.0, three=3.0;
449   PetscScalar    det,hx,hy,xt=0,yt=0;
450   PetscReal      fnorm, tol=1e-10;
451   PetscScalar    u1,u2,nf1,nf2,njac11,njac12,njac21,njac22;
452   PetscScalar    b=-0.5, t=0.5, l=-0.5, r=0.5;
453   PetscScalar    *boundary;
454   AppCtx         *user;
455   DM             da;
456 
457   PetscFunctionBeginUser;
458   PetscCall(SNESGetDM(snes,&da));
459   PetscCall(PetscNew(&user));
460   *ouser   = user;
461   user->lb = .05;
462   user->ub = PETSC_INFINITY;
463   PetscCall(DMDAGetInfo(da,PETSC_IGNORE,&mx,&my,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE));
464 
465   /* Check if lower and upper bounds are set */
466   PetscCall(PetscOptionsGetScalar(NULL,NULL, "-lb", &user->lb, 0));
467   PetscCall(PetscOptionsGetScalar(NULL,NULL, "-ub", &user->ub, 0));
468   bsize=mx+2; lsize=my+2; rsize=my+2; tsize=mx+2;
469 
470   PetscCall(PetscMalloc1(bsize, &user->bottom));
471   PetscCall(PetscMalloc1(tsize, &user->top));
472   PetscCall(PetscMalloc1(lsize, &user->left));
473   PetscCall(PetscMalloc1(rsize, &user->right));
474 
475   hx= (r-l)/(mx+1.0); hy=(t-b)/(my+1.0);
476 
477   for (j=0; j<4; j++) {
478     if (j==0) {
479       yt       = b;
480       xt       = l;
481       limit    = bsize;
482       boundary = user->bottom;
483     } else if (j==1) {
484       yt       = t;
485       xt       = l;
486       limit    = tsize;
487       boundary = user->top;
488     } else if (j==2) {
489       yt       = b;
490       xt       = l;
491       limit    = lsize;
492       boundary = user->left;
493     } else { /* if  (j==3) */
494       yt       = b;
495       xt       = r;
496       limit    = rsize;
497       boundary = user->right;
498     }
499 
500     for (i=0; i<limit; i++) {
501       u1=xt;
502       u2=-yt;
503       for (k=0; k<maxits; k++) {
504         nf1   = u1 + u1*u2*u2 - u1*u1*u1/three-xt;
505         nf2   = -u2 - u1*u1*u2 + u2*u2*u2/three-yt;
506         fnorm = PetscRealPart(PetscSqrtScalar(nf1*nf1+nf2*nf2));
507         if (fnorm <= tol) break;
508         njac11=one+u2*u2-u1*u1;
509         njac12=two*u1*u2;
510         njac21=-two*u1*u2;
511         njac22=-one - u1*u1 + u2*u2;
512         det   = njac11*njac22-njac21*njac12;
513         u1    = u1-(njac22*nf1-njac12*nf2)/det;
514         u2    = u2-(njac11*nf2-njac21*nf1)/det;
515       }
516 
517       boundary[i]=u1*u1-u2*u2;
518       if (j==0 || j==1) xt=xt+hx;
519       else yt=yt+hy; /* if (j==2 || j==3) */
520     }
521   }
522   PetscFunctionReturn(0);
523 }
524 
525 PetscErrorCode DestroyBoundaryConditions(AppCtx **ouser)
526 {
527   AppCtx         *user = *ouser;
528 
529   PetscFunctionBeginUser;
530   PetscCall(PetscFree(user->bottom));
531   PetscCall(PetscFree(user->top));
532   PetscCall(PetscFree(user->left));
533   PetscCall(PetscFree(user->right));
534   PetscCall(PetscFree(*ouser));
535   PetscFunctionReturn(0);
536 }
537 
538 /* ------------------------------------------------------------------- */
539 /*
540    ComputeInitialGuess - Calculates the initial guess
541 
542    Input Parameters:
543 .  user - user-defined application context
544 .  X - vector for initial guess
545 
546    Output Parameters:
547 .  X - newly computed initial guess
548 */
549 PetscErrorCode ComputeInitialGuess(SNES snes, Vec X,void *dummy)
550 {
551   PetscInt       i,j,mx,my;
552   DM             da;
553   AppCtx         *user;
554   PetscScalar    **x;
555   PetscInt       xs,xm,ys,ym;
556 
557   PetscFunctionBeginUser;
558   PetscCall(SNESGetDM(snes,&da));
559   PetscCall(SNESGetApplicationContext(snes,&user));
560 
561   PetscCall(DMDAGetCorners(da,&xs,&ys,NULL,&xm,&ym,NULL));
562   PetscCall(DMDAGetInfo(da,PETSC_IGNORE,&mx,&my,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE));
563 
564   /* Get pointers to vector data */
565   PetscCall(DMDAVecGetArray(da,X,&x));
566   /* Perform local computations */
567   for (j=ys; j<ys+ym; j++) {
568     for (i=xs; i< xs+xm; i++) {
569       x[j][i] = (((j+1.0)*user->bottom[i+1]+(my-j+1.0)*user->top[i+1])/(my+2.0)+((i+1.0)*user->left[j+1]+(mx-i+1.0)*user->right[j+1])/(mx+2.0))/2.0;
570     }
571   }
572   /* Restore vectors */
573   PetscCall(DMDAVecRestoreArray(da,X,&x));
574   PetscFunctionReturn(0);
575 }
576 
577 /*TEST
578 
579    test:
580       args: -snes_type vinewtonrsls -pc_type mg -ksp_monitor_short -pc_mg_galerkin pmat -da_refine 5 -snes_vi_monitor -pc_mg_type full -snes_max_it 100 -snes_converged_reason
581       requires: !single
582 
583    test:
584       suffix: 2
585       args: -snes_type vinewtonssls -pc_type mg -ksp_monitor_short -pc_mg_galerkin pmat -da_refine 5 -snes_vi_monitor -pc_mg_type full -snes_max_it 100 -snes_converged_reason
586       requires: !single
587 
588 TEST*/
589