xref: /petsc/src/snes/tutorials/ex14.c (revision b122ec5aa1bd4469eb4e0673542fb7de3f411254)
1 
2 static char help[] = "Bratu nonlinear PDE in 3d.\n\
3 We solve the  Bratu (SFI - solid fuel ignition) problem in a 3D rectangular\n\
4 domain, using distributed arrays (DMDAs) to partition the parallel grid.\n\
5 The command line options include:\n\
6   -par <parameter>, where <parameter> indicates the problem's nonlinearity\n\
7      problem SFI:  <parameter> = Bratu parameter (0 <= par <= 6.81)\n\n";
8 
9 /*T
10    Concepts: SNES^parallel Bratu example
11    Concepts: DMDA^using distributed arrays;
12    Processors: n
13 T*/
14 
15 /* ------------------------------------------------------------------------
16 
17     Solid Fuel Ignition (SFI) problem.  This problem is modeled by
18     the partial differential equation
19 
20             -Laplacian u - lambda*exp(u) = 0,  0 < x,y < 1,
21 
22     with boundary conditions
23 
24              u = 0  for  x = 0, x = 1, y = 0, y = 1, z = 0, z = 1
25 
26     A finite difference approximation with the usual 7-point stencil
27     is used to discretize the boundary value problem to obtain a nonlinear
28     system of equations.
29 
30   ------------------------------------------------------------------------- */
31 
32 /*
33    Include "petscdmda.h" so that we can use distributed arrays (DMDAs).
34    Include "petscsnes.h" so that we can use SNES solvers.  Note that this
35    file automatically includes:
36      petscsys.h       - base PETSc routines   petscvec.h - vectors
37      petscmat.h - matrices
38      petscis.h     - index sets            petscksp.h - Krylov subspace methods
39      petscviewer.h - viewers               petscpc.h  - preconditioners
40      petscksp.h   - linear solvers
41 */
42 #include <petscdm.h>
43 #include <petscdmda.h>
44 #include <petscsnes.h>
45 
46 /*
47    User-defined application context - contains data needed by the
48    application-provided call-back routines, FormJacobian() and
49    FormFunction().
50 */
51 typedef struct {
52   PetscReal param;             /* test problem parameter */
53   DM        da;                /* distributed array data structure */
54 } AppCtx;
55 
56 /*
57    User-defined routines
58 */
59 extern PetscErrorCode FormFunctionLocal(SNES,Vec,Vec,void*);
60 extern PetscErrorCode FormFunction(SNES,Vec,Vec,void*);
61 extern PetscErrorCode FormInitialGuess(AppCtx*,Vec);
62 extern PetscErrorCode FormJacobian(SNES,Vec,Mat,Mat,void*);
63 
64 int main(int argc,char **argv)
65 {
66   SNES           snes;                         /* nonlinear solver */
67   Vec            x,r;                          /* solution, residual vectors */
68   Mat            J = NULL;                            /* Jacobian matrix */
69   AppCtx         user;                         /* user-defined work context */
70   PetscInt       its;                          /* iterations for convergence */
71   MatFDColoring  matfdcoloring = NULL;
72   PetscBool      matrix_free = PETSC_FALSE,coloring = PETSC_FALSE, coloring_ds = PETSC_FALSE,local_coloring = PETSC_FALSE;
73   PetscReal      bratu_lambda_max = 6.81,bratu_lambda_min = 0.,fnorm;
74 
75   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
76      Initialize program
77      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
78 
79   CHKERRQ(PetscInitialize(&argc,&argv,(char*)0,help));
80 
81   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
82      Initialize problem parameters
83   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
84   user.param = 6.0;
85   CHKERRQ(PetscOptionsGetReal(NULL,NULL,"-par",&user.param,NULL));
86   PetscCheckFalse(user.param >= bratu_lambda_max || user.param <= bratu_lambda_min,PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Lambda is out of range");
87 
88   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
89      Create nonlinear solver context
90      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
91   CHKERRQ(SNESCreate(PETSC_COMM_WORLD,&snes));
92 
93   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
94      Create distributed array (DMDA) to manage parallel grid and vectors
95   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
96   CHKERRQ(DMDACreate3d(PETSC_COMM_WORLD,DM_BOUNDARY_NONE,DM_BOUNDARY_NONE,DM_BOUNDARY_NONE,DMDA_STENCIL_STAR,4,4,4,PETSC_DECIDE,PETSC_DECIDE,PETSC_DECIDE,1,1,NULL,NULL,NULL,&user.da));
97   CHKERRQ(DMSetFromOptions(user.da));
98   CHKERRQ(DMSetUp(user.da));
99   /*  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
100      Extract global vectors from DMDA; then duplicate for remaining
101      vectors that are the same types
102    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
103   CHKERRQ(DMCreateGlobalVector(user.da,&x));
104   CHKERRQ(VecDuplicate(x,&r));
105 
106   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
107      Set function evaluation routine and vector
108   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
109   CHKERRQ(SNESSetFunction(snes,r,FormFunction,(void*)&user));
110 
111   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
112      Create matrix data structure; set Jacobian evaluation routine
113 
114      Set Jacobian matrix data structure and default Jacobian evaluation
115      routine. User can override with:
116      -snes_mf : matrix-free Newton-Krylov method with no preconditioning
117                 (unless user explicitly sets preconditioner)
118      -snes_mf_operator : form preconditioning matrix as set by the user,
119                          but use matrix-free approx for Jacobian-vector
120                          products within Newton-Krylov method
121      -fdcoloring : using finite differences with coloring to compute the Jacobian
122 
123      Note one can use -matfd_coloring wp or ds the only reason for the -fdcoloring_ds option
124      below is to test the call to MatFDColoringSetType().
125      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
126   CHKERRQ(PetscOptionsGetBool(NULL,NULL,"-snes_mf",&matrix_free,NULL));
127   CHKERRQ(PetscOptionsGetBool(NULL,NULL,"-fdcoloring",&coloring,NULL));
128   CHKERRQ(PetscOptionsGetBool(NULL,NULL,"-fdcoloring_ds",&coloring_ds,NULL));
129   CHKERRQ(PetscOptionsGetBool(NULL,NULL,"-fdcoloring_local",&local_coloring,NULL));
130   if (!matrix_free) {
131     CHKERRQ(DMSetMatType(user.da,MATAIJ));
132     CHKERRQ(DMCreateMatrix(user.da,&J));
133     if (coloring) {
134       ISColoring iscoloring;
135       if (!local_coloring) {
136         CHKERRQ(DMCreateColoring(user.da,IS_COLORING_GLOBAL,&iscoloring));
137         CHKERRQ(MatFDColoringCreate(J,iscoloring,&matfdcoloring));
138         CHKERRQ(MatFDColoringSetFunction(matfdcoloring,(PetscErrorCode (*)(void))FormFunction,&user));
139       } else {
140         CHKERRQ(DMCreateColoring(user.da,IS_COLORING_LOCAL,&iscoloring));
141         CHKERRQ(MatFDColoringCreate(J,iscoloring,&matfdcoloring));
142         CHKERRQ(MatFDColoringUseDM(J,matfdcoloring));
143         CHKERRQ(MatFDColoringSetFunction(matfdcoloring,(PetscErrorCode (*)(void))FormFunctionLocal,&user));
144       }
145       if (coloring_ds) {
146         CHKERRQ(MatFDColoringSetType(matfdcoloring,MATMFFD_DS));
147       }
148       CHKERRQ(MatFDColoringSetFromOptions(matfdcoloring));
149       CHKERRQ(MatFDColoringSetUp(J,iscoloring,matfdcoloring));
150       CHKERRQ(SNESSetJacobian(snes,J,J,SNESComputeJacobianDefaultColor,matfdcoloring));
151       CHKERRQ(ISColoringDestroy(&iscoloring));
152     } else {
153       CHKERRQ(SNESSetJacobian(snes,J,J,FormJacobian,&user));
154     }
155   }
156 
157   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
158      Customize nonlinear solver; set runtime options
159    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
160   CHKERRQ(SNESSetDM(snes,user.da));
161   CHKERRQ(SNESSetFromOptions(snes));
162 
163   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
164      Evaluate initial guess
165      Note: The user should initialize the vector, x, with the initial guess
166      for the nonlinear solver prior to calling SNESSolve().  In particular,
167      to employ an initial guess of zero, the user should explicitly set
168      this vector to zero by calling VecSet().
169   */
170   CHKERRQ(FormInitialGuess(&user,x));
171 
172   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
173      Solve nonlinear system
174      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
175   CHKERRQ(SNESSolve(snes,NULL,x));
176   CHKERRQ(SNESGetIterationNumber(snes,&its));
177 
178   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
179      Explicitly check norm of the residual of the solution
180    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
181   CHKERRQ(FormFunction(snes,x,r,(void*)&user));
182   CHKERRQ(VecNorm(r,NORM_2,&fnorm));
183   CHKERRQ(PetscPrintf(PETSC_COMM_WORLD,"Number of SNES iterations = %D fnorm %g\n",its,(double)fnorm));
184 
185   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
186      Free work space.  All PETSc objects should be destroyed when they
187      are no longer needed.
188    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
189 
190   CHKERRQ(MatDestroy(&J));
191   CHKERRQ(VecDestroy(&x));
192   CHKERRQ(VecDestroy(&r));
193   CHKERRQ(SNESDestroy(&snes));
194   CHKERRQ(DMDestroy(&user.da));
195   CHKERRQ(MatFDColoringDestroy(&matfdcoloring));
196   CHKERRQ(PetscFinalize());
197   return 0;
198 }
199 /* ------------------------------------------------------------------- */
200 /*
201    FormInitialGuess - Forms initial approximation.
202 
203    Input Parameters:
204    user - user-defined application context
205    X - vector
206 
207    Output Parameter:
208    X - vector
209  */
210 PetscErrorCode FormInitialGuess(AppCtx *user,Vec X)
211 {
212   PetscInt       i,j,k,Mx,My,Mz,xs,ys,zs,xm,ym,zm;
213   PetscReal      lambda,temp1,hx,hy,hz,tempk,tempj;
214   PetscScalar    ***x;
215 
216   PetscFunctionBeginUser;
217   CHKERRQ(DMDAGetInfo(user->da,PETSC_IGNORE,&Mx,&My,&Mz,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE));
218 
219   lambda = user->param;
220   hx     = 1.0/(PetscReal)(Mx-1);
221   hy     = 1.0/(PetscReal)(My-1);
222   hz     = 1.0/(PetscReal)(Mz-1);
223   temp1  = lambda/(lambda + 1.0);
224 
225   /*
226      Get a pointer to vector data.
227        - For default PETSc vectors, VecGetArray() returns a pointer to
228          the data array.  Otherwise, the routine is implementation dependent.
229        - You MUST call VecRestoreArray() when you no longer need access to
230          the array.
231   */
232   CHKERRQ(DMDAVecGetArray(user->da,X,&x));
233 
234   /*
235      Get local grid boundaries (for 3-dimensional DMDA):
236        xs, ys, zs   - starting grid indices (no ghost points)
237        xm, ym, zm   - widths of local grid (no ghost points)
238 
239   */
240   CHKERRQ(DMDAGetCorners(user->da,&xs,&ys,&zs,&xm,&ym,&zm));
241 
242   /*
243      Compute initial guess over the locally owned part of the grid
244   */
245   for (k=zs; k<zs+zm; k++) {
246     tempk = (PetscReal)(PetscMin(k,Mz-k-1))*hz;
247     for (j=ys; j<ys+ym; j++) {
248       tempj = PetscMin((PetscReal)(PetscMin(j,My-j-1))*hy,tempk);
249       for (i=xs; i<xs+xm; i++) {
250         if (i == 0 || j == 0 || k == 0 || i == Mx-1 || j == My-1 || k == Mz-1) {
251           /* boundary conditions are all zero Dirichlet */
252           x[k][j][i] = 0.0;
253         } else {
254           x[k][j][i] = temp1*PetscSqrtReal(PetscMin((PetscReal)(PetscMin(i,Mx-i-1))*hx,tempj));
255         }
256       }
257     }
258   }
259 
260   /*
261      Restore vector
262   */
263   CHKERRQ(DMDAVecRestoreArray(user->da,X,&x));
264   PetscFunctionReturn(0);
265 }
266 /* ------------------------------------------------------------------- */
267 /*
268    FormFunctionLocal - Evaluates nonlinear function, F(x) on a ghosted patch
269 
270    Input Parameters:
271 .  snes - the SNES context
272 .  localX - input vector, this contains the ghosted region needed
273 .  ptr - optional user-defined context, as set by SNESSetFunction()
274 
275    Output Parameter:
276 .  F - function vector, this does not contain a ghosted region
277  */
278 PetscErrorCode FormFunctionLocal(SNES snes,Vec localX,Vec F,void *ptr)
279 {
280   AppCtx         *user = (AppCtx*)ptr;
281   PetscInt       i,j,k,Mx,My,Mz,xs,ys,zs,xm,ym,zm;
282   PetscReal      two = 2.0,lambda,hx,hy,hz,hxhzdhy,hyhzdhx,hxhydhz,sc;
283   PetscScalar    u_north,u_south,u_east,u_west,u_up,u_down,u,u_xx,u_yy,u_zz,***x,***f;
284   DM             da;
285 
286   PetscFunctionBeginUser;
287   CHKERRQ(SNESGetDM(snes,&da));
288   CHKERRQ(DMDAGetInfo(da,PETSC_IGNORE,&Mx,&My,&Mz,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE));
289 
290   lambda  = user->param;
291   hx      = 1.0/(PetscReal)(Mx-1);
292   hy      = 1.0/(PetscReal)(My-1);
293   hz      = 1.0/(PetscReal)(Mz-1);
294   sc      = hx*hy*hz*lambda;
295   hxhzdhy = hx*hz/hy;
296   hyhzdhx = hy*hz/hx;
297   hxhydhz = hx*hy/hz;
298 
299   /*
300      Get pointers to vector data
301   */
302   CHKERRQ(DMDAVecGetArrayRead(da,localX,&x));
303   CHKERRQ(DMDAVecGetArray(da,F,&f));
304 
305   /*
306      Get local grid boundaries
307   */
308   CHKERRQ(DMDAGetCorners(da,&xs,&ys,&zs,&xm,&ym,&zm));
309 
310   /*
311      Compute function over the locally owned part of the grid
312   */
313   for (k=zs; k<zs+zm; k++) {
314     for (j=ys; j<ys+ym; j++) {
315       for (i=xs; i<xs+xm; i++) {
316         if (i == 0 || j == 0 || k == 0 || i == Mx-1 || j == My-1 || k == Mz-1) {
317           f[k][j][i] = x[k][j][i];
318         } else {
319           u          = x[k][j][i];
320           u_east     = x[k][j][i+1];
321           u_west     = x[k][j][i-1];
322           u_north    = x[k][j+1][i];
323           u_south    = x[k][j-1][i];
324           u_up       = x[k+1][j][i];
325           u_down     = x[k-1][j][i];
326           u_xx       = (-u_east + two*u - u_west)*hyhzdhx;
327           u_yy       = (-u_north + two*u - u_south)*hxhzdhy;
328           u_zz       = (-u_up + two*u - u_down)*hxhydhz;
329           f[k][j][i] = u_xx + u_yy + u_zz - sc*PetscExpScalar(u);
330         }
331       }
332     }
333   }
334 
335   /*
336      Restore vectors
337   */
338   CHKERRQ(DMDAVecRestoreArrayRead(da,localX,&x));
339   CHKERRQ(DMDAVecRestoreArray(da,F,&f));
340   CHKERRQ(PetscLogFlops(11.0*ym*xm));
341   PetscFunctionReturn(0);
342 }
343 /* ------------------------------------------------------------------- */
344 /*
345    FormFunction - Evaluates nonlinear function, F(x) on the entire domain
346 
347    Input Parameters:
348 .  snes - the SNES context
349 .  X - input vector
350 .  ptr - optional user-defined context, as set by SNESSetFunction()
351 
352    Output Parameter:
353 .  F - function vector
354  */
355 PetscErrorCode FormFunction(SNES snes,Vec X,Vec F,void *ptr)
356 {
357   Vec            localX;
358   DM             da;
359 
360   PetscFunctionBeginUser;
361   CHKERRQ(SNESGetDM(snes,&da));
362   CHKERRQ(DMGetLocalVector(da,&localX));
363 
364   /*
365      Scatter ghost points to local vector,using the 2-step process
366         DMGlobalToLocalBegin(),DMGlobalToLocalEnd().
367      By placing code between these two statements, computations can be
368      done while messages are in transition.
369   */
370   CHKERRQ(DMGlobalToLocalBegin(da,X,INSERT_VALUES,localX));
371   CHKERRQ(DMGlobalToLocalEnd(da,X,INSERT_VALUES,localX));
372 
373   CHKERRQ(FormFunctionLocal(snes,localX,F,ptr));
374   CHKERRQ(DMRestoreLocalVector(da,&localX));
375   PetscFunctionReturn(0);
376 }
377 /* ------------------------------------------------------------------- */
378 /*
379    FormJacobian - Evaluates Jacobian matrix.
380 
381    Input Parameters:
382 .  snes - the SNES context
383 .  x - input vector
384 .  ptr - optional user-defined context, as set by SNESSetJacobian()
385 
386    Output Parameters:
387 .  A - Jacobian matrix
388 .  B - optionally different preconditioning matrix
389 
390 */
391 PetscErrorCode FormJacobian(SNES snes,Vec X,Mat J,Mat jac,void *ptr)
392 {
393   AppCtx         *user = (AppCtx*)ptr;  /* user-defined application context */
394   Vec            localX;
395   PetscInt       i,j,k,Mx,My,Mz;
396   MatStencil     col[7],row;
397   PetscInt       xs,ys,zs,xm,ym,zm;
398   PetscScalar    lambda,v[7],hx,hy,hz,hxhzdhy,hyhzdhx,hxhydhz,sc,***x;
399   DM             da;
400 
401   PetscFunctionBeginUser;
402   CHKERRQ(SNESGetDM(snes,&da));
403   CHKERRQ(DMGetLocalVector(da,&localX));
404   CHKERRQ(DMDAGetInfo(da,PETSC_IGNORE,&Mx,&My,&Mz,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE));
405 
406   lambda  = user->param;
407   hx      = 1.0/(PetscReal)(Mx-1);
408   hy      = 1.0/(PetscReal)(My-1);
409   hz      = 1.0/(PetscReal)(Mz-1);
410   sc      = hx*hy*hz*lambda;
411   hxhzdhy = hx*hz/hy;
412   hyhzdhx = hy*hz/hx;
413   hxhydhz = hx*hy/hz;
414 
415   /*
416      Scatter ghost points to local vector, using the 2-step process
417         DMGlobalToLocalBegin(), DMGlobalToLocalEnd().
418      By placing code between these two statements, computations can be
419      done while messages are in transition.
420   */
421   CHKERRQ(DMGlobalToLocalBegin(da,X,INSERT_VALUES,localX));
422   CHKERRQ(DMGlobalToLocalEnd(da,X,INSERT_VALUES,localX));
423 
424   /*
425      Get pointer to vector data
426   */
427   CHKERRQ(DMDAVecGetArrayRead(da,localX,&x));
428 
429   /*
430      Get local grid boundaries
431   */
432   CHKERRQ(DMDAGetCorners(da,&xs,&ys,&zs,&xm,&ym,&zm));
433 
434   /*
435      Compute entries for the locally owned part of the Jacobian.
436       - Currently, all PETSc parallel matrix formats are partitioned by
437         contiguous chunks of rows across the processors.
438       - Each processor needs to insert only elements that it owns
439         locally (but any non-local elements will be sent to the
440         appropriate processor during matrix assembly).
441       - Here, we set all entries for a particular row at once.
442       - We can set matrix entries either using either
443         MatSetValuesLocal() or MatSetValues(), as discussed above.
444   */
445   for (k=zs; k<zs+zm; k++) {
446     for (j=ys; j<ys+ym; j++) {
447       for (i=xs; i<xs+xm; i++) {
448         row.k = k; row.j = j; row.i = i;
449         /* boundary points */
450         if (i == 0 || j == 0 || k == 0|| i == Mx-1 || j == My-1 || k == Mz-1) {
451           v[0] = 1.0;
452           CHKERRQ(MatSetValuesStencil(jac,1,&row,1,&row,v,INSERT_VALUES));
453         } else {
454           /* interior grid points */
455           v[0] = -hxhydhz; col[0].k=k-1;col[0].j=j;  col[0].i = i;
456           v[1] = -hxhzdhy; col[1].k=k;  col[1].j=j-1;col[1].i = i;
457           v[2] = -hyhzdhx; col[2].k=k;  col[2].j=j;  col[2].i = i-1;
458           v[3] = 2.0*(hyhzdhx+hxhzdhy+hxhydhz)-sc*PetscExpScalar(x[k][j][i]);col[3].k=row.k;col[3].j=row.j;col[3].i = row.i;
459           v[4] = -hyhzdhx; col[4].k=k;  col[4].j=j;  col[4].i = i+1;
460           v[5] = -hxhzdhy; col[5].k=k;  col[5].j=j+1;col[5].i = i;
461           v[6] = -hxhydhz; col[6].k=k+1;col[6].j=j;  col[6].i = i;
462           CHKERRQ(MatSetValuesStencil(jac,1,&row,7,col,v,INSERT_VALUES));
463         }
464       }
465     }
466   }
467   CHKERRQ(DMDAVecRestoreArrayRead(da,localX,&x));
468   CHKERRQ(DMRestoreLocalVector(da,&localX));
469 
470   /*
471      Assemble matrix, using the 2-step process:
472        MatAssemblyBegin(), MatAssemblyEnd().
473   */
474   CHKERRQ(MatAssemblyBegin(jac,MAT_FINAL_ASSEMBLY));
475   CHKERRQ(MatAssemblyEnd(jac,MAT_FINAL_ASSEMBLY));
476 
477   /*
478      Normally since the matrix has already been assembled above; this
479      would do nothing. But in the matrix free mode -snes_mf_operator
480      this tells the "matrix-free" matrix that a new linear system solve
481      is about to be done.
482   */
483 
484   CHKERRQ(MatAssemblyBegin(J,MAT_FINAL_ASSEMBLY));
485   CHKERRQ(MatAssemblyEnd(J,MAT_FINAL_ASSEMBLY));
486 
487   /*
488      Tell the matrix we will never add a new nonzero location to the
489      matrix. If we do, it will generate an error.
490   */
491   CHKERRQ(MatSetOption(jac,MAT_NEW_NONZERO_LOCATION_ERR,PETSC_TRUE));
492   PetscFunctionReturn(0);
493 }
494 
495 /*TEST
496 
497    test:
498       nsize: 4
499       args: -snes_monitor_short -ksp_gmres_cgs_refinement_type refine_always
500 
501    test:
502       suffix: 2
503       nsize: 4
504       args: -snes_monitor_short -ksp_gmres_cgs_refinement_type refine_always
505 
506    test:
507       suffix: 3
508       nsize: 4
509       args: -fdcoloring -snes_monitor_short -ksp_gmres_cgs_refinement_type refine_always
510 
511    test:
512       suffix: 3_ds
513       nsize: 4
514       args: -fdcoloring -fdcoloring_ds -snes_monitor_short -ksp_gmres_cgs_refinement_type refine_always
515 
516    test:
517       suffix: 4
518       nsize: 4
519       args: -fdcoloring_local -fdcoloring -ksp_monitor_short -da_refine 1
520       requires: !single
521 
522    test:
523       suffix: 5
524       nsize: 4
525       args: -fdcoloring_local -fdcoloring -ksp_monitor_short -da_refine 1 -snes_type newtontrdc
526       requires: !single
527 
528 TEST*/
529