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