xref: /libCEED/interface/ceed-operator.c (revision 7a982d89c751e293e39d23a7c44a161cef1fcd06)
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] fieldnumber Number of field being viewed
132   @param[in] stream      Stream to view to, e.g., stdout
133 
134   @return An error code: 0 - success, otherwise - failure
135 
136   @ref Utility
137 **/
138 static int CeedOperatorFieldView(CeedOperatorField field,
139                                  CeedQFunctionField qffield,
140                                  CeedInt fieldnumber, bool sub, bool in,
141                                  FILE *stream) {
142   const char *pre = sub ? "  " : "";
143   const char *inout = in ? "Input" : "Output";
144 
145   fprintf(stream, "%s    %s Field [%d]:\n"
146           "%s      Name: \"%s\"\n",
147           pre, inout, fieldnumber, pre, qffield->fieldname);
148 
149   if (field->basis == CEED_BASIS_COLLOCATED)
150     fprintf(stream, "%s      Collocated basis\n", pre);
151 
152   if (field->vec == CEED_VECTOR_ACTIVE)
153     fprintf(stream, "%s      Active vector\n", pre);
154   else if (field->vec == CEED_VECTOR_NONE)
155     fprintf(stream, "%s      No vector\n", pre);
156 
157   return 0;
158 }
159 
160 /**
161   @brief View a single CeedOperator
162 
163   @param[in] op     CeedOperator to view
164   @param[in] sub    Boolean flag for sub-operator
165   @param[in] stream Stream to write; typically stdout/stderr or a file
166 
167   @return Error code: 0 - success, otherwise - failure
168 
169   @ref Utility
170 **/
171 int CeedOperatorSingleView(CeedOperator op, bool sub, FILE *stream) {
172   int ierr;
173   const char *pre = sub ? "  " : "";
174 
175   CeedInt totalfields;
176   ierr = CeedOperatorGetNumArgs(op, &totalfields); CeedChk(ierr);
177 
178   fprintf(stream, "%s  %d Field%s\n", pre, totalfields, totalfields>1 ? "s" : "");
179 
180   fprintf(stream, "%s  %d Input Field%s:\n", pre, op->qf->numinputfields,
181           op->qf->numinputfields>1 ? "s" : "");
182   for (CeedInt i=0; i<op->qf->numinputfields; i++) {
183     ierr = CeedOperatorFieldView(op->inputfields[i], op->qf->inputfields[i],
184                                  i, sub, 1, stream); CeedChk(ierr);
185   }
186 
187   fprintf(stream, "%s  %d Output Field%s:\n", pre, op->qf->numoutputfields,
188           op->qf->numoutputfields>1 ? "s" : "");
189   for (CeedInt i=0; i<op->qf->numoutputfields; i++) {
190     ierr = CeedOperatorFieldView(op->outputfields[i], op->qf->inputfields[i],
191                                  i, sub, 0, stream); CeedChk(ierr);
192   }
193 
194   return 0;
195 }
196 
197 /// @}
198 
199 /// ----------------------------------------------------------------------------
200 /// CeedOperator Backend API
201 /// ----------------------------------------------------------------------------
202 /// @addtogroup CeedOperatorBackend
203 /// @{
204 
205 /**
206   @brief Get the Ceed associated with a CeedOperator
207 
208   @param op              CeedOperator
209   @param[out] ceed       Variable to store Ceed
210 
211   @return An error code: 0 - success, otherwise - failure
212 
213   @ref Backend
214 **/
215 
216 int CeedOperatorGetCeed(CeedOperator op, Ceed *ceed) {
217   *ceed = op->ceed;
218   return 0;
219 }
220 
221 /**
222   @brief Get the number of elements associated with a CeedOperator
223 
224   @param op              CeedOperator
225   @param[out] numelem    Variable to store number of elements
226 
227   @return An error code: 0 - success, otherwise - failure
228 
229   @ref Backend
230 **/
231 
232 int CeedOperatorGetNumElements(CeedOperator op, CeedInt *numelem) {
233   if (op->composite)
234     // LCOV_EXCL_START
235     return CeedError(op->ceed, 1, "Not defined for composite operator");
236   // LCOV_EXCL_STOP
237 
238   *numelem = op->numelements;
239   return 0;
240 }
241 
242 /**
243   @brief Get the number of quadrature points associated with a CeedOperator
244 
245   @param op              CeedOperator
246   @param[out] numqpts    Variable to store vector number of quadrature points
247 
248   @return An error code: 0 - success, otherwise - failure
249 
250   @ref Backend
251 **/
252 
253 int CeedOperatorGetNumQuadraturePoints(CeedOperator op, CeedInt *numqpts) {
254   if (op->composite)
255     // LCOV_EXCL_START
256     return CeedError(op->ceed, 1, "Not defined for composite operator");
257   // LCOV_EXCL_STOP
258 
259   *numqpts = op->numqpoints;
260   return 0;
261 }
262 
263 /**
264   @brief Get the number of arguments associated with a CeedOperator
265 
266   @param op              CeedOperator
267   @param[out] numargs    Variable to store vector number of arguments
268 
269   @return An error code: 0 - success, otherwise - failure
270 
271   @ref Backend
272 **/
273 
274 int CeedOperatorGetNumArgs(CeedOperator op, CeedInt *numargs) {
275   if (op->composite)
276     // LCOV_EXCL_START
277     return CeedError(op->ceed, 1, "Not defined for composite operators");
278   // LCOV_EXCL_STOP
279 
280   *numargs = op->nfields;
281   return 0;
282 }
283 
284 /**
285   @brief Get the setup status of a CeedOperator
286 
287   @param op             CeedOperator
288   @param[out] setupdone Variable to store setup status
289 
290   @return An error code: 0 - success, otherwise - failure
291 
292   @ref Backend
293 **/
294 
295 int CeedOperatorGetSetupStatus(CeedOperator op, bool *setupdone) {
296   *setupdone = op->setupdone;
297   return 0;
298 }
299 
300 /**
301   @brief Get the QFunction associated with a CeedOperator
302 
303   @param op              CeedOperator
304   @param[out] qf         Variable to store QFunction
305 
306   @return An error code: 0 - success, otherwise - failure
307 
308   @ref Backend
309 **/
310 
311 int CeedOperatorGetQFunction(CeedOperator op, CeedQFunction *qf) {
312   if (op->composite)
313     // LCOV_EXCL_START
314     return CeedError(op->ceed, 1, "Not defined for composite operator");
315   // LCOV_EXCL_STOP
316 
317   *qf = op->qf;
318   return 0;
319 }
320 
321 /**
322   @brief Get the number of suboperators associated with a CeedOperator
323 
324   @param op              CeedOperator
325   @param[out] numsub     Variable to store number of suboperators
326 
327   @return An error code: 0 - success, otherwise - failure
328 
329   @ref Backend
330 **/
331 
332 int CeedOperatorGetNumSub(CeedOperator op, CeedInt *numsub) {
333   if (!op->composite)
334     // LCOV_EXCL_START
335     return CeedError(op->ceed, 1, "Not a composite operator");
336   // LCOV_EXCL_STOP
337 
338   *numsub = op->numsub;
339   return 0;
340 }
341 
342 /**
343   @brief Get the list of suboperators associated with a CeedOperator
344 
345   @param op                CeedOperator
346   @param[out] suboperators Variable to store list of suboperators
347 
348   @return An error code: 0 - success, otherwise - failure
349 
350   @ref Backend
351 **/
352 
353 int CeedOperatorGetSubList(CeedOperator op, CeedOperator **suboperators) {
354   if (!op->composite)
355     // LCOV_EXCL_START
356     return CeedError(op->ceed, 1, "Not a composite operator");
357   // LCOV_EXCL_STOP
358 
359   *suboperators = op->suboperators;
360   return 0;
361 }
362 
363 /**
364   @brief Get the backend data of a CeedOperator
365 
366   @param op              CeedOperator
367   @param[out] data       Variable to store data
368 
369   @return An error code: 0 - success, otherwise - failure
370 
371   @ref Backend
372 **/
373 
374 int CeedOperatorGetData(CeedOperator op, void **data) {
375   *data = op->data;
376   return 0;
377 }
378 
379 /**
380   @brief Set the backend data of a CeedOperator
381 
382   @param[out] op         CeedOperator
383   @param data            Data to set
384 
385   @return An error code: 0 - success, otherwise - failure
386 
387   @ref Backend
388 **/
389 
390 int CeedOperatorSetData(CeedOperator op, void **data) {
391   op->data = *data;
392   return 0;
393 }
394 
395 /**
396   @brief Set the setup flag of a CeedOperator to True
397 
398   @param op              CeedOperator
399 
400   @return An error code: 0 - success, otherwise - failure
401 
402   @ref Backend
403 **/
404 
405 int CeedOperatorSetSetupDone(CeedOperator op) {
406   op->setupdone = 1;
407   return 0;
408 }
409 
410 /**
411   @brief Get the CeedOperatorFields of a CeedOperator
412 
413   @param op                 CeedOperator
414   @param[out] inputfields   Variable to store inputfields
415   @param[out] outputfields  Variable to store outputfields
416 
417   @return An error code: 0 - success, otherwise - failure
418 
419   @ref Backend
420 **/
421 
422 int CeedOperatorGetFields(CeedOperator op, CeedOperatorField **inputfields,
423                           CeedOperatorField **outputfields) {
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   if (inputfields) *inputfields = op->inputfields;
430   if (outputfields) *outputfields = op->outputfields;
431   return 0;
432 }
433 
434 /**
435   @brief Get the CeedElemRestriction of a CeedOperatorField
436 
437   @param opfield         CeedOperatorField
438   @param[out] rstr       Variable to store CeedElemRestriction
439 
440   @return An error code: 0 - success, otherwise - failure
441 
442   @ref Backend
443 **/
444 
445 int CeedOperatorFieldGetElemRestriction(CeedOperatorField opfield,
446                                         CeedElemRestriction *rstr) {
447   *rstr = opfield->Erestrict;
448   return 0;
449 }
450 
451 /**
452   @brief Get the CeedBasis of a CeedOperatorField
453 
454   @param opfield         CeedOperatorField
455   @param[out] basis      Variable to store CeedBasis
456 
457   @return An error code: 0 - success, otherwise - failure
458 
459   @ref Backend
460 **/
461 
462 int CeedOperatorFieldGetBasis(CeedOperatorField opfield, CeedBasis *basis) {
463   *basis = opfield->basis;
464   return 0;
465 }
466 
467 /**
468   @brief Get the CeedVector of a CeedOperatorField
469 
470   @param opfield         CeedOperatorField
471   @param[out] vec        Variable to store CeedVector
472 
473   @return An error code: 0 - success, otherwise - failure
474 
475   @ref Backend
476 **/
477 
478 int CeedOperatorFieldGetVector(CeedOperatorField opfield, CeedVector *vec) {
479   *vec = opfield->vec;
480   return 0;
481 }
482 
483 /// @}
484 
485 /// ----------------------------------------------------------------------------
486 /// CeedOperator Public API
487 /// ----------------------------------------------------------------------------
488 /// @addtogroup CeedOperatorUser
489 /// @{
490 
491 /**
492   @brief Create a CeedOperator and associate a CeedQFunction. A CeedBasis and
493            CeedElemRestriction can be associated with CeedQFunction fields with
494            \ref CeedOperatorSetField.
495 
496   @param ceed    A Ceed object where the CeedOperator will be created
497   @param qf      QFunction defining the action of the operator at quadrature points
498   @param dqf     QFunction defining the action of the Jacobian of @a qf (or
499                    CEED_QFUNCTION_NONE)
500   @param dqfT    QFunction defining the action of the transpose of the Jacobian
501                    of @a qf (or CEED_QFUNCTION_NONE)
502   @param[out] op Address of the variable where the newly created
503                      CeedOperator will be stored
504 
505   @return An error code: 0 - success, otherwise - failure
506 
507   @ref User
508  */
509 int CeedOperatorCreate(Ceed ceed, CeedQFunction qf, CeedQFunction dqf,
510                        CeedQFunction dqfT, CeedOperator *op) {
511   int ierr;
512 
513   if (!ceed->OperatorCreate) {
514     Ceed delegate;
515     ierr = CeedGetObjectDelegate(ceed, &delegate, "Operator"); CeedChk(ierr);
516 
517     if (!delegate)
518       // LCOV_EXCL_START
519       return CeedError(ceed, 1, "Backend does not support OperatorCreate");
520     // LCOV_EXCL_STOP
521 
522     ierr = CeedOperatorCreate(delegate, qf, dqf, dqfT, op); CeedChk(ierr);
523     return 0;
524   }
525 
526   ierr = CeedCalloc(1, op); CeedChk(ierr);
527   (*op)->ceed = ceed;
528   ceed->refcount++;
529   (*op)->refcount = 1;
530   if (qf == CEED_QFUNCTION_NONE)
531     // LCOV_EXCL_START
532     return CeedError(ceed, 1, "Operator must have a valid QFunction.");
533   // LCOV_EXCL_STOP
534   (*op)->qf = qf;
535   qf->refcount++;
536   if (dqf && dqf != CEED_QFUNCTION_NONE) {
537     (*op)->dqf = dqf;
538     dqf->refcount++;
539   }
540   if (dqfT && dqfT != CEED_QFUNCTION_NONE) {
541     (*op)->dqfT = dqfT;
542     dqfT->refcount++;
543   }
544   ierr = CeedCalloc(16, &(*op)->inputfields); CeedChk(ierr);
545   ierr = CeedCalloc(16, &(*op)->outputfields); CeedChk(ierr);
546   ierr = ceed->OperatorCreate(*op); CeedChk(ierr);
547   return 0;
548 }
549 
550 /**
551   @brief Create an operator that composes the action of several operators
552 
553   @param ceed    A Ceed object where the CeedOperator will be created
554   @param[out] op Address of the variable where the newly created
555                      Composite CeedOperator will be stored
556 
557   @return An error code: 0 - success, otherwise - failure
558 
559   @ref User
560  */
561 int CeedCompositeOperatorCreate(Ceed ceed, CeedOperator *op) {
562   int ierr;
563 
564   if (!ceed->CompositeOperatorCreate) {
565     Ceed delegate;
566     ierr = CeedGetObjectDelegate(ceed, &delegate, "Operator"); CeedChk(ierr);
567 
568     if (delegate) {
569       ierr = CeedCompositeOperatorCreate(delegate, op); CeedChk(ierr);
570       return 0;
571     }
572   }
573 
574   ierr = CeedCalloc(1, op); CeedChk(ierr);
575   (*op)->ceed = ceed;
576   ceed->refcount++;
577   (*op)->composite = true;
578   ierr = CeedCalloc(16, &(*op)->suboperators); CeedChk(ierr);
579 
580   if (ceed->CompositeOperatorCreate) {
581     ierr = ceed->CompositeOperatorCreate(*op); CeedChk(ierr);
582   }
583   return 0;
584 }
585 
586 /**
587   @brief Provide a field to a CeedOperator for use by its CeedQFunction
588 
589   This function is used to specify both active and passive fields to a
590   CeedOperator.  For passive fields, a vector @arg v must be provided.  Passive
591   fields can inputs or outputs (updated in-place when operator is applied).
592 
593   Active fields must be specified using this function, but their data (in a
594   CeedVector) is passed in CeedOperatorApply().  There can be at most one active
595   input and at most one active output.
596 
597   @param op         CeedOperator on which to provide the field
598   @param fieldname  Name of the field (to be matched with the name used by
599                       CeedQFunction)
600   @param r          CeedElemRestriction
601   @param b          CeedBasis in which the field resides or CEED_BASIS_COLLOCATED
602                       if collocated with quadrature points
603   @param v          CeedVector to be used by CeedOperator or CEED_VECTOR_ACTIVE
604                       if field is active or CEED_VECTOR_NONE if using
605                       CEED_EVAL_WEIGHT in the QFunction
606 
607   @return An error code: 0 - success, otherwise - failure
608 
609   @ref User
610 **/
611 int CeedOperatorSetField(CeedOperator op, const char *fieldname,
612                          CeedElemRestriction r, CeedBasis b, CeedVector v) {
613   int ierr;
614   if (op->composite)
615     // LCOV_EXCL_START
616     return CeedError(op->ceed, 1, "Cannot add field to composite operator.");
617   // LCOV_EXCL_STOP
618   if (!r)
619     // LCOV_EXCL_START
620     return CeedError(op->ceed, 1,
621                      "ElemRestriction r for field \"%s\" must be non-NULL.",
622                      fieldname);
623   // LCOV_EXCL_STOP
624   if (!b)
625     // LCOV_EXCL_START
626     return CeedError(op->ceed, 1, "Basis b for field \"%s\" must be non-NULL.",
627                      fieldname);
628   // LCOV_EXCL_STOP
629   if (!v)
630     // LCOV_EXCL_START
631     return CeedError(op->ceed, 1, "Vector v for field \"%s\" must be non-NULL.",
632                      fieldname);
633   // LCOV_EXCL_STOP
634 
635   CeedInt numelements;
636   ierr = CeedElemRestrictionGetNumElements(r, &numelements); CeedChk(ierr);
637   if (r != CEED_ELEMRESTRICTION_NONE && op->hasrestriction &&
638       op->numelements != numelements)
639     // LCOV_EXCL_START
640     return CeedError(op->ceed, 1,
641                      "ElemRestriction with %d elements incompatible with prior "
642                      "%d elements", numelements, op->numelements);
643   // LCOV_EXCL_STOP
644   if (r != CEED_ELEMRESTRICTION_NONE) {
645     op->numelements = numelements;
646     op->hasrestriction = true; // Restriction set, but numelements may be 0
647   }
648 
649   if (b != CEED_BASIS_COLLOCATED) {
650     CeedInt numqpoints;
651     ierr = CeedBasisGetNumQuadraturePoints(b, &numqpoints); CeedChk(ierr);
652     if (op->numqpoints && op->numqpoints != numqpoints)
653       // LCOV_EXCL_START
654       return CeedError(op->ceed, 1, "Basis with %d quadrature points "
655                        "incompatible with prior %d points", numqpoints,
656                        op->numqpoints);
657     // LCOV_EXCL_STOP
658     op->numqpoints = numqpoints;
659   }
660   CeedQFunctionField qfield;
661   CeedOperatorField *ofield;
662   for (CeedInt i=0; i<op->qf->numinputfields; i++) {
663     if (!strcmp(fieldname, (*op->qf->inputfields[i]).fieldname)) {
664       qfield = op->qf->inputfields[i];
665       ofield = &op->inputfields[i];
666       goto found;
667     }
668   }
669   for (CeedInt i=0; i<op->qf->numoutputfields; i++) {
670     if (!strcmp(fieldname, (*op->qf->outputfields[i]).fieldname)) {
671       qfield = op->qf->inputfields[i];
672       ofield = &op->outputfields[i];
673       goto found;
674     }
675   }
676   // LCOV_EXCL_START
677   return CeedError(op->ceed, 1, "QFunction has no knowledge of field '%s'",
678                    fieldname);
679   // LCOV_EXCL_STOP
680 found:
681   if (r == CEED_ELEMRESTRICTION_NONE && qfield->emode != CEED_EVAL_WEIGHT)
682     return CeedError(op->ceed, 1, "CEED_ELEMRESTRICTION_NONE can only be used "
683                      "for a field with eval mode CEED_EVAL_WEIGHT");
684   ierr = CeedCalloc(1, ofield); CeedChk(ierr);
685   (*ofield)->Erestrict = r;
686   r->refcount += 1;
687   (*ofield)->basis = b;
688   if (b != CEED_BASIS_COLLOCATED)
689     b->refcount += 1;
690   (*ofield)->vec = v;
691   if (v != CEED_VECTOR_ACTIVE && v != CEED_VECTOR_NONE)
692     v->refcount += 1;
693   op->nfields += 1;
694   return 0;
695 }
696 
697 /**
698   @brief Add a sub-operator to a composite CeedOperator
699 
700   @param[out] compositeop Composite CeedOperator
701   @param      subop       Sub-operator CeedOperator
702 
703   @return An error code: 0 - success, otherwise - failure
704 
705   @ref User
706  */
707 int CeedCompositeOperatorAddSub(CeedOperator compositeop, CeedOperator subop) {
708   if (!compositeop->composite)
709     // LCOV_EXCL_START
710     return CeedError(compositeop->ceed, 1, "CeedOperator is not a composite "
711                      "operator");
712   // LCOV_EXCL_STOP
713 
714   if (compositeop->numsub == CEED_COMPOSITE_MAX)
715     // LCOV_EXCL_START
716     return CeedError(compositeop->ceed, 1, "Cannot add additional suboperators");
717   // LCOV_EXCL_STOP
718 
719   compositeop->suboperators[compositeop->numsub] = subop;
720   subop->refcount++;
721   compositeop->numsub++;
722   return 0;
723 }
724 
725 /**
726   @brief Assemble a linear CeedQFunction associated with a CeedOperator
727 
728   This returns a CeedVector containing a matrix at each quadrature point
729     providing the action of the CeedQFunction associated with the CeedOperator.
730     The vector 'assembled' is of shape
731       [num_elements, num_input_fields, num_output_fields, num_quad_points]
732     and contains column-major matrices representing the action of the
733     CeedQFunction for a corresponding quadrature point on an element. Inputs and
734     outputs are in the order provided by the user when adding CeedOperator fields.
735     For example, a QFunction with inputs 'u' and 'gradu' and outputs 'gradv' and
736     'v', provided in that order, would result in an assembled QFunction that
737     consists of (1 + dim) x (dim + 1) matrices at each quadrature point acting
738     on the input [u, du_0, du_1] and producing the output [dv_0, dv_1, v].
739 
740   @param op             CeedOperator to assemble CeedQFunction
741   @param[out] assembled CeedVector to store assembled CeedQFunction at
742                           quadrature points
743   @param[out] rstr      CeedElemRestriction for CeedVector containing assembled
744                           CeedQFunction
745   @param request        Address of CeedRequest for non-blocking completion, else
746                           CEED_REQUEST_IMMEDIATE
747 
748   @return An error code: 0 - success, otherwise - failure
749 
750   @ref User
751 **/
752 int CeedOperatorAssembleLinearQFunction(CeedOperator op, CeedVector *assembled,
753                                         CeedElemRestriction *rstr,
754                                         CeedRequest *request) {
755   int ierr;
756   Ceed ceed = op->ceed;
757   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
758 
759   // Backend version
760   if (op->AssembleLinearQFunction) {
761     ierr = op->AssembleLinearQFunction(op, assembled, rstr, request);
762     CeedChk(ierr);
763   } else {
764     // Fallback to reference Ceed
765     if (!op->opfallback) {
766       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
767     }
768     // Assemble
769     ierr = op->opfallback->AssembleLinearQFunction(op->opfallback, assembled,
770            rstr, request); CeedChk(ierr);
771   }
772 
773   return 0;
774 }
775 
776 /**
777   @brief Assemble the diagonal of a square linear Operator
778 
779   This returns a CeedVector containing the diagonal of a linear CeedOperator.
780 
781   @param op             CeedOperator to assemble CeedQFunction
782   @param[out] assembled CeedVector to store assembled CeedOperator diagonal
783   @param request        Address of CeedRequest for non-blocking completion, else
784                           CEED_REQUEST_IMMEDIATE
785 
786   @return An error code: 0 - success, otherwise - failure
787 
788   @ref User
789 **/
790 int CeedOperatorAssembleLinearDiagonal(CeedOperator op, CeedVector *assembled,
791                                        CeedRequest *request) {
792   int ierr;
793   Ceed ceed = op->ceed;
794   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
795 
796   // Use backend version, if available
797   if (op->AssembleLinearDiagonal) {
798     ierr = op->AssembleLinearDiagonal(op, assembled, request); CeedChk(ierr);
799   } else {
800     // Fallback to reference Ceed
801     if (!op->opfallback) {
802       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
803     }
804     // Assemble
805     ierr = op->opfallback->AssembleLinearDiagonal(op->opfallback, assembled,
806            request); CeedChk(ierr);
807   }
808 
809   return 0;
810 }
811 
812 /**
813   @brief Build a FDM based approximate inverse for each element for a
814            CeedOperator
815 
816   This returns a CeedOperator and CeedVector to apply a Fast Diagonalization
817     Method based approximate inverse. This function obtains the simultaneous
818     diagonalization for the 1D mass and Laplacian operators,
819       M = V^T V, K = V^T S V.
820     The assembled QFunction is used to modify the eigenvalues from simultaneous
821     diagonalization and obtain an approximate inverse of the form
822       V^T S^hat V. The CeedOperator must be linear and non-composite. The
823     associated CeedQFunction must therefore also be linear.
824 
825   @param op             CeedOperator to create element inverses
826   @param[out] fdminv    CeedOperator to apply the action of a FDM based inverse
827                           for each element
828   @param request        Address of CeedRequest for non-blocking completion, else
829                           CEED_REQUEST_IMMEDIATE
830 
831   @return An error code: 0 - success, otherwise - failure
832 
833   @ref Backend
834 **/
835 int CeedOperatorCreateFDMElementInverse(CeedOperator op, CeedOperator *fdminv,
836                                         CeedRequest *request) {
837   int ierr;
838   Ceed ceed = op->ceed;
839   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
840 
841   // Use backend version, if available
842   if (op->CreateFDMElementInverse) {
843     ierr = op->CreateFDMElementInverse(op, fdminv, request); CeedChk(ierr);
844   } else {
845     // Fallback to reference Ceed
846     if (!op->opfallback) {
847       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
848     }
849     // Assemble
850     ierr = op->opfallback->CreateFDMElementInverse(op->opfallback, fdminv,
851            request); CeedChk(ierr);
852   }
853 
854   return 0;
855 }
856 
857 /**
858   @brief View a CeedOperator
859 
860   @param[in] op     CeedOperator to view
861   @param[in] stream Stream to write; typically stdout/stderr or a file
862 
863   @return Error code: 0 - success, otherwise - failure
864 
865   @ref User
866 **/
867 int CeedOperatorView(CeedOperator op, FILE *stream) {
868   int ierr;
869 
870   if (op->composite) {
871     fprintf(stream, "Composite CeedOperator\n");
872 
873     for (CeedInt i=0; i<op->numsub; i++) {
874       fprintf(stream, "  SubOperator [%d]:\n", i);
875       ierr = CeedOperatorSingleView(op->suboperators[i], 1, stream);
876       CeedChk(ierr);
877     }
878   } else {
879     fprintf(stream, "CeedOperator\n");
880     ierr = CeedOperatorSingleView(op, 0, stream); CeedChk(ierr);
881   }
882 
883   return 0;
884 }
885 
886 /**
887   @brief Apply CeedOperator to a vector
888 
889   This computes the action of the operator on the specified (active) input,
890   yielding its (active) output.  All inputs and outputs must be specified using
891   CeedOperatorSetField().
892 
893   @param op        CeedOperator to apply
894   @param[in] in    CeedVector containing input state or CEED_VECTOR_NONE if
895                   there are no active inputs
896   @param[out] out  CeedVector to store result of applying operator (must be
897                      distinct from @a in) or CEED_VECTOR_NONE if there are no
898                      active outputs
899   @param request   Address of CeedRequest for non-blocking completion, else
900                      CEED_REQUEST_IMMEDIATE
901 
902   @return An error code: 0 - success, otherwise - failure
903 
904   @ref User
905 **/
906 int CeedOperatorApply(CeedOperator op, CeedVector in, CeedVector out,
907                       CeedRequest *request) {
908   int ierr;
909   Ceed ceed = op->ceed;
910   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
911 
912   if (op->numelements)  {
913     // Standard Operator
914     if (op->Apply) {
915       ierr = op->Apply(op, in, out, request); CeedChk(ierr);
916     } else {
917       // Zero all output vectors
918       CeedQFunction qf = op->qf;
919       for (CeedInt i=0; i<qf->numoutputfields; i++) {
920         CeedVector vec = op->outputfields[i]->vec;
921         if (vec == CEED_VECTOR_ACTIVE)
922           vec = out;
923         if (vec != CEED_VECTOR_NONE) {
924           ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
925         }
926       }
927       // Apply
928       ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
929     }
930   } else if (op->composite) {
931     // Composite Operator
932     if (op->ApplyComposite) {
933       ierr = op->ApplyComposite(op, in, out, request); CeedChk(ierr);
934     } else {
935       CeedInt numsub;
936       ierr = CeedOperatorGetNumSub(op, &numsub); CeedChk(ierr);
937       CeedOperator *suboperators;
938       ierr = CeedOperatorGetSubList(op, &suboperators); CeedChk(ierr);
939 
940       // Zero all output vectors
941       if (out != CEED_VECTOR_NONE) {
942         ierr = CeedVectorSetValue(out, 0.0); CeedChk(ierr);
943       }
944       for (CeedInt i=0; i<numsub; i++) {
945         for (CeedInt j=0; j<suboperators[i]->qf->numoutputfields; j++) {
946           CeedVector vec = suboperators[i]->outputfields[j]->vec;
947           if (vec != CEED_VECTOR_ACTIVE && vec != CEED_VECTOR_NONE) {
948             ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
949           }
950         }
951       }
952       // Apply
953       for (CeedInt i=0; i<op->numsub; i++) {
954         ierr = CeedOperatorApplyAdd(op->suboperators[i], in, out, request);
955         CeedChk(ierr);
956       }
957     }
958   }
959 
960   return 0;
961 }
962 
963 /**
964   @brief Apply CeedOperator to a vector and add result to output vector
965 
966   This computes the action of the operator on the specified (active) input,
967   yielding its (active) output.  All inputs and outputs must be specified using
968   CeedOperatorSetField().
969 
970   @param op        CeedOperator to apply
971   @param[in] in    CeedVector containing input state or NULL if there are no
972                      active inputs
973   @param[out] out  CeedVector to sum in result of applying operator (must be
974                      distinct from @a in) or NULL if there are no active outputs
975   @param request   Address of CeedRequest for non-blocking completion, else
976                      CEED_REQUEST_IMMEDIATE
977 
978   @return An error code: 0 - success, otherwise - failure
979 
980   @ref User
981 **/
982 int CeedOperatorApplyAdd(CeedOperator op, CeedVector in, CeedVector out,
983                          CeedRequest *request) {
984   int ierr;
985   Ceed ceed = op->ceed;
986   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
987 
988   if (op->numelements)  {
989     // Standard Operator
990     ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
991   } else if (op->composite) {
992     // Composite Operator
993     if (op->ApplyAddComposite) {
994       ierr = op->ApplyAddComposite(op, in, out, request); CeedChk(ierr);
995     } else {
996       CeedInt numsub;
997       ierr = CeedOperatorGetNumSub(op, &numsub); CeedChk(ierr);
998       CeedOperator *suboperators;
999       ierr = CeedOperatorGetSubList(op, &suboperators); CeedChk(ierr);
1000 
1001       for (CeedInt i=0; i<numsub; i++) {
1002         ierr = CeedOperatorApplyAdd(suboperators[i], in, out, request);
1003         CeedChk(ierr);
1004       }
1005     }
1006   }
1007 
1008   return 0;
1009 }
1010 
1011 /**
1012   @brief Destroy a CeedOperator
1013 
1014   @param op CeedOperator to destroy
1015 
1016   @return An error code: 0 - success, otherwise - failure
1017 
1018   @ref User
1019 **/
1020 int CeedOperatorDestroy(CeedOperator *op) {
1021   int ierr;
1022 
1023   if (!*op || --(*op)->refcount > 0) return 0;
1024   if ((*op)->Destroy) {
1025     ierr = (*op)->Destroy(*op); CeedChk(ierr);
1026   }
1027   ierr = CeedDestroy(&(*op)->ceed); CeedChk(ierr);
1028   // Free fields
1029   for (int i=0; i<(*op)->nfields; i++)
1030     if ((*op)->inputfields[i]) {
1031       if ((*op)->inputfields[i]->Erestrict != CEED_ELEMRESTRICTION_NONE) {
1032         ierr = CeedElemRestrictionDestroy(&(*op)->inputfields[i]->Erestrict);
1033         CeedChk(ierr);
1034       }
1035       if ((*op)->inputfields[i]->basis != CEED_BASIS_COLLOCATED) {
1036         ierr = CeedBasisDestroy(&(*op)->inputfields[i]->basis); CeedChk(ierr);
1037       }
1038       if ((*op)->inputfields[i]->vec != CEED_VECTOR_ACTIVE &&
1039           (*op)->inputfields[i]->vec != CEED_VECTOR_NONE ) {
1040         ierr = CeedVectorDestroy(&(*op)->inputfields[i]->vec); CeedChk(ierr);
1041       }
1042       ierr = CeedFree(&(*op)->inputfields[i]); CeedChk(ierr);
1043     }
1044   for (int i=0; i<(*op)->nfields; i++)
1045     if ((*op)->outputfields[i]) {
1046       ierr = CeedElemRestrictionDestroy(&(*op)->outputfields[i]->Erestrict);
1047       CeedChk(ierr);
1048       if ((*op)->outputfields[i]->basis != CEED_BASIS_COLLOCATED) {
1049         ierr = CeedBasisDestroy(&(*op)->outputfields[i]->basis); CeedChk(ierr);
1050       }
1051       if ((*op)->outputfields[i]->vec != CEED_VECTOR_ACTIVE &&
1052           (*op)->outputfields[i]->vec != CEED_VECTOR_NONE ) {
1053         ierr = CeedVectorDestroy(&(*op)->outputfields[i]->vec); CeedChk(ierr);
1054       }
1055       ierr = CeedFree(&(*op)->outputfields[i]); CeedChk(ierr);
1056     }
1057   // Destroy suboperators
1058   for (int i=0; i<(*op)->numsub; i++)
1059     if ((*op)->suboperators[i]) {
1060       ierr = CeedOperatorDestroy(&(*op)->suboperators[i]); CeedChk(ierr);
1061     }
1062   ierr = CeedQFunctionDestroy(&(*op)->qf); CeedChk(ierr);
1063   ierr = CeedQFunctionDestroy(&(*op)->dqf); CeedChk(ierr);
1064   ierr = CeedQFunctionDestroy(&(*op)->dqfT); CeedChk(ierr);
1065 
1066   // Destroy fallback
1067   if ((*op)->opfallback) {
1068     ierr = (*op)->qffallback->Destroy((*op)->qffallback); CeedChk(ierr);
1069     ierr = CeedFree(&(*op)->qffallback); CeedChk(ierr);
1070     ierr = (*op)->opfallback->Destroy((*op)->opfallback); CeedChk(ierr);
1071     ierr = CeedFree(&(*op)->opfallback); CeedChk(ierr);
1072   }
1073 
1074   ierr = CeedFree(&(*op)->inputfields); CeedChk(ierr);
1075   ierr = CeedFree(&(*op)->outputfields); CeedChk(ierr);
1076   ierr = CeedFree(&(*op)->suboperators); CeedChk(ierr);
1077   ierr = CeedFree(op); CeedChk(ierr);
1078   return 0;
1079 }
1080 
1081 /// @}
1082