xref: /petsc/src/snes/tutorials/ex9.c (revision b122ec5aa1bd4469eb4e0673542fb7de3f411254)
1c4762a1bSJed Brown static const char help[] = "Solves obstacle problem in 2D as a variational inequality\n\
2c4762a1bSJed Brown or nonlinear complementarity problem.  This is a form of the Laplace equation in\n\
3c4762a1bSJed Brown which the solution u is constrained to be above a given function psi.  In the\n\
4c4762a1bSJed Brown problem here an exact solution is known.\n";
5c4762a1bSJed Brown 
6c4762a1bSJed Brown /*  On a square S = {-2<x<2,-2<y<2}, the PDE
7c4762a1bSJed Brown     u_{xx} + u_{yy} = 0
8c4762a1bSJed Brown is solved on the set where membrane is above obstacle (u(x,y) >= psi(x,y)).
9c4762a1bSJed Brown Here psi is the upper hemisphere of the unit ball.  On the boundary of S
10c4762a1bSJed Brown we have Dirichlet boundary conditions from the exact solution.  Uses centered
11c4762a1bSJed Brown FD scheme.  This example contributed by Ed Bueler.
12c4762a1bSJed Brown 
13c4762a1bSJed Brown Example usage:
14c4762a1bSJed Brown   * get help:
15c4762a1bSJed Brown     ./ex9 -help
16c4762a1bSJed Brown   * monitor run:
17c4762a1bSJed Brown     ./ex9 -da_refine 2 -snes_vi_monitor
18c4762a1bSJed Brown   * use other SNESVI type (default is SNESVINEWTONRSLS):
19c4762a1bSJed Brown     ./ex9 -da_refine 2 -snes_vi_monitor -snes_type vinewtonssls
20c4762a1bSJed Brown   * use FD evaluation of Jacobian by coloring, instead of analytical:
21c4762a1bSJed Brown     ./ex9 -da_refine 2 -snes_fd_color
22c4762a1bSJed Brown   * X windows visualizations:
23c4762a1bSJed Brown     ./ex9 -snes_monitor_solution draw -draw_pause 1 -da_refine 4
24c4762a1bSJed Brown     ./ex9 -snes_vi_monitor_residual -draw_pause 1 -da_refine 4
25c4762a1bSJed Brown   * full-cycle multigrid:
26c4762a1bSJed Brown     ./ex9 -snes_converged_reason -snes_grid_sequence 4 -pc_type mg
27c4762a1bSJed Brown   * serial convergence evidence:
28c4762a1bSJed Brown     for M in 3 4 5 6 7; do ./ex9 -snes_grid_sequence $M -pc_type mg; done
29c4762a1bSJed Brown   * FIXME sporadic parallel bug:
30c4762a1bSJed Brown     mpiexec -n 4 ./ex9 -snes_converged_reason -snes_grid_sequence 4 -pc_type mg
31c4762a1bSJed Brown */
32c4762a1bSJed Brown 
33c4762a1bSJed Brown #include <petsc.h>
34c4762a1bSJed Brown 
35c4762a1bSJed Brown /* z = psi(x,y) is the hemispherical obstacle, but made C^1 with "skirt" at r=r0 */
36c4762a1bSJed Brown PetscReal psi(PetscReal x, PetscReal y)
37c4762a1bSJed Brown {
38c4762a1bSJed Brown     const PetscReal  r = x * x + y * y,r0 = 0.9,psi0 = PetscSqrtReal(1.0 - r0*r0),dpsi0 = - r0 / psi0;
39c4762a1bSJed Brown     if (r <= r0) {
40c4762a1bSJed Brown       return PetscSqrtReal(1.0 - r);
41c4762a1bSJed Brown     } else {
42c4762a1bSJed Brown       return psi0 + dpsi0 * (r - r0);
43c4762a1bSJed Brown     }
44c4762a1bSJed Brown }
45c4762a1bSJed Brown 
46c4762a1bSJed Brown /*  This exact solution solves a 1D radial free-boundary problem for the
47c4762a1bSJed Brown Laplace equation, on the interval 0 < r < 2, with above obstacle psi(x,y).
48c4762a1bSJed Brown The Laplace equation applies where u(r) > psi(r),
49c4762a1bSJed Brown     u''(r) + r^-1 u'(r) = 0
50c4762a1bSJed Brown with boundary conditions including free b.c.s at an unknown location r = a:
51c4762a1bSJed Brown     u(a) = psi(a),  u'(a) = psi'(a),  u(2) = 0
52c4762a1bSJed Brown The solution is  u(r) = - A log(r) + B   on  r > a.  The boundary conditions
53c4762a1bSJed Brown can then be reduced to a root-finding problem for a:
54c4762a1bSJed Brown     a^2 (log(2) - log(a)) = 1 - a^2
55c4762a1bSJed Brown The solution is a = 0.697965148223374 (giving residual 1.5e-15).  Then
56c4762a1bSJed Brown A = a^2*(1-a^2)^(-0.5) and B = A*log(2) are as given below in the code.  */
57c4762a1bSJed Brown PetscReal u_exact(PetscReal x, PetscReal y)
58c4762a1bSJed Brown {
59c4762a1bSJed Brown     const PetscReal afree = 0.697965148223374,
60c4762a1bSJed Brown                     A     = 0.680259411891719,
61c4762a1bSJed Brown                     B     = 0.471519893402112;
62c4762a1bSJed Brown     PetscReal  r;
63c4762a1bSJed Brown     r = PetscSqrtReal(x * x + y * y);
64c4762a1bSJed Brown     return (r <= afree) ? psi(x,y)  /* active set; on the obstacle */
65c4762a1bSJed Brown                         : - A * PetscLogReal(r) + B; /* solves laplace eqn */
66c4762a1bSJed Brown }
67c4762a1bSJed Brown 
68c4762a1bSJed Brown extern PetscErrorCode FormExactSolution(DMDALocalInfo*,Vec);
69c4762a1bSJed Brown extern PetscErrorCode FormBounds(SNES,Vec,Vec);
70c4762a1bSJed Brown extern PetscErrorCode FormFunctionLocal(DMDALocalInfo*,PetscReal**,PetscReal**,void*);
71c4762a1bSJed Brown extern PetscErrorCode FormJacobianLocal(DMDALocalInfo*,PetscReal**,Mat,Mat,void*);
72c4762a1bSJed Brown 
73c4762a1bSJed Brown int main(int argc,char **argv)
74c4762a1bSJed Brown {
75c4762a1bSJed Brown   PetscErrorCode      ierr;
76c4762a1bSJed Brown   SNES                snes;
77c4762a1bSJed Brown   DM                  da, da_after;
78c4762a1bSJed Brown   Vec                 u, u_exact;
79c4762a1bSJed Brown   DMDALocalInfo       info;
80c4762a1bSJed Brown   PetscReal           error1,errorinf;
81c4762a1bSJed Brown 
82*b122ec5aSJacob Faibussowitsch   CHKERRQ(PetscInitialize(&argc,&argv,(char*)0,help));
83c4762a1bSJed Brown 
84c4762a1bSJed Brown   ierr = DMDACreate2d(PETSC_COMM_WORLD,DM_BOUNDARY_NONE,DM_BOUNDARY_NONE,
85c4762a1bSJed Brown                       DMDA_STENCIL_STAR,5,5, /* 5x5 coarse grid; override with -da_grid_x,_y */
86c4762a1bSJed Brown                       PETSC_DECIDE,PETSC_DECIDE,
87c4762a1bSJed Brown                       1,1,  /* dof=1 and s = 1 (stencil extends out one cell) */
88c4762a1bSJed Brown                       NULL,NULL,&da);CHKERRQ(ierr);
895f80ce2aSJacob Faibussowitsch   CHKERRQ(DMSetFromOptions(da));
905f80ce2aSJacob Faibussowitsch   CHKERRQ(DMSetUp(da));
915f80ce2aSJacob Faibussowitsch   CHKERRQ(DMDASetUniformCoordinates(da,-2.0,2.0,-2.0,2.0,0.0,1.0));
92c4762a1bSJed Brown 
935f80ce2aSJacob Faibussowitsch   CHKERRQ(DMCreateGlobalVector(da,&u));
945f80ce2aSJacob Faibussowitsch   CHKERRQ(VecSet(u,0.0));
95c4762a1bSJed Brown 
965f80ce2aSJacob Faibussowitsch   CHKERRQ(SNESCreate(PETSC_COMM_WORLD,&snes));
975f80ce2aSJacob Faibussowitsch   CHKERRQ(SNESSetDM(snes,da));
985f80ce2aSJacob Faibussowitsch   CHKERRQ(SNESSetType(snes,SNESVINEWTONRSLS));
995f80ce2aSJacob Faibussowitsch   CHKERRQ(SNESVISetComputeVariableBounds(snes,&FormBounds));
1005f80ce2aSJacob Faibussowitsch   CHKERRQ(DMDASNESSetFunctionLocal(da,INSERT_VALUES,(DMDASNESFunction)FormFunctionLocal,NULL));
1015f80ce2aSJacob Faibussowitsch   CHKERRQ(DMDASNESSetJacobianLocal(da,(DMDASNESJacobian)FormJacobianLocal,NULL));
1025f80ce2aSJacob Faibussowitsch   CHKERRQ(SNESSetFromOptions(snes));
103c4762a1bSJed Brown 
104c4762a1bSJed Brown   /* solve nonlinear system */
1055f80ce2aSJacob Faibussowitsch   CHKERRQ(SNESSolve(snes,NULL,u));
1065f80ce2aSJacob Faibussowitsch   CHKERRQ(VecDestroy(&u));
1075f80ce2aSJacob Faibussowitsch   CHKERRQ(DMDestroy(&da));
108c4762a1bSJed Brown   /* DMDA after solve may be different, e.g. with -snes_grid_sequence */
1095f80ce2aSJacob Faibussowitsch   CHKERRQ(SNESGetDM(snes,&da_after));
1105f80ce2aSJacob Faibussowitsch   CHKERRQ(SNESGetSolution(snes,&u)); /* do not destroy u */
1115f80ce2aSJacob Faibussowitsch   CHKERRQ(DMDAGetLocalInfo(da_after,&info));
1125f80ce2aSJacob Faibussowitsch   CHKERRQ(VecDuplicate(u,&u_exact));
1135f80ce2aSJacob Faibussowitsch   CHKERRQ(FormExactSolution(&info,u_exact));
1145f80ce2aSJacob Faibussowitsch   CHKERRQ(VecAXPY(u,-1.0,u_exact)); /* u <-- u - u_exact */
1155f80ce2aSJacob Faibussowitsch   CHKERRQ(VecNorm(u,NORM_1,&error1));
116c4762a1bSJed Brown   error1 /= (PetscReal)info.mx * (PetscReal)info.my; /* average error */
1175f80ce2aSJacob Faibussowitsch   CHKERRQ(VecNorm(u,NORM_INFINITY,&errorinf));
1185f80ce2aSJacob Faibussowitsch   CHKERRQ(PetscPrintf(PETSC_COMM_WORLD,"errors on %D x %D grid:  av |u-uexact|  = %.3e,  |u-uexact|_inf = %.3e\n",info.mx,info.my,(double)error1,(double)errorinf));
1195f80ce2aSJacob Faibussowitsch   CHKERRQ(VecDestroy(&u_exact));
1205f80ce2aSJacob Faibussowitsch   CHKERRQ(SNESDestroy(&snes));
1215f80ce2aSJacob Faibussowitsch   CHKERRQ(DMDestroy(&da));
122*b122ec5aSJacob Faibussowitsch   CHKERRQ(PetscFinalize());
123*b122ec5aSJacob Faibussowitsch   return 0;
124c4762a1bSJed Brown }
125c4762a1bSJed Brown 
126c4762a1bSJed Brown PetscErrorCode FormExactSolution(DMDALocalInfo *info, Vec u)
127c4762a1bSJed Brown {
128c4762a1bSJed Brown   PetscInt       i,j;
129c4762a1bSJed Brown   PetscReal      **au, dx, dy, x, y;
130c4762a1bSJed Brown   dx = 4.0 / (PetscReal)(info->mx-1);
131c4762a1bSJed Brown   dy = 4.0 / (PetscReal)(info->my-1);
1325f80ce2aSJacob Faibussowitsch   CHKERRQ(DMDAVecGetArray(info->da, u, &au));
133c4762a1bSJed Brown   for (j=info->ys; j<info->ys+info->ym; j++) {
134c4762a1bSJed Brown     y = -2.0 + j * dy;
135c4762a1bSJed Brown     for (i=info->xs; i<info->xs+info->xm; i++) {
136c4762a1bSJed Brown       x = -2.0 + i * dx;
137c4762a1bSJed Brown       au[j][i] = u_exact(x,y);
138c4762a1bSJed Brown     }
139c4762a1bSJed Brown   }
1405f80ce2aSJacob Faibussowitsch   CHKERRQ(DMDAVecRestoreArray(info->da, u, &au));
141c4762a1bSJed Brown   return 0;
142c4762a1bSJed Brown }
143c4762a1bSJed Brown 
144c4762a1bSJed Brown PetscErrorCode FormBounds(SNES snes, Vec Xl, Vec Xu)
145c4762a1bSJed Brown {
146c4762a1bSJed Brown   DM             da;
147c4762a1bSJed Brown   DMDALocalInfo  info;
148c4762a1bSJed Brown   PetscInt       i, j;
149c4762a1bSJed Brown   PetscReal      **aXl, dx, dy, x, y;
150c4762a1bSJed Brown 
1515f80ce2aSJacob Faibussowitsch   CHKERRQ(SNESGetDM(snes,&da));
1525f80ce2aSJacob Faibussowitsch   CHKERRQ(DMDAGetLocalInfo(da,&info));
153c4762a1bSJed Brown   dx = 4.0 / (PetscReal)(info.mx-1);
154c4762a1bSJed Brown   dy = 4.0 / (PetscReal)(info.my-1);
1555f80ce2aSJacob Faibussowitsch   CHKERRQ(DMDAVecGetArray(da, Xl, &aXl));
156c4762a1bSJed Brown   for (j=info.ys; j<info.ys+info.ym; j++) {
157c4762a1bSJed Brown     y = -2.0 + j * dy;
158c4762a1bSJed Brown     for (i=info.xs; i<info.xs+info.xm; i++) {
159c4762a1bSJed Brown       x = -2.0 + i * dx;
160c4762a1bSJed Brown       aXl[j][i] = psi(x,y);
161c4762a1bSJed Brown     }
162c4762a1bSJed Brown   }
1635f80ce2aSJacob Faibussowitsch   CHKERRQ(DMDAVecRestoreArray(da, Xl, &aXl));
1645f80ce2aSJacob Faibussowitsch   CHKERRQ(VecSet(Xu,PETSC_INFINITY));
165c4762a1bSJed Brown   return 0;
166c4762a1bSJed Brown }
167c4762a1bSJed Brown 
168c4762a1bSJed Brown PetscErrorCode FormFunctionLocal(DMDALocalInfo *info, PetscScalar **au, PetscScalar **af, void *user)
169c4762a1bSJed Brown {
170c4762a1bSJed Brown   PetscInt       i,j;
171c4762a1bSJed Brown   PetscReal      dx,dy,x,y,ue,un,us,uw;
172c4762a1bSJed Brown 
173c4762a1bSJed Brown   PetscFunctionBeginUser;
174c4762a1bSJed Brown   dx = 4.0 / (PetscReal)(info->mx-1);
175c4762a1bSJed Brown   dy = 4.0 / (PetscReal)(info->my-1);
176c4762a1bSJed Brown   for (j=info->ys; j<info->ys+info->ym; j++) {
177c4762a1bSJed Brown     y = -2.0 + j * dy;
178c4762a1bSJed Brown     for (i=info->xs; i<info->xs+info->xm; i++) {
179c4762a1bSJed Brown       x = -2.0 + i * dx;
180c4762a1bSJed Brown       if (i == 0 || j == 0 || i == info->mx-1 || j == info->my-1) {
181c4762a1bSJed Brown         af[j][i] = 4.0 * (au[j][i] - u_exact(x,y));
182c4762a1bSJed Brown       } else {
183c4762a1bSJed Brown         uw = (i-1 == 0)          ? u_exact(x-dx,y) : au[j][i-1];
184c4762a1bSJed Brown         ue = (i+1 == info->mx-1) ? u_exact(x+dx,y) : au[j][i+1];
185c4762a1bSJed Brown         us = (j-1 == 0)          ? u_exact(x,y-dy) : au[j-1][i];
186c4762a1bSJed Brown         un = (j+1 == info->my-1) ? u_exact(x,y+dy) : au[j+1][i];
187c4762a1bSJed Brown         af[j][i] = - (dy/dx) * (uw - 2.0 * au[j][i] + ue) - (dx/dy) * (us - 2.0 * au[j][i] + un);
188c4762a1bSJed Brown       }
189c4762a1bSJed Brown     }
190c4762a1bSJed Brown   }
1915f80ce2aSJacob Faibussowitsch   CHKERRQ(PetscLogFlops(12.0*info->ym*info->xm));
192c4762a1bSJed Brown   PetscFunctionReturn(0);
193c4762a1bSJed Brown }
194c4762a1bSJed Brown 
195c4762a1bSJed Brown PetscErrorCode FormJacobianLocal(DMDALocalInfo *info, PetscScalar **au, Mat A, Mat jac, void *user)
196c4762a1bSJed Brown {
197c4762a1bSJed Brown   PetscInt       i,j,n;
198c4762a1bSJed Brown   MatStencil     col[5],row;
199c4762a1bSJed Brown   PetscReal      v[5],dx,dy,oxx,oyy;
200c4762a1bSJed Brown 
201c4762a1bSJed Brown   PetscFunctionBeginUser;
202c4762a1bSJed Brown   dx  = 4.0 / (PetscReal)(info->mx-1);
203c4762a1bSJed Brown   dy  = 4.0 / (PetscReal)(info->my-1);
204c4762a1bSJed Brown   oxx = dy / dx;
205c4762a1bSJed Brown   oyy = dx / dy;
206c4762a1bSJed Brown   for (j=info->ys; j<info->ys+info->ym; j++) {
207c4762a1bSJed Brown     for (i=info->xs; i<info->xs+info->xm; i++) {
208c4762a1bSJed Brown       row.j = j; row.i = i;
209c4762a1bSJed Brown       if (i == 0 || j == 0 || i == info->mx-1 || j == info->my-1) { /* boundary */
210c4762a1bSJed Brown         v[0] = 4.0;
2115f80ce2aSJacob Faibussowitsch         CHKERRQ(MatSetValuesStencil(jac,1,&row,1,&row,v,INSERT_VALUES));
212c4762a1bSJed Brown       } else { /* interior grid points */
213c4762a1bSJed Brown         v[0] = 2.0 * (oxx + oyy);  col[0].j = j;  col[0].i = i;
214c4762a1bSJed Brown         n = 1;
215c4762a1bSJed Brown         if (i-1 > 0) {
216c4762a1bSJed Brown           v[n] = -oxx;  col[n].j = j;  col[n++].i = i-1;
217c4762a1bSJed Brown         }
218c4762a1bSJed Brown         if (i+1 < info->mx-1) {
219c4762a1bSJed Brown           v[n] = -oxx;  col[n].j = j;  col[n++].i = i+1;
220c4762a1bSJed Brown         }
221c4762a1bSJed Brown         if (j-1 > 0) {
222c4762a1bSJed Brown           v[n] = -oyy;  col[n].j = j-1;  col[n++].i = i;
223c4762a1bSJed Brown         }
224c4762a1bSJed Brown         if (j+1 < info->my-1) {
225c4762a1bSJed Brown           v[n] = -oyy;  col[n].j = j+1;  col[n++].i = i;
226c4762a1bSJed Brown         }
2275f80ce2aSJacob Faibussowitsch         CHKERRQ(MatSetValuesStencil(jac,1,&row,n,col,v,INSERT_VALUES));
228c4762a1bSJed Brown       }
229c4762a1bSJed Brown     }
230c4762a1bSJed Brown   }
231c4762a1bSJed Brown 
232c4762a1bSJed Brown   /* Assemble matrix, using the 2-step process: */
2335f80ce2aSJacob Faibussowitsch   CHKERRQ(MatAssemblyBegin(jac,MAT_FINAL_ASSEMBLY));
2345f80ce2aSJacob Faibussowitsch   CHKERRQ(MatAssemblyEnd(jac,MAT_FINAL_ASSEMBLY));
235c4762a1bSJed Brown   if (A != jac) {
2365f80ce2aSJacob Faibussowitsch     CHKERRQ(MatAssemblyBegin(A,MAT_FINAL_ASSEMBLY));
2375f80ce2aSJacob Faibussowitsch     CHKERRQ(MatAssemblyEnd(A,MAT_FINAL_ASSEMBLY));
238c4762a1bSJed Brown   }
2395f80ce2aSJacob Faibussowitsch   CHKERRQ(PetscLogFlops(2.0*info->ym*info->xm));
240c4762a1bSJed Brown   PetscFunctionReturn(0);
241c4762a1bSJed Brown }
242c4762a1bSJed Brown 
243c4762a1bSJed Brown /*TEST
244c4762a1bSJed Brown 
245c4762a1bSJed Brown    build:
246c4762a1bSJed Brown       requires: !complex
247c4762a1bSJed Brown 
248c4762a1bSJed Brown    test:
249c4762a1bSJed Brown       suffix: 1
250c4762a1bSJed Brown       requires: !single
251c4762a1bSJed Brown       nsize: 1
252c4762a1bSJed Brown       args: -da_refine 1 -snes_monitor_short -snes_type vinewtonrsls
253c4762a1bSJed Brown 
254c4762a1bSJed Brown    test:
255c4762a1bSJed Brown       suffix: 2
256c4762a1bSJed Brown       requires: !single
257c4762a1bSJed Brown       nsize: 2
258c4762a1bSJed Brown       args: -da_refine 1 -snes_monitor_short -snes_type vinewtonssls
259c4762a1bSJed Brown 
260c4762a1bSJed Brown    test:
261c4762a1bSJed Brown       suffix: 3
262c4762a1bSJed Brown       requires: !single
263c4762a1bSJed Brown       nsize: 2
264c4762a1bSJed Brown       args: -snes_grid_sequence 2 -snes_vi_monitor -snes_type vinewtonrsls
265c4762a1bSJed Brown 
266c4762a1bSJed Brown    test:
267c4762a1bSJed Brown       suffix: mg
268c4762a1bSJed Brown       requires: !single
269c4762a1bSJed Brown       nsize: 4
270c4762a1bSJed Brown       args: -snes_grid_sequence 3 -snes_converged_reason -pc_type mg
271c4762a1bSJed Brown 
272c4762a1bSJed Brown    test:
273c4762a1bSJed Brown       suffix: 4
274c4762a1bSJed Brown       nsize: 1
275c4762a1bSJed Brown       args: -mat_is_symmetric
276c4762a1bSJed Brown 
277c4762a1bSJed Brown    test:
278c4762a1bSJed Brown       suffix: 5
279c4762a1bSJed Brown       nsize: 1
280c4762a1bSJed Brown       args: -ksp_converged_reason -snes_fd_color
281c4762a1bSJed Brown 
282c4762a1bSJed Brown    test:
283c4762a1bSJed Brown       suffix: 6
284c4762a1bSJed Brown       requires: !single
285c4762a1bSJed Brown       nsize: 2
286c4762a1bSJed Brown       args: -snes_grid_sequence 2 -pc_type mg -snes_monitor_short -ksp_converged_reason
287c4762a1bSJed Brown 
288c4762a1bSJed Brown    test:
289c4762a1bSJed Brown       suffix: 7
290c4762a1bSJed Brown       nsize: 2
291c4762a1bSJed Brown       args: -da_refine 1 -snes_monitor_short -snes_type composite -snes_composite_type multiplicative -snes_composite_sneses vinewtonrsls,vinewtonssls -sub_0_snes_vi_monitor -sub_1_snes_vi_monitor
292c4762a1bSJed Brown       TODO: fix nasty memory leak in SNESCOMPOSITE
293c4762a1bSJed Brown 
294c4762a1bSJed Brown    test:
295c4762a1bSJed Brown       suffix: 8
296c4762a1bSJed Brown       nsize: 2
297c4762a1bSJed Brown       args: -da_refine 1 -snes_monitor_short -snes_type composite -snes_composite_type additive -snes_composite_sneses vinewtonrsls -sub_0_snes_vi_monitor
298c4762a1bSJed Brown       TODO: fix nasty memory leak in SNESCOMPOSITE
299c4762a1bSJed Brown 
300c4762a1bSJed Brown    test:
301c4762a1bSJed Brown       suffix: 9
302c4762a1bSJed Brown       nsize: 2
303c4762a1bSJed Brown       args: -da_refine 1 -snes_monitor_short -snes_type composite -snes_composite_type additiveoptimal -snes_composite_sneses vinewtonrsls -sub_0_snes_vi_monitor
304c4762a1bSJed Brown       TODO: fix nasty memory leak in SNESCOMPOSITE
305c4762a1bSJed Brown 
306c4762a1bSJed Brown TEST*/
307