xref: /petsc/src/snes/tutorials/ex7.c (revision 3f02e49b19195914bf17f317a25cb39636853415)
1 static char help[] = "Fermions on a hypercubic lattice.\n\n";
2 
3 #include <petscdmplex.h>
4 #include <petscsnes.h>
5 
6 /* Common operations:
7 
8  - View the input \psi as ASCII in lexicographic order: -psi_view
9 */
10 
11 const PetscReal M = 1.0;
12 
13 typedef struct {
14   PetscBool printTr; // Print the traversal
15   PetscBool usePV;   // Use Pauli-Villars preconditioning
16 } AppCtx;
17 
18 PetscErrorCode ProcessOptions(MPI_Comm comm, AppCtx *options)
19 {
20   PetscFunctionBegin;
21   options->printTr = PETSC_FALSE;
22   options->usePV   = PETSC_TRUE;
23 
24   PetscOptionsBegin(comm, "", "Meshing Problem Options", "DMPLEX");
25   PetscCall(PetscOptionsBool("-print_traversal", "Print the mesh traversal", "ex1.c", options->printTr, &options->printTr, NULL));
26   PetscCall(PetscOptionsBool("-use_pv", "Use Pauli-Villars preconditioning", "ex1.c", options->usePV, &options->usePV, NULL));
27   PetscOptionsEnd();
28   PetscFunctionReturn(PETSC_SUCCESS);
29 }
30 
31 PetscErrorCode CreateMesh(MPI_Comm comm, AppCtx *user, DM *dm)
32 {
33   PetscFunctionBegin;
34   PetscCall(DMCreate(comm, dm));
35   PetscCall(DMSetType(*dm, DMPLEX));
36   PetscCall(DMSetFromOptions(*dm));
37   PetscCall(DMViewFromOptions(*dm, NULL, "-dm_view"));
38   PetscFunctionReturn(PETSC_SUCCESS);
39 }
40 
41 static PetscErrorCode SetupDiscretization(DM dm, AppCtx *ctx)
42 {
43   PetscSection s;
44   PetscInt     vStart, vEnd, v;
45 
46   PetscFunctionBegin;
47   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
48   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
49   PetscCall(PetscSectionSetChart(s, vStart, vEnd));
50   for (v = vStart; v < vEnd; ++v) {
51     PetscCall(PetscSectionSetDof(s, v, 12));
52     /* TODO Divide the values into fields/components */
53   }
54   PetscCall(PetscSectionSetUp(s));
55   PetscCall(DMSetLocalSection(dm, s));
56   PetscCall(PetscSectionDestroy(&s));
57   PetscFunctionReturn(PETSC_SUCCESS);
58 }
59 
60 static PetscErrorCode SetupAuxDiscretization(DM dm, AppCtx *user)
61 {
62   DM           dmAux, coordDM;
63   PetscSection s;
64   Vec          gauge;
65   PetscInt     eStart, eEnd, e;
66 
67   PetscFunctionBegin;
68   /* MUST call DMGetCoordinateDM() in order to get p4est setup if present */
69   PetscCall(DMGetCoordinateDM(dm, &coordDM));
70   PetscCall(DMClone(dm, &dmAux));
71   PetscCall(DMSetCoordinateDM(dmAux, coordDM));
72   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
73   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
74   PetscCall(PetscSectionSetChart(s, eStart, eEnd));
75   for (e = eStart; e < eEnd; ++e) {
76     /* TODO Should we store the whole SU(3) matrix, or the symmetric part? */
77     PetscCall(PetscSectionSetDof(s, e, 9));
78   }
79   PetscCall(PetscSectionSetUp(s));
80   PetscCall(DMSetLocalSection(dmAux, s));
81   PetscCall(PetscSectionDestroy(&s));
82   PetscCall(DMCreateLocalVector(dmAux, &gauge));
83   PetscCall(DMDestroy(&dmAux));
84   PetscCall(DMSetAuxiliaryVec(dm, NULL, 0, 0, gauge));
85   PetscCall(VecDestroy(&gauge));
86   PetscFunctionReturn(PETSC_SUCCESS);
87 }
88 
89 static PetscErrorCode PrintVertex(DM dm, PetscInt v)
90 {
91   MPI_Comm       comm;
92   PetscContainer c;
93   PetscInt      *extent;
94   PetscInt       dim, cStart, cEnd, sum;
95 
96   PetscFunctionBeginUser;
97   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
98   PetscCall(DMGetDimension(dm, &dim));
99   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
100   PetscCall(PetscObjectQuery((PetscObject)dm, "_extent", (PetscObject *)&c));
101   PetscCall(PetscContainerGetPointer(c, (void **)&extent));
102   sum = 1;
103   PetscCall(PetscPrintf(comm, "Vertex %" PetscInt_FMT ":", v));
104   for (PetscInt d = 0; d < dim; ++d) {
105     PetscCall(PetscPrintf(comm, " %" PetscInt_FMT, (v / sum) % extent[d]));
106     if (d < dim) sum *= extent[d];
107   }
108   PetscFunctionReturn(PETSC_SUCCESS);
109 }
110 
111 // Apply \gamma_\mu
112 static PetscErrorCode ComputeGamma(PetscInt d, PetscInt ldx, PetscScalar f[])
113 {
114   const PetscScalar fin[4] = {f[0 * ldx], f[1 * ldx], f[2 * ldx], f[3 * ldx]};
115 
116   PetscFunctionBeginHot;
117   switch (d) {
118   case 0:
119     f[0 * ldx] = PETSC_i * fin[3];
120     f[1 * ldx] = PETSC_i * fin[2];
121     f[2 * ldx] = -PETSC_i * fin[1];
122     f[3 * ldx] = -PETSC_i * fin[0];
123     break;
124   case 1:
125     f[0 * ldx] = -fin[3];
126     f[1 * ldx] = fin[2];
127     f[2 * ldx] = fin[1];
128     f[3 * ldx] = -fin[0];
129     break;
130   case 2:
131     f[0 * ldx] = PETSC_i * fin[2];
132     f[1 * ldx] = -PETSC_i * fin[3];
133     f[2 * ldx] = -PETSC_i * fin[0];
134     f[3 * ldx] = PETSC_i * fin[1];
135     break;
136   case 3:
137     f[0 * ldx] = fin[2];
138     f[1 * ldx] = fin[3];
139     f[2 * ldx] = fin[0];
140     f[3 * ldx] = fin[1];
141     break;
142   default:
143     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Direction for gamma %" PetscInt_FMT " not in [0, 4)", d);
144   }
145   PetscFunctionReturn(PETSC_SUCCESS);
146 }
147 
148 // Apply (1 \pm \gamma_\mu)/2
149 static PetscErrorCode ComputeGammaFactor(PetscInt d, PetscBool forward, PetscInt ldx, PetscScalar f[])
150 {
151   const PetscReal   sign   = forward ? -1. : 1.;
152   const PetscScalar fin[4] = {f[0 * ldx], f[1 * ldx], f[2 * ldx], f[3 * ldx]};
153 
154   PetscFunctionBeginHot;
155   switch (d) {
156   case 0:
157     f[0 * ldx] += sign * PETSC_i * fin[3];
158     f[1 * ldx] += sign * PETSC_i * fin[2];
159     f[2 * ldx] += sign * -PETSC_i * fin[1];
160     f[3 * ldx] += sign * -PETSC_i * fin[0];
161     break;
162   case 1:
163     f[0 * ldx] += -sign * fin[3];
164     f[1 * ldx] += sign * fin[2];
165     f[2 * ldx] += sign * fin[1];
166     f[3 * ldx] += -sign * fin[0];
167     break;
168   case 2:
169     f[0 * ldx] += sign * PETSC_i * fin[2];
170     f[1 * ldx] += sign * -PETSC_i * fin[3];
171     f[2 * ldx] += sign * -PETSC_i * fin[0];
172     f[3 * ldx] += sign * PETSC_i * fin[1];
173     break;
174   case 3:
175     f[0 * ldx] += sign * fin[2];
176     f[1 * ldx] += sign * fin[3];
177     f[2 * ldx] += sign * fin[0];
178     f[3 * ldx] += sign * fin[1];
179     break;
180   default:
181     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Direction for gamma %" PetscInt_FMT " not in [0, 4)", d);
182   }
183   f[0 * ldx] *= 0.5;
184   f[1 * ldx] *= 0.5;
185   f[2 * ldx] *= 0.5;
186   f[3 * ldx] *= 0.5;
187   PetscFunctionReturn(PETSC_SUCCESS);
188 }
189 
190 #include <petsc/private/dmpleximpl.h>
191 
192 // ComputeAction() sums the action of 1/2 (1 \pm \gamma_\mu) U \psi into f
193 static PetscErrorCode ComputeAction(PetscInt d, PetscBool forward, const PetscScalar U[], const PetscScalar psi[], PetscScalar f[])
194 {
195   PetscScalar tmp[12];
196 
197   PetscFunctionBeginHot;
198   // Apply U
199   for (PetscInt beta = 0; beta < 4; ++beta) {
200     if (forward) DMPlex_Mult3D_Internal(U, 1, &psi[beta * 3], &tmp[beta * 3]);
201     else DMPlex_MultTranspose3D_Internal(U, 1, &psi[beta * 3], &tmp[beta * 3]);
202   }
203   // Apply (1 \pm \gamma_\mu)/2 to each color
204   for (PetscInt c = 0; c < 3; ++c) PetscCall(ComputeGammaFactor(d, forward, 3, &tmp[c]));
205   // Note that we are subtracting this contribution
206   for (PetscInt i = 0; i < 12; ++i) f[i] -= tmp[i];
207   PetscFunctionReturn(PETSC_SUCCESS);
208 }
209 
210 /*
211   The assembly loop runs over vertices. Each vertex has 2d edges in its support. The edges are ordered first by the dimension along which they run, and second from smaller to larger index, expect for edges which loop periodically. The vertices on edges are also ordered from smaller to larger index except for periodic edges.
212 */
213 static PetscErrorCode ComputeResidualLocal(DM dm, Vec u, Vec f)
214 {
215   DM                 dmAux;
216   Vec                gauge;
217   PetscSection       s, sGauge;
218   const PetscScalar *ua;
219   PetscScalar       *fa, *link;
220   PetscInt           dim, vStart, vEnd;
221 
222   PetscFunctionBeginUser;
223   PetscCall(DMGetDimension(dm, &dim));
224   PetscCall(DMGetLocalSection(dm, &s));
225   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
226   PetscCall(VecGetArrayRead(u, &ua));
227   PetscCall(VecGetArray(f, &fa));
228 
229   PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &gauge));
230   PetscCall(VecGetDM(gauge, &dmAux));
231   PetscCall(DMGetLocalSection(dmAux, &sGauge));
232   PetscCall(VecGetArray(gauge, &link));
233   // Loop over y
234   for (PetscInt v = vStart; v < vEnd; ++v) {
235     const PetscInt *supp;
236     PetscInt        xdof, xoff;
237 
238     PetscCall(DMPlexGetSupport(dm, v, &supp));
239     PetscCall(PetscSectionGetDof(s, v, &xdof));
240     PetscCall(PetscSectionGetOffset(s, v, &xoff));
241     // Diagonal
242     for (PetscInt i = 0; i < xdof; ++i) fa[xoff + i] += (M + 4) * ua[xoff + i];
243     // Loop over mu
244     for (PetscInt d = 0; d < dim; ++d) {
245       const PetscInt *cone;
246       PetscInt        yoff, goff;
247 
248       // Left action -(1 + \gamma_\mu)/2 \otimes U^\dagger_\mu(y) \delta_{x - \mu,y} \psi(y)
249       PetscCall(DMPlexGetCone(dm, supp[2 * d + 0], &cone));
250       PetscCall(PetscSectionGetOffset(s, cone[0], &yoff));
251       PetscCall(PetscSectionGetOffset(sGauge, supp[2 * d + 0], &goff));
252       PetscCall(ComputeAction(d, PETSC_FALSE, &link[goff], &ua[yoff], &fa[xoff]));
253       // Right edge -(1 - \gamma_\mu)/2 \otimes U_\mu(x) \delta_{x + \mu,y} \psi(y)
254       PetscCall(DMPlexGetCone(dm, supp[2 * d + 1], &cone));
255       PetscCall(PetscSectionGetOffset(s, cone[1], &yoff));
256       PetscCall(PetscSectionGetOffset(sGauge, supp[2 * d + 1], &goff));
257       PetscCall(ComputeAction(d, PETSC_TRUE, &link[goff], &ua[yoff], &fa[xoff]));
258     }
259   }
260   PetscCall(VecRestoreArray(f, &fa));
261   PetscCall(VecRestoreArray(gauge, &link));
262   PetscCall(VecRestoreArrayRead(u, &ua));
263   PetscFunctionReturn(PETSC_SUCCESS);
264 }
265 
266 static PetscErrorCode ComputeResidual(DM dm, Vec u, Vec f)
267 {
268   Vec lu, lf;
269 
270   PetscFunctionBegin;
271   PetscCall(DMGetLocalVector(dm, &lu));
272   PetscCall(DMGetLocalVector(dm, &lf));
273   PetscCall(DMGlobalToLocalBegin(dm, u, INSERT_VALUES, lu));
274   PetscCall(DMGlobalToLocalEnd(dm, u, INSERT_VALUES, lu));
275   PetscCall(VecSet(lf, 0.));
276   PetscCall(ComputeResidualLocal(dm, lu, lf));
277   PetscCall(DMLocalToGlobalBegin(dm, lf, INSERT_VALUES, f));
278   PetscCall(DMLocalToGlobalEnd(dm, lf, INSERT_VALUES, f));
279   PetscCall(DMRestoreLocalVector(dm, &lu));
280   PetscCall(DMRestoreLocalVector(dm, &lf));
281   PetscFunctionReturn(PETSC_SUCCESS);
282 }
283 
284 static PetscErrorCode CreateJacobian(DM dm, Mat *J)
285 {
286   PetscFunctionBegin;
287   PetscFunctionReturn(PETSC_SUCCESS);
288 }
289 
290 static PetscErrorCode ComputeJacobian(DM dm, Vec u, Mat J)
291 {
292   PetscFunctionBegin;
293   PetscFunctionReturn(PETSC_SUCCESS);
294 }
295 
296 static PetscErrorCode PrintTraversal(DM dm)
297 {
298   MPI_Comm comm;
299   PetscInt vStart, vEnd;
300 
301   PetscFunctionBeginUser;
302   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
303   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
304   for (PetscInt v = vStart; v < vEnd; ++v) {
305     const PetscInt *supp;
306     PetscInt        Ns;
307 
308     PetscCall(DMPlexGetSupportSize(dm, v, &Ns));
309     PetscCall(DMPlexGetSupport(dm, v, &supp));
310     PetscCall(PrintVertex(dm, v));
311     PetscCall(PetscPrintf(comm, "\n"));
312     for (PetscInt s = 0; s < Ns; ++s) {
313       const PetscInt *cone;
314 
315       PetscCall(DMPlexGetCone(dm, supp[s], &cone));
316       PetscCall(PetscPrintf(comm, "  Edge %" PetscInt_FMT ": ", supp[s]));
317       PetscCall(PrintVertex(dm, cone[0]));
318       PetscCall(PetscPrintf(comm, " -- "));
319       PetscCall(PrintVertex(dm, cone[1]));
320       PetscCall(PetscPrintf(comm, "\n"));
321     }
322   }
323   PetscFunctionReturn(PETSC_SUCCESS);
324 }
325 
326 static PetscErrorCode ComputeFFT(Mat FT, PetscInt Nc, Vec x, Vec p)
327 {
328   Vec     *xComp, *pComp, xTmp, pTmp;
329   PetscInt n, N;
330 
331   PetscFunctionBeginUser;
332   PetscCall(PetscMalloc2(Nc, &xComp, Nc, &pComp));
333   PetscCall(VecGetLocalSize(x, &n));
334   PetscCall(VecGetSize(x, &N));
335   for (PetscInt i = 0; i < Nc; ++i) {
336     const char *vtype;
337 
338     // HACK: Make these from another DM up front
339     PetscCall(VecCreate(PetscObjectComm((PetscObject)x), &xComp[i]));
340     PetscCall(VecGetType(x, &vtype));
341     PetscCall(VecSetType(xComp[i], vtype));
342     PetscCall(VecSetSizes(xComp[i], n / Nc, N / Nc));
343     PetscCall(VecDuplicate(xComp[i], &pComp[i]));
344   }
345   PetscCall(MatCreateVecsFFTW(FT, &xTmp, &pTmp, NULL));
346   PetscCall(VecStrideGatherAll(x, xComp, INSERT_VALUES));
347   for (PetscInt i = 0; i < Nc; ++i) {
348     PetscCall(VecScatterPetscToFFTW(FT, xComp[i], xTmp));
349     PetscCall(MatMult(FT, xTmp, pTmp));
350     PetscCall(VecScatterFFTWToPetsc(FT, pTmp, pComp[i]));
351   }
352   PetscCall(VecStrideScatterAll(pComp, p, INSERT_VALUES));
353   for (PetscInt i = 0; i < Nc; ++i) {
354     PetscCall(VecDestroy(&xComp[i]));
355     PetscCall(VecDestroy(&pComp[i]));
356   }
357   PetscCall(VecDestroy(&xTmp));
358   PetscCall(VecDestroy(&pTmp));
359   PetscCall(PetscFree2(xComp, pComp));
360   PetscFunctionReturn(PETSC_SUCCESS);
361 }
362 
363 // Sets each link to be the identity for the free field test
364 static PetscErrorCode SetGauge_Identity(DM dm)
365 {
366   DM           auxDM;
367   Vec          auxVec;
368   PetscSection s;
369   PetscScalar  id[9] = {1., 0., 0., 0., 1., 0., 0., 0., 1.};
370   PetscInt     eStart, eEnd;
371 
372   PetscFunctionBegin;
373   PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &auxVec));
374   PetscCall(VecGetDM(auxVec, &auxDM));
375   PetscCall(DMGetLocalSection(auxDM, &s));
376   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
377   for (PetscInt i = eStart; i < eEnd; ++i) PetscCall(VecSetValuesSection(auxVec, s, i, id, INSERT_VALUES));
378   PetscCall(VecViewFromOptions(auxVec, NULL, "-gauge_view"));
379   PetscFunctionReturn(PETSC_SUCCESS);
380 }
381 
382 /*
383   Test the action of the Wilson operator in the free field case U = I,
384 
385     \eta(x) = D_W(x - y) \psi(y)
386 
387   The Wilson operator is a convolution for the free field, so we can check that by the convolution theorem
388 
389     \hat\eta(x) = \mathcal{F}(D_W(x - y) \psi(y))
390                 = \hat D_W(p) \mathcal{F}\psi(p)
391 
392   The Fourier series for the Wilson operator is
393 
394     M + \sum_\mu 2 \sin^2(p_\mu / 2) + i \gamma_\mu \sin(p_\mu)
395 */
396 static PetscErrorCode TestFreeField(DM dm)
397 {
398   PetscSection       s;
399   Mat                FT;
400   Vec                psi, psiHat;
401   Vec                eta, etaHat;
402   Vec                DHat; // The product \hat D_w \hat psi
403   PetscRandom        r;
404   const PetscScalar *psih;
405   PetscScalar       *dh;
406   PetscReal         *coef, nrm;
407   const PetscInt    *extent, Nc = 12;
408   PetscInt           dim, V     = 1, vStart, vEnd;
409   PetscContainer     c;
410   PetscBool          constRhs = PETSC_FALSE;
411   PetscMPIInt        size;
412 
413   PetscFunctionBeginUser;
414   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
415   if (size > 1) PetscFunctionReturn(PETSC_SUCCESS);
416   PetscCall(PetscOptionsGetBool(NULL, NULL, "-const_rhs", &constRhs, NULL));
417 
418   PetscCall(SetGauge_Identity(dm));
419   PetscCall(DMGetLocalSection(dm, &s));
420   PetscCall(DMGetGlobalVector(dm, &psi));
421   PetscCall(PetscObjectSetName((PetscObject)psi, "psi"));
422   PetscCall(DMGetGlobalVector(dm, &psiHat));
423   PetscCall(PetscObjectSetName((PetscObject)psiHat, "psihat"));
424   PetscCall(DMGetGlobalVector(dm, &eta));
425   PetscCall(PetscObjectSetName((PetscObject)eta, "eta"));
426   PetscCall(DMGetGlobalVector(dm, &etaHat));
427   PetscCall(PetscObjectSetName((PetscObject)etaHat, "etahat"));
428   PetscCall(DMGetGlobalVector(dm, &DHat));
429   PetscCall(PetscObjectSetName((PetscObject)DHat, "Dhat"));
430   PetscCall(PetscRandomCreate(PETSC_COMM_SELF, &r));
431   PetscCall(PetscRandomSetType(r, PETSCRAND48));
432   if (constRhs) PetscCall(VecSet(psi, 1.));
433   else PetscCall(VecSetRandom(psi, r));
434   PetscCall(PetscRandomDestroy(&r));
435 
436   PetscCall(DMGetDimension(dm, &dim));
437   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
438   PetscCall(PetscObjectQuery((PetscObject)dm, "_extent", (PetscObject *)&c));
439   PetscCall(PetscContainerGetPointer(c, (void **)&extent));
440   PetscCall(MatCreateFFT(PetscObjectComm((PetscObject)dm), dim, extent, MATFFTW, &FT));
441 
442   PetscCall(PetscMalloc1(dim, &coef));
443   for (PetscInt d = 0; d < dim; ++d) {
444     coef[d] = 2. * PETSC_PI / extent[d];
445     V *= extent[d];
446   }
447   PetscCall(ComputeResidual(dm, psi, eta));
448   PetscCall(VecViewFromOptions(psi, NULL, "-psi_view"));
449   PetscCall(VecViewFromOptions(eta, NULL, "-eta_view"));
450   PetscCall(ComputeFFT(FT, Nc, psi, psiHat));
451   PetscCall(VecScale(psiHat, 1. / V));
452   PetscCall(ComputeFFT(FT, Nc, eta, etaHat));
453   PetscCall(VecScale(etaHat, 1. / V));
454   PetscCall(VecGetArrayRead(psiHat, &psih));
455   PetscCall(VecGetArray(DHat, &dh));
456   for (PetscInt v = vStart; v < vEnd; ++v) {
457     PetscScalar tmp[12], tmp1 = 0.;
458     PetscInt    dof, off;
459 
460     PetscCall(PetscSectionGetDof(s, v, &dof));
461     PetscCall(PetscSectionGetOffset(s, v, &off));
462     for (PetscInt d = 0, prod = 1; d < dim; prod *= extent[d], ++d) {
463       const PetscInt idx = (v / prod) % extent[d];
464 
465       tmp1 += 2. * PetscSqr(PetscSinReal(0.5 * coef[d] * idx));
466       for (PetscInt i = 0; i < dof; ++i) tmp[i] = PETSC_i * PetscSinReal(coef[d] * idx) * psih[off + i];
467       for (PetscInt c = 0; c < 3; ++c) PetscCall(ComputeGamma(d, 3, &tmp[c]));
468       for (PetscInt i = 0; i < dof; ++i) dh[off + i] += tmp[i];
469     }
470     for (PetscInt i = 0; i < dof; ++i) dh[off + i] += (M + tmp1) * psih[off + i];
471   }
472   PetscCall(VecRestoreArrayRead(psiHat, &psih));
473   PetscCall(VecRestoreArray(DHat, &dh));
474 
475   {
476     Vec     *etaComp, *DComp;
477     PetscInt n, N;
478 
479     PetscCall(PetscMalloc2(Nc, &etaComp, Nc, &DComp));
480     PetscCall(VecGetLocalSize(etaHat, &n));
481     PetscCall(VecGetSize(etaHat, &N));
482     for (PetscInt i = 0; i < Nc; ++i) {
483       const char *vtype;
484 
485       // HACK: Make these from another DM up front
486       PetscCall(VecCreate(PetscObjectComm((PetscObject)etaHat), &etaComp[i]));
487       PetscCall(VecGetType(etaHat, &vtype));
488       PetscCall(VecSetType(etaComp[i], vtype));
489       PetscCall(VecSetSizes(etaComp[i], n / Nc, N / Nc));
490       PetscCall(VecDuplicate(etaComp[i], &DComp[i]));
491     }
492     PetscCall(VecStrideGatherAll(etaHat, etaComp, INSERT_VALUES));
493     PetscCall(VecStrideGatherAll(DHat, DComp, INSERT_VALUES));
494     for (PetscInt i = 0; i < Nc; ++i) {
495       if (!i) {
496         PetscCall(VecViewFromOptions(etaComp[i], NULL, "-etahat_view"));
497         PetscCall(VecViewFromOptions(DComp[i], NULL, "-dhat_view"));
498       }
499       PetscCall(VecAXPY(etaComp[i], -1., DComp[i]));
500       PetscCall(VecNorm(etaComp[i], NORM_INFINITY, &nrm));
501       PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Slice %" PetscInt_FMT ": %g\n", i, (double)nrm));
502     }
503     PetscCall(VecStrideScatterAll(etaComp, etaHat, INSERT_VALUES));
504     for (PetscInt i = 0; i < Nc; ++i) {
505       PetscCall(VecDestroy(&etaComp[i]));
506       PetscCall(VecDestroy(&DComp[i]));
507     }
508     PetscCall(PetscFree2(etaComp, DComp));
509     PetscCall(VecNorm(etaHat, NORM_INFINITY, &nrm));
510     PetscCheck(nrm < PETSC_SMALL, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Free field test failed: %g", (double)nrm);
511   }
512 
513   PetscCall(PetscFree(coef));
514   PetscCall(MatDestroy(&FT));
515   PetscCall(DMRestoreGlobalVector(dm, &psi));
516   PetscCall(DMRestoreGlobalVector(dm, &psiHat));
517   PetscCall(DMRestoreGlobalVector(dm, &eta));
518   PetscCall(DMRestoreGlobalVector(dm, &etaHat));
519   PetscCall(DMRestoreGlobalVector(dm, &DHat));
520   PetscFunctionReturn(PETSC_SUCCESS);
521 }
522 
523 int main(int argc, char **argv)
524 {
525   DM     dm;
526   Vec    u, f;
527   Mat    J;
528   AppCtx user;
529 
530   PetscCall(PetscInitialize(&argc, &argv, NULL, help));
531   PetscCall(ProcessOptions(PETSC_COMM_WORLD, &user));
532   PetscCall(CreateMesh(PETSC_COMM_WORLD, &user, &dm));
533   PetscCall(SetupDiscretization(dm, &user));
534   PetscCall(SetupAuxDiscretization(dm, &user));
535   PetscCall(DMCreateGlobalVector(dm, &u));
536   PetscCall(DMCreateGlobalVector(dm, &f));
537   if (user.printTr) PetscCall(PrintTraversal(dm));
538   PetscCall(ComputeResidual(dm, u, f));
539   PetscCall(VecViewFromOptions(f, NULL, "-res_view"));
540   PetscCall(CreateJacobian(dm, &J));
541   PetscCall(ComputeJacobian(dm, u, J));
542   PetscCall(VecViewFromOptions(u, NULL, "-sol_view"));
543   PetscCall(TestFreeField(dm));
544   PetscCall(VecDestroy(&f));
545   PetscCall(VecDestroy(&u));
546   PetscCall(DMDestroy(&dm));
547   PetscCall(PetscFinalize());
548   return 0;
549 }
550 
551 /*TEST
552 
553   build:
554     requires: complex
555 
556   test:
557     requires: fftw
558     suffix: dirac_free_field
559     args: -dm_plex_dim 4 -dm_plex_shape hypercubic -dm_plex_box_faces 3,3,3,3 -dm_view -sol_view \
560           -dm_plex_check_symmetry -dm_plex_check_skeleton -dm_plex_check_faces -dm_plex_check_pointsf
561 
562   # Problem on rank 3 on MacOSX
563   test:
564     requires: fftw
565     suffix: dirac_free_field_par
566     nsize: 16
567     args: -dm_plex_dim 4 -dm_plex_shape hypercubic -dm_plex_box_faces 4,4,4,4 -dm_view -sol_view \
568           -dm_plex_check_symmetry -dm_plex_check_skeleton -dm_plex_check_faces -dm_plex_check_pointsf
569 
570 TEST*/
571