xref: /petsc/src/dm/dt/fe/interface/fe.c (revision a02648fdf9ec0d41d7b5ca02cb70ddcfa0e65728)
1 /* Basis Jet Tabulation
2 
3 We would like to tabulate the nodal basis functions and derivatives at a set of points, usually quadrature points. We
4 follow here the derviation in http://www.math.ttu.edu/~kirby/papers/fiat-toms-2004.pdf. The nodal basis $\psi_i$ can
5 be expressed in terms of a prime basis $\phi_i$ which can be stably evaluated. In PETSc, we will use the Legendre basis
6 as a prime basis.
7 
8   \psi_i = \sum_k \alpha_{ki} \phi_k
9 
10 Our nodal basis is defined in terms of the dual basis $n_j$
11 
12   n_j \cdot \psi_i = \delta_{ji}
13 
14 and we may act on the first equation to obtain
15 
16   n_j \cdot \psi_i = \sum_k \alpha_{ki} n_j \cdot \phi_k
17        \delta_{ji} = \sum_k \alpha_{ki} V_{jk}
18                  I = V \alpha
19 
20 so the coefficients of the nodal basis in the prime basis are
21 
22    \alpha = V^{-1}
23 
24 We will define the dual basis vectors $n_j$ using a quadrature rule.
25 
26 Right now, we will just use the polynomial spaces P^k. I know some elements use the space of symmetric polynomials
27 (I think Nedelec), but we will neglect this for now. Constraints in the space, e.g. Arnold-Winther elements, can
28 be implemented exactly as in FIAT using functionals $L_j$.
29 
30 I will have to count the degrees correctly for the Legendre product when we are on simplices.
31 
32 We will have three objects:
33  - Space, P: this just need point evaluation I think
34  - Dual Space, P'+K: This looks like a set of functionals that can act on members of P, each n is defined by a Q
35  - FEM: This keeps {P, P', Q}
36 */
37 #include <petsc/private/petscfeimpl.h> /*I "petscfe.h" I*/
38 #include <petscdmplex.h>
39 
40 PetscBool  FEcite       = PETSC_FALSE;
41 const char FECitation[] = "@article{kirby2004,\n"
42                           "  title   = {Algorithm 839: FIAT, a New Paradigm for Computing Finite Element Basis Functions},\n"
43                           "  journal = {ACM Transactions on Mathematical Software},\n"
44                           "  author  = {Robert C. Kirby},\n"
45                           "  volume  = {30},\n"
46                           "  number  = {4},\n"
47                           "  pages   = {502--516},\n"
48                           "  doi     = {10.1145/1039813.1039820},\n"
49                           "  year    = {2004}\n}\n";
50 
51 PetscClassId PETSCFE_CLASSID = 0;
52 
53 PetscLogEvent PETSCFE_SetUp;
54 
55 PetscFunctionList PetscFEList              = NULL;
56 PetscBool         PetscFERegisterAllCalled = PETSC_FALSE;
57 
58 /*@C
59   PetscFERegister - Adds a new `PetscFEType`
60 
61   Not Collective, No Fortran Support
62 
63   Input Parameters:
64 + sname    - The name of a new user-defined creation routine
65 - function - The creation routine
66 
67   Example Usage:
68 .vb
69     PetscFERegister("my_fe", MyPetscFECreate);
70 .ve
71 
72   Then, your PetscFE type can be chosen with the procedural interface via
73 .vb
74     PetscFECreate(MPI_Comm, PetscFE *);
75     PetscFESetType(PetscFE, "my_fe");
76 .ve
77   or at runtime via the option
78 .vb
79     -petscfe_type my_fe
80 .ve
81 
82   Level: advanced
83 
84   Note:
85   `PetscFERegister()` may be called multiple times to add several user-defined `PetscFE`s
86 
87 .seealso: `PetscFE`, `PetscFEType`, `PetscFERegisterAll()`, `PetscFERegisterDestroy()`
88 @*/
89 PetscErrorCode PetscFERegister(const char sname[], PetscErrorCode (*function)(PetscFE))
90 {
91   PetscFunctionBegin;
92   PetscCall(PetscFunctionListAdd(&PetscFEList, sname, function));
93   PetscFunctionReturn(PETSC_SUCCESS);
94 }
95 
96 /*@
97   PetscFESetType - Builds a particular `PetscFE`
98 
99   Collective
100 
101   Input Parameters:
102 + fem  - The `PetscFE` object
103 - name - The kind of FEM space
104 
105   Options Database Key:
106 . -petscfe_type <type> - Sets the `PetscFE` type; use -help for a list of available types
107 
108   Level: intermediate
109 
110 .seealso: `PetscFEType`, `PetscFE`, `PetscFEGetType()`, `PetscFECreate()`
111 @*/
112 PetscErrorCode PetscFESetType(PetscFE fem, PetscFEType name)
113 {
114   PetscErrorCode (*r)(PetscFE);
115   PetscBool match;
116 
117   PetscFunctionBegin;
118   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
119   PetscCall(PetscObjectTypeCompare((PetscObject)fem, name, &match));
120   if (match) PetscFunctionReturn(PETSC_SUCCESS);
121 
122   if (!PetscFERegisterAllCalled) PetscCall(PetscFERegisterAll());
123   PetscCall(PetscFunctionListFind(PetscFEList, name, &r));
124   PetscCheck(r, PetscObjectComm((PetscObject)fem), PETSC_ERR_ARG_UNKNOWN_TYPE, "Unknown PetscFE type: %s", name);
125 
126   PetscTryTypeMethod(fem, destroy);
127   fem->ops->destroy = NULL;
128 
129   PetscCall((*r)(fem));
130   PetscCall(PetscObjectChangeTypeName((PetscObject)fem, name));
131   PetscFunctionReturn(PETSC_SUCCESS);
132 }
133 
134 /*@
135   PetscFEGetType - Gets the `PetscFEType` (as a string) from the `PetscFE` object.
136 
137   Not Collective
138 
139   Input Parameter:
140 . fem - The `PetscFE`
141 
142   Output Parameter:
143 . name - The `PetscFEType` name
144 
145   Level: intermediate
146 
147 .seealso: `PetscFEType`, `PetscFE`, `PetscFESetType()`, `PetscFECreate()`
148 @*/
149 PetscErrorCode PetscFEGetType(PetscFE fem, PetscFEType *name)
150 {
151   PetscFunctionBegin;
152   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
153   PetscAssertPointer(name, 2);
154   if (!PetscFERegisterAllCalled) PetscCall(PetscFERegisterAll());
155   *name = ((PetscObject)fem)->type_name;
156   PetscFunctionReturn(PETSC_SUCCESS);
157 }
158 
159 /*@
160   PetscFEViewFromOptions - View from a `PetscFE` based on values in the options database
161 
162   Collective
163 
164   Input Parameters:
165 + A    - the `PetscFE` object
166 . obj  - Optional object that provides the options prefix, pass `NULL` to use the options prefix of `A`
167 - name - command line option name
168 
169   Level: intermediate
170 
171 .seealso: `PetscFE`, `PetscFEView()`, `PetscObjectViewFromOptions()`, `PetscFECreate()`
172 @*/
173 PetscErrorCode PetscFEViewFromOptions(PetscFE A, PeOp PetscObject obj, const char name[])
174 {
175   PetscFunctionBegin;
176   PetscValidHeaderSpecific(A, PETSCFE_CLASSID, 1);
177   PetscCall(PetscObjectViewFromOptions((PetscObject)A, obj, name));
178   PetscFunctionReturn(PETSC_SUCCESS);
179 }
180 
181 /*@
182   PetscFEView - Views a `PetscFE`
183 
184   Collective
185 
186   Input Parameters:
187 + fem    - the `PetscFE` object to view
188 - viewer - the viewer
189 
190   Level: beginner
191 
192 .seealso: `PetscFE`, `PetscViewer`, `PetscFEDestroy()`, `PetscFEViewFromOptions()`
193 @*/
194 PetscErrorCode PetscFEView(PetscFE fem, PetscViewer viewer)
195 {
196   PetscBool iascii;
197 
198   PetscFunctionBegin;
199   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
200   if (viewer) PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
201   if (!viewer) PetscCall(PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)fem), &viewer));
202   PetscCall(PetscObjectPrintClassNamePrefixType((PetscObject)fem, viewer));
203   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
204   PetscTryTypeMethod(fem, view, viewer);
205   PetscFunctionReturn(PETSC_SUCCESS);
206 }
207 
208 /*@
209   PetscFESetFromOptions - sets parameters in a `PetscFE` from the options database
210 
211   Collective
212 
213   Input Parameter:
214 . fem - the `PetscFE` object to set options for
215 
216   Options Database Keys:
217 + -petscfe_num_blocks  - the number of cell blocks to integrate concurrently
218 - -petscfe_num_batches - the number of cell batches to integrate serially
219 
220   Level: intermediate
221 
222 .seealso: `PetscFE`, `PetscFEView()`
223 @*/
224 PetscErrorCode PetscFESetFromOptions(PetscFE fem)
225 {
226   const char *defaultType;
227   char        name[256];
228   PetscBool   flg;
229 
230   PetscFunctionBegin;
231   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
232   if (!((PetscObject)fem)->type_name) {
233     defaultType = PETSCFEBASIC;
234   } else {
235     defaultType = ((PetscObject)fem)->type_name;
236   }
237   if (!PetscFERegisterAllCalled) PetscCall(PetscFERegisterAll());
238 
239   PetscObjectOptionsBegin((PetscObject)fem);
240   PetscCall(PetscOptionsFList("-petscfe_type", "Finite element space", "PetscFESetType", PetscFEList, defaultType, name, 256, &flg));
241   if (flg) {
242     PetscCall(PetscFESetType(fem, name));
243   } else if (!((PetscObject)fem)->type_name) {
244     PetscCall(PetscFESetType(fem, defaultType));
245   }
246   PetscCall(PetscOptionsBoundedInt("-petscfe_num_blocks", "The number of cell blocks to integrate concurrently", "PetscSpaceSetTileSizes", fem->numBlocks, &fem->numBlocks, NULL, 1));
247   PetscCall(PetscOptionsBoundedInt("-petscfe_num_batches", "The number of cell batches to integrate serially", "PetscSpaceSetTileSizes", fem->numBatches, &fem->numBatches, NULL, 1));
248   PetscTryTypeMethod(fem, setfromoptions, PetscOptionsObject);
249   /* process any options handlers added with PetscObjectAddOptionsHandler() */
250   PetscCall(PetscObjectProcessOptionsHandlers((PetscObject)fem, PetscOptionsObject));
251   PetscOptionsEnd();
252   PetscCall(PetscFEViewFromOptions(fem, NULL, "-petscfe_view"));
253   PetscFunctionReturn(PETSC_SUCCESS);
254 }
255 
256 /*@
257   PetscFESetUp - Construct data structures for the `PetscFE` after the `PetscFEType` has been set
258 
259   Collective
260 
261   Input Parameter:
262 . fem - the `PetscFE` object to setup
263 
264   Level: intermediate
265 
266 .seealso: `PetscFE`, `PetscFEView()`, `PetscFEDestroy()`
267 @*/
268 PetscErrorCode PetscFESetUp(PetscFE fem)
269 {
270   PetscFunctionBegin;
271   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
272   if (fem->setupcalled) PetscFunctionReturn(PETSC_SUCCESS);
273   PetscCall(PetscLogEventBegin(PETSCFE_SetUp, fem, 0, 0, 0));
274   fem->setupcalled = PETSC_TRUE;
275   PetscTryTypeMethod(fem, setup);
276   PetscCall(PetscLogEventEnd(PETSCFE_SetUp, fem, 0, 0, 0));
277   PetscFunctionReturn(PETSC_SUCCESS);
278 }
279 
280 /*@
281   PetscFEDestroy - Destroys a `PetscFE` object
282 
283   Collective
284 
285   Input Parameter:
286 . fem - the `PetscFE` object to destroy
287 
288   Level: beginner
289 
290 .seealso: `PetscFE`, `PetscFEView()`
291 @*/
292 PetscErrorCode PetscFEDestroy(PetscFE *fem)
293 {
294   PetscFunctionBegin;
295   if (!*fem) PetscFunctionReturn(PETSC_SUCCESS);
296   PetscValidHeaderSpecific(*fem, PETSCFE_CLASSID, 1);
297 
298   if (--((PetscObject)*fem)->refct > 0) {
299     *fem = NULL;
300     PetscFunctionReturn(PETSC_SUCCESS);
301   }
302   ((PetscObject)*fem)->refct = 0;
303 
304   if ((*fem)->subspaces) {
305     PetscInt dim, d;
306 
307     PetscCall(PetscDualSpaceGetDimension((*fem)->dualSpace, &dim));
308     for (d = 0; d < dim; ++d) PetscCall(PetscFEDestroy(&(*fem)->subspaces[d]));
309   }
310   PetscCall(PetscFree((*fem)->subspaces));
311   PetscCall(PetscFree((*fem)->invV));
312   PetscCall(PetscTabulationDestroy(&(*fem)->T));
313   PetscCall(PetscTabulationDestroy(&(*fem)->Tf));
314   PetscCall(PetscTabulationDestroy(&(*fem)->Tc));
315   PetscCall(PetscSpaceDestroy(&(*fem)->basisSpace));
316   PetscCall(PetscDualSpaceDestroy(&(*fem)->dualSpace));
317   PetscCall(PetscQuadratureDestroy(&(*fem)->quadrature));
318   PetscCall(PetscQuadratureDestroy(&(*fem)->faceQuadrature));
319 #ifdef PETSC_HAVE_LIBCEED
320   PetscCallCEED(CeedBasisDestroy(&(*fem)->ceedBasis));
321   PetscCallCEED(CeedDestroy(&(*fem)->ceed));
322 #endif
323 
324   PetscTryTypeMethod(*fem, destroy);
325   PetscCall(PetscHeaderDestroy(fem));
326   PetscFunctionReturn(PETSC_SUCCESS);
327 }
328 
329 /*@
330   PetscFECreate - Creates an empty `PetscFE` object. The type can then be set with `PetscFESetType()`.
331 
332   Collective
333 
334   Input Parameter:
335 . comm - The communicator for the `PetscFE` object
336 
337   Output Parameter:
338 . fem - The `PetscFE` object
339 
340   Level: beginner
341 
342 .seealso: `PetscFE`, `PetscFEType`, `PetscFESetType()`, `PetscFECreateDefault()`, `PETSCFEGALERKIN`
343 @*/
344 PetscErrorCode PetscFECreate(MPI_Comm comm, PetscFE *fem)
345 {
346   PetscFE f;
347 
348   PetscFunctionBegin;
349   PetscAssertPointer(fem, 2);
350   PetscCall(PetscCitationsRegister(FECitation, &FEcite));
351   PetscCall(PetscFEInitializePackage());
352 
353   PetscCall(PetscHeaderCreate(f, PETSCFE_CLASSID, "PetscFE", "Finite Element", "PetscFE", comm, PetscFEDestroy, PetscFEView));
354 
355   f->basisSpace    = NULL;
356   f->dualSpace     = NULL;
357   f->numComponents = 1;
358   f->subspaces     = NULL;
359   f->invV          = NULL;
360   f->T             = NULL;
361   f->Tf            = NULL;
362   f->Tc            = NULL;
363   PetscCall(PetscArrayzero(&f->quadrature, 1));
364   PetscCall(PetscArrayzero(&f->faceQuadrature, 1));
365   f->blockSize  = 0;
366   f->numBlocks  = 1;
367   f->batchSize  = 0;
368   f->numBatches = 1;
369 
370   *fem = f;
371   PetscFunctionReturn(PETSC_SUCCESS);
372 }
373 
374 /*@
375   PetscFEGetSpatialDimension - Returns the spatial dimension of the element
376 
377   Not Collective
378 
379   Input Parameter:
380 . fem - The `PetscFE` object
381 
382   Output Parameter:
383 . dim - The spatial dimension
384 
385   Level: intermediate
386 
387 .seealso: `PetscFE`, `PetscFECreate()`
388 @*/
389 PetscErrorCode PetscFEGetSpatialDimension(PetscFE fem, PetscInt *dim)
390 {
391   DM dm;
392 
393   PetscFunctionBegin;
394   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
395   PetscAssertPointer(dim, 2);
396   PetscCall(PetscDualSpaceGetDM(fem->dualSpace, &dm));
397   PetscCall(DMGetDimension(dm, dim));
398   PetscFunctionReturn(PETSC_SUCCESS);
399 }
400 
401 /*@
402   PetscFESetNumComponents - Sets the number of field components in the element
403 
404   Not Collective
405 
406   Input Parameters:
407 + fem  - The `PetscFE` object
408 - comp - The number of field components
409 
410   Level: intermediate
411 
412 .seealso: `PetscFE`, `PetscFECreate()`, `PetscFEGetSpatialDimension()`, `PetscFEGetNumComponents()`
413 @*/
414 PetscErrorCode PetscFESetNumComponents(PetscFE fem, PetscInt comp)
415 {
416   PetscFunctionBegin;
417   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
418   fem->numComponents = comp;
419   PetscFunctionReturn(PETSC_SUCCESS);
420 }
421 
422 /*@
423   PetscFEGetNumComponents - Returns the number of components in the element
424 
425   Not Collective
426 
427   Input Parameter:
428 . fem - The `PetscFE` object
429 
430   Output Parameter:
431 . comp - The number of field components
432 
433   Level: intermediate
434 
435 .seealso: `PetscFE`, `PetscFECreate()`, `PetscFEGetSpatialDimension()`
436 @*/
437 PetscErrorCode PetscFEGetNumComponents(PetscFE fem, PetscInt *comp)
438 {
439   PetscFunctionBegin;
440   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
441   PetscAssertPointer(comp, 2);
442   *comp = fem->numComponents;
443   PetscFunctionReturn(PETSC_SUCCESS);
444 }
445 
446 /*@
447   PetscFESetTileSizes - Sets the tile sizes for evaluation
448 
449   Not Collective
450 
451   Input Parameters:
452 + fem        - The `PetscFE` object
453 . blockSize  - The number of elements in a block
454 . numBlocks  - The number of blocks in a batch
455 . batchSize  - The number of elements in a batch
456 - numBatches - The number of batches in a chunk
457 
458   Level: intermediate
459 
460 .seealso: `PetscFE`, `PetscFECreate()`, `PetscFEGetTileSizes()`
461 @*/
462 PetscErrorCode PetscFESetTileSizes(PetscFE fem, PetscInt blockSize, PetscInt numBlocks, PetscInt batchSize, PetscInt numBatches)
463 {
464   PetscFunctionBegin;
465   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
466   fem->blockSize  = blockSize;
467   fem->numBlocks  = numBlocks;
468   fem->batchSize  = batchSize;
469   fem->numBatches = numBatches;
470   PetscFunctionReturn(PETSC_SUCCESS);
471 }
472 
473 /*@
474   PetscFEGetTileSizes - Returns the tile sizes for evaluation
475 
476   Not Collective
477 
478   Input Parameter:
479 . fem - The `PetscFE` object
480 
481   Output Parameters:
482 + blockSize  - The number of elements in a block, pass `NULL` if not needed
483 . numBlocks  - The number of blocks in a batch, pass `NULL` if not needed
484 . batchSize  - The number of elements in a batch, pass `NULL` if not needed
485 - numBatches - The number of batches in a chunk, pass `NULL` if not needed
486 
487   Level: intermediate
488 
489 .seealso: `PetscFE`, `PetscFECreate()`, `PetscFESetTileSizes()`
490 @*/
491 PetscErrorCode PetscFEGetTileSizes(PetscFE fem, PeOp PetscInt *blockSize, PeOp PetscInt *numBlocks, PeOp PetscInt *batchSize, PeOp PetscInt *numBatches)
492 {
493   PetscFunctionBegin;
494   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
495   if (blockSize) PetscAssertPointer(blockSize, 2);
496   if (numBlocks) PetscAssertPointer(numBlocks, 3);
497   if (batchSize) PetscAssertPointer(batchSize, 4);
498   if (numBatches) PetscAssertPointer(numBatches, 5);
499   if (blockSize) *blockSize = fem->blockSize;
500   if (numBlocks) *numBlocks = fem->numBlocks;
501   if (batchSize) *batchSize = fem->batchSize;
502   if (numBatches) *numBatches = fem->numBatches;
503   PetscFunctionReturn(PETSC_SUCCESS);
504 }
505 
506 /*@
507   PetscFEGetBasisSpace - Returns the `PetscSpace` used for the approximation of the solution for the `PetscFE`
508 
509   Not Collective
510 
511   Input Parameter:
512 . fem - The `PetscFE` object
513 
514   Output Parameter:
515 . sp - The `PetscSpace` object
516 
517   Level: intermediate
518 
519 .seealso: `PetscFE`, `PetscSpace`, `PetscFECreate()`
520 @*/
521 PetscErrorCode PetscFEGetBasisSpace(PetscFE fem, PetscSpace *sp)
522 {
523   PetscFunctionBegin;
524   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
525   PetscAssertPointer(sp, 2);
526   *sp = fem->basisSpace;
527   PetscFunctionReturn(PETSC_SUCCESS);
528 }
529 
530 /*@
531   PetscFESetBasisSpace - Sets the `PetscSpace` used for the approximation of the solution
532 
533   Not Collective
534 
535   Input Parameters:
536 + fem - The `PetscFE` object
537 - sp  - The `PetscSpace` object
538 
539   Level: intermediate
540 
541   Developer Notes:
542   There is `PetscFESetBasisSpace()` but the `PetscFESetDualSpace()`, likely the Basis is unneeded in the function name
543 
544 .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscFECreate()`, `PetscFESetDualSpace()`
545 @*/
546 PetscErrorCode PetscFESetBasisSpace(PetscFE fem, PetscSpace sp)
547 {
548   PetscFunctionBegin;
549   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
550   PetscValidHeaderSpecific(sp, PETSCSPACE_CLASSID, 2);
551   PetscCall(PetscSpaceDestroy(&fem->basisSpace));
552   fem->basisSpace = sp;
553   PetscCall(PetscObjectReference((PetscObject)fem->basisSpace));
554   PetscFunctionReturn(PETSC_SUCCESS);
555 }
556 
557 /*@
558   PetscFEGetDualSpace - Returns the `PetscDualSpace` used to define the inner product for a `PetscFE`
559 
560   Not Collective
561 
562   Input Parameter:
563 . fem - The `PetscFE` object
564 
565   Output Parameter:
566 . sp - The `PetscDualSpace` object
567 
568   Level: intermediate
569 
570 .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscFECreate()`
571 @*/
572 PetscErrorCode PetscFEGetDualSpace(PetscFE fem, PetscDualSpace *sp)
573 {
574   PetscFunctionBegin;
575   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
576   PetscAssertPointer(sp, 2);
577   *sp = fem->dualSpace;
578   PetscFunctionReturn(PETSC_SUCCESS);
579 }
580 
581 /*@
582   PetscFESetDualSpace - Sets the `PetscDualSpace` used to define the inner product
583 
584   Not Collective
585 
586   Input Parameters:
587 + fem - The `PetscFE` object
588 - sp  - The `PetscDualSpace` object
589 
590   Level: intermediate
591 
592 .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscFECreate()`, `PetscFESetBasisSpace()`
593 @*/
594 PetscErrorCode PetscFESetDualSpace(PetscFE fem, PetscDualSpace sp)
595 {
596   PetscFunctionBegin;
597   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
598   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 2);
599   PetscCall(PetscDualSpaceDestroy(&fem->dualSpace));
600   fem->dualSpace = sp;
601   PetscCall(PetscObjectReference((PetscObject)fem->dualSpace));
602   PetscFunctionReturn(PETSC_SUCCESS);
603 }
604 
605 /*@
606   PetscFEGetQuadrature - Returns the `PetscQuadrature` used to calculate inner products
607 
608   Not Collective
609 
610   Input Parameter:
611 . fem - The `PetscFE` object
612 
613   Output Parameter:
614 . q - The `PetscQuadrature` object
615 
616   Level: intermediate
617 
618 .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscQuadrature`, `PetscFECreate()`
619 @*/
620 PetscErrorCode PetscFEGetQuadrature(PetscFE fem, PetscQuadrature *q)
621 {
622   PetscFunctionBegin;
623   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
624   PetscAssertPointer(q, 2);
625   *q = fem->quadrature;
626   PetscFunctionReturn(PETSC_SUCCESS);
627 }
628 
629 /*@
630   PetscFESetQuadrature - Sets the `PetscQuadrature` used to calculate inner products
631 
632   Not Collective
633 
634   Input Parameters:
635 + fem - The `PetscFE` object
636 - q   - The `PetscQuadrature` object
637 
638   Level: intermediate
639 
640 .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscQuadrature`, `PetscFECreate()`, `PetscFEGetFaceQuadrature()`
641 @*/
642 PetscErrorCode PetscFESetQuadrature(PetscFE fem, PetscQuadrature q)
643 {
644   PetscInt Nc, qNc;
645 
646   PetscFunctionBegin;
647   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
648   if (q == fem->quadrature) PetscFunctionReturn(PETSC_SUCCESS);
649   PetscCall(PetscFEGetNumComponents(fem, &Nc));
650   PetscCall(PetscQuadratureGetNumComponents(q, &qNc));
651   PetscCheck(!(qNc != 1) || !(Nc != qNc), PetscObjectComm((PetscObject)fem), PETSC_ERR_ARG_SIZ, "FE components %" PetscInt_FMT " != Quadrature components %" PetscInt_FMT " and non-scalar quadrature", Nc, qNc);
652   PetscCall(PetscTabulationDestroy(&fem->T));
653   PetscCall(PetscTabulationDestroy(&fem->Tc));
654   PetscCall(PetscObjectReference((PetscObject)q));
655   PetscCall(PetscQuadratureDestroy(&fem->quadrature));
656   fem->quadrature = q;
657   PetscFunctionReturn(PETSC_SUCCESS);
658 }
659 
660 /*@
661   PetscFEGetFaceQuadrature - Returns the `PetscQuadrature` used to calculate inner products on faces
662 
663   Not Collective
664 
665   Input Parameter:
666 . fem - The `PetscFE` object
667 
668   Output Parameter:
669 . q - The `PetscQuadrature` object
670 
671   Level: intermediate
672 
673   Developer Notes:
674   There is a special face quadrature but not edge, likely this API would benefit from a refactorization
675 
676 .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscQuadrature`, `PetscFECreate()`, `PetscFESetQuadrature()`, `PetscFESetFaceQuadrature()`
677 @*/
678 PetscErrorCode PetscFEGetFaceQuadrature(PetscFE fem, PetscQuadrature *q)
679 {
680   PetscFunctionBegin;
681   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
682   PetscAssertPointer(q, 2);
683   *q = fem->faceQuadrature;
684   PetscFunctionReturn(PETSC_SUCCESS);
685 }
686 
687 /*@
688   PetscFESetFaceQuadrature - Sets the `PetscQuadrature` used to calculate inner products on faces
689 
690   Not Collective
691 
692   Input Parameters:
693 + fem - The `PetscFE` object
694 - q   - The `PetscQuadrature` object
695 
696   Level: intermediate
697 
698 .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscQuadrature`, `PetscFECreate()`, `PetscFESetQuadrature()`
699 @*/
700 PetscErrorCode PetscFESetFaceQuadrature(PetscFE fem, PetscQuadrature q)
701 {
702   PetscInt Nc, qNc;
703 
704   PetscFunctionBegin;
705   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
706   if (q == fem->faceQuadrature) PetscFunctionReturn(PETSC_SUCCESS);
707   PetscCall(PetscFEGetNumComponents(fem, &Nc));
708   PetscCall(PetscQuadratureGetNumComponents(q, &qNc));
709   PetscCheck(!(qNc != 1) || !(Nc != qNc), PetscObjectComm((PetscObject)fem), PETSC_ERR_ARG_SIZ, "FE components %" PetscInt_FMT " != Quadrature components %" PetscInt_FMT " and non-scalar quadrature", Nc, qNc);
710   PetscCall(PetscTabulationDestroy(&fem->Tf));
711   PetscCall(PetscObjectReference((PetscObject)q));
712   PetscCall(PetscQuadratureDestroy(&fem->faceQuadrature));
713   fem->faceQuadrature = q;
714   PetscFunctionReturn(PETSC_SUCCESS);
715 }
716 
717 /*@
718   PetscFECopyQuadrature - Copy both volumetric and surface quadrature to a new `PetscFE`
719 
720   Not Collective
721 
722   Input Parameters:
723 + sfe - The `PetscFE` source for the quadratures
724 - tfe - The `PetscFE` target for the quadratures
725 
726   Level: intermediate
727 
728 .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscQuadrature`, `PetscFECreate()`, `PetscFESetQuadrature()`, `PetscFESetFaceQuadrature()`
729 @*/
730 PetscErrorCode PetscFECopyQuadrature(PetscFE sfe, PetscFE tfe)
731 {
732   PetscQuadrature q;
733 
734   PetscFunctionBegin;
735   PetscValidHeaderSpecific(sfe, PETSCFE_CLASSID, 1);
736   PetscValidHeaderSpecific(tfe, PETSCFE_CLASSID, 2);
737   PetscCall(PetscFEGetQuadrature(sfe, &q));
738   PetscCall(PetscFESetQuadrature(tfe, q));
739   PetscCall(PetscFEGetFaceQuadrature(sfe, &q));
740   PetscCall(PetscFESetFaceQuadrature(tfe, q));
741   PetscFunctionReturn(PETSC_SUCCESS);
742 }
743 
744 /*@C
745   PetscFEGetNumDof - Returns the number of dofs (dual basis vectors) associated to mesh points on the reference cell of a given dimension
746 
747   Not Collective
748 
749   Input Parameter:
750 . fem - The `PetscFE` object
751 
752   Output Parameter:
753 . numDof - Array of length `dim` with the number of dofs in each dimension
754 
755   Level: intermediate
756 
757 .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscFECreate()`
758 @*/
759 PetscErrorCode PetscFEGetNumDof(PetscFE fem, const PetscInt *numDof[])
760 {
761   PetscFunctionBegin;
762   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
763   PetscAssertPointer(numDof, 2);
764   PetscCall(PetscDualSpaceGetNumDof(fem->dualSpace, numDof));
765   PetscFunctionReturn(PETSC_SUCCESS);
766 }
767 
768 /*@C
769   PetscFEGetCellTabulation - Returns the tabulation of the basis functions at the quadrature points on the reference cell
770 
771   Not Collective
772 
773   Input Parameters:
774 + fem - The `PetscFE` object
775 - k   - The highest derivative we need to tabulate, very often 1
776 
777   Output Parameter:
778 . T - The basis function values and derivatives at quadrature points
779 
780   Level: intermediate
781 
782   Note:
783 .vb
784   T->T[0] = B[(p*pdim + i)*Nc + c] is the value at point p for basis function i and component c
785   T->T[1] = D[((p*pdim + i)*Nc + c)*dim + d] is the derivative value at point p for basis function i, component c, in direction d
786   T->T[2] = H[(((p*pdim + i)*Nc + c)*dim + d)*dim + e] is the value at point p for basis function i, component c, in directions d and e
787 .ve
788 
789 .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscTabulation`, `PetscFECreateTabulation()`, `PetscTabulationDestroy()`
790 @*/
791 PetscErrorCode PetscFEGetCellTabulation(PetscFE fem, PetscInt k, PetscTabulation *T)
792 {
793   PetscInt         npoints;
794   const PetscReal *points;
795 
796   PetscFunctionBegin;
797   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
798   PetscAssertPointer(T, 3);
799   PetscCall(PetscQuadratureGetData(fem->quadrature, NULL, NULL, &npoints, &points, NULL));
800   if (!fem->T) PetscCall(PetscFECreateTabulation(fem, 1, npoints, points, k, &fem->T));
801   PetscCheck(!fem->T || k <= fem->T->K || (!fem->T->cdim && !fem->T->K), PetscObjectComm((PetscObject)fem), PETSC_ERR_ARG_OUTOFRANGE, "Requested %" PetscInt_FMT " derivatives, but only tabulated %" PetscInt_FMT, k, fem->T->K);
802   *T = fem->T;
803   PetscFunctionReturn(PETSC_SUCCESS);
804 }
805 
806 PetscErrorCode PetscFEExpandFaceQuadrature(PetscFE fe, PetscQuadrature fq, PetscQuadrature *efq)
807 {
808   DM               dm;
809   PetscDualSpace   sp;
810   const PetscInt  *faces;
811   const PetscReal *points, *weights;
812   DMPolytopeType   ct;
813   PetscReal       *facePoints, *faceWeights;
814   PetscInt         dim, cStart, Nf, Nc, Np, order;
815 
816   PetscFunctionBegin;
817   PetscCall(PetscFEGetDualSpace(fe, &sp));
818   PetscCall(PetscDualSpaceGetDM(sp, &dm));
819   PetscCall(DMGetDimension(dm, &dim));
820   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, NULL));
821   PetscCall(DMPlexGetConeSize(dm, cStart, &Nf));
822   PetscCall(DMPlexGetCone(dm, cStart, &faces));
823   PetscCall(PetscQuadratureGetData(fq, NULL, &Nc, &Np, &points, &weights));
824   PetscCall(PetscMalloc1(Nf * Np * dim, &facePoints));
825   PetscCall(PetscMalloc1(Nf * Np * Nc, &faceWeights));
826   for (PetscInt f = 0; f < Nf; ++f) {
827     const PetscReal xi0[3] = {-1., -1., -1.};
828     PetscReal       v0[3], J[9], detJ;
829 
830     PetscCall(DMPlexComputeCellGeometryFEM(dm, faces[f], NULL, v0, J, NULL, &detJ));
831     for (PetscInt q = 0; q < Np; ++q) {
832       CoordinatesRefToReal(dim, dim - 1, xi0, v0, J, &points[q * (dim - 1)], &facePoints[(f * Np + q) * dim]);
833       for (PetscInt c = 0; c < Nc; ++c) faceWeights[(f * Np + q) * Nc + c] = weights[q * Nc + c];
834     }
835   }
836   PetscCall(PetscQuadratureCreate(PetscObjectComm((PetscObject)fq), efq));
837   PetscCall(PetscQuadratureGetCellType(fq, &ct));
838   PetscCall(PetscQuadratureSetCellType(*efq, ct));
839   PetscCall(PetscQuadratureGetOrder(fq, &order));
840   PetscCall(PetscQuadratureSetOrder(*efq, order));
841   PetscCall(PetscQuadratureSetData(*efq, dim, Nc, Nf * Np, facePoints, faceWeights));
842   PetscFunctionReturn(PETSC_SUCCESS);
843 }
844 
845 /*@C
846   PetscFEGetFaceTabulation - Returns the tabulation of the basis functions at the face quadrature points for each face of the reference cell
847 
848   Not Collective
849 
850   Input Parameters:
851 + fem - The `PetscFE` object
852 - k   - The highest derivative we need to tabulate, very often 1
853 
854   Output Parameter:
855 . Tf - The basis function values and derivatives at face quadrature points
856 
857   Level: intermediate
858 
859   Note:
860 .vb
861   T->T[0] = Bf[((f*Nq + q)*pdim + i)*Nc + c] is the value at point f,q for basis function i and component c
862   T->T[1] = Df[(((f*Nq + q)*pdim + i)*Nc + c)*dim + d] is the derivative value at point f,q for basis function i, component c, in direction d
863   T->T[2] = Hf[((((f*Nq + q)*pdim + i)*Nc + c)*dim + d)*dim + e] is the value at point f,q for basis function i, component c, in directions d and e
864 .ve
865 
866 .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscTabulation`, `PetscFEGetCellTabulation()`, `PetscFECreateTabulation()`, `PetscTabulationDestroy()`
867 @*/
868 PetscErrorCode PetscFEGetFaceTabulation(PetscFE fem, PetscInt k, PetscTabulation *Tf)
869 {
870   PetscFunctionBegin;
871   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
872   PetscAssertPointer(Tf, 3);
873   if (!fem->Tf) {
874     PetscQuadrature fq;
875 
876     PetscCall(PetscFEGetFaceQuadrature(fem, &fq));
877     if (fq) {
878       PetscQuadrature  efq;
879       const PetscReal *facePoints;
880       PetscInt         Np, eNp;
881 
882       PetscCall(PetscFEExpandFaceQuadrature(fem, fq, &efq));
883       PetscCall(PetscQuadratureGetData(fq, NULL, NULL, &Np, NULL, NULL));
884       PetscCall(PetscQuadratureGetData(efq, NULL, NULL, &eNp, &facePoints, NULL));
885       if (PetscDefined(USE_DEBUG)) {
886         PetscDualSpace sp;
887         DM             dm;
888         PetscInt       cStart, Nf;
889 
890         PetscCall(PetscFEGetDualSpace(fem, &sp));
891         PetscCall(PetscDualSpaceGetDM(sp, &dm));
892         PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, NULL));
893         PetscCall(DMPlexGetConeSize(dm, cStart, &Nf));
894         PetscCheck(Nf == eNp / Np, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Number of faces %" PetscInt_FMT " != %" PetscInt_FMT " number of quadrature replicas", Nf, eNp / Np);
895       }
896       PetscCall(PetscFECreateTabulation(fem, eNp / Np, Np, facePoints, k, &fem->Tf));
897       PetscCall(PetscQuadratureDestroy(&efq));
898     }
899   }
900   PetscCheck(!fem->Tf || k <= fem->Tf->K, PetscObjectComm((PetscObject)fem), PETSC_ERR_ARG_OUTOFRANGE, "Requested %" PetscInt_FMT " derivatives, but only tabulated %" PetscInt_FMT, k, fem->Tf->K);
901   *Tf = fem->Tf;
902   PetscFunctionReturn(PETSC_SUCCESS);
903 }
904 
905 /*@C
906   PetscFEGetFaceCentroidTabulation - Returns the tabulation of the basis functions at the face centroid points
907 
908   Not Collective
909 
910   Input Parameter:
911 . fem - The `PetscFE` object
912 
913   Output Parameter:
914 . Tc - The basis function values at face centroid points
915 
916   Level: intermediate
917 
918   Note:
919 .vb
920   T->T[0] = Bf[(f*pdim + i)*Nc + c] is the value at point f for basis function i and component c
921 .ve
922 
923 .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscTabulation`, `PetscFEGetFaceTabulation()`, `PetscFEGetCellTabulation()`, `PetscFECreateTabulation()`, `PetscTabulationDestroy()`
924 @*/
925 PetscErrorCode PetscFEGetFaceCentroidTabulation(PetscFE fem, PetscTabulation *Tc)
926 {
927   PetscFunctionBegin;
928   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
929   PetscAssertPointer(Tc, 2);
930   if (!fem->Tc) {
931     PetscDualSpace  sp;
932     DM              dm;
933     const PetscInt *cone;
934     PetscReal      *centroids;
935     PetscInt        dim, numFaces, f;
936 
937     PetscCall(PetscFEGetDualSpace(fem, &sp));
938     PetscCall(PetscDualSpaceGetDM(sp, &dm));
939     PetscCall(DMGetDimension(dm, &dim));
940     PetscCall(DMPlexGetConeSize(dm, 0, &numFaces));
941     PetscCall(DMPlexGetCone(dm, 0, &cone));
942     PetscCall(PetscMalloc1(numFaces * dim, &centroids));
943     for (f = 0; f < numFaces; ++f) PetscCall(DMPlexComputeCellGeometryFVM(dm, cone[f], NULL, &centroids[f * dim], NULL));
944     PetscCall(PetscFECreateTabulation(fem, 1, numFaces, centroids, 0, &fem->Tc));
945     PetscCall(PetscFree(centroids));
946   }
947   *Tc = fem->Tc;
948   PetscFunctionReturn(PETSC_SUCCESS);
949 }
950 
951 /*@C
952   PetscFECreateTabulation - Tabulates the basis functions, and perhaps derivatives, at the points provided.
953 
954   Not Collective
955 
956   Input Parameters:
957 + fem     - The `PetscFE` object
958 . nrepl   - The number of replicas
959 . npoints - The number of tabulation points in a replica
960 . points  - The tabulation point coordinates
961 - K       - The number of derivatives calculated
962 
963   Output Parameter:
964 . T - The basis function values and derivatives at tabulation points
965 
966   Level: intermediate
967 
968   Note:
969 .vb
970   T->T[0] = B[(p*pdim + i)*Nc + c] is the value at point p for basis function i and component c
971   T->T[1] = D[((p*pdim + i)*Nc + c)*dim + d] is the derivative value at point p for basis function i, component c, in direction d
972   T->T[2] = H[(((p*pdim + i)*Nc + c)*dim + d)*dim + e] is the value at point p for basis
973   T->function i, component c, in directions d and e
974 .ve
975 
976 .seealso: `PetscTabulation`, `PetscFEGetCellTabulation()`, `PetscTabulationDestroy()`
977 @*/
978 PetscErrorCode PetscFECreateTabulation(PetscFE fem, PetscInt nrepl, PetscInt npoints, const PetscReal points[], PetscInt K, PetscTabulation *T)
979 {
980   DM             dm;
981   PetscDualSpace Q;
982   PetscInt       Nb;   /* Dimension of FE space P */
983   PetscInt       Nc;   /* Field components */
984   PetscInt       cdim; /* Reference coordinate dimension */
985   PetscInt       k;
986 
987   PetscFunctionBegin;
988   if (!npoints || !fem->dualSpace || K < 0) {
989     *T = NULL;
990     PetscFunctionReturn(PETSC_SUCCESS);
991   }
992   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
993   PetscAssertPointer(points, 4);
994   PetscAssertPointer(T, 6);
995   PetscCall(PetscFEGetDualSpace(fem, &Q));
996   PetscCall(PetscDualSpaceGetDM(Q, &dm));
997   PetscCall(DMGetDimension(dm, &cdim));
998   PetscCall(PetscDualSpaceGetDimension(Q, &Nb));
999   PetscCall(PetscFEGetNumComponents(fem, &Nc));
1000   PetscCall(PetscMalloc1(1, T));
1001   (*T)->K    = !cdim ? 0 : K;
1002   (*T)->Nr   = nrepl;
1003   (*T)->Np   = npoints;
1004   (*T)->Nb   = Nb;
1005   (*T)->Nc   = Nc;
1006   (*T)->cdim = cdim;
1007   PetscCall(PetscMalloc1((*T)->K + 1, &(*T)->T));
1008   for (k = 0; k <= (*T)->K; ++k) PetscCall(PetscCalloc1(nrepl * npoints * Nb * Nc * PetscPowInt(cdim, k), &(*T)->T[k]));
1009   PetscUseTypeMethod(fem, computetabulation, nrepl * npoints, points, K, *T);
1010   PetscFunctionReturn(PETSC_SUCCESS);
1011 }
1012 
1013 /*@C
1014   PetscFEComputeTabulation - Tabulates the basis functions, and perhaps derivatives, at the points provided.
1015 
1016   Not Collective
1017 
1018   Input Parameters:
1019 + fem     - The `PetscFE` object
1020 . npoints - The number of tabulation points
1021 . points  - The tabulation point coordinates
1022 . K       - The number of derivatives calculated
1023 - T       - An existing tabulation object with enough allocated space
1024 
1025   Output Parameter:
1026 . T - The basis function values and derivatives at tabulation points
1027 
1028   Level: intermediate
1029 
1030   Note:
1031 .vb
1032   T->T[0] = B[(p*pdim + i)*Nc + c] is the value at point p for basis function i and component c
1033   T->T[1] = D[((p*pdim + i)*Nc + c)*dim + d] is the derivative value at point p for basis function i, component c, in direction d
1034   T->T[2] = H[(((p*pdim + i)*Nc + c)*dim + d)*dim + e] is the value at point p for basis function i, component c, in directions d and e
1035 .ve
1036 
1037 .seealso: `PetscTabulation`, `PetscFEGetCellTabulation()`, `PetscTabulationDestroy()`
1038 @*/
1039 PetscErrorCode PetscFEComputeTabulation(PetscFE fem, PetscInt npoints, const PetscReal points[], PetscInt K, PetscTabulation T)
1040 {
1041   PetscFunctionBeginHot;
1042   if (!npoints || !fem->dualSpace || K < 0) PetscFunctionReturn(PETSC_SUCCESS);
1043   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
1044   PetscAssertPointer(points, 3);
1045   PetscAssertPointer(T, 5);
1046   if (PetscDefined(USE_DEBUG)) {
1047     DM             dm;
1048     PetscDualSpace Q;
1049     PetscInt       Nb;   /* Dimension of FE space P */
1050     PetscInt       Nc;   /* Field components */
1051     PetscInt       cdim; /* Reference coordinate dimension */
1052 
1053     PetscCall(PetscFEGetDualSpace(fem, &Q));
1054     PetscCall(PetscDualSpaceGetDM(Q, &dm));
1055     PetscCall(DMGetDimension(dm, &cdim));
1056     PetscCall(PetscDualSpaceGetDimension(Q, &Nb));
1057     PetscCall(PetscFEGetNumComponents(fem, &Nc));
1058     PetscCheck(T->K == (!cdim ? 0 : K), PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Tabulation K %" PetscInt_FMT " must match requested K %" PetscInt_FMT, T->K, !cdim ? 0 : K);
1059     PetscCheck(T->Nb == Nb, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Tabulation Nb %" PetscInt_FMT " must match requested Nb %" PetscInt_FMT, T->Nb, Nb);
1060     PetscCheck(T->Nc == Nc, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Tabulation Nc %" PetscInt_FMT " must match requested Nc %" PetscInt_FMT, T->Nc, Nc);
1061     PetscCheck(T->cdim == cdim, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Tabulation cdim %" PetscInt_FMT " must match requested cdim %" PetscInt_FMT, T->cdim, cdim);
1062   }
1063   T->Nr = 1;
1064   T->Np = npoints;
1065   PetscUseTypeMethod(fem, computetabulation, npoints, points, K, T);
1066   PetscFunctionReturn(PETSC_SUCCESS);
1067 }
1068 
1069 /*@
1070   PetscTabulationDestroy - Frees memory from the associated tabulation.
1071 
1072   Not Collective
1073 
1074   Input Parameter:
1075 . T - The tabulation
1076 
1077   Level: intermediate
1078 
1079 .seealso: `PetscTabulation`, `PetscFECreateTabulation()`, `PetscFEGetCellTabulation()`
1080 @*/
1081 PetscErrorCode PetscTabulationDestroy(PetscTabulation *T)
1082 {
1083   PetscInt k;
1084 
1085   PetscFunctionBegin;
1086   PetscAssertPointer(T, 1);
1087   if (!T || !*T) PetscFunctionReturn(PETSC_SUCCESS);
1088   for (k = 0; k <= (*T)->K; ++k) PetscCall(PetscFree((*T)->T[k]));
1089   PetscCall(PetscFree((*T)->T));
1090   PetscCall(PetscFree(*T));
1091   *T = NULL;
1092   PetscFunctionReturn(PETSC_SUCCESS);
1093 }
1094 
1095 static PetscErrorCode PetscFECreatePointTraceDefault_Internal(PetscFE fe, PetscInt refPoint, PetscFE *trFE)
1096 {
1097   PetscSpace      bsp, bsubsp;
1098   PetscDualSpace  dsp, dsubsp;
1099   PetscInt        dim, depth, numComp, i, j, coneSize, order;
1100   DM              dm;
1101   DMLabel         label;
1102   PetscReal      *xi, *v, *J, detJ;
1103   const char     *name;
1104   PetscQuadrature origin, fullQuad, subQuad;
1105 
1106   PetscFunctionBegin;
1107   PetscCall(PetscFEGetBasisSpace(fe, &bsp));
1108   PetscCall(PetscFEGetDualSpace(fe, &dsp));
1109   PetscCall(PetscDualSpaceGetDM(dsp, &dm));
1110   PetscCall(DMGetDimension(dm, &dim));
1111   PetscCall(DMPlexGetDepthLabel(dm, &label));
1112   PetscCall(DMLabelGetValue(label, refPoint, &depth));
1113   PetscCall(PetscCalloc1(depth, &xi));
1114   PetscCall(PetscMalloc1(dim, &v));
1115   PetscCall(PetscMalloc1(dim * dim, &J));
1116   for (i = 0; i < depth; i++) xi[i] = 0.;
1117   PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, &origin));
1118   PetscCall(PetscQuadratureSetData(origin, depth, 0, 1, xi, NULL));
1119   PetscCall(DMPlexComputeCellGeometryFEM(dm, refPoint, origin, v, J, NULL, &detJ));
1120   /* CellGeometryFEM computes the expanded Jacobian, we want the true jacobian */
1121   for (i = 1; i < dim; i++) {
1122     for (j = 0; j < depth; j++) J[i * depth + j] = J[i * dim + j];
1123   }
1124   PetscCall(PetscQuadratureDestroy(&origin));
1125   PetscCall(PetscDualSpaceGetPointSubspace(dsp, refPoint, &dsubsp));
1126   PetscCall(PetscSpaceCreateSubspace(bsp, dsubsp, v, J, NULL, NULL, PETSC_OWN_POINTER, &bsubsp));
1127   PetscCall(PetscSpaceSetUp(bsubsp));
1128   PetscCall(PetscFECreate(PetscObjectComm((PetscObject)fe), trFE));
1129   PetscCall(PetscFESetType(*trFE, PETSCFEBASIC));
1130   PetscCall(PetscFEGetNumComponents(fe, &numComp));
1131   PetscCall(PetscFESetNumComponents(*trFE, numComp));
1132   PetscCall(PetscFESetBasisSpace(*trFE, bsubsp));
1133   PetscCall(PetscFESetDualSpace(*trFE, dsubsp));
1134   PetscCall(PetscObjectGetName((PetscObject)fe, &name));
1135   if (name) PetscCall(PetscFESetName(*trFE, name));
1136   PetscCall(PetscFEGetQuadrature(fe, &fullQuad));
1137   PetscCall(PetscQuadratureGetOrder(fullQuad, &order));
1138   PetscCall(DMPlexGetConeSize(dm, refPoint, &coneSize));
1139   if (coneSize == 2 * depth) PetscCall(PetscDTGaussTensorQuadrature(depth, 1, (order + 2) / 2, -1., 1., &subQuad));
1140   else PetscCall(PetscDTSimplexQuadrature(depth, order, PETSCDTSIMPLEXQUAD_DEFAULT, &subQuad));
1141   PetscCall(PetscFESetQuadrature(*trFE, subQuad));
1142   PetscCall(PetscFESetUp(*trFE));
1143   PetscCall(PetscQuadratureDestroy(&subQuad));
1144   PetscCall(PetscSpaceDestroy(&bsubsp));
1145   PetscFunctionReturn(PETSC_SUCCESS);
1146 }
1147 
1148 PETSC_EXTERN PetscErrorCode PetscFECreatePointTrace(PetscFE fe, PetscInt refPoint, PetscFE *trFE)
1149 {
1150   PetscFunctionBegin;
1151   PetscValidHeaderSpecific(fe, PETSCFE_CLASSID, 1);
1152   PetscAssertPointer(trFE, 3);
1153   if (fe->ops->createpointtrace) PetscUseTypeMethod(fe, createpointtrace, refPoint, trFE);
1154   else PetscCall(PetscFECreatePointTraceDefault_Internal(fe, refPoint, trFE));
1155   PetscFunctionReturn(PETSC_SUCCESS);
1156 }
1157 
1158 PetscErrorCode PetscFECreateHeightTrace(PetscFE fe, PetscInt height, PetscFE *trFE)
1159 {
1160   PetscInt       hStart, hEnd;
1161   PetscDualSpace dsp;
1162   DM             dm;
1163 
1164   PetscFunctionBegin;
1165   PetscValidHeaderSpecific(fe, PETSCFE_CLASSID, 1);
1166   PetscAssertPointer(trFE, 3);
1167   *trFE = NULL;
1168   PetscCall(PetscFEGetDualSpace(fe, &dsp));
1169   PetscCall(PetscDualSpaceGetDM(dsp, &dm));
1170   PetscCall(DMPlexGetHeightStratum(dm, height, &hStart, &hEnd));
1171   if (hEnd <= hStart) PetscFunctionReturn(PETSC_SUCCESS);
1172   PetscCall(PetscFECreatePointTrace(fe, hStart, trFE));
1173   PetscFunctionReturn(PETSC_SUCCESS);
1174 }
1175 
1176 /*@
1177   PetscFEGetDimension - Get the dimension of the finite element space on a cell
1178 
1179   Not Collective
1180 
1181   Input Parameter:
1182 . fem - The `PetscFE`
1183 
1184   Output Parameter:
1185 . dim - The dimension
1186 
1187   Level: intermediate
1188 
1189 .seealso: `PetscFE`, `PetscFECreate()`, `PetscSpaceGetDimension()`, `PetscDualSpaceGetDimension()`
1190 @*/
1191 PetscErrorCode PetscFEGetDimension(PetscFE fem, PetscInt *dim)
1192 {
1193   PetscFunctionBegin;
1194   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
1195   PetscAssertPointer(dim, 2);
1196   PetscTryTypeMethod(fem, getdimension, dim);
1197   PetscFunctionReturn(PETSC_SUCCESS);
1198 }
1199 
1200 /*@
1201   PetscFEPushforward - Map the reference element function to real space
1202 
1203   Input Parameters:
1204 + fe     - The `PetscFE`
1205 . fegeom - The cell geometry
1206 . Nv     - The number of function values
1207 - vals   - The function values
1208 
1209   Output Parameter:
1210 . vals - The transformed function values
1211 
1212   Level: advanced
1213 
1214   Notes:
1215   This just forwards the call onto `PetscDualSpacePushforward()`.
1216 
1217   It only handles transformations when the embedding dimension of the geometry in fegeom is the same as the reference dimension.
1218 
1219 .seealso: `PetscFE`, `PetscFEGeom`, `PetscDualSpace`, `PetscDualSpacePushforward()`
1220 @*/
1221 PetscErrorCode PetscFEPushforward(PetscFE fe, PetscFEGeom *fegeom, PetscInt Nv, PetscScalar vals[])
1222 {
1223   PetscFunctionBeginHot;
1224   PetscCall(PetscDualSpacePushforward(fe->dualSpace, fegeom, Nv, fe->numComponents, vals));
1225   PetscFunctionReturn(PETSC_SUCCESS);
1226 }
1227 
1228 /*@
1229   PetscFEPushforwardGradient - Map the reference element function gradient to real space
1230 
1231   Input Parameters:
1232 + fe     - The `PetscFE`
1233 . fegeom - The cell geometry
1234 . Nv     - The number of function gradient values
1235 - vals   - The function gradient values
1236 
1237   Output Parameter:
1238 . vals - The transformed function gradient values
1239 
1240   Level: advanced
1241 
1242   Notes:
1243   This just forwards the call onto `PetscDualSpacePushforwardGradient()`.
1244 
1245   It only handles transformations when the embedding dimension of the geometry in fegeom is the same as the reference dimension.
1246 
1247 .seealso: `PetscFE`, `PetscFEGeom`, `PetscDualSpace`, `PetscFEPushforward()`, `PetscDualSpacePushforwardGradient()`, `PetscDualSpacePushforward()`
1248 @*/
1249 PetscErrorCode PetscFEPushforwardGradient(PetscFE fe, PetscFEGeom *fegeom, PetscInt Nv, PetscScalar vals[])
1250 {
1251   PetscFunctionBeginHot;
1252   PetscCall(PetscDualSpacePushforwardGradient(fe->dualSpace, fegeom, Nv, fe->numComponents, vals));
1253   PetscFunctionReturn(PETSC_SUCCESS);
1254 }
1255 
1256 /*@
1257   PetscFEPushforwardHessian - Map the reference element function Hessian to real space
1258 
1259   Input Parameters:
1260 + fe     - The `PetscFE`
1261 . fegeom - The cell geometry
1262 . Nv     - The number of function Hessian values
1263 - vals   - The function Hessian values
1264 
1265   Output Parameter:
1266 . vals - The transformed function Hessian values
1267 
1268   Level: advanced
1269 
1270   Notes:
1271   This just forwards the call onto `PetscDualSpacePushforwardHessian()`.
1272 
1273   It only handles transformations when the embedding dimension of the geometry in fegeom is the same as the reference dimension.
1274 
1275   Developer Notes:
1276   It is unclear why all these one line convenience routines are desirable
1277 
1278 .seealso: `PetscFE`, `PetscFEGeom`, `PetscDualSpace`, `PetscFEPushforward()`, `PetscDualSpacePushforwardHessian()`, `PetscDualSpacePushforward()`
1279 @*/
1280 PetscErrorCode PetscFEPushforwardHessian(PetscFE fe, PetscFEGeom *fegeom, PetscInt Nv, PetscScalar vals[])
1281 {
1282   PetscFunctionBeginHot;
1283   PetscCall(PetscDualSpacePushforwardHessian(fe->dualSpace, fegeom, Nv, fe->numComponents, vals));
1284   PetscFunctionReturn(PETSC_SUCCESS);
1285 }
1286 
1287 /*
1288 Purpose: Compute element vector for chunk of elements
1289 
1290 Input:
1291   Sizes:
1292      Ne:  number of elements
1293      Nf:  number of fields
1294      PetscFE
1295        dim: spatial dimension
1296        Nb:  number of basis functions
1297        Nc:  number of field components
1298        PetscQuadrature
1299          Nq:  number of quadrature points
1300 
1301   Geometry:
1302      PetscFEGeom[Ne] possibly *Nq
1303        PetscReal v0s[dim]
1304        PetscReal n[dim]
1305        PetscReal jacobians[dim*dim]
1306        PetscReal jacobianInverses[dim*dim]
1307        PetscReal jacobianDeterminants
1308   FEM:
1309      PetscFE
1310        PetscQuadrature
1311          PetscReal   quadPoints[Nq*dim]
1312          PetscReal   quadWeights[Nq]
1313        PetscReal   basis[Nq*Nb*Nc]
1314        PetscReal   basisDer[Nq*Nb*Nc*dim]
1315      PetscScalar coefficients[Ne*Nb*Nc]
1316      PetscScalar elemVec[Ne*Nb*Nc]
1317 
1318   Problem:
1319      PetscInt f: the active field
1320      f0, f1
1321 
1322   Work Space:
1323      PetscFE
1324        PetscScalar f0[Nq*dim];
1325        PetscScalar f1[Nq*dim*dim];
1326        PetscScalar u[Nc];
1327        PetscScalar gradU[Nc*dim];
1328        PetscReal   x[dim];
1329        PetscScalar realSpaceDer[dim];
1330 
1331 Purpose: Compute element vector for N_cb batches of elements
1332 
1333 Input:
1334   Sizes:
1335      N_cb: Number of serial cell batches
1336 
1337   Geometry:
1338      PetscReal v0s[Ne*dim]
1339      PetscReal jacobians[Ne*dim*dim]        possibly *Nq
1340      PetscReal jacobianInverses[Ne*dim*dim] possibly *Nq
1341      PetscReal jacobianDeterminants[Ne]     possibly *Nq
1342   FEM:
1343      static PetscReal   quadPoints[Nq*dim]
1344      static PetscReal   quadWeights[Nq]
1345      static PetscReal   basis[Nq*Nb*Nc]
1346      static PetscReal   basisDer[Nq*Nb*Nc*dim]
1347      PetscScalar coefficients[Ne*Nb*Nc]
1348      PetscScalar elemVec[Ne*Nb*Nc]
1349 
1350 ex62.c:
1351   PetscErrorCode PetscFEIntegrateResidualBatch(PetscInt Ne, PetscInt numFields, PetscInt field, PetscQuadrature quad[], const PetscScalar coefficients[],
1352                                                const PetscReal v0s[], const PetscReal jacobians[], const PetscReal jacobianInverses[], const PetscReal jacobianDeterminants[],
1353                                                void (*f0_func)(const PetscScalar u[], const PetscScalar gradU[], const PetscReal x[], PetscScalar f0[]),
1354                                                void (*f1_func)(const PetscScalar u[], const PetscScalar gradU[], const PetscReal x[], PetscScalar f1[]), PetscScalar elemVec[])
1355 
1356 ex52.c:
1357   PetscErrorCode IntegrateLaplacianBatchCPU(PetscInt Ne, PetscInt Nb, const PetscScalar coefficients[], const PetscReal jacobianInverses[], const PetscReal jacobianDeterminants[], PetscInt Nq, const PetscReal quadPoints[], const PetscReal quadWeights[], const PetscReal basisTabulation[], const PetscReal basisDerTabulation[], PetscScalar elemVec[], AppCtx *user)
1358   PetscErrorCode IntegrateElasticityBatchCPU(PetscInt Ne, PetscInt Nb, PetscInt Ncomp, const PetscScalar coefficients[], const PetscReal jacobianInverses[], const PetscReal jacobianDeterminants[], PetscInt Nq, const PetscReal quadPoints[], const PetscReal quadWeights[], const PetscReal basisTabulation[], const PetscReal basisDerTabulation[], PetscScalar elemVec[], AppCtx *user)
1359 
1360 ex52_integrateElement.cu
1361 __global__ void integrateElementQuadrature(int N_cb, realType *coefficients, realType *jacobianInverses, realType *jacobianDeterminants, realType *elemVec)
1362 
1363 PETSC_EXTERN PetscErrorCode IntegrateElementBatchGPU(PetscInt spatial_dim, PetscInt Ne, PetscInt Ncb, PetscInt Nbc, PetscInt Nbl, const PetscScalar coefficients[],
1364                                                      const PetscReal jacobianInverses[], const PetscReal jacobianDeterminants[], PetscScalar elemVec[],
1365                                                      PetscLogEvent event, PetscInt debug, PetscInt pde_op)
1366 
1367 ex52_integrateElementOpenCL.c:
1368 PETSC_EXTERN PetscErrorCode IntegrateElementBatchGPU(PetscInt spatial_dim, PetscInt Ne, PetscInt Ncb, PetscInt Nbc, PetscInt N_bl, const PetscScalar coefficients[],
1369                                                      const PetscReal jacobianInverses[], const PetscReal jacobianDeterminants[], PetscScalar elemVec[],
1370                                                      PetscLogEvent event, PetscInt debug, PetscInt pde_op)
1371 
1372 __kernel void integrateElementQuadrature(int N_cb, __global float *coefficients, __global float *jacobianInverses, __global float *jacobianDeterminants, __global float *elemVec)
1373 */
1374 
1375 /*@
1376   PetscFEIntegrate - Produce the integral for the given field for a chunk of elements by quadrature integration
1377 
1378   Not Collective
1379 
1380   Input Parameters:
1381 + prob            - The `PetscDS` specifying the discretizations and continuum functions
1382 . field           - The field being integrated
1383 . Ne              - The number of elements in the chunk
1384 . cgeom           - The cell geometry for each cell in the chunk
1385 . coefficients    - The array of FEM basis coefficients for the elements
1386 . probAux         - The `PetscDS` specifying the auxiliary discretizations
1387 - coefficientsAux - The array of FEM auxiliary basis coefficients for the elements
1388 
1389   Output Parameter:
1390 . integral - the integral for this field
1391 
1392   Level: intermediate
1393 
1394   Developer Notes:
1395   The function name begins with `PetscFE` and yet the first argument is `PetscDS` and it has no `PetscFE` arguments.
1396 
1397 .seealso: `PetscFE`, `PetscDS`, `PetscFEIntegrateResidual()`, `PetscFEIntegrateBd()`
1398 @*/
1399 PetscErrorCode PetscFEIntegrate(PetscDS prob, PetscInt field, PetscInt Ne, PetscFEGeom *cgeom, const PetscScalar coefficients[], PetscDS probAux, const PetscScalar coefficientsAux[], PetscScalar integral[])
1400 {
1401   PetscFE fe;
1402 
1403   PetscFunctionBegin;
1404   PetscValidHeaderSpecific(prob, PETSCDS_CLASSID, 1);
1405   PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fe));
1406   if (fe->ops->integrate) PetscCall((*fe->ops->integrate)(prob, field, Ne, cgeom, coefficients, probAux, coefficientsAux, integral));
1407   PetscFunctionReturn(PETSC_SUCCESS);
1408 }
1409 
1410 /*@C
1411   PetscFEIntegrateBd - Produce the integral for the given field for a chunk of elements by quadrature integration
1412 
1413   Not Collective
1414 
1415   Input Parameters:
1416 + prob            - The `PetscDS` specifying the discretizations and continuum functions
1417 . field           - The field being integrated
1418 . obj_func        - The function to be integrated
1419 . Ne              - The number of elements in the chunk
1420 . geom            - The face geometry for each face in the chunk
1421 . coefficients    - The array of FEM basis coefficients for the elements
1422 . probAux         - The `PetscDS` specifying the auxiliary discretizations
1423 - coefficientsAux - The array of FEM auxiliary basis coefficients for the elements
1424 
1425   Output Parameter:
1426 . integral - the integral for this field
1427 
1428   Level: intermediate
1429 
1430   Developer Notes:
1431   The function name begins with `PetscFE` and yet the first argument is `PetscDS` and it has no `PetscFE` arguments.
1432 
1433 .seealso: `PetscFE`, `PetscDS`, `PetscFEIntegrateResidual()`, `PetscFEIntegrate()`
1434 @*/
1435 PetscErrorCode PetscFEIntegrateBd(PetscDS prob, PetscInt field, void (*obj_func)(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], PetscReal, const PetscReal[], const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]), PetscInt Ne, PetscFEGeom *geom, const PetscScalar coefficients[], PetscDS probAux, const PetscScalar coefficientsAux[], PetscScalar integral[])
1436 {
1437   PetscFE fe;
1438 
1439   PetscFunctionBegin;
1440   PetscValidHeaderSpecific(prob, PETSCDS_CLASSID, 1);
1441   PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fe));
1442   if (fe->ops->integratebd) PetscCall((*fe->ops->integratebd)(prob, field, obj_func, Ne, geom, coefficients, probAux, coefficientsAux, integral));
1443   PetscFunctionReturn(PETSC_SUCCESS);
1444 }
1445 
1446 /*@
1447   PetscFEIntegrateResidual - Produce the element residual vector for a chunk of elements by quadrature integration
1448 
1449   Not Collective
1450 
1451   Input Parameters:
1452 + ds              - The `PetscDS` specifying the discretizations and continuum functions
1453 . key             - The (label+value, field) being integrated
1454 . Ne              - The number of elements in the chunk
1455 . cgeom           - The cell geometry for each cell in the chunk
1456 . coefficients    - The array of FEM basis coefficients for the elements
1457 . coefficients_t  - The array of FEM basis time derivative coefficients for the elements
1458 . probAux         - The `PetscDS` specifying the auxiliary discretizations
1459 . coefficientsAux - The array of FEM auxiliary basis coefficients for the elements
1460 - t               - The time
1461 
1462   Output Parameter:
1463 . elemVec - the element residual vectors from each element
1464 
1465   Level: intermediate
1466 
1467   Note:
1468 .vb
1469   Loop over batch of elements (e):
1470     Loop over quadrature points (q):
1471       Make u_q and gradU_q (loops over fields,Nb,Ncomp) and x_q
1472       Call f_0 and f_1
1473     Loop over element vector entries (f,fc --> i):
1474       elemVec[i] += \psi^{fc}_f(q) f0_{fc}(u, \nabla u) + \nabla\psi^{fc}_f(q) \cdot f1_{fc,df}(u, \nabla u)
1475 .ve
1476 
1477 .seealso: `PetscFEIntegrateBdResidual()`
1478 @*/
1479 PetscErrorCode PetscFEIntegrateResidual(PetscDS ds, PetscFormKey key, PetscInt Ne, PetscFEGeom *cgeom, const PetscScalar coefficients[], const PetscScalar coefficients_t[], PetscDS probAux, const PetscScalar coefficientsAux[], PetscReal t, PetscScalar elemVec[])
1480 {
1481   PetscFE fe;
1482 
1483   PetscFunctionBeginHot;
1484   PetscValidHeaderSpecific(ds, PETSCDS_CLASSID, 1);
1485   PetscCall(PetscDSGetDiscretization(ds, key.field, (PetscObject *)&fe));
1486   if (fe->ops->integrateresidual) PetscCall((*fe->ops->integrateresidual)(ds, key, Ne, cgeom, coefficients, coefficients_t, probAux, coefficientsAux, t, elemVec));
1487   PetscFunctionReturn(PETSC_SUCCESS);
1488 }
1489 
1490 /*@
1491   PetscFEIntegrateBdResidual - Produce the element residual vector for a chunk of elements by quadrature integration over a boundary
1492 
1493   Not Collective
1494 
1495   Input Parameters:
1496 + ds              - The `PetscDS` specifying the discretizations and continuum functions
1497 . wf              - The PetscWeakForm object holding the pointwise functions
1498 . key             - The (label+value, field) being integrated
1499 . Ne              - The number of elements in the chunk
1500 . fgeom           - The face geometry for each cell in the chunk
1501 . coefficients    - The array of FEM basis coefficients for the elements
1502 . coefficients_t  - The array of FEM basis time derivative coefficients for the elements
1503 . probAux         - The `PetscDS` specifying the auxiliary discretizations
1504 . coefficientsAux - The array of FEM auxiliary basis coefficients for the elements
1505 - t               - The time
1506 
1507   Output Parameter:
1508 . elemVec - the element residual vectors from each element
1509 
1510   Level: intermediate
1511 
1512 .seealso: `PetscFEIntegrateResidual()`
1513 @*/
1514 PetscErrorCode PetscFEIntegrateBdResidual(PetscDS ds, PetscWeakForm wf, PetscFormKey key, PetscInt Ne, PetscFEGeom *fgeom, const PetscScalar coefficients[], const PetscScalar coefficients_t[], PetscDS probAux, const PetscScalar coefficientsAux[], PetscReal t, PetscScalar elemVec[])
1515 {
1516   PetscFE fe;
1517 
1518   PetscFunctionBegin;
1519   PetscValidHeaderSpecific(ds, PETSCDS_CLASSID, 1);
1520   PetscCall(PetscDSGetDiscretization(ds, key.field, (PetscObject *)&fe));
1521   if (fe->ops->integratebdresidual) PetscCall((*fe->ops->integratebdresidual)(ds, wf, key, Ne, fgeom, coefficients, coefficients_t, probAux, coefficientsAux, t, elemVec));
1522   PetscFunctionReturn(PETSC_SUCCESS);
1523 }
1524 
1525 /*@
1526   PetscFEIntegrateHybridResidual - Produce the element residual vector for a chunk of hybrid element faces by quadrature integration
1527 
1528   Not Collective
1529 
1530   Input Parameters:
1531 + ds              - The `PetscDS` specifying the discretizations and continuum functions
1532 . dsIn            - The `PetscDS` specifying the discretizations and continuum functions for input
1533 . key             - The (label+value, field) being integrated
1534 . s               - The side of the cell being integrated, 0 for negative and 1 for positive
1535 . Ne              - The number of elements in the chunk
1536 . fgeom           - The face geometry for each cell in the chunk
1537 . cgeom           - The cell geometry for each neighbor cell in the chunk
1538 . coefficients    - The array of FEM basis coefficients for the elements
1539 . coefficients_t  - The array of FEM basis time derivative coefficients for the elements
1540 . probAux         - The `PetscDS` specifying the auxiliary discretizations
1541 . coefficientsAux - The array of FEM auxiliary basis coefficients for the elements
1542 - t               - The time
1543 
1544   Output Parameter:
1545 . elemVec - the element residual vectors from each element
1546 
1547   Level: developer
1548 
1549 .seealso: `PetscFEIntegrateResidual()`
1550 @*/
1551 PetscErrorCode PetscFEIntegrateHybridResidual(PetscDS ds, PetscDS dsIn, PetscFormKey key, PetscInt s, PetscInt Ne, PetscFEGeom *fgeom, PetscFEGeom *cgeom, const PetscScalar coefficients[], const PetscScalar coefficients_t[], PetscDS probAux, const PetscScalar coefficientsAux[], PetscReal t, PetscScalar elemVec[])
1552 {
1553   PetscFE fe;
1554 
1555   PetscFunctionBegin;
1556   PetscValidHeaderSpecific(ds, PETSCDS_CLASSID, 1);
1557   PetscValidHeaderSpecific(dsIn, PETSCDS_CLASSID, 2);
1558   PetscCall(PetscDSGetDiscretization(ds, key.field, (PetscObject *)&fe));
1559   if (fe->ops->integratehybridresidual) PetscCall((*fe->ops->integratehybridresidual)(ds, dsIn, key, s, Ne, fgeom, cgeom, coefficients, coefficients_t, probAux, coefficientsAux, t, elemVec));
1560   PetscFunctionReturn(PETSC_SUCCESS);
1561 }
1562 
1563 /*@
1564   PetscFEIntegrateJacobian - Produce the element Jacobian for a chunk of elements by quadrature integration
1565 
1566   Not Collective
1567 
1568   Input Parameters:
1569 + ds              - The `PetscDS` specifying the discretizations and continuum functions
1570 . jtype           - The type of matrix pointwise functions that should be used
1571 . key             - The (label+value, fieldI*Nf + fieldJ) being integrated
1572 . Ne              - The number of elements in the chunk
1573 . cgeom           - The cell geometry for each cell in the chunk
1574 . coefficients    - The array of FEM basis coefficients for the elements for the Jacobian evaluation point
1575 . coefficients_t  - The array of FEM basis time derivative coefficients for the elements
1576 . probAux         - The `PetscDS` specifying the auxiliary discretizations
1577 . coefficientsAux - The array of FEM auxiliary basis coefficients for the elements
1578 . t               - The time
1579 - u_tshift        - A multiplier for the dF/du_t term (as opposed to the dF/du term)
1580 
1581   Output Parameter:
1582 . elemMat - the element matrices for the Jacobian from each element
1583 
1584   Level: intermediate
1585 
1586   Note:
1587 .vb
1588   Loop over batch of elements (e):
1589     Loop over element matrix entries (f,fc,g,gc --> i,j):
1590       Loop over quadrature points (q):
1591         Make u_q and gradU_q (loops over fields,Nb,Ncomp)
1592           elemMat[i,j] += \psi^{fc}_f(q) g0_{fc,gc}(u, \nabla u) \phi^{gc}_g(q)
1593                        + \psi^{fc}_f(q) \cdot g1_{fc,gc,dg}(u, \nabla u) \nabla\phi^{gc}_g(q)
1594                        + \nabla\psi^{fc}_f(q) \cdot g2_{fc,gc,df}(u, \nabla u) \phi^{gc}_g(q)
1595                        + \nabla\psi^{fc}_f(q) \cdot g3_{fc,gc,df,dg}(u, \nabla u) \nabla\phi^{gc}_g(q)
1596 .ve
1597 
1598 .seealso: `PetscFEIntegrateResidual()`
1599 @*/
1600 PetscErrorCode PetscFEIntegrateJacobian(PetscDS ds, PetscFEJacobianType jtype, PetscFormKey key, PetscInt Ne, PetscFEGeom *cgeom, const PetscScalar coefficients[], const PetscScalar coefficients_t[], PetscDS probAux, const PetscScalar coefficientsAux[], PetscReal t, PetscReal u_tshift, PetscScalar elemMat[])
1601 {
1602   PetscFE  fe;
1603   PetscInt Nf;
1604 
1605   PetscFunctionBegin;
1606   PetscValidHeaderSpecific(ds, PETSCDS_CLASSID, 1);
1607   PetscCall(PetscDSGetNumFields(ds, &Nf));
1608   PetscCall(PetscDSGetDiscretization(ds, key.field / Nf, (PetscObject *)&fe));
1609   if (fe->ops->integratejacobian) PetscCall((*fe->ops->integratejacobian)(ds, jtype, key, Ne, cgeom, coefficients, coefficients_t, probAux, coefficientsAux, t, u_tshift, elemMat));
1610   PetscFunctionReturn(PETSC_SUCCESS);
1611 }
1612 
1613 /*@
1614   PetscFEIntegrateBdJacobian - Produce the boundary element Jacobian for a chunk of elements by quadrature integration
1615 
1616   Not Collective
1617 
1618   Input Parameters:
1619 + ds              - The `PetscDS` specifying the discretizations and continuum functions
1620 . wf              - The PetscWeakForm holding the pointwise functions
1621 . jtype           - The type of matrix pointwise functions that should be used
1622 . key             - The (label+value, fieldI*Nf + fieldJ) being integrated
1623 . Ne              - The number of elements in the chunk
1624 . fgeom           - The face geometry for each cell in the chunk
1625 . coefficients    - The array of FEM basis coefficients for the elements for the Jacobian evaluation point
1626 . coefficients_t  - The array of FEM basis time derivative coefficients for the elements
1627 . probAux         - The `PetscDS` specifying the auxiliary discretizations
1628 . coefficientsAux - The array of FEM auxiliary basis coefficients for the elements
1629 . t               - The time
1630 - u_tshift        - A multiplier for the dF/du_t term (as opposed to the dF/du term)
1631 
1632   Output Parameter:
1633 . elemMat - the element matrices for the Jacobian from each element
1634 
1635   Level: intermediate
1636 
1637   Note:
1638 .vb
1639   Loop over batch of elements (e):
1640     Loop over element matrix entries (f,fc,g,gc --> i,j):
1641       Loop over quadrature points (q):
1642         Make u_q and gradU_q (loops over fields,Nb,Ncomp)
1643           elemMat[i,j] += \psi^{fc}_f(q) g0_{fc,gc}(u, \nabla u) \phi^{gc}_g(q)
1644                        + \psi^{fc}_f(q) \cdot g1_{fc,gc,dg}(u, \nabla u) \nabla\phi^{gc}_g(q)
1645                        + \nabla\psi^{fc}_f(q) \cdot g2_{fc,gc,df}(u, \nabla u) \phi^{gc}_g(q)
1646                        + \nabla\psi^{fc}_f(q) \cdot g3_{fc,gc,df,dg}(u, \nabla u) \nabla\phi^{gc}_g(q)
1647 .ve
1648 
1649 .seealso: `PetscFEIntegrateJacobian()`, `PetscFEIntegrateResidual()`
1650 @*/
1651 PetscErrorCode PetscFEIntegrateBdJacobian(PetscDS ds, PetscWeakForm wf, PetscFEJacobianType jtype, PetscFormKey key, PetscInt Ne, PetscFEGeom *fgeom, const PetscScalar coefficients[], const PetscScalar coefficients_t[], PetscDS probAux, const PetscScalar coefficientsAux[], PetscReal t, PetscReal u_tshift, PetscScalar elemMat[])
1652 {
1653   PetscFE  fe;
1654   PetscInt Nf;
1655 
1656   PetscFunctionBegin;
1657   PetscValidHeaderSpecific(ds, PETSCDS_CLASSID, 1);
1658   PetscCall(PetscDSGetNumFields(ds, &Nf));
1659   PetscCall(PetscDSGetDiscretization(ds, key.field / Nf, (PetscObject *)&fe));
1660   if (fe->ops->integratebdjacobian) PetscCall((*fe->ops->integratebdjacobian)(ds, wf, jtype, key, Ne, fgeom, coefficients, coefficients_t, probAux, coefficientsAux, t, u_tshift, elemMat));
1661   PetscFunctionReturn(PETSC_SUCCESS);
1662 }
1663 
1664 /*@
1665   PetscFEIntegrateHybridJacobian - Produce the boundary element Jacobian for a chunk of hybrid elements by quadrature integration
1666 
1667   Not Collective
1668 
1669   Input Parameters:
1670 + ds              - The `PetscDS` specifying the discretizations and continuum functions for the output
1671 . dsIn            - The `PetscDS` specifying the discretizations and continuum functions for the input
1672 . jtype           - The type of matrix pointwise functions that should be used
1673 . key             - The (label+value, fieldI*Nf + fieldJ) being integrated
1674 . s               - The side of the cell being integrated, 0 for negative and 1 for positive
1675 . Ne              - The number of elements in the chunk
1676 . fgeom           - The face geometry for each cell in the chunk
1677 . cgeom           - The cell geometry for each neighbor cell in the chunk
1678 . coefficients    - The array of FEM basis coefficients for the elements for the Jacobian evaluation point
1679 . coefficients_t  - The array of FEM basis time derivative coefficients for the elements
1680 . probAux         - The `PetscDS` specifying the auxiliary discretizations
1681 . coefficientsAux - The array of FEM auxiliary basis coefficients for the elements
1682 . t               - The time
1683 - u_tshift        - A multiplier for the dF/du_t term (as opposed to the dF/du term)
1684 
1685   Output Parameter:
1686 . elemMat - the element matrices for the Jacobian from each element
1687 
1688   Level: developer
1689 
1690   Note:
1691 .vb
1692   Loop over batch of elements (e):
1693     Loop over element matrix entries (f,fc,g,gc --> i,j):
1694       Loop over quadrature points (q):
1695         Make u_q and gradU_q (loops over fields,Nb,Ncomp)
1696           elemMat[i,j] += \psi^{fc}_f(q) g0_{fc,gc}(u, \nabla u) \phi^{gc}_g(q)
1697                        + \psi^{fc}_f(q) \cdot g1_{fc,gc,dg}(u, \nabla u) \nabla\phi^{gc}_g(q)
1698                        + \nabla\psi^{fc}_f(q) \cdot g2_{fc,gc,df}(u, \nabla u) \phi^{gc}_g(q)
1699                        + \nabla\psi^{fc}_f(q) \cdot g3_{fc,gc,df,dg}(u, \nabla u) \nabla\phi^{gc}_g(q)
1700 .ve
1701 
1702 .seealso: `PetscFEIntegrateJacobian()`, `PetscFEIntegrateResidual()`
1703 @*/
1704 PetscErrorCode PetscFEIntegrateHybridJacobian(PetscDS ds, PetscDS dsIn, PetscFEJacobianType jtype, PetscFormKey key, PetscInt s, PetscInt Ne, PetscFEGeom *fgeom, PetscFEGeom *cgeom, const PetscScalar coefficients[], const PetscScalar coefficients_t[], PetscDS probAux, const PetscScalar coefficientsAux[], PetscReal t, PetscReal u_tshift, PetscScalar elemMat[])
1705 {
1706   PetscFE  fe;
1707   PetscInt Nf;
1708 
1709   PetscFunctionBegin;
1710   PetscValidHeaderSpecific(ds, PETSCDS_CLASSID, 1);
1711   PetscCall(PetscDSGetNumFields(ds, &Nf));
1712   PetscCall(PetscDSGetDiscretization(ds, key.field / Nf, (PetscObject *)&fe));
1713   if (fe->ops->integratehybridjacobian) PetscCall((*fe->ops->integratehybridjacobian)(ds, dsIn, jtype, key, s, Ne, fgeom, cgeom, coefficients, coefficients_t, probAux, coefficientsAux, t, u_tshift, elemMat));
1714   PetscFunctionReturn(PETSC_SUCCESS);
1715 }
1716 
1717 /*@
1718   PetscFEGetHeightSubspace - Get the subspace of this space for a mesh point of a given height
1719 
1720   Input Parameters:
1721 + fe     - The finite element space
1722 - height - The height of the `DMPLEX` point
1723 
1724   Output Parameter:
1725 . subfe - The subspace of this `PetscFE` space
1726 
1727   Level: advanced
1728 
1729   Note:
1730   For example, if we want the subspace of this space for a face, we would choose height = 1.
1731 
1732 .seealso: `PetscFECreateDefault()`
1733 @*/
1734 PetscErrorCode PetscFEGetHeightSubspace(PetscFE fe, PetscInt height, PetscFE *subfe)
1735 {
1736   PetscSpace      P, subP;
1737   PetscDualSpace  Q, subQ;
1738   PetscQuadrature subq;
1739   PetscInt        dim, Nc;
1740 
1741   PetscFunctionBegin;
1742   PetscValidHeaderSpecific(fe, PETSCFE_CLASSID, 1);
1743   PetscAssertPointer(subfe, 3);
1744   if (height == 0) {
1745     *subfe = fe;
1746     PetscFunctionReturn(PETSC_SUCCESS);
1747   }
1748   PetscCall(PetscFEGetBasisSpace(fe, &P));
1749   PetscCall(PetscFEGetDualSpace(fe, &Q));
1750   PetscCall(PetscFEGetNumComponents(fe, &Nc));
1751   PetscCall(PetscFEGetFaceQuadrature(fe, &subq));
1752   PetscCall(PetscDualSpaceGetDimension(Q, &dim));
1753   PetscCheck(height <= dim && height >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Asked for space at height %" PetscInt_FMT " for dimension %" PetscInt_FMT " space", height, dim);
1754   if (!fe->subspaces) PetscCall(PetscCalloc1(dim, &fe->subspaces));
1755   if (height <= dim) {
1756     if (!fe->subspaces[height - 1]) {
1757       PetscFE     sub = NULL;
1758       const char *name;
1759 
1760       PetscCall(PetscSpaceGetHeightSubspace(P, height, &subP));
1761       PetscCall(PetscDualSpaceGetHeightSubspace(Q, height, &subQ));
1762       if (subQ) {
1763         PetscCall(PetscObjectReference((PetscObject)subP));
1764         PetscCall(PetscObjectReference((PetscObject)subQ));
1765         PetscCall(PetscObjectReference((PetscObject)subq));
1766         PetscCall(PetscFECreateFromSpaces(subP, subQ, subq, NULL, &sub));
1767       }
1768       if (sub) {
1769         PetscCall(PetscObjectGetName((PetscObject)fe, &name));
1770         if (name) PetscCall(PetscFESetName(sub, name));
1771       }
1772       fe->subspaces[height - 1] = sub;
1773     }
1774     *subfe = fe->subspaces[height - 1];
1775   } else {
1776     *subfe = NULL;
1777   }
1778   PetscFunctionReturn(PETSC_SUCCESS);
1779 }
1780 
1781 /*@
1782   PetscFERefine - Create a "refined" `PetscFE` object that refines the reference cell into
1783   smaller copies.
1784 
1785   Collective
1786 
1787   Input Parameter:
1788 . fe - The initial `PetscFE`
1789 
1790   Output Parameter:
1791 . feRef - The refined `PetscFE`
1792 
1793   Level: advanced
1794 
1795   Notes:
1796   This is typically used to generate a preconditioner for a higher order method from a lower order method on a
1797   refined mesh having the same number of dofs (but more sparsity). It is also used to create an
1798   interpolation between regularly refined meshes.
1799 
1800 .seealso: `PetscFEType`, `PetscFECreate()`, `PetscFESetType()`
1801 @*/
1802 PetscErrorCode PetscFERefine(PetscFE fe, PetscFE *feRef)
1803 {
1804   PetscSpace       P, Pref;
1805   PetscDualSpace   Q, Qref;
1806   DM               K, Kref;
1807   PetscQuadrature  q, qref;
1808   const PetscReal *v0, *jac;
1809   PetscInt         numComp, numSubelements;
1810   PetscInt         cStart, cEnd, c;
1811   PetscDualSpace  *cellSpaces;
1812 
1813   PetscFunctionBegin;
1814   PetscCall(PetscFEGetBasisSpace(fe, &P));
1815   PetscCall(PetscFEGetDualSpace(fe, &Q));
1816   PetscCall(PetscFEGetQuadrature(fe, &q));
1817   PetscCall(PetscDualSpaceGetDM(Q, &K));
1818   /* Create space */
1819   PetscCall(PetscObjectReference((PetscObject)P));
1820   Pref = P;
1821   /* Create dual space */
1822   PetscCall(PetscDualSpaceDuplicate(Q, &Qref));
1823   PetscCall(PetscDualSpaceSetType(Qref, PETSCDUALSPACEREFINED));
1824   PetscCall(DMRefine(K, PetscObjectComm((PetscObject)fe), &Kref));
1825   PetscCall(DMGetCoordinatesLocalSetUp(Kref));
1826   PetscCall(PetscDualSpaceSetDM(Qref, Kref));
1827   PetscCall(DMPlexGetHeightStratum(Kref, 0, &cStart, &cEnd));
1828   PetscCall(PetscMalloc1(cEnd - cStart, &cellSpaces));
1829   /* TODO: fix for non-uniform refinement */
1830   for (c = 0; c < cEnd - cStart; c++) cellSpaces[c] = Q;
1831   PetscCall(PetscDualSpaceRefinedSetCellSpaces(Qref, cellSpaces));
1832   PetscCall(PetscFree(cellSpaces));
1833   PetscCall(DMDestroy(&Kref));
1834   PetscCall(PetscDualSpaceSetUp(Qref));
1835   /* Create element */
1836   PetscCall(PetscFECreate(PetscObjectComm((PetscObject)fe), feRef));
1837   PetscCall(PetscFESetType(*feRef, PETSCFECOMPOSITE));
1838   PetscCall(PetscFESetBasisSpace(*feRef, Pref));
1839   PetscCall(PetscFESetDualSpace(*feRef, Qref));
1840   PetscCall(PetscFEGetNumComponents(fe, &numComp));
1841   PetscCall(PetscFESetNumComponents(*feRef, numComp));
1842   PetscCall(PetscFESetUp(*feRef));
1843   PetscCall(PetscSpaceDestroy(&Pref));
1844   PetscCall(PetscDualSpaceDestroy(&Qref));
1845   /* Create quadrature */
1846   PetscCall(PetscFECompositeGetMapping(*feRef, &numSubelements, &v0, &jac, NULL));
1847   PetscCall(PetscQuadratureExpandComposite(q, numSubelements, v0, jac, &qref));
1848   PetscCall(PetscFESetQuadrature(*feRef, qref));
1849   PetscCall(PetscQuadratureDestroy(&qref));
1850   PetscFunctionReturn(PETSC_SUCCESS);
1851 }
1852 
1853 static PetscErrorCode PetscFESetDefaultName_Private(PetscFE fe)
1854 {
1855   PetscSpace     P;
1856   PetscDualSpace Q;
1857   DM             K;
1858   DMPolytopeType ct;
1859   PetscInt       degree;
1860   char           name[64];
1861 
1862   PetscFunctionBegin;
1863   PetscCall(PetscFEGetBasisSpace(fe, &P));
1864   PetscCall(PetscSpaceGetDegree(P, &degree, NULL));
1865   PetscCall(PetscFEGetDualSpace(fe, &Q));
1866   PetscCall(PetscDualSpaceGetDM(Q, &K));
1867   PetscCall(DMPlexGetCellType(K, 0, &ct));
1868   switch (ct) {
1869   case DM_POLYTOPE_SEGMENT:
1870   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1871   case DM_POLYTOPE_QUADRILATERAL:
1872   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1873   case DM_POLYTOPE_HEXAHEDRON:
1874   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
1875     PetscCall(PetscSNPrintf(name, sizeof(name), "Q%" PetscInt_FMT, degree));
1876     break;
1877   case DM_POLYTOPE_TRIANGLE:
1878   case DM_POLYTOPE_TETRAHEDRON:
1879     PetscCall(PetscSNPrintf(name, sizeof(name), "P%" PetscInt_FMT, degree));
1880     break;
1881   case DM_POLYTOPE_TRI_PRISM:
1882   case DM_POLYTOPE_TRI_PRISM_TENSOR:
1883     PetscCall(PetscSNPrintf(name, sizeof(name), "P%" PetscInt_FMT "xQ%" PetscInt_FMT, degree, degree));
1884     break;
1885   default:
1886     PetscCall(PetscSNPrintf(name, sizeof(name), "FE"));
1887   }
1888   PetscCall(PetscFESetName(fe, name));
1889   PetscFunctionReturn(PETSC_SUCCESS);
1890 }
1891 
1892 /*@
1893   PetscFECreateFromSpaces - Create a `PetscFE` from the basis and dual spaces
1894 
1895   Collective
1896 
1897   Input Parameters:
1898 + P  - The basis space
1899 . Q  - The dual space
1900 . q  - The cell quadrature
1901 - fq - The face quadrature
1902 
1903   Output Parameter:
1904 . fem - The `PetscFE` object
1905 
1906   Level: beginner
1907 
1908   Note:
1909   The `PetscFE` takes ownership of these spaces by calling destroy on each. They should not be used after this call, and for borrowed references from `PetscFEGetSpace()` and the like, the caller must use `PetscObjectReference` before this call.
1910 
1911 .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscQuadrature`,
1912           `PetscFECreateLagrangeByCell()`, `PetscFECreateDefault()`, `PetscFECreateByCell()`, `PetscFECreate()`, `PetscSpaceCreate()`, `PetscDualSpaceCreate()`
1913 @*/
1914 PetscErrorCode PetscFECreateFromSpaces(PetscSpace P, PetscDualSpace Q, PetscQuadrature q, PetscQuadrature fq, PetscFE *fem)
1915 {
1916   PetscInt    Nc;
1917   PetscInt    p_Ns = -1, p_Nc = -1, q_Ns = -1, q_Nc = -1;
1918   PetscBool   p_is_uniform_sum = PETSC_FALSE, p_interleave_basis = PETSC_FALSE, p_interleave_components = PETSC_FALSE;
1919   PetscBool   q_is_uniform_sum = PETSC_FALSE, q_interleave_basis = PETSC_FALSE, q_interleave_components = PETSC_FALSE;
1920   const char *prefix;
1921 
1922   PetscFunctionBegin;
1923   PetscCall(PetscObjectTypeCompare((PetscObject)P, PETSCSPACESUM, &p_is_uniform_sum));
1924   if (p_is_uniform_sum) {
1925     PetscSpace subsp_0 = NULL;
1926     PetscCall(PetscSpaceSumGetNumSubspaces(P, &p_Ns));
1927     PetscCall(PetscSpaceGetNumComponents(P, &p_Nc));
1928     PetscCall(PetscSpaceSumGetConcatenate(P, &p_is_uniform_sum));
1929     PetscCall(PetscSpaceSumGetInterleave(P, &p_interleave_basis, &p_interleave_components));
1930     for (PetscInt s = 0; s < p_Ns; s++) {
1931       PetscSpace subsp;
1932 
1933       PetscCall(PetscSpaceSumGetSubspace(P, s, &subsp));
1934       if (!s) {
1935         subsp_0 = subsp;
1936       } else if (subsp != subsp_0) {
1937         p_is_uniform_sum = PETSC_FALSE;
1938       }
1939     }
1940   }
1941   PetscCall(PetscObjectTypeCompare((PetscObject)Q, PETSCDUALSPACESUM, &q_is_uniform_sum));
1942   if (q_is_uniform_sum) {
1943     PetscDualSpace subsp_0 = NULL;
1944     PetscCall(PetscDualSpaceSumGetNumSubspaces(Q, &q_Ns));
1945     PetscCall(PetscDualSpaceGetNumComponents(Q, &q_Nc));
1946     PetscCall(PetscDualSpaceSumGetConcatenate(Q, &q_is_uniform_sum));
1947     PetscCall(PetscDualSpaceSumGetInterleave(Q, &q_interleave_basis, &q_interleave_components));
1948     for (PetscInt s = 0; s < q_Ns; s++) {
1949       PetscDualSpace subsp;
1950 
1951       PetscCall(PetscDualSpaceSumGetSubspace(Q, s, &subsp));
1952       if (!s) {
1953         subsp_0 = subsp;
1954       } else if (subsp != subsp_0) {
1955         q_is_uniform_sum = PETSC_FALSE;
1956       }
1957     }
1958   }
1959   if (p_is_uniform_sum && q_is_uniform_sum && (p_interleave_basis == q_interleave_basis) && (p_interleave_components == q_interleave_components) && (p_Ns == q_Ns) && (p_Nc == q_Nc)) {
1960     PetscSpace     scalar_space;
1961     PetscDualSpace scalar_dspace;
1962     PetscFE        scalar_fe;
1963 
1964     PetscCall(PetscSpaceSumGetSubspace(P, 0, &scalar_space));
1965     PetscCall(PetscDualSpaceSumGetSubspace(Q, 0, &scalar_dspace));
1966     PetscCall(PetscObjectReference((PetscObject)scalar_space));
1967     PetscCall(PetscObjectReference((PetscObject)scalar_dspace));
1968     PetscCall(PetscObjectReference((PetscObject)q));
1969     PetscCall(PetscObjectReference((PetscObject)fq));
1970     PetscCall(PetscFECreateFromSpaces(scalar_space, scalar_dspace, q, fq, &scalar_fe));
1971     PetscCall(PetscFECreateVector(scalar_fe, p_Ns, p_interleave_basis, p_interleave_components, fem));
1972     PetscCall(PetscFEDestroy(&scalar_fe));
1973   } else {
1974     PetscCall(PetscFECreate(PetscObjectComm((PetscObject)P), fem));
1975     PetscCall(PetscFESetType(*fem, PETSCFEBASIC));
1976   }
1977   PetscCall(PetscSpaceGetNumComponents(P, &Nc));
1978   PetscCall(PetscFESetNumComponents(*fem, Nc));
1979   PetscCall(PetscFESetBasisSpace(*fem, P));
1980   PetscCall(PetscFESetDualSpace(*fem, Q));
1981   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)P, &prefix));
1982   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*fem, prefix));
1983   PetscCall(PetscFESetUp(*fem));
1984   PetscCall(PetscSpaceDestroy(&P));
1985   PetscCall(PetscDualSpaceDestroy(&Q));
1986   PetscCall(PetscFESetQuadrature(*fem, q));
1987   PetscCall(PetscFESetFaceQuadrature(*fem, fq));
1988   PetscCall(PetscQuadratureDestroy(&q));
1989   PetscCall(PetscQuadratureDestroy(&fq));
1990   PetscCall(PetscFESetDefaultName_Private(*fem));
1991   PetscFunctionReturn(PETSC_SUCCESS);
1992 }
1993 
1994 static PetscErrorCode PetscFECreate_Internal(MPI_Comm comm, PetscInt dim, PetscInt Nc, DMPolytopeType ct, const char prefix[], PetscInt degree, PetscInt qorder, PetscBool setFromOptions, PetscFE *fem)
1995 {
1996   DM              K;
1997   PetscSpace      P;
1998   PetscDualSpace  Q;
1999   PetscQuadrature q, fq;
2000   PetscBool       tensor;
2001 
2002   PetscFunctionBegin;
2003   if (prefix) PetscAssertPointer(prefix, 5);
2004   PetscAssertPointer(fem, 9);
2005   switch (ct) {
2006   case DM_POLYTOPE_SEGMENT:
2007   case DM_POLYTOPE_POINT_PRISM_TENSOR:
2008   case DM_POLYTOPE_QUADRILATERAL:
2009   case DM_POLYTOPE_SEG_PRISM_TENSOR:
2010   case DM_POLYTOPE_HEXAHEDRON:
2011   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
2012     tensor = PETSC_TRUE;
2013     break;
2014   default:
2015     tensor = PETSC_FALSE;
2016   }
2017   /* Create space */
2018   PetscCall(PetscSpaceCreate(comm, &P));
2019   PetscCall(PetscSpaceSetType(P, PETSCSPACEPOLYNOMIAL));
2020   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)P, prefix));
2021   PetscCall(PetscSpacePolynomialSetTensor(P, tensor));
2022   PetscCall(PetscSpaceSetNumComponents(P, Nc));
2023   PetscCall(PetscSpaceSetNumVariables(P, dim));
2024   if (degree >= 0) {
2025     PetscCall(PetscSpaceSetDegree(P, degree, PETSC_DETERMINE));
2026     if (ct == DM_POLYTOPE_TRI_PRISM || ct == DM_POLYTOPE_TRI_PRISM_TENSOR) {
2027       PetscSpace Pend, Pside;
2028 
2029       PetscCall(PetscSpaceSetNumComponents(P, 1));
2030       PetscCall(PetscSpaceCreate(comm, &Pend));
2031       PetscCall(PetscSpaceSetType(Pend, PETSCSPACEPOLYNOMIAL));
2032       PetscCall(PetscSpacePolynomialSetTensor(Pend, PETSC_FALSE));
2033       PetscCall(PetscSpaceSetNumComponents(Pend, 1));
2034       PetscCall(PetscSpaceSetNumVariables(Pend, dim - 1));
2035       PetscCall(PetscSpaceSetDegree(Pend, degree, PETSC_DETERMINE));
2036       PetscCall(PetscSpaceCreate(comm, &Pside));
2037       PetscCall(PetscSpaceSetType(Pside, PETSCSPACEPOLYNOMIAL));
2038       PetscCall(PetscSpacePolynomialSetTensor(Pside, PETSC_FALSE));
2039       PetscCall(PetscSpaceSetNumComponents(Pside, 1));
2040       PetscCall(PetscSpaceSetNumVariables(Pside, 1));
2041       PetscCall(PetscSpaceSetDegree(Pside, degree, PETSC_DETERMINE));
2042       PetscCall(PetscSpaceSetType(P, PETSCSPACETENSOR));
2043       PetscCall(PetscSpaceTensorSetNumSubspaces(P, 2));
2044       PetscCall(PetscSpaceTensorSetSubspace(P, 0, Pend));
2045       PetscCall(PetscSpaceTensorSetSubspace(P, 1, Pside));
2046       PetscCall(PetscSpaceDestroy(&Pend));
2047       PetscCall(PetscSpaceDestroy(&Pside));
2048 
2049       if (Nc > 1) {
2050         PetscSpace scalar_P = P;
2051 
2052         PetscCall(PetscSpaceCreate(comm, &P));
2053         PetscCall(PetscSpaceSetNumVariables(P, dim));
2054         PetscCall(PetscSpaceSetNumComponents(P, Nc));
2055         PetscCall(PetscSpaceSetType(P, PETSCSPACESUM));
2056         PetscCall(PetscSpaceSumSetNumSubspaces(P, Nc));
2057         PetscCall(PetscSpaceSumSetConcatenate(P, PETSC_TRUE));
2058         PetscCall(PetscSpaceSumSetInterleave(P, PETSC_TRUE, PETSC_FALSE));
2059         for (PetscInt i = 0; i < Nc; i++) PetscCall(PetscSpaceSumSetSubspace(P, i, scalar_P));
2060         PetscCall(PetscSpaceDestroy(&scalar_P));
2061       }
2062     }
2063   }
2064   if (setFromOptions) PetscCall(PetscSpaceSetFromOptions(P));
2065   PetscCall(PetscSpaceSetUp(P));
2066   PetscCall(PetscSpaceGetDegree(P, &degree, NULL));
2067   PetscCall(PetscSpacePolynomialGetTensor(P, &tensor));
2068   PetscCall(PetscSpaceGetNumComponents(P, &Nc));
2069   /* Create dual space */
2070   PetscCall(PetscDualSpaceCreate(comm, &Q));
2071   PetscCall(PetscDualSpaceSetType(Q, PETSCDUALSPACELAGRANGE));
2072   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)Q, prefix));
2073   PetscCall(DMPlexCreateReferenceCell(PETSC_COMM_SELF, ct, &K));
2074   PetscCall(PetscDualSpaceSetDM(Q, K));
2075   PetscCall(DMDestroy(&K));
2076   PetscCall(PetscDualSpaceSetNumComponents(Q, Nc));
2077   PetscCall(PetscDualSpaceSetOrder(Q, degree));
2078   PetscCall(PetscDualSpaceLagrangeSetTensor(Q, (tensor || (ct == DM_POLYTOPE_TRI_PRISM)) ? PETSC_TRUE : PETSC_FALSE));
2079   if (setFromOptions) PetscCall(PetscDualSpaceSetFromOptions(Q));
2080   PetscCall(PetscDualSpaceSetUp(Q));
2081   /* Create quadrature */
2082   qorder = qorder >= 0 ? qorder : degree;
2083   if (setFromOptions) {
2084     PetscObjectOptionsBegin((PetscObject)P);
2085     PetscCall(PetscOptionsBoundedInt("-petscfe_default_quadrature_order", "Quadrature order is one less than quadrature points per edge", "PetscFECreateDefault", qorder, &qorder, NULL, 0));
2086     PetscOptionsEnd();
2087   }
2088   PetscCall(PetscDTCreateDefaultQuadrature(ct, qorder, &q, &fq));
2089   /* Create finite element */
2090   PetscCall(PetscFECreateFromSpaces(P, Q, q, fq, fem));
2091   if (setFromOptions) PetscCall(PetscFESetFromOptions(*fem));
2092   PetscFunctionReturn(PETSC_SUCCESS);
2093 }
2094 
2095 /*@
2096   PetscFECreateDefault - Create a `PetscFE` for basic FEM computation
2097 
2098   Collective
2099 
2100   Input Parameters:
2101 + comm      - The MPI comm
2102 . dim       - The spatial dimension
2103 . Nc        - The number of components
2104 . isSimplex - Flag for simplex reference cell, otherwise its a tensor product
2105 . prefix    - The options prefix, or `NULL`
2106 - qorder    - The quadrature order or `PETSC_DETERMINE` to use `PetscSpace` polynomial degree
2107 
2108   Output Parameter:
2109 . fem - The `PetscFE` object
2110 
2111   Level: beginner
2112 
2113   Note:
2114   Each subobject is SetFromOption() during creation, so that the object may be customized from the command line, using the prefix specified above. See the links below for the particular options available.
2115 
2116 .seealso: `PetscFECreateLagrange()`, `PetscFECreateByCell()`, `PetscSpaceSetFromOptions()`, `PetscDualSpaceSetFromOptions()`, `PetscFESetFromOptions()`, `PetscFECreate()`, `PetscSpaceCreate()`, `PetscDualSpaceCreate()`
2117 @*/
2118 PetscErrorCode PetscFECreateDefault(MPI_Comm comm, PetscInt dim, PetscInt Nc, PetscBool isSimplex, const char prefix[], PetscInt qorder, PetscFE *fem)
2119 {
2120   PetscFunctionBegin;
2121   PetscCall(PetscFECreate_Internal(comm, dim, Nc, DMPolytopeTypeSimpleShape(dim, isSimplex), prefix, PETSC_DECIDE, qorder, PETSC_TRUE, fem));
2122   PetscFunctionReturn(PETSC_SUCCESS);
2123 }
2124 
2125 /*@
2126   PetscFECreateByCell - Create a `PetscFE` for basic FEM computation
2127 
2128   Collective
2129 
2130   Input Parameters:
2131 + comm   - The MPI comm
2132 . dim    - The spatial dimension
2133 . Nc     - The number of components
2134 . ct     - The celltype of the reference cell
2135 . prefix - The options prefix, or `NULL`
2136 - qorder - The quadrature order or `PETSC_DETERMINE` to use `PetscSpace` polynomial degree
2137 
2138   Output Parameter:
2139 . fem - The `PetscFE` object
2140 
2141   Level: beginner
2142 
2143   Note:
2144   Each subobject is SetFromOption() during creation, so that the object may be customized from the command line, using the prefix specified above. See the links below for the particular options available.
2145 
2146 .seealso: `PetscFECreateDefault()`, `PetscFECreateLagrange()`, `PetscSpaceSetFromOptions()`, `PetscDualSpaceSetFromOptions()`, `PetscFESetFromOptions()`, `PetscFECreate()`, `PetscSpaceCreate()`, `PetscDualSpaceCreate()`
2147 @*/
2148 PetscErrorCode PetscFECreateByCell(MPI_Comm comm, PetscInt dim, PetscInt Nc, DMPolytopeType ct, const char prefix[], PetscInt qorder, PetscFE *fem)
2149 {
2150   PetscFunctionBegin;
2151   PetscCall(PetscFECreate_Internal(comm, dim, Nc, ct, prefix, PETSC_DECIDE, qorder, PETSC_TRUE, fem));
2152   PetscFunctionReturn(PETSC_SUCCESS);
2153 }
2154 
2155 /*@
2156   PetscFECreateLagrange - Create a `PetscFE` for the basic Lagrange space of degree k
2157 
2158   Collective
2159 
2160   Input Parameters:
2161 + comm      - The MPI comm
2162 . dim       - The spatial dimension
2163 . Nc        - The number of components
2164 . isSimplex - Flag for simplex reference cell, otherwise its a tensor product
2165 . k         - The degree k of the space
2166 - qorder    - The quadrature order or `PETSC_DETERMINE` to use `PetscSpace` polynomial degree
2167 
2168   Output Parameter:
2169 . fem - The `PetscFE` object
2170 
2171   Level: beginner
2172 
2173   Note:
2174   For simplices, this element is the space of maximum polynomial degree k, otherwise it is a tensor product of 1D polynomials, each with maximal degree k.
2175 
2176 .seealso: `PetscFECreateLagrangeByCell()`, `PetscFECreateDefault()`, `PetscFECreateByCell()`, `PetscFECreate()`, `PetscSpaceCreate()`, `PetscDualSpaceCreate()`
2177 @*/
2178 PetscErrorCode PetscFECreateLagrange(MPI_Comm comm, PetscInt dim, PetscInt Nc, PetscBool isSimplex, PetscInt k, PetscInt qorder, PetscFE *fem)
2179 {
2180   PetscFunctionBegin;
2181   PetscCall(PetscFECreate_Internal(comm, dim, Nc, DMPolytopeTypeSimpleShape(dim, isSimplex), NULL, k, qorder, PETSC_FALSE, fem));
2182   PetscFunctionReturn(PETSC_SUCCESS);
2183 }
2184 
2185 /*@
2186   PetscFECreateLagrangeByCell - Create a `PetscFE` for the basic Lagrange space of degree k
2187 
2188   Collective
2189 
2190   Input Parameters:
2191 + comm   - The MPI comm
2192 . dim    - The spatial dimension
2193 . Nc     - The number of components
2194 . ct     - The celltype of the reference cell
2195 . k      - The degree k of the space
2196 - qorder - The quadrature order or `PETSC_DETERMINE` to use `PetscSpace` polynomial degree
2197 
2198   Output Parameter:
2199 . fem - The `PetscFE` object
2200 
2201   Level: beginner
2202 
2203   Note:
2204   For simplices, this element is the space of maximum polynomial degree k, otherwise it is a tensor product of 1D polynomials, each with maximal degree k.
2205 
2206 .seealso: `PetscFECreateLagrange()`, `PetscFECreateDefault()`, `PetscFECreateByCell()`, `PetscFECreate()`, `PetscSpaceCreate()`, `PetscDualSpaceCreate()`
2207 @*/
2208 PetscErrorCode PetscFECreateLagrangeByCell(MPI_Comm comm, PetscInt dim, PetscInt Nc, DMPolytopeType ct, PetscInt k, PetscInt qorder, PetscFE *fem)
2209 {
2210   PetscFunctionBegin;
2211   PetscCall(PetscFECreate_Internal(comm, dim, Nc, ct, NULL, k, qorder, PETSC_FALSE, fem));
2212   PetscFunctionReturn(PETSC_SUCCESS);
2213 }
2214 
2215 /*@
2216   PetscFELimitDegree - Copy a `PetscFE` but limit the degree to be in the given range
2217 
2218   Collective
2219 
2220   Input Parameters:
2221 + fe        - The `PetscFE`
2222 . minDegree - The minimum degree, or `PETSC_DETERMINE` for no limit
2223 - maxDegree - The maximum degree, or `PETSC_DETERMINE` for no limit
2224 
2225   Output Parameter:
2226 . newfe - The `PetscFE` object
2227 
2228   Level: advanced
2229 
2230   Note:
2231   This currently only works for Lagrange elements.
2232 
2233 .seealso: `PetscFECreateLagrange()`, `PetscFECreateDefault()`, `PetscFECreateByCell()`, `PetscFECreate()`, `PetscSpaceCreate()`, `PetscDualSpaceCreate()`
2234 @*/
2235 PetscErrorCode PetscFELimitDegree(PetscFE fe, PetscInt minDegree, PetscInt maxDegree, PetscFE *newfe)
2236 {
2237   PetscDualSpace Q;
2238   PetscBool      islag, issum;
2239   PetscInt       oldk = 0, k;
2240 
2241   PetscFunctionBegin;
2242   PetscCall(PetscFEGetDualSpace(fe, &Q));
2243   PetscCall(PetscObjectTypeCompare((PetscObject)Q, PETSCDUALSPACELAGRANGE, &islag));
2244   PetscCall(PetscObjectTypeCompare((PetscObject)Q, PETSCDUALSPACESUM, &issum));
2245   if (islag) {
2246     PetscCall(PetscDualSpaceGetOrder(Q, &oldk));
2247   } else if (issum) {
2248     PetscDualSpace subQ;
2249 
2250     PetscCall(PetscDualSpaceSumGetSubspace(Q, 0, &subQ));
2251     PetscCall(PetscDualSpaceGetOrder(subQ, &oldk));
2252   } else {
2253     PetscCall(PetscObjectReference((PetscObject)fe));
2254     *newfe = fe;
2255     PetscFunctionReturn(PETSC_SUCCESS);
2256   }
2257   k = oldk;
2258   if (minDegree >= 0) k = PetscMax(k, minDegree);
2259   if (maxDegree >= 0) k = PetscMin(k, maxDegree);
2260   if (k != oldk) {
2261     DM              K;
2262     PetscSpace      P;
2263     PetscQuadrature q;
2264     DMPolytopeType  ct;
2265     PetscInt        dim, Nc;
2266 
2267     PetscCall(PetscFEGetBasisSpace(fe, &P));
2268     PetscCall(PetscSpaceGetNumVariables(P, &dim));
2269     PetscCall(PetscSpaceGetNumComponents(P, &Nc));
2270     PetscCall(PetscDualSpaceGetDM(Q, &K));
2271     PetscCall(DMPlexGetCellType(K, 0, &ct));
2272     PetscCall(PetscFECreateLagrangeByCell(PetscObjectComm((PetscObject)fe), dim, Nc, ct, k, PETSC_DETERMINE, newfe));
2273     PetscCall(PetscFEGetQuadrature(fe, &q));
2274     PetscCall(PetscFESetQuadrature(*newfe, q));
2275   } else {
2276     PetscCall(PetscObjectReference((PetscObject)fe));
2277     *newfe = fe;
2278   }
2279   PetscFunctionReturn(PETSC_SUCCESS);
2280 }
2281 
2282 /*@
2283   PetscFESetName - Names the `PetscFE` and its subobjects
2284 
2285   Not Collective
2286 
2287   Input Parameters:
2288 + fe   - The `PetscFE`
2289 - name - The name
2290 
2291   Level: intermediate
2292 
2293 .seealso: `PetscFECreate()`, `PetscSpaceCreate()`, `PetscDualSpaceCreate()`
2294 @*/
2295 PetscErrorCode PetscFESetName(PetscFE fe, const char name[])
2296 {
2297   PetscSpace     P;
2298   PetscDualSpace Q;
2299 
2300   PetscFunctionBegin;
2301   PetscCall(PetscFEGetBasisSpace(fe, &P));
2302   PetscCall(PetscFEGetDualSpace(fe, &Q));
2303   PetscCall(PetscObjectSetName((PetscObject)fe, name));
2304   PetscCall(PetscObjectSetName((PetscObject)P, name));
2305   PetscCall(PetscObjectSetName((PetscObject)Q, name));
2306   PetscFunctionReturn(PETSC_SUCCESS);
2307 }
2308 
2309 PetscErrorCode PetscFEEvaluateFieldJets_Internal(PetscDS ds, PetscInt Nf, PetscInt r, PetscInt q, PetscTabulation T[], PetscFEGeom *fegeom, const PetscScalar coefficients[], const PetscScalar coefficients_t[], PetscScalar u[], PetscScalar u_x[], PetscScalar u_t[])
2310 {
2311   PetscInt dOffset = 0, fOffset = 0, f, g;
2312 
2313   for (f = 0; f < Nf; ++f) {
2314     PetscCheck(r < T[f]->Nr, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Replica number %" PetscInt_FMT " should be in [0, %" PetscInt_FMT ")", r, T[f]->Nr);
2315     PetscCheck(q < T[f]->Np, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Point number %" PetscInt_FMT " should be in [0, %" PetscInt_FMT ")", q, T[f]->Np);
2316     PetscFE          fe;
2317     const PetscInt   k       = ds->jetDegree[f];
2318     const PetscInt   cdim    = T[f]->cdim;
2319     const PetscInt   dE      = fegeom->dimEmbed;
2320     const PetscInt   Nq      = T[f]->Np;
2321     const PetscInt   Nbf     = T[f]->Nb;
2322     const PetscInt   Ncf     = T[f]->Nc;
2323     const PetscReal *Bq      = &T[f]->T[0][(r * Nq + q) * Nbf * Ncf];
2324     const PetscReal *Dq      = &T[f]->T[1][(r * Nq + q) * Nbf * Ncf * cdim];
2325     const PetscReal *Hq      = k > 1 ? &T[f]->T[2][(r * Nq + q) * Nbf * Ncf * cdim * cdim] : NULL;
2326     PetscInt         hOffset = 0, b, c, d;
2327 
2328     PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
2329     for (c = 0; c < Ncf; ++c) u[fOffset + c] = 0.0;
2330     for (d = 0; d < dE * Ncf; ++d) u_x[fOffset * dE + d] = 0.0;
2331     for (b = 0; b < Nbf; ++b) {
2332       for (c = 0; c < Ncf; ++c) {
2333         const PetscInt cidx = b * Ncf + c;
2334 
2335         u[fOffset + c] += Bq[cidx] * coefficients[dOffset + b];
2336         for (d = 0; d < cdim; ++d) u_x[(fOffset + c) * dE + d] += Dq[cidx * cdim + d] * coefficients[dOffset + b];
2337       }
2338     }
2339     if (k > 1) {
2340       for (g = 0; g < Nf; ++g) hOffset += T[g]->Nc * dE;
2341       for (d = 0; d < dE * dE * Ncf; ++d) u_x[hOffset + fOffset * dE * dE + d] = 0.0;
2342       for (b = 0; b < Nbf; ++b) {
2343         for (c = 0; c < Ncf; ++c) {
2344           const PetscInt cidx = b * Ncf + c;
2345 
2346           for (d = 0; d < cdim * cdim; ++d) u_x[hOffset + (fOffset + c) * dE * dE + d] += Hq[cidx * cdim * cdim + d] * coefficients[dOffset + b];
2347         }
2348       }
2349       PetscCall(PetscFEPushforwardHessian(fe, fegeom, 1, &u_x[hOffset + fOffset * dE * dE]));
2350     }
2351     PetscCall(PetscFEPushforward(fe, fegeom, 1, &u[fOffset]));
2352     PetscCall(PetscFEPushforwardGradient(fe, fegeom, 1, &u_x[fOffset * dE]));
2353     if (u_t) {
2354       for (c = 0; c < Ncf; ++c) u_t[fOffset + c] = 0.0;
2355       for (b = 0; b < Nbf; ++b) {
2356         for (c = 0; c < Ncf; ++c) {
2357           const PetscInt cidx = b * Ncf + c;
2358 
2359           u_t[fOffset + c] += Bq[cidx] * coefficients_t[dOffset + b];
2360         }
2361       }
2362       PetscCall(PetscFEPushforward(fe, fegeom, 1, &u_t[fOffset]));
2363     }
2364     fOffset += Ncf;
2365     dOffset += Nbf;
2366   }
2367   return PETSC_SUCCESS;
2368 }
2369 
2370 PetscErrorCode PetscFEEvaluateFieldJets_Hybrid_Internal(PetscDS ds, PetscInt Nf, PetscInt rc, PetscInt qc, PetscTabulation Tab[], const PetscInt rf[], const PetscInt qf[], PetscTabulation Tabf[], PetscFEGeom *fegeom, PetscFEGeom *fegeomNbr, const PetscScalar coefficients[], const PetscScalar coefficients_t[], PetscScalar u[], PetscScalar u_x[], PetscScalar u_t[])
2371 {
2372   PetscInt dOffset = 0, fOffset = 0, f, g;
2373 
2374   /* f is the field number in the DS, g is the field number in u[] */
2375   for (f = 0, g = 0; f < Nf; ++f) {
2376     PetscBool isCohesive;
2377     PetscInt  Ns, s;
2378 
2379     if (!Tab[f]) continue;
2380     PetscCall(PetscDSGetCohesive(ds, f, &isCohesive));
2381     Ns = isCohesive ? 1 : 2;
2382     {
2383       PetscTabulation T   = isCohesive ? Tab[f] : Tabf[f];
2384       PetscFE         fe  = (PetscFE)ds->disc[f];
2385       const PetscInt  dEt = T->cdim;
2386       const PetscInt  dE  = fegeom->dimEmbed;
2387       const PetscInt  Nq  = T->Np;
2388       const PetscInt  Nbf = T->Nb;
2389       const PetscInt  Ncf = T->Nc;
2390 
2391       for (s = 0; s < Ns; ++s, ++g) {
2392         const PetscInt   r  = isCohesive ? rc : rf[s];
2393         const PetscInt   q  = isCohesive ? qc : qf[s];
2394         const PetscReal *Bq = &T->T[0][(r * Nq + q) * Nbf * Ncf];
2395         const PetscReal *Dq = &T->T[1][(r * Nq + q) * Nbf * Ncf * dEt];
2396         PetscInt         b, c, d;
2397 
2398         PetscCheck(r < T->Nr, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Field %" PetscInt_FMT " Side %" PetscInt_FMT " Replica number %" PetscInt_FMT " should be in [0, %" PetscInt_FMT ")", f, s, r, T->Nr);
2399         PetscCheck(q < T->Np, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Field %" PetscInt_FMT " Side %" PetscInt_FMT " Point number %" PetscInt_FMT " should be in [0, %" PetscInt_FMT ")", f, s, q, T->Np);
2400         for (c = 0; c < Ncf; ++c) u[fOffset + c] = 0.0;
2401         for (d = 0; d < dE * Ncf; ++d) u_x[fOffset * dE + d] = 0.0;
2402         for (b = 0; b < Nbf; ++b) {
2403           for (c = 0; c < Ncf; ++c) {
2404             const PetscInt cidx = b * Ncf + c;
2405 
2406             u[fOffset + c] += Bq[cidx] * coefficients[dOffset + b];
2407             for (d = 0; d < dEt; ++d) u_x[(fOffset + c) * dE + d] += Dq[cidx * dEt + d] * coefficients[dOffset + b];
2408           }
2409         }
2410         PetscCall(PetscFEPushforward(fe, isCohesive ? fegeom : &fegeomNbr[s], 1, &u[fOffset]));
2411         PetscCall(PetscFEPushforwardGradient(fe, isCohesive ? fegeom : &fegeomNbr[s], 1, &u_x[fOffset * dE]));
2412         if (u_t) {
2413           for (c = 0; c < Ncf; ++c) u_t[fOffset + c] = 0.0;
2414           for (b = 0; b < Nbf; ++b) {
2415             for (c = 0; c < Ncf; ++c) {
2416               const PetscInt cidx = b * Ncf + c;
2417 
2418               u_t[fOffset + c] += Bq[cidx] * coefficients_t[dOffset + b];
2419             }
2420           }
2421           PetscCall(PetscFEPushforward(fe, fegeom, 1, &u_t[fOffset]));
2422         }
2423         fOffset += Ncf;
2424         dOffset += Nbf;
2425       }
2426     }
2427   }
2428   return PETSC_SUCCESS;
2429 }
2430 
2431 PetscErrorCode PetscFEEvaluateFaceFields_Internal(PetscDS prob, PetscInt field, PetscInt faceLoc, const PetscScalar coefficients[], PetscScalar u[])
2432 {
2433   PetscFE         fe;
2434   PetscTabulation Tc;
2435   PetscInt        b, c;
2436 
2437   if (!prob) return PETSC_SUCCESS;
2438   PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fe));
2439   PetscCall(PetscFEGetFaceCentroidTabulation(fe, &Tc));
2440   {
2441     const PetscReal *faceBasis = Tc->T[0];
2442     const PetscInt   Nb        = Tc->Nb;
2443     const PetscInt   Nc        = Tc->Nc;
2444 
2445     for (c = 0; c < Nc; ++c) u[c] = 0.0;
2446     for (b = 0; b < Nb; ++b) {
2447       for (c = 0; c < Nc; ++c) u[c] += coefficients[b] * faceBasis[(faceLoc * Nb + b) * Nc + c];
2448     }
2449   }
2450   return PETSC_SUCCESS;
2451 }
2452 
2453 PetscErrorCode PetscFEUpdateElementVec_Internal(PetscFE fe, PetscTabulation T, PetscInt r, PetscScalar tmpBasis[], PetscScalar tmpBasisDer[], PetscInt e, PetscFEGeom *fegeom, PetscScalar f0[], PetscScalar f1[], PetscScalar elemVec[])
2454 {
2455   PetscFEGeom      pgeom;
2456   const PetscInt   dEt      = T->cdim;
2457   const PetscInt   dE       = fegeom->dimEmbed;
2458   const PetscInt   Nq       = T->Np;
2459   const PetscInt   Nb       = T->Nb;
2460   const PetscInt   Nc       = T->Nc;
2461   const PetscReal *basis    = &T->T[0][r * Nq * Nb * Nc];
2462   const PetscReal *basisDer = &T->T[1][r * Nq * Nb * Nc * dEt];
2463   PetscInt         q, b, c, d;
2464 
2465   for (q = 0; q < Nq; ++q) {
2466     for (b = 0; b < Nb; ++b) {
2467       for (c = 0; c < Nc; ++c) {
2468         const PetscInt bcidx = b * Nc + c;
2469 
2470         tmpBasis[bcidx] = basis[q * Nb * Nc + bcidx];
2471         for (d = 0; d < dEt; ++d) tmpBasisDer[bcidx * dE + d] = basisDer[q * Nb * Nc * dEt + bcidx * dEt + d];
2472         for (d = dEt; d < dE; ++d) tmpBasisDer[bcidx * dE + d] = 0.0;
2473       }
2474     }
2475     PetscCall(PetscFEGeomGetCellPoint(fegeom, e, q, &pgeom));
2476     PetscCall(PetscFEPushforward(fe, &pgeom, Nb, tmpBasis));
2477     PetscCall(PetscFEPushforwardGradient(fe, &pgeom, Nb, tmpBasisDer));
2478     for (b = 0; b < Nb; ++b) {
2479       for (c = 0; c < Nc; ++c) {
2480         const PetscInt bcidx = b * Nc + c;
2481         const PetscInt qcidx = q * Nc + c;
2482 
2483         elemVec[b] += tmpBasis[bcidx] * f0[qcidx];
2484         for (d = 0; d < dE; ++d) elemVec[b] += tmpBasisDer[bcidx * dE + d] * f1[qcidx * dE + d];
2485       }
2486     }
2487   }
2488   return PETSC_SUCCESS;
2489 }
2490 
2491 PetscErrorCode PetscFEUpdateElementVec_Hybrid_Internal(PetscFE fe, PetscTabulation T, PetscInt r, PetscInt side, PetscScalar tmpBasis[], PetscScalar tmpBasisDer[], PetscFEGeom *fegeom, PetscScalar f0[], PetscScalar f1[], PetscScalar elemVec[])
2492 {
2493   const PetscInt   dE       = T->cdim;
2494   const PetscInt   Nq       = T->Np;
2495   const PetscInt   Nb       = T->Nb;
2496   const PetscInt   Nc       = T->Nc;
2497   const PetscReal *basis    = &T->T[0][r * Nq * Nb * Nc];
2498   const PetscReal *basisDer = &T->T[1][r * Nq * Nb * Nc * dE];
2499 
2500   for (PetscInt q = 0; q < Nq; ++q) {
2501     for (PetscInt b = 0; b < Nb; ++b) {
2502       for (PetscInt c = 0; c < Nc; ++c) {
2503         const PetscInt bcidx = b * Nc + c;
2504 
2505         tmpBasis[bcidx] = basis[q * Nb * Nc + bcidx];
2506         for (PetscInt d = 0; d < dE; ++d) tmpBasisDer[bcidx * dE + d] = basisDer[q * Nb * Nc * dE + bcidx * dE + d];
2507       }
2508     }
2509     PetscCall(PetscFEPushforward(fe, fegeom, Nb, tmpBasis));
2510     // TODO This is currently broken since we do not pull the geometry down to the lower dimension
2511     // PetscCall(PetscFEPushforwardGradient(fe, fegeom, Nb, tmpBasisDer));
2512     if (side == 2) {
2513       // Integrating over whole cohesive cell, so insert for both sides
2514       for (PetscInt s = 0; s < 2; ++s) {
2515         for (PetscInt b = 0; b < Nb; ++b) {
2516           for (PetscInt c = 0; c < Nc; ++c) {
2517             const PetscInt bcidx = b * Nc + c;
2518             const PetscInt qcidx = (q * 2 + s) * Nc + c;
2519 
2520             elemVec[Nb * s + b] += tmpBasis[bcidx] * f0[qcidx];
2521             for (PetscInt d = 0; d < dE; ++d) elemVec[Nb * s + b] += tmpBasisDer[bcidx * dE + d] * f1[qcidx * dE + d];
2522           }
2523         }
2524       }
2525     } else {
2526       // Integrating over endcaps of cohesive cell, so insert for correct side
2527       for (PetscInt b = 0; b < Nb; ++b) {
2528         for (PetscInt c = 0; c < Nc; ++c) {
2529           const PetscInt bcidx = b * Nc + c;
2530           const PetscInt qcidx = q * Nc + c;
2531 
2532           elemVec[Nb * side + b] += tmpBasis[bcidx] * f0[qcidx];
2533           for (PetscInt d = 0; d < dE; ++d) elemVec[Nb * side + b] += tmpBasisDer[bcidx * dE + d] * f1[qcidx * dE + d];
2534         }
2535       }
2536     }
2537   }
2538   return PETSC_SUCCESS;
2539 }
2540 
2541 #define petsc_elemmat_kernel_g1(_NbI, _NcI, _NbJ, _NcJ, _dE) \
2542   do { \
2543     for (PetscInt fc = 0; fc < (_NcI); ++fc) { \
2544       for (PetscInt gc = 0; gc < (_NcJ); ++gc) { \
2545         const PetscScalar *G = g1 + (fc * (_NcJ) + gc) * _dE; \
2546         for (PetscInt f = 0; f < (_NbI); ++f) { \
2547           const PetscScalar tBIv = tmpBasisI[f * (_NcI) + fc]; \
2548           for (PetscInt g = 0; g < (_NbJ); ++g) { \
2549             const PetscScalar *tBDJ = tmpBasisDerJ + (g * (_NcJ) + gc) * (_dE); \
2550             PetscScalar        s    = 0.0; \
2551             for (PetscInt df = 0; df < _dE; ++df) { s += G[df] * tBDJ[df]; } \
2552             elemMat[(offsetI + f) * totDim + (offsetJ + g)] += s * tBIv; \
2553           } \
2554         } \
2555       } \
2556     } \
2557   } while (0)
2558 
2559 #define petsc_elemmat_kernel_g2(_NbI, _NcI, _NbJ, _NcJ, _dE) \
2560   do { \
2561     for (PetscInt gc = 0; gc < (_NcJ); ++gc) { \
2562       for (PetscInt fc = 0; fc < (_NcI); ++fc) { \
2563         const PetscScalar *G = g2 + (fc * (_NcJ) + gc) * _dE; \
2564         for (PetscInt g = 0; g < (_NbJ); ++g) { \
2565           const PetscScalar tBJv = tmpBasisJ[g * (_NcJ) + gc]; \
2566           for (PetscInt f = 0; f < (_NbI); ++f) { \
2567             const PetscScalar *tBDI = tmpBasisDerI + (f * (_NcI) + fc) * (_dE); \
2568             PetscScalar        s    = 0.0; \
2569             for (PetscInt df = 0; df < _dE; ++df) { s += tBDI[df] * G[df]; } \
2570             elemMat[(offsetI + f) * totDim + (offsetJ + g)] += s * tBJv; \
2571           } \
2572         } \
2573       } \
2574     } \
2575   } while (0)
2576 
2577 #define petsc_elemmat_kernel_g3(_NbI, _NcI, _NbJ, _NcJ, _dE) \
2578   do { \
2579     for (PetscInt fc = 0; fc < (_NcI); ++fc) { \
2580       for (PetscInt gc = 0; gc < (_NcJ); ++gc) { \
2581         const PetscScalar *G = g3 + (fc * (_NcJ) + gc) * (_dE) * (_dE); \
2582         for (PetscInt f = 0; f < (_NbI); ++f) { \
2583           const PetscScalar *tBDI = tmpBasisDerI + (f * (_NcI) + fc) * (_dE); \
2584           for (PetscInt g = 0; g < (_NbJ); ++g) { \
2585             PetscScalar        s    = 0.0; \
2586             const PetscScalar *tBDJ = tmpBasisDerJ + (g * (_NcJ) + gc) * (_dE); \
2587             for (PetscInt df = 0; df < (_dE); ++df) { \
2588               for (PetscInt dg = 0; dg < (_dE); ++dg) { s += tBDI[df] * G[df * (_dE) + dg] * tBDJ[dg]; } \
2589             } \
2590             elemMat[(offsetI + f) * totDim + (offsetJ + g)] += s; \
2591           } \
2592         } \
2593       } \
2594     } \
2595   } while (0)
2596 
2597 PetscErrorCode PetscFEUpdateElementMat_Internal(PetscFE feI, PetscFE feJ, PetscInt r, PetscInt q, PetscTabulation TI, PetscScalar tmpBasisI[], PetscScalar tmpBasisDerI[], PetscTabulation TJ, PetscScalar tmpBasisJ[], PetscScalar tmpBasisDerJ[], PetscFEGeom *fegeom, const PetscScalar g0[], const PetscScalar g1[], const PetscScalar g2[], const PetscScalar g3[], PetscInt totDim, PetscInt offsetI, PetscInt offsetJ, PetscScalar elemMat[])
2598 {
2599   const PetscInt   cdim      = TI->cdim;
2600   const PetscInt   dE        = fegeom->dimEmbed;
2601   const PetscInt   NqI       = TI->Np;
2602   const PetscInt   NbI       = TI->Nb;
2603   const PetscInt   NcI       = TI->Nc;
2604   const PetscReal *basisI    = &TI->T[0][(r * NqI + q) * NbI * NcI];
2605   const PetscReal *basisDerI = &TI->T[1][(r * NqI + q) * NbI * NcI * cdim];
2606   const PetscInt   NqJ       = TJ->Np;
2607   const PetscInt   NbJ       = TJ->Nb;
2608   const PetscInt   NcJ       = TJ->Nc;
2609   const PetscReal *basisJ    = &TJ->T[0][(r * NqJ + q) * NbJ * NcJ];
2610   const PetscReal *basisDerJ = &TJ->T[1][(r * NqJ + q) * NbJ * NcJ * cdim];
2611 
2612   for (PetscInt f = 0; f < NbI; ++f) {
2613     for (PetscInt fc = 0; fc < NcI; ++fc) {
2614       const PetscInt fidx = f * NcI + fc; /* Test function basis index */
2615 
2616       tmpBasisI[fidx] = basisI[fidx];
2617       for (PetscInt df = 0; df < cdim; ++df) tmpBasisDerI[fidx * dE + df] = basisDerI[fidx * cdim + df];
2618     }
2619   }
2620   PetscCall(PetscFEPushforward(feI, fegeom, NbI, tmpBasisI));
2621   PetscCall(PetscFEPushforwardGradient(feI, fegeom, NbI, tmpBasisDerI));
2622   if (feI != feJ) {
2623     for (PetscInt g = 0; g < NbJ; ++g) {
2624       for (PetscInt gc = 0; gc < NcJ; ++gc) {
2625         const PetscInt gidx = g * NcJ + gc; /* Trial function basis index */
2626 
2627         tmpBasisJ[gidx] = basisJ[gidx];
2628         for (PetscInt dg = 0; dg < cdim; ++dg) tmpBasisDerJ[gidx * dE + dg] = basisDerJ[gidx * cdim + dg];
2629       }
2630     }
2631     PetscCall(PetscFEPushforward(feJ, fegeom, NbJ, tmpBasisJ));
2632     PetscCall(PetscFEPushforwardGradient(feJ, fegeom, NbJ, tmpBasisDerJ));
2633   } else {
2634     tmpBasisJ    = tmpBasisI;
2635     tmpBasisDerJ = tmpBasisDerI;
2636   }
2637   if (PetscUnlikely(g0)) {
2638     for (PetscInt f = 0; f < NbI; ++f) {
2639       const PetscInt i = offsetI + f; /* Element matrix row */
2640 
2641       for (PetscInt fc = 0; fc < NcI; ++fc) {
2642         const PetscScalar bI = tmpBasisI[f * NcI + fc]; /* Test function basis value */
2643 
2644         for (PetscInt g = 0; g < NbJ; ++g) {
2645           const PetscInt j    = offsetJ + g; /* Element matrix column */
2646           const PetscInt fOff = i * totDim + j;
2647 
2648           for (PetscInt gc = 0; gc < NcJ; ++gc) { elemMat[fOff] += bI * g0[fc * NcJ + gc] * tmpBasisJ[g * NcJ + gc]; }
2649         }
2650       }
2651     }
2652   }
2653   if (PetscUnlikely(g1)) {
2654 #if 1
2655     if (dE == 2) {
2656       petsc_elemmat_kernel_g1(NbI, NcI, NbJ, NcJ, 2);
2657     } else if (dE == 3) {
2658       petsc_elemmat_kernel_g1(NbI, NcI, NbJ, NcJ, 3);
2659     } else {
2660       petsc_elemmat_kernel_g1(NbI, NcI, NbJ, NcJ, dE);
2661     }
2662 #else
2663     for (PetscInt f = 0; f < NbI; ++f) {
2664       const PetscInt i = offsetI + f; /* Element matrix row */
2665 
2666       for (PetscInt fc = 0; fc < NcI; ++fc) {
2667         const PetscScalar bI = tmpBasisI[f * NcI + fc]; /* Test function basis value */
2668 
2669         for (PetscInt g = 0; g < NbJ; ++g) {
2670           const PetscInt j    = offsetJ + g; /* Element matrix column */
2671           const PetscInt fOff = i * totDim + j;
2672 
2673           for (PetscInt gc = 0; gc < NcJ; ++gc) {
2674             const PetscInt gidx = g * NcJ + gc; /* Trial function basis index */
2675 
2676             for (PetscInt df = 0; df < dE; ++df) { elemMat[fOff] += bI * g1[(fc * NcJ + gc) * dE + df] * tmpBasisDerJ[gidx * dE + df]; }
2677           }
2678         }
2679       }
2680     }
2681 #endif
2682   }
2683   if (PetscUnlikely(g2)) {
2684 #if 1
2685     if (dE == 2) {
2686       petsc_elemmat_kernel_g2(NbI, NcI, NbJ, NcJ, 2);
2687     } else if (dE == 3) {
2688       petsc_elemmat_kernel_g2(NbI, NcI, NbJ, NcJ, 3);
2689     } else {
2690       petsc_elemmat_kernel_g2(NbI, NcI, NbJ, NcJ, dE);
2691     }
2692 #else
2693     for (PetscInt g = 0; g < NbJ; ++g) {
2694       const PetscInt j = offsetJ + g; /* Element matrix column */
2695 
2696       for (PetscInt gc = 0; gc < NcJ; ++gc) {
2697         const PetscScalar bJ = tmpBasisJ[g * NcJ + gc]; /* Trial function basis value */
2698 
2699         for (PetscInt f = 0; f < NbI; ++f) {
2700           const PetscInt i    = offsetI + f; /* Element matrix row */
2701           const PetscInt fOff = i * totDim + j;
2702 
2703           for (PetscInt fc = 0; fc < NcI; ++fc) {
2704             const PetscInt fidx = f * NcI + fc; /* Test function basis index */
2705 
2706             for (PetscInt df = 0; df < dE; ++df) { elemMat[fOff] += tmpBasisDerI[fidx * dE + df] * g2[(fc * NcJ + gc) * dE + df] * bJ; }
2707           }
2708         }
2709       }
2710     }
2711 #endif
2712   }
2713   if (PetscUnlikely(g3)) {
2714 #if 1
2715     if (dE == 2) {
2716       petsc_elemmat_kernel_g3(NbI, NcI, NbJ, NcJ, 2);
2717     } else if (dE == 3) {
2718       petsc_elemmat_kernel_g3(NbI, NcI, NbJ, NcJ, 3);
2719     } else {
2720       petsc_elemmat_kernel_g3(NbI, NcI, NbJ, NcJ, dE);
2721     }
2722 #else
2723     for (PetscInt f = 0; f < NbI; ++f) {
2724       const PetscInt i = offsetI + f; /* Element matrix row */
2725 
2726       for (PetscInt fc = 0; fc < NcI; ++fc) {
2727         const PetscInt fidx = f * NcI + fc; /* Test function basis index */
2728 
2729         for (PetscInt g = 0; g < NbJ; ++g) {
2730           const PetscInt j    = offsetJ + g; /* Element matrix column */
2731           const PetscInt fOff = i * totDim + j;
2732 
2733           for (PetscInt gc = 0; gc < NcJ; ++gc) {
2734             const PetscInt gidx = g * NcJ + gc; /* Trial function basis index */
2735 
2736             for (PetscInt df = 0; df < dE; ++df) {
2737               for (PetscInt dg = 0; dg < dE; ++dg) { elemMat[fOff] += tmpBasisDerI[fidx * dE + df] * g3[((fc * NcJ + gc) * dE + df) * dE + dg] * tmpBasisDerJ[gidx * dE + dg]; }
2738             }
2739           }
2740         }
2741       }
2742     }
2743 #endif
2744   }
2745   return PETSC_SUCCESS;
2746 }
2747 
2748 #undef petsc_elemmat_kernel_g1
2749 #undef petsc_elemmat_kernel_g2
2750 #undef petsc_elemmat_kernel_g3
2751 
2752 PetscErrorCode PetscFEUpdateElementMat_Hybrid_Internal(PetscFE feI, PetscBool isHybridI, PetscFE feJ, PetscBool isHybridJ, PetscInt r, PetscInt s, PetscInt t, PetscInt q, PetscTabulation TI, PetscScalar tmpBasisI[], PetscScalar tmpBasisDerI[], PetscTabulation TJ, PetscScalar tmpBasisJ[], PetscScalar tmpBasisDerJ[], PetscFEGeom *fegeom, const PetscScalar g0[], const PetscScalar g1[], const PetscScalar g2[], const PetscScalar g3[], PetscInt eOffset, PetscInt totDim, PetscInt offsetI, PetscInt offsetJ, PetscScalar elemMat[])
2753 {
2754   const PetscInt   dE        = TI->cdim;
2755   const PetscInt   NqI       = TI->Np;
2756   const PetscInt   NbI       = TI->Nb;
2757   const PetscInt   NcI       = TI->Nc;
2758   const PetscReal *basisI    = &TI->T[0][(r * NqI + q) * NbI * NcI];
2759   const PetscReal *basisDerI = &TI->T[1][(r * NqI + q) * NbI * NcI * dE];
2760   const PetscInt   NqJ       = TJ->Np;
2761   const PetscInt   NbJ       = TJ->Nb;
2762   const PetscInt   NcJ       = TJ->Nc;
2763   const PetscReal *basisJ    = &TJ->T[0][(r * NqJ + q) * NbJ * NcJ];
2764   const PetscReal *basisDerJ = &TJ->T[1][(r * NqJ + q) * NbJ * NcJ * dE];
2765   const PetscInt   so        = isHybridI ? 0 : s;
2766   const PetscInt   to        = isHybridJ ? 0 : t;
2767   PetscInt         f, fc, g, gc, df, dg;
2768 
2769   for (f = 0; f < NbI; ++f) {
2770     for (fc = 0; fc < NcI; ++fc) {
2771       const PetscInt fidx = f * NcI + fc; /* Test function basis index */
2772 
2773       tmpBasisI[fidx] = basisI[fidx];
2774       for (df = 0; df < dE; ++df) tmpBasisDerI[fidx * dE + df] = basisDerI[fidx * dE + df];
2775     }
2776   }
2777   PetscCall(PetscFEPushforward(feI, fegeom, NbI, tmpBasisI));
2778   PetscCall(PetscFEPushforwardGradient(feI, fegeom, NbI, tmpBasisDerI));
2779   for (g = 0; g < NbJ; ++g) {
2780     for (gc = 0; gc < NcJ; ++gc) {
2781       const PetscInt gidx = g * NcJ + gc; /* Trial function basis index */
2782 
2783       tmpBasisJ[gidx] = basisJ[gidx];
2784       for (dg = 0; dg < dE; ++dg) tmpBasisDerJ[gidx * dE + dg] = basisDerJ[gidx * dE + dg];
2785     }
2786   }
2787   PetscCall(PetscFEPushforward(feJ, fegeom, NbJ, tmpBasisJ));
2788   // TODO This is currently broken since we do not pull the geometry down to the lower dimension
2789   // PetscCall(PetscFEPushforwardGradient(feJ, fegeom, NbJ, tmpBasisDerJ));
2790   for (f = 0; f < NbI; ++f) {
2791     for (fc = 0; fc < NcI; ++fc) {
2792       const PetscInt fidx = f * NcI + fc;           /* Test function basis index */
2793       const PetscInt i    = offsetI + NbI * so + f; /* Element matrix row */
2794       for (g = 0; g < NbJ; ++g) {
2795         for (gc = 0; gc < NcJ; ++gc) {
2796           const PetscInt gidx = g * NcJ + gc;           /* Trial function basis index */
2797           const PetscInt j    = offsetJ + NbJ * to + g; /* Element matrix column */
2798           const PetscInt fOff = eOffset + i * totDim + j;
2799 
2800           elemMat[fOff] += tmpBasisI[fidx] * g0[fc * NcJ + gc] * tmpBasisJ[gidx];
2801           for (df = 0; df < dE; ++df) {
2802             elemMat[fOff] += tmpBasisI[fidx] * g1[(fc * NcJ + gc) * dE + df] * tmpBasisDerJ[gidx * dE + df];
2803             elemMat[fOff] += tmpBasisDerI[fidx * dE + df] * g2[(fc * NcJ + gc) * dE + df] * tmpBasisJ[gidx];
2804             for (dg = 0; dg < dE; ++dg) elemMat[fOff] += tmpBasisDerI[fidx * dE + df] * g3[((fc * NcJ + gc) * dE + df) * dE + dg] * tmpBasisDerJ[gidx * dE + dg];
2805           }
2806         }
2807       }
2808     }
2809   }
2810   return PETSC_SUCCESS;
2811 }
2812 
2813 PetscErrorCode PetscFECreateCellGeometry(PetscFE fe, PetscQuadrature quad, PetscFEGeom *cgeom)
2814 {
2815   PetscDualSpace  dsp;
2816   DM              dm;
2817   PetscQuadrature quadDef;
2818   PetscInt        dim, cdim, Nq;
2819 
2820   PetscFunctionBegin;
2821   PetscCall(PetscFEGetDualSpace(fe, &dsp));
2822   PetscCall(PetscDualSpaceGetDM(dsp, &dm));
2823   PetscCall(DMGetDimension(dm, &dim));
2824   PetscCall(DMGetCoordinateDim(dm, &cdim));
2825   PetscCall(PetscFEGetQuadrature(fe, &quadDef));
2826   quad = quad ? quad : quadDef;
2827   PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
2828   PetscCall(PetscMalloc1(Nq * cdim, &cgeom->v));
2829   PetscCall(PetscMalloc1(Nq * cdim * cdim, &cgeom->J));
2830   PetscCall(PetscMalloc1(Nq * cdim * cdim, &cgeom->invJ));
2831   PetscCall(PetscMalloc1(Nq, &cgeom->detJ));
2832   cgeom->dim       = dim;
2833   cgeom->dimEmbed  = cdim;
2834   cgeom->numCells  = 1;
2835   cgeom->numPoints = Nq;
2836   PetscCall(DMPlexComputeCellGeometryFEM(dm, 0, quad, cgeom->v, cgeom->J, cgeom->invJ, cgeom->detJ));
2837   PetscFunctionReturn(PETSC_SUCCESS);
2838 }
2839 
2840 PetscErrorCode PetscFEDestroyCellGeometry(PetscFE fe, PetscFEGeom *cgeom)
2841 {
2842   PetscFunctionBegin;
2843   PetscCall(PetscFree(cgeom->v));
2844   PetscCall(PetscFree(cgeom->J));
2845   PetscCall(PetscFree(cgeom->invJ));
2846   PetscCall(PetscFree(cgeom->detJ));
2847   PetscFunctionReturn(PETSC_SUCCESS);
2848 }
2849 
2850 #if 0
2851 PetscErrorCode PetscFEUpdateElementMat_Internal_SparseIndices(PetscTabulation TI, PetscTabulation TJ, PetscInt dimEmbed, const PetscInt g0[], const PetscInt g1[], const PetscInt g2[], const PetscInt g3[], PetscInt totDim, PetscInt offsetI, PetscInt offsetJ, PetscInt *n_g0, PetscInt **g0_idxs_out, PetscInt *n_g1, PetscInt **g1_idxs_out, PetscInt *n_g2, PetscInt **g2_idxs_out, PetscInt *n_g3, PetscInt **g3_idxs_out)
2852 {
2853   const PetscInt dE      = dimEmbed;
2854   const PetscInt NbI     = TI->Nb;
2855   const PetscInt NcI     = TI->Nc;
2856   const PetscInt NbJ     = TJ->Nb;
2857   const PetscInt NcJ     = TJ->Nc;
2858   PetscBool      has_g0  = g0 ? PETSC_TRUE : PETSC_FALSE;
2859   PetscBool      has_g1  = g1 ? PETSC_TRUE : PETSC_FALSE;
2860   PetscBool      has_g2  = g2 ? PETSC_TRUE : PETSC_FALSE;
2861   PetscBool      has_g3  = g3 ? PETSC_TRUE : PETSC_FALSE;
2862   PetscInt      *g0_idxs = NULL, *g1_idxs = NULL, *g2_idxs = NULL, *g3_idxs = NULL;
2863   PetscInt       g0_i, g1_i, g2_i, g3_i;
2864 
2865   PetscFunctionBegin;
2866   g0_i = g1_i = g2_i = g3_i = 0;
2867   if (has_g0)
2868     for (PetscInt i = 0; i < NcI * NcJ; i++)
2869       if (g0[i]) g0_i += NbI * NbJ;
2870   if (has_g1)
2871     for (PetscInt i = 0; i < NcI * NcJ * dE; i++)
2872       if (g1[i]) g1_i += NbI * NbJ;
2873   if (has_g2)
2874     for (PetscInt i = 0; i < NcI * NcJ * dE; i++)
2875       if (g2[i]) g2_i += NbI * NbJ;
2876   if (has_g3)
2877     for (PetscInt i = 0; i < NcI * NcJ * dE * dE; i++)
2878       if (g3[i]) g3_i += NbI * NbJ;
2879   if (g0_i == NbI * NbJ * NcI * NcJ) g0_i = 0;
2880   if (g1_i == NbI * NbJ * NcI * NcJ * dE) g1_i = 0;
2881   if (g2_i == NbI * NbJ * NcI * NcJ * dE) g2_i = 0;
2882   if (g3_i == NbI * NbJ * NcI * NcJ * dE * dE) g3_i = 0;
2883   has_g0 = g0_i ? PETSC_TRUE : PETSC_FALSE;
2884   has_g1 = g1_i ? PETSC_TRUE : PETSC_FALSE;
2885   has_g2 = g2_i ? PETSC_TRUE : PETSC_FALSE;
2886   has_g3 = g3_i ? PETSC_TRUE : PETSC_FALSE;
2887   if (has_g0) PetscCall(PetscMalloc1(4 * g0_i, &g0_idxs));
2888   if (has_g1) PetscCall(PetscMalloc1(4 * g1_i, &g1_idxs));
2889   if (has_g2) PetscCall(PetscMalloc1(4 * g2_i, &g2_idxs));
2890   if (has_g3) PetscCall(PetscMalloc1(4 * g3_i, &g3_idxs));
2891   g0_i = g1_i = g2_i = g3_i = 0;
2892 
2893   for (PetscInt f = 0; f < NbI; ++f) {
2894     const PetscInt i = offsetI + f; /* Element matrix row */
2895     for (PetscInt fc = 0; fc < NcI; ++fc) {
2896       const PetscInt fidx = f * NcI + fc; /* Test function basis index */
2897 
2898       for (PetscInt g = 0; g < NbJ; ++g) {
2899         const PetscInt j    = offsetJ + g; /* Element matrix column */
2900         const PetscInt fOff = i * totDim + j;
2901         for (PetscInt gc = 0; gc < NcJ; ++gc) {
2902           const PetscInt gidx = g * NcJ + gc; /* Trial function basis index */
2903 
2904           if (has_g0) {
2905             if (g0[fc * NcJ + gc]) {
2906               g0_idxs[4 * g0_i + 0] = fidx;
2907               g0_idxs[4 * g0_i + 1] = fc * NcJ + gc;
2908               g0_idxs[4 * g0_i + 2] = gidx;
2909               g0_idxs[4 * g0_i + 3] = fOff;
2910               g0_i++;
2911             }
2912           }
2913 
2914           for (PetscInt df = 0; df < dE; ++df) {
2915             if (has_g1) {
2916               if (g1[(fc * NcJ + gc) * dE + df]) {
2917                 g1_idxs[4 * g1_i + 0] = fidx;
2918                 g1_idxs[4 * g1_i + 1] = (fc * NcJ + gc) * dE + df;
2919                 g1_idxs[4 * g1_i + 2] = gidx * dE + df;
2920                 g1_idxs[4 * g1_i + 3] = fOff;
2921                 g1_i++;
2922               }
2923             }
2924             if (has_g2) {
2925               if (g2[(fc * NcJ + gc) * dE + df]) {
2926                 g2_idxs[4 * g2_i + 0] = fidx * dE + df;
2927                 g2_idxs[4 * g2_i + 1] = (fc * NcJ + gc) * dE + df;
2928                 g2_idxs[4 * g2_i + 2] = gidx;
2929                 g2_idxs[4 * g2_i + 3] = fOff;
2930                 g2_i++;
2931               }
2932             }
2933             if (has_g3) {
2934               for (PetscInt dg = 0; dg < dE; ++dg) {
2935                 if (g3[((fc * NcJ + gc) * dE + df) * dE + dg]) {
2936                   g3_idxs[4 * g3_i + 0] = fidx * dE + df;
2937                   g3_idxs[4 * g3_i + 1] = ((fc * NcJ + gc) * dE + df) * dE + dg;
2938                   g3_idxs[4 * g3_i + 2] = gidx * dE + dg;
2939                   g3_idxs[4 * g3_i + 3] = fOff;
2940                   g3_i++;
2941                 }
2942               }
2943             }
2944           }
2945         }
2946       }
2947     }
2948   }
2949   *n_g0 = g0_i;
2950   *n_g1 = g1_i;
2951   *n_g2 = g2_i;
2952   *n_g3 = g3_i;
2953 
2954   *g0_idxs_out = g0_idxs;
2955   *g1_idxs_out = g1_idxs;
2956   *g2_idxs_out = g2_idxs;
2957   *g3_idxs_out = g3_idxs;
2958   PetscFunctionReturn(PETSC_SUCCESS);
2959 }
2960 
2961 //example HOW TO USE
2962       for (PetscInt i = 0; i < g0_sparse_n; i++) {
2963         PetscInt bM = g0_sparse_idxs[4 * i + 0];
2964         PetscInt bN = g0_sparse_idxs[4 * i + 1];
2965         PetscInt bK = g0_sparse_idxs[4 * i + 2];
2966         PetscInt bO = g0_sparse_idxs[4 * i + 3];
2967         elemMat[bO] += tmpBasisI[bM] * g0[bN] * tmpBasisJ[bK];
2968       }
2969 #endif
2970