xref: /petsc/src/dm/dt/fe/interface/fe.c (revision 98d129c30f3ee9fdddc40fdbc5a989b7be64f888)
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   Example Usage:
68 .vb
69     PetscFERegister("my_fe", MyPetscFECreate);
70 .ve
71 
72   Then, your PetscFE type can be chosen with the procedural interface via
73 .vb
74     PetscFECreate(MPI_Comm, PetscFE *);
75     PetscFESetType(PetscFE, "my_fe");
76 .ve
77   or at runtime via the option
78 .vb
79     -petscfe_type my_fe
80 .ve
81 
82   Level: advanced
83 
84   Note:
85   `PetscFERegister()` may be called multiple times to add several user-defined `PetscFE`s
86 
87 .seealso: `PetscFE`, `PetscFEType`, `PetscFERegisterAll()`, `PetscFERegisterDestroy()`
88 @*/
89 PetscErrorCode PetscFERegister(const char sname[], PetscErrorCode (*function)(PetscFE))
90 {
91   PetscFunctionBegin;
92   PetscCall(PetscFunctionListAdd(&PetscFEList, sname, function));
93   PetscFunctionReturn(PETSC_SUCCESS);
94 }
95 
96 /*@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   PetscAssertPointer(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   PetscAssertPointer(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   PetscAssertPointer(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()`
437 @*/
438 PetscErrorCode PetscFEGetNumComponents(PetscFE fem, PetscInt *comp)
439 {
440   PetscFunctionBegin;
441   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
442   PetscAssertPointer(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) PetscAssertPointer(blockSize, 2);
497   if (numBlocks) PetscAssertPointer(numBlocks, 3);
498   if (batchSize) PetscAssertPointer(batchSize, 4);
499   if (numBatches) PetscAssertPointer(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   PetscAssertPointer(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 Notes:
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   PetscAssertPointer(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   PetscAssertPointer(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 Notes:
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   PetscAssertPointer(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()`
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   PetscAssertPointer(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   PetscAssertPointer(T, 3);
800   PetscCall(PetscQuadratureGetData(fem->quadrature, NULL, NULL, &npoints, &points, NULL));
801   if (!fem->T) PetscCall(PetscFECreateTabulation(fem, 1, npoints, points, k, &fem->T));
802   PetscCheck(!fem->T || k <= fem->T->K || (!fem->T->cdim && !fem->T->K), PetscObjectComm((PetscObject)fem), PETSC_ERR_ARG_OUTOFRANGE, "Requested %" PetscInt_FMT " derivatives, but only tabulated %" PetscInt_FMT, k, fem->T->K);
803   *T = fem->T;
804   PetscFunctionReturn(PETSC_SUCCESS);
805 }
806 
807 /*@C
808   PetscFEGetFaceTabulation - Returns the tabulation of the basis functions at the face quadrature points for each face of the reference cell
809 
810   Not Collective
811 
812   Input Parameters:
813 + fem - The `PetscFE` object
814 - k   - The highest derivative we need to tabulate, very often 1
815 
816   Output Parameter:
817 . Tf - The basis function values and derivatives at face quadrature points
818 
819   Level: intermediate
820 
821   Note:
822 .vb
823   T->T[0] = Bf[((f*Nq + q)*pdim + i)*Nc + c] is the value at point f,q for basis function i and component c
824   T->T[1] = Df[(((f*Nq + q)*pdim + i)*Nc + c)*dim + d] is the derivative value at point f,q for basis function i, component c, in direction d
825   T->T[2] = Hf[((((f*Nq + q)*pdim + i)*Nc + c)*dim + d)*dim + e] is the value at point f,q for basis function i, component c, in directions d and e
826 .ve
827 
828 .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscTabulation`, `PetscFEGetCellTabulation()`, `PetscFECreateTabulation()`, `PetscTabulationDestroy()`
829 @*/
830 PetscErrorCode PetscFEGetFaceTabulation(PetscFE fem, PetscInt k, PetscTabulation *Tf)
831 {
832   PetscFunctionBegin;
833   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
834   PetscAssertPointer(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   PetscAssertPointer(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
936   T->function i, component c, in directions d and e
937 .ve
938 
939 .seealso: `PetscTabulation`, `PetscFEGetCellTabulation()`, `PetscTabulationDestroy()`
940 @*/
941 PetscErrorCode PetscFECreateTabulation(PetscFE fem, PetscInt nrepl, PetscInt npoints, const PetscReal points[], PetscInt K, PetscTabulation *T)
942 {
943   DM             dm;
944   PetscDualSpace Q;
945   PetscInt       Nb;   /* Dimension of FE space P */
946   PetscInt       Nc;   /* Field components */
947   PetscInt       cdim; /* Reference coordinate dimension */
948   PetscInt       k;
949 
950   PetscFunctionBegin;
951   if (!npoints || !fem->dualSpace || K < 0) {
952     *T = NULL;
953     PetscFunctionReturn(PETSC_SUCCESS);
954   }
955   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
956   PetscAssertPointer(points, 4);
957   PetscAssertPointer(T, 6);
958   PetscCall(PetscFEGetDualSpace(fem, &Q));
959   PetscCall(PetscDualSpaceGetDM(Q, &dm));
960   PetscCall(DMGetDimension(dm, &cdim));
961   PetscCall(PetscDualSpaceGetDimension(Q, &Nb));
962   PetscCall(PetscFEGetNumComponents(fem, &Nc));
963   PetscCall(PetscMalloc1(1, T));
964   (*T)->K    = !cdim ? 0 : K;
965   (*T)->Nr   = nrepl;
966   (*T)->Np   = npoints;
967   (*T)->Nb   = Nb;
968   (*T)->Nc   = Nc;
969   (*T)->cdim = cdim;
970   PetscCall(PetscMalloc1((*T)->K + 1, &(*T)->T));
971   for (k = 0; k <= (*T)->K; ++k) PetscCall(PetscCalloc1(nrepl * npoints * Nb * Nc * PetscPowInt(cdim, k), &(*T)->T[k]));
972   PetscUseTypeMethod(fem, createtabulation, nrepl * npoints, points, K, *T);
973   PetscFunctionReturn(PETSC_SUCCESS);
974 }
975 
976 /*@C
977   PetscFEComputeTabulation - Tabulates the basis functions, and perhaps derivatives, at the points provided.
978 
979   Not Collective
980 
981   Input Parameters:
982 + fem     - The `PetscFE` object
983 . npoints - The number of tabulation points
984 . points  - The tabulation point coordinates
985 . K       - The number of derivatives calculated
986 - T       - An existing tabulation object with enough allocated space
987 
988   Output Parameter:
989 . T - The basis function values and derivatives at tabulation points
990 
991   Level: intermediate
992 
993   Note:
994 .vb
995   T->T[0] = B[(p*pdim + i)*Nc + c] is the value at point p for basis function i and component c
996   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
997   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
998 .ve
999 
1000 .seealso: `PetscTabulation`, `PetscFEGetCellTabulation()`, `PetscTabulationDestroy()`
1001 @*/
1002 PetscErrorCode PetscFEComputeTabulation(PetscFE fem, PetscInt npoints, const PetscReal points[], PetscInt K, PetscTabulation T)
1003 {
1004   PetscFunctionBeginHot;
1005   if (!npoints || !fem->dualSpace || K < 0) PetscFunctionReturn(PETSC_SUCCESS);
1006   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
1007   PetscAssertPointer(points, 3);
1008   PetscAssertPointer(T, 5);
1009   if (PetscDefined(USE_DEBUG)) {
1010     DM             dm;
1011     PetscDualSpace Q;
1012     PetscInt       Nb;   /* Dimension of FE space P */
1013     PetscInt       Nc;   /* Field components */
1014     PetscInt       cdim; /* Reference coordinate dimension */
1015 
1016     PetscCall(PetscFEGetDualSpace(fem, &Q));
1017     PetscCall(PetscDualSpaceGetDM(Q, &dm));
1018     PetscCall(DMGetDimension(dm, &cdim));
1019     PetscCall(PetscDualSpaceGetDimension(Q, &Nb));
1020     PetscCall(PetscFEGetNumComponents(fem, &Nc));
1021     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);
1022     PetscCheck(T->Nb == Nb, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Tabulation Nb %" PetscInt_FMT " must match requested Nb %" PetscInt_FMT, T->Nb, Nb);
1023     PetscCheck(T->Nc == Nc, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Tabulation Nc %" PetscInt_FMT " must match requested Nc %" PetscInt_FMT, T->Nc, Nc);
1024     PetscCheck(T->cdim == cdim, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Tabulation cdim %" PetscInt_FMT " must match requested cdim %" PetscInt_FMT, T->cdim, cdim);
1025   }
1026   T->Nr = 1;
1027   T->Np = npoints;
1028   PetscUseTypeMethod(fem, createtabulation, npoints, points, K, T);
1029   PetscFunctionReturn(PETSC_SUCCESS);
1030 }
1031 
1032 /*@C
1033   PetscTabulationDestroy - Frees memory from the associated tabulation.
1034 
1035   Not Collective
1036 
1037   Input Parameter:
1038 . T - The tabulation
1039 
1040   Level: intermediate
1041 
1042 .seealso: `PetscTabulation`, `PetscFECreateTabulation()`, `PetscFEGetCellTabulation()`
1043 @*/
1044 PetscErrorCode PetscTabulationDestroy(PetscTabulation *T)
1045 {
1046   PetscInt k;
1047 
1048   PetscFunctionBegin;
1049   PetscAssertPointer(T, 1);
1050   if (!T || !(*T)) PetscFunctionReturn(PETSC_SUCCESS);
1051   for (k = 0; k <= (*T)->K; ++k) PetscCall(PetscFree((*T)->T[k]));
1052   PetscCall(PetscFree((*T)->T));
1053   PetscCall(PetscFree(*T));
1054   *T = NULL;
1055   PetscFunctionReturn(PETSC_SUCCESS);
1056 }
1057 
1058 static PetscErrorCode PetscFECreatePointTraceDefault_Internal(PetscFE fe, PetscInt refPoint, PetscFE *trFE)
1059 {
1060   PetscSpace      bsp, bsubsp;
1061   PetscDualSpace  dsp, dsubsp;
1062   PetscInt        dim, depth, numComp, i, j, coneSize, order;
1063   DM              dm;
1064   DMLabel         label;
1065   PetscReal      *xi, *v, *J, detJ;
1066   const char     *name;
1067   PetscQuadrature origin, fullQuad, subQuad;
1068 
1069   PetscFunctionBegin;
1070   PetscCall(PetscFEGetBasisSpace(fe, &bsp));
1071   PetscCall(PetscFEGetDualSpace(fe, &dsp));
1072   PetscCall(PetscDualSpaceGetDM(dsp, &dm));
1073   PetscCall(DMGetDimension(dm, &dim));
1074   PetscCall(DMPlexGetDepthLabel(dm, &label));
1075   PetscCall(DMLabelGetValue(label, refPoint, &depth));
1076   PetscCall(PetscCalloc1(depth, &xi));
1077   PetscCall(PetscMalloc1(dim, &v));
1078   PetscCall(PetscMalloc1(dim * dim, &J));
1079   for (i = 0; i < depth; i++) xi[i] = 0.;
1080   PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, &origin));
1081   PetscCall(PetscQuadratureSetData(origin, depth, 0, 1, xi, NULL));
1082   PetscCall(DMPlexComputeCellGeometryFEM(dm, refPoint, origin, v, J, NULL, &detJ));
1083   /* CellGeometryFEM computes the expanded Jacobian, we want the true jacobian */
1084   for (i = 1; i < dim; i++) {
1085     for (j = 0; j < depth; j++) J[i * depth + j] = J[i * dim + j];
1086   }
1087   PetscCall(PetscQuadratureDestroy(&origin));
1088   PetscCall(PetscDualSpaceGetPointSubspace(dsp, refPoint, &dsubsp));
1089   PetscCall(PetscSpaceCreateSubspace(bsp, dsubsp, v, J, NULL, NULL, PETSC_OWN_POINTER, &bsubsp));
1090   PetscCall(PetscSpaceSetUp(bsubsp));
1091   PetscCall(PetscFECreate(PetscObjectComm((PetscObject)fe), trFE));
1092   PetscCall(PetscFESetType(*trFE, PETSCFEBASIC));
1093   PetscCall(PetscFEGetNumComponents(fe, &numComp));
1094   PetscCall(PetscFESetNumComponents(*trFE, numComp));
1095   PetscCall(PetscFESetBasisSpace(*trFE, bsubsp));
1096   PetscCall(PetscFESetDualSpace(*trFE, dsubsp));
1097   PetscCall(PetscObjectGetName((PetscObject)fe, &name));
1098   if (name) PetscCall(PetscFESetName(*trFE, name));
1099   PetscCall(PetscFEGetQuadrature(fe, &fullQuad));
1100   PetscCall(PetscQuadratureGetOrder(fullQuad, &order));
1101   PetscCall(DMPlexGetConeSize(dm, refPoint, &coneSize));
1102   if (coneSize == 2 * depth) PetscCall(PetscDTGaussTensorQuadrature(depth, 1, (order + 2) / 2, -1., 1., &subQuad));
1103   else PetscCall(PetscDTSimplexQuadrature(depth, order, PETSCDTSIMPLEXQUAD_DEFAULT, &subQuad));
1104   PetscCall(PetscFESetQuadrature(*trFE, subQuad));
1105   PetscCall(PetscFESetUp(*trFE));
1106   PetscCall(PetscQuadratureDestroy(&subQuad));
1107   PetscCall(PetscSpaceDestroy(&bsubsp));
1108   PetscFunctionReturn(PETSC_SUCCESS);
1109 }
1110 
1111 PETSC_EXTERN PetscErrorCode PetscFECreatePointTrace(PetscFE fe, PetscInt refPoint, PetscFE *trFE)
1112 {
1113   PetscFunctionBegin;
1114   PetscValidHeaderSpecific(fe, PETSCFE_CLASSID, 1);
1115   PetscAssertPointer(trFE, 3);
1116   if (fe->ops->createpointtrace) PetscUseTypeMethod(fe, createpointtrace, refPoint, trFE);
1117   else PetscCall(PetscFECreatePointTraceDefault_Internal(fe, refPoint, trFE));
1118   PetscFunctionReturn(PETSC_SUCCESS);
1119 }
1120 
1121 PetscErrorCode PetscFECreateHeightTrace(PetscFE fe, PetscInt height, PetscFE *trFE)
1122 {
1123   PetscInt       hStart, hEnd;
1124   PetscDualSpace dsp;
1125   DM             dm;
1126 
1127   PetscFunctionBegin;
1128   PetscValidHeaderSpecific(fe, PETSCFE_CLASSID, 1);
1129   PetscAssertPointer(trFE, 3);
1130   *trFE = NULL;
1131   PetscCall(PetscFEGetDualSpace(fe, &dsp));
1132   PetscCall(PetscDualSpaceGetDM(dsp, &dm));
1133   PetscCall(DMPlexGetHeightStratum(dm, height, &hStart, &hEnd));
1134   if (hEnd <= hStart) PetscFunctionReturn(PETSC_SUCCESS);
1135   PetscCall(PetscFECreatePointTrace(fe, hStart, trFE));
1136   PetscFunctionReturn(PETSC_SUCCESS);
1137 }
1138 
1139 /*@
1140   PetscFEGetDimension - Get the dimension of the finite element space on a cell
1141 
1142   Not Collective
1143 
1144   Input Parameter:
1145 . fem - The `PetscFE`
1146 
1147   Output Parameter:
1148 . dim - The dimension
1149 
1150   Level: intermediate
1151 
1152 .seealso: `PetscFE`, `PetscFECreate()`, `PetscSpaceGetDimension()`, `PetscDualSpaceGetDimension()`
1153 @*/
1154 PetscErrorCode PetscFEGetDimension(PetscFE fem, PetscInt *dim)
1155 {
1156   PetscFunctionBegin;
1157   PetscValidHeaderSpecific(fem, PETSCFE_CLASSID, 1);
1158   PetscAssertPointer(dim, 2);
1159   PetscTryTypeMethod(fem, getdimension, dim);
1160   PetscFunctionReturn(PETSC_SUCCESS);
1161 }
1162 
1163 /*@C
1164   PetscFEPushforward - Map the reference element function to real space
1165 
1166   Input Parameters:
1167 + fe     - The `PetscFE`
1168 . fegeom - The cell geometry
1169 . Nv     - The number of function values
1170 - vals   - The function values
1171 
1172   Output Parameter:
1173 . vals - The transformed function values
1174 
1175   Level: advanced
1176 
1177   Notes:
1178   This just forwards the call onto `PetscDualSpacePushforward()`.
1179 
1180   It only handles transformations when the embedding dimension of the geometry in fegeom is the same as the reference dimension.
1181 
1182 .seealso: `PetscFE`, `PetscFEGeom`, `PetscDualSpace`, `PetscDualSpacePushforward()`
1183 @*/
1184 PetscErrorCode PetscFEPushforward(PetscFE fe, PetscFEGeom *fegeom, PetscInt Nv, PetscScalar vals[])
1185 {
1186   PetscFunctionBeginHot;
1187   PetscCall(PetscDualSpacePushforward(fe->dualSpace, fegeom, Nv, fe->numComponents, vals));
1188   PetscFunctionReturn(PETSC_SUCCESS);
1189 }
1190 
1191 /*@C
1192   PetscFEPushforwardGradient - Map the reference element function gradient to real space
1193 
1194   Input Parameters:
1195 + fe     - The `PetscFE`
1196 . fegeom - The cell geometry
1197 . Nv     - The number of function gradient values
1198 - vals   - The function gradient values
1199 
1200   Output Parameter:
1201 . vals - The transformed function gradient values
1202 
1203   Level: advanced
1204 
1205   Notes:
1206   This just forwards the call onto `PetscDualSpacePushforwardGradient()`.
1207 
1208   It only handles transformations when the embedding dimension of the geometry in fegeom is the same as the reference dimension.
1209 
1210 .seealso: `PetscFE`, `PetscFEGeom`, `PetscDualSpace`, `PetscFEPushforward()`, `PetscDualSpacePushforwardGradient()`, `PetscDualSpacePushforward()`
1211 @*/
1212 PetscErrorCode PetscFEPushforwardGradient(PetscFE fe, PetscFEGeom *fegeom, PetscInt Nv, PetscScalar vals[])
1213 {
1214   PetscFunctionBeginHot;
1215   PetscCall(PetscDualSpacePushforwardGradient(fe->dualSpace, fegeom, Nv, fe->numComponents, vals));
1216   PetscFunctionReturn(PETSC_SUCCESS);
1217 }
1218 
1219 /*@C
1220   PetscFEPushforwardHessian - Map the reference element function Hessian to real space
1221 
1222   Input Parameters:
1223 + fe     - The `PetscFE`
1224 . fegeom - The cell geometry
1225 . Nv     - The number of function Hessian values
1226 - vals   - The function Hessian values
1227 
1228   Output Parameter:
1229 . vals - The transformed function Hessian values
1230 
1231   Level: advanced
1232 
1233   Notes:
1234   This just forwards the call onto `PetscDualSpacePushforwardHessian()`.
1235 
1236   It only handles transformations when the embedding dimension of the geometry in fegeom is the same as the reference dimension.
1237 
1238   Developer Notes:
1239   It is unclear why all these one line convenience routines are desirable
1240 
1241 .seealso: `PetscFE`, `PetscFEGeom`, `PetscDualSpace`, `PetscFEPushforward()`, `PetscDualSpacePushforwardHessian()`, `PetscDualSpacePushforward()`
1242 @*/
1243 PetscErrorCode PetscFEPushforwardHessian(PetscFE fe, PetscFEGeom *fegeom, PetscInt Nv, PetscScalar vals[])
1244 {
1245   PetscFunctionBeginHot;
1246   PetscCall(PetscDualSpacePushforwardHessian(fe->dualSpace, fegeom, Nv, fe->numComponents, vals));
1247   PetscFunctionReturn(PETSC_SUCCESS);
1248 }
1249 
1250 /*
1251 Purpose: Compute element vector for chunk of elements
1252 
1253 Input:
1254   Sizes:
1255      Ne:  number of elements
1256      Nf:  number of fields
1257      PetscFE
1258        dim: spatial dimension
1259        Nb:  number of basis functions
1260        Nc:  number of field components
1261        PetscQuadrature
1262          Nq:  number of quadrature points
1263 
1264   Geometry:
1265      PetscFEGeom[Ne] possibly *Nq
1266        PetscReal v0s[dim]
1267        PetscReal n[dim]
1268        PetscReal jacobians[dim*dim]
1269        PetscReal jacobianInverses[dim*dim]
1270        PetscReal jacobianDeterminants
1271   FEM:
1272      PetscFE
1273        PetscQuadrature
1274          PetscReal   quadPoints[Nq*dim]
1275          PetscReal   quadWeights[Nq]
1276        PetscReal   basis[Nq*Nb*Nc]
1277        PetscReal   basisDer[Nq*Nb*Nc*dim]
1278      PetscScalar coefficients[Ne*Nb*Nc]
1279      PetscScalar elemVec[Ne*Nb*Nc]
1280 
1281   Problem:
1282      PetscInt f: the active field
1283      f0, f1
1284 
1285   Work Space:
1286      PetscFE
1287        PetscScalar f0[Nq*dim];
1288        PetscScalar f1[Nq*dim*dim];
1289        PetscScalar u[Nc];
1290        PetscScalar gradU[Nc*dim];
1291        PetscReal   x[dim];
1292        PetscScalar realSpaceDer[dim];
1293 
1294 Purpose: Compute element vector for N_cb batches of elements
1295 
1296 Input:
1297   Sizes:
1298      N_cb: Number of serial cell batches
1299 
1300   Geometry:
1301      PetscReal v0s[Ne*dim]
1302      PetscReal jacobians[Ne*dim*dim]        possibly *Nq
1303      PetscReal jacobianInverses[Ne*dim*dim] possibly *Nq
1304      PetscReal jacobianDeterminants[Ne]     possibly *Nq
1305   FEM:
1306      static PetscReal   quadPoints[Nq*dim]
1307      static PetscReal   quadWeights[Nq]
1308      static PetscReal   basis[Nq*Nb*Nc]
1309      static PetscReal   basisDer[Nq*Nb*Nc*dim]
1310      PetscScalar coefficients[Ne*Nb*Nc]
1311      PetscScalar elemVec[Ne*Nb*Nc]
1312 
1313 ex62.c:
1314   PetscErrorCode PetscFEIntegrateResidualBatch(PetscInt Ne, PetscInt numFields, PetscInt field, PetscQuadrature quad[], const PetscScalar coefficients[],
1315                                                const PetscReal v0s[], const PetscReal jacobians[], const PetscReal jacobianInverses[], const PetscReal jacobianDeterminants[],
1316                                                void (*f0_func)(const PetscScalar u[], const PetscScalar gradU[], const PetscReal x[], PetscScalar f0[]),
1317                                                void (*f1_func)(const PetscScalar u[], const PetscScalar gradU[], const PetscReal x[], PetscScalar f1[]), PetscScalar elemVec[])
1318 
1319 ex52.c:
1320   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)
1321   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)
1322 
1323 ex52_integrateElement.cu
1324 __global__ void integrateElementQuadrature(int N_cb, realType *coefficients, realType *jacobianInverses, realType *jacobianDeterminants, realType *elemVec)
1325 
1326 PETSC_EXTERN PetscErrorCode IntegrateElementBatchGPU(PetscInt spatial_dim, PetscInt Ne, PetscInt Ncb, PetscInt Nbc, PetscInt Nbl, const PetscScalar coefficients[],
1327                                                      const PetscReal jacobianInverses[], const PetscReal jacobianDeterminants[], PetscScalar elemVec[],
1328                                                      PetscLogEvent event, PetscInt debug, PetscInt pde_op)
1329 
1330 ex52_integrateElementOpenCL.c:
1331 PETSC_EXTERN PetscErrorCode IntegrateElementBatchGPU(PetscInt spatial_dim, PetscInt Ne, PetscInt Ncb, PetscInt Nbc, PetscInt N_bl, const PetscScalar coefficients[],
1332                                                      const PetscReal jacobianInverses[], const PetscReal jacobianDeterminants[], PetscScalar elemVec[],
1333                                                      PetscLogEvent event, PetscInt debug, PetscInt pde_op)
1334 
1335 __kernel void integrateElementQuadrature(int N_cb, __global float *coefficients, __global float *jacobianInverses, __global float *jacobianDeterminants, __global float *elemVec)
1336 */
1337 
1338 /*@C
1339   PetscFEIntegrate - Produce the integral for the given field for a chunk of elements by quadrature integration
1340 
1341   Not Collective
1342 
1343   Input Parameters:
1344 + prob            - The `PetscDS` specifying the discretizations and continuum functions
1345 . field           - The field being integrated
1346 . Ne              - The number of elements in the chunk
1347 . cgeom           - The cell geometry for each cell in the chunk
1348 . coefficients    - The array of FEM basis coefficients for the elements
1349 . probAux         - The `PetscDS` specifying the auxiliary discretizations
1350 - coefficientsAux - The array of FEM auxiliary basis coefficients for the elements
1351 
1352   Output Parameter:
1353 . integral - the integral for this field
1354 
1355   Level: intermediate
1356 
1357   Developer Notes:
1358   The function name begins with `PetscFE` and yet the first argument is `PetscDS` and it has no `PetscFE` arguments.
1359 
1360 .seealso: `PetscFE`, `PetscDS`, `PetscFEIntegrateResidual()`, `PetscFEIntegrateBd()`
1361 @*/
1362 PetscErrorCode PetscFEIntegrate(PetscDS prob, PetscInt field, PetscInt Ne, PetscFEGeom *cgeom, const PetscScalar coefficients[], PetscDS probAux, const PetscScalar coefficientsAux[], PetscScalar integral[])
1363 {
1364   PetscFE fe;
1365 
1366   PetscFunctionBegin;
1367   PetscValidHeaderSpecific(prob, PETSCDS_CLASSID, 1);
1368   PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fe));
1369   if (fe->ops->integrate) PetscCall((*fe->ops->integrate)(prob, field, Ne, cgeom, coefficients, probAux, coefficientsAux, integral));
1370   PetscFunctionReturn(PETSC_SUCCESS);
1371 }
1372 
1373 /*@C
1374   PetscFEIntegrateBd - Produce the integral for the given field for a chunk of elements by quadrature integration
1375 
1376   Not Collective
1377 
1378   Input Parameters:
1379 + prob            - The `PetscDS` specifying the discretizations and continuum functions
1380 . field           - The field being integrated
1381 . obj_func        - The function to be integrated
1382 . Ne              - The number of elements in the chunk
1383 . geom            - The face geometry for each face in the chunk
1384 . coefficients    - The array of FEM basis coefficients for the elements
1385 . probAux         - The `PetscDS` specifying the auxiliary discretizations
1386 - coefficientsAux - The array of FEM auxiliary basis coefficients for the elements
1387 
1388   Output Parameter:
1389 . integral - the integral for this field
1390 
1391   Level: intermediate
1392 
1393   Developer Notes:
1394   The function name begins with `PetscFE` and yet the first argument is `PetscDS` and it has no `PetscFE` arguments.
1395 
1396 .seealso: `PetscFE`, `PetscDS`, `PetscFEIntegrateResidual()`, `PetscFEIntegrate()`
1397 @*/
1398 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[])
1399 {
1400   PetscFE fe;
1401 
1402   PetscFunctionBegin;
1403   PetscValidHeaderSpecific(prob, PETSCDS_CLASSID, 1);
1404   PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fe));
1405   if (fe->ops->integratebd) PetscCall((*fe->ops->integratebd)(prob, field, obj_func, Ne, geom, coefficients, probAux, coefficientsAux, integral));
1406   PetscFunctionReturn(PETSC_SUCCESS);
1407 }
1408 
1409 /*@C
1410   PetscFEIntegrateResidual - Produce the element residual vector for a chunk of elements by quadrature integration
1411 
1412   Not Collective
1413 
1414   Input Parameters:
1415 + ds              - The `PetscDS` specifying the discretizations and continuum functions
1416 . key             - The (label+value, field) being integrated
1417 . Ne              - The number of elements in the chunk
1418 . cgeom           - The cell geometry for each cell in the chunk
1419 . coefficients    - The array of FEM basis coefficients for the elements
1420 . coefficients_t  - The array of FEM basis time derivative coefficients for the elements
1421 . probAux         - The `PetscDS` specifying the auxiliary discretizations
1422 . coefficientsAux - The array of FEM auxiliary basis coefficients for the elements
1423 - t               - The time
1424 
1425   Output Parameter:
1426 . elemVec - the element residual vectors from each element
1427 
1428   Level: intermediate
1429 
1430   Note:
1431 .vb
1432   Loop over batch of elements (e):
1433     Loop over quadrature points (q):
1434       Make u_q and gradU_q (loops over fields,Nb,Ncomp) and x_q
1435       Call f_0 and f_1
1436     Loop over element vector entries (f,fc --> i):
1437       elemVec[i] += \psi^{fc}_f(q) f0_{fc}(u, \nabla u) + \nabla\psi^{fc}_f(q) \cdot f1_{fc,df}(u, \nabla u)
1438 .ve
1439 
1440 .seealso: `PetscFEIntegrateBdResidual()`
1441 @*/
1442 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[])
1443 {
1444   PetscFE fe;
1445 
1446   PetscFunctionBeginHot;
1447   PetscValidHeaderSpecific(ds, PETSCDS_CLASSID, 1);
1448   PetscCall(PetscDSGetDiscretization(ds, key.field, (PetscObject *)&fe));
1449   if (fe->ops->integrateresidual) PetscCall((*fe->ops->integrateresidual)(ds, key, Ne, cgeom, coefficients, coefficients_t, probAux, coefficientsAux, t, elemVec));
1450   PetscFunctionReturn(PETSC_SUCCESS);
1451 }
1452 
1453 /*@C
1454   PetscFEIntegrateBdResidual - Produce the element residual vector for a chunk of elements by quadrature integration over a boundary
1455 
1456   Not Collective
1457 
1458   Input Parameters:
1459 + ds              - The `PetscDS` specifying the discretizations and continuum functions
1460 . wf              - The PetscWeakForm object holding the pointwise functions
1461 . key             - The (label+value, field) being integrated
1462 . Ne              - The number of elements in the chunk
1463 . fgeom           - The face geometry for each cell in the chunk
1464 . coefficients    - The array of FEM basis coefficients for the elements
1465 . coefficients_t  - The array of FEM basis time derivative coefficients for the elements
1466 . probAux         - The `PetscDS` specifying the auxiliary discretizations
1467 . coefficientsAux - The array of FEM auxiliary basis coefficients for the elements
1468 - t               - The time
1469 
1470   Output Parameter:
1471 . elemVec - the element residual vectors from each element
1472 
1473   Level: intermediate
1474 
1475 .seealso: `PetscFEIntegrateResidual()`
1476 @*/
1477 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[])
1478 {
1479   PetscFE fe;
1480 
1481   PetscFunctionBegin;
1482   PetscValidHeaderSpecific(ds, PETSCDS_CLASSID, 1);
1483   PetscCall(PetscDSGetDiscretization(ds, key.field, (PetscObject *)&fe));
1484   if (fe->ops->integratebdresidual) PetscCall((*fe->ops->integratebdresidual)(ds, wf, key, Ne, fgeom, coefficients, coefficients_t, probAux, coefficientsAux, t, elemVec));
1485   PetscFunctionReturn(PETSC_SUCCESS);
1486 }
1487 
1488 /*@C
1489   PetscFEIntegrateHybridResidual - Produce the element residual vector for a chunk of hybrid element faces by quadrature integration
1490 
1491   Not Collective
1492 
1493   Input Parameters:
1494 + ds              - The `PetscDS` specifying the discretizations and continuum functions
1495 . dsIn            - The `PetscDS` specifying the discretizations and continuum functions for input
1496 . key             - The (label+value, field) being integrated
1497 . s               - The side of the cell being integrated, 0 for negative and 1 for positive
1498 . Ne              - The number of elements in the chunk
1499 . fgeom           - The face geometry for each cell in the chunk
1500 . coefficients    - The array of FEM basis coefficients for the elements
1501 . coefficients_t  - The array of FEM basis time derivative coefficients for the elements
1502 . probAux         - The `PetscDS` specifying the auxiliary discretizations
1503 . coefficientsAux - The array of FEM auxiliary basis coefficients for the elements
1504 - t               - The time
1505 
1506   Output Parameter:
1507 . elemVec - the element residual vectors from each element
1508 
1509   Level: developer
1510 
1511 .seealso: `PetscFEIntegrateResidual()`
1512 @*/
1513 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[])
1514 {
1515   PetscFE fe;
1516 
1517   PetscFunctionBegin;
1518   PetscValidHeaderSpecific(ds, PETSCDS_CLASSID, 1);
1519   PetscValidHeaderSpecific(dsIn, PETSCDS_CLASSID, 2);
1520   PetscCall(PetscDSGetDiscretization(ds, key.field, (PetscObject *)&fe));
1521   if (fe->ops->integratehybridresidual) PetscCall((*fe->ops->integratehybridresidual)(ds, dsIn, key, s, Ne, fgeom, coefficients, coefficients_t, probAux, coefficientsAux, t, elemVec));
1522   PetscFunctionReturn(PETSC_SUCCESS);
1523 }
1524 
1525 /*@C
1526   PetscFEIntegrateJacobian - Produce the element Jacobian for a chunk of elements by quadrature integration
1527 
1528   Not Collective
1529 
1530   Input Parameters:
1531 + ds              - The `PetscDS` specifying the discretizations and continuum functions
1532 . jtype           - The type of matrix pointwise functions that should be used
1533 . key             - The (label+value, fieldI*Nf + fieldJ) being integrated
1534 . Ne              - The number of elements in the chunk
1535 . cgeom           - The cell geometry for each cell in the chunk
1536 . coefficients    - The array of FEM basis coefficients for the elements for the Jacobian evaluation point
1537 . coefficients_t  - The array of FEM basis time derivative coefficients for the elements
1538 . probAux         - The `PetscDS` specifying the auxiliary discretizations
1539 . coefficientsAux - The array of FEM auxiliary basis coefficients for the elements
1540 . t               - The time
1541 - u_tshift        - A multiplier for the dF/du_t term (as opposed to the dF/du term)
1542 
1543   Output Parameter:
1544 . elemMat - the element matrices for the Jacobian from each element
1545 
1546   Level: intermediate
1547 
1548   Note:
1549 .vb
1550   Loop over batch of elements (e):
1551     Loop over element matrix entries (f,fc,g,gc --> i,j):
1552       Loop over quadrature points (q):
1553         Make u_q and gradU_q (loops over fields,Nb,Ncomp)
1554           elemMat[i,j] += \psi^{fc}_f(q) g0_{fc,gc}(u, \nabla u) \phi^{gc}_g(q)
1555                        + \psi^{fc}_f(q) \cdot g1_{fc,gc,dg}(u, \nabla u) \nabla\phi^{gc}_g(q)
1556                        + \nabla\psi^{fc}_f(q) \cdot g2_{fc,gc,df}(u, \nabla u) \phi^{gc}_g(q)
1557                        + \nabla\psi^{fc}_f(q) \cdot g3_{fc,gc,df,dg}(u, \nabla u) \nabla\phi^{gc}_g(q)
1558 .ve
1559 
1560 .seealso: `PetscFEIntegrateResidual()`
1561 @*/
1562 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[])
1563 {
1564   PetscFE  fe;
1565   PetscInt Nf;
1566 
1567   PetscFunctionBegin;
1568   PetscValidHeaderSpecific(ds, PETSCDS_CLASSID, 1);
1569   PetscCall(PetscDSGetNumFields(ds, &Nf));
1570   PetscCall(PetscDSGetDiscretization(ds, key.field / Nf, (PetscObject *)&fe));
1571   if (fe->ops->integratejacobian) PetscCall((*fe->ops->integratejacobian)(ds, jtype, key, Ne, cgeom, coefficients, coefficients_t, probAux, coefficientsAux, t, u_tshift, elemMat));
1572   PetscFunctionReturn(PETSC_SUCCESS);
1573 }
1574 
1575 /*@C
1576   PetscFEIntegrateBdJacobian - Produce the boundary element Jacobian for a chunk of elements by quadrature integration
1577 
1578   Not Collective
1579 
1580   Input Parameters:
1581 + ds              - The `PetscDS` specifying the discretizations and continuum functions
1582 . wf              - The PetscWeakForm holding the pointwise functions
1583 . jtype           - The type of matrix pointwise functions that should be used
1584 . key             - The (label+value, fieldI*Nf + fieldJ) being integrated
1585 . Ne              - The number of elements in the chunk
1586 . fgeom           - The face geometry for each cell in the chunk
1587 . coefficients    - The array of FEM basis coefficients for the elements for the Jacobian evaluation point
1588 . coefficients_t  - The array of FEM basis time derivative coefficients for the elements
1589 . probAux         - The `PetscDS` specifying the auxiliary discretizations
1590 . coefficientsAux - The array of FEM auxiliary basis coefficients for the elements
1591 . t               - The time
1592 - u_tshift        - A multiplier for the dF/du_t term (as opposed to the dF/du term)
1593 
1594   Output Parameter:
1595 . elemMat - the element matrices for the Jacobian from each element
1596 
1597   Level: intermediate
1598 
1599   Note:
1600 .vb
1601   Loop over batch of elements (e):
1602     Loop over element matrix entries (f,fc,g,gc --> i,j):
1603       Loop over quadrature points (q):
1604         Make u_q and gradU_q (loops over fields,Nb,Ncomp)
1605           elemMat[i,j] += \psi^{fc}_f(q) g0_{fc,gc}(u, \nabla u) \phi^{gc}_g(q)
1606                        + \psi^{fc}_f(q) \cdot g1_{fc,gc,dg}(u, \nabla u) \nabla\phi^{gc}_g(q)
1607                        + \nabla\psi^{fc}_f(q) \cdot g2_{fc,gc,df}(u, \nabla u) \phi^{gc}_g(q)
1608                        + \nabla\psi^{fc}_f(q) \cdot g3_{fc,gc,df,dg}(u, \nabla u) \nabla\phi^{gc}_g(q)
1609 .ve
1610 
1611 .seealso: `PetscFEIntegrateJacobian()`, `PetscFEIntegrateResidual()`
1612 @*/
1613 PetscErrorCode PetscFEIntegrateBdJacobian(PetscDS ds, PetscWeakForm wf, PetscFEJacobianType jtype, PetscFormKey key, PetscInt Ne, PetscFEGeom *fgeom, const PetscScalar coefficients[], const PetscScalar coefficients_t[], PetscDS probAux, const PetscScalar coefficientsAux[], PetscReal t, PetscReal u_tshift, PetscScalar elemMat[])
1614 {
1615   PetscFE  fe;
1616   PetscInt Nf;
1617 
1618   PetscFunctionBegin;
1619   PetscValidHeaderSpecific(ds, PETSCDS_CLASSID, 1);
1620   PetscCall(PetscDSGetNumFields(ds, &Nf));
1621   PetscCall(PetscDSGetDiscretization(ds, key.field / Nf, (PetscObject *)&fe));
1622   if (fe->ops->integratebdjacobian) PetscCall((*fe->ops->integratebdjacobian)(ds, wf, jtype, key, Ne, fgeom, coefficients, coefficients_t, probAux, coefficientsAux, t, u_tshift, elemMat));
1623   PetscFunctionReturn(PETSC_SUCCESS);
1624 }
1625 
1626 /*@C
1627   PetscFEIntegrateHybridJacobian - Produce the boundary element Jacobian for a chunk of hybrid elements by quadrature integration
1628 
1629   Not Collective
1630 
1631   Input Parameters:
1632 + ds              - The `PetscDS` specifying the discretizations and continuum functions for the output
1633 . dsIn            - The `PetscDS` specifying the discretizations and continuum functions for the input
1634 . jtype           - The type of matrix pointwise functions that should be used
1635 . key             - The (label+value, fieldI*Nf + fieldJ) being integrated
1636 . s               - The side of the cell being integrated, 0 for negative and 1 for positive
1637 . Ne              - The number of elements in the chunk
1638 . fgeom           - The face geometry for each cell in the chunk
1639 . coefficients    - The array of FEM basis coefficients for the elements for the Jacobian evaluation point
1640 . coefficients_t  - The array of FEM basis time derivative coefficients for the elements
1641 . probAux         - The `PetscDS` specifying the auxiliary discretizations
1642 . coefficientsAux - The array of FEM auxiliary basis coefficients for the elements
1643 . t               - The time
1644 - u_tshift        - A multiplier for the dF/du_t term (as opposed to the dF/du term)
1645 
1646   Output Parameter:
1647 . elemMat - the element matrices for the Jacobian from each element
1648 
1649   Level: developer
1650 
1651   Note:
1652 .vb
1653   Loop over batch of elements (e):
1654     Loop over element matrix entries (f,fc,g,gc --> i,j):
1655       Loop over quadrature points (q):
1656         Make u_q and gradU_q (loops over fields,Nb,Ncomp)
1657           elemMat[i,j] += \psi^{fc}_f(q) g0_{fc,gc}(u, \nabla u) \phi^{gc}_g(q)
1658                        + \psi^{fc}_f(q) \cdot g1_{fc,gc,dg}(u, \nabla u) \nabla\phi^{gc}_g(q)
1659                        + \nabla\psi^{fc}_f(q) \cdot g2_{fc,gc,df}(u, \nabla u) \phi^{gc}_g(q)
1660                        + \nabla\psi^{fc}_f(q) \cdot g3_{fc,gc,df,dg}(u, \nabla u) \nabla\phi^{gc}_g(q)
1661 .ve
1662 
1663 .seealso: `PetscFEIntegrateJacobian()`, `PetscFEIntegrateResidual()`
1664 @*/
1665 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[])
1666 {
1667   PetscFE  fe;
1668   PetscInt Nf;
1669 
1670   PetscFunctionBegin;
1671   PetscValidHeaderSpecific(ds, PETSCDS_CLASSID, 1);
1672   PetscCall(PetscDSGetNumFields(ds, &Nf));
1673   PetscCall(PetscDSGetDiscretization(ds, key.field / Nf, (PetscObject *)&fe));
1674   if (fe->ops->integratehybridjacobian) PetscCall((*fe->ops->integratehybridjacobian)(ds, dsIn, jtype, key, s, Ne, fgeom, coefficients, coefficients_t, probAux, coefficientsAux, t, u_tshift, elemMat));
1675   PetscFunctionReturn(PETSC_SUCCESS);
1676 }
1677 
1678 /*@
1679   PetscFEGetHeightSubspace - Get the subspace of this space for a mesh point of a given height
1680 
1681   Input Parameters:
1682 + fe     - The finite element space
1683 - height - The height of the `DMPLEX` point
1684 
1685   Output Parameter:
1686 . subfe - The subspace of this `PetscFE` space
1687 
1688   Level: advanced
1689 
1690   Note:
1691   For example, if we want the subspace of this space for a face, we would choose height = 1.
1692 
1693 .seealso: `PetscFECreateDefault()`
1694 @*/
1695 PetscErrorCode PetscFEGetHeightSubspace(PetscFE fe, PetscInt height, PetscFE *subfe)
1696 {
1697   PetscSpace      P, subP;
1698   PetscDualSpace  Q, subQ;
1699   PetscQuadrature subq;
1700   PetscInt        dim, Nc;
1701 
1702   PetscFunctionBegin;
1703   PetscValidHeaderSpecific(fe, PETSCFE_CLASSID, 1);
1704   PetscAssertPointer(subfe, 3);
1705   if (height == 0) {
1706     *subfe = fe;
1707     PetscFunctionReturn(PETSC_SUCCESS);
1708   }
1709   PetscCall(PetscFEGetBasisSpace(fe, &P));
1710   PetscCall(PetscFEGetDualSpace(fe, &Q));
1711   PetscCall(PetscFEGetNumComponents(fe, &Nc));
1712   PetscCall(PetscFEGetFaceQuadrature(fe, &subq));
1713   PetscCall(PetscDualSpaceGetDimension(Q, &dim));
1714   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);
1715   if (!fe->subspaces) PetscCall(PetscCalloc1(dim, &fe->subspaces));
1716   if (height <= dim) {
1717     if (!fe->subspaces[height - 1]) {
1718       PetscFE     sub = NULL;
1719       const char *name;
1720 
1721       PetscCall(PetscSpaceGetHeightSubspace(P, height, &subP));
1722       PetscCall(PetscDualSpaceGetHeightSubspace(Q, height, &subQ));
1723       if (subQ) {
1724         PetscCall(PetscObjectReference((PetscObject)subP));
1725         PetscCall(PetscObjectReference((PetscObject)subQ));
1726         PetscCall(PetscObjectReference((PetscObject)subq));
1727         PetscCall(PetscFECreateFromSpaces(subP, subQ, subq, NULL, &sub));
1728       }
1729       if (sub) {
1730         PetscCall(PetscObjectGetName((PetscObject)fe, &name));
1731         if (name) PetscCall(PetscFESetName(sub, name));
1732       }
1733       fe->subspaces[height - 1] = sub;
1734     }
1735     *subfe = fe->subspaces[height - 1];
1736   } else {
1737     *subfe = NULL;
1738   }
1739   PetscFunctionReturn(PETSC_SUCCESS);
1740 }
1741 
1742 /*@
1743   PetscFERefine - Create a "refined" `PetscFE` object that refines the reference cell into
1744   smaller copies.
1745 
1746   Collective
1747 
1748   Input Parameter:
1749 . fe - The initial `PetscFE`
1750 
1751   Output Parameter:
1752 . feRef - The refined `PetscFE`
1753 
1754   Level: advanced
1755 
1756   Notes:
1757   This is typically used to generate a preconditioner for a higher order method from a lower order method on a
1758   refined mesh having the same number of dofs (but more sparsity). It is also used to create an
1759   interpolation between regularly refined meshes.
1760 
1761 .seealso: `PetscFEType`, `PetscFECreate()`, `PetscFESetType()`
1762 @*/
1763 PetscErrorCode PetscFERefine(PetscFE fe, PetscFE *feRef)
1764 {
1765   PetscSpace       P, Pref;
1766   PetscDualSpace   Q, Qref;
1767   DM               K, Kref;
1768   PetscQuadrature  q, qref;
1769   const PetscReal *v0, *jac;
1770   PetscInt         numComp, numSubelements;
1771   PetscInt         cStart, cEnd, c;
1772   PetscDualSpace  *cellSpaces;
1773 
1774   PetscFunctionBegin;
1775   PetscCall(PetscFEGetBasisSpace(fe, &P));
1776   PetscCall(PetscFEGetDualSpace(fe, &Q));
1777   PetscCall(PetscFEGetQuadrature(fe, &q));
1778   PetscCall(PetscDualSpaceGetDM(Q, &K));
1779   /* Create space */
1780   PetscCall(PetscObjectReference((PetscObject)P));
1781   Pref = P;
1782   /* Create dual space */
1783   PetscCall(PetscDualSpaceDuplicate(Q, &Qref));
1784   PetscCall(PetscDualSpaceSetType(Qref, PETSCDUALSPACEREFINED));
1785   PetscCall(DMRefine(K, PetscObjectComm((PetscObject)fe), &Kref));
1786   PetscCall(DMGetCoordinatesLocalSetUp(Kref));
1787   PetscCall(PetscDualSpaceSetDM(Qref, Kref));
1788   PetscCall(DMPlexGetHeightStratum(Kref, 0, &cStart, &cEnd));
1789   PetscCall(PetscMalloc1(cEnd - cStart, &cellSpaces));
1790   /* TODO: fix for non-uniform refinement */
1791   for (c = 0; c < cEnd - cStart; c++) cellSpaces[c] = Q;
1792   PetscCall(PetscDualSpaceRefinedSetCellSpaces(Qref, cellSpaces));
1793   PetscCall(PetscFree(cellSpaces));
1794   PetscCall(DMDestroy(&Kref));
1795   PetscCall(PetscDualSpaceSetUp(Qref));
1796   /* Create element */
1797   PetscCall(PetscFECreate(PetscObjectComm((PetscObject)fe), feRef));
1798   PetscCall(PetscFESetType(*feRef, PETSCFECOMPOSITE));
1799   PetscCall(PetscFESetBasisSpace(*feRef, Pref));
1800   PetscCall(PetscFESetDualSpace(*feRef, Qref));
1801   PetscCall(PetscFEGetNumComponents(fe, &numComp));
1802   PetscCall(PetscFESetNumComponents(*feRef, numComp));
1803   PetscCall(PetscFESetUp(*feRef));
1804   PetscCall(PetscSpaceDestroy(&Pref));
1805   PetscCall(PetscDualSpaceDestroy(&Qref));
1806   /* Create quadrature */
1807   PetscCall(PetscFECompositeGetMapping(*feRef, &numSubelements, &v0, &jac, NULL));
1808   PetscCall(PetscQuadratureExpandComposite(q, numSubelements, v0, jac, &qref));
1809   PetscCall(PetscFESetQuadrature(*feRef, qref));
1810   PetscCall(PetscQuadratureDestroy(&qref));
1811   PetscFunctionReturn(PETSC_SUCCESS);
1812 }
1813 
1814 static PetscErrorCode PetscFESetDefaultName_Private(PetscFE fe)
1815 {
1816   PetscSpace     P;
1817   PetscDualSpace Q;
1818   DM             K;
1819   DMPolytopeType ct;
1820   PetscInt       degree;
1821   char           name[64];
1822 
1823   PetscFunctionBegin;
1824   PetscCall(PetscFEGetBasisSpace(fe, &P));
1825   PetscCall(PetscSpaceGetDegree(P, &degree, NULL));
1826   PetscCall(PetscFEGetDualSpace(fe, &Q));
1827   PetscCall(PetscDualSpaceGetDM(Q, &K));
1828   PetscCall(DMPlexGetCellType(K, 0, &ct));
1829   switch (ct) {
1830   case DM_POLYTOPE_SEGMENT:
1831   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1832   case DM_POLYTOPE_QUADRILATERAL:
1833   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1834   case DM_POLYTOPE_HEXAHEDRON:
1835   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
1836     PetscCall(PetscSNPrintf(name, sizeof(name), "Q%" PetscInt_FMT, degree));
1837     break;
1838   case DM_POLYTOPE_TRIANGLE:
1839   case DM_POLYTOPE_TETRAHEDRON:
1840     PetscCall(PetscSNPrintf(name, sizeof(name), "P%" PetscInt_FMT, degree));
1841     break;
1842   case DM_POLYTOPE_TRI_PRISM:
1843   case DM_POLYTOPE_TRI_PRISM_TENSOR:
1844     PetscCall(PetscSNPrintf(name, sizeof(name), "P%" PetscInt_FMT "xQ%" PetscInt_FMT, degree, degree));
1845     break;
1846   default:
1847     PetscCall(PetscSNPrintf(name, sizeof(name), "FE"));
1848   }
1849   PetscCall(PetscFESetName(fe, name));
1850   PetscFunctionReturn(PETSC_SUCCESS);
1851 }
1852 
1853 /*@
1854   PetscFECreateFromSpaces - Create a `PetscFE` from the basis and dual spaces
1855 
1856   Collective
1857 
1858   Input Parameters:
1859 + P  - The basis space
1860 . Q  - The dual space
1861 . q  - The cell quadrature
1862 - fq - The face quadrature
1863 
1864   Output Parameter:
1865 . fem - The `PetscFE` object
1866 
1867   Level: beginner
1868 
1869   Note:
1870   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.
1871 
1872 .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscQuadrature`,
1873           `PetscFECreateLagrangeByCell()`, `PetscFECreateDefault()`, `PetscFECreateByCell()`, `PetscFECreate()`, `PetscSpaceCreate()`, `PetscDualSpaceCreate()`
1874 @*/
1875 PetscErrorCode PetscFECreateFromSpaces(PetscSpace P, PetscDualSpace Q, PetscQuadrature q, PetscQuadrature fq, PetscFE *fem)
1876 {
1877   PetscInt    Nc;
1878   PetscInt    p_Ns = -1, p_Nc = -1, q_Ns = -1, q_Nc = -1;
1879   PetscBool   p_is_uniform_sum = PETSC_FALSE, p_interleave_basis = PETSC_FALSE, p_interleave_components = PETSC_FALSE;
1880   PetscBool   q_is_uniform_sum = PETSC_FALSE, q_interleave_basis = PETSC_FALSE, q_interleave_components = PETSC_FALSE;
1881   const char *prefix;
1882 
1883   PetscFunctionBegin;
1884   PetscCall(PetscObjectTypeCompare((PetscObject)P, PETSCSPACESUM, &p_is_uniform_sum));
1885   if (p_is_uniform_sum) {
1886     PetscSpace subsp_0 = NULL;
1887     PetscCall(PetscSpaceSumGetNumSubspaces(P, &p_Ns));
1888     PetscCall(PetscSpaceGetNumComponents(P, &p_Nc));
1889     PetscCall(PetscSpaceSumGetConcatenate(P, &p_is_uniform_sum));
1890     PetscCall(PetscSpaceSumGetInterleave(P, &p_interleave_basis, &p_interleave_components));
1891     for (PetscInt s = 0; s < p_Ns; s++) {
1892       PetscSpace subsp;
1893 
1894       PetscCall(PetscSpaceSumGetSubspace(P, s, &subsp));
1895       if (!s) {
1896         subsp_0 = subsp;
1897       } else if (subsp != subsp_0) {
1898         p_is_uniform_sum = PETSC_FALSE;
1899       }
1900     }
1901   }
1902   PetscCall(PetscObjectTypeCompare((PetscObject)Q, PETSCDUALSPACESUM, &q_is_uniform_sum));
1903   if (q_is_uniform_sum) {
1904     PetscDualSpace subsp_0 = NULL;
1905     PetscCall(PetscDualSpaceSumGetNumSubspaces(Q, &q_Ns));
1906     PetscCall(PetscDualSpaceGetNumComponents(Q, &q_Nc));
1907     PetscCall(PetscDualSpaceSumGetConcatenate(Q, &q_is_uniform_sum));
1908     PetscCall(PetscDualSpaceSumGetInterleave(Q, &q_interleave_basis, &q_interleave_components));
1909     for (PetscInt s = 0; s < q_Ns; s++) {
1910       PetscDualSpace subsp;
1911 
1912       PetscCall(PetscDualSpaceSumGetSubspace(Q, s, &subsp));
1913       if (!s) {
1914         subsp_0 = subsp;
1915       } else if (subsp != subsp_0) {
1916         q_is_uniform_sum = PETSC_FALSE;
1917       }
1918     }
1919   }
1920   if (p_is_uniform_sum && q_is_uniform_sum && (p_interleave_basis == q_interleave_basis) && (p_interleave_components == q_interleave_components) && (p_Ns == q_Ns) && (p_Nc == q_Nc)) {
1921     PetscSpace     scalar_space;
1922     PetscDualSpace scalar_dspace;
1923     PetscFE        scalar_fe;
1924 
1925     PetscCall(PetscSpaceSumGetSubspace(P, 0, &scalar_space));
1926     PetscCall(PetscDualSpaceSumGetSubspace(Q, 0, &scalar_dspace));
1927     PetscCall(PetscObjectReference((PetscObject)scalar_space));
1928     PetscCall(PetscObjectReference((PetscObject)scalar_dspace));
1929     PetscCall(PetscObjectReference((PetscObject)q));
1930     PetscCall(PetscObjectReference((PetscObject)fq));
1931     PetscCall(PetscFECreateFromSpaces(scalar_space, scalar_dspace, q, fq, &scalar_fe));
1932     PetscCall(PetscFECreateVector(scalar_fe, p_Ns, p_interleave_basis, p_interleave_components, fem));
1933     PetscCall(PetscFEDestroy(&scalar_fe));
1934   } else {
1935     PetscCall(PetscFECreate(PetscObjectComm((PetscObject)P), fem));
1936     PetscCall(PetscFESetType(*fem, PETSCFEBASIC));
1937   }
1938   PetscCall(PetscSpaceGetNumComponents(P, &Nc));
1939   PetscCall(PetscFESetNumComponents(*fem, Nc));
1940   PetscCall(PetscFESetBasisSpace(*fem, P));
1941   PetscCall(PetscFESetDualSpace(*fem, Q));
1942   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)P, &prefix));
1943   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*fem, prefix));
1944   PetscCall(PetscFESetUp(*fem));
1945   PetscCall(PetscSpaceDestroy(&P));
1946   PetscCall(PetscDualSpaceDestroy(&Q));
1947   PetscCall(PetscFESetQuadrature(*fem, q));
1948   PetscCall(PetscFESetFaceQuadrature(*fem, fq));
1949   PetscCall(PetscQuadratureDestroy(&q));
1950   PetscCall(PetscQuadratureDestroy(&fq));
1951   PetscCall(PetscFESetDefaultName_Private(*fem));
1952   PetscFunctionReturn(PETSC_SUCCESS);
1953 }
1954 
1955 static PetscErrorCode PetscFECreate_Internal(MPI_Comm comm, PetscInt dim, PetscInt Nc, DMPolytopeType ct, const char prefix[], PetscInt degree, PetscInt qorder, PetscBool setFromOptions, PetscFE *fem)
1956 {
1957   DM              K;
1958   PetscSpace      P;
1959   PetscDualSpace  Q;
1960   PetscQuadrature q, fq;
1961   PetscBool       tensor;
1962 
1963   PetscFunctionBegin;
1964   if (prefix) PetscAssertPointer(prefix, 5);
1965   PetscAssertPointer(fem, 9);
1966   switch (ct) {
1967   case DM_POLYTOPE_SEGMENT:
1968   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1969   case DM_POLYTOPE_QUADRILATERAL:
1970   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1971   case DM_POLYTOPE_HEXAHEDRON:
1972   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
1973     tensor = PETSC_TRUE;
1974     break;
1975   default:
1976     tensor = PETSC_FALSE;
1977   }
1978   /* Create space */
1979   PetscCall(PetscSpaceCreate(comm, &P));
1980   PetscCall(PetscSpaceSetType(P, PETSCSPACEPOLYNOMIAL));
1981   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)P, prefix));
1982   PetscCall(PetscSpacePolynomialSetTensor(P, tensor));
1983   PetscCall(PetscSpaceSetNumComponents(P, Nc));
1984   PetscCall(PetscSpaceSetNumVariables(P, dim));
1985   if (degree >= 0) {
1986     PetscCall(PetscSpaceSetDegree(P, degree, PETSC_DETERMINE));
1987     if (ct == DM_POLYTOPE_TRI_PRISM || ct == DM_POLYTOPE_TRI_PRISM_TENSOR) {
1988       PetscSpace Pend, Pside;
1989 
1990       PetscCall(PetscSpaceSetNumComponents(P, 1));
1991       PetscCall(PetscSpaceCreate(comm, &Pend));
1992       PetscCall(PetscSpaceSetType(Pend, PETSCSPACEPOLYNOMIAL));
1993       PetscCall(PetscSpacePolynomialSetTensor(Pend, PETSC_FALSE));
1994       PetscCall(PetscSpaceSetNumComponents(Pend, 1));
1995       PetscCall(PetscSpaceSetNumVariables(Pend, dim - 1));
1996       PetscCall(PetscSpaceSetDegree(Pend, degree, PETSC_DETERMINE));
1997       PetscCall(PetscSpaceCreate(comm, &Pside));
1998       PetscCall(PetscSpaceSetType(Pside, PETSCSPACEPOLYNOMIAL));
1999       PetscCall(PetscSpacePolynomialSetTensor(Pside, PETSC_FALSE));
2000       PetscCall(PetscSpaceSetNumComponents(Pside, 1));
2001       PetscCall(PetscSpaceSetNumVariables(Pside, 1));
2002       PetscCall(PetscSpaceSetDegree(Pside, degree, PETSC_DETERMINE));
2003       PetscCall(PetscSpaceSetType(P, PETSCSPACETENSOR));
2004       PetscCall(PetscSpaceTensorSetNumSubspaces(P, 2));
2005       PetscCall(PetscSpaceTensorSetSubspace(P, 0, Pend));
2006       PetscCall(PetscSpaceTensorSetSubspace(P, 1, Pside));
2007       PetscCall(PetscSpaceDestroy(&Pend));
2008       PetscCall(PetscSpaceDestroy(&Pside));
2009 
2010       if (Nc > 1) {
2011         PetscSpace scalar_P = P;
2012 
2013         PetscCall(PetscSpaceCreate(comm, &P));
2014         PetscCall(PetscSpaceSetNumVariables(P, dim));
2015         PetscCall(PetscSpaceSetNumComponents(P, Nc));
2016         PetscCall(PetscSpaceSetType(P, PETSCSPACESUM));
2017         PetscCall(PetscSpaceSumSetNumSubspaces(P, Nc));
2018         PetscCall(PetscSpaceSumSetConcatenate(P, PETSC_TRUE));
2019         PetscCall(PetscSpaceSumSetInterleave(P, PETSC_TRUE, PETSC_FALSE));
2020         for (PetscInt i = 0; i < Nc; i++) PetscCall(PetscSpaceSumSetSubspace(P, i, scalar_P));
2021         PetscCall(PetscSpaceDestroy(&scalar_P));
2022       }
2023     }
2024   }
2025   if (setFromOptions) PetscCall(PetscSpaceSetFromOptions(P));
2026   PetscCall(PetscSpaceSetUp(P));
2027   PetscCall(PetscSpaceGetDegree(P, &degree, NULL));
2028   PetscCall(PetscSpacePolynomialGetTensor(P, &tensor));
2029   PetscCall(PetscSpaceGetNumComponents(P, &Nc));
2030   /* Create dual space */
2031   PetscCall(PetscDualSpaceCreate(comm, &Q));
2032   PetscCall(PetscDualSpaceSetType(Q, PETSCDUALSPACELAGRANGE));
2033   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)Q, prefix));
2034   PetscCall(DMPlexCreateReferenceCell(PETSC_COMM_SELF, ct, &K));
2035   PetscCall(PetscDualSpaceSetDM(Q, K));
2036   PetscCall(DMDestroy(&K));
2037   PetscCall(PetscDualSpaceSetNumComponents(Q, Nc));
2038   PetscCall(PetscDualSpaceSetOrder(Q, degree));
2039   PetscCall(PetscDualSpaceLagrangeSetTensor(Q, (tensor || (ct == DM_POLYTOPE_TRI_PRISM)) ? PETSC_TRUE : PETSC_FALSE));
2040   if (setFromOptions) PetscCall(PetscDualSpaceSetFromOptions(Q));
2041   PetscCall(PetscDualSpaceSetUp(Q));
2042   /* Create quadrature */
2043   qorder = qorder >= 0 ? qorder : degree;
2044   if (setFromOptions) {
2045     PetscObjectOptionsBegin((PetscObject)P);
2046     PetscCall(PetscOptionsBoundedInt("-petscfe_default_quadrature_order", "Quadrature order is one less than quadrature points per edge", "PetscFECreateDefault", qorder, &qorder, NULL, 0));
2047     PetscOptionsEnd();
2048   }
2049   PetscCall(PetscDTCreateDefaultQuadrature(ct, qorder, &q, &fq));
2050   /* Create finite element */
2051   PetscCall(PetscFECreateFromSpaces(P, Q, q, fq, fem));
2052   if (setFromOptions) PetscCall(PetscFESetFromOptions(*fem));
2053   PetscFunctionReturn(PETSC_SUCCESS);
2054 }
2055 
2056 /*@C
2057   PetscFECreateDefault - Create a `PetscFE` for basic FEM computation
2058 
2059   Collective
2060 
2061   Input Parameters:
2062 + comm      - The MPI comm
2063 . dim       - The spatial dimension
2064 . Nc        - The number of components
2065 . isSimplex - Flag for simplex reference cell, otherwise its a tensor product
2066 . prefix    - The options prefix, or `NULL`
2067 - qorder    - The quadrature order or `PETSC_DETERMINE` to use `PetscSpace` polynomial degree
2068 
2069   Output Parameter:
2070 . fem - The `PetscFE` object
2071 
2072   Level: beginner
2073 
2074   Note:
2075   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.
2076 
2077 .seealso: `PetscFECreateLagrange()`, `PetscFECreateByCell()`, `PetscSpaceSetFromOptions()`, `PetscDualSpaceSetFromOptions()`, `PetscFESetFromOptions()`, `PetscFECreate()`, `PetscSpaceCreate()`, `PetscDualSpaceCreate()`
2078 @*/
2079 PetscErrorCode PetscFECreateDefault(MPI_Comm comm, PetscInt dim, PetscInt Nc, PetscBool isSimplex, const char prefix[], PetscInt qorder, PetscFE *fem)
2080 {
2081   PetscFunctionBegin;
2082   PetscCall(PetscFECreate_Internal(comm, dim, Nc, DMPolytopeTypeSimpleShape(dim, isSimplex), prefix, PETSC_DECIDE, qorder, PETSC_TRUE, fem));
2083   PetscFunctionReturn(PETSC_SUCCESS);
2084 }
2085 
2086 /*@C
2087   PetscFECreateByCell - Create a `PetscFE` for basic FEM computation
2088 
2089   Collective
2090 
2091   Input Parameters:
2092 + comm   - The MPI comm
2093 . dim    - The spatial dimension
2094 . Nc     - The number of components
2095 . ct     - The celltype of the reference cell
2096 . prefix - The options prefix, or `NULL`
2097 - qorder - The quadrature order or `PETSC_DETERMINE` to use `PetscSpace` polynomial degree
2098 
2099   Output Parameter:
2100 . fem - The `PetscFE` object
2101 
2102   Level: beginner
2103 
2104   Note:
2105   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.
2106 
2107 .seealso: `PetscFECreateDefault()`, `PetscFECreateLagrange()`, `PetscSpaceSetFromOptions()`, `PetscDualSpaceSetFromOptions()`, `PetscFESetFromOptions()`, `PetscFECreate()`, `PetscSpaceCreate()`, `PetscDualSpaceCreate()`
2108 @*/
2109 PetscErrorCode PetscFECreateByCell(MPI_Comm comm, PetscInt dim, PetscInt Nc, DMPolytopeType ct, const char prefix[], PetscInt qorder, PetscFE *fem)
2110 {
2111   PetscFunctionBegin;
2112   PetscCall(PetscFECreate_Internal(comm, dim, Nc, ct, prefix, PETSC_DECIDE, qorder, PETSC_TRUE, fem));
2113   PetscFunctionReturn(PETSC_SUCCESS);
2114 }
2115 
2116 /*@
2117   PetscFECreateLagrange - Create a `PetscFE` for the basic Lagrange space of degree k
2118 
2119   Collective
2120 
2121   Input Parameters:
2122 + comm      - The MPI comm
2123 . dim       - The spatial dimension
2124 . Nc        - The number of components
2125 . isSimplex - Flag for simplex reference cell, otherwise its a tensor product
2126 . k         - The degree k of the space
2127 - qorder    - The quadrature order or `PETSC_DETERMINE` to use `PetscSpace` polynomial degree
2128 
2129   Output Parameter:
2130 . fem - The `PetscFE` object
2131 
2132   Level: beginner
2133 
2134   Note:
2135   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.
2136 
2137 .seealso: `PetscFECreateLagrangeByCell()`, `PetscFECreateDefault()`, `PetscFECreateByCell()`, `PetscFECreate()`, `PetscSpaceCreate()`, `PetscDualSpaceCreate()`
2138 @*/
2139 PetscErrorCode PetscFECreateLagrange(MPI_Comm comm, PetscInt dim, PetscInt Nc, PetscBool isSimplex, PetscInt k, PetscInt qorder, PetscFE *fem)
2140 {
2141   PetscFunctionBegin;
2142   PetscCall(PetscFECreate_Internal(comm, dim, Nc, DMPolytopeTypeSimpleShape(dim, isSimplex), NULL, k, qorder, PETSC_FALSE, fem));
2143   PetscFunctionReturn(PETSC_SUCCESS);
2144 }
2145 
2146 /*@
2147   PetscFECreateLagrangeByCell - Create a `PetscFE` for the basic Lagrange space of degree k
2148 
2149   Collective
2150 
2151   Input Parameters:
2152 + comm   - The MPI comm
2153 . dim    - The spatial dimension
2154 . Nc     - The number of components
2155 . ct     - The celltype of the reference cell
2156 . k      - The degree k of the space
2157 - qorder - The quadrature order or `PETSC_DETERMINE` to use `PetscSpace` polynomial degree
2158 
2159   Output Parameter:
2160 . fem - The `PetscFE` object
2161 
2162   Level: beginner
2163 
2164   Note:
2165   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.
2166 
2167 .seealso: `PetscFECreateLagrange()`, `PetscFECreateDefault()`, `PetscFECreateByCell()`, `PetscFECreate()`, `PetscSpaceCreate()`, `PetscDualSpaceCreate()`
2168 @*/
2169 PetscErrorCode PetscFECreateLagrangeByCell(MPI_Comm comm, PetscInt dim, PetscInt Nc, DMPolytopeType ct, PetscInt k, PetscInt qorder, PetscFE *fem)
2170 {
2171   PetscFunctionBegin;
2172   PetscCall(PetscFECreate_Internal(comm, dim, Nc, ct, NULL, k, qorder, PETSC_FALSE, fem));
2173   PetscFunctionReturn(PETSC_SUCCESS);
2174 }
2175 
2176 /*@C
2177   PetscFESetName - Names the `PetscFE` and its subobjects
2178 
2179   Not Collective
2180 
2181   Input Parameters:
2182 + fe   - The `PetscFE`
2183 - name - The name
2184 
2185   Level: intermediate
2186 
2187 .seealso: `PetscFECreate()`, `PetscSpaceCreate()`, `PetscDualSpaceCreate()`
2188 @*/
2189 PetscErrorCode PetscFESetName(PetscFE fe, const char name[])
2190 {
2191   PetscSpace     P;
2192   PetscDualSpace Q;
2193 
2194   PetscFunctionBegin;
2195   PetscCall(PetscFEGetBasisSpace(fe, &P));
2196   PetscCall(PetscFEGetDualSpace(fe, &Q));
2197   PetscCall(PetscObjectSetName((PetscObject)fe, name));
2198   PetscCall(PetscObjectSetName((PetscObject)P, name));
2199   PetscCall(PetscObjectSetName((PetscObject)Q, name));
2200   PetscFunctionReturn(PETSC_SUCCESS);
2201 }
2202 
2203 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[])
2204 {
2205   PetscInt dOffset = 0, fOffset = 0, f, g;
2206 
2207   for (f = 0; f < Nf; ++f) {
2208     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);
2209     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);
2210     PetscFE          fe;
2211     const PetscInt   k       = ds->jetDegree[f];
2212     const PetscInt   cdim    = T[f]->cdim;
2213     const PetscInt   dE      = fegeom->dimEmbed;
2214     const PetscInt   Nq      = T[f]->Np;
2215     const PetscInt   Nbf     = T[f]->Nb;
2216     const PetscInt   Ncf     = T[f]->Nc;
2217     const PetscReal *Bq      = &T[f]->T[0][(r * Nq + q) * Nbf * Ncf];
2218     const PetscReal *Dq      = &T[f]->T[1][(r * Nq + q) * Nbf * Ncf * cdim];
2219     const PetscReal *Hq      = k > 1 ? &T[f]->T[2][(r * Nq + q) * Nbf * Ncf * cdim * cdim] : NULL;
2220     PetscInt         hOffset = 0, b, c, d;
2221 
2222     PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
2223     for (c = 0; c < Ncf; ++c) u[fOffset + c] = 0.0;
2224     for (d = 0; d < dE * Ncf; ++d) u_x[fOffset * dE + d] = 0.0;
2225     for (b = 0; b < Nbf; ++b) {
2226       for (c = 0; c < Ncf; ++c) {
2227         const PetscInt cidx = b * Ncf + c;
2228 
2229         u[fOffset + c] += Bq[cidx] * coefficients[dOffset + b];
2230         for (d = 0; d < cdim; ++d) u_x[(fOffset + c) * dE + d] += Dq[cidx * cdim + d] * coefficients[dOffset + b];
2231       }
2232     }
2233     if (k > 1) {
2234       for (g = 0; g < Nf; ++g) hOffset += T[g]->Nc * dE;
2235       for (d = 0; d < dE * dE * Ncf; ++d) u_x[hOffset + fOffset * dE * dE + d] = 0.0;
2236       for (b = 0; b < Nbf; ++b) {
2237         for (c = 0; c < Ncf; ++c) {
2238           const PetscInt cidx = b * Ncf + c;
2239 
2240           for (d = 0; d < cdim * cdim; ++d) u_x[hOffset + (fOffset + c) * dE * dE + d] += Hq[cidx * cdim * cdim + d] * coefficients[dOffset + b];
2241         }
2242       }
2243       PetscCall(PetscFEPushforwardHessian(fe, fegeom, 1, &u_x[hOffset + fOffset * dE * dE]));
2244     }
2245     PetscCall(PetscFEPushforward(fe, fegeom, 1, &u[fOffset]));
2246     PetscCall(PetscFEPushforwardGradient(fe, fegeom, 1, &u_x[fOffset * dE]));
2247     if (u_t) {
2248       for (c = 0; c < Ncf; ++c) u_t[fOffset + c] = 0.0;
2249       for (b = 0; b < Nbf; ++b) {
2250         for (c = 0; c < Ncf; ++c) {
2251           const PetscInt cidx = b * Ncf + c;
2252 
2253           u_t[fOffset + c] += Bq[cidx] * coefficients_t[dOffset + b];
2254         }
2255       }
2256       PetscCall(PetscFEPushforward(fe, fegeom, 1, &u_t[fOffset]));
2257     }
2258     fOffset += Ncf;
2259     dOffset += Nbf;
2260   }
2261   return PETSC_SUCCESS;
2262 }
2263 
2264 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[])
2265 {
2266   PetscInt dOffset = 0, fOffset = 0, f, g;
2267 
2268   /* f is the field number in the DS, g is the field number in u[] */
2269   for (f = 0, g = 0; f < Nf; ++f) {
2270     PetscBool isCohesive;
2271     PetscInt  Ns, s;
2272 
2273     if (!Tab[f]) continue;
2274     PetscCall(PetscDSGetCohesive(ds, f, &isCohesive));
2275     Ns = isCohesive ? 1 : 2;
2276     {
2277       PetscTabulation T   = isCohesive ? Tab[f] : Tabf[f];
2278       PetscFE         fe  = (PetscFE)ds->disc[f];
2279       const PetscInt  dEt = T->cdim;
2280       const PetscInt  dE  = fegeom->dimEmbed;
2281       const PetscInt  Nq  = T->Np;
2282       const PetscInt  Nbf = T->Nb;
2283       const PetscInt  Ncf = T->Nc;
2284 
2285       for (s = 0; s < Ns; ++s, ++g) {
2286         const PetscInt   r  = isCohesive ? rc : rf[s];
2287         const PetscInt   q  = isCohesive ? qc : qf[s];
2288         const PetscReal *Bq = &T->T[0][(r * Nq + q) * Nbf * Ncf];
2289         const PetscReal *Dq = &T->T[1][(r * Nq + q) * Nbf * Ncf * dEt];
2290         PetscInt         b, c, d;
2291 
2292         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);
2293         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);
2294         for (c = 0; c < Ncf; ++c) u[fOffset + c] = 0.0;
2295         for (d = 0; d < dE * Ncf; ++d) u_x[fOffset * dE + d] = 0.0;
2296         for (b = 0; b < Nbf; ++b) {
2297           for (c = 0; c < Ncf; ++c) {
2298             const PetscInt cidx = b * Ncf + c;
2299 
2300             u[fOffset + c] += Bq[cidx] * coefficients[dOffset + b];
2301             for (d = 0; d < dEt; ++d) u_x[(fOffset + c) * dE + d] += Dq[cidx * dEt + d] * coefficients[dOffset + b];
2302           }
2303         }
2304         PetscCall(PetscFEPushforward(fe, fegeom, 1, &u[fOffset]));
2305         PetscCall(PetscFEPushforwardGradient(fe, fegeom, 1, &u_x[fOffset * dE]));
2306         if (u_t) {
2307           for (c = 0; c < Ncf; ++c) u_t[fOffset + c] = 0.0;
2308           for (b = 0; b < Nbf; ++b) {
2309             for (c = 0; c < Ncf; ++c) {
2310               const PetscInt cidx = b * Ncf + c;
2311 
2312               u_t[fOffset + c] += Bq[cidx] * coefficients_t[dOffset + b];
2313             }
2314           }
2315           PetscCall(PetscFEPushforward(fe, fegeom, 1, &u_t[fOffset]));
2316         }
2317         fOffset += Ncf;
2318         dOffset += Nbf;
2319       }
2320     }
2321   }
2322   return PETSC_SUCCESS;
2323 }
2324 
2325 PetscErrorCode PetscFEEvaluateFaceFields_Internal(PetscDS prob, PetscInt field, PetscInt faceLoc, const PetscScalar coefficients[], PetscScalar u[])
2326 {
2327   PetscFE         fe;
2328   PetscTabulation Tc;
2329   PetscInt        b, c;
2330 
2331   if (!prob) return PETSC_SUCCESS;
2332   PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fe));
2333   PetscCall(PetscFEGetFaceCentroidTabulation(fe, &Tc));
2334   {
2335     const PetscReal *faceBasis = Tc->T[0];
2336     const PetscInt   Nb        = Tc->Nb;
2337     const PetscInt   Nc        = Tc->Nc;
2338 
2339     for (c = 0; c < Nc; ++c) u[c] = 0.0;
2340     for (b = 0; b < Nb; ++b) {
2341       for (c = 0; c < Nc; ++c) u[c] += coefficients[b] * faceBasis[(faceLoc * Nb + b) * Nc + c];
2342     }
2343   }
2344   return PETSC_SUCCESS;
2345 }
2346 
2347 PetscErrorCode PetscFEUpdateElementVec_Internal(PetscFE fe, PetscTabulation T, PetscInt r, PetscScalar tmpBasis[], PetscScalar tmpBasisDer[], PetscInt e, PetscFEGeom *fegeom, PetscScalar f0[], PetscScalar f1[], PetscScalar elemVec[])
2348 {
2349   PetscFEGeom      pgeom;
2350   const PetscInt   dEt      = T->cdim;
2351   const PetscInt   dE       = fegeom->dimEmbed;
2352   const PetscInt   Nq       = T->Np;
2353   const PetscInt   Nb       = T->Nb;
2354   const PetscInt   Nc       = T->Nc;
2355   const PetscReal *basis    = &T->T[0][r * Nq * Nb * Nc];
2356   const PetscReal *basisDer = &T->T[1][r * Nq * Nb * Nc * dEt];
2357   PetscInt         q, b, c, d;
2358 
2359   for (q = 0; q < Nq; ++q) {
2360     for (b = 0; b < Nb; ++b) {
2361       for (c = 0; c < Nc; ++c) {
2362         const PetscInt bcidx = b * Nc + c;
2363 
2364         tmpBasis[bcidx] = basis[q * Nb * Nc + bcidx];
2365         for (d = 0; d < dEt; ++d) tmpBasisDer[bcidx * dE + d] = basisDer[q * Nb * Nc * dEt + bcidx * dEt + d];
2366         for (d = dEt; d < dE; ++d) tmpBasisDer[bcidx * dE + d] = 0.0;
2367       }
2368     }
2369     PetscCall(PetscFEGeomGetCellPoint(fegeom, e, q, &pgeom));
2370     PetscCall(PetscFEPushforward(fe, &pgeom, Nb, tmpBasis));
2371     PetscCall(PetscFEPushforwardGradient(fe, &pgeom, Nb, tmpBasisDer));
2372     for (b = 0; b < Nb; ++b) {
2373       for (c = 0; c < Nc; ++c) {
2374         const PetscInt bcidx = b * Nc + c;
2375         const PetscInt qcidx = q * Nc + c;
2376 
2377         elemVec[b] += tmpBasis[bcidx] * f0[qcidx];
2378         for (d = 0; d < dE; ++d) elemVec[b] += tmpBasisDer[bcidx * dE + d] * f1[qcidx * dE + d];
2379       }
2380     }
2381   }
2382   return PETSC_SUCCESS;
2383 }
2384 
2385 PetscErrorCode PetscFEUpdateElementVec_Hybrid_Internal(PetscFE fe, PetscTabulation T, PetscInt r, PetscInt side, PetscScalar tmpBasis[], PetscScalar tmpBasisDer[], PetscFEGeom *fegeom, PetscScalar f0[], PetscScalar f1[], PetscScalar elemVec[])
2386 {
2387   const PetscInt   dE       = T->cdim;
2388   const PetscInt   Nq       = T->Np;
2389   const PetscInt   Nb       = T->Nb;
2390   const PetscInt   Nc       = T->Nc;
2391   const PetscReal *basis    = &T->T[0][r * Nq * Nb * Nc];
2392   const PetscReal *basisDer = &T->T[1][r * Nq * Nb * Nc * dE];
2393 
2394   for (PetscInt q = 0; q < Nq; ++q) {
2395     for (PetscInt b = 0; b < Nb; ++b) {
2396       for (PetscInt c = 0; c < Nc; ++c) {
2397         const PetscInt bcidx = b * Nc + c;
2398 
2399         tmpBasis[bcidx] = basis[q * Nb * Nc + bcidx];
2400         for (PetscInt d = 0; d < dE; ++d) tmpBasisDer[bcidx * dE + d] = basisDer[q * Nb * Nc * dE + bcidx * dE + d];
2401       }
2402     }
2403     PetscCall(PetscFEPushforward(fe, fegeom, Nb, tmpBasis));
2404     // TODO This is currently broken since we do not pull the geometry down to the lower dimension
2405     // PetscCall(PetscFEPushforwardGradient(fe, fegeom, Nb, tmpBasisDer));
2406     if (side == 2) {
2407       // Integrating over whole cohesive cell, so insert for both sides
2408       for (PetscInt s = 0; s < 2; ++s) {
2409         for (PetscInt b = 0; b < Nb; ++b) {
2410           for (PetscInt c = 0; c < Nc; ++c) {
2411             const PetscInt bcidx = b * Nc + c;
2412             const PetscInt qcidx = (q * 2 + s) * Nc + c;
2413 
2414             elemVec[Nb * s + b] += tmpBasis[bcidx] * f0[qcidx];
2415             for (PetscInt d = 0; d < dE; ++d) elemVec[Nb * s + b] += tmpBasisDer[bcidx * dE + d] * f1[qcidx * dE + d];
2416           }
2417         }
2418       }
2419     } else {
2420       // Integrating over endcaps of cohesive cell, so insert for correct side
2421       for (PetscInt b = 0; b < Nb; ++b) {
2422         for (PetscInt c = 0; c < Nc; ++c) {
2423           const PetscInt bcidx = b * Nc + c;
2424           const PetscInt qcidx = q * Nc + c;
2425 
2426           elemVec[Nb * side + b] += tmpBasis[bcidx] * f0[qcidx];
2427           for (PetscInt d = 0; d < dE; ++d) elemVec[Nb * side + b] += tmpBasisDer[bcidx * dE + d] * f1[qcidx * dE + d];
2428         }
2429       }
2430     }
2431   }
2432   return PETSC_SUCCESS;
2433 }
2434 
2435 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[])
2436 {
2437   const PetscInt   cdim      = TI->cdim;
2438   const PetscInt   dE        = fegeom->dimEmbed;
2439   const PetscInt   NqI       = TI->Np;
2440   const PetscInt   NbI       = TI->Nb;
2441   const PetscInt   NcI       = TI->Nc;
2442   const PetscReal *basisI    = &TI->T[0][(r * NqI + q) * NbI * NcI];
2443   const PetscReal *basisDerI = &TI->T[1][(r * NqI + q) * NbI * NcI * cdim];
2444   const PetscInt   NqJ       = TJ->Np;
2445   const PetscInt   NbJ       = TJ->Nb;
2446   const PetscInt   NcJ       = TJ->Nc;
2447   const PetscReal *basisJ    = &TJ->T[0][(r * NqJ + q) * NbJ * NcJ];
2448   const PetscReal *basisDerJ = &TJ->T[1][(r * NqJ + q) * NbJ * NcJ * cdim];
2449   PetscInt         f, fc, g, gc, df, dg;
2450 
2451   for (f = 0; f < NbI; ++f) {
2452     for (fc = 0; fc < NcI; ++fc) {
2453       const PetscInt fidx = f * NcI + fc; /* Test function basis index */
2454 
2455       tmpBasisI[fidx] = basisI[fidx];
2456       for (df = 0; df < cdim; ++df) tmpBasisDerI[fidx * dE + df] = basisDerI[fidx * cdim + df];
2457     }
2458   }
2459   PetscCall(PetscFEPushforward(feI, fegeom, NbI, tmpBasisI));
2460   PetscCall(PetscFEPushforwardGradient(feI, fegeom, NbI, tmpBasisDerI));
2461   for (g = 0; g < NbJ; ++g) {
2462     for (gc = 0; gc < NcJ; ++gc) {
2463       const PetscInt gidx = g * NcJ + gc; /* Trial function basis index */
2464 
2465       tmpBasisJ[gidx] = basisJ[gidx];
2466       for (dg = 0; dg < cdim; ++dg) tmpBasisDerJ[gidx * dE + dg] = basisDerJ[gidx * cdim + dg];
2467     }
2468   }
2469   PetscCall(PetscFEPushforward(feJ, fegeom, NbJ, tmpBasisJ));
2470   PetscCall(PetscFEPushforwardGradient(feJ, fegeom, NbJ, tmpBasisDerJ));
2471   for (f = 0; f < NbI; ++f) {
2472     for (fc = 0; fc < NcI; ++fc) {
2473       const PetscInt fidx = f * NcI + fc; /* Test function basis index */
2474       const PetscInt i    = offsetI + f;  /* Element matrix row */
2475       for (g = 0; g < NbJ; ++g) {
2476         for (gc = 0; gc < NcJ; ++gc) {
2477           const PetscInt gidx = g * NcJ + gc; /* Trial function basis index */
2478           const PetscInt j    = offsetJ + g;  /* Element matrix column */
2479           const PetscInt fOff = eOffset + i * totDim + j;
2480 
2481           elemMat[fOff] += tmpBasisI[fidx] * g0[fc * NcJ + gc] * tmpBasisJ[gidx];
2482           for (df = 0; df < dE; ++df) {
2483             elemMat[fOff] += tmpBasisI[fidx] * g1[(fc * NcJ + gc) * dE + df] * tmpBasisDerJ[gidx * dE + df];
2484             elemMat[fOff] += tmpBasisDerI[fidx * dE + df] * g2[(fc * NcJ + gc) * dE + df] * tmpBasisJ[gidx];
2485             for (dg = 0; dg < dE; ++dg) elemMat[fOff] += tmpBasisDerI[fidx * dE + df] * g3[((fc * NcJ + gc) * dE + df) * dE + dg] * tmpBasisDerJ[gidx * dE + dg];
2486           }
2487         }
2488       }
2489     }
2490   }
2491   return PETSC_SUCCESS;
2492 }
2493 
2494 PetscErrorCode PetscFEUpdateElementMat_Hybrid_Internal(PetscFE feI, PetscBool isHybridI, PetscFE feJ, PetscBool isHybridJ, PetscInt r, PetscInt s, PetscInt t, PetscInt q, PetscTabulation TI, PetscScalar tmpBasisI[], PetscScalar tmpBasisDerI[], PetscTabulation TJ, PetscScalar tmpBasisJ[], PetscScalar tmpBasisDerJ[], PetscFEGeom *fegeom, const PetscScalar g0[], const PetscScalar g1[], const PetscScalar g2[], const PetscScalar g3[], PetscInt eOffset, PetscInt totDim, PetscInt offsetI, PetscInt offsetJ, PetscScalar elemMat[])
2495 {
2496   const PetscInt   dE        = TI->cdim;
2497   const PetscInt   NqI       = TI->Np;
2498   const PetscInt   NbI       = TI->Nb;
2499   const PetscInt   NcI       = TI->Nc;
2500   const PetscReal *basisI    = &TI->T[0][(r * NqI + q) * NbI * NcI];
2501   const PetscReal *basisDerI = &TI->T[1][(r * NqI + q) * NbI * NcI * dE];
2502   const PetscInt   NqJ       = TJ->Np;
2503   const PetscInt   NbJ       = TJ->Nb;
2504   const PetscInt   NcJ       = TJ->Nc;
2505   const PetscReal *basisJ    = &TJ->T[0][(r * NqJ + q) * NbJ * NcJ];
2506   const PetscReal *basisDerJ = &TJ->T[1][(r * NqJ + q) * NbJ * NcJ * dE];
2507   const PetscInt   so        = isHybridI ? 0 : s;
2508   const PetscInt   to        = isHybridJ ? 0 : t;
2509   PetscInt         f, fc, g, gc, df, dg;
2510 
2511   for (f = 0; f < NbI; ++f) {
2512     for (fc = 0; fc < NcI; ++fc) {
2513       const PetscInt fidx = f * NcI + fc; /* Test function basis index */
2514 
2515       tmpBasisI[fidx] = basisI[fidx];
2516       for (df = 0; df < dE; ++df) tmpBasisDerI[fidx * dE + df] = basisDerI[fidx * dE + df];
2517     }
2518   }
2519   PetscCall(PetscFEPushforward(feI, fegeom, NbI, tmpBasisI));
2520   PetscCall(PetscFEPushforwardGradient(feI, fegeom, NbI, tmpBasisDerI));
2521   for (g = 0; g < NbJ; ++g) {
2522     for (gc = 0; gc < NcJ; ++gc) {
2523       const PetscInt gidx = g * NcJ + gc; /* Trial function basis index */
2524 
2525       tmpBasisJ[gidx] = basisJ[gidx];
2526       for (dg = 0; dg < dE; ++dg) tmpBasisDerJ[gidx * dE + dg] = basisDerJ[gidx * dE + dg];
2527     }
2528   }
2529   PetscCall(PetscFEPushforward(feJ, fegeom, NbJ, tmpBasisJ));
2530   // TODO This is currently broken since we do not pull the geometry down to the lower dimension
2531   // PetscCall(PetscFEPushforwardGradient(feJ, fegeom, NbJ, tmpBasisDerJ));
2532   for (f = 0; f < NbI; ++f) {
2533     for (fc = 0; fc < NcI; ++fc) {
2534       const PetscInt fidx = f * NcI + fc;           /* Test function basis index */
2535       const PetscInt i    = offsetI + NbI * so + f; /* Element matrix row */
2536       for (g = 0; g < NbJ; ++g) {
2537         for (gc = 0; gc < NcJ; ++gc) {
2538           const PetscInt gidx = g * NcJ + gc;           /* Trial function basis index */
2539           const PetscInt j    = offsetJ + NbJ * to + g; /* Element matrix column */
2540           const PetscInt fOff = eOffset + i * totDim + j;
2541 
2542           elemMat[fOff] += tmpBasisI[fidx] * g0[fc * NcJ + gc] * tmpBasisJ[gidx];
2543           for (df = 0; df < dE; ++df) {
2544             elemMat[fOff] += tmpBasisI[fidx] * g1[(fc * NcJ + gc) * dE + df] * tmpBasisDerJ[gidx * dE + df];
2545             elemMat[fOff] += tmpBasisDerI[fidx * dE + df] * g2[(fc * NcJ + gc) * dE + df] * tmpBasisJ[gidx];
2546             for (dg = 0; dg < dE; ++dg) elemMat[fOff] += tmpBasisDerI[fidx * dE + df] * g3[((fc * NcJ + gc) * dE + df) * dE + dg] * tmpBasisDerJ[gidx * dE + dg];
2547           }
2548         }
2549       }
2550     }
2551   }
2552   return PETSC_SUCCESS;
2553 }
2554 
2555 PetscErrorCode PetscFECreateCellGeometry(PetscFE fe, PetscQuadrature quad, PetscFEGeom *cgeom)
2556 {
2557   PetscDualSpace  dsp;
2558   DM              dm;
2559   PetscQuadrature quadDef;
2560   PetscInt        dim, cdim, Nq;
2561 
2562   PetscFunctionBegin;
2563   PetscCall(PetscFEGetDualSpace(fe, &dsp));
2564   PetscCall(PetscDualSpaceGetDM(dsp, &dm));
2565   PetscCall(DMGetDimension(dm, &dim));
2566   PetscCall(DMGetCoordinateDim(dm, &cdim));
2567   PetscCall(PetscFEGetQuadrature(fe, &quadDef));
2568   quad = quad ? quad : quadDef;
2569   PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
2570   PetscCall(PetscMalloc1(Nq * cdim, &cgeom->v));
2571   PetscCall(PetscMalloc1(Nq * cdim * cdim, &cgeom->J));
2572   PetscCall(PetscMalloc1(Nq * cdim * cdim, &cgeom->invJ));
2573   PetscCall(PetscMalloc1(Nq, &cgeom->detJ));
2574   cgeom->dim       = dim;
2575   cgeom->dimEmbed  = cdim;
2576   cgeom->numCells  = 1;
2577   cgeom->numPoints = Nq;
2578   PetscCall(DMPlexComputeCellGeometryFEM(dm, 0, quad, cgeom->v, cgeom->J, cgeom->invJ, cgeom->detJ));
2579   PetscFunctionReturn(PETSC_SUCCESS);
2580 }
2581 
2582 PetscErrorCode PetscFEDestroyCellGeometry(PetscFE fe, PetscFEGeom *cgeom)
2583 {
2584   PetscFunctionBegin;
2585   PetscCall(PetscFree(cgeom->v));
2586   PetscCall(PetscFree(cgeom->J));
2587   PetscCall(PetscFree(cgeom->invJ));
2588   PetscCall(PetscFree(cgeom->detJ));
2589   PetscFunctionReturn(PETSC_SUCCESS);
2590 }
2591