xref: /petsc/src/dm/dt/fe/impls/basic/febasic.c (revision 390d3996fed1e5c2d89175ebb68cf53cdee176f4)
1 #include <petsc/private/petscfeimpl.h> /*I "petscfe.h" I*/
2 #include <petscblaslapack.h>
3 
4 static PetscErrorCode PetscFEDestroy_Basic(PetscFE fem)
5 {
6   PetscFE_Basic *b = (PetscFE_Basic *)fem->data;
7 
8   PetscFunctionBegin;
9   PetscCall(PetscFree(b));
10   PetscFunctionReturn(PETSC_SUCCESS);
11 }
12 
13 static PetscErrorCode PetscFEView_Basic_Ascii(PetscFE fe, PetscViewer v)
14 {
15   PetscInt        dim, Nc;
16   PetscSpace      basis = NULL;
17   PetscDualSpace  dual  = NULL;
18   PetscQuadrature quad  = NULL;
19 
20   PetscFunctionBegin;
21   PetscCall(PetscFEGetSpatialDimension(fe, &dim));
22   PetscCall(PetscFEGetNumComponents(fe, &Nc));
23   PetscCall(PetscFEGetBasisSpace(fe, &basis));
24   PetscCall(PetscFEGetDualSpace(fe, &dual));
25   PetscCall(PetscFEGetQuadrature(fe, &quad));
26   PetscCall(PetscViewerASCIIPushTab(v));
27   PetscCall(PetscViewerASCIIPrintf(v, "Basic Finite Element in %" PetscInt_FMT " dimensions with %" PetscInt_FMT " components\n", dim, Nc));
28   if (basis) PetscCall(PetscSpaceView(basis, v));
29   if (dual) PetscCall(PetscDualSpaceView(dual, v));
30   if (quad) PetscCall(PetscQuadratureView(quad, v));
31   PetscCall(PetscViewerASCIIPopTab(v));
32   PetscFunctionReturn(PETSC_SUCCESS);
33 }
34 
35 static PetscErrorCode PetscFEView_Basic(PetscFE fe, PetscViewer v)
36 {
37   PetscBool iascii;
38 
39   PetscFunctionBegin;
40   PetscCall(PetscObjectTypeCompare((PetscObject)v, PETSCVIEWERASCII, &iascii));
41   if (iascii) PetscCall(PetscFEView_Basic_Ascii(fe, v));
42   PetscFunctionReturn(PETSC_SUCCESS);
43 }
44 
45 /* Construct the change of basis from prime basis to nodal basis */
46 PETSC_INTERN PetscErrorCode PetscFESetUp_Basic(PetscFE fem)
47 {
48   PetscReal    *work;
49   PetscBLASInt *pivots;
50   PetscBLASInt  n, info;
51   PetscInt      pdim, j;
52 
53   PetscFunctionBegin;
54   PetscCall(PetscDualSpaceGetDimension(fem->dualSpace, &pdim));
55   PetscCall(PetscMalloc1(pdim * pdim, &fem->invV));
56   for (j = 0; j < pdim; ++j) {
57     PetscReal       *Bf;
58     PetscQuadrature  f;
59     const PetscReal *points, *weights;
60     PetscInt         Nc, Nq, q, k, c;
61 
62     PetscCall(PetscDualSpaceGetFunctional(fem->dualSpace, j, &f));
63     PetscCall(PetscQuadratureGetData(f, NULL, &Nc, &Nq, &points, &weights));
64     PetscCall(PetscMalloc1(Nc * Nq * pdim, &Bf));
65     PetscCall(PetscSpaceEvaluate(fem->basisSpace, Nq, points, Bf, NULL, NULL));
66     for (k = 0; k < pdim; ++k) {
67       /* V_{jk} = n_j(\phi_k) = \int \phi_k(x) n_j(x) dx */
68       fem->invV[j * pdim + k] = 0.0;
69 
70       for (q = 0; q < Nq; ++q) {
71         for (c = 0; c < Nc; ++c) fem->invV[j * pdim + k] += Bf[(q * pdim + k) * Nc + c] * weights[q * Nc + c];
72       }
73     }
74     PetscCall(PetscFree(Bf));
75   }
76 
77   PetscCall(PetscMalloc2(pdim, &pivots, pdim, &work));
78   PetscCall(PetscBLASIntCast(pdim, &n));
79   PetscCallBLAS("LAPACKgetrf", LAPACKREALgetrf_(&n, &n, fem->invV, &n, pivots, &info));
80   PetscCheck(!info, PETSC_COMM_SELF, PETSC_ERR_LIB, "Error returned from LAPACKgetrf %" PetscBLASInt_FMT, info);
81   PetscCallBLAS("LAPACKgetri", LAPACKREALgetri_(&n, fem->invV, &n, pivots, work, &n, &info));
82   PetscCheck(!info, PETSC_COMM_SELF, PETSC_ERR_LIB, "Error returned from LAPACKgetri %" PetscBLASInt_FMT, info);
83   PetscCall(PetscFree2(pivots, work));
84   PetscFunctionReturn(PETSC_SUCCESS);
85 }
86 
87 PetscErrorCode PetscFEGetDimension_Basic(PetscFE fem, PetscInt *dim)
88 {
89   PetscFunctionBegin;
90   PetscCall(PetscDualSpaceGetDimension(fem->dualSpace, dim));
91   PetscFunctionReturn(PETSC_SUCCESS);
92 }
93 
94 /* Tensor contraction on the middle index,
95  *    C[m,n,p] = A[m,k,p] * B[k,n]
96  * where all matrices use C-style ordering.
97  */
98 static PetscErrorCode TensorContract_Private(PetscInt m, PetscInt n, PetscInt p, PetscInt k, const PetscReal *A, const PetscReal *B, PetscReal *C)
99 {
100   PetscInt i;
101 
102   PetscFunctionBegin;
103   PetscCheck(n && p, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Empty tensor is not allowed %" PetscInt_FMT " %" PetscInt_FMT, n, p);
104   for (i = 0; i < m; i++) {
105     PetscBLASInt n_, p_, k_, lda, ldb, ldc;
106     PetscReal    one = 1, zero = 0;
107     /* Taking contiguous submatrices, we wish to comput c[n,p] = a[k,p] * B[k,n]
108      * or, in Fortran ordering, c(p,n) = a(p,k) * B(n,k)
109      */
110     PetscCall(PetscBLASIntCast(n, &n_));
111     PetscCall(PetscBLASIntCast(p, &p_));
112     PetscCall(PetscBLASIntCast(k, &k_));
113     lda = p_;
114     ldb = n_;
115     ldc = p_;
116     PetscCallBLAS("BLASgemm", BLASREALgemm_("N", "T", &p_, &n_, &k_, &one, A + i * k * p, &lda, B, &ldb, &zero, C + i * n * p, &ldc));
117   }
118   PetscCall(PetscLogFlops(2. * m * n * p * k));
119   PetscFunctionReturn(PETSC_SUCCESS);
120 }
121 
122 PETSC_INTERN PetscErrorCode PetscFEComputeTabulation_Basic(PetscFE fem, PetscInt npoints, const PetscReal points[], PetscInt K, PetscTabulation T)
123 {
124   DM         dm;
125   PetscInt   pdim; /* Dimension of FE space P */
126   PetscInt   dim;  /* Spatial dimension */
127   PetscInt   Nc;   /* Field components */
128   PetscReal *B    = K >= 0 ? T->T[0] : NULL;
129   PetscReal *D    = K >= 1 ? T->T[1] : NULL;
130   PetscReal *H    = K >= 2 ? T->T[2] : NULL;
131   PetscReal *tmpB = NULL, *tmpD = NULL, *tmpH = NULL;
132 
133   PetscFunctionBegin;
134   PetscCall(PetscDualSpaceGetDM(fem->dualSpace, &dm));
135   PetscCall(DMGetDimension(dm, &dim));
136   PetscCall(PetscDualSpaceGetDimension(fem->dualSpace, &pdim));
137   PetscCall(PetscFEGetNumComponents(fem, &Nc));
138   /* Evaluate the prime basis functions at all points */
139   if (K >= 0) PetscCall(DMGetWorkArray(dm, npoints * pdim * Nc, MPIU_REAL, &tmpB));
140   if (K >= 1) PetscCall(DMGetWorkArray(dm, npoints * pdim * Nc * dim, MPIU_REAL, &tmpD));
141   if (K >= 2) PetscCall(DMGetWorkArray(dm, npoints * pdim * Nc * dim * dim, MPIU_REAL, &tmpH));
142   PetscCall(PetscSpaceEvaluate(fem->basisSpace, npoints, points, tmpB, tmpD, tmpH));
143   /* Translate from prime to nodal basis */
144   if (B) {
145     /* B[npoints, nodes, Nc] = tmpB[npoints, prime, Nc] * invV[prime, nodes] */
146     PetscCall(TensorContract_Private(npoints, pdim, Nc, pdim, tmpB, fem->invV, B));
147   }
148   if (D && dim) {
149     /* D[npoints, nodes, Nc, dim] = tmpD[npoints, prime, Nc, dim] * invV[prime, nodes] */
150     PetscCall(TensorContract_Private(npoints, pdim, Nc * dim, pdim, tmpD, fem->invV, D));
151   }
152   if (H && dim) {
153     /* H[npoints, nodes, Nc, dim, dim] = tmpH[npoints, prime, Nc, dim, dim] * invV[prime, nodes] */
154     PetscCall(TensorContract_Private(npoints, pdim, Nc * dim * dim, pdim, tmpH, fem->invV, H));
155   }
156   if (K >= 0) PetscCall(DMRestoreWorkArray(dm, npoints * pdim * Nc, MPIU_REAL, &tmpB));
157   if (K >= 1) PetscCall(DMRestoreWorkArray(dm, npoints * pdim * Nc * dim, MPIU_REAL, &tmpD));
158   if (K >= 2) PetscCall(DMRestoreWorkArray(dm, npoints * pdim * Nc * dim * dim, MPIU_REAL, &tmpH));
159   PetscFunctionReturn(PETSC_SUCCESS);
160 }
161 
162 PETSC_INTERN PetscErrorCode PetscFEIntegrate_Basic(PetscDS ds, PetscInt field, PetscInt Ne, PetscFEGeom *cgeom, const PetscScalar coefficients[], PetscDS dsAux, const PetscScalar coefficientsAux[], PetscScalar integral[])
163 {
164   const PetscInt     debug = ds->printIntegrate;
165   PetscFE            fe;
166   PetscPointFn      *obj_func;
167   PetscQuadrature    quad;
168   PetscTabulation   *T, *TAux = NULL;
169   PetscScalar       *u, *u_x, *a, *a_x;
170   const PetscScalar *constants;
171   PetscReal         *x, cellScale;
172   PetscInt          *uOff, *uOff_x, *aOff = NULL, *aOff_x = NULL;
173   PetscInt           dim, dE, Np, numConstants, Nf, NfAux = 0, totDim, totDimAux = 0, cOffset = 0, cOffsetAux = 0, e;
174   PetscBool          isAffine;
175   const PetscReal   *quadPoints, *quadWeights;
176   PetscInt           qNc, Nq, q;
177 
178   PetscFunctionBegin;
179   PetscCall(PetscDSGetObjective(ds, field, &obj_func));
180   if (!obj_func) PetscFunctionReturn(PETSC_SUCCESS);
181   PetscCall(PetscDSGetDiscretization(ds, field, (PetscObject *)&fe));
182   PetscCall(PetscFEGetSpatialDimension(fe, &dim));
183   cellScale = (PetscReal)PetscPowInt(2, dim);
184   PetscCall(PetscFEGetQuadrature(fe, &quad));
185   PetscCall(PetscDSGetNumFields(ds, &Nf));
186   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
187   PetscCall(PetscDSGetComponentOffsets(ds, &uOff));
188   PetscCall(PetscDSGetComponentDerivativeOffsets(ds, &uOff_x));
189   PetscCall(PetscDSGetTabulation(ds, &T));
190   PetscCall(PetscDSGetEvaluationArrays(ds, &u, NULL, &u_x));
191   PetscCall(PetscDSGetWorkspace(ds, &x, NULL, NULL, NULL, NULL));
192   PetscCall(PetscDSSetIntegrationParameters(ds, field, PETSC_DETERMINE));
193   PetscCall(PetscDSGetConstants(ds, &numConstants, &constants));
194   if (dsAux) {
195     PetscCall(PetscDSGetNumFields(dsAux, &NfAux));
196     PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
197     PetscCall(PetscDSGetComponentOffsets(dsAux, &aOff));
198     PetscCall(PetscDSGetComponentDerivativeOffsets(dsAux, &aOff_x));
199     PetscCall(PetscDSGetTabulation(dsAux, &TAux));
200     PetscCall(PetscDSGetEvaluationArrays(dsAux, &a, NULL, &a_x));
201     PetscCheck(T[0]->Np == TAux[0]->Np, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of tabulation points %" PetscInt_FMT " != %" PetscInt_FMT " number of auxiliary tabulation points", T[0]->Np, TAux[0]->Np);
202   }
203   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
204   PetscCheck(qNc == 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Only supports scalar quadrature, not %" PetscInt_FMT " components", qNc);
205   Np       = cgeom->numPoints;
206   dE       = cgeom->dimEmbed;
207   isAffine = cgeom->isAffine;
208   for (e = 0; e < Ne; ++e) {
209     PetscFEGeom fegeom;
210 
211     fegeom.dim      = cgeom->dim;
212     fegeom.dimEmbed = cgeom->dimEmbed;
213     if (isAffine) {
214       fegeom.v    = x;
215       fegeom.xi   = cgeom->xi;
216       fegeom.J    = &cgeom->J[e * Np * dE * dE];
217       fegeom.invJ = &cgeom->invJ[e * Np * dE * dE];
218       fegeom.detJ = &cgeom->detJ[e * Np];
219     }
220     for (q = 0; q < Nq; ++q) {
221       PetscScalar integrand = 0.;
222       PetscReal   w;
223 
224       if (isAffine) {
225         CoordinatesRefToReal(dE, dim, fegeom.xi, &cgeom->v[e * Np * dE], fegeom.J, &quadPoints[q * dim], x);
226       } else {
227         fegeom.v    = &cgeom->v[(e * Np + q) * dE];
228         fegeom.J    = &cgeom->J[(e * Np + q) * dE * dE];
229         fegeom.invJ = &cgeom->invJ[(e * Np + q) * dE * dE];
230         fegeom.detJ = &cgeom->detJ[e * Np + q];
231       }
232       PetscCall(PetscDSSetCellParameters(ds, fegeom.detJ[0] * cellScale));
233       w = fegeom.detJ[0] * quadWeights[q];
234       if (debug > 1 && q < Np) {
235         PetscCall(PetscPrintf(PETSC_COMM_SELF, "  detJ: %g\n", (double)fegeom.detJ[0]));
236 #if !defined(PETSC_USE_COMPLEX)
237         PetscCall(DMPrintCellMatrix(e, "invJ", dim, dim, fegeom.invJ));
238 #endif
239       }
240       if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  quad point %" PetscInt_FMT "\n", q));
241       PetscCall(PetscFEEvaluateFieldJets_Internal(ds, Nf, 0, q, T, &fegeom, &coefficients[cOffset], NULL, u, u_x, NULL));
242       if (dsAux) PetscCall(PetscFEEvaluateFieldJets_Internal(dsAux, NfAux, 0, q, TAux, &fegeom, &coefficientsAux[cOffsetAux], NULL, a, a_x, NULL));
243       obj_func(dim, Nf, NfAux, uOff, uOff_x, u, NULL, u_x, aOff, aOff_x, a, NULL, a_x, 0.0, fegeom.v, numConstants, constants, &integrand);
244       integrand *= w;
245       integral[e * Nf + field] += integrand;
246     }
247     if (debug > 1) PetscCall(PetscPrintf(PETSC_COMM_SELF, "    Element Field %" PetscInt_FMT " integral: %g\n", Nf, (double)PetscRealPart(integral[e * Nf + field])));
248     cOffset += totDim;
249     cOffsetAux += totDimAux;
250   }
251   PetscFunctionReturn(PETSC_SUCCESS);
252 }
253 
254 PETSC_INTERN PetscErrorCode PetscFEIntegrateBd_Basic(PetscDS ds, PetscInt field, PetscBdPointFn *obj_func, PetscInt Ne, PetscFEGeom *fgeom, const PetscScalar coefficients[], PetscDS dsAux, const PetscScalar coefficientsAux[], PetscScalar integral[])
255 {
256   const PetscInt     debug = ds->printIntegrate;
257   PetscFE            fe;
258   PetscQuadrature    quad;
259   PetscTabulation   *Tf, *TfAux = NULL;
260   PetscScalar       *u, *u_x, *a, *a_x, *basisReal, *basisDerReal;
261   const PetscScalar *constants;
262   PetscReal         *x, cellScale;
263   PetscInt          *uOff, *uOff_x, *aOff = NULL, *aOff_x = NULL;
264   PetscBool          isAffine, auxOnBd;
265   const PetscReal   *quadPoints, *quadWeights;
266   PetscInt           qNc, Nq, q, Np, dE;
267   PetscInt           dim, dimAux, numConstants, Nf, NfAux = 0, totDim, totDimAux = 0, cOffset = 0, cOffsetAux = 0, e;
268 
269   PetscFunctionBegin;
270   if (!obj_func) PetscFunctionReturn(PETSC_SUCCESS);
271   PetscCall(PetscDSGetDiscretization(ds, field, (PetscObject *)&fe));
272   PetscCall(PetscFEGetSpatialDimension(fe, &dim));
273   cellScale = (PetscReal)PetscPowInt(2, dim);
274   PetscCall(PetscFEGetFaceQuadrature(fe, &quad));
275   PetscCall(PetscDSGetNumFields(ds, &Nf));
276   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
277   PetscCall(PetscDSGetComponentOffsets(ds, &uOff));
278   PetscCall(PetscDSGetComponentDerivativeOffsets(ds, &uOff_x));
279   PetscCall(PetscDSGetEvaluationArrays(ds, &u, NULL, &u_x));
280   PetscCall(PetscDSGetWorkspace(ds, &x, &basisReal, &basisDerReal, NULL, NULL));
281   PetscCall(PetscDSGetFaceTabulation(ds, &Tf));
282   PetscCall(PetscDSSetIntegrationParameters(ds, field, PETSC_DETERMINE));
283   PetscCall(PetscDSGetConstants(ds, &numConstants, &constants));
284   if (dsAux) {
285     PetscCall(PetscDSGetSpatialDimension(dsAux, &dimAux));
286     PetscCall(PetscDSGetNumFields(dsAux, &NfAux));
287     PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
288     PetscCall(PetscDSGetComponentOffsets(dsAux, &aOff));
289     PetscCall(PetscDSGetComponentDerivativeOffsets(dsAux, &aOff_x));
290     PetscCall(PetscDSGetEvaluationArrays(dsAux, &a, NULL, &a_x));
291     auxOnBd = dimAux < dim ? PETSC_TRUE : PETSC_FALSE;
292     if (auxOnBd) PetscCall(PetscDSGetTabulation(dsAux, &TfAux));
293     else PetscCall(PetscDSGetFaceTabulation(dsAux, &TfAux));
294     PetscCheck(Tf[0]->Np == TfAux[0]->Np, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of tabulation points %" PetscInt_FMT " != %" PetscInt_FMT " number of auxiliary tabulation points", Tf[0]->Np, TfAux[0]->Np);
295   }
296   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
297   PetscCheck(qNc == 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Only supports scalar quadrature, not %" PetscInt_FMT " components", qNc);
298   if (debug > 1) PetscCall(PetscPrintf(PETSC_COMM_SELF, "Field: %" PetscInt_FMT " Nface: %" PetscInt_FMT " Nq: %" PetscInt_FMT "\n", field, Ne, Nq));
299   Np       = fgeom->numPoints;
300   dE       = fgeom->dimEmbed;
301   isAffine = fgeom->isAffine;
302   for (e = 0; e < Ne; ++e) {
303     PetscFEGeom    fegeom, cgeom;
304     const PetscInt face = fgeom->face[e][0]; /* Local face number in cell */
305     fegeom.n            = NULL;
306     fegeom.v            = NULL;
307     fegeom.J            = NULL;
308     fegeom.invJ         = NULL;
309     fegeom.detJ         = NULL;
310     fegeom.dim          = fgeom->dim;
311     fegeom.dimEmbed     = fgeom->dimEmbed;
312     cgeom.dim           = fgeom->dim;
313     cgeom.dimEmbed      = fgeom->dimEmbed;
314     if (isAffine) {
315       fegeom.v    = x;
316       fegeom.xi   = fgeom->xi;
317       fegeom.J    = &fgeom->J[e * Np * dE * dE];
318       fegeom.invJ = &fgeom->invJ[e * Np * dE * dE];
319       fegeom.detJ = &fgeom->detJ[e * Np];
320       fegeom.n    = &fgeom->n[e * Np * dE];
321 
322       cgeom.J    = &fgeom->suppJ[0][e * Np * dE * dE];
323       cgeom.invJ = &fgeom->suppInvJ[0][e * Np * dE * dE];
324       cgeom.detJ = &fgeom->suppDetJ[0][e * Np];
325     }
326     for (q = 0; q < Nq; ++q) {
327       PetscScalar integrand = 0.;
328       PetscReal   w;
329 
330       if (isAffine) {
331         CoordinatesRefToReal(dE, dim - 1, fegeom.xi, &fgeom->v[e * Np * dE], fegeom.J, &quadPoints[q * (dim - 1)], x);
332       } else {
333         fegeom.v    = &fgeom->v[(e * Np + q) * dE];
334         fegeom.J    = &fgeom->J[(e * Np + q) * dE * dE];
335         fegeom.invJ = &fgeom->invJ[(e * Np + q) * dE * dE];
336         fegeom.detJ = &fgeom->detJ[e * Np + q];
337         fegeom.n    = &fgeom->n[(e * Np + q) * dE];
338 
339         cgeom.J    = &fgeom->suppJ[0][(e * Np + q) * dE * dE];
340         cgeom.invJ = &fgeom->suppInvJ[0][(e * Np + q) * dE * dE];
341         cgeom.detJ = &fgeom->suppDetJ[0][e * Np + q];
342       }
343       PetscCall(PetscDSSetCellParameters(ds, fegeom.detJ[0] * cellScale));
344       w = fegeom.detJ[0] * quadWeights[q];
345       if (debug > 1 && q < Np) {
346         PetscCall(PetscPrintf(PETSC_COMM_SELF, "  detJ: %g\n", (double)fegeom.detJ[0]));
347 #ifndef PETSC_USE_COMPLEX
348         PetscCall(DMPrintCellMatrix(e, "invJ", dim, dim, fegeom.invJ));
349 #endif
350       }
351       if (debug > 1) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  quad point %" PetscInt_FMT "\n", q));
352       if (debug > 3) {
353         PetscCall(PetscPrintf(PETSC_COMM_SELF, "    x_q ("));
354         for (PetscInt d = 0; d < dE; ++d) {
355           if (d) PetscCall(PetscPrintf(PETSC_COMM_SELF, ", "));
356           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%g", (double)fegeom.v[d]));
357         }
358         PetscCall(PetscPrintf(PETSC_COMM_SELF, ")\n"));
359         PetscCall(PetscPrintf(PETSC_COMM_SELF, "    n_q ("));
360         for (PetscInt d = 0; d < dE; ++d) {
361           if (d) PetscCall(PetscPrintf(PETSC_COMM_SELF, ", "));
362           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%g", (double)fegeom.n[d]));
363         }
364         PetscCall(PetscPrintf(PETSC_COMM_SELF, ")\n"));
365         for (PetscInt f = 0; f < Nf; ++f) {
366           PetscCall(PetscPrintf(PETSC_COMM_SELF, "    u_%" PetscInt_FMT " (", f));
367           for (PetscInt c = 0; c < uOff[f + 1] - uOff[f]; ++c) {
368             if (c) PetscCall(PetscPrintf(PETSC_COMM_SELF, ", "));
369             PetscCall(PetscPrintf(PETSC_COMM_SELF, "%g", (double)PetscRealPart(u[uOff[f] + c])));
370           }
371           PetscCall(PetscPrintf(PETSC_COMM_SELF, ")\n"));
372         }
373       }
374       PetscCall(PetscFEEvaluateFieldJets_Internal(ds, Nf, face, q, Tf, &cgeom, &coefficients[cOffset], NULL, u, u_x, NULL));
375       if (dsAux) PetscCall(PetscFEEvaluateFieldJets_Internal(dsAux, NfAux, face, q, TfAux, &cgeom, &coefficientsAux[cOffsetAux], NULL, a, a_x, NULL));
376       obj_func(dim, Nf, NfAux, uOff, uOff_x, u, NULL, u_x, aOff, aOff_x, a, NULL, a_x, 0.0, fegeom.v, fegeom.n, numConstants, constants, &integrand);
377       integrand *= w;
378       integral[e * Nf + field] += integrand;
379       if (debug > 1) PetscCall(PetscPrintf(PETSC_COMM_SELF, "    int: %g tot: %g\n", (double)PetscRealPart(integrand), (double)PetscRealPart(integral[e * Nf + field])));
380     }
381     cOffset += totDim;
382     cOffsetAux += totDimAux;
383   }
384   PetscFunctionReturn(PETSC_SUCCESS);
385 }
386 
387 PetscErrorCode PetscFEIntegrateResidual_Basic(PetscDS ds, PetscFormKey key, PetscInt Ne, PetscFEGeom *cgeom, const PetscScalar coefficients[], const PetscScalar coefficients_t[], PetscDS dsAux, const PetscScalar coefficientsAux[], PetscReal t, PetscScalar elemVec[])
388 {
389   const PetscInt     debug = ds->printIntegrate;
390   const PetscInt     field = key.field;
391   PetscFE            fe;
392   PetscWeakForm      wf;
393   PetscInt           n0, n1, i;
394   PetscPointFn     **f0_func, **f1_func;
395   PetscQuadrature    quad;
396   PetscTabulation   *T, *TAux = NULL;
397   PetscScalar       *f0, *f1, *u, *u_t = NULL, *u_x, *a, *a_x, *basisReal, *basisDerReal;
398   const PetscScalar *constants;
399   PetscReal         *x, cellScale;
400   PetscInt          *uOff, *uOff_x, *aOff = NULL, *aOff_x = NULL;
401   PetscInt           dim, numConstants, Nf, NfAux = 0, totDim, totDimAux = 0, cOffset = 0, cOffsetAux = 0, fOffset, e;
402   const PetscReal   *quadPoints, *quadWeights;
403   PetscInt           qdim, qNc, Nq, q, dE;
404 
405   PetscFunctionBegin;
406   PetscCall(PetscDSGetDiscretization(ds, field, (PetscObject *)&fe));
407   PetscCall(PetscFEGetSpatialDimension(fe, &dim));
408   cellScale = (PetscReal)PetscPowInt(2, dim);
409   PetscCall(PetscFEGetQuadrature(fe, &quad));
410   PetscCall(PetscDSGetNumFields(ds, &Nf));
411   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
412   PetscCall(PetscDSGetComponentOffsets(ds, &uOff));
413   PetscCall(PetscDSGetComponentDerivativeOffsets(ds, &uOff_x));
414   PetscCall(PetscDSGetFieldOffset(ds, field, &fOffset));
415   PetscCall(PetscDSGetWeakForm(ds, &wf));
416   PetscCall(PetscWeakFormGetResidual(wf, key.label, key.value, key.field, key.part, &n0, &f0_func, &n1, &f1_func));
417   if (!n0 && !n1) PetscFunctionReturn(PETSC_SUCCESS);
418   PetscCall(PetscDSGetEvaluationArrays(ds, &u, coefficients_t ? &u_t : NULL, &u_x));
419   PetscCall(PetscDSGetWorkspace(ds, &x, &basisReal, &basisDerReal, NULL, NULL));
420   PetscCall(PetscDSGetWeakFormArrays(ds, &f0, &f1, NULL, NULL, NULL, NULL));
421   PetscCall(PetscDSGetTabulation(ds, &T));
422   PetscCall(PetscDSSetIntegrationParameters(ds, field, PETSC_DETERMINE));
423   PetscCall(PetscDSGetConstants(ds, &numConstants, &constants));
424   if (dsAux) {
425     PetscCall(PetscDSGetNumFields(dsAux, &NfAux));
426     PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
427     PetscCall(PetscDSGetComponentOffsets(dsAux, &aOff));
428     PetscCall(PetscDSGetComponentDerivativeOffsets(dsAux, &aOff_x));
429     PetscCall(PetscDSGetEvaluationArrays(dsAux, &a, NULL, &a_x));
430     PetscCall(PetscDSGetTabulation(dsAux, &TAux));
431     PetscCheck(T[0]->Np == TAux[0]->Np, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of tabulation points %" PetscInt_FMT " != %" PetscInt_FMT " number of auxiliary tabulation points", T[0]->Np, TAux[0]->Np);
432   }
433   PetscCall(PetscQuadratureGetData(quad, &qdim, &qNc, &Nq, &quadPoints, &quadWeights));
434   PetscCheck(qNc == 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Only supports scalar quadrature, not %" PetscInt_FMT " components", qNc);
435   dE = cgeom->dimEmbed;
436   PetscCheck(cgeom->dim == qdim, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "FEGeom dim %" PetscInt_FMT " != %" PetscInt_FMT " quadrature dim", cgeom->dim, qdim);
437   for (e = 0; e < Ne; ++e) {
438     PetscFEGeom fegeom;
439 
440     fegeom.v = x; /* workspace */
441     PetscCall(PetscArrayzero(f0, Nq * T[field]->Nc));
442     PetscCall(PetscArrayzero(f1, Nq * T[field]->Nc * dE));
443     for (q = 0; q < Nq; ++q) {
444       PetscReal w;
445       PetscInt  c, d;
446 
447       PetscCall(PetscFEGeomGetPoint(cgeom, e, q, &quadPoints[q * cgeom->dim], &fegeom));
448       PetscCall(PetscDSSetCellParameters(ds, fegeom.detJ[0] * cellScale));
449       w = fegeom.detJ[0] * quadWeights[q];
450       if (debug > 1 && q < cgeom->numPoints) {
451         PetscCall(PetscPrintf(PETSC_COMM_SELF, "  detJ: %g\n", (double)fegeom.detJ[0]));
452 #if !defined(PETSC_USE_COMPLEX)
453         PetscCall(DMPrintCellMatrix(e, "invJ", dE, dE, fegeom.invJ));
454 #endif
455       }
456       PetscCall(PetscFEEvaluateFieldJets_Internal(ds, Nf, 0, q, T, &fegeom, &coefficients[cOffset], PetscSafePointerPlusOffset(coefficients_t, cOffset), u, u_x, u_t));
457       if (dsAux) PetscCall(PetscFEEvaluateFieldJets_Internal(dsAux, NfAux, 0, q, TAux, &fegeom, &coefficientsAux[cOffsetAux], NULL, a, a_x, NULL));
458       for (i = 0; i < n0; ++i) f0_func[i](dE, Nf, NfAux, uOff, uOff_x, u, u_t, u_x, aOff, aOff_x, a, NULL, a_x, t, fegeom.v, numConstants, constants, &f0[q * T[field]->Nc]);
459       for (c = 0; c < T[field]->Nc; ++c) f0[q * T[field]->Nc + c] *= w;
460       for (i = 0; i < n1; ++i) f1_func[i](dE, Nf, NfAux, uOff, uOff_x, u, u_t, u_x, aOff, aOff_x, a, NULL, a_x, t, fegeom.v, numConstants, constants, &f1[q * T[field]->Nc * dE]);
461       for (c = 0; c < T[field]->Nc; ++c)
462         for (d = 0; d < dE; ++d) f1[(q * T[field]->Nc + c) * dE + d] *= w;
463       if (debug) {
464         // LCOV_EXCL_START
465         PetscCall(PetscPrintf(PETSC_COMM_SELF, "  quad point %" PetscInt_FMT " wt %g x:", q, (double)quadWeights[q]));
466         for (c = 0; c < dE; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %g", (double)fegeom.v[c]));
467         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
468         if (debug > 2) {
469           PetscCall(PetscPrintf(PETSC_COMM_SELF, "  field %" PetscInt_FMT ":", field));
470           for (c = 0; c < T[field]->Nc; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %g", (double)PetscRealPart(u[uOff[field] + c])));
471           PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
472           PetscCall(PetscPrintf(PETSC_COMM_SELF, "  field der %" PetscInt_FMT ":", field));
473           for (c = 0; c < T[field]->Nc * dE; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %g", (double)PetscRealPart(u_x[uOff[field] + c])));
474           PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
475           PetscCall(PetscPrintf(PETSC_COMM_SELF, "  resid %" PetscInt_FMT ":", field));
476           for (c = 0; c < T[field]->Nc; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %g", (double)PetscRealPart(f0[q * T[field]->Nc + c])));
477           PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
478           PetscCall(PetscPrintf(PETSC_COMM_SELF, "  res der %" PetscInt_FMT ":", field));
479           for (c = 0; c < T[field]->Nc; ++c) {
480             for (d = 0; d < dE; ++d) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %g", (double)PetscRealPart(f1[(q * T[field]->Nc + c) * dE + d])));
481           }
482           PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
483         }
484         // LCOV_EXCL_STOP
485       }
486     }
487     PetscCall(PetscFEUpdateElementVec_Internal(fe, T[field], 0, basisReal, basisDerReal, e, cgeom, f0, f1, &elemVec[cOffset + fOffset]));
488     cOffset += totDim;
489     cOffsetAux += totDimAux;
490   }
491   PetscFunctionReturn(PETSC_SUCCESS);
492 }
493 
494 PetscErrorCode PetscFEIntegrateBdResidual_Basic(PetscDS ds, PetscWeakForm wf, PetscFormKey key, PetscInt Ne, PetscFEGeom *fgeom, const PetscScalar coefficients[], const PetscScalar coefficients_t[], PetscDS dsAux, const PetscScalar coefficientsAux[], PetscReal t, PetscScalar elemVec[])
495 {
496   const PetscInt     debug = ds->printIntegrate;
497   const PetscInt     field = key.field;
498   PetscFE            fe;
499   PetscInt           n0, n1, i;
500   PetscBdPointFn   **f0_func, **f1_func;
501   PetscQuadrature    quad;
502   PetscTabulation   *Tf, *TfAux = NULL;
503   PetscScalar       *f0, *f1, *u, *u_t = NULL, *u_x, *a, *a_x, *basisReal, *basisDerReal;
504   const PetscScalar *constants;
505   PetscReal         *x, cellScale;
506   PetscInt          *uOff, *uOff_x, *aOff = NULL, *aOff_x = NULL;
507   PetscInt           dim, dimAux, numConstants, Nf, NfAux = 0, totDim, totDimAux = 0, cOffset = 0, cOffsetAux = 0, fOffset, e, NcI;
508   PetscBool          auxOnBd = PETSC_FALSE;
509   const PetscReal   *quadPoints, *quadWeights;
510   PetscInt           qdim, qNc, Nq, q, dE;
511 
512   PetscFunctionBegin;
513   PetscCall(PetscDSGetDiscretization(ds, field, (PetscObject *)&fe));
514   PetscCall(PetscFEGetSpatialDimension(fe, &dim));
515   cellScale = (PetscReal)PetscPowInt(2, dim);
516   PetscCall(PetscFEGetFaceQuadrature(fe, &quad));
517   PetscCall(PetscDSGetNumFields(ds, &Nf));
518   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
519   PetscCall(PetscDSGetComponentOffsets(ds, &uOff));
520   PetscCall(PetscDSGetComponentDerivativeOffsets(ds, &uOff_x));
521   PetscCall(PetscDSGetFieldOffset(ds, field, &fOffset));
522   PetscCall(PetscWeakFormGetBdResidual(wf, key.label, key.value, key.field, key.part, &n0, &f0_func, &n1, &f1_func));
523   if (!n0 && !n1) PetscFunctionReturn(PETSC_SUCCESS);
524   PetscCall(PetscDSGetEvaluationArrays(ds, &u, coefficients_t ? &u_t : NULL, &u_x));
525   PetscCall(PetscDSGetWorkspace(ds, &x, &basisReal, &basisDerReal, NULL, NULL));
526   PetscCall(PetscDSGetWeakFormArrays(ds, &f0, &f1, NULL, NULL, NULL, NULL));
527   PetscCall(PetscDSGetFaceTabulation(ds, &Tf));
528   PetscCall(PetscDSSetIntegrationParameters(ds, field, PETSC_DETERMINE));
529   PetscCall(PetscDSGetConstants(ds, &numConstants, &constants));
530   if (dsAux) {
531     PetscCall(PetscDSGetSpatialDimension(dsAux, &dimAux));
532     PetscCall(PetscDSGetNumFields(dsAux, &NfAux));
533     PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
534     PetscCall(PetscDSGetComponentOffsets(dsAux, &aOff));
535     PetscCall(PetscDSGetComponentDerivativeOffsets(dsAux, &aOff_x));
536     PetscCall(PetscDSGetEvaluationArrays(dsAux, &a, NULL, &a_x));
537     auxOnBd = dimAux < dim ? PETSC_TRUE : PETSC_FALSE;
538     if (auxOnBd) PetscCall(PetscDSGetTabulation(dsAux, &TfAux));
539     else PetscCall(PetscDSGetFaceTabulation(dsAux, &TfAux));
540     PetscCheck(Tf[0]->Np == TfAux[0]->Np, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of tabulation points %" PetscInt_FMT " != %" PetscInt_FMT " number of auxiliary tabulation points", Tf[0]->Np, TfAux[0]->Np);
541   }
542   NcI = Tf[field]->Nc;
543   PetscCall(PetscQuadratureGetData(quad, &qdim, &qNc, &Nq, &quadPoints, &quadWeights));
544   PetscCheck(qNc == 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Only supports scalar quadrature, not %" PetscInt_FMT " components", qNc);
545   dE = fgeom->dimEmbed;
546   /* TODO FIX THIS */
547   fgeom->dim = dim - 1;
548   PetscCheck(fgeom->dim == qdim, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "FEGeom dim %" PetscInt_FMT " != %" PetscInt_FMT " quadrature dim", fgeom->dim, qdim);
549   for (e = 0; e < Ne; ++e) {
550     PetscFEGeom    fegeom, cgeom;
551     const PetscInt face = fgeom->face[e][0];
552 
553     fegeom.v = x; /* Workspace */
554     PetscCall(PetscArrayzero(f0, Nq * NcI));
555     PetscCall(PetscArrayzero(f1, Nq * NcI * dE));
556     for (q = 0; q < Nq; ++q) {
557       PetscReal w;
558       PetscInt  c, d;
559 
560       PetscCall(PetscFEGeomGetPoint(fgeom, e, q, &quadPoints[q * fgeom->dim], &fegeom));
561       PetscCall(PetscFEGeomGetCellPoint(fgeom, e, q, &cgeom));
562       PetscCall(PetscDSSetCellParameters(ds, fegeom.detJ[0] * cellScale));
563       w = fegeom.detJ[0] * quadWeights[q];
564       if (debug > 1) {
565         if ((fgeom->isAffine && q == 0) || (!fgeom->isAffine)) {
566           PetscCall(PetscPrintf(PETSC_COMM_SELF, "  detJ: %g\n", (double)fegeom.detJ[0]));
567 #if !defined(PETSC_USE_COMPLEX)
568           PetscCall(DMPrintCellMatrix(e, "invJ", dim, dim, fegeom.invJ));
569           PetscCall(DMPrintCellVector(e, "n", dim, fegeom.n));
570 #endif
571         }
572       }
573       PetscCall(PetscFEEvaluateFieldJets_Internal(ds, Nf, face, q, Tf, &cgeom, &coefficients[cOffset], PetscSafePointerPlusOffset(coefficients_t, cOffset), u, u_x, u_t));
574       if (dsAux) PetscCall(PetscFEEvaluateFieldJets_Internal(dsAux, NfAux, auxOnBd ? 0 : face, q, TfAux, &cgeom, &coefficientsAux[cOffsetAux], NULL, a, a_x, NULL));
575       for (i = 0; i < n0; ++i) f0_func[i](dE, Nf, NfAux, uOff, uOff_x, u, u_t, u_x, aOff, aOff_x, a, NULL, a_x, t, fegeom.v, fegeom.n, numConstants, constants, &f0[q * NcI]);
576       for (c = 0; c < NcI; ++c) f0[q * NcI + c] *= w;
577       for (i = 0; i < n1; ++i) f1_func[i](dE, Nf, NfAux, uOff, uOff_x, u, u_t, u_x, aOff, aOff_x, a, NULL, a_x, t, fegeom.v, fegeom.n, numConstants, constants, &f1[q * NcI * dE]);
578       for (c = 0; c < NcI; ++c)
579         for (d = 0; d < dE; ++d) f1[(q * NcI + c) * dE + d] *= w;
580       if (debug) {
581         PetscCall(PetscPrintf(PETSC_COMM_SELF, "  elem %" PetscInt_FMT " quad point %" PetscInt_FMT "\n", e, q));
582         for (c = 0; c < NcI; ++c) {
583           if (n0) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  f0[%" PetscInt_FMT "] %g\n", c, (double)PetscRealPart(f0[q * NcI + c])));
584           if (n1) {
585             for (d = 0; d < dim; ++d) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  f1[%" PetscInt_FMT ",%" PetscInt_FMT "] %g", c, d, (double)PetscRealPart(f1[(q * NcI + c) * dim + d])));
586             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
587           }
588         }
589       }
590     }
591     PetscCall(PetscFEUpdateElementVec_Internal(fe, Tf[field], face, basisReal, basisDerReal, e, fgeom, f0, f1, &elemVec[cOffset + fOffset]));
592     cOffset += totDim;
593     cOffsetAux += totDimAux;
594   }
595   PetscFunctionReturn(PETSC_SUCCESS);
596 }
597 
598 /*
599   BdIntegral: Operates completely in the embedding dimension. The trick is to have special "face quadrature" so we only integrate over the face, but
600               all transforms operate in the full space and are square.
601 
602   HybridIntegral: The discretization is lower dimensional. That means the transforms are non-square.
603     1) DMPlexGetCellFields() retrieves from the hybrid cell, so it gets fields from both faces
604     2) We need to assume that the orientation is 0 for both
605     3) TODO We need to use a non-square Jacobian for the derivative maps, meaning the embedding dimension has to go to EvaluateFieldJets() and UpdateElementVec()
606 */
607 PETSC_INTERN PetscErrorCode PetscFEIntegrateHybridResidual_Basic(PetscDS ds, PetscDS dsIn, PetscFormKey key, PetscInt s, PetscInt Ne, PetscFEGeom *fgeom, PetscFEGeom *nbrgeom, const PetscScalar coefficients[], const PetscScalar coefficients_t[], PetscDS dsAux, const PetscScalar coefficientsAux[], PetscReal t, PetscScalar elemVec[])
608 {
609   const PetscInt     debug = ds->printIntegrate;
610   const PetscInt     field = key.field;
611   PetscFE            fe;
612   PetscWeakForm      wf;
613   PetscInt           n0, n1, i;
614   PetscBdPointFn   **f0_func, **f1_func;
615   PetscQuadrature    quad;
616   DMPolytopeType     ct;
617   PetscTabulation   *Tf, *TfIn, *TfAux = NULL;
618   PetscScalar       *f0, *f1, *u, *u_t = NULL, *u_x, *a, *a_x, *basisReal, *basisDerReal;
619   const PetscScalar *constants;
620   PetscReal         *x;
621   PetscInt          *uOff, *uOff_x, *aOff = NULL, *aOff_x = NULL;
622   PetscInt           dim, dimAux, numConstants, Nf, NfAux = 0, totDim, totDimIn, totDimAux = 0, cOffset = 0, cOffsetIn = 0, cOffsetAux = 0, fOffset, e, NcI, NcS;
623   PetscBool          isCohesiveField, auxOnBd = PETSC_FALSE;
624   const PetscReal   *quadPoints, *quadWeights;
625   PetscInt           qdim, qNc, Nq, q, dE;
626 
627   PetscFunctionBegin;
628   /* Hybrid discretization is posed directly on faces */
629   PetscCall(PetscDSGetDiscretization(ds, field, (PetscObject *)&fe));
630   PetscCall(PetscFEGetSpatialDimension(fe, &dim));
631   PetscCall(PetscFEGetQuadrature(fe, &quad));
632   PetscCall(PetscDSGetNumFields(ds, &Nf));
633   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
634   PetscCall(PetscDSGetTotalDimension(dsIn, &totDimIn));
635   PetscCall(PetscDSGetComponentOffsetsCohesive(dsIn, 0, &uOff)); // Change 0 to s for one-sided offsets
636   PetscCall(PetscDSGetComponentDerivativeOffsetsCohesive(dsIn, s, &uOff_x));
637   PetscCall(PetscDSGetFieldOffsetCohesive(ds, field, &fOffset));
638   PetscCall(PetscDSGetWeakForm(ds, &wf));
639   PetscCall(PetscWeakFormGetBdResidual(wf, key.label, key.value, key.field, key.part, &n0, &f0_func, &n1, &f1_func));
640   if (!n0 && !n1) PetscFunctionReturn(PETSC_SUCCESS);
641   PetscCall(PetscDSGetEvaluationArrays(ds, &u, coefficients_t ? &u_t : NULL, &u_x));
642   PetscCall(PetscDSGetWorkspace(ds, &x, &basisReal, &basisDerReal, NULL, NULL));
643   PetscCall(PetscDSGetWeakFormArrays(ds, &f0, &f1, NULL, NULL, NULL, NULL));
644   /* NOTE This is a bulk tabulation because the DS is a face discretization */
645   PetscCall(PetscDSGetTabulation(ds, &Tf));
646   PetscCall(PetscDSGetFaceTabulation(dsIn, &TfIn));
647   PetscCall(PetscDSSetIntegrationParameters(ds, field, PETSC_DETERMINE));
648   PetscCall(PetscDSGetConstants(ds, &numConstants, &constants));
649   if (dsAux) {
650     PetscCall(PetscDSGetSpatialDimension(dsAux, &dimAux));
651     PetscCall(PetscDSGetNumFields(dsAux, &NfAux));
652     PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
653     PetscCall(PetscDSGetComponentOffsets(dsAux, &aOff));
654     PetscCall(PetscDSGetComponentDerivativeOffsets(dsAux, &aOff_x));
655     PetscCall(PetscDSGetEvaluationArrays(dsAux, &a, NULL, &a_x));
656     auxOnBd = dimAux == dim ? PETSC_TRUE : PETSC_FALSE;
657     if (auxOnBd) PetscCall(PetscDSGetTabulation(dsAux, &TfAux));
658     else PetscCall(PetscDSGetFaceTabulation(dsAux, &TfAux));
659     PetscCheck(Tf[0]->Np == TfAux[0]->Np, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of tabulation points %" PetscInt_FMT " != %" PetscInt_FMT " number of auxiliary tabulation points", Tf[0]->Np, TfAux[0]->Np);
660   }
661   PetscCall(PetscDSGetCohesive(ds, field, &isCohesiveField));
662   NcI = Tf[field]->Nc;
663   NcS = NcI;
664   if (!isCohesiveField && s == 2) {
665     // If we are integrating over a cohesive cell (s = 2) for a non-cohesive fields, we use both sides
666     NcS *= 2;
667   }
668   PetscCall(PetscQuadratureGetData(quad, &qdim, &qNc, &Nq, &quadPoints, &quadWeights));
669   PetscCall(PetscQuadratureGetCellType(quad, &ct));
670   PetscCheck(qNc == 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Only supports scalar quadrature, not %" PetscInt_FMT " components", qNc);
671   dE = fgeom->dimEmbed;
672   PetscCheck(fgeom->dim == qdim, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "FEGeom dim %" PetscInt_FMT " != %" PetscInt_FMT " quadrature dim", fgeom->dim, qdim);
673   for (e = 0; e < Ne; ++e) {
674     // In order for the face information to be correct, the support of endcap faces _must_ be correctly oriented
675     PetscFEGeom    fegeom, fegeomN[2];
676     const PetscInt face[2]  = {fgeom->face[e * 2 + 0][0], fgeom->face[e * 2 + 1][2]};
677     const PetscInt ornt[2]  = {fgeom->face[e * 2 + 0][1], fgeom->face[e * 2 + 1][3]};
678     const PetscInt cornt[2] = {fgeom->face[e * 2 + 0][3], fgeom->face[e * 2 + 1][1]};
679 
680     fegeom.v = x; /* Workspace */
681     PetscCall(PetscArrayzero(f0, Nq * NcS));
682     PetscCall(PetscArrayzero(f1, Nq * NcS * dE));
683     if (debug > 2) {
684       PetscCall(PetscPrintf(PETSC_COMM_SELF, "Negative %s face: %" PetscInt_FMT " (%" PetscInt_FMT ") (%" PetscInt_FMT ") perm %" PetscInt_FMT "\n", DMPolytopeTypes[ct], face[0], ornt[0], cornt[0], DMPolytopeTypeComposeOrientationInv(ct, cornt[0], ornt[0])));
685       PetscCall(PetscPrintf(PETSC_COMM_SELF, "Positive %s face: %" PetscInt_FMT " (%" PetscInt_FMT ") (%" PetscInt_FMT ") perm %" PetscInt_FMT "\n", DMPolytopeTypes[ct], face[1], ornt[1], cornt[1], DMPolytopeTypeComposeOrientationInv(ct, cornt[1], ornt[1])));
686     }
687     for (q = 0; q < Nq; ++q) {
688       PetscInt  qpt[2];
689       PetscReal w;
690       PetscInt  c, d;
691 
692       PetscCall(PetscDSPermuteQuadPoint(ds, DMPolytopeTypeComposeOrientationInv(ct, cornt[0], ornt[0]), field, q, &qpt[0]));
693       PetscCall(PetscDSPermuteQuadPoint(ds, DMPolytopeTypeComposeOrientationInv(ct, cornt[1], ornt[1]), field, q, &qpt[1]));
694       PetscCall(PetscFEGeomGetPoint(fgeom, e * 2, q, &quadPoints[q * fgeom->dim], &fegeom));
695       PetscCall(PetscFEGeomGetPoint(nbrgeom, e * 2, q, NULL, &fegeomN[0]));
696       PetscCall(PetscFEGeomGetPoint(nbrgeom, e * 2 + 1, q, NULL, &fegeomN[1]));
697       w = fegeom.detJ[0] * quadWeights[q];
698       if (debug > 1 && q < fgeom->numPoints) {
699         PetscCall(PetscPrintf(PETSC_COMM_SELF, "  detJ: %g\n", (double)fegeom.detJ[0]));
700 #if !defined(PETSC_USE_COMPLEX)
701         PetscCall(DMPrintCellMatrix(e, "invJ", dim, dE, fegeom.invJ));
702 #endif
703       }
704       if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  quad point %" PetscInt_FMT " weight %g detJ %g\n", q, (double)quadWeights[q], (double)fegeom.detJ[0]));
705       /* TODO Is this cell or face quadrature, meaning should we use 'q' or 'face*Nq+q' */
706       PetscCall(PetscFEEvaluateFieldJets_Hybrid_Internal(dsIn, Nf, 0, q, Tf, face, qpt, TfIn, &fegeom, fegeomN, &coefficients[cOffsetIn], PetscSafePointerPlusOffset(coefficients_t, cOffsetIn), u, u_x, u_t));
707       if (dsAux) PetscCall(PetscFEEvaluateFieldJets_Internal(dsAux, NfAux, auxOnBd ? 0 : face[s], auxOnBd ? q : qpt[s], TfAux, &fegeom, &coefficientsAux[cOffsetAux], NULL, a, a_x, NULL));
708       for (i = 0; i < n0; ++i) f0_func[i](dE, Nf, NfAux, uOff, uOff_x, u, u_t, u_x, aOff, aOff_x, a, NULL, a_x, t, fegeom.v, fegeom.n, numConstants, constants, &f0[q * NcS]);
709       for (c = 0; c < NcS; ++c) f0[q * NcS + c] *= w;
710       for (i = 0; i < n1; ++i) f1_func[i](dE, Nf, NfAux, uOff, uOff_x, u, u_t, u_x, aOff, aOff_x, a, NULL, a_x, t, fegeom.v, fegeom.n, numConstants, constants, &f1[q * NcS * dE]);
711       for (c = 0; c < NcS; ++c)
712         for (d = 0; d < dE; ++d) f1[(q * NcS + c) * dE + d] *= w;
713       if (debug) {
714         PetscCall(PetscPrintf(PETSC_COMM_SELF, "  elem %" PetscInt_FMT " quad point %" PetscInt_FMT " field %" PetscInt_FMT " side %" PetscInt_FMT "\n", e, q, field, s));
715         for (PetscInt f = 0; f < Nf; ++f) {
716           PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Field %" PetscInt_FMT ":", f));
717           for (PetscInt c = uOff[f]; c < uOff[f + 1]; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  %g", (double)PetscRealPart(u[c])));
718           PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
719         }
720         for (c = 0; c < NcS; ++c) {
721           if (n0) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  f0[%" PetscInt_FMT "] %g\n", c, (double)PetscRealPart(f0[q * NcS + c])));
722           if (n1) {
723             for (d = 0; d < dE; ++d) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  f1[%" PetscInt_FMT ",%" PetscInt_FMT "] %g", c, d, (double)PetscRealPart(f1[(q * NcS + c) * dE + d])));
724             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
725           }
726         }
727       }
728     }
729     if (isCohesiveField) {
730       PetscCall(PetscFEUpdateElementVec_Internal(fe, Tf[field], 0, basisReal, basisDerReal, e, fgeom, f0, f1, &elemVec[cOffset + fOffset]));
731     } else {
732       PetscCall(PetscFEUpdateElementVec_Hybrid_Internal(fe, Tf[field], 0, s, basisReal, basisDerReal, fgeom, f0, f1, &elemVec[cOffset + fOffset]));
733     }
734     cOffset += totDim;
735     cOffsetIn += totDimIn;
736     cOffsetAux += totDimAux;
737   }
738   PetscFunctionReturn(PETSC_SUCCESS);
739 }
740 
741 PetscErrorCode PetscFEIntegrateJacobian_Basic(PetscDS rds, PetscDS cds, PetscFEJacobianType jtype, PetscFormKey key, PetscInt Ne, PetscFEGeom *cgeom, const PetscScalar coefficients[], const PetscScalar coefficients_t[], PetscDS dsAux, const PetscScalar coefficientsAux[], PetscReal t, PetscReal u_tshift, PetscScalar elemMat[])
742 {
743   const PetscInt     debug = rds->printIntegrate;
744   PetscFE            feI, feJ;
745   PetscWeakForm      wf;
746   PetscPointJacFn  **g0_func, **g1_func, **g2_func, **g3_func;
747   PetscInt           n0, n1, n2, n3;
748   PetscInt           cOffset    = 0; /* Offset into coefficients[] for element e */
749   PetscInt           cOffsetAux = 0; /* Offset into coefficientsAux[] for element e */
750   PetscInt           eOffset    = 0; /* Offset into elemMat[] for element e */
751   PetscInt           offsetI    = 0; /* Offset into an element vector for fieldI */
752   PetscInt           offsetJ    = 0; /* Offset into an element vector for fieldJ */
753   PetscQuadrature    quad;
754   PetscTabulation   *rT, *cT, *TAux = NULL;
755   PetscScalar       *g0 = NULL, *g1 = NULL, *g2 = NULL, *g3 = NULL, *u, *u_t = NULL, *u_x, *a, *a_x, *basisReal, *basisDerReal, *testReal, *testDerReal;
756   const PetscScalar *constants;
757   PetscReal         *x, cellScale;
758   PetscInt          *uOff, *uOff_x, *aOff = NULL, *aOff_x = NULL;
759   PetscInt           NcI = 0, NcJ = 0;
760   PetscInt           dim, numConstants, Nf, fieldI, fieldJ, NfAux = 0, rtotDim, ctotDim, totDimAux = 0;
761   PetscInt           dE, Np;
762   PetscBool          isAffine;
763   const PetscReal   *quadPoints, *quadWeights;
764   PetscInt           qNc, Nq;
765 
766   PetscFunctionBegin;
767   PetscCall(PetscDSGetNumFields(rds, &Nf));
768   fieldI = key.field / Nf;
769   fieldJ = key.field % Nf;
770   PetscCall(PetscDSGetDiscretization(rds, fieldI, (PetscObject *)&feI));
771   PetscCall(PetscDSGetDiscretization(cds, fieldJ, (PetscObject *)&feJ));
772   PetscCall(PetscFEGetSpatialDimension(feI, &dim));
773   cellScale = (PetscReal)PetscPowInt(2, dim);
774   PetscCall(PetscFEGetQuadrature(feI, &quad));
775   PetscCall(PetscDSGetTotalDimension(rds, &rtotDim));
776   PetscCall(PetscDSGetTotalDimension(cds, &ctotDim));
777   PetscCall(PetscDSGetComponentOffsets(rds, &uOff));
778   PetscCall(PetscDSGetComponentDerivativeOffsets(rds, &uOff_x));
779   PetscCall(PetscDSGetWeakForm(rds, &wf));
780   switch (jtype) {
781   case PETSCFE_JACOBIAN_DYN:
782     PetscCall(PetscWeakFormGetDynamicJacobian(wf, key.label, key.value, fieldI, fieldJ, key.part, &n0, &g0_func, &n1, &g1_func, &n2, &g2_func, &n3, &g3_func));
783     break;
784   case PETSCFE_JACOBIAN_PRE:
785     PetscCall(PetscWeakFormGetJacobianPreconditioner(wf, key.label, key.value, fieldI, fieldJ, key.part, &n0, &g0_func, &n1, &g1_func, &n2, &g2_func, &n3, &g3_func));
786     break;
787   case PETSCFE_JACOBIAN:
788     PetscCall(PetscWeakFormGetJacobian(wf, key.label, key.value, fieldI, fieldJ, key.part, &n0, &g0_func, &n1, &g1_func, &n2, &g2_func, &n3, &g3_func));
789     break;
790   }
791   if (!n0 && !n1 && !n2 && !n3) PetscFunctionReturn(PETSC_SUCCESS);
792   PetscCall(PetscDSGetEvaluationArrays(rds, &u, coefficients_t ? &u_t : NULL, &u_x));
793   PetscCall(PetscDSGetWorkspace(rds, &x, &basisReal, &basisDerReal, &testReal, &testDerReal));
794   PetscCall(PetscDSGetWeakFormArrays(rds, NULL, NULL, n0 ? &g0 : NULL, n1 ? &g1 : NULL, n2 ? &g2 : NULL, n3 ? &g3 : NULL));
795 
796   PetscCall(PetscDSGetTabulation(rds, &rT));
797   PetscCall(PetscDSGetTabulation(cds, &cT));
798   PetscCheck(rT[0]->Np == cT[0]->Np, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of row tabulation points %" PetscInt_FMT " != %" PetscInt_FMT " number of col tabulation points", rT[0]->Np, cT[0]->Np);
799   PetscCall(PetscDSGetFieldOffset(rds, fieldI, &offsetI));
800   PetscCall(PetscDSGetFieldOffset(cds, fieldJ, &offsetJ));
801   PetscCall(PetscDSSetIntegrationParameters(rds, fieldI, fieldJ));
802   PetscCall(PetscDSSetIntegrationParameters(cds, fieldI, fieldJ));
803   PetscCall(PetscDSGetConstants(rds, &numConstants, &constants));
804   if (dsAux) {
805     PetscCall(PetscDSGetNumFields(dsAux, &NfAux));
806     PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
807     PetscCall(PetscDSGetComponentOffsets(dsAux, &aOff));
808     PetscCall(PetscDSGetComponentDerivativeOffsets(dsAux, &aOff_x));
809     PetscCall(PetscDSGetEvaluationArrays(dsAux, &a, NULL, &a_x));
810     PetscCall(PetscDSGetTabulation(dsAux, &TAux));
811     PetscCheck(rT[0]->Np == TAux[0]->Np, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of tabulation points %" PetscInt_FMT " != %" PetscInt_FMT " number of auxiliary tabulation points", rT[0]->Np, TAux[0]->Np);
812   }
813   NcI      = rT[fieldI]->Nc;
814   NcJ      = cT[fieldJ]->Nc;
815   Np       = cgeom->numPoints;
816   dE       = cgeom->dimEmbed;
817   isAffine = cgeom->isAffine;
818   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
819   PetscCheck(qNc == 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Only supports scalar quadrature, not %" PetscInt_FMT " components", qNc);
820 
821   for (PetscInt e = 0; e < Ne; ++e) {
822     PetscFEGeom fegeom;
823 
824     fegeom.dim      = cgeom->dim;
825     fegeom.dimEmbed = cgeom->dimEmbed;
826     if (isAffine) {
827       fegeom.v    = x;
828       fegeom.xi   = cgeom->xi;
829       fegeom.J    = &cgeom->J[e * Np * dE * dE];
830       fegeom.invJ = &cgeom->invJ[e * Np * dE * dE];
831       fegeom.detJ = &cgeom->detJ[e * Np];
832     }
833     for (PetscInt q = 0; q < Nq; ++q) {
834       PetscReal w;
835 
836       if (isAffine) {
837         CoordinatesRefToReal(dE, dim, fegeom.xi, &cgeom->v[e * Np * dE], fegeom.J, &quadPoints[q * dim], x);
838       } else {
839         fegeom.v    = &cgeom->v[(e * Np + q) * dE];
840         fegeom.J    = &cgeom->J[(e * Np + q) * dE * dE];
841         fegeom.invJ = &cgeom->invJ[(e * Np + q) * dE * dE];
842         fegeom.detJ = &cgeom->detJ[e * Np + q];
843       }
844       PetscCall(PetscDSSetCellParameters(rds, fegeom.detJ[0] * cellScale));
845       if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  quad point %" PetscInt_FMT " weight %g detJ %g\n", q, (double)quadWeights[q], (double)fegeom.detJ[0]));
846       w = fegeom.detJ[0] * quadWeights[q];
847       if (coefficients) PetscCall(PetscFEEvaluateFieldJets_Internal(rds, Nf, 0, q, rT, &fegeom, &coefficients[cOffset], PetscSafePointerPlusOffset(coefficients_t, cOffset), u, u_x, u_t));
848       if (dsAux) PetscCall(PetscFEEvaluateFieldJets_Internal(dsAux, NfAux, 0, q, TAux, &fegeom, &coefficientsAux[cOffsetAux], NULL, a, a_x, NULL));
849       if (n0) {
850         PetscCall(PetscArrayzero(g0, NcI * NcJ));
851         for (PetscInt i = 0; i < n0; ++i) g0_func[i](dE, Nf, NfAux, uOff, uOff_x, u, u_t, u_x, aOff, aOff_x, a, NULL, a_x, t, u_tshift, fegeom.v, numConstants, constants, g0);
852         for (PetscInt c = 0; c < NcI * NcJ; ++c) g0[c] *= w;
853       }
854       if (n1) {
855         PetscCall(PetscArrayzero(g1, NcI * NcJ * dE));
856         for (PetscInt i = 0; i < n1; ++i) g1_func[i](dE, Nf, NfAux, uOff, uOff_x, u, u_t, u_x, aOff, aOff_x, a, NULL, a_x, t, u_tshift, fegeom.v, numConstants, constants, g1);
857         for (PetscInt c = 0; c < NcI * NcJ * dE; ++c) g1[c] *= w;
858       }
859       if (n2) {
860         PetscCall(PetscArrayzero(g2, NcI * NcJ * dE));
861         for (PetscInt i = 0; i < n2; ++i) g2_func[i](dE, Nf, NfAux, uOff, uOff_x, u, u_t, u_x, aOff, aOff_x, a, NULL, a_x, t, u_tshift, fegeom.v, numConstants, constants, g2);
862         for (PetscInt c = 0; c < NcI * NcJ * dE; ++c) g2[c] *= w;
863       }
864       if (n3) {
865         PetscCall(PetscArrayzero(g3, NcI * NcJ * dE * dE));
866         for (PetscInt i = 0; i < n3; ++i) g3_func[i](dE, Nf, NfAux, uOff, uOff_x, u, u_t, u_x, aOff, aOff_x, a, NULL, a_x, t, u_tshift, fegeom.v, numConstants, constants, g3);
867         for (PetscInt c = 0; c < NcI * NcJ * dE * dE; ++c) g3[c] *= w;
868       }
869 
870       PetscCall(PetscFEUpdateElementMat_Internal(feI, feJ, 0, q, rT[fieldI], basisReal, basisDerReal, cT[fieldJ], testReal, testDerReal, &fegeom, g0, g1, g2, g3, ctotDim, offsetI, offsetJ, elemMat + eOffset));
871     }
872     if (debug > 1) {
873       PetscCall(PetscPrintf(PETSC_COMM_SELF, "Element matrix for fields %" PetscInt_FMT " and %" PetscInt_FMT "\n", fieldI, fieldJ));
874       for (PetscInt f = 0; f < rT[fieldI]->Nb; ++f) {
875         const PetscInt i = offsetI + f;
876         for (PetscInt g = 0; g < cT[fieldJ]->Nb; ++g) {
877           const PetscInt j = offsetJ + g;
878           PetscCall(PetscPrintf(PETSC_COMM_SELF, "    elemMat[%" PetscInt_FMT ", %" PetscInt_FMT "]: %g\n", f, g, (double)PetscRealPart(elemMat[eOffset + i * ctotDim + j])));
879         }
880         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
881       }
882     }
883     cOffset += rtotDim;
884     cOffsetAux += totDimAux;
885     eOffset += rtotDim * ctotDim;
886   }
887   PetscFunctionReturn(PETSC_SUCCESS);
888 }
889 
890 PETSC_INTERN PetscErrorCode PetscFEIntegrateBdJacobian_Basic(PetscDS ds, PetscWeakForm wf, PetscFEJacobianType jtype, PetscFormKey key, PetscInt Ne, PetscFEGeom *fgeom, const PetscScalar coefficients[], const PetscScalar coefficients_t[], PetscDS dsAux, const PetscScalar coefficientsAux[], PetscReal t, PetscReal u_tshift, PetscScalar elemMat[])
891 {
892   const PetscInt      debug = ds->printIntegrate;
893   PetscFE             feI, feJ;
894   PetscBdPointJacFn **g0_func, **g1_func, **g2_func, **g3_func;
895   PetscInt            n0, n1, n2, n3, i;
896   PetscInt            cOffset    = 0; /* Offset into coefficients[] for element e */
897   PetscInt            cOffsetAux = 0; /* Offset into coefficientsAux[] for element e */
898   PetscInt            eOffset    = 0; /* Offset into elemMat[] for element e */
899   PetscInt            offsetI    = 0; /* Offset into an element vector for fieldI */
900   PetscInt            offsetJ    = 0; /* Offset into an element vector for fieldJ */
901   PetscQuadrature     quad;
902   PetscTabulation    *T, *TAux = NULL;
903   PetscScalar        *g0, *g1, *g2, *g3, *u, *u_t = NULL, *u_x, *a, *a_x, *basisReal, *basisDerReal, *testReal, *testDerReal;
904   const PetscScalar  *constants;
905   PetscReal          *x, cellScale;
906   PetscInt           *uOff, *uOff_x, *aOff = NULL, *aOff_x = NULL;
907   PetscInt            NcI = 0, NcJ = 0;
908   PetscInt            dim, numConstants, Nf, fieldI, fieldJ, NfAux = 0, totDim, totDimAux = 0, e;
909   PetscBool           isAffine;
910   const PetscReal    *quadPoints, *quadWeights;
911   PetscInt            qNc, Nq, q, Np, dE;
912 
913   PetscFunctionBegin;
914   PetscCall(PetscDSGetNumFields(ds, &Nf));
915   fieldI = key.field / Nf;
916   fieldJ = key.field % Nf;
917   PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&feI));
918   PetscCall(PetscDSGetDiscretization(ds, fieldJ, (PetscObject *)&feJ));
919   PetscCall(PetscFEGetSpatialDimension(feI, &dim));
920   cellScale = (PetscReal)PetscPowInt(2, dim);
921   PetscCall(PetscFEGetFaceQuadrature(feI, &quad));
922   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
923   PetscCall(PetscDSGetComponentOffsets(ds, &uOff));
924   PetscCall(PetscDSGetComponentDerivativeOffsets(ds, &uOff_x));
925   PetscCall(PetscDSGetFieldOffset(ds, fieldI, &offsetI));
926   PetscCall(PetscDSGetFieldOffset(ds, fieldJ, &offsetJ));
927   switch (jtype) {
928   case PETSCFE_JACOBIAN_PRE:
929     PetscCall(PetscWeakFormGetBdJacobianPreconditioner(wf, key.label, key.value, fieldI, fieldJ, key.part, &n0, &g0_func, &n1, &g1_func, &n2, &g2_func, &n3, &g3_func));
930     break;
931   case PETSCFE_JACOBIAN:
932     PetscCall(PetscWeakFormGetBdJacobian(wf, key.label, key.value, fieldI, fieldJ, key.part, &n0, &g0_func, &n1, &g1_func, &n2, &g2_func, &n3, &g3_func));
933     break;
934   case PETSCFE_JACOBIAN_DYN:
935     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "PETSCFE_JACOBIAN_DYN is not supported for PetscFEIntegrateBdJacobian()");
936   }
937   if (!n0 && !n1 && !n2 && !n3) PetscFunctionReturn(PETSC_SUCCESS);
938   PetscCall(PetscDSGetEvaluationArrays(ds, &u, coefficients_t ? &u_t : NULL, &u_x));
939   PetscCall(PetscDSGetWorkspace(ds, &x, &basisReal, &basisDerReal, &testReal, &testDerReal));
940   PetscCall(PetscDSGetWeakFormArrays(ds, NULL, NULL, &g0, &g1, &g2, &g3));
941   PetscCall(PetscDSGetFaceTabulation(ds, &T));
942   PetscCall(PetscDSSetIntegrationParameters(ds, fieldI, fieldJ));
943   PetscCall(PetscDSGetConstants(ds, &numConstants, &constants));
944   if (dsAux) {
945     PetscCall(PetscDSGetNumFields(dsAux, &NfAux));
946     PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
947     PetscCall(PetscDSGetComponentOffsets(dsAux, &aOff));
948     PetscCall(PetscDSGetComponentDerivativeOffsets(dsAux, &aOff_x));
949     PetscCall(PetscDSGetEvaluationArrays(dsAux, &a, NULL, &a_x));
950     PetscCall(PetscDSGetFaceTabulation(dsAux, &TAux));
951   }
952   NcI = T[fieldI]->Nc, NcJ = T[fieldJ]->Nc;
953   Np       = fgeom->numPoints;
954   dE       = fgeom->dimEmbed;
955   isAffine = fgeom->isAffine;
956   /* Initialize here in case the function is not defined */
957   PetscCall(PetscArrayzero(g0, NcI * NcJ));
958   PetscCall(PetscArrayzero(g1, NcI * NcJ * dE));
959   PetscCall(PetscArrayzero(g2, NcI * NcJ * dE));
960   PetscCall(PetscArrayzero(g3, NcI * NcJ * dE * dE));
961   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
962   PetscCheck(qNc == 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Only supports scalar quadrature, not %" PetscInt_FMT " components", qNc);
963   for (e = 0; e < Ne; ++e) {
964     PetscFEGeom    fegeom, cgeom;
965     const PetscInt face = fgeom->face[e][0];
966     fegeom.n            = NULL;
967     fegeom.v            = NULL;
968     fegeom.J            = NULL;
969     fegeom.detJ         = NULL;
970     fegeom.dim          = fgeom->dim;
971     fegeom.dimEmbed     = fgeom->dimEmbed;
972     cgeom.dim           = fgeom->dim;
973     cgeom.dimEmbed      = fgeom->dimEmbed;
974     if (isAffine) {
975       fegeom.v    = x;
976       fegeom.xi   = fgeom->xi;
977       fegeom.J    = &fgeom->J[e * Np * dE * dE];
978       fegeom.invJ = &fgeom->invJ[e * Np * dE * dE];
979       fegeom.detJ = &fgeom->detJ[e * Np];
980       fegeom.n    = &fgeom->n[e * Np * dE];
981 
982       cgeom.J    = &fgeom->suppJ[0][e * Np * dE * dE];
983       cgeom.invJ = &fgeom->suppInvJ[0][e * Np * dE * dE];
984       cgeom.detJ = &fgeom->suppDetJ[0][e * Np];
985     }
986     for (q = 0; q < Nq; ++q) {
987       PetscReal w;
988       PetscInt  c;
989 
990       if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  quad point %" PetscInt_FMT "\n", q));
991       if (isAffine) {
992         CoordinatesRefToReal(dE, dim - 1, fegeom.xi, &fgeom->v[e * Np * dE], fegeom.J, &quadPoints[q * (dim - 1)], x);
993       } else {
994         fegeom.v    = &fgeom->v[(e * Np + q) * dE];
995         fegeom.J    = &fgeom->J[(e * Np + q) * dE * dE];
996         fegeom.invJ = &fgeom->invJ[(e * Np + q) * dE * dE];
997         fegeom.detJ = &fgeom->detJ[e * Np + q];
998         fegeom.n    = &fgeom->n[(e * Np + q) * dE];
999 
1000         cgeom.J    = &fgeom->suppJ[0][(e * Np + q) * dE * dE];
1001         cgeom.invJ = &fgeom->suppInvJ[0][(e * Np + q) * dE * dE];
1002         cgeom.detJ = &fgeom->suppDetJ[0][e * Np + q];
1003       }
1004       PetscCall(PetscDSSetCellParameters(ds, fegeom.detJ[0] * cellScale));
1005       w = fegeom.detJ[0] * quadWeights[q];
1006       if (coefficients) PetscCall(PetscFEEvaluateFieldJets_Internal(ds, Nf, face, q, T, &cgeom, &coefficients[cOffset], &coefficients_t[cOffset], u, u_x, u_t));
1007       if (dsAux) PetscCall(PetscFEEvaluateFieldJets_Internal(dsAux, NfAux, face, q, TAux, &cgeom, &coefficientsAux[cOffsetAux], NULL, a, a_x, NULL));
1008       if (n0) {
1009         PetscCall(PetscArrayzero(g0, NcI * NcJ));
1010         for (i = 0; i < n0; ++i) g0_func[i](dE, Nf, NfAux, uOff, uOff_x, u, u_t, u_x, aOff, aOff_x, a, NULL, a_x, t, u_tshift, fegeom.v, fegeom.n, numConstants, constants, g0);
1011         for (c = 0; c < NcI * NcJ; ++c) g0[c] *= w;
1012       }
1013       if (n1) {
1014         PetscCall(PetscArrayzero(g1, NcI * NcJ * dE));
1015         for (i = 0; i < n1; ++i) g1_func[i](dE, Nf, NfAux, uOff, uOff_x, u, u_t, u_x, aOff, aOff_x, a, NULL, a_x, t, u_tshift, fegeom.v, fegeom.n, numConstants, constants, g1);
1016         for (c = 0; c < NcI * NcJ * dim; ++c) g1[c] *= w;
1017       }
1018       if (n2) {
1019         PetscCall(PetscArrayzero(g2, NcI * NcJ * dE));
1020         for (i = 0; i < n2; ++i) g2_func[i](dE, Nf, NfAux, uOff, uOff_x, u, u_t, u_x, aOff, aOff_x, a, NULL, a_x, t, u_tshift, fegeom.v, fegeom.n, numConstants, constants, g2);
1021         for (c = 0; c < NcI * NcJ * dim; ++c) g2[c] *= w;
1022       }
1023       if (n3) {
1024         PetscCall(PetscArrayzero(g3, NcI * NcJ * dE * dE));
1025         for (i = 0; i < n3; ++i) g3_func[i](dE, Nf, NfAux, uOff, uOff_x, u, u_t, u_x, aOff, aOff_x, a, NULL, a_x, t, u_tshift, fegeom.v, fegeom.n, numConstants, constants, g3);
1026         for (c = 0; c < NcI * NcJ * dim * dim; ++c) g3[c] *= w;
1027       }
1028 
1029       PetscCall(PetscFEUpdateElementMat_Internal(feI, feJ, face, q, T[fieldI], basisReal, basisDerReal, T[fieldJ], testReal, testDerReal, &cgeom, g0, g1, g2, g3, totDim, offsetI, offsetJ, elemMat + eOffset));
1030     }
1031     if (debug > 1) {
1032       PetscInt fc, f, gc, g;
1033 
1034       PetscCall(PetscPrintf(PETSC_COMM_SELF, "Element matrix for fields %" PetscInt_FMT " and %" PetscInt_FMT "\n", fieldI, fieldJ));
1035       for (fc = 0; fc < T[fieldI]->Nc; ++fc) {
1036         for (f = 0; f < T[fieldI]->Nb; ++f) {
1037           const PetscInt i = offsetI + f * T[fieldI]->Nc + fc;
1038           for (gc = 0; gc < T[fieldJ]->Nc; ++gc) {
1039             for (g = 0; g < T[fieldJ]->Nb; ++g) {
1040               const PetscInt j = offsetJ + g * T[fieldJ]->Nc + gc;
1041               PetscCall(PetscPrintf(PETSC_COMM_SELF, "    elemMat[%" PetscInt_FMT ",%" PetscInt_FMT ",%" PetscInt_FMT ",%" PetscInt_FMT "]: %g\n", f, fc, g, gc, (double)PetscRealPart(elemMat[eOffset + i * totDim + j])));
1042             }
1043           }
1044           PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
1045         }
1046       }
1047     }
1048     cOffset += totDim;
1049     cOffsetAux += totDimAux;
1050     eOffset += PetscSqr(totDim);
1051   }
1052   PetscFunctionReturn(PETSC_SUCCESS);
1053 }
1054 
1055 PETSC_INTERN PetscErrorCode PetscFEIntegrateHybridJacobian_Basic(PetscDS ds, PetscDS dsIn, PetscFEJacobianType jtype, PetscFormKey key, PetscInt s, PetscInt Ne, PetscFEGeom *fgeom, PetscFEGeom *nbrgeom, const PetscScalar coefficients[], const PetscScalar coefficients_t[], PetscDS dsAux, const PetscScalar coefficientsAux[], PetscReal t, PetscReal u_tshift, PetscScalar elemMat[])
1056 {
1057   const PetscInt      debug = ds->printIntegrate;
1058   PetscFE             feI, feJ;
1059   PetscWeakForm       wf;
1060   PetscBdPointJacFn **g0_func, **g1_func, **g2_func, **g3_func;
1061   PetscInt            n0, n1, n2, n3, i;
1062   PetscInt            cOffset    = 0; /* Offset into coefficients[] for element e */
1063   PetscInt            cOffsetAux = 0; /* Offset into coefficientsAux[] for element e */
1064   PetscInt            eOffset    = 0; /* Offset into elemMat[] for element e */
1065   PetscInt            offsetI    = 0; /* Offset into an element vector for fieldI */
1066   PetscInt            offsetJ    = 0; /* Offset into an element vector for fieldJ */
1067   PetscQuadrature     quad;
1068   DMPolytopeType      ct;
1069   PetscTabulation    *T, *TfIn, *TAux = NULL;
1070   PetscScalar        *g0, *g1, *g2, *g3, *u, *u_t = NULL, *u_x, *a, *a_x, *basisReal, *basisDerReal, *testReal, *testDerReal;
1071   const PetscScalar  *constants;
1072   PetscReal          *x;
1073   PetscInt           *uOff, *uOff_x, *aOff = NULL, *aOff_x = NULL;
1074   PetscInt            NcI = 0, NcJ = 0, NcS, NcT;
1075   PetscInt            dim, dimAux, numConstants, Nf, fieldI, fieldJ, NfAux = 0, totDim, totDimAux = 0, e;
1076   PetscBool           isCohesiveFieldI, isCohesiveFieldJ, auxOnBd = PETSC_FALSE;
1077   const PetscReal    *quadPoints, *quadWeights;
1078   PetscInt            qNc, Nq, q, dE;
1079 
1080   PetscFunctionBegin;
1081   PetscCall(PetscDSGetNumFields(ds, &Nf));
1082   fieldI = key.field / Nf;
1083   fieldJ = key.field % Nf;
1084   /* Hybrid discretization is posed directly on faces */
1085   PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&feI));
1086   PetscCall(PetscDSGetDiscretization(ds, fieldJ, (PetscObject *)&feJ));
1087   PetscCall(PetscFEGetSpatialDimension(feI, &dim));
1088   PetscCall(PetscFEGetQuadrature(feI, &quad));
1089   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
1090   PetscCall(PetscDSGetComponentOffsetsCohesive(ds, 0, &uOff)); // Change 0 to s for one-sided offsets
1091   PetscCall(PetscDSGetComponentDerivativeOffsetsCohesive(ds, s, &uOff_x));
1092   PetscCall(PetscDSGetWeakForm(ds, &wf));
1093   switch (jtype) {
1094   case PETSCFE_JACOBIAN_PRE:
1095     PetscCall(PetscWeakFormGetBdJacobianPreconditioner(wf, key.label, key.value, fieldI, fieldJ, key.part, &n0, &g0_func, &n1, &g1_func, &n2, &g2_func, &n3, &g3_func));
1096     break;
1097   case PETSCFE_JACOBIAN:
1098     PetscCall(PetscWeakFormGetBdJacobian(wf, key.label, key.value, fieldI, fieldJ, key.part, &n0, &g0_func, &n1, &g1_func, &n2, &g2_func, &n3, &g3_func));
1099     break;
1100   case PETSCFE_JACOBIAN_DYN:
1101     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "No boundary hybrid Jacobians :)");
1102   }
1103   if (!n0 && !n1 && !n2 && !n3) PetscFunctionReturn(PETSC_SUCCESS);
1104   PetscCall(PetscDSGetEvaluationArrays(ds, &u, coefficients_t ? &u_t : NULL, &u_x));
1105   PetscCall(PetscDSGetWorkspace(ds, &x, &basisReal, &basisDerReal, &testReal, &testDerReal));
1106   PetscCall(PetscDSGetWeakFormArrays(ds, NULL, NULL, &g0, &g1, &g2, &g3));
1107   PetscCall(PetscDSGetTabulation(ds, &T));
1108   PetscCall(PetscDSGetFaceTabulation(dsIn, &TfIn));
1109   PetscCall(PetscDSGetFieldOffsetCohesive(ds, fieldI, &offsetI));
1110   PetscCall(PetscDSGetFieldOffsetCohesive(ds, fieldJ, &offsetJ));
1111   PetscCall(PetscDSSetIntegrationParameters(ds, fieldI, fieldJ));
1112   PetscCall(PetscDSGetConstants(ds, &numConstants, &constants));
1113   if (dsAux) {
1114     PetscCall(PetscDSGetSpatialDimension(dsAux, &dimAux));
1115     PetscCall(PetscDSGetNumFields(dsAux, &NfAux));
1116     PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
1117     PetscCall(PetscDSGetComponentOffsets(dsAux, &aOff));
1118     PetscCall(PetscDSGetComponentDerivativeOffsets(dsAux, &aOff_x));
1119     PetscCall(PetscDSGetEvaluationArrays(dsAux, &a, NULL, &a_x));
1120     auxOnBd = dimAux == dim ? PETSC_TRUE : PETSC_FALSE;
1121     if (auxOnBd) PetscCall(PetscDSGetTabulation(dsAux, &TAux));
1122     else PetscCall(PetscDSGetFaceTabulation(dsAux, &TAux));
1123     PetscCheck(T[0]->Np == TAux[0]->Np, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of tabulation points %" PetscInt_FMT " != %" PetscInt_FMT " number of auxiliary tabulation points", T[0]->Np, TAux[0]->Np);
1124   }
1125   PetscCall(PetscDSGetCohesive(ds, fieldI, &isCohesiveFieldI));
1126   PetscCall(PetscDSGetCohesive(ds, fieldJ, &isCohesiveFieldJ));
1127   dE  = fgeom->dimEmbed;
1128   NcI = T[fieldI]->Nc;
1129   NcJ = T[fieldJ]->Nc;
1130   NcS = isCohesiveFieldI ? NcI : 2 * NcI;
1131   NcT = isCohesiveFieldJ ? NcJ : 2 * NcJ;
1132   if (!isCohesiveFieldI && s == 2) {
1133     // If we are integrating over a cohesive cell (s = 2) for a non-cohesive fields, we use both sides
1134     NcS *= 2;
1135   }
1136   if (!isCohesiveFieldJ && s == 2) {
1137     // If we are integrating over a cohesive cell (s = 2) for a non-cohesive fields, we use both sides
1138     NcT *= 2;
1139   }
1140   // The derivatives are constrained to be along the cell, so there are dim, not dE, components, even though
1141   // the coordinates are in dE dimensions
1142   PetscCall(PetscArrayzero(g0, NcS * NcT));
1143   PetscCall(PetscArrayzero(g1, NcS * NcT * dim));
1144   PetscCall(PetscArrayzero(g2, NcS * NcT * dim));
1145   PetscCall(PetscArrayzero(g3, NcS * NcT * dim * dim));
1146   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
1147   PetscCall(PetscQuadratureGetCellType(quad, &ct));
1148   PetscCheck(qNc == 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Only supports scalar quadrature, not %" PetscInt_FMT " components", qNc);
1149   for (e = 0; e < Ne; ++e) {
1150     PetscFEGeom    fegeom, fegeomN[2];
1151     const PetscInt face[2]  = {fgeom->face[e * 2 + 0][0], fgeom->face[e * 2 + 1][2]};
1152     const PetscInt ornt[2]  = {fgeom->face[e * 2 + 0][1], fgeom->face[e * 2 + 1][3]};
1153     const PetscInt cornt[2] = {fgeom->face[e * 2 + 0][3], fgeom->face[e * 2 + 1][1]};
1154 
1155     fegeom.v = x; /* Workspace */
1156     for (q = 0; q < Nq; ++q) {
1157       PetscInt  qpt[2];
1158       PetscReal w;
1159       PetscInt  c;
1160 
1161       PetscCall(PetscDSPermuteQuadPoint(ds, DMPolytopeTypeComposeOrientationInv(ct, cornt[0], ornt[0]), fieldI, q, &qpt[0]));
1162       PetscCall(PetscDSPermuteQuadPoint(ds, DMPolytopeTypeComposeOrientationInv(ct, ornt[1], cornt[1]), fieldI, q, &qpt[1]));
1163       PetscCall(PetscFEGeomGetPoint(fgeom, e * 2, q, &quadPoints[q * fgeom->dim], &fegeom));
1164       PetscCall(PetscFEGeomGetPoint(nbrgeom, e * 2, q, NULL, &fegeomN[0]));
1165       PetscCall(PetscFEGeomGetPoint(nbrgeom, e * 2 + 1, q, NULL, &fegeomN[1]));
1166       w = fegeom.detJ[0] * quadWeights[q];
1167       if (debug > 1 && q < fgeom->numPoints) {
1168         PetscCall(PetscPrintf(PETSC_COMM_SELF, "  detJ: %g\n", (double)fegeom.detJ[0]));
1169 #if !defined(PETSC_USE_COMPLEX)
1170         PetscCall(DMPrintCellMatrix(e, "invJ", dim, dim, fegeom.invJ));
1171 #endif
1172       }
1173       if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  quad point %" PetscInt_FMT "\n", q));
1174       if (coefficients) PetscCall(PetscFEEvaluateFieldJets_Hybrid_Internal(dsIn, Nf, 0, q, T, face, qpt, TfIn, &fegeom, fegeomN, &coefficients[cOffset], PetscSafePointerPlusOffset(coefficients_t, cOffset), u, u_x, u_t));
1175       if (dsAux) PetscCall(PetscFEEvaluateFieldJets_Internal(dsAux, NfAux, auxOnBd ? 0 : face[s], auxOnBd ? q : qpt[s], TAux, &fegeom, &coefficientsAux[cOffsetAux], NULL, a, a_x, NULL));
1176       if (n0) {
1177         PetscCall(PetscArrayzero(g0, NcS * NcT));
1178         for (i = 0; i < n0; ++i) g0_func[i](dE, Nf, NfAux, uOff, uOff_x, u, u_t, u_x, aOff, aOff_x, a, NULL, a_x, t, u_tshift, fegeom.v, fegeom.n, numConstants, constants, g0);
1179         for (c = 0; c < NcS * NcT; ++c) g0[c] *= w;
1180       }
1181       if (n1) {
1182         PetscCall(PetscArrayzero(g1, NcS * NcT * dim));
1183         for (i = 0; i < n1; ++i) g1_func[i](dE, Nf, NfAux, uOff, uOff_x, u, u_t, u_x, aOff, aOff_x, a, NULL, a_x, t, u_tshift, fegeom.v, fegeom.n, numConstants, constants, g1);
1184         for (c = 0; c < NcS * NcT * dim; ++c) g1[c] *= w;
1185       }
1186       if (n2) {
1187         PetscCall(PetscArrayzero(g2, NcS * NcT * dim));
1188         for (i = 0; i < n2; ++i) g2_func[i](dE, Nf, NfAux, uOff, uOff_x, u, u_t, u_x, aOff, aOff_x, a, NULL, a_x, t, u_tshift, fegeom.v, fegeom.n, numConstants, constants, g2);
1189         for (c = 0; c < NcS * NcT * dim; ++c) g2[c] *= w;
1190       }
1191       if (n3) {
1192         PetscCall(PetscArrayzero(g3, NcS * NcT * dim * dim));
1193         for (i = 0; i < n3; ++i) g3_func[i](dE, Nf, NfAux, uOff, uOff_x, u, u_t, u_x, aOff, aOff_x, a, NULL, a_x, t, u_tshift, fegeom.v, fegeom.n, numConstants, constants, g3);
1194         for (c = 0; c < NcS * NcT * dim * dim; ++c) g3[c] *= w;
1195       }
1196 
1197       if (isCohesiveFieldI) {
1198         if (isCohesiveFieldJ) {
1199           //PetscCall(PetscFEUpdateElementMat_Internal(feI, feJ, 0, q, T[fieldI], basisReal, basisDerReal, T[fieldJ], testReal, testDerReal, &fegeom, g0, g1, g2, g3, totDim, offsetI, offsetJ, elemMat + eOffset));
1200           PetscCall(PetscFEUpdateElementMat_Hybrid_Internal(feI, isCohesiveFieldI, feJ, isCohesiveFieldJ, 0, 0, 0, q, T[fieldI], basisReal, basisDerReal, T[fieldJ], testReal, testDerReal, &fegeom, g0, g1, g2, g3, eOffset, totDim, offsetI, offsetJ, elemMat));
1201         } else {
1202           PetscCall(PetscFEUpdateElementMat_Hybrid_Internal(feI, isCohesiveFieldI, feJ, isCohesiveFieldJ, 0, 0, 0, q, T[fieldI], basisReal, basisDerReal, T[fieldJ], testReal, testDerReal, &fegeom, g0, g1, g2, g3, eOffset, totDim, offsetI, offsetJ, elemMat));
1203           PetscCall(PetscFEUpdateElementMat_Hybrid_Internal(feI, isCohesiveFieldI, feJ, isCohesiveFieldJ, 0, 1, 1, q, T[fieldI], basisReal, basisDerReal, T[fieldJ], testReal, testDerReal, &fegeom, &g0[NcI * NcJ], &g1[NcI * NcJ * dim], &g2[NcI * NcJ * dim], &g3[NcI * NcJ * dim * dim], eOffset, totDim, offsetI, offsetJ, elemMat));
1204         }
1205       } else {
1206         if (s == 2) {
1207           if (isCohesiveFieldJ) {
1208             PetscCall(PetscFEUpdateElementMat_Hybrid_Internal(feI, isCohesiveFieldI, feJ, isCohesiveFieldJ, 0, 0, 0, q, T[fieldI], basisReal, basisDerReal, T[fieldJ], testReal, testDerReal, &fegeom, g0, g1, g2, g3, eOffset, totDim, offsetI, offsetJ, elemMat));
1209             PetscCall(PetscFEUpdateElementMat_Hybrid_Internal(feI, isCohesiveFieldI, feJ, isCohesiveFieldJ, 0, 1, 1, q, T[fieldI], basisReal, basisDerReal, T[fieldJ], testReal, testDerReal, &fegeom, &g0[NcI * NcJ], &g1[NcI * NcJ * dim], &g2[NcI * NcJ * dim], &g3[NcI * NcJ * dim * dim], eOffset, totDim, offsetI, offsetJ, elemMat));
1210           } else {
1211             PetscCall(PetscFEUpdateElementMat_Hybrid_Internal(feI, isCohesiveFieldI, feJ, isCohesiveFieldJ, 0, 0, 0, q, T[fieldI], basisReal, basisDerReal, T[fieldJ], testReal, testDerReal, &fegeom, g0, g1, g2, g3, eOffset, totDim, offsetI, offsetJ, elemMat));
1212             PetscCall(PetscFEUpdateElementMat_Hybrid_Internal(feI, isCohesiveFieldI, feJ, isCohesiveFieldJ, 0, 0, 1, q, T[fieldI], basisReal, basisDerReal, T[fieldJ], testReal, testDerReal, &fegeom, &g0[NcI * NcJ], &g1[NcI * NcJ * dim], &g2[NcI * NcJ * dim], &g3[NcI * NcJ * dim * dim], eOffset, totDim, offsetI, offsetJ, elemMat));
1213             PetscCall(PetscFEUpdateElementMat_Hybrid_Internal(feI, isCohesiveFieldI, feJ, isCohesiveFieldJ, 0, 1, 0, q, T[fieldI], basisReal, basisDerReal, T[fieldJ], testReal, testDerReal, &fegeom, &g0[NcI * NcJ * 2], &g1[NcI * NcJ * dim * 2], &g2[NcI * NcJ * dim * 2], &g3[NcI * NcJ * dim * dim * 2], eOffset, totDim, offsetI, offsetJ, elemMat));
1214             PetscCall(PetscFEUpdateElementMat_Hybrid_Internal(feI, isCohesiveFieldI, feJ, isCohesiveFieldJ, 0, 1, 1, q, T[fieldI], basisReal, basisDerReal, T[fieldJ], testReal, testDerReal, &fegeom, &g0[NcI * NcJ * 3], &g1[NcI * NcJ * dim * 3], &g2[NcI * NcJ * dim * 3], &g3[NcI * NcJ * dim * dim * 3], eOffset, totDim, offsetI, offsetJ, elemMat));
1215           }
1216         } else
1217           PetscCall(PetscFEUpdateElementMat_Hybrid_Internal(feI, isCohesiveFieldI, feJ, isCohesiveFieldJ, 0, s, s, q, T[fieldI], basisReal, basisDerReal, T[fieldJ], testReal, testDerReal, &fegeom, g0, g1, g2, g3, eOffset, totDim, offsetI, offsetJ, elemMat));
1218       }
1219     }
1220     if (debug > 1) {
1221       const PetscInt fS = 0 + (isCohesiveFieldI ? 0 : (s == 2 ? 0 : s * T[fieldI]->Nb));
1222       const PetscInt fE = T[fieldI]->Nb + (isCohesiveFieldI ? 0 : (s == 2 ? T[fieldI]->Nb : s * T[fieldI]->Nb));
1223       const PetscInt gS = 0 + (isCohesiveFieldJ ? 0 : (s == 2 ? 0 : s * T[fieldJ]->Nb));
1224       const PetscInt gE = T[fieldJ]->Nb + (isCohesiveFieldJ ? 0 : (s == 2 ? T[fieldJ]->Nb : s * T[fieldJ]->Nb));
1225       PetscInt       f, g;
1226 
1227       PetscCall(PetscPrintf(PETSC_COMM_SELF, "Element matrix for fields %" PetscInt_FMT " and %" PetscInt_FMT " s %s totDim %" PetscInt_FMT " offsets (%" PetscInt_FMT ", %" PetscInt_FMT ", %" PetscInt_FMT ")\n", fieldI, fieldJ, s ? (s > 1 ? "Coh" : "Pos") : "Neg", totDim, eOffset, offsetI, offsetJ));
1228       for (f = fS; f < fE; ++f) {
1229         const PetscInt i = offsetI + f;
1230         for (g = gS; g < gE; ++g) {
1231           const PetscInt j = offsetJ + g;
1232           PetscCheck(i < totDim && j < totDim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Fuck up %" PetscInt_FMT " %" PetscInt_FMT " %" PetscInt_FMT " %" PetscInt_FMT, f, i, g, j);
1233           PetscCall(PetscPrintf(PETSC_COMM_SELF, "    elemMat[%" PetscInt_FMT ",%" PetscInt_FMT ",%" PetscInt_FMT ",%" PetscInt_FMT "]: %g\n", f / NcI, f % NcI, g / NcJ, g % NcJ, (double)PetscRealPart(elemMat[eOffset + i * totDim + j])));
1234         }
1235         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
1236       }
1237     }
1238     cOffset += totDim;
1239     cOffsetAux += totDimAux;
1240     eOffset += PetscSqr(totDim);
1241   }
1242   PetscFunctionReturn(PETSC_SUCCESS);
1243 }
1244 
1245 static PetscErrorCode PetscFEInitialize_Basic(PetscFE fem)
1246 {
1247   PetscFunctionBegin;
1248   fem->ops->setfromoptions          = NULL;
1249   fem->ops->setup                   = PetscFESetUp_Basic;
1250   fem->ops->view                    = PetscFEView_Basic;
1251   fem->ops->destroy                 = PetscFEDestroy_Basic;
1252   fem->ops->getdimension            = PetscFEGetDimension_Basic;
1253   fem->ops->computetabulation       = PetscFEComputeTabulation_Basic;
1254   fem->ops->integrate               = PetscFEIntegrate_Basic;
1255   fem->ops->integratebd             = PetscFEIntegrateBd_Basic;
1256   fem->ops->integrateresidual       = PetscFEIntegrateResidual_Basic;
1257   fem->ops->integratebdresidual     = PetscFEIntegrateBdResidual_Basic;
1258   fem->ops->integratehybridresidual = PetscFEIntegrateHybridResidual_Basic;
1259   fem->ops->integratejacobianaction = NULL /* PetscFEIntegrateJacobianAction_Basic */;
1260   fem->ops->integratejacobian       = PetscFEIntegrateJacobian_Basic;
1261   fem->ops->integratebdjacobian     = PetscFEIntegrateBdJacobian_Basic;
1262   fem->ops->integratehybridjacobian = PetscFEIntegrateHybridJacobian_Basic;
1263   PetscFunctionReturn(PETSC_SUCCESS);
1264 }
1265 
1266 /*MC
1267   PETSCFEBASIC = "basic" - A `PetscFE` object that integrates with basic tiling and no vectorization
1268 
1269   Level: intermediate
1270 
1271 .seealso: `PetscFE`, `PetscFEType`, `PetscFECreate()`, `PetscFESetType()`
1272 M*/
1273 
1274 PETSC_EXTERN PetscErrorCode PetscFECreate_Basic(PetscFE fem)
1275 {
1276   PetscFE_Basic *b;
1277 
1278   PetscFunctionBegin;
1279   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
1280   PetscCall(PetscNew(&b));
1281   fem->data = b;
1282 
1283   PetscCall(PetscFEInitialize_Basic(fem));
1284   PetscFunctionReturn(PETSC_SUCCESS);
1285 }
1286