xref: /libCEED/interface/ceed-operator.c (revision 91dfd1cde504855b06db4052ed3242027f67af19)
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->inputfields[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 *ctxR;
318   ierr = CeedCalloc(1, &ctxR); CeedChk(ierr);
319   ctxR[0] = ncomp;
320   ierr = CeedQFunctionSetContext(qfRestrict, ctxR, sizeof(*ctxR)); CeedChk(ierr);
321   qfRestrict->ctx_allocated = qfRestrict->ctx;
322   ierr = CeedQFunctionAddInput(qfRestrict, "input", ncomp, CEED_EVAL_NONE);
323   CeedChk(ierr);
324   ierr = CeedQFunctionAddInput(qfRestrict, "scale", ncomp, CEED_EVAL_NONE);
325   CeedChk(ierr);
326   ierr = CeedQFunctionAddOutput(qfRestrict, "output", ncomp, CEED_EVAL_INTERP);
327   CeedChk(ierr);
328 
329   ierr = CeedOperatorCreate(ceed, qfRestrict, CEED_QFUNCTION_NONE,
330                             CEED_QFUNCTION_NONE, opRestrict);
331   CeedChk(ierr);
332   ierr = CeedOperatorSetField(*opRestrict, "input", rstrFine,
333                               CEED_BASIS_COLLOCATED, CEED_VECTOR_ACTIVE);
334   CeedChk(ierr);
335   ierr = CeedOperatorSetField(*opRestrict, "scale", rstrFine,
336                               CEED_BASIS_COLLOCATED, multVec);
337   CeedChk(ierr);
338   ierr = CeedOperatorSetField(*opRestrict, "output", rstrCoarse, basisCtoF,
339                               CEED_VECTOR_ACTIVE); CeedChk(ierr);
340 
341   // Prolongation
342   CeedQFunction qfProlong;
343   ierr = CeedQFunctionCreateInteriorByName(ceed, "Scale", &qfProlong);
344   CeedChk(ierr);
345   CeedInt *ctxP;
346   ierr = CeedCalloc(1, &ctxP); CeedChk(ierr);
347   ctxP[0] = ncomp;
348   ierr = CeedQFunctionSetContext(qfProlong, ctxP, sizeof(*ctxP)); CeedChk(ierr);
349   qfProlong->ctx_allocated = qfProlong->ctx;
350   ierr = CeedQFunctionAddInput(qfProlong, "input", ncomp, CEED_EVAL_INTERP);
351   CeedChk(ierr);
352   ierr = CeedQFunctionAddInput(qfProlong, "scale", ncomp, CEED_EVAL_NONE);
353   CeedChk(ierr);
354   ierr = CeedQFunctionAddOutput(qfProlong, "output", ncomp, CEED_EVAL_NONE);
355   CeedChk(ierr);
356 
357   ierr = CeedOperatorCreate(ceed, qfProlong, CEED_QFUNCTION_NONE,
358                             CEED_QFUNCTION_NONE, opProlong);
359   CeedChk(ierr);
360   ierr = CeedOperatorSetField(*opProlong, "input", rstrCoarse, basisCtoF,
361                               CEED_VECTOR_ACTIVE); CeedChk(ierr);
362   ierr = CeedOperatorSetField(*opProlong, "scale", rstrFine,
363                               CEED_BASIS_COLLOCATED, multVec);
364   CeedChk(ierr);
365   ierr = CeedOperatorSetField(*opProlong, "output", rstrFine,
366                               CEED_BASIS_COLLOCATED, CEED_VECTOR_ACTIVE);
367   CeedChk(ierr);
368 
369   // Cleanup
370   ierr = CeedVectorDestroy(&multVec); CeedChk(ierr);
371   ierr = CeedBasisDestroy(&basisCtoF); CeedChk(ierr);
372   ierr = CeedQFunctionDestroy(&qfRestrict); CeedChk(ierr);
373   ierr = CeedQFunctionDestroy(&qfProlong); CeedChk(ierr);
374 
375   return 0;
376 }
377 
378 /// @}
379 
380 /// ----------------------------------------------------------------------------
381 /// CeedOperator Backend API
382 /// ----------------------------------------------------------------------------
383 /// @addtogroup CeedOperatorBackend
384 /// @{
385 
386 /**
387   @brief Get the Ceed associated with a CeedOperator
388 
389   @param op              CeedOperator
390   @param[out] ceed       Variable to store Ceed
391 
392   @return An error code: 0 - success, otherwise - failure
393 
394   @ref Backend
395 **/
396 
397 int CeedOperatorGetCeed(CeedOperator op, Ceed *ceed) {
398   *ceed = op->ceed;
399   return 0;
400 }
401 
402 /**
403   @brief Get the number of elements associated with a CeedOperator
404 
405   @param op              CeedOperator
406   @param[out] numelem    Variable to store number of elements
407 
408   @return An error code: 0 - success, otherwise - failure
409 
410   @ref Backend
411 **/
412 
413 int CeedOperatorGetNumElements(CeedOperator op, CeedInt *numelem) {
414   if (op->composite)
415     // LCOV_EXCL_START
416     return CeedError(op->ceed, 1, "Not defined for composite operator");
417   // LCOV_EXCL_STOP
418 
419   *numelem = op->numelements;
420   return 0;
421 }
422 
423 /**
424   @brief Get the number of quadrature points associated with a CeedOperator
425 
426   @param op              CeedOperator
427   @param[out] numqpts    Variable to store vector number of quadrature points
428 
429   @return An error code: 0 - success, otherwise - failure
430 
431   @ref Backend
432 **/
433 
434 int CeedOperatorGetNumQuadraturePoints(CeedOperator op, CeedInt *numqpts) {
435   if (op->composite)
436     // LCOV_EXCL_START
437     return CeedError(op->ceed, 1, "Not defined for composite operator");
438   // LCOV_EXCL_STOP
439 
440   *numqpts = op->numqpoints;
441   return 0;
442 }
443 
444 /**
445   @brief Get the number of arguments associated with a CeedOperator
446 
447   @param op              CeedOperator
448   @param[out] numargs    Variable to store vector number of arguments
449 
450   @return An error code: 0 - success, otherwise - failure
451 
452   @ref Backend
453 **/
454 
455 int CeedOperatorGetNumArgs(CeedOperator op, CeedInt *numargs) {
456   if (op->composite)
457     // LCOV_EXCL_START
458     return CeedError(op->ceed, 1, "Not defined for composite operators");
459   // LCOV_EXCL_STOP
460 
461   *numargs = op->nfields;
462   return 0;
463 }
464 
465 /**
466   @brief Get the setup status of a CeedOperator
467 
468   @param op                CeedOperator
469   @param[out] issetupdone  Variable to store setup status
470 
471   @return An error code: 0 - success, otherwise - failure
472 
473   @ref Backend
474 **/
475 
476 int CeedOperatorIsSetupDone(CeedOperator op, bool *issetupdone) {
477   *issetupdone = op->setupdone;
478   return 0;
479 }
480 
481 /**
482   @brief Get the QFunction associated with a CeedOperator
483 
484   @param op              CeedOperator
485   @param[out] qf         Variable to store QFunction
486 
487   @return An error code: 0 - success, otherwise - failure
488 
489   @ref Backend
490 **/
491 
492 int CeedOperatorGetQFunction(CeedOperator op, CeedQFunction *qf) {
493   if (op->composite)
494     // LCOV_EXCL_START
495     return CeedError(op->ceed, 1, "Not defined for composite operator");
496   // LCOV_EXCL_STOP
497 
498   *qf = op->qf;
499   return 0;
500 }
501 
502 /**
503   @brief Get a boolean value indicating if the CeedOperator is composite
504 
505   @param op                CeedOperator
506   @param[out] iscomposite  Variable to store composite status
507 
508   @return An error code: 0 - success, otherwise - failure
509 
510   @ref Backend
511 **/
512 
513 int CeedOperatorIsComposite(CeedOperator op, bool *iscomposite) {
514   *iscomposite = op->composite;
515   return 0;
516 }
517 
518 /**
519   @brief Get the number of suboperators associated with a CeedOperator
520 
521   @param op              CeedOperator
522   @param[out] numsub     Variable to store number of suboperators
523 
524   @return An error code: 0 - success, otherwise - failure
525 
526   @ref Backend
527 **/
528 
529 int CeedOperatorGetNumSub(CeedOperator op, CeedInt *numsub) {
530   if (!op->composite)
531     // LCOV_EXCL_START
532     return CeedError(op->ceed, 1, "Not a composite operator");
533   // LCOV_EXCL_STOP
534 
535   *numsub = op->numsub;
536   return 0;
537 }
538 
539 /**
540   @brief Get the list of suboperators associated with a CeedOperator
541 
542   @param op                CeedOperator
543   @param[out] suboperators Variable to store list of suboperators
544 
545   @return An error code: 0 - success, otherwise - failure
546 
547   @ref Backend
548 **/
549 
550 int CeedOperatorGetSubList(CeedOperator op, CeedOperator **suboperators) {
551   if (!op->composite)
552     // LCOV_EXCL_START
553     return CeedError(op->ceed, 1, "Not a composite operator");
554   // LCOV_EXCL_STOP
555 
556   *suboperators = op->suboperators;
557   return 0;
558 }
559 
560 /**
561   @brief Get the backend data of a CeedOperator
562 
563   @param op              CeedOperator
564   @param[out] data       Variable to store data
565 
566   @return An error code: 0 - success, otherwise - failure
567 
568   @ref Backend
569 **/
570 
571 int CeedOperatorGetData(CeedOperator op, void **data) {
572   *data = op->data;
573   return 0;
574 }
575 
576 /**
577   @brief Set the backend data of a CeedOperator
578 
579   @param[out] op         CeedOperator
580   @param data            Data to set
581 
582   @return An error code: 0 - success, otherwise - failure
583 
584   @ref Backend
585 **/
586 
587 int CeedOperatorSetData(CeedOperator op, void **data) {
588   op->data = *data;
589   return 0;
590 }
591 
592 /**
593   @brief Set the setup flag of a CeedOperator to True
594 
595   @param op              CeedOperator
596 
597   @return An error code: 0 - success, otherwise - failure
598 
599   @ref Backend
600 **/
601 
602 int CeedOperatorSetSetupDone(CeedOperator op) {
603   op->setupdone = 1;
604   return 0;
605 }
606 
607 /**
608   @brief Get the CeedOperatorFields of a CeedOperator
609 
610   @param op                 CeedOperator
611   @param[out] inputfields   Variable to store inputfields
612   @param[out] outputfields  Variable to store outputfields
613 
614   @return An error code: 0 - success, otherwise - failure
615 
616   @ref Backend
617 **/
618 
619 int CeedOperatorGetFields(CeedOperator op, CeedOperatorField **inputfields,
620                           CeedOperatorField **outputfields) {
621   if (op->composite)
622     // LCOV_EXCL_START
623     return CeedError(op->ceed, 1, "Not defined for composite operator");
624   // LCOV_EXCL_STOP
625 
626   if (inputfields) *inputfields = op->inputfields;
627   if (outputfields) *outputfields = op->outputfields;
628   return 0;
629 }
630 
631 /**
632   @brief Get the CeedElemRestriction of a CeedOperatorField
633 
634   @param opfield         CeedOperatorField
635   @param[out] rstr       Variable to store CeedElemRestriction
636 
637   @return An error code: 0 - success, otherwise - failure
638 
639   @ref Backend
640 **/
641 
642 int CeedOperatorFieldGetElemRestriction(CeedOperatorField opfield,
643                                         CeedElemRestriction *rstr) {
644   *rstr = opfield->Erestrict;
645   return 0;
646 }
647 
648 /**
649   @brief Get the CeedBasis of a CeedOperatorField
650 
651   @param opfield         CeedOperatorField
652   @param[out] basis      Variable to store CeedBasis
653 
654   @return An error code: 0 - success, otherwise - failure
655 
656   @ref Backend
657 **/
658 
659 int CeedOperatorFieldGetBasis(CeedOperatorField opfield, CeedBasis *basis) {
660   *basis = opfield->basis;
661   return 0;
662 }
663 
664 /**
665   @brief Get the CeedVector of a CeedOperatorField
666 
667   @param opfield         CeedOperatorField
668   @param[out] vec        Variable to store CeedVector
669 
670   @return An error code: 0 - success, otherwise - failure
671 
672   @ref Backend
673 **/
674 
675 int CeedOperatorFieldGetVector(CeedOperatorField opfield, CeedVector *vec) {
676   *vec = opfield->vec;
677   return 0;
678 }
679 
680 /// @}
681 
682 /// ----------------------------------------------------------------------------
683 /// CeedOperator Public API
684 /// ----------------------------------------------------------------------------
685 /// @addtogroup CeedOperatorUser
686 /// @{
687 
688 /**
689   @brief Create a CeedOperator and associate a CeedQFunction. A CeedBasis and
690            CeedElemRestriction can be associated with CeedQFunction fields with
691            \ref CeedOperatorSetField.
692 
693   @param ceed    A Ceed object where the CeedOperator will be created
694   @param qf      QFunction defining the action of the operator at quadrature points
695   @param dqf     QFunction defining the action of the Jacobian of @a qf (or
696                    @ref CEED_QFUNCTION_NONE)
697   @param dqfT    QFunction defining the action of the transpose of the Jacobian
698                    of @a qf (or @ref CEED_QFUNCTION_NONE)
699   @param[out] op Address of the variable where the newly created
700                      CeedOperator will be stored
701 
702   @return An error code: 0 - success, otherwise - failure
703 
704   @ref User
705  */
706 int CeedOperatorCreate(Ceed ceed, CeedQFunction qf, CeedQFunction dqf,
707                        CeedQFunction dqfT, CeedOperator *op) {
708   int ierr;
709 
710   if (!ceed->OperatorCreate) {
711     Ceed delegate;
712     ierr = CeedGetObjectDelegate(ceed, &delegate, "Operator"); CeedChk(ierr);
713 
714     if (!delegate)
715       // LCOV_EXCL_START
716       return CeedError(ceed, 1, "Backend does not support OperatorCreate");
717     // LCOV_EXCL_STOP
718 
719     ierr = CeedOperatorCreate(delegate, qf, dqf, dqfT, op); CeedChk(ierr);
720     return 0;
721   }
722 
723   if (!qf || qf == CEED_QFUNCTION_NONE)
724     // LCOV_EXCL_START
725     return CeedError(ceed, 1, "Operator must have a valid QFunction.");
726   // LCOV_EXCL_STOP
727   ierr = CeedCalloc(1, op); CeedChk(ierr);
728   (*op)->ceed = ceed;
729   ceed->refcount++;
730   (*op)->refcount = 1;
731   (*op)->qf = qf;
732   qf->refcount++;
733   if (dqf && dqf != CEED_QFUNCTION_NONE) {
734     (*op)->dqf = dqf;
735     dqf->refcount++;
736   }
737   if (dqfT && dqfT != CEED_QFUNCTION_NONE) {
738     (*op)->dqfT = dqfT;
739     dqfT->refcount++;
740   }
741   ierr = CeedCalloc(16, &(*op)->inputfields); CeedChk(ierr);
742   ierr = CeedCalloc(16, &(*op)->outputfields); CeedChk(ierr);
743   ierr = ceed->OperatorCreate(*op); CeedChk(ierr);
744   return 0;
745 }
746 
747 /**
748   @brief Create an operator that composes the action of several operators
749 
750   @param ceed    A Ceed object where the CeedOperator will be created
751   @param[out] op Address of the variable where the newly created
752                      Composite CeedOperator will be stored
753 
754   @return An error code: 0 - success, otherwise - failure
755 
756   @ref User
757  */
758 int CeedCompositeOperatorCreate(Ceed ceed, CeedOperator *op) {
759   int ierr;
760 
761   if (!ceed->CompositeOperatorCreate) {
762     Ceed delegate;
763     ierr = CeedGetObjectDelegate(ceed, &delegate, "Operator"); CeedChk(ierr);
764 
765     if (delegate) {
766       ierr = CeedCompositeOperatorCreate(delegate, op); CeedChk(ierr);
767       return 0;
768     }
769   }
770 
771   ierr = CeedCalloc(1, op); CeedChk(ierr);
772   (*op)->ceed = ceed;
773   ceed->refcount++;
774   (*op)->composite = true;
775   ierr = CeedCalloc(16, &(*op)->suboperators); CeedChk(ierr);
776 
777   if (ceed->CompositeOperatorCreate) {
778     ierr = ceed->CompositeOperatorCreate(*op); CeedChk(ierr);
779   }
780   return 0;
781 }
782 
783 /**
784   @brief Provide a field to a CeedOperator for use by its CeedQFunction
785 
786   This function is used to specify both active and passive fields to a
787   CeedOperator.  For passive fields, a vector @arg v must be provided.  Passive
788   fields can inputs or outputs (updated in-place when operator is applied).
789 
790   Active fields must be specified using this function, but their data (in a
791   CeedVector) is passed in CeedOperatorApply().  There can be at most one active
792   input and at most one active output.
793 
794   @param op         CeedOperator on which to provide the field
795   @param fieldname  Name of the field (to be matched with the name used by
796                       CeedQFunction)
797   @param r          CeedElemRestriction
798   @param b          CeedBasis in which the field resides or @ref CEED_BASIS_COLLOCATED
799                       if collocated with quadrature points
800   @param v          CeedVector to be used by CeedOperator or @ref CEED_VECTOR_ACTIVE
801                       if field is active or @ref CEED_VECTOR_NONE if using
802                       @ref CEED_EVAL_WEIGHT in the QFunction
803 
804   @return An error code: 0 - success, otherwise - failure
805 
806   @ref User
807 **/
808 int CeedOperatorSetField(CeedOperator op, const char *fieldname,
809                          CeedElemRestriction r, CeedBasis b, CeedVector v) {
810   int ierr;
811   if (op->composite)
812     // LCOV_EXCL_START
813     return CeedError(op->ceed, 1, "Cannot add field to composite operator.");
814   // LCOV_EXCL_STOP
815   if (!r)
816     // LCOV_EXCL_START
817     return CeedError(op->ceed, 1,
818                      "ElemRestriction r for field \"%s\" must be non-NULL.",
819                      fieldname);
820   // LCOV_EXCL_STOP
821   if (!b)
822     // LCOV_EXCL_START
823     return CeedError(op->ceed, 1, "Basis b for field \"%s\" must be non-NULL.",
824                      fieldname);
825   // LCOV_EXCL_STOP
826   if (!v)
827     // LCOV_EXCL_START
828     return CeedError(op->ceed, 1, "Vector v for field \"%s\" must be non-NULL.",
829                      fieldname);
830   // LCOV_EXCL_STOP
831 
832   CeedInt numelements;
833   ierr = CeedElemRestrictionGetNumElements(r, &numelements); CeedChk(ierr);
834   if (r != CEED_ELEMRESTRICTION_NONE && op->hasrestriction &&
835       op->numelements != numelements)
836     // LCOV_EXCL_START
837     return CeedError(op->ceed, 1,
838                      "ElemRestriction with %d elements incompatible with prior "
839                      "%d elements", numelements, op->numelements);
840   // LCOV_EXCL_STOP
841   if (r != CEED_ELEMRESTRICTION_NONE) {
842     op->numelements = numelements;
843     op->hasrestriction = true; // Restriction set, but numelements may be 0
844   }
845 
846   if (b != CEED_BASIS_COLLOCATED) {
847     CeedInt numqpoints;
848     ierr = CeedBasisGetNumQuadraturePoints(b, &numqpoints); CeedChk(ierr);
849     if (op->numqpoints && op->numqpoints != numqpoints)
850       // LCOV_EXCL_START
851       return CeedError(op->ceed, 1, "Basis with %d quadrature points "
852                        "incompatible with prior %d points", numqpoints,
853                        op->numqpoints);
854     // LCOV_EXCL_STOP
855     op->numqpoints = numqpoints;
856   }
857   CeedQFunctionField qfield;
858   CeedOperatorField *ofield;
859   for (CeedInt i=0; i<op->qf->numinputfields; i++) {
860     if (!strcmp(fieldname, (*op->qf->inputfields[i]).fieldname)) {
861       qfield = op->qf->inputfields[i];
862       ofield = &op->inputfields[i];
863       goto found;
864     }
865   }
866   for (CeedInt i=0; i<op->qf->numoutputfields; i++) {
867     if (!strcmp(fieldname, (*op->qf->outputfields[i]).fieldname)) {
868       qfield = op->qf->inputfields[i];
869       ofield = &op->outputfields[i];
870       goto found;
871     }
872   }
873   // LCOV_EXCL_START
874   return CeedError(op->ceed, 1, "QFunction has no knowledge of field '%s'",
875                    fieldname);
876   // LCOV_EXCL_STOP
877 found:
878   if (r == CEED_ELEMRESTRICTION_NONE && qfield->emode != CEED_EVAL_WEIGHT)
879     // LCOV_EXCL_START
880     return CeedError(op->ceed, 1, "CEED_ELEMRESTRICTION_NONE can only be used "
881                      "for a field with eval mode CEED_EVAL_WEIGHT");
882   // LCOV_EXCL_STOP
883   ierr = CeedCalloc(1, ofield); CeedChk(ierr);
884   (*ofield)->Erestrict = r;
885   r->refcount += 1;
886   (*ofield)->basis = b;
887   if (b != CEED_BASIS_COLLOCATED)
888     b->refcount += 1;
889   (*ofield)->vec = v;
890   if (v != CEED_VECTOR_ACTIVE && v != CEED_VECTOR_NONE)
891     v->refcount += 1;
892   op->nfields += 1;
893 
894   size_t len = strlen(fieldname);
895   char *tmp;
896   ierr = CeedCalloc(len+1, &tmp); CeedChk(ierr);
897   memcpy(tmp, fieldname, len+1);
898   (*ofield)->fieldname = tmp;
899   return 0;
900 }
901 
902 /**
903   @brief Add a sub-operator to a composite CeedOperator
904 
905   @param[out] compositeop Composite CeedOperator
906   @param      subop       Sub-operator CeedOperator
907 
908   @return An error code: 0 - success, otherwise - failure
909 
910   @ref User
911  */
912 int CeedCompositeOperatorAddSub(CeedOperator compositeop, CeedOperator subop) {
913   if (!compositeop->composite)
914     // LCOV_EXCL_START
915     return CeedError(compositeop->ceed, 1, "CeedOperator is not a composite "
916                      "operator");
917   // LCOV_EXCL_STOP
918 
919   if (compositeop->numsub == CEED_COMPOSITE_MAX)
920     // LCOV_EXCL_START
921     return CeedError(compositeop->ceed, 1, "Cannot add additional suboperators");
922   // LCOV_EXCL_STOP
923 
924   compositeop->suboperators[compositeop->numsub] = subop;
925   subop->refcount++;
926   compositeop->numsub++;
927   return 0;
928 }
929 
930 /**
931   @brief Assemble a linear CeedQFunction associated with a CeedOperator
932 
933   This returns a CeedVector containing a matrix at each quadrature point
934     providing the action of the CeedQFunction associated with the CeedOperator.
935     The vector 'assembled' is of shape
936       [num_elements, num_input_fields, num_output_fields, num_quad_points]
937     and contains column-major matrices representing the action of the
938     CeedQFunction for a corresponding quadrature point on an element. Inputs and
939     outputs are in the order provided by the user when adding CeedOperator fields.
940     For example, a CeedQFunction with inputs 'u' and 'gradu' and outputs 'gradv' and
941     'v', provided in that order, would result in an assembled QFunction that
942     consists of (1 + dim) x (dim + 1) matrices at each quadrature point acting
943     on the input [u, du_0, du_1] and producing the output [dv_0, dv_1, v].
944 
945   @param op             CeedOperator to assemble CeedQFunction
946   @param[out] assembled CeedVector to store assembled CeedQFunction at
947                           quadrature points
948   @param[out] rstr      CeedElemRestriction for CeedVector containing assembled
949                           CeedQFunction
950   @param request        Address of CeedRequest for non-blocking completion, else
951                           @ref CEED_REQUEST_IMMEDIATE
952 
953   @return An error code: 0 - success, otherwise - failure
954 
955   @ref User
956 **/
957 int CeedOperatorLinearAssembleQFunction(CeedOperator op, CeedVector *assembled,
958                                         CeedElemRestriction *rstr,
959                                         CeedRequest *request) {
960   int ierr;
961   Ceed ceed = op->ceed;
962   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
963 
964   // Backend version
965   if (op->LinearAssembleQFunction) {
966     ierr = op->LinearAssembleQFunction(op, assembled, rstr, request);
967     CeedChk(ierr);
968   } else {
969     // Fallback to reference Ceed
970     if (!op->opfallback) {
971       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
972     }
973     // Assemble
974     ierr = op->opfallback->LinearAssembleQFunction(op->opfallback, assembled,
975            rstr, request); CeedChk(ierr);
976   }
977 
978   return 0;
979 }
980 
981 /**
982   @brief Assemble the diagonal of a square linear CeedOperator
983 
984   This overwrites a CeedVector with the diagonal of a linear CeedOperator.
985 
986   Note: Currently only non-composite CeedOperators with a single field and
987           composite CeedOperators with single field sub-operators are supported.
988 
989   @param op             CeedOperator to assemble CeedQFunction
990   @param[out] assembled CeedVector to store assembled CeedOperator diagonal
991   @param request        Address of CeedRequest for non-blocking completion, else
992                           @ref CEED_REQUEST_IMMEDIATE
993 
994   @return An error code: 0 - success, otherwise - failure
995 
996   @ref User
997 **/
998 int CeedOperatorLinearAssembleDiagonal(CeedOperator op, CeedVector assembled,
999                                        CeedRequest *request) {
1000   int ierr;
1001   Ceed ceed = op->ceed;
1002   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
1003 
1004   // Use backend version, if available
1005   if (op->LinearAssembleDiagonal) {
1006     ierr = op->LinearAssembleDiagonal(op, assembled, request); CeedChk(ierr);
1007   } else if (op->LinearAssembleAddDiagonal) {
1008     ierr = CeedVectorSetValue(assembled, 0.0); CeedChk(ierr);
1009     return CeedOperatorLinearAssembleAddDiagonal(op, assembled, request);
1010   } else {
1011     // Fallback to reference Ceed
1012     if (!op->opfallback) {
1013       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
1014     }
1015     // Assemble
1016     if (op->opfallback->LinearAssembleDiagonal) {
1017       ierr = op->opfallback->LinearAssembleDiagonal(op->opfallback, assembled,
1018              request); CeedChk(ierr);
1019     } else {
1020       ierr = CeedVectorSetValue(assembled, 0.0); CeedChk(ierr);
1021       return CeedOperatorLinearAssembleAddDiagonal(op, assembled, request);
1022     }
1023   }
1024 
1025   return 0;
1026 }
1027 
1028 /**
1029   @brief Assemble the diagonal of a square linear CeedOperator
1030 
1031   This sums into a CeedVector the diagonal of a linear CeedOperator.
1032 
1033   Note: Currently only non-composite CeedOperators with a single field and
1034           composite CeedOperators with single field sub-operators are supported.
1035 
1036   @param op             CeedOperator to assemble CeedQFunction
1037   @param[out] assembled CeedVector to store assembled CeedOperator diagonal
1038   @param request        Address of CeedRequest for non-blocking completion, else
1039                           @ref CEED_REQUEST_IMMEDIATE
1040 
1041   @return An error code: 0 - success, otherwise - failure
1042 
1043   @ref User
1044 **/
1045 int CeedOperatorLinearAssembleAddDiagonal(CeedOperator op, CeedVector assembled,
1046     CeedRequest *request) {
1047   int ierr;
1048   Ceed ceed = op->ceed;
1049   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
1050 
1051   // Use backend version, if available
1052   if (op->LinearAssembleAddDiagonal) {
1053     ierr = op->LinearAssembleAddDiagonal(op, assembled, request); CeedChk(ierr);
1054   } else {
1055     // Fallback to reference Ceed
1056     if (!op->opfallback) {
1057       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
1058     }
1059     // Assemble
1060     ierr = CeedVectorSetValue(assembled, 0.0); CeedChk(ierr);
1061     ierr = op->opfallback->LinearAssembleAddDiagonal(op->opfallback, assembled,
1062            request); CeedChk(ierr);
1063   }
1064 
1065   return 0;
1066 }
1067 
1068 /**
1069   @brief Assemble the point block diagonal of a square linear CeedOperator
1070 
1071   This overwrites a CeedVector with the point block diagonal of a linear
1072     CeedOperator.
1073 
1074   Note: Currently only non-composite CeedOperators with a single field and
1075           composite CeedOperators with single field sub-operators are supported.
1076 
1077   @param op             CeedOperator to assemble CeedQFunction
1078   @param[out] assembled CeedVector to store assembled CeedOperator point block
1079                           diagonal, provided in row-major form with an
1080                           @a ncomp * @a ncomp block at each node. The dimensions
1081                           of this vector are derived from the active vector
1082                           for the CeedOperator. The array has shape
1083                           [nodes, component out, component in].
1084   @param request        Address of CeedRequest for non-blocking completion, else
1085                           CEED_REQUEST_IMMEDIATE
1086 
1087   @return An error code: 0 - success, otherwise - failure
1088 
1089   @ref User
1090 **/
1091 int CeedOperatorLinearAssemblePointBlockDiagonal(CeedOperator op,
1092     CeedVector assembled, CeedRequest *request) {
1093   int ierr;
1094   Ceed ceed = op->ceed;
1095   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
1096 
1097   // Use backend version, if available
1098   if (op->LinearAssemblePointBlockDiagonal) {
1099     ierr = op->LinearAssemblePointBlockDiagonal(op, assembled, request);
1100     CeedChk(ierr);
1101   } else if (op->LinearAssembleAddPointBlockDiagonal) {
1102     ierr = CeedVectorSetValue(assembled, 0.0); CeedChk(ierr);
1103     return CeedOperatorLinearAssembleAddPointBlockDiagonal(op, assembled,
1104            request);
1105   } else {
1106     // Fallback to reference Ceed
1107     if (!op->opfallback) {
1108       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
1109     }
1110     // Assemble
1111     if (op->opfallback->LinearAssemblePointBlockDiagonal) {
1112       ierr = op->opfallback->LinearAssemblePointBlockDiagonal(op->opfallback,
1113              assembled, request); CeedChk(ierr);
1114     } else {
1115       ierr = CeedVectorSetValue(assembled, 0.0); CeedChk(ierr);
1116       return CeedOperatorLinearAssembleAddPointBlockDiagonal(op, assembled,
1117              request);
1118     }
1119   }
1120 
1121   return 0;
1122 }
1123 
1124 /**
1125   @brief Assemble the point block diagonal of a square linear CeedOperator
1126 
1127   This sums into a CeedVector with the point block diagonal of a linear
1128     CeedOperator.
1129 
1130   Note: Currently only non-composite CeedOperators with a single field and
1131           composite CeedOperators with single field sub-operators are supported.
1132 
1133   @param op             CeedOperator to assemble CeedQFunction
1134   @param[out] assembled CeedVector to store assembled CeedOperator point block
1135                           diagonal, provided in row-major form with an
1136                           @a ncomp * @a ncomp block at each node. The dimensions
1137                           of this vector are derived from the active vector
1138                           for the CeedOperator. The array has shape
1139                           [nodes, component out, component in].
1140   @param request        Address of CeedRequest for non-blocking completion, else
1141                           CEED_REQUEST_IMMEDIATE
1142 
1143   @return An error code: 0 - success, otherwise - failure
1144 
1145   @ref User
1146 **/
1147 int CeedOperatorLinearAssembleAddPointBlockDiagonal(CeedOperator op,
1148     CeedVector assembled, CeedRequest *request) {
1149   int ierr;
1150   Ceed ceed = op->ceed;
1151   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
1152 
1153   // Use backend version, if available
1154   if (op->LinearAssembleAddPointBlockDiagonal) {
1155     ierr = op->LinearAssembleAddPointBlockDiagonal(op, assembled, request);
1156     CeedChk(ierr);
1157   } else {
1158     // Fallback to reference Ceed
1159     if (!op->opfallback) {
1160       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
1161     }
1162     // Assemble
1163     ierr = op->opfallback->LinearAssembleAddPointBlockDiagonal(op->opfallback,
1164            assembled, request); CeedChk(ierr);
1165   }
1166 
1167   return 0;
1168 }
1169 
1170 /**
1171   @brief Create a multigrid coarse operator and level transfer operators
1172            for a CeedOperator, creating the prolongation basis from the
1173            fine and coarse grid interpolation
1174 
1175   @param[in] opFine       Fine grid operator
1176   @param[in] PMultFine    L-vector multiplicity in parallel gather/scatter
1177   @param[in] rstrCoarse   Coarse grid restriction
1178   @param[in] basisCoarse  Coarse grid active vector basis
1179   @param[out] opCoarse    Coarse grid operator
1180   @param[out] opProlong   Coarse to fine operator
1181   @param[out] opRestrict  Fine to coarse operator
1182 
1183   @return An error code: 0 - success, otherwise - failure
1184 
1185   @ref User
1186 **/
1187 int CeedOperatorMultigridLevelCreate(CeedOperator opFine, CeedVector PMultFine,
1188                                      CeedElemRestriction rstrCoarse, CeedBasis basisCoarse,
1189                                      CeedOperator *opCoarse, CeedOperator *opProlong, CeedOperator *opRestrict) {
1190   int ierr;
1191   Ceed ceed;
1192   ierr = CeedOperatorGetCeed(opFine, &ceed); CeedChk(ierr);
1193 
1194   // Check for compatible quadrature spaces
1195   CeedBasis basisFine;
1196   ierr = CeedOperatorGetActiveBasis(opFine, &basisFine); CeedChk(ierr);
1197   CeedInt Qf, Qc;
1198   ierr = CeedBasisGetNumQuadraturePoints(basisFine, &Qf); CeedChk(ierr);
1199   ierr = CeedBasisGetNumQuadraturePoints(basisCoarse, &Qc); CeedChk(ierr);
1200   if (Qf != Qc)
1201     // LCOV_EXCL_START
1202     return CeedError(ceed, 1, "Bases must have compatible quadrature spaces");
1203   // LCOV_EXCL_STOP
1204 
1205   // Coarse to fine basis
1206   CeedInt Pf, Pc, Q = Qf;
1207   bool isTensorF, isTensorC;
1208   ierr = CeedBasisIsTensor(basisFine, &isTensorF); CeedChk(ierr);
1209   ierr = CeedBasisIsTensor(basisCoarse, &isTensorC); CeedChk(ierr);
1210   CeedScalar *interpC, *interpF, *interpCtoF, *tau;
1211   if (isTensorF && isTensorC) {
1212     ierr = CeedBasisGetNumNodes1D(basisFine, &Pf); CeedChk(ierr);
1213     ierr = CeedBasisGetNumNodes1D(basisCoarse, &Pc); CeedChk(ierr);
1214     ierr = CeedBasisGetNumQuadraturePoints1D(basisCoarse, &Q); CeedChk(ierr);
1215   } else if (!isTensorF && !isTensorC) {
1216     ierr = CeedBasisGetNumNodes(basisFine, &Pf); CeedChk(ierr);
1217     ierr = CeedBasisGetNumNodes(basisCoarse, &Pc); CeedChk(ierr);
1218   } else {
1219     // LCOV_EXCL_START
1220     return CeedError(ceed, 1, "Bases must both be tensor or non-tensor");
1221     // LCOV_EXCL_STOP
1222   }
1223 
1224   ierr = CeedMalloc(Q*Pf, &interpF); CeedChk(ierr);
1225   ierr = CeedMalloc(Q*Pc, &interpC); CeedChk(ierr);
1226   ierr = CeedCalloc(Pc*Pf, &interpCtoF); CeedChk(ierr);
1227   ierr = CeedMalloc(Q, &tau); CeedChk(ierr);
1228   if (isTensorF) {
1229     memcpy(interpF, basisFine->interp1d, Q*Pf*sizeof basisFine->interp1d[0]);
1230     memcpy(interpC, basisCoarse->interp1d, Q*Pc*sizeof basisCoarse->interp1d[0]);
1231   } else {
1232     memcpy(interpF, basisFine->interp, Q*Pf*sizeof basisFine->interp[0]);
1233     memcpy(interpC, basisCoarse->interp, Q*Pc*sizeof basisCoarse->interp[0]);
1234   }
1235 
1236   // -- QR Factorization, interpF = Q R
1237   ierr = CeedQRFactorization(ceed, interpF, tau, Q, Pf); CeedChk(ierr);
1238 
1239   // -- Apply Qtranspose, interpC = Qtranspose interpC
1240   CeedHouseholderApplyQ(interpC, interpF, tau, CEED_TRANSPOSE,
1241                         Q, Pc, Pf, Pc, 1);
1242 
1243   // -- Apply Rinv, interpCtoF = Rinv interpC
1244   for (CeedInt j=0; j<Pc; j++) { // Column j
1245     interpCtoF[j+Pc*(Pf-1)] = interpC[j+Pc*(Pf-1)]/interpF[Pf*Pf-1];
1246     for (CeedInt i=Pf-2; i>=0; i--) { // Row i
1247       interpCtoF[j+Pc*i] = interpC[j+Pc*i];
1248       for (CeedInt k=i+1; k<Pf; k++)
1249         interpCtoF[j+Pc*i] -= interpF[k+Pf*i]*interpCtoF[j+Pc*k];
1250       interpCtoF[j+Pc*i] /= interpF[i+Pf*i];
1251     }
1252   }
1253   ierr = CeedFree(&tau); CeedChk(ierr);
1254   ierr = CeedFree(&interpC); CeedChk(ierr);
1255   ierr = CeedFree(&interpF); CeedChk(ierr);
1256 
1257   // Fallback to interpCtoF versions of code
1258   if (isTensorF) {
1259     ierr = CeedOperatorMultigridLevelCreateTensorH1(opFine, PMultFine,
1260            rstrCoarse, basisCoarse, interpCtoF, opCoarse, opProlong, opRestrict);
1261     CeedChk(ierr);
1262   } else {
1263     ierr = CeedOperatorMultigridLevelCreateH1(opFine, PMultFine,
1264            rstrCoarse, basisCoarse, interpCtoF, opCoarse, opProlong, opRestrict);
1265     CeedChk(ierr);
1266   }
1267 
1268   // Cleanup
1269   ierr = CeedFree(&interpCtoF); CeedChk(ierr);
1270   return 0;
1271 }
1272 
1273 /**
1274   @brief Create a multigrid coarse operator and level transfer operators
1275            for a CeedOperator with a tensor basis for the active basis
1276 
1277   @param[in] opFine       Fine grid operator
1278   @param[in] PMultFine    L-vector multiplicity in parallel gather/scatter
1279   @param[in] rstrCoarse   Coarse grid restriction
1280   @param[in] basisCoarse  Coarse grid active vector basis
1281   @param[in] interpCtoF   Matrix for coarse to fine interpolation
1282   @param[out] opCoarse    Coarse grid operator
1283   @param[out] opProlong   Coarse to fine operator
1284   @param[out] opRestrict  Fine to coarse operator
1285 
1286   @return An error code: 0 - success, otherwise - failure
1287 
1288   @ref User
1289 **/
1290 int CeedOperatorMultigridLevelCreateTensorH1(CeedOperator opFine,
1291     CeedVector PMultFine, CeedElemRestriction rstrCoarse, CeedBasis basisCoarse,
1292     const CeedScalar *interpCtoF, CeedOperator *opCoarse,
1293     CeedOperator *opProlong, CeedOperator *opRestrict) {
1294   int ierr;
1295   Ceed ceed;
1296   ierr = CeedOperatorGetCeed(opFine, &ceed); CeedChk(ierr);
1297 
1298   // Check for compatible quadrature spaces
1299   CeedBasis basisFine;
1300   ierr = CeedOperatorGetActiveBasis(opFine, &basisFine); CeedChk(ierr);
1301   CeedInt Qf, Qc;
1302   ierr = CeedBasisGetNumQuadraturePoints(basisFine, &Qf); CeedChk(ierr);
1303   ierr = CeedBasisGetNumQuadraturePoints(basisCoarse, &Qc); CeedChk(ierr);
1304   if (Qf != Qc)
1305     // LCOV_EXCL_START
1306     return CeedError(ceed, 1, "Bases must have compatible quadrature spaces");
1307   // LCOV_EXCL_STOP
1308 
1309   // Coarse to fine basis
1310   CeedInt dim, ncomp, nnodesCoarse, P1dFine, P1dCoarse;
1311   ierr = CeedBasisGetDimension(basisFine, &dim); CeedChk(ierr);
1312   ierr = CeedBasisGetNumComponents(basisFine, &ncomp); CeedChk(ierr);
1313   ierr = CeedBasisGetNumNodes1D(basisFine, &P1dFine); CeedChk(ierr);
1314   ierr = CeedElemRestrictionGetElementSize(rstrCoarse, &nnodesCoarse);
1315   CeedChk(ierr);
1316   P1dCoarse = dim == 1 ? nnodesCoarse :
1317               dim == 2 ? sqrt(nnodesCoarse) :
1318               cbrt(nnodesCoarse);
1319   CeedScalar *qref, *qweight, *grad;
1320   ierr = CeedCalloc(P1dFine, &qref); CeedChk(ierr);
1321   ierr = CeedCalloc(P1dFine, &qweight); CeedChk(ierr);
1322   ierr = CeedCalloc(P1dFine*P1dCoarse*dim, &grad); CeedChk(ierr);
1323   CeedBasis basisCtoF;
1324   ierr = CeedBasisCreateTensorH1(ceed, dim, ncomp, P1dCoarse, P1dFine,
1325                                  interpCtoF, grad, qref, qweight, &basisCtoF);
1326   CeedChk(ierr);
1327   ierr = CeedFree(&qref); CeedChk(ierr);
1328   ierr = CeedFree(&qweight); CeedChk(ierr);
1329   ierr = CeedFree(&grad); CeedChk(ierr);
1330 
1331   // Core code
1332   ierr = CeedOperatorMultigridLevel_Core(opFine, PMultFine, rstrCoarse,
1333                                          basisCoarse, basisCtoF, opCoarse,
1334                                          opProlong, opRestrict);
1335   CeedChk(ierr);
1336   return 0;
1337 }
1338 
1339 /**
1340   @brief Create a multigrid coarse operator and level transfer operators
1341            for a CeedOperator with a non-tensor basis for the active vector
1342 
1343   @param[in] opFine       Fine grid operator
1344   @param[in] PMultFine    L-vector multiplicity in parallel gather/scatter
1345   @param[in] rstrCoarse   Coarse grid restriction
1346   @param[in] basisCoarse  Coarse grid active vector basis
1347   @param[in] interpCtoF   Matrix for coarse to fine interpolation
1348   @param[out] opCoarse    Coarse grid operator
1349   @param[out] opProlong   Coarse to fine operator
1350   @param[out] opRestrict  Fine to coarse operator
1351 
1352   @return An error code: 0 - success, otherwise - failure
1353 
1354   @ref User
1355 **/
1356 int CeedOperatorMultigridLevelCreateH1(CeedOperator opFine,
1357                                        CeedVector PMultFine,
1358                                        CeedElemRestriction rstrCoarse,
1359                                        CeedBasis basisCoarse,
1360                                        const CeedScalar *interpCtoF,
1361                                        CeedOperator *opCoarse,
1362                                        CeedOperator *opProlong,
1363                                        CeedOperator *opRestrict) {
1364   int ierr;
1365   Ceed ceed;
1366   ierr = CeedOperatorGetCeed(opFine, &ceed); CeedChk(ierr);
1367 
1368   // Check for compatible quadrature spaces
1369   CeedBasis basisFine;
1370   ierr = CeedOperatorGetActiveBasis(opFine, &basisFine); CeedChk(ierr);
1371   CeedInt Qf, Qc;
1372   ierr = CeedBasisGetNumQuadraturePoints(basisFine, &Qf); CeedChk(ierr);
1373   ierr = CeedBasisGetNumQuadraturePoints(basisCoarse, &Qc); CeedChk(ierr);
1374   if (Qf != Qc)
1375     // LCOV_EXCL_START
1376     return CeedError(ceed, 1, "Bases must have compatible quadrature spaces");
1377   // LCOV_EXCL_STOP
1378 
1379   // Coarse to fine basis
1380   CeedElemTopology topo;
1381   ierr = CeedBasisGetTopology(basisFine, &topo); CeedChk(ierr);
1382   CeedInt dim, ncomp, nnodesCoarse, nnodesFine;
1383   ierr = CeedBasisGetDimension(basisFine, &dim); CeedChk(ierr);
1384   ierr = CeedBasisGetNumComponents(basisFine, &ncomp); CeedChk(ierr);
1385   ierr = CeedBasisGetNumNodes(basisFine, &nnodesFine); CeedChk(ierr);
1386   ierr = CeedElemRestrictionGetElementSize(rstrCoarse, &nnodesCoarse);
1387   CeedChk(ierr);
1388   CeedScalar *qref, *qweight, *grad;
1389   ierr = CeedCalloc(nnodesFine, &qref); CeedChk(ierr);
1390   ierr = CeedCalloc(nnodesFine, &qweight); CeedChk(ierr);
1391   ierr = CeedCalloc(nnodesFine*nnodesCoarse*dim, &grad); CeedChk(ierr);
1392   CeedBasis basisCtoF;
1393   ierr = CeedBasisCreateH1(ceed, topo, ncomp, nnodesCoarse, nnodesFine,
1394                            interpCtoF, grad, qref, qweight, &basisCtoF);
1395   CeedChk(ierr);
1396   ierr = CeedFree(&qref); CeedChk(ierr);
1397   ierr = CeedFree(&qweight); CeedChk(ierr);
1398   ierr = CeedFree(&grad); CeedChk(ierr);
1399 
1400   // Core code
1401   ierr = CeedOperatorMultigridLevel_Core(opFine, PMultFine, rstrCoarse,
1402                                          basisCoarse, basisCtoF, opCoarse,
1403                                          opProlong, opRestrict);
1404   CeedChk(ierr);
1405   return 0;
1406 }
1407 
1408 /**
1409   @brief Build a FDM based approximate inverse for each element for a
1410            CeedOperator
1411 
1412   This returns a CeedOperator and CeedVector to apply a Fast Diagonalization
1413     Method based approximate inverse. This function obtains the simultaneous
1414     diagonalization for the 1D mass and Laplacian operators,
1415       M = V^T V, K = V^T S V.
1416     The assembled QFunction is used to modify the eigenvalues from simultaneous
1417     diagonalization and obtain an approximate inverse of the form
1418       V^T S^hat V. The CeedOperator must be linear and non-composite. The
1419     associated CeedQFunction must therefore also be linear.
1420 
1421   @param op             CeedOperator to create element inverses
1422   @param[out] fdminv    CeedOperator to apply the action of a FDM based inverse
1423                           for each element
1424   @param request        Address of CeedRequest for non-blocking completion, else
1425                           @ref CEED_REQUEST_IMMEDIATE
1426 
1427   @return An error code: 0 - success, otherwise - failure
1428 
1429   @ref Backend
1430 **/
1431 int CeedOperatorCreateFDMElementInverse(CeedOperator op, CeedOperator *fdminv,
1432                                         CeedRequest *request) {
1433   int ierr;
1434   Ceed ceed = op->ceed;
1435   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
1436 
1437   // Use backend version, if available
1438   if (op->CreateFDMElementInverse) {
1439     ierr = op->CreateFDMElementInverse(op, fdminv, request); CeedChk(ierr);
1440   } else {
1441     // Fallback to reference Ceed
1442     if (!op->opfallback) {
1443       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
1444     }
1445     // Assemble
1446     ierr = op->opfallback->CreateFDMElementInverse(op->opfallback, fdminv,
1447            request); CeedChk(ierr);
1448   }
1449 
1450   return 0;
1451 }
1452 
1453 /**
1454   @brief View a CeedOperator
1455 
1456   @param[in] op     CeedOperator to view
1457   @param[in] stream Stream to write; typically stdout/stderr or a file
1458 
1459   @return Error code: 0 - success, otherwise - failure
1460 
1461   @ref User
1462 **/
1463 int CeedOperatorView(CeedOperator op, FILE *stream) {
1464   int ierr;
1465 
1466   if (op->composite) {
1467     fprintf(stream, "Composite CeedOperator\n");
1468 
1469     for (CeedInt i=0; i<op->numsub; i++) {
1470       fprintf(stream, "  SubOperator [%d]:\n", i);
1471       ierr = CeedOperatorSingleView(op->suboperators[i], 1, stream);
1472       CeedChk(ierr);
1473     }
1474   } else {
1475     fprintf(stream, "CeedOperator\n");
1476     ierr = CeedOperatorSingleView(op, 0, stream); CeedChk(ierr);
1477   }
1478 
1479   return 0;
1480 }
1481 
1482 /**
1483   @brief Apply CeedOperator to a vector
1484 
1485   This computes the action of the operator on the specified (active) input,
1486   yielding its (active) output.  All inputs and outputs must be specified using
1487   CeedOperatorSetField().
1488 
1489   @param op        CeedOperator to apply
1490   @param[in] in    CeedVector containing input state or @ref CEED_VECTOR_NONE if
1491                   there are no active inputs
1492   @param[out] out  CeedVector to store result of applying operator (must be
1493                      distinct from @a in) or @ref CEED_VECTOR_NONE if there are no
1494                      active outputs
1495   @param request   Address of CeedRequest for non-blocking completion, else
1496                      @ref CEED_REQUEST_IMMEDIATE
1497 
1498   @return An error code: 0 - success, otherwise - failure
1499 
1500   @ref User
1501 **/
1502 int CeedOperatorApply(CeedOperator op, CeedVector in, CeedVector out,
1503                       CeedRequest *request) {
1504   int ierr;
1505   Ceed ceed = op->ceed;
1506   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
1507 
1508   if (op->numelements)  {
1509     // Standard Operator
1510     if (op->Apply) {
1511       ierr = op->Apply(op, in, out, request); CeedChk(ierr);
1512     } else {
1513       // Zero all output vectors
1514       CeedQFunction qf = op->qf;
1515       for (CeedInt i=0; i<qf->numoutputfields; i++) {
1516         CeedVector vec = op->outputfields[i]->vec;
1517         if (vec == CEED_VECTOR_ACTIVE)
1518           vec = out;
1519         if (vec != CEED_VECTOR_NONE) {
1520           ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
1521         }
1522       }
1523       // Apply
1524       ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
1525     }
1526   } else if (op->composite) {
1527     // Composite Operator
1528     if (op->ApplyComposite) {
1529       ierr = op->ApplyComposite(op, in, out, request); CeedChk(ierr);
1530     } else {
1531       CeedInt numsub;
1532       ierr = CeedOperatorGetNumSub(op, &numsub); CeedChk(ierr);
1533       CeedOperator *suboperators;
1534       ierr = CeedOperatorGetSubList(op, &suboperators); CeedChk(ierr);
1535 
1536       // Zero all output vectors
1537       if (out != CEED_VECTOR_NONE) {
1538         ierr = CeedVectorSetValue(out, 0.0); CeedChk(ierr);
1539       }
1540       for (CeedInt i=0; i<numsub; i++) {
1541         for (CeedInt j=0; j<suboperators[i]->qf->numoutputfields; j++) {
1542           CeedVector vec = suboperators[i]->outputfields[j]->vec;
1543           if (vec != CEED_VECTOR_ACTIVE && vec != CEED_VECTOR_NONE) {
1544             ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
1545           }
1546         }
1547       }
1548       // Apply
1549       for (CeedInt i=0; i<op->numsub; i++) {
1550         ierr = CeedOperatorApplyAdd(op->suboperators[i], in, out, request);
1551         CeedChk(ierr);
1552       }
1553     }
1554   }
1555 
1556   return 0;
1557 }
1558 
1559 /**
1560   @brief Apply CeedOperator to a vector and add result to output vector
1561 
1562   This computes the action of the operator on the specified (active) input,
1563   yielding its (active) output.  All inputs and outputs must be specified using
1564   CeedOperatorSetField().
1565 
1566   @param op        CeedOperator to apply
1567   @param[in] in    CeedVector containing input state or NULL if there are no
1568                      active inputs
1569   @param[out] out  CeedVector to sum in result of applying operator (must be
1570                      distinct from @a in) or NULL if there are no active outputs
1571   @param request   Address of CeedRequest for non-blocking completion, else
1572                      @ref CEED_REQUEST_IMMEDIATE
1573 
1574   @return An error code: 0 - success, otherwise - failure
1575 
1576   @ref User
1577 **/
1578 int CeedOperatorApplyAdd(CeedOperator op, CeedVector in, CeedVector out,
1579                          CeedRequest *request) {
1580   int ierr;
1581   Ceed ceed = op->ceed;
1582   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
1583 
1584   if (op->numelements)  {
1585     // Standard Operator
1586     ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
1587   } else if (op->composite) {
1588     // Composite Operator
1589     if (op->ApplyAddComposite) {
1590       ierr = op->ApplyAddComposite(op, in, out, request); CeedChk(ierr);
1591     } else {
1592       CeedInt numsub;
1593       ierr = CeedOperatorGetNumSub(op, &numsub); CeedChk(ierr);
1594       CeedOperator *suboperators;
1595       ierr = CeedOperatorGetSubList(op, &suboperators); CeedChk(ierr);
1596 
1597       for (CeedInt i=0; i<numsub; i++) {
1598         ierr = CeedOperatorApplyAdd(suboperators[i], in, out, request);
1599         CeedChk(ierr);
1600       }
1601     }
1602   }
1603 
1604   return 0;
1605 }
1606 
1607 /**
1608   @brief Destroy a CeedOperator
1609 
1610   @param op CeedOperator to destroy
1611 
1612   @return An error code: 0 - success, otherwise - failure
1613 
1614   @ref User
1615 **/
1616 int CeedOperatorDestroy(CeedOperator *op) {
1617   int ierr;
1618 
1619   if (!*op || --(*op)->refcount > 0) return 0;
1620   if ((*op)->Destroy) {
1621     ierr = (*op)->Destroy(*op); CeedChk(ierr);
1622   }
1623   ierr = CeedDestroy(&(*op)->ceed); CeedChk(ierr);
1624   // Free fields
1625   for (int i=0; i<(*op)->nfields; i++)
1626     if ((*op)->inputfields[i]) {
1627       if ((*op)->inputfields[i]->Erestrict != CEED_ELEMRESTRICTION_NONE) {
1628         ierr = CeedElemRestrictionDestroy(&(*op)->inputfields[i]->Erestrict);
1629         CeedChk(ierr);
1630       }
1631       if ((*op)->inputfields[i]->basis != CEED_BASIS_COLLOCATED) {
1632         ierr = CeedBasisDestroy(&(*op)->inputfields[i]->basis); CeedChk(ierr);
1633       }
1634       if ((*op)->inputfields[i]->vec != CEED_VECTOR_ACTIVE &&
1635           (*op)->inputfields[i]->vec != CEED_VECTOR_NONE ) {
1636         ierr = CeedVectorDestroy(&(*op)->inputfields[i]->vec); CeedChk(ierr);
1637       }
1638       ierr = CeedFree(&(*op)->inputfields[i]->fieldname); CeedChk(ierr);
1639       ierr = CeedFree(&(*op)->inputfields[i]); CeedChk(ierr);
1640     }
1641   for (int i=0; i<(*op)->nfields; i++)
1642     if ((*op)->outputfields[i]) {
1643       ierr = CeedElemRestrictionDestroy(&(*op)->outputfields[i]->Erestrict);
1644       CeedChk(ierr);
1645       if ((*op)->outputfields[i]->basis != CEED_BASIS_COLLOCATED) {
1646         ierr = CeedBasisDestroy(&(*op)->outputfields[i]->basis); CeedChk(ierr);
1647       }
1648       if ((*op)->outputfields[i]->vec != CEED_VECTOR_ACTIVE &&
1649           (*op)->outputfields[i]->vec != CEED_VECTOR_NONE ) {
1650         ierr = CeedVectorDestroy(&(*op)->outputfields[i]->vec); CeedChk(ierr);
1651       }
1652       ierr = CeedFree(&(*op)->outputfields[i]->fieldname); CeedChk(ierr);
1653       ierr = CeedFree(&(*op)->outputfields[i]); CeedChk(ierr);
1654     }
1655   // Destroy suboperators
1656   for (int i=0; i<(*op)->numsub; i++)
1657     if ((*op)->suboperators[i]) {
1658       ierr = CeedOperatorDestroy(&(*op)->suboperators[i]); CeedChk(ierr);
1659     }
1660   ierr = CeedQFunctionDestroy(&(*op)->qf); CeedChk(ierr);
1661   ierr = CeedQFunctionDestroy(&(*op)->dqf); CeedChk(ierr);
1662   ierr = CeedQFunctionDestroy(&(*op)->dqfT); CeedChk(ierr);
1663 
1664   // Destroy fallback
1665   if ((*op)->opfallback) {
1666     ierr = (*op)->qffallback->Destroy((*op)->qffallback); CeedChk(ierr);
1667     ierr = CeedFree(&(*op)->qffallback); CeedChk(ierr);
1668     ierr = (*op)->opfallback->Destroy((*op)->opfallback); CeedChk(ierr);
1669     ierr = CeedFree(&(*op)->opfallback); CeedChk(ierr);
1670   }
1671 
1672   ierr = CeedFree(&(*op)->inputfields); CeedChk(ierr);
1673   ierr = CeedFree(&(*op)->outputfields); CeedChk(ierr);
1674   ierr = CeedFree(&(*op)->suboperators); CeedChk(ierr);
1675   ierr = CeedFree(op); CeedChk(ierr);
1676   return 0;
1677 }
1678 
1679 /// @}
1680