xref: /petsc/src/dm/dt/fe/impls/basic/febasic.c (revision 9140fee14acbea959c6ed74f4836a1a89f708038)
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   n = pdim;
79   PetscCallBLAS("LAPACKgetrf", LAPACKREALgetrf_(&n, &n, fem->invV, &n, pivots, &info));
80   PetscCheck(!info, PETSC_COMM_SELF, PETSC_ERR_LIB, "Error returned from LAPACKgetrf %" PetscInt_FMT, (PetscInt)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 %" PetscInt_FMT, (PetscInt)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 PetscFECreateTabulation_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 = 0;
165   PetscFE            fe;
166   PetscPointFunc     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;
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   PetscCall(PetscFEGetQuadrature(fe, &quad));
184   PetscCall(PetscDSGetNumFields(ds, &Nf));
185   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
186   PetscCall(PetscDSGetComponentOffsets(ds, &uOff));
187   PetscCall(PetscDSGetComponentDerivativeOffsets(ds, &uOff_x));
188   PetscCall(PetscDSGetTabulation(ds, &T));
189   PetscCall(PetscDSGetEvaluationArrays(ds, &u, NULL, &u_x));
190   PetscCall(PetscDSGetWorkspace(ds, &x, NULL, NULL, NULL, NULL));
191   PetscCall(PetscDSGetConstants(ds, &numConstants, &constants));
192   if (dsAux) {
193     PetscCall(PetscDSGetNumFields(dsAux, &NfAux));
194     PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
195     PetscCall(PetscDSGetComponentOffsets(dsAux, &aOff));
196     PetscCall(PetscDSGetComponentDerivativeOffsets(dsAux, &aOff_x));
197     PetscCall(PetscDSGetTabulation(dsAux, &TAux));
198     PetscCall(PetscDSGetEvaluationArrays(dsAux, &a, NULL, &a_x));
199     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);
200   }
201   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
202   PetscCheck(qNc == 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Only supports scalar quadrature, not %" PetscInt_FMT " components", qNc);
203   Np       = cgeom->numPoints;
204   dE       = cgeom->dimEmbed;
205   isAffine = cgeom->isAffine;
206   for (e = 0; e < Ne; ++e) {
207     PetscFEGeom fegeom;
208 
209     fegeom.dim      = cgeom->dim;
210     fegeom.dimEmbed = cgeom->dimEmbed;
211     if (isAffine) {
212       fegeom.v    = x;
213       fegeom.xi   = cgeom->xi;
214       fegeom.J    = &cgeom->J[e * Np * dE * dE];
215       fegeom.invJ = &cgeom->invJ[e * Np * dE * dE];
216       fegeom.detJ = &cgeom->detJ[e * Np];
217     }
218     for (q = 0; q < Nq; ++q) {
219       PetscScalar integrand;
220       PetscReal   w;
221 
222       if (isAffine) {
223         CoordinatesRefToReal(dE, dim, fegeom.xi, &cgeom->v[e * Np * dE], fegeom.J, &quadPoints[q * dim], x);
224       } else {
225         fegeom.v    = &cgeom->v[(e * Np + q) * dE];
226         fegeom.J    = &cgeom->J[(e * Np + q) * dE * dE];
227         fegeom.invJ = &cgeom->invJ[(e * Np + q) * dE * dE];
228         fegeom.detJ = &cgeom->detJ[e * Np + q];
229       }
230       w = fegeom.detJ[0] * quadWeights[q];
231       if (debug > 1 && q < Np) {
232         PetscCall(PetscPrintf(PETSC_COMM_SELF, "  detJ: %g\n", (double)fegeom.detJ[0]));
233 #if !defined(PETSC_USE_COMPLEX)
234         PetscCall(DMPrintCellMatrix(e, "invJ", dim, dim, fegeom.invJ));
235 #endif
236       }
237       if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  quad point %" PetscInt_FMT "\n", q));
238       PetscCall(PetscFEEvaluateFieldJets_Internal(ds, Nf, 0, q, T, &fegeom, &coefficients[cOffset], NULL, u, u_x, NULL));
239       if (dsAux) PetscCall(PetscFEEvaluateFieldJets_Internal(dsAux, NfAux, 0, q, TAux, &fegeom, &coefficientsAux[cOffsetAux], NULL, a, a_x, NULL));
240       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);
241       integrand *= w;
242       integral[e * Nf + field] += integrand;
243       if (debug > 1) PetscCall(PetscPrintf(PETSC_COMM_SELF, "    int: %g %g\n", (double)PetscRealPart(integrand), (double)PetscRealPart(integral[field])));
244     }
245     cOffset += totDim;
246     cOffsetAux += totDimAux;
247   }
248   PetscFunctionReturn(PETSC_SUCCESS);
249 }
250 
251 PETSC_INTERN PetscErrorCode PetscFEIntegrateBd_Basic(PetscDS ds, PetscInt field, PetscBdPointFunc obj_func, PetscInt Ne, PetscFEGeom *fgeom, const PetscScalar coefficients[], PetscDS dsAux, const PetscScalar coefficientsAux[], PetscScalar integral[])
252 {
253   const PetscInt     debug = 0;
254   PetscFE            fe;
255   PetscQuadrature    quad;
256   PetscTabulation   *Tf, *TfAux = NULL;
257   PetscScalar       *u, *u_x, *a, *a_x, *basisReal, *basisDerReal;
258   const PetscScalar *constants;
259   PetscReal         *x;
260   PetscInt          *uOff, *uOff_x, *aOff = NULL, *aOff_x = NULL;
261   PetscBool          isAffine, auxOnBd;
262   const PetscReal   *quadPoints, *quadWeights;
263   PetscInt           qNc, Nq, q, Np, dE;
264   PetscInt           dim, dimAux, numConstants, Nf, NfAux = 0, totDim, totDimAux = 0, cOffset = 0, cOffsetAux = 0, e;
265 
266   PetscFunctionBegin;
267   if (!obj_func) PetscFunctionReturn(PETSC_SUCCESS);
268   PetscCall(PetscDSGetDiscretization(ds, field, (PetscObject *)&fe));
269   PetscCall(PetscFEGetSpatialDimension(fe, &dim));
270   PetscCall(PetscFEGetFaceQuadrature(fe, &quad));
271   PetscCall(PetscDSGetNumFields(ds, &Nf));
272   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
273   PetscCall(PetscDSGetComponentOffsets(ds, &uOff));
274   PetscCall(PetscDSGetComponentDerivativeOffsets(ds, &uOff_x));
275   PetscCall(PetscDSGetEvaluationArrays(ds, &u, NULL, &u_x));
276   PetscCall(PetscDSGetWorkspace(ds, &x, &basisReal, &basisDerReal, NULL, NULL));
277   PetscCall(PetscDSGetFaceTabulation(ds, &Tf));
278   PetscCall(PetscDSGetConstants(ds, &numConstants, &constants));
279   if (dsAux) {
280     PetscCall(PetscDSGetSpatialDimension(dsAux, &dimAux));
281     PetscCall(PetscDSGetNumFields(dsAux, &NfAux));
282     PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
283     PetscCall(PetscDSGetComponentOffsets(dsAux, &aOff));
284     PetscCall(PetscDSGetComponentDerivativeOffsets(dsAux, &aOff_x));
285     PetscCall(PetscDSGetEvaluationArrays(dsAux, &a, NULL, &a_x));
286     auxOnBd = dimAux < dim ? PETSC_TRUE : PETSC_FALSE;
287     if (auxOnBd) PetscCall(PetscDSGetTabulation(dsAux, &TfAux));
288     else PetscCall(PetscDSGetFaceTabulation(dsAux, &TfAux));
289     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);
290   }
291   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
292   PetscCheck(qNc == 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Only supports scalar quadrature, not %" PetscInt_FMT " components", qNc);
293   Np       = fgeom->numPoints;
294   dE       = fgeom->dimEmbed;
295   isAffine = fgeom->isAffine;
296   for (e = 0; e < Ne; ++e) {
297     PetscFEGeom    fegeom, cgeom;
298     const PetscInt face = fgeom->face[e][0]; /* Local face number in cell */
299     fegeom.n            = NULL;
300     fegeom.v            = NULL;
301     fegeom.J            = NULL;
302     fegeom.detJ         = NULL;
303     fegeom.dim          = fgeom->dim;
304     fegeom.dimEmbed     = fgeom->dimEmbed;
305     cgeom.dim           = fgeom->dim;
306     cgeom.dimEmbed      = fgeom->dimEmbed;
307     if (isAffine) {
308       fegeom.v    = x;
309       fegeom.xi   = fgeom->xi;
310       fegeom.J    = &fgeom->J[e * Np * dE * dE];
311       fegeom.invJ = &fgeom->invJ[e * Np * dE * dE];
312       fegeom.detJ = &fgeom->detJ[e * Np];
313       fegeom.n    = &fgeom->n[e * Np * dE];
314 
315       cgeom.J    = &fgeom->suppJ[0][e * Np * dE * dE];
316       cgeom.invJ = &fgeom->suppInvJ[0][e * Np * dE * dE];
317       cgeom.detJ = &fgeom->suppDetJ[0][e * Np];
318     }
319     for (q = 0; q < Nq; ++q) {
320       PetscScalar integrand;
321       PetscReal   w;
322 
323       if (isAffine) {
324         CoordinatesRefToReal(dE, dim - 1, fegeom.xi, &fgeom->v[e * Np * dE], fegeom.J, &quadPoints[q * (dim - 1)], x);
325       } else {
326         fegeom.v    = &fgeom->v[(e * Np + q) * dE];
327         fegeom.J    = &fgeom->J[(e * Np + q) * dE * dE];
328         fegeom.invJ = &fgeom->invJ[(e * Np + q) * dE * dE];
329         fegeom.detJ = &fgeom->detJ[e * Np + q];
330         fegeom.n    = &fgeom->n[(e * Np + q) * dE];
331 
332         cgeom.J    = &fgeom->suppJ[0][(e * Np + q) * dE * dE];
333         cgeom.invJ = &fgeom->suppInvJ[0][(e * Np + q) * dE * dE];
334         cgeom.detJ = &fgeom->suppDetJ[0][e * Np + q];
335       }
336       w = fegeom.detJ[0] * quadWeights[q];
337       if (debug > 1 && q < Np) {
338         PetscCall(PetscPrintf(PETSC_COMM_SELF, "  detJ: %g\n", (double)fegeom.detJ[0]));
339 #ifndef PETSC_USE_COMPLEX
340         PetscCall(DMPrintCellMatrix(e, "invJ", dim, dim, fegeom.invJ));
341 #endif
342       }
343       if (debug > 1) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  quad point %" PetscInt_FMT "\n", q));
344       PetscCall(PetscFEEvaluateFieldJets_Internal(ds, Nf, face, q, Tf, &cgeom, &coefficients[cOffset], NULL, u, u_x, NULL));
345       if (dsAux) PetscCall(PetscFEEvaluateFieldJets_Internal(dsAux, NfAux, face, q, TfAux, &cgeom, &coefficientsAux[cOffsetAux], NULL, a, a_x, NULL));
346       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);
347       integrand *= w;
348       integral[e * Nf + field] += integrand;
349       if (debug > 1) PetscCall(PetscPrintf(PETSC_COMM_SELF, "    int: %g %g\n", (double)PetscRealPart(integrand), (double)PetscRealPart(integral[e * Nf + field])));
350     }
351     cOffset += totDim;
352     cOffsetAux += totDimAux;
353   }
354   PetscFunctionReturn(PETSC_SUCCESS);
355 }
356 
357 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[])
358 {
359   const PetscInt     debug = 0;
360   const PetscInt     field = key.field;
361   PetscFE            fe;
362   PetscWeakForm      wf;
363   PetscInt           n0, n1, i;
364   PetscPointFunc    *f0_func, *f1_func;
365   PetscQuadrature    quad;
366   PetscTabulation   *T, *TAux = NULL;
367   PetscScalar       *f0, *f1, *u, *u_t = NULL, *u_x, *a, *a_x, *basisReal, *basisDerReal;
368   const PetscScalar *constants;
369   PetscReal         *x;
370   PetscInt          *uOff, *uOff_x, *aOff = NULL, *aOff_x = NULL;
371   PetscInt           dim, numConstants, Nf, NfAux = 0, totDim, totDimAux = 0, cOffset = 0, cOffsetAux = 0, fOffset, e;
372   const PetscReal   *quadPoints, *quadWeights;
373   PetscInt           qdim, qNc, Nq, q, dE;
374 
375   PetscFunctionBegin;
376   PetscCall(PetscDSGetDiscretization(ds, field, (PetscObject *)&fe));
377   PetscCall(PetscFEGetSpatialDimension(fe, &dim));
378   PetscCall(PetscFEGetQuadrature(fe, &quad));
379   PetscCall(PetscDSGetNumFields(ds, &Nf));
380   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
381   PetscCall(PetscDSGetComponentOffsets(ds, &uOff));
382   PetscCall(PetscDSGetComponentDerivativeOffsets(ds, &uOff_x));
383   PetscCall(PetscDSGetFieldOffset(ds, field, &fOffset));
384   PetscCall(PetscDSGetWeakForm(ds, &wf));
385   PetscCall(PetscWeakFormGetResidual(wf, key.label, key.value, key.field, key.part, &n0, &f0_func, &n1, &f1_func));
386   if (!n0 && !n1) PetscFunctionReturn(PETSC_SUCCESS);
387   PetscCall(PetscDSGetEvaluationArrays(ds, &u, coefficients_t ? &u_t : NULL, &u_x));
388   PetscCall(PetscDSGetWorkspace(ds, &x, &basisReal, &basisDerReal, NULL, NULL));
389   PetscCall(PetscDSGetWeakFormArrays(ds, &f0, &f1, NULL, NULL, NULL, NULL));
390   PetscCall(PetscDSGetTabulation(ds, &T));
391   PetscCall(PetscDSGetConstants(ds, &numConstants, &constants));
392   if (dsAux) {
393     PetscCall(PetscDSGetNumFields(dsAux, &NfAux));
394     PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
395     PetscCall(PetscDSGetComponentOffsets(dsAux, &aOff));
396     PetscCall(PetscDSGetComponentDerivativeOffsets(dsAux, &aOff_x));
397     PetscCall(PetscDSGetEvaluationArrays(dsAux, &a, NULL, &a_x));
398     PetscCall(PetscDSGetTabulation(dsAux, &TAux));
399     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);
400   }
401   PetscCall(PetscQuadratureGetData(quad, &qdim, &qNc, &Nq, &quadPoints, &quadWeights));
402   PetscCheck(qNc == 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Only supports scalar quadrature, not %" PetscInt_FMT " components", qNc);
403   dE = cgeom->dimEmbed;
404   PetscCheck(cgeom->dim == qdim, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "FEGeom dim %" PetscInt_FMT " != %" PetscInt_FMT " quadrature dim", cgeom->dim, qdim);
405   for (e = 0; e < Ne; ++e) {
406     PetscFEGeom fegeom;
407 
408     fegeom.v = x; /* workspace */
409     PetscCall(PetscArrayzero(f0, Nq * T[field]->Nc));
410     PetscCall(PetscArrayzero(f1, Nq * T[field]->Nc * dE));
411     for (q = 0; q < Nq; ++q) {
412       PetscReal w;
413       PetscInt  c, d;
414 
415       PetscCall(PetscFEGeomGetPoint(cgeom, e, q, &quadPoints[q * cgeom->dim], &fegeom));
416       w = fegeom.detJ[0] * quadWeights[q];
417       if (debug > 1 && q < cgeom->numPoints) {
418         PetscCall(PetscPrintf(PETSC_COMM_SELF, "  detJ: %g\n", (double)fegeom.detJ[0]));
419 #if !defined(PETSC_USE_COMPLEX)
420         PetscCall(DMPrintCellMatrix(e, "invJ", dim, dim, fegeom.invJ));
421 #endif
422       }
423       PetscCall(PetscFEEvaluateFieldJets_Internal(ds, Nf, 0, q, T, &fegeom, &coefficients[cOffset], &coefficients_t[cOffset], u, u_x, u_t));
424       if (dsAux) PetscCall(PetscFEEvaluateFieldJets_Internal(dsAux, NfAux, 0, q, TAux, &fegeom, &coefficientsAux[cOffsetAux], NULL, a, a_x, NULL));
425       for (i = 0; i < n0; ++i) f0_func[i](dim, 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]);
426       for (c = 0; c < T[field]->Nc; ++c) f0[q * T[field]->Nc + c] *= w;
427       for (i = 0; i < n1; ++i) f1_func[i](dim, 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 * dim]);
428       for (c = 0; c < T[field]->Nc; ++c)
429         for (d = 0; d < dim; ++d) f1[(q * T[field]->Nc + c) * dim + d] *= w;
430       if (debug) {
431         // LCOV_EXCL_START
432         PetscCall(PetscPrintf(PETSC_COMM_SELF, "  quad point %" PetscInt_FMT " wt %g x:", q, (double)quadWeights[q]));
433         for (c = 0; c < dE; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %g", (double)fegeom.v[c]));
434         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
435         if (debug > 2) {
436           PetscCall(PetscPrintf(PETSC_COMM_SELF, "  field %" PetscInt_FMT ":", field));
437           for (c = 0; c < T[field]->Nc; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %g", (double)PetscRealPart(u[uOff[field] + c])));
438           PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
439           PetscCall(PetscPrintf(PETSC_COMM_SELF, "  field der %" PetscInt_FMT ":", field));
440           for (c = 0; c < T[field]->Nc * dE; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %g", (double)PetscRealPart(u_x[uOff[field] + c])));
441           PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
442           PetscCall(PetscPrintf(PETSC_COMM_SELF, "  resid %" PetscInt_FMT ":", field));
443           for (c = 0; c < T[field]->Nc; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %g", (double)PetscRealPart(f0[q * T[field]->Nc + c])));
444           PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
445           PetscCall(PetscPrintf(PETSC_COMM_SELF, "  res der %" PetscInt_FMT ":", field));
446           for (c = 0; c < T[field]->Nc; ++c) {
447             for (d = 0; d < dim; ++d) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %g", (double)PetscRealPart(f1[(q * T[field]->Nc + c) * dim + d])));
448           }
449           PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
450         }
451         // LCOV_EXCL_STOP
452       }
453     }
454     PetscCall(PetscFEUpdateElementVec_Internal(fe, T[field], 0, basisReal, basisDerReal, e, cgeom, f0, f1, &elemVec[cOffset + fOffset]));
455     cOffset += totDim;
456     cOffsetAux += totDimAux;
457   }
458   PetscFunctionReturn(PETSC_SUCCESS);
459 }
460 
461 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[])
462 {
463   const PetscInt     debug = 0;
464   const PetscInt     field = key.field;
465   PetscFE            fe;
466   PetscInt           n0, n1, i;
467   PetscBdPointFunc  *f0_func, *f1_func;
468   PetscQuadrature    quad;
469   PetscTabulation   *Tf, *TfAux = NULL;
470   PetscScalar       *f0, *f1, *u, *u_t = NULL, *u_x, *a, *a_x, *basisReal, *basisDerReal;
471   const PetscScalar *constants;
472   PetscReal         *x;
473   PetscInt          *uOff, *uOff_x, *aOff = NULL, *aOff_x = NULL;
474   PetscInt           dim, dimAux, numConstants, Nf, NfAux = 0, totDim, totDimAux = 0, cOffset = 0, cOffsetAux = 0, fOffset, e, NcI;
475   PetscBool          auxOnBd = PETSC_FALSE;
476   const PetscReal   *quadPoints, *quadWeights;
477   PetscInt           qdim, qNc, Nq, q, dE;
478 
479   PetscFunctionBegin;
480   PetscCall(PetscDSGetDiscretization(ds, field, (PetscObject *)&fe));
481   PetscCall(PetscFEGetSpatialDimension(fe, &dim));
482   PetscCall(PetscFEGetFaceQuadrature(fe, &quad));
483   PetscCall(PetscDSGetNumFields(ds, &Nf));
484   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
485   PetscCall(PetscDSGetComponentOffsets(ds, &uOff));
486   PetscCall(PetscDSGetComponentDerivativeOffsets(ds, &uOff_x));
487   PetscCall(PetscDSGetFieldOffset(ds, field, &fOffset));
488   PetscCall(PetscWeakFormGetBdResidual(wf, key.label, key.value, key.field, key.part, &n0, &f0_func, &n1, &f1_func));
489   if (!n0 && !n1) PetscFunctionReturn(PETSC_SUCCESS);
490   PetscCall(PetscDSGetEvaluationArrays(ds, &u, coefficients_t ? &u_t : NULL, &u_x));
491   PetscCall(PetscDSGetWorkspace(ds, &x, &basisReal, &basisDerReal, NULL, NULL));
492   PetscCall(PetscDSGetWeakFormArrays(ds, &f0, &f1, NULL, NULL, NULL, NULL));
493   PetscCall(PetscDSGetFaceTabulation(ds, &Tf));
494   PetscCall(PetscDSGetConstants(ds, &numConstants, &constants));
495   if (dsAux) {
496     PetscCall(PetscDSGetSpatialDimension(dsAux, &dimAux));
497     PetscCall(PetscDSGetNumFields(dsAux, &NfAux));
498     PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
499     PetscCall(PetscDSGetComponentOffsets(dsAux, &aOff));
500     PetscCall(PetscDSGetComponentDerivativeOffsets(dsAux, &aOff_x));
501     PetscCall(PetscDSGetEvaluationArrays(dsAux, &a, NULL, &a_x));
502     auxOnBd = dimAux < dim ? PETSC_TRUE : PETSC_FALSE;
503     if (auxOnBd) PetscCall(PetscDSGetTabulation(dsAux, &TfAux));
504     else PetscCall(PetscDSGetFaceTabulation(dsAux, &TfAux));
505     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);
506   }
507   NcI = Tf[field]->Nc;
508   PetscCall(PetscQuadratureGetData(quad, &qdim, &qNc, &Nq, &quadPoints, &quadWeights));
509   PetscCheck(qNc == 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Only supports scalar quadrature, not %" PetscInt_FMT " components", qNc);
510   dE = fgeom->dimEmbed;
511   /* TODO FIX THIS */
512   fgeom->dim = dim - 1;
513   PetscCheck(fgeom->dim == qdim, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "FEGeom dim %" PetscInt_FMT " != %" PetscInt_FMT " quadrature dim", fgeom->dim, qdim);
514   for (e = 0; e < Ne; ++e) {
515     PetscFEGeom    fegeom, cgeom;
516     const PetscInt face = fgeom->face[e][0];
517 
518     fegeom.v = x; /* Workspace */
519     PetscCall(PetscArrayzero(f0, Nq * NcI));
520     PetscCall(PetscArrayzero(f1, Nq * NcI * dE));
521     for (q = 0; q < Nq; ++q) {
522       PetscReal w;
523       PetscInt  c, d;
524 
525       PetscCall(PetscFEGeomGetPoint(fgeom, e, q, &quadPoints[q * fgeom->dim], &fegeom));
526       PetscCall(PetscFEGeomGetCellPoint(fgeom, e, q, &cgeom));
527       w = fegeom.detJ[0] * quadWeights[q];
528       if (debug > 1) {
529         if ((fgeom->isAffine && q == 0) || (!fgeom->isAffine)) {
530           PetscCall(PetscPrintf(PETSC_COMM_SELF, "  detJ: %g\n", (double)fegeom.detJ[0]));
531 #if !defined(PETSC_USE_COMPLEX)
532           PetscCall(DMPrintCellMatrix(e, "invJ", dim, dim, fegeom.invJ));
533           PetscCall(DMPrintCellVector(e, "n", dim, fegeom.n));
534 #endif
535         }
536       }
537       PetscCall(PetscFEEvaluateFieldJets_Internal(ds, Nf, face, q, Tf, &cgeom, &coefficients[cOffset], &coefficients_t[cOffset], u, u_x, u_t));
538       if (dsAux) PetscCall(PetscFEEvaluateFieldJets_Internal(dsAux, NfAux, auxOnBd ? 0 : face, q, TfAux, &cgeom, &coefficientsAux[cOffsetAux], NULL, a, a_x, NULL));
539       for (i = 0; i < n0; ++i) f0_func[i](dim, 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]);
540       for (c = 0; c < NcI; ++c) f0[q * NcI + c] *= w;
541       for (i = 0; i < n1; ++i) f1_func[i](dim, 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 * dim]);
542       for (c = 0; c < NcI; ++c)
543         for (d = 0; d < dim; ++d) f1[(q * NcI + c) * dim + d] *= w;
544       if (debug) {
545         PetscCall(PetscPrintf(PETSC_COMM_SELF, "  elem %" PetscInt_FMT " quad point %" PetscInt_FMT "\n", e, q));
546         for (c = 0; c < NcI; ++c) {
547           if (n0) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  f0[%" PetscInt_FMT "] %g\n", c, (double)PetscRealPart(f0[q * NcI + c])));
548           if (n1) {
549             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])));
550             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
551           }
552         }
553       }
554     }
555     PetscCall(PetscFEUpdateElementVec_Internal(fe, Tf[field], face, basisReal, basisDerReal, e, fgeom, f0, f1, &elemVec[cOffset + fOffset]));
556     cOffset += totDim;
557     cOffsetAux += totDimAux;
558   }
559   PetscFunctionReturn(PETSC_SUCCESS);
560 }
561 
562 /*
563   BdIntegral: Operates completely in the embedding dimension. The trick is to have special "face quadrature" so we only integrate over the face, but
564               all transforms operate in the full space and are square.
565 
566   HybridIntegral: The discretization is lower dimensional. That means the transforms are non-square.
567     1) DMPlexGetCellFields() retrieves from the hybrid cell, so it gets fields from both faces
568     2) We need to assume that the orientation is 0 for both
569     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()
570 */
571 PETSC_INTERN PetscErrorCode PetscFEIntegrateHybridResidual_Basic(PetscDS ds, PetscDS dsIn, PetscFormKey key, PetscInt s, PetscInt Ne, PetscFEGeom *fgeom, const PetscScalar coefficients[], const PetscScalar coefficients_t[], PetscDS dsAux, const PetscScalar coefficientsAux[], PetscReal t, PetscScalar elemVec[])
572 {
573   const PetscInt     debug = 0;
574   const PetscInt     field = key.field;
575   PetscFE            fe;
576   PetscWeakForm      wf;
577   PetscInt           n0, n1, i;
578   PetscBdPointFunc  *f0_func, *f1_func;
579   PetscQuadrature    quad;
580   DMPolytopeType     ct;
581   PetscTabulation   *Tf, *TfIn, *TfAux = NULL;
582   PetscScalar       *f0, *f1, *u, *u_t = NULL, *u_x, *a, *a_x, *basisReal, *basisDerReal;
583   const PetscScalar *constants;
584   PetscReal         *x;
585   PetscInt          *uOff, *uOff_x, *aOff = NULL, *aOff_x = NULL;
586   PetscInt           dim, dimAux, numConstants, Nf, NfAux = 0, totDim, totDimIn, totDimAux = 0, cOffset = 0, cOffsetIn = 0, cOffsetAux = 0, fOffset, e, NcI, NcS;
587   PetscBool          isCohesiveField, auxOnBd = PETSC_FALSE;
588   const PetscReal   *quadPoints, *quadWeights;
589   PetscInt           qdim, qNc, Nq, q, dE;
590 
591   PetscFunctionBegin;
592   /* Hybrid discretization is posed directly on faces */
593   PetscCall(PetscDSGetDiscretization(ds, field, (PetscObject *)&fe));
594   PetscCall(PetscFEGetSpatialDimension(fe, &dim));
595   PetscCall(PetscFEGetQuadrature(fe, &quad));
596   PetscCall(PetscDSGetNumFields(ds, &Nf));
597   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
598   PetscCall(PetscDSGetTotalDimension(dsIn, &totDimIn));
599   PetscCall(PetscDSGetComponentOffsetsCohesive(dsIn, 0, &uOff)); // Change 0 to s for one-sided offsets
600   PetscCall(PetscDSGetComponentDerivativeOffsetsCohesive(dsIn, s, &uOff_x));
601   PetscCall(PetscDSGetFieldOffsetCohesive(ds, field, &fOffset));
602   PetscCall(PetscDSGetWeakForm(ds, &wf));
603   PetscCall(PetscWeakFormGetBdResidual(wf, key.label, key.value, key.field, key.part, &n0, &f0_func, &n1, &f1_func));
604   if (!n0 && !n1) PetscFunctionReturn(PETSC_SUCCESS);
605   PetscCall(PetscDSGetEvaluationArrays(ds, &u, coefficients_t ? &u_t : NULL, &u_x));
606   PetscCall(PetscDSGetWorkspace(ds, &x, &basisReal, &basisDerReal, NULL, NULL));
607   PetscCall(PetscDSGetWeakFormArrays(ds, &f0, &f1, NULL, NULL, NULL, NULL));
608   /* NOTE This is a bulk tabulation because the DS is a face discretization */
609   PetscCall(PetscDSGetTabulation(ds, &Tf));
610   PetscCall(PetscDSGetFaceTabulation(dsIn, &TfIn));
611   PetscCall(PetscDSGetConstants(ds, &numConstants, &constants));
612   if (dsAux) {
613     PetscCall(PetscDSGetSpatialDimension(dsAux, &dimAux));
614     PetscCall(PetscDSGetNumFields(dsAux, &NfAux));
615     PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
616     PetscCall(PetscDSGetComponentOffsets(dsAux, &aOff));
617     PetscCall(PetscDSGetComponentDerivativeOffsets(dsAux, &aOff_x));
618     PetscCall(PetscDSGetEvaluationArrays(dsAux, &a, NULL, &a_x));
619     auxOnBd = dimAux == dim ? PETSC_TRUE : PETSC_FALSE;
620     if (auxOnBd) PetscCall(PetscDSGetTabulation(dsAux, &TfAux));
621     else PetscCall(PetscDSGetFaceTabulation(dsAux, &TfAux));
622     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);
623   }
624   PetscCall(PetscDSGetCohesive(ds, field, &isCohesiveField));
625   NcI = Tf[field]->Nc;
626   NcS = NcI;
627   if (!isCohesiveField && s == 2) {
628     // If we are integrating over a cohesive cell (s = 2) for a non-cohesive fields, we use both sides
629     NcS *= 2;
630   }
631   PetscCall(PetscQuadratureGetData(quad, &qdim, &qNc, &Nq, &quadPoints, &quadWeights));
632   PetscCall(PetscQuadratureGetCellType(quad, &ct));
633   PetscCheck(qNc == 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Only supports scalar quadrature, not %" PetscInt_FMT " components", qNc);
634   dE = fgeom->dimEmbed;
635   PetscCheck(fgeom->dim == qdim, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "FEGeom dim %" PetscInt_FMT " != %" PetscInt_FMT " quadrature dim", fgeom->dim, qdim);
636   for (e = 0; e < Ne; ++e) {
637     PetscFEGeom    fegeom;
638     const PetscInt face[2]  = {fgeom->face[e * 2 + 0][0], fgeom->face[e * 2 + 1][2]};
639     const PetscInt ornt[2]  = {fgeom->face[e * 2 + 0][1], fgeom->face[e * 2 + 1][3]};
640     const PetscInt cornt[2] = {fgeom->face[e * 2 + 0][3], fgeom->face[e * 2 + 1][1]};
641 
642     fegeom.v = x; /* Workspace */
643     PetscCall(PetscArrayzero(f0, Nq * NcS));
644     PetscCall(PetscArrayzero(f1, Nq * NcS * dE));
645     for (q = 0; q < Nq; ++q) {
646       PetscInt  qpt[2];
647       PetscReal w;
648       PetscInt  c, d;
649 
650       PetscCall(PetscDSPermuteQuadPoint(ds, DMPolytopeTypeComposeOrientationInv(ct, cornt[0], ornt[0]), field, q, &qpt[0]));
651       PetscCall(PetscDSPermuteQuadPoint(ds, DMPolytopeTypeComposeOrientationInv(ct, ornt[1], cornt[1]), field, q, &qpt[1]));
652       PetscCall(PetscFEGeomGetPoint(fgeom, e * 2, q, &quadPoints[q * fgeom->dim], &fegeom));
653       w = fegeom.detJ[0] * quadWeights[q];
654       if (debug > 1 && q < fgeom->numPoints) {
655         PetscCall(PetscPrintf(PETSC_COMM_SELF, "  detJ: %g\n", (double)fegeom.detJ[0]));
656 #if !defined(PETSC_USE_COMPLEX)
657         PetscCall(DMPrintCellMatrix(e, "invJ", dim, dE, fegeom.invJ));
658 #endif
659       }
660       if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  quad point %" PetscInt_FMT " weight %g detJ %g\n", q, (double)quadWeights[q], (double)fegeom.detJ[0]));
661       /* TODO Is this cell or face quadrature, meaning should we use 'q' or 'face*Nq+q' */
662       PetscCall(PetscFEEvaluateFieldJets_Hybrid_Internal(ds, Nf, 0, q, Tf, face, qpt, TfIn, &fegeom, &coefficients[cOffsetIn], &coefficients_t[cOffsetIn], u, u_x, u_t));
663       if (dsAux) PetscCall(PetscFEEvaluateFieldJets_Internal(dsAux, NfAux, auxOnBd ? 0 : face[s], auxOnBd ? q : qpt[s], TfAux, &fegeom, &coefficientsAux[cOffsetAux], NULL, a, a_x, NULL));
664       for (i = 0; i < n0; ++i) f0_func[i](dim, 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]);
665       for (c = 0; c < NcS; ++c) f0[q * NcS + c] *= w;
666       for (i = 0; i < n1; ++i) f1_func[i](dim, 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]);
667       for (c = 0; c < NcS; ++c)
668         for (d = 0; d < dE; ++d) f1[(q * NcS + c) * dE + d] *= w;
669     }
670     if (isCohesiveField) {
671       PetscCall(PetscFEUpdateElementVec_Internal(fe, Tf[field], 0, basisReal, basisDerReal, e, fgeom, f0, f1, &elemVec[cOffset + fOffset]));
672     } else {
673       PetscCall(PetscFEUpdateElementVec_Hybrid_Internal(fe, Tf[field], 0, s, basisReal, basisDerReal, fgeom, f0, f1, &elemVec[cOffset + fOffset]));
674     }
675     cOffset += totDim;
676     cOffsetIn += totDimIn;
677     cOffsetAux += totDimAux;
678   }
679   PetscFunctionReturn(PETSC_SUCCESS);
680 }
681 
682 PetscErrorCode PetscFEIntegrateJacobian_Basic(PetscDS ds, 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[])
683 {
684   const PetscInt     debug = 0;
685   PetscFE            feI, feJ;
686   PetscWeakForm      wf;
687   PetscPointJac     *g0_func, *g1_func, *g2_func, *g3_func;
688   PetscInt           n0, n1, n2, n3, i;
689   PetscInt           cOffset    = 0; /* Offset into coefficients[] for element e */
690   PetscInt           cOffsetAux = 0; /* Offset into coefficientsAux[] for element e */
691   PetscInt           eOffset    = 0; /* Offset into elemMat[] for element e */
692   PetscInt           offsetI    = 0; /* Offset into an element vector for fieldI */
693   PetscInt           offsetJ    = 0; /* Offset into an element vector for fieldJ */
694   PetscQuadrature    quad;
695   PetscTabulation   *T, *TAux = NULL;
696   PetscScalar       *g0, *g1, *g2, *g3, *u, *u_t = NULL, *u_x, *a, *a_x, *basisReal, *basisDerReal, *testReal, *testDerReal;
697   const PetscScalar *constants;
698   PetscReal         *x;
699   PetscInt          *uOff, *uOff_x, *aOff = NULL, *aOff_x = NULL;
700   PetscInt           NcI = 0, NcJ = 0;
701   PetscInt           dim, numConstants, Nf, fieldI, fieldJ, NfAux = 0, totDim, totDimAux = 0, e;
702   PetscInt           dE, Np;
703   PetscBool          isAffine;
704   const PetscReal   *quadPoints, *quadWeights;
705   PetscInt           qNc, Nq, q;
706 
707   PetscFunctionBegin;
708   PetscCall(PetscDSGetNumFields(ds, &Nf));
709   fieldI = key.field / Nf;
710   fieldJ = key.field % Nf;
711   PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&feI));
712   PetscCall(PetscDSGetDiscretization(ds, fieldJ, (PetscObject *)&feJ));
713   PetscCall(PetscFEGetSpatialDimension(feI, &dim));
714   PetscCall(PetscFEGetQuadrature(feI, &quad));
715   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
716   PetscCall(PetscDSGetComponentOffsets(ds, &uOff));
717   PetscCall(PetscDSGetComponentDerivativeOffsets(ds, &uOff_x));
718   PetscCall(PetscDSGetWeakForm(ds, &wf));
719   switch (jtype) {
720   case PETSCFE_JACOBIAN_DYN:
721     PetscCall(PetscWeakFormGetDynamicJacobian(wf, key.label, key.value, fieldI, fieldJ, key.part, &n0, &g0_func, &n1, &g1_func, &n2, &g2_func, &n3, &g3_func));
722     break;
723   case PETSCFE_JACOBIAN_PRE:
724     PetscCall(PetscWeakFormGetJacobianPreconditioner(wf, key.label, key.value, fieldI, fieldJ, key.part, &n0, &g0_func, &n1, &g1_func, &n2, &g2_func, &n3, &g3_func));
725     break;
726   case PETSCFE_JACOBIAN:
727     PetscCall(PetscWeakFormGetJacobian(wf, key.label, key.value, fieldI, fieldJ, key.part, &n0, &g0_func, &n1, &g1_func, &n2, &g2_func, &n3, &g3_func));
728     break;
729   }
730   if (!n0 && !n1 && !n2 && !n3) PetscFunctionReturn(PETSC_SUCCESS);
731   PetscCall(PetscDSGetEvaluationArrays(ds, &u, coefficients_t ? &u_t : NULL, &u_x));
732   PetscCall(PetscDSGetWorkspace(ds, &x, &basisReal, &basisDerReal, &testReal, &testDerReal));
733   PetscCall(PetscDSGetWeakFormArrays(ds, NULL, NULL, &g0, &g1, &g2, &g3));
734   PetscCall(PetscDSGetTabulation(ds, &T));
735   PetscCall(PetscDSGetFieldOffset(ds, fieldI, &offsetI));
736   PetscCall(PetscDSGetFieldOffset(ds, fieldJ, &offsetJ));
737   PetscCall(PetscDSGetConstants(ds, &numConstants, &constants));
738   if (dsAux) {
739     PetscCall(PetscDSGetNumFields(dsAux, &NfAux));
740     PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
741     PetscCall(PetscDSGetComponentOffsets(dsAux, &aOff));
742     PetscCall(PetscDSGetComponentDerivativeOffsets(dsAux, &aOff_x));
743     PetscCall(PetscDSGetEvaluationArrays(dsAux, &a, NULL, &a_x));
744     PetscCall(PetscDSGetTabulation(dsAux, &TAux));
745     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);
746   }
747   NcI      = T[fieldI]->Nc;
748   NcJ      = T[fieldJ]->Nc;
749   Np       = cgeom->numPoints;
750   dE       = cgeom->dimEmbed;
751   isAffine = cgeom->isAffine;
752   /* Initialize here in case the function is not defined */
753   PetscCall(PetscArrayzero(g0, NcI * NcJ));
754   PetscCall(PetscArrayzero(g1, NcI * NcJ * dE));
755   PetscCall(PetscArrayzero(g2, NcI * NcJ * dE));
756   PetscCall(PetscArrayzero(g3, NcI * NcJ * dE * dE));
757   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
758   PetscCheck(qNc == 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Only supports scalar quadrature, not %" PetscInt_FMT " components", qNc);
759   for (e = 0; e < Ne; ++e) {
760     PetscFEGeom fegeom;
761 
762     fegeom.dim      = cgeom->dim;
763     fegeom.dimEmbed = cgeom->dimEmbed;
764     if (isAffine) {
765       fegeom.v    = x;
766       fegeom.xi   = cgeom->xi;
767       fegeom.J    = &cgeom->J[e * Np * dE * dE];
768       fegeom.invJ = &cgeom->invJ[e * Np * dE * dE];
769       fegeom.detJ = &cgeom->detJ[e * Np];
770     }
771     for (q = 0; q < Nq; ++q) {
772       PetscReal w;
773       PetscInt  c;
774 
775       if (isAffine) {
776         CoordinatesRefToReal(dE, dim, fegeom.xi, &cgeom->v[e * Np * dE], fegeom.J, &quadPoints[q * dim], x);
777       } else {
778         fegeom.v    = &cgeom->v[(e * Np + q) * dE];
779         fegeom.J    = &cgeom->J[(e * Np + q) * dE * dE];
780         fegeom.invJ = &cgeom->invJ[(e * Np + q) * dE * dE];
781         fegeom.detJ = &cgeom->detJ[e * Np + q];
782       }
783       if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  quad point %" PetscInt_FMT " weight %g detJ %g\n", q, (double)quadWeights[q], (double)fegeom.detJ[0]));
784       w = fegeom.detJ[0] * quadWeights[q];
785       if (coefficients) PetscCall(PetscFEEvaluateFieldJets_Internal(ds, Nf, 0, q, T, &fegeom, &coefficients[cOffset], &coefficients_t[cOffset], u, u_x, u_t));
786       if (dsAux) PetscCall(PetscFEEvaluateFieldJets_Internal(dsAux, NfAux, 0, q, TAux, &fegeom, &coefficientsAux[cOffsetAux], NULL, a, a_x, NULL));
787       if (n0) {
788         PetscCall(PetscArrayzero(g0, NcI * NcJ));
789         for (i = 0; i < n0; ++i) g0_func[i](dim, 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);
790         for (c = 0; c < NcI * NcJ; ++c) g0[c] *= w;
791       }
792       if (n1) {
793         PetscCall(PetscArrayzero(g1, NcI * NcJ * dE));
794         for (i = 0; i < n1; ++i) g1_func[i](dim, 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);
795         for (c = 0; c < NcI * NcJ * dim; ++c) g1[c] *= w;
796       }
797       if (n2) {
798         PetscCall(PetscArrayzero(g2, NcI * NcJ * dE));
799         for (i = 0; i < n2; ++i) g2_func[i](dim, 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);
800         for (c = 0; c < NcI * NcJ * dim; ++c) g2[c] *= w;
801       }
802       if (n3) {
803         PetscCall(PetscArrayzero(g3, NcI * NcJ * dE * dE));
804         for (i = 0; i < n3; ++i) g3_func[i](dim, 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);
805         for (c = 0; c < NcI * NcJ * dim * dim; ++c) g3[c] *= w;
806       }
807 
808       PetscCall(PetscFEUpdateElementMat_Internal(feI, feJ, 0, q, T[fieldI], basisReal, basisDerReal, T[fieldJ], testReal, testDerReal, &fegeom, g0, g1, g2, g3, eOffset, totDim, offsetI, offsetJ, elemMat));
809     }
810     if (debug > 1) {
811       PetscInt fc, f, gc, g;
812 
813       PetscCall(PetscPrintf(PETSC_COMM_SELF, "Element matrix for fields %" PetscInt_FMT " and %" PetscInt_FMT "\n", fieldI, fieldJ));
814       for (fc = 0; fc < T[fieldI]->Nc; ++fc) {
815         for (f = 0; f < T[fieldI]->Nb; ++f) {
816           const PetscInt i = offsetI + f * T[fieldI]->Nc + fc;
817           for (gc = 0; gc < T[fieldJ]->Nc; ++gc) {
818             for (g = 0; g < T[fieldJ]->Nb; ++g) {
819               const PetscInt j = offsetJ + g * T[fieldJ]->Nc + gc;
820               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])));
821             }
822           }
823           PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
824         }
825       }
826     }
827     cOffset += totDim;
828     cOffsetAux += totDimAux;
829     eOffset += PetscSqr(totDim);
830   }
831   PetscFunctionReturn(PETSC_SUCCESS);
832 }
833 
834 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[])
835 {
836   const PetscInt     debug = 0;
837   PetscFE            feI, feJ;
838   PetscBdPointJac   *g0_func, *g1_func, *g2_func, *g3_func;
839   PetscInt           n0, n1, n2, n3, i;
840   PetscInt           cOffset    = 0; /* Offset into coefficients[] for element e */
841   PetscInt           cOffsetAux = 0; /* Offset into coefficientsAux[] for element e */
842   PetscInt           eOffset    = 0; /* Offset into elemMat[] for element e */
843   PetscInt           offsetI    = 0; /* Offset into an element vector for fieldI */
844   PetscInt           offsetJ    = 0; /* Offset into an element vector for fieldJ */
845   PetscQuadrature    quad;
846   PetscTabulation   *T, *TAux = NULL;
847   PetscScalar       *g0, *g1, *g2, *g3, *u, *u_t = NULL, *u_x, *a, *a_x, *basisReal, *basisDerReal, *testReal, *testDerReal;
848   const PetscScalar *constants;
849   PetscReal         *x;
850   PetscInt          *uOff, *uOff_x, *aOff = NULL, *aOff_x = NULL;
851   PetscInt           NcI = 0, NcJ = 0;
852   PetscInt           dim, numConstants, Nf, fieldI, fieldJ, NfAux = 0, totDim, totDimAux = 0, e;
853   PetscBool          isAffine;
854   const PetscReal   *quadPoints, *quadWeights;
855   PetscInt           qNc, Nq, q, Np, dE;
856 
857   PetscFunctionBegin;
858   PetscCall(PetscDSGetNumFields(ds, &Nf));
859   fieldI = key.field / Nf;
860   fieldJ = key.field % Nf;
861   PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&feI));
862   PetscCall(PetscDSGetDiscretization(ds, fieldJ, (PetscObject *)&feJ));
863   PetscCall(PetscFEGetSpatialDimension(feI, &dim));
864   PetscCall(PetscFEGetFaceQuadrature(feI, &quad));
865   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
866   PetscCall(PetscDSGetComponentOffsets(ds, &uOff));
867   PetscCall(PetscDSGetComponentDerivativeOffsets(ds, &uOff_x));
868   PetscCall(PetscDSGetFieldOffset(ds, fieldI, &offsetI));
869   PetscCall(PetscDSGetFieldOffset(ds, fieldJ, &offsetJ));
870   switch (jtype) {
871   case PETSCFE_JACOBIAN_PRE:
872     PetscCall(PetscWeakFormGetBdJacobianPreconditioner(wf, key.label, key.value, fieldI, fieldJ, key.part, &n0, &g0_func, &n1, &g1_func, &n2, &g2_func, &n3, &g3_func));
873     break;
874   case PETSCFE_JACOBIAN:
875     PetscCall(PetscWeakFormGetBdJacobian(wf, key.label, key.value, fieldI, fieldJ, key.part, &n0, &g0_func, &n1, &g1_func, &n2, &g2_func, &n3, &g3_func));
876     break;
877   case PETSCFE_JACOBIAN_DYN:
878     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "PETSCFE_JACOBIAN_DYN is not supported for PetscFEIntegrateBdJacobian()");
879   }
880   if (!n0 && !n1 && !n2 && !n3) PetscFunctionReturn(PETSC_SUCCESS);
881   PetscCall(PetscDSGetEvaluationArrays(ds, &u, coefficients_t ? &u_t : NULL, &u_x));
882   PetscCall(PetscDSGetWorkspace(ds, &x, &basisReal, &basisDerReal, &testReal, &testDerReal));
883   PetscCall(PetscDSGetWeakFormArrays(ds, NULL, NULL, &g0, &g1, &g2, &g3));
884   PetscCall(PetscDSGetFaceTabulation(ds, &T));
885   PetscCall(PetscDSGetConstants(ds, &numConstants, &constants));
886   if (dsAux) {
887     PetscCall(PetscDSGetNumFields(dsAux, &NfAux));
888     PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
889     PetscCall(PetscDSGetComponentOffsets(dsAux, &aOff));
890     PetscCall(PetscDSGetComponentDerivativeOffsets(dsAux, &aOff_x));
891     PetscCall(PetscDSGetEvaluationArrays(dsAux, &a, NULL, &a_x));
892     PetscCall(PetscDSGetFaceTabulation(dsAux, &TAux));
893   }
894   NcI = T[fieldI]->Nc, NcJ = T[fieldJ]->Nc;
895   Np       = fgeom->numPoints;
896   dE       = fgeom->dimEmbed;
897   isAffine = fgeom->isAffine;
898   /* Initialize here in case the function is not defined */
899   PetscCall(PetscArrayzero(g0, NcI * NcJ));
900   PetscCall(PetscArrayzero(g1, NcI * NcJ * dE));
901   PetscCall(PetscArrayzero(g2, NcI * NcJ * dE));
902   PetscCall(PetscArrayzero(g3, NcI * NcJ * dE * dE));
903   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
904   PetscCheck(qNc == 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Only supports scalar quadrature, not %" PetscInt_FMT " components", qNc);
905   for (e = 0; e < Ne; ++e) {
906     PetscFEGeom    fegeom, cgeom;
907     const PetscInt face = fgeom->face[e][0];
908     fegeom.n            = NULL;
909     fegeom.v            = NULL;
910     fegeom.J            = NULL;
911     fegeom.detJ         = NULL;
912     fegeom.dim          = fgeom->dim;
913     fegeom.dimEmbed     = fgeom->dimEmbed;
914     cgeom.dim           = fgeom->dim;
915     cgeom.dimEmbed      = fgeom->dimEmbed;
916     if (isAffine) {
917       fegeom.v    = x;
918       fegeom.xi   = fgeom->xi;
919       fegeom.J    = &fgeom->J[e * Np * dE * dE];
920       fegeom.invJ = &fgeom->invJ[e * Np * dE * dE];
921       fegeom.detJ = &fgeom->detJ[e * Np];
922       fegeom.n    = &fgeom->n[e * Np * dE];
923 
924       cgeom.J    = &fgeom->suppJ[0][e * Np * dE * dE];
925       cgeom.invJ = &fgeom->suppInvJ[0][e * Np * dE * dE];
926       cgeom.detJ = &fgeom->suppDetJ[0][e * Np];
927     }
928     for (q = 0; q < Nq; ++q) {
929       PetscReal w;
930       PetscInt  c;
931 
932       if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  quad point %" PetscInt_FMT "\n", q));
933       if (isAffine) {
934         CoordinatesRefToReal(dE, dim - 1, fegeom.xi, &fgeom->v[e * Np * dE], fegeom.J, &quadPoints[q * (dim - 1)], x);
935       } else {
936         fegeom.v    = &fgeom->v[(e * Np + q) * dE];
937         fegeom.J    = &fgeom->J[(e * Np + q) * dE * dE];
938         fegeom.invJ = &fgeom->invJ[(e * Np + q) * dE * dE];
939         fegeom.detJ = &fgeom->detJ[e * Np + q];
940         fegeom.n    = &fgeom->n[(e * Np + q) * dE];
941 
942         cgeom.J    = &fgeom->suppJ[0][(e * Np + q) * dE * dE];
943         cgeom.invJ = &fgeom->suppInvJ[0][(e * Np + q) * dE * dE];
944         cgeom.detJ = &fgeom->suppDetJ[0][e * Np + q];
945       }
946       w = fegeom.detJ[0] * quadWeights[q];
947       if (coefficients) PetscCall(PetscFEEvaluateFieldJets_Internal(ds, Nf, face, q, T, &cgeom, &coefficients[cOffset], &coefficients_t[cOffset], u, u_x, u_t));
948       if (dsAux) PetscCall(PetscFEEvaluateFieldJets_Internal(dsAux, NfAux, face, q, TAux, &cgeom, &coefficientsAux[cOffsetAux], NULL, a, a_x, NULL));
949       if (n0) {
950         PetscCall(PetscArrayzero(g0, NcI * NcJ));
951         for (i = 0; i < n0; ++i) g0_func[i](dim, 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);
952         for (c = 0; c < NcI * NcJ; ++c) g0[c] *= w;
953       }
954       if (n1) {
955         PetscCall(PetscArrayzero(g1, NcI * NcJ * dE));
956         for (i = 0; i < n1; ++i) g1_func[i](dim, 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);
957         for (c = 0; c < NcI * NcJ * dim; ++c) g1[c] *= w;
958       }
959       if (n2) {
960         PetscCall(PetscArrayzero(g2, NcI * NcJ * dE));
961         for (i = 0; i < n2; ++i) g2_func[i](dim, 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);
962         for (c = 0; c < NcI * NcJ * dim; ++c) g2[c] *= w;
963       }
964       if (n3) {
965         PetscCall(PetscArrayzero(g3, NcI * NcJ * dE * dE));
966         for (i = 0; i < n3; ++i) g3_func[i](dim, 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);
967         for (c = 0; c < NcI * NcJ * dim * dim; ++c) g3[c] *= w;
968       }
969 
970       PetscCall(PetscFEUpdateElementMat_Internal(feI, feJ, face, q, T[fieldI], basisReal, basisDerReal, T[fieldJ], testReal, testDerReal, &cgeom, g0, g1, g2, g3, eOffset, totDim, offsetI, offsetJ, elemMat));
971     }
972     if (debug > 1) {
973       PetscInt fc, f, gc, g;
974 
975       PetscCall(PetscPrintf(PETSC_COMM_SELF, "Element matrix for fields %" PetscInt_FMT " and %" PetscInt_FMT "\n", fieldI, fieldJ));
976       for (fc = 0; fc < T[fieldI]->Nc; ++fc) {
977         for (f = 0; f < T[fieldI]->Nb; ++f) {
978           const PetscInt i = offsetI + f * T[fieldI]->Nc + fc;
979           for (gc = 0; gc < T[fieldJ]->Nc; ++gc) {
980             for (g = 0; g < T[fieldJ]->Nb; ++g) {
981               const PetscInt j = offsetJ + g * T[fieldJ]->Nc + gc;
982               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])));
983             }
984           }
985           PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
986         }
987       }
988     }
989     cOffset += totDim;
990     cOffsetAux += totDimAux;
991     eOffset += PetscSqr(totDim);
992   }
993   PetscFunctionReturn(PETSC_SUCCESS);
994 }
995 
996 PETSC_INTERN PetscErrorCode PetscFEIntegrateHybridJacobian_Basic(PetscDS ds, PetscDS dsIn, PetscFEJacobianType jtype, PetscFormKey key, PetscInt s, PetscInt Ne, PetscFEGeom *fgeom, const PetscScalar coefficients[], const PetscScalar coefficients_t[], PetscDS dsAux, const PetscScalar coefficientsAux[], PetscReal t, PetscReal u_tshift, PetscScalar elemMat[])
997 {
998   const PetscInt     debug = 0;
999   PetscFE            feI, feJ;
1000   PetscWeakForm      wf;
1001   PetscBdPointJac   *g0_func, *g1_func, *g2_func, *g3_func;
1002   PetscInt           n0, n1, n2, n3, i;
1003   PetscInt           cOffset    = 0; /* Offset into coefficients[] for element e */
1004   PetscInt           cOffsetAux = 0; /* Offset into coefficientsAux[] for element e */
1005   PetscInt           eOffset    = 0; /* Offset into elemMat[] for element e */
1006   PetscInt           offsetI    = 0; /* Offset into an element vector for fieldI */
1007   PetscInt           offsetJ    = 0; /* Offset into an element vector for fieldJ */
1008   PetscQuadrature    quad;
1009   DMPolytopeType     ct;
1010   PetscTabulation   *T, *TfIn, *TAux = NULL;
1011   PetscScalar       *g0, *g1, *g2, *g3, *u, *u_t = NULL, *u_x, *a, *a_x, *basisReal, *basisDerReal, *testReal, *testDerReal;
1012   const PetscScalar *constants;
1013   PetscReal         *x;
1014   PetscInt          *uOff, *uOff_x, *aOff = NULL, *aOff_x = NULL;
1015   PetscInt           NcI = 0, NcJ = 0, NcS, NcT;
1016   PetscInt           dim, dimAux, numConstants, Nf, fieldI, fieldJ, NfAux = 0, totDim, totDimAux = 0, e;
1017   PetscBool          isCohesiveFieldI, isCohesiveFieldJ, auxOnBd = PETSC_FALSE;
1018   const PetscReal   *quadPoints, *quadWeights;
1019   PetscInt           qNc, Nq, q;
1020 
1021   PetscFunctionBegin;
1022   PetscCall(PetscDSGetNumFields(ds, &Nf));
1023   fieldI = key.field / Nf;
1024   fieldJ = key.field % Nf;
1025   /* Hybrid discretization is posed directly on faces */
1026   PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&feI));
1027   PetscCall(PetscDSGetDiscretization(ds, fieldJ, (PetscObject *)&feJ));
1028   PetscCall(PetscFEGetSpatialDimension(feI, &dim));
1029   PetscCall(PetscFEGetQuadrature(feI, &quad));
1030   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
1031   PetscCall(PetscDSGetComponentOffsetsCohesive(ds, 0, &uOff)); // Change 0 to s for one-sided offsets
1032   PetscCall(PetscDSGetComponentDerivativeOffsetsCohesive(ds, s, &uOff_x));
1033   PetscCall(PetscDSGetWeakForm(ds, &wf));
1034   switch (jtype) {
1035   case PETSCFE_JACOBIAN_PRE:
1036     PetscCall(PetscWeakFormGetBdJacobianPreconditioner(wf, key.label, key.value, fieldI, fieldJ, key.part, &n0, &g0_func, &n1, &g1_func, &n2, &g2_func, &n3, &g3_func));
1037     break;
1038   case PETSCFE_JACOBIAN:
1039     PetscCall(PetscWeakFormGetBdJacobian(wf, key.label, key.value, fieldI, fieldJ, key.part, &n0, &g0_func, &n1, &g1_func, &n2, &g2_func, &n3, &g3_func));
1040     break;
1041   case PETSCFE_JACOBIAN_DYN:
1042     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "No boundary hybrid Jacobians :)");
1043   }
1044   if (!n0 && !n1 && !n2 && !n3) PetscFunctionReturn(PETSC_SUCCESS);
1045   PetscCall(PetscDSGetEvaluationArrays(ds, &u, coefficients_t ? &u_t : NULL, &u_x));
1046   PetscCall(PetscDSGetWorkspace(ds, &x, &basisReal, &basisDerReal, &testReal, &testDerReal));
1047   PetscCall(PetscDSGetWeakFormArrays(ds, NULL, NULL, &g0, &g1, &g2, &g3));
1048   PetscCall(PetscDSGetTabulation(ds, &T));
1049   PetscCall(PetscDSGetFaceTabulation(dsIn, &TfIn));
1050   PetscCall(PetscDSGetFieldOffsetCohesive(ds, fieldI, &offsetI));
1051   PetscCall(PetscDSGetFieldOffsetCohesive(ds, fieldJ, &offsetJ));
1052   PetscCall(PetscDSGetConstants(ds, &numConstants, &constants));
1053   if (dsAux) {
1054     PetscCall(PetscDSGetSpatialDimension(dsAux, &dimAux));
1055     PetscCall(PetscDSGetNumFields(dsAux, &NfAux));
1056     PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
1057     PetscCall(PetscDSGetComponentOffsets(dsAux, &aOff));
1058     PetscCall(PetscDSGetComponentDerivativeOffsets(dsAux, &aOff_x));
1059     PetscCall(PetscDSGetEvaluationArrays(dsAux, &a, NULL, &a_x));
1060     auxOnBd = dimAux == dim ? PETSC_TRUE : PETSC_FALSE;
1061     if (auxOnBd) PetscCall(PetscDSGetTabulation(dsAux, &TAux));
1062     else PetscCall(PetscDSGetFaceTabulation(dsAux, &TAux));
1063     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);
1064   }
1065   PetscCall(PetscDSGetCohesive(ds, fieldI, &isCohesiveFieldI));
1066   PetscCall(PetscDSGetCohesive(ds, fieldJ, &isCohesiveFieldJ));
1067   NcI = T[fieldI]->Nc;
1068   NcJ = T[fieldJ]->Nc;
1069   NcS = isCohesiveFieldI ? NcI : 2 * NcI;
1070   NcT = isCohesiveFieldJ ? NcJ : 2 * NcJ;
1071   if (!isCohesiveFieldI && s == 2) {
1072     // If we are integrating over a cohesive cell (s = 2) for a non-cohesive fields, we use both sides
1073     NcS *= 2;
1074   }
1075   if (!isCohesiveFieldJ && s == 2) {
1076     // If we are integrating over a cohesive cell (s = 2) for a non-cohesive fields, we use both sides
1077     NcT *= 2;
1078   }
1079   // The derivatives are constrained to be along the cell, so there are dim, not dE, components, even though
1080   // the coordinates are in dE dimensions
1081   PetscCall(PetscArrayzero(g0, NcS * NcT));
1082   PetscCall(PetscArrayzero(g1, NcS * NcT * dim));
1083   PetscCall(PetscArrayzero(g2, NcS * NcT * dim));
1084   PetscCall(PetscArrayzero(g3, NcS * NcT * dim * dim));
1085   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
1086   PetscCall(PetscQuadratureGetCellType(quad, &ct));
1087   PetscCheck(qNc == 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Only supports scalar quadrature, not %" PetscInt_FMT " components", qNc);
1088   for (e = 0; e < Ne; ++e) {
1089     PetscFEGeom    fegeom;
1090     const PetscInt face[2]  = {fgeom->face[e * 2 + 0][0], fgeom->face[e * 2 + 1][2]};
1091     const PetscInt ornt[2]  = {fgeom->face[e * 2 + 0][1], fgeom->face[e * 2 + 1][3]};
1092     const PetscInt cornt[2] = {fgeom->face[e * 2 + 0][3], fgeom->face[e * 2 + 1][1]};
1093 
1094     fegeom.v = x; /* Workspace */
1095     for (q = 0; q < Nq; ++q) {
1096       PetscInt  qpt[2];
1097       PetscReal w;
1098       PetscInt  c;
1099 
1100       PetscCall(PetscDSPermuteQuadPoint(ds, DMPolytopeTypeComposeOrientationInv(ct, cornt[0], ornt[0]), fieldI, q, &qpt[0]));
1101       PetscCall(PetscDSPermuteQuadPoint(ds, DMPolytopeTypeComposeOrientationInv(ct, ornt[1], cornt[1]), fieldI, q, &qpt[1]));
1102       PetscCall(PetscFEGeomGetPoint(fgeom, e * 2, q, &quadPoints[q * fgeom->dim], &fegeom));
1103       w = fegeom.detJ[0] * quadWeights[q];
1104       if (debug > 1 && q < fgeom->numPoints) {
1105         PetscCall(PetscPrintf(PETSC_COMM_SELF, "  detJ: %g\n", (double)fegeom.detJ[0]));
1106 #if !defined(PETSC_USE_COMPLEX)
1107         PetscCall(DMPrintCellMatrix(e, "invJ", dim, dim, fegeom.invJ));
1108 #endif
1109       }
1110       if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  quad point %" PetscInt_FMT "\n", q));
1111       if (coefficients) PetscCall(PetscFEEvaluateFieldJets_Hybrid_Internal(ds, Nf, 0, q, T, face, qpt, TfIn, &fegeom, &coefficients[cOffset], &coefficients_t[cOffset], u, u_x, u_t));
1112       if (dsAux) PetscCall(PetscFEEvaluateFieldJets_Internal(dsAux, NfAux, auxOnBd ? 0 : face[s], auxOnBd ? q : qpt[s], TAux, &fegeom, &coefficientsAux[cOffsetAux], NULL, a, a_x, NULL));
1113       if (n0) {
1114         PetscCall(PetscArrayzero(g0, NcS * NcT));
1115         for (i = 0; i < n0; ++i) g0_func[i](dim, 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);
1116         for (c = 0; c < NcS * NcT; ++c) g0[c] *= w;
1117       }
1118       if (n1) {
1119         PetscCall(PetscArrayzero(g1, NcS * NcT * dim));
1120         for (i = 0; i < n1; ++i) g1_func[i](dim, 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);
1121         for (c = 0; c < NcS * NcT * dim; ++c) g1[c] *= w;
1122       }
1123       if (n2) {
1124         PetscCall(PetscArrayzero(g2, NcS * NcT * dim));
1125         for (i = 0; i < n2; ++i) g2_func[i](dim, 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);
1126         for (c = 0; c < NcS * NcT * dim; ++c) g2[c] *= w;
1127       }
1128       if (n3) {
1129         PetscCall(PetscArrayzero(g3, NcS * NcT * dim * dim));
1130         for (i = 0; i < n3; ++i) g3_func[i](dim, 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);
1131         for (c = 0; c < NcS * NcT * dim * dim; ++c) g3[c] *= w;
1132       }
1133 
1134       if (isCohesiveFieldI) {
1135         if (isCohesiveFieldJ) {
1136           PetscCall(PetscFEUpdateElementMat_Internal(feI, feJ, 0, q, T[fieldI], basisReal, basisDerReal, T[fieldJ], testReal, testDerReal, &fegeom, g0, g1, g2, g3, eOffset, totDim, offsetI, offsetJ, elemMat));
1137         } else {
1138           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));
1139           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));
1140         }
1141       } else {
1142         if (s == 2) {
1143           if (isCohesiveFieldJ) {
1144             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));
1145             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));
1146           } else {
1147             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));
1148             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));
1149             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));
1150             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));
1151           }
1152         } else
1153           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));
1154       }
1155     }
1156     if (debug > 1) {
1157       const PetscInt fS = 0 + (isCohesiveFieldI ? 0 : (s == 2 ? 0 : s * T[fieldI]->Nb));
1158       const PetscInt fE = T[fieldI]->Nb + (isCohesiveFieldI ? 0 : (s == 2 ? T[fieldI]->Nb : s * T[fieldI]->Nb));
1159       const PetscInt gS = 0 + (isCohesiveFieldJ ? 0 : (s == 2 ? 0 : s * T[fieldJ]->Nb));
1160       const PetscInt gE = T[fieldJ]->Nb + (isCohesiveFieldJ ? 0 : (s == 2 ? T[fieldJ]->Nb : s * T[fieldJ]->Nb));
1161       PetscInt       f, g;
1162 
1163       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));
1164       for (f = fS; f < fE; ++f) {
1165         const PetscInt i = offsetI + f;
1166         for (g = gS; g < gE; ++g) {
1167           const PetscInt j = offsetJ + g;
1168           PetscCheck(i < totDim && j < totDim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Fuck up %" PetscInt_FMT " %" PetscInt_FMT " %" PetscInt_FMT " %" PetscInt_FMT "\n", f, i, g, j);
1169           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])));
1170         }
1171         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
1172       }
1173     }
1174     cOffset += totDim;
1175     cOffsetAux += totDimAux;
1176     eOffset += PetscSqr(totDim);
1177   }
1178   PetscFunctionReturn(PETSC_SUCCESS);
1179 }
1180 
1181 static PetscErrorCode PetscFEInitialize_Basic(PetscFE fem)
1182 {
1183   PetscFunctionBegin;
1184   fem->ops->setfromoptions          = NULL;
1185   fem->ops->setup                   = PetscFESetUp_Basic;
1186   fem->ops->view                    = PetscFEView_Basic;
1187   fem->ops->destroy                 = PetscFEDestroy_Basic;
1188   fem->ops->getdimension            = PetscFEGetDimension_Basic;
1189   fem->ops->createtabulation        = PetscFECreateTabulation_Basic;
1190   fem->ops->integrate               = PetscFEIntegrate_Basic;
1191   fem->ops->integratebd             = PetscFEIntegrateBd_Basic;
1192   fem->ops->integrateresidual       = PetscFEIntegrateResidual_Basic;
1193   fem->ops->integratebdresidual     = PetscFEIntegrateBdResidual_Basic;
1194   fem->ops->integratehybridresidual = PetscFEIntegrateHybridResidual_Basic;
1195   fem->ops->integratejacobianaction = NULL /* PetscFEIntegrateJacobianAction_Basic */;
1196   fem->ops->integratejacobian       = PetscFEIntegrateJacobian_Basic;
1197   fem->ops->integratebdjacobian     = PetscFEIntegrateBdJacobian_Basic;
1198   fem->ops->integratehybridjacobian = PetscFEIntegrateHybridJacobian_Basic;
1199   PetscFunctionReturn(PETSC_SUCCESS);
1200 }
1201 
1202 /*MC
1203   PETSCFEBASIC = "basic" - A `PetscFE` object that integrates with basic tiling and no vectorization
1204 
1205   Level: intermediate
1206 
1207 .seealso: `PetscFE`, `PetscFEType`, `PetscFECreate()`, `PetscFESetType()`
1208 M*/
1209 
1210 PETSC_EXTERN PetscErrorCode PetscFECreate_Basic(PetscFE fem)
1211 {
1212   PetscFE_Basic *b;
1213 
1214   PetscFunctionBegin;
1215   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
1216   PetscCall(PetscNew(&b));
1217   fem->data = b;
1218 
1219   PetscCall(PetscFEInitialize_Basic(fem));
1220   PetscFunctionReturn(PETSC_SUCCESS);
1221 }
1222