xref: /libCEED/interface/ceed-operator.c (revision ddb811d62b4892d33efe74864db9a98287f88a1a)
1 // Copyright (c) 2017, Lawrence Livermore National Security, LLC. Produced at
2 // the Lawrence Livermore National Laboratory. LLNL-CODE-734707. All Rights
3 // reserved. See files LICENSE and NOTICE for details.
4 //
5 // This file is part of CEED, a collection of benchmarks, miniapps, software
6 // libraries and APIs for efficient high-order finite element and spectral
7 // element discretizations for exascale applications. For more information and
8 // source code availability see http://github.com/ceed.
9 //
10 // The CEED research is supported by the Exascale Computing Project 17-SC-20-SC,
11 // a collaborative effort of two U.S. Department of Energy organizations (Office
12 // of Science and the National Nuclear Security Administration) responsible for
13 // the planning and preparation of a capable exascale ecosystem, including
14 // software, applications, hardware, advanced system engineering and early
15 // testbed platforms, in support of the nation's exascale computing imperative.
16 
17 #include <ceed-impl.h>
18 #include <ceed-backend.h>
19 #include <string.h>
20 #include <math.h>
21 
22 /// @file
23 /// Implementation of CeedOperator interfaces
24 
25 /// ----------------------------------------------------------------------------
26 /// CeedOperator Library Internal Functions
27 /// ----------------------------------------------------------------------------
28 /// @addtogroup CeedOperatorDeveloper
29 /// @{
30 
31 /**
32   @brief Duplicate a CeedOperator with a reference Ceed to fallback for advanced
33            CeedOperator functionality
34 
35   @param op           CeedOperator to create fallback for
36 
37   @return An error code: 0 - success, otherwise - failure
38 
39   @ref Developer
40 **/
41 int CeedOperatorCreateFallback(CeedOperator op) {
42   int ierr;
43 
44   // Fallback Ceed
45   const char *resource, *fallbackresource;
46   ierr = CeedGetResource(op->ceed, &resource); CeedChk(ierr);
47   ierr = CeedGetOperatorFallbackResource(op->ceed, &fallbackresource);
48   CeedChk(ierr);
49   if (!strcmp(resource, fallbackresource))
50     // LCOV_EXCL_START
51     return CeedError(op->ceed, 1, "Backend %s cannot create an operator"
52                      "fallback to resource %s", resource, fallbackresource);
53   // LCOV_EXCL_STOP
54 
55   // Fallback Ceed
56   Ceed ceedref;
57   if (!op->ceed->opfallbackceed) {
58     ierr = CeedInit(fallbackresource, &ceedref); CeedChk(ierr);
59     ceedref->opfallbackparent = op->ceed;
60     op->ceed->opfallbackceed = ceedref;
61   }
62   ceedref = op->ceed->opfallbackceed;
63 
64   // Clone Op
65   CeedOperator opref;
66   ierr = CeedCalloc(1, &opref); CeedChk(ierr);
67   memcpy(opref, op, sizeof(*opref)); CeedChk(ierr);
68   opref->data = NULL;
69   opref->setupdone = 0;
70   opref->ceed = ceedref;
71   ierr = ceedref->OperatorCreate(opref); CeedChk(ierr);
72   op->opfallback = opref;
73 
74   // Clone QF
75   CeedQFunction qfref;
76   ierr = CeedCalloc(1, &qfref); CeedChk(ierr);
77   memcpy(qfref, (op->qf), sizeof(*qfref)); CeedChk(ierr);
78   qfref->data = NULL;
79   qfref->ceed = ceedref;
80   ierr = ceedref->QFunctionCreate(qfref); CeedChk(ierr);
81   opref->qf = qfref;
82   op->qffallback = qfref;
83 
84   return 0;
85 }
86 
87 /**
88   @brief Check if a CeedOperator is ready to be used.
89 
90   @param[in] ceed Ceed object for error handling
91   @param[in] op   CeedOperator to check
92 
93   @return An error code: 0 - success, otherwise - failure
94 
95   @ref Developer
96 **/
97 static int CeedOperatorCheckReady(Ceed ceed, CeedOperator op) {
98   CeedQFunction qf = op->qf;
99 
100   if (op->composite) {
101     if (!op->numsub)
102       // LCOV_EXCL_START
103       return CeedError(ceed, 1, "No suboperators set");
104     // LCOV_EXCL_STOP
105   } else {
106     if (op->nfields == 0)
107       // LCOV_EXCL_START
108       return CeedError(ceed, 1, "No operator fields set");
109     // LCOV_EXCL_STOP
110     if (op->nfields < qf->numinputfields + qf->numoutputfields)
111       // LCOV_EXCL_START
112       return CeedError(ceed, 1, "Not all operator fields set");
113     // LCOV_EXCL_STOP
114     if (!op->hasrestriction)
115       // LCOV_EXCL_START
116       return CeedError(ceed, 1,"At least one restriction required");
117     // LCOV_EXCL_STOP
118     if (op->numqpoints == 0)
119       // LCOV_EXCL_START
120       return CeedError(ceed, 1,"At least one non-collocated basis required");
121     // LCOV_EXCL_STOP
122   }
123 
124   return 0;
125 }
126 
127 /**
128   @brief View a field of a CeedOperator
129 
130   @param[in] field       Operator field to view
131   @param[in] qffield     QFunction field (carries field name)
132   @param[in] fieldnumber Number of field being viewed
133   @param[in] sub         true indicates sub-operator, which increases indentation; false for top-level operator
134   @param[in] in          true for an input field; false for output field
135   @param[in] stream      Stream to view to, e.g., stdout
136 
137   @return An error code: 0 - success, otherwise - failure
138 
139   @ref Utility
140 **/
141 static int CeedOperatorFieldView(CeedOperatorField field,
142                                  CeedQFunctionField qffield,
143                                  CeedInt fieldnumber, bool sub, bool in,
144                                  FILE *stream) {
145   const char *pre = sub ? "  " : "";
146   const char *inout = in ? "Input" : "Output";
147 
148   fprintf(stream, "%s    %s Field [%d]:\n"
149           "%s      Name: \"%s\"\n",
150           pre, inout, fieldnumber, pre, qffield->fieldname);
151 
152   if (field->basis == CEED_BASIS_COLLOCATED)
153     fprintf(stream, "%s      Collocated basis\n", pre);
154 
155   if (field->vec == CEED_VECTOR_ACTIVE)
156     fprintf(stream, "%s      Active vector\n", pre);
157   else if (field->vec == CEED_VECTOR_NONE)
158     fprintf(stream, "%s      No vector\n", pre);
159 
160   return 0;
161 }
162 
163 /**
164   @brief View a single CeedOperator
165 
166   @param[in] op     CeedOperator to view
167   @param[in] sub    Boolean flag for sub-operator
168   @param[in] stream Stream to write; typically stdout/stderr or a file
169 
170   @return Error code: 0 - success, otherwise - failure
171 
172   @ref Utility
173 **/
174 int CeedOperatorSingleView(CeedOperator op, bool sub, FILE *stream) {
175   int ierr;
176   const char *pre = sub ? "  " : "";
177 
178   CeedInt totalfields;
179   ierr = CeedOperatorGetNumArgs(op, &totalfields); CeedChk(ierr);
180 
181   fprintf(stream, "%s  %d Field%s\n", pre, totalfields, totalfields>1 ? "s" : "");
182 
183   fprintf(stream, "%s  %d Input Field%s:\n", pre, op->qf->numinputfields,
184           op->qf->numinputfields>1 ? "s" : "");
185   for (CeedInt i=0; i<op->qf->numinputfields; i++) {
186     ierr = CeedOperatorFieldView(op->inputfields[i], op->qf->inputfields[i],
187                                  i, sub, 1, stream); CeedChk(ierr);
188   }
189 
190   fprintf(stream, "%s  %d Output Field%s:\n", pre, op->qf->numoutputfields,
191           op->qf->numoutputfields>1 ? "s" : "");
192   for (CeedInt i=0; i<op->qf->numoutputfields; i++) {
193     ierr = CeedOperatorFieldView(op->outputfields[i], op->qf->outputfields[i],
194                                  i, sub, 0, stream); CeedChk(ierr);
195   }
196 
197   return 0;
198 }
199 
200 /**
201   @brief Find the active vector basis for a CeedOperator
202 
203   @param[in] op            CeedOperator to find active basis for
204   @param[out] activeBasis  Basis for active input vector
205 
206   @return An error code: 0 - success, otherwise - failure
207 
208   @ ref Developer
209 **/
210 static int CeedOperatorGetActiveBasis(CeedOperator op,
211                                       CeedBasis *activeBasis) {
212   *activeBasis = NULL;
213   for (int i = 0; i < op->qf->numinputfields; i++)
214     if (op->inputfields[i]->vec == CEED_VECTOR_ACTIVE) {
215       *activeBasis = op->inputfields[i]->basis;
216       break;
217     }
218 
219   if (!*activeBasis) {
220     // LCOV_EXCL_START
221     int ierr;
222     Ceed ceed;
223     ierr = CeedOperatorGetCeed(op, &ceed); CeedChk(ierr);
224     return CeedError(ceed, 1,
225                      "No active basis found for automatic multigrid setup");
226     // LCOV_EXCL_STOP
227   }
228   return 0;
229 }
230 
231 
232 /**
233   @brief Common code for creating a multigrid coarse operator and level
234            transfer operators for a CeedOperator
235 
236   @param[in] opFine       Fine grid operator
237   @param[in] PMultFine    L-vector multiplicity in parallel gather/scatter
238   @param[in] rstrCoarse   Coarse grid restriction
239   @param[in] basisCoarse  Coarse grid active vector basis
240   @param[in] basisCtoF    Basis for coarse to fine interpolation
241   @param[out] opCoarse    Coarse grid operator
242   @param[out] opProlong   Coarse to fine operator
243   @param[out] opRestrict  Fine to coarse operator
244 
245   @return An error code: 0 - success, otherwise - failure
246 
247   @ref Developer
248 **/
249 static int CeedOperatorMultigridLevel_Core(CeedOperator opFine,
250     CeedVector PMultFine, CeedElemRestriction rstrCoarse, CeedBasis basisCoarse,
251     CeedBasis basisCtoF, CeedOperator *opCoarse, CeedOperator *opProlong,
252     CeedOperator *opRestrict) {
253   int ierr;
254   Ceed ceed;
255   ierr = CeedOperatorGetCeed(opFine, &ceed); CeedChk(ierr);
256 
257   // Check for composite operator
258   bool isComposite;
259   ierr = CeedOperatorIsComposite(opFine, &isComposite); CeedChk(ierr);
260   if (isComposite)
261     // LCOV_EXCL_START
262     return CeedError(ceed, 1,
263                      "Automatic multigrid setup for composite operators not supported");
264   // LCOV_EXCL_STOP
265 
266   // Coarse Grid
267   ierr = CeedOperatorCreate(ceed, opFine->qf, opFine->dqf, opFine->dqfT,
268                             opCoarse); CeedChk(ierr);
269   CeedElemRestriction rstrFine = NULL;
270   // -- Clone input fields
271   for (int i = 0; i < opFine->qf->numinputfields; i++) {
272     if (opFine->inputfields[i]->vec == CEED_VECTOR_ACTIVE) {
273       rstrFine = opFine->inputfields[i]->Erestrict;
274       ierr = CeedOperatorSetField(*opCoarse, opFine->inputfields[i]->fieldname,
275                                   rstrCoarse, basisCoarse, CEED_VECTOR_ACTIVE);
276       CeedChk(ierr);
277     } else {
278       ierr = CeedOperatorSetField(*opCoarse, opFine->inputfields[i]->fieldname,
279                                   opFine->inputfields[i]->Erestrict,
280                                   opFine->inputfields[i]->basis,
281                                   opFine->inputfields[i]->vec); CeedChk(ierr);
282     }
283   }
284   // -- Clone output fields
285   for (int i = 0; i < opFine->qf->numoutputfields; i++) {
286     if (opFine->outputfields[i]->vec == CEED_VECTOR_ACTIVE) {
287       ierr = CeedOperatorSetField(*opCoarse, opFine->outputfields[i]->fieldname,
288                                   rstrCoarse, basisCoarse, CEED_VECTOR_ACTIVE);
289       CeedChk(ierr);
290     } else {
291       ierr = CeedOperatorSetField(*opCoarse, opFine->outputfields[i]->fieldname,
292                                   opFine->outputfields[i]->Erestrict,
293                                   opFine->outputfields[i]->basis,
294                                   opFine->outputfields[i]->vec); CeedChk(ierr);
295     }
296   }
297 
298   // Multiplicity vector
299   CeedVector multVec, multE;
300   ierr = CeedElemRestrictionCreateVector(rstrFine, &multVec, &multE);
301   CeedChk(ierr);
302   ierr = CeedVectorSetValue(multE, 0.0); CeedChk(ierr);
303   ierr = CeedElemRestrictionApply(rstrFine, CEED_NOTRANSPOSE, PMultFine, multE,
304                                   CEED_REQUEST_IMMEDIATE); CeedChk(ierr);
305   ierr = CeedVectorSetValue(multVec, 0.0); CeedChk(ierr);
306   ierr = CeedElemRestrictionApply(rstrFine, CEED_TRANSPOSE, multE, multVec,
307                                   CEED_REQUEST_IMMEDIATE); CeedChk(ierr);
308   ierr = CeedVectorDestroy(&multE); CeedChk(ierr);
309   ierr = CeedVectorReciprocal(multVec); CeedChk(ierr);
310 
311   // Restriction
312   CeedInt ncomp;
313   ierr = CeedBasisGetNumComponents(basisCoarse, &ncomp); CeedChk(ierr);
314   CeedQFunction qfRestrict;
315   ierr = CeedQFunctionCreateInteriorByName(ceed, "Scale", &qfRestrict);
316   CeedChk(ierr);
317   CeedInt *ncompRData;
318   ierr = CeedCalloc(1, &ncompRData); CeedChk(ierr);
319   ncompRData[0] = ncomp;
320   CeedQFunctionContext ctxR;
321   ierr = CeedQFunctionContextCreate(ceed, &ctxR); CeedChk(ierr);
322   ierr = CeedQFunctionContextSetData(ctxR, CEED_MEM_HOST, CEED_OWN_POINTER,
323                                      sizeof(*ncompRData), ncompRData);
324   CeedChk(ierr);
325   ierr = CeedQFunctionSetContext(qfRestrict, ctxR); CeedChk(ierr);
326   ierr = CeedQFunctionContextDestroy(&ctxR); CeedChk(ierr);
327   ierr = CeedQFunctionAddInput(qfRestrict, "input", ncomp, CEED_EVAL_NONE);
328   CeedChk(ierr);
329   ierr = CeedQFunctionAddInput(qfRestrict, "scale", ncomp, CEED_EVAL_NONE);
330   CeedChk(ierr);
331   ierr = CeedQFunctionAddOutput(qfRestrict, "output", ncomp, CEED_EVAL_INTERP);
332   CeedChk(ierr);
333 
334   ierr = CeedOperatorCreate(ceed, qfRestrict, CEED_QFUNCTION_NONE,
335                             CEED_QFUNCTION_NONE, opRestrict);
336   CeedChk(ierr);
337   ierr = CeedOperatorSetField(*opRestrict, "input", rstrFine,
338                               CEED_BASIS_COLLOCATED, CEED_VECTOR_ACTIVE);
339   CeedChk(ierr);
340   ierr = CeedOperatorSetField(*opRestrict, "scale", rstrFine,
341                               CEED_BASIS_COLLOCATED, multVec);
342   CeedChk(ierr);
343   ierr = CeedOperatorSetField(*opRestrict, "output", rstrCoarse, basisCtoF,
344                               CEED_VECTOR_ACTIVE); CeedChk(ierr);
345 
346   // Prolongation
347   CeedQFunction qfProlong;
348   ierr = CeedQFunctionCreateInteriorByName(ceed, "Scale", &qfProlong);
349   CeedChk(ierr);
350   CeedInt *ncompPData;
351   ierr = CeedCalloc(1, &ncompPData); CeedChk(ierr);
352   ncompPData[0] = ncomp;
353   CeedQFunctionContext ctxP;
354   ierr = CeedQFunctionContextCreate(ceed, &ctxP); CeedChk(ierr);
355   ierr = CeedQFunctionContextSetData(ctxP, CEED_MEM_HOST, CEED_OWN_POINTER,
356                                      sizeof(*ncompPData), ncompPData);
357   CeedChk(ierr);
358   ierr = CeedQFunctionSetContext(qfProlong, ctxP); CeedChk(ierr);
359   ierr = CeedQFunctionContextDestroy(&ctxP); CeedChk(ierr);
360   ierr = CeedQFunctionAddInput(qfProlong, "input", ncomp, CEED_EVAL_INTERP);
361   CeedChk(ierr);
362   ierr = CeedQFunctionAddInput(qfProlong, "scale", ncomp, CEED_EVAL_NONE);
363   CeedChk(ierr);
364   ierr = CeedQFunctionAddOutput(qfProlong, "output", ncomp, CEED_EVAL_NONE);
365   CeedChk(ierr);
366 
367   ierr = CeedOperatorCreate(ceed, qfProlong, CEED_QFUNCTION_NONE,
368                             CEED_QFUNCTION_NONE, opProlong);
369   CeedChk(ierr);
370   ierr = CeedOperatorSetField(*opProlong, "input", rstrCoarse, basisCtoF,
371                               CEED_VECTOR_ACTIVE); CeedChk(ierr);
372   ierr = CeedOperatorSetField(*opProlong, "scale", rstrFine,
373                               CEED_BASIS_COLLOCATED, multVec);
374   CeedChk(ierr);
375   ierr = CeedOperatorSetField(*opProlong, "output", rstrFine,
376                               CEED_BASIS_COLLOCATED, CEED_VECTOR_ACTIVE);
377   CeedChk(ierr);
378 
379   // Cleanup
380   ierr = CeedVectorDestroy(&multVec); CeedChk(ierr);
381   ierr = CeedBasisDestroy(&basisCtoF); CeedChk(ierr);
382   ierr = CeedQFunctionDestroy(&qfRestrict); CeedChk(ierr);
383   ierr = CeedQFunctionDestroy(&qfProlong); CeedChk(ierr);
384 
385   return 0;
386 }
387 
388 /// @}
389 
390 /// ----------------------------------------------------------------------------
391 /// CeedOperator Backend API
392 /// ----------------------------------------------------------------------------
393 /// @addtogroup CeedOperatorBackend
394 /// @{
395 
396 /**
397   @brief Get the Ceed associated with a CeedOperator
398 
399   @param op              CeedOperator
400   @param[out] ceed       Variable to store Ceed
401 
402   @return An error code: 0 - success, otherwise - failure
403 
404   @ref Backend
405 **/
406 
407 int CeedOperatorGetCeed(CeedOperator op, Ceed *ceed) {
408   *ceed = op->ceed;
409   return 0;
410 }
411 
412 /**
413   @brief Get the number of elements associated with a CeedOperator
414 
415   @param op              CeedOperator
416   @param[out] numelem    Variable to store number of elements
417 
418   @return An error code: 0 - success, otherwise - failure
419 
420   @ref Backend
421 **/
422 
423 int CeedOperatorGetNumElements(CeedOperator op, CeedInt *numelem) {
424   if (op->composite)
425     // LCOV_EXCL_START
426     return CeedError(op->ceed, 1, "Not defined for composite operator");
427   // LCOV_EXCL_STOP
428 
429   *numelem = op->numelements;
430   return 0;
431 }
432 
433 /**
434   @brief Get the number of quadrature points associated with a CeedOperator
435 
436   @param op              CeedOperator
437   @param[out] numqpts    Variable to store vector number of quadrature points
438 
439   @return An error code: 0 - success, otherwise - failure
440 
441   @ref Backend
442 **/
443 
444 int CeedOperatorGetNumQuadraturePoints(CeedOperator op, CeedInt *numqpts) {
445   if (op->composite)
446     // LCOV_EXCL_START
447     return CeedError(op->ceed, 1, "Not defined for composite operator");
448   // LCOV_EXCL_STOP
449 
450   *numqpts = op->numqpoints;
451   return 0;
452 }
453 
454 /**
455   @brief Get the number of arguments associated with a CeedOperator
456 
457   @param op              CeedOperator
458   @param[out] numargs    Variable to store vector number of arguments
459 
460   @return An error code: 0 - success, otherwise - failure
461 
462   @ref Backend
463 **/
464 
465 int CeedOperatorGetNumArgs(CeedOperator op, CeedInt *numargs) {
466   if (op->composite)
467     // LCOV_EXCL_START
468     return CeedError(op->ceed, 1, "Not defined for composite operators");
469   // LCOV_EXCL_STOP
470 
471   *numargs = op->nfields;
472   return 0;
473 }
474 
475 /**
476   @brief Get the setup status of a CeedOperator
477 
478   @param op                CeedOperator
479   @param[out] issetupdone  Variable to store setup status
480 
481   @return An error code: 0 - success, otherwise - failure
482 
483   @ref Backend
484 **/
485 
486 int CeedOperatorIsSetupDone(CeedOperator op, bool *issetupdone) {
487   *issetupdone = op->setupdone;
488   return 0;
489 }
490 
491 /**
492   @brief Get the QFunction associated with a CeedOperator
493 
494   @param op              CeedOperator
495   @param[out] qf         Variable to store QFunction
496 
497   @return An error code: 0 - success, otherwise - failure
498 
499   @ref Backend
500 **/
501 
502 int CeedOperatorGetQFunction(CeedOperator op, CeedQFunction *qf) {
503   if (op->composite)
504     // LCOV_EXCL_START
505     return CeedError(op->ceed, 1, "Not defined for composite operator");
506   // LCOV_EXCL_STOP
507 
508   *qf = op->qf;
509   return 0;
510 }
511 
512 /**
513   @brief Get a boolean value indicating if the CeedOperator is composite
514 
515   @param op                CeedOperator
516   @param[out] iscomposite  Variable to store composite status
517 
518   @return An error code: 0 - success, otherwise - failure
519 
520   @ref Backend
521 **/
522 
523 int CeedOperatorIsComposite(CeedOperator op, bool *iscomposite) {
524   *iscomposite = op->composite;
525   return 0;
526 }
527 
528 /**
529   @brief Get the number of suboperators associated with a CeedOperator
530 
531   @param op              CeedOperator
532   @param[out] numsub     Variable to store number of suboperators
533 
534   @return An error code: 0 - success, otherwise - failure
535 
536   @ref Backend
537 **/
538 
539 int CeedOperatorGetNumSub(CeedOperator op, CeedInt *numsub) {
540   if (!op->composite)
541     // LCOV_EXCL_START
542     return CeedError(op->ceed, 1, "Not a composite operator");
543   // LCOV_EXCL_STOP
544 
545   *numsub = op->numsub;
546   return 0;
547 }
548 
549 /**
550   @brief Get the list of suboperators associated with a CeedOperator
551 
552   @param op                CeedOperator
553   @param[out] suboperators Variable to store list of suboperators
554 
555   @return An error code: 0 - success, otherwise - failure
556 
557   @ref Backend
558 **/
559 
560 int CeedOperatorGetSubList(CeedOperator op, CeedOperator **suboperators) {
561   if (!op->composite)
562     // LCOV_EXCL_START
563     return CeedError(op->ceed, 1, "Not a composite operator");
564   // LCOV_EXCL_STOP
565 
566   *suboperators = op->suboperators;
567   return 0;
568 }
569 
570 /**
571   @brief Get the backend data of a CeedOperator
572 
573   @param op              CeedOperator
574   @param[out] data       Variable to store data
575 
576   @return An error code: 0 - success, otherwise - failure
577 
578   @ref Backend
579 **/
580 
581 int CeedOperatorGetData(CeedOperator op, void *data) {
582   *(void **)data = op->data;
583   return 0;
584 }
585 
586 /**
587   @brief Set the backend data of a CeedOperator
588 
589   @param[out] op         CeedOperator
590   @param data            Data to set
591 
592   @return An error code: 0 - success, otherwise - failure
593 
594   @ref Backend
595 **/
596 
597 int CeedOperatorSetData(CeedOperator op, void *data) {
598   op->data = data;
599   return 0;
600 }
601 
602 /**
603   @brief Set the setup flag of a CeedOperator to True
604 
605   @param op              CeedOperator
606 
607   @return An error code: 0 - success, otherwise - failure
608 
609   @ref Backend
610 **/
611 
612 int CeedOperatorSetSetupDone(CeedOperator op) {
613   op->setupdone = 1;
614   return 0;
615 }
616 
617 /**
618   @brief Get the CeedOperatorFields of a CeedOperator
619 
620   @param op                 CeedOperator
621   @param[out] inputfields   Variable to store inputfields
622   @param[out] outputfields  Variable to store outputfields
623 
624   @return An error code: 0 - success, otherwise - failure
625 
626   @ref Backend
627 **/
628 
629 int CeedOperatorGetFields(CeedOperator op, CeedOperatorField **inputfields,
630                           CeedOperatorField **outputfields) {
631   if (op->composite)
632     // LCOV_EXCL_START
633     return CeedError(op->ceed, 1, "Not defined for composite operator");
634   // LCOV_EXCL_STOP
635 
636   if (inputfields) *inputfields = op->inputfields;
637   if (outputfields) *outputfields = op->outputfields;
638   return 0;
639 }
640 
641 /**
642   @brief Get the CeedElemRestriction of a CeedOperatorField
643 
644   @param opfield         CeedOperatorField
645   @param[out] rstr       Variable to store CeedElemRestriction
646 
647   @return An error code: 0 - success, otherwise - failure
648 
649   @ref Backend
650 **/
651 
652 int CeedOperatorFieldGetElemRestriction(CeedOperatorField opfield,
653                                         CeedElemRestriction *rstr) {
654   *rstr = opfield->Erestrict;
655   return 0;
656 }
657 
658 /**
659   @brief Get the CeedBasis of a CeedOperatorField
660 
661   @param opfield         CeedOperatorField
662   @param[out] basis      Variable to store CeedBasis
663 
664   @return An error code: 0 - success, otherwise - failure
665 
666   @ref Backend
667 **/
668 
669 int CeedOperatorFieldGetBasis(CeedOperatorField opfield, CeedBasis *basis) {
670   *basis = opfield->basis;
671   return 0;
672 }
673 
674 /**
675   @brief Get the CeedVector of a CeedOperatorField
676 
677   @param opfield         CeedOperatorField
678   @param[out] vec        Variable to store CeedVector
679 
680   @return An error code: 0 - success, otherwise - failure
681 
682   @ref Backend
683 **/
684 
685 int CeedOperatorFieldGetVector(CeedOperatorField opfield, CeedVector *vec) {
686   *vec = opfield->vec;
687   return 0;
688 }
689 
690 /// @}
691 
692 /// ----------------------------------------------------------------------------
693 /// CeedOperator Public API
694 /// ----------------------------------------------------------------------------
695 /// @addtogroup CeedOperatorUser
696 /// @{
697 
698 /**
699   @brief Create a CeedOperator and associate a CeedQFunction. A CeedBasis and
700            CeedElemRestriction can be associated with CeedQFunction fields with
701            \ref CeedOperatorSetField.
702 
703   @param ceed    A Ceed object where the CeedOperator will be created
704   @param qf      QFunction defining the action of the operator at quadrature points
705   @param dqf     QFunction defining the action of the Jacobian of @a qf (or
706                    @ref CEED_QFUNCTION_NONE)
707   @param dqfT    QFunction defining the action of the transpose of the Jacobian
708                    of @a qf (or @ref CEED_QFUNCTION_NONE)
709   @param[out] op Address of the variable where the newly created
710                      CeedOperator will be stored
711 
712   @return An error code: 0 - success, otherwise - failure
713 
714   @ref User
715  */
716 int CeedOperatorCreate(Ceed ceed, CeedQFunction qf, CeedQFunction dqf,
717                        CeedQFunction dqfT, CeedOperator *op) {
718   int ierr;
719 
720   if (!ceed->OperatorCreate) {
721     Ceed delegate;
722     ierr = CeedGetObjectDelegate(ceed, &delegate, "Operator"); CeedChk(ierr);
723 
724     if (!delegate)
725       // LCOV_EXCL_START
726       return CeedError(ceed, 1, "Backend does not support OperatorCreate");
727     // LCOV_EXCL_STOP
728 
729     ierr = CeedOperatorCreate(delegate, qf, dqf, dqfT, op); CeedChk(ierr);
730     return 0;
731   }
732 
733   if (!qf || qf == CEED_QFUNCTION_NONE)
734     // LCOV_EXCL_START
735     return CeedError(ceed, 1, "Operator must have a valid QFunction.");
736   // LCOV_EXCL_STOP
737   ierr = CeedCalloc(1, op); CeedChk(ierr);
738   (*op)->ceed = ceed;
739   ceed->refcount++;
740   (*op)->refcount = 1;
741   (*op)->qf = qf;
742   qf->refcount++;
743   if (dqf && dqf != CEED_QFUNCTION_NONE) {
744     (*op)->dqf = dqf;
745     dqf->refcount++;
746   }
747   if (dqfT && dqfT != CEED_QFUNCTION_NONE) {
748     (*op)->dqfT = dqfT;
749     dqfT->refcount++;
750   }
751   ierr = CeedCalloc(16, &(*op)->inputfields); CeedChk(ierr);
752   ierr = CeedCalloc(16, &(*op)->outputfields); CeedChk(ierr);
753   ierr = ceed->OperatorCreate(*op); CeedChk(ierr);
754   return 0;
755 }
756 
757 /**
758   @brief Create an operator that composes the action of several operators
759 
760   @param ceed    A Ceed object where the CeedOperator will be created
761   @param[out] op Address of the variable where the newly created
762                      Composite CeedOperator will be stored
763 
764   @return An error code: 0 - success, otherwise - failure
765 
766   @ref User
767  */
768 int CeedCompositeOperatorCreate(Ceed ceed, CeedOperator *op) {
769   int ierr;
770 
771   if (!ceed->CompositeOperatorCreate) {
772     Ceed delegate;
773     ierr = CeedGetObjectDelegate(ceed, &delegate, "Operator"); CeedChk(ierr);
774 
775     if (delegate) {
776       ierr = CeedCompositeOperatorCreate(delegate, op); CeedChk(ierr);
777       return 0;
778     }
779   }
780 
781   ierr = CeedCalloc(1, op); CeedChk(ierr);
782   (*op)->ceed = ceed;
783   ceed->refcount++;
784   (*op)->composite = true;
785   ierr = CeedCalloc(16, &(*op)->suboperators); CeedChk(ierr);
786 
787   if (ceed->CompositeOperatorCreate) {
788     ierr = ceed->CompositeOperatorCreate(*op); CeedChk(ierr);
789   }
790   return 0;
791 }
792 
793 /**
794   @brief Provide a field to a CeedOperator for use by its CeedQFunction
795 
796   This function is used to specify both active and passive fields to a
797   CeedOperator.  For passive fields, a vector @arg v must be provided.  Passive
798   fields can inputs or outputs (updated in-place when operator is applied).
799 
800   Active fields must be specified using this function, but their data (in a
801   CeedVector) is passed in CeedOperatorApply().  There can be at most one active
802   input and at most one active output.
803 
804   @param op         CeedOperator on which to provide the field
805   @param fieldname  Name of the field (to be matched with the name used by
806                       CeedQFunction)
807   @param r          CeedElemRestriction
808   @param b          CeedBasis in which the field resides or @ref CEED_BASIS_COLLOCATED
809                       if collocated with quadrature points
810   @param v          CeedVector to be used by CeedOperator or @ref CEED_VECTOR_ACTIVE
811                       if field is active or @ref CEED_VECTOR_NONE if using
812                       @ref CEED_EVAL_WEIGHT in the QFunction
813 
814   @return An error code: 0 - success, otherwise - failure
815 
816   @ref User
817 **/
818 int CeedOperatorSetField(CeedOperator op, const char *fieldname,
819                          CeedElemRestriction r, CeedBasis b, CeedVector v) {
820   int ierr;
821   if (op->composite)
822     // LCOV_EXCL_START
823     return CeedError(op->ceed, 1, "Cannot add field to composite operator.");
824   // LCOV_EXCL_STOP
825   if (!r)
826     // LCOV_EXCL_START
827     return CeedError(op->ceed, 1,
828                      "ElemRestriction r for field \"%s\" must be non-NULL.",
829                      fieldname);
830   // LCOV_EXCL_STOP
831   if (!b)
832     // LCOV_EXCL_START
833     return CeedError(op->ceed, 1, "Basis b for field \"%s\" must be non-NULL.",
834                      fieldname);
835   // LCOV_EXCL_STOP
836   if (!v)
837     // LCOV_EXCL_START
838     return CeedError(op->ceed, 1, "Vector v for field \"%s\" must be non-NULL.",
839                      fieldname);
840   // LCOV_EXCL_STOP
841 
842   CeedInt numelements;
843   ierr = CeedElemRestrictionGetNumElements(r, &numelements); CeedChk(ierr);
844   if (r != CEED_ELEMRESTRICTION_NONE && op->hasrestriction &&
845       op->numelements != numelements)
846     // LCOV_EXCL_START
847     return CeedError(op->ceed, 1,
848                      "ElemRestriction with %d elements incompatible with prior "
849                      "%d elements", numelements, op->numelements);
850   // LCOV_EXCL_STOP
851   if (r != CEED_ELEMRESTRICTION_NONE) {
852     op->numelements = numelements;
853     op->hasrestriction = true; // Restriction set, but numelements may be 0
854   }
855 
856   if (b != CEED_BASIS_COLLOCATED) {
857     CeedInt numqpoints;
858     ierr = CeedBasisGetNumQuadraturePoints(b, &numqpoints); CeedChk(ierr);
859     if (op->numqpoints && op->numqpoints != numqpoints)
860       // LCOV_EXCL_START
861       return CeedError(op->ceed, 1, "Basis with %d quadrature points "
862                        "incompatible with prior %d points", numqpoints,
863                        op->numqpoints);
864     // LCOV_EXCL_STOP
865     op->numqpoints = numqpoints;
866   }
867   CeedQFunctionField qfield;
868   CeedOperatorField *ofield;
869   for (CeedInt i=0; i<op->qf->numinputfields; i++) {
870     if (!strcmp(fieldname, (*op->qf->inputfields[i]).fieldname)) {
871       qfield = op->qf->inputfields[i];
872       ofield = &op->inputfields[i];
873       goto found;
874     }
875   }
876   for (CeedInt i=0; i<op->qf->numoutputfields; i++) {
877     if (!strcmp(fieldname, (*op->qf->outputfields[i]).fieldname)) {
878       qfield = op->qf->inputfields[i];
879       ofield = &op->outputfields[i];
880       goto found;
881     }
882   }
883   // LCOV_EXCL_START
884   return CeedError(op->ceed, 1, "QFunction has no knowledge of field '%s'",
885                    fieldname);
886   // LCOV_EXCL_STOP
887 found:
888   if (r == CEED_ELEMRESTRICTION_NONE && qfield->emode != CEED_EVAL_WEIGHT)
889     // LCOV_EXCL_START
890     return CeedError(op->ceed, 1, "CEED_ELEMRESTRICTION_NONE can only be used "
891                      "for a field with eval mode CEED_EVAL_WEIGHT");
892   // LCOV_EXCL_STOP
893   ierr = CeedCalloc(1, ofield); CeedChk(ierr);
894   (*ofield)->Erestrict = r;
895   r->refcount += 1;
896   (*ofield)->basis = b;
897   if (b != CEED_BASIS_COLLOCATED)
898     b->refcount += 1;
899   (*ofield)->vec = v;
900   if (v != CEED_VECTOR_ACTIVE && v != CEED_VECTOR_NONE)
901     v->refcount += 1;
902   op->nfields += 1;
903 
904   size_t len = strlen(fieldname);
905   char *tmp;
906   ierr = CeedCalloc(len+1, &tmp); CeedChk(ierr);
907   memcpy(tmp, fieldname, len+1);
908   (*ofield)->fieldname = tmp;
909   return 0;
910 }
911 
912 /**
913   @brief Add a sub-operator to a composite CeedOperator
914 
915   @param[out] compositeop Composite CeedOperator
916   @param      subop       Sub-operator CeedOperator
917 
918   @return An error code: 0 - success, otherwise - failure
919 
920   @ref User
921  */
922 int CeedCompositeOperatorAddSub(CeedOperator compositeop, CeedOperator subop) {
923   if (!compositeop->composite)
924     // LCOV_EXCL_START
925     return CeedError(compositeop->ceed, 1, "CeedOperator is not a composite "
926                      "operator");
927   // LCOV_EXCL_STOP
928 
929   if (compositeop->numsub == CEED_COMPOSITE_MAX)
930     // LCOV_EXCL_START
931     return CeedError(compositeop->ceed, 1, "Cannot add additional suboperators");
932   // LCOV_EXCL_STOP
933 
934   compositeop->suboperators[compositeop->numsub] = subop;
935   subop->refcount++;
936   compositeop->numsub++;
937   return 0;
938 }
939 
940 /**
941   @brief Assemble a linear CeedQFunction associated with a CeedOperator
942 
943   This returns a CeedVector containing a matrix at each quadrature point
944     providing the action of the CeedQFunction associated with the CeedOperator.
945     The vector 'assembled' is of shape
946       [num_elements, num_input_fields, num_output_fields, num_quad_points]
947     and contains column-major matrices representing the action of the
948     CeedQFunction for a corresponding quadrature point on an element. Inputs and
949     outputs are in the order provided by the user when adding CeedOperator fields.
950     For example, a CeedQFunction with inputs 'u' and 'gradu' and outputs 'gradv' and
951     'v', provided in that order, would result in an assembled QFunction that
952     consists of (1 + dim) x (dim + 1) matrices at each quadrature point acting
953     on the input [u, du_0, du_1] and producing the output [dv_0, dv_1, v].
954 
955   @param op             CeedOperator to assemble CeedQFunction
956   @param[out] assembled CeedVector to store assembled CeedQFunction at
957                           quadrature points
958   @param[out] rstr      CeedElemRestriction for CeedVector containing assembled
959                           CeedQFunction
960   @param request        Address of CeedRequest for non-blocking completion, else
961                           @ref CEED_REQUEST_IMMEDIATE
962 
963   @return An error code: 0 - success, otherwise - failure
964 
965   @ref User
966 **/
967 int CeedOperatorLinearAssembleQFunction(CeedOperator op, CeedVector *assembled,
968                                         CeedElemRestriction *rstr,
969                                         CeedRequest *request) {
970   int ierr;
971   Ceed ceed = op->ceed;
972   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
973 
974   // Backend version
975   if (op->LinearAssembleQFunction) {
976     ierr = op->LinearAssembleQFunction(op, assembled, rstr, request);
977     CeedChk(ierr);
978   } else {
979     // Fallback to reference Ceed
980     if (!op->opfallback) {
981       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
982     }
983     // Assemble
984     ierr = op->opfallback->LinearAssembleQFunction(op->opfallback, assembled,
985            rstr, request); CeedChk(ierr);
986   }
987 
988   return 0;
989 }
990 
991 /**
992   @brief Assemble the diagonal of a square linear CeedOperator
993 
994   This overwrites a CeedVector with the diagonal of a linear CeedOperator.
995 
996   Note: Currently only non-composite CeedOperators with a single field and
997           composite CeedOperators with single field sub-operators are supported.
998 
999   @param op             CeedOperator to assemble CeedQFunction
1000   @param[out] assembled CeedVector to store assembled CeedOperator diagonal
1001   @param request        Address of CeedRequest for non-blocking completion, else
1002                           @ref CEED_REQUEST_IMMEDIATE
1003 
1004   @return An error code: 0 - success, otherwise - failure
1005 
1006   @ref User
1007 **/
1008 int CeedOperatorLinearAssembleDiagonal(CeedOperator op, CeedVector assembled,
1009                                        CeedRequest *request) {
1010   int ierr;
1011   Ceed ceed = op->ceed;
1012   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
1013 
1014   // Use backend version, if available
1015   if (op->LinearAssembleDiagonal) {
1016     ierr = op->LinearAssembleDiagonal(op, assembled, request); CeedChk(ierr);
1017   } else if (op->LinearAssembleAddDiagonal) {
1018     ierr = CeedVectorSetValue(assembled, 0.0); CeedChk(ierr);
1019     return CeedOperatorLinearAssembleAddDiagonal(op, assembled, request);
1020   } else {
1021     // Fallback to reference Ceed
1022     if (!op->opfallback) {
1023       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
1024     }
1025     // Assemble
1026     if (op->opfallback->LinearAssembleDiagonal) {
1027       ierr = op->opfallback->LinearAssembleDiagonal(op->opfallback, assembled,
1028              request); CeedChk(ierr);
1029     } else {
1030       ierr = CeedVectorSetValue(assembled, 0.0); CeedChk(ierr);
1031       return CeedOperatorLinearAssembleAddDiagonal(op, assembled, request);
1032     }
1033   }
1034 
1035   return 0;
1036 }
1037 
1038 /**
1039   @brief Assemble the diagonal of a square linear CeedOperator
1040 
1041   This sums into a CeedVector the diagonal of a linear CeedOperator.
1042 
1043   Note: Currently only non-composite CeedOperators with a single field and
1044           composite CeedOperators with single field sub-operators are supported.
1045 
1046   @param op             CeedOperator to assemble CeedQFunction
1047   @param[out] assembled CeedVector to store assembled CeedOperator diagonal
1048   @param request        Address of CeedRequest for non-blocking completion, else
1049                           @ref CEED_REQUEST_IMMEDIATE
1050 
1051   @return An error code: 0 - success, otherwise - failure
1052 
1053   @ref User
1054 **/
1055 int CeedOperatorLinearAssembleAddDiagonal(CeedOperator op, CeedVector assembled,
1056     CeedRequest *request) {
1057   int ierr;
1058   Ceed ceed = op->ceed;
1059   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
1060 
1061   // Use backend version, if available
1062   if (op->LinearAssembleAddDiagonal) {
1063     ierr = op->LinearAssembleAddDiagonal(op, assembled, request); CeedChk(ierr);
1064   } else {
1065     // Fallback to reference Ceed
1066     if (!op->opfallback) {
1067       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
1068     }
1069     // Assemble
1070     ierr = CeedVectorSetValue(assembled, 0.0); CeedChk(ierr);
1071     ierr = op->opfallback->LinearAssembleAddDiagonal(op->opfallback, assembled,
1072            request); CeedChk(ierr);
1073   }
1074 
1075   return 0;
1076 }
1077 
1078 /**
1079   @brief Assemble the point block diagonal of a square linear CeedOperator
1080 
1081   This overwrites a CeedVector with the point block diagonal of a linear
1082     CeedOperator.
1083 
1084   Note: Currently only non-composite CeedOperators with a single field and
1085           composite CeedOperators with single field sub-operators are supported.
1086 
1087   @param op             CeedOperator to assemble CeedQFunction
1088   @param[out] assembled CeedVector to store assembled CeedOperator point block
1089                           diagonal, provided in row-major form with an
1090                           @a ncomp * @a ncomp block at each node. The dimensions
1091                           of this vector are derived from the active vector
1092                           for the CeedOperator. The array has shape
1093                           [nodes, component out, component in].
1094   @param request        Address of CeedRequest for non-blocking completion, else
1095                           CEED_REQUEST_IMMEDIATE
1096 
1097   @return An error code: 0 - success, otherwise - failure
1098 
1099   @ref User
1100 **/
1101 int CeedOperatorLinearAssemblePointBlockDiagonal(CeedOperator op,
1102     CeedVector assembled, CeedRequest *request) {
1103   int ierr;
1104   Ceed ceed = op->ceed;
1105   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
1106 
1107   // Use backend version, if available
1108   if (op->LinearAssemblePointBlockDiagonal) {
1109     ierr = op->LinearAssemblePointBlockDiagonal(op, assembled, request);
1110     CeedChk(ierr);
1111   } else if (op->LinearAssembleAddPointBlockDiagonal) {
1112     ierr = CeedVectorSetValue(assembled, 0.0); CeedChk(ierr);
1113     return CeedOperatorLinearAssembleAddPointBlockDiagonal(op, assembled,
1114            request);
1115   } else {
1116     // Fallback to reference Ceed
1117     if (!op->opfallback) {
1118       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
1119     }
1120     // Assemble
1121     if (op->opfallback->LinearAssemblePointBlockDiagonal) {
1122       ierr = op->opfallback->LinearAssemblePointBlockDiagonal(op->opfallback,
1123              assembled, request); CeedChk(ierr);
1124     } else {
1125       ierr = CeedVectorSetValue(assembled, 0.0); CeedChk(ierr);
1126       return CeedOperatorLinearAssembleAddPointBlockDiagonal(op, assembled,
1127              request);
1128     }
1129   }
1130 
1131   return 0;
1132 }
1133 
1134 /**
1135   @brief Assemble the point block diagonal of a square linear CeedOperator
1136 
1137   This sums into a CeedVector with the point block diagonal of a linear
1138     CeedOperator.
1139 
1140   Note: Currently only non-composite CeedOperators with a single field and
1141           composite CeedOperators with single field sub-operators are supported.
1142 
1143   @param op             CeedOperator to assemble CeedQFunction
1144   @param[out] assembled CeedVector to store assembled CeedOperator point block
1145                           diagonal, provided in row-major form with an
1146                           @a ncomp * @a ncomp block at each node. The dimensions
1147                           of this vector are derived from the active vector
1148                           for the CeedOperator. The array has shape
1149                           [nodes, component out, component in].
1150   @param request        Address of CeedRequest for non-blocking completion, else
1151                           CEED_REQUEST_IMMEDIATE
1152 
1153   @return An error code: 0 - success, otherwise - failure
1154 
1155   @ref User
1156 **/
1157 int CeedOperatorLinearAssembleAddPointBlockDiagonal(CeedOperator op,
1158     CeedVector assembled, CeedRequest *request) {
1159   int ierr;
1160   Ceed ceed = op->ceed;
1161   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
1162 
1163   // Use backend version, if available
1164   if (op->LinearAssembleAddPointBlockDiagonal) {
1165     ierr = op->LinearAssembleAddPointBlockDiagonal(op, assembled, request);
1166     CeedChk(ierr);
1167   } else {
1168     // Fallback to reference Ceed
1169     if (!op->opfallback) {
1170       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
1171     }
1172     // Assemble
1173     ierr = op->opfallback->LinearAssembleAddPointBlockDiagonal(op->opfallback,
1174            assembled, request); CeedChk(ierr);
1175   }
1176 
1177   return 0;
1178 }
1179 
1180 /**
1181   @brief Create a multigrid coarse operator and level transfer operators
1182            for a CeedOperator, creating the prolongation basis from the
1183            fine and coarse grid interpolation
1184 
1185   @param[in] opFine       Fine grid operator
1186   @param[in] PMultFine    L-vector multiplicity in parallel gather/scatter
1187   @param[in] rstrCoarse   Coarse grid restriction
1188   @param[in] basisCoarse  Coarse grid active vector basis
1189   @param[out] opCoarse    Coarse grid operator
1190   @param[out] opProlong   Coarse to fine operator
1191   @param[out] opRestrict  Fine to coarse operator
1192 
1193   @return An error code: 0 - success, otherwise - failure
1194 
1195   @ref User
1196 **/
1197 int CeedOperatorMultigridLevelCreate(CeedOperator opFine, CeedVector PMultFine,
1198                                      CeedElemRestriction rstrCoarse, CeedBasis basisCoarse,
1199                                      CeedOperator *opCoarse, CeedOperator *opProlong, CeedOperator *opRestrict) {
1200   int ierr;
1201   Ceed ceed;
1202   ierr = CeedOperatorGetCeed(opFine, &ceed); CeedChk(ierr);
1203 
1204   // Check for compatible quadrature spaces
1205   CeedBasis basisFine;
1206   ierr = CeedOperatorGetActiveBasis(opFine, &basisFine); CeedChk(ierr);
1207   CeedInt Qf, Qc;
1208   ierr = CeedBasisGetNumQuadraturePoints(basisFine, &Qf); CeedChk(ierr);
1209   ierr = CeedBasisGetNumQuadraturePoints(basisCoarse, &Qc); CeedChk(ierr);
1210   if (Qf != Qc)
1211     // LCOV_EXCL_START
1212     return CeedError(ceed, 1, "Bases must have compatible quadrature spaces");
1213   // LCOV_EXCL_STOP
1214 
1215   // Coarse to fine basis
1216   CeedInt Pf, Pc, Q = Qf;
1217   bool isTensorF, isTensorC;
1218   ierr = CeedBasisIsTensor(basisFine, &isTensorF); CeedChk(ierr);
1219   ierr = CeedBasisIsTensor(basisCoarse, &isTensorC); CeedChk(ierr);
1220   CeedScalar *interpC, *interpF, *interpCtoF, *tau;
1221   if (isTensorF && isTensorC) {
1222     ierr = CeedBasisGetNumNodes1D(basisFine, &Pf); CeedChk(ierr);
1223     ierr = CeedBasisGetNumNodes1D(basisCoarse, &Pc); CeedChk(ierr);
1224     ierr = CeedBasisGetNumQuadraturePoints1D(basisCoarse, &Q); CeedChk(ierr);
1225   } else if (!isTensorF && !isTensorC) {
1226     ierr = CeedBasisGetNumNodes(basisFine, &Pf); CeedChk(ierr);
1227     ierr = CeedBasisGetNumNodes(basisCoarse, &Pc); CeedChk(ierr);
1228   } else {
1229     // LCOV_EXCL_START
1230     return CeedError(ceed, 1, "Bases must both be tensor or non-tensor");
1231     // LCOV_EXCL_STOP
1232   }
1233 
1234   ierr = CeedMalloc(Q*Pf, &interpF); CeedChk(ierr);
1235   ierr = CeedMalloc(Q*Pc, &interpC); CeedChk(ierr);
1236   ierr = CeedCalloc(Pc*Pf, &interpCtoF); CeedChk(ierr);
1237   ierr = CeedMalloc(Q, &tau); CeedChk(ierr);
1238   if (isTensorF) {
1239     memcpy(interpF, basisFine->interp1d, Q*Pf*sizeof basisFine->interp1d[0]);
1240     memcpy(interpC, basisCoarse->interp1d, Q*Pc*sizeof basisCoarse->interp1d[0]);
1241   } else {
1242     memcpy(interpF, basisFine->interp, Q*Pf*sizeof basisFine->interp[0]);
1243     memcpy(interpC, basisCoarse->interp, Q*Pc*sizeof basisCoarse->interp[0]);
1244   }
1245 
1246   // -- QR Factorization, interpF = Q R
1247   ierr = CeedQRFactorization(ceed, interpF, tau, Q, Pf); CeedChk(ierr);
1248 
1249   // -- Apply Qtranspose, interpC = Qtranspose interpC
1250   CeedHouseholderApplyQ(interpC, interpF, tau, CEED_TRANSPOSE,
1251                         Q, Pc, Pf, Pc, 1);
1252 
1253   // -- Apply Rinv, interpCtoF = Rinv interpC
1254   for (CeedInt j=0; j<Pc; j++) { // Column j
1255     interpCtoF[j+Pc*(Pf-1)] = interpC[j+Pc*(Pf-1)]/interpF[Pf*Pf-1];
1256     for (CeedInt i=Pf-2; i>=0; i--) { // Row i
1257       interpCtoF[j+Pc*i] = interpC[j+Pc*i];
1258       for (CeedInt k=i+1; k<Pf; k++)
1259         interpCtoF[j+Pc*i] -= interpF[k+Pf*i]*interpCtoF[j+Pc*k];
1260       interpCtoF[j+Pc*i] /= interpF[i+Pf*i];
1261     }
1262   }
1263   ierr = CeedFree(&tau); CeedChk(ierr);
1264   ierr = CeedFree(&interpC); CeedChk(ierr);
1265   ierr = CeedFree(&interpF); CeedChk(ierr);
1266 
1267   // Fallback to interpCtoF versions of code
1268   if (isTensorF) {
1269     ierr = CeedOperatorMultigridLevelCreateTensorH1(opFine, PMultFine,
1270            rstrCoarse, basisCoarse, interpCtoF, opCoarse, opProlong, opRestrict);
1271     CeedChk(ierr);
1272   } else {
1273     ierr = CeedOperatorMultigridLevelCreateH1(opFine, PMultFine,
1274            rstrCoarse, basisCoarse, interpCtoF, opCoarse, opProlong, opRestrict);
1275     CeedChk(ierr);
1276   }
1277 
1278   // Cleanup
1279   ierr = CeedFree(&interpCtoF); CeedChk(ierr);
1280   return 0;
1281 }
1282 
1283 /**
1284   @brief Create a multigrid coarse operator and level transfer operators
1285            for a CeedOperator with a tensor basis for the active basis
1286 
1287   @param[in] opFine       Fine grid operator
1288   @param[in] PMultFine    L-vector multiplicity in parallel gather/scatter
1289   @param[in] rstrCoarse   Coarse grid restriction
1290   @param[in] basisCoarse  Coarse grid active vector basis
1291   @param[in] interpCtoF   Matrix for coarse to fine interpolation
1292   @param[out] opCoarse    Coarse grid operator
1293   @param[out] opProlong   Coarse to fine operator
1294   @param[out] opRestrict  Fine to coarse operator
1295 
1296   @return An error code: 0 - success, otherwise - failure
1297 
1298   @ref User
1299 **/
1300 int CeedOperatorMultigridLevelCreateTensorH1(CeedOperator opFine,
1301     CeedVector PMultFine, CeedElemRestriction rstrCoarse, CeedBasis basisCoarse,
1302     const CeedScalar *interpCtoF, CeedOperator *opCoarse,
1303     CeedOperator *opProlong, CeedOperator *opRestrict) {
1304   int ierr;
1305   Ceed ceed;
1306   ierr = CeedOperatorGetCeed(opFine, &ceed); CeedChk(ierr);
1307 
1308   // Check for compatible quadrature spaces
1309   CeedBasis basisFine;
1310   ierr = CeedOperatorGetActiveBasis(opFine, &basisFine); CeedChk(ierr);
1311   CeedInt Qf, Qc;
1312   ierr = CeedBasisGetNumQuadraturePoints(basisFine, &Qf); CeedChk(ierr);
1313   ierr = CeedBasisGetNumQuadraturePoints(basisCoarse, &Qc); CeedChk(ierr);
1314   if (Qf != Qc)
1315     // LCOV_EXCL_START
1316     return CeedError(ceed, 1, "Bases must have compatible quadrature spaces");
1317   // LCOV_EXCL_STOP
1318 
1319   // Coarse to fine basis
1320   CeedInt dim, ncomp, nnodesCoarse, P1dFine, P1dCoarse;
1321   ierr = CeedBasisGetDimension(basisFine, &dim); CeedChk(ierr);
1322   ierr = CeedBasisGetNumComponents(basisFine, &ncomp); CeedChk(ierr);
1323   ierr = CeedBasisGetNumNodes1D(basisFine, &P1dFine); CeedChk(ierr);
1324   ierr = CeedElemRestrictionGetElementSize(rstrCoarse, &nnodesCoarse);
1325   CeedChk(ierr);
1326   P1dCoarse = dim == 1 ? nnodesCoarse :
1327               dim == 2 ? sqrt(nnodesCoarse) :
1328               cbrt(nnodesCoarse);
1329   CeedScalar *qref, *qweight, *grad;
1330   ierr = CeedCalloc(P1dFine, &qref); CeedChk(ierr);
1331   ierr = CeedCalloc(P1dFine, &qweight); CeedChk(ierr);
1332   ierr = CeedCalloc(P1dFine*P1dCoarse*dim, &grad); CeedChk(ierr);
1333   CeedBasis basisCtoF;
1334   ierr = CeedBasisCreateTensorH1(ceed, dim, ncomp, P1dCoarse, P1dFine,
1335                                  interpCtoF, grad, qref, qweight, &basisCtoF);
1336   CeedChk(ierr);
1337   ierr = CeedFree(&qref); CeedChk(ierr);
1338   ierr = CeedFree(&qweight); CeedChk(ierr);
1339   ierr = CeedFree(&grad); CeedChk(ierr);
1340 
1341   // Core code
1342   ierr = CeedOperatorMultigridLevel_Core(opFine, PMultFine, rstrCoarse,
1343                                          basisCoarse, basisCtoF, opCoarse,
1344                                          opProlong, opRestrict);
1345   CeedChk(ierr);
1346   return 0;
1347 }
1348 
1349 /**
1350   @brief Create a multigrid coarse operator and level transfer operators
1351            for a CeedOperator with a non-tensor basis for the active vector
1352 
1353   @param[in] opFine       Fine grid operator
1354   @param[in] PMultFine    L-vector multiplicity in parallel gather/scatter
1355   @param[in] rstrCoarse   Coarse grid restriction
1356   @param[in] basisCoarse  Coarse grid active vector basis
1357   @param[in] interpCtoF   Matrix for coarse to fine interpolation
1358   @param[out] opCoarse    Coarse grid operator
1359   @param[out] opProlong   Coarse to fine operator
1360   @param[out] opRestrict  Fine to coarse operator
1361 
1362   @return An error code: 0 - success, otherwise - failure
1363 
1364   @ref User
1365 **/
1366 int CeedOperatorMultigridLevelCreateH1(CeedOperator opFine,
1367                                        CeedVector PMultFine,
1368                                        CeedElemRestriction rstrCoarse,
1369                                        CeedBasis basisCoarse,
1370                                        const CeedScalar *interpCtoF,
1371                                        CeedOperator *opCoarse,
1372                                        CeedOperator *opProlong,
1373                                        CeedOperator *opRestrict) {
1374   int ierr;
1375   Ceed ceed;
1376   ierr = CeedOperatorGetCeed(opFine, &ceed); CeedChk(ierr);
1377 
1378   // Check for compatible quadrature spaces
1379   CeedBasis basisFine;
1380   ierr = CeedOperatorGetActiveBasis(opFine, &basisFine); CeedChk(ierr);
1381   CeedInt Qf, Qc;
1382   ierr = CeedBasisGetNumQuadraturePoints(basisFine, &Qf); CeedChk(ierr);
1383   ierr = CeedBasisGetNumQuadraturePoints(basisCoarse, &Qc); CeedChk(ierr);
1384   if (Qf != Qc)
1385     // LCOV_EXCL_START
1386     return CeedError(ceed, 1, "Bases must have compatible quadrature spaces");
1387   // LCOV_EXCL_STOP
1388 
1389   // Coarse to fine basis
1390   CeedElemTopology topo;
1391   ierr = CeedBasisGetTopology(basisFine, &topo); CeedChk(ierr);
1392   CeedInt dim, ncomp, nnodesCoarse, nnodesFine;
1393   ierr = CeedBasisGetDimension(basisFine, &dim); CeedChk(ierr);
1394   ierr = CeedBasisGetNumComponents(basisFine, &ncomp); CeedChk(ierr);
1395   ierr = CeedBasisGetNumNodes(basisFine, &nnodesFine); CeedChk(ierr);
1396   ierr = CeedElemRestrictionGetElementSize(rstrCoarse, &nnodesCoarse);
1397   CeedChk(ierr);
1398   CeedScalar *qref, *qweight, *grad;
1399   ierr = CeedCalloc(nnodesFine, &qref); CeedChk(ierr);
1400   ierr = CeedCalloc(nnodesFine, &qweight); CeedChk(ierr);
1401   ierr = CeedCalloc(nnodesFine*nnodesCoarse*dim, &grad); CeedChk(ierr);
1402   CeedBasis basisCtoF;
1403   ierr = CeedBasisCreateH1(ceed, topo, ncomp, nnodesCoarse, nnodesFine,
1404                            interpCtoF, grad, qref, qweight, &basisCtoF);
1405   CeedChk(ierr);
1406   ierr = CeedFree(&qref); CeedChk(ierr);
1407   ierr = CeedFree(&qweight); CeedChk(ierr);
1408   ierr = CeedFree(&grad); CeedChk(ierr);
1409 
1410   // Core code
1411   ierr = CeedOperatorMultigridLevel_Core(opFine, PMultFine, rstrCoarse,
1412                                          basisCoarse, basisCtoF, opCoarse,
1413                                          opProlong, opRestrict);
1414   CeedChk(ierr);
1415   return 0;
1416 }
1417 
1418 /**
1419   @brief Build a FDM based approximate inverse for each element for a
1420            CeedOperator
1421 
1422   This returns a CeedOperator and CeedVector to apply a Fast Diagonalization
1423     Method based approximate inverse. This function obtains the simultaneous
1424     diagonalization for the 1D mass and Laplacian operators,
1425       M = V^T V, K = V^T S V.
1426     The assembled QFunction is used to modify the eigenvalues from simultaneous
1427     diagonalization and obtain an approximate inverse of the form
1428       V^T S^hat V. The CeedOperator must be linear and non-composite. The
1429     associated CeedQFunction must therefore also be linear.
1430 
1431   @param op             CeedOperator to create element inverses
1432   @param[out] fdminv    CeedOperator to apply the action of a FDM based inverse
1433                           for each element
1434   @param request        Address of CeedRequest for non-blocking completion, else
1435                           @ref CEED_REQUEST_IMMEDIATE
1436 
1437   @return An error code: 0 - success, otherwise - failure
1438 
1439   @ref Backend
1440 **/
1441 int CeedOperatorCreateFDMElementInverse(CeedOperator op, CeedOperator *fdminv,
1442                                         CeedRequest *request) {
1443   int ierr;
1444   Ceed ceed = op->ceed;
1445   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
1446 
1447   // Use backend version, if available
1448   if (op->CreateFDMElementInverse) {
1449     ierr = op->CreateFDMElementInverse(op, fdminv, request); CeedChk(ierr);
1450   } else {
1451     // Fallback to reference Ceed
1452     if (!op->opfallback) {
1453       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
1454     }
1455     // Assemble
1456     ierr = op->opfallback->CreateFDMElementInverse(op->opfallback, fdminv,
1457            request); CeedChk(ierr);
1458   }
1459 
1460   return 0;
1461 }
1462 
1463 /**
1464   @brief View a CeedOperator
1465 
1466   @param[in] op     CeedOperator to view
1467   @param[in] stream Stream to write; typically stdout/stderr or a file
1468 
1469   @return Error code: 0 - success, otherwise - failure
1470 
1471   @ref User
1472 **/
1473 int CeedOperatorView(CeedOperator op, FILE *stream) {
1474   int ierr;
1475 
1476   if (op->composite) {
1477     fprintf(stream, "Composite CeedOperator\n");
1478 
1479     for (CeedInt i=0; i<op->numsub; i++) {
1480       fprintf(stream, "  SubOperator [%d]:\n", i);
1481       ierr = CeedOperatorSingleView(op->suboperators[i], 1, stream);
1482       CeedChk(ierr);
1483     }
1484   } else {
1485     fprintf(stream, "CeedOperator\n");
1486     ierr = CeedOperatorSingleView(op, 0, stream); CeedChk(ierr);
1487   }
1488 
1489   return 0;
1490 }
1491 
1492 /**
1493   @brief Apply CeedOperator to a vector
1494 
1495   This computes the action of the operator on the specified (active) input,
1496   yielding its (active) output.  All inputs and outputs must be specified using
1497   CeedOperatorSetField().
1498 
1499   @param op        CeedOperator to apply
1500   @param[in] in    CeedVector containing input state or @ref CEED_VECTOR_NONE if
1501                   there are no active inputs
1502   @param[out] out  CeedVector to store result of applying operator (must be
1503                      distinct from @a in) or @ref CEED_VECTOR_NONE if there are no
1504                      active outputs
1505   @param request   Address of CeedRequest for non-blocking completion, else
1506                      @ref CEED_REQUEST_IMMEDIATE
1507 
1508   @return An error code: 0 - success, otherwise - failure
1509 
1510   @ref User
1511 **/
1512 int CeedOperatorApply(CeedOperator op, CeedVector in, CeedVector out,
1513                       CeedRequest *request) {
1514   int ierr;
1515   Ceed ceed = op->ceed;
1516   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
1517 
1518   if (op->numelements)  {
1519     // Standard Operator
1520     if (op->Apply) {
1521       ierr = op->Apply(op, in, out, request); CeedChk(ierr);
1522     } else {
1523       // Zero all output vectors
1524       CeedQFunction qf = op->qf;
1525       for (CeedInt i=0; i<qf->numoutputfields; i++) {
1526         CeedVector vec = op->outputfields[i]->vec;
1527         if (vec == CEED_VECTOR_ACTIVE)
1528           vec = out;
1529         if (vec != CEED_VECTOR_NONE) {
1530           ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
1531         }
1532       }
1533       // Apply
1534       ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
1535     }
1536   } else if (op->composite) {
1537     // Composite Operator
1538     if (op->ApplyComposite) {
1539       ierr = op->ApplyComposite(op, in, out, request); CeedChk(ierr);
1540     } else {
1541       CeedInt numsub;
1542       ierr = CeedOperatorGetNumSub(op, &numsub); CeedChk(ierr);
1543       CeedOperator *suboperators;
1544       ierr = CeedOperatorGetSubList(op, &suboperators); CeedChk(ierr);
1545 
1546       // Zero all output vectors
1547       if (out != CEED_VECTOR_NONE) {
1548         ierr = CeedVectorSetValue(out, 0.0); CeedChk(ierr);
1549       }
1550       for (CeedInt i=0; i<numsub; i++) {
1551         for (CeedInt j=0; j<suboperators[i]->qf->numoutputfields; j++) {
1552           CeedVector vec = suboperators[i]->outputfields[j]->vec;
1553           if (vec != CEED_VECTOR_ACTIVE && vec != CEED_VECTOR_NONE) {
1554             ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
1555           }
1556         }
1557       }
1558       // Apply
1559       for (CeedInt i=0; i<op->numsub; i++) {
1560         ierr = CeedOperatorApplyAdd(op->suboperators[i], in, out, request);
1561         CeedChk(ierr);
1562       }
1563     }
1564   }
1565 
1566   return 0;
1567 }
1568 
1569 /**
1570   @brief Apply CeedOperator to a vector and add result to output vector
1571 
1572   This computes the action of the operator on the specified (active) input,
1573   yielding its (active) output.  All inputs and outputs must be specified using
1574   CeedOperatorSetField().
1575 
1576   @param op        CeedOperator to apply
1577   @param[in] in    CeedVector containing input state or NULL if there are no
1578                      active inputs
1579   @param[out] out  CeedVector to sum in result of applying operator (must be
1580                      distinct from @a in) or NULL if there are no active outputs
1581   @param request   Address of CeedRequest for non-blocking completion, else
1582                      @ref CEED_REQUEST_IMMEDIATE
1583 
1584   @return An error code: 0 - success, otherwise - failure
1585 
1586   @ref User
1587 **/
1588 int CeedOperatorApplyAdd(CeedOperator op, CeedVector in, CeedVector out,
1589                          CeedRequest *request) {
1590   int ierr;
1591   Ceed ceed = op->ceed;
1592   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
1593 
1594   if (op->numelements)  {
1595     // Standard Operator
1596     ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
1597   } else if (op->composite) {
1598     // Composite Operator
1599     if (op->ApplyAddComposite) {
1600       ierr = op->ApplyAddComposite(op, in, out, request); CeedChk(ierr);
1601     } else {
1602       CeedInt numsub;
1603       ierr = CeedOperatorGetNumSub(op, &numsub); CeedChk(ierr);
1604       CeedOperator *suboperators;
1605       ierr = CeedOperatorGetSubList(op, &suboperators); CeedChk(ierr);
1606 
1607       for (CeedInt i=0; i<numsub; i++) {
1608         ierr = CeedOperatorApplyAdd(suboperators[i], in, out, request);
1609         CeedChk(ierr);
1610       }
1611     }
1612   }
1613 
1614   return 0;
1615 }
1616 
1617 /**
1618   @brief Destroy a CeedOperator
1619 
1620   @param op CeedOperator to destroy
1621 
1622   @return An error code: 0 - success, otherwise - failure
1623 
1624   @ref User
1625 **/
1626 int CeedOperatorDestroy(CeedOperator *op) {
1627   int ierr;
1628 
1629   if (!*op || --(*op)->refcount > 0) return 0;
1630   if ((*op)->Destroy) {
1631     ierr = (*op)->Destroy(*op); CeedChk(ierr);
1632   }
1633   ierr = CeedDestroy(&(*op)->ceed); CeedChk(ierr);
1634   // Free fields
1635   for (int i=0; i<(*op)->nfields; i++)
1636     if ((*op)->inputfields[i]) {
1637       if ((*op)->inputfields[i]->Erestrict != CEED_ELEMRESTRICTION_NONE) {
1638         ierr = CeedElemRestrictionDestroy(&(*op)->inputfields[i]->Erestrict);
1639         CeedChk(ierr);
1640       }
1641       if ((*op)->inputfields[i]->basis != CEED_BASIS_COLLOCATED) {
1642         ierr = CeedBasisDestroy(&(*op)->inputfields[i]->basis); CeedChk(ierr);
1643       }
1644       if ((*op)->inputfields[i]->vec != CEED_VECTOR_ACTIVE &&
1645           (*op)->inputfields[i]->vec != CEED_VECTOR_NONE ) {
1646         ierr = CeedVectorDestroy(&(*op)->inputfields[i]->vec); CeedChk(ierr);
1647       }
1648       ierr = CeedFree(&(*op)->inputfields[i]->fieldname); CeedChk(ierr);
1649       ierr = CeedFree(&(*op)->inputfields[i]); CeedChk(ierr);
1650     }
1651   for (int i=0; i<(*op)->nfields; i++)
1652     if ((*op)->outputfields[i]) {
1653       ierr = CeedElemRestrictionDestroy(&(*op)->outputfields[i]->Erestrict);
1654       CeedChk(ierr);
1655       if ((*op)->outputfields[i]->basis != CEED_BASIS_COLLOCATED) {
1656         ierr = CeedBasisDestroy(&(*op)->outputfields[i]->basis); CeedChk(ierr);
1657       }
1658       if ((*op)->outputfields[i]->vec != CEED_VECTOR_ACTIVE &&
1659           (*op)->outputfields[i]->vec != CEED_VECTOR_NONE ) {
1660         ierr = CeedVectorDestroy(&(*op)->outputfields[i]->vec); CeedChk(ierr);
1661       }
1662       ierr = CeedFree(&(*op)->outputfields[i]->fieldname); CeedChk(ierr);
1663       ierr = CeedFree(&(*op)->outputfields[i]); CeedChk(ierr);
1664     }
1665   // Destroy suboperators
1666   for (int i=0; i<(*op)->numsub; i++)
1667     if ((*op)->suboperators[i]) {
1668       ierr = CeedOperatorDestroy(&(*op)->suboperators[i]); CeedChk(ierr);
1669     }
1670   ierr = CeedQFunctionDestroy(&(*op)->qf); CeedChk(ierr);
1671   ierr = CeedQFunctionDestroy(&(*op)->dqf); CeedChk(ierr);
1672   ierr = CeedQFunctionDestroy(&(*op)->dqfT); CeedChk(ierr);
1673 
1674   // Destroy fallback
1675   if ((*op)->opfallback) {
1676     ierr = (*op)->qffallback->Destroy((*op)->qffallback); CeedChk(ierr);
1677     ierr = CeedFree(&(*op)->qffallback); CeedChk(ierr);
1678     ierr = (*op)->opfallback->Destroy((*op)->opfallback); CeedChk(ierr);
1679     ierr = CeedFree(&(*op)->opfallback); CeedChk(ierr);
1680   }
1681 
1682   ierr = CeedFree(&(*op)->inputfields); CeedChk(ierr);
1683   ierr = CeedFree(&(*op)->outputfields); CeedChk(ierr);
1684   ierr = CeedFree(&(*op)->suboperators); CeedChk(ierr);
1685   ierr = CeedFree(op); CeedChk(ierr);
1686   return 0;
1687 }
1688 
1689 /// @}
1690