xref: /petsc/src/dm/dt/fe/interface/fe.c (revision f1a0fde416081eec0802fc3fe2b62de3a9ab880c)
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, 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   Nq      = T[f]->Np;
2135     const PetscInt   Nbf     = T[f]->Nb;
2136     const PetscInt   Ncf     = T[f]->Nc;
2137     const PetscReal *Bq      = &T[f]->T[0][(r * Nq + q) * Nbf * Ncf];
2138     const PetscReal *Dq      = &T[f]->T[1][(r * Nq + q) * Nbf * Ncf * cdim];
2139     const PetscReal *Hq      = k > 1 ? &T[f]->T[2][(r * Nq + q) * Nbf * Ncf * cdim * cdim] : NULL;
2140     PetscInt         hOffset = 0, b, c, d;
2141 
2142     PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
2143     for (c = 0; c < Ncf; ++c) u[fOffset + c] = 0.0;
2144     for (d = 0; d < cdim * Ncf; ++d) u_x[fOffset * cdim + d] = 0.0;
2145     for (b = 0; b < Nbf; ++b) {
2146       for (c = 0; c < Ncf; ++c) {
2147         const PetscInt cidx = b * Ncf + c;
2148 
2149         u[fOffset + c] += Bq[cidx] * coefficients[dOffset + b];
2150         for (d = 0; d < cdim; ++d) u_x[(fOffset + c) * cdim + d] += Dq[cidx * cdim + d] * coefficients[dOffset + b];
2151       }
2152     }
2153     if (k > 1) {
2154       for (g = 0; g < Nf; ++g) hOffset += T[g]->Nc * cdim;
2155       for (d = 0; d < cdim * cdim * Ncf; ++d) u_x[hOffset + fOffset * cdim * cdim + d] = 0.0;
2156       for (b = 0; b < Nbf; ++b) {
2157         for (c = 0; c < Ncf; ++c) {
2158           const PetscInt cidx = b * Ncf + c;
2159 
2160           for (d = 0; d < cdim * cdim; ++d) u_x[hOffset + (fOffset + c) * cdim * cdim + d] += Hq[cidx * cdim * cdim + d] * coefficients[dOffset + b];
2161         }
2162       }
2163       PetscCall(PetscFEPushforwardHessian(fe, fegeom, 1, &u_x[hOffset + fOffset * cdim * cdim]));
2164     }
2165     PetscCall(PetscFEPushforward(fe, fegeom, 1, &u[fOffset]));
2166     PetscCall(PetscFEPushforwardGradient(fe, fegeom, 1, &u_x[fOffset * cdim]));
2167     if (u_t) {
2168       for (c = 0; c < Ncf; ++c) u_t[fOffset + c] = 0.0;
2169       for (b = 0; b < Nbf; ++b) {
2170         for (c = 0; c < Ncf; ++c) {
2171           const PetscInt cidx = b * Ncf + c;
2172 
2173           u_t[fOffset + c] += Bq[cidx] * coefficients_t[dOffset + b];
2174         }
2175       }
2176       PetscCall(PetscFEPushforward(fe, fegeom, 1, &u_t[fOffset]));
2177     }
2178     fOffset += Ncf;
2179     dOffset += Nbf;
2180   }
2181   return PETSC_SUCCESS;
2182 }
2183 
2184 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[])
2185 {
2186   PetscInt dOffset = 0, fOffset = 0, f, g;
2187 
2188   /* f is the field number in the DS, g is the field number in u[] */
2189   for (f = 0, g = 0; f < Nf; ++f) {
2190     PetscBool isCohesive;
2191     PetscInt  Ns, s;
2192 
2193     if (!Tab[f]) continue;
2194     PetscCall(PetscDSGetCohesive(ds, f, &isCohesive));
2195     Ns = isCohesive ? 1 : 2;
2196     {
2197       PetscTabulation T   = isCohesive ? Tab[f] : Tabf[f];
2198       PetscFE         fe  = (PetscFE)ds->disc[f];
2199       const PetscInt  dEt = T->cdim;
2200       const PetscInt  dE  = fegeom->dimEmbed;
2201       const PetscInt  Nq  = T->Np;
2202       const PetscInt  Nbf = T->Nb;
2203       const PetscInt  Ncf = T->Nc;
2204 
2205       for (s = 0; s < Ns; ++s, ++g) {
2206         const PetscInt   r  = isCohesive ? rc : rf[s];
2207         const PetscInt   q  = isCohesive ? qc : qf[s];
2208         const PetscReal *Bq = &T->T[0][(r * Nq + q) * Nbf * Ncf];
2209         const PetscReal *Dq = &T->T[1][(r * Nq + q) * Nbf * Ncf * dEt];
2210         PetscInt         b, c, d;
2211 
2212         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);
2213         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);
2214         for (c = 0; c < Ncf; ++c) u[fOffset + c] = 0.0;
2215         for (d = 0; d < dE * Ncf; ++d) u_x[fOffset * dE + d] = 0.0;
2216         for (b = 0; b < Nbf; ++b) {
2217           for (c = 0; c < Ncf; ++c) {
2218             const PetscInt cidx = b * Ncf + c;
2219 
2220             u[fOffset + c] += Bq[cidx] * coefficients[dOffset + b];
2221             for (d = 0; d < dEt; ++d) u_x[(fOffset + c) * dE + d] += Dq[cidx * dEt + d] * coefficients[dOffset + b];
2222           }
2223         }
2224         PetscCall(PetscFEPushforward(fe, fegeom, 1, &u[fOffset]));
2225         PetscCall(PetscFEPushforwardGradient(fe, fegeom, 1, &u_x[fOffset * dE]));
2226         if (u_t) {
2227           for (c = 0; c < Ncf; ++c) u_t[fOffset + c] = 0.0;
2228           for (b = 0; b < Nbf; ++b) {
2229             for (c = 0; c < Ncf; ++c) {
2230               const PetscInt cidx = b * Ncf + c;
2231 
2232               u_t[fOffset + c] += Bq[cidx] * coefficients_t[dOffset + b];
2233             }
2234           }
2235           PetscCall(PetscFEPushforward(fe, fegeom, 1, &u_t[fOffset]));
2236         }
2237         fOffset += Ncf;
2238         dOffset += Nbf;
2239       }
2240     }
2241   }
2242   return PETSC_SUCCESS;
2243 }
2244 
2245 PetscErrorCode PetscFEEvaluateFaceFields_Internal(PetscDS prob, PetscInt field, PetscInt faceLoc, const PetscScalar coefficients[], PetscScalar u[])
2246 {
2247   PetscFE         fe;
2248   PetscTabulation Tc;
2249   PetscInt        b, c;
2250 
2251   if (!prob) return PETSC_SUCCESS;
2252   PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fe));
2253   PetscCall(PetscFEGetFaceCentroidTabulation(fe, &Tc));
2254   {
2255     const PetscReal *faceBasis = Tc->T[0];
2256     const PetscInt   Nb        = Tc->Nb;
2257     const PetscInt   Nc        = Tc->Nc;
2258 
2259     for (c = 0; c < Nc; ++c) u[c] = 0.0;
2260     for (b = 0; b < Nb; ++b) {
2261       for (c = 0; c < Nc; ++c) u[c] += coefficients[b] * faceBasis[(faceLoc * Nb + b) * Nc + c];
2262     }
2263   }
2264   return PETSC_SUCCESS;
2265 }
2266 
2267 PetscErrorCode PetscFEUpdateElementVec_Internal(PetscFE fe, PetscTabulation T, PetscInt r, PetscScalar tmpBasis[], PetscScalar tmpBasisDer[], PetscInt e, PetscFEGeom *fegeom, PetscScalar f0[], PetscScalar f1[], PetscScalar elemVec[])
2268 {
2269   PetscFEGeom      pgeom;
2270   const PetscInt   dEt      = T->cdim;
2271   const PetscInt   dE       = fegeom->dimEmbed;
2272   const PetscInt   Nq       = T->Np;
2273   const PetscInt   Nb       = T->Nb;
2274   const PetscInt   Nc       = T->Nc;
2275   const PetscReal *basis    = &T->T[0][r * Nq * Nb * Nc];
2276   const PetscReal *basisDer = &T->T[1][r * Nq * Nb * Nc * dEt];
2277   PetscInt         q, b, c, d;
2278 
2279   for (q = 0; q < Nq; ++q) {
2280     for (b = 0; b < Nb; ++b) {
2281       for (c = 0; c < Nc; ++c) {
2282         const PetscInt bcidx = b * Nc + c;
2283 
2284         tmpBasis[bcidx] = basis[q * Nb * Nc + bcidx];
2285         for (d = 0; d < dEt; ++d) tmpBasisDer[bcidx * dE + d] = basisDer[q * Nb * Nc * dEt + bcidx * dEt + d];
2286         for (d = dEt; d < dE; ++d) tmpBasisDer[bcidx * dE + d] = 0.0;
2287       }
2288     }
2289     PetscCall(PetscFEGeomGetCellPoint(fegeom, e, q, &pgeom));
2290     PetscCall(PetscFEPushforward(fe, &pgeom, Nb, tmpBasis));
2291     PetscCall(PetscFEPushforwardGradient(fe, &pgeom, Nb, tmpBasisDer));
2292     for (b = 0; b < Nb; ++b) {
2293       for (c = 0; c < Nc; ++c) {
2294         const PetscInt bcidx = b * Nc + c;
2295         const PetscInt qcidx = q * Nc + c;
2296 
2297         elemVec[b] += tmpBasis[bcidx] * f0[qcidx];
2298         for (d = 0; d < dE; ++d) elemVec[b] += tmpBasisDer[bcidx * dE + d] * f1[qcidx * dE + d];
2299       }
2300     }
2301   }
2302   return PETSC_SUCCESS;
2303 }
2304 
2305 PetscErrorCode PetscFEUpdateElementVec_Hybrid_Internal(PetscFE fe, PetscTabulation T, PetscInt r, PetscInt s, PetscScalar tmpBasis[], PetscScalar tmpBasisDer[], PetscFEGeom *fegeom, PetscScalar f0[], PetscScalar f1[], PetscScalar elemVec[])
2306 {
2307   const PetscInt   dE       = T->cdim;
2308   const PetscInt   Nq       = T->Np;
2309   const PetscInt   Nb       = T->Nb;
2310   const PetscInt   Nc       = T->Nc;
2311   const PetscReal *basis    = &T->T[0][r * Nq * Nb * Nc];
2312   const PetscReal *basisDer = &T->T[1][r * Nq * Nb * Nc * dE];
2313   PetscInt         q, b, c, d;
2314 
2315   for (q = 0; q < Nq; ++q) {
2316     for (b = 0; b < Nb; ++b) {
2317       for (c = 0; c < Nc; ++c) {
2318         const PetscInt bcidx = b * Nc + c;
2319 
2320         tmpBasis[bcidx] = basis[q * Nb * Nc + bcidx];
2321         for (d = 0; d < dE; ++d) tmpBasisDer[bcidx * dE + d] = basisDer[q * Nb * Nc * dE + bcidx * dE + d];
2322       }
2323     }
2324     PetscCall(PetscFEPushforward(fe, fegeom, Nb, tmpBasis));
2325     PetscCall(PetscFEPushforwardGradient(fe, fegeom, Nb, tmpBasisDer));
2326     for (b = 0; b < Nb; ++b) {
2327       for (c = 0; c < Nc; ++c) {
2328         const PetscInt bcidx = b * Nc + c;
2329         const PetscInt qcidx = q * Nc + c;
2330 
2331         elemVec[Nb * s + b] += tmpBasis[bcidx] * f0[qcidx];
2332         for (d = 0; d < dE; ++d) elemVec[Nb * s + b] += tmpBasisDer[bcidx * dE + d] * f1[qcidx * dE + d];
2333       }
2334     }
2335   }
2336   return PETSC_SUCCESS;
2337 }
2338 
2339 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[])
2340 {
2341   const PetscInt   dE        = TI->cdim;
2342   const PetscInt   NqI       = TI->Np;
2343   const PetscInt   NbI       = TI->Nb;
2344   const PetscInt   NcI       = TI->Nc;
2345   const PetscReal *basisI    = &TI->T[0][(r * NqI + q) * NbI * NcI];
2346   const PetscReal *basisDerI = &TI->T[1][(r * NqI + q) * NbI * NcI * dE];
2347   const PetscInt   NqJ       = TJ->Np;
2348   const PetscInt   NbJ       = TJ->Nb;
2349   const PetscInt   NcJ       = TJ->Nc;
2350   const PetscReal *basisJ    = &TJ->T[0][(r * NqJ + q) * NbJ * NcJ];
2351   const PetscReal *basisDerJ = &TJ->T[1][(r * NqJ + q) * NbJ * NcJ * dE];
2352   PetscInt         f, fc, g, gc, df, dg;
2353 
2354   for (f = 0; f < NbI; ++f) {
2355     for (fc = 0; fc < NcI; ++fc) {
2356       const PetscInt fidx = f * NcI + fc; /* Test function basis index */
2357 
2358       tmpBasisI[fidx] = basisI[fidx];
2359       for (df = 0; df < dE; ++df) tmpBasisDerI[fidx * dE + df] = basisDerI[fidx * dE + df];
2360     }
2361   }
2362   PetscCall(PetscFEPushforward(feI, fegeom, NbI, tmpBasisI));
2363   PetscCall(PetscFEPushforwardGradient(feI, fegeom, NbI, tmpBasisDerI));
2364   for (g = 0; g < NbJ; ++g) {
2365     for (gc = 0; gc < NcJ; ++gc) {
2366       const PetscInt gidx = g * NcJ + gc; /* Trial function basis index */
2367 
2368       tmpBasisJ[gidx] = basisJ[gidx];
2369       for (dg = 0; dg < dE; ++dg) tmpBasisDerJ[gidx * dE + dg] = basisDerJ[gidx * dE + dg];
2370     }
2371   }
2372   PetscCall(PetscFEPushforward(feJ, fegeom, NbJ, tmpBasisJ));
2373   PetscCall(PetscFEPushforwardGradient(feJ, fegeom, NbJ, tmpBasisDerJ));
2374   for (f = 0; f < NbI; ++f) {
2375     for (fc = 0; fc < NcI; ++fc) {
2376       const PetscInt fidx = f * NcI + fc; /* Test function basis index */
2377       const PetscInt i    = offsetI + f;  /* Element matrix row */
2378       for (g = 0; g < NbJ; ++g) {
2379         for (gc = 0; gc < NcJ; ++gc) {
2380           const PetscInt gidx = g * NcJ + gc; /* Trial function basis index */
2381           const PetscInt j    = offsetJ + g;  /* Element matrix column */
2382           const PetscInt fOff = eOffset + i * totDim + j;
2383 
2384           elemMat[fOff] += tmpBasisI[fidx] * g0[fc * NcJ + gc] * tmpBasisJ[gidx];
2385           for (df = 0; df < dE; ++df) {
2386             elemMat[fOff] += tmpBasisI[fidx] * g1[(fc * NcJ + gc) * dE + df] * tmpBasisDerJ[gidx * dE + df];
2387             elemMat[fOff] += tmpBasisDerI[fidx * dE + df] * g2[(fc * NcJ + gc) * dE + df] * tmpBasisJ[gidx];
2388             for (dg = 0; dg < dE; ++dg) elemMat[fOff] += tmpBasisDerI[fidx * dE + df] * g3[((fc * NcJ + gc) * dE + df) * dE + dg] * tmpBasisDerJ[gidx * dE + dg];
2389           }
2390         }
2391       }
2392     }
2393   }
2394   return PETSC_SUCCESS;
2395 }
2396 
2397 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[])
2398 {
2399   const PetscInt   dE        = TI->cdim;
2400   const PetscInt   NqI       = TI->Np;
2401   const PetscInt   NbI       = TI->Nb;
2402   const PetscInt   NcI       = TI->Nc;
2403   const PetscReal *basisI    = &TI->T[0][(r * NqI + q) * NbI * NcI];
2404   const PetscReal *basisDerI = &TI->T[1][(r * NqI + q) * NbI * NcI * dE];
2405   const PetscInt   NqJ       = TJ->Np;
2406   const PetscInt   NbJ       = TJ->Nb;
2407   const PetscInt   NcJ       = TJ->Nc;
2408   const PetscReal *basisJ    = &TJ->T[0][(r * NqJ + q) * NbJ * NcJ];
2409   const PetscReal *basisDerJ = &TJ->T[1][(r * NqJ + q) * NbJ * NcJ * dE];
2410   const PetscInt   so        = isHybridI ? 0 : s;
2411   const PetscInt   to        = isHybridJ ? 0 : s;
2412   PetscInt         f, fc, g, gc, df, dg;
2413 
2414   for (f = 0; f < NbI; ++f) {
2415     for (fc = 0; fc < NcI; ++fc) {
2416       const PetscInt fidx = f * NcI + fc; /* Test function basis index */
2417 
2418       tmpBasisI[fidx] = basisI[fidx];
2419       for (df = 0; df < dE; ++df) tmpBasisDerI[fidx * dE + df] = basisDerI[fidx * dE + df];
2420     }
2421   }
2422   PetscCall(PetscFEPushforward(feI, fegeom, NbI, tmpBasisI));
2423   PetscCall(PetscFEPushforwardGradient(feI, fegeom, NbI, tmpBasisDerI));
2424   for (g = 0; g < NbJ; ++g) {
2425     for (gc = 0; gc < NcJ; ++gc) {
2426       const PetscInt gidx = g * NcJ + gc; /* Trial function basis index */
2427 
2428       tmpBasisJ[gidx] = basisJ[gidx];
2429       for (dg = 0; dg < dE; ++dg) tmpBasisDerJ[gidx * dE + dg] = basisDerJ[gidx * dE + dg];
2430     }
2431   }
2432   PetscCall(PetscFEPushforward(feJ, fegeom, NbJ, tmpBasisJ));
2433   PetscCall(PetscFEPushforwardGradient(feJ, fegeom, NbJ, tmpBasisDerJ));
2434   for (f = 0; f < NbI; ++f) {
2435     for (fc = 0; fc < NcI; ++fc) {
2436       const PetscInt fidx = f * NcI + fc;           /* Test function basis index */
2437       const PetscInt i    = offsetI + NbI * so + f; /* Element matrix row */
2438       for (g = 0; g < NbJ; ++g) {
2439         for (gc = 0; gc < NcJ; ++gc) {
2440           const PetscInt gidx = g * NcJ + gc;           /* Trial function basis index */
2441           const PetscInt j    = offsetJ + NbJ * to + g; /* Element matrix column */
2442           const PetscInt fOff = eOffset + i * totDim + j;
2443 
2444           elemMat[fOff] += tmpBasisI[fidx] * g0[fc * NcJ + gc] * tmpBasisJ[gidx];
2445           for (df = 0; df < dE; ++df) {
2446             elemMat[fOff] += tmpBasisI[fidx] * g1[(fc * NcJ + gc) * dE + df] * tmpBasisDerJ[gidx * dE + df];
2447             elemMat[fOff] += tmpBasisDerI[fidx * dE + df] * g2[(fc * NcJ + gc) * dE + df] * tmpBasisJ[gidx];
2448             for (dg = 0; dg < dE; ++dg) elemMat[fOff] += tmpBasisDerI[fidx * dE + df] * g3[((fc * NcJ + gc) * dE + df) * dE + dg] * tmpBasisDerJ[gidx * dE + dg];
2449           }
2450         }
2451       }
2452     }
2453   }
2454   return PETSC_SUCCESS;
2455 }
2456 
2457 PetscErrorCode PetscFECreateCellGeometry(PetscFE fe, PetscQuadrature quad, PetscFEGeom *cgeom)
2458 {
2459   PetscDualSpace  dsp;
2460   DM              dm;
2461   PetscQuadrature quadDef;
2462   PetscInt        dim, cdim, Nq;
2463 
2464   PetscFunctionBegin;
2465   PetscCall(PetscFEGetDualSpace(fe, &dsp));
2466   PetscCall(PetscDualSpaceGetDM(dsp, &dm));
2467   PetscCall(DMGetDimension(dm, &dim));
2468   PetscCall(DMGetCoordinateDim(dm, &cdim));
2469   PetscCall(PetscFEGetQuadrature(fe, &quadDef));
2470   quad = quad ? quad : quadDef;
2471   PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
2472   PetscCall(PetscMalloc1(Nq * cdim, &cgeom->v));
2473   PetscCall(PetscMalloc1(Nq * cdim * cdim, &cgeom->J));
2474   PetscCall(PetscMalloc1(Nq * cdim * cdim, &cgeom->invJ));
2475   PetscCall(PetscMalloc1(Nq, &cgeom->detJ));
2476   cgeom->dim       = dim;
2477   cgeom->dimEmbed  = cdim;
2478   cgeom->numCells  = 1;
2479   cgeom->numPoints = Nq;
2480   PetscCall(DMPlexComputeCellGeometryFEM(dm, 0, quad, cgeom->v, cgeom->J, cgeom->invJ, cgeom->detJ));
2481   PetscFunctionReturn(PETSC_SUCCESS);
2482 }
2483 
2484 PetscErrorCode PetscFEDestroyCellGeometry(PetscFE fe, PetscFEGeom *cgeom)
2485 {
2486   PetscFunctionBegin;
2487   PetscCall(PetscFree(cgeom->v));
2488   PetscCall(PetscFree(cgeom->J));
2489   PetscCall(PetscFree(cgeom->invJ));
2490   PetscCall(PetscFree(cgeom->detJ));
2491   PetscFunctionReturn(PETSC_SUCCESS);
2492 }
2493