xref: /petsc/src/dm/dt/fe/interface/fe.c (revision 1404853cd07923df46691f50dfd5de8bf2e82a6a)
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   PetscFECreateBrokenElement - Create a discontinuous version of the input `PetscFE`
2284 
2285   Collective
2286 
2287   Input Parameters:
2288 . cgfe - The continuous `PetscFE` object
2289 
2290   Output Parameter:
2291 . dgfe - The discontinuous `PetscFE` object
2292 
2293   Level: advanced
2294 
2295   Note:
2296   This only works for Lagrange elements.
2297 
2298 .seealso: `PetscFECreate()`, `PetscSpaceCreate()`, `PetscDualSpaceCreate()`, `PetscFECreateLagrange()`, `PetscFECreateLagrangeByCell()`, `PetscDualSpaceLagrangeSetContinuity()`
2299 @*/
2300 PetscErrorCode PetscFECreateBrokenElement(PetscFE cgfe, PetscFE *dgfe)
2301 {
2302   PetscSpace      P;
2303   PetscDualSpace  Q, dgQ;
2304   PetscQuadrature q, fq;
2305   PetscBool       is_lagrange, is_sum;
2306 
2307   PetscFunctionBegin;
2308   PetscCall(PetscFEGetBasisSpace(cgfe, &P));
2309   PetscCall(PetscObjectReference((PetscObject)P));
2310   PetscCall(PetscFEGetDualSpace(cgfe, &Q));
2311   PetscCall(PetscObjectTypeCompare((PetscObject)Q, PETSCDUALSPACELAGRANGE, &is_lagrange));
2312   PetscCall(PetscObjectTypeCompare((PetscObject)Q, PETSCDUALSPACESUM, &is_sum));
2313   PetscCheck(is_lagrange || is_sum, PETSC_COMM_SELF, PETSC_ERR_SUP, "Can only create broken elements of Lagrange elements");
2314   PetscCall(PetscDualSpaceDuplicate(Q, &dgQ));
2315   PetscCall(PetscDualSpaceLagrangeSetContinuity(dgQ, PETSC_FALSE));
2316   PetscCall(PetscDualSpaceSetUp(dgQ));
2317   PetscCall(PetscFEGetQuadrature(cgfe, &q));
2318   PetscCall(PetscObjectReference((PetscObject)q));
2319   PetscCall(PetscFEGetFaceQuadrature(cgfe, &fq));
2320   PetscCall(PetscObjectReference((PetscObject)fq));
2321   PetscCall(PetscFECreateFromSpaces(P, dgQ, q, fq, dgfe));
2322   PetscFunctionReturn(PETSC_SUCCESS);
2323 }
2324 
2325 /*@
2326   PetscFESetName - Names the `PetscFE` and its subobjects
2327 
2328   Not Collective
2329 
2330   Input Parameters:
2331 + fe   - The `PetscFE`
2332 - name - The name
2333 
2334   Level: intermediate
2335 
2336 .seealso: `PetscFECreate()`, `PetscSpaceCreate()`, `PetscDualSpaceCreate()`
2337 @*/
2338 PetscErrorCode PetscFESetName(PetscFE fe, const char name[])
2339 {
2340   PetscSpace     P;
2341   PetscDualSpace Q;
2342 
2343   PetscFunctionBegin;
2344   PetscCall(PetscFEGetBasisSpace(fe, &P));
2345   PetscCall(PetscFEGetDualSpace(fe, &Q));
2346   PetscCall(PetscObjectSetName((PetscObject)fe, name));
2347   PetscCall(PetscObjectSetName((PetscObject)P, name));
2348   PetscCall(PetscObjectSetName((PetscObject)Q, name));
2349   PetscFunctionReturn(PETSC_SUCCESS);
2350 }
2351 
2352 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[])
2353 {
2354   PetscInt dOffset = 0, fOffset = 0, f, g;
2355 
2356   for (f = 0; f < Nf; ++f) {
2357     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);
2358     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);
2359     PetscFE          fe;
2360     const PetscInt   k       = ds->jetDegree[f];
2361     const PetscInt   cdim    = T[f]->cdim;
2362     const PetscInt   dE      = fegeom->dimEmbed;
2363     const PetscInt   Nq      = T[f]->Np;
2364     const PetscInt   Nbf     = T[f]->Nb;
2365     const PetscInt   Ncf     = T[f]->Nc;
2366     const PetscReal *Bq      = &T[f]->T[0][(r * Nq + q) * Nbf * Ncf];
2367     const PetscReal *Dq      = &T[f]->T[1][(r * Nq + q) * Nbf * Ncf * cdim];
2368     const PetscReal *Hq      = k > 1 ? &T[f]->T[2][(r * Nq + q) * Nbf * Ncf * cdim * cdim] : NULL;
2369     PetscInt         hOffset = 0, b, c, d;
2370 
2371     PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
2372     for (c = 0; c < Ncf; ++c) u[fOffset + c] = 0.0;
2373     for (d = 0; d < dE * Ncf; ++d) u_x[fOffset * dE + d] = 0.0;
2374     for (b = 0; b < Nbf; ++b) {
2375       for (c = 0; c < Ncf; ++c) {
2376         const PetscInt cidx = b * Ncf + c;
2377 
2378         u[fOffset + c] += Bq[cidx] * coefficients[dOffset + b];
2379         for (d = 0; d < cdim; ++d) u_x[(fOffset + c) * dE + d] += Dq[cidx * cdim + d] * coefficients[dOffset + b];
2380       }
2381     }
2382     if (k > 1) {
2383       for (g = 0; g < Nf; ++g) hOffset += T[g]->Nc * dE;
2384       for (d = 0; d < dE * dE * Ncf; ++d) u_x[hOffset + fOffset * dE * dE + d] = 0.0;
2385       for (b = 0; b < Nbf; ++b) {
2386         for (c = 0; c < Ncf; ++c) {
2387           const PetscInt cidx = b * Ncf + c;
2388 
2389           for (d = 0; d < cdim * cdim; ++d) u_x[hOffset + (fOffset + c) * dE * dE + d] += Hq[cidx * cdim * cdim + d] * coefficients[dOffset + b];
2390         }
2391       }
2392       PetscCall(PetscFEPushforwardHessian(fe, fegeom, 1, &u_x[hOffset + fOffset * dE * dE]));
2393     }
2394     PetscCall(PetscFEPushforward(fe, fegeom, 1, &u[fOffset]));
2395     PetscCall(PetscFEPushforwardGradient(fe, fegeom, 1, &u_x[fOffset * dE]));
2396     if (u_t) {
2397       for (c = 0; c < Ncf; ++c) u_t[fOffset + c] = 0.0;
2398       for (b = 0; b < Nbf; ++b) {
2399         for (c = 0; c < Ncf; ++c) {
2400           const PetscInt cidx = b * Ncf + c;
2401 
2402           u_t[fOffset + c] += Bq[cidx] * coefficients_t[dOffset + b];
2403         }
2404       }
2405       PetscCall(PetscFEPushforward(fe, fegeom, 1, &u_t[fOffset]));
2406     }
2407     fOffset += Ncf;
2408     dOffset += Nbf;
2409   }
2410   return PETSC_SUCCESS;
2411 }
2412 
2413 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[])
2414 {
2415   PetscInt dOffset = 0, fOffset = 0, f, g;
2416 
2417   /* f is the field number in the DS, g is the field number in u[] */
2418   for (f = 0, g = 0; f < Nf; ++f) {
2419     PetscBool isCohesive;
2420     PetscInt  Ns, s;
2421 
2422     if (!Tab[f]) continue;
2423     PetscCall(PetscDSGetCohesive(ds, f, &isCohesive));
2424     Ns = isCohesive ? 1 : 2;
2425     {
2426       PetscTabulation T   = isCohesive ? Tab[f] : Tabf[f];
2427       PetscFE         fe  = (PetscFE)ds->disc[f];
2428       const PetscInt  dEt = T->cdim;
2429       const PetscInt  dE  = fegeom->dimEmbed;
2430       const PetscInt  Nq  = T->Np;
2431       const PetscInt  Nbf = T->Nb;
2432       const PetscInt  Ncf = T->Nc;
2433 
2434       for (s = 0; s < Ns; ++s, ++g) {
2435         const PetscInt   r  = isCohesive ? rc : rf[s];
2436         const PetscInt   q  = isCohesive ? qc : qf[s];
2437         const PetscReal *Bq = &T->T[0][(r * Nq + q) * Nbf * Ncf];
2438         const PetscReal *Dq = &T->T[1][(r * Nq + q) * Nbf * Ncf * dEt];
2439         PetscInt         b, c, d;
2440 
2441         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);
2442         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);
2443         for (c = 0; c < Ncf; ++c) u[fOffset + c] = 0.0;
2444         for (d = 0; d < dE * Ncf; ++d) u_x[fOffset * dE + d] = 0.0;
2445         for (b = 0; b < Nbf; ++b) {
2446           for (c = 0; c < Ncf; ++c) {
2447             const PetscInt cidx = b * Ncf + c;
2448 
2449             u[fOffset + c] += Bq[cidx] * coefficients[dOffset + b];
2450             for (d = 0; d < dEt; ++d) u_x[(fOffset + c) * dE + d] += Dq[cidx * dEt + d] * coefficients[dOffset + b];
2451           }
2452         }
2453         PetscCall(PetscFEPushforward(fe, isCohesive ? fegeom : &fegeomNbr[s], 1, &u[fOffset]));
2454         PetscCall(PetscFEPushforwardGradient(fe, isCohesive ? fegeom : &fegeomNbr[s], 1, &u_x[fOffset * dE]));
2455         if (u_t) {
2456           for (c = 0; c < Ncf; ++c) u_t[fOffset + c] = 0.0;
2457           for (b = 0; b < Nbf; ++b) {
2458             for (c = 0; c < Ncf; ++c) {
2459               const PetscInt cidx = b * Ncf + c;
2460 
2461               u_t[fOffset + c] += Bq[cidx] * coefficients_t[dOffset + b];
2462             }
2463           }
2464           PetscCall(PetscFEPushforward(fe, fegeom, 1, &u_t[fOffset]));
2465         }
2466         fOffset += Ncf;
2467         dOffset += Nbf;
2468       }
2469     }
2470   }
2471   return PETSC_SUCCESS;
2472 }
2473 
2474 PetscErrorCode PetscFEEvaluateFaceFields_Internal(PetscDS prob, PetscInt field, PetscInt faceLoc, const PetscScalar coefficients[], PetscScalar u[])
2475 {
2476   PetscFE         fe;
2477   PetscTabulation Tc;
2478   PetscInt        b, c;
2479 
2480   if (!prob) return PETSC_SUCCESS;
2481   PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fe));
2482   PetscCall(PetscFEGetFaceCentroidTabulation(fe, &Tc));
2483   {
2484     const PetscReal *faceBasis = Tc->T[0];
2485     const PetscInt   Nb        = Tc->Nb;
2486     const PetscInt   Nc        = Tc->Nc;
2487 
2488     for (c = 0; c < Nc; ++c) u[c] = 0.0;
2489     for (b = 0; b < Nb; ++b) {
2490       for (c = 0; c < Nc; ++c) u[c] += coefficients[b] * faceBasis[(faceLoc * Nb + b) * Nc + c];
2491     }
2492   }
2493   return PETSC_SUCCESS;
2494 }
2495 
2496 PetscErrorCode PetscFEUpdateElementVec_Internal(PetscFE fe, PetscTabulation T, PetscInt r, PetscScalar tmpBasis[], PetscScalar tmpBasisDer[], PetscInt e, PetscFEGeom *fegeom, PetscScalar f0[], PetscScalar f1[], PetscScalar elemVec[])
2497 {
2498   PetscFEGeom      pgeom;
2499   const PetscInt   dEt      = T->cdim;
2500   const PetscInt   dE       = fegeom->dimEmbed;
2501   const PetscInt   Nq       = T->Np;
2502   const PetscInt   Nb       = T->Nb;
2503   const PetscInt   Nc       = T->Nc;
2504   const PetscReal *basis    = &T->T[0][r * Nq * Nb * Nc];
2505   const PetscReal *basisDer = &T->T[1][r * Nq * Nb * Nc * dEt];
2506   PetscInt         q, b, c, d;
2507 
2508   for (q = 0; q < Nq; ++q) {
2509     for (b = 0; b < Nb; ++b) {
2510       for (c = 0; c < Nc; ++c) {
2511         const PetscInt bcidx = b * Nc + c;
2512 
2513         tmpBasis[bcidx] = basis[q * Nb * Nc + bcidx];
2514         for (d = 0; d < dEt; ++d) tmpBasisDer[bcidx * dE + d] = basisDer[q * Nb * Nc * dEt + bcidx * dEt + d];
2515         for (d = dEt; d < dE; ++d) tmpBasisDer[bcidx * dE + d] = 0.0;
2516       }
2517     }
2518     PetscCall(PetscFEGeomGetCellPoint(fegeom, e, q, &pgeom));
2519     PetscCall(PetscFEPushforward(fe, &pgeom, Nb, tmpBasis));
2520     PetscCall(PetscFEPushforwardGradient(fe, &pgeom, Nb, tmpBasisDer));
2521     for (b = 0; b < Nb; ++b) {
2522       for (c = 0; c < Nc; ++c) {
2523         const PetscInt bcidx = b * Nc + c;
2524         const PetscInt qcidx = q * Nc + c;
2525 
2526         elemVec[b] += tmpBasis[bcidx] * f0[qcidx];
2527         for (d = 0; d < dE; ++d) elemVec[b] += tmpBasisDer[bcidx * dE + d] * f1[qcidx * dE + d];
2528       }
2529     }
2530   }
2531   return PETSC_SUCCESS;
2532 }
2533 
2534 PetscErrorCode PetscFEUpdateElementVec_Hybrid_Internal(PetscFE fe, PetscTabulation T, PetscInt r, PetscInt side, PetscScalar tmpBasis[], PetscScalar tmpBasisDer[], PetscFEGeom *fegeom, PetscScalar f0[], PetscScalar f1[], PetscScalar elemVec[])
2535 {
2536   const PetscInt   dE       = T->cdim;
2537   const PetscInt   Nq       = T->Np;
2538   const PetscInt   Nb       = T->Nb;
2539   const PetscInt   Nc       = T->Nc;
2540   const PetscReal *basis    = &T->T[0][r * Nq * Nb * Nc];
2541   const PetscReal *basisDer = &T->T[1][r * Nq * Nb * Nc * dE];
2542 
2543   for (PetscInt q = 0; q < Nq; ++q) {
2544     for (PetscInt b = 0; b < Nb; ++b) {
2545       for (PetscInt c = 0; c < Nc; ++c) {
2546         const PetscInt bcidx = b * Nc + c;
2547 
2548         tmpBasis[bcidx] = basis[q * Nb * Nc + bcidx];
2549         for (PetscInt d = 0; d < dE; ++d) tmpBasisDer[bcidx * dE + d] = basisDer[q * Nb * Nc * dE + bcidx * dE + d];
2550       }
2551     }
2552     PetscCall(PetscFEPushforward(fe, fegeom, Nb, tmpBasis));
2553     // TODO This is currently broken since we do not pull the geometry down to the lower dimension
2554     // PetscCall(PetscFEPushforwardGradient(fe, fegeom, Nb, tmpBasisDer));
2555     if (side == 2) {
2556       // Integrating over whole cohesive cell, so insert for both sides
2557       for (PetscInt s = 0; s < 2; ++s) {
2558         for (PetscInt b = 0; b < Nb; ++b) {
2559           for (PetscInt c = 0; c < Nc; ++c) {
2560             const PetscInt bcidx = b * Nc + c;
2561             const PetscInt qcidx = (q * 2 + s) * Nc + c;
2562 
2563             elemVec[Nb * s + b] += tmpBasis[bcidx] * f0[qcidx];
2564             for (PetscInt d = 0; d < dE; ++d) elemVec[Nb * s + b] += tmpBasisDer[bcidx * dE + d] * f1[qcidx * dE + d];
2565           }
2566         }
2567       }
2568     } else {
2569       // Integrating over endcaps of cohesive cell, so insert for correct side
2570       for (PetscInt b = 0; b < Nb; ++b) {
2571         for (PetscInt c = 0; c < Nc; ++c) {
2572           const PetscInt bcidx = b * Nc + c;
2573           const PetscInt qcidx = q * Nc + c;
2574 
2575           elemVec[Nb * side + b] += tmpBasis[bcidx] * f0[qcidx];
2576           for (PetscInt d = 0; d < dE; ++d) elemVec[Nb * side + b] += tmpBasisDer[bcidx * dE + d] * f1[qcidx * dE + d];
2577         }
2578       }
2579     }
2580   }
2581   return PETSC_SUCCESS;
2582 }
2583 
2584 #define petsc_elemmat_kernel_g1(_NbI, _NcI, _NbJ, _NcJ, _dE) \
2585   do { \
2586     for (PetscInt fc = 0; fc < (_NcI); ++fc) { \
2587       for (PetscInt gc = 0; gc < (_NcJ); ++gc) { \
2588         const PetscScalar *G = g1 + (fc * (_NcJ) + gc) * _dE; \
2589         for (PetscInt f = 0; f < (_NbI); ++f) { \
2590           const PetscScalar tBIv = tmpBasisI[f * (_NcI) + fc]; \
2591           for (PetscInt g = 0; g < (_NbJ); ++g) { \
2592             const PetscScalar *tBDJ = tmpBasisDerJ + (g * (_NcJ) + gc) * (_dE); \
2593             PetscScalar        s    = 0.0; \
2594             for (PetscInt df = 0; df < _dE; ++df) { s += G[df] * tBDJ[df]; } \
2595             elemMat[(offsetI + f) * totDim + (offsetJ + g)] += s * tBIv; \
2596           } \
2597         } \
2598       } \
2599     } \
2600   } while (0)
2601 
2602 #define petsc_elemmat_kernel_g2(_NbI, _NcI, _NbJ, _NcJ, _dE) \
2603   do { \
2604     for (PetscInt gc = 0; gc < (_NcJ); ++gc) { \
2605       for (PetscInt fc = 0; fc < (_NcI); ++fc) { \
2606         const PetscScalar *G = g2 + (fc * (_NcJ) + gc) * _dE; \
2607         for (PetscInt g = 0; g < (_NbJ); ++g) { \
2608           const PetscScalar tBJv = tmpBasisJ[g * (_NcJ) + gc]; \
2609           for (PetscInt f = 0; f < (_NbI); ++f) { \
2610             const PetscScalar *tBDI = tmpBasisDerI + (f * (_NcI) + fc) * (_dE); \
2611             PetscScalar        s    = 0.0; \
2612             for (PetscInt df = 0; df < _dE; ++df) { s += tBDI[df] * G[df]; } \
2613             elemMat[(offsetI + f) * totDim + (offsetJ + g)] += s * tBJv; \
2614           } \
2615         } \
2616       } \
2617     } \
2618   } while (0)
2619 
2620 #define petsc_elemmat_kernel_g3(_NbI, _NcI, _NbJ, _NcJ, _dE) \
2621   do { \
2622     for (PetscInt fc = 0; fc < (_NcI); ++fc) { \
2623       for (PetscInt gc = 0; gc < (_NcJ); ++gc) { \
2624         const PetscScalar *G = g3 + (fc * (_NcJ) + gc) * (_dE) * (_dE); \
2625         for (PetscInt f = 0; f < (_NbI); ++f) { \
2626           const PetscScalar *tBDI = tmpBasisDerI + (f * (_NcI) + fc) * (_dE); \
2627           for (PetscInt g = 0; g < (_NbJ); ++g) { \
2628             PetscScalar        s    = 0.0; \
2629             const PetscScalar *tBDJ = tmpBasisDerJ + (g * (_NcJ) + gc) * (_dE); \
2630             for (PetscInt df = 0; df < (_dE); ++df) { \
2631               for (PetscInt dg = 0; dg < (_dE); ++dg) { s += tBDI[df] * G[df * (_dE) + dg] * tBDJ[dg]; } \
2632             } \
2633             elemMat[(offsetI + f) * totDim + (offsetJ + g)] += s; \
2634           } \
2635         } \
2636       } \
2637     } \
2638   } while (0)
2639 
2640 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[])
2641 {
2642   const PetscInt   cdim      = TI->cdim;
2643   const PetscInt   dE        = fegeom->dimEmbed;
2644   const PetscInt   NqI       = TI->Np;
2645   const PetscInt   NbI       = TI->Nb;
2646   const PetscInt   NcI       = TI->Nc;
2647   const PetscReal *basisI    = &TI->T[0][(r * NqI + q) * NbI * NcI];
2648   const PetscReal *basisDerI = &TI->T[1][(r * NqI + q) * NbI * NcI * cdim];
2649   const PetscInt   NqJ       = TJ->Np;
2650   const PetscInt   NbJ       = TJ->Nb;
2651   const PetscInt   NcJ       = TJ->Nc;
2652   const PetscReal *basisJ    = &TJ->T[0][(r * NqJ + q) * NbJ * NcJ];
2653   const PetscReal *basisDerJ = &TJ->T[1][(r * NqJ + q) * NbJ * NcJ * cdim];
2654 
2655   for (PetscInt f = 0; f < NbI; ++f) {
2656     for (PetscInt fc = 0; fc < NcI; ++fc) {
2657       const PetscInt fidx = f * NcI + fc; /* Test function basis index */
2658 
2659       tmpBasisI[fidx] = basisI[fidx];
2660       for (PetscInt df = 0; df < cdim; ++df) tmpBasisDerI[fidx * dE + df] = basisDerI[fidx * cdim + df];
2661     }
2662   }
2663   PetscCall(PetscFEPushforward(feI, fegeom, NbI, tmpBasisI));
2664   PetscCall(PetscFEPushforwardGradient(feI, fegeom, NbI, tmpBasisDerI));
2665   if (feI != feJ) {
2666     for (PetscInt g = 0; g < NbJ; ++g) {
2667       for (PetscInt gc = 0; gc < NcJ; ++gc) {
2668         const PetscInt gidx = g * NcJ + gc; /* Trial function basis index */
2669 
2670         tmpBasisJ[gidx] = basisJ[gidx];
2671         for (PetscInt dg = 0; dg < cdim; ++dg) tmpBasisDerJ[gidx * dE + dg] = basisDerJ[gidx * cdim + dg];
2672       }
2673     }
2674     PetscCall(PetscFEPushforward(feJ, fegeom, NbJ, tmpBasisJ));
2675     PetscCall(PetscFEPushforwardGradient(feJ, fegeom, NbJ, tmpBasisDerJ));
2676   } else {
2677     tmpBasisJ    = tmpBasisI;
2678     tmpBasisDerJ = tmpBasisDerI;
2679   }
2680   if (PetscUnlikely(g0)) {
2681     for (PetscInt f = 0; f < NbI; ++f) {
2682       const PetscInt i = offsetI + f; /* Element matrix row */
2683 
2684       for (PetscInt fc = 0; fc < NcI; ++fc) {
2685         const PetscScalar bI = tmpBasisI[f * NcI + fc]; /* Test function basis value */
2686 
2687         for (PetscInt g = 0; g < NbJ; ++g) {
2688           const PetscInt j    = offsetJ + g; /* Element matrix column */
2689           const PetscInt fOff = i * totDim + j;
2690 
2691           for (PetscInt gc = 0; gc < NcJ; ++gc) { elemMat[fOff] += bI * g0[fc * NcJ + gc] * tmpBasisJ[g * NcJ + gc]; }
2692         }
2693       }
2694     }
2695   }
2696   if (PetscUnlikely(g1)) {
2697 #if 1
2698     if (dE == 2) {
2699       petsc_elemmat_kernel_g1(NbI, NcI, NbJ, NcJ, 2);
2700     } else if (dE == 3) {
2701       petsc_elemmat_kernel_g1(NbI, NcI, NbJ, NcJ, 3);
2702     } else {
2703       petsc_elemmat_kernel_g1(NbI, NcI, NbJ, NcJ, dE);
2704     }
2705 #else
2706     for (PetscInt f = 0; f < NbI; ++f) {
2707       const PetscInt i = offsetI + f; /* Element matrix row */
2708 
2709       for (PetscInt fc = 0; fc < NcI; ++fc) {
2710         const PetscScalar bI = tmpBasisI[f * NcI + fc]; /* Test function basis value */
2711 
2712         for (PetscInt g = 0; g < NbJ; ++g) {
2713           const PetscInt j    = offsetJ + g; /* Element matrix column */
2714           const PetscInt fOff = i * totDim + j;
2715 
2716           for (PetscInt gc = 0; gc < NcJ; ++gc) {
2717             const PetscInt gidx = g * NcJ + gc; /* Trial function basis index */
2718 
2719             for (PetscInt df = 0; df < dE; ++df) { elemMat[fOff] += bI * g1[(fc * NcJ + gc) * dE + df] * tmpBasisDerJ[gidx * dE + df]; }
2720           }
2721         }
2722       }
2723     }
2724 #endif
2725   }
2726   if (PetscUnlikely(g2)) {
2727 #if 1
2728     if (dE == 2) {
2729       petsc_elemmat_kernel_g2(NbI, NcI, NbJ, NcJ, 2);
2730     } else if (dE == 3) {
2731       petsc_elemmat_kernel_g2(NbI, NcI, NbJ, NcJ, 3);
2732     } else {
2733       petsc_elemmat_kernel_g2(NbI, NcI, NbJ, NcJ, dE);
2734     }
2735 #else
2736     for (PetscInt g = 0; g < NbJ; ++g) {
2737       const PetscInt j = offsetJ + g; /* Element matrix column */
2738 
2739       for (PetscInt gc = 0; gc < NcJ; ++gc) {
2740         const PetscScalar bJ = tmpBasisJ[g * NcJ + gc]; /* Trial function basis value */
2741 
2742         for (PetscInt f = 0; f < NbI; ++f) {
2743           const PetscInt i    = offsetI + f; /* Element matrix row */
2744           const PetscInt fOff = i * totDim + j;
2745 
2746           for (PetscInt fc = 0; fc < NcI; ++fc) {
2747             const PetscInt fidx = f * NcI + fc; /* Test function basis index */
2748 
2749             for (PetscInt df = 0; df < dE; ++df) { elemMat[fOff] += tmpBasisDerI[fidx * dE + df] * g2[(fc * NcJ + gc) * dE + df] * bJ; }
2750           }
2751         }
2752       }
2753     }
2754 #endif
2755   }
2756   if (PetscUnlikely(g3)) {
2757 #if 1
2758     if (dE == 2) {
2759       petsc_elemmat_kernel_g3(NbI, NcI, NbJ, NcJ, 2);
2760     } else if (dE == 3) {
2761       petsc_elemmat_kernel_g3(NbI, NcI, NbJ, NcJ, 3);
2762     } else {
2763       petsc_elemmat_kernel_g3(NbI, NcI, NbJ, NcJ, dE);
2764     }
2765 #else
2766     for (PetscInt f = 0; f < NbI; ++f) {
2767       const PetscInt i = offsetI + f; /* Element matrix row */
2768 
2769       for (PetscInt fc = 0; fc < NcI; ++fc) {
2770         const PetscInt fidx = f * NcI + fc; /* Test function basis index */
2771 
2772         for (PetscInt g = 0; g < NbJ; ++g) {
2773           const PetscInt j    = offsetJ + g; /* Element matrix column */
2774           const PetscInt fOff = i * totDim + j;
2775 
2776           for (PetscInt gc = 0; gc < NcJ; ++gc) {
2777             const PetscInt gidx = g * NcJ + gc; /* Trial function basis index */
2778 
2779             for (PetscInt df = 0; df < dE; ++df) {
2780               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]; }
2781             }
2782           }
2783         }
2784       }
2785     }
2786 #endif
2787   }
2788   return PETSC_SUCCESS;
2789 }
2790 
2791 #undef petsc_elemmat_kernel_g1
2792 #undef petsc_elemmat_kernel_g2
2793 #undef petsc_elemmat_kernel_g3
2794 
2795 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[])
2796 {
2797   const PetscInt   dE        = TI->cdim;
2798   const PetscInt   NqI       = TI->Np;
2799   const PetscInt   NbI       = TI->Nb;
2800   const PetscInt   NcI       = TI->Nc;
2801   const PetscReal *basisI    = &TI->T[0][(r * NqI + q) * NbI * NcI];
2802   const PetscReal *basisDerI = &TI->T[1][(r * NqI + q) * NbI * NcI * dE];
2803   const PetscInt   NqJ       = TJ->Np;
2804   const PetscInt   NbJ       = TJ->Nb;
2805   const PetscInt   NcJ       = TJ->Nc;
2806   const PetscReal *basisJ    = &TJ->T[0][(r * NqJ + q) * NbJ * NcJ];
2807   const PetscReal *basisDerJ = &TJ->T[1][(r * NqJ + q) * NbJ * NcJ * dE];
2808   const PetscInt   so        = isHybridI ? 0 : s;
2809   const PetscInt   to        = isHybridJ ? 0 : t;
2810   PetscInt         f, fc, g, gc, df, dg;
2811 
2812   for (f = 0; f < NbI; ++f) {
2813     for (fc = 0; fc < NcI; ++fc) {
2814       const PetscInt fidx = f * NcI + fc; /* Test function basis index */
2815 
2816       tmpBasisI[fidx] = basisI[fidx];
2817       for (df = 0; df < dE; ++df) tmpBasisDerI[fidx * dE + df] = basisDerI[fidx * dE + df];
2818     }
2819   }
2820   PetscCall(PetscFEPushforward(feI, fegeom, NbI, tmpBasisI));
2821   PetscCall(PetscFEPushforwardGradient(feI, fegeom, NbI, tmpBasisDerI));
2822   for (g = 0; g < NbJ; ++g) {
2823     for (gc = 0; gc < NcJ; ++gc) {
2824       const PetscInt gidx = g * NcJ + gc; /* Trial function basis index */
2825 
2826       tmpBasisJ[gidx] = basisJ[gidx];
2827       for (dg = 0; dg < dE; ++dg) tmpBasisDerJ[gidx * dE + dg] = basisDerJ[gidx * dE + dg];
2828     }
2829   }
2830   PetscCall(PetscFEPushforward(feJ, fegeom, NbJ, tmpBasisJ));
2831   // TODO This is currently broken since we do not pull the geometry down to the lower dimension
2832   // PetscCall(PetscFEPushforwardGradient(feJ, fegeom, NbJ, tmpBasisDerJ));
2833   for (f = 0; f < NbI; ++f) {
2834     for (fc = 0; fc < NcI; ++fc) {
2835       const PetscInt fidx = f * NcI + fc;           /* Test function basis index */
2836       const PetscInt i    = offsetI + NbI * so + f; /* Element matrix row */
2837       for (g = 0; g < NbJ; ++g) {
2838         for (gc = 0; gc < NcJ; ++gc) {
2839           const PetscInt gidx = g * NcJ + gc;           /* Trial function basis index */
2840           const PetscInt j    = offsetJ + NbJ * to + g; /* Element matrix column */
2841           const PetscInt fOff = eOffset + i * totDim + j;
2842 
2843           elemMat[fOff] += tmpBasisI[fidx] * g0[fc * NcJ + gc] * tmpBasisJ[gidx];
2844           for (df = 0; df < dE; ++df) {
2845             elemMat[fOff] += tmpBasisI[fidx] * g1[(fc * NcJ + gc) * dE + df] * tmpBasisDerJ[gidx * dE + df];
2846             elemMat[fOff] += tmpBasisDerI[fidx * dE + df] * g2[(fc * NcJ + gc) * dE + df] * tmpBasisJ[gidx];
2847             for (dg = 0; dg < dE; ++dg) elemMat[fOff] += tmpBasisDerI[fidx * dE + df] * g3[((fc * NcJ + gc) * dE + df) * dE + dg] * tmpBasisDerJ[gidx * dE + dg];
2848           }
2849         }
2850       }
2851     }
2852   }
2853   return PETSC_SUCCESS;
2854 }
2855 
2856 PetscErrorCode PetscFECreateCellGeometry(PetscFE fe, PetscQuadrature quad, PetscFEGeom *cgeom)
2857 {
2858   PetscDualSpace  dsp;
2859   DM              dm;
2860   PetscQuadrature quadDef;
2861   PetscInt        dim, cdim, Nq;
2862 
2863   PetscFunctionBegin;
2864   PetscCall(PetscFEGetDualSpace(fe, &dsp));
2865   PetscCall(PetscDualSpaceGetDM(dsp, &dm));
2866   PetscCall(DMGetDimension(dm, &dim));
2867   PetscCall(DMGetCoordinateDim(dm, &cdim));
2868   PetscCall(PetscFEGetQuadrature(fe, &quadDef));
2869   quad = quad ? quad : quadDef;
2870   PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
2871   PetscCall(PetscMalloc1(Nq * cdim, &cgeom->v));
2872   PetscCall(PetscMalloc1(Nq * cdim * cdim, &cgeom->J));
2873   PetscCall(PetscMalloc1(Nq * cdim * cdim, &cgeom->invJ));
2874   PetscCall(PetscMalloc1(Nq, &cgeom->detJ));
2875   cgeom->dim       = dim;
2876   cgeom->dimEmbed  = cdim;
2877   cgeom->numCells  = 1;
2878   cgeom->numPoints = Nq;
2879   PetscCall(DMPlexComputeCellGeometryFEM(dm, 0, quad, cgeom->v, cgeom->J, cgeom->invJ, cgeom->detJ));
2880   PetscFunctionReturn(PETSC_SUCCESS);
2881 }
2882 
2883 PetscErrorCode PetscFEDestroyCellGeometry(PetscFE fe, PetscFEGeom *cgeom)
2884 {
2885   PetscFunctionBegin;
2886   PetscCall(PetscFree(cgeom->v));
2887   PetscCall(PetscFree(cgeom->J));
2888   PetscCall(PetscFree(cgeom->invJ));
2889   PetscCall(PetscFree(cgeom->detJ));
2890   PetscFunctionReturn(PETSC_SUCCESS);
2891 }
2892 
2893 #if 0
2894 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)
2895 {
2896   const PetscInt dE      = dimEmbed;
2897   const PetscInt NbI     = TI->Nb;
2898   const PetscInt NcI     = TI->Nc;
2899   const PetscInt NbJ     = TJ->Nb;
2900   const PetscInt NcJ     = TJ->Nc;
2901   PetscBool      has_g0  = g0 ? PETSC_TRUE : PETSC_FALSE;
2902   PetscBool      has_g1  = g1 ? PETSC_TRUE : PETSC_FALSE;
2903   PetscBool      has_g2  = g2 ? PETSC_TRUE : PETSC_FALSE;
2904   PetscBool      has_g3  = g3 ? PETSC_TRUE : PETSC_FALSE;
2905   PetscInt      *g0_idxs = NULL, *g1_idxs = NULL, *g2_idxs = NULL, *g3_idxs = NULL;
2906   PetscInt       g0_i, g1_i, g2_i, g3_i;
2907 
2908   PetscFunctionBegin;
2909   g0_i = g1_i = g2_i = g3_i = 0;
2910   if (has_g0)
2911     for (PetscInt i = 0; i < NcI * NcJ; i++)
2912       if (g0[i]) g0_i += NbI * NbJ;
2913   if (has_g1)
2914     for (PetscInt i = 0; i < NcI * NcJ * dE; i++)
2915       if (g1[i]) g1_i += NbI * NbJ;
2916   if (has_g2)
2917     for (PetscInt i = 0; i < NcI * NcJ * dE; i++)
2918       if (g2[i]) g2_i += NbI * NbJ;
2919   if (has_g3)
2920     for (PetscInt i = 0; i < NcI * NcJ * dE * dE; i++)
2921       if (g3[i]) g3_i += NbI * NbJ;
2922   if (g0_i == NbI * NbJ * NcI * NcJ) g0_i = 0;
2923   if (g1_i == NbI * NbJ * NcI * NcJ * dE) g1_i = 0;
2924   if (g2_i == NbI * NbJ * NcI * NcJ * dE) g2_i = 0;
2925   if (g3_i == NbI * NbJ * NcI * NcJ * dE * dE) g3_i = 0;
2926   has_g0 = g0_i ? PETSC_TRUE : PETSC_FALSE;
2927   has_g1 = g1_i ? PETSC_TRUE : PETSC_FALSE;
2928   has_g2 = g2_i ? PETSC_TRUE : PETSC_FALSE;
2929   has_g3 = g3_i ? PETSC_TRUE : PETSC_FALSE;
2930   if (has_g0) PetscCall(PetscMalloc1(4 * g0_i, &g0_idxs));
2931   if (has_g1) PetscCall(PetscMalloc1(4 * g1_i, &g1_idxs));
2932   if (has_g2) PetscCall(PetscMalloc1(4 * g2_i, &g2_idxs));
2933   if (has_g3) PetscCall(PetscMalloc1(4 * g3_i, &g3_idxs));
2934   g0_i = g1_i = g2_i = g3_i = 0;
2935 
2936   for (PetscInt f = 0; f < NbI; ++f) {
2937     const PetscInt i = offsetI + f; /* Element matrix row */
2938     for (PetscInt fc = 0; fc < NcI; ++fc) {
2939       const PetscInt fidx = f * NcI + fc; /* Test function basis index */
2940 
2941       for (PetscInt g = 0; g < NbJ; ++g) {
2942         const PetscInt j    = offsetJ + g; /* Element matrix column */
2943         const PetscInt fOff = i * totDim + j;
2944         for (PetscInt gc = 0; gc < NcJ; ++gc) {
2945           const PetscInt gidx = g * NcJ + gc; /* Trial function basis index */
2946 
2947           if (has_g0) {
2948             if (g0[fc * NcJ + gc]) {
2949               g0_idxs[4 * g0_i + 0] = fidx;
2950               g0_idxs[4 * g0_i + 1] = fc * NcJ + gc;
2951               g0_idxs[4 * g0_i + 2] = gidx;
2952               g0_idxs[4 * g0_i + 3] = fOff;
2953               g0_i++;
2954             }
2955           }
2956 
2957           for (PetscInt df = 0; df < dE; ++df) {
2958             if (has_g1) {
2959               if (g1[(fc * NcJ + gc) * dE + df]) {
2960                 g1_idxs[4 * g1_i + 0] = fidx;
2961                 g1_idxs[4 * g1_i + 1] = (fc * NcJ + gc) * dE + df;
2962                 g1_idxs[4 * g1_i + 2] = gidx * dE + df;
2963                 g1_idxs[4 * g1_i + 3] = fOff;
2964                 g1_i++;
2965               }
2966             }
2967             if (has_g2) {
2968               if (g2[(fc * NcJ + gc) * dE + df]) {
2969                 g2_idxs[4 * g2_i + 0] = fidx * dE + df;
2970                 g2_idxs[4 * g2_i + 1] = (fc * NcJ + gc) * dE + df;
2971                 g2_idxs[4 * g2_i + 2] = gidx;
2972                 g2_idxs[4 * g2_i + 3] = fOff;
2973                 g2_i++;
2974               }
2975             }
2976             if (has_g3) {
2977               for (PetscInt dg = 0; dg < dE; ++dg) {
2978                 if (g3[((fc * NcJ + gc) * dE + df) * dE + dg]) {
2979                   g3_idxs[4 * g3_i + 0] = fidx * dE + df;
2980                   g3_idxs[4 * g3_i + 1] = ((fc * NcJ + gc) * dE + df) * dE + dg;
2981                   g3_idxs[4 * g3_i + 2] = gidx * dE + dg;
2982                   g3_idxs[4 * g3_i + 3] = fOff;
2983                   g3_i++;
2984                 }
2985               }
2986             }
2987           }
2988         }
2989       }
2990     }
2991   }
2992   *n_g0 = g0_i;
2993   *n_g1 = g1_i;
2994   *n_g2 = g2_i;
2995   *n_g3 = g3_i;
2996 
2997   *g0_idxs_out = g0_idxs;
2998   *g1_idxs_out = g1_idxs;
2999   *g2_idxs_out = g2_idxs;
3000   *g3_idxs_out = g3_idxs;
3001   PetscFunctionReturn(PETSC_SUCCESS);
3002 }
3003 
3004 //example HOW TO USE
3005       for (PetscInt i = 0; i < g0_sparse_n; i++) {
3006         PetscInt bM = g0_sparse_idxs[4 * i + 0];
3007         PetscInt bN = g0_sparse_idxs[4 * i + 1];
3008         PetscInt bK = g0_sparse_idxs[4 * i + 2];
3009         PetscInt bO = g0_sparse_idxs[4 * i + 3];
3010         elemMat[bO] += tmpBasisI[bM] * g0[bN] * tmpBasisJ[bK];
3011       }
3012 #endif
3013