xref: /petsc/src/dm/dt/dualspace/interface/dualspace.c (revision d5c9c0c4eebc2f2a01a1bd0c86fca87e2acd2a03)
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           const char *name;
1628 
1629           ierr = PetscObjectReference((PetscObject)(sp->pointSpaces[hStart]));CHKERRQ(ierr);
1630           if (sp->pointSpaces[hStart]) {
1631             ierr = PetscObjectGetName((PetscObject) sp,                     &name);CHKERRQ(ierr);
1632             ierr = PetscObjectSetName((PetscObject) sp->pointSpaces[hStart], name);CHKERRQ(ierr);
1633           }
1634           sp->heightSpaces[h] = sp->pointSpaces[hStart];
1635         }
1636       }
1637     }
1638   }
1639   *subsp = sp->heightSpaces[height];
1640   PetscFunctionReturn(0);
1641 }
1642 
1643 /*@
1644   PetscDualSpaceGetPointSubspace - Get the subset of the dual space basis that is supported on a particular mesh point.
1645 
1646   If the dual space is not defined on the mesh point (e.g. if the space is discontinuous and pointwise values are not
1647   defined on the element boundaries), or if the implementation of PetscDualSpace does not support extracting
1648   subspaces, then NULL is returned.
1649 
1650   This does not increment the reference count on the returned dual space, and the user should not destroy it.
1651 
1652   Not collective
1653 
1654   Input Parameters:
1655 + sp - the PetscDualSpace object
1656 - point - the point (in the dual space's DM) for which the subspace is desired
1657 
1658   Output Parameters:
1659   bdsp - the subspace.  Note that the functionals in the subspace are with respect to the intrinsic geometry of the
1660   point, which will be of lesser dimension if height > 0.
1661 
1662   Level: advanced
1663 
1664 .seealso: PetscDualSpace
1665 @*/
1666 PetscErrorCode PetscDualSpaceGetPointSubspace(PetscDualSpace sp, PetscInt point, PetscDualSpace *bdsp)
1667 {
1668   PetscInt       pStart = 0, pEnd = 0, cStart, cEnd;
1669   DM             dm;
1670   PetscErrorCode ierr;
1671 
1672   PetscFunctionBegin;
1673   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
1674   PetscValidPointer(bdsp,2);
1675   *bdsp = NULL;
1676   dm = sp->dm;
1677   ierr = DMPlexGetChart(dm, &pStart, &pEnd);CHKERRQ(ierr);
1678   if (point < pStart || point > pEnd) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid point");
1679   ierr = DMPlexGetHeightStratum(dm,0,&cStart,&cEnd);CHKERRQ(ierr);
1680   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 */
1681     *bdsp = sp;
1682     PetscFunctionReturn(0);
1683   }
1684   if (!sp->pointSpaces) {
1685     PetscInt p;
1686     ierr = PetscCalloc1(pEnd - pStart, &(sp->pointSpaces));CHKERRQ(ierr);
1687 
1688     for (p = 0; p < pEnd - pStart; p++) {
1689       if (p + pStart == cStart && cEnd == cStart + 1) continue;
1690       if (sp->ops->createpointsubspace) {ierr = (*sp->ops->createpointsubspace)(sp,p+pStart,&(sp->pointSpaces[p]));CHKERRQ(ierr);}
1691       else if (sp->heightSpaces || sp->ops->createheightsubspace) {
1692         PetscInt dim, depth, height;
1693         DMLabel  label;
1694 
1695         ierr = DMPlexGetDepth(dm,&dim);CHKERRQ(ierr);
1696         ierr = DMPlexGetDepthLabel(dm,&label);CHKERRQ(ierr);
1697         ierr = DMLabelGetValue(label,p+pStart,&depth);CHKERRQ(ierr);
1698         height = dim - depth;
1699         ierr = PetscDualSpaceGetHeightSubspace(sp, height, &(sp->pointSpaces[p]));CHKERRQ(ierr);
1700         ierr = PetscObjectReference((PetscObject)sp->pointSpaces[p]);CHKERRQ(ierr);
1701       }
1702     }
1703   }
1704   *bdsp = sp->pointSpaces[point - pStart];
1705   PetscFunctionReturn(0);
1706 }
1707 
1708 /*@C
1709   PetscDualSpaceGetSymmetries - Returns a description of the symmetries of this basis
1710 
1711   Not collective
1712 
1713   Input Parameter:
1714 . sp - the PetscDualSpace object
1715 
1716   Output Parameters:
1717 + perms - Permutations of the interior degrees of freedom, parameterized by the point orientation
1718 - flips - Sign reversal of the interior degrees of freedom, parameterized by the point orientation
1719 
1720   Note: The permutation and flip arrays are organized in the following way
1721 $ perms[p][ornt][dof # on point] = new local dof #
1722 $ flips[p][ornt][dof # on point] = reversal or not
1723 
1724   Level: developer
1725 
1726 @*/
1727 PetscErrorCode PetscDualSpaceGetSymmetries(PetscDualSpace sp, const PetscInt ****perms, const PetscScalar ****flips)
1728 {
1729   PetscErrorCode ierr;
1730 
1731   PetscFunctionBegin;
1732   PetscValidHeaderSpecific(sp,PETSCDUALSPACE_CLASSID,1);
1733   if (perms) {PetscValidPointer(perms,2); *perms = NULL;}
1734   if (flips) {PetscValidPointer(flips,3); *flips = NULL;}
1735   if (sp->ops->getsymmetries) {ierr = (sp->ops->getsymmetries)(sp,perms,flips);CHKERRQ(ierr);}
1736   PetscFunctionReturn(0);
1737 }
1738 
1739 /*@
1740   PetscDualSpaceGetFormDegree - Get the form degree k for the k-form the describes the pushforwards/pullbacks of this
1741   dual space's functionals.
1742 
1743   Input Parameter:
1744 . dsp - The PetscDualSpace
1745 
1746   Output Parameter:
1747 . k   - The *signed* degree k of the k.  If k >= 0, this means that the degrees of freedom are k-forms, and are stored
1748         in lexicographic order according to the basis of k-forms constructed from the wedge product of 1-forms.  So for example,
1749         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).
1750         If k < 0, this means that the degrees transform as k-forms, but are stored as (N-k) forms according to the
1751         Hodge star map.  So for example if k = -2 and N = 3, this means that the degrees of freedom transform as 2-forms
1752         but are stored as 1-forms.
1753 
1754   Level: developer
1755 
1756 .seealso: PetscDTAltV, PetscDualSpacePullback(), PetscDualSpacePushforward(), PetscDualSpaceTransform(), PetscDualSpaceTransformType
1757 @*/
1758 PetscErrorCode PetscDualSpaceGetFormDegree(PetscDualSpace dsp, PetscInt *k)
1759 {
1760   PetscFunctionBeginHot;
1761   PetscValidHeaderSpecific(dsp, PETSCDUALSPACE_CLASSID, 1);
1762   PetscValidPointer(k, 2);
1763   *k = dsp->k;
1764   PetscFunctionReturn(0);
1765 }
1766 
1767 /*@
1768   PetscDualSpaceSetFormDegree - Set the form degree k for the k-form the describes the pushforwards/pullbacks of this
1769   dual space's functionals.
1770 
1771   Input Parameter:
1772 + dsp - The PetscDualSpace
1773 - k   - The *signed* degree k of the k.  If k >= 0, this means that the degrees of freedom are k-forms, and are stored
1774         in lexicographic order according to the basis of k-forms constructed from the wedge product of 1-forms.  So for example,
1775         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).
1776         If k < 0, this means that the degrees transform as k-forms, but are stored as (N-k) forms according to the
1777         Hodge star map.  So for example if k = -2 and N = 3, this means that the degrees of freedom transform as 2-forms
1778         but are stored as 1-forms.
1779 
1780   Level: developer
1781 
1782 .seealso: PetscDTAltV, PetscDualSpacePullback(), PetscDualSpacePushforward(), PetscDualSpaceTransform(), PetscDualSpaceTransformType
1783 @*/
1784 PetscErrorCode PetscDualSpaceSetFormDegree(PetscDualSpace dsp, PetscInt k)
1785 {
1786   PetscInt dim;
1787 
1788   PetscFunctionBeginHot;
1789   PetscValidHeaderSpecific(dsp, PETSCDUALSPACE_CLASSID, 1);
1790   if (dsp->setupcalled) SETERRQ(PetscObjectComm((PetscObject)dsp), PETSC_ERR_ARG_WRONGSTATE, "Cannot change number of components after dualspace is set up");
1791   dim = dsp->dm->dim;
1792   if (k < -dim || k > dim) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unsupported %D-form on %D-dimensional reference cell", PetscAbsInt(k), dim);
1793   dsp->k = k;
1794   PetscFunctionReturn(0);
1795 }
1796 
1797 /*@
1798   PetscDualSpaceGetDeRahm - Get the k-simplex associated with the functionals in this dual space
1799 
1800   Input Parameter:
1801 . dsp - The PetscDualSpace
1802 
1803   Output Parameter:
1804 . k   - The simplex dimension
1805 
1806   Level: developer
1807 
1808   Note: Currently supported values are
1809 $ 0: These are H_1 methods that only transform coordinates
1810 $ 1: These are Hcurl methods that transform functions using the covariant Piola transform (COVARIANT_PIOLA_TRANSFORM)
1811 $ 2: These are the same as 1
1812 $ 3: These are Hdiv methods that transform functions using the contravariant Piola transform (CONTRAVARIANT_PIOLA_TRANSFORM)
1813 
1814 .seealso: PetscDualSpacePullback(), PetscDualSpacePushforward(), PetscDualSpaceTransform(), PetscDualSpaceTransformType
1815 @*/
1816 PetscErrorCode PetscDualSpaceGetDeRahm(PetscDualSpace dsp, PetscInt *k)
1817 {
1818   PetscInt dim;
1819 
1820   PetscFunctionBeginHot;
1821   PetscValidHeaderSpecific(dsp, PETSCDUALSPACE_CLASSID, 1);
1822   PetscValidPointer(k, 2);
1823   dim = dsp->dm->dim;
1824   if (!dsp->k) *k = IDENTITY_TRANSFORM;
1825   else if (dsp->k == 1) *k = COVARIANT_PIOLA_TRANSFORM;
1826   else if (dsp->k == -(dim - 1)) *k = CONTRAVARIANT_PIOLA_TRANSFORM;
1827   else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unsupported transformation");
1828   PetscFunctionReturn(0);
1829 }
1830 
1831 /*@C
1832   PetscDualSpaceTransform - Transform the function values
1833 
1834   Input Parameters:
1835 + dsp       - The PetscDualSpace
1836 . trans     - The type of transform
1837 . isInverse - Flag to invert the transform
1838 . fegeom    - The cell geometry
1839 . Nv        - The number of function samples
1840 . Nc        - The number of function components
1841 - vals      - The function values
1842 
1843   Output Parameter:
1844 . vals      - The transformed function values
1845 
1846   Level: intermediate
1847 
1848   Note: This only handles tranformations when the embedding dimension of the geometry in fegeom is the same as the reference dimension.
1849 
1850 .seealso: PetscDualSpaceTransformGradient(), PetscDualSpacePullback(), PetscDualSpacePushforward(), PetscDualSpaceTransformType
1851 @*/
1852 PetscErrorCode PetscDualSpaceTransform(PetscDualSpace dsp, PetscDualSpaceTransformType trans, PetscBool isInverse, PetscFEGeom *fegeom, PetscInt Nv, PetscInt Nc, PetscScalar vals[])
1853 {
1854   PetscReal Jstar[9] = {0};
1855   PetscInt dim, v, c, Nk;
1856   PetscErrorCode ierr;
1857 
1858   PetscFunctionBeginHot;
1859   PetscValidHeaderSpecific(dsp, PETSCDUALSPACE_CLASSID, 1);
1860   PetscValidPointer(fegeom, 4);
1861   PetscValidPointer(vals, 7);
1862   /* TODO: not handling dimEmbed != dim right now */
1863   dim = dsp->dm->dim;
1864   /* No change needed for 0-forms */
1865   if (!dsp->k) PetscFunctionReturn(0);
1866   ierr = PetscDTBinomialInt(dim, PetscAbsInt(dsp->k), &Nk);CHKERRQ(ierr);
1867   /* TODO: use fegeom->isAffine */
1868   ierr = PetscDTAltVPullbackMatrix(dim, dim, isInverse ? fegeom->J : fegeom->invJ, dsp->k, Jstar);CHKERRQ(ierr);
1869   for (v = 0; v < Nv; ++v) {
1870     switch (Nk) {
1871     case 1:
1872       for (c = 0; c < Nc; c++) vals[v*Nc + c] *= Jstar[0];
1873       break;
1874     case 2:
1875       for (c = 0; c < Nc; c += 2) DMPlex_Mult2DReal_Internal(Jstar, 1, &vals[v*Nc + c], &vals[v*Nc + c]);
1876       break;
1877     case 3:
1878       for (c = 0; c < Nc; c += 3) DMPlex_Mult3DReal_Internal(Jstar, 1, &vals[v*Nc + c], &vals[v*Nc + c]);
1879       break;
1880     default: SETERRQ1(PetscObjectComm((PetscObject) dsp), PETSC_ERR_ARG_OUTOFRANGE, "Unsupported form size %D for transformation", Nk);
1881     }
1882   }
1883   PetscFunctionReturn(0);
1884 }
1885 
1886 /*@C
1887   PetscDualSpaceTransformGradient - Transform the function gradient values
1888 
1889   Input Parameters:
1890 + dsp       - The PetscDualSpace
1891 . trans     - The type of transform
1892 . isInverse - Flag to invert the transform
1893 . fegeom    - The cell geometry
1894 . Nv        - The number of function gradient samples
1895 . Nc        - The number of function components
1896 - vals      - The function gradient values
1897 
1898   Output Parameter:
1899 . vals      - The transformed function values
1900 
1901   Level: intermediate
1902 
1903   Note: This only handles tranformations when the embedding dimension of the geometry in fegeom is the same as the reference dimension.
1904 
1905 .seealso: PetscDualSpaceTransform(), PetscDualSpacePullback(), PetscDualSpacePushforward(), PetscDualSpaceTransformType
1906 @*/
1907 PetscErrorCode PetscDualSpaceTransformGradient(PetscDualSpace dsp, PetscDualSpaceTransformType trans, PetscBool isInverse, PetscFEGeom *fegeom, PetscInt Nv, PetscInt Nc, PetscScalar vals[])
1908 {
1909   const PetscInt dim = dsp->dm->dim, dE = fegeom->dimEmbed;
1910   PetscInt       v, c, d;
1911 
1912   PetscFunctionBeginHot;
1913   PetscValidHeaderSpecific(dsp, PETSCDUALSPACE_CLASSID, 1);
1914   PetscValidPointer(fegeom, 4);
1915   PetscValidPointer(vals, 7);
1916 #ifdef PETSC_USE_DEBUG
1917   if (dE <= 0) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid embedding dimension %D", dE);
1918 #endif
1919   /* Transform gradient */
1920   if (dim == dE) {
1921     for (v = 0; v < Nv; ++v) {
1922       for (c = 0; c < Nc; ++c) {
1923         switch (dim)
1924         {
1925           case 1: vals[(v*Nc+c)*dim] *= fegeom->invJ[0];break;
1926           case 2: DMPlex_MultTranspose2DReal_Internal(fegeom->invJ, 1, &vals[(v*Nc+c)*dim], &vals[(v*Nc+c)*dim]);break;
1927           case 3: DMPlex_MultTranspose3DReal_Internal(fegeom->invJ, 1, &vals[(v*Nc+c)*dim], &vals[(v*Nc+c)*dim]);break;
1928           default: SETERRQ1(PetscObjectComm((PetscObject) dsp), PETSC_ERR_ARG_OUTOFRANGE, "Unsupported dim %D for transformation", dim);
1929         }
1930       }
1931     }
1932   } else {
1933     for (v = 0; v < Nv; ++v) {
1934       for (c = 0; c < Nc; ++c) {
1935         DMPlex_MultTransposeReal_Internal(fegeom->invJ, dim, dE, 1, &vals[(v*Nc+c)*dE], &vals[(v*Nc+c)*dE]);
1936       }
1937     }
1938   }
1939   /* Assume its a vector, otherwise assume its a bunch of scalars */
1940   if (Nc == 1 || Nc != dim) PetscFunctionReturn(0);
1941   switch (trans) {
1942     case IDENTITY_TRANSFORM: break;
1943     case COVARIANT_PIOLA_TRANSFORM: /* Covariant Piola mapping $\sigma^*(F) = J^{-T} F \circ \phi^{-1)$ */
1944     if (isInverse) {
1945       for (v = 0; v < Nv; ++v) {
1946         for (d = 0; d < dim; ++d) {
1947           switch (dim)
1948           {
1949             case 2: DMPlex_MultTranspose2DReal_Internal(fegeom->J, dim, &vals[v*Nc*dim+d], &vals[v*Nc*dim+d]);break;
1950             case 3: DMPlex_MultTranspose3DReal_Internal(fegeom->J, dim, &vals[v*Nc*dim+d], &vals[v*Nc*dim+d]);break;
1951             default: SETERRQ1(PetscObjectComm((PetscObject) dsp), PETSC_ERR_ARG_OUTOFRANGE, "Unsupported dim %D for transformation", dim);
1952           }
1953         }
1954       }
1955     } else {
1956       for (v = 0; v < Nv; ++v) {
1957         for (d = 0; d < dim; ++d) {
1958           switch (dim)
1959           {
1960             case 2: DMPlex_MultTranspose2DReal_Internal(fegeom->invJ, dim, &vals[v*Nc*dim+d], &vals[v*Nc*dim+d]);break;
1961             case 3: DMPlex_MultTranspose3DReal_Internal(fegeom->invJ, dim, &vals[v*Nc*dim+d], &vals[v*Nc*dim+d]);break;
1962             default: SETERRQ1(PetscObjectComm((PetscObject) dsp), PETSC_ERR_ARG_OUTOFRANGE, "Unsupported dim %D for transformation", dim);
1963           }
1964         }
1965       }
1966     }
1967     break;
1968     case CONTRAVARIANT_PIOLA_TRANSFORM: /* Contravariant Piola mapping $\sigma^*(F) = \frac{1}{|\det J|} J F \circ \phi^{-1}$ */
1969     if (isInverse) {
1970       for (v = 0; v < Nv; ++v) {
1971         for (d = 0; d < dim; ++d) {
1972           switch (dim)
1973           {
1974             case 2: DMPlex_Mult2DReal_Internal(fegeom->invJ, dim, &vals[v*Nc*dim+d], &vals[v*Nc*dim+d]);break;
1975             case 3: DMPlex_Mult3DReal_Internal(fegeom->invJ, dim, &vals[v*Nc*dim+d], &vals[v*Nc*dim+d]);break;
1976             default: SETERRQ1(PetscObjectComm((PetscObject) dsp), PETSC_ERR_ARG_OUTOFRANGE, "Unsupported dim %D for transformation", dim);
1977           }
1978           for (c = 0; c < Nc; ++c) vals[(v*Nc+c)*dim+d] *= fegeom->detJ[0];
1979         }
1980       }
1981     } else {
1982       for (v = 0; v < Nv; ++v) {
1983         for (d = 0; d < dim; ++d) {
1984           switch (dim)
1985           {
1986             case 2: DMPlex_Mult2DReal_Internal(fegeom->J, dim, &vals[v*Nc*dim+d], &vals[v*Nc*dim+d]);break;
1987             case 3: DMPlex_Mult3DReal_Internal(fegeom->J, dim, &vals[v*Nc*dim+d], &vals[v*Nc*dim+d]);break;
1988             default: SETERRQ1(PetscObjectComm((PetscObject) dsp), PETSC_ERR_ARG_OUTOFRANGE, "Unsupported dim %D for transformation", dim);
1989           }
1990           for (c = 0; c < Nc; ++c) vals[(v*Nc+c)*dim+d] /= fegeom->detJ[0];
1991         }
1992       }
1993     }
1994     break;
1995   }
1996   PetscFunctionReturn(0);
1997 }
1998 
1999 /*@C
2000   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.
2001 
2002   Input Parameters:
2003 + dsp        - The PetscDualSpace
2004 . fegeom     - The geometry for this cell
2005 . Nq         - The number of function samples
2006 . Nc         - The number of function components
2007 - pointEval  - The function values
2008 
2009   Output Parameter:
2010 . pointEval  - The transformed function values
2011 
2012   Level: advanced
2013 
2014   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.
2015 
2016   Note: This only handles tranformations when the embedding dimension of the geometry in fegeom is the same as the reference dimension.
2017 
2018 .seealso: PetscDualSpacePushforward(), PetscDualSpaceTransform(), PetscDualSpaceGetDeRahm()
2019 @*/
2020 PetscErrorCode PetscDualSpacePullback(PetscDualSpace dsp, PetscFEGeom *fegeom, PetscInt Nq, PetscInt Nc, PetscScalar pointEval[])
2021 {
2022   PetscDualSpaceTransformType trans;
2023   PetscInt                    k;
2024   PetscErrorCode              ierr;
2025 
2026   PetscFunctionBeginHot;
2027   PetscValidHeaderSpecific(dsp, PETSCDUALSPACE_CLASSID, 1);
2028   PetscValidPointer(fegeom, 2);
2029   PetscValidPointer(pointEval, 5);
2030   /* The dualspace dofs correspond to some simplex in the DeRahm complex, which we label by k.
2031      This determines their transformation properties. */
2032   ierr = PetscDualSpaceGetDeRahm(dsp, &k);CHKERRQ(ierr);
2033   switch (k)
2034   {
2035     case 0: /* H^1 point evaluations */
2036     trans = IDENTITY_TRANSFORM;break;
2037     case 1: /* Hcurl preserves tangential edge traces  */
2038     trans = COVARIANT_PIOLA_TRANSFORM;break;
2039     case 2:
2040     case 3: /* Hdiv preserve normal traces */
2041     trans = CONTRAVARIANT_PIOLA_TRANSFORM;break;
2042     default: SETERRQ1(PetscObjectComm((PetscObject) dsp), PETSC_ERR_ARG_OUTOFRANGE, "Unsupported simplex dim %D for transformation", k);
2043   }
2044   ierr = PetscDualSpaceTransform(dsp, trans, PETSC_TRUE, fegeom, Nq, Nc, pointEval);CHKERRQ(ierr);
2045   PetscFunctionReturn(0);
2046 }
2047 
2048 /*@C
2049   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.
2050 
2051   Input Parameters:
2052 + dsp        - The PetscDualSpace
2053 . fegeom     - The geometry for this cell
2054 . Nq         - The number of function samples
2055 . Nc         - The number of function components
2056 - pointEval  - The function values
2057 
2058   Output Parameter:
2059 . pointEval  - The transformed function values
2060 
2061   Level: advanced
2062 
2063   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.
2064 
2065   Note: This only handles tranformations when the embedding dimension of the geometry in fegeom is the same as the reference dimension.
2066 
2067 .seealso: PetscDualSpacePullback(), PetscDualSpaceTransform(), PetscDualSpaceGetDeRahm()
2068 @*/
2069 PetscErrorCode PetscDualSpacePushforward(PetscDualSpace dsp, PetscFEGeom *fegeom, PetscInt Nq, PetscInt Nc, PetscScalar pointEval[])
2070 {
2071   PetscDualSpaceTransformType trans;
2072   PetscInt                    k;
2073   PetscErrorCode              ierr;
2074 
2075   PetscFunctionBeginHot;
2076   PetscValidHeaderSpecific(dsp, PETSCDUALSPACE_CLASSID, 1);
2077   PetscValidPointer(fegeom, 2);
2078   PetscValidPointer(pointEval, 5);
2079   /* The dualspace dofs correspond to some simplex in the DeRahm complex, which we label by k.
2080      This determines their transformation properties. */
2081   ierr = PetscDualSpaceGetDeRahm(dsp, &k);CHKERRQ(ierr);
2082   switch (k)
2083   {
2084     case 0: /* H^1 point evaluations */
2085     trans = IDENTITY_TRANSFORM;break;
2086     case 1: /* Hcurl preserves tangential edge traces  */
2087     trans = COVARIANT_PIOLA_TRANSFORM;break;
2088     case 2:
2089     case 3: /* Hdiv preserve normal traces */
2090     trans = CONTRAVARIANT_PIOLA_TRANSFORM;break;
2091     default: SETERRQ1(PetscObjectComm((PetscObject) dsp), PETSC_ERR_ARG_OUTOFRANGE, "Unsupported simplex dim %D for transformation", k);
2092   }
2093   ierr = PetscDualSpaceTransform(dsp, trans, PETSC_FALSE, fegeom, Nq, Nc, pointEval);CHKERRQ(ierr);
2094   PetscFunctionReturn(0);
2095 }
2096 
2097 /*@C
2098   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.
2099 
2100   Input Parameters:
2101 + dsp        - The PetscDualSpace
2102 . fegeom     - The geometry for this cell
2103 . Nq         - The number of function gradient samples
2104 . Nc         - The number of function components
2105 - pointEval  - The function gradient values
2106 
2107   Output Parameter:
2108 . pointEval  - The transformed function gradient values
2109 
2110   Level: advanced
2111 
2112   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.
2113 
2114   Note: This only handles tranformations when the embedding dimension of the geometry in fegeom is the same as the reference dimension.
2115 
2116 .seealso: PetscDualSpacePushforward(), PPetscDualSpacePullback(), PetscDualSpaceTransform(), PetscDualSpaceGetDeRahm()
2117 @*/
2118 PetscErrorCode PetscDualSpacePushforwardGradient(PetscDualSpace dsp, PetscFEGeom *fegeom, PetscInt Nq, PetscInt Nc, PetscScalar pointEval[])
2119 {
2120   PetscDualSpaceTransformType trans;
2121   PetscInt                    k;
2122   PetscErrorCode              ierr;
2123 
2124   PetscFunctionBeginHot;
2125   PetscValidHeaderSpecific(dsp, PETSCDUALSPACE_CLASSID, 1);
2126   PetscValidPointer(fegeom, 2);
2127   PetscValidPointer(pointEval, 5);
2128   /* The dualspace dofs correspond to some simplex in the DeRahm complex, which we label by k.
2129      This determines their transformation properties. */
2130   ierr = PetscDualSpaceGetDeRahm(dsp, &k);CHKERRQ(ierr);
2131   switch (k)
2132   {
2133     case 0: /* H^1 point evaluations */
2134     trans = IDENTITY_TRANSFORM;break;
2135     case 1: /* Hcurl preserves tangential edge traces  */
2136     trans = COVARIANT_PIOLA_TRANSFORM;break;
2137     case 2:
2138     case 3: /* Hdiv preserve normal traces */
2139     trans = CONTRAVARIANT_PIOLA_TRANSFORM;break;
2140     default: SETERRQ1(PetscObjectComm((PetscObject) dsp), PETSC_ERR_ARG_OUTOFRANGE, "Unsupported simplex dim %D for transformation", k);
2141   }
2142   ierr = PetscDualSpaceTransformGradient(dsp, trans, PETSC_FALSE, fegeom, Nq, Nc, pointEval);CHKERRQ(ierr);
2143   PetscFunctionReturn(0);
2144 }
2145