xref: /petsc/src/dm/impls/plex/tests/ex3.c (revision dfd676b1a855b7f967ece75a22ee7f6626d10f89)
1 static char help[] = "Check that a DM can accurately represent and interpolate functions of a given polynomial order\n\n";
2 
3 #include <petscdmplex.h>
4 #include <petscdm.h>
5 #include <petscdmda.h>
6 #include <petscfe.h>
7 #include <petscds.h>
8 #include <petscksp.h>
9 #include <petscsnes.h>
10 
11 typedef struct {
12   /* Domain and mesh definition */
13   PetscBool useDA;             /* Flag DMDA tensor product mesh */
14   PetscBool shearCoords;       /* Flag for shear transform */
15   PetscBool nonaffineCoords;   /* Flag for non-affine transform */
16   /* Element definition */
17   PetscInt  qorder;            /* Order of the quadrature */
18   PetscInt  numComponents;     /* Number of field components */
19   PetscFE   fe;                /* The finite element */
20   /* Testing space */
21   PetscInt  porder;            /* Order of polynomials to test */
22   PetscBool convergence;       /* Test for order of convergence */
23   PetscBool convRefine;        /* Test for convergence using refinement, otherwise use coarsening */
24   PetscBool constraints;       /* Test local constraints */
25   PetscBool tree;              /* Test tree routines */
26   PetscBool testFEjacobian;    /* Test finite element Jacobian assembly */
27   PetscBool testFVgrad;        /* Test finite difference gradient routine */
28   PetscBool testInjector;      /* Test finite element injection routines */
29   PetscInt  treeCell;          /* Cell to refine in tree test */
30   PetscReal constants[3];      /* Constant values for each dimension */
31 } AppCtx;
32 
33 /* u = 1 */
34 PetscErrorCode constant(PetscInt dim, PetscReal time, const PetscReal coords[], PetscInt Nf, PetscScalar *u, void *ctx)
35 {
36   AppCtx   *user = (AppCtx *) ctx;
37   PetscInt d;
38   for (d = 0; d < dim; ++d) u[d] = user->constants[d];
39   return 0;
40 }
41 PetscErrorCode constantDer(PetscInt dim, PetscReal time, const PetscReal coords[], const PetscReal n[], PetscInt Nf, PetscScalar *u, void *ctx)
42 {
43   PetscInt d;
44   for (d = 0; d < dim; ++d) u[d] = 0.0;
45   return 0;
46 }
47 
48 /* u = x */
49 PetscErrorCode linear(PetscInt dim, PetscReal time, const PetscReal coords[], PetscInt Nf, PetscScalar *u, void *ctx)
50 {
51   PetscInt d;
52   for (d = 0; d < dim; ++d) u[d] = coords[d];
53   return 0;
54 }
55 PetscErrorCode linearDer(PetscInt dim, PetscReal time, const PetscReal coords[], const PetscReal n[], PetscInt Nf, PetscScalar *u, void *ctx)
56 {
57   PetscInt d, e;
58   for (d = 0; d < dim; ++d) {
59     u[d] = 0.0;
60     for (e = 0; e < dim; ++e) u[d] += (d == e ? 1.0 : 0.0) * n[e];
61   }
62   return 0;
63 }
64 
65 /* u = x^2 or u = (x^2, xy) or u = (xy, yz, zx) */
66 PetscErrorCode quadratic(PetscInt dim, PetscReal time, const PetscReal coords[], PetscInt Nf, PetscScalar *u, void *ctx)
67 {
68   if (dim > 2)      {u[0] = coords[0]*coords[1]; u[1] = coords[1]*coords[2]; u[2] = coords[2]*coords[0];}
69   else if (dim > 1) {u[0] = coords[0]*coords[0]; u[1] = coords[0]*coords[1];}
70   else if (dim > 0) {u[0] = coords[0]*coords[0];}
71   return 0;
72 }
73 PetscErrorCode quadraticDer(PetscInt dim, PetscReal time, const PetscReal coords[], const PetscReal n[], PetscInt Nf, PetscScalar *u, void *ctx)
74 {
75   if (dim > 2)      {u[0] = coords[1]*n[0] + coords[0]*n[1]; u[1] = coords[2]*n[1] + coords[1]*n[2]; u[2] = coords[2]*n[0] + coords[0]*n[2];}
76   else if (dim > 1) {u[0] = 2.0*coords[0]*n[0]; u[1] = coords[1]*n[0] + coords[0]*n[1];}
77   else if (dim > 0) {u[0] = 2.0*coords[0]*n[0];}
78   return 0;
79 }
80 
81 /* u = x^3 or u = (x^3, x^2y) or u = (x^2y, y^2z, z^2x) */
82 PetscErrorCode cubic(PetscInt dim, PetscReal time, const PetscReal coords[], PetscInt Nf, PetscScalar *u, void *ctx)
83 {
84   if (dim > 2)      {u[0] = coords[0]*coords[0]*coords[1]; u[1] = coords[1]*coords[1]*coords[2]; u[2] = coords[2]*coords[2]*coords[0];}
85   else if (dim > 1) {u[0] = coords[0]*coords[0]*coords[0]; u[1] = coords[0]*coords[0]*coords[1];}
86   else if (dim > 0) {u[0] = coords[0]*coords[0]*coords[0];}
87   return 0;
88 }
89 PetscErrorCode cubicDer(PetscInt dim, PetscReal time, const PetscReal coords[], const PetscReal n[], PetscInt Nf, PetscScalar *u, void *ctx)
90 {
91   if (dim > 2)      {u[0] = 2.0*coords[0]*coords[1]*n[0] + coords[0]*coords[0]*n[1]; u[1] = 2.0*coords[1]*coords[2]*n[1] + coords[1]*coords[1]*n[2]; u[2] = 2.0*coords[2]*coords[0]*n[2] + coords[2]*coords[2]*n[0];}
92   else if (dim > 1) {u[0] = 3.0*coords[0]*coords[0]*n[0]; u[1] = 2.0*coords[0]*coords[1]*n[0] + coords[0]*coords[0]*n[1];}
93   else if (dim > 0) {u[0] = 3.0*coords[0]*coords[0]*n[0];}
94   return 0;
95 }
96 
97 /* u = tanh(x) */
98 PetscErrorCode trig(PetscInt dim, PetscReal time, const PetscReal coords[], PetscInt Nf, PetscScalar *u, void *ctx)
99 {
100   PetscInt d;
101   for (d = 0; d < dim; ++d) u[d] = PetscTanhReal(coords[d] - 0.5);
102   return 0;
103 }
104 PetscErrorCode trigDer(PetscInt dim, PetscReal time, const PetscReal coords[], const PetscReal n[], PetscInt Nf, PetscScalar *u, void *ctx)
105 {
106   PetscInt d;
107   for (d = 0; d < dim; ++d) u[d] = 1.0/PetscSqr(PetscCoshReal(coords[d] - 0.5)) * n[d];
108   return 0;
109 }
110 
111 static PetscErrorCode ProcessOptions(MPI_Comm comm, AppCtx *options)
112 {
113   PetscInt       n = 3;
114 
115   PetscFunctionBeginUser;
116   options->useDA           = PETSC_FALSE;
117   options->shearCoords     = PETSC_FALSE;
118   options->nonaffineCoords = PETSC_FALSE;
119   options->qorder          = 0;
120   options->numComponents   = PETSC_DEFAULT;
121   options->porder          = 0;
122   options->convergence     = PETSC_FALSE;
123   options->convRefine      = PETSC_TRUE;
124   options->constraints     = PETSC_FALSE;
125   options->tree            = PETSC_FALSE;
126   options->treeCell        = 0;
127   options->testFEjacobian  = PETSC_FALSE;
128   options->testFVgrad      = PETSC_FALSE;
129   options->testInjector    = PETSC_FALSE;
130   options->constants[0]    = 1.0;
131   options->constants[1]    = 2.0;
132   options->constants[2]    = 3.0;
133 
134   PetscOptionsBegin(comm, "", "Projection Test Options", "DMPlex");
135   PetscCall(PetscOptionsBool("-use_da", "Flag for DMDA mesh", "ex3.c", options->useDA, &options->useDA, NULL));
136   PetscCall(PetscOptionsBool("-shear_coords", "Transform coordinates with a shear", "ex3.c", options->shearCoords, &options->shearCoords, NULL));
137   PetscCall(PetscOptionsBool("-non_affine_coords", "Transform coordinates with a non-affine transform", "ex3.c", options->nonaffineCoords, &options->nonaffineCoords, NULL));
138   PetscCall(PetscOptionsBoundedInt("-qorder", "The quadrature order", "ex3.c", options->qorder, &options->qorder, NULL,0));
139   PetscCall(PetscOptionsBoundedInt("-num_comp", "The number of field components", "ex3.c", options->numComponents, &options->numComponents, NULL,PETSC_DEFAULT));
140   PetscCall(PetscOptionsBoundedInt("-porder", "The order of polynomials to test", "ex3.c", options->porder, &options->porder, NULL,0));
141   PetscCall(PetscOptionsBool("-convergence", "Check the convergence rate", "ex3.c", options->convergence, &options->convergence, NULL));
142   PetscCall(PetscOptionsBool("-conv_refine", "Use refinement for the convergence rate", "ex3.c", options->convRefine, &options->convRefine, NULL));
143   PetscCall(PetscOptionsBool("-constraints", "Test local constraints (serial only)", "ex3.c", options->constraints, &options->constraints, NULL));
144   PetscCall(PetscOptionsBool("-tree", "Test tree routines", "ex3.c", options->tree, &options->tree, NULL));
145   PetscCall(PetscOptionsBoundedInt("-tree_cell", "cell to refine in tree test", "ex3.c", options->treeCell, &options->treeCell, NULL,0));
146   PetscCall(PetscOptionsBool("-test_fe_jacobian", "Test finite element Jacobian assembly", "ex3.c", options->testFEjacobian, &options->testFEjacobian, NULL));
147   PetscCall(PetscOptionsBool("-test_fv_grad", "Test finite volume gradient reconstruction", "ex3.c", options->testFVgrad, &options->testFVgrad, NULL));
148   PetscCall(PetscOptionsBool("-test_injector","Test finite element injection", "ex3.c", options->testInjector, &options->testInjector,NULL));
149   PetscCall(PetscOptionsRealArray("-constants","Set the constant values", "ex3.c", options->constants, &n,NULL));
150   PetscOptionsEnd();
151 
152   PetscFunctionReturn(0);
153 }
154 
155 static PetscErrorCode TransformCoordinates(DM dm, AppCtx *user)
156 {
157   PetscSection   coordSection;
158   Vec            coordinates;
159   PetscScalar   *coords;
160   PetscInt       vStart, vEnd, v;
161 
162   PetscFunctionBeginUser;
163   if (user->nonaffineCoords) {
164     /* x' = r^(1/p) (x/r), y' = r^(1/p) (y/r), z' = z */
165     PetscCall(DMGetCoordinateSection(dm, &coordSection));
166     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
167     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
168     PetscCall(VecGetArray(coordinates, &coords));
169     for (v = vStart; v < vEnd; ++v) {
170       PetscInt  dof, off;
171       PetscReal p = 4.0, r;
172 
173       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
174       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
175       switch (dof) {
176       case 2:
177         r             = PetscSqr(PetscRealPart(coords[off+0])) + PetscSqr(PetscRealPart(coords[off+1]));
178         coords[off+0] = r == 0.0 ? 0.0 : PetscPowReal(r, (1 - p)/(2*p))*coords[off+0];
179         coords[off+1] = r == 0.0 ? 0.0 : PetscPowReal(r, (1 - p)/(2*p))*coords[off+1];
180         break;
181       case 3:
182         r             = PetscSqr(PetscRealPart(coords[off+0])) + PetscSqr(PetscRealPart(coords[off+1]));
183         coords[off+0] = r == 0.0 ? 0.0 : PetscPowReal(r, (1 - p)/(2*p))*coords[off+0];
184         coords[off+1] = r == 0.0 ? 0.0 : PetscPowReal(r, (1 - p)/(2*p))*coords[off+1];
185         coords[off+2] = coords[off+2];
186         break;
187       }
188     }
189     PetscCall(VecRestoreArray(coordinates, &coords));
190   }
191   if (user->shearCoords) {
192     /* x' = x + m y + m z, y' = y + m z,  z' = z */
193     PetscCall(DMGetCoordinateSection(dm, &coordSection));
194     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
195     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
196     PetscCall(VecGetArray(coordinates, &coords));
197     for (v = vStart; v < vEnd; ++v) {
198       PetscInt  dof, off;
199       PetscReal m = 1.0;
200 
201       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
202       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
203       switch (dof) {
204       case 2:
205         coords[off+0] = coords[off+0] + m*coords[off+1];
206         coords[off+1] = coords[off+1];
207         break;
208       case 3:
209         coords[off+0] = coords[off+0] + m*coords[off+1] + m*coords[off+2];
210         coords[off+1] = coords[off+1] + m*coords[off+2];
211         coords[off+2] = coords[off+2];
212         break;
213       }
214     }
215     PetscCall(VecRestoreArray(coordinates, &coords));
216   }
217   PetscFunctionReturn(0);
218 }
219 
220 static PetscErrorCode CreateMesh(MPI_Comm comm, AppCtx *user, DM *dm)
221 {
222   PetscInt       dim = 2;
223   PetscBool      simplex;
224 
225   PetscFunctionBeginUser;
226   if (user->useDA) {
227     switch (dim) {
228     case 2:
229       PetscCall(DMDACreate2d(comm, DM_BOUNDARY_NONE, DM_BOUNDARY_NONE, DMDA_STENCIL_BOX, 2, 2, PETSC_DETERMINE, PETSC_DETERMINE, 1, 1, NULL, NULL, dm));
230       PetscCall(DMSetFromOptions(*dm));
231       PetscCall(DMSetUp(*dm));
232       PetscCall(DMDASetVertexCoordinates(*dm, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0));
233       break;
234     default:
235       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cannot create structured mesh of dimension %" PetscInt_FMT, dim);
236     }
237     PetscCall(PetscObjectSetName((PetscObject) *dm, "Hexahedral Mesh"));
238   } else {
239     PetscCall(DMCreate(comm, dm));
240     PetscCall(DMSetType(*dm, DMPLEX));
241     PetscCall(DMPlexDistributeSetDefault(*dm, PETSC_FALSE));
242     PetscCall(DMSetFromOptions(*dm));
243 
244     PetscCall(DMGetDimension(*dm, &dim));
245     PetscCall(DMPlexIsSimplex(*dm, &simplex));
246     PetscCallMPI(MPI_Bcast(&simplex, 1, MPIU_BOOL, 0, comm));
247     if (user->tree) {
248       DM refTree, ncdm = NULL;
249 
250       PetscCall(DMPlexCreateDefaultReferenceTree(comm,dim,simplex,&refTree));
251       PetscCall(DMViewFromOptions(refTree,NULL,"-reftree_dm_view"));
252       PetscCall(DMPlexSetReferenceTree(*dm,refTree));
253       PetscCall(DMDestroy(&refTree));
254       PetscCall(DMPlexTreeRefineCell(*dm,user->treeCell,&ncdm));
255       if (ncdm) {
256         PetscCall(DMDestroy(dm));
257         *dm = ncdm;
258         PetscCall(DMPlexSetRefinementUniform(*dm, PETSC_FALSE));
259       }
260       PetscCall(PetscObjectSetOptionsPrefix((PetscObject) *dm, "tree_"));
261       PetscCall(DMPlexDistributeSetDefault(*dm, PETSC_FALSE));
262       PetscCall(DMSetFromOptions(*dm));
263       PetscCall(DMViewFromOptions(*dm,NULL,"-dm_view"));
264     } else {
265       PetscCall(DMPlexSetRefinementUniform(*dm, PETSC_TRUE));
266     }
267     PetscCall(PetscObjectSetOptionsPrefix((PetscObject) *dm, "dist_"));
268     PetscCall(DMPlexDistributeSetDefault(*dm, PETSC_FALSE));
269     PetscCall(DMSetFromOptions(*dm));
270     PetscCall(PetscObjectSetOptionsPrefix((PetscObject) *dm, NULL));
271     if (simplex) PetscCall(PetscObjectSetName((PetscObject) *dm, "Simplicial Mesh"));
272     else         PetscCall(PetscObjectSetName((PetscObject) *dm, "Hexahedral Mesh"));
273   }
274   PetscCall(DMSetFromOptions(*dm));
275   PetscCall(TransformCoordinates(*dm, user));
276   PetscCall(DMViewFromOptions(*dm,NULL,"-dm_view"));
277   PetscFunctionReturn(0);
278 }
279 
280 static void simple_mass(PetscInt dim, PetscInt Nf, PetscInt NfAux,
281                         const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[],
282                         const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[],
283                         PetscReal t, PetscReal u_tShift, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar g0[])
284 {
285   PetscInt d, e;
286   for (d = 0, e = 0; d < dim; d++, e+=dim+1) {
287     g0[e] = 1.;
288   }
289 }
290 
291 /* < \nabla v, 1/2(\nabla u + {\nabla u}^T) > */
292 static void symmetric_gradient_inner_product(PetscInt dim, PetscInt Nf, PetscInt NfAux,
293                                              const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[],
294                                              const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[],
295                                              PetscReal t, PetscReal u_tShift, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar C[])
296 {
297   PetscInt compI, compJ, d, e;
298 
299   for (compI = 0; compI < dim; ++compI) {
300     for (compJ = 0; compJ < dim; ++compJ) {
301       for (d = 0; d < dim; ++d) {
302         for (e = 0; e < dim; e++) {
303           if (d == e && d == compI && d == compJ) {
304             C[((compI*dim+compJ)*dim+d)*dim+e] = 1.0;
305           } else if ((d == compJ && e == compI) || (d == e && compI == compJ)) {
306             C[((compI*dim+compJ)*dim+d)*dim+e] = 0.5;
307           } else {
308             C[((compI*dim+compJ)*dim+d)*dim+e] = 0.0;
309           }
310         }
311       }
312     }
313   }
314 }
315 
316 static PetscErrorCode SetupSection(DM dm, AppCtx *user)
317 {
318   PetscFunctionBeginUser;
319   if (user->constraints) {
320     /* test local constraints */
321     DM            coordDM;
322     PetscInt      fStart, fEnd, f, vStart, vEnd, v;
323     PetscInt      edgesx = 2, vertsx;
324     PetscInt      edgesy = 2, vertsy;
325     PetscMPIInt   size;
326     PetscInt      numConst;
327     PetscSection  aSec;
328     PetscInt     *anchors;
329     PetscInt      offset;
330     IS            aIS;
331     MPI_Comm      comm = PetscObjectComm((PetscObject)dm);
332 
333     PetscCallMPI(MPI_Comm_size(comm,&size));
334     PetscCheck(size <= 1,comm,PETSC_ERR_SUP,"Local constraint test can only be performed in serial");
335 
336     /* we are going to test constraints by using them to enforce periodicity
337      * in one direction, and comparing to the existing method of enforcing
338      * periodicity */
339 
340     /* first create the coordinate section so that it does not clone the
341      * constraints */
342     PetscCall(DMGetCoordinateDM(dm,&coordDM));
343 
344     /* create the constrained-to-anchor section */
345     PetscCall(DMPlexGetDepthStratum(dm,0,&vStart,&vEnd));
346     PetscCall(DMPlexGetDepthStratum(dm,1,&fStart,&fEnd));
347     PetscCall(PetscSectionCreate(PETSC_COMM_SELF,&aSec));
348     PetscCall(PetscSectionSetChart(aSec,PetscMin(fStart,vStart),PetscMax(fEnd,vEnd)));
349 
350     /* define the constraints */
351     PetscCall(PetscOptionsGetInt(NULL,NULL,"-da_grid_x",&edgesx,NULL));
352     PetscCall(PetscOptionsGetInt(NULL,NULL,"-da_grid_y",&edgesy,NULL));
353     vertsx = edgesx + 1;
354     vertsy = edgesy + 1;
355     numConst = vertsy + edgesy;
356     PetscCall(PetscMalloc1(numConst,&anchors));
357     offset = 0;
358     for (v = vStart + edgesx; v < vEnd; v+= vertsx) {
359       PetscCall(PetscSectionSetDof(aSec,v,1));
360       anchors[offset++] = v - edgesx;
361     }
362     for (f = fStart + edgesx * vertsy + edgesx * edgesy; f < fEnd; f++) {
363       PetscCall(PetscSectionSetDof(aSec,f,1));
364       anchors[offset++] = f - edgesx * edgesy;
365     }
366     PetscCall(PetscSectionSetUp(aSec));
367     PetscCall(ISCreateGeneral(PETSC_COMM_SELF,numConst,anchors,PETSC_OWN_POINTER,&aIS));
368 
369     PetscCall(DMPlexSetAnchors(dm,aSec,aIS));
370     PetscCall(PetscSectionDestroy(&aSec));
371     PetscCall(ISDestroy(&aIS));
372   }
373   PetscCall(DMSetNumFields(dm, 1));
374   PetscCall(DMSetField(dm, 0, NULL, (PetscObject) user->fe));
375   PetscCall(DMCreateDS(dm));
376   if (user->constraints) {
377     /* test getting local constraint matrix that matches section */
378     PetscSection aSec;
379     IS           aIS;
380 
381     PetscCall(DMPlexGetAnchors(dm,&aSec,&aIS));
382     if (aSec) {
383       PetscDS         ds;
384       PetscSection    cSec, section;
385       PetscInt        cStart, cEnd, c, numComp;
386       Mat             cMat, mass;
387       Vec             local;
388       const PetscInt *anchors;
389 
390       PetscCall(DMGetLocalSection(dm,&section));
391       /* this creates the matrix and preallocates the matrix structure: we
392        * just have to fill in the values */
393       PetscCall(DMGetDefaultConstraints(dm,&cSec,&cMat,NULL));
394       PetscCall(PetscSectionGetChart(cSec,&cStart,&cEnd));
395       PetscCall(ISGetIndices(aIS,&anchors));
396       PetscCall(PetscFEGetNumComponents(user->fe, &numComp));
397       for (c = cStart; c < cEnd; c++) {
398         PetscInt cDof;
399 
400         /* is this point constrained? (does it have an anchor?) */
401         PetscCall(PetscSectionGetDof(aSec,c,&cDof));
402         if (cDof) {
403           PetscInt cOff, a, aDof, aOff, j;
404           PetscCheck(cDof == 1,PETSC_COMM_SELF,PETSC_ERR_PLIB,"Found %" PetscInt_FMT " anchor points: should be just one",cDof);
405 
406           /* find the anchor point */
407           PetscCall(PetscSectionGetOffset(aSec,c,&cOff));
408           a    = anchors[cOff];
409 
410           /* find the constrained dofs (row in constraint matrix) */
411           PetscCall(PetscSectionGetDof(cSec,c,&cDof));
412           PetscCall(PetscSectionGetOffset(cSec,c,&cOff));
413 
414           /* find the anchor dofs (column in constraint matrix) */
415           PetscCall(PetscSectionGetDof(section,a,&aDof));
416           PetscCall(PetscSectionGetOffset(section,a,&aOff));
417 
418           PetscCheck(cDof == aDof,PETSC_COMM_SELF,PETSC_ERR_PLIB,"Point and anchor have different number of dofs: %" PetscInt_FMT ", %" PetscInt_FMT,cDof,aDof);
419           PetscCheck(cDof % numComp == 0,PETSC_COMM_SELF,PETSC_ERR_PLIB,"Point dofs not divisible by field components: %" PetscInt_FMT ", %" PetscInt_FMT,cDof,numComp);
420 
421           /* put in a simple equality constraint */
422           for (j = 0; j < cDof; j++) {
423             PetscCall(MatSetValue(cMat,cOff+j,aOff+j,1.,INSERT_VALUES));
424           }
425         }
426       }
427       PetscCall(MatAssemblyBegin(cMat,MAT_FINAL_ASSEMBLY));
428       PetscCall(MatAssemblyEnd(cMat,MAT_FINAL_ASSEMBLY));
429       PetscCall(ISRestoreIndices(aIS,&anchors));
430 
431       /* Now that we have constructed the constraint matrix, any FE matrix
432        * that we construct will apply the constraints during construction */
433 
434       PetscCall(DMCreateMatrix(dm,&mass));
435       /* get a dummy local variable to serve as the solution */
436       PetscCall(DMGetLocalVector(dm,&local));
437       PetscCall(DMGetDS(dm,&ds));
438       /* set the jacobian to be the mass matrix */
439       PetscCall(PetscDSSetJacobian(ds, 0, 0, simple_mass, NULL,  NULL, NULL));
440       /* build the mass matrix */
441       PetscCall(DMPlexSNESComputeJacobianFEM(dm,local,mass,mass,NULL));
442       PetscCall(MatView(mass,PETSC_VIEWER_STDOUT_WORLD));
443       PetscCall(MatDestroy(&mass));
444       PetscCall(DMRestoreLocalVector(dm,&local));
445     }
446   }
447   PetscFunctionReturn(0);
448 }
449 
450 static PetscErrorCode TestFEJacobian(DM dm, AppCtx *user)
451 {
452   PetscFunctionBeginUser;
453   if (!user->useDA) {
454     Vec          local;
455     const Vec    *vecs;
456     Mat          E;
457     MatNullSpace sp;
458     PetscBool    isNullSpace, hasConst;
459     PetscInt     dim, n, i;
460     Vec          res = NULL, localX, localRes;
461     PetscDS      ds;
462 
463     PetscCall(DMGetDimension(dm, &dim));
464     PetscCheck(user->numComponents == dim,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_OUTOFRANGE, "The number of components %" PetscInt_FMT " must be equal to the dimension %" PetscInt_FMT " for this test", user->numComponents, dim);
465     PetscCall(DMGetDS(dm,&ds));
466     PetscCall(PetscDSSetJacobian(ds,0,0,NULL,NULL,NULL,symmetric_gradient_inner_product));
467     PetscCall(DMCreateMatrix(dm,&E));
468     PetscCall(DMGetLocalVector(dm,&local));
469     PetscCall(DMPlexSNESComputeJacobianFEM(dm,local,E,E,NULL));
470     PetscCall(DMPlexCreateRigidBody(dm,0,&sp));
471     PetscCall(MatNullSpaceGetVecs(sp,&hasConst,&n,&vecs));
472     if (n) PetscCall(VecDuplicate(vecs[0],&res));
473     PetscCall(DMCreateLocalVector(dm,&localX));
474     PetscCall(DMCreateLocalVector(dm,&localRes));
475     for (i = 0; i < n; i++) { /* also test via matrix-free Jacobian application */
476       PetscReal resNorm;
477 
478       PetscCall(VecSet(localRes,0.));
479       PetscCall(VecSet(localX,0.));
480       PetscCall(VecSet(local,0.));
481       PetscCall(VecSet(res,0.));
482       PetscCall(DMGlobalToLocalBegin(dm,vecs[i],INSERT_VALUES,localX));
483       PetscCall(DMGlobalToLocalEnd(dm,vecs[i],INSERT_VALUES,localX));
484       PetscCall(DMSNESComputeJacobianAction(dm,local,localX,localRes,NULL));
485       PetscCall(DMLocalToGlobalBegin(dm,localRes,ADD_VALUES,res));
486       PetscCall(DMLocalToGlobalEnd(dm,localRes,ADD_VALUES,res));
487       PetscCall(VecNorm(res,NORM_2,&resNorm));
488       if (resNorm > PETSC_SMALL) {
489         PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm),"Symmetric gradient action null space vector %" PetscInt_FMT " residual: %E\n",i,(double)resNorm));
490       }
491     }
492     PetscCall(VecDestroy(&localRes));
493     PetscCall(VecDestroy(&localX));
494     PetscCall(VecDestroy(&res));
495     PetscCall(MatNullSpaceTest(sp,E,&isNullSpace));
496     if (isNullSpace) {
497       PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm),"Symmetric gradient null space: PASS\n"));
498     } else {
499       PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm),"Symmetric gradient null space: FAIL\n"));
500     }
501     PetscCall(MatNullSpaceDestroy(&sp));
502     PetscCall(MatDestroy(&E));
503     PetscCall(DMRestoreLocalVector(dm,&local));
504   }
505   PetscFunctionReturn(0);
506 }
507 
508 static PetscErrorCode TestInjector(DM dm, AppCtx *user)
509 {
510   DM             refTree;
511   PetscMPIInt    rank;
512 
513   PetscFunctionBegin;
514   PetscCall(DMPlexGetReferenceTree(dm,&refTree));
515   if (refTree) {
516     Mat inj;
517 
518     PetscCall(DMPlexComputeInjectorReferenceTree(refTree,&inj));
519     PetscCall(PetscObjectSetName((PetscObject)inj,"Reference Tree Injector"));
520     PetscCallMPI(MPI_Comm_rank(PETSC_COMM_WORLD,&rank));
521     if (rank == 0) {
522       PetscCall(MatView(inj,PETSC_VIEWER_STDOUT_SELF));
523     }
524     PetscCall(MatDestroy(&inj));
525   }
526   PetscFunctionReturn(0);
527 }
528 
529 static PetscErrorCode TestFVGrad(DM dm, AppCtx *user)
530 {
531   MPI_Comm          comm;
532   DM                dmRedist, dmfv, dmgrad, dmCell, refTree;
533   PetscFV           fv;
534   PetscInt          dim, nvecs, v, cStart, cEnd, cEndInterior;
535   PetscMPIInt       size;
536   Vec               cellgeom, grad, locGrad;
537   const PetscScalar *cgeom;
538   PetscReal         allVecMaxDiff = 0., fvTol = 100. * PETSC_MACHINE_EPSILON;
539 
540   PetscFunctionBeginUser;
541   comm = PetscObjectComm((PetscObject)dm);
542   PetscCall(DMGetDimension(dm, &dim));
543   /* duplicate DM, give dup. a FV discretization */
544   PetscCall(DMSetBasicAdjacency(dm,PETSC_TRUE,PETSC_FALSE));
545   PetscCallMPI(MPI_Comm_size(comm,&size));
546   dmRedist = NULL;
547   if (size > 1) {
548     PetscCall(DMPlexDistributeOverlap(dm,1,NULL,&dmRedist));
549   }
550   if (!dmRedist) {
551     dmRedist = dm;
552     PetscCall(PetscObjectReference((PetscObject)dmRedist));
553   }
554   PetscCall(PetscFVCreate(comm,&fv));
555   PetscCall(PetscFVSetType(fv,PETSCFVLEASTSQUARES));
556   PetscCall(PetscFVSetNumComponents(fv,user->numComponents));
557   PetscCall(PetscFVSetSpatialDimension(fv,dim));
558   PetscCall(PetscFVSetFromOptions(fv));
559   PetscCall(PetscFVSetUp(fv));
560   PetscCall(DMPlexConstructGhostCells(dmRedist,NULL,NULL,&dmfv));
561   PetscCall(DMDestroy(&dmRedist));
562   PetscCall(DMSetNumFields(dmfv,1));
563   PetscCall(DMSetField(dmfv, 0, NULL, (PetscObject) fv));
564   PetscCall(DMCreateDS(dmfv));
565   PetscCall(DMPlexGetReferenceTree(dm,&refTree));
566   if (refTree) PetscCall(DMCopyDisc(dmfv,refTree));
567   PetscCall(DMPlexGetGradientDM(dmfv, fv, &dmgrad));
568   PetscCall(DMPlexGetHeightStratum(dmfv,0,&cStart,&cEnd));
569   nvecs = dim * (dim+1) / 2;
570   PetscCall(DMPlexGetGeometryFVM(dmfv,NULL,&cellgeom,NULL));
571   PetscCall(VecGetDM(cellgeom,&dmCell));
572   PetscCall(VecGetArrayRead(cellgeom,&cgeom));
573   PetscCall(DMGetGlobalVector(dmgrad,&grad));
574   PetscCall(DMGetLocalVector(dmgrad,&locGrad));
575   PetscCall(DMPlexGetGhostCellStratum(dmgrad,&cEndInterior,NULL));
576   cEndInterior = (cEndInterior < 0) ? cEnd: cEndInterior;
577   for (v = 0; v < nvecs; v++) {
578     Vec               locX;
579     PetscInt          c;
580     PetscScalar       trueGrad[3][3] = {{0.}};
581     const PetscScalar *gradArray;
582     PetscReal         maxDiff, maxDiffGlob;
583 
584     PetscCall(DMGetLocalVector(dmfv,&locX));
585     /* get the local projection of the rigid body mode */
586     for (c = cStart; c < cEnd; c++) {
587       PetscFVCellGeom *cg;
588       PetscScalar     cx[3] = {0.,0.,0.};
589 
590       PetscCall(DMPlexPointLocalRead(dmCell, c, cgeom, &cg));
591       if (v < dim) {
592         cx[v] = 1.;
593       } else {
594         PetscInt w = v - dim;
595 
596         cx[(w + 1) % dim] =  cg->centroid[(w + 2) % dim];
597         cx[(w + 2) % dim] = -cg->centroid[(w + 1) % dim];
598       }
599       PetscCall(DMPlexVecSetClosure(dmfv,NULL,locX,c,cx,INSERT_ALL_VALUES));
600     }
601     /* TODO: this isn't in any header */
602     PetscCall(DMPlexReconstructGradientsFVM(dmfv,locX,grad));
603     PetscCall(DMGlobalToLocalBegin(dmgrad,grad,INSERT_VALUES,locGrad));
604     PetscCall(DMGlobalToLocalEnd(dmgrad,grad,INSERT_VALUES,locGrad));
605     PetscCall(VecGetArrayRead(locGrad,&gradArray));
606     /* compare computed gradient to exact gradient */
607     if (v >= dim) {
608       PetscInt w = v - dim;
609 
610       trueGrad[(w + 1) % dim][(w + 2) % dim] =  1.;
611       trueGrad[(w + 2) % dim][(w + 1) % dim] = -1.;
612     }
613     maxDiff = 0.;
614     for (c = cStart; c < cEndInterior; c++) {
615       PetscScalar *compGrad;
616       PetscInt    i, j, k;
617       PetscReal   FrobDiff = 0.;
618 
619       PetscCall(DMPlexPointLocalRead(dmgrad, c, gradArray, &compGrad));
620 
621       for (i = 0, k = 0; i < dim; i++) {
622         for (j = 0; j < dim; j++, k++) {
623           PetscScalar diff = compGrad[k] - trueGrad[i][j];
624           FrobDiff += PetscRealPart(diff * PetscConj(diff));
625         }
626       }
627       FrobDiff = PetscSqrtReal(FrobDiff);
628       maxDiff  = PetscMax(maxDiff,FrobDiff);
629     }
630     PetscCallMPI(MPI_Allreduce(&maxDiff,&maxDiffGlob,1,MPIU_REAL,MPIU_MAX,comm));
631     allVecMaxDiff = PetscMax(allVecMaxDiff,maxDiffGlob);
632     PetscCall(VecRestoreArrayRead(locGrad,&gradArray));
633     PetscCall(DMRestoreLocalVector(dmfv,&locX));
634   }
635   if (allVecMaxDiff < fvTol) {
636     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm),"Finite volume gradient reconstruction: PASS\n"));
637   } else {
638     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm),"Finite volume gradient reconstruction: FAIL at tolerance %g with max difference %g\n",(double)fvTol,(double)allVecMaxDiff));
639   }
640   PetscCall(DMRestoreLocalVector(dmgrad,&locGrad));
641   PetscCall(DMRestoreGlobalVector(dmgrad,&grad));
642   PetscCall(VecRestoreArrayRead(cellgeom,&cgeom));
643   PetscCall(DMDestroy(&dmfv));
644   PetscCall(PetscFVDestroy(&fv));
645   PetscFunctionReturn(0);
646 }
647 
648 static PetscErrorCode ComputeError(DM dm, PetscErrorCode (**exactFuncs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *),
649                                    PetscErrorCode (**exactFuncDers)(PetscInt, PetscReal, const PetscReal[], const PetscReal[], PetscInt, PetscScalar *, void *),
650                                    void **exactCtxs, PetscReal *error, PetscReal *errorDer, AppCtx *user)
651 {
652   Vec            u;
653   PetscReal      n[3] = {1.0, 1.0, 1.0};
654 
655   PetscFunctionBeginUser;
656   PetscCall(DMGetGlobalVector(dm, &u));
657   /* Project function into FE function space */
658   PetscCall(DMProjectFunction(dm, 0.0, exactFuncs, exactCtxs, INSERT_ALL_VALUES, u));
659   PetscCall(VecViewFromOptions(u, NULL, "-projection_view"));
660   /* Compare approximation to exact in L_2 */
661   PetscCall(DMComputeL2Diff(dm, 0.0, exactFuncs, exactCtxs, u, error));
662   PetscCall(DMComputeL2GradientDiff(dm, 0.0, exactFuncDers, exactCtxs, u, n, errorDer));
663   PetscCall(DMRestoreGlobalVector(dm, &u));
664   PetscFunctionReturn(0);
665 }
666 
667 static PetscErrorCode CheckFunctions(DM dm, PetscInt order, AppCtx *user)
668 {
669   PetscErrorCode (*exactFuncs[1]) (PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nf, PetscScalar *u, void *ctx);
670   PetscErrorCode (*exactFuncDers[1]) (PetscInt dim, PetscReal time, const PetscReal x[], const PetscReal n[], PetscInt Nf, PetscScalar *u, void *ctx);
671   void            *exactCtxs[3];
672   MPI_Comm         comm;
673   PetscReal        error, errorDer, tol = PETSC_SMALL;
674 
675   PetscFunctionBeginUser;
676   exactCtxs[0]       = user;
677   exactCtxs[1]       = user;
678   exactCtxs[2]       = user;
679   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
680   /* Setup functions to approximate */
681   switch (order) {
682   case 0:
683     exactFuncs[0]    = constant;
684     exactFuncDers[0] = constantDer;
685     break;
686   case 1:
687     exactFuncs[0]    = linear;
688     exactFuncDers[0] = linearDer;
689     break;
690   case 2:
691     exactFuncs[0]    = quadratic;
692     exactFuncDers[0] = quadraticDer;
693     break;
694   case 3:
695     exactFuncs[0]    = cubic;
696     exactFuncDers[0] = cubicDer;
697     break;
698   default:
699     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Could not determine functions to test for order %" PetscInt_FMT, order);
700   }
701   PetscCall(ComputeError(dm, exactFuncs, exactFuncDers, exactCtxs, &error, &errorDer, user));
702   /* Report result */
703   if (error > tol)    PetscCall(PetscPrintf(comm, "Function tests FAIL for order %" PetscInt_FMT " at tolerance %g error %g\n", order, (double)tol,(double) error));
704   else                PetscCall(PetscPrintf(comm, "Function tests pass for order %" PetscInt_FMT " at tolerance %g\n", order, (double)tol));
705   if (errorDer > tol) PetscCall(PetscPrintf(comm, "Function tests FAIL for order %" PetscInt_FMT " derivatives at tolerance %g error %g\n", order, (double)tol, (double)errorDer));
706   else                PetscCall(PetscPrintf(comm, "Function tests pass for order %" PetscInt_FMT " derivatives at tolerance %g\n", order, (double)tol));
707   PetscFunctionReturn(0);
708 }
709 
710 static PetscErrorCode CheckInterpolation(DM dm, PetscBool checkRestrict, PetscInt order, AppCtx *user)
711 {
712   PetscErrorCode (*exactFuncs[1]) (PetscInt, PetscReal, const PetscReal x[], PetscInt, PetscScalar *u, void *ctx);
713   PetscErrorCode (*exactFuncDers[1]) (PetscInt, PetscReal, const PetscReal x[], const PetscReal n[], PetscInt, PetscScalar *u, void *ctx);
714   PetscReal       n[3]         = {1.0, 1.0, 1.0};
715   void           *exactCtxs[3];
716   DM              rdm, idm, fdm;
717   Mat             Interp;
718   Vec             iu, fu, scaling;
719   MPI_Comm        comm;
720   PetscInt        dim;
721   PetscReal       error, errorDer, tol = PETSC_SMALL;
722 
723   PetscFunctionBeginUser;
724   exactCtxs[0]       = user;
725   exactCtxs[1]       = user;
726   exactCtxs[2]       = user;
727   PetscCall(PetscObjectGetComm((PetscObject)dm,&comm));
728   PetscCall(DMGetDimension(dm, &dim));
729   PetscCall(DMRefine(dm, comm, &rdm));
730   PetscCall(DMSetCoarseDM(rdm, dm));
731   PetscCall(DMPlexSetRegularRefinement(rdm, user->convRefine));
732   if (user->tree) {
733     DM refTree;
734     PetscCall(DMPlexGetReferenceTree(dm,&refTree));
735     PetscCall(DMPlexSetReferenceTree(rdm,refTree));
736   }
737   if (user->useDA) PetscCall(DMDASetVertexCoordinates(rdm, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0));
738   PetscCall(SetupSection(rdm, user));
739   /* Setup functions to approximate */
740   switch (order) {
741   case 0:
742     exactFuncs[0]    = constant;
743     exactFuncDers[0] = constantDer;
744     break;
745   case 1:
746     exactFuncs[0]    = linear;
747     exactFuncDers[0] = linearDer;
748     break;
749   case 2:
750     exactFuncs[0]    = quadratic;
751     exactFuncDers[0] = quadraticDer;
752     break;
753   case 3:
754     exactFuncs[0]    = cubic;
755     exactFuncDers[0] = cubicDer;
756     break;
757   default:
758     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Could not determine functions to test for dimension %" PetscInt_FMT " order %" PetscInt_FMT, dim, order);
759   }
760   idm  = checkRestrict ? rdm :  dm;
761   fdm  = checkRestrict ?  dm : rdm;
762   PetscCall(DMGetGlobalVector(idm, &iu));
763   PetscCall(DMGetGlobalVector(fdm, &fu));
764   PetscCall(DMSetApplicationContext(dm, user));
765   PetscCall(DMSetApplicationContext(rdm, user));
766   PetscCall(DMCreateInterpolation(dm, rdm, &Interp, &scaling));
767   /* Project function into initial FE function space */
768   PetscCall(DMProjectFunction(idm, 0.0, exactFuncs, exactCtxs, INSERT_ALL_VALUES, iu));
769   /* Interpolate function into final FE function space */
770   if (checkRestrict) {PetscCall(MatRestrict(Interp, iu, fu));PetscCall(VecPointwiseMult(fu, scaling, fu));}
771   else               PetscCall(MatInterpolate(Interp, iu, fu));
772   /* Compare approximation to exact in L_2 */
773   PetscCall(DMComputeL2Diff(fdm, 0.0, exactFuncs, exactCtxs, fu, &error));
774   PetscCall(DMComputeL2GradientDiff(fdm, 0.0, exactFuncDers, exactCtxs, fu, n, &errorDer));
775   /* Report result */
776   if (error > tol)    PetscCall(PetscPrintf(comm, "Interpolation tests FAIL for order %" PetscInt_FMT " at tolerance %g error %g\n", order, (double)tol, (double)error));
777   else                PetscCall(PetscPrintf(comm, "Interpolation tests pass for order %" PetscInt_FMT " at tolerance %g\n", order, (double)tol));
778   if (errorDer > tol) PetscCall(PetscPrintf(comm, "Interpolation tests FAIL for order %" PetscInt_FMT " derivatives at tolerance %g error %g\n", order, (double)tol, (double)errorDer));
779   else                PetscCall(PetscPrintf(comm, "Interpolation tests pass for order %" PetscInt_FMT " derivatives at tolerance %g\n", order, (double)tol));
780   PetscCall(DMRestoreGlobalVector(idm, &iu));
781   PetscCall(DMRestoreGlobalVector(fdm, &fu));
782   PetscCall(MatDestroy(&Interp));
783   PetscCall(VecDestroy(&scaling));
784   PetscCall(DMDestroy(&rdm));
785   PetscFunctionReturn(0);
786 }
787 
788 static PetscErrorCode CheckConvergence(DM dm, PetscInt Nr, AppCtx *user)
789 {
790   DM               odm = dm, rdm = NULL, cdm = NULL;
791   PetscErrorCode (*exactFuncs[1]) (PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nf, PetscScalar *u, void *ctx) = {trig};
792   PetscErrorCode (*exactFuncDers[1]) (PetscInt dim, PetscReal time, const PetscReal x[], const PetscReal n[], PetscInt Nf, PetscScalar *u, void *ctx) = {trigDer};
793   void            *exactCtxs[3];
794   PetscInt         r, c, cStart, cEnd;
795   PetscReal        errorOld, errorDerOld, error, errorDer, rel, len, lenOld;
796   double           p;
797 
798   PetscFunctionBeginUser;
799   if (!user->convergence) PetscFunctionReturn(0);
800   exactCtxs[0] = user;
801   exactCtxs[1] = user;
802   exactCtxs[2] = user;
803   PetscCall(PetscObjectReference((PetscObject) odm));
804   if (!user->convRefine) {
805     for (r = 0; r < Nr; ++r) {
806       PetscCall(DMRefine(odm, PetscObjectComm((PetscObject) dm), &rdm));
807       PetscCall(DMDestroy(&odm));
808       odm  = rdm;
809     }
810     PetscCall(SetupSection(odm, user));
811   }
812   PetscCall(ComputeError(odm, exactFuncs, exactFuncDers, exactCtxs, &errorOld, &errorDerOld, user));
813   if (user->convRefine) {
814     for (r = 0; r < Nr; ++r) {
815       PetscCall(DMRefine(odm, PetscObjectComm((PetscObject) dm), &rdm));
816       if (user->useDA) PetscCall(DMDASetVertexCoordinates(rdm, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0));
817       PetscCall(SetupSection(rdm, user));
818       PetscCall(ComputeError(rdm, exactFuncs, exactFuncDers, exactCtxs, &error, &errorDer, user));
819       p    = PetscLog2Real(errorOld/error);
820       PetscCall(PetscPrintf(PetscObjectComm((PetscObject) dm), "Function   convergence rate at refinement %" PetscInt_FMT ": %.2f\n", r, (double)p));
821       p    = PetscLog2Real(errorDerOld/errorDer);
822       PetscCall(PetscPrintf(PetscObjectComm((PetscObject) dm), "Derivative convergence rate at refinement %" PetscInt_FMT ": %.2f\n", r, (double)p));
823       PetscCall(DMDestroy(&odm));
824       odm         = rdm;
825       errorOld    = error;
826       errorDerOld = errorDer;
827     }
828   } else {
829     /* PetscCall(ComputeLongestEdge(dm, &lenOld)); */
830     PetscCall(DMPlexGetHeightStratum(odm, 0, &cStart, &cEnd));
831     lenOld = cEnd - cStart;
832     for (c = 0; c < Nr; ++c) {
833       PetscCall(DMCoarsen(odm, PetscObjectComm((PetscObject) dm), &cdm));
834       if (user->useDA) PetscCall(DMDASetVertexCoordinates(cdm, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0));
835       PetscCall(SetupSection(cdm, user));
836       PetscCall(ComputeError(cdm, exactFuncs, exactFuncDers, exactCtxs, &error, &errorDer, user));
837       /* PetscCall(ComputeLongestEdge(cdm, &len)); */
838       PetscCall(DMPlexGetHeightStratum(cdm, 0, &cStart, &cEnd));
839       len  = cEnd - cStart;
840       rel  = error/errorOld;
841       p    = PetscLogReal(rel) / PetscLogReal(lenOld / len);
842       PetscCall(PetscPrintf(PetscObjectComm((PetscObject) dm), "Function   convergence rate at coarsening %" PetscInt_FMT ": %.2f\n", c, (double)p));
843       rel  = errorDer/errorDerOld;
844       p    = PetscLogReal(rel) / PetscLogReal(lenOld / len);
845       PetscCall(PetscPrintf(PetscObjectComm((PetscObject) dm), "Derivative convergence rate at coarsening %" PetscInt_FMT ": %.2f\n", c, (double)p));
846       PetscCall(DMDestroy(&odm));
847       odm         = cdm;
848       errorOld    = error;
849       errorDerOld = errorDer;
850       lenOld      = len;
851     }
852   }
853   PetscCall(DMDestroy(&odm));
854   PetscFunctionReturn(0);
855 }
856 
857 int main(int argc, char **argv)
858 {
859   DM             dm;
860   AppCtx         user;                 /* user-defined work context */
861   PetscInt       dim = 2;
862   PetscBool      simplex = PETSC_FALSE;
863 
864   PetscCall(PetscInitialize(&argc, &argv, NULL, help));
865   PetscCall(ProcessOptions(PETSC_COMM_WORLD, &user));
866   PetscCall(CreateMesh(PETSC_COMM_WORLD, &user, &dm));
867   if (!user.useDA) {
868     PetscCall(DMGetDimension(dm, &dim));
869     PetscCall(DMPlexIsSimplex(dm, &simplex));
870   }
871   PetscCall(DMPlexMetricSetFromOptions(dm));
872   user.numComponents = user.numComponents < 0 ? dim : user.numComponents;
873   PetscCall(PetscFECreateDefault(PETSC_COMM_WORLD, dim, user.numComponents, simplex, NULL, user.qorder, &user.fe));
874   PetscCall(SetupSection(dm, &user));
875   if (user.testFEjacobian) PetscCall(TestFEJacobian(dm, &user));
876   if (user.testFVgrad) PetscCall(TestFVGrad(dm, &user));
877   if (user.testInjector) PetscCall(TestInjector(dm, &user));
878   PetscCall(CheckFunctions(dm, user.porder, &user));
879   {
880     PetscDualSpace dsp;
881     PetscInt       k;
882 
883     PetscCall(PetscFEGetDualSpace(user.fe, &dsp));
884     PetscCall(PetscDualSpaceGetDeRahm(dsp, &k));
885     if (dim == 2 && simplex == PETSC_TRUE && user.tree == PETSC_FALSE && k == 0) {
886       PetscCall(CheckInterpolation(dm, PETSC_FALSE, user.porder, &user));
887       PetscCall(CheckInterpolation(dm, PETSC_TRUE,  user.porder, &user));
888     }
889   }
890   PetscCall(CheckConvergence(dm, 3, &user));
891   PetscCall(PetscFEDestroy(&user.fe));
892   PetscCall(DMDestroy(&dm));
893   PetscCall(PetscFinalize());
894   return 0;
895 }
896 
897 /*TEST
898 
899   test:
900     suffix: 1
901     requires: triangle
902 
903   # 2D P_1 on a triangle
904   test:
905     suffix: p1_2d_0
906     requires: triangle
907     args: -petscspace_degree 1 -qorder 1 -convergence
908   test:
909     suffix: p1_2d_1
910     requires: triangle
911     args: -petscspace_degree 1 -qorder 1 -porder 1
912   test:
913     suffix: p1_2d_2
914     requires: triangle
915     args: -petscspace_degree 1 -qorder 1 -porder 2
916   test:
917     suffix: p1_2d_3
918     requires: triangle mmg
919     args: -petscspace_degree 1 -qorder 1 -dm_plex_hash_location -convergence -conv_refine 0
920   test:
921     suffix: p1_2d_4
922     requires: triangle mmg
923     args: -petscspace_degree 1 -qorder 1 -dm_plex_hash_location -porder 1 -conv_refine 0
924   test:
925     suffix: p1_2d_5
926     requires: triangle mmg
927     args: -petscspace_degree 1 -qorder 1 -dm_plex_hash_location -porder 2 -conv_refine 0
928 
929   # 3D P_1 on a tetrahedron
930   test:
931     suffix: p1_3d_0
932     requires: ctetgen
933     args: -dm_plex_dim 3 -petscspace_degree 1 -qorder 1 -convergence
934   test:
935     suffix: p1_3d_1
936     requires: ctetgen
937     args: -dm_plex_dim 3 -petscspace_degree 1 -qorder 1 -porder 1
938   test:
939     suffix: p1_3d_2
940     requires: ctetgen
941     args: -dm_plex_dim 3 -petscspace_degree 1 -qorder 1 -porder 2
942   test:
943     suffix: p1_3d_3
944     requires: ctetgen mmg
945     args: -dm_plex_dim 3 -petscspace_degree 1 -qorder 1 -dm_plex_hash_location -convergence -conv_refine 0
946   test:
947     suffix: p1_3d_4
948     requires: ctetgen mmg
949     args: -dm_plex_dim 3 -petscspace_degree 1 -qorder 1 -dm_plex_hash_location -porder 1 -conv_refine 0
950   test:
951     suffix: p1_3d_5
952     requires: ctetgen mmg
953     args: -dm_plex_dim 3 -petscspace_degree 1 -qorder 1 -dm_plex_hash_location -porder 2 -conv_refine 0
954 
955   # 2D P_2 on a triangle
956   test:
957     suffix: p2_2d_0
958     requires: triangle
959     args: -petscspace_degree 2 -qorder 2 -convergence
960   test:
961     suffix: p2_2d_1
962     requires: triangle
963     args: -petscspace_degree 2 -qorder 2 -porder 1
964   test:
965     suffix: p2_2d_2
966     requires: triangle
967     args: -petscspace_degree 2 -qorder 2 -porder 2
968   test:
969     suffix: p2_2d_3
970     requires: triangle mmg
971     args: -petscspace_degree 2 -qorder 2 -dm_plex_hash_location -convergence -conv_refine 0
972   test:
973     suffix: p2_2d_4
974     requires: triangle mmg
975     args: -petscspace_degree 2 -qorder 2 -dm_plex_hash_location -porder 1 -conv_refine 0
976   test:
977     suffix: p2_2d_5
978     requires: triangle mmg
979     args: -petscspace_degree 2 -qorder 2 -dm_plex_hash_location -porder 2 -conv_refine 0
980 
981   # 3D P_2 on a tetrahedron
982   test:
983     suffix: p2_3d_0
984     requires: ctetgen
985     args: -dm_plex_dim 3 -petscspace_degree 2 -qorder 2 -convergence
986   test:
987     suffix: p2_3d_1
988     requires: ctetgen
989     args: -dm_plex_dim 3 -petscspace_degree 2 -qorder 2 -porder 1
990   test:
991     suffix: p2_3d_2
992     requires: ctetgen
993     args: -dm_plex_dim 3 -petscspace_degree 2 -qorder 2 -porder 2
994   test:
995     suffix: p2_3d_3
996     requires: ctetgen mmg
997     args: -dm_plex_dim 3 -petscspace_degree 2 -qorder 2 -dm_plex_hash_location -convergence -conv_refine 0
998   test:
999     suffix: p2_3d_4
1000     requires: ctetgen mmg
1001     args: -dm_plex_dim 3 -petscspace_degree 2 -qorder 2 -dm_plex_hash_location -porder 1 -conv_refine 0
1002   test:
1003     suffix: p2_3d_5
1004     requires: ctetgen mmg
1005     args: -dm_plex_dim 3 -petscspace_degree 2 -qorder 2 -dm_plex_hash_location -porder 2 -conv_refine 0
1006 
1007   # 2D Q_1 on a quadrilaterial DA
1008   test:
1009     suffix: q1_2d_da_0
1010     requires: broken
1011     args: -use_da 1 -petscspace_degree 1 -qorder 1 -convergence
1012   test:
1013     suffix: q1_2d_da_1
1014     requires: broken
1015     args: -use_da 1 -petscspace_degree 1 -qorder 1 -porder 1
1016   test:
1017     suffix: q1_2d_da_2
1018     requires: broken
1019     args: -use_da 1 -petscspace_degree 1 -qorder 1 -porder 2
1020 
1021   # 2D Q_1 on a quadrilaterial Plex
1022   test:
1023     suffix: q1_2d_plex_0
1024     args: -dm_plex_simplex 0 -petscspace_degree 1 -qorder 1 -convergence
1025   test:
1026     suffix: q1_2d_plex_1
1027     args: -dm_plex_simplex 0 -petscspace_degree 1 -qorder 1 -porder 1
1028   test:
1029     suffix: q1_2d_plex_2
1030     args: -dm_plex_simplex 0 -petscspace_degree 1 -qorder 1 -porder 2
1031   test:
1032     suffix: q1_2d_plex_3
1033     args: -dm_plex_simplex 0 -petscspace_degree 1 -qorder 1 -porder 1 -shear_coords
1034   test:
1035     suffix: q1_2d_plex_4
1036     args: -dm_plex_simplex 0 -petscspace_degree 1 -qorder 1 -porder 2 -shear_coords
1037   test:
1038     suffix: q1_2d_plex_5
1039     args: -dm_plex_simplex 0 -petscspace_degree 1 -petscspace_type tensor -qorder 1 -porder 0 -non_affine_coords -convergence
1040   test:
1041     suffix: q1_2d_plex_6
1042     args: -dm_plex_simplex 0 -petscspace_degree 1 -petscspace_type tensor -qorder 1 -porder 1 -non_affine_coords -convergence
1043   test:
1044     suffix: q1_2d_plex_7
1045     args: -dm_plex_simplex 0 -petscspace_degree 1 -petscspace_type tensor -qorder 1 -porder 2 -non_affine_coords -convergence
1046 
1047   # 2D Q_2 on a quadrilaterial
1048   test:
1049     suffix: q2_2d_plex_0
1050     args: -dm_plex_simplex 0 -petscspace_degree 2 -qorder 2 -convergence
1051   test:
1052     suffix: q2_2d_plex_1
1053     args: -dm_plex_simplex 0 -petscspace_degree 2 -qorder 2 -porder 1
1054   test:
1055     suffix: q2_2d_plex_2
1056     args: -dm_plex_simplex 0 -petscspace_degree 2 -qorder 2 -porder 2
1057   test:
1058     suffix: q2_2d_plex_3
1059     args: -dm_plex_simplex 0 -petscspace_degree 2 -qorder 2 -porder 1 -shear_coords
1060   test:
1061     suffix: q2_2d_plex_4
1062     args: -dm_plex_simplex 0 -petscspace_degree 2 -qorder 2 -porder 2 -shear_coords
1063   test:
1064     suffix: q2_2d_plex_5
1065     args: -dm_plex_simplex 0 -petscspace_degree 2 -petscspace_type tensor -qorder 2 -porder 0 -non_affine_coords -convergence
1066   test:
1067     suffix: q2_2d_plex_6
1068     args: -dm_plex_simplex 0 -petscspace_degree 2 -petscspace_type tensor -qorder 2 -porder 1 -non_affine_coords -convergence
1069   test:
1070     suffix: q2_2d_plex_7
1071     args: -dm_plex_simplex 0 -petscspace_degree 2 -petscspace_type tensor -qorder 2 -porder 2 -non_affine_coords -convergence
1072 
1073   # 2D P_3 on a triangle
1074   test:
1075     suffix: p3_2d_0
1076     requires: triangle !single
1077     args: -petscspace_degree 3 -qorder 3 -convergence
1078   test:
1079     suffix: p3_2d_1
1080     requires: triangle !single
1081     args: -petscspace_degree 3 -qorder 3 -porder 1
1082   test:
1083     suffix: p3_2d_2
1084     requires: triangle !single
1085     args: -petscspace_degree 3 -qorder 3 -porder 2
1086   test:
1087     suffix: p3_2d_3
1088     requires: triangle !single
1089     args: -petscspace_degree 3 -qorder 3 -porder 3
1090   test:
1091     suffix: p3_2d_4
1092     requires: triangle mmg
1093     args: -petscspace_degree 3 -qorder 3 -dm_plex_hash_location -convergence -conv_refine 0
1094   test:
1095     suffix: p3_2d_5
1096     requires: triangle mmg
1097     args: -petscspace_degree 3 -qorder 3 -dm_plex_hash_location -porder 1 -conv_refine 0
1098   test:
1099     suffix: p3_2d_6
1100     requires: triangle mmg
1101     args: -petscspace_degree 3 -qorder 3 -dm_plex_hash_location -porder 3 -conv_refine 0
1102 
1103   # 2D Q_3 on a quadrilaterial
1104   test:
1105     suffix: q3_2d_0
1106     requires: !single
1107     args: -dm_plex_simplex 0 -petscspace_degree 3 -qorder 3 -convergence
1108   test:
1109     suffix: q3_2d_1
1110     requires: !single
1111     args: -dm_plex_simplex 0 -petscspace_degree 3 -qorder 3 -porder 1
1112   test:
1113     suffix: q3_2d_2
1114     requires: !single
1115     args: -dm_plex_simplex 0 -petscspace_degree 3 -qorder 3 -porder 2
1116   test:
1117     suffix: q3_2d_3
1118     requires: !single
1119     args: -dm_plex_simplex 0 -petscspace_degree 3 -qorder 3 -porder 3
1120 
1121   # 2D P_1disc on a triangle/quadrilateral
1122   test:
1123     suffix: p1d_2d_0
1124     requires: triangle
1125     args: -petscspace_degree 1 -petscdualspace_lagrange_continuity 0 -qorder 1 -convergence
1126   test:
1127     suffix: p1d_2d_1
1128     requires: triangle
1129     args: -petscspace_degree 1 -petscdualspace_lagrange_continuity 0 -qorder 1 -porder 1
1130   test:
1131     suffix: p1d_2d_2
1132     requires: triangle
1133     args: -petscspace_degree 1 -petscdualspace_lagrange_continuity 0 -qorder 1 -porder 2
1134   test:
1135     suffix: p1d_2d_3
1136     requires: triangle
1137     args: -dm_plex_simplex 0 -petscspace_degree 1 -petscdualspace_lagrange_continuity 0 -qorder 1 -convergence
1138     filter: sed  -e "s/convergence rate at refinement 0: 2/convergence rate at refinement 0: 1.9/g"
1139   test:
1140     suffix: p1d_2d_4
1141     requires: triangle
1142     args: -dm_plex_simplex 0 -petscspace_degree 1 -petscdualspace_lagrange_continuity 0 -qorder 1 -porder 1
1143   test:
1144     suffix: p1d_2d_5
1145     requires: triangle
1146     args: -dm_plex_simplex 0 -petscspace_degree 1 -petscdualspace_lagrange_continuity 0 -qorder 1 -porder 2
1147 
1148   # 2D BDM_1 on a triangle
1149   test:
1150     suffix: bdm1_2d_0
1151     requires: triangle
1152     args: -petscspace_degree 1 -petscdualspace_type bdm \
1153           -num_comp 2 -qorder 1 -convergence
1154   test:
1155     suffix: bdm1_2d_1
1156     requires: triangle
1157     args: -petscspace_degree 1 -petscdualspace_type bdm \
1158           -num_comp 2 -qorder 1 -porder 1
1159   test:
1160     suffix: bdm1_2d_2
1161     requires: triangle
1162     args: -petscspace_degree 1 -petscdualspace_type bdm \
1163           -num_comp 2 -qorder 1 -porder 2
1164 
1165   # 2D BDM_1 on a quadrilateral
1166   test:
1167     suffix: bdm1q_2d_0
1168     requires: triangle
1169     args: -petscspace_degree 1 -petscdualspace_type bdm \
1170           -petscdualspace_lagrange_tensor 1 \
1171           -dm_plex_simplex 0 -num_comp 2 -qorder 1 -convergence
1172   test:
1173     suffix: bdm1q_2d_1
1174     requires: triangle
1175     args: -petscspace_degree 1 -petscdualspace_type bdm \
1176           -petscdualspace_lagrange_tensor 1 \
1177           -dm_plex_simplex 0 -num_comp 2 -qorder 1 -porder 1
1178   test:
1179     suffix: bdm1q_2d_2
1180     requires: triangle
1181     args: -petscspace_degree 1 -petscdualspace_type bdm \
1182           -petscdualspace_lagrange_tensor 1 \
1183           -dm_plex_simplex 0 -num_comp 2 -qorder 1 -porder 2
1184 
1185   # Test high order quadrature
1186   test:
1187     suffix: p1_quad_2
1188     requires: triangle
1189     args: -petscspace_degree 1 -qorder 2 -porder 1
1190   test:
1191     suffix: p1_quad_5
1192     requires: triangle
1193     args: -petscspace_degree 1 -qorder 5 -porder 1
1194   test:
1195     suffix: p2_quad_3
1196     requires: triangle
1197     args: -petscspace_degree 2 -qorder 3 -porder 2
1198   test:
1199     suffix: p2_quad_5
1200     requires: triangle
1201     args: -petscspace_degree 2 -qorder 5 -porder 2
1202   test:
1203     suffix: q1_quad_2
1204     args: -dm_plex_simplex 0 -petscspace_degree 1 -qorder 2 -porder 1
1205   test:
1206     suffix: q1_quad_5
1207     args: -dm_plex_simplex 0 -petscspace_degree 1 -qorder 5 -porder 1
1208   test:
1209     suffix: q2_quad_3
1210     args: -dm_plex_simplex 0 -petscspace_degree 2 -qorder 3 -porder 1
1211   test:
1212     suffix: q2_quad_5
1213     args: -dm_plex_simplex 0 -petscspace_degree 2 -qorder 5 -porder 1
1214 
1215   # Nonconforming tests
1216   test:
1217     suffix: constraints
1218     args: -dm_coord_space 0 -dm_plex_simplex 0 -petscspace_type tensor -petscspace_degree 1 -qorder 0 -constraints
1219   test:
1220     suffix: nonconforming_tensor_2
1221     nsize: 4
1222     args: -dist_dm_distribute -test_fe_jacobian -test_injector -petscpartitioner_type simple -tree -dm_plex_simplex 0 -dm_plex_max_projection_height 1 -petscspace_type tensor -petscspace_degree 2 -qorder 2 -dm_view ascii::ASCII_INFO_DETAIL
1223   test:
1224     suffix: nonconforming_tensor_3
1225     nsize: 4
1226     args: -dist_dm_distribute -test_fe_jacobian -petscpartitioner_type simple -tree -dm_plex_simplex 0 -dm_plex_dim 3 -dm_plex_box_faces 2,2,2 -dm_plex_max_projection_height 2 -petscspace_type tensor -petscspace_degree 1 -qorder 1 -dm_view ascii::ASCII_INFO_DETAIL
1227   test:
1228     suffix: nonconforming_tensor_2_fv
1229     nsize: 4
1230     args: -dist_dm_distribute -test_fv_grad -test_injector -petsclimiter_type none -petscpartitioner_type simple -tree -dm_plex_simplex 0 -num_comp 2
1231   test:
1232     suffix: nonconforming_tensor_3_fv
1233     nsize: 4
1234     args: -dist_dm_distribute -test_fv_grad -test_injector -petsclimiter_type none -petscpartitioner_type simple -tree -dm_plex_simplex 0 -dm_plex_dim 3 -dm_plex_box_faces 2,2,2 -num_comp 3
1235   test:
1236     suffix: nonconforming_tensor_2_hi
1237     requires: !single
1238     nsize: 4
1239     args: -dist_dm_distribute -test_fe_jacobian -petscpartitioner_type simple -tree -dm_plex_simplex 0 -dm_plex_max_projection_height 1 -petscspace_type tensor -petscspace_degree 4 -qorder 4
1240   test:
1241     suffix: nonconforming_tensor_3_hi
1242     requires: !single skip
1243     nsize: 4
1244     args: -dist_dm_distribute -test_fe_jacobian -petscpartitioner_type simple -tree -dm_plex_simplex 0 -dm_plex_dim 3 -dm_plex_box_faces 2,2,2 -dm_plex_max_projection_height 2 -petscspace_type tensor -petscspace_degree 4 -qorder 4
1245   test:
1246     suffix: nonconforming_simplex_2
1247     requires: triangle
1248     nsize: 4
1249     args: -dist_dm_distribute -test_fe_jacobian -test_injector -petscpartitioner_type simple -tree -dm_plex_max_projection_height 1 -petscspace_degree 2 -qorder 2 -dm_view ascii::ASCII_INFO_DETAIL
1250   test:
1251     suffix: nonconforming_simplex_2_hi
1252     requires: triangle !single
1253     nsize: 4
1254     args: -dist_dm_distribute -test_fe_jacobian -petscpartitioner_type simple -tree -dm_plex_max_projection_height 1 -petscspace_degree 4 -qorder 4
1255   test:
1256     suffix: nonconforming_simplex_2_fv
1257     requires: triangle
1258     nsize: 4
1259     args: -dist_dm_distribute -test_fv_grad -test_injector -petsclimiter_type none -petscpartitioner_type simple -tree -num_comp 2
1260   test:
1261     suffix: nonconforming_simplex_3
1262     requires: ctetgen
1263     nsize: 4
1264     args: -dist_dm_distribute -test_fe_jacobian -test_injector -petscpartitioner_type simple -tree -dm_plex_dim 3 -dm_plex_max_projection_height 2 -petscspace_degree 2 -qorder 2 -dm_view ascii::ASCII_INFO_DETAIL
1265   test:
1266     suffix: nonconforming_simplex_3_hi
1267     requires: ctetgen skip
1268     nsize: 4
1269     args: -dist_dm_distribute -test_fe_jacobian -petscpartitioner_type simple -tree -dm_plex_dim 3 -dm_plex_max_projection_height 2 -petscspace_degree 4 -qorder 4
1270   test:
1271     suffix: nonconforming_simplex_3_fv
1272     requires: ctetgen
1273     nsize: 4
1274     args: -dist_dm_distribute -test_fv_grad -test_injector -petsclimiter_type none -petscpartitioner_type simple -tree -dm_plex_dim 3 -num_comp 3
1275 
1276   # 3D WXY on a triangular prism
1277   test:
1278     suffix: wxy_0
1279     args: -dm_plex_reference_cell_domain -dm_plex_cell triangular_prism -qorder 2 -porder 0 \
1280           -petscspace_type sum \
1281           -petscspace_variables 3 \
1282           -petscspace_components 3 \
1283           -petscspace_sum_spaces 2 \
1284           -petscspace_sum_concatenate false \
1285           -sumcomp_0_petscspace_variables 3 \
1286           -sumcomp_0_petscspace_components 3 \
1287           -sumcomp_0_petscspace_degree 1 \
1288           -sumcomp_1_petscspace_variables 3 \
1289           -sumcomp_1_petscspace_components 3 \
1290           -sumcomp_1_petscspace_type wxy \
1291           -petscdualspace_refcell triangular_prism \
1292           -petscdualspace_form_degree 0 \
1293           -petscdualspace_order 1 \
1294           -petscdualspace_components 3
1295 
1296 TEST*/
1297 
1298 /*
1299    # 2D Q_2 on a quadrilaterial Plex
1300   test:
1301     suffix: q2_2d_plex_0
1302     args: -dm_plex_simplex 0 -petscspace_degree 2 -qorder 2 -convergence
1303   test:
1304     suffix: q2_2d_plex_1
1305     args: -dm_plex_simplex 0 -petscspace_degree 2 -qorder 2 -porder 1
1306   test:
1307     suffix: q2_2d_plex_2
1308     args: -dm_plex_simplex 0 -petscspace_degree 2 -qorder 2 -porder 2
1309   test:
1310     suffix: q2_2d_plex_3
1311     args: -dm_plex_simplex 0 -petscspace_degree 2 -qorder 2 -porder 1 -shear_coords
1312   test:
1313     suffix: q2_2d_plex_4
1314     args: -dm_plex_simplex 0 -petscspace_degree 2 -qorder 2 -porder 2 -shear_coords
1315   test:
1316     suffix: q2_2d_plex_5
1317     args: -dm_plex_simplex 0 -petscspace_degree 2 -petscspace_poly_tensor 1 -qorder 2 -porder 0 -non_affine_coords
1318   test:
1319     suffix: q2_2d_plex_6
1320     args: -dm_plex_simplex 0 -petscspace_degree 2 -petscspace_poly_tensor 1 -qorder 2 -porder 1 -non_affine_coords
1321   test:
1322     suffix: q2_2d_plex_7
1323     args: -dm_plex_simplex 0 -petscspace_degree 2 -petscspace_poly_tensor 1 -qorder 2 -porder 2 -non_affine_coords
1324 
1325   test:
1326     suffix: p1d_2d_6
1327     requires: mmg
1328     args: -petscspace_degree 1 -qorder 1 -dm_plex_hash_location -convergence -conv_refine 0
1329   test:
1330     suffix: p1d_2d_7
1331     requires: mmg
1332     args: -petscspace_degree 1 -qorder 1 -dm_plex_hash_location -porder 1 -conv_refine 0
1333   test:
1334     suffix: p1d_2d_8
1335     requires: mmg
1336     args: -petscspace_degree 1 -qorder 1 -dm_plex_hash_location -porder 2 -conv_refine 0
1337 */
1338