xref: /petsc/src/snes/utils/dmplexsnes.c (revision 5d7a6ebe9dde080aedbe86be0085708de8f97bb7)
1 #include <petsc/private/dmpleximpl.h> /*I "petscdmplex.h" I*/
2 #include <petsc/private/snesimpl.h>   /*I "petscsnes.h"   I*/
3 #include <petscds.h>
4 #include <petsc/private/petscimpl.h>
5 #include <petsc/private/petscfeimpl.h>
6 
7 static void pressure_Private(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar p[])
8 {
9   p[0] = u[uOff[1]];
10 }
11 
12 /*
13   SNESCorrectDiscretePressure_Private - Add a vector in the nullspace to make the continuum integral of the pressure field equal to zero.
14   This is normally used only to evaluate convergence rates for the pressure accurately.
15 
16   Collective
17 
18   Input Parameters:
19 + snes      - The SNES
20 . pfield    - The field number for pressure
21 . nullspace - The pressure nullspace
22 . u         - The solution vector
23 - ctx       - An optional user context
24 
25   Output Parameter:
26 . u         - The solution with a continuum pressure integral of zero
27 
28   Level: developer
29 
30   Notes:
31   If int(u) = a and int(n) = b, then int(u - a/b n) = a - a/b b = 0. We assume that the nullspace is a single vector given explicitly.
32 
33 .seealso: `SNESConvergedCorrectPressure()`
34 */
35 static PetscErrorCode SNESCorrectDiscretePressure_Private(SNES snes, PetscInt pfield, MatNullSpace nullspace, Vec u, void *ctx)
36 {
37   DM          dm;
38   PetscDS     ds;
39   const Vec  *nullvecs;
40   PetscScalar pintd, *intc, *intn;
41   MPI_Comm    comm;
42   PetscInt    Nf, Nv;
43 
44   PetscFunctionBegin;
45   PetscCall(PetscObjectGetComm((PetscObject)snes, &comm));
46   PetscCall(SNESGetDM(snes, &dm));
47   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Cannot compute test without a SNES DM");
48   PetscCheck(nullspace, comm, PETSC_ERR_ARG_WRONG, "Cannot compute test without a Jacobian nullspace");
49   PetscCall(DMGetDS(dm, &ds));
50   PetscCall(PetscDSSetObjective(ds, pfield, pressure_Private));
51   PetscCall(MatNullSpaceGetVecs(nullspace, NULL, &Nv, &nullvecs));
52   PetscCheck(Nv == 1, comm, PETSC_ERR_ARG_OUTOFRANGE, "Can only handle a single null vector for pressure, not %" PetscInt_FMT, Nv);
53   PetscCall(VecDot(nullvecs[0], u, &pintd));
54   PetscCheck(PetscAbsScalar(pintd) <= PETSC_SMALL, comm, PETSC_ERR_ARG_WRONG, "Discrete integral of pressure: %g", (double)PetscRealPart(pintd));
55   PetscCall(PetscDSGetNumFields(ds, &Nf));
56   PetscCall(PetscMalloc2(Nf, &intc, Nf, &intn));
57   PetscCall(DMPlexComputeIntegralFEM(dm, nullvecs[0], intn, ctx));
58   PetscCall(DMPlexComputeIntegralFEM(dm, u, intc, ctx));
59   PetscCall(VecAXPY(u, -intc[pfield] / intn[pfield], nullvecs[0]));
60 #if defined(PETSC_USE_DEBUG)
61   PetscCall(DMPlexComputeIntegralFEM(dm, u, intc, ctx));
62   PetscCheck(PetscAbsScalar(intc[pfield]) <= PETSC_SMALL, comm, PETSC_ERR_ARG_WRONG, "Continuum integral of pressure after correction: %g", (double)PetscRealPart(intc[pfield]));
63 #endif
64   PetscCall(PetscFree2(intc, intn));
65   PetscFunctionReturn(PETSC_SUCCESS);
66 }
67 
68 /*@C
69   SNESConvergedCorrectPressure - The regular `SNES` convergence test that, up on convergence, adds a vector in the nullspace
70   to make the continuum integral of the pressure field equal to zero.
71 
72   Logically Collective
73 
74   Input Parameters:
75 + snes  - the `SNES` context
76 . it    - the iteration (0 indicates before any Newton steps)
77 . xnorm - 2-norm of current iterate
78 . gnorm - 2-norm of current step
79 . f     - 2-norm of function at current iterate
80 - ctx   - Optional user context
81 
82   Output Parameter:
83 . reason - `SNES_CONVERGED_ITERATING`, `SNES_CONVERGED_ITS`, or `SNES_DIVERGED_FNORM_NAN`
84 
85   Options Database Key:
86 . -snes_convergence_test correct_pressure - see `SNESSetFromOptions()`
87 
88   Level: advanced
89 
90   Notes:
91   In order to use this convergence test, you must set up several PETSc structures. First fields must be added to the `DM`, and a `PetscDS`
92   must be created with discretizations of those fields. We currently assume that the pressure field has index 1.
93   The pressure field must have a nullspace, likely created using the `DMSetNullSpaceConstructor()` interface.
94   Last we must be able to integrate the pressure over the domain, so the `DM` attached to the SNES `must` be a `DMPLEX` at this time.
95 
96   Developer Note:
97   This is a total misuse of the `SNES` convergence test handling system. It should be removed. Perhaps a `SNESSetPostSolve()` could
98   be constructed to handle this process.
99 
100 .seealso: `SNES`, `DM`, `SNESConvergedDefault()`, `SNESSetConvergenceTest()`, `DMSetNullSpaceConstructor()`
101 @*/
102 PetscErrorCode SNESConvergedCorrectPressure(SNES snes, PetscInt it, PetscReal xnorm, PetscReal gnorm, PetscReal f, SNESConvergedReason *reason, void *ctx)
103 {
104   PetscBool monitorIntegral = PETSC_FALSE;
105 
106   PetscFunctionBegin;
107   PetscCall(SNESConvergedDefault(snes, it, xnorm, gnorm, f, reason, ctx));
108   if (monitorIntegral) {
109     Mat          J;
110     Vec          u;
111     MatNullSpace nullspace;
112     const Vec   *nullvecs;
113     PetscScalar  pintd;
114 
115     PetscCall(SNESGetSolution(snes, &u));
116     PetscCall(SNESGetJacobian(snes, &J, NULL, NULL, NULL));
117     PetscCall(MatGetNullSpace(J, &nullspace));
118     PetscCall(MatNullSpaceGetVecs(nullspace, NULL, NULL, &nullvecs));
119     PetscCall(VecDot(nullvecs[0], u, &pintd));
120     PetscCall(PetscInfo(snes, "SNES: Discrete integral of pressure: %g\n", (double)PetscRealPart(pintd)));
121   }
122   if (*reason > 0) {
123     Mat          J;
124     Vec          u;
125     MatNullSpace nullspace;
126     PetscInt     pfield = 1;
127 
128     PetscCall(SNESGetSolution(snes, &u));
129     PetscCall(SNESGetJacobian(snes, &J, NULL, NULL, NULL));
130     PetscCall(MatGetNullSpace(J, &nullspace));
131     PetscCall(SNESCorrectDiscretePressure_Private(snes, pfield, nullspace, u, ctx));
132   }
133   PetscFunctionReturn(PETSC_SUCCESS);
134 }
135 
136 /************************** Interpolation *******************************/
137 
138 static PetscErrorCode DMSNESConvertPlex(DM dm, DM *plex, PetscBool copy)
139 {
140   PetscBool isPlex;
141 
142   PetscFunctionBegin;
143   PetscCall(PetscObjectTypeCompare((PetscObject)dm, DMPLEX, &isPlex));
144   if (isPlex) {
145     *plex = dm;
146     PetscCall(PetscObjectReference((PetscObject)dm));
147   } else {
148     PetscCall(PetscObjectQuery((PetscObject)dm, "dm_plex", (PetscObject *)plex));
149     if (!*plex) {
150       PetscCall(DMConvert(dm, DMPLEX, plex));
151       PetscCall(PetscObjectCompose((PetscObject)dm, "dm_plex", (PetscObject)*plex));
152       if (copy) {
153         PetscCall(DMCopyDMSNES(dm, *plex));
154         PetscCall(DMCopyAuxiliaryVec(dm, *plex));
155       }
156     } else {
157       PetscCall(PetscObjectReference((PetscObject)*plex));
158     }
159   }
160   PetscFunctionReturn(PETSC_SUCCESS);
161 }
162 
163 /*@C
164   DMInterpolationCreate - Creates a `DMInterpolationInfo` context
165 
166   Collective
167 
168   Input Parameter:
169 . comm - the communicator
170 
171   Output Parameter:
172 . ctx - the context
173 
174   Level: beginner
175 
176 .seealso: `DMInterpolationInfo`, `DMInterpolationEvaluate()`, `DMInterpolationAddPoints()`, `DMInterpolationDestroy()`
177 @*/
178 PetscErrorCode DMInterpolationCreate(MPI_Comm comm, DMInterpolationInfo *ctx)
179 {
180   PetscFunctionBegin;
181   PetscAssertPointer(ctx, 2);
182   PetscCall(PetscNew(ctx));
183 
184   (*ctx)->comm   = comm;
185   (*ctx)->dim    = -1;
186   (*ctx)->nInput = 0;
187   (*ctx)->points = NULL;
188   (*ctx)->cells  = NULL;
189   (*ctx)->n      = -1;
190   (*ctx)->coords = NULL;
191   PetscFunctionReturn(PETSC_SUCCESS);
192 }
193 
194 /*@C
195   DMInterpolationSetDim - Sets the spatial dimension for the interpolation context
196 
197   Not Collective
198 
199   Input Parameters:
200 + ctx - the context
201 - dim - the spatial dimension
202 
203   Level: intermediate
204 
205 .seealso: `DMInterpolationInfo`, `DMInterpolationGetDim()`, `DMInterpolationEvaluate()`, `DMInterpolationAddPoints()`
206 @*/
207 PetscErrorCode DMInterpolationSetDim(DMInterpolationInfo ctx, PetscInt dim)
208 {
209   PetscFunctionBegin;
210   PetscCheck(!(dim < 1) && !(dim > 3), ctx->comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid dimension for points: %" PetscInt_FMT, dim);
211   ctx->dim = dim;
212   PetscFunctionReturn(PETSC_SUCCESS);
213 }
214 
215 /*@C
216   DMInterpolationGetDim - Gets the spatial dimension for the interpolation context
217 
218   Not Collective
219 
220   Input Parameter:
221 . ctx - the context
222 
223   Output Parameter:
224 . dim - the spatial dimension
225 
226   Level: intermediate
227 
228 .seealso: `DMInterpolationInfo`, `DMInterpolationSetDim()`, `DMInterpolationEvaluate()`, `DMInterpolationAddPoints()`
229 @*/
230 PetscErrorCode DMInterpolationGetDim(DMInterpolationInfo ctx, PetscInt *dim)
231 {
232   PetscFunctionBegin;
233   PetscAssertPointer(dim, 2);
234   *dim = ctx->dim;
235   PetscFunctionReturn(PETSC_SUCCESS);
236 }
237 
238 /*@C
239   DMInterpolationSetDof - Sets the number of fields interpolated at a point for the interpolation context
240 
241   Not Collective
242 
243   Input Parameters:
244 + ctx - the context
245 - dof - the number of fields
246 
247   Level: intermediate
248 
249 .seealso: `DMInterpolationInfo`, `DMInterpolationGetDof()`, `DMInterpolationEvaluate()`, `DMInterpolationAddPoints()`
250 @*/
251 PetscErrorCode DMInterpolationSetDof(DMInterpolationInfo ctx, PetscInt dof)
252 {
253   PetscFunctionBegin;
254   PetscCheck(dof >= 1, ctx->comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of components: %" PetscInt_FMT, dof);
255   ctx->dof = dof;
256   PetscFunctionReturn(PETSC_SUCCESS);
257 }
258 
259 /*@C
260   DMInterpolationGetDof - Gets the number of fields interpolated at a point for the interpolation context
261 
262   Not Collective
263 
264   Input Parameter:
265 . ctx - the context
266 
267   Output Parameter:
268 . dof - the number of fields
269 
270   Level: intermediate
271 
272 .seealso: `DMInterpolationInfo`, `DMInterpolationSetDof()`, `DMInterpolationEvaluate()`, `DMInterpolationAddPoints()`
273 @*/
274 PetscErrorCode DMInterpolationGetDof(DMInterpolationInfo ctx, PetscInt *dof)
275 {
276   PetscFunctionBegin;
277   PetscAssertPointer(dof, 2);
278   *dof = ctx->dof;
279   PetscFunctionReturn(PETSC_SUCCESS);
280 }
281 
282 /*@C
283   DMInterpolationAddPoints - Add points at which we will interpolate the fields
284 
285   Not Collective
286 
287   Input Parameters:
288 + ctx    - the context
289 . n      - the number of points
290 - points - the coordinates for each point, an array of size n * dim
291 
292   Level: intermediate
293 
294   Note:
295   The coordinate information is copied.
296 
297 .seealso: `DMInterpolationInfo`, `DMInterpolationSetDim()`, `DMInterpolationEvaluate()`, `DMInterpolationCreate()`
298 @*/
299 PetscErrorCode DMInterpolationAddPoints(DMInterpolationInfo ctx, PetscInt n, PetscReal points[])
300 {
301   PetscFunctionBegin;
302   PetscCheck(ctx->dim >= 0, ctx->comm, PETSC_ERR_ARG_WRONGSTATE, "The spatial dimension has not been set");
303   PetscCheck(!ctx->points, ctx->comm, PETSC_ERR_ARG_WRONGSTATE, "Cannot add points multiple times yet");
304   ctx->nInput = n;
305 
306   PetscCall(PetscMalloc1(n * ctx->dim, &ctx->points));
307   PetscCall(PetscArraycpy(ctx->points, points, n * ctx->dim));
308   PetscFunctionReturn(PETSC_SUCCESS);
309 }
310 
311 /*@C
312   DMInterpolationSetUp - Compute spatial indices for point location during interpolation
313 
314   Collective
315 
316   Input Parameters:
317 + ctx                 - the context
318 . dm                  - the `DM` for the function space used for interpolation
319 . redundantPoints     - If `PETSC_TRUE`, all processes are passing in the same array of points. Otherwise, points need to be communicated among processes.
320 - ignoreOutsideDomain - If `PETSC_TRUE`, ignore points outside the domain, otherwise return an error
321 
322   Level: intermediate
323 
324 .seealso: `DMInterpolationInfo`, `DMInterpolationEvaluate()`, `DMInterpolationAddPoints()`, `DMInterpolationCreate()`
325 @*/
326 PetscErrorCode DMInterpolationSetUp(DMInterpolationInfo ctx, DM dm, PetscBool redundantPoints, PetscBool ignoreOutsideDomain)
327 {
328   MPI_Comm           comm = ctx->comm;
329   PetscScalar       *a;
330   PetscInt           p, q, i;
331   PetscMPIInt        rank, size;
332   Vec                pointVec;
333   PetscSF            cellSF;
334   PetscLayout        layout;
335   PetscReal         *globalPoints;
336   PetscScalar       *globalPointsScalar;
337   const PetscInt    *ranges;
338   PetscMPIInt       *counts, *displs;
339   const PetscSFNode *foundCells;
340   const PetscInt    *foundPoints;
341   PetscMPIInt       *foundProcs, *globalProcs;
342   PetscInt           n, N, numFound;
343 
344   PetscFunctionBegin;
345   PetscValidHeaderSpecific(dm, DM_CLASSID, 2);
346   PetscCallMPI(MPI_Comm_size(comm, &size));
347   PetscCallMPI(MPI_Comm_rank(comm, &rank));
348   PetscCheck(ctx->dim >= 0, comm, PETSC_ERR_ARG_WRONGSTATE, "The spatial dimension has not been set");
349   /* Locate points */
350   n = ctx->nInput;
351   if (!redundantPoints) {
352     PetscCall(PetscLayoutCreate(comm, &layout));
353     PetscCall(PetscLayoutSetBlockSize(layout, 1));
354     PetscCall(PetscLayoutSetLocalSize(layout, n));
355     PetscCall(PetscLayoutSetUp(layout));
356     PetscCall(PetscLayoutGetSize(layout, &N));
357     /* Communicate all points to all processes */
358     PetscCall(PetscMalloc3(N * ctx->dim, &globalPoints, size, &counts, size, &displs));
359     PetscCall(PetscLayoutGetRanges(layout, &ranges));
360     for (p = 0; p < size; ++p) {
361       counts[p] = (ranges[p + 1] - ranges[p]) * ctx->dim;
362       displs[p] = ranges[p] * ctx->dim;
363     }
364     PetscCallMPI(MPI_Allgatherv(ctx->points, n * ctx->dim, MPIU_REAL, globalPoints, counts, displs, MPIU_REAL, comm));
365   } else {
366     N            = n;
367     globalPoints = ctx->points;
368     counts = displs = NULL;
369     layout          = NULL;
370   }
371 #if 0
372   PetscCall(PetscMalloc3(N,&foundCells,N,&foundProcs,N,&globalProcs));
373   /* foundCells[p] = m->locatePoint(&globalPoints[p*ctx->dim]); */
374 #else
375   #if defined(PETSC_USE_COMPLEX)
376   PetscCall(PetscMalloc1(N * ctx->dim, &globalPointsScalar));
377   for (i = 0; i < N * ctx->dim; i++) globalPointsScalar[i] = globalPoints[i];
378   #else
379   globalPointsScalar = globalPoints;
380   #endif
381   PetscCall(VecCreateSeqWithArray(PETSC_COMM_SELF, ctx->dim, N * ctx->dim, globalPointsScalar, &pointVec));
382   PetscCall(PetscMalloc2(N, &foundProcs, N, &globalProcs));
383   for (p = 0; p < N; ++p) foundProcs[p] = size;
384   cellSF = NULL;
385   PetscCall(DMLocatePoints(dm, pointVec, DM_POINTLOCATION_REMOVE, &cellSF));
386   PetscCall(PetscSFGetGraph(cellSF, NULL, &numFound, &foundPoints, &foundCells));
387 #endif
388   for (p = 0; p < numFound; ++p) {
389     if (foundCells[p].index >= 0) foundProcs[foundPoints ? foundPoints[p] : p] = rank;
390   }
391   /* Let the lowest rank process own each point */
392   PetscCall(MPIU_Allreduce(foundProcs, globalProcs, N, MPI_INT, MPI_MIN, comm));
393   ctx->n = 0;
394   for (p = 0; p < N; ++p) {
395     if (globalProcs[p] == size) {
396       PetscCheck(ignoreOutsideDomain, comm, PETSC_ERR_PLIB, "Point %" PetscInt_FMT ": %g %g %g not located in mesh", p, (double)globalPoints[p * ctx->dim + 0], (double)(ctx->dim > 1 ? globalPoints[p * ctx->dim + 1] : 0.0),
397                  (double)(ctx->dim > 2 ? globalPoints[p * ctx->dim + 2] : 0.0));
398       if (rank == 0) ++ctx->n;
399     } else if (globalProcs[p] == rank) ++ctx->n;
400   }
401   /* Create coordinates vector and array of owned cells */
402   PetscCall(PetscMalloc1(ctx->n, &ctx->cells));
403   PetscCall(VecCreate(comm, &ctx->coords));
404   PetscCall(VecSetSizes(ctx->coords, ctx->n * ctx->dim, PETSC_DECIDE));
405   PetscCall(VecSetBlockSize(ctx->coords, ctx->dim));
406   PetscCall(VecSetType(ctx->coords, VECSTANDARD));
407   PetscCall(VecGetArray(ctx->coords, &a));
408   for (p = 0, q = 0, i = 0; p < N; ++p) {
409     if (globalProcs[p] == rank) {
410       PetscInt d;
411 
412       for (d = 0; d < ctx->dim; ++d, ++i) a[i] = globalPoints[p * ctx->dim + d];
413       ctx->cells[q] = foundCells[q].index;
414       ++q;
415     }
416     if (globalProcs[p] == size && rank == 0) {
417       PetscInt d;
418 
419       for (d = 0; d < ctx->dim; ++d, ++i) a[i] = 0.;
420       ctx->cells[q] = -1;
421       ++q;
422     }
423   }
424   PetscCall(VecRestoreArray(ctx->coords, &a));
425 #if 0
426   PetscCall(PetscFree3(foundCells,foundProcs,globalProcs));
427 #else
428   PetscCall(PetscFree2(foundProcs, globalProcs));
429   PetscCall(PetscSFDestroy(&cellSF));
430   PetscCall(VecDestroy(&pointVec));
431 #endif
432   if ((void *)globalPointsScalar != (void *)globalPoints) PetscCall(PetscFree(globalPointsScalar));
433   if (!redundantPoints) PetscCall(PetscFree3(globalPoints, counts, displs));
434   PetscCall(PetscLayoutDestroy(&layout));
435   PetscFunctionReturn(PETSC_SUCCESS);
436 }
437 
438 /*@C
439   DMInterpolationGetCoordinates - Gets a `Vec` with the coordinates of each interpolation point
440 
441   Collective
442 
443   Input Parameter:
444 . ctx - the context
445 
446   Output Parameter:
447 . coordinates - the coordinates of interpolation points
448 
449   Level: intermediate
450 
451   Note:
452   The local vector entries correspond to interpolation points lying on this process, according to the associated `DM`.
453   This is a borrowed vector that the user should not destroy.
454 
455 .seealso: `DMInterpolationInfo`, `DMInterpolationEvaluate()`, `DMInterpolationAddPoints()`, `DMInterpolationCreate()`
456 @*/
457 PetscErrorCode DMInterpolationGetCoordinates(DMInterpolationInfo ctx, Vec *coordinates)
458 {
459   PetscFunctionBegin;
460   PetscAssertPointer(coordinates, 2);
461   PetscCheck(ctx->coords, ctx->comm, PETSC_ERR_ARG_WRONGSTATE, "The interpolation context has not been setup.");
462   *coordinates = ctx->coords;
463   PetscFunctionReturn(PETSC_SUCCESS);
464 }
465 
466 /*@C
467   DMInterpolationGetVector - Gets a `Vec` which can hold all the interpolated field values
468 
469   Collective
470 
471   Input Parameter:
472 . ctx - the context
473 
474   Output Parameter:
475 . v - a vector capable of holding the interpolated field values
476 
477   Level: intermediate
478 
479   Note:
480   This vector should be returned using `DMInterpolationRestoreVector()`.
481 
482 .seealso: `DMInterpolationInfo`, `DMInterpolationRestoreVector()`, `DMInterpolationEvaluate()`, `DMInterpolationAddPoints()`, `DMInterpolationCreate()`
483 @*/
484 PetscErrorCode DMInterpolationGetVector(DMInterpolationInfo ctx, Vec *v)
485 {
486   PetscFunctionBegin;
487   PetscAssertPointer(v, 2);
488   PetscCheck(ctx->coords, ctx->comm, PETSC_ERR_ARG_WRONGSTATE, "The interpolation context has not been setup.");
489   PetscCall(VecCreate(ctx->comm, v));
490   PetscCall(VecSetSizes(*v, ctx->n * ctx->dof, PETSC_DECIDE));
491   PetscCall(VecSetBlockSize(*v, ctx->dof));
492   PetscCall(VecSetType(*v, VECSTANDARD));
493   PetscFunctionReturn(PETSC_SUCCESS);
494 }
495 
496 /*@C
497   DMInterpolationRestoreVector - Returns a `Vec` which can hold all the interpolated field values
498 
499   Collective
500 
501   Input Parameters:
502 + ctx - the context
503 - v   - a vector capable of holding the interpolated field values
504 
505   Level: intermediate
506 
507 .seealso: `DMInterpolationInfo`, `DMInterpolationGetVector()`, `DMInterpolationEvaluate()`, `DMInterpolationAddPoints()`, `DMInterpolationCreate()`
508 @*/
509 PetscErrorCode DMInterpolationRestoreVector(DMInterpolationInfo ctx, Vec *v)
510 {
511   PetscFunctionBegin;
512   PetscAssertPointer(v, 2);
513   PetscCheck(ctx->coords, ctx->comm, PETSC_ERR_ARG_WRONGSTATE, "The interpolation context has not been setup.");
514   PetscCall(VecDestroy(v));
515   PetscFunctionReturn(PETSC_SUCCESS);
516 }
517 
518 static inline PetscErrorCode DMInterpolate_Segment_Private(DMInterpolationInfo ctx, DM dm, Vec xLocal, Vec v)
519 {
520   PetscReal          v0, J, invJ, detJ;
521   const PetscInt     dof = ctx->dof;
522   const PetscScalar *coords;
523   PetscScalar       *a;
524   PetscInt           p;
525 
526   PetscFunctionBegin;
527   PetscCall(VecGetArrayRead(ctx->coords, &coords));
528   PetscCall(VecGetArray(v, &a));
529   for (p = 0; p < ctx->n; ++p) {
530     PetscInt     c = ctx->cells[p];
531     PetscScalar *x = NULL;
532     PetscReal    xir[1];
533     PetscInt     xSize, comp;
534 
535     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, &v0, &J, &invJ, &detJ));
536     PetscCheck(detJ > 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for element %" PetscInt_FMT, (double)detJ, c);
537     xir[0] = invJ * PetscRealPart(coords[p] - v0);
538     PetscCall(DMPlexVecGetClosure(dm, NULL, xLocal, c, &xSize, &x));
539     if (2 * dof == xSize) {
540       for (comp = 0; comp < dof; ++comp) a[p * dof + comp] = x[0 * dof + comp] * (1 - xir[0]) + x[1 * dof + comp] * xir[0];
541     } else if (dof == xSize) {
542       for (comp = 0; comp < dof; ++comp) a[p * dof + comp] = x[0 * dof + comp];
543     } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Input closure size %" PetscInt_FMT " must be either %" PetscInt_FMT " or %" PetscInt_FMT, xSize, 2 * dof, dof);
544     PetscCall(DMPlexVecRestoreClosure(dm, NULL, xLocal, c, &xSize, &x));
545   }
546   PetscCall(VecRestoreArray(v, &a));
547   PetscCall(VecRestoreArrayRead(ctx->coords, &coords));
548   PetscFunctionReturn(PETSC_SUCCESS);
549 }
550 
551 static inline PetscErrorCode DMInterpolate_Triangle_Private(DMInterpolationInfo ctx, DM dm, Vec xLocal, Vec v)
552 {
553   PetscReal         *v0, *J, *invJ, detJ;
554   const PetscScalar *coords;
555   PetscScalar       *a;
556   PetscInt           p;
557 
558   PetscFunctionBegin;
559   PetscCall(PetscMalloc3(ctx->dim, &v0, ctx->dim * ctx->dim, &J, ctx->dim * ctx->dim, &invJ));
560   PetscCall(VecGetArrayRead(ctx->coords, &coords));
561   PetscCall(VecGetArray(v, &a));
562   for (p = 0; p < ctx->n; ++p) {
563     PetscInt     c = ctx->cells[p];
564     PetscScalar *x = NULL;
565     PetscReal    xi[4];
566     PetscInt     d, f, comp;
567 
568     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, v0, J, invJ, &detJ));
569     PetscCheck(detJ > 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for element %" PetscInt_FMT, (double)detJ, c);
570     PetscCall(DMPlexVecGetClosure(dm, NULL, xLocal, c, NULL, &x));
571     for (comp = 0; comp < ctx->dof; ++comp) a[p * ctx->dof + comp] = x[0 * ctx->dof + comp];
572 
573     for (d = 0; d < ctx->dim; ++d) {
574       xi[d] = 0.0;
575       for (f = 0; f < ctx->dim; ++f) xi[d] += invJ[d * ctx->dim + f] * 0.5 * PetscRealPart(coords[p * ctx->dim + f] - v0[f]);
576       for (comp = 0; comp < ctx->dof; ++comp) a[p * ctx->dof + comp] += PetscRealPart(x[(d + 1) * ctx->dof + comp] - x[0 * ctx->dof + comp]) * xi[d];
577     }
578     PetscCall(DMPlexVecRestoreClosure(dm, NULL, xLocal, c, NULL, &x));
579   }
580   PetscCall(VecRestoreArray(v, &a));
581   PetscCall(VecRestoreArrayRead(ctx->coords, &coords));
582   PetscCall(PetscFree3(v0, J, invJ));
583   PetscFunctionReturn(PETSC_SUCCESS);
584 }
585 
586 static inline PetscErrorCode DMInterpolate_Tetrahedron_Private(DMInterpolationInfo ctx, DM dm, Vec xLocal, Vec v)
587 {
588   PetscReal         *v0, *J, *invJ, detJ;
589   const PetscScalar *coords;
590   PetscScalar       *a;
591   PetscInt           p;
592 
593   PetscFunctionBegin;
594   PetscCall(PetscMalloc3(ctx->dim, &v0, ctx->dim * ctx->dim, &J, ctx->dim * ctx->dim, &invJ));
595   PetscCall(VecGetArrayRead(ctx->coords, &coords));
596   PetscCall(VecGetArray(v, &a));
597   for (p = 0; p < ctx->n; ++p) {
598     PetscInt       c        = ctx->cells[p];
599     const PetscInt order[3] = {2, 1, 3};
600     PetscScalar   *x        = NULL;
601     PetscReal      xi[4];
602     PetscInt       d, f, comp;
603 
604     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, v0, J, invJ, &detJ));
605     PetscCheck(detJ > 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for element %" PetscInt_FMT, (double)detJ, c);
606     PetscCall(DMPlexVecGetClosure(dm, NULL, xLocal, c, NULL, &x));
607     for (comp = 0; comp < ctx->dof; ++comp) a[p * ctx->dof + comp] = x[0 * ctx->dof + comp];
608 
609     for (d = 0; d < ctx->dim; ++d) {
610       xi[d] = 0.0;
611       for (f = 0; f < ctx->dim; ++f) xi[d] += invJ[d * ctx->dim + f] * 0.5 * PetscRealPart(coords[p * ctx->dim + f] - v0[f]);
612       for (comp = 0; comp < ctx->dof; ++comp) a[p * ctx->dof + comp] += PetscRealPart(x[order[d] * ctx->dof + comp] - x[0 * ctx->dof + comp]) * xi[d];
613     }
614     PetscCall(DMPlexVecRestoreClosure(dm, NULL, xLocal, c, NULL, &x));
615   }
616   PetscCall(VecRestoreArray(v, &a));
617   PetscCall(VecRestoreArrayRead(ctx->coords, &coords));
618   PetscCall(PetscFree3(v0, J, invJ));
619   PetscFunctionReturn(PETSC_SUCCESS);
620 }
621 
622 static inline PetscErrorCode QuadMap_Private(SNES snes, Vec Xref, Vec Xreal, void *ctx)
623 {
624   const PetscScalar *vertices = (const PetscScalar *)ctx;
625   const PetscScalar  x0       = vertices[0];
626   const PetscScalar  y0       = vertices[1];
627   const PetscScalar  x1       = vertices[2];
628   const PetscScalar  y1       = vertices[3];
629   const PetscScalar  x2       = vertices[4];
630   const PetscScalar  y2       = vertices[5];
631   const PetscScalar  x3       = vertices[6];
632   const PetscScalar  y3       = vertices[7];
633   const PetscScalar  f_1      = x1 - x0;
634   const PetscScalar  g_1      = y1 - y0;
635   const PetscScalar  f_3      = x3 - x0;
636   const PetscScalar  g_3      = y3 - y0;
637   const PetscScalar  f_01     = x2 - x1 - x3 + x0;
638   const PetscScalar  g_01     = y2 - y1 - y3 + y0;
639   const PetscScalar *ref;
640   PetscScalar       *real;
641 
642   PetscFunctionBegin;
643   PetscCall(VecGetArrayRead(Xref, &ref));
644   PetscCall(VecGetArray(Xreal, &real));
645   {
646     const PetscScalar p0 = ref[0];
647     const PetscScalar p1 = ref[1];
648 
649     real[0] = x0 + f_1 * p0 + f_3 * p1 + f_01 * p0 * p1;
650     real[1] = y0 + g_1 * p0 + g_3 * p1 + g_01 * p0 * p1;
651   }
652   PetscCall(PetscLogFlops(28));
653   PetscCall(VecRestoreArrayRead(Xref, &ref));
654   PetscCall(VecRestoreArray(Xreal, &real));
655   PetscFunctionReturn(PETSC_SUCCESS);
656 }
657 
658 #include <petsc/private/dmimpl.h>
659 static inline PetscErrorCode QuadJacobian_Private(SNES snes, Vec Xref, Mat J, Mat M, void *ctx)
660 {
661   const PetscScalar *vertices = (const PetscScalar *)ctx;
662   const PetscScalar  x0       = vertices[0];
663   const PetscScalar  y0       = vertices[1];
664   const PetscScalar  x1       = vertices[2];
665   const PetscScalar  y1       = vertices[3];
666   const PetscScalar  x2       = vertices[4];
667   const PetscScalar  y2       = vertices[5];
668   const PetscScalar  x3       = vertices[6];
669   const PetscScalar  y3       = vertices[7];
670   const PetscScalar  f_01     = x2 - x1 - x3 + x0;
671   const PetscScalar  g_01     = y2 - y1 - y3 + y0;
672   const PetscScalar *ref;
673 
674   PetscFunctionBegin;
675   PetscCall(VecGetArrayRead(Xref, &ref));
676   {
677     const PetscScalar x       = ref[0];
678     const PetscScalar y       = ref[1];
679     const PetscInt    rows[2] = {0, 1};
680     PetscScalar       values[4];
681 
682     values[0] = (x1 - x0 + f_01 * y) * 0.5;
683     values[1] = (x3 - x0 + f_01 * x) * 0.5;
684     values[2] = (y1 - y0 + g_01 * y) * 0.5;
685     values[3] = (y3 - y0 + g_01 * x) * 0.5;
686     PetscCall(MatSetValues(J, 2, rows, 2, rows, values, INSERT_VALUES));
687   }
688   PetscCall(PetscLogFlops(30));
689   PetscCall(VecRestoreArrayRead(Xref, &ref));
690   PetscCall(MatAssemblyBegin(J, MAT_FINAL_ASSEMBLY));
691   PetscCall(MatAssemblyEnd(J, MAT_FINAL_ASSEMBLY));
692   PetscFunctionReturn(PETSC_SUCCESS);
693 }
694 
695 static inline PetscErrorCode DMInterpolate_Quad_Private(DMInterpolationInfo ctx, DM dm, Vec xLocal, Vec v)
696 {
697   DM                 dmCoord;
698   PetscFE            fem = NULL;
699   SNES               snes;
700   KSP                ksp;
701   PC                 pc;
702   Vec                coordsLocal, r, ref, real;
703   Mat                J;
704   PetscTabulation    T = NULL;
705   const PetscScalar *coords;
706   PetscScalar       *a;
707   PetscReal          xir[2] = {0., 0.};
708   PetscInt           Nf, p;
709   const PetscInt     dof = ctx->dof;
710 
711   PetscFunctionBegin;
712   PetscCall(DMGetNumFields(dm, &Nf));
713   if (Nf) {
714     PetscObject  obj;
715     PetscClassId id;
716 
717     PetscCall(DMGetField(dm, 0, NULL, &obj));
718     PetscCall(PetscObjectGetClassId(obj, &id));
719     if (id == PETSCFE_CLASSID) {
720       fem = (PetscFE)obj;
721       PetscCall(PetscFECreateTabulation(fem, 1, 1, xir, 0, &T));
722     }
723   }
724   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
725   PetscCall(DMGetCoordinateDM(dm, &dmCoord));
726   PetscCall(SNESCreate(PETSC_COMM_SELF, &snes));
727   PetscCall(SNESSetOptionsPrefix(snes, "quad_interp_"));
728   PetscCall(VecCreate(PETSC_COMM_SELF, &r));
729   PetscCall(VecSetSizes(r, 2, 2));
730   PetscCall(VecSetType(r, dm->vectype));
731   PetscCall(VecDuplicate(r, &ref));
732   PetscCall(VecDuplicate(r, &real));
733   PetscCall(MatCreate(PETSC_COMM_SELF, &J));
734   PetscCall(MatSetSizes(J, 2, 2, 2, 2));
735   PetscCall(MatSetType(J, MATSEQDENSE));
736   PetscCall(MatSetUp(J));
737   PetscCall(SNESSetFunction(snes, r, QuadMap_Private, NULL));
738   PetscCall(SNESSetJacobian(snes, J, J, QuadJacobian_Private, NULL));
739   PetscCall(SNESGetKSP(snes, &ksp));
740   PetscCall(KSPGetPC(ksp, &pc));
741   PetscCall(PCSetType(pc, PCLU));
742   PetscCall(SNESSetFromOptions(snes));
743 
744   PetscCall(VecGetArrayRead(ctx->coords, &coords));
745   PetscCall(VecGetArray(v, &a));
746   for (p = 0; p < ctx->n; ++p) {
747     PetscScalar *x = NULL, *vertices = NULL;
748     PetscScalar *xi;
749     PetscInt     c = ctx->cells[p], comp, coordSize, xSize;
750 
751     /* Can make this do all points at once */
752     PetscCall(DMPlexVecGetClosure(dmCoord, NULL, coordsLocal, c, &coordSize, &vertices));
753     PetscCheck(4 * 2 == coordSize, ctx->comm, PETSC_ERR_ARG_SIZ, "Invalid closure size %" PetscInt_FMT " should be %d", coordSize, 4 * 2);
754     PetscCall(DMPlexVecGetClosure(dm, NULL, xLocal, c, &xSize, &x));
755     PetscCall(SNESSetFunction(snes, NULL, NULL, vertices));
756     PetscCall(SNESSetJacobian(snes, NULL, NULL, NULL, vertices));
757     PetscCall(VecGetArray(real, &xi));
758     xi[0] = coords[p * ctx->dim + 0];
759     xi[1] = coords[p * ctx->dim + 1];
760     PetscCall(VecRestoreArray(real, &xi));
761     PetscCall(SNESSolve(snes, real, ref));
762     PetscCall(VecGetArray(ref, &xi));
763     xir[0] = PetscRealPart(xi[0]);
764     xir[1] = PetscRealPart(xi[1]);
765     if (4 * dof == xSize) {
766       for (comp = 0; comp < dof; ++comp) a[p * dof + comp] = x[0 * dof + comp] * (1 - xir[0]) * (1 - xir[1]) + x[1 * dof + comp] * xir[0] * (1 - xir[1]) + x[2 * dof + comp] * xir[0] * xir[1] + x[3 * dof + comp] * (1 - xir[0]) * xir[1];
767     } else if (dof == xSize) {
768       for (comp = 0; comp < dof; ++comp) a[p * dof + comp] = x[0 * dof + comp];
769     } else {
770       PetscInt d;
771 
772       PetscCheck(fem, ctx->comm, PETSC_ERR_ARG_WRONG, "Cannot have a higher order interpolant if the discretization is not PetscFE");
773       xir[0] = 2.0 * xir[0] - 1.0;
774       xir[1] = 2.0 * xir[1] - 1.0;
775       PetscCall(PetscFEComputeTabulation(fem, 1, xir, 0, T));
776       for (comp = 0; comp < dof; ++comp) {
777         a[p * dof + comp] = 0.0;
778         for (d = 0; d < xSize / dof; ++d) a[p * dof + comp] += x[d * dof + comp] * T->T[0][d * dof + comp];
779       }
780     }
781     PetscCall(VecRestoreArray(ref, &xi));
782     PetscCall(DMPlexVecRestoreClosure(dmCoord, NULL, coordsLocal, c, &coordSize, &vertices));
783     PetscCall(DMPlexVecRestoreClosure(dm, NULL, xLocal, c, &xSize, &x));
784   }
785   PetscCall(PetscTabulationDestroy(&T));
786   PetscCall(VecRestoreArray(v, &a));
787   PetscCall(VecRestoreArrayRead(ctx->coords, &coords));
788 
789   PetscCall(SNESDestroy(&snes));
790   PetscCall(VecDestroy(&r));
791   PetscCall(VecDestroy(&ref));
792   PetscCall(VecDestroy(&real));
793   PetscCall(MatDestroy(&J));
794   PetscFunctionReturn(PETSC_SUCCESS);
795 }
796 
797 static inline PetscErrorCode HexMap_Private(SNES snes, Vec Xref, Vec Xreal, void *ctx)
798 {
799   const PetscScalar *vertices = (const PetscScalar *)ctx;
800   const PetscScalar  x0       = vertices[0];
801   const PetscScalar  y0       = vertices[1];
802   const PetscScalar  z0       = vertices[2];
803   const PetscScalar  x1       = vertices[9];
804   const PetscScalar  y1       = vertices[10];
805   const PetscScalar  z1       = vertices[11];
806   const PetscScalar  x2       = vertices[6];
807   const PetscScalar  y2       = vertices[7];
808   const PetscScalar  z2       = vertices[8];
809   const PetscScalar  x3       = vertices[3];
810   const PetscScalar  y3       = vertices[4];
811   const PetscScalar  z3       = vertices[5];
812   const PetscScalar  x4       = vertices[12];
813   const PetscScalar  y4       = vertices[13];
814   const PetscScalar  z4       = vertices[14];
815   const PetscScalar  x5       = vertices[15];
816   const PetscScalar  y5       = vertices[16];
817   const PetscScalar  z5       = vertices[17];
818   const PetscScalar  x6       = vertices[18];
819   const PetscScalar  y6       = vertices[19];
820   const PetscScalar  z6       = vertices[20];
821   const PetscScalar  x7       = vertices[21];
822   const PetscScalar  y7       = vertices[22];
823   const PetscScalar  z7       = vertices[23];
824   const PetscScalar  f_1      = x1 - x0;
825   const PetscScalar  g_1      = y1 - y0;
826   const PetscScalar  h_1      = z1 - z0;
827   const PetscScalar  f_3      = x3 - x0;
828   const PetscScalar  g_3      = y3 - y0;
829   const PetscScalar  h_3      = z3 - z0;
830   const PetscScalar  f_4      = x4 - x0;
831   const PetscScalar  g_4      = y4 - y0;
832   const PetscScalar  h_4      = z4 - z0;
833   const PetscScalar  f_01     = x2 - x1 - x3 + x0;
834   const PetscScalar  g_01     = y2 - y1 - y3 + y0;
835   const PetscScalar  h_01     = z2 - z1 - z3 + z0;
836   const PetscScalar  f_12     = x7 - x3 - x4 + x0;
837   const PetscScalar  g_12     = y7 - y3 - y4 + y0;
838   const PetscScalar  h_12     = z7 - z3 - z4 + z0;
839   const PetscScalar  f_02     = x5 - x1 - x4 + x0;
840   const PetscScalar  g_02     = y5 - y1 - y4 + y0;
841   const PetscScalar  h_02     = z5 - z1 - z4 + z0;
842   const PetscScalar  f_012    = x6 - x0 + x1 - x2 + x3 + x4 - x5 - x7;
843   const PetscScalar  g_012    = y6 - y0 + y1 - y2 + y3 + y4 - y5 - y7;
844   const PetscScalar  h_012    = z6 - z0 + z1 - z2 + z3 + z4 - z5 - z7;
845   const PetscScalar *ref;
846   PetscScalar       *real;
847 
848   PetscFunctionBegin;
849   PetscCall(VecGetArrayRead(Xref, &ref));
850   PetscCall(VecGetArray(Xreal, &real));
851   {
852     const PetscScalar p0 = ref[0];
853     const PetscScalar p1 = ref[1];
854     const PetscScalar p2 = ref[2];
855 
856     real[0] = x0 + f_1 * p0 + f_3 * p1 + f_4 * p2 + f_01 * p0 * p1 + f_12 * p1 * p2 + f_02 * p0 * p2 + f_012 * p0 * p1 * p2;
857     real[1] = y0 + g_1 * p0 + g_3 * p1 + g_4 * p2 + g_01 * p0 * p1 + g_01 * p0 * p1 + g_12 * p1 * p2 + g_02 * p0 * p2 + g_012 * p0 * p1 * p2;
858     real[2] = z0 + h_1 * p0 + h_3 * p1 + h_4 * p2 + h_01 * p0 * p1 + h_01 * p0 * p1 + h_12 * p1 * p2 + h_02 * p0 * p2 + h_012 * p0 * p1 * p2;
859   }
860   PetscCall(PetscLogFlops(114));
861   PetscCall(VecRestoreArrayRead(Xref, &ref));
862   PetscCall(VecRestoreArray(Xreal, &real));
863   PetscFunctionReturn(PETSC_SUCCESS);
864 }
865 
866 static inline PetscErrorCode HexJacobian_Private(SNES snes, Vec Xref, Mat J, Mat M, void *ctx)
867 {
868   const PetscScalar *vertices = (const PetscScalar *)ctx;
869   const PetscScalar  x0       = vertices[0];
870   const PetscScalar  y0       = vertices[1];
871   const PetscScalar  z0       = vertices[2];
872   const PetscScalar  x1       = vertices[9];
873   const PetscScalar  y1       = vertices[10];
874   const PetscScalar  z1       = vertices[11];
875   const PetscScalar  x2       = vertices[6];
876   const PetscScalar  y2       = vertices[7];
877   const PetscScalar  z2       = vertices[8];
878   const PetscScalar  x3       = vertices[3];
879   const PetscScalar  y3       = vertices[4];
880   const PetscScalar  z3       = vertices[5];
881   const PetscScalar  x4       = vertices[12];
882   const PetscScalar  y4       = vertices[13];
883   const PetscScalar  z4       = vertices[14];
884   const PetscScalar  x5       = vertices[15];
885   const PetscScalar  y5       = vertices[16];
886   const PetscScalar  z5       = vertices[17];
887   const PetscScalar  x6       = vertices[18];
888   const PetscScalar  y6       = vertices[19];
889   const PetscScalar  z6       = vertices[20];
890   const PetscScalar  x7       = vertices[21];
891   const PetscScalar  y7       = vertices[22];
892   const PetscScalar  z7       = vertices[23];
893   const PetscScalar  f_xy     = x2 - x1 - x3 + x0;
894   const PetscScalar  g_xy     = y2 - y1 - y3 + y0;
895   const PetscScalar  h_xy     = z2 - z1 - z3 + z0;
896   const PetscScalar  f_yz     = x7 - x3 - x4 + x0;
897   const PetscScalar  g_yz     = y7 - y3 - y4 + y0;
898   const PetscScalar  h_yz     = z7 - z3 - z4 + z0;
899   const PetscScalar  f_xz     = x5 - x1 - x4 + x0;
900   const PetscScalar  g_xz     = y5 - y1 - y4 + y0;
901   const PetscScalar  h_xz     = z5 - z1 - z4 + z0;
902   const PetscScalar  f_xyz    = x6 - x0 + x1 - x2 + x3 + x4 - x5 - x7;
903   const PetscScalar  g_xyz    = y6 - y0 + y1 - y2 + y3 + y4 - y5 - y7;
904   const PetscScalar  h_xyz    = z6 - z0 + z1 - z2 + z3 + z4 - z5 - z7;
905   const PetscScalar *ref;
906 
907   PetscFunctionBegin;
908   PetscCall(VecGetArrayRead(Xref, &ref));
909   {
910     const PetscScalar x       = ref[0];
911     const PetscScalar y       = ref[1];
912     const PetscScalar z       = ref[2];
913     const PetscInt    rows[3] = {0, 1, 2};
914     PetscScalar       values[9];
915 
916     values[0] = (x1 - x0 + f_xy * y + f_xz * z + f_xyz * y * z) / 2.0;
917     values[1] = (x3 - x0 + f_xy * x + f_yz * z + f_xyz * x * z) / 2.0;
918     values[2] = (x4 - x0 + f_yz * y + f_xz * x + f_xyz * x * y) / 2.0;
919     values[3] = (y1 - y0 + g_xy * y + g_xz * z + g_xyz * y * z) / 2.0;
920     values[4] = (y3 - y0 + g_xy * x + g_yz * z + g_xyz * x * z) / 2.0;
921     values[5] = (y4 - y0 + g_yz * y + g_xz * x + g_xyz * x * y) / 2.0;
922     values[6] = (z1 - z0 + h_xy * y + h_xz * z + h_xyz * y * z) / 2.0;
923     values[7] = (z3 - z0 + h_xy * x + h_yz * z + h_xyz * x * z) / 2.0;
924     values[8] = (z4 - z0 + h_yz * y + h_xz * x + h_xyz * x * y) / 2.0;
925 
926     PetscCall(MatSetValues(J, 3, rows, 3, rows, values, INSERT_VALUES));
927   }
928   PetscCall(PetscLogFlops(152));
929   PetscCall(VecRestoreArrayRead(Xref, &ref));
930   PetscCall(MatAssemblyBegin(J, MAT_FINAL_ASSEMBLY));
931   PetscCall(MatAssemblyEnd(J, MAT_FINAL_ASSEMBLY));
932   PetscFunctionReturn(PETSC_SUCCESS);
933 }
934 
935 static inline PetscErrorCode DMInterpolate_Hex_Private(DMInterpolationInfo ctx, DM dm, Vec xLocal, Vec v)
936 {
937   DM                 dmCoord;
938   SNES               snes;
939   KSP                ksp;
940   PC                 pc;
941   Vec                coordsLocal, r, ref, real;
942   Mat                J;
943   const PetscScalar *coords;
944   PetscScalar       *a;
945   PetscInt           p;
946 
947   PetscFunctionBegin;
948   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
949   PetscCall(DMGetCoordinateDM(dm, &dmCoord));
950   PetscCall(SNESCreate(PETSC_COMM_SELF, &snes));
951   PetscCall(SNESSetOptionsPrefix(snes, "hex_interp_"));
952   PetscCall(VecCreate(PETSC_COMM_SELF, &r));
953   PetscCall(VecSetSizes(r, 3, 3));
954   PetscCall(VecSetType(r, dm->vectype));
955   PetscCall(VecDuplicate(r, &ref));
956   PetscCall(VecDuplicate(r, &real));
957   PetscCall(MatCreate(PETSC_COMM_SELF, &J));
958   PetscCall(MatSetSizes(J, 3, 3, 3, 3));
959   PetscCall(MatSetType(J, MATSEQDENSE));
960   PetscCall(MatSetUp(J));
961   PetscCall(SNESSetFunction(snes, r, HexMap_Private, NULL));
962   PetscCall(SNESSetJacobian(snes, J, J, HexJacobian_Private, NULL));
963   PetscCall(SNESGetKSP(snes, &ksp));
964   PetscCall(KSPGetPC(ksp, &pc));
965   PetscCall(PCSetType(pc, PCLU));
966   PetscCall(SNESSetFromOptions(snes));
967 
968   PetscCall(VecGetArrayRead(ctx->coords, &coords));
969   PetscCall(VecGetArray(v, &a));
970   for (p = 0; p < ctx->n; ++p) {
971     PetscScalar *x = NULL, *vertices = NULL;
972     PetscScalar *xi;
973     PetscReal    xir[3];
974     PetscInt     c = ctx->cells[p], comp, coordSize, xSize;
975 
976     /* Can make this do all points at once */
977     PetscCall(DMPlexVecGetClosure(dmCoord, NULL, coordsLocal, c, &coordSize, &vertices));
978     PetscCheck(8 * 3 == coordSize, ctx->comm, PETSC_ERR_ARG_SIZ, "Invalid coordinate closure size %" PetscInt_FMT " should be %d", coordSize, 8 * 3);
979     PetscCall(DMPlexVecGetClosure(dm, NULL, xLocal, c, &xSize, &x));
980     PetscCheck((8 * ctx->dof == xSize) || (ctx->dof == xSize), ctx->comm, PETSC_ERR_ARG_SIZ, "Invalid input closure size %" PetscInt_FMT " should be %" PetscInt_FMT " or %" PetscInt_FMT, xSize, 8 * ctx->dof, ctx->dof);
981     PetscCall(SNESSetFunction(snes, NULL, NULL, vertices));
982     PetscCall(SNESSetJacobian(snes, NULL, NULL, NULL, vertices));
983     PetscCall(VecGetArray(real, &xi));
984     xi[0] = coords[p * ctx->dim + 0];
985     xi[1] = coords[p * ctx->dim + 1];
986     xi[2] = coords[p * ctx->dim + 2];
987     PetscCall(VecRestoreArray(real, &xi));
988     PetscCall(SNESSolve(snes, real, ref));
989     PetscCall(VecGetArray(ref, &xi));
990     xir[0] = PetscRealPart(xi[0]);
991     xir[1] = PetscRealPart(xi[1]);
992     xir[2] = PetscRealPart(xi[2]);
993     if (8 * ctx->dof == xSize) {
994       for (comp = 0; comp < ctx->dof; ++comp) {
995         a[p * ctx->dof + comp] = x[0 * ctx->dof + comp] * (1 - xir[0]) * (1 - xir[1]) * (1 - xir[2]) + x[3 * ctx->dof + comp] * xir[0] * (1 - xir[1]) * (1 - xir[2]) + x[2 * ctx->dof + comp] * xir[0] * xir[1] * (1 - xir[2]) + x[1 * ctx->dof + comp] * (1 - xir[0]) * xir[1] * (1 - xir[2]) +
996                                  x[4 * ctx->dof + comp] * (1 - xir[0]) * (1 - xir[1]) * xir[2] + x[5 * ctx->dof + comp] * xir[0] * (1 - xir[1]) * xir[2] + x[6 * ctx->dof + comp] * xir[0] * xir[1] * xir[2] + x[7 * ctx->dof + comp] * (1 - xir[0]) * xir[1] * xir[2];
997       }
998     } else {
999       for (comp = 0; comp < ctx->dof; ++comp) a[p * ctx->dof + comp] = x[0 * ctx->dof + comp];
1000     }
1001     PetscCall(VecRestoreArray(ref, &xi));
1002     PetscCall(DMPlexVecRestoreClosure(dmCoord, NULL, coordsLocal, c, &coordSize, &vertices));
1003     PetscCall(DMPlexVecRestoreClosure(dm, NULL, xLocal, c, &xSize, &x));
1004   }
1005   PetscCall(VecRestoreArray(v, &a));
1006   PetscCall(VecRestoreArrayRead(ctx->coords, &coords));
1007 
1008   PetscCall(SNESDestroy(&snes));
1009   PetscCall(VecDestroy(&r));
1010   PetscCall(VecDestroy(&ref));
1011   PetscCall(VecDestroy(&real));
1012   PetscCall(MatDestroy(&J));
1013   PetscFunctionReturn(PETSC_SUCCESS);
1014 }
1015 
1016 /*@C
1017   DMInterpolationEvaluate - Using the input from dm and x, calculates interpolated field values at the interpolation points.
1018 
1019   Input Parameters:
1020 + ctx - The `DMInterpolationInfo` context
1021 . dm  - The `DM`
1022 - x   - The local vector containing the field to be interpolated
1023 
1024   Output Parameter:
1025 . v - The vector containing the interpolated values
1026 
1027   Level: beginner
1028 
1029   Note:
1030   A suitable `v` can be obtained using `DMInterpolationGetVector()`.
1031 
1032 .seealso: `DMInterpolationInfo`, `DMInterpolationGetVector()`, `DMInterpolationAddPoints()`, `DMInterpolationCreate()`
1033 @*/
1034 PetscErrorCode DMInterpolationEvaluate(DMInterpolationInfo ctx, DM dm, Vec x, Vec v)
1035 {
1036   PetscDS   ds;
1037   PetscInt  n, p, Nf, field;
1038   PetscBool useDS = PETSC_FALSE;
1039 
1040   PetscFunctionBegin;
1041   PetscValidHeaderSpecific(dm, DM_CLASSID, 2);
1042   PetscValidHeaderSpecific(x, VEC_CLASSID, 3);
1043   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
1044   PetscCall(VecGetLocalSize(v, &n));
1045   PetscCheck(n == ctx->n * ctx->dof, ctx->comm, PETSC_ERR_ARG_SIZ, "Invalid input vector size %" PetscInt_FMT " should be %" PetscInt_FMT, n, ctx->n * ctx->dof);
1046   if (!n) PetscFunctionReturn(PETSC_SUCCESS);
1047   PetscCall(DMGetDS(dm, &ds));
1048   if (ds) {
1049     useDS = PETSC_TRUE;
1050     PetscCall(PetscDSGetNumFields(ds, &Nf));
1051     for (field = 0; field < Nf; ++field) {
1052       PetscObject  obj;
1053       PetscClassId id;
1054 
1055       PetscCall(PetscDSGetDiscretization(ds, field, &obj));
1056       PetscCall(PetscObjectGetClassId(obj, &id));
1057       if (id != PETSCFE_CLASSID) {
1058         useDS = PETSC_FALSE;
1059         break;
1060       }
1061     }
1062   }
1063   if (useDS) {
1064     const PetscScalar *coords;
1065     PetscScalar       *interpolant;
1066     PetscInt           cdim, d;
1067 
1068     PetscCall(DMGetCoordinateDim(dm, &cdim));
1069     PetscCall(VecGetArrayRead(ctx->coords, &coords));
1070     PetscCall(VecGetArrayWrite(v, &interpolant));
1071     for (p = 0; p < ctx->n; ++p) {
1072       PetscReal    pcoords[3], xi[3];
1073       PetscScalar *xa   = NULL;
1074       PetscInt     coff = 0, foff = 0, clSize;
1075 
1076       if (ctx->cells[p] < 0) continue;
1077       for (d = 0; d < cdim; ++d) pcoords[d] = PetscRealPart(coords[p * cdim + d]);
1078       PetscCall(DMPlexCoordinatesToReference(dm, ctx->cells[p], 1, pcoords, xi));
1079       PetscCall(DMPlexVecGetClosure(dm, NULL, x, ctx->cells[p], &clSize, &xa));
1080       for (field = 0; field < Nf; ++field) {
1081         PetscTabulation T;
1082         PetscFE         fe;
1083 
1084         PetscCall(PetscDSGetDiscretization(ds, field, (PetscObject *)&fe));
1085         PetscCall(PetscFECreateTabulation(fe, 1, 1, xi, 0, &T));
1086         {
1087           const PetscReal *basis = T->T[0];
1088           const PetscInt   Nb    = T->Nb;
1089           const PetscInt   Nc    = T->Nc;
1090           PetscInt         f, fc;
1091 
1092           for (fc = 0; fc < Nc; ++fc) {
1093             interpolant[p * ctx->dof + coff + fc] = 0.0;
1094             for (f = 0; f < Nb; ++f) interpolant[p * ctx->dof + coff + fc] += xa[foff + f] * basis[(0 * Nb + f) * Nc + fc];
1095           }
1096           coff += Nc;
1097           foff += Nb;
1098         }
1099         PetscCall(PetscTabulationDestroy(&T));
1100       }
1101       PetscCall(DMPlexVecRestoreClosure(dm, NULL, x, ctx->cells[p], &clSize, &xa));
1102       PetscCheck(coff == ctx->dof, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Total components %" PetscInt_FMT " != %" PetscInt_FMT " dof specified for interpolation", coff, ctx->dof);
1103       PetscCheck(foff == clSize, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Total FE space size %" PetscInt_FMT " != %" PetscInt_FMT " closure size", foff, clSize);
1104     }
1105     PetscCall(VecRestoreArrayRead(ctx->coords, &coords));
1106     PetscCall(VecRestoreArrayWrite(v, &interpolant));
1107   } else {
1108     DMPolytopeType ct;
1109 
1110     /* TODO Check each cell individually */
1111     PetscCall(DMPlexGetCellType(dm, ctx->cells[0], &ct));
1112     switch (ct) {
1113     case DM_POLYTOPE_SEGMENT:
1114       PetscCall(DMInterpolate_Segment_Private(ctx, dm, x, v));
1115       break;
1116     case DM_POLYTOPE_TRIANGLE:
1117       PetscCall(DMInterpolate_Triangle_Private(ctx, dm, x, v));
1118       break;
1119     case DM_POLYTOPE_QUADRILATERAL:
1120       PetscCall(DMInterpolate_Quad_Private(ctx, dm, x, v));
1121       break;
1122     case DM_POLYTOPE_TETRAHEDRON:
1123       PetscCall(DMInterpolate_Tetrahedron_Private(ctx, dm, x, v));
1124       break;
1125     case DM_POLYTOPE_HEXAHEDRON:
1126       PetscCall(DMInterpolate_Hex_Private(ctx, dm, x, v));
1127       break;
1128     default:
1129       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "No support for cell type %s", DMPolytopeTypes[PetscMax(0, PetscMin(ct, DM_NUM_POLYTOPES))]);
1130     }
1131   }
1132   PetscFunctionReturn(PETSC_SUCCESS);
1133 }
1134 
1135 /*@C
1136   DMInterpolationDestroy - Destroys a `DMInterpolationInfo` context
1137 
1138   Collective
1139 
1140   Input Parameter:
1141 . ctx - the context
1142 
1143   Level: beginner
1144 
1145 .seealso: `DMInterpolationInfo`, `DMInterpolationEvaluate()`, `DMInterpolationAddPoints()`, `DMInterpolationCreate()`
1146 @*/
1147 PetscErrorCode DMInterpolationDestroy(DMInterpolationInfo *ctx)
1148 {
1149   PetscFunctionBegin;
1150   PetscAssertPointer(ctx, 1);
1151   PetscCall(VecDestroy(&(*ctx)->coords));
1152   PetscCall(PetscFree((*ctx)->points));
1153   PetscCall(PetscFree((*ctx)->cells));
1154   PetscCall(PetscFree(*ctx));
1155   *ctx = NULL;
1156   PetscFunctionReturn(PETSC_SUCCESS);
1157 }
1158 
1159 /*@C
1160   SNESMonitorFields - Monitors the residual for each field separately
1161 
1162   Collective
1163 
1164   Input Parameters:
1165 + snes   - the `SNES` context
1166 . its    - iteration number
1167 . fgnorm - 2-norm of residual
1168 - vf     - `PetscViewerAndFormat` of `PetscViewerType` `PETSCVIEWERASCII`
1169 
1170   Level: intermediate
1171 
1172   Note:
1173   This routine prints the residual norm at each iteration.
1174 
1175 .seealso: `SNES`, `SNESMonitorSet()`, `SNESMonitorDefault()`
1176 @*/
1177 PetscErrorCode SNESMonitorFields(SNES snes, PetscInt its, PetscReal fgnorm, PetscViewerAndFormat *vf)
1178 {
1179   PetscViewer        viewer = vf->viewer;
1180   Vec                res;
1181   DM                 dm;
1182   PetscSection       s;
1183   const PetscScalar *r;
1184   PetscReal         *lnorms, *norms;
1185   PetscInt           numFields, f, pStart, pEnd, p;
1186 
1187   PetscFunctionBegin;
1188   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 4);
1189   PetscCall(SNESGetFunction(snes, &res, NULL, NULL));
1190   PetscCall(SNESGetDM(snes, &dm));
1191   PetscCall(DMGetLocalSection(dm, &s));
1192   PetscCall(PetscSectionGetNumFields(s, &numFields));
1193   PetscCall(PetscSectionGetChart(s, &pStart, &pEnd));
1194   PetscCall(PetscCalloc2(numFields, &lnorms, numFields, &norms));
1195   PetscCall(VecGetArrayRead(res, &r));
1196   for (p = pStart; p < pEnd; ++p) {
1197     for (f = 0; f < numFields; ++f) {
1198       PetscInt fdof, foff, d;
1199 
1200       PetscCall(PetscSectionGetFieldDof(s, p, f, &fdof));
1201       PetscCall(PetscSectionGetFieldOffset(s, p, f, &foff));
1202       for (d = 0; d < fdof; ++d) lnorms[f] += PetscRealPart(PetscSqr(r[foff + d]));
1203     }
1204   }
1205   PetscCall(VecRestoreArrayRead(res, &r));
1206   PetscCall(MPIU_Allreduce(lnorms, norms, numFields, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
1207   PetscCall(PetscViewerPushFormat(viewer, vf->format));
1208   PetscCall(PetscViewerASCIIAddTab(viewer, ((PetscObject)snes)->tablevel));
1209   PetscCall(PetscViewerASCIIPrintf(viewer, "%3" PetscInt_FMT " SNES Function norm %14.12e [", its, (double)fgnorm));
1210   for (f = 0; f < numFields; ++f) {
1211     if (f > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1212     PetscCall(PetscViewerASCIIPrintf(viewer, "%14.12e", (double)PetscSqrtReal(norms[f])));
1213   }
1214   PetscCall(PetscViewerASCIIPrintf(viewer, "]\n"));
1215   PetscCall(PetscViewerASCIISubtractTab(viewer, ((PetscObject)snes)->tablevel));
1216   PetscCall(PetscViewerPopFormat(viewer));
1217   PetscCall(PetscFree2(lnorms, norms));
1218   PetscFunctionReturn(PETSC_SUCCESS);
1219 }
1220 
1221 /********************* Residual Computation **************************/
1222 
1223 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
1224 {
1225   PetscInt depth;
1226 
1227   PetscFunctionBegin;
1228   PetscCall(DMPlexGetDepth(plex, &depth));
1229   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
1230   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
1231   PetscFunctionReturn(PETSC_SUCCESS);
1232 }
1233 
1234 /*@
1235   DMPlexSNESComputeResidualFEM - Sums the local residual into vector F from the local input X using pointwise functions specified by the user
1236 
1237   Input Parameters:
1238 + dm   - The mesh
1239 . X    - Local solution
1240 - user - The user context
1241 
1242   Output Parameter:
1243 . F - Local output vector
1244 
1245   Level: developer
1246 
1247   Note:
1248   The residual is summed into F; the caller is responsible for using `VecZeroEntries()` or otherwise ensuring that any data in F is intentional.
1249 
1250 .seealso: `DM`, `DMPlexComputeJacobianAction()`
1251 @*/
1252 PetscErrorCode DMPlexSNESComputeResidualFEM(DM dm, Vec X, Vec F, void *user)
1253 {
1254   DM       plex;
1255   IS       allcellIS;
1256   PetscInt Nds, s;
1257 
1258   PetscFunctionBegin;
1259   PetscCall(DMSNESConvertPlex(dm, &plex, PETSC_TRUE));
1260   PetscCall(DMPlexGetAllCells_Internal(plex, &allcellIS));
1261   PetscCall(DMGetNumDS(dm, &Nds));
1262   for (s = 0; s < Nds; ++s) {
1263     PetscDS      ds;
1264     IS           cellIS;
1265     PetscFormKey key;
1266 
1267     PetscCall(DMGetRegionNumDS(dm, s, &key.label, NULL, &ds, NULL));
1268     key.value = 0;
1269     key.field = 0;
1270     key.part  = 0;
1271     if (!key.label) {
1272       PetscCall(PetscObjectReference((PetscObject)allcellIS));
1273       cellIS = allcellIS;
1274     } else {
1275       IS pointIS;
1276 
1277       key.value = 1;
1278       PetscCall(DMLabelGetStratumIS(key.label, key.value, &pointIS));
1279       PetscCall(ISIntersect_Caching_Internal(allcellIS, pointIS, &cellIS));
1280       PetscCall(ISDestroy(&pointIS));
1281     }
1282     PetscCall(DMPlexComputeResidual_Internal(plex, key, cellIS, PETSC_MIN_REAL, X, NULL, 0.0, F, user));
1283     PetscCall(ISDestroy(&cellIS));
1284   }
1285   PetscCall(ISDestroy(&allcellIS));
1286   PetscCall(DMDestroy(&plex));
1287   PetscFunctionReturn(PETSC_SUCCESS);
1288 }
1289 
1290 /*@
1291   DMPlexSNESComputeBoundaryFEM - Form the boundary values for the local input X
1292 
1293   Input Parameters:
1294 + dm   - The mesh
1295 - user - The user context
1296 
1297   Output Parameter:
1298 . X - Local solution
1299 
1300   Level: developer
1301 
1302 .seealso: `DMPLEX`, `DMPlexComputeJacobianAction()`
1303 @*/
1304 PetscErrorCode DMPlexSNESComputeBoundaryFEM(DM dm, Vec X, void *user)
1305 {
1306   DM plex;
1307 
1308   PetscFunctionBegin;
1309   PetscCall(DMSNESConvertPlex(dm, &plex, PETSC_TRUE));
1310   PetscCall(DMPlexInsertBoundaryValues(plex, PETSC_TRUE, X, PETSC_MIN_REAL, NULL, NULL, NULL));
1311   PetscCall(DMDestroy(&plex));
1312   PetscFunctionReturn(PETSC_SUCCESS);
1313 }
1314 
1315 /*@
1316   DMSNESComputeJacobianAction - Compute the action of the Jacobian J(X) on Y
1317 
1318   Input Parameters:
1319 + dm   - The `DM`
1320 . X    - Local solution vector
1321 . Y    - Local input vector
1322 - user - The user context
1323 
1324   Output Parameter:
1325 . F - local output vector
1326 
1327   Level: developer
1328 
1329   Notes:
1330   Users will typically use `DMSNESCreateJacobianMF()` followed by `MatMult()` instead of calling this routine directly.
1331 
1332 .seealso: `DM`, ``DMSNESCreateJacobianMF()`, `DMPlexSNESComputeResidualFEM()`
1333 @*/
1334 PetscErrorCode DMSNESComputeJacobianAction(DM dm, Vec X, Vec Y, Vec F, void *user)
1335 {
1336   DM       plex;
1337   IS       allcellIS;
1338   PetscInt Nds, s;
1339 
1340   PetscFunctionBegin;
1341   PetscCall(DMSNESConvertPlex(dm, &plex, PETSC_TRUE));
1342   PetscCall(DMPlexGetAllCells_Internal(plex, &allcellIS));
1343   PetscCall(DMGetNumDS(dm, &Nds));
1344   for (s = 0; s < Nds; ++s) {
1345     PetscDS ds;
1346     DMLabel label;
1347     IS      cellIS;
1348 
1349     PetscCall(DMGetRegionNumDS(dm, s, &label, NULL, &ds, NULL));
1350     {
1351       PetscWeakFormKind jacmap[4] = {PETSC_WF_G0, PETSC_WF_G1, PETSC_WF_G2, PETSC_WF_G3};
1352       PetscWeakForm     wf;
1353       PetscInt          Nm = 4, m, Nk = 0, k, kp, off = 0;
1354       PetscFormKey     *jackeys;
1355 
1356       /* Get unique Jacobian keys */
1357       for (m = 0; m < Nm; ++m) {
1358         PetscInt Nkm;
1359         PetscCall(PetscHMapFormGetSize(ds->wf->form[jacmap[m]], &Nkm));
1360         Nk += Nkm;
1361       }
1362       PetscCall(PetscMalloc1(Nk, &jackeys));
1363       for (m = 0; m < Nm; ++m) PetscCall(PetscHMapFormGetKeys(ds->wf->form[jacmap[m]], &off, jackeys));
1364       PetscCheck(off == Nk, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Number of keys %" PetscInt_FMT " should be %" PetscInt_FMT, off, Nk);
1365       PetscCall(PetscFormKeySort(Nk, jackeys));
1366       for (k = 0, kp = 1; kp < Nk; ++kp) {
1367         if ((jackeys[k].label != jackeys[kp].label) || (jackeys[k].value != jackeys[kp].value)) {
1368           ++k;
1369           if (kp != k) jackeys[k] = jackeys[kp];
1370         }
1371       }
1372       Nk = k;
1373 
1374       PetscCall(PetscDSGetWeakForm(ds, &wf));
1375       for (k = 0; k < Nk; ++k) {
1376         DMLabel  label = jackeys[k].label;
1377         PetscInt val   = jackeys[k].value;
1378 
1379         if (!label) {
1380           PetscCall(PetscObjectReference((PetscObject)allcellIS));
1381           cellIS = allcellIS;
1382         } else {
1383           IS pointIS;
1384 
1385           PetscCall(DMLabelGetStratumIS(label, val, &pointIS));
1386           PetscCall(ISIntersect_Caching_Internal(allcellIS, pointIS, &cellIS));
1387           PetscCall(ISDestroy(&pointIS));
1388         }
1389         PetscCall(DMPlexComputeJacobian_Action_Internal(plex, jackeys[k], cellIS, 0.0, 0.0, X, NULL, Y, F, user));
1390         PetscCall(ISDestroy(&cellIS));
1391       }
1392       PetscCall(PetscFree(jackeys));
1393     }
1394   }
1395   PetscCall(ISDestroy(&allcellIS));
1396   PetscCall(DMDestroy(&plex));
1397   PetscFunctionReturn(PETSC_SUCCESS);
1398 }
1399 
1400 /*@
1401   DMPlexSNESComputeJacobianFEM - Form the local portion of the Jacobian matrix `Jac` at the local solution `X` using pointwise functions specified by the user.
1402 
1403   Input Parameters:
1404 + dm   - The `DM`
1405 . X    - Local input vector
1406 - user - The user context
1407 
1408   Output Parameters:
1409 + Jac  - Jacobian matrix
1410 - JacP - approximate Jacobian from which the preconditioner will be built, often `Jac`
1411 
1412   Level: developer
1413 
1414   Note:
1415   We form the residual one batch of elements at a time. This allows us to offload work onto an accelerator,
1416   like a GPU, or vectorize on a multicore machine.
1417 
1418 .seealso: `DMPLEX`, `Mat`
1419 @*/
1420 PetscErrorCode DMPlexSNESComputeJacobianFEM(DM dm, Vec X, Mat Jac, Mat JacP, void *user)
1421 {
1422   DM        plex;
1423   IS        allcellIS;
1424   PetscBool hasJac, hasPrec;
1425   PetscInt  Nds, s;
1426 
1427   PetscFunctionBegin;
1428   PetscCall(DMSNESConvertPlex(dm, &plex, PETSC_TRUE));
1429   PetscCall(DMPlexGetAllCells_Internal(plex, &allcellIS));
1430   PetscCall(DMGetNumDS(dm, &Nds));
1431   for (s = 0; s < Nds; ++s) {
1432     PetscDS      ds;
1433     IS           cellIS;
1434     PetscFormKey key;
1435 
1436     PetscCall(DMGetRegionNumDS(dm, s, &key.label, NULL, &ds, NULL));
1437     key.value = 0;
1438     key.field = 0;
1439     key.part  = 0;
1440     if (!key.label) {
1441       PetscCall(PetscObjectReference((PetscObject)allcellIS));
1442       cellIS = allcellIS;
1443     } else {
1444       IS pointIS;
1445 
1446       key.value = 1;
1447       PetscCall(DMLabelGetStratumIS(key.label, key.value, &pointIS));
1448       PetscCall(ISIntersect_Caching_Internal(allcellIS, pointIS, &cellIS));
1449       PetscCall(ISDestroy(&pointIS));
1450     }
1451     if (!s) {
1452       PetscCall(PetscDSHasJacobian(ds, &hasJac));
1453       PetscCall(PetscDSHasJacobianPreconditioner(ds, &hasPrec));
1454       if (hasJac && hasPrec) PetscCall(MatZeroEntries(Jac));
1455       PetscCall(MatZeroEntries(JacP));
1456     }
1457     PetscCall(DMPlexComputeJacobian_Internal(plex, key, cellIS, 0.0, 0.0, X, NULL, Jac, JacP, user));
1458     PetscCall(ISDestroy(&cellIS));
1459   }
1460   PetscCall(ISDestroy(&allcellIS));
1461   PetscCall(DMDestroy(&plex));
1462   PetscFunctionReturn(PETSC_SUCCESS);
1463 }
1464 
1465 struct _DMSNESJacobianMFCtx {
1466   DM    dm;
1467   Vec   X;
1468   void *ctx;
1469 };
1470 
1471 static PetscErrorCode DMSNESJacobianMF_Destroy_Private(Mat A)
1472 {
1473   struct _DMSNESJacobianMFCtx *ctx;
1474 
1475   PetscFunctionBegin;
1476   PetscCall(MatShellGetContext(A, &ctx));
1477   PetscCall(MatShellSetContext(A, NULL));
1478   PetscCall(DMDestroy(&ctx->dm));
1479   PetscCall(VecDestroy(&ctx->X));
1480   PetscCall(PetscFree(ctx));
1481   PetscFunctionReturn(PETSC_SUCCESS);
1482 }
1483 
1484 static PetscErrorCode DMSNESJacobianMF_Mult_Private(Mat A, Vec Y, Vec Z)
1485 {
1486   struct _DMSNESJacobianMFCtx *ctx;
1487 
1488   PetscFunctionBegin;
1489   PetscCall(MatShellGetContext(A, &ctx));
1490   PetscCall(DMSNESComputeJacobianAction(ctx->dm, ctx->X, Y, Z, ctx->ctx));
1491   PetscFunctionReturn(PETSC_SUCCESS);
1492 }
1493 
1494 /*@
1495   DMSNESCreateJacobianMF - Create a `Mat` which computes the action of the Jacobian matrix-free
1496 
1497   Collective
1498 
1499   Input Parameters:
1500 + dm   - The `DM`
1501 . X    - The evaluation point for the Jacobian
1502 - user - A user context, or `NULL`
1503 
1504   Output Parameter:
1505 . J - The `Mat`
1506 
1507   Level: advanced
1508 
1509   Note:
1510   Vec `X` is kept in `J`, so updating `X` then updates the evaluation point.
1511 
1512 .seealso: `DM`, `DMSNESComputeJacobianAction()`
1513 @*/
1514 PetscErrorCode DMSNESCreateJacobianMF(DM dm, Vec X, void *user, Mat *J)
1515 {
1516   struct _DMSNESJacobianMFCtx *ctx;
1517   PetscInt                     n, N;
1518 
1519   PetscFunctionBegin;
1520   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
1521   PetscCall(MatSetType(*J, MATSHELL));
1522   PetscCall(VecGetLocalSize(X, &n));
1523   PetscCall(VecGetSize(X, &N));
1524   PetscCall(MatSetSizes(*J, n, n, N, N));
1525   PetscCall(PetscObjectReference((PetscObject)dm));
1526   PetscCall(PetscObjectReference((PetscObject)X));
1527   PetscCall(PetscMalloc1(1, &ctx));
1528   ctx->dm  = dm;
1529   ctx->X   = X;
1530   ctx->ctx = user;
1531   PetscCall(MatShellSetContext(*J, ctx));
1532   PetscCall(MatShellSetOperation(*J, MATOP_DESTROY, (void (*)(void))DMSNESJacobianMF_Destroy_Private));
1533   PetscCall(MatShellSetOperation(*J, MATOP_MULT, (void (*)(void))DMSNESJacobianMF_Mult_Private));
1534   PetscFunctionReturn(PETSC_SUCCESS);
1535 }
1536 
1537 /*
1538      MatComputeNeumannOverlap - Computes an unassembled (Neumann) local overlapping Mat in nonlinear context.
1539 
1540    Input Parameters:
1541 +     X - `SNES` linearization point
1542 .     ovl - index set of overlapping subdomains
1543 
1544    Output Parameter:
1545 .     J - unassembled (Neumann) local matrix
1546 
1547    Level: intermediate
1548 
1549 .seealso: `DMCreateNeumannOverlap()`, `MATIS`, `PCHPDDMSetAuxiliaryMat()`
1550 */
1551 static PetscErrorCode MatComputeNeumannOverlap_Plex(Mat J, PetscReal t, Vec X, Vec X_t, PetscReal s, IS ovl, void *ctx)
1552 {
1553   SNES   snes;
1554   Mat    pJ;
1555   DM     ovldm, origdm;
1556   DMSNES sdm;
1557   PetscErrorCode (*bfun)(DM, Vec, void *);
1558   PetscErrorCode (*jfun)(DM, Vec, Mat, Mat, void *);
1559   void *bctx, *jctx;
1560 
1561   PetscFunctionBegin;
1562   PetscCall(PetscObjectQuery((PetscObject)ovl, "_DM_Overlap_HPDDM_MATIS", (PetscObject *)&pJ));
1563   PetscCheck(pJ, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Missing overlapping Mat");
1564   PetscCall(PetscObjectQuery((PetscObject)ovl, "_DM_Original_HPDDM", (PetscObject *)&origdm));
1565   PetscCheck(origdm, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Missing original DM");
1566   PetscCall(MatGetDM(pJ, &ovldm));
1567   PetscCall(DMSNESGetBoundaryLocal(origdm, &bfun, &bctx));
1568   PetscCall(DMSNESSetBoundaryLocal(ovldm, bfun, bctx));
1569   PetscCall(DMSNESGetJacobianLocal(origdm, &jfun, &jctx));
1570   PetscCall(DMSNESSetJacobianLocal(ovldm, jfun, jctx));
1571   PetscCall(PetscObjectQuery((PetscObject)ovl, "_DM_Overlap_HPDDM_SNES", (PetscObject *)&snes));
1572   if (!snes) {
1573     PetscCall(SNESCreate(PetscObjectComm((PetscObject)ovl), &snes));
1574     PetscCall(SNESSetDM(snes, ovldm));
1575     PetscCall(PetscObjectCompose((PetscObject)ovl, "_DM_Overlap_HPDDM_SNES", (PetscObject)snes));
1576     PetscCall(PetscObjectDereference((PetscObject)snes));
1577   }
1578   PetscCall(DMGetDMSNES(ovldm, &sdm));
1579   PetscCall(VecLockReadPush(X));
1580   {
1581     void *ctx;
1582     PetscErrorCode (*J)(SNES, Vec, Mat, Mat, void *);
1583     PetscCall(DMSNESGetJacobian(ovldm, &J, &ctx));
1584     PetscCallBack("SNES callback Jacobian", (*J)(snes, X, pJ, pJ, ctx));
1585   }
1586   PetscCall(VecLockReadPop(X));
1587   /* this is a no-hop, just in case we decide to change the placeholder for the local Neumann matrix */
1588   {
1589     Mat locpJ;
1590 
1591     PetscCall(MatISGetLocalMat(pJ, &locpJ));
1592     PetscCall(MatCopy(locpJ, J, SAME_NONZERO_PATTERN));
1593   }
1594   PetscFunctionReturn(PETSC_SUCCESS);
1595 }
1596 
1597 /*@
1598   DMPlexSetSNESLocalFEM - Use `DMPLEX`'s internal FEM routines to compute `SNES` boundary values, residual, and Jacobian.
1599 
1600   Input Parameters:
1601 + dm          - The `DM` object
1602 . boundaryctx - the user context that will be passed to pointwise evaluation of boundary values (see `PetscDSAddBoundary()`)
1603 . residualctx - the user context that will be passed to pointwise evaluation of finite element residual computations (see `PetscDSSetResidual()`)
1604 - jacobianctx - the user context that will be passed to pointwise evaluation of finite element Jacobian construction (see `PetscDSSetJacobian()`)
1605 
1606   Level: developer
1607 
1608 .seealso: `DMPLEX`, `SNES`
1609 @*/
1610 PetscErrorCode DMPlexSetSNESLocalFEM(DM dm, void *boundaryctx, void *residualctx, void *jacobianctx)
1611 {
1612   PetscFunctionBegin;
1613   PetscCall(DMSNESSetBoundaryLocal(dm, DMPlexSNESComputeBoundaryFEM, boundaryctx));
1614   PetscCall(DMSNESSetFunctionLocal(dm, DMPlexSNESComputeResidualFEM, residualctx));
1615   PetscCall(DMSNESSetJacobianLocal(dm, DMPlexSNESComputeJacobianFEM, jacobianctx));
1616   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", MatComputeNeumannOverlap_Plex));
1617   PetscFunctionReturn(PETSC_SUCCESS);
1618 }
1619 
1620 /*@C
1621   DMSNESCheckDiscretization - Check the discretization error of the exact solution
1622 
1623   Input Parameters:
1624 + snes - the `SNES` object
1625 . dm   - the `DM`
1626 . t    - the time
1627 . u    - a `DM` vector
1628 - tol  - A tolerance for the check, or -1 to print the results instead
1629 
1630   Output Parameter:
1631 . error - An array which holds the discretization error in each field, or `NULL`
1632 
1633   Level: developer
1634 
1635   Note:
1636   The user must call `PetscDSSetExactSolution()` beforehand
1637 
1638 .seealso: `PetscDSSetExactSolution()`, `DNSNESCheckFromOptions()`, `DMSNESCheckResidual()`, `DMSNESCheckJacobian()`
1639 @*/
1640 PetscErrorCode DMSNESCheckDiscretization(SNES snes, DM dm, PetscReal t, Vec u, PetscReal tol, PetscReal error[])
1641 {
1642   PetscErrorCode (**exacts)(PetscInt, PetscReal, const PetscReal x[], PetscInt, PetscScalar *u, void *ctx);
1643   void     **ectxs;
1644   PetscReal *err;
1645   MPI_Comm   comm;
1646   PetscInt   Nf, f;
1647 
1648   PetscFunctionBegin;
1649   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
1650   PetscValidHeaderSpecific(dm, DM_CLASSID, 2);
1651   PetscValidHeaderSpecific(u, VEC_CLASSID, 4);
1652   if (error) PetscAssertPointer(error, 6);
1653 
1654   PetscCall(DMComputeExactSolution(dm, t, u, NULL));
1655   PetscCall(VecViewFromOptions(u, NULL, "-vec_view"));
1656 
1657   PetscCall(PetscObjectGetComm((PetscObject)snes, &comm));
1658   PetscCall(DMGetNumFields(dm, &Nf));
1659   PetscCall(PetscCalloc3(Nf, &exacts, Nf, &ectxs, PetscMax(1, Nf), &err));
1660   {
1661     PetscInt Nds, s;
1662 
1663     PetscCall(DMGetNumDS(dm, &Nds));
1664     for (s = 0; s < Nds; ++s) {
1665       PetscDS         ds;
1666       DMLabel         label;
1667       IS              fieldIS;
1668       const PetscInt *fields;
1669       PetscInt        dsNf, f;
1670 
1671       PetscCall(DMGetRegionNumDS(dm, s, &label, &fieldIS, &ds, NULL));
1672       PetscCall(PetscDSGetNumFields(ds, &dsNf));
1673       PetscCall(ISGetIndices(fieldIS, &fields));
1674       for (f = 0; f < dsNf; ++f) {
1675         const PetscInt field = fields[f];
1676         PetscCall(PetscDSGetExactSolution(ds, field, &exacts[field], &ectxs[field]));
1677       }
1678       PetscCall(ISRestoreIndices(fieldIS, &fields));
1679     }
1680   }
1681   if (Nf > 1) {
1682     PetscCall(DMComputeL2FieldDiff(dm, t, exacts, ectxs, u, err));
1683     if (tol >= 0.0) {
1684       for (f = 0; f < Nf; ++f) PetscCheck(err[f] <= tol, comm, PETSC_ERR_ARG_WRONG, "L_2 Error %g for field %" PetscInt_FMT " exceeds tolerance %g", (double)err[f], f, (double)tol);
1685     } else if (error) {
1686       for (f = 0; f < Nf; ++f) error[f] = err[f];
1687     } else {
1688       PetscCall(PetscPrintf(comm, "L_2 Error: ["));
1689       for (f = 0; f < Nf; ++f) {
1690         if (f) PetscCall(PetscPrintf(comm, ", "));
1691         PetscCall(PetscPrintf(comm, "%g", (double)err[f]));
1692       }
1693       PetscCall(PetscPrintf(comm, "]\n"));
1694     }
1695   } else {
1696     PetscCall(DMComputeL2Diff(dm, t, exacts, ectxs, u, &err[0]));
1697     if (tol >= 0.0) {
1698       PetscCheck(err[0] <= tol, comm, PETSC_ERR_ARG_WRONG, "L_2 Error %g exceeds tolerance %g", (double)err[0], (double)tol);
1699     } else if (error) {
1700       error[0] = err[0];
1701     } else {
1702       PetscCall(PetscPrintf(comm, "L_2 Error: %g\n", (double)err[0]));
1703     }
1704   }
1705   PetscCall(PetscFree3(exacts, ectxs, err));
1706   PetscFunctionReturn(PETSC_SUCCESS);
1707 }
1708 
1709 /*@C
1710   DMSNESCheckResidual - Check the residual of the exact solution
1711 
1712   Input Parameters:
1713 + snes - the `SNES` object
1714 . dm   - the `DM`
1715 . u    - a `DM` vector
1716 - tol  - A tolerance for the check, or -1 to print the results instead
1717 
1718   Output Parameter:
1719 . residual - The residual norm of the exact solution, or `NULL`
1720 
1721   Level: developer
1722 
1723 .seealso: `DNSNESCheckFromOptions()`, `DMSNESCheckDiscretization()`, `DMSNESCheckJacobian()`
1724 @*/
1725 PetscErrorCode DMSNESCheckResidual(SNES snes, DM dm, Vec u, PetscReal tol, PetscReal *residual)
1726 {
1727   MPI_Comm  comm;
1728   Vec       r;
1729   PetscReal res;
1730 
1731   PetscFunctionBegin;
1732   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
1733   PetscValidHeaderSpecific(dm, DM_CLASSID, 2);
1734   PetscValidHeaderSpecific(u, VEC_CLASSID, 3);
1735   if (residual) PetscAssertPointer(residual, 5);
1736   PetscCall(PetscObjectGetComm((PetscObject)snes, &comm));
1737   PetscCall(DMComputeExactSolution(dm, 0.0, u, NULL));
1738   PetscCall(VecDuplicate(u, &r));
1739   PetscCall(SNESComputeFunction(snes, u, r));
1740   PetscCall(VecNorm(r, NORM_2, &res));
1741   if (tol >= 0.0) {
1742     PetscCheck(res <= tol, comm, PETSC_ERR_ARG_WRONG, "L_2 Residual %g exceeds tolerance %g", (double)res, (double)tol);
1743   } else if (residual) {
1744     *residual = res;
1745   } else {
1746     PetscCall(PetscPrintf(comm, "L_2 Residual: %g\n", (double)res));
1747     PetscCall(VecFilter(r, 1.0e-10));
1748     PetscCall(PetscObjectSetName((PetscObject)r, "Initial Residual"));
1749     PetscCall(PetscObjectSetOptionsPrefix((PetscObject)r, "res_"));
1750     PetscCall(VecViewFromOptions(r, NULL, "-vec_view"));
1751   }
1752   PetscCall(VecDestroy(&r));
1753   PetscFunctionReturn(PETSC_SUCCESS);
1754 }
1755 
1756 /*@C
1757   DMSNESCheckJacobian - Check the Jacobian of the exact solution against the residual using the Taylor Test
1758 
1759   Input Parameters:
1760 + snes - the `SNES` object
1761 . dm   - the `DM`
1762 . u    - a `DM` vector
1763 - tol  - A tolerance for the check, or -1 to print the results instead
1764 
1765   Output Parameters:
1766 + isLinear - Flag indicaing that the function looks linear, or `NULL`
1767 - convRate - The rate of convergence of the linear model, or `NULL`
1768 
1769   Level: developer
1770 
1771 .seealso: `DNSNESCheckFromOptions()`, `DMSNESCheckDiscretization()`, `DMSNESCheckResidual()`
1772 @*/
1773 PetscErrorCode DMSNESCheckJacobian(SNES snes, DM dm, Vec u, PetscReal tol, PetscBool *isLinear, PetscReal *convRate)
1774 {
1775   MPI_Comm     comm;
1776   PetscDS      ds;
1777   Mat          J, M;
1778   MatNullSpace nullspace;
1779   PetscReal    slope, intercept;
1780   PetscBool    hasJac, hasPrec, isLin = PETSC_FALSE;
1781 
1782   PetscFunctionBegin;
1783   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
1784   PetscValidHeaderSpecific(dm, DM_CLASSID, 2);
1785   PetscValidHeaderSpecific(u, VEC_CLASSID, 3);
1786   if (isLinear) PetscAssertPointer(isLinear, 5);
1787   if (convRate) PetscAssertPointer(convRate, 6);
1788   PetscCall(PetscObjectGetComm((PetscObject)snes, &comm));
1789   PetscCall(DMComputeExactSolution(dm, 0.0, u, NULL));
1790   /* Create and view matrices */
1791   PetscCall(DMCreateMatrix(dm, &J));
1792   PetscCall(DMGetDS(dm, &ds));
1793   PetscCall(PetscDSHasJacobian(ds, &hasJac));
1794   PetscCall(PetscDSHasJacobianPreconditioner(ds, &hasPrec));
1795   if (hasJac && hasPrec) {
1796     PetscCall(DMCreateMatrix(dm, &M));
1797     PetscCall(SNESComputeJacobian(snes, u, J, M));
1798     PetscCall(PetscObjectSetName((PetscObject)M, "Preconditioning Matrix"));
1799     PetscCall(PetscObjectSetOptionsPrefix((PetscObject)M, "jacpre_"));
1800     PetscCall(MatViewFromOptions(M, NULL, "-mat_view"));
1801     PetscCall(MatDestroy(&M));
1802   } else {
1803     PetscCall(SNESComputeJacobian(snes, u, J, J));
1804   }
1805   PetscCall(PetscObjectSetName((PetscObject)J, "Jacobian"));
1806   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)J, "jac_"));
1807   PetscCall(MatViewFromOptions(J, NULL, "-mat_view"));
1808   /* Check nullspace */
1809   PetscCall(MatGetNullSpace(J, &nullspace));
1810   if (nullspace) {
1811     PetscBool isNull;
1812     PetscCall(MatNullSpaceTest(nullspace, J, &isNull));
1813     PetscCheck(isNull, comm, PETSC_ERR_PLIB, "The null space calculated for the system operator is invalid.");
1814   }
1815   /* Taylor test */
1816   {
1817     PetscRandom rand;
1818     Vec         du, uhat, r, rhat, df;
1819     PetscReal   h;
1820     PetscReal  *es, *hs, *errors;
1821     PetscReal   hMax = 1.0, hMin = 1e-6, hMult = 0.1;
1822     PetscInt    Nv, v;
1823 
1824     /* Choose a perturbation direction */
1825     PetscCall(PetscRandomCreate(comm, &rand));
1826     PetscCall(VecDuplicate(u, &du));
1827     PetscCall(VecSetRandom(du, rand));
1828     PetscCall(PetscRandomDestroy(&rand));
1829     PetscCall(VecDuplicate(u, &df));
1830     PetscCall(MatMult(J, du, df));
1831     /* Evaluate residual at u, F(u), save in vector r */
1832     PetscCall(VecDuplicate(u, &r));
1833     PetscCall(SNESComputeFunction(snes, u, r));
1834     /* Look at the convergence of our Taylor approximation as we approach u */
1835     for (h = hMax, Nv = 0; h >= hMin; h *= hMult, ++Nv)
1836       ;
1837     PetscCall(PetscCalloc3(Nv, &es, Nv, &hs, Nv, &errors));
1838     PetscCall(VecDuplicate(u, &uhat));
1839     PetscCall(VecDuplicate(u, &rhat));
1840     for (h = hMax, Nv = 0; h >= hMin; h *= hMult, ++Nv) {
1841       PetscCall(VecWAXPY(uhat, h, du, u));
1842       /* F(\hat u) \approx F(u) + J(u) (uhat - u) = F(u) + h * J(u) du */
1843       PetscCall(SNESComputeFunction(snes, uhat, rhat));
1844       PetscCall(VecAXPBYPCZ(rhat, -1.0, -h, 1.0, r, df));
1845       PetscCall(VecNorm(rhat, NORM_2, &errors[Nv]));
1846 
1847       es[Nv] = errors[Nv] == 0 ? -16.0 : PetscLog10Real(errors[Nv]);
1848       hs[Nv] = PetscLog10Real(h);
1849     }
1850     PetscCall(VecDestroy(&uhat));
1851     PetscCall(VecDestroy(&rhat));
1852     PetscCall(VecDestroy(&df));
1853     PetscCall(VecDestroy(&r));
1854     PetscCall(VecDestroy(&du));
1855     for (v = 0; v < Nv; ++v) {
1856       if ((tol >= 0) && (errors[v] > tol)) break;
1857       else if (errors[v] > PETSC_SMALL) break;
1858     }
1859     if (v == Nv) isLin = PETSC_TRUE;
1860     PetscCall(PetscLinearRegression(Nv, hs, es, &slope, &intercept));
1861     PetscCall(PetscFree3(es, hs, errors));
1862     /* Slope should be about 2 */
1863     if (tol >= 0) {
1864       PetscCheck(isLin || PetscAbsReal(2 - slope) <= tol, comm, PETSC_ERR_ARG_WRONG, "Taylor approximation convergence rate should be 2, not %0.2f", (double)slope);
1865     } else if (isLinear || convRate) {
1866       if (isLinear) *isLinear = isLin;
1867       if (convRate) *convRate = slope;
1868     } else {
1869       if (!isLin) PetscCall(PetscPrintf(comm, "Taylor approximation converging at order %3.2f\n", (double)slope));
1870       else PetscCall(PetscPrintf(comm, "Function appears to be linear\n"));
1871     }
1872   }
1873   PetscCall(MatDestroy(&J));
1874   PetscFunctionReturn(PETSC_SUCCESS);
1875 }
1876 
1877 PetscErrorCode DMSNESCheck_Internal(SNES snes, DM dm, Vec u)
1878 {
1879   PetscFunctionBegin;
1880   PetscCall(DMSNESCheckDiscretization(snes, dm, 0.0, u, -1.0, NULL));
1881   PetscCall(DMSNESCheckResidual(snes, dm, u, -1.0, NULL));
1882   PetscCall(DMSNESCheckJacobian(snes, dm, u, -1.0, NULL, NULL));
1883   PetscFunctionReturn(PETSC_SUCCESS);
1884 }
1885 
1886 /*@C
1887   DMSNESCheckFromOptions - Check the residual and Jacobian functions using the exact solution by outputting some diagnostic information
1888 
1889   Input Parameters:
1890 + snes - the `SNES` object
1891 - u    - representative `SNES` vector
1892 
1893   Level: developer
1894 
1895   Note:
1896   The user must call `PetscDSSetExactSolution()` beforehand
1897 
1898 .seealso: `SNES`, `DM`
1899 @*/
1900 PetscErrorCode DMSNESCheckFromOptions(SNES snes, Vec u)
1901 {
1902   DM        dm;
1903   Vec       sol;
1904   PetscBool check;
1905 
1906   PetscFunctionBegin;
1907   PetscCall(PetscOptionsHasName(((PetscObject)snes)->options, ((PetscObject)snes)->prefix, "-dmsnes_check", &check));
1908   if (!check) PetscFunctionReturn(PETSC_SUCCESS);
1909   PetscCall(SNESGetDM(snes, &dm));
1910   PetscCall(VecDuplicate(u, &sol));
1911   PetscCall(SNESSetSolution(snes, sol));
1912   PetscCall(DMSNESCheck_Internal(snes, dm, sol));
1913   PetscCall(VecDestroy(&sol));
1914   PetscFunctionReturn(PETSC_SUCCESS);
1915 }
1916