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