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