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