xref: /petsc/src/dm/dt/fe/interface/fe.c (revision bcee047adeeb73090d7e36cc71e39fc287cdbb97)
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
62 
63   Input Parameters:
64 + sname - The name of a new user-defined creation routine
65 - function - The creation routine
66 
67   Sample 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 /*@C
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 /*@C
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   PetscValidPointer(name, 2);
154   if (!PetscFERegisterAllCalled) PetscCall(PetscFERegisterAll());
155   *name = ((PetscObject)fem)->type_name;
156   PetscFunctionReturn(PETSC_SUCCESS);
157 }
158 
159 /*@C
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
167 -  name - command line option name
168 
169    Level: intermediate
170 
171 .seealso: `PetscFE`, `PetscFEView()`, `PetscObjectViewFromOptions()`, `PetscFECreate()`
172 @*/
173 PetscErrorCode PetscFEViewFromOptions(PetscFE A, 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 /*@C
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: `PetscFEV`, `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 /*@C
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   PetscValidPointer(fem, 2);
350   PetscCall(PetscCitationsRegister(FECitation, &FEcite));
351   *fem = NULL;
352   PetscCall(PetscFEInitializePackage());
353 
354   PetscCall(PetscHeaderCreate(f, PETSCFE_CLASSID, "PetscFE", "Finite Element", "PetscFE", comm, PetscFEDestroy, PetscFEView));
355 
356   f->basisSpace    = NULL;
357   f->dualSpace     = NULL;
358   f->numComponents = 1;
359   f->subspaces     = NULL;
360   f->invV          = NULL;
361   f->T             = NULL;
362   f->Tf            = NULL;
363   f->Tc            = NULL;
364   PetscCall(PetscArrayzero(&f->quadrature, 1));
365   PetscCall(PetscArrayzero(&f->faceQuadrature, 1));
366   f->blockSize  = 0;
367   f->numBlocks  = 1;
368   f->batchSize  = 0;
369   f->numBatches = 1;
370 
371   *fem = f;
372   PetscFunctionReturn(PETSC_SUCCESS);
373 }
374 
375 /*@
376   PetscFEGetSpatialDimension - Returns the spatial dimension of the element
377 
378   Not Collective
379 
380   Input Parameter:
381 . fem - The `PetscFE` object
382 
383   Output Parameter:
384 . dim - The spatial dimension
385 
386   Level: intermediate
387 
388 .seealso: `PetscFE`, `PetscFECreate()`
389 @*/
390 PetscErrorCode PetscFEGetSpatialDimension(PetscFE fem, PetscInt *dim)
391 {
392   DM dm;
393 
394   PetscFunctionBegin;
395   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
396   PetscValidIntPointer(dim, 2);
397   PetscCall(PetscDualSpaceGetDM(fem->dualSpace, &dm));
398   PetscCall(DMGetDimension(dm, dim));
399   PetscFunctionReturn(PETSC_SUCCESS);
400 }
401 
402 /*@
403   PetscFESetNumComponents - Sets the number of field components in the element
404 
405   Not Collective
406 
407   Input Parameters:
408 + fem - The `PetscFE` object
409 - comp - The number of field components
410 
411   Level: intermediate
412 
413 .seealso: `PetscFE`, `PetscFECreate()`, `PetscFEGetSpatialDimension()`, `PetscFEGetNumComponents()`
414 @*/
415 PetscErrorCode PetscFESetNumComponents(PetscFE fem, PetscInt comp)
416 {
417   PetscFunctionBegin;
418   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
419   fem->numComponents = comp;
420   PetscFunctionReturn(PETSC_SUCCESS);
421 }
422 
423 /*@
424   PetscFEGetNumComponents - Returns the number of components in the element
425 
426   Not Collective
427 
428   Input Parameter:
429 . fem - The `PetscFE` object
430 
431   Output Parameter:
432 . comp - The number of field components
433 
434   Level: intermediate
435 
436 .seealso: `PetscFE`, `PetscFECreate()`, `PetscFEGetSpatialDimension()`, `PetscFEGetNumComponents()`
437 @*/
438 PetscErrorCode PetscFEGetNumComponents(PetscFE fem, PetscInt *comp)
439 {
440   PetscFunctionBegin;
441   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
442   PetscValidIntPointer(comp, 2);
443   *comp = fem->numComponents;
444   PetscFunctionReturn(PETSC_SUCCESS);
445 }
446 
447 /*@
448   PetscFESetTileSizes - Sets the tile sizes for evaluation
449 
450   Not Collective
451 
452   Input Parameters:
453 + fem - The `PetscFE` object
454 . blockSize - The number of elements in a block
455 . numBlocks - The number of blocks in a batch
456 . batchSize - The number of elements in a batch
457 - numBatches - The number of batches in a chunk
458 
459   Level: intermediate
460 
461 .seealso: `PetscFE`, `PetscFECreate()`, `PetscFEGetTileSizes()`
462 @*/
463 PetscErrorCode PetscFESetTileSizes(PetscFE fem, PetscInt blockSize, PetscInt numBlocks, PetscInt batchSize, PetscInt numBatches)
464 {
465   PetscFunctionBegin;
466   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
467   fem->blockSize  = blockSize;
468   fem->numBlocks  = numBlocks;
469   fem->batchSize  = batchSize;
470   fem->numBatches = numBatches;
471   PetscFunctionReturn(PETSC_SUCCESS);
472 }
473 
474 /*@
475   PetscFEGetTileSizes - Returns the tile sizes for evaluation
476 
477   Not Collective
478 
479   Input Parameter:
480 . fem - The `PetscFE` object
481 
482   Output Parameters:
483 + blockSize - The number of elements in a block
484 . numBlocks - The number of blocks in a batch
485 . batchSize - The number of elements in a batch
486 - numBatches - The number of batches in a chunk
487 
488   Level: intermediate
489 
490 .seealso: `PetscFE`, `PetscFECreate()`, `PetscFESetTileSizes()`
491 @*/
492 PetscErrorCode PetscFEGetTileSizes(PetscFE fem, PetscInt *blockSize, PetscInt *numBlocks, PetscInt *batchSize, PetscInt *numBatches)
493 {
494   PetscFunctionBegin;
495   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
496   if (blockSize) PetscValidIntPointer(blockSize, 2);
497   if (numBlocks) PetscValidIntPointer(numBlocks, 3);
498   if (batchSize) PetscValidIntPointer(batchSize, 4);
499   if (numBatches) PetscValidIntPointer(numBatches, 5);
500   if (blockSize) *blockSize = fem->blockSize;
501   if (numBlocks) *numBlocks = fem->numBlocks;
502   if (batchSize) *batchSize = fem->batchSize;
503   if (numBatches) *numBatches = fem->numBatches;
504   PetscFunctionReturn(PETSC_SUCCESS);
505 }
506 
507 /*@
508   PetscFEGetBasisSpace - Returns the `PetscSpace` used for the approximation of the solution for the `PetscFE`
509 
510   Not Collective
511 
512   Input Parameter:
513 . fem - The `PetscFE` object
514 
515   Output Parameter:
516 . sp - The `PetscSpace` object
517 
518   Level: intermediate
519 
520 .seealso: `PetscFE`, `PetscSpace`, `PetscFECreate()`
521 @*/
522 PetscErrorCode PetscFEGetBasisSpace(PetscFE fem, PetscSpace *sp)
523 {
524   PetscFunctionBegin;
525   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
526   PetscValidPointer(sp, 2);
527   *sp = fem->basisSpace;
528   PetscFunctionReturn(PETSC_SUCCESS);
529 }
530 
531 /*@
532   PetscFESetBasisSpace - Sets the `PetscSpace` used for the approximation of the solution
533 
534   Not Collective
535 
536   Input Parameters:
537 + fem - The `PetscFE` object
538 - sp - The `PetscSpace` object
539 
540   Level: intermediate
541 
542   Developer Note:
543   There is `PetscFESetBasisSpace()` but the `PetscFESetDualSpace()`, likely the Basis is unneeded in the function name
544 
545 .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscFECreate()`, `PetscFESetDualSpace()`
546 @*/
547 PetscErrorCode PetscFESetBasisSpace(PetscFE fem, PetscSpace sp)
548 {
549   PetscFunctionBegin;
550   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
551   PetscValidHeaderSpecific(sp, PETSCSPACE_CLASSID, 2);
552   PetscCall(PetscSpaceDestroy(&fem->basisSpace));
553   fem->basisSpace = sp;
554   PetscCall(PetscObjectReference((PetscObject)fem->basisSpace));
555   PetscFunctionReturn(PETSC_SUCCESS);
556 }
557 
558 /*@
559   PetscFEGetDualSpace - Returns the `PetscDualSpace` used to define the inner product for a `PetscFE`
560 
561   Not Collective
562 
563   Input Parameter:
564 . fem - The `PetscFE` object
565 
566   Output Parameter:
567 . sp - The `PetscDualSpace` object
568 
569   Level: intermediate
570 
571 .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscFECreate()`
572 @*/
573 PetscErrorCode PetscFEGetDualSpace(PetscFE fem, PetscDualSpace *sp)
574 {
575   PetscFunctionBegin;
576   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
577   PetscValidPointer(sp, 2);
578   *sp = fem->dualSpace;
579   PetscFunctionReturn(PETSC_SUCCESS);
580 }
581 
582 /*@
583   PetscFESetDualSpace - Sets the `PetscDualSpace` used to define the inner product
584 
585   Not Collective
586 
587   Input Parameters:
588 + fem - The `PetscFE` object
589 - sp - The `PetscDualSpace` object
590 
591   Level: intermediate
592 
593 .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscFECreate()`, `PetscFESetBasisSpace()`
594 @*/
595 PetscErrorCode PetscFESetDualSpace(PetscFE fem, PetscDualSpace sp)
596 {
597   PetscFunctionBegin;
598   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
599   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 2);
600   PetscCall(PetscDualSpaceDestroy(&fem->dualSpace));
601   fem->dualSpace = sp;
602   PetscCall(PetscObjectReference((PetscObject)fem->dualSpace));
603   PetscFunctionReturn(PETSC_SUCCESS);
604 }
605 
606 /*@
607   PetscFEGetQuadrature - Returns the `PetscQuadrature` used to calculate inner products
608 
609   Not Collective
610 
611   Input Parameter:
612 . fem - The `PetscFE` object
613 
614   Output Parameter:
615 . q - The `PetscQuadrature` object
616 
617   Level: intermediate
618 
619 .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscQuadrature`, `PetscFECreate()`
620 @*/
621 PetscErrorCode PetscFEGetQuadrature(PetscFE fem, PetscQuadrature *q)
622 {
623   PetscFunctionBegin;
624   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
625   PetscValidPointer(q, 2);
626   *q = fem->quadrature;
627   PetscFunctionReturn(PETSC_SUCCESS);
628 }
629 
630 /*@
631   PetscFESetQuadrature - Sets the `PetscQuadrature` used to calculate inner products
632 
633   Not Collective
634 
635   Input Parameters:
636 + fem - The `PetscFE` object
637 - q - The `PetscQuadrature` object
638 
639   Level: intermediate
640 
641 .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscQuadrature`, `PetscFECreate()`, `PetscFEGetFaceQuadrature()`
642 @*/
643 PetscErrorCode PetscFESetQuadrature(PetscFE fem, PetscQuadrature q)
644 {
645   PetscInt Nc, qNc;
646 
647   PetscFunctionBegin;
648   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
649   if (q == fem->quadrature) PetscFunctionReturn(PETSC_SUCCESS);
650   PetscCall(PetscFEGetNumComponents(fem, &Nc));
651   PetscCall(PetscQuadratureGetNumComponents(q, &qNc));
652   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);
653   PetscCall(PetscTabulationDestroy(&fem->T));
654   PetscCall(PetscTabulationDestroy(&fem->Tc));
655   PetscCall(PetscObjectReference((PetscObject)q));
656   PetscCall(PetscQuadratureDestroy(&fem->quadrature));
657   fem->quadrature = q;
658   PetscFunctionReturn(PETSC_SUCCESS);
659 }
660 
661 /*@
662   PetscFEGetFaceQuadrature - Returns the `PetscQuadrature` used to calculate inner products on faces
663 
664   Not Collective
665 
666   Input Parameter:
667 . fem - The `PetscFE` object
668 
669   Output Parameter:
670 . q - The `PetscQuadrature` object
671 
672   Level: intermediate
673 
674   Developer Note:
675   There is a special face quadrature but not edge, likely this API would benefit from a refactorization
676 
677 .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscQuadrature`, `PetscFECreate()`, `PetscFESetQuadrature()`, `PetscFESetFaceQuadrature()`
678 @*/
679 PetscErrorCode PetscFEGetFaceQuadrature(PetscFE fem, PetscQuadrature *q)
680 {
681   PetscFunctionBegin;
682   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
683   PetscValidPointer(q, 2);
684   *q = fem->faceQuadrature;
685   PetscFunctionReturn(PETSC_SUCCESS);
686 }
687 
688 /*@
689   PetscFESetFaceQuadrature - Sets the `PetscQuadrature` used to calculate inner products on faces
690 
691   Not Collective
692 
693   Input Parameters:
694 + fem - The `PetscFE` object
695 - q - The `PetscQuadrature` object
696 
697   Level: intermediate
698 
699 .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscQuadrature`, `PetscFECreate()`, `PetscFESetQuadrature()`, `PetscFESetFaceQuadrature()`
700 @*/
701 PetscErrorCode PetscFESetFaceQuadrature(PetscFE fem, PetscQuadrature q)
702 {
703   PetscInt Nc, qNc;
704 
705   PetscFunctionBegin;
706   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
707   if (q == fem->faceQuadrature) PetscFunctionReturn(PETSC_SUCCESS);
708   PetscCall(PetscFEGetNumComponents(fem, &Nc));
709   PetscCall(PetscQuadratureGetNumComponents(q, &qNc));
710   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);
711   PetscCall(PetscTabulationDestroy(&fem->Tf));
712   PetscCall(PetscObjectReference((PetscObject)q));
713   PetscCall(PetscQuadratureDestroy(&fem->faceQuadrature));
714   fem->faceQuadrature = q;
715   PetscFunctionReturn(PETSC_SUCCESS);
716 }
717 
718 /*@
719   PetscFECopyQuadrature - Copy both volumetric and surface quadrature to a new `PetscFE`
720 
721   Not Collective
722 
723   Input Parameters:
724 + sfe - The `PetscFE` source for the quadratures
725 - tfe - The `PetscFE` target for the quadratures
726 
727   Level: intermediate
728 
729 .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscQuadrature`, `PetscFECreate()`, `PetscFESetQuadrature()`, `PetscFESetFaceQuadrature()`
730 @*/
731 PetscErrorCode PetscFECopyQuadrature(PetscFE sfe, PetscFE tfe)
732 {
733   PetscQuadrature q;
734 
735   PetscFunctionBegin;
736   PetscValidHeaderSpecific(sfe, PETSCFE_CLASSID, 1);
737   PetscValidHeaderSpecific(tfe, PETSCFE_CLASSID, 2);
738   PetscCall(PetscFEGetQuadrature(sfe, &q));
739   PetscCall(PetscFESetQuadrature(tfe, q));
740   PetscCall(PetscFEGetFaceQuadrature(sfe, &q));
741   PetscCall(PetscFESetFaceQuadrature(tfe, q));
742   PetscFunctionReturn(PETSC_SUCCESS);
743 }
744 
745 /*@C
746   PetscFEGetNumDof - Returns the number of dofs (dual basis vectors) associated to mesh points on the reference cell of a given dimension
747 
748   Not Collective
749 
750   Input Parameter:
751 . fem - The `PetscFE` object
752 
753   Output Parameter:
754 . numDof - Array with the number of dofs per dimension
755 
756   Level: intermediate
757 
758 .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscFECreate()`
759 @*/
760 PetscErrorCode PetscFEGetNumDof(PetscFE fem, const PetscInt **numDof)
761 {
762   PetscFunctionBegin;
763   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
764   PetscValidPointer(numDof, 2);
765   PetscCall(PetscDualSpaceGetNumDof(fem->dualSpace, numDof));
766   PetscFunctionReturn(PETSC_SUCCESS);
767 }
768 
769 /*@C
770   PetscFEGetCellTabulation - Returns the tabulation of the basis functions at the quadrature points on the reference cell
771 
772   Not Collective
773 
774   Input Parameters:
775 + fem - The `PetscFE` object
776 - k   - The highest derivative we need to tabulate, very often 1
777 
778   Output Parameter:
779 . T - The basis function values and derivatives at quadrature points
780 
781   Level: intermediate
782 
783   Note:
784 .vb
785   T->T[0] = B[(p*pdim + i)*Nc + c] is the value at point p for basis function i and component c
786   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
787   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
788 .ve
789 
790 .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscTabulation`, `PetscFECreateTabulation()`, `PetscTabulationDestroy()`
791 @*/
792 PetscErrorCode PetscFEGetCellTabulation(PetscFE fem, PetscInt k, PetscTabulation *T)
793 {
794   PetscInt         npoints;
795   const PetscReal *points;
796 
797   PetscFunctionBegin;
798   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
799   PetscValidPointer(T, 3);
800   PetscCall(PetscQuadratureGetData(fem->quadrature, NULL, NULL, &npoints, &points, NULL));
801   if (!fem->T) PetscCall(PetscFECreateTabulation(fem, 1, npoints, points, k, &fem->T));
802   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);
803   *T = fem->T;
804   PetscFunctionReturn(PETSC_SUCCESS);
805 }
806 
807 /*@C
808   PetscFEGetFaceTabulation - Returns the tabulation of the basis functions at the face quadrature points for each face of the reference cell
809 
810   Not Collective
811 
812   Input Parameters:
813 + fem - The `PetscFE` object
814 - k   - The highest derivative we need to tabulate, very often 1
815 
816   Output Parameter:
817 . Tf - The basis function values and derivatives at face quadrature points
818 
819   Level: intermediate
820 
821   Note:
822 .vb
823   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
824   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
825   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
826 .ve
827 
828 .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscTabulation`, `PetscFEGetCellTabulation()`, `PetscFECreateTabulation()`, `PetscTabulationDestroy()`
829 @*/
830 PetscErrorCode PetscFEGetFaceTabulation(PetscFE fem, PetscInt k, PetscTabulation *Tf)
831 {
832   PetscFunctionBegin;
833   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
834   PetscValidPointer(Tf, 3);
835   if (!fem->Tf) {
836     const PetscReal  xi0[3] = {-1., -1., -1.};
837     PetscReal        v0[3], J[9], detJ;
838     PetscQuadrature  fq;
839     PetscDualSpace   sp;
840     DM               dm;
841     const PetscInt  *faces;
842     PetscInt         dim, numFaces, f, npoints, q;
843     const PetscReal *points;
844     PetscReal       *facePoints;
845 
846     PetscCall(PetscFEGetDualSpace(fem, &sp));
847     PetscCall(PetscDualSpaceGetDM(sp, &dm));
848     PetscCall(DMGetDimension(dm, &dim));
849     PetscCall(DMPlexGetConeSize(dm, 0, &numFaces));
850     PetscCall(DMPlexGetCone(dm, 0, &faces));
851     PetscCall(PetscFEGetFaceQuadrature(fem, &fq));
852     if (fq) {
853       PetscCall(PetscQuadratureGetData(fq, NULL, NULL, &npoints, &points, NULL));
854       PetscCall(PetscMalloc1(numFaces * npoints * dim, &facePoints));
855       for (f = 0; f < numFaces; ++f) {
856         PetscCall(DMPlexComputeCellGeometryFEM(dm, faces[f], NULL, v0, J, NULL, &detJ));
857         for (q = 0; q < npoints; ++q) CoordinatesRefToReal(dim, dim - 1, xi0, v0, J, &points[q * (dim - 1)], &facePoints[(f * npoints + q) * dim]);
858       }
859       PetscCall(PetscFECreateTabulation(fem, numFaces, npoints, facePoints, k, &fem->Tf));
860       PetscCall(PetscFree(facePoints));
861     }
862   }
863   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);
864   *Tf = fem->Tf;
865   PetscFunctionReturn(PETSC_SUCCESS);
866 }
867 
868 /*@C
869   PetscFEGetFaceCentroidTabulation - Returns the tabulation of the basis functions at the face centroid points
870 
871   Not Collective
872 
873   Input Parameter:
874 . fem - The `PetscFE` object
875 
876   Output Parameter:
877 . Tc - The basis function values at face centroid points
878 
879   Level: intermediate
880 
881   Note:
882 .vb
883   T->T[0] = Bf[(f*pdim + i)*Nc + c] is the value at point f for basis function i and component c
884 .ve
885 
886 .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscTabulation`, `PetscFEGetFaceTabulation()`, `PetscFEGetCellTabulation()`, `PetscFECreateTabulation()`, `PetscTabulationDestroy()`
887 @*/
888 PetscErrorCode PetscFEGetFaceCentroidTabulation(PetscFE fem, PetscTabulation *Tc)
889 {
890   PetscFunctionBegin;
891   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
892   PetscValidPointer(Tc, 2);
893   if (!fem->Tc) {
894     PetscDualSpace  sp;
895     DM              dm;
896     const PetscInt *cone;
897     PetscReal      *centroids;
898     PetscInt        dim, numFaces, f;
899 
900     PetscCall(PetscFEGetDualSpace(fem, &sp));
901     PetscCall(PetscDualSpaceGetDM(sp, &dm));
902     PetscCall(DMGetDimension(dm, &dim));
903     PetscCall(DMPlexGetConeSize(dm, 0, &numFaces));
904     PetscCall(DMPlexGetCone(dm, 0, &cone));
905     PetscCall(PetscMalloc1(numFaces * dim, &centroids));
906     for (f = 0; f < numFaces; ++f) PetscCall(DMPlexComputeCellGeometryFVM(dm, cone[f], NULL, &centroids[f * dim], NULL));
907     PetscCall(PetscFECreateTabulation(fem, 1, numFaces, centroids, 0, &fem->Tc));
908     PetscCall(PetscFree(centroids));
909   }
910   *Tc = fem->Tc;
911   PetscFunctionReturn(PETSC_SUCCESS);
912 }
913 
914 /*@C
915   PetscFECreateTabulation - Tabulates the basis functions, and perhaps derivatives, at the points provided.
916 
917   Not Collective
918 
919   Input Parameters:
920 + fem     - The `PetscFE` object
921 . nrepl   - The number of replicas
922 . npoints - The number of tabulation points in a replica
923 . points  - The tabulation point coordinates
924 - K       - The number of derivatives calculated
925 
926   Output Parameter:
927 . T - The basis function values and derivatives at tabulation points
928 
929   Level: intermediate
930 
931   Note:
932 .vb
933   T->T[0] = B[(p*pdim + i)*Nc + c] is the value at point p for basis function i and component c
934   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
935   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
936 
937 .seealso: `PetscTabulation`, `PetscFEGetCellTabulation()`, `PetscTabulationDestroy()`
938 @*/
939 PetscErrorCode PetscFECreateTabulation(PetscFE fem, PetscInt nrepl, PetscInt npoints, const PetscReal points[], PetscInt K, PetscTabulation *T)
940 {
941   DM             dm;
942   PetscDualSpace Q;
943   PetscInt       Nb;   /* Dimension of FE space P */
944   PetscInt       Nc;   /* Field components */
945   PetscInt       cdim; /* Reference coordinate dimension */
946   PetscInt       k;
947 
948   PetscFunctionBegin;
949   if (!npoints || !fem->dualSpace || K < 0) {
950     *T = NULL;
951     PetscFunctionReturn(PETSC_SUCCESS);
952   }
953   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
954   PetscValidRealPointer(points, 4);
955   PetscValidPointer(T, 6);
956   PetscCall(PetscFEGetDualSpace(fem, &Q));
957   PetscCall(PetscDualSpaceGetDM(Q, &dm));
958   PetscCall(DMGetDimension(dm, &cdim));
959   PetscCall(PetscDualSpaceGetDimension(Q, &Nb));
960   PetscCall(PetscFEGetNumComponents(fem, &Nc));
961   PetscCall(PetscMalloc1(1, T));
962   (*T)->K    = !cdim ? 0 : K;
963   (*T)->Nr   = nrepl;
964   (*T)->Np   = npoints;
965   (*T)->Nb   = Nb;
966   (*T)->Nc   = Nc;
967   (*T)->cdim = cdim;
968   PetscCall(PetscMalloc1((*T)->K + 1, &(*T)->T));
969   for (k = 0; k <= (*T)->K; ++k) PetscCall(PetscMalloc1(nrepl * npoints * Nb * Nc * PetscPowInt(cdim, k), &(*T)->T[k]));
970   PetscUseTypeMethod(fem, createtabulation, nrepl * npoints, points, K, *T);
971   PetscFunctionReturn(PETSC_SUCCESS);
972 }
973 
974 /*@C
975   PetscFEComputeTabulation - Tabulates the basis functions, and perhaps derivatives, at the points provided.
976 
977   Not Collective
978 
979   Input Parameters:
980 + fem     - The `PetscFE` object
981 . npoints - The number of tabulation points
982 . points  - The tabulation point coordinates
983 . K       - The number of derivatives calculated
984 - T       - An existing tabulation object with enough allocated space
985 
986   Output Parameter:
987 . T - The basis function values and derivatives at tabulation points
988 
989   Level: intermediate
990 
991   Note:
992 .vb
993   T->T[0] = B[(p*pdim + i)*Nc + c] is the value at point p for basis function i and component c
994   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
995   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
996 .ve
997 
998 .seealso: `PetscTabulation`, `PetscFEGetCellTabulation()`, `PetscTabulationDestroy()`
999 @*/
1000 PetscErrorCode PetscFEComputeTabulation(PetscFE fem, PetscInt npoints, const PetscReal points[], PetscInt K, PetscTabulation T)
1001 {
1002   PetscFunctionBeginHot;
1003   if (!npoints || !fem->dualSpace || K < 0) PetscFunctionReturn(PETSC_SUCCESS);
1004   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
1005   PetscValidRealPointer(points, 3);
1006   PetscValidPointer(T, 5);
1007   if (PetscDefined(USE_DEBUG)) {
1008     DM             dm;
1009     PetscDualSpace Q;
1010     PetscInt       Nb;   /* Dimension of FE space P */
1011     PetscInt       Nc;   /* Field components */
1012     PetscInt       cdim; /* Reference coordinate dimension */
1013 
1014     PetscCall(PetscFEGetDualSpace(fem, &Q));
1015     PetscCall(PetscDualSpaceGetDM(Q, &dm));
1016     PetscCall(DMGetDimension(dm, &cdim));
1017     PetscCall(PetscDualSpaceGetDimension(Q, &Nb));
1018     PetscCall(PetscFEGetNumComponents(fem, &Nc));
1019     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);
1020     PetscCheck(T->Nb == Nb, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Tabulation Nb %" PetscInt_FMT " must match requested Nb %" PetscInt_FMT, T->Nb, Nb);
1021     PetscCheck(T->Nc == Nc, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Tabulation Nc %" PetscInt_FMT " must match requested Nc %" PetscInt_FMT, T->Nc, Nc);
1022     PetscCheck(T->cdim == cdim, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Tabulation cdim %" PetscInt_FMT " must match requested cdim %" PetscInt_FMT, T->cdim, cdim);
1023   }
1024   T->Nr = 1;
1025   T->Np = npoints;
1026   PetscUseTypeMethod(fem, createtabulation, npoints, points, K, T);
1027   PetscFunctionReturn(PETSC_SUCCESS);
1028 }
1029 
1030 /*@C
1031   PetscTabulationDestroy - Frees memory from the associated tabulation.
1032 
1033   Not Collective
1034 
1035   Input Parameter:
1036 . T - The tabulation
1037 
1038   Level: intermediate
1039 
1040 .seealso: `PetscTabulation`, `PetscFECreateTabulation()`, `PetscFEGetCellTabulation()`
1041 @*/
1042 PetscErrorCode PetscTabulationDestroy(PetscTabulation *T)
1043 {
1044   PetscInt k;
1045 
1046   PetscFunctionBegin;
1047   PetscValidPointer(T, 1);
1048   if (!T || !(*T)) PetscFunctionReturn(PETSC_SUCCESS);
1049   for (k = 0; k <= (*T)->K; ++k) PetscCall(PetscFree((*T)->T[k]));
1050   PetscCall(PetscFree((*T)->T));
1051   PetscCall(PetscFree(*T));
1052   *T = NULL;
1053   PetscFunctionReturn(PETSC_SUCCESS);
1054 }
1055 
1056 PETSC_EXTERN PetscErrorCode PetscFECreatePointTrace(PetscFE fe, PetscInt refPoint, PetscFE *trFE)
1057 {
1058   PetscSpace      bsp, bsubsp;
1059   PetscDualSpace  dsp, dsubsp;
1060   PetscInt        dim, depth, numComp, i, j, coneSize, order;
1061   PetscFEType     type;
1062   DM              dm;
1063   DMLabel         label;
1064   PetscReal      *xi, *v, *J, detJ;
1065   const char     *name;
1066   PetscQuadrature origin, fullQuad, subQuad;
1067 
1068   PetscFunctionBegin;
1069   PetscValidHeaderSpecific(fe, PETSCFE_CLASSID, 1);
1070   PetscValidPointer(trFE, 3);
1071   PetscCall(PetscFEGetBasisSpace(fe, &bsp));
1072   PetscCall(PetscFEGetDualSpace(fe, &dsp));
1073   PetscCall(PetscDualSpaceGetDM(dsp, &dm));
1074   PetscCall(DMGetDimension(dm, &dim));
1075   PetscCall(DMPlexGetDepthLabel(dm, &label));
1076   PetscCall(DMLabelGetValue(label, refPoint, &depth));
1077   PetscCall(PetscCalloc1(depth, &xi));
1078   PetscCall(PetscMalloc1(dim, &v));
1079   PetscCall(PetscMalloc1(dim * dim, &J));
1080   for (i = 0; i < depth; i++) xi[i] = 0.;
1081   PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, &origin));
1082   PetscCall(PetscQuadratureSetData(origin, depth, 0, 1, xi, NULL));
1083   PetscCall(DMPlexComputeCellGeometryFEM(dm, refPoint, origin, v, J, NULL, &detJ));
1084   /* CellGeometryFEM computes the expanded Jacobian, we want the true jacobian */
1085   for (i = 1; i < dim; i++) {
1086     for (j = 0; j < depth; j++) J[i * depth + j] = J[i * dim + j];
1087   }
1088   PetscCall(PetscQuadratureDestroy(&origin));
1089   PetscCall(PetscDualSpaceGetPointSubspace(dsp, refPoint, &dsubsp));
1090   PetscCall(PetscSpaceCreateSubspace(bsp, dsubsp, v, J, NULL, NULL, PETSC_OWN_POINTER, &bsubsp));
1091   PetscCall(PetscSpaceSetUp(bsubsp));
1092   PetscCall(PetscFECreate(PetscObjectComm((PetscObject)fe), trFE));
1093   PetscCall(PetscFEGetType(fe, &type));
1094   PetscCall(PetscFESetType(*trFE, type));
1095   PetscCall(PetscFEGetNumComponents(fe, &numComp));
1096   PetscCall(PetscFESetNumComponents(*trFE, numComp));
1097   PetscCall(PetscFESetBasisSpace(*trFE, bsubsp));
1098   PetscCall(PetscFESetDualSpace(*trFE, dsubsp));
1099   PetscCall(PetscObjectGetName((PetscObject)fe, &name));
1100   if (name) PetscCall(PetscFESetName(*trFE, name));
1101   PetscCall(PetscFEGetQuadrature(fe, &fullQuad));
1102   PetscCall(PetscQuadratureGetOrder(fullQuad, &order));
1103   PetscCall(DMPlexGetConeSize(dm, refPoint, &coneSize));
1104   if (coneSize == 2 * depth) PetscCall(PetscDTGaussTensorQuadrature(depth, 1, (order + 2) / 2, -1., 1., &subQuad));
1105   else PetscCall(PetscDTSimplexQuadrature(depth, order, PETSCDTSIMPLEXQUAD_DEFAULT, &subQuad));
1106   PetscCall(PetscFESetQuadrature(*trFE, subQuad));
1107   PetscCall(PetscFESetUp(*trFE));
1108   PetscCall(PetscQuadratureDestroy(&subQuad));
1109   PetscCall(PetscSpaceDestroy(&bsubsp));
1110   PetscFunctionReturn(PETSC_SUCCESS);
1111 }
1112 
1113 PetscErrorCode PetscFECreateHeightTrace(PetscFE fe, PetscInt height, PetscFE *trFE)
1114 {
1115   PetscInt       hStart, hEnd;
1116   PetscDualSpace dsp;
1117   DM             dm;
1118 
1119   PetscFunctionBegin;
1120   PetscValidHeaderSpecific(fe, PETSCFE_CLASSID, 1);
1121   PetscValidPointer(trFE, 3);
1122   *trFE = NULL;
1123   PetscCall(PetscFEGetDualSpace(fe, &dsp));
1124   PetscCall(PetscDualSpaceGetDM(dsp, &dm));
1125   PetscCall(DMPlexGetHeightStratum(dm, height, &hStart, &hEnd));
1126   if (hEnd <= hStart) PetscFunctionReturn(PETSC_SUCCESS);
1127   PetscCall(PetscFECreatePointTrace(fe, hStart, trFE));
1128   PetscFunctionReturn(PETSC_SUCCESS);
1129 }
1130 
1131 /*@
1132   PetscFEGetDimension - Get the dimension of the finite element space on a cell
1133 
1134   Not Collective
1135 
1136   Input Parameter:
1137 . fe - The `PetscFE`
1138 
1139   Output Parameter:
1140 . dim - The dimension
1141 
1142   Level: intermediate
1143 
1144 .seealso: `PetscFE`, `PetscFECreate()`, `PetscSpaceGetDimension()`, `PetscDualSpaceGetDimension()`
1145 @*/
1146 PetscErrorCode PetscFEGetDimension(PetscFE fem, PetscInt *dim)
1147 {
1148   PetscFunctionBegin;
1149   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
1150   PetscValidIntPointer(dim, 2);
1151   PetscTryTypeMethod(fem, getdimension, dim);
1152   PetscFunctionReturn(PETSC_SUCCESS);
1153 }
1154 
1155 /*@C
1156   PetscFEPushforward - Map the reference element function to real space
1157 
1158   Input Parameters:
1159 + fe     - The `PetscFE`
1160 . fegeom - The cell geometry
1161 . Nv     - The number of function values
1162 - vals   - The function values
1163 
1164   Output Parameter:
1165 . vals   - The transformed function values
1166 
1167   Level: advanced
1168 
1169   Notes:
1170   This just forwards the call onto `PetscDualSpacePushforward()`.
1171 
1172   It only handles transformations when the embedding dimension of the geometry in fegeom is the same as the reference dimension.
1173 
1174 .seealso: `PetscFE`, `PetscFEGeom`, `PetscDualSpace`, `PetscDualSpacePushforward()`
1175 @*/
1176 PetscErrorCode PetscFEPushforward(PetscFE fe, PetscFEGeom *fegeom, PetscInt Nv, PetscScalar vals[])
1177 {
1178   PetscFunctionBeginHot;
1179   PetscCall(PetscDualSpacePushforward(fe->dualSpace, fegeom, Nv, fe->numComponents, vals));
1180   PetscFunctionReturn(PETSC_SUCCESS);
1181 }
1182 
1183 /*@C
1184   PetscFEPushforwardGradient - Map the reference element function gradient to real space
1185 
1186   Input Parameters:
1187 + fe     - The `PetscFE`
1188 . fegeom - The cell geometry
1189 . Nv     - The number of function gradient values
1190 - vals   - The function gradient values
1191 
1192   Output Parameter:
1193 . vals   - The transformed function gradient values
1194 
1195   Level: advanced
1196 
1197   Notes:
1198   This just forwards the call onto `PetscDualSpacePushforwardGradient()`.
1199 
1200   It only handles transformations when the embedding dimension of the geometry in fegeom is the same as the reference dimension.
1201 
1202 .seealso: `PetscFE`, `PetscFEGeom`, `PetscDualSpace`, `PetscFEPushforward()`, `PetscDualSpacePushforwardGradient()`, `PetscDualSpacePushforward()`
1203 @*/
1204 PetscErrorCode PetscFEPushforwardGradient(PetscFE fe, PetscFEGeom *fegeom, PetscInt Nv, PetscScalar vals[])
1205 {
1206   PetscFunctionBeginHot;
1207   PetscCall(PetscDualSpacePushforwardGradient(fe->dualSpace, fegeom, Nv, fe->numComponents, vals));
1208   PetscFunctionReturn(PETSC_SUCCESS);
1209 }
1210 
1211 /*@C
1212   PetscFEPushforwardHessian - Map the reference element function Hessian to real space
1213 
1214   Input Parameters:
1215 + fe     - The `PetscFE`
1216 . fegeom - The cell geometry
1217 . Nv     - The number of function Hessian values
1218 - vals   - The function Hessian values
1219 
1220   Output Parameter:
1221 . vals   - The transformed function Hessian values
1222 
1223   Level: advanced
1224 
1225   Notes:
1226   This just forwards the call onto `PetscDualSpacePushforwardHessian()`.
1227 
1228   It only handles transformations when the embedding dimension of the geometry in fegeom is the same as the reference dimension.
1229 
1230   Developer Note:
1231   It is unclear why all these one line convenience routines are desirable
1232 
1233 .seealso: `PetscFE`, `PetscFEGeom`, `PetscDualSpace`, `PetscFEPushforward()`, `PetscDualSpacePushforwardHessian()`, `PetscDualSpacePushforward()`
1234 @*/
1235 PetscErrorCode PetscFEPushforwardHessian(PetscFE fe, PetscFEGeom *fegeom, PetscInt Nv, PetscScalar vals[])
1236 {
1237   PetscFunctionBeginHot;
1238   PetscCall(PetscDualSpacePushforwardHessian(fe->dualSpace, fegeom, Nv, fe->numComponents, vals));
1239   PetscFunctionReturn(PETSC_SUCCESS);
1240 }
1241 
1242 /*
1243 Purpose: Compute element vector for chunk of elements
1244 
1245 Input:
1246   Sizes:
1247      Ne:  number of elements
1248      Nf:  number of fields
1249      PetscFE
1250        dim: spatial dimension
1251        Nb:  number of basis functions
1252        Nc:  number of field components
1253        PetscQuadrature
1254          Nq:  number of quadrature points
1255 
1256   Geometry:
1257      PetscFEGeom[Ne] possibly *Nq
1258        PetscReal v0s[dim]
1259        PetscReal n[dim]
1260        PetscReal jacobians[dim*dim]
1261        PetscReal jacobianInverses[dim*dim]
1262        PetscReal jacobianDeterminants
1263   FEM:
1264      PetscFE
1265        PetscQuadrature
1266          PetscReal   quadPoints[Nq*dim]
1267          PetscReal   quadWeights[Nq]
1268        PetscReal   basis[Nq*Nb*Nc]
1269        PetscReal   basisDer[Nq*Nb*Nc*dim]
1270      PetscScalar coefficients[Ne*Nb*Nc]
1271      PetscScalar elemVec[Ne*Nb*Nc]
1272 
1273   Problem:
1274      PetscInt f: the active field
1275      f0, f1
1276 
1277   Work Space:
1278      PetscFE
1279        PetscScalar f0[Nq*dim];
1280        PetscScalar f1[Nq*dim*dim];
1281        PetscScalar u[Nc];
1282        PetscScalar gradU[Nc*dim];
1283        PetscReal   x[dim];
1284        PetscScalar realSpaceDer[dim];
1285 
1286 Purpose: Compute element vector for N_cb batches of elements
1287 
1288 Input:
1289   Sizes:
1290      N_cb: Number of serial cell batches
1291 
1292   Geometry:
1293      PetscReal v0s[Ne*dim]
1294      PetscReal jacobians[Ne*dim*dim]        possibly *Nq
1295      PetscReal jacobianInverses[Ne*dim*dim] possibly *Nq
1296      PetscReal jacobianDeterminants[Ne]     possibly *Nq
1297   FEM:
1298      static PetscReal   quadPoints[Nq*dim]
1299      static PetscReal   quadWeights[Nq]
1300      static PetscReal   basis[Nq*Nb*Nc]
1301      static PetscReal   basisDer[Nq*Nb*Nc*dim]
1302      PetscScalar coefficients[Ne*Nb*Nc]
1303      PetscScalar elemVec[Ne*Nb*Nc]
1304 
1305 ex62.c:
1306   PetscErrorCode PetscFEIntegrateResidualBatch(PetscInt Ne, PetscInt numFields, PetscInt field, PetscQuadrature quad[], const PetscScalar coefficients[],
1307                                                const PetscReal v0s[], const PetscReal jacobians[], const PetscReal jacobianInverses[], const PetscReal jacobianDeterminants[],
1308                                                void (*f0_func)(const PetscScalar u[], const PetscScalar gradU[], const PetscReal x[], PetscScalar f0[]),
1309                                                void (*f1_func)(const PetscScalar u[], const PetscScalar gradU[], const PetscReal x[], PetscScalar f1[]), PetscScalar elemVec[])
1310 
1311 ex52.c:
1312   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)
1313   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)
1314 
1315 ex52_integrateElement.cu
1316 __global__ void integrateElementQuadrature(int N_cb, realType *coefficients, realType *jacobianInverses, realType *jacobianDeterminants, realType *elemVec)
1317 
1318 PETSC_EXTERN PetscErrorCode IntegrateElementBatchGPU(PetscInt spatial_dim, PetscInt Ne, PetscInt Ncb, PetscInt Nbc, PetscInt Nbl, const PetscScalar coefficients[],
1319                                                      const PetscReal jacobianInverses[], const PetscReal jacobianDeterminants[], PetscScalar elemVec[],
1320                                                      PetscLogEvent event, PetscInt debug, PetscInt pde_op)
1321 
1322 ex52_integrateElementOpenCL.c:
1323 PETSC_EXTERN PetscErrorCode IntegrateElementBatchGPU(PetscInt spatial_dim, PetscInt Ne, PetscInt Ncb, PetscInt Nbc, PetscInt N_bl, const PetscScalar coefficients[],
1324                                                      const PetscReal jacobianInverses[], const PetscReal jacobianDeterminants[], PetscScalar elemVec[],
1325                                                      PetscLogEvent event, PetscInt debug, PetscInt pde_op)
1326 
1327 __kernel void integrateElementQuadrature(int N_cb, __global float *coefficients, __global float *jacobianInverses, __global float *jacobianDeterminants, __global float *elemVec)
1328 */
1329 
1330 /*@C
1331   PetscFEIntegrate - Produce the integral for the given field for a chunk of elements by quadrature integration
1332 
1333   Not Collective
1334 
1335   Input Parameters:
1336 + prob         - The `PetscDS` specifying the discretizations and continuum functions
1337 . field        - The field being integrated
1338 . Ne           - The number of elements in the chunk
1339 . cgeom        - The cell geometry for each cell in the chunk
1340 . coefficients - The array of FEM basis coefficients for the elements
1341 . probAux      - The `PetscDS` specifying the auxiliary discretizations
1342 - coefficientsAux - The array of FEM auxiliary basis coefficients for the elements
1343 
1344   Output Parameter:
1345 . integral     - the integral for this field
1346 
1347   Level: intermediate
1348 
1349   Developer Note:
1350   The function name begins with `PetscFE` and yet the first argument is `PetscDS` and it has no `PetscFE` arguments.
1351 
1352 .seealso: `PetscFE`, `PetscDS`, `PetscFEIntegrateResidual()`, `PetscFEIntegrateBd()`
1353 @*/
1354 PetscErrorCode PetscFEIntegrate(PetscDS prob, PetscInt field, PetscInt Ne, PetscFEGeom *cgeom, const PetscScalar coefficients[], PetscDS probAux, const PetscScalar coefficientsAux[], PetscScalar integral[])
1355 {
1356   PetscFE fe;
1357 
1358   PetscFunctionBegin;
1359   PetscValidHeaderSpecific(prob, PETSCDS_CLASSID, 1);
1360   PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fe));
1361   if (fe->ops->integrate) PetscCall((*fe->ops->integrate)(prob, field, Ne, cgeom, coefficients, probAux, coefficientsAux, integral));
1362   PetscFunctionReturn(PETSC_SUCCESS);
1363 }
1364 
1365 /*@C
1366   PetscFEIntegrateBd - Produce the integral for the given field for a chunk of elements by quadrature integration
1367 
1368   Not Collective
1369 
1370   Input Parameters:
1371 + prob         - The `PetscDS` specifying the discretizations and continuum functions
1372 . field        - The field being integrated
1373 . obj_func     - The function to be integrated
1374 . Ne           - The number of elements in the chunk
1375 . fgeom        - The face geometry for each face in the chunk
1376 . coefficients - The array of FEM basis coefficients for the elements
1377 . probAux      - The `PetscDS` specifying the auxiliary discretizations
1378 - coefficientsAux - The array of FEM auxiliary basis coefficients for the elements
1379 
1380   Output Parameter:
1381 . integral     - the integral for this field
1382 
1383   Level: intermediate
1384 
1385   Developer Note:
1386   The function name begins with `PetscFE` and yet the first argument is `PetscDS` and it has no `PetscFE` arguments.
1387 
1388 .seealso: `PetscFE`, `PetscDS`, `PetscFEIntegrateResidual()`, `PetscFEIntegrate()`
1389 @*/
1390 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[])
1391 {
1392   PetscFE fe;
1393 
1394   PetscFunctionBegin;
1395   PetscValidHeaderSpecific(prob, PETSCDS_CLASSID, 1);
1396   PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fe));
1397   if (fe->ops->integratebd) PetscCall((*fe->ops->integratebd)(prob, field, obj_func, Ne, geom, coefficients, probAux, coefficientsAux, integral));
1398   PetscFunctionReturn(PETSC_SUCCESS);
1399 }
1400 
1401 /*@C
1402   PetscFEIntegrateResidual - Produce the element residual vector for a chunk of elements by quadrature integration
1403 
1404   Not Collective
1405 
1406   Input Parameters:
1407 + ds           - The `PetscDS` specifying the discretizations and continuum functions
1408 . key          - The (label+value, field) being integrated
1409 . Ne           - The number of elements in the chunk
1410 . cgeom        - The cell geometry for each cell in the chunk
1411 . coefficients - The array of FEM basis coefficients for the elements
1412 . coefficients_t - The array of FEM basis time derivative coefficients for the elements
1413 . probAux      - The `PetscDS` specifying the auxiliary discretizations
1414 . coefficientsAux - The array of FEM auxiliary basis coefficients for the elements
1415 - t            - The time
1416 
1417   Output Parameter:
1418 . elemVec      - the element residual vectors from each element
1419 
1420   Level: intermediate
1421 
1422   Note:
1423 .vb
1424   Loop over batch of elements (e):
1425     Loop over quadrature points (q):
1426       Make u_q and gradU_q (loops over fields,Nb,Ncomp) and x_q
1427       Call f_0 and f_1
1428     Loop over element vector entries (f,fc --> i):
1429       elemVec[i] += \psi^{fc}_f(q) f0_{fc}(u, \nabla u) + \nabla\psi^{fc}_f(q) \cdot f1_{fc,df}(u, \nabla u)
1430 .ve
1431 
1432 .seealso: `PetscFEIntegrateResidual()`
1433 @*/
1434 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[])
1435 {
1436   PetscFE fe;
1437 
1438   PetscFunctionBeginHot;
1439   PetscValidHeaderSpecific(ds, PETSCDS_CLASSID, 1);
1440   PetscCall(PetscDSGetDiscretization(ds, key.field, (PetscObject *)&fe));
1441   if (fe->ops->integrateresidual) PetscCall((*fe->ops->integrateresidual)(ds, key, Ne, cgeom, coefficients, coefficients_t, probAux, coefficientsAux, t, elemVec));
1442   PetscFunctionReturn(PETSC_SUCCESS);
1443 }
1444 
1445 /*@C
1446   PetscFEIntegrateBdResidual - Produce the element residual vector for a chunk of elements by quadrature integration over a boundary
1447 
1448   Not Collective
1449 
1450   Input Parameters:
1451 + ds           - The `PetscDS` specifying the discretizations and continuum functions
1452 . wf           - The PetscWeakForm object holding the pointwise functions
1453 . key          - The (label+value, field) being integrated
1454 . Ne           - The number of elements in the chunk
1455 . fgeom        - The face 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 .seealso: `PetscFEIntegrateResidual()`
1468 @*/
1469 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[])
1470 {
1471   PetscFE fe;
1472 
1473   PetscFunctionBegin;
1474   PetscValidHeaderSpecific(ds, PETSCDS_CLASSID, 1);
1475   PetscCall(PetscDSGetDiscretization(ds, key.field, (PetscObject *)&fe));
1476   if (fe->ops->integratebdresidual) PetscCall((*fe->ops->integratebdresidual)(ds, wf, key, Ne, fgeom, coefficients, coefficients_t, probAux, coefficientsAux, t, elemVec));
1477   PetscFunctionReturn(PETSC_SUCCESS);
1478 }
1479 
1480 /*@C
1481   PetscFEIntegrateHybridResidual - Produce the element residual vector for a chunk of hybrid element faces by quadrature integration
1482 
1483   Not Collective
1484 
1485   Input Parameters:
1486 + ds           - The `PetscDS` specifying the discretizations and continuum functions
1487 . dsIn         - The `PetscDS` specifying the discretizations and continuum functions for input
1488 . key          - The (label+value, field) being integrated
1489 . s            - The side of the cell being integrated, 0 for negative and 1 for positive
1490 . Ne           - The number of elements in the chunk
1491 . fgeom        - The face geometry for each cell in the chunk
1492 . coefficients - The array of FEM basis coefficients for the elements
1493 . coefficients_t - The array of FEM basis time derivative coefficients for the elements
1494 . probAux      - The `PetscDS` specifying the auxiliary discretizations
1495 . coefficientsAux - The array of FEM auxiliary basis coefficients for the elements
1496 - t            - The time
1497 
1498   Output Parameter
1499 . elemVec      - the element residual vectors from each element
1500 
1501   Level: developer
1502 
1503 .seealso: `PetscFEIntegrateResidual()`
1504 @*/
1505 PetscErrorCode PetscFEIntegrateHybridResidual(PetscDS ds, PetscDS dsIn, PetscFormKey key, PetscInt s, PetscInt Ne, PetscFEGeom *fgeom, const PetscScalar coefficients[], const PetscScalar coefficients_t[], PetscDS probAux, const PetscScalar coefficientsAux[], PetscReal t, PetscScalar elemVec[])
1506 {
1507   PetscFE fe;
1508 
1509   PetscFunctionBegin;
1510   PetscValidHeaderSpecific(ds, PETSCDS_CLASSID, 1);
1511   PetscValidHeaderSpecific(dsIn, PETSCDS_CLASSID, 2);
1512   PetscCall(PetscDSGetDiscretization(ds, key.field, (PetscObject *)&fe));
1513   if (fe->ops->integratehybridresidual) PetscCall((*fe->ops->integratehybridresidual)(ds, dsIn, key, s, Ne, fgeom, coefficients, coefficients_t, probAux, coefficientsAux, t, elemVec));
1514   PetscFunctionReturn(PETSC_SUCCESS);
1515 }
1516 
1517 /*@C
1518   PetscFEIntegrateJacobian - Produce the element Jacobian for a chunk of elements by quadrature integration
1519 
1520   Not Collective
1521 
1522   Input Parameters:
1523 + ds           - The `PetscDS` specifying the discretizations and continuum functions
1524 . jtype        - The type of matrix pointwise functions that should be used
1525 . key          - The (label+value, fieldI*Nf + fieldJ) being integrated
1526 . s            - The side of the cell being integrated, 0 for negative and 1 for positive
1527 . Ne           - The number of elements in the chunk
1528 . cgeom        - The cell geometry for each cell in the chunk
1529 . coefficients - The array of FEM basis coefficients for the elements for the Jacobian evaluation point
1530 . coefficients_t - The array of FEM basis time derivative coefficients for the elements
1531 . probAux      - The `PetscDS` specifying the auxiliary discretizations
1532 . coefficientsAux - The array of FEM auxiliary basis coefficients for the elements
1533 . t            - The time
1534 - u_tShift     - A multiplier for the dF/du_t term (as opposed to the dF/du term)
1535 
1536   Output Parameter:
1537 . elemMat      - the element matrices for the Jacobian from each element
1538 
1539   Level: intermediate
1540 
1541   Note:
1542 .vb
1543   Loop over batch of elements (e):
1544     Loop over element matrix entries (f,fc,g,gc --> i,j):
1545       Loop over quadrature points (q):
1546         Make u_q and gradU_q (loops over fields,Nb,Ncomp)
1547           elemMat[i,j] += \psi^{fc}_f(q) g0_{fc,gc}(u, \nabla u) \phi^{gc}_g(q)
1548                        + \psi^{fc}_f(q) \cdot g1_{fc,gc,dg}(u, \nabla u) \nabla\phi^{gc}_g(q)
1549                        + \nabla\psi^{fc}_f(q) \cdot g2_{fc,gc,df}(u, \nabla u) \phi^{gc}_g(q)
1550                        + \nabla\psi^{fc}_f(q) \cdot g3_{fc,gc,df,dg}(u, \nabla u) \nabla\phi^{gc}_g(q)
1551 .ve
1552 
1553 .seealso: `PetscFEIntegrateResidual()`
1554 @*/
1555 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[])
1556 {
1557   PetscFE  fe;
1558   PetscInt Nf;
1559 
1560   PetscFunctionBegin;
1561   PetscValidHeaderSpecific(ds, PETSCDS_CLASSID, 1);
1562   PetscCall(PetscDSGetNumFields(ds, &Nf));
1563   PetscCall(PetscDSGetDiscretization(ds, key.field / Nf, (PetscObject *)&fe));
1564   if (fe->ops->integratejacobian) PetscCall((*fe->ops->integratejacobian)(ds, jtype, key, Ne, cgeom, coefficients, coefficients_t, probAux, coefficientsAux, t, u_tshift, elemMat));
1565   PetscFunctionReturn(PETSC_SUCCESS);
1566 }
1567 
1568 /*@C
1569   PetscFEIntegrateBdJacobian - Produce the boundary element Jacobian for a chunk of elements by quadrature integration
1570 
1571   Not Collective
1572 
1573   Input Parameters:
1574 + ds           - The `PetscDS` specifying the discretizations and continuum functions
1575 . wf           - The PetscWeakForm holding the pointwise functions
1576 . key          - The (label+value, fieldI*Nf + fieldJ) being integrated
1577 . Ne           - The number of elements in the chunk
1578 . fgeom        - The face geometry for each cell in the chunk
1579 . coefficients - The array of FEM basis coefficients for the elements for the Jacobian evaluation point
1580 . coefficients_t - The array of FEM basis time derivative coefficients for the elements
1581 . probAux      - The `PetscDS` specifying the auxiliary discretizations
1582 . coefficientsAux - The array of FEM auxiliary basis coefficients for the elements
1583 . t            - The time
1584 - u_tShift     - A multiplier for the dF/du_t term (as opposed to the dF/du term)
1585 
1586   Output Parameter:
1587 . elemMat              - the element matrices for the Jacobian from each element
1588 
1589   Level: intermediate
1590 
1591   Note:
1592 .vb
1593   Loop over batch of elements (e):
1594     Loop over element matrix entries (f,fc,g,gc --> i,j):
1595       Loop over quadrature points (q):
1596         Make u_q and gradU_q (loops over fields,Nb,Ncomp)
1597           elemMat[i,j] += \psi^{fc}_f(q) g0_{fc,gc}(u, \nabla u) \phi^{gc}_g(q)
1598                        + \psi^{fc}_f(q) \cdot g1_{fc,gc,dg}(u, \nabla u) \nabla\phi^{gc}_g(q)
1599                        + \nabla\psi^{fc}_f(q) \cdot g2_{fc,gc,df}(u, \nabla u) \phi^{gc}_g(q)
1600                        + \nabla\psi^{fc}_f(q) \cdot g3_{fc,gc,df,dg}(u, \nabla u) \nabla\phi^{gc}_g(q)
1601 .ve
1602 
1603 .seealso: `PetscFEIntegrateJacobian()`, `PetscFEIntegrateResidual()`
1604 @*/
1605 PetscErrorCode PetscFEIntegrateBdJacobian(PetscDS ds, PetscWeakForm wf, 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[])
1606 {
1607   PetscFE  fe;
1608   PetscInt Nf;
1609 
1610   PetscFunctionBegin;
1611   PetscValidHeaderSpecific(ds, PETSCDS_CLASSID, 1);
1612   PetscCall(PetscDSGetNumFields(ds, &Nf));
1613   PetscCall(PetscDSGetDiscretization(ds, key.field / Nf, (PetscObject *)&fe));
1614   if (fe->ops->integratebdjacobian) PetscCall((*fe->ops->integratebdjacobian)(ds, wf, key, Ne, fgeom, coefficients, coefficients_t, probAux, coefficientsAux, t, u_tshift, elemMat));
1615   PetscFunctionReturn(PETSC_SUCCESS);
1616 }
1617 
1618 /*@C
1619   PetscFEIntegrateHybridJacobian - Produce the boundary element Jacobian for a chunk of hybrid elements by quadrature integration
1620 
1621   Not Collective
1622 
1623   Input Parameters:
1624 + ds           - The `PetscDS` specifying the discretizations and continuum functions for the output
1625 . dsIn         - The `PetscDS` specifying the discretizations and continuum functions for the input
1626 . jtype        - The type of matrix pointwise functions that should be used
1627 . key          - The (label+value, fieldI*Nf + fieldJ) being integrated
1628 . s            - The side of the cell being integrated, 0 for negative and 1 for positive
1629 . Ne           - The number of elements in the chunk
1630 . fgeom        - The face geometry for each cell in the chunk
1631 . coefficients - The array of FEM basis coefficients for the elements for the Jacobian evaluation point
1632 . coefficients_t - The array of FEM basis time derivative coefficients for the elements
1633 . probAux      - The `PetscDS` specifying the auxiliary discretizations
1634 . coefficientsAux - The array of FEM auxiliary basis coefficients for the elements
1635 . t            - The time
1636 - u_tShift     - A multiplier for the dF/du_t term (as opposed to the dF/du term)
1637 
1638   Output Parameter
1639 . elemMat      - the element matrices for the Jacobian from each element
1640 
1641   Level: developer
1642 
1643   Note:
1644 .vb
1645   Loop over batch of elements (e):
1646     Loop over element matrix entries (f,fc,g,gc --> i,j):
1647       Loop over quadrature points (q):
1648         Make u_q and gradU_q (loops over fields,Nb,Ncomp)
1649           elemMat[i,j] += \psi^{fc}_f(q) g0_{fc,gc}(u, \nabla u) \phi^{gc}_g(q)
1650                        + \psi^{fc}_f(q) \cdot g1_{fc,gc,dg}(u, \nabla u) \nabla\phi^{gc}_g(q)
1651                        + \nabla\psi^{fc}_f(q) \cdot g2_{fc,gc,df}(u, \nabla u) \phi^{gc}_g(q)
1652                        + \nabla\psi^{fc}_f(q) \cdot g3_{fc,gc,df,dg}(u, \nabla u) \nabla\phi^{gc}_g(q)
1653 .ve
1654 
1655 .seealso: `PetscFEIntegrateJacobian()`, `PetscFEIntegrateResidual()`
1656 @*/
1657 PetscErrorCode PetscFEIntegrateHybridJacobian(PetscDS ds, PetscDS dsIn, PetscFEJacobianType jtype, PetscFormKey key, PetscInt s, PetscInt Ne, PetscFEGeom *fgeom, const PetscScalar coefficients[], const PetscScalar coefficients_t[], PetscDS probAux, const PetscScalar coefficientsAux[], PetscReal t, PetscReal u_tshift, PetscScalar elemMat[])
1658 {
1659   PetscFE  fe;
1660   PetscInt Nf;
1661 
1662   PetscFunctionBegin;
1663   PetscValidHeaderSpecific(ds, PETSCDS_CLASSID, 1);
1664   PetscCall(PetscDSGetNumFields(ds, &Nf));
1665   PetscCall(PetscDSGetDiscretization(ds, key.field / Nf, (PetscObject *)&fe));
1666   if (fe->ops->integratehybridjacobian) PetscCall((*fe->ops->integratehybridjacobian)(ds, dsIn, jtype, key, s, Ne, fgeom, coefficients, coefficients_t, probAux, coefficientsAux, t, u_tshift, elemMat));
1667   PetscFunctionReturn(PETSC_SUCCESS);
1668 }
1669 
1670 /*@
1671   PetscFEGetHeightSubspace - Get the subspace of this space for a mesh point of a given height
1672 
1673   Input Parameters:
1674 + fe     - The finite element space
1675 - height - The height of the `DMPLEX` point
1676 
1677   Output Parameter:
1678 . subfe  - The subspace of this `PetscFE` space
1679 
1680   Level: advanced
1681 
1682   Note:
1683   For example, if we want the subspace of this space for a face, we would choose height = 1.
1684 
1685 .seealso: `PetscFECreateDefault()`
1686 @*/
1687 PetscErrorCode PetscFEGetHeightSubspace(PetscFE fe, PetscInt height, PetscFE *subfe)
1688 {
1689   PetscSpace      P, subP;
1690   PetscDualSpace  Q, subQ;
1691   PetscQuadrature subq;
1692   PetscFEType     fetype;
1693   PetscInt        dim, Nc;
1694 
1695   PetscFunctionBegin;
1696   PetscValidHeaderSpecific(fe, PETSCFE_CLASSID, 1);
1697   PetscValidPointer(subfe, 3);
1698   if (height == 0) {
1699     *subfe = fe;
1700     PetscFunctionReturn(PETSC_SUCCESS);
1701   }
1702   PetscCall(PetscFEGetBasisSpace(fe, &P));
1703   PetscCall(PetscFEGetDualSpace(fe, &Q));
1704   PetscCall(PetscFEGetNumComponents(fe, &Nc));
1705   PetscCall(PetscFEGetFaceQuadrature(fe, &subq));
1706   PetscCall(PetscDualSpaceGetDimension(Q, &dim));
1707   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);
1708   if (!fe->subspaces) PetscCall(PetscCalloc1(dim, &fe->subspaces));
1709   if (height <= dim) {
1710     if (!fe->subspaces[height - 1]) {
1711       PetscFE     sub = NULL;
1712       const char *name;
1713 
1714       PetscCall(PetscSpaceGetHeightSubspace(P, height, &subP));
1715       PetscCall(PetscDualSpaceGetHeightSubspace(Q, height, &subQ));
1716       if (subQ) {
1717         PetscCall(PetscFECreate(PetscObjectComm((PetscObject)fe), &sub));
1718         PetscCall(PetscObjectGetName((PetscObject)fe, &name));
1719         PetscCall(PetscObjectSetName((PetscObject)sub, name));
1720         PetscCall(PetscFEGetType(fe, &fetype));
1721         PetscCall(PetscFESetType(sub, fetype));
1722         PetscCall(PetscFESetBasisSpace(sub, subP));
1723         PetscCall(PetscFESetDualSpace(sub, subQ));
1724         PetscCall(PetscFESetNumComponents(sub, Nc));
1725         PetscCall(PetscFESetUp(sub));
1726         PetscCall(PetscFESetQuadrature(sub, subq));
1727       }
1728       fe->subspaces[height - 1] = sub;
1729     }
1730     *subfe = fe->subspaces[height - 1];
1731   } else {
1732     *subfe = NULL;
1733   }
1734   PetscFunctionReturn(PETSC_SUCCESS);
1735 }
1736 
1737 /*@
1738   PetscFERefine - Create a "refined" `PetscFE` object that refines the reference cell into smaller copies. This is typically used
1739   to precondition a higher order method with a lower order method on a refined mesh having the same number of dofs (but more
1740   sparsity). It is also used to create an interpolation between regularly refined meshes.
1741 
1742   Collective
1743 
1744   Input Parameter:
1745 . fe - The initial `PetscFE`
1746 
1747   Output Parameter:
1748 . feRef - The refined `PetscFE`
1749 
1750   Level: advanced
1751 
1752 .seealso: `PetscFEType`, `PetscFECreate()`, `PetscFESetType()`
1753 @*/
1754 PetscErrorCode PetscFERefine(PetscFE fe, PetscFE *feRef)
1755 {
1756   PetscSpace       P, Pref;
1757   PetscDualSpace   Q, Qref;
1758   DM               K, Kref;
1759   PetscQuadrature  q, qref;
1760   const PetscReal *v0, *jac;
1761   PetscInt         numComp, numSubelements;
1762   PetscInt         cStart, cEnd, c;
1763   PetscDualSpace  *cellSpaces;
1764 
1765   PetscFunctionBegin;
1766   PetscCall(PetscFEGetBasisSpace(fe, &P));
1767   PetscCall(PetscFEGetDualSpace(fe, &Q));
1768   PetscCall(PetscFEGetQuadrature(fe, &q));
1769   PetscCall(PetscDualSpaceGetDM(Q, &K));
1770   /* Create space */
1771   PetscCall(PetscObjectReference((PetscObject)P));
1772   Pref = P;
1773   /* Create dual space */
1774   PetscCall(PetscDualSpaceDuplicate(Q, &Qref));
1775   PetscCall(PetscDualSpaceSetType(Qref, PETSCDUALSPACEREFINED));
1776   PetscCall(DMRefine(K, PetscObjectComm((PetscObject)fe), &Kref));
1777   PetscCall(PetscDualSpaceSetDM(Qref, Kref));
1778   PetscCall(DMPlexGetHeightStratum(Kref, 0, &cStart, &cEnd));
1779   PetscCall(PetscMalloc1(cEnd - cStart, &cellSpaces));
1780   /* TODO: fix for non-uniform refinement */
1781   for (c = 0; c < cEnd - cStart; c++) cellSpaces[c] = Q;
1782   PetscCall(PetscDualSpaceRefinedSetCellSpaces(Qref, cellSpaces));
1783   PetscCall(PetscFree(cellSpaces));
1784   PetscCall(DMDestroy(&Kref));
1785   PetscCall(PetscDualSpaceSetUp(Qref));
1786   /* Create element */
1787   PetscCall(PetscFECreate(PetscObjectComm((PetscObject)fe), feRef));
1788   PetscCall(PetscFESetType(*feRef, PETSCFECOMPOSITE));
1789   PetscCall(PetscFESetBasisSpace(*feRef, Pref));
1790   PetscCall(PetscFESetDualSpace(*feRef, Qref));
1791   PetscCall(PetscFEGetNumComponents(fe, &numComp));
1792   PetscCall(PetscFESetNumComponents(*feRef, numComp));
1793   PetscCall(PetscFESetUp(*feRef));
1794   PetscCall(PetscSpaceDestroy(&Pref));
1795   PetscCall(PetscDualSpaceDestroy(&Qref));
1796   /* Create quadrature */
1797   PetscCall(PetscFECompositeGetMapping(*feRef, &numSubelements, &v0, &jac, NULL));
1798   PetscCall(PetscQuadratureExpandComposite(q, numSubelements, v0, jac, &qref));
1799   PetscCall(PetscFESetQuadrature(*feRef, qref));
1800   PetscCall(PetscQuadratureDestroy(&qref));
1801   PetscFunctionReturn(PETSC_SUCCESS);
1802 }
1803 
1804 static PetscErrorCode PetscFESetDefaultName_Private(PetscFE fe)
1805 {
1806   PetscSpace     P;
1807   PetscDualSpace Q;
1808   DM             K;
1809   DMPolytopeType ct;
1810   PetscInt       degree;
1811   char           name[64];
1812 
1813   PetscFunctionBegin;
1814   PetscCall(PetscFEGetBasisSpace(fe, &P));
1815   PetscCall(PetscSpaceGetDegree(P, &degree, NULL));
1816   PetscCall(PetscFEGetDualSpace(fe, &Q));
1817   PetscCall(PetscDualSpaceGetDM(Q, &K));
1818   PetscCall(DMPlexGetCellType(K, 0, &ct));
1819   switch (ct) {
1820   case DM_POLYTOPE_SEGMENT:
1821   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1822   case DM_POLYTOPE_QUADRILATERAL:
1823   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1824   case DM_POLYTOPE_HEXAHEDRON:
1825   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
1826     PetscCall(PetscSNPrintf(name, sizeof(name), "Q%" PetscInt_FMT, degree));
1827     break;
1828   case DM_POLYTOPE_TRIANGLE:
1829   case DM_POLYTOPE_TETRAHEDRON:
1830     PetscCall(PetscSNPrintf(name, sizeof(name), "P%" PetscInt_FMT, degree));
1831     break;
1832   case DM_POLYTOPE_TRI_PRISM:
1833   case DM_POLYTOPE_TRI_PRISM_TENSOR:
1834     PetscCall(PetscSNPrintf(name, sizeof(name), "P%" PetscInt_FMT "xQ%" PetscInt_FMT, degree, degree));
1835     break;
1836   default:
1837     PetscCall(PetscSNPrintf(name, sizeof(name), "FE"));
1838   }
1839   PetscCall(PetscFESetName(fe, name));
1840   PetscFunctionReturn(PETSC_SUCCESS);
1841 }
1842 
1843 /*@
1844   PetscFECreateFromSpaces - Create a `PetscFE` from the basis and dual spaces
1845 
1846   Collective
1847 
1848   Input Parameters:
1849 + P  - The basis space
1850 . Q  - The dual space
1851 . q  - The cell quadrature
1852 - fq - The face quadrature
1853 
1854   Output Parameter:
1855 . fem    - The `PetscFE` object
1856 
1857   Level: beginner
1858 
1859   Note:
1860   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.
1861 
1862 .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscQuadrature`,
1863           `PetscFECreateLagrangeByCell()`, `PetscFECreateDefault()`, `PetscFECreateByCell()`, `PetscFECreate()`, `PetscSpaceCreate()`, `PetscDualSpaceCreate()`
1864 @*/
1865 PetscErrorCode PetscFECreateFromSpaces(PetscSpace P, PetscDualSpace Q, PetscQuadrature q, PetscQuadrature fq, PetscFE *fem)
1866 {
1867   PetscInt    Nc;
1868   const char *prefix;
1869 
1870   PetscFunctionBegin;
1871   PetscCall(PetscFECreate(PetscObjectComm((PetscObject)P), fem));
1872   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)P, &prefix));
1873   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*fem, prefix));
1874   PetscCall(PetscFESetType(*fem, PETSCFEBASIC));
1875   PetscCall(PetscFESetBasisSpace(*fem, P));
1876   PetscCall(PetscFESetDualSpace(*fem, Q));
1877   PetscCall(PetscSpaceGetNumComponents(P, &Nc));
1878   PetscCall(PetscFESetNumComponents(*fem, Nc));
1879   PetscCall(PetscFESetUp(*fem));
1880   PetscCall(PetscSpaceDestroy(&P));
1881   PetscCall(PetscDualSpaceDestroy(&Q));
1882   PetscCall(PetscFESetQuadrature(*fem, q));
1883   PetscCall(PetscFESetFaceQuadrature(*fem, fq));
1884   PetscCall(PetscQuadratureDestroy(&q));
1885   PetscCall(PetscQuadratureDestroy(&fq));
1886   PetscCall(PetscFESetDefaultName_Private(*fem));
1887   PetscFunctionReturn(PETSC_SUCCESS);
1888 }
1889 
1890 static PetscErrorCode PetscFECreate_Internal(MPI_Comm comm, PetscInt dim, PetscInt Nc, DMPolytopeType ct, const char prefix[], PetscInt degree, PetscInt qorder, PetscBool setFromOptions, PetscFE *fem)
1891 {
1892   DM              K;
1893   PetscSpace      P;
1894   PetscDualSpace  Q;
1895   PetscQuadrature q, fq;
1896   PetscBool       tensor;
1897 
1898   PetscFunctionBegin;
1899   if (prefix) PetscValidCharPointer(prefix, 5);
1900   PetscValidPointer(fem, 9);
1901   switch (ct) {
1902   case DM_POLYTOPE_SEGMENT:
1903   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1904   case DM_POLYTOPE_QUADRILATERAL:
1905   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1906   case DM_POLYTOPE_HEXAHEDRON:
1907   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
1908     tensor = PETSC_TRUE;
1909     break;
1910   default:
1911     tensor = PETSC_FALSE;
1912   }
1913   /* Create space */
1914   PetscCall(PetscSpaceCreate(comm, &P));
1915   PetscCall(PetscSpaceSetType(P, PETSCSPACEPOLYNOMIAL));
1916   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)P, prefix));
1917   PetscCall(PetscSpacePolynomialSetTensor(P, tensor));
1918   PetscCall(PetscSpaceSetNumComponents(P, Nc));
1919   PetscCall(PetscSpaceSetNumVariables(P, dim));
1920   if (degree >= 0) {
1921     PetscCall(PetscSpaceSetDegree(P, degree, PETSC_DETERMINE));
1922     if (ct == DM_POLYTOPE_TRI_PRISM || ct == DM_POLYTOPE_TRI_PRISM_TENSOR) {
1923       PetscSpace Pend, Pside;
1924 
1925       PetscCall(PetscSpaceCreate(comm, &Pend));
1926       PetscCall(PetscSpaceSetType(Pend, PETSCSPACEPOLYNOMIAL));
1927       PetscCall(PetscSpacePolynomialSetTensor(Pend, PETSC_FALSE));
1928       PetscCall(PetscSpaceSetNumComponents(Pend, Nc));
1929       PetscCall(PetscSpaceSetNumVariables(Pend, dim - 1));
1930       PetscCall(PetscSpaceSetDegree(Pend, degree, PETSC_DETERMINE));
1931       PetscCall(PetscSpaceCreate(comm, &Pside));
1932       PetscCall(PetscSpaceSetType(Pside, PETSCSPACEPOLYNOMIAL));
1933       PetscCall(PetscSpacePolynomialSetTensor(Pside, PETSC_FALSE));
1934       PetscCall(PetscSpaceSetNumComponents(Pside, 1));
1935       PetscCall(PetscSpaceSetNumVariables(Pside, 1));
1936       PetscCall(PetscSpaceSetDegree(Pside, degree, PETSC_DETERMINE));
1937       PetscCall(PetscSpaceSetType(P, PETSCSPACETENSOR));
1938       PetscCall(PetscSpaceTensorSetNumSubspaces(P, 2));
1939       PetscCall(PetscSpaceTensorSetSubspace(P, 0, Pend));
1940       PetscCall(PetscSpaceTensorSetSubspace(P, 1, Pside));
1941       PetscCall(PetscSpaceDestroy(&Pend));
1942       PetscCall(PetscSpaceDestroy(&Pside));
1943     }
1944   }
1945   if (setFromOptions) PetscCall(PetscSpaceSetFromOptions(P));
1946   PetscCall(PetscSpaceSetUp(P));
1947   PetscCall(PetscSpaceGetDegree(P, &degree, NULL));
1948   PetscCall(PetscSpacePolynomialGetTensor(P, &tensor));
1949   PetscCall(PetscSpaceGetNumComponents(P, &Nc));
1950   /* Create dual space */
1951   PetscCall(PetscDualSpaceCreate(comm, &Q));
1952   PetscCall(PetscDualSpaceSetType(Q, PETSCDUALSPACELAGRANGE));
1953   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)Q, prefix));
1954   PetscCall(DMPlexCreateReferenceCell(PETSC_COMM_SELF, ct, &K));
1955   PetscCall(PetscDualSpaceSetDM(Q, K));
1956   PetscCall(DMDestroy(&K));
1957   PetscCall(PetscDualSpaceSetNumComponents(Q, Nc));
1958   PetscCall(PetscDualSpaceSetOrder(Q, degree));
1959   /* TODO For some reason, we need a tensor dualspace with wedges */
1960   PetscCall(PetscDualSpaceLagrangeSetTensor(Q, (tensor || (ct == DM_POLYTOPE_TRI_PRISM)) ? PETSC_TRUE : PETSC_FALSE));
1961   if (setFromOptions) PetscCall(PetscDualSpaceSetFromOptions(Q));
1962   PetscCall(PetscDualSpaceSetUp(Q));
1963   /* Create quadrature */
1964   qorder = qorder >= 0 ? qorder : degree;
1965   if (setFromOptions) {
1966     PetscObjectOptionsBegin((PetscObject)P);
1967     PetscCall(PetscOptionsBoundedInt("-petscfe_default_quadrature_order", "Quadrature order is one less than quadrature points per edge", "PetscFECreateDefault", qorder, &qorder, NULL, 0));
1968     PetscOptionsEnd();
1969   }
1970   PetscCall(PetscDTCreateDefaultQuadrature(ct, qorder, &q, &fq));
1971   /* Create finite element */
1972   PetscCall(PetscFECreateFromSpaces(P, Q, q, fq, fem));
1973   if (setFromOptions) PetscCall(PetscFESetFromOptions(*fem));
1974   PetscFunctionReturn(PETSC_SUCCESS);
1975 }
1976 
1977 /*@C
1978   PetscFECreateDefault - Create a `PetscFE` for basic FEM computation
1979 
1980   Collective
1981 
1982   Input Parameters:
1983 + comm      - The MPI comm
1984 . dim       - The spatial dimension
1985 . Nc        - The number of components
1986 . isSimplex - Flag for simplex reference cell, otherwise its a tensor product
1987 . prefix    - The options prefix, or `NULL`
1988 - qorder    - The quadrature order or `PETSC_DETERMINE` to use `PetscSpace` polynomial degree
1989 
1990   Output Parameter:
1991 . fem - The `PetscFE` object
1992 
1993   Level: beginner
1994 
1995   Note:
1996   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.
1997 
1998 .seealso: `PetscFECreateLagrange()`, `PetscFECreateByCell()`, `PetscSpaceSetFromOptions()`, `PetscDualSpaceSetFromOptions()`, `PetscFESetFromOptions()`, `PetscFECreate()`, `PetscSpaceCreate()`, `PetscDualSpaceCreate()`
1999 @*/
2000 PetscErrorCode PetscFECreateDefault(MPI_Comm comm, PetscInt dim, PetscInt Nc, PetscBool isSimplex, const char prefix[], PetscInt qorder, PetscFE *fem)
2001 {
2002   PetscFunctionBegin;
2003   PetscCall(PetscFECreate_Internal(comm, dim, Nc, DMPolytopeTypeSimpleShape(dim, isSimplex), prefix, PETSC_DECIDE, qorder, PETSC_TRUE, fem));
2004   PetscFunctionReturn(PETSC_SUCCESS);
2005 }
2006 
2007 /*@C
2008   PetscFECreateByCell - Create a `PetscFE` for basic FEM computation
2009 
2010   Collective
2011 
2012   Input Parameters:
2013 + comm   - The MPI comm
2014 . dim    - The spatial dimension
2015 . Nc     - The number of components
2016 . ct     - The celltype of the reference cell
2017 . prefix - The options prefix, or `NULL`
2018 - qorder - The quadrature order or `PETSC_DETERMINE` to use `PetscSpace` polynomial degree
2019 
2020   Output Parameter:
2021 . fem - The `PetscFE` object
2022 
2023   Level: beginner
2024 
2025   Note:
2026   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.
2027 
2028 .seealso: `PetscFECreateDefault()`, `PetscFECreateLagrange()`, `PetscSpaceSetFromOptions()`, `PetscDualSpaceSetFromOptions()`, `PetscFESetFromOptions()`, `PetscFECreate()`, `PetscSpaceCreate()`, `PetscDualSpaceCreate()`
2029 @*/
2030 PetscErrorCode PetscFECreateByCell(MPI_Comm comm, PetscInt dim, PetscInt Nc, DMPolytopeType ct, const char prefix[], PetscInt qorder, PetscFE *fem)
2031 {
2032   PetscFunctionBegin;
2033   PetscCall(PetscFECreate_Internal(comm, dim, Nc, ct, prefix, PETSC_DECIDE, qorder, PETSC_TRUE, fem));
2034   PetscFunctionReturn(PETSC_SUCCESS);
2035 }
2036 
2037 /*@
2038   PetscFECreateLagrange - Create a `PetscFE` for the basic Lagrange space of degree k
2039 
2040   Collective
2041 
2042   Input Parameters:
2043 + comm      - The MPI comm
2044 . dim       - The spatial dimension
2045 . Nc        - The number of components
2046 . isSimplex - Flag for simplex reference cell, otherwise its a tensor product
2047 . k         - The degree k of the space
2048 - qorder    - The quadrature order or `PETSC_DETERMINE` to use `PetscSpace` polynomial degree
2049 
2050   Output Parameter:
2051 . fem       - The `PetscFE` object
2052 
2053   Level: beginner
2054 
2055   Note:
2056   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.
2057 
2058 .seealso: `PetscFECreateLagrangeByCell()`, `PetscFECreateDefault()`, `PetscFECreateByCell()`, `PetscFECreate()`, `PetscSpaceCreate()`, `PetscDualSpaceCreate()`
2059 @*/
2060 PetscErrorCode PetscFECreateLagrange(MPI_Comm comm, PetscInt dim, PetscInt Nc, PetscBool isSimplex, PetscInt k, PetscInt qorder, PetscFE *fem)
2061 {
2062   PetscFunctionBegin;
2063   PetscCall(PetscFECreate_Internal(comm, dim, Nc, DMPolytopeTypeSimpleShape(dim, isSimplex), NULL, k, qorder, PETSC_FALSE, fem));
2064   PetscFunctionReturn(PETSC_SUCCESS);
2065 }
2066 
2067 /*@
2068   PetscFECreateLagrangeByCell - Create a `PetscFE` for the basic Lagrange space of degree k
2069 
2070   Collective
2071 
2072   Input Parameters:
2073 + comm      - The MPI comm
2074 . dim       - The spatial dimension
2075 . Nc        - The number of components
2076 . ct        - The celltype of the reference cell
2077 . k         - The degree k of the space
2078 - qorder    - The quadrature order or `PETSC_DETERMINE` to use `PetscSpace` polynomial degree
2079 
2080   Output Parameter:
2081 . fem       - The `PetscFE` object
2082 
2083   Level: beginner
2084 
2085   Note:
2086   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.
2087 
2088 .seealso: `PetscFECreateLagrange()`, `PetscFECreateDefault()`, `PetscFECreateByCell()`, `PetscFECreate()`, `PetscSpaceCreate()`, `PetscDualSpaceCreate()`
2089 @*/
2090 PetscErrorCode PetscFECreateLagrangeByCell(MPI_Comm comm, PetscInt dim, PetscInt Nc, DMPolytopeType ct, PetscInt k, PetscInt qorder, PetscFE *fem)
2091 {
2092   PetscFunctionBegin;
2093   PetscCall(PetscFECreate_Internal(comm, dim, Nc, ct, NULL, k, qorder, PETSC_FALSE, fem));
2094   PetscFunctionReturn(PETSC_SUCCESS);
2095 }
2096 
2097 /*@C
2098   PetscFESetName - Names the `PetscFE` and its subobjects
2099 
2100   Not Collective
2101 
2102   Input Parameters:
2103 + fe   - The `PetscFE`
2104 - name - The name
2105 
2106   Level: intermediate
2107 
2108 .seealso: `PetscFECreate()`, `PetscSpaceCreate()`, `PetscDualSpaceCreate()`
2109 @*/
2110 PetscErrorCode PetscFESetName(PetscFE fe, const char name[])
2111 {
2112   PetscSpace     P;
2113   PetscDualSpace Q;
2114 
2115   PetscFunctionBegin;
2116   PetscCall(PetscFEGetBasisSpace(fe, &P));
2117   PetscCall(PetscFEGetDualSpace(fe, &Q));
2118   PetscCall(PetscObjectSetName((PetscObject)fe, name));
2119   PetscCall(PetscObjectSetName((PetscObject)P, name));
2120   PetscCall(PetscObjectSetName((PetscObject)Q, name));
2121   PetscFunctionReturn(PETSC_SUCCESS);
2122 }
2123 
2124 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[])
2125 {
2126   PetscInt dOffset = 0, fOffset = 0, f, g;
2127 
2128   for (f = 0; f < Nf; ++f) {
2129     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);
2130     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);
2131     PetscFE          fe;
2132     const PetscInt   k       = ds->jetDegree[f];
2133     const PetscInt   cdim    = T[f]->cdim;
2134     const PetscInt   dE      = fegeom->dimEmbed;
2135     const PetscInt   Nq      = T[f]->Np;
2136     const PetscInt   Nbf     = T[f]->Nb;
2137     const PetscInt   Ncf     = T[f]->Nc;
2138     const PetscReal *Bq      = &T[f]->T[0][(r * Nq + q) * Nbf * Ncf];
2139     const PetscReal *Dq      = &T[f]->T[1][(r * Nq + q) * Nbf * Ncf * cdim];
2140     const PetscReal *Hq      = k > 1 ? &T[f]->T[2][(r * Nq + q) * Nbf * Ncf * cdim * cdim] : NULL;
2141     PetscInt         hOffset = 0, b, c, d;
2142 
2143     PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
2144     for (c = 0; c < Ncf; ++c) u[fOffset + c] = 0.0;
2145     for (d = 0; d < dE * Ncf; ++d) u_x[fOffset * dE + d] = 0.0;
2146     for (b = 0; b < Nbf; ++b) {
2147       for (c = 0; c < Ncf; ++c) {
2148         const PetscInt cidx = b * Ncf + c;
2149 
2150         u[fOffset + c] += Bq[cidx] * coefficients[dOffset + b];
2151         for (d = 0; d < cdim; ++d) u_x[(fOffset + c) * dE + d] += Dq[cidx * cdim + d] * coefficients[dOffset + b];
2152       }
2153     }
2154     if (k > 1) {
2155       for (g = 0; g < Nf; ++g) hOffset += T[g]->Nc * dE;
2156       for (d = 0; d < dE * dE * Ncf; ++d) u_x[hOffset + fOffset * dE * dE + d] = 0.0;
2157       for (b = 0; b < Nbf; ++b) {
2158         for (c = 0; c < Ncf; ++c) {
2159           const PetscInt cidx = b * Ncf + c;
2160 
2161           for (d = 0; d < cdim * cdim; ++d) u_x[hOffset + (fOffset + c) * dE * dE + d] += Hq[cidx * cdim * cdim + d] * coefficients[dOffset + b];
2162         }
2163       }
2164       PetscCall(PetscFEPushforwardHessian(fe, fegeom, 1, &u_x[hOffset + fOffset * dE * dE]));
2165     }
2166     PetscCall(PetscFEPushforward(fe, fegeom, 1, &u[fOffset]));
2167     PetscCall(PetscFEPushforwardGradient(fe, fegeom, 1, &u_x[fOffset * dE]));
2168     if (u_t) {
2169       for (c = 0; c < Ncf; ++c) u_t[fOffset + c] = 0.0;
2170       for (b = 0; b < Nbf; ++b) {
2171         for (c = 0; c < Ncf; ++c) {
2172           const PetscInt cidx = b * Ncf + c;
2173 
2174           u_t[fOffset + c] += Bq[cidx] * coefficients_t[dOffset + b];
2175         }
2176       }
2177       PetscCall(PetscFEPushforward(fe, fegeom, 1, &u_t[fOffset]));
2178     }
2179     fOffset += Ncf;
2180     dOffset += Nbf;
2181   }
2182   return PETSC_SUCCESS;
2183 }
2184 
2185 PetscErrorCode PetscFEEvaluateFieldJets_Hybrid_Internal(PetscDS ds, PetscInt Nf, PetscInt rc, PetscInt qc, PetscTabulation Tab[], const PetscInt rf[], const PetscInt qf[], PetscTabulation Tabf[], PetscFEGeom *fegeom, const PetscScalar coefficients[], const PetscScalar coefficients_t[], PetscScalar u[], PetscScalar u_x[], PetscScalar u_t[])
2186 {
2187   PetscInt dOffset = 0, fOffset = 0, f, g;
2188 
2189   /* f is the field number in the DS, g is the field number in u[] */
2190   for (f = 0, g = 0; f < Nf; ++f) {
2191     PetscBool isCohesive;
2192     PetscInt  Ns, s;
2193 
2194     if (!Tab[f]) continue;
2195     PetscCall(PetscDSGetCohesive(ds, f, &isCohesive));
2196     Ns = isCohesive ? 1 : 2;
2197     {
2198       PetscTabulation T   = isCohesive ? Tab[f] : Tabf[f];
2199       PetscFE         fe  = (PetscFE)ds->disc[f];
2200       const PetscInt  dEt = T->cdim;
2201       const PetscInt  dE  = fegeom->dimEmbed;
2202       const PetscInt  Nq  = T->Np;
2203       const PetscInt  Nbf = T->Nb;
2204       const PetscInt  Ncf = T->Nc;
2205 
2206       for (s = 0; s < Ns; ++s, ++g) {
2207         const PetscInt   r  = isCohesive ? rc : rf[s];
2208         const PetscInt   q  = isCohesive ? qc : qf[s];
2209         const PetscReal *Bq = &T->T[0][(r * Nq + q) * Nbf * Ncf];
2210         const PetscReal *Dq = &T->T[1][(r * Nq + q) * Nbf * Ncf * dEt];
2211         PetscInt         b, c, d;
2212 
2213         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);
2214         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);
2215         for (c = 0; c < Ncf; ++c) u[fOffset + c] = 0.0;
2216         for (d = 0; d < dE * Ncf; ++d) u_x[fOffset * dE + d] = 0.0;
2217         for (b = 0; b < Nbf; ++b) {
2218           for (c = 0; c < Ncf; ++c) {
2219             const PetscInt cidx = b * Ncf + c;
2220 
2221             u[fOffset + c] += Bq[cidx] * coefficients[dOffset + b];
2222             for (d = 0; d < dEt; ++d) u_x[(fOffset + c) * dE + d] += Dq[cidx * dEt + d] * coefficients[dOffset + b];
2223           }
2224         }
2225         PetscCall(PetscFEPushforward(fe, fegeom, 1, &u[fOffset]));
2226         PetscCall(PetscFEPushforwardGradient(fe, fegeom, 1, &u_x[fOffset * dE]));
2227         if (u_t) {
2228           for (c = 0; c < Ncf; ++c) u_t[fOffset + c] = 0.0;
2229           for (b = 0; b < Nbf; ++b) {
2230             for (c = 0; c < Ncf; ++c) {
2231               const PetscInt cidx = b * Ncf + c;
2232 
2233               u_t[fOffset + c] += Bq[cidx] * coefficients_t[dOffset + b];
2234             }
2235           }
2236           PetscCall(PetscFEPushforward(fe, fegeom, 1, &u_t[fOffset]));
2237         }
2238         fOffset += Ncf;
2239         dOffset += Nbf;
2240       }
2241     }
2242   }
2243   return PETSC_SUCCESS;
2244 }
2245 
2246 PetscErrorCode PetscFEEvaluateFaceFields_Internal(PetscDS prob, PetscInt field, PetscInt faceLoc, const PetscScalar coefficients[], PetscScalar u[])
2247 {
2248   PetscFE         fe;
2249   PetscTabulation Tc;
2250   PetscInt        b, c;
2251 
2252   if (!prob) return PETSC_SUCCESS;
2253   PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fe));
2254   PetscCall(PetscFEGetFaceCentroidTabulation(fe, &Tc));
2255   {
2256     const PetscReal *faceBasis = Tc->T[0];
2257     const PetscInt   Nb        = Tc->Nb;
2258     const PetscInt   Nc        = Tc->Nc;
2259 
2260     for (c = 0; c < Nc; ++c) u[c] = 0.0;
2261     for (b = 0; b < Nb; ++b) {
2262       for (c = 0; c < Nc; ++c) u[c] += coefficients[b] * faceBasis[(faceLoc * Nb + b) * Nc + c];
2263     }
2264   }
2265   return PETSC_SUCCESS;
2266 }
2267 
2268 PetscErrorCode PetscFEUpdateElementVec_Internal(PetscFE fe, PetscTabulation T, PetscInt r, PetscScalar tmpBasis[], PetscScalar tmpBasisDer[], PetscInt e, PetscFEGeom *fegeom, PetscScalar f0[], PetscScalar f1[], PetscScalar elemVec[])
2269 {
2270   PetscFEGeom      pgeom;
2271   const PetscInt   dEt      = T->cdim;
2272   const PetscInt   dE       = fegeom->dimEmbed;
2273   const PetscInt   Nq       = T->Np;
2274   const PetscInt   Nb       = T->Nb;
2275   const PetscInt   Nc       = T->Nc;
2276   const PetscReal *basis    = &T->T[0][r * Nq * Nb * Nc];
2277   const PetscReal *basisDer = &T->T[1][r * Nq * Nb * Nc * dEt];
2278   PetscInt         q, b, c, d;
2279 
2280   for (q = 0; q < Nq; ++q) {
2281     for (b = 0; b < Nb; ++b) {
2282       for (c = 0; c < Nc; ++c) {
2283         const PetscInt bcidx = b * Nc + c;
2284 
2285         tmpBasis[bcidx] = basis[q * Nb * Nc + bcidx];
2286         for (d = 0; d < dEt; ++d) tmpBasisDer[bcidx * dE + d] = basisDer[q * Nb * Nc * dEt + bcidx * dEt + d];
2287         for (d = dEt; d < dE; ++d) tmpBasisDer[bcidx * dE + d] = 0.0;
2288       }
2289     }
2290     PetscCall(PetscFEGeomGetCellPoint(fegeom, e, q, &pgeom));
2291     PetscCall(PetscFEPushforward(fe, &pgeom, Nb, tmpBasis));
2292     PetscCall(PetscFEPushforwardGradient(fe, &pgeom, Nb, tmpBasisDer));
2293     for (b = 0; b < Nb; ++b) {
2294       for (c = 0; c < Nc; ++c) {
2295         const PetscInt bcidx = b * Nc + c;
2296         const PetscInt qcidx = q * Nc + c;
2297 
2298         elemVec[b] += tmpBasis[bcidx] * f0[qcidx];
2299         for (d = 0; d < dE; ++d) elemVec[b] += tmpBasisDer[bcidx * dE + d] * f1[qcidx * dE + d];
2300       }
2301     }
2302   }
2303   return PETSC_SUCCESS;
2304 }
2305 
2306 PetscErrorCode PetscFEUpdateElementVec_Hybrid_Internal(PetscFE fe, PetscTabulation T, PetscInt r, PetscInt s, PetscScalar tmpBasis[], PetscScalar tmpBasisDer[], PetscFEGeom *fegeom, PetscScalar f0[], PetscScalar f1[], PetscScalar elemVec[])
2307 {
2308   const PetscInt   dE       = T->cdim;
2309   const PetscInt   Nq       = T->Np;
2310   const PetscInt   Nb       = T->Nb;
2311   const PetscInt   Nc       = T->Nc;
2312   const PetscReal *basis    = &T->T[0][r * Nq * Nb * Nc];
2313   const PetscReal *basisDer = &T->T[1][r * Nq * Nb * Nc * dE];
2314   PetscInt         q, b, c, d;
2315 
2316   for (q = 0; q < Nq; ++q) {
2317     for (b = 0; b < Nb; ++b) {
2318       for (c = 0; c < Nc; ++c) {
2319         const PetscInt bcidx = b * Nc + c;
2320 
2321         tmpBasis[bcidx] = basis[q * Nb * Nc + bcidx];
2322         for (d = 0; d < dE; ++d) tmpBasisDer[bcidx * dE + d] = basisDer[q * Nb * Nc * dE + bcidx * dE + d];
2323       }
2324     }
2325     PetscCall(PetscFEPushforward(fe, fegeom, Nb, tmpBasis));
2326     // TODO This is currently broken since we do not pull the geometry down to the lower dimension
2327     // PetscCall(PetscFEPushforwardGradient(fe, fegeom, Nb, tmpBasisDer));
2328     for (b = 0; b < Nb; ++b) {
2329       for (c = 0; c < Nc; ++c) {
2330         const PetscInt bcidx = b * Nc + c;
2331         const PetscInt qcidx = q * Nc + c;
2332 
2333         elemVec[Nb * s + b] += tmpBasis[bcidx] * f0[qcidx];
2334         for (d = 0; d < dE; ++d) elemVec[Nb * s + b] += tmpBasisDer[bcidx * dE + d] * f1[qcidx * dE + d];
2335       }
2336     }
2337   }
2338   return PETSC_SUCCESS;
2339 }
2340 
2341 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 eOffset, PetscInt totDim, PetscInt offsetI, PetscInt offsetJ, PetscScalar elemMat[])
2342 {
2343   const PetscInt   cdim      = TI->cdim;
2344   const PetscInt   dE        = fegeom->dimEmbed;
2345   const PetscInt   NqI       = TI->Np;
2346   const PetscInt   NbI       = TI->Nb;
2347   const PetscInt   NcI       = TI->Nc;
2348   const PetscReal *basisI    = &TI->T[0][(r * NqI + q) * NbI * NcI];
2349   const PetscReal *basisDerI = &TI->T[1][(r * NqI + q) * NbI * NcI * cdim];
2350   const PetscInt   NqJ       = TJ->Np;
2351   const PetscInt   NbJ       = TJ->Nb;
2352   const PetscInt   NcJ       = TJ->Nc;
2353   const PetscReal *basisJ    = &TJ->T[0][(r * NqJ + q) * NbJ * NcJ];
2354   const PetscReal *basisDerJ = &TJ->T[1][(r * NqJ + q) * NbJ * NcJ * cdim];
2355   PetscInt         f, fc, g, gc, df, dg;
2356 
2357   for (f = 0; f < NbI; ++f) {
2358     for (fc = 0; fc < NcI; ++fc) {
2359       const PetscInt fidx = f * NcI + fc; /* Test function basis index */
2360 
2361       tmpBasisI[fidx] = basisI[fidx];
2362       for (df = 0; df < cdim; ++df) tmpBasisDerI[fidx * dE + df] = basisDerI[fidx * cdim + df];
2363     }
2364   }
2365   PetscCall(PetscFEPushforward(feI, fegeom, NbI, tmpBasisI));
2366   PetscCall(PetscFEPushforwardGradient(feI, fegeom, NbI, tmpBasisDerI));
2367   for (g = 0; g < NbJ; ++g) {
2368     for (gc = 0; gc < NcJ; ++gc) {
2369       const PetscInt gidx = g * NcJ + gc; /* Trial function basis index */
2370 
2371       tmpBasisJ[gidx] = basisJ[gidx];
2372       for (dg = 0; dg < cdim; ++dg) tmpBasisDerJ[gidx * dE + dg] = basisDerJ[gidx * cdim + dg];
2373     }
2374   }
2375   PetscCall(PetscFEPushforward(feJ, fegeom, NbJ, tmpBasisJ));
2376   PetscCall(PetscFEPushforwardGradient(feJ, fegeom, NbJ, tmpBasisDerJ));
2377   for (f = 0; f < NbI; ++f) {
2378     for (fc = 0; fc < NcI; ++fc) {
2379       const PetscInt fidx = f * NcI + fc; /* Test function basis index */
2380       const PetscInt i    = offsetI + f;  /* Element matrix row */
2381       for (g = 0; g < NbJ; ++g) {
2382         for (gc = 0; gc < NcJ; ++gc) {
2383           const PetscInt gidx = g * NcJ + gc; /* Trial function basis index */
2384           const PetscInt j    = offsetJ + g;  /* Element matrix column */
2385           const PetscInt fOff = eOffset + i * totDim + j;
2386 
2387           elemMat[fOff] += tmpBasisI[fidx] * g0[fc * NcJ + gc] * tmpBasisJ[gidx];
2388           for (df = 0; df < dE; ++df) {
2389             elemMat[fOff] += tmpBasisI[fidx] * g1[(fc * NcJ + gc) * dE + df] * tmpBasisDerJ[gidx * dE + df];
2390             elemMat[fOff] += tmpBasisDerI[fidx * dE + df] * g2[(fc * NcJ + gc) * dE + df] * tmpBasisJ[gidx];
2391             for (dg = 0; dg < dE; ++dg) elemMat[fOff] += tmpBasisDerI[fidx * dE + df] * g3[((fc * NcJ + gc) * dE + df) * dE + dg] * tmpBasisDerJ[gidx * dE + dg];
2392           }
2393         }
2394       }
2395     }
2396   }
2397   return PETSC_SUCCESS;
2398 }
2399 
2400 PetscErrorCode PetscFEUpdateElementMat_Hybrid_Internal(PetscFE feI, PetscBool isHybridI, PetscFE feJ, PetscBool isHybridJ, PetscInt r, PetscInt s, 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[])
2401 {
2402   const PetscInt   dE        = TI->cdim;
2403   const PetscInt   NqI       = TI->Np;
2404   const PetscInt   NbI       = TI->Nb;
2405   const PetscInt   NcI       = TI->Nc;
2406   const PetscReal *basisI    = &TI->T[0][(r * NqI + q) * NbI * NcI];
2407   const PetscReal *basisDerI = &TI->T[1][(r * NqI + q) * NbI * NcI * dE];
2408   const PetscInt   NqJ       = TJ->Np;
2409   const PetscInt   NbJ       = TJ->Nb;
2410   const PetscInt   NcJ       = TJ->Nc;
2411   const PetscReal *basisJ    = &TJ->T[0][(r * NqJ + q) * NbJ * NcJ];
2412   const PetscReal *basisDerJ = &TJ->T[1][(r * NqJ + q) * NbJ * NcJ * dE];
2413   const PetscInt   so        = isHybridI ? 0 : s;
2414   const PetscInt   to        = isHybridJ ? 0 : s;
2415   PetscInt         f, fc, g, gc, df, dg;
2416 
2417   for (f = 0; f < NbI; ++f) {
2418     for (fc = 0; fc < NcI; ++fc) {
2419       const PetscInt fidx = f * NcI + fc; /* Test function basis index */
2420 
2421       tmpBasisI[fidx] = basisI[fidx];
2422       for (df = 0; df < dE; ++df) tmpBasisDerI[fidx * dE + df] = basisDerI[fidx * dE + df];
2423     }
2424   }
2425   PetscCall(PetscFEPushforward(feI, fegeom, NbI, tmpBasisI));
2426   PetscCall(PetscFEPushforwardGradient(feI, fegeom, NbI, tmpBasisDerI));
2427   for (g = 0; g < NbJ; ++g) {
2428     for (gc = 0; gc < NcJ; ++gc) {
2429       const PetscInt gidx = g * NcJ + gc; /* Trial function basis index */
2430 
2431       tmpBasisJ[gidx] = basisJ[gidx];
2432       for (dg = 0; dg < dE; ++dg) tmpBasisDerJ[gidx * dE + dg] = basisDerJ[gidx * dE + dg];
2433     }
2434   }
2435   PetscCall(PetscFEPushforward(feJ, fegeom, NbJ, tmpBasisJ));
2436   // TODO This is currently broken since we do not pull the geometry down to the lower dimension
2437   // PetscCall(PetscFEPushforwardGradient(feJ, fegeom, NbJ, tmpBasisDerJ));
2438   for (f = 0; f < NbI; ++f) {
2439     for (fc = 0; fc < NcI; ++fc) {
2440       const PetscInt fidx = f * NcI + fc;           /* Test function basis index */
2441       const PetscInt i    = offsetI + NbI * so + f; /* Element matrix row */
2442       for (g = 0; g < NbJ; ++g) {
2443         for (gc = 0; gc < NcJ; ++gc) {
2444           const PetscInt gidx = g * NcJ + gc;           /* Trial function basis index */
2445           const PetscInt j    = offsetJ + NbJ * to + g; /* Element matrix column */
2446           const PetscInt fOff = eOffset + i * totDim + j;
2447 
2448           elemMat[fOff] += tmpBasisI[fidx] * g0[fc * NcJ + gc] * tmpBasisJ[gidx];
2449           for (df = 0; df < dE; ++df) {
2450             elemMat[fOff] += tmpBasisI[fidx] * g1[(fc * NcJ + gc) * dE + df] * tmpBasisDerJ[gidx * dE + df];
2451             elemMat[fOff] += tmpBasisDerI[fidx * dE + df] * g2[(fc * NcJ + gc) * dE + df] * tmpBasisJ[gidx];
2452             for (dg = 0; dg < dE; ++dg) elemMat[fOff] += tmpBasisDerI[fidx * dE + df] * g3[((fc * NcJ + gc) * dE + df) * dE + dg] * tmpBasisDerJ[gidx * dE + dg];
2453           }
2454         }
2455       }
2456     }
2457   }
2458   return PETSC_SUCCESS;
2459 }
2460 
2461 PetscErrorCode PetscFECreateCellGeometry(PetscFE fe, PetscQuadrature quad, PetscFEGeom *cgeom)
2462 {
2463   PetscDualSpace  dsp;
2464   DM              dm;
2465   PetscQuadrature quadDef;
2466   PetscInt        dim, cdim, Nq;
2467 
2468   PetscFunctionBegin;
2469   PetscCall(PetscFEGetDualSpace(fe, &dsp));
2470   PetscCall(PetscDualSpaceGetDM(dsp, &dm));
2471   PetscCall(DMGetDimension(dm, &dim));
2472   PetscCall(DMGetCoordinateDim(dm, &cdim));
2473   PetscCall(PetscFEGetQuadrature(fe, &quadDef));
2474   quad = quad ? quad : quadDef;
2475   PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
2476   PetscCall(PetscMalloc1(Nq * cdim, &cgeom->v));
2477   PetscCall(PetscMalloc1(Nq * cdim * cdim, &cgeom->J));
2478   PetscCall(PetscMalloc1(Nq * cdim * cdim, &cgeom->invJ));
2479   PetscCall(PetscMalloc1(Nq, &cgeom->detJ));
2480   cgeom->dim       = dim;
2481   cgeom->dimEmbed  = cdim;
2482   cgeom->numCells  = 1;
2483   cgeom->numPoints = Nq;
2484   PetscCall(DMPlexComputeCellGeometryFEM(dm, 0, quad, cgeom->v, cgeom->J, cgeom->invJ, cgeom->detJ));
2485   PetscFunctionReturn(PETSC_SUCCESS);
2486 }
2487 
2488 PetscErrorCode PetscFEDestroyCellGeometry(PetscFE fe, PetscFEGeom *cgeom)
2489 {
2490   PetscFunctionBegin;
2491   PetscCall(PetscFree(cgeom->v));
2492   PetscCall(PetscFree(cgeom->J));
2493   PetscCall(PetscFree(cgeom->invJ));
2494   PetscCall(PetscFree(cgeom->detJ));
2495   PetscFunctionReturn(PETSC_SUCCESS);
2496 }
2497