xref: /petsc/src/mat/matfd/fdmatrix.c (revision d8e47b638cf8f604a99e9678e1df24f82d959cd7)
1 /*
2    This is where the abstract matrix operations are defined that are
3   used for finite difference computations of Jacobians using coloring.
4 */
5 
6 #include <petsc/private/matimpl.h> /*I "petscmat.h" I*/
7 #include <petsc/private/isimpl.h>
8 
9 PetscErrorCode MatFDColoringSetF(MatFDColoring fd, Vec F)
10 {
11   PetscFunctionBegin;
12   if (F) {
13     PetscCall(VecCopy(F, fd->w1));
14     fd->fset = PETSC_TRUE;
15   } else {
16     fd->fset = PETSC_FALSE;
17   }
18   PetscFunctionReturn(PETSC_SUCCESS);
19 }
20 
21 #include <petscdraw.h>
22 static PetscErrorCode MatFDColoringView_Draw_Zoom(PetscDraw draw, void *Aa)
23 {
24   MatFDColoring fd = (MatFDColoring)Aa;
25   PetscMPIInt   i, j, nz;
26   PetscInt      row;
27   PetscReal     x, y;
28   MatEntry     *Jentry = fd->matentry;
29 
30   PetscFunctionBegin;
31   /* loop over colors  */
32   nz = 0;
33   for (i = 0; i < fd->ncolors; i++) {
34     for (j = 0; j < fd->nrows[i]; j++) {
35       row = Jentry[nz].row;
36       y   = fd->M - row - fd->rstart;
37       x   = (PetscReal)Jentry[nz++].col;
38       PetscCall(PetscDrawRectangle(draw, x, y, x + 1, y + 1, i + 1, i + 1, i + 1, i + 1));
39     }
40   }
41   PetscFunctionReturn(PETSC_SUCCESS);
42 }
43 
44 static PetscErrorCode MatFDColoringView_Draw(MatFDColoring fd, PetscViewer viewer)
45 {
46   PetscBool isnull;
47   PetscDraw draw;
48   PetscReal xr, yr, xl, yl, h, w;
49 
50   PetscFunctionBegin;
51   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
52   PetscCall(PetscDrawIsNull(draw, &isnull));
53   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
54 
55   xr = fd->N;
56   yr = fd->M;
57   h  = yr / 10.0;
58   w  = xr / 10.0;
59   xr += w;
60   yr += h;
61   xl = -w;
62   yl = -h;
63   PetscCall(PetscDrawSetCoordinates(draw, xl, yl, xr, yr));
64   PetscCall(PetscObjectCompose((PetscObject)fd, "Zoomviewer", (PetscObject)viewer));
65   PetscCall(PetscDrawZoom(draw, MatFDColoringView_Draw_Zoom, fd));
66   PetscCall(PetscObjectCompose((PetscObject)fd, "Zoomviewer", NULL));
67   PetscCall(PetscDrawSave(draw));
68   PetscFunctionReturn(PETSC_SUCCESS);
69 }
70 
71 /*@
72   MatFDColoringView - Views a finite difference coloring context.
73 
74   Collective
75 
76   Input Parameters:
77 + c      - the coloring context
78 - viewer - visualization context
79 
80   Level: intermediate
81 
82   Notes:
83   The available visualization contexts include
84 +     `PETSC_VIEWER_STDOUT_SELF` - standard output (default)
85 .     `PETSC_VIEWER_STDOUT_WORLD` - synchronized standard
86   output where only the first processor opens
87   the file.  All other processors send their
88   data to the first processor to print.
89 -     `PETSC_VIEWER_DRAW_WORLD` - graphical display of nonzero structure
90 
91   Since PETSc uses only a small number of basic colors (currently 33), if the coloring
92   involves more than 33 then some seemingly identical colors are displayed making it look
93   like an illegal coloring. This is just a graphical artifact.
94 
95 .seealso: `Mat`, `MatFDColoring`, `MatFDColoringCreate()`
96 @*/
97 PetscErrorCode MatFDColoringView(MatFDColoring c, PetscViewer viewer)
98 {
99   PetscInt          i, j;
100   PetscBool         isdraw, iascii;
101   PetscViewerFormat format;
102 
103   PetscFunctionBegin;
104   PetscValidHeaderSpecific(c, MAT_FDCOLORING_CLASSID, 1);
105   if (!viewer) PetscCall(PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)c), &viewer));
106   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
107   PetscCheckSameComm(c, 1, viewer, 2);
108 
109   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
110   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
111   if (isdraw) {
112     PetscCall(MatFDColoringView_Draw(c, viewer));
113   } else if (iascii) {
114     PetscCall(PetscObjectPrintClassNamePrefixType((PetscObject)c, viewer));
115     PetscCall(PetscViewerASCIIPrintf(viewer, "  Error tolerance=%g\n", (double)c->error_rel));
116     PetscCall(PetscViewerASCIIPrintf(viewer, "  Umin=%g\n", (double)c->umin));
117     PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of colors=%" PetscInt_FMT "\n", c->ncolors));
118 
119     PetscCall(PetscViewerGetFormat(viewer, &format));
120     if (format != PETSC_VIEWER_ASCII_INFO) {
121       PetscInt row, col, nz;
122       nz = 0;
123       for (i = 0; i < c->ncolors; i++) {
124         PetscCall(PetscViewerASCIIPrintf(viewer, "  Information for color %" PetscInt_FMT "\n", i));
125         PetscCall(PetscViewerASCIIPrintf(viewer, "    Number of columns %" PetscInt_FMT "\n", c->ncolumns[i]));
126         for (j = 0; j < c->ncolumns[i]; j++) PetscCall(PetscViewerASCIIPrintf(viewer, "      %" PetscInt_FMT "\n", c->columns[i][j]));
127         PetscCall(PetscViewerASCIIPrintf(viewer, "    Number of rows %" PetscInt_FMT "\n", c->nrows[i]));
128         if (c->matentry) {
129           for (j = 0; j < c->nrows[i]; j++) {
130             row = c->matentry[nz].row;
131             col = c->matentry[nz++].col;
132             PetscCall(PetscViewerASCIIPrintf(viewer, "      %" PetscInt_FMT " %" PetscInt_FMT " \n", row, col));
133           }
134         }
135       }
136     }
137     PetscCall(PetscViewerFlush(viewer));
138   }
139   PetscFunctionReturn(PETSC_SUCCESS);
140 }
141 
142 /*@
143   MatFDColoringSetParameters - Sets the parameters for the sparse approximation of
144   a Jacobian matrix using finite differences.
145 
146   Logically Collective
147 
148   Input Parameters:
149 + matfd - the coloring context
150 . error - relative error
151 - umin  - minimum allowable u-value magnitude
152 
153   Level: advanced
154 
155   Note:
156   The Jacobian is estimated with the differencing approximation
157 .vb
158        F'(u)_{:,i} = [F(u+h*dx_{i}) - F(u)]/h where
159        htype = 'ds':
160          h = error_rel*u[i]                 if  abs(u[i]) > umin
161            = +/- error_rel*umin             otherwise, with +/- determined by the sign of u[i]
162          dx_{i} = (0, ... 1, .... 0)
163 
164        htype = 'wp':
165          h = error_rel * sqrt(1 + ||u||)
166 .ve
167 
168 .seealso: `Mat`, `MatFDColoring`, `MatFDColoringCreate()`, `MatFDColoringSetFromOptions()`
169 @*/
170 PetscErrorCode MatFDColoringSetParameters(MatFDColoring matfd, PetscReal error, PetscReal umin)
171 {
172   PetscFunctionBegin;
173   PetscValidHeaderSpecific(matfd, MAT_FDCOLORING_CLASSID, 1);
174   PetscValidLogicalCollectiveReal(matfd, error, 2);
175   PetscValidLogicalCollectiveReal(matfd, umin, 3);
176   if (error != (PetscReal)PETSC_DEFAULT) matfd->error_rel = error;
177   if (umin != (PetscReal)PETSC_DEFAULT) matfd->umin = umin;
178   PetscFunctionReturn(PETSC_SUCCESS);
179 }
180 
181 /*@
182   MatFDColoringSetBlockSize - Sets block size for efficient inserting entries of Jacobian matrix.
183 
184   Logically Collective
185 
186   Input Parameters:
187 + matfd - the coloring context
188 . brows - number of rows in the block
189 - bcols - number of columns in the block
190 
191   Level: intermediate
192 
193 .seealso: `Mat`, `MatFDColoring`, `MatFDColoringCreate()`, `MatFDColoringSetFromOptions()`
194 @*/
195 PetscErrorCode MatFDColoringSetBlockSize(MatFDColoring matfd, PetscInt brows, PetscInt bcols)
196 {
197   PetscFunctionBegin;
198   PetscValidHeaderSpecific(matfd, MAT_FDCOLORING_CLASSID, 1);
199   PetscValidLogicalCollectiveInt(matfd, brows, 2);
200   PetscValidLogicalCollectiveInt(matfd, bcols, 3);
201   if (brows != PETSC_DEFAULT) matfd->brows = brows;
202   if (bcols != PETSC_DEFAULT) matfd->bcols = bcols;
203   PetscFunctionReturn(PETSC_SUCCESS);
204 }
205 
206 /*@
207   MatFDColoringSetUp - Sets up the internal data structures of matrix coloring context for the later use.
208 
209   Collective
210 
211   Input Parameters:
212 + mat        - the matrix containing the nonzero structure of the Jacobian
213 . iscoloring - the coloring of the matrix; usually obtained with `MatGetColoring()` or `DMCreateColoring()`
214 - color      - the matrix coloring context
215 
216   Level: beginner
217 
218   Notes:
219   When the coloring type is `IS_COLORING_LOCAL` the coloring is in the local ordering of the unknowns.
220 
221 .seealso: `Mat`, `MatFDColoring`, `MatFDColoringCreate()`, `MatFDColoringDestroy()`
222 @*/
223 PetscErrorCode MatFDColoringSetUp(Mat mat, ISColoring iscoloring, MatFDColoring color)
224 {
225   PetscBool eq;
226 
227   PetscFunctionBegin;
228   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
229   PetscValidHeaderSpecific(color, MAT_FDCOLORING_CLASSID, 3);
230   if (color->setupcalled) PetscFunctionReturn(PETSC_SUCCESS);
231   PetscCall(PetscObjectCompareId((PetscObject)mat, color->matid, &eq));
232   PetscCheck(eq, PetscObjectComm((PetscObject)mat), PETSC_ERR_ARG_WRONG, "Matrix used with MatFDColoringSetUp() must be that used with MatFDColoringCreate()");
233 
234   PetscCall(PetscLogEventBegin(MAT_FDColoringSetUp, mat, 0, 0, 0));
235   PetscUseTypeMethod(mat, fdcoloringsetup, iscoloring, color);
236 
237   color->setupcalled = PETSC_TRUE;
238   PetscCall(PetscLogEventEnd(MAT_FDColoringSetUp, mat, 0, 0, 0));
239   PetscFunctionReturn(PETSC_SUCCESS);
240 }
241 
242 /*@C
243   MatFDColoringGetFunction - Gets the function to use for computing the Jacobian.
244 
245   Not Collective
246 
247   Input Parameter:
248 . matfd - the coloring context
249 
250   Output Parameters:
251 + f    - the function
252 - fctx - the optional user-defined function context
253 
254   Level: intermediate
255 
256 .seealso: `Mat`, `MatFDColoring`, `MatFDColoringCreate()`, `MatFDColoringSetFunction()`, `MatFDColoringSetFromOptions()`
257 @*/
258 PetscErrorCode MatFDColoringGetFunction(MatFDColoring matfd, PetscErrorCode (**f)(void), void **fctx)
259 {
260   PetscFunctionBegin;
261   PetscValidHeaderSpecific(matfd, MAT_FDCOLORING_CLASSID, 1);
262   if (f) *f = matfd->f;
263   if (fctx) *fctx = matfd->fctx;
264   PetscFunctionReturn(PETSC_SUCCESS);
265 }
266 
267 /*@C
268   MatFDColoringSetFunction - Sets the function to use for computing the Jacobian.
269 
270   Logically Collective
271 
272   Input Parameters:
273 + matfd - the coloring context
274 . f     - the function
275 - fctx  - the optional user-defined function context
276 
277   Level: advanced
278 
279   Note:
280   `f` has two possible calling configurations\:
281 $ PetscErrorCode f(SNES snes, Vec in, Vec out, void *fctx)
282 + snes - the nonlinear solver `SNES` object
283 . in   - the location where the Jacobian is to be computed
284 . out  - the location to put the computed function value
285 - fctx - the function context
286 
287   and
288 $ PetscErrorCode f(void *dummy, Vec in, Vec out, void *fctx)
289 + dummy - an unused parameter
290 . in    - the location where the Jacobian is to be computed
291 . out   - the location to put the computed function value
292 - fctx  - the function context
293 
294   This function is usually used automatically by `SNES` (when one uses `SNESSetJacobian()` with the argument
295   `SNESComputeJacobianDefaultColor()`) and only needs to be used by someone computing a matrix via coloring directly by
296   calling `MatFDColoringApply()`
297 
298   Fortran Notes:
299   In Fortran you must call `MatFDColoringSetFunction()` for a coloring object to
300   be used without `SNES` or within the `SNES` solvers.
301 
302 .seealso: `Mat`, `MatFDColoring`, `MatFDColoringCreate()`, `MatFDColoringGetFunction()`, `MatFDColoringSetFromOptions()`
303 @*/
304 PetscErrorCode MatFDColoringSetFunction(MatFDColoring matfd, PetscErrorCode (*f)(void), void *fctx)
305 {
306   PetscFunctionBegin;
307   PetscValidHeaderSpecific(matfd, MAT_FDCOLORING_CLASSID, 1);
308   matfd->f    = f;
309   matfd->fctx = fctx;
310   PetscFunctionReturn(PETSC_SUCCESS);
311 }
312 
313 /*@
314   MatFDColoringSetFromOptions - Sets coloring finite difference parameters from
315   the options database.
316 
317   Collective
318 
319   The Jacobian, F'(u), is estimated with the differencing approximation
320 .vb
321        F'(u)_{:,i} = [F(u+h*dx_{i}) - F(u)]/h where
322        h = error_rel*u[i]                 if  abs(u[i]) > umin
323          = +/- error_rel*umin             otherwise, with +/- determined by the sign of u[i]
324        dx_{i} = (0, ... 1, .... 0)
325 .ve
326 
327   Input Parameter:
328 . matfd - the coloring context
329 
330   Options Database Keys:
331 + -mat_fd_coloring_err <err>         - Sets <err> (square root of relative error in the function)
332 . -mat_fd_coloring_umin <umin>       - Sets umin, the minimum allowable u-value magnitude
333 . -mat_fd_type                       - "wp" or "ds" (see MATMFFD_WP or MATMFFD_DS)
334 . -mat_fd_coloring_view              - Activates basic viewing
335 . -mat_fd_coloring_view ::ascii_info - Activates viewing info
336 - -mat_fd_coloring_view draw         - Activates drawing
337 
338   Level: intermediate
339 
340 .seealso: `Mat`, `MatFDColoring`, `MatFDColoringCreate()`, `MatFDColoringView()`, `MatFDColoringSetParameters()`
341 @*/
342 PetscErrorCode MatFDColoringSetFromOptions(MatFDColoring matfd)
343 {
344   PetscBool flg;
345   char      value[3];
346 
347   PetscFunctionBegin;
348   PetscValidHeaderSpecific(matfd, MAT_FDCOLORING_CLASSID, 1);
349 
350   PetscObjectOptionsBegin((PetscObject)matfd);
351   PetscCall(PetscOptionsReal("-mat_fd_coloring_err", "Square root of relative error in function", "MatFDColoringSetParameters", matfd->error_rel, &matfd->error_rel, NULL));
352   PetscCall(PetscOptionsReal("-mat_fd_coloring_umin", "Minimum allowable u magnitude", "MatFDColoringSetParameters", matfd->umin, &matfd->umin, NULL));
353   PetscCall(PetscOptionsString("-mat_fd_type", "Algorithm to compute h, wp or ds", "MatFDColoringCreate", matfd->htype, value, sizeof(value), &flg));
354   if (flg) {
355     if (value[0] == 'w' && value[1] == 'p') matfd->htype = "wp";
356     else if (value[0] == 'd' && value[1] == 's') matfd->htype = "ds";
357     else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Unknown finite differencing type %s", value);
358   }
359   PetscCall(PetscOptionsInt("-mat_fd_coloring_brows", "Number of block rows", "MatFDColoringSetBlockSize", matfd->brows, &matfd->brows, NULL));
360   PetscCall(PetscOptionsInt("-mat_fd_coloring_bcols", "Number of block columns", "MatFDColoringSetBlockSize", matfd->bcols, &matfd->bcols, &flg));
361   if (flg && matfd->bcols > matfd->ncolors) {
362     /* input bcols cannot be > matfd->ncolors, thus set it as ncolors */
363     matfd->bcols = matfd->ncolors;
364   }
365 
366   /* process any options handlers added with PetscObjectAddOptionsHandler() */
367   PetscCall(PetscObjectProcessOptionsHandlers((PetscObject)matfd, PetscOptionsObject));
368   PetscOptionsEnd();
369   PetscFunctionReturn(PETSC_SUCCESS);
370 }
371 
372 /*@
373   MatFDColoringSetType - Sets the approach for computing the finite difference parameter
374 
375   Collective
376 
377   Input Parameters:
378 + matfd - the coloring context
379 - type  - either `MATMFFD_WP` or `MATMFFD_DS`
380 
381   Options Database Key:
382 . -mat_fd_type - "wp" or "ds"
383 
384   Level: intermediate
385 
386   Note:
387   It is goofy that the argument type is `MatMFFDType` since the `MatFDColoring` actually computes the matrix entries
388   but the process of computing the entries is the same as with the `MATMFFD` operation so we should reuse the names instead of
389   introducing another one.
390 
391 .seealso: `Mat`, `MatFDColoring`, `MatFDColoringCreate()`, `MatFDColoringView()`, `MatFDColoringSetParameters()`
392 @*/
393 PetscErrorCode MatFDColoringSetType(MatFDColoring matfd, MatMFFDType type)
394 {
395   PetscFunctionBegin;
396   PetscValidHeaderSpecific(matfd, MAT_FDCOLORING_CLASSID, 1);
397   /*
398      It is goofy to handle the strings this way but currently there is no code to free a dynamically created matfd->htype
399      and this function is being provided as patch for a release so it shouldn't change the implementation
400   */
401   if (type[0] == 'w' && type[1] == 'p') matfd->htype = "wp";
402   else if (type[0] == 'd' && type[1] == 's') matfd->htype = "ds";
403   else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Unknown finite differencing type %s", type);
404   PetscFunctionReturn(PETSC_SUCCESS);
405 }
406 
407 static PetscErrorCode MatFDColoringViewFromOptions(MatFDColoring fd, const char prefix[], const char optionname[])
408 {
409   PetscBool         flg;
410   PetscViewer       viewer;
411   PetscViewerFormat format;
412 
413   PetscFunctionBegin;
414   if (prefix) {
415     PetscCall(PetscOptionsCreateViewer(PetscObjectComm((PetscObject)fd), ((PetscObject)fd)->options, prefix, optionname, &viewer, &format, &flg));
416   } else {
417     PetscCall(PetscOptionsCreateViewer(PetscObjectComm((PetscObject)fd), ((PetscObject)fd)->options, ((PetscObject)fd)->prefix, optionname, &viewer, &format, &flg));
418   }
419   if (flg) {
420     PetscCall(PetscViewerPushFormat(viewer, format));
421     PetscCall(MatFDColoringView(fd, viewer));
422     PetscCall(PetscViewerPopFormat(viewer));
423     PetscCall(PetscViewerDestroy(&viewer));
424   }
425   PetscFunctionReturn(PETSC_SUCCESS);
426 }
427 
428 /*@
429   MatFDColoringCreate - Creates a matrix coloring context for finite difference
430   computation of Jacobians.
431 
432   Collective
433 
434   Input Parameters:
435 + mat        - the matrix containing the nonzero structure of the Jacobian
436 - iscoloring - the coloring of the matrix; usually obtained with `MatColoringCreate()` or `DMCreateColoring()`
437 
438   Output Parameter:
439 . color - the new coloring context
440 
441   Level: intermediate
442 
443 .seealso: `Mat`, `MatFDColoring`, `MatFDColoringDestroy()`, `SNESComputeJacobianDefaultColor()`, `ISColoringCreate()`,
444           `MatFDColoringSetFunction()`, `MatFDColoringSetFromOptions()`, `MatFDColoringApply()`,
445           `MatFDColoringView()`, `MatFDColoringSetParameters()`, `MatColoringCreate()`, `DMCreateColoring()`, `MatFDColoringSetValues()`
446 @*/
447 PetscErrorCode MatFDColoringCreate(Mat mat, ISColoring iscoloring, MatFDColoring *color)
448 {
449   MatFDColoring c;
450   MPI_Comm      comm;
451   PetscInt      M, N;
452 
453   PetscFunctionBegin;
454   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
455   PetscAssertPointer(color, 3);
456   PetscCheck(mat->assembled, PetscObjectComm((PetscObject)mat), PETSC_ERR_ARG_WRONGSTATE, "Matrix must be assembled by calls to MatAssemblyBegin/End();");
457   PetscCall(PetscLogEventBegin(MAT_FDColoringCreate, mat, 0, 0, 0));
458   PetscCall(MatGetSize(mat, &M, &N));
459   PetscCheck(M == N, PetscObjectComm((PetscObject)mat), PETSC_ERR_SUP, "Only for square matrices");
460   PetscCall(PetscObjectGetComm((PetscObject)mat, &comm));
461   PetscCall(PetscHeaderCreate(c, MAT_FDCOLORING_CLASSID, "MatFDColoring", "Jacobian computation via finite differences with coloring", "Mat", comm, MatFDColoringDestroy, MatFDColoringView));
462 
463   c->ctype = iscoloring->ctype;
464   PetscCall(PetscObjectGetId((PetscObject)mat, &c->matid));
465 
466   PetscUseTypeMethod(mat, fdcoloringcreate, iscoloring, c);
467 
468   PetscCall(MatCreateVecs(mat, NULL, &c->w1));
469   /* Vec is used intensively in particular piece of scalar CPU code; won't benefit from bouncing back and forth to the GPU */
470   PetscCall(VecBindToCPU(c->w1, PETSC_TRUE));
471   PetscCall(VecDuplicate(c->w1, &c->w2));
472   /* Vec is used intensively in particular piece of scalar CPU code; won't benefit from bouncing back and forth to the GPU */
473   PetscCall(VecBindToCPU(c->w2, PETSC_TRUE));
474 
475   c->error_rel    = PETSC_SQRT_MACHINE_EPSILON;
476   c->umin         = 100.0 * PETSC_SQRT_MACHINE_EPSILON;
477   c->currentcolor = -1;
478   c->htype        = "wp";
479   c->fset         = PETSC_FALSE;
480   c->setupcalled  = PETSC_FALSE;
481 
482   *color = c;
483   PetscCall(PetscObjectCompose((PetscObject)mat, "SNESMatFDColoring", (PetscObject)c));
484   PetscCall(PetscLogEventEnd(MAT_FDColoringCreate, mat, 0, 0, 0));
485   PetscFunctionReturn(PETSC_SUCCESS);
486 }
487 
488 /*@
489   MatFDColoringDestroy - Destroys a matrix coloring context that was created
490   via `MatFDColoringCreate()`.
491 
492   Collective
493 
494   Input Parameter:
495 . c - coloring context
496 
497   Level: intermediate
498 
499 .seealso: `Mat`, `MatFDColoring`, `MatFDColoringCreate()`
500 @*/
501 PetscErrorCode MatFDColoringDestroy(MatFDColoring *c)
502 {
503   PetscInt      i;
504   MatFDColoring color = *c;
505 
506   PetscFunctionBegin;
507   if (!*c) PetscFunctionReturn(PETSC_SUCCESS);
508   if (--((PetscObject)color)->refct > 0) {
509     *c = NULL;
510     PetscFunctionReturn(PETSC_SUCCESS);
511   }
512 
513   /* we do not free the column arrays since their entries are owned by the ISs in color->isa */
514   for (i = 0; i < color->ncolors; i++) PetscCall(ISDestroy(&color->isa[i]));
515   PetscCall(PetscFree(color->isa));
516   PetscCall(PetscFree2(color->ncolumns, color->columns));
517   PetscCall(PetscFree(color->nrows));
518   if (color->htype[0] == 'w') {
519     PetscCall(PetscFree(color->matentry2));
520   } else {
521     PetscCall(PetscFree(color->matentry));
522   }
523   PetscCall(PetscFree(color->dy));
524   if (color->vscale) PetscCall(VecDestroy(&color->vscale));
525   PetscCall(VecDestroy(&color->w1));
526   PetscCall(VecDestroy(&color->w2));
527   PetscCall(VecDestroy(&color->w3));
528   PetscCall(PetscHeaderDestroy(c));
529   PetscFunctionReturn(PETSC_SUCCESS);
530 }
531 
532 /*@C
533   MatFDColoringGetPerturbedColumns - Returns the indices of the columns that
534   that are currently being perturbed.
535 
536   Not Collective
537 
538   Input Parameter:
539 . coloring - coloring context created with `MatFDColoringCreate()`
540 
541   Output Parameters:
542 + n    - the number of local columns being perturbed
543 - cols - the column indices, in global numbering
544 
545   Level: advanced
546 
547   Note:
548   IF the matrix type is `MATBAIJ`, then the block column indices are returned
549 
550   Fortran Notes:
551   This routine has a different interface for Fortran
552 .vb
553      #include <petsc/finclude/petscmat.h>
554           use petscmat
555           PetscInt, pointer :: array(:)
556           PetscErrorCode  ierr
557           MatFDColoring   i
558           call MatFDColoringGetPerturbedColumnsF90(i,array,ierr)
559       use the entries of array ...
560           call MatFDColoringRestorePerturbedColumnsF90(i,array,ierr)
561 .ve
562 
563 .seealso: `Mat`, `MatFDColoring`, `MatFDColoringCreate()`, `MatFDColoringDestroy()`, `MatFDColoringView()`, `MatFDColoringApply()`
564 @*/
565 PetscErrorCode MatFDColoringGetPerturbedColumns(MatFDColoring coloring, PetscInt *n, const PetscInt *cols[])
566 {
567   PetscFunctionBegin;
568   if (coloring->currentcolor >= 0) {
569     *n    = coloring->ncolumns[coloring->currentcolor];
570     *cols = coloring->columns[coloring->currentcolor];
571   } else {
572     *n = 0;
573   }
574   PetscFunctionReturn(PETSC_SUCCESS);
575 }
576 
577 /*@
578   MatFDColoringApply - Given a matrix for which a `MatFDColoring` context
579   has been created, computes the Jacobian for a function via finite differences.
580 
581   Collective
582 
583   Input Parameters:
584 + J        - location to store Jacobian
585 . coloring - coloring context created with `MatFDColoringCreate()`
586 . x1       - location at which Jacobian is to be computed
587 - sctx     - context required by function, if this is being used with the SNES solver then it is `SNES` object, otherwise it is null
588 
589   Options Database Keys:
590 + -mat_fd_type                       - "wp" or "ds"  (see `MATMFFD_WP` or `MATMFFD_DS`)
591 . -mat_fd_coloring_view              - Activates basic viewing or coloring
592 . -mat_fd_coloring_view draw         - Activates drawing of coloring
593 - -mat_fd_coloring_view ::ascii_info - Activates viewing of coloring info
594 
595   Level: intermediate
596 
597 .seealso: `Mat`, `MatFDColoring`, `MatFDColoringCreate()`, `MatFDColoringDestroy()`, `MatFDColoringView()`, `MatFDColoringSetFunction()`, `MatFDColoringSetValues()`
598 @*/
599 PetscErrorCode MatFDColoringApply(Mat J, MatFDColoring coloring, Vec x1, void *sctx)
600 {
601   PetscBool eq;
602 
603   PetscFunctionBegin;
604   PetscValidHeaderSpecific(J, MAT_CLASSID, 1);
605   PetscValidHeaderSpecific(coloring, MAT_FDCOLORING_CLASSID, 2);
606   PetscValidHeaderSpecific(x1, VEC_CLASSID, 3);
607   PetscCall(PetscObjectCompareId((PetscObject)J, coloring->matid, &eq));
608   PetscCheck(eq, PetscObjectComm((PetscObject)J), PETSC_ERR_ARG_WRONG, "Matrix used with MatFDColoringApply() must be that used with MatFDColoringCreate()");
609   PetscCheck(coloring->f, PetscObjectComm((PetscObject)J), PETSC_ERR_ARG_WRONGSTATE, "Must call MatFDColoringSetFunction()");
610   PetscCheck(coloring->setupcalled, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Must call MatFDColoringSetUp()");
611 
612   PetscCall(MatSetUnfactored(J));
613   PetscCall(PetscLogEventBegin(MAT_FDColoringApply, coloring, J, x1, 0));
614   PetscUseTypeMethod(J, fdcoloringapply, coloring, x1, sctx);
615   PetscCall(PetscLogEventEnd(MAT_FDColoringApply, coloring, J, x1, 0));
616   if (!coloring->viewed) {
617     PetscCall(MatFDColoringViewFromOptions(coloring, NULL, "-mat_fd_coloring_view"));
618     coloring->viewed = PETSC_TRUE;
619   }
620   PetscFunctionReturn(PETSC_SUCCESS);
621 }
622