xref: /petsc/src/dm/dt/dualspace/interface/dualspace.c (revision f13dfd9ea68e0ddeee984e65c377a1819eab8a8a)
1 #include <petsc/private/petscfeimpl.h> /*I "petscfe.h" I*/
2 #include <petscdmplex.h>
3 
4 PetscClassId PETSCDUALSPACE_CLASSID = 0;
5 
6 PetscLogEvent PETSCDUALSPACE_SetUp;
7 
8 PetscFunctionList PetscDualSpaceList              = NULL;
9 PetscBool         PetscDualSpaceRegisterAllCalled = PETSC_FALSE;
10 
11 /*
12   PetscDualSpaceLatticePointLexicographic_Internal - Returns all tuples of size 'len' with nonnegative integers that sum up to at most 'max'.
13                                                      Ordering is lexicographic with lowest index as least significant in ordering.
14                                                      e.g. for len == 2 and max == 2, this will return, in order, {0,0}, {1,0}, {2,0}, {0,1}, {1,1}, {0,2}.
15 
16   Input Parameters:
17 + len - The length of the tuple
18 . max - The maximum sum
19 - tup - A tuple of length len+1: tup[len] > 0 indicates a stopping condition
20 
21   Output Parameter:
22 . tup - A tuple of `len` integers whose sum is at most `max`
23 
24   Level: developer
25 
26 .seealso: `PetscDualSpaceType`, `PetscDualSpaceTensorPointLexicographic_Internal()`
27 */
28 PetscErrorCode PetscDualSpaceLatticePointLexicographic_Internal(PetscInt len, PetscInt max, PetscInt tup[])
29 {
30   PetscFunctionBegin;
31   while (len--) {
32     max -= tup[len];
33     if (!max) {
34       tup[len] = 0;
35       break;
36     }
37   }
38   tup[++len]++;
39   PetscFunctionReturn(PETSC_SUCCESS);
40 }
41 
42 /*
43   PetscDualSpaceTensorPointLexicographic_Internal - Returns all tuples of size 'len' with nonnegative integers that are all less than or equal to 'max'.
44                                                     Ordering is lexicographic with lowest index as least significant in ordering.
45                                                     e.g. for len == 2 and max == 2, this will return, in order, {0,0}, {1,0}, {2,0}, {0,1}, {1,1}, {2,1}, {0,2}, {1,2}, {2,2}.
46 
47   Input Parameters:
48 + len - The length of the tuple
49 . max - The maximum value
50 - tup - A tuple of length len+1: tup[len] > 0 indicates a stopping condition
51 
52   Output Parameter:
53 . tup - A tuple of `len` integers whose entries are at most `max`
54 
55   Level: developer
56 
57 .seealso: `PetscDualSpaceType`, `PetscDualSpaceLatticePointLexicographic_Internal()`
58 */
59 PetscErrorCode PetscDualSpaceTensorPointLexicographic_Internal(PetscInt len, PetscInt max, PetscInt tup[])
60 {
61   PetscInt i;
62 
63   PetscFunctionBegin;
64   for (i = 0; i < len; i++) {
65     if (tup[i] < max) {
66       break;
67     } else {
68       tup[i] = 0;
69     }
70   }
71   tup[i]++;
72   PetscFunctionReturn(PETSC_SUCCESS);
73 }
74 
75 /*@C
76   PetscDualSpaceRegister - Adds a new `PetscDualSpaceType`
77 
78   Not Collective
79 
80   Input Parameters:
81 + sname    - The name of a new user-defined creation routine
82 - function - The creation routine
83 
84   Example Usage:
85 .vb
86     PetscDualSpaceRegister("my_space", MyPetscDualSpaceCreate);
87 .ve
88 
89   Then, your PetscDualSpace type can be chosen with the procedural interface via
90 .vb
91     PetscDualSpaceCreate(MPI_Comm, PetscDualSpace *);
92     PetscDualSpaceSetType(PetscDualSpace, "my_dual_space");
93 .ve
94   or at runtime via the option
95 .vb
96     -petscdualspace_type my_dual_space
97 .ve
98 
99   Level: advanced
100 
101   Note:
102   `PetscDualSpaceRegister()` may be called multiple times to add several user-defined `PetscDualSpace`
103 
104 .seealso: `PetscDualSpace`, `PetscDualSpaceType`, `PetscDualSpaceRegisterAll()`, `PetscDualSpaceRegisterDestroy()`
105 @*/
106 PetscErrorCode PetscDualSpaceRegister(const char sname[], PetscErrorCode (*function)(PetscDualSpace))
107 {
108   PetscFunctionBegin;
109   PetscCall(PetscFunctionListAdd(&PetscDualSpaceList, sname, function));
110   PetscFunctionReturn(PETSC_SUCCESS);
111 }
112 
113 /*@C
114   PetscDualSpaceSetType - Builds a particular `PetscDualSpace` based on its `PetscDualSpaceType`
115 
116   Collective
117 
118   Input Parameters:
119 + sp   - The `PetscDualSpace` object
120 - name - The kind of space
121 
122   Options Database Key:
123 . -petscdualspace_type <type> - Sets the PetscDualSpace type; use -help for a list of available types
124 
125   Level: intermediate
126 
127 .seealso: `PetscDualSpace`, `PetscDualSpaceType`, `PetscDualSpaceGetType()`, `PetscDualSpaceCreate()`
128 @*/
129 PetscErrorCode PetscDualSpaceSetType(PetscDualSpace sp, PetscDualSpaceType name)
130 {
131   PetscErrorCode (*r)(PetscDualSpace);
132   PetscBool match;
133 
134   PetscFunctionBegin;
135   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
136   PetscCall(PetscObjectTypeCompare((PetscObject)sp, name, &match));
137   if (match) PetscFunctionReturn(PETSC_SUCCESS);
138 
139   if (!PetscDualSpaceRegisterAllCalled) PetscCall(PetscDualSpaceRegisterAll());
140   PetscCall(PetscFunctionListFind(PetscDualSpaceList, name, &r));
141   PetscCheck(r, PetscObjectComm((PetscObject)sp), PETSC_ERR_ARG_UNKNOWN_TYPE, "Unknown PetscDualSpace type: %s", name);
142 
143   PetscTryTypeMethod(sp, destroy);
144   sp->ops->destroy = NULL;
145 
146   PetscCall((*r)(sp));
147   PetscCall(PetscObjectChangeTypeName((PetscObject)sp, name));
148   PetscFunctionReturn(PETSC_SUCCESS);
149 }
150 
151 /*@C
152   PetscDualSpaceGetType - Gets the `PetscDualSpaceType` name (as a string) from the object.
153 
154   Not Collective
155 
156   Input Parameter:
157 . sp - The `PetscDualSpace`
158 
159   Output Parameter:
160 . name - The `PetscDualSpaceType` name
161 
162   Level: intermediate
163 
164 .seealso: `PetscDualSpace`, `PetscDualSpaceType`, `PetscDualSpaceSetType()`, `PetscDualSpaceCreate()`
165 @*/
166 PetscErrorCode PetscDualSpaceGetType(PetscDualSpace sp, PetscDualSpaceType *name)
167 {
168   PetscFunctionBegin;
169   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
170   PetscAssertPointer(name, 2);
171   if (!PetscDualSpaceRegisterAllCalled) PetscCall(PetscDualSpaceRegisterAll());
172   *name = ((PetscObject)sp)->type_name;
173   PetscFunctionReturn(PETSC_SUCCESS);
174 }
175 
176 static PetscErrorCode PetscDualSpaceView_ASCII(PetscDualSpace sp, PetscViewer v)
177 {
178   PetscViewerFormat format;
179   PetscInt          pdim, f;
180 
181   PetscFunctionBegin;
182   PetscCall(PetscDualSpaceGetDimension(sp, &pdim));
183   PetscCall(PetscObjectPrintClassNamePrefixType((PetscObject)sp, v));
184   PetscCall(PetscViewerASCIIPushTab(v));
185   if (sp->k != 0 && sp->k != PETSC_FORM_DEGREE_UNDEFINED) {
186     PetscCall(PetscViewerASCIIPrintf(v, "Dual space for %" PetscInt_FMT "-forms %swith %" PetscInt_FMT " components, size %" PetscInt_FMT "\n", PetscAbsInt(sp->k), sp->k < 0 ? "(stored in dual form) " : "", sp->Nc, pdim));
187   } else {
188     PetscCall(PetscViewerASCIIPrintf(v, "Dual space with %" PetscInt_FMT " components, size %" PetscInt_FMT "\n", sp->Nc, pdim));
189   }
190   PetscTryTypeMethod(sp, view, v);
191   PetscCall(PetscViewerGetFormat(v, &format));
192   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
193     PetscCall(PetscViewerASCIIPushTab(v));
194     for (f = 0; f < pdim; ++f) {
195       PetscCall(PetscViewerASCIIPrintf(v, "Dual basis vector %" PetscInt_FMT "\n", f));
196       PetscCall(PetscViewerASCIIPushTab(v));
197       PetscCall(PetscQuadratureView(sp->functional[f], v));
198       PetscCall(PetscViewerASCIIPopTab(v));
199     }
200     PetscCall(PetscViewerASCIIPopTab(v));
201   }
202   PetscCall(PetscViewerASCIIPopTab(v));
203   PetscFunctionReturn(PETSC_SUCCESS);
204 }
205 
206 /*@C
207   PetscDualSpaceViewFromOptions - View a `PetscDualSpace` based on values in the options database
208 
209   Collective
210 
211   Input Parameters:
212 + A    - the `PetscDualSpace` object
213 . obj  - Optional object, provides the options prefix
214 - name - command line option name
215 
216   Level: intermediate
217 
218   Note:
219   See `PetscObjectViewFromOptions()` for possible command line values
220 
221 .seealso: `PetscDualSpace`, `PetscDualSpaceView()`, `PetscObjectViewFromOptions()`, `PetscDualSpaceCreate()`
222 @*/
223 PetscErrorCode PetscDualSpaceViewFromOptions(PetscDualSpace A, PetscObject obj, const char name[])
224 {
225   PetscFunctionBegin;
226   PetscValidHeaderSpecific(A, PETSCDUALSPACE_CLASSID, 1);
227   PetscCall(PetscObjectViewFromOptions((PetscObject)A, obj, name));
228   PetscFunctionReturn(PETSC_SUCCESS);
229 }
230 
231 /*@
232   PetscDualSpaceView - Views a `PetscDualSpace`
233 
234   Collective
235 
236   Input Parameters:
237 + sp - the `PetscDualSpace` object to view
238 - v  - the viewer
239 
240   Level: beginner
241 
242 .seealso: `PetscViewer`, `PetscDualSpaceDestroy()`, `PetscDualSpace`
243 @*/
244 PetscErrorCode PetscDualSpaceView(PetscDualSpace sp, PetscViewer v)
245 {
246   PetscBool iascii;
247 
248   PetscFunctionBegin;
249   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
250   if (v) PetscValidHeaderSpecific(v, PETSC_VIEWER_CLASSID, 2);
251   if (!v) PetscCall(PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)sp), &v));
252   PetscCall(PetscObjectTypeCompare((PetscObject)v, PETSCVIEWERASCII, &iascii));
253   if (iascii) PetscCall(PetscDualSpaceView_ASCII(sp, v));
254   PetscFunctionReturn(PETSC_SUCCESS);
255 }
256 
257 /*@
258   PetscDualSpaceSetFromOptions - sets parameters in a `PetscDualSpace` from the options database
259 
260   Collective
261 
262   Input Parameter:
263 . sp - the `PetscDualSpace` object to set options for
264 
265   Options Database Keys:
266 + -petscdualspace_order <order>                 - the approximation order of the space
267 . -petscdualspace_form_degree <deg>             - the form degree, say 0 for point evaluations, or 2 for area integrals
268 . -petscdualspace_components <c>                - the number of components, say d for a vector field
269 . -petscdualspace_refcell <celltype>            - Reference cell type name
270 . -petscdualspace_lagrange_continuity           - Flag for continuous element
271 . -petscdualspace_lagrange_tensor               - Flag for tensor dual space
272 . -petscdualspace_lagrange_trimmed              - Flag for trimmed dual space
273 . -petscdualspace_lagrange_node_type <nodetype> - Lagrange node location type
274 . -petscdualspace_lagrange_node_endpoints       - Flag for nodes that include endpoints
275 . -petscdualspace_lagrange_node_exponent        - Gauss-Jacobi weight function exponent
276 . -petscdualspace_lagrange_use_moments          - Use moments (where appropriate) for functionals
277 - -petscdualspace_lagrange_moment_order <order> - Quadrature order for moment functionals
278 
279   Level: intermediate
280 
281 .seealso: `PetscDualSpaceView()`, `PetscDualSpace`, `PetscObjectSetFromOptions()`
282 @*/
283 PetscErrorCode PetscDualSpaceSetFromOptions(PetscDualSpace sp)
284 {
285   DMPolytopeType refCell = DM_POLYTOPE_TRIANGLE;
286   const char    *defaultType;
287   char           name[256];
288   PetscBool      flg;
289 
290   PetscFunctionBegin;
291   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
292   if (!((PetscObject)sp)->type_name) {
293     defaultType = PETSCDUALSPACELAGRANGE;
294   } else {
295     defaultType = ((PetscObject)sp)->type_name;
296   }
297   if (!PetscSpaceRegisterAllCalled) PetscCall(PetscSpaceRegisterAll());
298 
299   PetscObjectOptionsBegin((PetscObject)sp);
300   PetscCall(PetscOptionsFList("-petscdualspace_type", "Dual space", "PetscDualSpaceSetType", PetscDualSpaceList, defaultType, name, 256, &flg));
301   if (flg) {
302     PetscCall(PetscDualSpaceSetType(sp, name));
303   } else if (!((PetscObject)sp)->type_name) {
304     PetscCall(PetscDualSpaceSetType(sp, defaultType));
305   }
306   PetscCall(PetscOptionsBoundedInt("-petscdualspace_order", "The approximation order", "PetscDualSpaceSetOrder", sp->order, &sp->order, NULL, 0));
307   PetscCall(PetscOptionsInt("-petscdualspace_form_degree", "The form degree of the dofs", "PetscDualSpaceSetFormDegree", sp->k, &sp->k, NULL));
308   PetscCall(PetscOptionsBoundedInt("-petscdualspace_components", "The number of components", "PetscDualSpaceSetNumComponents", sp->Nc, &sp->Nc, NULL, 1));
309   PetscTryTypeMethod(sp, setfromoptions, PetscOptionsObject);
310   PetscCall(PetscOptionsEnum("-petscdualspace_refcell", "Reference cell shape", "PetscDualSpaceSetReferenceCell", DMPolytopeTypes, (PetscEnum)refCell, (PetscEnum *)&refCell, &flg));
311   if (flg) {
312     DM K;
313 
314     PetscCall(DMPlexCreateReferenceCell(PETSC_COMM_SELF, refCell, &K));
315     PetscCall(PetscDualSpaceSetDM(sp, K));
316     PetscCall(DMDestroy(&K));
317   }
318 
319   /* process any options handlers added with PetscObjectAddOptionsHandler() */
320   PetscCall(PetscObjectProcessOptionsHandlers((PetscObject)sp, PetscOptionsObject));
321   PetscOptionsEnd();
322   sp->setfromoptionscalled = PETSC_TRUE;
323   PetscFunctionReturn(PETSC_SUCCESS);
324 }
325 
326 /*@
327   PetscDualSpaceSetUp - Construct a basis for a `PetscDualSpace`
328 
329   Collective
330 
331   Input Parameter:
332 . sp - the `PetscDualSpace` object to setup
333 
334   Level: intermediate
335 
336 .seealso: `PetscDualSpaceView()`, `PetscDualSpaceDestroy()`, `PetscDualSpace`
337 @*/
338 PetscErrorCode PetscDualSpaceSetUp(PetscDualSpace sp)
339 {
340   PetscFunctionBegin;
341   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
342   if (sp->setupcalled) PetscFunctionReturn(PETSC_SUCCESS);
343   PetscCall(PetscLogEventBegin(PETSCDUALSPACE_SetUp, sp, 0, 0, 0));
344   sp->setupcalled = PETSC_TRUE;
345   PetscTryTypeMethod(sp, setup);
346   PetscCall(PetscLogEventEnd(PETSCDUALSPACE_SetUp, sp, 0, 0, 0));
347   if (sp->setfromoptionscalled) PetscCall(PetscDualSpaceViewFromOptions(sp, NULL, "-petscdualspace_view"));
348   PetscFunctionReturn(PETSC_SUCCESS);
349 }
350 
351 static PetscErrorCode PetscDualSpaceClearDMData_Internal(PetscDualSpace sp, DM dm)
352 {
353   PetscInt pStart = -1, pEnd = -1, depth = -1;
354 
355   PetscFunctionBegin;
356   if (!dm) PetscFunctionReturn(PETSC_SUCCESS);
357   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
358   PetscCall(DMPlexGetDepth(dm, &depth));
359 
360   if (sp->pointSpaces) {
361     PetscInt i;
362 
363     for (i = 0; i < pEnd - pStart; i++) PetscCall(PetscDualSpaceDestroy(&sp->pointSpaces[i]));
364   }
365   PetscCall(PetscFree(sp->pointSpaces));
366 
367   if (sp->heightSpaces) {
368     PetscInt i;
369 
370     for (i = 0; i <= depth; i++) PetscCall(PetscDualSpaceDestroy(&sp->heightSpaces[i]));
371   }
372   PetscCall(PetscFree(sp->heightSpaces));
373 
374   PetscCall(PetscSectionDestroy(&sp->pointSection));
375   PetscCall(PetscSectionDestroy(&sp->intPointSection));
376   PetscCall(PetscQuadratureDestroy(&sp->intNodes));
377   PetscCall(VecDestroy(&sp->intDofValues));
378   PetscCall(VecDestroy(&sp->intNodeValues));
379   PetscCall(MatDestroy(&sp->intMat));
380   PetscCall(PetscQuadratureDestroy(&sp->allNodes));
381   PetscCall(VecDestroy(&sp->allDofValues));
382   PetscCall(VecDestroy(&sp->allNodeValues));
383   PetscCall(MatDestroy(&sp->allMat));
384   PetscCall(PetscFree(sp->numDof));
385   PetscFunctionReturn(PETSC_SUCCESS);
386 }
387 
388 /*@
389   PetscDualSpaceDestroy - Destroys a `PetscDualSpace` object
390 
391   Collective
392 
393   Input Parameter:
394 . sp - the `PetscDualSpace` object to destroy
395 
396   Level: beginner
397 
398 .seealso: `PetscDualSpace`, `PetscDualSpaceView()`, `PetscDualSpace()`, `PetscDualSpaceCreate()`
399 @*/
400 PetscErrorCode PetscDualSpaceDestroy(PetscDualSpace *sp)
401 {
402   PetscInt dim, f;
403   DM       dm;
404 
405   PetscFunctionBegin;
406   if (!*sp) PetscFunctionReturn(PETSC_SUCCESS);
407   PetscValidHeaderSpecific(*sp, PETSCDUALSPACE_CLASSID, 1);
408 
409   if (--((PetscObject)*sp)->refct > 0) {
410     *sp = NULL;
411     PetscFunctionReturn(PETSC_SUCCESS);
412   }
413   ((PetscObject)*sp)->refct = 0;
414 
415   PetscCall(PetscDualSpaceGetDimension(*sp, &dim));
416   dm = (*sp)->dm;
417 
418   PetscTryTypeMethod(*sp, destroy);
419   PetscCall(PetscDualSpaceClearDMData_Internal(*sp, dm));
420 
421   for (f = 0; f < dim; ++f) PetscCall(PetscQuadratureDestroy(&(*sp)->functional[f]));
422   PetscCall(PetscFree((*sp)->functional));
423   PetscCall(DMDestroy(&(*sp)->dm));
424   PetscCall(PetscHeaderDestroy(sp));
425   PetscFunctionReturn(PETSC_SUCCESS);
426 }
427 
428 /*@
429   PetscDualSpaceCreate - Creates an empty `PetscDualSpace` object. The type can then be set with `PetscDualSpaceSetType()`.
430 
431   Collective
432 
433   Input Parameter:
434 . comm - The communicator for the `PetscDualSpace` object
435 
436   Output Parameter:
437 . sp - The `PetscDualSpace` object
438 
439   Level: beginner
440 
441 .seealso: `PetscDualSpace`, `PetscDualSpaceSetType()`, `PETSCDUALSPACELAGRANGE`
442 @*/
443 PetscErrorCode PetscDualSpaceCreate(MPI_Comm comm, PetscDualSpace *sp)
444 {
445   PetscDualSpace s;
446 
447   PetscFunctionBegin;
448   PetscAssertPointer(sp, 2);
449   PetscCall(PetscCitationsRegister(FECitation, &FEcite));
450   *sp = NULL;
451   PetscCall(PetscFEInitializePackage());
452 
453   PetscCall(PetscHeaderCreate(s, PETSCDUALSPACE_CLASSID, "PetscDualSpace", "Dual Space", "PetscDualSpace", comm, PetscDualSpaceDestroy, PetscDualSpaceView));
454 
455   s->order       = 0;
456   s->Nc          = 1;
457   s->k           = 0;
458   s->spdim       = -1;
459   s->spintdim    = -1;
460   s->uniform     = PETSC_TRUE;
461   s->setupcalled = PETSC_FALSE;
462 
463   *sp = s;
464   PetscFunctionReturn(PETSC_SUCCESS);
465 }
466 
467 /*@
468   PetscDualSpaceDuplicate - Creates a duplicate `PetscDualSpace` object that is not setup.
469 
470   Collective
471 
472   Input Parameter:
473 . sp - The original `PetscDualSpace`
474 
475   Output Parameter:
476 . spNew - The duplicate `PetscDualSpace`
477 
478   Level: beginner
479 
480 .seealso: `PetscDualSpace`, `PetscDualSpaceCreate()`, `PetscDualSpaceSetType()`
481 @*/
482 PetscErrorCode PetscDualSpaceDuplicate(PetscDualSpace sp, PetscDualSpace *spNew)
483 {
484   DM                 dm;
485   PetscDualSpaceType type;
486   const char        *name;
487 
488   PetscFunctionBegin;
489   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
490   PetscAssertPointer(spNew, 2);
491   PetscCall(PetscDualSpaceCreate(PetscObjectComm((PetscObject)sp), spNew));
492   name = ((PetscObject)sp)->name;
493   if (name) { PetscCall(PetscObjectSetName((PetscObject)*spNew, name)); }
494   PetscCall(PetscDualSpaceGetType(sp, &type));
495   PetscCall(PetscDualSpaceSetType(*spNew, type));
496   PetscCall(PetscDualSpaceGetDM(sp, &dm));
497   PetscCall(PetscDualSpaceSetDM(*spNew, dm));
498 
499   (*spNew)->order   = sp->order;
500   (*spNew)->k       = sp->k;
501   (*spNew)->Nc      = sp->Nc;
502   (*spNew)->uniform = sp->uniform;
503   PetscTryTypeMethod(sp, duplicate, *spNew);
504   PetscFunctionReturn(PETSC_SUCCESS);
505 }
506 
507 /*@
508   PetscDualSpaceGetDM - Get the `DM` representing the reference cell of a `PetscDualSpace`
509 
510   Not Collective
511 
512   Input Parameter:
513 . sp - The `PetscDualSpace`
514 
515   Output Parameter:
516 . dm - The reference cell, that is a `DM` that consists of a single cell
517 
518   Level: intermediate
519 
520 .seealso: `PetscDualSpace`, `PetscDualSpaceSetDM()`, `PetscDualSpaceCreate()`
521 @*/
522 PetscErrorCode PetscDualSpaceGetDM(PetscDualSpace sp, DM *dm)
523 {
524   PetscFunctionBegin;
525   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
526   PetscAssertPointer(dm, 2);
527   *dm = sp->dm;
528   PetscFunctionReturn(PETSC_SUCCESS);
529 }
530 
531 /*@
532   PetscDualSpaceSetDM - Get the `DM` representing the reference cell
533 
534   Not Collective
535 
536   Input Parameters:
537 + sp - The `PetscDual`Space
538 - dm - The reference cell
539 
540   Level: intermediate
541 
542 .seealso: `PetscDualSpace`, `DM`, `PetscDualSpaceGetDM()`, `PetscDualSpaceCreate()`
543 @*/
544 PetscErrorCode PetscDualSpaceSetDM(PetscDualSpace sp, DM dm)
545 {
546   PetscFunctionBegin;
547   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
548   PetscValidHeaderSpecific(dm, DM_CLASSID, 2);
549   PetscCheck(!sp->setupcalled, PetscObjectComm((PetscObject)sp), PETSC_ERR_ARG_WRONGSTATE, "Cannot change DM after dualspace is set up");
550   PetscCall(PetscObjectReference((PetscObject)dm));
551   if (sp->dm && sp->dm != dm) PetscCall(PetscDualSpaceClearDMData_Internal(sp, sp->dm));
552   PetscCall(DMDestroy(&sp->dm));
553   sp->dm = dm;
554   PetscFunctionReturn(PETSC_SUCCESS);
555 }
556 
557 /*@
558   PetscDualSpaceGetOrder - Get the order of the dual space
559 
560   Not Collective
561 
562   Input Parameter:
563 . sp - The `PetscDualSpace`
564 
565   Output Parameter:
566 . order - The order
567 
568   Level: intermediate
569 
570 .seealso: `PetscDualSpace`, `PetscDualSpaceSetOrder()`, `PetscDualSpaceCreate()`
571 @*/
572 PetscErrorCode PetscDualSpaceGetOrder(PetscDualSpace sp, PetscInt *order)
573 {
574   PetscFunctionBegin;
575   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
576   PetscAssertPointer(order, 2);
577   *order = sp->order;
578   PetscFunctionReturn(PETSC_SUCCESS);
579 }
580 
581 /*@
582   PetscDualSpaceSetOrder - Set the order of the dual space
583 
584   Not Collective
585 
586   Input Parameters:
587 + sp    - The `PetscDualSpace`
588 - order - The order
589 
590   Level: intermediate
591 
592 .seealso: `PetscDualSpace`, `PetscDualSpaceGetOrder()`, `PetscDualSpaceCreate()`
593 @*/
594 PetscErrorCode PetscDualSpaceSetOrder(PetscDualSpace sp, PetscInt order)
595 {
596   PetscFunctionBegin;
597   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
598   PetscCheck(!sp->setupcalled, PetscObjectComm((PetscObject)sp), PETSC_ERR_ARG_WRONGSTATE, "Cannot change order after dualspace is set up");
599   sp->order = order;
600   PetscFunctionReturn(PETSC_SUCCESS);
601 }
602 
603 /*@
604   PetscDualSpaceGetNumComponents - Return the number of components for this space
605 
606   Input Parameter:
607 . sp - The `PetscDualSpace`
608 
609   Output Parameter:
610 . Nc - The number of components
611 
612   Level: intermediate
613 
614   Note:
615   A vector space, for example, will have d components, where d is the spatial dimension
616 
617 .seealso: `PetscDualSpaceSetNumComponents()`, `PetscDualSpaceGetDimension()`, `PetscDualSpaceCreate()`, `PetscDualSpace`
618 @*/
619 PetscErrorCode PetscDualSpaceGetNumComponents(PetscDualSpace sp, PetscInt *Nc)
620 {
621   PetscFunctionBegin;
622   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
623   PetscAssertPointer(Nc, 2);
624   *Nc = sp->Nc;
625   PetscFunctionReturn(PETSC_SUCCESS);
626 }
627 
628 /*@
629   PetscDualSpaceSetNumComponents - Set the number of components for this space
630 
631   Input Parameters:
632 + sp - The `PetscDualSpace`
633 - Nc - The number of components
634 
635   Level: intermediate
636 
637 .seealso: `PetscDualSpaceGetNumComponents()`, `PetscDualSpaceCreate()`, `PetscDualSpace`
638 @*/
639 PetscErrorCode PetscDualSpaceSetNumComponents(PetscDualSpace sp, PetscInt Nc)
640 {
641   PetscFunctionBegin;
642   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
643   PetscCheck(!sp->setupcalled, PetscObjectComm((PetscObject)sp), PETSC_ERR_ARG_WRONGSTATE, "Cannot change number of components after dualspace is set up");
644   sp->Nc = Nc;
645   PetscFunctionReturn(PETSC_SUCCESS);
646 }
647 
648 /*@
649   PetscDualSpaceGetFunctional - Get the i-th basis functional in the dual space
650 
651   Not Collective
652 
653   Input Parameters:
654 + sp - The `PetscDualSpace`
655 - i  - The basis number
656 
657   Output Parameter:
658 . functional - The basis functional
659 
660   Level: intermediate
661 
662 .seealso: `PetscDualSpace`, `PetscQuadrature`, `PetscDualSpaceGetDimension()`, `PetscDualSpaceCreate()`
663 @*/
664 PetscErrorCode PetscDualSpaceGetFunctional(PetscDualSpace sp, PetscInt i, PetscQuadrature *functional)
665 {
666   PetscInt dim;
667 
668   PetscFunctionBegin;
669   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
670   PetscAssertPointer(functional, 3);
671   PetscCall(PetscDualSpaceGetDimension(sp, &dim));
672   PetscCheck(!(i < 0) && !(i >= dim), PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Functional index %" PetscInt_FMT " must be in [0, %" PetscInt_FMT ")", i, dim);
673   *functional = sp->functional[i];
674   PetscFunctionReturn(PETSC_SUCCESS);
675 }
676 
677 /*@
678   PetscDualSpaceGetDimension - Get the dimension of the dual space, i.e. the number of basis functionals
679 
680   Not Collective
681 
682   Input Parameter:
683 . sp - The `PetscDualSpace`
684 
685   Output Parameter:
686 . dim - The dimension
687 
688   Level: intermediate
689 
690 .seealso: `PetscDualSpace`, `PetscDualSpaceGetFunctional()`, `PetscDualSpaceCreate()`
691 @*/
692 PetscErrorCode PetscDualSpaceGetDimension(PetscDualSpace sp, PetscInt *dim)
693 {
694   PetscFunctionBegin;
695   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
696   PetscAssertPointer(dim, 2);
697   if (sp->spdim < 0) {
698     PetscSection section;
699 
700     PetscCall(PetscDualSpaceGetSection(sp, &section));
701     if (section) {
702       PetscCall(PetscSectionGetStorageSize(section, &sp->spdim));
703     } else sp->spdim = 0;
704   }
705   *dim = sp->spdim;
706   PetscFunctionReturn(PETSC_SUCCESS);
707 }
708 
709 /*@
710   PetscDualSpaceGetInteriorDimension - Get the interior dimension of the dual space, i.e. the number of basis functionals assigned to the interior of the reference domain
711 
712   Not Collective
713 
714   Input Parameter:
715 . sp - The `PetscDualSpace`
716 
717   Output Parameter:
718 . intdim - The dimension
719 
720   Level: intermediate
721 
722 .seealso: `PetscDualSpace`, `PetscDualSpaceGetFunctional()`, `PetscDualSpaceCreate()`
723 @*/
724 PetscErrorCode PetscDualSpaceGetInteriorDimension(PetscDualSpace sp, PetscInt *intdim)
725 {
726   PetscFunctionBegin;
727   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
728   PetscAssertPointer(intdim, 2);
729   if (sp->spintdim < 0) {
730     PetscSection section;
731 
732     PetscCall(PetscDualSpaceGetSection(sp, &section));
733     if (section) {
734       PetscCall(PetscSectionGetConstrainedStorageSize(section, &sp->spintdim));
735     } else sp->spintdim = 0;
736   }
737   *intdim = sp->spintdim;
738   PetscFunctionReturn(PETSC_SUCCESS);
739 }
740 
741 /*@
742   PetscDualSpaceGetUniform - Whether this dual space is uniform
743 
744   Not Collective
745 
746   Input Parameter:
747 . sp - A dual space
748 
749   Output Parameter:
750 . uniform - `PETSC_TRUE` if (a) the dual space is the same for each point in a stratum of the reference `DMPLEX`, and
751              (b) every symmetry of each point in the reference `DMPLEX` is also a symmetry of the point's dual space.
752 
753   Level: advanced
754 
755   Note:
756   All of the usual spaces on simplex or tensor-product elements will be uniform, only reference cells
757   with non-uniform strata (like trianguar-prisms) or anisotropic hp dual spaces will not be uniform.
758 
759 .seealso: `PetscDualSpace`, `PetscDualSpaceGetPointSubspace()`, `PetscDualSpaceGetSymmetries()`
760 @*/
761 PetscErrorCode PetscDualSpaceGetUniform(PetscDualSpace sp, PetscBool *uniform)
762 {
763   PetscFunctionBegin;
764   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
765   PetscAssertPointer(uniform, 2);
766   *uniform = sp->uniform;
767   PetscFunctionReturn(PETSC_SUCCESS);
768 }
769 
770 /*@C
771   PetscDualSpaceGetNumDof - Get the number of degrees of freedom for each spatial (topological) dimension
772 
773   Not Collective
774 
775   Input Parameter:
776 . sp - The `PetscDualSpace`
777 
778   Output Parameter:
779 . numDof - An array of length dim+1 which holds the number of dofs for each dimension
780 
781   Level: intermediate
782 
783   Note:
784   Do not free `numDof`
785 
786 .seealso: `PetscDualSpace`, `PetscDualSpaceGetFunctional()`, `PetscDualSpaceCreate()`
787 @*/
788 PetscErrorCode PetscDualSpaceGetNumDof(PetscDualSpace sp, const PetscInt *numDof[])
789 {
790   PetscFunctionBegin;
791   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
792   PetscAssertPointer(numDof, 2);
793   PetscCheck(sp->uniform, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "A non-uniform space does not have a fixed number of dofs for each height");
794   if (!sp->numDof) {
795     DM           dm;
796     PetscInt     depth, d;
797     PetscSection section;
798 
799     PetscCall(PetscDualSpaceGetDM(sp, &dm));
800     PetscCall(DMPlexGetDepth(dm, &depth));
801     PetscCall(PetscCalloc1(depth + 1, &sp->numDof));
802     PetscCall(PetscDualSpaceGetSection(sp, &section));
803     for (d = 0; d <= depth; d++) {
804       PetscInt dStart, dEnd;
805 
806       PetscCall(DMPlexGetDepthStratum(dm, d, &dStart, &dEnd));
807       if (dEnd <= dStart) continue;
808       PetscCall(PetscSectionGetDof(section, dStart, &sp->numDof[d]));
809     }
810   }
811   *numDof = sp->numDof;
812   PetscCheck(*numDof, PetscObjectComm((PetscObject)sp), PETSC_ERR_LIB, "Empty numDof[] returned from dual space implementation");
813   PetscFunctionReturn(PETSC_SUCCESS);
814 }
815 
816 /* create the section of the right size and set a permutation for topological ordering */
817 PetscErrorCode PetscDualSpaceSectionCreate_Internal(PetscDualSpace sp, PetscSection *topSection)
818 {
819   DM           dm;
820   PetscInt     pStart, pEnd, cStart, cEnd, c, depth, count, i;
821   PetscInt    *seen, *perm;
822   PetscSection section;
823 
824   PetscFunctionBegin;
825   dm = sp->dm;
826   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &section));
827   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
828   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
829   PetscCall(PetscCalloc1(pEnd - pStart, &seen));
830   PetscCall(PetscMalloc1(pEnd - pStart, &perm));
831   PetscCall(DMPlexGetDepth(dm, &depth));
832   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
833   for (c = cStart, count = 0; c < cEnd; c++) {
834     PetscInt  closureSize = -1, e;
835     PetscInt *closure     = NULL;
836 
837     perm[count++]    = c;
838     seen[c - pStart] = 1;
839     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
840     for (e = 0; e < closureSize; e++) {
841       PetscInt point = closure[2 * e];
842 
843       if (seen[point - pStart]) continue;
844       perm[count++]        = point;
845       seen[point - pStart] = 1;
846     }
847     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
848   }
849   PetscCheck(count == pEnd - pStart, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Bad topological ordering");
850   for (i = 0; i < pEnd - pStart; i++)
851     if (perm[i] != i) break;
852   if (i < pEnd - pStart) {
853     IS permIS;
854 
855     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, pEnd - pStart, perm, PETSC_OWN_POINTER, &permIS));
856     PetscCall(ISSetPermutation(permIS));
857     PetscCall(PetscSectionSetPermutation(section, permIS));
858     PetscCall(ISDestroy(&permIS));
859   } else {
860     PetscCall(PetscFree(perm));
861   }
862   PetscCall(PetscFree(seen));
863   *topSection = section;
864   PetscFunctionReturn(PETSC_SUCCESS);
865 }
866 
867 /* mark boundary points and set up */
868 PetscErrorCode PetscDualSpaceSectionSetUp_Internal(PetscDualSpace sp, PetscSection section)
869 {
870   DM       dm;
871   DMLabel  boundary;
872   PetscInt pStart, pEnd, p;
873 
874   PetscFunctionBegin;
875   dm = sp->dm;
876   PetscCall(DMLabelCreate(PETSC_COMM_SELF, "boundary", &boundary));
877   PetscCall(PetscDualSpaceGetDM(sp, &dm));
878   PetscCall(DMPlexMarkBoundaryFaces(dm, 1, boundary));
879   PetscCall(DMPlexLabelComplete(dm, boundary));
880   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
881   for (p = pStart; p < pEnd; p++) {
882     PetscInt bval;
883 
884     PetscCall(DMLabelGetValue(boundary, p, &bval));
885     if (bval == 1) {
886       PetscInt dof;
887 
888       PetscCall(PetscSectionGetDof(section, p, &dof));
889       PetscCall(PetscSectionSetConstraintDof(section, p, dof));
890     }
891   }
892   PetscCall(DMLabelDestroy(&boundary));
893   PetscCall(PetscSectionSetUp(section));
894   PetscFunctionReturn(PETSC_SUCCESS);
895 }
896 
897 /*@
898   PetscDualSpaceGetSection - Create a `PetscSection` over the reference cell with the layout from this space
899 
900   Collective
901 
902   Input Parameter:
903 . sp - The `PetscDualSpace`
904 
905   Output Parameter:
906 . section - The section
907 
908   Level: advanced
909 
910 .seealso: `PetscDualSpace`, `PetscSection`, `PetscDualSpaceCreate()`, `DMPLEX`
911 @*/
912 PetscErrorCode PetscDualSpaceGetSection(PetscDualSpace sp, PetscSection *section)
913 {
914   PetscInt pStart, pEnd, p;
915 
916   PetscFunctionBegin;
917   if (!sp->dm) {
918     *section = NULL;
919     PetscFunctionReturn(PETSC_SUCCESS);
920   }
921   if (!sp->pointSection) {
922     /* mark the boundary */
923     PetscCall(PetscDualSpaceSectionCreate_Internal(sp, &sp->pointSection));
924     PetscCall(DMPlexGetChart(sp->dm, &pStart, &pEnd));
925     for (p = pStart; p < pEnd; p++) {
926       PetscDualSpace psp;
927 
928       PetscCall(PetscDualSpaceGetPointSubspace(sp, p, &psp));
929       if (psp) {
930         PetscInt dof;
931 
932         PetscCall(PetscDualSpaceGetInteriorDimension(psp, &dof));
933         PetscCall(PetscSectionSetDof(sp->pointSection, p, dof));
934       }
935     }
936     PetscCall(PetscDualSpaceSectionSetUp_Internal(sp, sp->pointSection));
937   }
938   *section = sp->pointSection;
939   PetscFunctionReturn(PETSC_SUCCESS);
940 }
941 
942 /*@
943   PetscDualSpaceGetInteriorSection - Create a `PetscSection` over the reference cell with the layout from this space
944   for interior degrees of freedom
945 
946   Collective
947 
948   Input Parameter:
949 . sp - The `PetscDualSpace`
950 
951   Output Parameter:
952 . section - The interior section
953 
954   Level: advanced
955 
956   Note:
957   Most reference domains have one cell, in which case the only cell will have
958   all of the interior degrees of freedom in the interior section.  But
959   for `PETSCDUALSPACEREFINED` there may be other mesh points in the interior,
960   and this section describes their layout.
961 
962 .seealso: `PetscDualSpace`, `PetscSection`, `PetscDualSpaceCreate()`, `DMPLEX`
963 @*/
964 PetscErrorCode PetscDualSpaceGetInteriorSection(PetscDualSpace sp, PetscSection *section)
965 {
966   PetscInt pStart, pEnd, p;
967 
968   PetscFunctionBegin;
969   if (!sp->dm) {
970     *section = NULL;
971     PetscFunctionReturn(PETSC_SUCCESS);
972   }
973   if (!sp->intPointSection) {
974     PetscSection full_section;
975 
976     PetscCall(PetscDualSpaceGetSection(sp, &full_section));
977     PetscCall(PetscDualSpaceSectionCreate_Internal(sp, &sp->intPointSection));
978     PetscCall(PetscSectionGetChart(full_section, &pStart, &pEnd));
979     for (p = pStart; p < pEnd; p++) {
980       PetscInt dof, cdof;
981 
982       PetscCall(PetscSectionGetDof(full_section, p, &dof));
983       PetscCall(PetscSectionGetConstraintDof(full_section, p, &cdof));
984       PetscCall(PetscSectionSetDof(sp->intPointSection, p, dof - cdof));
985     }
986     PetscCall(PetscDualSpaceSectionSetUp_Internal(sp, sp->intPointSection));
987   }
988   *section = sp->intPointSection;
989   PetscFunctionReturn(PETSC_SUCCESS);
990 }
991 
992 /* this assumes that all of the point dual spaces store their interior dofs first, which is true when the point DMs
993  * have one cell */
994 PetscErrorCode PetscDualSpacePushForwardSubspaces_Internal(PetscDualSpace sp, PetscInt sStart, PetscInt sEnd)
995 {
996   PetscReal   *sv0, *v0, *J;
997   PetscSection section;
998   PetscInt     dim, s, k;
999   DM           dm;
1000 
1001   PetscFunctionBegin;
1002   PetscCall(PetscDualSpaceGetDM(sp, &dm));
1003   PetscCall(DMGetDimension(dm, &dim));
1004   PetscCall(PetscDualSpaceGetSection(sp, &section));
1005   PetscCall(PetscMalloc3(dim, &v0, dim, &sv0, dim * dim, &J));
1006   PetscCall(PetscDualSpaceGetFormDegree(sp, &k));
1007   for (s = sStart; s < sEnd; s++) {
1008     PetscReal      detJ, hdetJ;
1009     PetscDualSpace ssp;
1010     PetscInt       dof, off, f, sdim;
1011     PetscInt       i, j;
1012     DM             sdm;
1013 
1014     PetscCall(PetscDualSpaceGetPointSubspace(sp, s, &ssp));
1015     if (!ssp) continue;
1016     PetscCall(PetscSectionGetDof(section, s, &dof));
1017     PetscCall(PetscSectionGetOffset(section, s, &off));
1018     /* get the first vertex of the reference cell */
1019     PetscCall(PetscDualSpaceGetDM(ssp, &sdm));
1020     PetscCall(DMGetDimension(sdm, &sdim));
1021     PetscCall(DMPlexComputeCellGeometryAffineFEM(sdm, 0, sv0, NULL, NULL, &hdetJ));
1022     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, s, v0, J, NULL, &detJ));
1023     /* compactify Jacobian */
1024     for (i = 0; i < dim; i++)
1025       for (j = 0; j < sdim; j++) J[i * sdim + j] = J[i * dim + j];
1026     for (f = 0; f < dof; f++) {
1027       PetscQuadrature fn;
1028 
1029       PetscCall(PetscDualSpaceGetFunctional(ssp, f, &fn));
1030       PetscCall(PetscQuadraturePushForward(fn, dim, sv0, v0, J, k, &sp->functional[off + f]));
1031     }
1032   }
1033   PetscCall(PetscFree3(v0, sv0, J));
1034   PetscFunctionReturn(PETSC_SUCCESS);
1035 }
1036 
1037 /*@C
1038   PetscDualSpaceApply - Apply a functional from the dual space basis to an input function
1039 
1040   Input Parameters:
1041 + sp      - The `PetscDualSpace` object
1042 . f       - The basis functional index
1043 . time    - The time
1044 . cgeom   - A context with geometric information for this cell, we use v0 (the initial vertex) and J (the Jacobian) (or evaluated at the coordinates of the functional)
1045 . numComp - The number of components for the function
1046 . func    - The input function
1047 - ctx     - A context for the function
1048 
1049   Output Parameter:
1050 . value - numComp output values
1051 
1052   Calling sequence:
1053 .vb
1054   PetscErrorCode func(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt numComponents, PetscScalar values[], void *ctx)
1055 .ve
1056 
1057   Level: beginner
1058 
1059 .seealso: `PetscDualSpace`, `PetscDualSpaceCreate()`
1060 @*/
1061 PetscErrorCode PetscDualSpaceApply(PetscDualSpace sp, PetscInt f, PetscReal time, PetscFEGeom *cgeom, PetscInt numComp, PetscErrorCode (*func)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void *ctx, PetscScalar *value)
1062 {
1063   PetscFunctionBegin;
1064   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
1065   PetscAssertPointer(cgeom, 4);
1066   PetscAssertPointer(value, 8);
1067   PetscUseTypeMethod(sp, apply, f, time, cgeom, numComp, func, ctx, value);
1068   PetscFunctionReturn(PETSC_SUCCESS);
1069 }
1070 
1071 /*@C
1072   PetscDualSpaceApplyAll - Apply all functionals from the dual space basis to the result of an evaluation at the points returned by `PetscDualSpaceGetAllData()`
1073 
1074   Input Parameters:
1075 + sp        - The `PetscDualSpace` object
1076 - pointEval - Evaluation at the points returned by `PetscDualSpaceGetAllData()`
1077 
1078   Output Parameter:
1079 . spValue - The values of all dual space functionals
1080 
1081   Level: advanced
1082 
1083 .seealso: `PetscDualSpace`, `PetscDualSpaceCreate()`
1084 @*/
1085 PetscErrorCode PetscDualSpaceApplyAll(PetscDualSpace sp, const PetscScalar *pointEval, PetscScalar *spValue)
1086 {
1087   PetscFunctionBegin;
1088   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
1089   PetscUseTypeMethod(sp, applyall, pointEval, spValue);
1090   PetscFunctionReturn(PETSC_SUCCESS);
1091 }
1092 
1093 /*@C
1094   PetscDualSpaceApplyInterior - Apply interior functionals from the dual space basis to the result of an evaluation at the points returned by `PetscDualSpaceGetInteriorData()`
1095 
1096   Input Parameters:
1097 + sp        - The `PetscDualSpace` object
1098 - pointEval - Evaluation at the points returned by `PetscDualSpaceGetInteriorData()`
1099 
1100   Output Parameter:
1101 . spValue - The values of interior dual space functionals
1102 
1103   Level: advanced
1104 
1105 .seealso: `PetscDualSpace`, `PetscDualSpaceCreate()`
1106 @*/
1107 PetscErrorCode PetscDualSpaceApplyInterior(PetscDualSpace sp, const PetscScalar *pointEval, PetscScalar *spValue)
1108 {
1109   PetscFunctionBegin;
1110   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
1111   PetscUseTypeMethod(sp, applyint, pointEval, spValue);
1112   PetscFunctionReturn(PETSC_SUCCESS);
1113 }
1114 
1115 /*@C
1116   PetscDualSpaceApplyDefault - Apply a functional from the dual space basis to an input function by assuming a point evaluation functional.
1117 
1118   Input Parameters:
1119 + sp    - The `PetscDualSpace` object
1120 . f     - The basis functional index
1121 . time  - The time
1122 . cgeom - A context with geometric information for this cell, we use v0 (the initial vertex) and J (the Jacobian)
1123 . Nc    - The number of components for the function
1124 . func  - The input function
1125 - ctx   - A context for the function
1126 
1127   Output Parameter:
1128 . value - The output value
1129 
1130   Calling sequence:
1131 .vb
1132    PetscErrorCode func(PetscInt dim, PetscReal time, const PetscReal x[],PetscInt numComponents, PetscScalar values[], void *ctx)
1133 .ve
1134 
1135   Level: advanced
1136 
1137   Note:
1138   The idea is to evaluate the functional as an integral $ n(f) = \int dx n(x) . f(x) $ where both n and f have Nc components.
1139 
1140 .seealso: `PetscDualSpace`, `PetscDualSpaceCreate()`
1141 @*/
1142 PetscErrorCode PetscDualSpaceApplyDefault(PetscDualSpace sp, PetscInt f, PetscReal time, PetscFEGeom *cgeom, PetscInt Nc, PetscErrorCode (*func)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void *ctx, PetscScalar *value)
1143 {
1144   DM               dm;
1145   PetscQuadrature  n;
1146   const PetscReal *points, *weights;
1147   PetscReal        x[3];
1148   PetscScalar     *val;
1149   PetscInt         dim, dE, qNc, c, Nq, q;
1150   PetscBool        isAffine;
1151 
1152   PetscFunctionBegin;
1153   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
1154   PetscAssertPointer(value, 8);
1155   PetscCall(PetscDualSpaceGetDM(sp, &dm));
1156   PetscCall(PetscDualSpaceGetFunctional(sp, f, &n));
1157   PetscCall(PetscQuadratureGetData(n, &dim, &qNc, &Nq, &points, &weights));
1158   PetscCheck(dim == cgeom->dim, PetscObjectComm((PetscObject)sp), PETSC_ERR_ARG_SIZ, "The quadrature spatial dimension %" PetscInt_FMT " != cell geometry dimension %" PetscInt_FMT, dim, cgeom->dim);
1159   PetscCheck(qNc == Nc, PetscObjectComm((PetscObject)sp), PETSC_ERR_ARG_SIZ, "The quadrature components %" PetscInt_FMT " != function components %" PetscInt_FMT, qNc, Nc);
1160   PetscCall(DMGetWorkArray(dm, Nc, MPIU_SCALAR, &val));
1161   *value   = 0.0;
1162   isAffine = cgeom->isAffine;
1163   dE       = cgeom->dimEmbed;
1164   for (q = 0; q < Nq; ++q) {
1165     if (isAffine) {
1166       CoordinatesRefToReal(dE, cgeom->dim, cgeom->xi, cgeom->v, cgeom->J, &points[q * dim], x);
1167       PetscCall((*func)(dE, time, x, Nc, val, ctx));
1168     } else {
1169       PetscCall((*func)(dE, time, &cgeom->v[dE * q], Nc, val, ctx));
1170     }
1171     for (c = 0; c < Nc; ++c) *value += val[c] * weights[q * Nc + c];
1172   }
1173   PetscCall(DMRestoreWorkArray(dm, Nc, MPIU_SCALAR, &val));
1174   PetscFunctionReturn(PETSC_SUCCESS);
1175 }
1176 
1177 /*@C
1178   PetscDualSpaceApplyAllDefault - Apply all functionals from the dual space basis to the result of an evaluation at the points returned by `PetscDualSpaceGetAllData()`
1179 
1180   Input Parameters:
1181 + sp        - The `PetscDualSpace` object
1182 - pointEval - Evaluation at the points returned by `PetscDualSpaceGetAllData()`
1183 
1184   Output Parameter:
1185 . spValue - The values of all dual space functionals
1186 
1187   Level: advanced
1188 
1189 .seealso: `PetscDualSpace`, `PetscDualSpaceCreate()`
1190 @*/
1191 PetscErrorCode PetscDualSpaceApplyAllDefault(PetscDualSpace sp, const PetscScalar *pointEval, PetscScalar *spValue)
1192 {
1193   Vec pointValues, dofValues;
1194   Mat allMat;
1195 
1196   PetscFunctionBegin;
1197   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
1198   PetscAssertPointer(pointEval, 2);
1199   PetscAssertPointer(spValue, 3);
1200   PetscCall(PetscDualSpaceGetAllData(sp, NULL, &allMat));
1201   if (!sp->allNodeValues) PetscCall(MatCreateVecs(allMat, &sp->allNodeValues, NULL));
1202   pointValues = sp->allNodeValues;
1203   if (!sp->allDofValues) PetscCall(MatCreateVecs(allMat, NULL, &sp->allDofValues));
1204   dofValues = sp->allDofValues;
1205   PetscCall(VecPlaceArray(pointValues, pointEval));
1206   PetscCall(VecPlaceArray(dofValues, spValue));
1207   PetscCall(MatMult(allMat, pointValues, dofValues));
1208   PetscCall(VecResetArray(dofValues));
1209   PetscCall(VecResetArray(pointValues));
1210   PetscFunctionReturn(PETSC_SUCCESS);
1211 }
1212 
1213 /*@C
1214   PetscDualSpaceApplyInteriorDefault - Apply interior functionals from the dual space basis to the result of an evaluation at the points returned by `PetscDualSpaceGetInteriorData()`
1215 
1216   Input Parameters:
1217 + sp        - The `PetscDualSpace` object
1218 - pointEval - Evaluation at the points returned by `PetscDualSpaceGetInteriorData()`
1219 
1220   Output Parameter:
1221 . spValue - The values of interior dual space functionals
1222 
1223   Level: advanced
1224 
1225 .seealso: `PetscDualSpace`, `PetscDualSpaceCreate()`
1226 @*/
1227 PetscErrorCode PetscDualSpaceApplyInteriorDefault(PetscDualSpace sp, const PetscScalar *pointEval, PetscScalar *spValue)
1228 {
1229   Vec pointValues, dofValues;
1230   Mat intMat;
1231 
1232   PetscFunctionBegin;
1233   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
1234   PetscAssertPointer(pointEval, 2);
1235   PetscAssertPointer(spValue, 3);
1236   PetscCall(PetscDualSpaceGetInteriorData(sp, NULL, &intMat));
1237   if (!sp->intNodeValues) PetscCall(MatCreateVecs(intMat, &sp->intNodeValues, NULL));
1238   pointValues = sp->intNodeValues;
1239   if (!sp->intDofValues) PetscCall(MatCreateVecs(intMat, NULL, &sp->intDofValues));
1240   dofValues = sp->intDofValues;
1241   PetscCall(VecPlaceArray(pointValues, pointEval));
1242   PetscCall(VecPlaceArray(dofValues, spValue));
1243   PetscCall(MatMult(intMat, pointValues, dofValues));
1244   PetscCall(VecResetArray(dofValues));
1245   PetscCall(VecResetArray(pointValues));
1246   PetscFunctionReturn(PETSC_SUCCESS);
1247 }
1248 
1249 /*@
1250   PetscDualSpaceGetAllData - Get all quadrature nodes from this space, and the matrix that sends quadrature node values to degree-of-freedom values
1251 
1252   Input Parameter:
1253 . sp - The dualspace
1254 
1255   Output Parameters:
1256 + allNodes - A `PetscQuadrature` object containing all evaluation nodes
1257 - allMat   - A `Mat` for the node-to-dof transformation
1258 
1259   Level: advanced
1260 
1261 .seealso: `PetscQuadrature`, `PetscDualSpace`, `PetscDualSpaceCreate()`, `Mat`
1262 @*/
1263 PetscErrorCode PetscDualSpaceGetAllData(PetscDualSpace sp, PetscQuadrature *allNodes, Mat *allMat)
1264 {
1265   PetscFunctionBegin;
1266   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
1267   if (allNodes) PetscAssertPointer(allNodes, 2);
1268   if (allMat) PetscAssertPointer(allMat, 3);
1269   if ((!sp->allNodes || !sp->allMat) && sp->ops->createalldata) {
1270     PetscQuadrature qpoints;
1271     Mat             amat;
1272 
1273     PetscUseTypeMethod(sp, createalldata, &qpoints, &amat);
1274     PetscCall(PetscQuadratureDestroy(&sp->allNodes));
1275     PetscCall(MatDestroy(&sp->allMat));
1276     sp->allNodes = qpoints;
1277     sp->allMat   = amat;
1278   }
1279   if (allNodes) *allNodes = sp->allNodes;
1280   if (allMat) *allMat = sp->allMat;
1281   PetscFunctionReturn(PETSC_SUCCESS);
1282 }
1283 
1284 /*@
1285   PetscDualSpaceCreateAllDataDefault - Create all evaluation nodes and the node-to-dof matrix by examining functionals
1286 
1287   Input Parameter:
1288 . sp - The dualspace
1289 
1290   Output Parameters:
1291 + allNodes - A `PetscQuadrature` object containing all evaluation nodes
1292 - allMat   - A `Mat` for the node-to-dof transformation
1293 
1294   Level: advanced
1295 
1296 .seealso: `PetscDualSpace`, `PetscDualSpaceCreate()`, `Mat`, `PetscQuadrature`
1297 @*/
1298 PetscErrorCode PetscDualSpaceCreateAllDataDefault(PetscDualSpace sp, PetscQuadrature *allNodes, Mat *allMat)
1299 {
1300   PetscInt        spdim;
1301   PetscInt        numPoints, offset;
1302   PetscReal      *points;
1303   PetscInt        f, dim;
1304   PetscInt        Nc, nrows, ncols;
1305   PetscInt        maxNumPoints;
1306   PetscQuadrature q;
1307   Mat             A;
1308 
1309   PetscFunctionBegin;
1310   PetscCall(PetscDualSpaceGetNumComponents(sp, &Nc));
1311   PetscCall(PetscDualSpaceGetDimension(sp, &spdim));
1312   if (!spdim) {
1313     PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, allNodes));
1314     PetscCall(PetscQuadratureSetData(*allNodes, 0, 0, 0, NULL, NULL));
1315   }
1316   nrows = spdim;
1317   PetscCall(PetscDualSpaceGetFunctional(sp, 0, &q));
1318   PetscCall(PetscQuadratureGetData(q, &dim, NULL, &numPoints, NULL, NULL));
1319   maxNumPoints = numPoints;
1320   for (f = 1; f < spdim; f++) {
1321     PetscInt Np;
1322 
1323     PetscCall(PetscDualSpaceGetFunctional(sp, f, &q));
1324     PetscCall(PetscQuadratureGetData(q, NULL, NULL, &Np, NULL, NULL));
1325     numPoints += Np;
1326     maxNumPoints = PetscMax(maxNumPoints, Np);
1327   }
1328   ncols = numPoints * Nc;
1329   PetscCall(PetscMalloc1(dim * numPoints, &points));
1330   PetscCall(MatCreateSeqAIJ(PETSC_COMM_SELF, nrows, ncols, maxNumPoints * Nc, NULL, &A));
1331   for (f = 0, offset = 0; f < spdim; f++) {
1332     const PetscReal *p, *w;
1333     PetscInt         Np, i;
1334     PetscInt         fnc;
1335 
1336     PetscCall(PetscDualSpaceGetFunctional(sp, f, &q));
1337     PetscCall(PetscQuadratureGetData(q, NULL, &fnc, &Np, &p, &w));
1338     PetscCheck(fnc == Nc, PETSC_COMM_SELF, PETSC_ERR_PLIB, "functional component mismatch");
1339     for (i = 0; i < Np * dim; i++) points[offset * dim + i] = p[i];
1340     for (i = 0; i < Np * Nc; i++) PetscCall(MatSetValue(A, f, offset * Nc, w[i], INSERT_VALUES));
1341     offset += Np;
1342   }
1343   PetscCall(MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY));
1344   PetscCall(MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY));
1345   PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, allNodes));
1346   PetscCall(PetscQuadratureSetData(*allNodes, dim, 0, numPoints, points, NULL));
1347   *allMat = A;
1348   PetscFunctionReturn(PETSC_SUCCESS);
1349 }
1350 
1351 /*@
1352   PetscDualSpaceGetInteriorData - Get all quadrature points necessary to compute the interior degrees of freedom from
1353   this space, as well as the matrix that computes the degrees of freedom from the quadrature
1354   values.
1355 
1356   Input Parameter:
1357 . sp - The dualspace
1358 
1359   Output Parameters:
1360 + intNodes - A `PetscQuadrature` object containing all evaluation points needed to evaluate interior degrees of freedom
1361 - intMat   - A matrix that computes dual space values from point values: size [spdim0 x (npoints * nc)], where spdim0 is
1362              the size of the constrained layout (`PetscSectionGetConstrainStorageSize()`) of the dual space section,
1363              npoints is the number of points in intNodes and nc is `PetscDualSpaceGetNumComponents()`.
1364 
1365   Level: advanced
1366 
1367   Notes:
1368   Degrees of freedom are interior degrees of freedom if they belong (by
1369   `PetscDualSpaceGetSection()`) to interior points in the references, complementary boundary
1370   degrees of freedom are marked as constrained in the section returned by
1371   `PetscDualSpaceGetSection()`).
1372 
1373 .seealso: `PetscDualSpace`, `PetscQuadrature`, `Mat`, `PetscDualSpaceCreate()`, `PetscDualSpaceGetDimension()`, `PetscDualSpaceGetNumComponents()`, `PetscQuadratureGetData()`
1374 @*/
1375 PetscErrorCode PetscDualSpaceGetInteriorData(PetscDualSpace sp, PetscQuadrature *intNodes, Mat *intMat)
1376 {
1377   PetscFunctionBegin;
1378   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
1379   if (intNodes) PetscAssertPointer(intNodes, 2);
1380   if (intMat) PetscAssertPointer(intMat, 3);
1381   if ((!sp->intNodes || !sp->intMat) && sp->ops->createintdata) {
1382     PetscQuadrature qpoints;
1383     Mat             imat;
1384 
1385     PetscUseTypeMethod(sp, createintdata, &qpoints, &imat);
1386     PetscCall(PetscQuadratureDestroy(&sp->intNodes));
1387     PetscCall(MatDestroy(&sp->intMat));
1388     sp->intNodes = qpoints;
1389     sp->intMat   = imat;
1390   }
1391   if (intNodes) *intNodes = sp->intNodes;
1392   if (intMat) *intMat = sp->intMat;
1393   PetscFunctionReturn(PETSC_SUCCESS);
1394 }
1395 
1396 /*@
1397   PetscDualSpaceCreateInteriorDataDefault - Create quadrature points by examining interior functionals and create the matrix mapping quadrature point values to interior dual space values
1398 
1399   Input Parameter:
1400 . sp - The dualspace
1401 
1402   Output Parameters:
1403 + intNodes - A `PetscQuadrature` object containing all evaluation points needed to evaluate interior degrees of freedom
1404 - intMat   - A matrix that computes dual space values from point values: size [spdim0 x (npoints * nc)], where spdim0 is
1405               the size of the constrained layout (`PetscSectionGetConstrainStorageSize()`) of the dual space section,
1406               npoints is the number of points in allNodes and nc is `PetscDualSpaceGetNumComponents()`.
1407 
1408   Level: advanced
1409 
1410 .seealso: `PetscDualSpace`, `PetscQuadrature`, `Mat`, `PetscDualSpaceCreate()`, `PetscDualSpaceGetInteriorData()`
1411 @*/
1412 PetscErrorCode PetscDualSpaceCreateInteriorDataDefault(PetscDualSpace sp, PetscQuadrature *intNodes, Mat *intMat)
1413 {
1414   DM              dm;
1415   PetscInt        spdim0;
1416   PetscInt        Nc;
1417   PetscInt        pStart, pEnd, p, f;
1418   PetscSection    section;
1419   PetscInt        numPoints, offset, matoffset;
1420   PetscReal      *points;
1421   PetscInt        dim;
1422   PetscInt       *nnz;
1423   PetscQuadrature q;
1424   Mat             imat;
1425 
1426   PetscFunctionBegin;
1427   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
1428   PetscCall(PetscDualSpaceGetSection(sp, &section));
1429   PetscCall(PetscSectionGetConstrainedStorageSize(section, &spdim0));
1430   if (!spdim0) {
1431     *intNodes = NULL;
1432     *intMat   = NULL;
1433     PetscFunctionReturn(PETSC_SUCCESS);
1434   }
1435   PetscCall(PetscDualSpaceGetNumComponents(sp, &Nc));
1436   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
1437   PetscCall(PetscDualSpaceGetDM(sp, &dm));
1438   PetscCall(DMGetDimension(dm, &dim));
1439   PetscCall(PetscMalloc1(spdim0, &nnz));
1440   for (p = pStart, f = 0, numPoints = 0; p < pEnd; p++) {
1441     PetscInt dof, cdof, off, d;
1442 
1443     PetscCall(PetscSectionGetDof(section, p, &dof));
1444     PetscCall(PetscSectionGetConstraintDof(section, p, &cdof));
1445     if (!(dof - cdof)) continue;
1446     PetscCall(PetscSectionGetOffset(section, p, &off));
1447     for (d = 0; d < dof; d++, off++, f++) {
1448       PetscInt Np;
1449 
1450       PetscCall(PetscDualSpaceGetFunctional(sp, off, &q));
1451       PetscCall(PetscQuadratureGetData(q, NULL, NULL, &Np, NULL, NULL));
1452       nnz[f] = Np * Nc;
1453       numPoints += Np;
1454     }
1455   }
1456   PetscCall(MatCreateSeqAIJ(PETSC_COMM_SELF, spdim0, numPoints * Nc, 0, nnz, &imat));
1457   PetscCall(PetscFree(nnz));
1458   PetscCall(PetscMalloc1(dim * numPoints, &points));
1459   for (p = pStart, f = 0, offset = 0, matoffset = 0; p < pEnd; p++) {
1460     PetscInt dof, cdof, off, d;
1461 
1462     PetscCall(PetscSectionGetDof(section, p, &dof));
1463     PetscCall(PetscSectionGetConstraintDof(section, p, &cdof));
1464     if (!(dof - cdof)) continue;
1465     PetscCall(PetscSectionGetOffset(section, p, &off));
1466     for (d = 0; d < dof; d++, off++, f++) {
1467       const PetscReal *p;
1468       const PetscReal *w;
1469       PetscInt         Np, i;
1470 
1471       PetscCall(PetscDualSpaceGetFunctional(sp, off, &q));
1472       PetscCall(PetscQuadratureGetData(q, NULL, NULL, &Np, &p, &w));
1473       for (i = 0; i < Np * dim; i++) points[offset + i] = p[i];
1474       for (i = 0; i < Np * Nc; i++) PetscCall(MatSetValue(imat, f, matoffset + i, w[i], INSERT_VALUES));
1475       offset += Np * dim;
1476       matoffset += Np * Nc;
1477     }
1478   }
1479   PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, intNodes));
1480   PetscCall(PetscQuadratureSetData(*intNodes, dim, 0, numPoints, points, NULL));
1481   PetscCall(MatAssemblyBegin(imat, MAT_FINAL_ASSEMBLY));
1482   PetscCall(MatAssemblyEnd(imat, MAT_FINAL_ASSEMBLY));
1483   *intMat = imat;
1484   PetscFunctionReturn(PETSC_SUCCESS);
1485 }
1486 
1487 /*@
1488   PetscDualSpaceEqual - Determine if two dual spaces are equivalent
1489 
1490   Input Parameters:
1491 + A - A `PetscDualSpace` object
1492 - B - Another `PetscDualSpace` object
1493 
1494   Output Parameter:
1495 . equal - `PETSC_TRUE` if the dual spaces are equivalent
1496 
1497   Level: advanced
1498 
1499 .seealso: `PetscDualSpace`, `PetscDualSpaceCreate()`
1500 @*/
1501 PetscErrorCode PetscDualSpaceEqual(PetscDualSpace A, PetscDualSpace B, PetscBool *equal)
1502 {
1503   PetscInt        sizeA, sizeB, dimA, dimB;
1504   const PetscInt *dofA, *dofB;
1505   PetscQuadrature quadA, quadB;
1506   Mat             matA, matB;
1507 
1508   PetscFunctionBegin;
1509   PetscValidHeaderSpecific(A, PETSCDUALSPACE_CLASSID, 1);
1510   PetscValidHeaderSpecific(B, PETSCDUALSPACE_CLASSID, 2);
1511   PetscAssertPointer(equal, 3);
1512   *equal = PETSC_FALSE;
1513   PetscCall(PetscDualSpaceGetDimension(A, &sizeA));
1514   PetscCall(PetscDualSpaceGetDimension(B, &sizeB));
1515   if (sizeB != sizeA) PetscFunctionReturn(PETSC_SUCCESS);
1516   PetscCall(DMGetDimension(A->dm, &dimA));
1517   PetscCall(DMGetDimension(B->dm, &dimB));
1518   if (dimA != dimB) PetscFunctionReturn(PETSC_SUCCESS);
1519 
1520   PetscCall(PetscDualSpaceGetNumDof(A, &dofA));
1521   PetscCall(PetscDualSpaceGetNumDof(B, &dofB));
1522   for (PetscInt d = 0; d < dimA; d++) {
1523     if (dofA[d] != dofB[d]) PetscFunctionReturn(PETSC_SUCCESS);
1524   }
1525 
1526   PetscCall(PetscDualSpaceGetInteriorData(A, &quadA, &matA));
1527   PetscCall(PetscDualSpaceGetInteriorData(B, &quadB, &matB));
1528   if (!quadA && !quadB) {
1529     *equal = PETSC_TRUE;
1530   } else if (quadA && quadB) {
1531     PetscCall(PetscQuadratureEqual(quadA, quadB, equal));
1532     if (*equal == PETSC_FALSE) PetscFunctionReturn(PETSC_SUCCESS);
1533     if (!matA && !matB) PetscFunctionReturn(PETSC_SUCCESS);
1534     if (matA && matB) PetscCall(MatEqual(matA, matB, equal));
1535     else *equal = PETSC_FALSE;
1536   }
1537   PetscFunctionReturn(PETSC_SUCCESS);
1538 }
1539 
1540 /*@C
1541   PetscDualSpaceApplyFVM - Apply a functional from the dual space basis to an input function by assuming a point evaluation functional at the cell centroid.
1542 
1543   Input Parameters:
1544 + sp    - The `PetscDualSpace` object
1545 . f     - The basis functional index
1546 . time  - The time
1547 . cgeom - A context with geometric information for this cell, we currently just use the centroid
1548 . Nc    - The number of components for the function
1549 . func  - The input function
1550 - ctx   - A context for the function
1551 
1552   Output Parameter:
1553 . value - The output value (scalar)
1554 
1555   Calling sequence:
1556 .vb
1557   PetscErrorCode func(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt numComponents, PetscScalar values[], void *ctx)
1558 .ve
1559 
1560   Level: advanced
1561 
1562   Note:
1563   The idea is to evaluate the functional as an integral $ n(f) = \int dx n(x) . f(x)$ where both n and f have Nc components.
1564 
1565 .seealso: `PetscDualSpace`, `PetscDualSpaceCreate()`
1566 @*/
1567 PetscErrorCode PetscDualSpaceApplyFVM(PetscDualSpace sp, PetscInt f, PetscReal time, PetscFVCellGeom *cgeom, PetscInt Nc, PetscErrorCode (*func)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void *ctx, PetscScalar *value)
1568 {
1569   DM               dm;
1570   PetscQuadrature  n;
1571   const PetscReal *points, *weights;
1572   PetscScalar     *val;
1573   PetscInt         dimEmbed, qNc, c, Nq, q;
1574 
1575   PetscFunctionBegin;
1576   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
1577   PetscAssertPointer(value, 8);
1578   PetscCall(PetscDualSpaceGetDM(sp, &dm));
1579   PetscCall(DMGetCoordinateDim(dm, &dimEmbed));
1580   PetscCall(PetscDualSpaceGetFunctional(sp, f, &n));
1581   PetscCall(PetscQuadratureGetData(n, NULL, &qNc, &Nq, &points, &weights));
1582   PetscCheck(qNc == Nc, PetscObjectComm((PetscObject)sp), PETSC_ERR_ARG_SIZ, "The quadrature components %" PetscInt_FMT " != function components %" PetscInt_FMT, qNc, Nc);
1583   PetscCall(DMGetWorkArray(dm, Nc, MPIU_SCALAR, &val));
1584   *value = 0.;
1585   for (q = 0; q < Nq; ++q) {
1586     PetscCall((*func)(dimEmbed, time, cgeom->centroid, Nc, val, ctx));
1587     for (c = 0; c < Nc; ++c) *value += val[c] * weights[q * Nc + c];
1588   }
1589   PetscCall(DMRestoreWorkArray(dm, Nc, MPIU_SCALAR, &val));
1590   PetscFunctionReturn(PETSC_SUCCESS);
1591 }
1592 
1593 /*@
1594   PetscDualSpaceGetHeightSubspace - Get the subset of the dual space basis that is supported on a mesh point of a
1595   given height.  This assumes that the reference cell is symmetric over points of this height.
1596 
1597   Not Collective
1598 
1599   Input Parameters:
1600 + sp     - the `PetscDualSpace` object
1601 - height - the height of the mesh point for which the subspace is desired
1602 
1603   Output Parameter:
1604 . subsp - the subspace.  Note that the functionals in the subspace are with respect to the intrinsic geometry of the
1605   point, which will be of lesser dimension if height > 0.
1606 
1607   Level: advanced
1608 
1609   Notes:
1610   If the dual space is not defined on mesh points of the given height (e.g. if the space is discontinuous and
1611   pointwise values are not defined on the element boundaries), or if the implementation of `PetscDualSpace` does not
1612   support extracting subspaces, then NULL is returned.
1613 
1614   This does not increment the reference count on the returned dual space, and the user should not destroy it.
1615 
1616 .seealso: `PetscDualSpace`, `PetscSpaceGetHeightSubspace()`, `PetscDualSpaceGetPointSubspace()`
1617 @*/
1618 PetscErrorCode PetscDualSpaceGetHeightSubspace(PetscDualSpace sp, PetscInt height, PetscDualSpace *subsp)
1619 {
1620   PetscInt depth = -1, cStart, cEnd;
1621   DM       dm;
1622 
1623   PetscFunctionBegin;
1624   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
1625   PetscAssertPointer(subsp, 3);
1626   PetscCheck(sp->uniform, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "A non-uniform dual space does not have a single dual space at each height");
1627   *subsp = NULL;
1628   dm     = sp->dm;
1629   PetscCall(DMPlexGetDepth(dm, &depth));
1630   PetscCheck(height >= 0 && height <= depth, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid height");
1631   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1632   if (height == 0 && cEnd == cStart + 1) {
1633     *subsp = sp;
1634     PetscFunctionReturn(PETSC_SUCCESS);
1635   }
1636   if (!sp->heightSpaces) {
1637     PetscInt h;
1638     PetscCall(PetscCalloc1(depth + 1, &sp->heightSpaces));
1639 
1640     for (h = 0; h <= depth; h++) {
1641       if (h == 0 && cEnd == cStart + 1) continue;
1642       if (sp->ops->createheightsubspace) PetscUseTypeMethod(sp, createheightsubspace, height, &sp->heightSpaces[h]);
1643       else if (sp->pointSpaces) {
1644         PetscInt hStart, hEnd;
1645 
1646         PetscCall(DMPlexGetHeightStratum(dm, h, &hStart, &hEnd));
1647         if (hEnd > hStart) {
1648           const char *name;
1649 
1650           PetscCall(PetscObjectReference((PetscObject)sp->pointSpaces[hStart]));
1651           if (sp->pointSpaces[hStart]) {
1652             PetscCall(PetscObjectGetName((PetscObject)sp, &name));
1653             PetscCall(PetscObjectSetName((PetscObject)sp->pointSpaces[hStart], name));
1654           }
1655           sp->heightSpaces[h] = sp->pointSpaces[hStart];
1656         }
1657       }
1658     }
1659   }
1660   *subsp = sp->heightSpaces[height];
1661   PetscFunctionReturn(PETSC_SUCCESS);
1662 }
1663 
1664 /*@
1665   PetscDualSpaceGetPointSubspace - Get the subset of the dual space basis that is supported on a particular mesh point.
1666 
1667   Not Collective
1668 
1669   Input Parameters:
1670 + sp    - the `PetscDualSpace` object
1671 - point - the point (in the dual space's DM) for which the subspace is desired
1672 
1673   Output Parameters:
1674 . bdsp - the subspace.
1675 
1676   Level: advanced
1677 
1678   Notes:
1679   The functionals in the subspace are with respect to the intrinsic geometry of the point,
1680   which will be of lesser dimension if height > 0.
1681 
1682   If the dual space is not defined on the mesh point (e.g. if the space is discontinuous and pointwise values are not
1683   defined on the element boundaries), or if the implementation of `PetscDualSpace` does not support extracting
1684   subspaces, then `NULL` is returned.
1685 
1686   This does not increment the reference count on the returned dual space, and the user should not destroy it.
1687 
1688 .seealso: `PetscDualSpace`, `PetscDualSpaceGetHeightSubspace()`
1689 @*/
1690 PetscErrorCode PetscDualSpaceGetPointSubspace(PetscDualSpace sp, PetscInt point, PetscDualSpace *bdsp)
1691 {
1692   PetscInt pStart = 0, pEnd = 0, cStart, cEnd;
1693   DM       dm;
1694 
1695   PetscFunctionBegin;
1696   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
1697   PetscAssertPointer(bdsp, 3);
1698   *bdsp = NULL;
1699   dm    = sp->dm;
1700   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1701   PetscCheck(point >= pStart && point <= pEnd, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid point");
1702   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1703   if (point == cStart && cEnd == cStart + 1) { /* the dual space is only equivalent to the dual space on a cell if the reference mesh has just one cell */
1704     *bdsp = sp;
1705     PetscFunctionReturn(PETSC_SUCCESS);
1706   }
1707   if (!sp->pointSpaces) {
1708     PetscInt p;
1709     PetscCall(PetscCalloc1(pEnd - pStart, &sp->pointSpaces));
1710 
1711     for (p = 0; p < pEnd - pStart; p++) {
1712       if (p + pStart == cStart && cEnd == cStart + 1) continue;
1713       if (sp->ops->createpointsubspace) PetscUseTypeMethod(sp, createpointsubspace, p + pStart, &sp->pointSpaces[p]);
1714       else if (sp->heightSpaces || sp->ops->createheightsubspace) {
1715         PetscInt dim, depth, height;
1716         DMLabel  label;
1717 
1718         PetscCall(DMPlexGetDepth(dm, &dim));
1719         PetscCall(DMPlexGetDepthLabel(dm, &label));
1720         PetscCall(DMLabelGetValue(label, p + pStart, &depth));
1721         height = dim - depth;
1722         PetscCall(PetscDualSpaceGetHeightSubspace(sp, height, &sp->pointSpaces[p]));
1723         PetscCall(PetscObjectReference((PetscObject)sp->pointSpaces[p]));
1724       }
1725     }
1726   }
1727   *bdsp = sp->pointSpaces[point - pStart];
1728   PetscFunctionReturn(PETSC_SUCCESS);
1729 }
1730 
1731 /*@C
1732   PetscDualSpaceGetSymmetries - Returns a description of the symmetries of this basis
1733 
1734   Not Collective
1735 
1736   Input Parameter:
1737 . sp - the `PetscDualSpace` object
1738 
1739   Output Parameters:
1740 + perms - Permutations of the interior degrees of freedom, parameterized by the point orientation
1741 - flips - Sign reversal of the interior degrees of freedom, parameterized by the point orientation
1742 
1743   Level: developer
1744 
1745   Note:
1746   The permutation and flip arrays are organized in the following way
1747 .vb
1748   perms[p][ornt][dof # on point] = new local dof #
1749   flips[p][ornt][dof # on point] = reversal or not
1750 .ve
1751 
1752 .seealso: `PetscDualSpace`
1753 @*/
1754 PetscErrorCode PetscDualSpaceGetSymmetries(PetscDualSpace sp, const PetscInt ****perms, const PetscScalar ****flips)
1755 {
1756   PetscFunctionBegin;
1757   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
1758   if (perms) {
1759     PetscAssertPointer(perms, 2);
1760     *perms = NULL;
1761   }
1762   if (flips) {
1763     PetscAssertPointer(flips, 3);
1764     *flips = NULL;
1765   }
1766   PetscTryTypeMethod(sp, getsymmetries, perms, flips);
1767   PetscFunctionReturn(PETSC_SUCCESS);
1768 }
1769 
1770 /*@
1771   PetscDualSpaceGetFormDegree - Get the form degree k for the k-form the describes the pushforwards/pullbacks of this
1772   dual space's functionals.
1773 
1774   Input Parameter:
1775 . dsp - The `PetscDualSpace`
1776 
1777   Output Parameter:
1778 . k - The *signed* degree k of the k.  If k >= 0, this means that the degrees of freedom are k-forms, and are stored
1779         in lexicographic order according to the basis of k-forms constructed from the wedge product of 1-forms.  So for example,
1780         the 1-form basis in 3-D is (dx, dy, dz), and the 2-form basis in 3-D is (dx wedge dy, dx wedge dz, dy wedge dz).
1781         If k < 0, this means that the degrees transform as k-forms, but are stored as (N-k) forms according to the
1782         Hodge star map.  So for example if k = -2 and N = 3, this means that the degrees of freedom transform as 2-forms
1783         but are stored as 1-forms.
1784 
1785   Level: developer
1786 
1787 .seealso: `PetscDualSpace`, `PetscDTAltV`, `PetscDualSpacePullback()`, `PetscDualSpacePushforward()`, `PetscDualSpaceTransform()`, `PetscDualSpaceTransformType`
1788 @*/
1789 PetscErrorCode PetscDualSpaceGetFormDegree(PetscDualSpace dsp, PetscInt *k)
1790 {
1791   PetscFunctionBeginHot;
1792   PetscValidHeaderSpecific(dsp, PETSCDUALSPACE_CLASSID, 1);
1793   PetscAssertPointer(k, 2);
1794   *k = dsp->k;
1795   PetscFunctionReturn(PETSC_SUCCESS);
1796 }
1797 
1798 /*@
1799   PetscDualSpaceSetFormDegree - Set the form degree k for the k-form the describes the pushforwards/pullbacks of this
1800   dual space's functionals.
1801 
1802   Input Parameters:
1803 + dsp - The `PetscDualSpace`
1804 - k   - The *signed* degree k of the k.  If k >= 0, this means that the degrees of freedom are k-forms, and are stored
1805         in lexicographic order according to the basis of k-forms constructed from the wedge product of 1-forms.  So for example,
1806         the 1-form basis in 3-D is (dx, dy, dz), and the 2-form basis in 3-D is (dx wedge dy, dx wedge dz, dy wedge dz).
1807         If k < 0, this means that the degrees transform as k-forms, but are stored as (N-k) forms according to the
1808         Hodge star map.  So for example if k = -2 and N = 3, this means that the degrees of freedom transform as 2-forms
1809         but are stored as 1-forms.
1810 
1811   Level: developer
1812 
1813 .seealso: `PetscDualSpace`, `PetscDTAltV`, `PetscDualSpacePullback()`, `PetscDualSpacePushforward()`, `PetscDualSpaceTransform()`, `PetscDualSpaceTransformType`
1814 @*/
1815 PetscErrorCode PetscDualSpaceSetFormDegree(PetscDualSpace dsp, PetscInt k)
1816 {
1817   PetscInt dim;
1818 
1819   PetscFunctionBeginHot;
1820   PetscValidHeaderSpecific(dsp, PETSCDUALSPACE_CLASSID, 1);
1821   PetscCheck(!dsp->setupcalled, PetscObjectComm((PetscObject)dsp), PETSC_ERR_ARG_WRONGSTATE, "Cannot change number of components after dualspace is set up");
1822   dim = dsp->dm->dim;
1823   PetscCheck((k >= -dim && k <= dim) || k == PETSC_FORM_DEGREE_UNDEFINED, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unsupported %" PetscInt_FMT "-form on %" PetscInt_FMT "-dimensional reference cell", PetscAbsInt(k), dim);
1824   dsp->k = k;
1825   PetscFunctionReturn(PETSC_SUCCESS);
1826 }
1827 
1828 /*@
1829   PetscDualSpaceGetDeRahm - Get the k-simplex associated with the functionals in this dual space
1830 
1831   Input Parameter:
1832 . dsp - The `PetscDualSpace`
1833 
1834   Output Parameter:
1835 . k - The simplex dimension
1836 
1837   Level: developer
1838 
1839   Note:
1840   Currently supported values are
1841 .vb
1842   0: These are H_1 methods that only transform coordinates
1843   1: These are Hcurl methods that transform functions using the covariant Piola transform (COVARIANT_PIOLA_TRANSFORM)
1844   2: These are the same as 1
1845   3: These are Hdiv methods that transform functions using the contravariant Piola transform (CONTRAVARIANT_PIOLA_TRANSFORM)
1846 .ve
1847 
1848 .seealso: `PetscDualSpace`, `PetscDualSpacePullback()`, `PetscDualSpacePushforward()`, `PetscDualSpaceTransform()`, `PetscDualSpaceTransformType`
1849 @*/
1850 PetscErrorCode PetscDualSpaceGetDeRahm(PetscDualSpace dsp, PetscInt *k)
1851 {
1852   PetscInt dim;
1853 
1854   PetscFunctionBeginHot;
1855   PetscValidHeaderSpecific(dsp, PETSCDUALSPACE_CLASSID, 1);
1856   PetscAssertPointer(k, 2);
1857   dim = dsp->dm->dim;
1858   if (!dsp->k) *k = IDENTITY_TRANSFORM;
1859   else if (dsp->k == 1) *k = COVARIANT_PIOLA_TRANSFORM;
1860   else if (dsp->k == -(dim - 1)) *k = CONTRAVARIANT_PIOLA_TRANSFORM;
1861   else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unsupported transformation");
1862   PetscFunctionReturn(PETSC_SUCCESS);
1863 }
1864 
1865 /*@C
1866   PetscDualSpaceTransform - Transform the function values
1867 
1868   Input Parameters:
1869 + dsp       - The `PetscDualSpace`
1870 . trans     - The type of transform
1871 . isInverse - Flag to invert the transform
1872 . fegeom    - The cell geometry
1873 . Nv        - The number of function samples
1874 . Nc        - The number of function components
1875 - vals      - The function values
1876 
1877   Output Parameter:
1878 . vals - The transformed function values
1879 
1880   Level: intermediate
1881 
1882   Note:
1883   This only handles transformations when the embedding dimension of the geometry in fegeom is the same as the reference dimension.
1884 
1885 .seealso: `PetscDualSpace`, `PetscDualSpaceTransformGradient()`, `PetscDualSpaceTransformHessian()`, `PetscDualSpacePullback()`, `PetscDualSpacePushforward()`, `PetscDualSpaceTransformType`
1886 @*/
1887 PetscErrorCode PetscDualSpaceTransform(PetscDualSpace dsp, PetscDualSpaceTransformType trans, PetscBool isInverse, PetscFEGeom *fegeom, PetscInt Nv, PetscInt Nc, PetscScalar vals[])
1888 {
1889   PetscReal Jstar[9] = {0};
1890   PetscInt  dim, v, c, Nk;
1891 
1892   PetscFunctionBeginHot;
1893   PetscValidHeaderSpecific(dsp, PETSCDUALSPACE_CLASSID, 1);
1894   PetscAssertPointer(fegeom, 4);
1895   PetscAssertPointer(vals, 7);
1896   /* TODO: not handling dimEmbed != dim right now */
1897   dim = dsp->dm->dim;
1898   /* No change needed for 0-forms */
1899   if (!dsp->k) PetscFunctionReturn(PETSC_SUCCESS);
1900   PetscCall(PetscDTBinomialInt(dim, PetscAbsInt(dsp->k), &Nk));
1901   /* TODO: use fegeom->isAffine */
1902   PetscCall(PetscDTAltVPullbackMatrix(dim, dim, isInverse ? fegeom->J : fegeom->invJ, dsp->k, Jstar));
1903   for (v = 0; v < Nv; ++v) {
1904     switch (Nk) {
1905     case 1:
1906       for (c = 0; c < Nc; c++) vals[v * Nc + c] *= Jstar[0];
1907       break;
1908     case 2:
1909       for (c = 0; c < Nc; c += 2) DMPlex_Mult2DReal_Internal(Jstar, 1, &vals[v * Nc + c], &vals[v * Nc + c]);
1910       break;
1911     case 3:
1912       for (c = 0; c < Nc; c += 3) DMPlex_Mult3DReal_Internal(Jstar, 1, &vals[v * Nc + c], &vals[v * Nc + c]);
1913       break;
1914     default:
1915       SETERRQ(PetscObjectComm((PetscObject)dsp), PETSC_ERR_ARG_OUTOFRANGE, "Unsupported form size %" PetscInt_FMT " for transformation", Nk);
1916     }
1917   }
1918   PetscFunctionReturn(PETSC_SUCCESS);
1919 }
1920 
1921 /*@C
1922   PetscDualSpaceTransformGradient - Transform the function gradient values
1923 
1924   Input Parameters:
1925 + dsp       - The `PetscDualSpace`
1926 . trans     - The type of transform
1927 . isInverse - Flag to invert the transform
1928 . fegeom    - The cell geometry
1929 . Nv        - The number of function gradient samples
1930 . Nc        - The number of function components
1931 - vals      - The function gradient values
1932 
1933   Output Parameter:
1934 . vals - The transformed function gradient values
1935 
1936   Level: intermediate
1937 
1938   Note:
1939   This only handles transformations when the embedding dimension of the geometry in fegeom is the same as the reference dimension.
1940 
1941 .seealso: `PetscDualSpace`, `PetscDualSpaceTransform()`, `PetscDualSpacePullback()`, `PetscDualSpacePushforward()`, `PetscDualSpaceTransformType`
1942 @*/
1943 PetscErrorCode PetscDualSpaceTransformGradient(PetscDualSpace dsp, PetscDualSpaceTransformType trans, PetscBool isInverse, PetscFEGeom *fegeom, PetscInt Nv, PetscInt Nc, PetscScalar vals[])
1944 {
1945   const PetscInt dim = dsp->dm->dim, dE = fegeom->dimEmbed;
1946   PetscInt       v, c, d;
1947 
1948   PetscFunctionBeginHot;
1949   PetscValidHeaderSpecific(dsp, PETSCDUALSPACE_CLASSID, 1);
1950   PetscAssertPointer(fegeom, 4);
1951   PetscAssertPointer(vals, 7);
1952   PetscAssert(dE > 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid embedding dimension %" PetscInt_FMT, dE);
1953   /* Transform gradient */
1954   if (dim == dE) {
1955     for (v = 0; v < Nv; ++v) {
1956       for (c = 0; c < Nc; ++c) {
1957         switch (dim) {
1958         case 1:
1959           vals[(v * Nc + c) * dim] *= fegeom->invJ[0];
1960           break;
1961         case 2:
1962           DMPlex_MultTranspose2DReal_Internal(fegeom->invJ, 1, &vals[(v * Nc + c) * dim], &vals[(v * Nc + c) * dim]);
1963           break;
1964         case 3:
1965           DMPlex_MultTranspose3DReal_Internal(fegeom->invJ, 1, &vals[(v * Nc + c) * dim], &vals[(v * Nc + c) * dim]);
1966           break;
1967         default:
1968           SETERRQ(PetscObjectComm((PetscObject)dsp), PETSC_ERR_ARG_OUTOFRANGE, "Unsupported dim %" PetscInt_FMT " for transformation", dim);
1969         }
1970       }
1971     }
1972   } else {
1973     for (v = 0; v < Nv; ++v) {
1974       for (c = 0; c < Nc; ++c) DMPlex_MultTransposeReal_Internal(fegeom->invJ, dim, dE, 1, &vals[(v * Nc + c) * dE], &vals[(v * Nc + c) * dE]);
1975     }
1976   }
1977   /* Assume its a vector, otherwise assume its a bunch of scalars */
1978   if (Nc == 1 || Nc != dim) PetscFunctionReturn(PETSC_SUCCESS);
1979   switch (trans) {
1980   case IDENTITY_TRANSFORM:
1981     break;
1982   case COVARIANT_PIOLA_TRANSFORM: /* Covariant Piola mapping $\sigma^*(F) = J^{-T} F \circ \phi^{-1)$ */
1983     if (isInverse) {
1984       for (v = 0; v < Nv; ++v) {
1985         for (d = 0; d < dim; ++d) {
1986           switch (dim) {
1987           case 2:
1988             DMPlex_MultTranspose2DReal_Internal(fegeom->J, dim, &vals[v * Nc * dim + d], &vals[v * Nc * dim + d]);
1989             break;
1990           case 3:
1991             DMPlex_MultTranspose3DReal_Internal(fegeom->J, dim, &vals[v * Nc * dim + d], &vals[v * Nc * dim + d]);
1992             break;
1993           default:
1994             SETERRQ(PetscObjectComm((PetscObject)dsp), PETSC_ERR_ARG_OUTOFRANGE, "Unsupported dim %" PetscInt_FMT " for transformation", dim);
1995           }
1996         }
1997       }
1998     } else {
1999       for (v = 0; v < Nv; ++v) {
2000         for (d = 0; d < dim; ++d) {
2001           switch (dim) {
2002           case 2:
2003             DMPlex_MultTranspose2DReal_Internal(fegeom->invJ, dim, &vals[v * Nc * dim + d], &vals[v * Nc * dim + d]);
2004             break;
2005           case 3:
2006             DMPlex_MultTranspose3DReal_Internal(fegeom->invJ, dim, &vals[v * Nc * dim + d], &vals[v * Nc * dim + d]);
2007             break;
2008           default:
2009             SETERRQ(PetscObjectComm((PetscObject)dsp), PETSC_ERR_ARG_OUTOFRANGE, "Unsupported dim %" PetscInt_FMT " for transformation", dim);
2010           }
2011         }
2012       }
2013     }
2014     break;
2015   case CONTRAVARIANT_PIOLA_TRANSFORM: /* Contravariant Piola mapping $\sigma^*(F) = \frac{1}{|\det J|} J F \circ \phi^{-1}$ */
2016     if (isInverse) {
2017       for (v = 0; v < Nv; ++v) {
2018         for (d = 0; d < dim; ++d) {
2019           switch (dim) {
2020           case 2:
2021             DMPlex_Mult2DReal_Internal(fegeom->invJ, dim, &vals[v * Nc * dim + d], &vals[v * Nc * dim + d]);
2022             break;
2023           case 3:
2024             DMPlex_Mult3DReal_Internal(fegeom->invJ, dim, &vals[v * Nc * dim + d], &vals[v * Nc * dim + d]);
2025             break;
2026           default:
2027             SETERRQ(PetscObjectComm((PetscObject)dsp), PETSC_ERR_ARG_OUTOFRANGE, "Unsupported dim %" PetscInt_FMT " for transformation", dim);
2028           }
2029           for (c = 0; c < Nc; ++c) vals[(v * Nc + c) * dim + d] *= fegeom->detJ[0];
2030         }
2031       }
2032     } else {
2033       for (v = 0; v < Nv; ++v) {
2034         for (d = 0; d < dim; ++d) {
2035           switch (dim) {
2036           case 2:
2037             DMPlex_Mult2DReal_Internal(fegeom->J, dim, &vals[v * Nc * dim + d], &vals[v * Nc * dim + d]);
2038             break;
2039           case 3:
2040             DMPlex_Mult3DReal_Internal(fegeom->J, dim, &vals[v * Nc * dim + d], &vals[v * Nc * dim + d]);
2041             break;
2042           default:
2043             SETERRQ(PetscObjectComm((PetscObject)dsp), PETSC_ERR_ARG_OUTOFRANGE, "Unsupported dim %" PetscInt_FMT " for transformation", dim);
2044           }
2045           for (c = 0; c < Nc; ++c) vals[(v * Nc + c) * dim + d] /= fegeom->detJ[0];
2046         }
2047       }
2048     }
2049     break;
2050   }
2051   PetscFunctionReturn(PETSC_SUCCESS);
2052 }
2053 
2054 /*@C
2055   PetscDualSpaceTransformHessian - Transform the function Hessian values
2056 
2057   Input Parameters:
2058 + dsp       - The `PetscDualSpace`
2059 . trans     - The type of transform
2060 . isInverse - Flag to invert the transform
2061 . fegeom    - The cell geometry
2062 . Nv        - The number of function Hessian samples
2063 . Nc        - The number of function components
2064 - vals      - The function gradient values
2065 
2066   Output Parameter:
2067 . vals - The transformed function Hessian values
2068 
2069   Level: intermediate
2070 
2071   Note:
2072   This only handles transformations when the embedding dimension of the geometry in fegeom is the same as the reference dimension.
2073 
2074 .seealso: `PetscDualSpace`, `PetscDualSpaceTransform()`, `PetscDualSpacePullback()`, `PetscDualSpacePushforward()`, `PetscDualSpaceTransformType`
2075 @*/
2076 PetscErrorCode PetscDualSpaceTransformHessian(PetscDualSpace dsp, PetscDualSpaceTransformType trans, PetscBool isInverse, PetscFEGeom *fegeom, PetscInt Nv, PetscInt Nc, PetscScalar vals[])
2077 {
2078   const PetscInt dim = dsp->dm->dim, dE = fegeom->dimEmbed;
2079   PetscInt       v, c;
2080 
2081   PetscFunctionBeginHot;
2082   PetscValidHeaderSpecific(dsp, PETSCDUALSPACE_CLASSID, 1);
2083   PetscAssertPointer(fegeom, 4);
2084   PetscAssertPointer(vals, 7);
2085   PetscAssert(dE > 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid embedding dimension %" PetscInt_FMT, dE);
2086   /* Transform Hessian: J^{-T}_{ik} J^{-T}_{jl} H(f)_{kl} = J^{-T}_{ik} H(f)_{kl} J^{-1}_{lj} */
2087   if (dim == dE) {
2088     for (v = 0; v < Nv; ++v) {
2089       for (c = 0; c < Nc; ++c) {
2090         switch (dim) {
2091         case 1:
2092           vals[(v * Nc + c) * dim * dim] *= PetscSqr(fegeom->invJ[0]);
2093           break;
2094         case 2:
2095           DMPlex_PTAP2DReal_Internal(fegeom->invJ, &vals[(v * Nc + c) * dim * dim], &vals[(v * Nc + c) * dim * dim]);
2096           break;
2097         case 3:
2098           DMPlex_PTAP3DReal_Internal(fegeom->invJ, &vals[(v * Nc + c) * dim * dim], &vals[(v * Nc + c) * dim * dim]);
2099           break;
2100         default:
2101           SETERRQ(PetscObjectComm((PetscObject)dsp), PETSC_ERR_ARG_OUTOFRANGE, "Unsupported dim %" PetscInt_FMT " for transformation", dim);
2102         }
2103       }
2104     }
2105   } else {
2106     for (v = 0; v < Nv; ++v) {
2107       for (c = 0; c < Nc; ++c) DMPlex_PTAPReal_Internal(fegeom->invJ, dim, dE, &vals[(v * Nc + c) * dE * dE], &vals[(v * Nc + c) * dE * dE]);
2108     }
2109   }
2110   /* Assume its a vector, otherwise assume its a bunch of scalars */
2111   if (Nc == 1 || Nc != dim) PetscFunctionReturn(PETSC_SUCCESS);
2112   switch (trans) {
2113   case IDENTITY_TRANSFORM:
2114     break;
2115   case COVARIANT_PIOLA_TRANSFORM: /* Covariant Piola mapping $\sigma^*(F) = J^{-T} F \circ \phi^{-1)$ */
2116     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Piola mapping for Hessians not yet supported");
2117   case CONTRAVARIANT_PIOLA_TRANSFORM: /* Contravariant Piola mapping $\sigma^*(F) = \frac{1}{|\det J|} J F \circ \phi^{-1}$ */
2118     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Piola mapping for Hessians not yet supported");
2119   }
2120   PetscFunctionReturn(PETSC_SUCCESS);
2121 }
2122 
2123 /*@C
2124   PetscDualSpacePullback - Transform the given functional so that it operates on real space, rather than the reference element. Operationally, this means that we map the function evaluations depending on continuity requirements of our finite element method.
2125 
2126   Input Parameters:
2127 + dsp       - The `PetscDualSpace`
2128 . fegeom    - The geometry for this cell
2129 . Nq        - The number of function samples
2130 . Nc        - The number of function components
2131 - pointEval - The function values
2132 
2133   Output Parameter:
2134 . pointEval - The transformed function values
2135 
2136   Level: advanced
2137 
2138   Notes:
2139   Functions transform in a complementary way (pushforward) to functionals, so that the scalar product is invariant. The type of transform is dependent on the associated k-simplex from the DeRahm complex.
2140 
2141   This only handles transformations when the embedding dimension of the geometry in fegeom is the same as the reference dimension.
2142 
2143 .seealso: `PetscDualSpace`, `PetscDualSpacePushforward()`, `PetscDualSpaceTransform()`, `PetscDualSpaceGetDeRahm()`
2144 @*/
2145 PetscErrorCode PetscDualSpacePullback(PetscDualSpace dsp, PetscFEGeom *fegeom, PetscInt Nq, PetscInt Nc, PetscScalar pointEval[])
2146 {
2147   PetscDualSpaceTransformType trans;
2148   PetscInt                    k;
2149 
2150   PetscFunctionBeginHot;
2151   PetscValidHeaderSpecific(dsp, PETSCDUALSPACE_CLASSID, 1);
2152   PetscAssertPointer(fegeom, 2);
2153   PetscAssertPointer(pointEval, 5);
2154   /* The dualspace dofs correspond to some simplex in the DeRahm complex, which we label by k.
2155      This determines their transformation properties. */
2156   PetscCall(PetscDualSpaceGetDeRahm(dsp, &k));
2157   switch (k) {
2158   case 0: /* H^1 point evaluations */
2159     trans = IDENTITY_TRANSFORM;
2160     break;
2161   case 1: /* Hcurl preserves tangential edge traces  */
2162     trans = COVARIANT_PIOLA_TRANSFORM;
2163     break;
2164   case 2:
2165   case 3: /* Hdiv preserve normal traces */
2166     trans = CONTRAVARIANT_PIOLA_TRANSFORM;
2167     break;
2168   default:
2169     SETERRQ(PetscObjectComm((PetscObject)dsp), PETSC_ERR_ARG_OUTOFRANGE, "Unsupported simplex dim %" PetscInt_FMT " for transformation", k);
2170   }
2171   PetscCall(PetscDualSpaceTransform(dsp, trans, PETSC_TRUE, fegeom, Nq, Nc, pointEval));
2172   PetscFunctionReturn(PETSC_SUCCESS);
2173 }
2174 
2175 /*@C
2176   PetscDualSpacePushforward - Transform the given function so that it operates on real space, rather than the reference element. Operationally, this means that we map the function evaluations depending on continuity requirements of our finite element method.
2177 
2178   Input Parameters:
2179 + dsp       - The `PetscDualSpace`
2180 . fegeom    - The geometry for this cell
2181 . Nq        - The number of function samples
2182 . Nc        - The number of function components
2183 - pointEval - The function values
2184 
2185   Output Parameter:
2186 . pointEval - The transformed function values
2187 
2188   Level: advanced
2189 
2190   Notes:
2191   Functionals transform in a complementary way (pullback) to functions, so that the scalar product is invariant. The type of transform is dependent on the associated k-simplex from the DeRahm complex.
2192 
2193   This only handles transformations when the embedding dimension of the geometry in fegeom is the same as the reference dimension.
2194 
2195 .seealso: `PetscDualSpace`, `PetscDualSpacePullback()`, `PetscDualSpaceTransform()`, `PetscDualSpaceGetDeRahm()`
2196 @*/
2197 PetscErrorCode PetscDualSpacePushforward(PetscDualSpace dsp, PetscFEGeom *fegeom, PetscInt Nq, PetscInt Nc, PetscScalar pointEval[])
2198 {
2199   PetscDualSpaceTransformType trans;
2200   PetscInt                    k;
2201 
2202   PetscFunctionBeginHot;
2203   PetscValidHeaderSpecific(dsp, PETSCDUALSPACE_CLASSID, 1);
2204   PetscAssertPointer(fegeom, 2);
2205   PetscAssertPointer(pointEval, 5);
2206   /* The dualspace dofs correspond to some simplex in the DeRahm complex, which we label by k.
2207      This determines their transformation properties. */
2208   PetscCall(PetscDualSpaceGetDeRahm(dsp, &k));
2209   switch (k) {
2210   case 0: /* H^1 point evaluations */
2211     trans = IDENTITY_TRANSFORM;
2212     break;
2213   case 1: /* Hcurl preserves tangential edge traces  */
2214     trans = COVARIANT_PIOLA_TRANSFORM;
2215     break;
2216   case 2:
2217   case 3: /* Hdiv preserve normal traces */
2218     trans = CONTRAVARIANT_PIOLA_TRANSFORM;
2219     break;
2220   default:
2221     SETERRQ(PetscObjectComm((PetscObject)dsp), PETSC_ERR_ARG_OUTOFRANGE, "Unsupported simplex dim %" PetscInt_FMT " for transformation", k);
2222   }
2223   PetscCall(PetscDualSpaceTransform(dsp, trans, PETSC_FALSE, fegeom, Nq, Nc, pointEval));
2224   PetscFunctionReturn(PETSC_SUCCESS);
2225 }
2226 
2227 /*@C
2228   PetscDualSpacePushforwardGradient - Transform the given function gradient so that it operates on real space, rather than the reference element. Operationally, this means that we map the function evaluations depending on continuity requirements of our finite element method.
2229 
2230   Input Parameters:
2231 + dsp       - The `PetscDualSpace`
2232 . fegeom    - The geometry for this cell
2233 . Nq        - The number of function gradient samples
2234 . Nc        - The number of function components
2235 - pointEval - The function gradient values
2236 
2237   Output Parameter:
2238 . pointEval - The transformed function gradient values
2239 
2240   Level: advanced
2241 
2242   Notes:
2243   Functionals transform in a complementary way (pullback) to functions, so that the scalar product is invariant. The type of transform is dependent on the associated k-simplex from the DeRahm complex.
2244 
2245   This only handles transformations when the embedding dimension of the geometry in fegeom is the same as the reference dimension.
2246 
2247 .seealso: `PetscDualSpace`, `PetscDualSpacePushforward()`, `PPetscDualSpacePullback()`, `PetscDualSpaceTransform()`, `PetscDualSpaceGetDeRahm()`
2248 @*/
2249 PetscErrorCode PetscDualSpacePushforwardGradient(PetscDualSpace dsp, PetscFEGeom *fegeom, PetscInt Nq, PetscInt Nc, PetscScalar pointEval[])
2250 {
2251   PetscDualSpaceTransformType trans;
2252   PetscInt                    k;
2253 
2254   PetscFunctionBeginHot;
2255   PetscValidHeaderSpecific(dsp, PETSCDUALSPACE_CLASSID, 1);
2256   PetscAssertPointer(fegeom, 2);
2257   PetscAssertPointer(pointEval, 5);
2258   /* The dualspace dofs correspond to some simplex in the DeRahm complex, which we label by k.
2259      This determines their transformation properties. */
2260   PetscCall(PetscDualSpaceGetDeRahm(dsp, &k));
2261   switch (k) {
2262   case 0: /* H^1 point evaluations */
2263     trans = IDENTITY_TRANSFORM;
2264     break;
2265   case 1: /* Hcurl preserves tangential edge traces  */
2266     trans = COVARIANT_PIOLA_TRANSFORM;
2267     break;
2268   case 2:
2269   case 3: /* Hdiv preserve normal traces */
2270     trans = CONTRAVARIANT_PIOLA_TRANSFORM;
2271     break;
2272   default:
2273     SETERRQ(PetscObjectComm((PetscObject)dsp), PETSC_ERR_ARG_OUTOFRANGE, "Unsupported simplex dim %" PetscInt_FMT " for transformation", k);
2274   }
2275   PetscCall(PetscDualSpaceTransformGradient(dsp, trans, PETSC_FALSE, fegeom, Nq, Nc, pointEval));
2276   PetscFunctionReturn(PETSC_SUCCESS);
2277 }
2278 
2279 /*@C
2280   PetscDualSpacePushforwardHessian - Transform the given function Hessian so that it operates on real space, rather than the reference element. Operationally, this means that we map the function evaluations depending on continuity requirements of our finite element method.
2281 
2282   Input Parameters:
2283 + dsp       - The `PetscDualSpace`
2284 . fegeom    - The geometry for this cell
2285 . Nq        - The number of function Hessian samples
2286 . Nc        - The number of function components
2287 - pointEval - The function gradient values
2288 
2289   Output Parameter:
2290 . pointEval - The transformed function Hessian values
2291 
2292   Level: advanced
2293 
2294   Notes:
2295   Functionals transform in a complementary way (pullback) to functions, so that the scalar product is invariant. The type of transform is dependent on the associated k-simplex from the DeRahm complex.
2296 
2297   This only handles transformations when the embedding dimension of the geometry in fegeom is the same as the reference dimension.
2298 
2299 .seealso: `PetscDualSpace`, `PetscDualSpacePushforward()`, `PPetscDualSpacePullback()`, `PetscDualSpaceTransform()`, `PetscDualSpaceGetDeRahm()`
2300 @*/
2301 PetscErrorCode PetscDualSpacePushforwardHessian(PetscDualSpace dsp, PetscFEGeom *fegeom, PetscInt Nq, PetscInt Nc, PetscScalar pointEval[])
2302 {
2303   PetscDualSpaceTransformType trans;
2304   PetscInt                    k;
2305 
2306   PetscFunctionBeginHot;
2307   PetscValidHeaderSpecific(dsp, PETSCDUALSPACE_CLASSID, 1);
2308   PetscAssertPointer(fegeom, 2);
2309   PetscAssertPointer(pointEval, 5);
2310   /* The dualspace dofs correspond to some simplex in the DeRahm complex, which we label by k.
2311      This determines their transformation properties. */
2312   PetscCall(PetscDualSpaceGetDeRahm(dsp, &k));
2313   switch (k) {
2314   case 0: /* H^1 point evaluations */
2315     trans = IDENTITY_TRANSFORM;
2316     break;
2317   case 1: /* Hcurl preserves tangential edge traces  */
2318     trans = COVARIANT_PIOLA_TRANSFORM;
2319     break;
2320   case 2:
2321   case 3: /* Hdiv preserve normal traces */
2322     trans = CONTRAVARIANT_PIOLA_TRANSFORM;
2323     break;
2324   default:
2325     SETERRQ(PetscObjectComm((PetscObject)dsp), PETSC_ERR_ARG_OUTOFRANGE, "Unsupported simplex dim %" PetscInt_FMT " for transformation", k);
2326   }
2327   PetscCall(PetscDualSpaceTransformHessian(dsp, trans, PETSC_FALSE, fegeom, Nq, Nc, pointEval));
2328   PetscFunctionReturn(PETSC_SUCCESS);
2329 }
2330